diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/core/input/test_input_event_key.h | 7 | ||||
| -rw-r--r-- | tests/core/input/test_input_event_mouse.h | 81 | ||||
| -rw-r--r-- | tests/core/io/test_file_access.h | 4 | ||||
| -rw-r--r-- | tests/core/io/test_image.h | 5 | ||||
| -rw-r--r-- | tests/core/io/test_xml_parser.h | 2 | ||||
| -rw-r--r-- | tests/core/math/test_geometry_3d.h | 8 | ||||
| -rw-r--r-- | tests/core/math/test_plane.h | 4 | ||||
| -rw-r--r-- | tests/core/math/test_transform_2d.h | 13 | ||||
| -rw-r--r-- | tests/core/object/test_class_db.h | 4 | ||||
| -rw-r--r-- | tests/core/variant/test_variant.h | 190 | ||||
| -rw-r--r-- | tests/display_server_mock.h | 150 | ||||
| -rw-r--r-- | tests/scene/test_arraymesh.h | 21 | ||||
| -rw-r--r-- | tests/scene/test_code_edit.h | 50 | ||||
| -rw-r--r-- | tests/scene/test_curve_2d.h | 228 | ||||
| -rw-r--r-- | tests/scene/test_primitives.h | 2 | ||||
| -rw-r--r-- | tests/scene/test_text_edit.h | 15 | ||||
| -rw-r--r-- | tests/test_macros.h | 16 | ||||
| -rw-r--r-- | tests/test_main.cpp | 7 |
18 files changed, 748 insertions, 59 deletions
diff --git a/tests/core/input/test_input_event_key.h b/tests/core/input/test_input_event_key.h index 697215bab7..3317941fad 100644 --- a/tests/core/input/test_input_event_key.h +++ b/tests/core/input/test_input_event_key.h @@ -106,11 +106,11 @@ TEST_CASE("[InputEventKey] Key correctly converts itself to text") { // Key is None without a physical key. none_key.set_keycode(Key::NONE); - CHECK(none_key.as_text() == " (Physical)"); + CHECK(none_key.as_text() == "(Unset)"); // Key is none and has modifiers. none_key.set_ctrl_pressed(true); - CHECK(none_key.as_text() == "Ctrl+ (Physical)"); + CHECK(none_key.as_text() == "Ctrl+(Unset)"); // Key is None WITH a physical key AND modifiers. none_key.set_physical_keycode(Key::ENTER); @@ -144,8 +144,7 @@ TEST_CASE("[InputEventKey] Key correctly converts itself to text") { TEST_CASE("[InputEventKey] Key correctly converts its state to a string representation") { InputEventKey none_key; - // Set physical to true. - CHECK(none_key.to_string() == "InputEventKey: keycode=0 (), mods=none, physical=true, pressed=false, echo=false"); + CHECK(none_key.to_string() == "InputEventKey: keycode=(Unset), mods=none, physical=false, pressed=false, echo=false"); // Set physical key to Escape. none_key.set_physical_keycode(Key::ESCAPE); CHECK(none_key.to_string() == "InputEventKey: keycode=4194305 (Escape), mods=none, physical=true, pressed=false, echo=false"); diff --git a/tests/core/input/test_input_event_mouse.h b/tests/core/input/test_input_event_mouse.h new file mode 100644 index 0000000000..0da4f14160 --- /dev/null +++ b/tests/core/input/test_input_event_mouse.h @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* test_input_event_mouse.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TEST_INPUT_EVENT_MOUSE_H +#define TEST_INPUT_EVENT_MOUSE_H + +#include "core/input/input_event.h" +#include "tests/test_macros.h" + +namespace TestInputEventMouse { + +TEST_CASE("[InputEventMouse] Mouse button mask is set correctly") { + InputEventMouse mousekey; + + mousekey.set_button_mask(MouseButtonMask::LEFT); + CHECK(mousekey.get_button_mask().has_flag(MouseButtonMask::LEFT)); + + mousekey.set_button_mask(MouseButtonMask::MB_XBUTTON1); + CHECK(mousekey.get_button_mask().has_flag(MouseButtonMask::MB_XBUTTON1)); + + mousekey.set_button_mask(MouseButtonMask::MB_XBUTTON2); + CHECK(mousekey.get_button_mask().has_flag(MouseButtonMask::MB_XBUTTON2)); + + mousekey.set_button_mask(MouseButtonMask::MIDDLE); + CHECK(mousekey.get_button_mask().has_flag(MouseButtonMask::MIDDLE)); + + mousekey.set_button_mask(MouseButtonMask::RIGHT); + CHECK(mousekey.get_button_mask().has_flag(MouseButtonMask::RIGHT)); +} + +TEST_CASE("[InputEventMouse] Setting the mouse position works correctly") { + InputEventMouse mousekey; + + mousekey.set_position(Vector2{ 10, 10 }); + CHECK(mousekey.get_position() == Vector2{ 10, 10 }); + + mousekey.set_position(Vector2{ -1, -1 }); + CHECK(mousekey.get_position() == Vector2{ -1, -1 }); +} + +TEST_CASE("[InputEventMouse] Setting the global mouse position works correctly") { + InputEventMouse mousekey; + + mousekey.set_global_position(Vector2{ 10, 10 }); + CHECK(mousekey.get_global_position() == Vector2{ 10, 10 }); + CHECK(mousekey.get_global_position() != Vector2{ 1, 1 }); + + mousekey.set_global_position(Vector2{ -1, -1 }); + CHECK(mousekey.get_global_position() == Vector2{ -1, -1 }); + CHECK(mousekey.get_global_position() != Vector2{ 1, 1 }); +} +} // namespace TestInputEventMouse + +#endif // TEST_INPUT_EVENT_MOUSE_H diff --git a/tests/core/io/test_file_access.h b/tests/core/io/test_file_access.h index 7117cb137d..243b75626f 100644 --- a/tests/core/io/test_file_access.h +++ b/tests/core/io/test_file_access.h @@ -39,6 +39,7 @@ namespace TestFileAccess { TEST_CASE("[FileAccess] CSV read") { Ref<FileAccess> f = FileAccess::open(TestUtils::get_data_path("translations.csv"), FileAccess::READ); + REQUIRE(!f.is_null()); Vector<String> header = f->get_csv_line(); // Default delimiter: ",". REQUIRE(header.size() == 3); @@ -81,6 +82,7 @@ TEST_CASE("[FileAccess] CSV read") { TEST_CASE("[FileAccess] Get as UTF-8 String") { Ref<FileAccess> f_lf = FileAccess::open(TestUtils::get_data_path("line_endings_lf.test.txt"), FileAccess::READ); + REQUIRE(!f_lf.is_null()); String s_lf = f_lf->get_as_utf8_string(); f_lf->seek(0); String s_lf_nocr = f_lf->get_as_utf8_string(true); @@ -88,6 +90,7 @@ TEST_CASE("[FileAccess] Get as UTF-8 String") { CHECK(s_lf_nocr == "Hello darkness\nMy old friend\nI've come to talk\nWith you again\n"); Ref<FileAccess> f_crlf = FileAccess::open(TestUtils::get_data_path("line_endings_crlf.test.txt"), FileAccess::READ); + REQUIRE(!f_crlf.is_null()); String s_crlf = f_crlf->get_as_utf8_string(); f_crlf->seek(0); String s_crlf_nocr = f_crlf->get_as_utf8_string(true); @@ -95,6 +98,7 @@ TEST_CASE("[FileAccess] Get as UTF-8 String") { CHECK(s_crlf_nocr == "Hello darkness\nMy old friend\nI've come to talk\nWith you again\n"); Ref<FileAccess> f_cr = FileAccess::open(TestUtils::get_data_path("line_endings_cr.test.txt"), FileAccess::READ); + REQUIRE(!f_cr.is_null()); String s_cr = f_cr->get_as_utf8_string(); f_cr->seek(0); String s_cr_nocr = f_cr->get_as_utf8_string(true); diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h index 3d52bc96d1..763eaed2f8 100644 --- a/tests/core/io/test_image.h +++ b/tests/core/io/test_image.h @@ -107,6 +107,7 @@ TEST_CASE("[Image] Saving and loading") { // Load BMP Ref<Image> image_bmp = memnew(Image()); Ref<FileAccess> f_bmp = FileAccess::open(TestUtils::get_data_path("images/icon.bmp"), FileAccess::READ, &err); + REQUIRE(!f_bmp.is_null()); PackedByteArray data_bmp; data_bmp.resize(f_bmp->get_length() + 1); f_bmp->get_buffer(data_bmp.ptrw(), f_bmp->get_length()); @@ -117,6 +118,7 @@ TEST_CASE("[Image] Saving and loading") { // Load JPG Ref<Image> image_jpg = memnew(Image()); Ref<FileAccess> f_jpg = FileAccess::open(TestUtils::get_data_path("images/icon.jpg"), FileAccess::READ, &err); + REQUIRE(!f_jpg.is_null()); PackedByteArray data_jpg; data_jpg.resize(f_jpg->get_length() + 1); f_jpg->get_buffer(data_jpg.ptrw(), f_jpg->get_length()); @@ -127,6 +129,7 @@ TEST_CASE("[Image] Saving and loading") { // Load WebP Ref<Image> image_webp = memnew(Image()); Ref<FileAccess> f_webp = FileAccess::open(TestUtils::get_data_path("images/icon.webp"), FileAccess::READ, &err); + REQUIRE(!f_webp.is_null()); PackedByteArray data_webp; data_webp.resize(f_webp->get_length() + 1); f_webp->get_buffer(data_webp.ptrw(), f_webp->get_length()); @@ -137,6 +140,7 @@ TEST_CASE("[Image] Saving and loading") { // Load PNG Ref<Image> image_png = memnew(Image()); Ref<FileAccess> f_png = FileAccess::open(TestUtils::get_data_path("images/icon.png"), FileAccess::READ, &err); + REQUIRE(!f_png.is_null()); PackedByteArray data_png; data_png.resize(f_png->get_length() + 1); f_png->get_buffer(data_png.ptrw(), f_png->get_length()); @@ -147,6 +151,7 @@ TEST_CASE("[Image] Saving and loading") { // Load TGA Ref<Image> image_tga = memnew(Image()); Ref<FileAccess> f_tga = FileAccess::open(TestUtils::get_data_path("images/icon.tga"), FileAccess::READ, &err); + REQUIRE(!f_tga.is_null()); PackedByteArray data_tga; data_tga.resize(f_tga->get_length() + 1); f_tga->get_buffer(data_tga.ptrw(), f_tga->get_length()); diff --git a/tests/core/io/test_xml_parser.h b/tests/core/io/test_xml_parser.h index f4e3f34be2..40cbea2dab 100644 --- a/tests/core/io/test_xml_parser.h +++ b/tests/core/io/test_xml_parser.h @@ -54,7 +54,7 @@ TEST_CASE("[XMLParser] End-to-end") { CHECK(parser.get_node_type() == XMLParser::NodeType::NODE_ELEMENT); CHECK(parser.get_node_name() == "top"); CHECK(parser.has_attribute("attr")); - CHECK(parser.get_attribute_value("attr") == "attr value"); + CHECK(parser.get_named_attribute_value("attr") == "attr value"); CHECK(parser.read() == OK); CHECK(parser.get_node_type() == XMLParser::NodeType::NODE_TEXT); diff --git a/tests/core/math/test_geometry_3d.h b/tests/core/math/test_geometry_3d.h index 8d57bd5319..46a99aa4b6 100644 --- a/tests/core/math/test_geometry_3d.h +++ b/tests/core/math/test_geometry_3d.h @@ -388,13 +388,13 @@ TEST_CASE("[Geometry3D] Segment Intersects Triangle") { TEST_CASE("[Geometry3D] Triangle and Box Overlap") { struct Case { - Vector3 box_centre; + Vector3 box_center; Vector3 box_half_size; Vector3 *tri_verts = nullptr; bool want; Case(){}; - Case(Vector3 p_centre, Vector3 p_half_size, Vector3 *p_verts, bool p_want) : - box_centre(p_centre), box_half_size(p_half_size), tri_verts(p_verts), want(p_want){}; + Case(Vector3 p_center, Vector3 p_half_size, Vector3 *p_verts, bool p_want) : + box_center(p_center), box_half_size(p_half_size), tri_verts(p_verts), want(p_want){}; }; Vector<Case> tt; Vector3 GoodTriangle[3] = { Vector3(3, 2, 3), Vector3(2, 2, 1), Vector3(2, 1, 1) }; @@ -403,7 +403,7 @@ TEST_CASE("[Geometry3D] Triangle and Box Overlap") { tt.push_back(Case(Vector3(1000, 1000, 1000), Vector3(1, 1, 1), BadTriangle, false)); for (int i = 0; i < tt.size(); ++i) { Case current_case = tt[i]; - bool output = Geometry3D::triangle_box_overlap(current_case.box_centre, current_case.box_half_size, current_case.tri_verts); + bool output = Geometry3D::triangle_box_overlap(current_case.box_center, current_case.box_half_size, current_case.tri_verts); CHECK(output == current_case.want); } } diff --git a/tests/core/math/test_plane.h b/tests/core/math/test_plane.h index b2b857ca69..f784a29a17 100644 --- a/tests/core/math/test_plane.h +++ b/tests/core/math/test_plane.h @@ -87,8 +87,8 @@ TEST_CASE("[Plane] Plane-point operations") { const Plane y_facing_plane = Plane(0, 1, 0, 4); CHECK_MESSAGE( - plane.center().is_equal_approx(Vector3(32 * 3, 22 * 3, 16 * 3)), - "center() should return a vector pointing to the center of the plane."); + plane.get_center().is_equal_approx(Vector3(32 * 3, 22 * 3, 16 * 3)), + "get_center() should return a vector pointing to the center of the plane."); CHECK_MESSAGE( y_facing_plane.is_point_over(Vector3(0, 5, 0)), diff --git a/tests/core/math/test_transform_2d.h b/tests/core/math/test_transform_2d.h index dc2b6e2ba8..ca27776180 100644 --- a/tests/core/math/test_transform_2d.h +++ b/tests/core/math/test_transform_2d.h @@ -84,6 +84,19 @@ TEST_CASE("[Transform2D] rotation") { CHECK(orig.rotated_local(phi) == orig * R); } +TEST_CASE("[Transform2D] Interpolation") { + Transform2D rotate_scale_skew_pos = Transform2D(Math::deg_to_rad(170.0), Vector2(3.6, 8.0), Math::deg_to_rad(20.0), Vector2(2.4, 6.8)); + Transform2D rotate_scale_skew_pos_halfway = Transform2D(Math::deg_to_rad(85.0), Vector2(2.3, 4.5), Math::deg_to_rad(10.0), Vector2(1.2, 3.4)); + Transform2D interpolated = Transform2D().interpolate_with(rotate_scale_skew_pos, 0.5); + CHECK(interpolated.get_origin().is_equal_approx(rotate_scale_skew_pos_halfway.get_origin())); + CHECK(interpolated.get_rotation() == doctest::Approx(rotate_scale_skew_pos_halfway.get_rotation())); + CHECK(interpolated.get_scale().is_equal_approx(rotate_scale_skew_pos_halfway.get_scale())); + CHECK(interpolated.get_skew() == doctest::Approx(rotate_scale_skew_pos_halfway.get_skew())); + CHECK(interpolated.is_equal_approx(rotate_scale_skew_pos_halfway)); + interpolated = rotate_scale_skew_pos.interpolate_with(Transform2D(), 0.5); + CHECK(interpolated.is_equal_approx(rotate_scale_skew_pos_halfway)); +} + TEST_CASE("[Transform2D] Finite number checks") { const Vector2 x(0, 1); const Vector2 infinite(NAN, NAN); diff --git a/tests/core/object/test_class_db.h b/tests/core/object/test_class_db.h index eeb1648924..e68995e539 100644 --- a/tests/core/object/test_class_db.h +++ b/tests/core/object/test_class_db.h @@ -559,6 +559,8 @@ void add_exposed_classes(Context &r_context) { MethodData method; method.name = method_info.name; + TEST_FAIL_COND(!String(method.name).is_valid_identifier(), + "Method name is not a valid identifier: '", exposed_class.name, ".", method.name, "'."); if (method_info.flags & METHOD_FLAG_VIRTUAL) { method.is_virtual = true; @@ -682,6 +684,8 @@ void add_exposed_classes(Context &r_context) { const MethodInfo &method_info = signal_map.get(K.key); signal.name = method_info.name; + TEST_FAIL_COND(!String(signal.name).is_valid_identifier(), + "Signal name is not a valid identifier: '", exposed_class.name, ".", signal.name, "'."); int argc = method_info.arguments.size(); diff --git a/tests/core/variant/test_variant.h b/tests/core/variant/test_variant.h index 85b53264f2..dfbaa36af8 100644 --- a/tests/core/variant/test_variant.h +++ b/tests/core/variant/test_variant.h @@ -868,6 +868,196 @@ TEST_CASE("[Variant] Basic comparison") { CHECK_NE(Variant(Dictionary()), Variant()); } +TEST_CASE("[Variant] Identity comparison") { + // Value types are compared by value + Variant aabb = AABB(); + CHECK(aabb.identity_compare(aabb)); + CHECK(aabb.identity_compare(AABB())); + CHECK_FALSE(aabb.identity_compare(AABB(Vector3(1, 2, 3), Vector3(1, 2, 3)))); + + Variant basis = Basis(); + CHECK(basis.identity_compare(basis)); + CHECK(basis.identity_compare(Basis())); + CHECK_FALSE(basis.identity_compare(Basis(Quaternion(Vector3(1, 2, 3).normalized(), 45)))); + + Variant bool_var = true; + CHECK(bool_var.identity_compare(bool_var)); + CHECK(bool_var.identity_compare(true)); + CHECK_FALSE(bool_var.identity_compare(false)); + + Variant callable = Callable(); + CHECK(callable.identity_compare(callable)); + CHECK(callable.identity_compare(Callable())); + CHECK_FALSE(callable.identity_compare(Callable(ObjectID(), StringName("lambda")))); + + Variant color = Color(); + CHECK(color.identity_compare(color)); + CHECK(color.identity_compare(Color())); + CHECK_FALSE(color.identity_compare(Color(255, 0, 255))); + + Variant float_var = 1.0; + CHECK(float_var.identity_compare(float_var)); + CHECK(float_var.identity_compare(1.0)); + CHECK_FALSE(float_var.identity_compare(2.0)); + + Variant int_var = 1; + CHECK(int_var.identity_compare(int_var)); + CHECK(int_var.identity_compare(1)); + CHECK_FALSE(int_var.identity_compare(2)); + + Variant nil = Variant(); + CHECK(nil.identity_compare(nil)); + CHECK(nil.identity_compare(Variant())); + CHECK_FALSE(nil.identity_compare(true)); + + Variant node_path = NodePath("godot"); + CHECK(node_path.identity_compare(node_path)); + CHECK(node_path.identity_compare(NodePath("godot"))); + CHECK_FALSE(node_path.identity_compare(NodePath("waiting"))); + + Variant plane = Plane(); + CHECK(plane.identity_compare(plane)); + CHECK(plane.identity_compare(Plane())); + CHECK_FALSE(plane.identity_compare(Plane(Vector3(1, 2, 3), 42))); + + Variant projection = Projection(); + CHECK(projection.identity_compare(projection)); + CHECK(projection.identity_compare(Projection())); + CHECK_FALSE(projection.identity_compare(Projection(Transform3D(Basis(Vector3(1, 2, 3).normalized(), 45), Vector3(1, 2, 3))))); + + Variant quaternion = Quaternion(); + CHECK(quaternion.identity_compare(quaternion)); + CHECK(quaternion.identity_compare(Quaternion())); + CHECK_FALSE(quaternion.identity_compare(Quaternion(Vector3(1, 2, 3).normalized(), 45))); + + Variant rect2 = Rect2(); + CHECK(rect2.identity_compare(rect2)); + CHECK(rect2.identity_compare(Rect2())); + CHECK_FALSE(rect2.identity_compare(Rect2(Point2(Vector2(1, 2)), Size2(Vector2(1, 2))))); + + Variant rect2i = Rect2i(); + CHECK(rect2i.identity_compare(rect2i)); + CHECK(rect2i.identity_compare(Rect2i())); + CHECK_FALSE(rect2i.identity_compare(Rect2i(Point2i(Vector2i(1, 2)), Size2i(Vector2i(1, 2))))); + + Variant rid = RID(); + CHECK(rid.identity_compare(rid)); + CHECK(rid.identity_compare(RID())); + CHECK_FALSE(rid.identity_compare(RID::from_uint64(123))); + + Variant signal = Signal(); + CHECK(signal.identity_compare(signal)); + CHECK(signal.identity_compare(Signal())); + CHECK_FALSE(signal.identity_compare(Signal(ObjectID(), StringName("lambda")))); + + Variant str = "godot"; + CHECK(str.identity_compare(str)); + CHECK(str.identity_compare("godot")); + CHECK_FALSE(str.identity_compare("waiting")); + + Variant str_name = StringName("godot"); + CHECK(str_name.identity_compare(str_name)); + CHECK(str_name.identity_compare(StringName("godot"))); + CHECK_FALSE(str_name.identity_compare(StringName("waiting"))); + + Variant transform2d = Transform2D(); + CHECK(transform2d.identity_compare(transform2d)); + CHECK(transform2d.identity_compare(Transform2D())); + CHECK_FALSE(transform2d.identity_compare(Transform2D(45, Vector2(1, 2)))); + + Variant transform3d = Transform3D(); + CHECK(transform3d.identity_compare(transform3d)); + CHECK(transform3d.identity_compare(Transform3D())); + CHECK_FALSE(transform3d.identity_compare(Transform3D(Basis(Quaternion(Vector3(1, 2, 3).normalized(), 45)), Vector3(1, 2, 3)))); + + Variant vect2 = Vector2(); + CHECK(vect2.identity_compare(vect2)); + CHECK(vect2.identity_compare(Vector2())); + CHECK_FALSE(vect2.identity_compare(Vector2(1, 2))); + + Variant vect2i = Vector2i(); + CHECK(vect2i.identity_compare(vect2i)); + CHECK(vect2i.identity_compare(Vector2i())); + CHECK_FALSE(vect2i.identity_compare(Vector2i(1, 2))); + + Variant vect3 = Vector3(); + CHECK(vect3.identity_compare(vect3)); + CHECK(vect3.identity_compare(Vector3())); + CHECK_FALSE(vect3.identity_compare(Vector3(1, 2, 3))); + + Variant vect3i = Vector3i(); + CHECK(vect3i.identity_compare(vect3i)); + CHECK(vect3i.identity_compare(Vector3i())); + CHECK_FALSE(vect3i.identity_compare(Vector3i(1, 2, 3))); + + Variant vect4 = Vector4(); + CHECK(vect4.identity_compare(vect4)); + CHECK(vect4.identity_compare(Vector4())); + CHECK_FALSE(vect4.identity_compare(Vector4(1, 2, 3, 4))); + + Variant vect4i = Vector4i(); + CHECK(vect4i.identity_compare(vect4i)); + CHECK(vect4i.identity_compare(Vector4i())); + CHECK_FALSE(vect4i.identity_compare(Vector4i(1, 2, 3, 4))); + + // Reference types are compared by reference + Variant array = Array(); + CHECK(array.identity_compare(array)); + CHECK_FALSE(array.identity_compare(Array())); + + Variant dictionary = Dictionary(); + CHECK(dictionary.identity_compare(dictionary)); + CHECK_FALSE(dictionary.identity_compare(Dictionary())); + + Variant packed_byte_array = PackedByteArray(); + CHECK(packed_byte_array.identity_compare(packed_byte_array)); + CHECK_FALSE(packed_byte_array.identity_compare(PackedByteArray())); + + Variant packed_color_array = PackedColorArray(); + CHECK(packed_color_array.identity_compare(packed_color_array)); + CHECK_FALSE(packed_color_array.identity_compare(PackedColorArray())); + + Variant packed_float32_array = PackedFloat32Array(); + CHECK(packed_float32_array.identity_compare(packed_float32_array)); + CHECK_FALSE(packed_float32_array.identity_compare(PackedFloat32Array())); + + Variant packed_float64_array = PackedFloat64Array(); + CHECK(packed_float64_array.identity_compare(packed_float64_array)); + CHECK_FALSE(packed_float64_array.identity_compare(PackedFloat64Array())); + + Variant packed_int32_array = PackedInt32Array(); + CHECK(packed_int32_array.identity_compare(packed_int32_array)); + CHECK_FALSE(packed_int32_array.identity_compare(PackedInt32Array())); + + Variant packed_int64_array = PackedInt64Array(); + CHECK(packed_int64_array.identity_compare(packed_int64_array)); + CHECK_FALSE(packed_int64_array.identity_compare(PackedInt64Array())); + + Variant packed_string_array = PackedStringArray(); + CHECK(packed_string_array.identity_compare(packed_string_array)); + CHECK_FALSE(packed_string_array.identity_compare(PackedStringArray())); + + Variant packed_vector2_array = PackedVector2Array(); + CHECK(packed_vector2_array.identity_compare(packed_vector2_array)); + CHECK_FALSE(packed_vector2_array.identity_compare(PackedVector2Array())); + + Variant packed_vector3_array = PackedVector3Array(); + CHECK(packed_vector3_array.identity_compare(packed_vector3_array)); + CHECK_FALSE(packed_vector3_array.identity_compare(PackedVector3Array())); + + Object obj_one = Object(); + Variant obj_one_var = &obj_one; + Object obj_two = Object(); + Variant obj_two_var = &obj_two; + CHECK(obj_one_var.identity_compare(obj_one_var)); + CHECK_FALSE(obj_one_var.identity_compare(obj_two_var)); + + Variant obj_null_one_var = Variant((Object *)nullptr); + Variant obj_null_two_var = Variant((Object *)nullptr); + CHECK(obj_null_one_var.identity_compare(obj_null_one_var)); + CHECK(obj_null_one_var.identity_compare(obj_null_two_var)); +} + TEST_CASE("[Variant] Nested array comparison") { Array a1 = build_array(1, build_array(2, 3)); Array a2 = build_array(1, build_array(2, 3)); diff --git a/tests/display_server_mock.h b/tests/display_server_mock.h new file mode 100644 index 0000000000..1736f2c452 --- /dev/null +++ b/tests/display_server_mock.h @@ -0,0 +1,150 @@ +/**************************************************************************/ +/* display_server_mock.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISPLAY_SERVER_MOCK_H +#define DISPLAY_SERVER_MOCK_H + +#include "servers/display_server_headless.h" + +#include "servers/rendering/dummy/rasterizer_dummy.h" + +// Specialized DisplayServer for unittests based on DisplayServerHeadless, that +// additionally supports rudimentary InputEvent handling and mouse position. +class DisplayServerMock : public DisplayServerHeadless { +private: + friend class DisplayServer; + + Point2i mouse_position = Point2i(-1, -1); // Outside of Window. + bool window_over = false; + Callable event_callback; + Callable input_event_callback; + + static Vector<String> get_rendering_drivers_func() { + Vector<String> drivers; + drivers.push_back("dummy"); + return drivers; + } + + static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { + r_error = OK; + RasterizerDummy::make_current(); + return memnew(DisplayServerMock()); + } + + static void _dispatch_input_events(const Ref<InputEvent> &p_event) { + static_cast<DisplayServerMock *>(get_singleton())->_dispatch_input_event(p_event); + } + + void _dispatch_input_event(const Ref<InputEvent> &p_event) { + Variant ev = p_event; + Variant *evp = &ev; + Variant ret; + Callable::CallError ce; + + if (input_event_callback.is_valid()) { + input_event_callback.callp((const Variant **)&evp, 1, ret, ce); + } + } + + void _set_mouse_position(const Point2i &p_position) { + if (mouse_position == p_position) { + return; + } + mouse_position = p_position; + _set_window_over(Rect2i(Point2i(0, 0), window_get_size()).has_point(p_position)); + } + + void _set_window_over(bool p_over) { + if (p_over == window_over) { + return; + } + window_over = p_over; + _send_window_event(p_over ? WINDOW_EVENT_MOUSE_ENTER : WINDOW_EVENT_MOUSE_EXIT); + } + + void _send_window_event(WindowEvent p_event) { + if (!event_callback.is_null()) { + Variant event = int(p_event); + Variant *eventp = &event; + Variant ret; + Callable::CallError ce; + event_callback.callp((const Variant **)&eventp, 1, ret, ce); + } + } + +public: + bool has_feature(Feature p_feature) const override { + switch (p_feature) { + case FEATURE_MOUSE: + return true; + default: { + } + } + return false; + } + + String get_name() const override { return "mock"; } + + // You can simulate DisplayServer-events by calling this function. + // The events will be deliverd to Godot's Input-system. + // Mouse-events (Button & Motion) will additionally update the DisplayServer's mouse position. + void simulate_event(Ref<InputEvent> p_event) { + Ref<InputEventMouse> me = p_event; + if (me.is_valid()) { + _set_mouse_position(me->get_position()); + } + Input::get_singleton()->parse_input_event(p_event); + } + + virtual Point2i mouse_get_position() const override { return mouse_position; } + + virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const override { + return Size2i(1920, 1080); + } + + virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override { + event_callback = p_callable; + } + + virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override { + input_event_callback = p_callable; + } + + static void register_mock_driver() { + register_create_function("mock", create_func, get_rendering_drivers_func); + } + + DisplayServerMock() { + Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); + } + ~DisplayServerMock() {} +}; + +#endif // DISPLAY_SERVER_MOCK_H diff --git a/tests/scene/test_arraymesh.h b/tests/scene/test_arraymesh.h index 4d9feeb4fa..b2a2ecc3bf 100644 --- a/tests/scene/test_arraymesh.h +++ b/tests/scene/test_arraymesh.h @@ -114,6 +114,17 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") { CHECK(mesh->get_blend_shape_count() == 0); } + SUBCASE("Can't add surface with incorrect number of blend shapes.") { + mesh->add_blend_shape(name_a); + mesh->add_blend_shape(name_b); + Ref<CylinderMesh> cylinder = memnew(CylinderMesh); + Array cylinder_array{}; + ERR_PRINT_OFF + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array); + ERR_PRINT_ON + CHECK(mesh->get_surface_count() == 0); + } + SUBCASE("Can't clear blend shapes after surface had been added.") { mesh->add_blend_shape(name_a); mesh->add_blend_shape(name_b); @@ -121,7 +132,15 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") { Array cylinder_array{}; cylinder_array.resize(Mesh::ARRAY_MAX); cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f); - mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array); + Array blend_shape{}; + blend_shape.resize(Mesh::ARRAY_MAX); + blend_shape[Mesh::ARRAY_VERTEX] = cylinder_array[Mesh::ARRAY_VERTEX]; + blend_shape[Mesh::ARRAY_NORMAL] = cylinder_array[Mesh::ARRAY_NORMAL]; + blend_shape[Mesh::ARRAY_TANGENT] = cylinder_array[Mesh::ARRAY_TANGENT]; + Array blend_shapes{}; + blend_shapes.push_back(blend_shape); + blend_shapes.push_back(blend_shape); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, blend_shapes); ERR_PRINT_OFF mesh->clear_blend_shapes(); diff --git a/tests/scene/test_code_edit.h b/tests/scene/test_code_edit.h index 5447c99a64..828029dabe 100644 --- a/tests/scene/test_code_edit.h +++ b/tests/scene/test_code_edit.h @@ -1954,7 +1954,7 @@ TEST_CASE("[SceneTree][CodeEdit] indent") { code_edit->set_editable(false); - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_line(0) == "\t"); code_edit->unindent_lines(); @@ -1963,16 +1963,9 @@ TEST_CASE("[SceneTree][CodeEdit] indent") { code_edit->set_editable(true); /* Simple unindent. */ - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_line(0) == ""); - /* Should inindent inplace. */ - code_edit->set_text(""); - code_edit->insert_text_at_caret("test\t"); - - code_edit->do_unindent(); - CHECK(code_edit->get_line(0) == "test"); - /* Backspace does a simple unindent. */ code_edit->set_text(""); code_edit->insert_text_at_caret("\t"); @@ -1987,7 +1980,7 @@ TEST_CASE("[SceneTree][CodeEdit] indent") { /* Caret on col zero unindent line. */ code_edit->set_text("\t\ttest"); - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_line(0) == "\ttest"); /* Check input action. */ @@ -1998,34 +1991,34 @@ TEST_CASE("[SceneTree][CodeEdit] indent") { /* Selection does entire line. */ code_edit->set_text("\t\ttest"); code_edit->select_all(); - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_line(0) == "\ttest"); /* Handles multiple lines. */ code_edit->set_text("\ttest\n\ttext"); code_edit->select_all(); - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_line(0) == "test"); CHECK(code_edit->get_line(1) == "text"); /* Do not unindent line if last col is zero. */ code_edit->set_text("\ttest\n\ttext"); code_edit->select(0, 0, 1, 0); - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_line(0) == "test"); CHECK(code_edit->get_line(1) == "\ttext"); /* Unindent even if last column of first line. */ code_edit->set_text("\ttest\n\ttext"); code_edit->select(0, 5, 1, 1); - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_line(0) == "test"); CHECK(code_edit->get_line(1) == "text"); /* Check selection is adjusted. */ code_edit->set_text("\ttest"); code_edit->select(0, 1, 0, 2); - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_selection_from_column() == 0); CHECK(code_edit->get_selection_to_column() == 1); CHECK(code_edit->get_line(0) == "test"); @@ -2041,7 +2034,7 @@ TEST_CASE("[SceneTree][CodeEdit] indent") { code_edit->set_editable(false); - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_line(0) == " "); code_edit->unindent_lines(); @@ -2050,16 +2043,9 @@ TEST_CASE("[SceneTree][CodeEdit] indent") { code_edit->set_editable(true); /* Simple unindent. */ - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_line(0) == ""); - /* Should inindent inplace. */ - code_edit->set_text(""); - code_edit->insert_text_at_caret("test "); - - code_edit->do_unindent(); - CHECK(code_edit->get_line(0) == "test"); - /* Backspace does a simple unindent. */ code_edit->set_text(""); code_edit->insert_text_at_caret(" "); @@ -2080,12 +2066,12 @@ TEST_CASE("[SceneTree][CodeEdit] indent") { /* Caret on col zero unindent line. */ code_edit->set_text(" test"); - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_line(0) == " test"); /* Only as far as needed */ code_edit->set_text(" test"); - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_line(0) == " test"); /* Check input action. */ @@ -2096,34 +2082,34 @@ TEST_CASE("[SceneTree][CodeEdit] indent") { /* Selection does entire line. */ code_edit->set_text(" test"); code_edit->select_all(); - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_line(0) == " test"); /* Handles multiple lines. */ code_edit->set_text(" test\n text"); code_edit->select_all(); - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_line(0) == "test"); CHECK(code_edit->get_line(1) == "text"); /* Do not unindent line if last col is zero. */ code_edit->set_text(" test\n text"); code_edit->select(0, 0, 1, 0); - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_line(0) == "test"); CHECK(code_edit->get_line(1) == " text"); /* Unindent even if last column of first line. */ code_edit->set_text(" test\n text"); code_edit->select(0, 5, 1, 1); - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_line(0) == "test"); CHECK(code_edit->get_line(1) == "text"); /* Check selection is adjusted. */ code_edit->set_text(" test"); code_edit->select(0, 4, 0, 5); - code_edit->do_unindent(); + code_edit->unindent_lines(); CHECK(code_edit->get_selection_from_column() == 0); CHECK(code_edit->get_selection_to_column() == 1); CHECK(code_edit->get_line(0) == "test"); @@ -2787,7 +2773,7 @@ TEST_CASE("[SceneTree][CodeEdit] completion") { CHECK(code_edit->get_line(0) == "''"); CHECK(code_edit->get_caret_column() == 1); - /* Move out from centre, Should match and insert larger key. */ + /* Move out from center, Should match and insert larger key. */ SEND_GUI_ACTION(code_edit, "ui_text_caret_right"); SEND_GUI_KEY_EVENT(code_edit, Key::APOSTROPHE); CHECK(code_edit->get_line(0) == "''''''"); diff --git a/tests/scene/test_curve_2d.h b/tests/scene/test_curve_2d.h new file mode 100644 index 0000000000..fc141f3d09 --- /dev/null +++ b/tests/scene/test_curve_2d.h @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* test_curve_2d.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TEST_CURVE_2D_H +#define TEST_CURVE_2D_H + +#include "core/math/math_funcs.h" +#include "scene/resources/curve.h" + +#include "tests/test_macros.h" + +namespace TestCurve2D { + +void add_sample_curve_points(Ref<Curve2D> &curve) { + Vector2 p0 = Vector2(0, 0); + Vector2 p1 = Vector2(50, 0); + Vector2 p2 = Vector2(50, 50); + Vector2 p3 = Vector2(0, 50); + + Vector2 control0 = p1 - p0; + Vector2 control1 = p3 - p2; + + curve->add_point(p0, Vector2(), control0); + curve->add_point(p3, control1, Vector2()); +} + +TEST_CASE("[Curve2D] Default curve is empty") { + const Ref<Curve2D> curve = memnew(Curve2D); + CHECK(curve->get_point_count() == 0); +} + +TEST_CASE("[Curve2D] Point management") { + Ref<Curve2D> curve = memnew(Curve2D); + + SUBCASE("Functions for adding/removing points should behave as expected") { + curve->set_point_count(2); + CHECK(curve->get_point_count() == 2); + + curve->remove_point(0); + CHECK(curve->get_point_count() == 1); + + curve->add_point(Vector2()); + CHECK(curve->get_point_count() == 2); + + curve->clear_points(); + CHECK(curve->get_point_count() == 0); + } + + SUBCASE("Functions for changing single point properties should behave as expected") { + Vector2 new_in = Vector2(1, 1); + Vector2 new_out = Vector2(1, 1); + Vector2 new_pos = Vector2(1, 1); + + curve->add_point(Vector2()); + + CHECK(curve->get_point_in(0) != new_in); + curve->set_point_in(0, new_in); + CHECK(curve->get_point_in(0) == new_in); + + CHECK(curve->get_point_out(0) != new_out); + curve->set_point_out(0, new_out); + CHECK(curve->get_point_out(0) == new_out); + + CHECK(curve->get_point_position(0) != new_pos); + curve->set_point_position(0, new_pos); + CHECK(curve->get_point_position(0) == new_pos); + } +} + +TEST_CASE("[Curve2D] Baked") { + Ref<Curve2D> curve = memnew(Curve2D); + + SUBCASE("Single Point") { + curve->add_point(Vector2()); + + CHECK(curve->get_baked_length() == 0); + CHECK(curve->get_baked_points().size() == 1); + } + + SUBCASE("Straight line") { + curve->add_point(Vector2()); + curve->add_point(Vector2(0, 50)); + + CHECK(Math::is_equal_approx(curve->get_baked_length(), 50)); + CHECK(curve->get_baked_points().size() == 15); + } + + SUBCASE("BeziƩr Curve") { + add_sample_curve_points(curve); + + real_t len = curve->get_baked_length(); + real_t n_points = curve->get_baked_points().size(); + // Curve length should be bigger than a straight between points + CHECK(len > 50); + + SUBCASE("Increase bake interval") { + curve->set_bake_interval(10.0); + // Lower resolution should imply less points and smaller length + CHECK(curve->get_baked_length() < len); + CHECK(curve->get_baked_points().size() < n_points); + } + } +} + +TEST_CASE("[Curve2D] Sampling") { + // Sampling over a simple straight line to make assertions simpler + Ref<Curve2D> curve = memnew(Curve2D); + curve->add_point(Vector2()); + curve->add_point(Vector2(0, 50)); + + SUBCASE("sample") { + CHECK(curve->sample(0, 0) == Vector2(0, 0)); + CHECK(curve->sample(0, 0.5) == Vector2(0, 25)); + CHECK(curve->sample(0, 1) == Vector2(0, 50)); + } + + SUBCASE("samplef") { + CHECK(curve->samplef(0) == Vector2(0, 0)); + CHECK(curve->samplef(0.5) == Vector2(0, 25)); + CHECK(curve->samplef(1) == Vector2(0, 50)); + } + + SUBCASE("sample_baked") { + CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 0))) == Vector2(0, 0)); + CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 25))) == Vector2(0, 25)); + CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 50))) == Vector2(0, 50)); + } + + SUBCASE("sample_baked_with_rotation") { + const real_t pi = 3.14159; + Transform2D t = curve->sample_baked_with_rotation(curve->get_closest_offset(Vector2(0, 0))); + CHECK(t.get_origin() == Vector2(0, 0)); + CHECK(Math::is_equal_approx(t.get_rotation(), pi)); + + t = curve->sample_baked_with_rotation(curve->get_closest_offset(Vector2(0, 25))); + CHECK(t.get_origin() == Vector2(0, 25)); + CHECK(Math::is_equal_approx(t.get_rotation(), pi)); + + t = curve->sample_baked_with_rotation(curve->get_closest_offset(Vector2(0, 50))); + CHECK(t.get_origin() == Vector2(0, 50)); + CHECK(Math::is_equal_approx(t.get_rotation(), pi)); + } + + SUBCASE("get_closest_point") { + CHECK(curve->get_closest_point(Vector2(0, 0)) == Vector2(0, 0)); + CHECK(curve->get_closest_point(Vector2(0, 25)) == Vector2(0, 25)); + CHECK(curve->get_closest_point(Vector2(50, 25)) == Vector2(0, 25)); + CHECK(curve->get_closest_point(Vector2(0, 50)) == Vector2(0, 50)); + CHECK(curve->get_closest_point(Vector2(50, 50)) == Vector2(0, 50)); + CHECK(curve->get_closest_point(Vector2(0, 100)) == Vector2(0, 50)); + } +} + +TEST_CASE("[Curve2D] Tessellation") { + Ref<Curve2D> curve = memnew(Curve2D); + add_sample_curve_points(curve); + + const int default_size = curve->tessellate().size(); + + SUBCASE("Increase to max stages should increase num of points") { + CHECK(curve->tessellate(6).size() > default_size); + } + + SUBCASE("Decrease to max stages should decrease num of points") { + CHECK(curve->tessellate(4).size() < default_size); + } + + SUBCASE("Increase to tolerance should decrease num of points") { + CHECK(curve->tessellate(5, 5).size() < default_size); + } + + SUBCASE("Decrease to tolerance should increase num of points") { + CHECK(curve->tessellate(5, 3).size() > default_size); + } + + SUBCASE("Adding a straight segment should only add the last point to tessellate return array") { + curve->add_point(Vector2(0, 100)); + PackedVector2Array tes = curve->tessellate(); + CHECK(tes.size() == default_size + 1); + CHECK(tes[tes.size() - 1] == Vector2(0, 100)); + CHECK(tes[tes.size() - 2] == Vector2(0, 50)); + } +} + +TEST_CASE("[Curve2D] Even length tessellation") { + Ref<Curve2D> curve = memnew(Curve2D); + add_sample_curve_points(curve); + + const int default_size = curve->tessellate_even_length().size(); + + // Default tessellate_even_length tolerance_length is 20.0, by adding a 100 units + // straight, we expect the total size to be increased by more than 5, + // that is, the algo will pick a length < 20.0 and will divide the straight as + // well as the curve as opposed to tessellate() which only adds the final point + curve->add_point(Vector2(0, 150)); + CHECK(curve->tessellate_even_length().size() > default_size + 5); +} + +} // namespace TestCurve2D + +#endif // TEST_CURVE_2D_H diff --git a/tests/scene/test_primitives.h b/tests/scene/test_primitives.h index 6cdb5fb0a5..9232a3020d 100644 --- a/tests/scene/test_primitives.h +++ b/tests/scene/test_primitives.h @@ -734,7 +734,7 @@ TEST_CASE("[SceneTree][Primitive][Text] Text Primitive") { text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_FILE || text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_EMAIL || text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_LIST || - text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_NONE || + text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_GDSCRIPT || text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_CUSTOM)); CHECK(text->get_structured_text_bidi_override_options().size() >= 0); CHECK(text->get_width() > 0); diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index 944f9cb9f6..77312a8604 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -3086,7 +3086,7 @@ TEST_CASE("[SceneTree][TextEdit] context menu") { text_edit->get_viewport()->set_embedding_subwindows(true); // Bypass display server for drop handling. text_edit->set_size(Size2(800, 200)); - text_edit->set_line(0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec varius mattis leo, sed porta ex lacinia bibendum. Nunc bibendum pellentesque."); + text_edit->set_line(0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vasius mattis leo, sed porta ex lacinia bibendum. Nunc bibendum pellentesque."); MessageQueue::get_singleton()->flush(); text_edit->set_context_menu_enabled(false); @@ -3227,7 +3227,7 @@ TEST_CASE("[SceneTree][TextEdit] mouse") { SceneTree::get_singleton()->get_root()->add_child(text_edit); text_edit->set_size(Size2(800, 200)); - text_edit->set_line(0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec varius mattis leo, sed porta ex lacinia bibendum. Nunc bibendum pellentesque."); + text_edit->set_line(0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vasius mattis leo, sed porta ex lacinia bibendum. Nunc bibendum pellentesque."); MessageQueue::get_singleton()->flush(); CHECK(text_edit->get_word_at_pos(text_edit->get_pos_at_line_column(0, 1)) == "Lorem"); @@ -3271,6 +3271,7 @@ TEST_CASE("[SceneTree][TextEdit] mouse") { TEST_CASE("[SceneTree][TextEdit] caret") { TextEdit *text_edit = memnew(TextEdit); + text_edit->set_context_menu_enabled(false); // Prohibit sending InputEvents to the context menu. SceneTree::get_singleton()->get_root()->add_child(text_edit); text_edit->set_size(Size2(800, 200)); @@ -3304,9 +3305,9 @@ TEST_CASE("[SceneTree][TextEdit] caret") { SEND_GUI_ACTION(text_edit, "ui_text_caret_left"); CHECK(text_edit->get_caret_column() == 0); - text_edit->set_line(0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec varius mattis leo, sed porta ex lacinia bibendum. Nunc bibendum pellentesque."); + text_edit->set_line(0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vasius mattis leo, sed porta ex lacinia bibendum. Nunc bibendum pellentesque."); for (int i = 0; i < 3; i++) { - text_edit->insert_line_at(0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec varius mattis leo, sed porta ex lacinia bibendum. Nunc bibendum pellentesque."); + text_edit->insert_line_at(0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vasius mattis leo, sed porta ex lacinia bibendum. Nunc bibendum pellentesque."); } MessageQueue::get_singleton()->flush(); @@ -3518,7 +3519,7 @@ TEST_CASE("[SceneTree][TextEdit] line wrapping") { // Set size for boundary. text_edit->set_size(Size2(800, 200)); - text_edit->set_line(0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec varius mattis leo, sed porta ex lacinia bibendum. Nunc bibendum pellentesque."); + text_edit->set_line(0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vasius mattis leo, sed porta ex lacinia bibendum. Nunc bibendum pellentesque."); CHECK_FALSE(text_edit->is_line_wrapped(0)); CHECK(text_edit->get_line_wrap_count(0) == 0); CHECK(text_edit->get_line_wrap_index_at_column(0, 130) == 0); @@ -3568,7 +3569,7 @@ TEST_CASE("[SceneTree][TextEdit] viewport") { // No subcases here for performance. text_edit->set_size(Size2(800, 600)); for (int i = 0; i < 50; i++) { - text_edit->insert_line_at(0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec varius mattis leo, sed porta ex lacinia bibendum. Nunc bibendum pellentesque."); + text_edit->insert_line_at(0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vasius mattis leo, sed porta ex lacinia bibendum. Nunc bibendum pellentesque."); } MessageQueue::get_singleton()->flush(); @@ -3896,7 +3897,7 @@ TEST_CASE("[SceneTree][TextEdit] viewport") { CHECK(text_edit->get_h_scroll() == 0); text_edit->set_h_scroll(10000000); - CHECK(text_edit->get_h_scroll() == 313); + CHECK(text_edit->get_h_scroll() == 314); text_edit->set_h_scroll(-100); CHECK(text_edit->get_h_scroll() == 0); diff --git a/tests/test_macros.h b/tests/test_macros.h index 3074c1abf5..80a93c8327 100644 --- a/tests/test_macros.h +++ b/tests/test_macros.h @@ -31,6 +31,8 @@ #ifndef TEST_MACROS_H #define TEST_MACROS_H +#include "display_server_mock.h" + #include "core/core_globals.h" #include "core/input/input_map.h" #include "core/object/message_queue.h" @@ -139,13 +141,15 @@ int register_test_command(String p_command, TestFunc p_function); // SEND_GUI_MOUSE_MOTION_EVENT - takes an object, position, mouse mask and modifiers e.g SEND_GUI_MOUSE_MOTION_EVENT(code_edit, Vector2(50, 50), MouseButtonMask::LEFT, KeyModifierMask::META); // SEND_GUI_DOUBLE_CLICK - takes an object, position and modifiers. e.g SEND_GUI_DOUBLE_CLICK(code_edit, Vector2(50, 50), KeyModifierMask::META); +#define _SEND_DISPLAYSERVER_EVENT(m_event) ((DisplayServerMock *)(DisplayServer::get_singleton()))->simulate_event(m_event); + #define SEND_GUI_ACTION(m_object, m_action) \ { \ const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(m_action); \ const List<Ref<InputEvent>>::Element *first_event = events->front(); \ Ref<InputEventKey> event = first_event->get(); \ event->set_pressed(true); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -153,7 +157,7 @@ int register_test_command(String p_command, TestFunc p_function); { \ Ref<InputEventKey> event = InputEventKey::create_reference(m_input); \ event->set_pressed(true); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -176,7 +180,7 @@ int register_test_command(String p_command, TestFunc p_function); #define SEND_GUI_MOUSE_BUTTON_EVENT(m_object, m_local_pos, m_input, m_mask, m_modifers) \ { \ _CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, m_input, m_mask, m_modifers); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -184,7 +188,7 @@ int register_test_command(String p_command, TestFunc p_function); { \ _CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, m_input, m_mask, m_modifers); \ event->set_pressed(false); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -192,7 +196,7 @@ int register_test_command(String p_command, TestFunc p_function); { \ _CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, MouseButton::LEFT, 0, m_modifers); \ event->set_double_click(true); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -207,7 +211,7 @@ int register_test_command(String p_command, TestFunc p_function); event->set_button_mask(m_mask); \ event->set_relative(Vector2(10, 10)); \ _UPDATE_EVENT_MODIFERS(event, m_modifers); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ CoreGlobals::print_error_enabled = errors_enabled; \ } diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 85a271fc9c..ea6058f707 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -32,6 +32,7 @@ #include "tests/core/config/test_project_settings.h" #include "tests/core/input/test_input_event_key.h" +#include "tests/core/input/test_input_event_mouse.h" #include "tests/core/input/test_shortcut.h" #include "tests/core/io/test_config_file.h" #include "tests/core/io/test_file_access.h" @@ -91,6 +92,7 @@ #include "tests/scene/test_bit_map.h" #include "tests/scene/test_code_edit.h" #include "tests/scene/test_curve.h" +#include "tests/scene/test_curve_2d.h" #include "tests/scene/test_gradient.h" #include "tests/scene/test_node.h" #include "tests/scene/test_path_2d.h" @@ -105,6 +107,7 @@ #include "modules/modules_tests.gen.h" +#include "tests/display_server_mock.h" #include "tests/test_macros.h" #include "scene/theme/theme_db.h" @@ -124,6 +127,7 @@ int test_main(int argc, char *argv[]) { args.push_back(String::utf8(argv[i])); } OS::get_singleton()->set_cmdline("", args, List<String>()); + DisplayServerMock::register_mock_driver(); // Run custom test tools. if (test_commands) { @@ -198,11 +202,12 @@ struct GodotTestCaseListener : public doctest::IReporter { memnew(MessageQueue); memnew(Input); + Input::get_singleton()->set_use_accumulated_input(false); Error err = OK; OS::get_singleton()->set_has_server_feature_callback(nullptr); for (int i = 0; i < DisplayServer::get_create_function_count(); i++) { - if (String("headless") == DisplayServer::get_create_function_name(i)) { + if (String("mock") == DisplayServer::get_create_function_name(i)) { DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, nullptr, Vector2i(0, 0), DisplayServer::SCREEN_PRIMARY, err); break; } |