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_resource.h5
-rw-r--r--tests/core/math/test_aabb.h61
-rw-r--r--tests/core/math/test_geometry_2d.h94
-rw-r--r--tests/core/math/test_math_funcs.h3
-rw-r--r--tests/core/math/test_transform_3d.h29
-rw-r--r--tests/core/object/test_class_db.h30
-rw-r--r--tests/core/object/test_object.h7
-rw-r--r--tests/core/os/test_os.h4
-rw-r--r--tests/core/string/test_string.h309
-rw-r--r--tests/core/templates/test_command_queue.h44
-rw-r--r--tests/core/templates/test_local_vector.h11
-rw-r--r--tests/core/threads/test_worker_thread_pool.h67
-rw-r--r--tests/core/variant/test_array.h4
-rw-r--r--tests/core/variant/test_dictionary.h2
-rw-r--r--tests/core/variant/test_variant.h4
16 files changed, 467 insertions, 211 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_resource.h b/tests/core/io/test_resource.h
index 9ddb51220b..a83e7f88ba 100644
--- a/tests/core/io/test_resource.h
+++ b/tests/core/io/test_resource.h
@@ -38,6 +38,8 @@
#include "thirdparty/doctest/doctest.h"
+#include "tests/test_macros.h"
+
namespace TestResource {
TEST_CASE("[Resource] Duplication") {
@@ -124,9 +126,12 @@ TEST_CASE("[Resource] Breaking circular references on save") {
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");
ResourceSaver::save(resource_a, save_path_binary);
+ // Suppress expected errors caused by the resources above being uncached.
+ ERR_PRINT_OFF;
ResourceSaver::save(resource_a, save_path_text);
const Ref<Resource> &loaded_resource_a_binary = ResourceLoader::load(save_path_binary);
+ ERR_PRINT_ON;
CHECK_MESSAGE(
loaded_resource_a_binary->get_name() == "A",
"The loaded resource name should be equal to the expected value.");
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_geometry_2d.h b/tests/core/math/test_geometry_2d.h
index c3ff4f3ec9..a4bb6dfca0 100644
--- a/tests/core/math/test_geometry_2d.h
+++ b/tests/core/math/test_geometry_2d.h
@@ -282,41 +282,67 @@ TEST_CASE("[Geometry2D] Closest point to uncapped segment") {
TEST_CASE("[Geometry2D] Closest points between segments") {
Vector2 c1, c2;
- Geometry2D::get_closest_points_between_segments(Vector2(2, 2), Vector2(3, 3), Vector2(4, 4), Vector2(4, 5), c1, c2);
- CHECK(c1.is_equal_approx(Vector2(3, 3)));
- CHECK(c2.is_equal_approx(Vector2(4, 4)));
+ // Basis Path Testing suite
+ SUBCASE("[Geometry2D] Both segments degenerate to a point") {
+ Geometry2D::get_closest_points_between_segments(Vector2(0, 0), Vector2(0, 0), Vector2(0, 0), Vector2(0, 0), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(0, 0)));
+ CHECK(c2.is_equal_approx(Vector2(0, 0)));
+ }
- Geometry2D::get_closest_points_between_segments(Vector2(0, 1), Vector2(-2, -1), Vector2(0, 0), Vector2(2, -2), c1, c2);
- CHECK(c1.is_equal_approx(Vector2(-0.5, 0.5)));
- CHECK(c2.is_equal_approx(Vector2(0, 0)));
+ SUBCASE("[Geometry2D] Closest point on second segment trajectory is above [0,1]") {
+ Geometry2D::get_closest_points_between_segments(Vector2(50, -25), Vector2(50, -10), Vector2(-50, 10), Vector2(-40, 10), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(50, -10)));
+ CHECK(c2.is_equal_approx(Vector2(-40, 10)));
+ }
- Geometry2D::get_closest_points_between_segments(Vector2(-1, 1), Vector2(1, -1), Vector2(1, 1), Vector2(-1, -1), c1, c2);
- CHECK(c1.is_equal_approx(Vector2(0, 0)));
- CHECK(c2.is_equal_approx(Vector2(0, 0)));
+ SUBCASE("[Geometry2D] Parallel segments") {
+ Geometry2D::get_closest_points_between_segments(Vector2(2, 1), Vector2(4, 3), Vector2(2, 3), Vector2(4, 5), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(3, 2)));
+ CHECK(c2.is_equal_approx(Vector2(2, 3)));
+ }
- Geometry2D::get_closest_points_between_segments(Vector2(-3, 4), Vector2(-3, 4), Vector2(-4, 3), Vector2(-2, 3), c1, c2);
- CHECK_MESSAGE(
- c1.is_equal_approx(Vector2(-3, 4)),
- "1st line segment is only a point, this point should be the closest point to the 2nd line segment.");
- CHECK_MESSAGE(
- c2.is_equal_approx(Vector2(-3, 3)),
- "1st line segment is only a point, this should not matter when determining the closest point on the 2nd line segment.");
+ SUBCASE("[Geometry2D] Closest point on second segment trajectory is within [0,1]") {
+ Geometry2D::get_closest_points_between_segments(Vector2(2, 4), Vector2(2, 3), Vector2(1, 1), Vector2(4, 4), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(2, 3)));
+ CHECK(c2.is_equal_approx(Vector2(2.5, 2.5)));
+ }
- Geometry2D::get_closest_points_between_segments(Vector2(-4, 3), Vector2(-2, 3), Vector2(-3, 4), Vector2(-3, 4), c1, c2);
- CHECK_MESSAGE(
- c1.is_equal_approx(Vector2(-3, 3)),
- "2nd line segment is only a point, this should not matter when determining the closest point on the 1st line segment.");
- CHECK_MESSAGE(
- c2.is_equal_approx(Vector2(-3, 4)),
- "2nd line segment is only a point, this point should be the closest point to the 1st line segment.");
+ SUBCASE("[Geometry2D] Closest point on second segment trajectory is below [0,1]") {
+ Geometry2D::get_closest_points_between_segments(Vector2(-20, -20), Vector2(-10, -40), Vector2(10, 25), Vector2(25, 40), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(-20, -20)));
+ CHECK(c2.is_equal_approx(Vector2(10, 25)));
+ }
- Geometry2D::get_closest_points_between_segments(Vector2(5, -4), Vector2(5, -4), Vector2(-2, 1), Vector2(-2, 1), c1, c2);
- CHECK_MESSAGE(
- c1.is_equal_approx(Vector2(5, -4)),
- "Both line segments are only a point. On the 1st line segment, that point should be the closest point to the 2nd line segment.");
- CHECK_MESSAGE(
- c2.is_equal_approx(Vector2(-2, 1)),
- "Both line segments are only a point. On the 2nd line segment, that point should be the closest point to the 1st line segment.");
+ SUBCASE("[Geometry2D] Second segment degenerates to a point") {
+ Geometry2D::get_closest_points_between_segments(Vector2(1, 2), Vector2(2, 1), Vector2(3, 3), Vector2(3, 3), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(1.5, 1.5)));
+ CHECK(c2.is_equal_approx(Vector2(3, 3)));
+ }
+
+ SUBCASE("[Geometry2D] First segment degenerates to a point") {
+ Geometry2D::get_closest_points_between_segments(Vector2(1, 1), Vector2(1, 1), Vector2(2, 2), Vector2(4, 4), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(1, 1)));
+ CHECK(c2.is_equal_approx(Vector2(2, 2)));
+ }
+ // End Basis Path Testing suite
+
+ SUBCASE("[Geometry2D] Segments are equal vectors") {
+ Geometry2D::get_closest_points_between_segments(Vector2(2, 2), Vector2(3, 3), Vector2(4, 4), Vector2(4, 5), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(3, 3)));
+ CHECK(c2.is_equal_approx(Vector2(4, 4)));
+ }
+
+ SUBCASE("[Geometry2D] Standard case") {
+ Geometry2D::get_closest_points_between_segments(Vector2(0, 1), Vector2(-2, -1), Vector2(0, 0), Vector2(2, -2), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(-0.5, 0.5)));
+ CHECK(c2.is_equal_approx(Vector2(0, 0)));
+ }
+
+ SUBCASE("[Geometry2D] Segments intersect") {
+ Geometry2D::get_closest_points_between_segments(Vector2(-1, 1), Vector2(1, -1), Vector2(1, 1), Vector2(-1, -1), c1, c2);
+ CHECK(c1.is_equal_approx(Vector2(0, 0)));
+ CHECK(c2.is_equal_approx(Vector2(0, 0)));
+ }
}
TEST_CASE("[Geometry2D] Make atlas") {
@@ -685,12 +711,12 @@ TEST_CASE("[Geometry2D] Clip polyline with polygon") {
r = Geometry2D::clip_polyline_with_polygon(l, p);
REQUIRE_MESSAGE(r.size() == 2, "There should be 2 resulting clipped lines.");
REQUIRE_MESSAGE(r[0].size() == 3, "The resulting clipped line should have 3 vertices.");
- CHECK(r[0][0].is_equal_approx(Vector2(160, 320)));
+ CHECK(r[0][0].is_equal_approx(Vector2(121.412682, 225.038757)));
CHECK(r[0][1].is_equal_approx(Vector2(122, 250)));
- CHECK(r[0][2].is_equal_approx(Vector2(121.412682, 225.038757)));
+ CHECK(r[0][2].is_equal_approx(Vector2(160, 320)));
REQUIRE_MESSAGE(r[1].size() == 2, "The resulting clipped line should have 2 vertices.");
- CHECK(r[1][0].is_equal_approx(Vector2(53.07737, 116.143021)));
- CHECK(r[1][1].is_equal_approx(Vector2(55, 70)));
+ CHECK(r[1][0].is_equal_approx(Vector2(55, 70)));
+ CHECK(r[1][1].is_equal_approx(Vector2(53.07737, 116.143021)));
}
}
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_3d.h b/tests/core/math/test_transform_3d.h
index 551b20fe74..fba0fcb280 100644
--- a/tests/core/math/test_transform_3d.h
+++ b/tests/core/math/test_transform_3d.h
@@ -107,6 +107,35 @@ TEST_CASE("[Transform3D] Finite number checks") {
"Transform3D with two components infinite should not be finite.");
}
+TEST_CASE("[Transform3D] Rotate around global origin") {
+ // Start with the default orientation, but not centered on the origin.
+ // Rotating should rotate both our basis and the origin.
+ Transform3D transform = Transform3D();
+ transform.origin = Vector3(0, 0, 1);
+
+ Transform3D expected = Transform3D();
+ expected.origin = Vector3(0, 0, -1);
+ expected.basis[0] = Vector3(-1, 0, 0);
+ expected.basis[2] = Vector3(0, 0, -1);
+
+ const Transform3D rotated_transform = transform.rotated(Vector3(0, 1, 0), Math_PI);
+ CHECK_MESSAGE(rotated_transform.is_equal_approx(expected), "The rotated transform should have a new orientation and basis.");
+}
+
+TEST_CASE("[Transform3D] Rotate in-place (local rotation)") {
+ // Start with the default orientation.
+ // Local rotation should not change the origin, only the basis.
+ Transform3D transform = Transform3D();
+ transform.origin = Vector3(1, 2, 3);
+
+ Transform3D expected = Transform3D();
+ expected.origin = Vector3(1, 2, 3);
+ expected.basis[0] = Vector3(-1, 0, 0);
+ expected.basis[2] = Vector3(0, 0, -1);
+
+ const Transform3D rotated_transform = Transform3D(transform.rotated_local(Vector3(0, 1, 0), Math_PI));
+ CHECK_MESSAGE(rotated_transform.is_equal_approx(expected), "The rotated transform should have a new orientation but still be based on the same origin.");
+}
} // namespace TestTransform3D
#endif // TEST_TRANSFORM_3D_H
diff --git a/tests/core/object/test_class_db.h b/tests/core/object/test_class_db.h
index 5f7de11c71..381d759e5b 100644
--- a/tests/core/object/test_class_db.h
+++ b/tests/core/object/test_class_db.h
@@ -141,7 +141,7 @@ struct NamesCache {
StringName vector3_type = StaticCString::create("Vector3");
// Object not included as it must be checked for all derived classes
- static constexpr int nullable_types_count = 17;
+ static constexpr int nullable_types_count = 18;
StringName nullable_types[nullable_types_count] = {
string_type,
string_name_type,
@@ -161,6 +161,7 @@ struct NamesCache {
StaticCString::create(_STR(PackedVector2Array)),
StaticCString::create(_STR(PackedVector3Array)),
StaticCString::create(_STR(PackedColorArray)),
+ StaticCString::create(_STR(PackedVector4Array)),
};
bool is_nullable_type(const StringName &p_type) const {
@@ -194,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;
}
@@ -258,6 +259,7 @@ bool arg_default_value_is_assignable_to_type(const Context &p_context, const Var
case Variant::PACKED_VECTOR2_ARRAY:
case Variant::PACKED_VECTOR3_ARRAY:
case Variant::PACKED_COLOR_ARRAY:
+ case Variant::PACKED_VECTOR4_ARRAY:
case Variant::CALLABLE:
case Variant::SIGNAL:
return p_arg_type.name == Variant::get_type_name(p_val.get_type());
@@ -353,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), "'.");
}
}
@@ -365,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), "'.");
}
}
@@ -548,8 +550,6 @@ void add_exposed_classes(Context &r_context) {
for (const MethodInfo &E : method_list) {
const MethodInfo &method_info = E;
- int argc = method_info.arguments.size();
-
if (method_info.name.is_empty()) {
continue;
}
@@ -611,8 +611,9 @@ void add_exposed_classes(Context &r_context) {
method.return_type.name = Variant::get_type_name(return_info.type);
}
- for (int i = 0; i < argc; i++) {
- PropertyInfo arg_info = method_info.arguments[i];
+ int i = 0;
+ for (List<PropertyInfo>::ConstIterator itr = method_info.arguments.begin(); itr != method_info.arguments.end(); ++itr, ++i) {
+ const PropertyInfo &arg_info = *itr;
String orig_arg_name = arg_info.name;
@@ -684,10 +685,9 @@ void add_exposed_classes(Context &r_context) {
TEST_FAIL_COND(!String(signal.name).is_valid_identifier(),
"Signal name is not a valid identifier: '", exposed_class.name, ".", signal.name, "'.");
- int argc = method_info.arguments.size();
-
- for (int i = 0; i < argc; i++) {
- PropertyInfo arg_info = method_info.arguments[i];
+ int i = 0;
+ for (List<PropertyInfo>::ConstIterator itr = method_info.arguments.begin(); itr != method_info.arguments.end(); ++itr, ++i) {
+ const PropertyInfo &arg_info = *itr;
String orig_arg_name = arg_info.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 3a3013a102..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"
@@ -142,7 +141,7 @@ TEST_CASE("[Object] Core getters") {
inheritance_list.size() == 1,
"The inheritance list should consist of Object only");
CHECK_MESSAGE(
- inheritance_list[0] == "Object",
+ inheritance_list.front()->get() == "Object",
"The inheritance list should consist of Object only");
}
@@ -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/os/test_os.h b/tests/core/os/test_os.h
index 63f8b18238..6ee0ff82e7 100644
--- a/tests/core/os/test_os.h
+++ b/tests/core/os/test_os.h
@@ -79,8 +79,8 @@ TEST_CASE("[OS] Non-UTF-8 environment variables") {
TEST_CASE("[OS] Command line arguments") {
List<String> arguments = OS::get_singleton()->get_cmdline_args();
bool found = false;
- for (int i = 0; i < arguments.size(); i++) {
- if (arguments[i] == "--test") {
+ for (const String &arg : arguments) {
+ if (arg == "--test") {
found = true;
break;
}
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/core/templates/test_command_queue.h b/tests/core/templates/test_command_queue.h
index e94c108694..d2957b5c40 100644
--- a/tests/core/templates/test_command_queue.h
+++ b/tests/core/templates/test_command_queue.h
@@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "core/math/random_number_generator.h"
+#include "core/object/worker_thread_pool.h"
#include "core/os/os.h"
#include "core/os/thread.h"
#include "core/templates/command_queue_mt.h"
@@ -100,7 +101,7 @@ public:
ThreadWork reader_threadwork;
ThreadWork writer_threadwork;
- CommandQueueMT command_queue = CommandQueueMT(true);
+ CommandQueueMT command_queue;
enum TestMsgType {
TEST_MSG_FUNC1_TRANSFORM,
@@ -119,6 +120,7 @@ public:
bool exit_threads = false;
Thread reader_thread;
+ WorkerThreadPool::TaskID reader_task_id = WorkerThreadPool::INVALID_TASK_ID;
Thread writer_thread;
int func1_count = 0;
@@ -148,11 +150,16 @@ public:
void reader_thread_loop() {
reader_threadwork.thread_wait_for_work();
while (!exit_threads) {
- if (message_count_to_read < 0) {
+ if (reader_task_id == WorkerThreadPool::INVALID_TASK_ID) {
command_queue.flush_all();
- }
- for (int i = 0; i < message_count_to_read; i++) {
- command_queue.wait_and_flush();
+ } else {
+ if (message_count_to_read < 0) {
+ command_queue.flush_all();
+ }
+ for (int i = 0; i < message_count_to_read; i++) {
+ WorkerThreadPool::get_singleton()->yield();
+ command_queue.wait_and_flush();
+ }
}
message_count_to_read = 0;
@@ -216,8 +223,13 @@ public:
sts->writer_thread_loop();
}
- void init_threads() {
- reader_thread.start(&SharedThreadState::static_reader_thread_loop, this);
+ void init_threads(bool p_use_thread_pool_sync = false) {
+ if (p_use_thread_pool_sync) {
+ reader_task_id = WorkerThreadPool::get_singleton()->add_native_task(&SharedThreadState::static_reader_thread_loop, this, true);
+ command_queue.set_pump_task_id(reader_task_id);
+ } else {
+ reader_thread.start(&SharedThreadState::static_reader_thread_loop, this);
+ }
writer_thread.start(&SharedThreadState::static_writer_thread_loop, this);
}
void destroy_threads() {
@@ -225,16 +237,20 @@ public:
reader_threadwork.main_start_work();
writer_threadwork.main_start_work();
- reader_thread.wait_to_finish();
+ if (reader_task_id != WorkerThreadPool::INVALID_TASK_ID) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(reader_task_id);
+ } else {
+ reader_thread.wait_to_finish();
+ }
writer_thread.wait_to_finish();
}
};
-TEST_CASE("[CommandQueue] Test Queue Basics") {
+static void test_command_queue_basic(bool p_use_thread_pool_sync) {
const char *COMMAND_QUEUE_SETTING = "memory/limits/command_queue/multithreading_queue_size_kb";
ProjectSettings::get_singleton()->set_setting(COMMAND_QUEUE_SETTING, 1);
SharedThreadState sts;
- sts.init_threads();
+ sts.init_threads(p_use_thread_pool_sync);
sts.add_msg_to_write(SharedThreadState::TEST_MSG_FUNC1_TRANSFORM);
sts.writer_threadwork.main_start_work();
@@ -272,6 +288,14 @@ TEST_CASE("[CommandQueue] Test Queue Basics") {
ProjectSettings::get_singleton()->property_get_revert(COMMAND_QUEUE_SETTING));
}
+TEST_CASE("[CommandQueue] Test Queue Basics") {
+ test_command_queue_basic(false);
+}
+
+TEST_CASE("[CommandQueue] Test Queue Basics with WorkerThreadPool sync.") {
+ test_command_queue_basic(true);
+}
+
TEST_CASE("[CommandQueue] Test Queue Wrapping to same spot.") {
const char *COMMAND_QUEUE_SETTING = "memory/limits/command_queue/multithreading_queue_size_kb";
ProjectSettings::get_singleton()->set_setting(COMMAND_QUEUE_SETTING, 1);
diff --git a/tests/core/templates/test_local_vector.h b/tests/core/templates/test_local_vector.h
index 2873a9a028..c9544c625b 100644
--- a/tests/core/templates/test_local_vector.h
+++ b/tests/core/templates/test_local_vector.h
@@ -63,7 +63,7 @@ TEST_CASE("[LocalVector] Push Back.") {
CHECK(vector[4] == 4);
}
-TEST_CASE("[LocalVector] Find.") {
+TEST_CASE("[LocalVector] Find, has.") {
LocalVector<int> vector;
vector.push_back(3);
vector.push_back(1);
@@ -85,6 +85,15 @@ TEST_CASE("[LocalVector] Find.") {
CHECK(vector.find(-1) == -1);
CHECK(vector.find(5) == -1);
+
+ CHECK(vector.has(0));
+ CHECK(vector.has(1));
+ CHECK(vector.has(2));
+ CHECK(vector.has(3));
+ CHECK(vector.has(4));
+
+ CHECK(!vector.has(-1));
+ CHECK(!vector.has(5));
}
TEST_CASE("[LocalVector] Remove.") {
diff --git a/tests/core/threads/test_worker_thread_pool.h b/tests/core/threads/test_worker_thread_pool.h
index e9a762b57b..0a0291d11b 100644
--- a/tests/core/threads/test_worker_thread_pool.h
+++ b/tests/core/threads/test_worker_thread_pool.h
@@ -38,6 +38,7 @@
namespace TestWorkerThreadPool {
static LocalVector<SafeNumeric<int>> counter;
+static SafeFlag exit;
static void static_test(void *p_arg) {
counter[(uint64_t)p_arg].increment();
@@ -106,6 +107,72 @@ TEST_CASE("[WorkerThreadPool] Process elements using group tasks") {
}
}
+static void static_test_daemon(void *p_arg) {
+ while (!exit.is_set()) {
+ counter[0].add(1);
+ WorkerThreadPool::get_singleton()->yield();
+ }
+}
+
+static void static_busy_task(void *p_arg) {
+ while (!exit.is_set()) {
+ OS::get_singleton()->delay_usec(1);
+ }
+}
+
+static void static_legit_task(void *p_arg) {
+ *((bool *)p_arg) = counter[0].get() > 0;
+ counter[1].add(1);
+}
+
+TEST_CASE("[WorkerThreadPool] Run a yielding daemon as the only hope for other tasks to run") {
+ exit.clear();
+ counter.clear();
+ counter.resize(2);
+
+ WorkerThreadPool::TaskID daemon_task_id = WorkerThreadPool::get_singleton()->add_native_task(static_test_daemon, nullptr, true);
+
+ int num_threads = WorkerThreadPool::get_singleton()->get_thread_count();
+
+ // Keep all the other threads busy.
+ LocalVector<WorkerThreadPool::TaskID> task_ids;
+ for (int i = 0; i < num_threads - 1; i++) {
+ task_ids.push_back(WorkerThreadPool::get_singleton()->add_native_task(static_busy_task, nullptr, true));
+ }
+
+ LocalVector<WorkerThreadPool::TaskID> legit_task_ids;
+ LocalVector<bool> legit_task_needed_yield;
+ int legit_tasks_count = num_threads * 4;
+ legit_task_needed_yield.resize(legit_tasks_count);
+ for (int i = 0; i < legit_tasks_count; i++) {
+ legit_task_needed_yield[i] = false;
+ task_ids.push_back(WorkerThreadPool::get_singleton()->add_native_task(static_legit_task, &legit_task_needed_yield[i], i >= legit_tasks_count / 2));
+ }
+
+ while (counter[1].get() != legit_tasks_count) {
+ OS::get_singleton()->delay_usec(1);
+ }
+
+ exit.set();
+ for (uint32_t i = 0; i < task_ids.size(); i++) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(task_ids[i]);
+ }
+ WorkerThreadPool::get_singleton()->notify_yield_over(daemon_task_id);
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(daemon_task_id);
+
+ CHECK_MESSAGE(counter[0].get() > 0, "Daemon task should have looped at least once.");
+ CHECK_MESSAGE(counter[1].get() == legit_tasks_count, "All legit tasks should have been able to run.");
+
+ bool all_needed_yield = true;
+ for (int i = 0; i < legit_tasks_count; i++) {
+ if (!legit_task_needed_yield[i]) {
+ all_needed_yield = false;
+ break;
+ }
+ }
+ CHECK_MESSAGE(all_needed_yield, "All legit tasks should have needed the daemon yielding to run.");
+}
+
} // namespace TestWorkerThreadPool
#endif // TEST_WORKER_THREAD_POOL_H
diff --git a/tests/core/variant/test_array.h b/tests/core/variant/test_array.h
index 287345e831..c54854e4d7 100644
--- a/tests/core/variant/test_array.h
+++ b/tests/core/variant/test_array.h
@@ -555,6 +555,8 @@ TEST_CASE("[Array] Iteration") {
idx++;
}
+ CHECK_EQ(idx, a1.size());
+
idx = 0;
for (const Variant &E : (const Array &)a1) {
@@ -562,6 +564,8 @@ TEST_CASE("[Array] Iteration") {
idx++;
}
+ CHECK_EQ(idx, a1.size());
+
a1.clear();
}
diff --git a/tests/core/variant/test_dictionary.h b/tests/core/variant/test_dictionary.h
index 5bc56075da..aba20972d9 100644
--- a/tests/core/variant/test_dictionary.h
+++ b/tests/core/variant/test_dictionary.h
@@ -105,7 +105,7 @@ TEST_CASE("[Dictionary] get_key_lists()") {
map[1] = 3;
map.get_key_list(ptr);
CHECK(keys.size() == 1);
- CHECK(int(keys[0]) == 1);
+ CHECK(int(keys.front()->get()) == 1);
map[2] = 4;
map.get_key_list(ptr);
CHECK(keys.size() == 3);
diff --git a/tests/core/variant/test_variant.h b/tests/core/variant/test_variant.h
index 54ca06c6c4..be615975f8 100644
--- a/tests/core/variant/test_variant.h
+++ b/tests/core/variant/test_variant.h
@@ -2034,6 +2034,10 @@ TEST_CASE("[Variant] Identity comparison") {
CHECK(packed_color_array.identity_compare(packed_color_array));
CHECK_FALSE(packed_color_array.identity_compare(PackedColorArray()));
+ Variant packed_vector4_array = PackedVector4Array();
+ CHECK(packed_vector4_array.identity_compare(packed_vector4_array));
+ CHECK_FALSE(packed_vector4_array.identity_compare(PackedVector4Array()));
+
Variant packed_float32_array = PackedFloat32Array();
CHECK(packed_float32_array.identity_compare(packed_float32_array));
CHECK_FALSE(packed_float32_array.identity_compare(PackedFloat32Array()));