diff options
Diffstat (limited to 'tests/core')
-rw-r--r-- | tests/core/input/test_input_event.h | 4 | ||||
-rw-r--r-- | tests/core/io/test_image.h | 4 | ||||
-rw-r--r-- | tests/core/io/test_pck_packer.h | 8 | ||||
-rw-r--r-- | tests/core/io/test_resource.h | 8 | ||||
-rw-r--r-- | tests/core/math/test_aabb.h | 61 | ||||
-rw-r--r-- | tests/core/math/test_math_funcs.h | 3 | ||||
-rw-r--r-- | tests/core/math/test_transform_2d.h | 110 | ||||
-rw-r--r-- | tests/core/object/test_class_db.h | 12 | ||||
-rw-r--r-- | tests/core/object/test_object.h | 5 | ||||
-rw-r--r-- | tests/core/string/test_string.h | 309 |
10 files changed, 348 insertions, 176 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") { |