summaryrefslogtreecommitdiffstats
path: root/tests/core
diff options
context:
space:
mode:
Diffstat (limited to 'tests/core')
-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
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") {