summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/core/input/test_input_event.h4
-rw-r--r--tests/core/io/test_image.h4
-rw-r--r--tests/core/io/test_pck_packer.h8
-rw-r--r--tests/core/io/test_resource.h8
-rw-r--r--tests/core/math/test_aabb.h61
-rw-r--r--tests/core/math/test_math_funcs.h3
-rw-r--r--tests/core/math/test_transform_2d.h110
-rw-r--r--tests/core/object/test_class_db.h12
-rw-r--r--tests/core/object/test_object.h5
-rw-r--r--tests/core/string/test_string.h309
-rwxr-xr-x[-rw-r--r--]tests/create_test.py0
-rw-r--r--tests/display_server_mock.h2
-rw-r--r--tests/python_build/fixtures/gles3/vertex_fragment_expected_full.glsl1
-rw-r--r--tests/python_build/test_gles3_builder.py2
-rw-r--r--tests/python_build/test_glsl_builder.py2
-rw-r--r--tests/scene/test_audio_stream_wav.h6
-rw-r--r--tests/scene/test_code_edit.h39
-rw-r--r--tests/scene/test_curve_2d.h45
-rw-r--r--tests/scene/test_curve_3d.h20
-rw-r--r--tests/scene/test_image_texture_3d.h101
-rw-r--r--tests/scene/test_instance_placeholder.h532
-rw-r--r--tests/scene/test_node.h128
-rw-r--r--tests/scene/test_viewport.h174
-rw-r--r--tests/servers/test_navigation_server_3d.h6
-rw-r--r--tests/test_macros.h67
-rw-r--r--tests/test_main.cpp12
-rw-r--r--tests/test_utils.cpp7
-rw-r--r--tests/test_utils.h1
28 files changed, 1390 insertions, 279 deletions
diff --git a/tests/core/input/test_input_event.h b/tests/core/input/test_input_event.h
index 6b4b80486c..c6a287e47b 100644
--- a/tests/core/input/test_input_event.h
+++ b/tests/core/input/test_input_event.h
@@ -43,7 +43,7 @@ TEST_CASE("[InputEvent] Signal is emitted when device is changed") {
Ref<InputEventKey> input_event;
input_event.instantiate();
- SIGNAL_WATCH(*input_event, SNAME("changed"));
+ SIGNAL_WATCH(*input_event, CoreStringName(changed));
Array args1;
Array empty_args;
empty_args.push_back(args1);
@@ -53,7 +53,7 @@ TEST_CASE("[InputEvent] Signal is emitted when device is changed") {
SIGNAL_CHECK("changed", empty_args);
CHECK(input_event->get_device() == 1);
- SIGNAL_UNWATCH(*input_event, SNAME("changed"));
+ SIGNAL_UNWATCH(*input_event, CoreStringName(changed));
}
TEST_CASE("[InputEvent] Test accumulate") {
diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h
index 7a0cbb13f9..1b51286a9f 100644
--- a/tests/core/io/test_image.h
+++ b/tests/core/io/test_image.h
@@ -78,8 +78,8 @@ TEST_CASE("[Image] Instantiation") {
TEST_CASE("[Image] Saving and loading") {
Ref<Image> image = memnew(Image(4, 4, false, Image::FORMAT_RGBA8));
- const String save_path_png = OS::get_singleton()->get_cache_path().path_join("image.png");
- const String save_path_exr = OS::get_singleton()->get_cache_path().path_join("image.exr");
+ const String save_path_png = TestUtils::get_temp_path("image.png");
+ const String save_path_exr = TestUtils::get_temp_path("image.exr");
// Save PNG
Error err;
diff --git a/tests/core/io/test_pck_packer.h b/tests/core/io/test_pck_packer.h
index fc4534a949..7ef9451963 100644
--- a/tests/core/io/test_pck_packer.h
+++ b/tests/core/io/test_pck_packer.h
@@ -42,7 +42,7 @@ namespace TestPCKPacker {
TEST_CASE("[PCKPacker] Pack an empty PCK file") {
PCKPacker pck_packer;
- const String output_pck_path = OS::get_singleton()->get_cache_path().path_join("output_empty.pck");
+ const String output_pck_path = TestUtils::get_temp_path("output_empty.pck");
CHECK_MESSAGE(
pck_packer.pck_start(output_pck_path) == OK,
"Starting a PCK file should return an OK error code.");
@@ -66,7 +66,7 @@ TEST_CASE("[PCKPacker] Pack an empty PCK file") {
TEST_CASE("[PCKPacker] Pack empty with zero alignment invalid") {
PCKPacker pck_packer;
- const String output_pck_path = OS::get_singleton()->get_cache_path().path_join("output_empty.pck");
+ const String output_pck_path = TestUtils::get_temp_path("output_empty.pck");
ERR_PRINT_OFF;
CHECK_MESSAGE(pck_packer.pck_start(output_pck_path, 0) != OK, "PCK with zero alignment should fail.");
ERR_PRINT_ON;
@@ -74,7 +74,7 @@ TEST_CASE("[PCKPacker] Pack empty with zero alignment invalid") {
TEST_CASE("[PCKPacker] Pack empty with invalid key") {
PCKPacker pck_packer;
- const String output_pck_path = OS::get_singleton()->get_cache_path().path_join("output_empty.pck");
+ const String output_pck_path = TestUtils::get_temp_path("output_empty.pck");
ERR_PRINT_OFF;
CHECK_MESSAGE(pck_packer.pck_start(output_pck_path, 32, "") != OK, "PCK with invalid key should fail.");
ERR_PRINT_ON;
@@ -82,7 +82,7 @@ TEST_CASE("[PCKPacker] Pack empty with invalid key") {
TEST_CASE("[PCKPacker] Pack a PCK file with some files and directories") {
PCKPacker pck_packer;
- const String output_pck_path = OS::get_singleton()->get_cache_path().path_join("output_with_files.pck");
+ const String output_pck_path = TestUtils::get_temp_path("output_with_files.pck");
CHECK_MESSAGE(
pck_packer.pck_start(output_pck_path) == OK,
"Starting a PCK file should return an OK error code.");
diff --git a/tests/core/io/test_resource.h b/tests/core/io/test_resource.h
index a83e7f88ba..cb1fa290b3 100644
--- a/tests/core/io/test_resource.h
+++ b/tests/core/io/test_resource.h
@@ -76,8 +76,8 @@ TEST_CASE("[Resource] Saving and loading") {
Ref<Resource> child_resource = memnew(Resource);
child_resource->set_name("I'm a child resource");
resource->set_meta("other_resource", child_resource);
- const String save_path_binary = OS::get_singleton()->get_cache_path().path_join("resource.res");
- const String save_path_text = OS::get_singleton()->get_cache_path().path_join("resource.tres");
+ const String save_path_binary = TestUtils::get_temp_path("resource.res");
+ const String save_path_text = TestUtils::get_temp_path("resource.tres");
ResourceSaver::save(resource, save_path_binary);
ResourceSaver::save(resource, save_path_text);
@@ -123,8 +123,8 @@ TEST_CASE("[Resource] Breaking circular references on save") {
resource_b->set_meta("next", resource_c);
resource_c->set_meta("next", resource_b);
- const String save_path_binary = OS::get_singleton()->get_cache_path().path_join("resource.res");
- const String save_path_text = OS::get_singleton()->get_cache_path().path_join("resource.tres");
+ const String save_path_binary = TestUtils::get_temp_path("resource.res");
+ const String save_path_text = TestUtils::get_temp_path("resource.tres");
ResourceSaver::save(resource_a, save_path_binary);
// Suppress expected errors caused by the resources above being uncached.
ERR_PRINT_OFF;
diff --git a/tests/core/math/test_aabb.h b/tests/core/math/test_aabb.h
index b9f84cca24..dbc62bc248 100644
--- a/tests/core/math/test_aabb.h
+++ b/tests/core/math/test_aabb.h
@@ -204,6 +204,67 @@ TEST_CASE("[AABB] Intersection") {
CHECK_MESSAGE(
!aabb_big.intersects_segment(Vector3(0, 300, 0), Vector3(0, 300, 0)),
"intersects_segment() should return the expected result with segment of length 0.");
+ CHECK_MESSAGE( // Simple ray intersection test.
+ aabb_big.intersects_ray(Vector3(-100, 3, 0), Vector3(1, 0, 0)),
+ "intersects_ray() should return true when ray points directly to AABB from outside.");
+ CHECK_MESSAGE( // Ray parallel to an edge.
+ !aabb_big.intersects_ray(Vector3(10, 10, 0), Vector3(0, 1, 0)),
+ "intersects_ray() should return false for ray parallel and outside of AABB.");
+ CHECK_MESSAGE( // Ray origin inside aabb.
+ aabb_big.intersects_ray(Vector3(1, 1, 1), Vector3(0, 1, 0)),
+ "intersects_ray() should return true for rays originating inside the AABB.");
+ CHECK_MESSAGE( // Ray pointing away from aabb.
+ !aabb_big.intersects_ray(Vector3(-10, 0, 0), Vector3(-1, 0, 0)),
+ "intersects_ray() should return false when ray points away from AABB.");
+ CHECK_MESSAGE( // Ray along a diagonal of aabb.
+ aabb_big.intersects_ray(Vector3(0, 0, 0), Vector3(1, 1, 1)),
+ "intersects_ray() should return true for rays along the AABB diagonal.");
+ CHECK_MESSAGE( // Ray originating at aabb edge.
+ aabb_big.intersects_ray(aabb_big.position, Vector3(-1, 0, 0)),
+ "intersects_ray() should return true for rays starting on AABB's edge.");
+ CHECK_MESSAGE( // Ray with zero direction inside.
+ aabb_big.intersects_ray(Vector3(-1, 3, -2), Vector3(0, 0, 0)),
+ "intersects_ray() should return true because its inside.");
+ CHECK_MESSAGE( // Ray with zero direction outside.
+ !aabb_big.intersects_ray(Vector3(-1000, 3, -2), Vector3(0, 0, 0)),
+ "intersects_ray() should return false for being outside.");
+
+ // Finding ray intersections.
+ const AABB aabb_simple = AABB(Vector3(), Vector3(1, 1, 1));
+ bool inside = false;
+ Vector3 intersection_point;
+ Vector3 intersection_normal;
+
+ // Borders.
+ aabb_simple.find_intersects_ray(Vector3(0.5, 0, 0.5), Vector3(0, 1, 0), inside, &intersection_point, &intersection_normal);
+ CHECK_MESSAGE(inside == false, "find_intersects_ray() should return outside on borders.");
+ CHECK_MESSAGE(intersection_point.is_equal_approx(Vector3(0.5, 0, 0.5)), "find_intersects_ray() border intersection point incorrect.");
+ CHECK_MESSAGE(intersection_normal.is_equal_approx(Vector3(0, -1, 0)), "find_intersects_ray() border intersection normal incorrect.");
+ aabb_simple.find_intersects_ray(Vector3(0.5, 1, 0.5), Vector3(0, -1, 0), inside, &intersection_point, &intersection_normal);
+ CHECK_MESSAGE(inside == false, "find_intersects_ray() should return outside on borders.");
+ CHECK_MESSAGE(intersection_point.is_equal_approx(Vector3(0.5, 1, 0.5)), "find_intersects_ray() border intersection point incorrect.");
+ CHECK_MESSAGE(intersection_normal.is_equal_approx(Vector3(0, 1, 0)), "find_intersects_ray() border intersection normal incorrect.");
+
+ // Inside.
+ aabb_simple.find_intersects_ray(Vector3(0.5, 0.1, 0.5), Vector3(0, 1, 0), inside, &intersection_point, &intersection_normal);
+ CHECK_MESSAGE(inside == true, "find_intersects_ray() should return inside when inside.");
+ CHECK_MESSAGE(intersection_point.is_equal_approx(Vector3(0.5, 0, 0.5)), "find_intersects_ray() inside backtracking intersection point incorrect.");
+ CHECK_MESSAGE(intersection_normal.is_equal_approx(Vector3(0, -1, 0)), "find_intersects_ray() inside intersection normal incorrect.");
+
+ // Zero sized AABB.
+ const AABB aabb_zero = AABB(Vector3(), Vector3(1, 0, 1));
+ aabb_zero.find_intersects_ray(Vector3(0.5, 0, 0.5), Vector3(0, 1, 0), inside, &intersection_point, &intersection_normal);
+ CHECK_MESSAGE(inside == false, "find_intersects_ray() should return outside on borders of zero sized AABB.");
+ CHECK_MESSAGE(intersection_point.is_equal_approx(Vector3(0.5, 0, 0.5)), "find_intersects_ray() border intersection point incorrect for zero sized AABB.");
+ CHECK_MESSAGE(intersection_normal.is_equal_approx(Vector3(0, -1, 0)), "find_intersects_ray() border intersection normal incorrect for zero sized AABB.");
+ aabb_zero.find_intersects_ray(Vector3(0.5, 0, 0.5), Vector3(0, -1, 0), inside, &intersection_point, &intersection_normal);
+ CHECK_MESSAGE(inside == false, "find_intersects_ray() should return outside on borders of zero sized AABB.");
+ CHECK_MESSAGE(intersection_point.is_equal_approx(Vector3(0.5, 0, 0.5)), "find_intersects_ray() border intersection point incorrect for zero sized AABB.");
+ CHECK_MESSAGE(intersection_normal.is_equal_approx(Vector3(0, 1, 0)), "find_intersects_ray() border intersection normal incorrect for zero sized AABB.");
+ aabb_zero.find_intersects_ray(Vector3(0.5, -1, 0.5), Vector3(0, 1, 0), inside, &intersection_point, &intersection_normal);
+ CHECK_MESSAGE(inside == false, "find_intersects_ray() should return outside on borders of zero sized AABB.");
+ CHECK_MESSAGE(intersection_point.is_equal_approx(Vector3(0.5, 0, 0.5)), "find_intersects_ray() border intersection point incorrect for zero sized AABB.");
+ CHECK_MESSAGE(intersection_normal.is_equal_approx(Vector3(0, -1, 0)), "find_intersects_ray() border intersection normal incorrect for zero sized AABB.");
}
TEST_CASE("[AABB] Merging") {
diff --git a/tests/core/math/test_math_funcs.h b/tests/core/math/test_math_funcs.h
index 0a9d9c97d9..68540e4d61 100644
--- a/tests/core/math/test_math_funcs.h
+++ b/tests/core/math/test_math_funcs.h
@@ -381,6 +381,9 @@ TEST_CASE_TEMPLATE("[Math] remap", T, float, double) {
CHECK(Math::remap((T)-100.0, (T)-100.0, (T)-200.0, (T)0.0, (T)-1000.0) == doctest::Approx((T)0.0));
CHECK(Math::remap((T)-200.0, (T)-100.0, (T)-200.0, (T)0.0, (T)-1000.0) == doctest::Approx((T)-1000.0));
CHECK(Math::remap((T)-250.0, (T)-100.0, (T)-200.0, (T)0.0, (T)-1000.0) == doctest::Approx((T)-1500.0));
+
+ // Note: undefined behavior can happen when `p_istart == p_istop`. We don't bother testing this as it will
+ // vary between hardware and compilers properly implementing IEEE 754.
}
TEST_CASE_TEMPLATE("[Math] angle_difference", T, float, double) {
diff --git a/tests/core/math/test_transform_2d.h b/tests/core/math/test_transform_2d.h
index 36d27ce7a9..6d3c80e5ca 100644
--- a/tests/core/math/test_transform_2d.h
+++ b/tests/core/math/test_transform_2d.h
@@ -45,48 +45,132 @@ Transform2D identity() {
return Transform2D();
}
+TEST_CASE("[Transform2D] Default constructor") {
+ Transform2D default_constructor = Transform2D();
+ CHECK(default_constructor == Transform2D(Vector2(1, 0), Vector2(0, 1), Vector2(0, 0)));
+}
+
+TEST_CASE("[Transform2D] Copy constructor") {
+ Transform2D T = create_dummy_transform();
+ Transform2D copy_constructor = Transform2D(T);
+ CHECK(T == copy_constructor);
+}
+
+TEST_CASE("[Transform2D] Constructor from angle and position") {
+ constexpr float ROTATION = Math_PI / 4;
+ const Vector2 TRANSLATION = Vector2(20, -20);
+
+ const Transform2D test = Transform2D(ROTATION, TRANSLATION);
+ const Transform2D expected = Transform2D().rotated(ROTATION).translated(TRANSLATION);
+ CHECK(test == expected);
+}
+
+TEST_CASE("[Transform2D] Constructor from angle, scale, skew and position") {
+ constexpr float ROTATION = Math_PI / 2;
+ const Vector2 SCALE = Vector2(2, 0.5);
+ constexpr float SKEW = Math_PI / 4;
+ const Vector2 TRANSLATION = Vector2(30, 0);
+
+ const Transform2D test = Transform2D(ROTATION, SCALE, SKEW, TRANSLATION);
+ Transform2D expected = Transform2D().scaled(SCALE).rotated(ROTATION).translated(TRANSLATION);
+ expected.set_skew(SKEW);
+
+ CHECK(test.is_equal_approx(expected));
+}
+
+TEST_CASE("[Transform2D] Constructor from raw values") {
+ const Transform2D test = Transform2D(1, 2, 3, 4, 5, 6);
+ const Transform2D expected = Transform2D(Vector2(1, 2), Vector2(3, 4), Vector2(5, 6));
+ CHECK(test == expected);
+}
+
+TEST_CASE("[Transform2D] xform") {
+ const Vector2 v = Vector2(2, 3);
+ const Transform2D T = Transform2D(Vector2(1, 2), Vector2(3, 4), Vector2(5, 6));
+ const Vector2 expected = Vector2(1 * 2 + 3 * 3 + 5 * 1, 2 * 2 + 4 * 3 + 6 * 1);
+ CHECK(T.xform(v) == expected);
+}
+
+TEST_CASE("[Transform2D] Basis xform") {
+ const Vector2 v = Vector2(2, 2);
+ const Transform2D T1 = Transform2D(Vector2(1, 2), Vector2(3, 4), Vector2(0, 0));
+
+ // Both versions should be the same when the origin is (0,0).
+ CHECK(T1.basis_xform(v) == T1.xform(v));
+
+ const Transform2D T2 = Transform2D(Vector2(1, 2), Vector2(3, 4), Vector2(5, 6));
+
+ // Each version should be different when the origin is not (0,0).
+ CHECK_FALSE(T2.basis_xform(v) == T2.xform(v));
+}
+
+TEST_CASE("[Transform2D] Affine inverse") {
+ const Transform2D orig = create_dummy_transform();
+ const Transform2D affine_inverted = orig.affine_inverse();
+ const Transform2D affine_inverted_again = affine_inverted.affine_inverse();
+ CHECK(affine_inverted_again == orig);
+}
+
+TEST_CASE("[Transform2D] Orthonormalized") {
+ const Transform2D T = create_dummy_transform();
+ const Transform2D orthonormalized_T = T.orthonormalized();
+
+ // Check each basis has length 1.
+ CHECK(Math::is_equal_approx(orthonormalized_T[0].length_squared(), 1));
+ CHECK(Math::is_equal_approx(orthonormalized_T[1].length_squared(), 1));
+
+ const Vector2 vx = Vector2(orthonormalized_T[0].x, orthonormalized_T[1].x);
+ const Vector2 vy = Vector2(orthonormalized_T[0].y, orthonormalized_T[1].y);
+
+ // Check the basis are orthogonal.
+ CHECK(Math::is_equal_approx(orthonormalized_T.tdotx(vx), 1));
+ CHECK(Math::is_equal_approx(orthonormalized_T.tdotx(vy), 0));
+ CHECK(Math::is_equal_approx(orthonormalized_T.tdoty(vx), 0));
+ CHECK(Math::is_equal_approx(orthonormalized_T.tdoty(vy), 1));
+}
+
TEST_CASE("[Transform2D] translation") {
- Vector2 offset = Vector2(1, 2);
+ const Vector2 offset = Vector2(1, 2);
// Both versions should give the same result applied to identity.
CHECK(identity().translated(offset) == identity().translated_local(offset));
// Check both versions against left and right multiplications.
- Transform2D orig = create_dummy_transform();
- Transform2D T = identity().translated(offset);
+ const Transform2D orig = create_dummy_transform();
+ const Transform2D T = identity().translated(offset);
CHECK(orig.translated(offset) == T * orig);
CHECK(orig.translated_local(offset) == orig * T);
}
TEST_CASE("[Transform2D] scaling") {
- Vector2 scaling = Vector2(1, 2);
+ const Vector2 scaling = Vector2(1, 2);
// Both versions should give the same result applied to identity.
CHECK(identity().scaled(scaling) == identity().scaled_local(scaling));
// Check both versions against left and right multiplications.
- Transform2D orig = create_dummy_transform();
- Transform2D S = identity().scaled(scaling);
+ const Transform2D orig = create_dummy_transform();
+ const Transform2D S = identity().scaled(scaling);
CHECK(orig.scaled(scaling) == S * orig);
CHECK(orig.scaled_local(scaling) == orig * S);
}
TEST_CASE("[Transform2D] rotation") {
- real_t phi = 1.0;
+ constexpr real_t phi = 1.0;
// Both versions should give the same result applied to identity.
CHECK(identity().rotated(phi) == identity().rotated_local(phi));
// Check both versions against left and right multiplications.
- Transform2D orig = create_dummy_transform();
- Transform2D R = identity().rotated(phi);
+ const Transform2D orig = create_dummy_transform();
+ const Transform2D R = identity().rotated(phi);
CHECK(orig.rotated(phi) == R * orig);
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));
+ const 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));
+ const 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()));
@@ -98,8 +182,8 @@ TEST_CASE("[Transform2D] Interpolation") {
}
TEST_CASE("[Transform2D] Finite number checks") {
- const Vector2 x(0, 1);
- const Vector2 infinite(NAN, NAN);
+ const Vector2 x = Vector2(0, 1);
+ const Vector2 infinite = Vector2(NAN, NAN);
CHECK_MESSAGE(
Transform2D(x, x, x).is_finite(),
diff --git a/tests/core/object/test_class_db.h b/tests/core/object/test_class_db.h
index fb62d0f056..381d759e5b 100644
--- a/tests/core/object/test_class_db.h
+++ b/tests/core/object/test_class_db.h
@@ -195,12 +195,12 @@ struct Context {
}
bool has_type(const TypeReference &p_type_ref) const {
- if (builtin_types.find(p_type_ref.name) >= 0) {
+ if (builtin_types.has(p_type_ref.name)) {
return true;
}
if (p_type_ref.is_enum) {
- if (enum_types.find(p_type_ref.name) >= 0) {
+ if (enum_types.has(p_type_ref.name)) {
return true;
}
@@ -355,7 +355,7 @@ void validate_property(const Context &p_context, const ExposedClass &p_class, co
const ArgumentData &idx_arg = getter->arguments.front()->get();
if (idx_arg.type.name != p_context.names_cache.int_type) {
// If not an int, it can be an enum
- TEST_COND(p_context.enum_types.find(idx_arg.type.name) < 0,
+ TEST_COND(!p_context.enum_types.has(idx_arg.type.name),
"Invalid type '", idx_arg.type.name, "' for index argument of property getter: '", p_class.name, ".", String(p_prop.name), "'.");
}
}
@@ -367,7 +367,7 @@ void validate_property(const Context &p_context, const ExposedClass &p_class, co
if (idx_arg.type.name != p_context.names_cache.int_type) {
// Assume the index parameter is an enum
// If not an int, it can be an enum
- TEST_COND(p_context.enum_types.find(idx_arg.type.name) < 0,
+ TEST_COND(!p_context.enum_types.has(idx_arg.type.name),
"Invalid type '", idx_arg.type.name, "' for index argument of property setter: '", p_class.name, ".", String(p_prop.name), "'.");
}
}
@@ -736,7 +736,7 @@ void add_exposed_classes(Context &r_context) {
for (const StringName &E : K.value.constants) {
const StringName &constant_name = E;
- TEST_FAIL_COND(String(constant_name).find("::") != -1,
+ TEST_FAIL_COND(String(constant_name).contains("::"),
"Enum constant contains '::', check bindings to remove the scope: '",
String(class_name), ".", String(enum_.name), ".", String(constant_name), "'.");
int64_t *value = class_info->constant_map.getptr(constant_name);
@@ -758,7 +758,7 @@ void add_exposed_classes(Context &r_context) {
for (const String &E : constants) {
const String &constant_name = E;
- TEST_FAIL_COND(constant_name.find("::") != -1,
+ TEST_FAIL_COND(constant_name.contains("::"),
"Constant contains '::', check bindings to remove the scope: '",
String(class_name), ".", constant_name, "'.");
int64_t *value = class_info->constant_map.getptr(StringName(E));
diff --git a/tests/core/object/test_object.h b/tests/core/object/test_object.h
index d714d71416..57bc65328a 100644
--- a/tests/core/object/test_object.h
+++ b/tests/core/object/test_object.h
@@ -31,7 +31,6 @@
#ifndef TEST_OBJECT_H
#define TEST_OBJECT_H
-#include "core/core_string_names.h"
#include "core/object/class_db.h"
#include "core/object/object.h"
#include "core/object/script_language.h"
@@ -251,7 +250,7 @@ TEST_CASE("[Object] Script property setter") {
Variant script;
bool valid = false;
- object.set(CoreStringNames::get_singleton()->_script, script, &valid);
+ object.set(CoreStringName(script), script, &valid);
CHECK(valid);
CHECK_MESSAGE(
object.get_script() == script,
@@ -264,7 +263,7 @@ TEST_CASE("[Object] Script property getter") {
object.set_script(script);
bool valid = false;
- const Variant &actual_value = object.get(CoreStringNames::get_singleton()->_script, &valid);
+ const Variant &actual_value = object.get(CoreStringName(script), &valid);
CHECK(valid);
CHECK_MESSAGE(
actual_value == script,
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index 64f03e5879..cf57183a02 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -297,6 +297,19 @@ TEST_CASE("[String] Contains") {
CHECK(!s.contains(String("\\char_test.tscn")));
}
+TEST_CASE("[String] Contains case insensitive") {
+ String s = "C:\\Godot\\project\\string_test.tscn";
+ CHECK(s.containsn("Godot"));
+ CHECK(s.containsn("godot"));
+ CHECK(s.containsn(String("Project\\string_test")));
+ CHECK(s.containsn(String("\\string_Test.tscn")));
+
+ CHECK(!s.containsn("Godoh"));
+ CHECK(!s.containsn("godoh"));
+ CHECK(!s.containsn(String("project\\string test")));
+ CHECK(!s.containsn(String("\\char_test.tscn")));
+}
+
TEST_CASE("[String] Test chr") {
CHECK(String::chr('H') == "H");
CHECK(String::chr(0x3012)[0] == 0x3012);
@@ -360,18 +373,37 @@ TEST_CASE("[String] Substr") {
TEST_CASE("[String] Find") {
String s = "Pretty Woman Woman";
- CHECK(s.find("tty") == 3);
- CHECK(s.find("Wo", 9) == 13);
- CHECK(s.find("Revenge of the Monster Truck") == -1);
- CHECK(s.rfind("man") == 15);
-}
-
-TEST_CASE("[String] Find no case") {
+ MULTICHECK_STRING_EQ(s, find, "tty", 3);
+ MULTICHECK_STRING_EQ(s, find, "Revenge of the Monster Truck", -1);
+ MULTICHECK_STRING_INT_EQ(s, find, "Wo", 9, 13);
+ MULTICHECK_STRING_EQ(s, find, "", -1);
+ MULTICHECK_STRING_EQ(s, find, "Pretty Woman Woman", 0);
+ MULTICHECK_STRING_EQ(s, find, "WOMAN", -1);
+ MULTICHECK_STRING_INT_EQ(s, find, "", 9, -1);
+
+ MULTICHECK_STRING_EQ(s, rfind, "", -1);
+ MULTICHECK_STRING_EQ(s, rfind, "foo", -1);
+ MULTICHECK_STRING_EQ(s, rfind, "Pretty Woman Woman", 0);
+ MULTICHECK_STRING_EQ(s, rfind, "man", 15);
+ MULTICHECK_STRING_EQ(s, rfind, "WOMAN", -1);
+ MULTICHECK_STRING_INT_EQ(s, rfind, "", 15, -1);
+}
+
+TEST_CASE("[String] Find case insensitive") {
String s = "Pretty Whale Whale";
- CHECK(s.findn("WHA") == 7);
- CHECK(s.findn("WHA", 9) == 13);
- CHECK(s.findn("Revenge of the Monster SawFish") == -1);
- CHECK(s.rfindn("WHA") == 13);
+ MULTICHECK_STRING_EQ(s, findn, "WHA", 7);
+ MULTICHECK_STRING_INT_EQ(s, findn, "WHA", 9, 13);
+ MULTICHECK_STRING_EQ(s, findn, "Revenge of the Monster SawFish", -1);
+ MULTICHECK_STRING_EQ(s, findn, "", -1);
+ MULTICHECK_STRING_EQ(s, findn, "wha", 7);
+ MULTICHECK_STRING_EQ(s, findn, "Wha", 7);
+ MULTICHECK_STRING_INT_EQ(s, findn, "", 3, -1);
+
+ MULTICHECK_STRING_EQ(s, rfindn, "WHA", 13);
+ MULTICHECK_STRING_EQ(s, rfindn, "", -1);
+ MULTICHECK_STRING_EQ(s, rfindn, "wha", 13);
+ MULTICHECK_STRING_EQ(s, rfindn, "Wha", 13);
+ MULTICHECK_STRING_INT_EQ(s, rfindn, "", 13, -1);
}
TEST_CASE("[String] Find MK") {
@@ -392,11 +424,9 @@ TEST_CASE("[String] Find MK") {
TEST_CASE("[String] Find and replace") {
String s = "Happy Birthday, Anna!";
- s = s.replace("Birthday", "Halloween");
- CHECK(s == "Happy Halloween, Anna!");
-
- s = s.replace_first("H", "W");
- CHECK(s == "Wappy Halloween, Anna!");
+ MULTICHECK_STRING_STRING_EQ(s, replace, "Birthday", "Halloween", "Happy Halloween, Anna!");
+ MULTICHECK_STRING_STRING_EQ(s, replace_first, "y", "Y", "HappY Birthday, Anna!");
+ MULTICHECK_STRING_STRING_EQ(s, replacen, "Y", "Y", "HappY BirthdaY, Anna!");
}
TEST_CASE("[String] Insertion") {
@@ -557,51 +587,76 @@ TEST_CASE("[String] String to float") {
TEST_CASE("[String] Slicing") {
String s = "Mars,Jupiter,Saturn,Uranus";
-
const char *slices[4] = { "Mars", "Jupiter", "Saturn", "Uranus" };
- for (int i = 0; i < s.get_slice_count(","); i++) {
- CHECK(s.get_slice(",", i) == slices[i]);
- }
+ MULTICHECK_GET_SLICE(s, ",", slices);
+}
+
+TEST_CASE("[String] Begins with") {
+ // Test cases for true:
+ MULTICHECK_STRING_EQ(String("res://foobar"), begins_with, "res://", true);
+ MULTICHECK_STRING_EQ(String("abc"), begins_with, "abc", true);
+ MULTICHECK_STRING_EQ(String("abc"), begins_with, "", true);
+ MULTICHECK_STRING_EQ(String(""), begins_with, "", true);
+
+ // Test cases for false:
+ MULTICHECK_STRING_EQ(String("res"), begins_with, "res://", false);
+ MULTICHECK_STRING_EQ(String("abcdef"), begins_with, "foo", false);
+ MULTICHECK_STRING_EQ(String("abc"), begins_with, "ax", false);
+ MULTICHECK_STRING_EQ(String(""), begins_with, "abc", false);
+
+ // Test "const char *" version also with nullptr.
+ String s("foo");
+ bool state = s.begins_with(nullptr) == false;
+ CHECK_MESSAGE(state, "nullptr check failed");
+
+ String empty("");
+ state = empty.begins_with(nullptr) == false;
+ CHECK_MESSAGE(state, "nullptr check with empty string failed");
+}
+
+TEST_CASE("[String] Ends with") {
+ // Test cases for true:
+ MULTICHECK_STRING_EQ(String("res://foobar"), ends_with, "foobar", true);
+ MULTICHECK_STRING_EQ(String("abc"), ends_with, "abc", true);
+ MULTICHECK_STRING_EQ(String("abc"), ends_with, "", true);
+ MULTICHECK_STRING_EQ(String(""), ends_with, "", true);
+
+ // Test cases for false:
+ MULTICHECK_STRING_EQ(String("res"), ends_with, "res://", false);
+ MULTICHECK_STRING_EQ(String("abcdef"), ends_with, "foo", false);
+ MULTICHECK_STRING_EQ(String("abc"), ends_with, "ax", false);
+ MULTICHECK_STRING_EQ(String(""), ends_with, "abc", false);
+
+ // Test "const char *" version also with nullptr.
+ String s("foo");
+ bool state = s.ends_with(nullptr) == false;
+ CHECK_MESSAGE(state, "nullptr check failed");
+
+ String empty("");
+ state = empty.ends_with(nullptr) == false;
+ CHECK_MESSAGE(state, "nullptr check with empty string failed");
}
TEST_CASE("[String] Splitting") {
String s = "Mars,Jupiter,Saturn,Uranus";
- Vector<String> l;
-
const char *slices_l[3] = { "Mars", "Jupiter", "Saturn,Uranus" };
- const char *slices_r[3] = { "Mars,Jupiter", "Saturn", "Uranus" };
- const char *slices_3[4] = { "t", "e", "s", "t" };
+ MULTICHECK_SPLIT(s, split, ",", true, 2, slices_l, 3);
- l = s.split(",", true, 2);
- CHECK(l.size() == 3);
- for (int i = 0; i < l.size(); i++) {
- CHECK(l[i] == slices_l[i]);
- }
-
- l = s.rsplit(",", true, 2);
- CHECK(l.size() == 3);
- for (int i = 0; i < l.size(); i++) {
- CHECK(l[i] == slices_r[i]);
- }
+ const char *slices_r[3] = { "Mars,Jupiter", "Saturn", "Uranus" };
+ MULTICHECK_SPLIT(s, rsplit, ",", true, 2, slices_r, 3);
s = "test";
- l = s.split();
- CHECK(l.size() == 4);
- for (int i = 0; i < l.size(); i++) {
- CHECK(l[i] == slices_3[i]);
- }
+ const char *slices_3[4] = { "t", "e", "s", "t" };
+ MULTICHECK_SPLIT(s, split, "", true, 0, slices_3, 4);
s = "";
- l = s.split();
- CHECK(l.size() == 1);
- CHECK(l[0] == "");
-
- l = s.split("", false);
- CHECK(l.size() == 0);
+ 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" };
- l = s.split_spaces();
+ Vector<String> l = s.split_spaces();
for (int i = 0; i < l.size(); i++) {
CHECK(l[i] == slices_s[i]);
}
@@ -644,69 +699,6 @@ TEST_CASE("[String] Splitting") {
}
}
-struct test_27_data {
- char const *data;
- char const *part;
- bool expected;
-};
-
-TEST_CASE("[String] Begins with") {
- test_27_data tc[] = {
- // Test cases for true:
- { "res://foobar", "res://", true },
- { "abc", "abc", true },
- { "abc", "", true },
- { "", "", true },
- // Test cases for false:
- { "res", "res://", false },
- { "abcdef", "foo", false },
- { "abc", "ax", false },
- { "", "abc", false }
- };
- size_t count = sizeof(tc) / sizeof(tc[0]);
- bool state = true;
- for (size_t i = 0; i < count; ++i) {
- String s = tc[i].data;
- state = s.begins_with(tc[i].part) == tc[i].expected;
- CHECK_MESSAGE(state, "first check failed at: ", i);
-
- String sb = tc[i].part;
- state = s.begins_with(sb) == tc[i].expected;
- CHECK_MESSAGE(state, "second check failed at: ", i);
- }
-
- // Test "const char *" version also with nullptr.
- String s("foo");
- state = s.begins_with(nullptr) == false;
- CHECK_MESSAGE(state, "nullptr check failed");
-
- String empty("");
- state = empty.begins_with(nullptr) == false;
- CHECK_MESSAGE(state, "nullptr check with empty string failed");
-}
-
-TEST_CASE("[String] Ends with") {
- test_27_data tc[] = {
- // test cases for true:
- { "res://foobar", "foobar", true },
- { "abc", "abc", true },
- { "abc", "", true },
- { "", "", true },
- // test cases for false:
- { "res", "res://", false },
- { "", "abc", false },
- { "abcdef", "foo", false },
- { "abc", "xc", false }
- };
- size_t count = sizeof(tc) / sizeof(tc[0]);
- for (size_t i = 0; i < count; ++i) {
- String s = tc[i].data;
- String sb = tc[i].part;
- bool state = s.ends_with(sb) == tc[i].expected;
- CHECK_MESSAGE(state, "check failed at: ", i);
- }
-}
-
TEST_CASE("[String] format") {
const String value_format = "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\"";
@@ -1498,39 +1490,62 @@ TEST_CASE("[String] Cyrillic to_lower()") {
}
TEST_CASE("[String] Count and countn functionality") {
-#define COUNT_TEST(x) \
- { \
- bool success = x; \
- state = state && success; \
- }
+ String s = String("");
+ MULTICHECK_STRING_EQ(s, count, "Test", 0);
- bool state = true;
+ s = "Test";
+ MULTICHECK_STRING_EQ(s, count, "", 0);
- COUNT_TEST(String("").count("Test") == 0);
- COUNT_TEST(String("Test").count("") == 0);
- COUNT_TEST(String("Test").count("test") == 0);
- COUNT_TEST(String("Test").count("TEST") == 0);
- COUNT_TEST(String("TEST").count("TEST") == 1);
- COUNT_TEST(String("Test").count("Test") == 1);
- COUNT_TEST(String("aTest").count("Test") == 1);
- COUNT_TEST(String("Testa").count("Test") == 1);
- COUNT_TEST(String("TestTestTest").count("Test") == 3);
- COUNT_TEST(String("TestTestTest").count("TestTest") == 1);
- COUNT_TEST(String("TestGodotTestGodotTestGodot").count("Test") == 3);
-
- COUNT_TEST(String("TestTestTestTest").count("Test", 4, 8) == 1);
- COUNT_TEST(String("TestTestTestTest").count("Test", 4, 12) == 2);
- COUNT_TEST(String("TestTestTestTest").count("Test", 4, 16) == 3);
- COUNT_TEST(String("TestTestTestTest").count("Test", 4) == 3);
-
- COUNT_TEST(String("Test").countn("test") == 1);
- COUNT_TEST(String("Test").countn("TEST") == 1);
- COUNT_TEST(String("testTest-Testatest").countn("tEst") == 4);
- COUNT_TEST(String("testTest-TeStatest").countn("tEsT", 4, 16) == 2);
+ s = "Test";
+ MULTICHECK_STRING_EQ(s, count, "test", 0);
- CHECK(state);
+ s = "Test";
+ MULTICHECK_STRING_EQ(s, count, "TEST", 0);
+
+ s = "TEST";
+ MULTICHECK_STRING_EQ(s, count, "TEST", 1);
+
+ s = "Test";
+ MULTICHECK_STRING_EQ(s, count, "Test", 1);
+
+ s = "aTest";
+ MULTICHECK_STRING_EQ(s, count, "Test", 1);
+
+ s = "Testa";
+ MULTICHECK_STRING_EQ(s, count, "Test", 1);
+
+ s = "TestTestTest";
+ MULTICHECK_STRING_EQ(s, count, "Test", 3);
+
+ s = "TestTestTest";
+ MULTICHECK_STRING_EQ(s, count, "TestTest", 1);
+
+ s = "TestGodotTestGodotTestGodot";
+ MULTICHECK_STRING_EQ(s, count, "Test", 3);
+
+ s = "TestTestTestTest";
+ MULTICHECK_STRING_INT_INT_EQ(s, count, "Test", 4, 8, 1);
+
+ s = "TestTestTestTest";
+ MULTICHECK_STRING_INT_INT_EQ(s, count, "Test", 4, 12, 2);
+
+ s = "TestTestTestTest";
+ MULTICHECK_STRING_INT_INT_EQ(s, count, "Test", 4, 16, 3);
+
+ s = "TestTestTestTest";
+ MULTICHECK_STRING_INT_EQ(s, count, "Test", 4, 3);
+
+ s = "Test";
+ MULTICHECK_STRING_EQ(s, countn, "test", 1);
+
+ s = "Test";
+ MULTICHECK_STRING_EQ(s, countn, "TEST", 1);
+
+ s = "testTest-Testatest";
+ MULTICHECK_STRING_EQ(s, countn, "tEst", 4);
-#undef COUNT_TEST
+ s = "testTest-TeStatest";
+ MULTICHECK_STRING_INT_INT_EQ(s, countn, "tEsT", 4, 16, 2);
}
TEST_CASE("[String] Bigrams") {
@@ -1703,9 +1718,19 @@ TEST_CASE("[String] Strip edges") {
TEST_CASE("[String] Trim") {
String s = "aaaTestbbb";
- CHECK(s.trim_prefix("aaa") == "Testbbb");
- CHECK(s.trim_suffix("bbb") == "aaaTest");
- CHECK(s.trim_suffix("Test") == s);
+ MULTICHECK_STRING_EQ(s, trim_prefix, "aaa", "Testbbb");
+ MULTICHECK_STRING_EQ(s, trim_prefix, "Test", s);
+ MULTICHECK_STRING_EQ(s, trim_prefix, "", s);
+ MULTICHECK_STRING_EQ(s, trim_prefix, "aaaTestbbb", "");
+ MULTICHECK_STRING_EQ(s, trim_prefix, "bbb", s);
+ MULTICHECK_STRING_EQ(s, trim_prefix, "AAA", s);
+
+ MULTICHECK_STRING_EQ(s, trim_suffix, "bbb", "aaaTest");
+ MULTICHECK_STRING_EQ(s, trim_suffix, "Test", s);
+ MULTICHECK_STRING_EQ(s, trim_suffix, "", s);
+ MULTICHECK_STRING_EQ(s, trim_suffix, "aaaTestbbb", "");
+ MULTICHECK_STRING_EQ(s, trim_suffix, "aaa", s);
+ MULTICHECK_STRING_EQ(s, trim_suffix, "BBB", s);
}
TEST_CASE("[String] Right/Left") {
diff --git a/tests/create_test.py b/tests/create_test.py
index deb53aca20..deb53aca20 100644..100755
--- a/tests/create_test.py
+++ b/tests/create_test.py
diff --git a/tests/display_server_mock.h b/tests/display_server_mock.h
index fd79a46c5c..e4946995a7 100644
--- a/tests/display_server_mock.h
+++ b/tests/display_server_mock.h
@@ -56,7 +56,7 @@ private:
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) {
+ 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, Context p_context, Error &r_error) {
r_error = OK;
RasterizerDummy::make_current();
return memnew(DisplayServerMock());
diff --git a/tests/python_build/fixtures/gles3/vertex_fragment_expected_full.glsl b/tests/python_build/fixtures/gles3/vertex_fragment_expected_full.glsl
index 58ddd08cdd..8ad5a23eb5 100644
--- a/tests/python_build/fixtures/gles3/vertex_fragment_expected_full.glsl
+++ b/tests/python_build/fixtures/gles3/vertex_fragment_expected_full.glsl
@@ -48,4 +48,3 @@ protected:
};
#endif
-
diff --git a/tests/python_build/test_gles3_builder.py b/tests/python_build/test_gles3_builder.py
index 6f16139eb9..b34d33bde7 100644
--- a/tests/python_build/test_gles3_builder.py
+++ b/tests/python_build/test_gles3_builder.py
@@ -2,7 +2,7 @@ import json
import pytest
-from gles3_builders import build_gles3_header, GLES3HeaderStruct
+from gles3_builders import GLES3HeaderStruct, build_gles3_header
@pytest.mark.parametrize(
diff --git a/tests/python_build/test_glsl_builder.py b/tests/python_build/test_glsl_builder.py
index 348ef8441c..9f548855ff 100644
--- a/tests/python_build/test_glsl_builder.py
+++ b/tests/python_build/test_glsl_builder.py
@@ -2,7 +2,7 @@ import json
import pytest
-from glsl_builders import build_raw_header, RAWHeaderStruct, build_rd_header, RDHeaderStruct
+from glsl_builders import RAWHeaderStruct, RDHeaderStruct, build_raw_header, build_rd_header
@pytest.mark.parametrize(
diff --git a/tests/scene/test_audio_stream_wav.h b/tests/scene/test_audio_stream_wav.h
index ed1697929e..e8f3c9e8f5 100644
--- a/tests/scene/test_audio_stream_wav.h
+++ b/tests/scene/test_audio_stream_wav.h
@@ -115,7 +115,7 @@ Vector<uint8_t> gen_pcm16_test(float wav_rate, int wav_count, bool stereo) {
}
void run_test(String file_name, AudioStreamWAV::Format data_format, bool stereo, float wav_rate, float wav_count) {
- String save_path = OS::get_singleton()->get_cache_path().path_join(file_name);
+ String save_path = TestUtils::get_temp_path(file_name);
Vector<uint8_t> test_data;
if (data_format == AudioStreamWAV::FORMAT_8_BITS) {
@@ -200,7 +200,7 @@ TEST_CASE("[AudioStreamWAV] Alternate mix rate") {
}
TEST_CASE("[AudioStreamWAV] save_to_wav() adds '.wav' file extension automatically") {
- String save_path = OS::get_singleton()->get_cache_path().path_join("test_wav_extension");
+ String save_path = TestUtils::get_temp_path("test_wav_extension");
Vector<uint8_t> test_data = gen_pcm8_test(WAV_RATE, WAV_COUNT, false);
Ref<AudioStreamWAV> stream = memnew(AudioStreamWAV);
stream->set_data(test_data);
@@ -230,7 +230,7 @@ TEST_CASE("[AudioStreamWAV] Save empty file") {
}
TEST_CASE("[AudioStreamWAV] Saving IMA ADPCM is not supported") {
- String save_path = OS::get_singleton()->get_cache_path().path_join("test_adpcm.wav");
+ String save_path = TestUtils::get_temp_path("test_adpcm.wav");
Ref<AudioStreamWAV> stream = memnew(AudioStreamWAV);
stream->set_format(AudioStreamWAV::FORMAT_IMA_ADPCM);
ERR_PRINT_OFF;
diff --git a/tests/scene/test_code_edit.h b/tests/scene/test_code_edit.h
index c02830b6df..317dbe9ab9 100644
--- a/tests/scene/test_code_edit.h
+++ b/tests/scene/test_code_edit.h
@@ -3331,6 +3331,45 @@ TEST_CASE("[SceneTree][CodeEdit] folding") {
CHECK_FALSE(code_edit->is_line_folded(1));
}
+ SUBCASE("[CodeEdit] actions unfold") {
+ // add_selection_for_next_occurrence unfolds.
+ code_edit->set_text("test\n\tline1 test\n\t\tline 2\ntest2");
+ code_edit->select(0, 0, 0, 4);
+ code_edit->fold_line(0);
+ CHECK(code_edit->is_line_folded(0));
+ code_edit->add_selection_for_next_occurrence();
+
+ CHECK(code_edit->get_caret_count() == 2);
+ CHECK(code_edit->has_selection(0));
+ CHECK(code_edit->get_caret_line() == 0);
+ CHECK(code_edit->get_selection_origin_line() == 0);
+ CHECK(code_edit->get_caret_column() == 4);
+ CHECK(code_edit->get_selection_origin_column() == 0);
+ CHECK(code_edit->has_selection(1));
+ CHECK(code_edit->get_caret_line(1) == 1);
+ CHECK(code_edit->get_selection_origin_line(1) == 1);
+ CHECK(code_edit->get_caret_column(1) == 11);
+ CHECK(code_edit->get_selection_origin_column(1) == 7);
+ CHECK_FALSE(code_edit->is_line_folded(0));
+ code_edit->remove_secondary_carets();
+
+ // skip_selection_for_next_occurrence unfolds.
+ code_edit->select(0, 0, 0, 4);
+ code_edit->fold_line(0);
+ CHECK(code_edit->is_line_folded(0));
+ code_edit->skip_selection_for_next_occurrence();
+
+ CHECK(code_edit->get_caret_count() == 1);
+ CHECK(code_edit->has_selection(0));
+ CHECK(code_edit->get_caret_line() == 1);
+ CHECK(code_edit->get_selection_origin_line() == 1);
+ CHECK(code_edit->get_caret_column() == 11);
+ CHECK(code_edit->get_selection_origin_column() == 7);
+ CHECK_FALSE(code_edit->is_line_folded(0));
+ code_edit->remove_secondary_carets();
+ code_edit->deselect();
+ }
+
SUBCASE("[CodeEdit] toggle folding carets") {
code_edit->set_text("test\n\tline1\ntest2\n\tline2");
diff --git a/tests/scene/test_curve_2d.h b/tests/scene/test_curve_2d.h
index 099f6fefa9..1248632630 100644
--- a/tests/scene/test_curve_2d.h
+++ b/tests/scene/test_curve_2d.h
@@ -147,13 +147,19 @@ TEST_CASE("[Curve2D] Sampling") {
CHECK(curve->samplef(1) == Vector2(0, 50));
}
- SUBCASE("sample_baked") {
+ SUBCASE("sample_baked, cubic = false") {
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") {
+ SUBCASE("sample_baked, cubic = true") {
+ CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 0)), true) == Vector2(0, 0));
+ CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 25)), true) == Vector2(0, 25));
+ CHECK(curve->sample_baked(curve->get_closest_offset(Vector2(0, 50)), true) == Vector2(0, 50));
+ }
+
+ SUBCASE("sample_baked_with_rotation, cubic = false") {
const real_t pi = 3.14159;
const real_t half_pi = pi * 0.5;
Ref<Curve2D> rot_curve = memnew(Curve2D);
@@ -188,6 +194,41 @@ TEST_CASE("[Curve2D] Sampling") {
CHECK(Math::is_equal_approx(t.get_rotation(), -half_pi));
}
+ SUBCASE("sample_baked_with_rotation, cubic = true") {
+ const real_t pi = 3.14159;
+ const real_t half_pi = pi * 0.5;
+ Ref<Curve2D> rot_curve = memnew(Curve2D);
+ Transform2D t;
+
+ rot_curve->clear_points();
+ rot_curve->add_point(Vector2());
+ rot_curve->add_point(Vector2(50, 0));
+ t = rot_curve->sample_baked_with_rotation(25, true);
+ CHECK(t.get_origin() == Vector2(25, 0));
+ CHECK(Math::is_equal_approx(t.get_rotation(), 0));
+
+ rot_curve->clear_points();
+ rot_curve->add_point(Vector2());
+ rot_curve->add_point(Vector2(0, 50));
+ t = rot_curve->sample_baked_with_rotation(25, true);
+ CHECK(t.get_origin() == Vector2(0, 25));
+ CHECK(Math::is_equal_approx(t.get_rotation(), half_pi));
+
+ rot_curve->clear_points();
+ rot_curve->add_point(Vector2());
+ rot_curve->add_point(Vector2(-50, 0));
+ t = rot_curve->sample_baked_with_rotation(25, true);
+ CHECK(t.get_origin() == Vector2(-25, 0));
+ CHECK(Math::is_equal_approx(t.get_rotation(), pi));
+
+ rot_curve->clear_points();
+ rot_curve->add_point(Vector2());
+ rot_curve->add_point(Vector2(0, -50));
+ t = rot_curve->sample_baked_with_rotation(25, true);
+ CHECK(t.get_origin() == Vector2(0, -25));
+ CHECK(Math::is_equal_approx(t.get_rotation(), -half_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));
diff --git a/tests/scene/test_curve_3d.h b/tests/scene/test_curve_3d.h
index d73bb1ad35..2e60a9c6e6 100644
--- a/tests/scene/test_curve_3d.h
+++ b/tests/scene/test_curve_3d.h
@@ -177,12 +177,30 @@ TEST_CASE("[Curve3D] Sampling") {
CHECK(curve->sample_baked(curve->get_closest_offset(Vector3(0, 50, 0)), true) == Vector3(0, 50, 0));
}
- SUBCASE("sample_baked_with_rotation") {
+ SUBCASE("sample_baked_with_rotation, cubic = false, p_apply_tilt = false") {
CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 0, 0))) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 0, 0)));
CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 25, 0))) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 25, 0)));
CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 50, 0))) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 50, 0)));
}
+ SUBCASE("sample_baked_with_rotation, cubic = false, p_apply_tilt = true") {
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 0, 0)), false, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 0, 0)));
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 25, 0)), false, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 25, 0)));
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 50, 0)), false, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 50, 0)));
+ }
+
+ SUBCASE("sample_baked_with_rotation, cubic = true, p_apply_tilt = false") {
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 0, 0)), true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 0, 0)));
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 25, 0)), true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 25, 0)));
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 50, 0)), true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 50, 0)));
+ }
+
+ SUBCASE("sample_baked_with_rotation, cubic = true, p_apply_tilt = true") {
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 0, 0)), true, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 0, 0)));
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 25, 0)), true, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 25, 0)));
+ CHECK(curve->sample_baked_with_rotation(curve->get_closest_offset(Vector3(0, 50, 0)), true, true) == Transform3D(Basis(Vector3(0, 0, -1), Vector3(1, 0, 0), Vector3(0, -1, 0)), Vector3(0, 50, 0)));
+ }
+
SUBCASE("sample_baked_tilt") {
CHECK(curve->sample_baked_tilt(curve->get_closest_offset(Vector3(0, 0, 0))) == 0);
CHECK(curve->sample_baked_tilt(curve->get_closest_offset(Vector3(0, 25, 0))) == 0);
diff --git a/tests/scene/test_image_texture_3d.h b/tests/scene/test_image_texture_3d.h
new file mode 100644
index 0000000000..f2a7abcf69
--- /dev/null
+++ b/tests/scene/test_image_texture_3d.h
@@ -0,0 +1,101 @@
+/**************************************************************************/
+/* test_image_texture_3d.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_IMAGE_TEXTURE_3D_H
+#define TEST_IMAGE_TEXTURE_3D_H
+
+#include "core/io/image.h"
+#include "scene/resources/image_texture.h"
+
+#include "tests/test_macros.h"
+#include "tests/test_utils.h"
+
+namespace TestImageTexture3D {
+
+// [SceneTree] in a test case name enables initializing a mock render server,
+// which ImageTexture3D is dependent on.
+TEST_CASE("[SceneTree][ImageTexture3D] Constructor") {
+ Ref<ImageTexture3D> image_texture_3d = memnew(ImageTexture3D);
+ CHECK(image_texture_3d->get_format() == Image::FORMAT_L8);
+ CHECK(image_texture_3d->get_width() == 1);
+ CHECK(image_texture_3d->get_height() == 1);
+ CHECK(image_texture_3d->get_depth() == 1);
+ CHECK(image_texture_3d->has_mipmaps() == false);
+}
+
+TEST_CASE("[SceneTree][ImageTexture3D] get_format") {
+ Ref<ImageTexture3D> image_texture_3d = memnew(ImageTexture3D);
+ CHECK(image_texture_3d->get_format() == Image::FORMAT_L8);
+}
+
+TEST_CASE("[SceneTree][ImageTexture3D] get_width") {
+ Ref<ImageTexture3D> image_texture_3d = memnew(ImageTexture3D);
+ CHECK(image_texture_3d->get_width() == 1);
+}
+
+TEST_CASE("[SceneTree][ImageTexture3D] get_height") {
+ Ref<ImageTexture3D> image_texture_3d = memnew(ImageTexture3D);
+ CHECK(image_texture_3d->get_height() == 1);
+}
+
+TEST_CASE("[SceneTree][ImageTexture3D] get_depth") {
+ Ref<ImageTexture3D> image_texture_3d = memnew(ImageTexture3D);
+ CHECK(image_texture_3d->get_depth() == 1);
+}
+
+TEST_CASE("[SceneTree][ImageTexture3D] has_mipmaps") {
+ const Vector<Ref<Image>> images = { memnew(Image(8, 8, false, Image::FORMAT_RGBA8)), memnew(Image(8, 8, false, Image::FORMAT_RGBA8)) };
+ Ref<ImageTexture3D> image_texture_3d = memnew(ImageTexture3D);
+ CHECK(image_texture_3d->has_mipmaps() == false); // No mipmaps.
+ image_texture_3d->create(Image::FORMAT_RGBA8, 2, 2, 2, true, images);
+ CHECK(image_texture_3d->has_mipmaps() == true); // Mipmaps.
+}
+
+TEST_CASE("[SceneTree][ImageTexture3D] create") {
+ const Vector<Ref<Image>> images = { memnew(Image(8, 8, false, Image::FORMAT_RGBA8)), memnew(Image(8, 8, false, Image::FORMAT_RGBA8)) };
+ Ref<ImageTexture3D> image_texture_3d = memnew(ImageTexture3D);
+ CHECK(image_texture_3d->create(Image::FORMAT_RGBA8, 2, 2, 2, true, images) == OK); // Run create and check return value simultaneously.
+ CHECK(image_texture_3d->get_format() == Image::FORMAT_RGBA8);
+ CHECK(image_texture_3d->get_width() == 2);
+ CHECK(image_texture_3d->get_height() == 2);
+ CHECK(image_texture_3d->get_depth() == 2);
+ CHECK(image_texture_3d->has_mipmaps() == true);
+}
+
+TEST_CASE("[SceneTree][ImageTexture3D] set_path") {
+ Ref<ImageTexture3D> image_texture_3d = memnew(ImageTexture3D);
+ String path = TestUtils::get_data_path("images/icon.png");
+ image_texture_3d->set_path(path, true);
+ CHECK(image_texture_3d->get_path() == path);
+}
+
+} //namespace TestImageTexture3D
+
+#endif // TEST_IMAGE_TEXTURE_3D_H
diff --git a/tests/scene/test_instance_placeholder.h b/tests/scene/test_instance_placeholder.h
new file mode 100644
index 0000000000..d915c5d961
--- /dev/null
+++ b/tests/scene/test_instance_placeholder.h
@@ -0,0 +1,532 @@
+/**************************************************************************/
+/* test_instance_placeholder.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_INSTANCE_PLACEHOLDER_H
+#define TEST_INSTANCE_PLACEHOLDER_H
+
+#include "scene/main/instance_placeholder.h"
+#include "scene/resources/packed_scene.h"
+
+#include "tests/test_macros.h"
+
+class _TestInstancePlaceholderNode : public Node {
+ GDCLASS(_TestInstancePlaceholderNode, Node);
+
+protected:
+ static void _bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_int_property", "int_property"), &_TestInstancePlaceholderNode::set_int_property);
+ ClassDB::bind_method(D_METHOD("get_int_property"), &_TestInstancePlaceholderNode::get_int_property);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "int_property"), "set_int_property", "get_int_property");
+
+ ClassDB::bind_method(D_METHOD("set_reference_property", "reference_property"), &_TestInstancePlaceholderNode::set_reference_property);
+ ClassDB::bind_method(D_METHOD("get_reference_property"), &_TestInstancePlaceholderNode::get_reference_property);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "reference_property", PROPERTY_HINT_NODE_TYPE), "set_reference_property", "get_reference_property");
+
+ ClassDB::bind_method(D_METHOD("set_reference_array_property", "reference_array_property"), &_TestInstancePlaceholderNode::set_reference_array_property);
+ ClassDB::bind_method(D_METHOD("get_reference_array_property"), &_TestInstancePlaceholderNode::get_reference_array_property);
+
+ // The hint string value "24/34:Node" is determined from existing PackedScenes with typed Array properties.
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "reference_array_property", PROPERTY_HINT_TYPE_STRING, "24/34:Node"), "set_reference_array_property", "get_reference_array_property");
+ }
+
+public:
+ int int_property = 0;
+
+ void set_int_property(int p_int) {
+ int_property = p_int;
+ }
+
+ int get_int_property() const {
+ return int_property;
+ }
+
+ Variant reference_property;
+
+ void set_reference_property(const Variant &p_node) {
+ reference_property = p_node;
+ }
+
+ Variant get_reference_property() const {
+ return reference_property;
+ }
+
+ Array reference_array_property;
+
+ void set_reference_array_property(const Array &p_array) {
+ reference_array_property = p_array;
+ }
+
+ Array get_reference_array_property() const {
+ return reference_array_property;
+ }
+
+ _TestInstancePlaceholderNode() {
+ reference_array_property.set_typed(Variant::OBJECT, "Node", Variant());
+ }
+};
+
+namespace TestInstancePlaceholder {
+
+TEST_CASE("[SceneTree][InstancePlaceholder] Instantiate from placeholder with no overrides") {
+ GDREGISTER_CLASS(_TestInstancePlaceholderNode);
+
+ SUBCASE("with non-node values") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ ip->set_name("TestScene");
+ Node *root = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ root->add_child(ip);
+ // Create a scene to instance.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ scene->set_int_property(12);
+
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ const Error err = packed_scene->pack(scene);
+ REQUIRE(err == OK);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_name() == "TestScene");
+ CHECK(created->get_int_property() == 12);
+
+ root->queue_free();
+ memdelete(scene);
+ }
+
+ SUBCASE("with node value") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ ip->set_name("TestScene");
+ Node *root = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ root->add_child(ip);
+ // Create a scene to instance.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ Node *referenced = memnew(Node);
+ scene->add_child(referenced);
+ referenced->set_owner(scene);
+ scene->set_reference_property(referenced);
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ const Error err = packed_scene->pack(scene);
+ REQUIRE(err == OK);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_name() == "TestScene");
+ CHECK(created->get_child_count() == 1);
+ CHECK(created->get_reference_property().identity_compare(created->get_child(0, false)));
+ CHECK_FALSE(created->get_reference_property().identity_compare(referenced));
+
+ root->queue_free();
+ memdelete(scene);
+ }
+
+ SUBCASE("with node-array value") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ ip->set_name("TestScene");
+ Node *root = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ root->add_child(ip);
+ // Create a scene to instance.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ Node *referenced1 = memnew(Node);
+ Node *referenced2 = memnew(Node);
+ scene->add_child(referenced1);
+ scene->add_child(referenced2);
+ referenced1->set_owner(scene);
+ referenced2->set_owner(scene);
+ Array node_array;
+ node_array.set_typed(Variant::OBJECT, "Node", Variant());
+ node_array.push_back(referenced1);
+ node_array.push_back(referenced2);
+ scene->set_reference_array_property(node_array);
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ const Error err = packed_scene->pack(scene);
+ REQUIRE(err == OK);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_name() == "TestScene");
+ CHECK(created->get_child_count() == 2);
+ Array created_array = created->get_reference_array_property();
+ REQUIRE(created_array.size() == node_array.size());
+ REQUIRE(created_array.size() == created->get_child_count());
+
+ // Iterate over all nodes, since the ordering is not guaranteed.
+ for (int i = 0; i < node_array.size(); i++) {
+ bool node_found = false;
+ for (int j = 0; j < created->get_child_count(); j++) {
+ if (created_array[i].identity_compare(created->get_child(j, true))) {
+ node_found = true;
+ }
+ }
+ CHECK(node_found);
+ }
+ root->queue_free();
+ memdelete(scene);
+ }
+}
+
+TEST_CASE("[SceneTree][InstancePlaceholder] Instantiate from placeholder with overrides") {
+ GDREGISTER_CLASS(_TestInstancePlaceholderNode);
+
+ SUBCASE("with non-node values") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ Node *root = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ root->add_child(ip);
+ ip->set_name("TestScene");
+ ip->set("int_property", 45);
+ // Create a scene to pack.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ scene->set_int_property(12);
+
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ packed_scene->pack(scene);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_int_property() == 45);
+
+ root->queue_free();
+ memdelete(scene);
+ }
+
+ SUBCASE("with node values") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ ip->set_name("TestScene");
+ Node *root = memnew(Node);
+ Node *overriding = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ root->add_child(ip);
+ root->add_child(overriding);
+ ip->set("reference_property", overriding);
+ // Create a scene to instance.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ Node *referenced = memnew(Node);
+ scene->add_child(referenced);
+ referenced->set_owner(scene);
+ scene->set_reference_property(referenced);
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ const Error err = packed_scene->pack(scene);
+ REQUIRE(err == OK);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_name() == "TestScene");
+ CHECK(created->get_child_count() == 1);
+ CHECK(created->get_reference_property().identity_compare(overriding));
+ CHECK_FALSE(created->get_reference_property().identity_compare(referenced));
+
+ root->queue_free();
+ memdelete(scene);
+ }
+
+ SUBCASE("with node-array value") {
+ InstancePlaceholder *ip = memnew(InstancePlaceholder);
+ ip->set_name("TestScene");
+ Node *root = memnew(Node);
+ SceneTree::get_singleton()->get_root()->add_child(root);
+
+ Node *override1 = memnew(Node);
+ Node *override2 = memnew(Node);
+ Node *override3 = memnew(Node);
+ root->add_child(ip);
+ root->add_child(override1);
+ root->add_child(override2);
+ root->add_child(override3);
+
+ Array override_node_array;
+ override_node_array.set_typed(Variant::OBJECT, "Node", Variant());
+ override_node_array.push_back(override1);
+ override_node_array.push_back(override2);
+ override_node_array.push_back(override3);
+
+ ip->set("reference_array_property", override_node_array);
+
+ // Create a scene to instance.
+ _TestInstancePlaceholderNode *scene = memnew(_TestInstancePlaceholderNode);
+ Node *referenced1 = memnew(Node);
+ Node *referenced2 = memnew(Node);
+
+ scene->add_child(referenced1);
+ scene->add_child(referenced2);
+
+ referenced1->set_owner(scene);
+ referenced2->set_owner(scene);
+ Array referenced_array;
+ referenced_array.set_typed(Variant::OBJECT, "Node", Variant());
+ referenced_array.push_back(referenced1);
+ referenced_array.push_back(referenced2);
+
+ scene->set_reference_array_property(referenced_array);
+ // Pack the scene.
+ PackedScene *packed_scene = memnew(PackedScene);
+ const Error err = packed_scene->pack(scene);
+ REQUIRE(err == OK);
+
+ // Instantiate the scene.
+ _TestInstancePlaceholderNode *created = Object::cast_to<_TestInstancePlaceholderNode>(ip->create_instance(true, packed_scene));
+ REQUIRE(created != nullptr);
+ CHECK(created->get_name() == "TestScene");
+ CHECK(created->get_child_count() == 2);
+ Array created_array = created->get_reference_array_property();
+ REQUIRE_FALSE(created_array.size() == referenced_array.size());
+ REQUIRE(created_array.size() == override_node_array.size());
+ REQUIRE_FALSE(created_array.size() == created->get_child_count());
+
+ // Iterate over all nodes, since the ordering is not guaranteed.
+ for (int i = 0; i < override_node_array.size(); i++) {
+ bool node_found = false;
+ for (int j = 0; j < created_array.size(); j++) {
+ if (override_node_array[i].identity_compare(created_array[j])) {
+ node_found = true;
+ }
+ }
+ CHECK(node_found);
+ }
+ root->queue_free();
+ memdelete(scene);
+ }
+}
+
+TEST_CASE("[SceneTree][InstancePlaceholder] Instance a PackedScene containing an InstancePlaceholder with no overrides") {
+ GDREGISTER_CLASS(_TestInstancePlaceholderNode);
+
+ // Create the internal scene.
+ _TestInstancePlaceholderNode *internal = memnew(_TestInstancePlaceholderNode);
+ internal->set_name("InternalNode");
+ Node *referenced = memnew(Node);
+ referenced->set_name("OriginalReference");
+ internal->add_child(referenced);
+ referenced->set_owner(internal);
+ internal->set_reference_property(referenced);
+
+ // Pack the internal scene.
+ PackedScene *internal_scene = memnew(PackedScene);
+ Error err = internal_scene->pack(internal);
+ REQUIRE(err == OK);
+
+ const String internal_path = TestUtils::get_temp_path("instance_placeholder_test_internal.tscn");
+ err = ResourceSaver::save(internal_scene, internal_path);
+ REQUIRE(err == OK);
+
+ Ref<PackedScene> internal_scene_loaded = ResourceLoader::load(internal_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);
+ REQUIRE(err == OK);
+
+ // Create the main scene.
+ Node *root = memnew(Node);
+ root->set_name("MainNode");
+ Node *overriding = memnew(Node);
+ overriding->set_name("OverridingReference");
+
+ _TestInstancePlaceholderNode *internal_created = Object::cast_to<_TestInstancePlaceholderNode>(internal_scene_loaded->instantiate(PackedScene::GEN_EDIT_STATE_MAIN_INHERITED));
+ internal_created->set_scene_instance_load_placeholder(true);
+ root->add_child(internal_created);
+ internal_created->set_owner(root);
+
+ root->add_child(overriding);
+ overriding->set_owner(root);
+ // Here we introduce an error, we override the property with an internal node to the instance placeholder.
+ // The InstancePlaceholder is now forced to properly resolve the Node.
+ internal_created->set("reference_property", NodePath("OriginalReference"));
+
+ // Pack the main scene.
+ PackedScene *main_scene = memnew(PackedScene);
+ err = main_scene->pack(root);
+ REQUIRE(err == OK);
+
+ const String main_path = TestUtils::get_temp_path("instance_placeholder_test_main.tscn");
+ err = ResourceSaver::save(main_scene, main_path);
+ REQUIRE(err == OK);
+
+ // // Instantiate the scene.
+ Ref<PackedScene> main_scene_loaded = ResourceLoader::load(main_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);
+ REQUIRE(err == OK);
+
+ Node *instanced_main_node = main_scene_loaded->instantiate();
+ REQUIRE(instanced_main_node != nullptr);
+ SceneTree::get_singleton()->get_root()->add_child(instanced_main_node);
+ CHECK(instanced_main_node->get_name() == "MainNode");
+ REQUIRE(instanced_main_node->get_child_count() == 2);
+ InstancePlaceholder *instanced_placeholder = Object::cast_to<InstancePlaceholder>(instanced_main_node->get_child(0, true));
+ REQUIRE(instanced_placeholder != nullptr);
+
+ _TestInstancePlaceholderNode *final_node = Object::cast_to<_TestInstancePlaceholderNode>(instanced_placeholder->create_instance(true));
+ REQUIRE(final_node != nullptr);
+ REQUIRE(final_node->get_child_count() == 1);
+ REQUIRE(final_node->get_reference_property().identity_compare(final_node->get_child(0, true)));
+
+ instanced_main_node->queue_free();
+ memdelete(overriding);
+ memdelete(root);
+ memdelete(internal);
+ DirAccess::remove_file_or_error(internal_path);
+ DirAccess::remove_file_or_error(main_path);
+}
+
+TEST_CASE("[SceneTree][InstancePlaceholder] Instance a PackedScene containing an InstancePlaceholder with overrides") {
+ GDREGISTER_CLASS(_TestInstancePlaceholderNode);
+
+ // Create the internal scene.
+ _TestInstancePlaceholderNode *internal = memnew(_TestInstancePlaceholderNode);
+ internal->set_name("InternalNode");
+ Node *referenced = memnew(Node);
+ referenced->set_name("OriginalReference");
+ internal->add_child(referenced);
+ referenced->set_owner(internal);
+ internal->set_reference_property(referenced);
+
+ Node *array_ref1 = memnew(Node);
+ array_ref1->set_name("ArrayRef1");
+ internal->add_child(array_ref1);
+ array_ref1->set_owner(internal);
+ Node *array_ref2 = memnew(Node);
+ array_ref2->set_name("ArrayRef2");
+ internal->add_child(array_ref2);
+ array_ref2->set_owner(internal);
+ Array referenced_array;
+ referenced_array.set_typed(Variant::OBJECT, "Node", Variant());
+ referenced_array.push_back(array_ref1);
+ referenced_array.push_back(array_ref2);
+ internal->set_reference_array_property(referenced_array);
+
+ // Pack the internal scene.
+ PackedScene *internal_scene = memnew(PackedScene);
+ Error err = internal_scene->pack(internal);
+ REQUIRE(err == OK);
+
+ const String internal_path = TestUtils::get_temp_path("instance_placeholder_test_internal_override.tscn");
+ err = ResourceSaver::save(internal_scene, internal_path);
+ REQUIRE(err == OK);
+
+ Ref<PackedScene> internal_scene_loaded = ResourceLoader::load(internal_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);
+ REQUIRE(err == OK);
+
+ // Create the main scene.
+ Node *root = memnew(Node);
+ root->set_name("MainNode");
+ Node *overriding = memnew(Node);
+ overriding->set_name("OverridingReference");
+ Node *array_ext = memnew(Node);
+ array_ext->set_name("ExternalArrayMember");
+
+ _TestInstancePlaceholderNode *internal_created = Object::cast_to<_TestInstancePlaceholderNode>(internal_scene_loaded->instantiate(PackedScene::GEN_EDIT_STATE_MAIN_INHERITED));
+ internal_created->set_scene_instance_load_placeholder(true);
+ root->add_child(internal_created);
+ internal_created->set_owner(root);
+
+ root->add_child(overriding);
+ overriding->set_owner(root);
+ root->add_child(array_ext);
+ array_ext->set_owner(root);
+ // Here we introduce an error, we override the property with an internal node to the instance placeholder.
+ // The InstancePlaceholder is now forced to properly resolve the Node.
+ internal_created->set_reference_property(overriding);
+ Array internal_array = internal_created->get_reference_array_property();
+ Array override_array;
+ override_array.set_typed(Variant::OBJECT, "Node", Variant());
+ for (int i = 0; i < internal_array.size(); i++) {
+ override_array.push_back(internal_array[i]);
+ }
+ override_array.push_back(array_ext);
+ internal_created->set_reference_array_property(override_array);
+
+ // Pack the main scene.
+ PackedScene *main_scene = memnew(PackedScene);
+ err = main_scene->pack(root);
+ REQUIRE(err == OK);
+
+ const String main_path = TestUtils::get_temp_path("instance_placeholder_test_main_override.tscn");
+ err = ResourceSaver::save(main_scene, main_path);
+ REQUIRE(err == OK);
+
+ // // Instantiate the scene.
+ Ref<PackedScene> main_scene_loaded = ResourceLoader::load(main_path, "PackedScene", ResourceFormatLoader::CacheMode::CACHE_MODE_IGNORE, &err);
+ REQUIRE(err == OK);
+
+ Node *instanced_main_node = main_scene_loaded->instantiate();
+ REQUIRE(instanced_main_node != nullptr);
+ SceneTree::get_singleton()->get_root()->add_child(instanced_main_node);
+ CHECK(instanced_main_node->get_name() == "MainNode");
+ REQUIRE(instanced_main_node->get_child_count() == 3);
+ InstancePlaceholder *instanced_placeholder = Object::cast_to<InstancePlaceholder>(instanced_main_node->get_child(0, true));
+ REQUIRE(instanced_placeholder != nullptr);
+
+ _TestInstancePlaceholderNode *final_node = Object::cast_to<_TestInstancePlaceholderNode>(instanced_placeholder->create_instance(true));
+ REQUIRE(final_node != nullptr);
+ REQUIRE(final_node->get_child_count() == 3);
+ REQUIRE(final_node->get_reference_property().identity_compare(instanced_main_node->get_child(1, true)));
+ Array final_array = final_node->get_reference_array_property();
+ REQUIRE(final_array.size() == 3);
+ Array wanted_node_array;
+ wanted_node_array.push_back(instanced_main_node->get_child(2, true)); // ExternalArrayMember
+ wanted_node_array.push_back(final_node->get_child(1, true)); // ArrayRef1
+ wanted_node_array.push_back(final_node->get_child(2, true)); // ArrayRef2
+
+ // Iterate over all nodes, since the ordering is not guaranteed.
+ for (int i = 0; i < wanted_node_array.size(); i++) {
+ bool node_found = false;
+ for (int j = 0; j < final_array.size(); j++) {
+ if (wanted_node_array[i].identity_compare(final_array[j])) {
+ node_found = true;
+ }
+ }
+ CHECK(node_found);
+ }
+
+ instanced_main_node->queue_free();
+ memdelete(array_ext);
+ memdelete(overriding);
+ memdelete(root);
+ memdelete(internal);
+ DirAccess::remove_file_or_error(internal_path);
+ DirAccess::remove_file_or_error(main_path);
+}
+
+} //namespace TestInstancePlaceholder
+
+#endif // TEST_INSTANCE_PLACEHOLDER_H
diff --git a/tests/scene/test_node.h b/tests/scene/test_node.h
index b3362b02a8..05764d8f29 100644
--- a/tests/scene/test_node.h
+++ b/tests/scene/test_node.h
@@ -31,7 +31,9 @@
#ifndef TEST_NODE_H
#define TEST_NODE_H
+#include "core/object/class_db.h"
#include "scene/main/node.h"
+#include "scene/resources/packed_scene.h"
#include "tests/test_macros.h"
@@ -62,6 +64,16 @@ protected:
}
}
+ static void _bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_exported_node", "node"), &TestNode::set_exported_node);
+ ClassDB::bind_method(D_METHOD("get_exported_node"), &TestNode::get_exported_node);
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "exported_node", PROPERTY_HINT_NODE_TYPE, "Node"), "set_exported_node", "get_exported_node");
+
+ ClassDB::bind_method(D_METHOD("set_exported_nodes", "node"), &TestNode::set_exported_nodes);
+ ClassDB::bind_method(D_METHOD("get_exported_nodes"), &TestNode::get_exported_nodes);
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "exported_nodes", PROPERTY_HINT_TYPE_STRING, "24/34:Node"), "set_exported_nodes", "get_exported_nodes");
+ }
+
private:
void push_self() {
if (callback_list) {
@@ -75,7 +87,16 @@ public:
int process_counter = 0;
int physics_process_counter = 0;
+ Node *exported_node = nullptr;
+ Array exported_nodes;
+
List<Node *> *callback_list = nullptr;
+
+ void set_exported_node(Node *p_node) { exported_node = p_node; }
+ Node *get_exported_node() const { return exported_node; }
+
+ void set_exported_nodes(const Array &p_nodes) { exported_nodes = p_nodes; }
+ Array get_exported_nodes() const { return exported_nodes; }
};
TEST_CASE("[SceneTree][Node] Testing node operations with a very simple scene tree") {
@@ -478,6 +499,113 @@ TEST_CASE("[SceneTree][Node] Testing node operations with a more complex simple
memdelete(node2);
}
+TEST_CASE("[SceneTree][Node]Exported node checks") {
+ TestNode *node = memnew(TestNode);
+ SceneTree::get_singleton()->get_root()->add_child(node);
+
+ Node *child = memnew(Node);
+ child->set_name("Child");
+ node->add_child(child);
+ child->set_owner(node);
+
+ Node *child2 = memnew(Node);
+ child2->set_name("Child2");
+ node->add_child(child2);
+ child2->set_owner(node);
+
+ Array children;
+ children.append(child);
+
+ node->set("exported_node", child);
+ node->set("exported_nodes", children);
+
+ SUBCASE("Property of duplicated node should point to duplicated child") {
+ GDREGISTER_CLASS(TestNode);
+
+ TestNode *dup = Object::cast_to<TestNode>(node->duplicate());
+ Node *new_exported = Object::cast_to<Node>(dup->get("exported_node"));
+ CHECK(new_exported == dup->get_child(0));
+
+ memdelete(dup);
+ }
+
+ SUBCASE("Saving instance with exported nodes should not store the unchanged property") {
+ Ref<PackedScene> ps;
+ ps.instantiate();
+ ps->pack(node);
+
+ String scene_path = TestUtils::get_temp_path("test_scene.tscn");
+ ps->set_path(scene_path);
+
+ Node *root = memnew(Node);
+
+ Node *sub_child = ps->instantiate(PackedScene::GEN_EDIT_STATE_MAIN);
+ root->add_child(sub_child);
+ sub_child->set_owner(root);
+
+ Ref<PackedScene> ps2;
+ ps2.instantiate();
+ ps2->pack(root);
+
+ scene_path = TestUtils::get_temp_path("new_test_scene.tscn");
+ ResourceSaver::save(ps2, scene_path);
+ memdelete(root);
+
+ bool is_wrong = false;
+ Ref<FileAccess> fa = FileAccess::open(scene_path, FileAccess::READ);
+ while (!fa->eof_reached()) {
+ const String line = fa->get_line();
+ if (line.begins_with("exported_node")) {
+ // The property was saved, while it shouldn't.
+ is_wrong = true;
+ break;
+ }
+ }
+ CHECK_FALSE(is_wrong);
+ }
+
+ SUBCASE("Saving instance with exported nodes should store property if changed") {
+ Ref<PackedScene> ps;
+ ps.instantiate();
+ ps->pack(node);
+
+ String scene_path = TestUtils::get_temp_path("test_scene.tscn");
+ ps->set_path(scene_path);
+
+ Node *root = memnew(Node);
+
+ Node *sub_child = ps->instantiate(PackedScene::GEN_EDIT_STATE_MAIN);
+ root->add_child(sub_child);
+ sub_child->set_owner(root);
+
+ sub_child->set("exported_node", sub_child->get_child(1));
+
+ children = Array();
+ children.append(sub_child->get_child(1));
+ sub_child->set("exported_nodes", children);
+
+ Ref<PackedScene> ps2;
+ ps2.instantiate();
+ ps2->pack(root);
+
+ scene_path = TestUtils::get_temp_path("new_test_scene2.tscn");
+ ResourceSaver::save(ps2, scene_path);
+ memdelete(root);
+
+ int stored_properties = 0;
+ Ref<FileAccess> fa = FileAccess::open(scene_path, FileAccess::READ);
+ while (!fa->eof_reached()) {
+ const String line = fa->get_line();
+ if (line.begins_with("exported_node")) {
+ stored_properties++;
+ }
+ }
+ CHECK_EQ(stored_properties, 2);
+ }
+
+ memdelete(node);
+}
+
TEST_CASE("[Node] Processing checks") {
Node *node = memnew(Node);
diff --git a/tests/scene/test_viewport.h b/tests/scene/test_viewport.h
index 66b0f438cc..1341cc0332 100644
--- a/tests/scene/test_viewport.h
+++ b/tests/scene/test_viewport.h
@@ -548,8 +548,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
}
SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification propagation when moving into child.") {
- SIGNAL_WATCH(node_i, SNAME("mouse_entered"));
- SIGNAL_WATCH(node_i, SNAME("mouse_exited"));
+ SIGNAL_WATCH(node_i, SceneStringName(mouse_entered));
+ SIGNAL_WATCH(node_i, SceneStringName(mouse_exited));
Array signal_args;
signal_args.push_back(Array());
@@ -568,8 +568,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK(node_i->mouse_over_self);
CHECK_FALSE(node_j->mouse_over);
CHECK_FALSE(node_j->mouse_over_self);
- SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Move to child Control node_j. node_i should not receive any new Mouse Enter signals.
SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
@@ -577,8 +577,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Move to parent Control node_i. node_i should not receive any new Mouse Enter signals.
SEND_GUI_MOUSE_MOTION_EVENT(on_i, MouseButtonMask::NONE, Key::NONE);
@@ -586,8 +586,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK(node_i->mouse_over_self);
CHECK_FALSE(node_j->mouse_over);
CHECK_FALSE(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Move to background.
SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
@@ -595,16 +595,16 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK_FALSE(node_j->mouse_over);
CHECK_FALSE(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK(SNAME("mouse_exited"), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK(SceneStringName(mouse_exited), signal_args);
CHECK_FALSE(node_i->invalid_order);
CHECK_FALSE(node_j->invalid_order);
node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
- SIGNAL_UNWATCH(node_i, SNAME("mouse_entered"));
- SIGNAL_UNWATCH(node_i, SNAME("mouse_exited"));
+ SIGNAL_UNWATCH(node_i, SceneStringName(mouse_entered));
+ SIGNAL_UNWATCH(node_i, SceneStringName(mouse_exited));
}
SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification propagation with top level.") {
@@ -756,8 +756,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
}
SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when changing top level.") {
- SIGNAL_WATCH(node_i, SNAME("mouse_entered"));
- SIGNAL_WATCH(node_i, SNAME("mouse_exited"));
+ SIGNAL_WATCH(node_i, SceneStringName(mouse_entered));
+ SIGNAL_WATCH(node_i, SceneStringName(mouse_exited));
Array signal_args;
signal_args.push_back(Array());
@@ -794,8 +794,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Change node_i to top level. node_h should receive Mouse Exit. node_i should not receive any new signals.
node_i->set_as_top_level(true);
@@ -805,8 +805,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Change node_i to not top level. node_h should receive Mouse Enter. node_i should not receive any new signals.
node_i->set_as_top_level(false);
@@ -816,8 +816,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
CHECK_FALSE(node_b->invalid_order);
CHECK_FALSE(node_d->invalid_order);
@@ -830,13 +830,13 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
- SIGNAL_UNWATCH(node_i, SNAME("mouse_entered"));
- SIGNAL_UNWATCH(node_i, SNAME("mouse_exited"));
+ SIGNAL_UNWATCH(node_i, SceneStringName(mouse_entered));
+ SIGNAL_UNWATCH(node_i, SceneStringName(mouse_exited));
}
SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when changing the mouse filter to stop.") {
- SIGNAL_WATCH(node_i, SNAME("mouse_entered"));
- SIGNAL_WATCH(node_i, SNAME("mouse_exited"));
+ SIGNAL_WATCH(node_i, SceneStringName(mouse_entered));
+ SIGNAL_WATCH(node_i, SceneStringName(mouse_exited));
Array signal_args;
signal_args.push_back(Array());
@@ -851,8 +851,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Change node_i to MOUSE_FILTER_STOP. node_h should receive Mouse Exit. node_i should not receive any new signals.
node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
@@ -862,8 +862,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Change node_i to MOUSE_FILTER_PASS. node_h should receive Mouse Enter. node_i should not receive any new signals.
node_i->set_mouse_filter(Control::MOUSE_FILTER_PASS);
@@ -873,8 +873,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
CHECK_FALSE(node_h->invalid_order);
CHECK_FALSE(node_i->invalid_order);
@@ -883,13 +883,13 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
- SIGNAL_UNWATCH(node_i, SNAME("mouse_entered"));
- SIGNAL_UNWATCH(node_i, SNAME("mouse_exited"));
+ SIGNAL_UNWATCH(node_i, SceneStringName(mouse_entered));
+ SIGNAL_UNWATCH(node_i, SceneStringName(mouse_exited));
}
SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when changing the mouse filter to ignore.") {
- SIGNAL_WATCH(node_i, SNAME("mouse_entered"));
- SIGNAL_WATCH(node_i, SNAME("mouse_exited"));
+ SIGNAL_WATCH(node_i, SceneStringName(mouse_entered));
+ SIGNAL_WATCH(node_i, SceneStringName(mouse_exited));
Array signal_args;
signal_args.push_back(Array());
@@ -904,8 +904,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Change node_i to MOUSE_FILTER_IGNORE. node_i should receive Mouse Exit.
node_i->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
@@ -915,8 +915,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK(SNAME("mouse_exited"), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK(SceneStringName(mouse_exited), signal_args);
// Change node_i to MOUSE_FILTER_PASS. node_i should receive Mouse Enter.
node_i->set_mouse_filter(Control::MOUSE_FILTER_PASS);
@@ -926,8 +926,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Change node_j to MOUSE_FILTER_IGNORE. After updating the mouse motion, node_i should now have mouse_over_self.
node_j->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
@@ -938,8 +938,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK(node_i->mouse_over_self);
CHECK_FALSE(node_j->mouse_over);
CHECK_FALSE(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Change node_j to MOUSE_FILTER_PASS. After updating the mouse motion, node_j should now have mouse_over_self.
node_j->set_mouse_filter(Control::MOUSE_FILTER_PASS);
@@ -950,8 +950,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
CHECK_FALSE(node_h->invalid_order);
CHECK_FALSE(node_i->invalid_order);
@@ -960,13 +960,13 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
- SIGNAL_UNWATCH(node_i, SNAME("mouse_entered"));
- SIGNAL_UNWATCH(node_i, SNAME("mouse_exited"));
+ SIGNAL_UNWATCH(node_i, SceneStringName(mouse_entered));
+ SIGNAL_UNWATCH(node_i, SceneStringName(mouse_exited));
}
SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when removing the hovered Control.") {
- SIGNAL_WATCH(node_h, SNAME("mouse_entered"));
- SIGNAL_WATCH(node_h, SNAME("mouse_exited"));
+ SIGNAL_WATCH(node_h, SceneStringName(mouse_entered));
+ SIGNAL_WATCH(node_h, SceneStringName(mouse_exited));
Array signal_args;
signal_args.push_back(Array());
@@ -981,8 +981,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Remove node_i from the tree. node_i and node_j should receive Mouse Exit. node_h should not receive any new signals.
node_h->remove_child(node_i);
@@ -992,8 +992,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK_FALSE(node_j->mouse_over);
CHECK_FALSE(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Add node_i to the tree and update the mouse. node_i and node_j should receive Mouse Enter. node_h should not receive any new signals.
node_h->add_child(node_i);
@@ -1004,8 +1004,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
CHECK_FALSE(node_h->invalid_order);
CHECK_FALSE(node_i->invalid_order);
@@ -1014,13 +1014,13 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
- SIGNAL_UNWATCH(node_h, SNAME("mouse_entered"));
- SIGNAL_UNWATCH(node_h, SNAME("mouse_exited"));
+ SIGNAL_UNWATCH(node_h, SceneStringName(mouse_entered));
+ SIGNAL_UNWATCH(node_h, SceneStringName(mouse_exited));
}
SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when hiding the hovered Control.") {
- SIGNAL_WATCH(node_h, SNAME("mouse_entered"));
- SIGNAL_WATCH(node_h, SNAME("mouse_exited"));
+ SIGNAL_WATCH(node_h, SceneStringName(mouse_entered));
+ SIGNAL_WATCH(node_h, SceneStringName(mouse_exited));
Array signal_args;
signal_args.push_back(Array());
@@ -1035,8 +1035,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Hide node_i. node_i and node_j should receive Mouse Exit. node_h should not receive any new signals.
node_i->hide();
@@ -1046,8 +1046,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK_FALSE(node_j->mouse_over);
CHECK_FALSE(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
// Show node_i and update the mouse. node_i and node_j should receive Mouse Enter. node_h should not receive any new signals.
node_i->show();
@@ -1058,8 +1058,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
CHECK_FALSE(node_i->mouse_over_self);
CHECK(node_j->mouse_over);
CHECK(node_j->mouse_over_self);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
CHECK_FALSE(node_h->invalid_order);
CHECK_FALSE(node_i->invalid_order);
@@ -1068,26 +1068,26 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
- SIGNAL_UNWATCH(node_h, SNAME("mouse_entered"));
- SIGNAL_UNWATCH(node_h, SNAME("mouse_exited"));
+ SIGNAL_UNWATCH(node_h, SceneStringName(mouse_entered));
+ SIGNAL_UNWATCH(node_h, SceneStringName(mouse_exited));
}
SUBCASE("[Viewport][GuiInputEvent] Window Mouse Enter/Exit signals.") {
- SIGNAL_WATCH(root, SNAME("mouse_entered"));
- SIGNAL_WATCH(root, SNAME("mouse_exited"));
+ SIGNAL_WATCH(root, SceneStringName(mouse_entered));
+ SIGNAL_WATCH(root, SceneStringName(mouse_exited));
Array signal_args;
signal_args.push_back(Array());
SEND_GUI_MOUSE_MOTION_EVENT(on_outside, MouseButtonMask::NONE, Key::NONE);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK(SNAME("mouse_exited"), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK(SceneStringName(mouse_exited), signal_args);
SEND_GUI_MOUSE_MOTION_EVENT(on_a, MouseButtonMask::NONE, Key::NONE);
- SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), signal_args);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
- SIGNAL_UNWATCH(root, SNAME("mouse_entered"));
- SIGNAL_UNWATCH(root, SNAME("mouse_exited"));
+ SIGNAL_UNWATCH(root, SceneStringName(mouse_entered));
+ SIGNAL_UNWATCH(root, SceneStringName(mouse_exited));
}
SUBCASE("[Viewport][GuiInputEvent] Process-Mode affects, if GUI Mouse Motion Events are processed.") {
@@ -1415,9 +1415,9 @@ public:
Ref<InputEvent> last_input_event;
void init_signals() {
- connect(SNAME("mouse_entered"), callable_mp(this, &TestArea2D::_on_mouse_entered));
- connect(SNAME("mouse_exited"), callable_mp(this, &TestArea2D::_on_mouse_exited));
- connect(SNAME("input_event"), callable_mp(this, &TestArea2D::_on_input_event));
+ connect(SceneStringName(mouse_entered), callable_mp(this, &TestArea2D::_on_mouse_entered));
+ connect(SceneStringName(mouse_exited), callable_mp(this, &TestArea2D::_on_mouse_exited));
+ connect(SceneStringName(input_event), callable_mp(this, &TestArea2D::_on_input_event));
}
void test_reset() {
@@ -1459,8 +1459,8 @@ TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") {
pc.a->set_name("A" + itos(i));
pc.c->set_name("C" + itos(i));
v.push_back(pc);
- SIGNAL_WATCH(pc.a, SNAME("mouse_entered"));
- SIGNAL_WATCH(pc.a, SNAME("mouse_exited"));
+ SIGNAL_WATCH(pc.a, SceneStringName(mouse_entered));
+ SIGNAL_WATCH(pc.a, SceneStringName(mouse_exited));
}
Node2D *node_a = memnew(Node2D);
@@ -1499,8 +1499,8 @@ TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") {
SUBCASE("[Viewport][Picking2D] Mouse Motion") {
SEND_GUI_MOUSE_MOTION_EVENT(on_all, MouseButtonMask::NONE, Key::NONE);
tree->physics_process(1);
- SIGNAL_CHECK(SNAME("mouse_entered"), empty_signal_args_4);
- SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+ SIGNAL_CHECK(SceneStringName(mouse_entered), empty_signal_args_4);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_exited));
for (PickingCollider E : v) {
CHECK(E.a->enter_id);
CHECK_FALSE(E.a->exit_id);
@@ -1509,8 +1509,8 @@ TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") {
SEND_GUI_MOUSE_MOTION_EVENT(on_01, MouseButtonMask::NONE, Key::NONE);
tree->physics_process(1);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK(SNAME("mouse_exited"), empty_signal_args_2);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK(SceneStringName(mouse_exited), empty_signal_args_2);
for (int i = 0; i < v.size(); i++) {
CHECK_FALSE(v[i].a->enter_id);
@@ -1524,8 +1524,8 @@ TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") {
SEND_GUI_MOUSE_MOTION_EVENT(on_outside, MouseButtonMask::NONE, Key::NONE);
tree->physics_process(1);
- SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
- SIGNAL_CHECK(SNAME("mouse_exited"), empty_signal_args_2);
+ SIGNAL_CHECK_FALSE(SceneStringName(mouse_entered));
+ SIGNAL_CHECK(SceneStringName(mouse_exited), empty_signal_args_2);
for (int i = 0; i < v.size(); i++) {
CHECK_FALSE(v[i].a->enter_id);
if (i < 2) {
@@ -1788,8 +1788,8 @@ TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") {
}
for (PickingCollider E : v) {
- SIGNAL_UNWATCH(E.a, SNAME("mouse_entered"));
- SIGNAL_UNWATCH(E.a, SNAME("mouse_exited"));
+ SIGNAL_UNWATCH(E.a, SceneStringName(mouse_entered));
+ SIGNAL_UNWATCH(E.a, SceneStringName(mouse_exited));
memdelete(E.c);
memdelete(E.a);
}
diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h
index 8778ea86a6..cf6b89c330 100644
--- a/tests/servers/test_navigation_server_3d.h
+++ b/tests/servers/test_navigation_server_3d.h
@@ -697,12 +697,16 @@ TEST_SUITE("[Navigation]") {
CHECK_NE(navigation_server->map_get_closest_point(map, Vector3(0, 0, 0)), Vector3(0, 0, 0));
CHECK_NE(navigation_server->map_get_closest_point_normal(map, Vector3(0, 0, 0)), Vector3());
CHECK(navigation_server->map_get_closest_point_owner(map, Vector3(0, 0, 0)).is_valid());
- // TODO: Test map_get_closest_point_to_segment() with p_use_collision=true as well.
CHECK_NE(navigation_server->map_get_closest_point_to_segment(map, Vector3(0, 0, 0), Vector3(1, 1, 1), false), Vector3());
+ CHECK_NE(navigation_server->map_get_closest_point_to_segment(map, Vector3(0, 0, 0), Vector3(1, 1, 1), true), Vector3());
CHECK_NE(navigation_server->map_get_path(map, Vector3(0, 0, 0), Vector3(10, 0, 10), true).size(), 0);
CHECK_NE(navigation_server->map_get_path(map, Vector3(0, 0, 0), Vector3(10, 0, 10), false).size(), 0);
}
+ SUBCASE("'map_get_closest_point_to_segment' with 'use_collision' should return default if segment doesn't intersect map") {
+ CHECK_EQ(navigation_server->map_get_closest_point_to_segment(map, Vector3(1, 2, 1), Vector3(1, 1, 1), true), Vector3());
+ }
+
SUBCASE("Elaborate query with 'CORRIDORFUNNEL' post-processing should yield non-empty result") {
Ref<NavigationPathQueryParameters3D> query_parameters = memnew(NavigationPathQueryParameters3D);
query_parameters->set_map(map);
diff --git a/tests/test_macros.h b/tests/test_macros.h
index 25e48c1e05..10f4c59a90 100644
--- a/tests/test_macros.h
+++ b/tests/test_macros.h
@@ -406,4 +406,71 @@ public:
#define SIGNAL_CHECK_FALSE(m_signal) CHECK(SignalWatcher::get_singleton()->check_false(m_signal));
#define SIGNAL_DISCARD(m_signal) SignalWatcher::get_singleton()->discard_signal(m_signal);
+#define MULTICHECK_STRING_EQ(m_obj, m_func, m_param1, m_eq) \
+ CHECK(m_obj.m_func(m_param1) == m_eq); \
+ CHECK(m_obj.m_func(U##m_param1) == m_eq); \
+ CHECK(m_obj.m_func(L##m_param1) == m_eq); \
+ CHECK(m_obj.m_func(String(m_param1)) == m_eq);
+
+#define MULTICHECK_STRING_INT_EQ(m_obj, m_func, m_param1, m_param2, m_eq) \
+ CHECK(m_obj.m_func(m_param1, m_param2) == m_eq); \
+ CHECK(m_obj.m_func(U##m_param1, m_param2) == m_eq); \
+ CHECK(m_obj.m_func(L##m_param1, m_param2) == m_eq); \
+ CHECK(m_obj.m_func(String(m_param1), m_param2) == m_eq);
+
+#define MULTICHECK_STRING_INT_INT_EQ(m_obj, m_func, m_param1, m_param2, m_param3, m_eq) \
+ CHECK(m_obj.m_func(m_param1, m_param2, m_param3) == m_eq); \
+ CHECK(m_obj.m_func(U##m_param1, m_param2, m_param3) == m_eq); \
+ CHECK(m_obj.m_func(L##m_param1, m_param2, m_param3) == m_eq); \
+ CHECK(m_obj.m_func(String(m_param1), m_param2, m_param3) == m_eq);
+
+#define MULTICHECK_STRING_STRING_EQ(m_obj, m_func, m_param1, m_param2, m_eq) \
+ CHECK(m_obj.m_func(m_param1, m_param2) == m_eq); \
+ CHECK(m_obj.m_func(U##m_param1, U##m_param2) == m_eq); \
+ CHECK(m_obj.m_func(L##m_param1, L##m_param2) == m_eq); \
+ CHECK(m_obj.m_func(String(m_param1), String(m_param2)) == m_eq);
+
+#define MULTICHECK_GET_SLICE(m_obj, m_param1, m_slices) \
+ for (int i = 0; i < m_obj.get_slice_count(m_param1); ++i) { \
+ CHECK(m_obj.get_slice(m_param1, i) == m_slices[i]); \
+ } \
+ for (int i = 0; i < m_obj.get_slice_count(U##m_param1); ++i) { \
+ CHECK(m_obj.get_slice(U##m_param1, i) == m_slices[i]); \
+ } \
+ for (int i = 0; i < m_obj.get_slice_count(L##m_param1); ++i) { \
+ CHECK(m_obj.get_slice(L##m_param1, i) == m_slices[i]); \
+ } \
+ for (int i = 0; i < m_obj.get_slice_count(String(m_param1)); ++i) { \
+ CHECK(m_obj.get_slice(String(m_param1), i) == m_slices[i]); \
+ }
+
+#define MULTICHECK_SPLIT(m_obj, m_func, m_param1, m_param2, m_param3, m_slices, m_expected_size) \
+ do { \
+ Vector<String> string_list; \
+ \
+ string_list = m_obj.m_func(m_param1, m_param2, m_param3); \
+ CHECK(m_expected_size == string_list.size()); \
+ for (int i = 0; i < string_list.size(); ++i) { \
+ CHECK(string_list[i] == m_slices[i]); \
+ } \
+ \
+ string_list = m_obj.m_func(U##m_param1, m_param2, m_param3); \
+ CHECK(m_expected_size == string_list.size()); \
+ for (int i = 0; i < string_list.size(); ++i) { \
+ CHECK(string_list[i] == m_slices[i]); \
+ } \
+ \
+ string_list = m_obj.m_func(L##m_param1, m_param2, m_param3); \
+ CHECK(m_expected_size == string_list.size()); \
+ for (int i = 0; i < string_list.size(); ++i) { \
+ CHECK(string_list[i] == m_slices[i]); \
+ } \
+ \
+ string_list = m_obj.m_func(String(m_param1), m_param2, m_param3); \
+ CHECK(m_expected_size == string_list.size()); \
+ for (int i = 0; i < string_list.size(); ++i) { \
+ CHECK(string_list[i] == m_slices[i]); \
+ } \
+ } while (0)
+
#endif // TEST_MACROS_H
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 69d8113e64..3c875797a4 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -111,6 +111,8 @@
#include "tests/scene/test_curve_3d.h"
#include "tests/scene/test_gradient.h"
#include "tests/scene/test_image_texture.h"
+#include "tests/scene/test_image_texture_3d.h"
+#include "tests/scene/test_instance_placeholder.h"
#include "tests/scene/test_node.h"
#include "tests/scene/test_node_2d.h"
#include "tests/scene/test_packed_scene.h"
@@ -242,7 +244,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
String name = String(p_in.m_name);
String suite_name = String(p_in.m_test_suite);
- if (name.find("[SceneTree]") != -1 || name.find("[Editor]") != -1) {
+ if (name.contains("[SceneTree]") || name.contains("[Editor]")) {
memnew(MessageQueue);
memnew(Input);
@@ -252,7 +254,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
OS::get_singleton()->set_has_server_feature_callback(nullptr);
for (int i = 0; i < DisplayServer::get_create_function_count(); 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);
+ DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, nullptr, Vector2i(0, 0), DisplayServer::SCREEN_PRIMARY, DisplayServer::CONTEXT_EDITOR, err);
break;
}
}
@@ -291,7 +293,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
}
#ifdef TOOLS_ENABLED
- if (name.find("[Editor]") != -1) {
+ if (name.contains("[Editor]")) {
Engine::get_singleton()->set_editor_hint(true);
EditorPaths::create();
EditorSettings::create();
@@ -301,7 +303,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
return;
}
- if (name.find("Audio") != -1) {
+ if (name.contains("Audio")) {
// The last driver index should always be the dummy driver.
int dummy_idx = AudioDriverManager::get_driver_count() - 1;
AudioDriverManager::initialize(dummy_idx);
@@ -311,7 +313,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
}
#ifndef _3D_DISABLED
- if (suite_name.find("[Navigation]") != -1 && navigation_server_2d == nullptr && navigation_server_3d == nullptr) {
+ if (suite_name.contains("[Navigation]") && navigation_server_2d == nullptr && navigation_server_3d == nullptr) {
ERR_PRINT_OFF;
navigation_server_3d = NavigationServer3DManager::new_default_server();
navigation_server_2d = NavigationServer2DManager::new_default_server();
diff --git a/tests/test_utils.cpp b/tests/test_utils.cpp
index cbd6d1ffbb..9d41e74020 100644
--- a/tests/test_utils.cpp
+++ b/tests/test_utils.cpp
@@ -30,6 +30,7 @@
#include "tests/test_utils.h"
+#include "core/io/dir_access.h"
#include "core/os/os.h"
String TestUtils::get_data_path(const String &p_file) {
@@ -40,3 +41,9 @@ String TestUtils::get_data_path(const String &p_file) {
String TestUtils::get_executable_dir() {
return OS::get_singleton()->get_executable_path().get_base_dir();
}
+
+String TestUtils::get_temp_path(const String &p_suffix) {
+ const String temp_base = OS::get_singleton()->get_cache_path().path_join("godot_test");
+ DirAccess::make_dir_absolute(temp_base); // Ensure the directory exists.
+ return temp_base.path_join(p_suffix);
+}
diff --git a/tests/test_utils.h b/tests/test_utils.h
index 48abe75c06..876a59ee7b 100644
--- a/tests/test_utils.h
+++ b/tests/test_utils.h
@@ -37,6 +37,7 @@ namespace TestUtils {
String get_data_path(const String &p_file);
String get_executable_dir();
+String get_temp_path(const String &p_suffix);
} // namespace TestUtils
#endif // TEST_UTILS_H