diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/core/config/test_project_settings.h | 7 | ||||
-rw-r--r-- | tests/core/math/test_aabb.h | 14 | ||||
-rw-r--r-- | tests/core/math/test_rect2.h | 22 | ||||
-rw-r--r-- | tests/core/object/test_class_db.h | 53 | ||||
-rw-r--r-- | tests/core/string/test_string.h | 152 | ||||
-rw-r--r-- | tests/core/string/test_translation.h | 1 | ||||
-rw-r--r-- | tests/core/string/test_translation_server.h | 2 | ||||
-rw-r--r-- | tests/scene/test_code_edit.h | 20 | ||||
-rw-r--r-- | tests/scene/test_node_2d.h | 125 | ||||
-rw-r--r-- | tests/scene/test_text_edit.h | 66 | ||||
-rw-r--r-- | tests/scene/test_tree.h | 151 | ||||
-rw-r--r-- | tests/test_main.cpp | 1 |
12 files changed, 532 insertions, 82 deletions
diff --git a/tests/core/config/test_project_settings.h b/tests/core/config/test_project_settings.h index 8fc2489f8b..0e1058a626 100644 --- a/tests/core/config/test_project_settings.h +++ b/tests/core/config/test_project_settings.h @@ -126,10 +126,9 @@ TEST_CASE("[ProjectSettings] localize_path") { CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path\\.\\filename"), "res://path/filename"); #endif - // FIXME?: These checks pass, but that doesn't seems correct - CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../filename"), "res://filename"); - CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../path/filename"), "res://path/filename"); - CHECK_EQ(ProjectSettings::get_singleton()->localize_path("..\\path\\filename"), "res://path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../filename"), "../filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../path/filename"), "../path/filename"); + CHECK_EQ(ProjectSettings::get_singleton()->localize_path("..\\path\\filename"), "../path/filename"); CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/filename"), "/testroot/filename"); CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/path/filename"), "/testroot/path/filename"); diff --git a/tests/core/math/test_aabb.h b/tests/core/math/test_aabb.h index dbc62bc248..741e6af5d4 100644 --- a/tests/core/math/test_aabb.h +++ b/tests/core/math/test_aabb.h @@ -377,23 +377,23 @@ TEST_CASE("[AABB] Get longest/shortest axis") { TEST_CASE("[AABB] Get support") { const AABB aabb = AABB(Vector3(-1.5, 2, -2.5), Vector3(4, 5, 6)); CHECK_MESSAGE( - aabb.get_support(Vector3(1, 0, 0)).is_equal_approx(Vector3(2.5, 2, -2.5)), + aabb.get_support(Vector3(1, 0, 0)) == Vector3(2.5, 2, -2.5), "get_support() should return the expected value."); CHECK_MESSAGE( - aabb.get_support(Vector3(0.5, 1, 0)).is_equal_approx(Vector3(2.5, 7, -2.5)), + aabb.get_support(Vector3(0.5, 1, 1)) == Vector3(2.5, 7, 3.5), "get_support() should return the expected value."); CHECK_MESSAGE( - aabb.get_support(Vector3(0.5, 1, -400)).is_equal_approx(Vector3(2.5, 7, -2.5)), + aabb.get_support(Vector3(0.5, 1, -400)) == Vector3(2.5, 7, -2.5), "get_support() should return the expected value."); CHECK_MESSAGE( - aabb.get_support(Vector3(0, -1, 0)).is_equal_approx(Vector3(-1.5, 2, -2.5)), + aabb.get_support(Vector3(0, -1, 0)) == Vector3(-1.5, 2, -2.5), "get_support() should return the expected value."); CHECK_MESSAGE( - aabb.get_support(Vector3(0, -0.1, 0)).is_equal_approx(Vector3(-1.5, 2, -2.5)), + aabb.get_support(Vector3(0, -0.1, 0)) == Vector3(-1.5, 2, -2.5), "get_support() should return the expected value."); CHECK_MESSAGE( - aabb.get_support(Vector3()).is_equal_approx(Vector3(-1.5, 2, -2.5)), - "get_support() should return the expected value with a null vector."); + aabb.get_support(Vector3()) == Vector3(-1.5, 2, -2.5), + "get_support() should return the AABB position when given a zero vector."); } TEST_CASE("[AABB] Grow") { diff --git a/tests/core/math/test_rect2.h b/tests/core/math/test_rect2.h index 26ab185aa2..c4368808a6 100644 --- a/tests/core/math/test_rect2.h +++ b/tests/core/math/test_rect2.h @@ -180,6 +180,28 @@ TEST_CASE("[Rect2] Expanding") { "expand() with non-contained Vector2 should return the expected result."); } +TEST_CASE("[Rect2] Get support") { + const Rect2 rect = Rect2(Vector2(-1.5, 2), Vector2(4, 5)); + CHECK_MESSAGE( + rect.get_support(Vector2(1, 0)) == Vector2(2.5, 2), + "get_support() should return the expected value."); + CHECK_MESSAGE( + rect.get_support(Vector2(0.5, 1)) == Vector2(2.5, 7), + "get_support() should return the expected value."); + CHECK_MESSAGE( + rect.get_support(Vector2(0.5, 1)) == Vector2(2.5, 7), + "get_support() should return the expected value."); + CHECK_MESSAGE( + rect.get_support(Vector2(0, -1)) == Vector2(-1.5, 2), + "get_support() should return the expected value."); + CHECK_MESSAGE( + rect.get_support(Vector2(0, -0.1)) == Vector2(-1.5, 2), + "get_support() should return the expected value."); + CHECK_MESSAGE( + rect.get_support(Vector2()) == Vector2(-1.5, 2), + "get_support() should return the Rect2 position when given a zero vector."); +} + TEST_CASE("[Rect2] Growing") { CHECK_MESSAGE( Rect2(0, 100, 1280, 720).grow(100).is_equal_approx(Rect2(-100, 0, 1480, 920)), diff --git a/tests/core/object/test_class_db.h b/tests/core/object/test_class_db.h index c1aa39031d..924e93129d 100644 --- a/tests/core/object/test_class_db.h +++ b/tests/core/object/test_class_db.h @@ -139,6 +139,7 @@ struct NamesCache { StringName vector2_type = StaticCString::create("Vector2"); StringName rect2_type = StaticCString::create("Rect2"); StringName vector3_type = StaticCString::create("Vector3"); + StringName vector4_type = StaticCString::create("Vector4"); // Object not included as it must be checked for all derived classes static constexpr int nullable_types_count = 18; @@ -247,6 +248,8 @@ bool arg_default_value_is_assignable_to_type(const Context &p_context, const Var case Variant::VECTOR2: case Variant::RECT2: case Variant::VECTOR3: + case Variant::VECTOR4: + case Variant::PROJECTION: case Variant::RID: case Variant::ARRAY: case Variant::DICTIONARY: @@ -274,11 +277,45 @@ bool arg_default_value_is_assignable_to_type(const Context &p_context, const Var case Variant::VECTOR3I: return p_arg_type.name == p_context.names_cache.vector3_type || p_arg_type.name == Variant::get_type_name(p_val.get_type()); - default: + case Variant::VECTOR4I: + return p_arg_type.name == p_context.names_cache.vector4_type || + p_arg_type.name == Variant::get_type_name(p_val.get_type()); + case Variant::VARIANT_MAX: + break; + } + if (r_err_msg) { + *r_err_msg = "Unexpected Variant type: " + itos(p_val.get_type()); + } + return false; +} + +bool arg_default_value_is_valid_data(const Variant &p_val, String *r_err_msg = nullptr) { + switch (p_val.get_type()) { + case Variant::RID: + case Variant::ARRAY: + case Variant::DICTIONARY: + case Variant::PACKED_BYTE_ARRAY: + case Variant::PACKED_INT32_ARRAY: + case Variant::PACKED_INT64_ARRAY: + case Variant::PACKED_FLOAT32_ARRAY: + case Variant::PACKED_FLOAT64_ARRAY: + case Variant::PACKED_STRING_ARRAY: + case Variant::PACKED_VECTOR2_ARRAY: + case Variant::PACKED_VECTOR3_ARRAY: + case Variant::PACKED_COLOR_ARRAY: + case Variant::PACKED_VECTOR4_ARRAY: + case Variant::CALLABLE: + case Variant::SIGNAL: + case Variant::OBJECT: + if (p_val.is_zero()) { + return true; + } if (r_err_msg) { - *r_err_msg = "Unexpected Variant type: " + itos(p_val.get_type()); + *r_err_msg = "Must be zero."; } break; + default: + return true; } return false; @@ -406,6 +443,14 @@ void validate_argument(const Context &p_context, const ExposedClass &p_class, co } TEST_COND(!arg_defval_assignable_to_type, err_msg); + + bool arg_defval_valid_data = arg_default_value_is_valid_data(p_arg.defval, &type_error_msg); + + if (!type_error_msg.is_empty()) { + err_msg += " " + type_error_msg; + } + + TEST_COND(!arg_defval_valid_data, err_msg); } } @@ -558,7 +603,7 @@ void add_exposed_classes(Context &r_context) { MethodData method; method.name = method_info.name; - TEST_FAIL_COND(!String(method.name).is_valid_identifier(), + TEST_FAIL_COND(!String(method.name).is_valid_ascii_identifier(), "Method name is not a valid identifier: '", exposed_class.name, ".", method.name, "'."); if (method_info.flags & METHOD_FLAG_VIRTUAL) { @@ -684,7 +729,7 @@ 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(), + TEST_FAIL_COND(!String(signal.name).is_valid_ascii_identifier(), "Signal name is not a valid identifier: '", exposed_class.name, ".", signal.name, "'."); int i = 0; diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index cf57183a02..b47e5b1eb9 100644 --- a/tests/core/string/test_string.h +++ b/tests/core/string/test_string.h @@ -638,64 +638,90 @@ TEST_CASE("[String] Ends with") { } TEST_CASE("[String] Splitting") { - String s = "Mars,Jupiter,Saturn,Uranus"; - const char *slices_l[3] = { "Mars", "Jupiter", "Saturn,Uranus" }; - MULTICHECK_SPLIT(s, split, ",", true, 2, slices_l, 3); - - const char *slices_r[3] = { "Mars,Jupiter", "Saturn", "Uranus" }; - MULTICHECK_SPLIT(s, rsplit, ",", true, 2, slices_r, 3); - - s = "test"; - const char *slices_3[4] = { "t", "e", "s", "t" }; - MULTICHECK_SPLIT(s, split, "", true, 0, slices_3, 4); - - s = ""; - const char *slices_4[1] = { "" }; - MULTICHECK_SPLIT(s, split, "", true, 0, slices_4, 1); - MULTICHECK_SPLIT(s, split, "", false, 0, slices_4, 0); - - s = "Mars Jupiter Saturn Uranus"; - const char *slices_s[4] = { "Mars", "Jupiter", "Saturn", "Uranus" }; - Vector<String> l = s.split_spaces(); - for (int i = 0; i < l.size(); i++) { - CHECK(l[i] == slices_s[i]); + { + const String s = "Mars,Jupiter,Saturn,Uranus"; + + const char *slices_l[3] = { "Mars", "Jupiter", "Saturn,Uranus" }; + MULTICHECK_SPLIT(s, split, ",", true, 2, slices_l, 3); + + const char *slices_r[3] = { "Mars,Jupiter", "Saturn", "Uranus" }; + MULTICHECK_SPLIT(s, rsplit, ",", true, 2, slices_r, 3); } - s = "1.2;2.3 4.5"; - const double slices_d[3] = { 1.2, 2.3, 4.5 }; + { + const String s = "test"; + const char *slices[4] = { "t", "e", "s", "t" }; + MULTICHECK_SPLIT(s, split, "", true, 0, slices, 4); + } - Vector<double> d_arr; - d_arr = s.split_floats(";"); - CHECK(d_arr.size() == 2); - for (int i = 0; i < d_arr.size(); i++) { - CHECK(ABS(d_arr[i] - slices_d[i]) <= 0.00001); + { + const String s = ""; + const char *slices[1] = { "" }; + MULTICHECK_SPLIT(s, split, "", true, 0, slices, 1); + MULTICHECK_SPLIT(s, split, "", false, 0, slices, 0); } - Vector<String> keys; - keys.push_back(";"); - keys.push_back(" "); - - Vector<float> f_arr; - f_arr = s.split_floats_mk(keys); - CHECK(f_arr.size() == 3); - for (int i = 0; i < f_arr.size(); i++) { - CHECK(ABS(f_arr[i] - slices_d[i]) <= 0.00001); + { + const String s = "Mars Jupiter Saturn Uranus"; + const char *slices[4] = { "Mars", "Jupiter", "Saturn", "Uranus" }; + Vector<String> l = s.split_spaces(); + for (int i = 0; i < l.size(); i++) { + CHECK(l[i] == slices[i]); + } } - s = "1;2 4"; - const int slices_i[3] = { 1, 2, 4 }; + { + const String s = "1.2;2.3 4.5"; + const double slices[3] = { 1.2, 2.3, 4.5 }; + + const Vector<double> d_arr = s.split_floats(";"); + CHECK(d_arr.size() == 2); + for (int i = 0; i < d_arr.size(); i++) { + CHECK(ABS(d_arr[i] - slices[i]) <= 0.00001); + } - Vector<int> ii; - ii = s.split_ints(";"); - CHECK(ii.size() == 2); - for (int i = 0; i < ii.size(); i++) { - CHECK(ii[i] == slices_i[i]); + const Vector<String> keys = { ";", " " }; + const Vector<float> f_arr = s.split_floats_mk(keys); + CHECK(f_arr.size() == 3); + for (int i = 0; i < f_arr.size(); i++) { + CHECK(ABS(f_arr[i] - slices[i]) <= 0.00001); + } } - ii = s.split_ints_mk(keys); - CHECK(ii.size() == 3); - for (int i = 0; i < ii.size(); i++) { - CHECK(ii[i] == slices_i[i]); + { + const String s = " -2.0 5"; + const double slices[10] = { 0, -2, 0, 0, 0, 0, 0, 0, 0, 5 }; + + const Vector<double> arr = s.split_floats(" "); + CHECK(arr.size() == 10); + for (int i = 0; i < arr.size(); i++) { + CHECK(ABS(arr[i] - slices[i]) <= 0.00001); + } + + const Vector<String> keys = { ";", " " }; + const Vector<float> mk = s.split_floats_mk(keys); + CHECK(mk.size() == 10); + for (int i = 0; i < mk.size(); i++) { + CHECK(mk[i] == slices[i]); + } + } + + { + const String s = "1;2 4"; + const int slices[3] = { 1, 2, 4 }; + + const Vector<int> arr = s.split_ints(";"); + CHECK(arr.size() == 2); + for (int i = 0; i < arr.size(); i++) { + CHECK(arr[i] == slices[i]); + } + + const Vector<String> keys = { ";", " " }; + const Vector<int> mk = s.split_ints_mk(keys); + CHECK(mk.size() == 3); + for (int i = 0; i < mk.size(); i++) { + CHECK(mk[i] == slices[i]); + } } } @@ -1594,7 +1620,7 @@ TEST_CASE("[String] Path functions") { static const char *base_name[8] = { "C:\\Godot\\project\\test", "/Godot/project/test", "../Godot/project/test", "Godot\\test", "C:\\test", "res://test", "user://test", "/" }; static const char *ext[8] = { "tscn", "xscn", "scn", "doc", "", "", "", "test" }; static const char *file[8] = { "test.tscn", "test.xscn", "test.scn", "test.doc", "test.", "test", "test", ".test" }; - static const char *simplified[8] = { "C:/Godot/project/test.tscn", "/Godot/project/test.xscn", "Godot/project/test.scn", "Godot/test.doc", "C:/test.", "res://test", "user://test", "/.test" }; + static const char *simplified[8] = { "C:/Godot/project/test.tscn", "/Godot/project/test.xscn", "../Godot/project/test.scn", "Godot/test.doc", "C:/test.", "res://test", "user://test", "/.test" }; static const bool abs[8] = { true, true, false, false, true, true, true, true }; for (int i = 0; i < 8; i++) { @@ -1795,21 +1821,23 @@ TEST_CASE("[String] Join") { } TEST_CASE("[String] Is_*") { - static const char *data[12] = { "-30", "100", "10.1", "10,1", "1e2", "1e-2", "1e2e3", "0xAB", "AB", "Test1", "1Test", "Test*1" }; - static bool isnum[12] = { true, true, true, false, false, false, false, false, false, false, false, false }; - static bool isint[12] = { true, true, false, false, false, false, false, false, false, false, false, false }; - static bool ishex[12] = { true, true, false, false, true, false, true, false, true, false, false, false }; - static bool ishex_p[12] = { false, false, false, false, false, false, false, true, false, false, false, false }; - static bool isflt[12] = { true, true, true, false, true, true, false, false, false, false, false, false }; - static bool isid[12] = { false, false, false, false, false, false, false, false, true, true, false, false }; + static const char *data[13] = { "-30", "100", "10.1", "10,1", "1e2", "1e-2", "1e2e3", "0xAB", "AB", "Test1", "1Test", "Test*1", "文字" }; + static bool isnum[13] = { true, true, true, false, false, false, false, false, false, false, false, false, false }; + static bool isint[13] = { true, true, false, false, false, false, false, false, false, false, false, false, false }; + static bool ishex[13] = { true, true, false, false, true, false, true, false, true, false, false, false, false }; + static bool ishex_p[13] = { false, false, false, false, false, false, false, true, false, false, false, false, false }; + static bool isflt[13] = { true, true, true, false, true, true, false, false, false, false, false, false, false }; + static bool isaid[13] = { false, false, false, false, false, false, false, false, true, true, false, false, false }; + static bool isuid[13] = { false, false, false, false, false, false, false, false, true, true, false, false, true }; for (int i = 0; i < 12; i++) { - String s = String(data[i]); + String s = String::utf8(data[i]); CHECK(s.is_numeric() == isnum[i]); CHECK(s.is_valid_int() == isint[i]); CHECK(s.is_valid_hex_number(false) == ishex[i]); CHECK(s.is_valid_hex_number(true) == ishex_p[i]); CHECK(s.is_valid_float() == isflt[i]); - CHECK(s.is_valid_identifier() == isid[i]); + CHECK(s.is_valid_ascii_identifier() == isaid[i]); + CHECK(s.is_valid_unicode_identifier() == isuid[i]); } } @@ -1837,16 +1865,16 @@ TEST_CASE("[String] validate_node_name") { TEST_CASE("[String] validate_identifier") { String empty_string; - CHECK(empty_string.validate_identifier() == "_"); + CHECK(empty_string.validate_ascii_identifier() == "_"); String numeric_only = "12345"; - CHECK(numeric_only.validate_identifier() == "_12345"); + CHECK(numeric_only.validate_ascii_identifier() == "_12345"); String name_with_spaces = "Name with spaces"; - CHECK(name_with_spaces.validate_identifier() == "Name_with_spaces"); + CHECK(name_with_spaces.validate_ascii_identifier() == "Name_with_spaces"); String name_with_invalid_chars = U"Invalid characters:@*#&世界"; - CHECK(name_with_invalid_chars.validate_identifier() == "Invalid_characters_______"); + CHECK(name_with_invalid_chars.validate_ascii_identifier() == "Invalid_characters_______"); } TEST_CASE("[String] Variant indexed get") { diff --git a/tests/core/string/test_translation.h b/tests/core/string/test_translation.h index acdd851b29..7c389191e3 100644 --- a/tests/core/string/test_translation.h +++ b/tests/core/string/test_translation.h @@ -34,6 +34,7 @@ #include "core/string/optimized_translation.h" #include "core/string/translation.h" #include "core/string/translation_po.h" +#include "core/string/translation_server.h" #ifdef TOOLS_ENABLED #include "editor/import/resource_importer_csv_translation.h" diff --git a/tests/core/string/test_translation_server.h b/tests/core/string/test_translation_server.h index 2c20574309..ac1599f2e8 100644 --- a/tests/core/string/test_translation_server.h +++ b/tests/core/string/test_translation_server.h @@ -31,7 +31,7 @@ #ifndef TEST_TRANSLATION_SERVER_H #define TEST_TRANSLATION_SERVER_H -#include "core/string/translation.h" +#include "core/string/translation_server.h" #include "tests/test_macros.h" diff --git a/tests/scene/test_code_edit.h b/tests/scene/test_code_edit.h index a166002cdd..9ec1b812df 100644 --- a/tests/scene/test_code_edit.h +++ b/tests/scene/test_code_edit.h @@ -4609,6 +4609,26 @@ TEST_CASE("[SceneTree][CodeEdit] text manipulation") { CHECK(code_edit->get_text() == "line 1\nline 2\nline 3"); CHECK(code_edit->get_caret_line() == 0); CHECK(code_edit->get_caret_column() == 0); + + // Unfold previous folded line on backspace if the caret is at the first column. + code_edit->set_line_folding_enabled(true); + code_edit->set_text("line 1\n\tline 2\nline 3"); + code_edit->set_caret_line(2); + code_edit->set_caret_column(0); + code_edit->fold_line(0); + code_edit->backspace(); + CHECK_FALSE(code_edit->is_line_folded(0)); + code_edit->set_line_folding_enabled(false); + + // Do not unfold previous line on backspace if the caret is not at the first column. + code_edit->set_line_folding_enabled(true); + code_edit->set_text("line 1\n\tline 2\nline 3"); + code_edit->set_caret_line(2); + code_edit->set_caret_column(4); + code_edit->fold_line(0); + code_edit->backspace(); + CHECK(code_edit->is_line_folded(0)); + code_edit->set_line_folding_enabled(false); } SUBCASE("[TextEdit] cut") { diff --git a/tests/scene/test_node_2d.h b/tests/scene/test_node_2d.h index 8cf6408438..e8e7b2880d 100644 --- a/tests/scene/test_node_2d.h +++ b/tests/scene/test_node_2d.h @@ -86,6 +86,131 @@ TEST_CASE("[SceneTree][Node2D]") { } } +TEST_CASE("[SceneTree][Node2D] Utility methods") { + Node2D *test_node1 = memnew(Node2D); + Node2D *test_node2 = memnew(Node2D); + Node2D *test_node3 = memnew(Node2D); + Node2D *test_sibling = memnew(Node2D); + SceneTree::get_singleton()->get_root()->add_child(test_node1); + + test_node1->set_position(Point2(100, 100)); + test_node1->set_rotation(Math::deg_to_rad(30.0)); + test_node1->set_scale(Size2(1, 1)); + test_node1->add_child(test_node2); + + test_node2->set_position(Point2(10, 0)); + test_node2->set_rotation(Math::deg_to_rad(60.0)); + test_node2->set_scale(Size2(1, 1)); + test_node2->add_child(test_node3); + test_node2->add_child(test_sibling); + + test_node3->set_position(Point2(0, 10)); + test_node3->set_rotation(Math::deg_to_rad(0.0)); + test_node3->set_scale(Size2(2, 2)); + + test_sibling->set_position(Point2(5, 10)); + test_sibling->set_rotation(Math::deg_to_rad(90.0)); + test_sibling->set_scale(Size2(2, 1)); + + SUBCASE("[Node2D] look_at") { + test_node3->look_at(Vector2(1, 1)); + + CHECK(test_node3->get_global_position().is_equal_approx(Point2(98.66026, 105))); + CHECK(Math::is_equal_approx(test_node3->get_global_rotation(), real_t(-2.32477))); + CHECK(test_node3->get_global_scale().is_equal_approx(Vector2(2, 2))); + + CHECK(test_node3->get_position().is_equal_approx(Vector2(0, 10))); + CHECK(Math::is_equal_approx(test_node3->get_rotation(), real_t(2.38762))); + CHECK(test_node3->get_scale().is_equal_approx(Vector2(2, 2))); + + test_node3->look_at(Vector2(0, 10)); + + CHECK(test_node3->get_global_position().is_equal_approx(Vector2(98.66026, 105))); + CHECK(Math::is_equal_approx(test_node3->get_global_rotation(), real_t(-2.37509))); + CHECK(test_node3->get_global_scale().is_equal_approx(Vector2(2, 2))); + + CHECK(test_node3->get_position().is_equal_approx(Vector2(0, 10))); + CHECK(Math::is_equal_approx(test_node3->get_rotation(), real_t(2.3373))); + CHECK(test_node3->get_scale().is_equal_approx(Vector2(2, 2))); + + // Don't do anything if look_at own position. + test_node3->look_at(test_node3->get_global_position()); + + CHECK(test_node3->get_global_position().is_equal_approx(Vector2(98.66026, 105))); + CHECK(Math::is_equal_approx(test_node3->get_global_rotation(), real_t(-2.37509))); + CHECK(test_node3->get_global_scale().is_equal_approx(Vector2(2, 2))); + + CHECK(test_node3->get_position().is_equal_approx(Vector2(0, 10))); + CHECK(Math::is_equal_approx(test_node3->get_rotation(), real_t(2.3373))); + CHECK(test_node3->get_scale().is_equal_approx(Vector2(2, 2))); + + // Revert any rotation caused by look_at, must run after look_at tests + test_node3->set_rotation(Math::deg_to_rad(0.0)); + } + + SUBCASE("[Node2D] get_angle_to") { + CHECK(Math::is_equal_approx(test_node3->get_angle_to(Vector2(1, 1)), real_t(2.38762))); + CHECK(Math::is_equal_approx(test_node3->get_angle_to(Vector2(0, 10)), real_t(2.3373))); + CHECK(Math::is_equal_approx(test_node3->get_angle_to(Vector2(2, -5)), real_t(2.42065))); + CHECK(Math::is_equal_approx(test_node3->get_angle_to(Vector2(-2, 5)), real_t(2.3529))); + + // Return 0 when get_angle_to own position. + CHECK(Math::is_equal_approx(test_node3->get_angle_to(test_node3->get_global_position()), real_t(0))); + } + + SUBCASE("[Node2D] to_local") { + Point2 node3_local = test_node3->to_local(Point2(1, 2)); + CHECK(node3_local.is_equal_approx(Point2(-51.5, 48.83013))); + + node3_local = test_node3->to_local(Point2(-2, 1)); + CHECK(node3_local.is_equal_approx(Point2(-52, 50.33013))); + + node3_local = test_node3->to_local(Point2(0, 0)); + CHECK(node3_local.is_equal_approx(Point2(-52.5, 49.33013))); + + node3_local = test_node3->to_local(test_node3->get_global_position()); + CHECK(node3_local.is_equal_approx(Point2(0, 0))); + } + + SUBCASE("[Node2D] to_global") { + Point2 node3_global = test_node3->to_global(Point2(1, 2)); + CHECK(node3_global.is_equal_approx(Point2(94.66026, 107))); + + node3_global = test_node3->to_global(Point2(-2, 1)); + CHECK(node3_global.is_equal_approx(Point2(96.66026, 101))); + + node3_global = test_node3->to_global(Point2(0, 0)); + CHECK(node3_global.is_equal_approx(test_node3->get_global_position())); + } + + SUBCASE("[Node2D] get_relative_transform_to_parent") { + Transform2D relative_xform = test_node3->get_relative_transform_to_parent(test_node3); + CHECK(relative_xform.is_equal_approx(Transform2D())); + + relative_xform = test_node3->get_relative_transform_to_parent(test_node2); + CHECK(relative_xform.get_origin().is_equal_approx(Vector2(0, 10))); + CHECK(Math::is_equal_approx(relative_xform.get_rotation(), real_t(0))); + CHECK(relative_xform.get_scale().is_equal_approx(Vector2(2, 2))); + + relative_xform = test_node3->get_relative_transform_to_parent(test_node1); + CHECK(relative_xform.get_origin().is_equal_approx(Vector2(1.339746, 5))); + CHECK(Math::is_equal_approx(relative_xform.get_rotation(), real_t(1.0472))); + CHECK(relative_xform.get_scale().is_equal_approx(Vector2(2, 2))); + + ERR_PRINT_OFF; + // In case of a sibling all transforms until the root are accumulated. + Transform2D xform = test_node3->get_relative_transform_to_parent(test_sibling); + Transform2D return_xform = test_node1->get_global_transform().inverse() * test_node3->get_global_transform(); + CHECK(xform.is_equal_approx(return_xform)); + ERR_PRINT_ON; + } + + memdelete(test_sibling); + memdelete(test_node3); + memdelete(test_node2); + memdelete(test_node1); +} + } // namespace TestNode2D #endif // TEST_NODE_2D_H diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index b2d9f5100e..69e27fe7a0 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -1763,6 +1763,28 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK_FALSE(text_edit->has_selection()); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 4); + + // Wrapped lines. + text_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); + text_edit->set_text("this is some text\nfor selection"); + text_edit->set_size(Size2(110, 100)); + MessageQueue::get_singleton()->flush(); + + // Line 0 wraps: 'this is ', 'some text'. + // Line 1 wraps: 'for ', 'selection'. + CHECK(text_edit->is_line_wrapped(0)); + + // Select to the first character of a wrapped line. + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 11).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); + SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 8).get_center(), MouseButtonMask::LEFT, Key::NONE); + CHECK(text_edit->has_selection()); + CHECK(text_edit->get_selected_text() == "so"); + CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_POINTER); + CHECK(text_edit->get_selection_origin_line() == 0); + CHECK(text_edit->get_selection_origin_column() == 10); + CHECK(text_edit->get_caret_line() == 0); + CHECK(text_edit->get_caret_column() == 8); + CHECK(text_edit->is_dragging_cursor()); } SUBCASE("[TextEdit] mouse word select") { @@ -5713,6 +5735,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK(text_edit->get_caret_count() == 2); MessageQueue::get_singleton()->flush(); + // Lines 0 and 4 are wrapped into 2 parts: 'this is ' and 'some'. CHECK(text_edit->is_line_wrapped(0)); SIGNAL_DISCARD("text_set"); SIGNAL_DISCARD("text_changed"); @@ -5762,9 +5785,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); - text_edit->set_caret_column(12, false); // Normal up over wrapped line to line 0. + text_edit->set_caret_column(12, false); SEND_GUI_ACTION("ui_text_caret_up"); CHECK(text_edit->get_viewport()->is_input_handled()); CHECK(text_edit->get_caret_line() == 0); @@ -5777,6 +5800,23 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + + // Normal up from column 0 to a wrapped line. + text_edit->remove_secondary_carets(); + text_edit->set_caret_line(5); + text_edit->set_caret_column(0); + SEND_GUI_ACTION("ui_text_caret_up"); + CHECK(text_edit->get_viewport()->is_input_handled()); + CHECK(text_edit->get_caret_line() == 4); + CHECK(text_edit->get_caret_column() == 8); + CHECK_FALSE(text_edit->has_selection(0)); + + // Normal up to column 0 of a wrapped line. + SEND_GUI_ACTION("ui_text_caret_up"); + CHECK(text_edit->get_viewport()->is_input_handled()); + CHECK(text_edit->get_caret_line() == 4); + CHECK(text_edit->get_caret_column() == 0); + CHECK_FALSE(text_edit->has_selection(0)); } SUBCASE("[TextEdit] ui_text_caret_down") { @@ -5792,6 +5832,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { MessageQueue::get_singleton()->flush(); + // Lines 3 and 7 are wrapped into 2 parts: 'this is ' and 'some'. CHECK(text_edit->is_line_wrapped(3)); SIGNAL_DISCARD("text_set"); SIGNAL_DISCARD("text_changed"); @@ -5841,9 +5882,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); - text_edit->set_caret_column(7, false); // Normal down over wrapped line to last wrapped line. + text_edit->set_caret_column(7, false); SEND_GUI_ACTION("ui_text_caret_down"); CHECK(text_edit->get_viewport()->is_input_handled()); CHECK(text_edit->get_caret_line() == 3); @@ -5856,6 +5897,23 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + + // Normal down to column 0 of a wrapped line. + text_edit->remove_secondary_carets(); + text_edit->set_caret_line(3); + text_edit->set_caret_column(0); + SEND_GUI_ACTION("ui_text_caret_down"); + CHECK(text_edit->get_viewport()->is_input_handled()); + CHECK(text_edit->get_caret_line() == 3); + CHECK(text_edit->get_caret_column() == 8); + CHECK_FALSE(text_edit->has_selection(0)); + + // Normal down out of visual column 0 of a wrapped line moves to start of next line. + SEND_GUI_ACTION("ui_text_caret_down"); + CHECK(text_edit->get_viewport()->is_input_handled()); + CHECK(text_edit->get_caret_line() == 4); + CHECK(text_edit->get_caret_column() == 0); + CHECK_FALSE(text_edit->has_selection(0)); } SUBCASE("[TextEdit] ui_text_caret_document_start") { @@ -7162,7 +7220,7 @@ TEST_CASE("[SceneTree][TextEdit] multicaret") { CHECK(text_edit->get_caret_line(0) == 2); CHECK(text_edit->get_caret_column(0) == 5); CHECK(text_edit->get_caret_line(1) == 2); - CHECK(text_edit->get_caret_column(1) == 10); + CHECK(text_edit->get_caret_column(1) == 6); // Cannot add caret below from last line last line wrap. text_edit->add_caret_at_carets(true); @@ -7171,7 +7229,7 @@ TEST_CASE("[SceneTree][TextEdit] multicaret") { CHECK(text_edit->get_caret_line(0) == 2); CHECK(text_edit->get_caret_column(0) == 5); CHECK(text_edit->get_caret_line(1) == 2); - CHECK(text_edit->get_caret_column(1) == 10); + CHECK(text_edit->get_caret_column(1) == 6); // Add caret above from not first line wrap. text_edit->remove_secondary_carets(); diff --git a/tests/scene/test_tree.h b/tests/scene/test_tree.h new file mode 100644 index 0000000000..41ef39d621 --- /dev/null +++ b/tests/scene/test_tree.h @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* test_tree.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_TREE_H +#define TEST_TREE_H + +#include "scene/gui/tree.h" + +#include "tests/test_macros.h" + +namespace TestTree { + +TEST_CASE("[SceneTree][Tree]") { + SUBCASE("[Tree] Create and remove items.") { + Tree *tree = memnew(Tree); + TreeItem *root = tree->create_item(); + + TreeItem *child1 = tree->create_item(); + CHECK_EQ(root->get_child_count(), 1); + + TreeItem *child2 = tree->create_item(root); + CHECK_EQ(root->get_child_count(), 2); + + TreeItem *child3 = tree->create_item(root, 0); + CHECK_EQ(root->get_child_count(), 3); + + CHECK_EQ(root->get_child(0), child3); + CHECK_EQ(root->get_child(1), child1); + CHECK_EQ(root->get_child(2), child2); + + root->remove_child(child3); + CHECK_EQ(root->get_child_count(), 2); + + root->add_child(child3); + CHECK_EQ(root->get_child_count(), 3); + + TreeItem *child4 = root->create_child(); + CHECK_EQ(root->get_child_count(), 4); + + CHECK_EQ(root->get_child(0), child1); + CHECK_EQ(root->get_child(1), child2); + CHECK_EQ(root->get_child(2), child3); + CHECK_EQ(root->get_child(3), child4); + + memdelete(tree); + } + + SUBCASE("[Tree] Clear items.") { + Tree *tree = memnew(Tree); + TreeItem *root = tree->create_item(); + + for (int i = 0; i < 10; i++) { + tree->create_item(); + } + CHECK_EQ(root->get_child_count(), 10); + + root->clear_children(); + CHECK_EQ(root->get_child_count(), 0); + + memdelete(tree); + } + + SUBCASE("[Tree] Get last item.") { + Tree *tree = memnew(Tree); + TreeItem *root = tree->create_item(); + + TreeItem *last; + for (int i = 0; i < 10; i++) { + last = tree->create_item(); + } + CHECK_EQ(root->get_child_count(), 10); + CHECK_EQ(tree->get_last_item(), last); + + // Check nested. + TreeItem *old_last = last; + for (int i = 0; i < 10; i++) { + last = tree->create_item(old_last); + } + CHECK_EQ(tree->get_last_item(), last); + + memdelete(tree); + } + + SUBCASE("[Tree] Previous and Next items.") { + Tree *tree = memnew(Tree); + TreeItem *root = tree->create_item(); + + TreeItem *child1 = tree->create_item(); + TreeItem *child2 = tree->create_item(); + TreeItem *child3 = tree->create_item(); + CHECK_EQ(child1->get_next(), child2); + CHECK_EQ(child1->get_next_in_tree(), child2); + CHECK_EQ(child2->get_next(), child3); + CHECK_EQ(child2->get_next_in_tree(), child3); + CHECK_EQ(child3->get_next(), nullptr); + CHECK_EQ(child3->get_next_in_tree(), nullptr); + + CHECK_EQ(child1->get_prev(), nullptr); + CHECK_EQ(child1->get_prev_in_tree(), root); + CHECK_EQ(child2->get_prev(), child1); + CHECK_EQ(child2->get_prev_in_tree(), child1); + CHECK_EQ(child3->get_prev(), child2); + CHECK_EQ(child3->get_prev_in_tree(), child2); + + TreeItem *nested1 = tree->create_item(child2); + TreeItem *nested2 = tree->create_item(child2); + TreeItem *nested3 = tree->create_item(child2); + + CHECK_EQ(child1->get_next(), child2); + CHECK_EQ(child1->get_next_in_tree(), child2); + CHECK_EQ(child2->get_next(), child3); + CHECK_EQ(child2->get_next_in_tree(), nested1); + CHECK_EQ(child3->get_prev(), child2); + CHECK_EQ(child3->get_prev_in_tree(), nested3); + CHECK_EQ(nested1->get_prev_in_tree(), child2); + CHECK_EQ(nested1->get_next_in_tree(), nested2); + + memdelete(tree); + } +} + +} // namespace TestTree + +#endif // TEST_TREE_H diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 1f35757ed6..5308fe5e80 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -134,6 +134,7 @@ #include "tests/scene/test_color_picker.h" #include "tests/scene/test_graph_node.h" #include "tests/scene/test_text_edit.h" +#include "tests/scene/test_tree.h" #endif // ADVANCED_GUI_DISABLED #ifndef _3D_DISABLED |