summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/core/config/test_project_settings.h7
-rw-r--r--tests/core/math/test_aabb.h14
-rw-r--r--tests/core/math/test_rect2.h22
-rw-r--r--tests/core/object/test_class_db.h53
-rw-r--r--tests/core/string/test_string.h152
-rw-r--r--tests/core/string/test_translation.h1
-rw-r--r--tests/core/string/test_translation_server.h2
-rw-r--r--tests/scene/test_code_edit.h20
-rw-r--r--tests/scene/test_node_2d.h125
-rw-r--r--tests/scene/test_text_edit.h66
-rw-r--r--tests/scene/test_tree.h151
-rw-r--r--tests/test_main.cpp1
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