summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/core/io/test_image.h33
-rw-r--r--tests/core/io/test_resource.h4
-rw-r--r--tests/core/math/test_basis.h124
-rw-r--r--tests/core/math/test_geometry_3d.h341
-rw-r--r--tests/core/math/test_math_funcs.h92
-rw-r--r--tests/core/math/test_transform_2d.h30
-rw-r--r--tests/core/math/test_vector2i.h6
-rw-r--r--tests/core/math/test_vector3i.h6
-rw-r--r--tests/core/math/test_vector4i.h6
-rw-r--r--tests/core/object/test_object.h72
-rw-r--r--tests/core/string/test_string.h99
-rw-r--r--tests/core/variant/test_dictionary.h2
-rw-r--r--tests/core/variant/test_variant.h1033
-rw-r--r--tests/core/variant/test_variant_utility.h141
-rw-r--r--tests/data/images/icon.ddsbin87536 -> 0 bytes
-rw-r--r--tests/display_server_mock.h12
-rw-r--r--tests/scene/test_arraymesh.h4
-rw-r--r--tests/scene/test_camera_3d.h370
-rw-r--r--tests/scene/test_code_edit.h429
-rw-r--r--tests/scene/test_control.h66
-rw-r--r--tests/scene/test_curve_3d.h8
-rw-r--r--tests/scene/test_node_2d.h63
-rw-r--r--tests/scene/test_primitives.h2
-rw-r--r--tests/scene/test_text_edit.h75
-rw-r--r--tests/scene/test_viewport.h660
-rw-r--r--tests/scene/test_window.h6
-rw-r--r--tests/servers/rendering/test_shader_preprocessor.h6
-rw-r--r--tests/servers/test_navigation_server_3d.h107
-rw-r--r--tests/servers/test_text_server.h6
-rw-r--r--tests/test_main.cpp31
30 files changed, 3443 insertions, 391 deletions
diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h
index 1897971113..945a7e1ba3 100644
--- a/tests/core/io/test_image.h
+++ b/tests/core/io/test_image.h
@@ -115,16 +115,6 @@ TEST_CASE("[Image] Saving and loading") {
image_bmp->load_bmp_from_buffer(data_bmp) == OK,
"The BMP image should load successfully.");
- // Load DDS
- Ref<Image> image_dds = memnew(Image());
- Ref<FileAccess> f_dds = FileAccess::open(TestUtils::get_data_path("images/icon.dds"), FileAccess::READ, &err);
- PackedByteArray data_dds;
- data_dds.resize(f_dds->get_length() + 1);
- f_dds->get_buffer(data_dds.ptrw(), f_dds->get_length());
- CHECK_MESSAGE(
- image_dds->load_dds_from_buffer(data_dds) == OK,
- "The DDS image should load successfully.");
-
// Load JPG
Ref<Image> image_jpg = memnew(Image());
Ref<FileAccess> f_jpg = FileAccess::open(TestUtils::get_data_path("images/icon.jpg"), FileAccess::READ, &err);
@@ -413,6 +403,29 @@ TEST_CASE("[Image] Custom mipmaps") {
}
}
+TEST_CASE("[Image] Convert image") {
+ for (int format = Image::FORMAT_RF; format < Image::FORMAT_RGBE9995; format++) {
+ for (int new_format = Image::FORMAT_RF; new_format < Image::FORMAT_RGBE9995; new_format++) {
+ Ref<Image> image = memnew(Image(4, 4, false, (Image::Format)format));
+ image->convert((Image::Format)new_format);
+ String format_string = Image::format_names[(Image::Format)format];
+ String new_format_string = Image::format_names[(Image::Format)new_format];
+ format_string = "Error converting from " + format_string + " to " + new_format_string + ".";
+ CHECK_MESSAGE(image->get_format() == new_format, format_string);
+ }
+ }
+
+ Ref<Image> image = memnew(Image(4, 4, false, Image::FORMAT_RGBA8));
+ PackedByteArray image_data = image->get_data();
+ image->convert((Image::Format)-1);
+ CHECK_MESSAGE(image->get_data() == image_data, "Image conversion to invalid type (-1) should not alter image.");
+
+ Ref<Image> image2 = memnew(Image(4, 4, false, Image::FORMAT_RGBA8));
+ image_data = image2->get_data();
+ image2->convert((Image::Format)(Image::FORMAT_MAX + 1));
+ CHECK_MESSAGE(image2->get_data() == image_data, "Image conversion to invalid type (Image::FORMAT_MAX + 1) should not alter image.");
+}
+
} // namespace TestImage
#endif // TEST_IMAGE_H
diff --git a/tests/core/io/test_resource.h b/tests/core/io/test_resource.h
index 8fc2a2f040..9ddb51220b 100644
--- a/tests/core/io/test_resource.h
+++ b/tests/core/io/test_resource.h
@@ -139,7 +139,7 @@ TEST_CASE("[Resource] Breaking circular references on save") {
loaded_resource_c_binary->get_name() == "C",
"The loaded child resource name should be equal to the expected value.");
CHECK_MESSAGE(
- !loaded_resource_c_binary->get_meta("next"),
+ !loaded_resource_c_binary->has_meta("next"),
"The loaded child resource circular reference should be NULL.");
const Ref<Resource> &loaded_resource_a_text = ResourceLoader::load(save_path_text);
@@ -155,7 +155,7 @@ TEST_CASE("[Resource] Breaking circular references on save") {
loaded_resource_c_text->get_name() == "C",
"The loaded child resource name should be equal to the expected value.");
CHECK_MESSAGE(
- !loaded_resource_c_text->get_meta("next"),
+ !loaded_resource_c_text->has_meta("next"),
"The loaded child resource circular reference should be NULL.");
// Break circular reference to avoid memory leak
diff --git a/tests/core/math/test_basis.h b/tests/core/math/test_basis.h
index 0a34954bd3..a9bc2e9b99 100644
--- a/tests/core/math/test_basis.h
+++ b/tests/core/math/test_basis.h
@@ -296,6 +296,130 @@ TEST_CASE("[Basis] Finite number checks") {
"Basis with three components infinite should not be finite.");
}
+TEST_CASE("[Basis] Is conformal checks") {
+ CHECK_MESSAGE(
+ Basis().is_conformal(),
+ "Identity Basis should be conformal.");
+
+ CHECK_MESSAGE(
+ Basis::from_euler(Vector3(1.2, 3.4, 5.6)).is_conformal(),
+ "Basis with only rotation should be conformal.");
+
+ CHECK_MESSAGE(
+ Basis::from_scale(Vector3(-1, -1, -1)).is_conformal(),
+ "Basis with only a flip should be conformal.");
+
+ CHECK_MESSAGE(
+ Basis::from_scale(Vector3(1.2, 1.2, 1.2)).is_conformal(),
+ "Basis with only uniform scale should be conformal.");
+
+ CHECK_MESSAGE(
+ Basis(Vector3(3, 4, 0), Vector3(4, -3, 0.0), Vector3(0, 0, 5)).is_conformal(),
+ "Basis with a flip, rotation, and uniform scale should be conformal.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis::from_scale(Vector3(1.2, 3.4, 5.6)).is_conformal(),
+ "Basis with non-uniform scale should not be conformal.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(Vector3(Math_SQRT12, Math_SQRT12, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)).is_conformal(),
+ "Basis with the X axis skewed 45 degrees should not be conformal.");
+
+ CHECK_MESSAGE(
+ Basis(0, 0, 0, 0, 0, 0, 0, 0, 0).is_conformal(),
+ "Edge case: Basis with all zeroes should return true for is_conformal (because a 0 scale is uniform).");
+}
+
+TEST_CASE("[Basis] Is orthogonal checks") {
+ CHECK_MESSAGE(
+ Basis().is_orthogonal(),
+ "Identity Basis should be orthogonal.");
+
+ CHECK_MESSAGE(
+ Basis::from_euler(Vector3(1.2, 3.4, 5.6)).is_orthogonal(),
+ "Basis with only rotation should be orthogonal.");
+
+ CHECK_MESSAGE(
+ Basis::from_scale(Vector3(-1, -1, -1)).is_orthogonal(),
+ "Basis with only a flip should be orthogonal.");
+
+ CHECK_MESSAGE(
+ Basis::from_scale(Vector3(1.2, 3.4, 5.6)).is_orthogonal(),
+ "Basis with only scale should be orthogonal.");
+
+ CHECK_MESSAGE(
+ Basis(Vector3(3, 4, 0), Vector3(4, -3, 0), Vector3(0, 0, 5)).is_orthogonal(),
+ "Basis with a flip, rotation, and uniform scale should be orthogonal.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(Vector3(Math_SQRT12, Math_SQRT12, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)).is_orthogonal(),
+ "Basis with the X axis skewed 45 degrees should not be orthogonal.");
+
+ CHECK_MESSAGE(
+ Basis(0, 0, 0, 0, 0, 0, 0, 0, 0).is_orthogonal(),
+ "Edge case: Basis with all zeroes should return true for is_orthogonal, since zero vectors are orthogonal to all vectors.");
+}
+
+TEST_CASE("[Basis] Is orthonormal checks") {
+ CHECK_MESSAGE(
+ Basis().is_orthonormal(),
+ "Identity Basis should be orthonormal.");
+
+ CHECK_MESSAGE(
+ Basis::from_euler(Vector3(1.2, 3.4, 5.6)).is_orthonormal(),
+ "Basis with only rotation should be orthonormal.");
+
+ CHECK_MESSAGE(
+ Basis::from_scale(Vector3(-1, -1, -1)).is_orthonormal(),
+ "Basis with only a flip should be orthonormal.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis::from_scale(Vector3(1.2, 3.4, 5.6)).is_orthonormal(),
+ "Basis with only scale should not be orthonormal.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(Vector3(3, 4, 0), Vector3(4, -3, 0), Vector3(0, 0, 5)).is_orthonormal(),
+ "Basis with a flip, rotation, and uniform scale should not be orthonormal.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(Vector3(Math_SQRT12, Math_SQRT12, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)).is_orthonormal(),
+ "Basis with the X axis skewed 45 degrees should not be orthonormal.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(0, 0, 0, 0, 0, 0, 0, 0, 0).is_orthonormal(),
+ "Edge case: Basis with all zeroes should return false for is_orthonormal, since the vectors do not have a length of 1.");
+}
+
+TEST_CASE("[Basis] Is rotation checks") {
+ CHECK_MESSAGE(
+ Basis().is_rotation(),
+ "Identity Basis should be a rotation (a rotation of zero).");
+
+ CHECK_MESSAGE(
+ Basis::from_euler(Vector3(1.2, 3.4, 5.6)).is_rotation(),
+ "Basis with only rotation should be a rotation.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis::from_scale(Vector3(-1, -1, -1)).is_rotation(),
+ "Basis with only a flip should not be a rotation.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis::from_scale(Vector3(1.2, 3.4, 5.6)).is_rotation(),
+ "Basis with only scale should not be a rotation.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(Vector3(2, 0, 0), Vector3(0, 0.5, 0), Vector3(0, 0, 1)).is_rotation(),
+ "Basis with a squeeze should not be a rotation.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(Vector3(Math_SQRT12, Math_SQRT12, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)).is_rotation(),
+ "Basis with the X axis skewed 45 degrees should not be a rotation.");
+
+ CHECK_FALSE_MESSAGE(
+ Basis(0, 0, 0, 0, 0, 0, 0, 0, 0).is_rotation(),
+ "Edge case: Basis with all zeroes should return false for is_rotation, because it is not just a rotation (has a scale of 0).");
+}
+
} // namespace TestBasis
#endif // TEST_BASIS_H
diff --git a/tests/core/math/test_geometry_3d.h b/tests/core/math/test_geometry_3d.h
index 46a99aa4b6..3d83653f11 100644
--- a/tests/core/math/test_geometry_3d.h
+++ b/tests/core/math/test_geometry_3d.h
@@ -36,39 +36,14 @@
namespace TestGeometry3D {
TEST_CASE("[Geometry3D] Closest Points Between Segments") {
- struct Case {
- Vector3 p_1, p_2, p_3, p_4;
- Vector3 got_1, got_2;
- Vector3 want_1, want_2;
- Case(){};
- Case(Vector3 p_p_1, Vector3 p_p_2, Vector3 p_p_3, Vector3 p_p_4, Vector3 p_want_1, Vector3 p_want_2) :
- p_1(p_p_1), p_2(p_p_2), p_3(p_p_3), p_4(p_p_4), want_1(p_want_1), want_2(p_want_2){};
- };
- Vector<Case> tt;
- tt.push_back(Case(Vector3(1, -1, 1), Vector3(1, 1, -1), Vector3(-1, -2, -1), Vector3(-1, 1, 1), Vector3(1, -0.2, 0.2), Vector3(-1, -0.2, 0.2)));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- Geometry3D::get_closest_points_between_segments(current_case.p_1, current_case.p_2, current_case.p_3, current_case.p_4, current_case.got_1, current_case.got_2);
- CHECK(current_case.got_1.is_equal_approx(current_case.want_1));
- CHECK(current_case.got_2.is_equal_approx(current_case.want_2));
- }
+ Vector3 ps, qt;
+ Geometry3D::get_closest_points_between_segments(Vector3(1, -1, 1), Vector3(1, 1, -1), Vector3(-1, -2, -1), Vector3(-1, 1, 1), ps, qt);
+ CHECK(ps.is_equal_approx(Vector3(1, -0.2, 0.2)));
+ CHECK(qt.is_equal_approx(Vector3(-1, -0.2, 0.2)));
}
TEST_CASE("[Geometry3D] Closest Distance Between Segments") {
- struct Case {
- Vector3 p_1, p_2, p_3, p_4;
- float want;
- Case(){};
- Case(Vector3 p_p_1, Vector3 p_p_2, Vector3 p_p_3, Vector3 p_p_4, float p_want) :
- p_1(p_p_1), p_2(p_p_2), p_3(p_p_3), p_4(p_p_4), want(p_want){};
- };
- Vector<Case> tt;
- tt.push_back(Case(Vector3(1, -2, 0), Vector3(1, 2, 0), Vector3(-1, 2, 0), Vector3(-1, -2, 0), 2.0f));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- float out = Geometry3D::get_closest_distance_between_segments(current_case.p_1, current_case.p_2, current_case.p_3, current_case.p_4);
- CHECK(out == current_case.want);
- }
+ CHECK(Geometry3D::get_closest_distance_between_segments(Vector3(1, -2, 0), Vector3(1, 2, 0), Vector3(-1, 2, 0), Vector3(-1, -2, 0)) == 2.0f);
}
TEST_CASE("[Geometry3D] Build Box Planes") {
@@ -90,61 +65,18 @@ TEST_CASE("[Geometry3D] Build Box Planes") {
}
TEST_CASE("[Geometry3D] Build Capsule Planes") {
- struct Case {
- real_t radius, height;
- int sides, lats;
- Vector3::Axis axis;
- int want_size;
- Case(){};
- Case(real_t p_radius, real_t p_height, int p_sides, int p_lats, Vector3::Axis p_axis, int p_want) :
- radius(p_radius), height(p_height), sides(p_sides), lats(p_lats), axis(p_axis), want_size(p_want){};
- };
- Vector<Case> tt;
- tt.push_back(Case(10, 20, 6, 10, Vector3::Axis(), 126));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- Vector<Plane> capsule = Geometry3D::build_capsule_planes(current_case.radius, current_case.height, current_case.sides, current_case.lats, current_case.axis);
- // Should equal (p_sides * p_lats) * 2 + p_sides
- CHECK(capsule.size() == current_case.want_size);
- }
+ Vector<Plane> capsule = Geometry3D::build_capsule_planes(10, 20, 6, 10);
+ CHECK(capsule.size() == 126);
}
TEST_CASE("[Geometry3D] Build Cylinder Planes") {
- struct Case {
- real_t radius, height;
- int sides;
- Vector3::Axis axis;
- int want_size;
- Case(){};
- Case(real_t p_radius, real_t p_height, int p_sides, Vector3::Axis p_axis, int p_want) :
- radius(p_radius), height(p_height), sides(p_sides), axis(p_axis), want_size(p_want){};
- };
- Vector<Case> tt;
- tt.push_back(Case(3.0f, 10.0f, 10, Vector3::Axis(), 12));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- Vector<Plane> planes = Geometry3D::build_cylinder_planes(current_case.radius, current_case.height, current_case.sides, current_case.axis);
- CHECK(planes.size() == current_case.want_size);
- }
+ Vector<Plane> planes = Geometry3D::build_cylinder_planes(3.0f, 10.0f, 10);
+ CHECK(planes.size() == 12);
}
TEST_CASE("[Geometry3D] Build Sphere Planes") {
- struct Case {
- real_t radius;
- int lats, lons;
- Vector3::Axis axis;
- int want_size;
- Case(){};
- Case(real_t p_radius, int p_lat, int p_lons, Vector3::Axis p_axis, int p_want) :
- radius(p_radius), lats(p_lat), lons(p_lons), axis(p_axis), want_size(p_want){};
- };
- Vector<Case> tt;
- tt.push_back(Case(10.0f, 10, 3, Vector3::Axis(), 63));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- Vector<Plane> planes = Geometry3D::build_sphere_planes(current_case.radius, current_case.lats, current_case.lons, current_case.axis);
- CHECK(planes.size() == 63);
- }
+ Vector<Plane> planes = Geometry3D::build_sphere_planes(10.0f, 10, 3);
+ CHECK(planes.size() == 63);
}
#if false
@@ -175,39 +107,15 @@ TEST_CASE("[Geometry3D] Build Convex Mesh") {
#endif
TEST_CASE("[Geometry3D] Clip Polygon") {
- struct Case {
- Plane clipping_plane;
- Vector<Vector3> polygon;
- bool want;
- Case(){};
- Case(Plane p_clipping_plane, Vector<Vector3> p_polygon, bool p_want) :
- clipping_plane(p_clipping_plane), polygon(p_polygon), want(p_want){};
- };
- Vector<Case> tt;
Vector<Plane> box_planes = Geometry3D::build_box_planes(Vector3(5, 10, 5));
Vector<Vector3> box = Geometry3D::compute_convex_mesh_points(&box_planes[0], box_planes.size());
- tt.push_back(Case(Plane(), box, true));
- tt.push_back(Case(Plane(Vector3(0, 1, 0), Vector3(0, 3, 0)), box, false));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- Vector<Vector3> output = Geometry3D::clip_polygon(current_case.polygon, current_case.clipping_plane);
- if (current_case.want) {
- CHECK(output == current_case.polygon);
- } else {
- CHECK(output != current_case.polygon);
- }
- }
+ Vector<Vector3> output = Geometry3D::clip_polygon(box, Plane());
+ CHECK(output == box);
+ output = Geometry3D::clip_polygon(box, Plane(Vector3(0, 1, 0), Vector3(0, 3, 0)));
+ CHECK(output != box);
}
TEST_CASE("[Geometry3D] Compute Convex Mesh Points") {
- struct Case {
- Vector<Plane> mesh;
- Vector<Vector3> want;
- Case(){};
- Case(Vector<Plane> p_mesh, Vector<Vector3> p_want) :
- mesh(p_mesh), want(p_want){};
- };
- Vector<Case> tt;
Vector<Vector3> cube;
cube.push_back(Vector3(-5, -5, -5));
cube.push_back(Vector3(5, -5, -5));
@@ -217,220 +125,79 @@ TEST_CASE("[Geometry3D] Compute Convex Mesh Points") {
cube.push_back(Vector3(5, -5, 5));
cube.push_back(Vector3(-5, 5, 5));
cube.push_back(Vector3(5, 5, 5));
- tt.push_back(Case(Geometry3D::build_box_planes(Vector3(5, 5, 5)), cube));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- Vector<Vector3> vectors = Geometry3D::compute_convex_mesh_points(&current_case.mesh[0], current_case.mesh.size());
- CHECK(vectors == current_case.want);
- }
+ Vector<Plane> box_planes = Geometry3D::build_box_planes(Vector3(5, 5, 5));
+ CHECK(Geometry3D::compute_convex_mesh_points(&box_planes[0], box_planes.size()) == cube);
}
TEST_CASE("[Geometry3D] Get Closest Point To Segment") {
- struct Case {
- Vector3 point;
- Vector<Vector3> segment;
- Vector3 want;
- Case(){};
- Case(Vector3 p_point, Vector<Vector3> p_segment, Vector3 p_want) :
- point(p_point), segment(p_segment), want(p_want){};
- };
- Vector<Case> tt;
- Vector<Vector3> test_segment;
- test_segment.push_back(Vector3(1, 1, 1));
- test_segment.push_back(Vector3(5, 5, 5));
- tt.push_back(Case(Vector3(2, 1, 4), test_segment, Vector3(2.33333, 2.33333, 2.33333)));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- Vector3 output = Geometry3D::get_closest_point_to_segment(current_case.point, &current_case.segment[0]);
- CHECK(output.is_equal_approx(current_case.want));
- }
+ Vector3 segment[2] = { Vector3(1, 1, 1), Vector3(5, 5, 5) };
+ Vector3 output = Geometry3D::get_closest_point_to_segment(Vector3(2, 1, 4), segment);
+ CHECK(output.is_equal_approx(Vector3(2.33333, 2.33333, 2.33333)));
}
TEST_CASE("[Geometry3D] Plane and Box Overlap") {
- struct Case {
- Vector3 normal, max_box;
- float d;
- bool want;
- Case(){};
- Case(Vector3 p_normal, float p_d, Vector3 p_max_box, bool p_want) :
- normal(p_normal), max_box(p_max_box), d(p_d), want(p_want){};
- };
- Vector<Case> tt;
- tt.push_back(Case(Vector3(3, 4, 2), 5, Vector3(5, 5, 5), true));
- tt.push_back(Case(Vector3(0, 1, 0), -10, Vector3(5, 5, 5), false));
- tt.push_back(Case(Vector3(1, 0, 0), -6, Vector3(5, 5, 5), false));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- bool overlap = Geometry3D::planeBoxOverlap(current_case.normal, current_case.d, current_case.max_box);
- CHECK(overlap == current_case.want);
- }
+ CHECK(Geometry3D::planeBoxOverlap(Vector3(3, 4, 2), 5, Vector3(5, 5, 5)) == true);
+ CHECK(Geometry3D::planeBoxOverlap(Vector3(0, 1, 0), -10, Vector3(5, 5, 5)) == false);
+ CHECK(Geometry3D::planeBoxOverlap(Vector3(1, 0, 0), -6, Vector3(5, 5, 5)) == false);
}
TEST_CASE("[Geometry3D] Is Point in Projected Triangle") {
- struct Case {
- Vector3 point, v_1, v_2, v_3;
- bool want;
- Case(){};
- Case(Vector3 p_point, Vector3 p_v_1, Vector3 p_v_2, Vector3 p_v_3, bool p_want) :
- point(p_point), v_1(p_v_1), v_2(p_v_2), v_3(p_v_3), want(p_want){};
- };
- Vector<Case> tt;
- tt.push_back(Case(Vector3(1, 1, 0), Vector3(3, 0, 0), Vector3(0, 3, 0), Vector3(-3, 0, 0), true));
- tt.push_back(Case(Vector3(5, 1, 0), Vector3(3, 0, 0), Vector3(0, 3, 0), Vector3(-3, 0, 0), false));
- tt.push_back(Case(Vector3(3, 0, 0), Vector3(3, 0, 0), Vector3(0, 3, 0), Vector3(-3, 0, 0), true));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- bool output = Geometry3D::point_in_projected_triangle(current_case.point, current_case.v_1, current_case.v_2, current_case.v_3);
- CHECK(output == current_case.want);
- }
+ CHECK(Geometry3D::point_in_projected_triangle(Vector3(1, 1, 0), Vector3(3, 0, 0), Vector3(0, 3, 0), Vector3(-3, 0, 0)) == true);
+ CHECK(Geometry3D::point_in_projected_triangle(Vector3(5, 1, 0), Vector3(3, 0, 0), Vector3(0, 3, 0), Vector3(-3, 0, 0)) == false);
+ CHECK(Geometry3D::point_in_projected_triangle(Vector3(3, 0, 0), Vector3(3, 0, 0), Vector3(0, 3, 0), Vector3(-3, 0, 0)) == true);
}
TEST_CASE("[Geometry3D] Does Ray Intersect Triangle") {
- struct Case {
- Vector3 from, direction, v_1, v_2, v_3;
- Vector3 *result = nullptr;
- bool want;
- Case(){};
- Case(Vector3 p_from, Vector3 p_direction, Vector3 p_v_1, Vector3 p_v_2, Vector3 p_v_3, bool p_want) :
- from(p_from), direction(p_direction), v_1(p_v_1), v_2(p_v_2), v_3(p_v_3), result(nullptr), want(p_want){};
- };
- Vector<Case> tt;
- tt.push_back(Case(Vector3(0, 1, 1), Vector3(0, 0, -10), Vector3(0, 3, 0), Vector3(-3, 0, 0), Vector3(3, 0, 0), true));
- tt.push_back(Case(Vector3(5, 10, 1), Vector3(0, 0, -10), Vector3(0, 3, 0), Vector3(-3, 0, 0), Vector3(3, 0, 0), false));
- tt.push_back(Case(Vector3(0, 1, 1), Vector3(0, 0, 10), Vector3(0, 3, 0), Vector3(-3, 0, 0), Vector3(3, 0, 0), false));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- bool output = Geometry3D::ray_intersects_triangle(current_case.from, current_case.direction, current_case.v_1, current_case.v_2, current_case.v_3, current_case.result);
- CHECK(output == current_case.want);
- }
+ Vector3 result;
+ CHECK(Geometry3D::ray_intersects_triangle(Vector3(0, 1, 1), Vector3(0, 0, -10), Vector3(0, 3, 0), Vector3(-3, 0, 0), Vector3(3, 0, 0), &result) == true);
+ CHECK(Geometry3D::ray_intersects_triangle(Vector3(5, 10, 1), Vector3(0, 0, -10), Vector3(0, 3, 0), Vector3(-3, 0, 0), Vector3(3, 0, 0), &result) == false);
+ CHECK(Geometry3D::ray_intersects_triangle(Vector3(0, 1, 1), Vector3(0, 0, 10), Vector3(0, 3, 0), Vector3(-3, 0, 0), Vector3(3, 0, 0), &result) == false);
}
TEST_CASE("[Geometry3D] Does Segment Intersect Convex") {
- struct Case {
- Vector3 from, to;
- Vector<Plane> planes;
- Vector3 *result, *normal;
- bool want;
- Case(){};
- Case(Vector3 p_from, Vector3 p_to, Vector<Plane> p_planes, bool p_want) :
- from(p_from), to(p_to), planes(p_planes), result(nullptr), normal(nullptr), want(p_want){};
- };
- Vector<Case> tt;
- tt.push_back(Case(Vector3(10, 10, 10), Vector3(0, 0, 0), Geometry3D::build_box_planes(Vector3(5, 5, 5)), true));
- tt.push_back(Case(Vector3(10, 10, 10), Vector3(5, 5, 5), Geometry3D::build_box_planes(Vector3(5, 5, 5)), true));
- tt.push_back(Case(Vector3(10, 10, 10), Vector3(6, 5, 5), Geometry3D::build_box_planes(Vector3(5, 5, 5)), false));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- bool output = Geometry3D::segment_intersects_convex(current_case.from, current_case.to, &current_case.planes[0], current_case.planes.size(), current_case.result, current_case.normal);
- CHECK(output == current_case.want);
- }
+ Vector<Plane> box_planes = Geometry3D::build_box_planes(Vector3(5, 5, 5));
+ Vector3 result, normal;
+ CHECK(Geometry3D::segment_intersects_convex(Vector3(10, 10, 10), Vector3(0, 0, 0), &box_planes[0], box_planes.size(), &result, &normal) == true);
+ CHECK(Geometry3D::segment_intersects_convex(Vector3(10, 10, 10), Vector3(5, 5, 5), &box_planes[0], box_planes.size(), &result, &normal) == true);
+ CHECK(Geometry3D::segment_intersects_convex(Vector3(10, 10, 10), Vector3(6, 5, 5), &box_planes[0], box_planes.size(), &result, &normal) == false);
}
TEST_CASE("[Geometry3D] Segment Intersects Cylinder") {
- struct Case {
- Vector3 from, to;
- real_t height, radius;
- Vector3 *result, *normal;
- bool want;
- Case(){};
- Case(Vector3 p_from, Vector3 p_to, real_t p_height, real_t p_radius, bool p_want) :
- from(p_from), to(p_to), height(p_height), radius(p_radius), result(nullptr), normal(nullptr), want(p_want){};
- };
- Vector<Case> tt;
- tt.push_back(Case(Vector3(10, 10, 10), Vector3(0, 0, 0), 5, 5, true));
- tt.push_back(Case(Vector3(10, 10, 10), Vector3(6, 6, 6), 5, 5, false));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- bool output = Geometry3D::segment_intersects_cylinder(current_case.from, current_case.to, current_case.height, current_case.radius, current_case.result, current_case.normal);
- CHECK(output == current_case.want);
- }
+ Vector3 result, normal;
+ CHECK(Geometry3D::segment_intersects_cylinder(Vector3(10, 10, 10), Vector3(0, 0, 0), 5, 5, &result, &normal) == true);
+ CHECK(Geometry3D::segment_intersects_cylinder(Vector3(10, 10, 10), Vector3(6, 6, 6), 5, 5, &result, &normal) == false);
}
TEST_CASE("[Geometry3D] Segment Intersects Cylinder") {
- struct Case {
- Vector3 from, to, sphere_pos;
- real_t radius;
- Vector3 *result, *normal;
- bool want;
- Case(){};
- Case(Vector3 p_from, Vector3 p_to, Vector3 p_sphere_pos, real_t p_radius, bool p_want) :
- from(p_from), to(p_to), sphere_pos(p_sphere_pos), radius(p_radius), result(nullptr), normal(nullptr), want(p_want){};
- };
- Vector<Case> tt;
- tt.push_back(Case(Vector3(10, 10, 10), Vector3(0, 0, 0), Vector3(0, 0, 0), 5, true));
- tt.push_back(Case(Vector3(10, 10, 10), Vector3(0, 0, 2.5), Vector3(0, 0, 0), 5, true));
- tt.push_back(Case(Vector3(10, 10, 10), Vector3(5, 5, 5), Vector3(0, 0, 0), 5, false));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- bool output = Geometry3D::segment_intersects_sphere(current_case.from, current_case.to, current_case.sphere_pos, current_case.radius, current_case.result, current_case.normal);
- CHECK(output == current_case.want);
- }
+ Vector3 result, normal;
+ CHECK(Geometry3D::segment_intersects_sphere(Vector3(10, 10, 10), Vector3(0, 0, 0), Vector3(0, 0, 0), 5, &result, &normal) == true);
+ CHECK(Geometry3D::segment_intersects_sphere(Vector3(10, 10, 10), Vector3(0, 0, 2.5), Vector3(0, 0, 0), 5, &result, &normal) == true);
+ CHECK(Geometry3D::segment_intersects_sphere(Vector3(10, 10, 10), Vector3(5, 5, 5), Vector3(0, 0, 0), 5, &result, &normal) == false);
}
TEST_CASE("[Geometry3D] Segment Intersects Triangle") {
- struct Case {
- Vector3 from, to, v_1, v_2, v_3, *result;
- bool want;
- Case(){};
- Case(Vector3 p_from, Vector3 p_to, Vector3 p_v_1, Vector3 p_v_2, Vector3 p_v_3, bool p_want) :
- from(p_from), to(p_to), v_1(p_v_1), v_2(p_v_2), v_3(p_v_3), result(nullptr), want(p_want){};
- };
- Vector<Case> tt;
- tt.push_back(Case(Vector3(1, 1, 1), Vector3(-1, -1, -1), Vector3(-3, 0, 0), Vector3(0, 3, 0), Vector3(3, 0, 0), true));
- tt.push_back(Case(Vector3(1, 1, 1), Vector3(3, 0, 0), Vector3(-3, 0, 0), Vector3(0, 3, 0), Vector3(3, 0, 0), true));
- tt.push_back(Case(Vector3(1, 1, 1), Vector3(10, -1, -1), Vector3(-3, 0, 0), Vector3(0, 3, 0), Vector3(3, 0, 0), false));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- bool output = Geometry3D::segment_intersects_triangle(current_case.from, current_case.to, current_case.v_1, current_case.v_2, current_case.v_3, current_case.result);
- CHECK(output == current_case.want);
- }
+ Vector3 result;
+ CHECK(Geometry3D::segment_intersects_triangle(Vector3(1, 1, 1), Vector3(-1, -1, -1), Vector3(-3, 0, 0), Vector3(0, 3, 0), Vector3(3, 0, 0), &result) == true);
+ CHECK(Geometry3D::segment_intersects_triangle(Vector3(1, 1, 1), Vector3(3, 0, 0), Vector3(-3, 0, 0), Vector3(0, 3, 0), Vector3(3, 0, 0), &result) == true);
+ CHECK(Geometry3D::segment_intersects_triangle(Vector3(1, 1, 1), Vector3(10, -1, -1), Vector3(-3, 0, 0), Vector3(0, 3, 0), Vector3(3, 0, 0), &result) == false);
}
TEST_CASE("[Geometry3D] Triangle and Box Overlap") {
- struct Case {
- Vector3 box_center;
- Vector3 box_half_size;
- Vector3 *tri_verts = nullptr;
- bool want;
- Case(){};
- Case(Vector3 p_center, Vector3 p_half_size, Vector3 *p_verts, bool p_want) :
- box_center(p_center), box_half_size(p_half_size), tri_verts(p_verts), want(p_want){};
- };
- Vector<Case> tt;
- Vector3 GoodTriangle[3] = { Vector3(3, 2, 3), Vector3(2, 2, 1), Vector3(2, 1, 1) };
- tt.push_back(Case(Vector3(0, 0, 0), Vector3(5, 5, 5), GoodTriangle, true));
- Vector3 BadTriangle[3] = { Vector3(100, 100, 100), Vector3(-100, -100, -100), Vector3(10, 10, 10) };
- tt.push_back(Case(Vector3(1000, 1000, 1000), Vector3(1, 1, 1), BadTriangle, false));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- bool output = Geometry3D::triangle_box_overlap(current_case.box_center, current_case.box_half_size, current_case.tri_verts);
- CHECK(output == current_case.want);
- }
+ Vector3 good_triangle[3] = { Vector3(3, 2, 3), Vector3(2, 2, 1), Vector3(2, 1, 1) };
+ CHECK(Geometry3D::triangle_box_overlap(Vector3(0, 0, 0), Vector3(5, 5, 5), good_triangle) == true);
+ Vector3 bad_triangle[3] = { Vector3(100, 100, 100), Vector3(-100, -100, -100), Vector3(10, 10, 10) };
+ CHECK(Geometry3D::triangle_box_overlap(Vector3(1000, 1000, 1000), Vector3(1, 1, 1), bad_triangle) == false);
}
TEST_CASE("[Geometry3D] Triangle and Sphere Intersect") {
- struct Case {
- Vector<Vector3> triangle;
- Vector3 normal, sphere_pos, triangle_contact, sphere_contact;
- real_t sphere_radius;
- bool want;
- Case(){};
- Case(Vector<Vector3> p_triangle, Vector3 p_normal, Vector3 p_sphere_pos, real_t p_sphere_radius, bool p_want) :
- triangle(p_triangle), normal(p_normal), sphere_pos(p_sphere_pos), triangle_contact(Vector3()), sphere_contact(Vector3()), sphere_radius(p_sphere_radius), want(p_want){};
- };
- Vector<Case> tt;
Vector<Vector3> triangle;
triangle.push_back(Vector3(3, 0, 0));
triangle.push_back(Vector3(-3, 0, 0));
triangle.push_back(Vector3(0, 3, 0));
- tt.push_back(Case(triangle, Vector3(0, -1, 0), Vector3(0, 0, 0), 5, true));
- tt.push_back(Case(triangle, Vector3(0, 1, 0), Vector3(0, 0, 0), 5, true));
- tt.push_back(Case(triangle, Vector3(0, 1, 0), Vector3(20, 0, 0), 5, false));
- for (int i = 0; i < tt.size(); ++i) {
- Case current_case = tt[i];
- bool output = Geometry3D::triangle_sphere_intersection_test(&current_case.triangle[0], current_case.normal, current_case.sphere_pos, current_case.sphere_radius, current_case.triangle_contact, current_case.sphere_contact);
- CHECK(output == current_case.want);
- }
+ Vector3 triangle_contact, sphere_contact;
+ CHECK(Geometry3D::triangle_sphere_intersection_test(&triangle[0], Vector3(0, -1, 0), Vector3(0, 0, 0), 5, triangle_contact, sphere_contact) == true);
+ CHECK(Geometry3D::triangle_sphere_intersection_test(&triangle[0], Vector3(0, 1, 0), Vector3(0, 0, 0), 5, triangle_contact, sphere_contact) == true);
+ CHECK(Geometry3D::triangle_sphere_intersection_test(&triangle[0], Vector3(0, 1, 0), Vector3(20, 0, 0), 5, triangle_contact, sphere_contact) == false);
}
} // namespace TestGeometry3D
diff --git a/tests/core/math/test_math_funcs.h b/tests/core/math/test_math_funcs.h
index b6cb9620f1..0a9d9c97d9 100644
--- a/tests/core/math/test_math_funcs.h
+++ b/tests/core/math/test_math_funcs.h
@@ -54,6 +54,8 @@ TEST_CASE("[Math] C++ macros") {
CHECK(SIGN(-5) == -1.0);
CHECK(SIGN(0) == 0.0);
CHECK(SIGN(5) == 1.0);
+ // Check that SIGN(NAN) returns 0.0.
+ CHECK(SIGN(NAN) == 0.0);
}
TEST_CASE("[Math] Power of two functions") {
@@ -108,6 +110,29 @@ TEST_CASE_TEMPLATE("[Math] round/floor/ceil", T, float, double) {
CHECK(Math::ceil((T)-1.9) == (T)-1.0);
}
+TEST_CASE_TEMPLATE("[Math] integer division round up unsigned", T, uint32_t, uint64_t) {
+ CHECK(Math::division_round_up((T)0, (T)64) == 0);
+ CHECK(Math::division_round_up((T)1, (T)64) == 1);
+ CHECK(Math::division_round_up((T)63, (T)64) == 1);
+ CHECK(Math::division_round_up((T)64, (T)64) == 1);
+ CHECK(Math::division_round_up((T)65, (T)64) == 2);
+ CHECK(Math::division_round_up((T)65, (T)1) == 65);
+}
+
+TEST_CASE_TEMPLATE("[Math] integer division round up signed", T, int32_t, int64_t) {
+ CHECK(Math::division_round_up((T)0, (T)64) == 0);
+ CHECK(Math::division_round_up((T)1, (T)64) == 1);
+ CHECK(Math::division_round_up((T)63, (T)64) == 1);
+ CHECK(Math::division_round_up((T)64, (T)64) == 1);
+ CHECK(Math::division_round_up((T)65, (T)64) == 2);
+ CHECK(Math::division_round_up((T)65, (T)1) == 65);
+ CHECK(Math::division_round_up((T)-1, (T)64) == 0);
+ CHECK(Math::division_round_up((T)-1, (T)-1) == 1);
+ CHECK(Math::division_round_up((T)-1, (T)1) == -1);
+ CHECK(Math::division_round_up((T)-1, (T)-2) == 1);
+ CHECK(Math::division_round_up((T)-4, (T)-2) == 2);
+}
+
TEST_CASE_TEMPLATE("[Math] sin/cos/tan", T, float, double) {
CHECK(Math::sin((T)-0.1) == doctest::Approx((T)-0.0998334166));
CHECK(Math::sin((T)0.1) == doctest::Approx((T)0.0998334166));
@@ -175,6 +200,37 @@ TEST_CASE_TEMPLATE("[Math] asin/acos/atan", T, float, double) {
CHECK(Math::atan((T)450.0) == doctest::Approx((T)1.5685741082));
}
+TEST_CASE_TEMPLATE("[Math] asinh/acosh/atanh", T, float, double) {
+ CHECK(Math::asinh((T)-2.0) == doctest::Approx((T)-1.4436354751));
+ CHECK(Math::asinh((T)-0.1) == doctest::Approx((T)-0.0998340788));
+ CHECK(Math::asinh((T)0.1) == doctest::Approx((T)0.0998340788));
+ CHECK(Math::asinh((T)0.5) == doctest::Approx((T)0.4812118250));
+ CHECK(Math::asinh((T)1.0) == doctest::Approx((T)0.8813735870));
+ CHECK(Math::asinh((T)2.0) == doctest::Approx((T)1.4436354751));
+
+ CHECK(Math::acosh((T)-2.0) == doctest::Approx((T)0.0));
+ CHECK(Math::acosh((T)-0.1) == doctest::Approx((T)0.0));
+ CHECK(Math::acosh((T)0.1) == doctest::Approx((T)0.0));
+ CHECK(Math::acosh((T)0.5) == doctest::Approx((T)0.0));
+ CHECK(Math::acosh((T)1.0) == doctest::Approx((T)0.0));
+ CHECK(Math::acosh((T)2.0) == doctest::Approx((T)1.3169578969));
+ CHECK(Math::acosh((T)450.0) == doctest::Approx((T)6.8023935287));
+
+ CHECK(Math::is_inf(Math::atanh((T)-2.0)));
+ CHECK(Math::atanh((T)-2.0) < (T)0.0);
+ CHECK(Math::is_inf(Math::atanh((T)-1.0)));
+ CHECK(Math::atanh((T)-1.0) < (T)0.0);
+ CHECK(Math::atanh((T)-0.1) == doctest::Approx((T)-0.1003353477));
+ CHECK(Math::atanh((T)0.1) == doctest::Approx((T)0.1003353477));
+ CHECK(Math::atanh((T)0.5) == doctest::Approx((T)0.5493061443));
+ CHECK(Math::is_inf(Math::atanh((T)1.0)));
+ CHECK(Math::atanh((T)1.0) > (T)0.0);
+ CHECK(Math::is_inf(Math::atanh((T)1.5)));
+ CHECK(Math::atanh((T)1.5) > (T)0.0);
+ CHECK(Math::is_inf(Math::atanh((T)450.0)));
+ CHECK(Math::atanh((T)450.0) > (T)0.0);
+}
+
TEST_CASE_TEMPLATE("[Math] sinc/sincn/atan2", T, float, double) {
CHECK(Math::sinc((T)-0.1) == doctest::Approx((T)0.9983341665));
CHECK(Math::sinc((T)0.1) == doctest::Approx((T)0.9983341665));
@@ -327,6 +383,25 @@ TEST_CASE_TEMPLATE("[Math] remap", T, float, double) {
CHECK(Math::remap((T)-250.0, (T)-100.0, (T)-200.0, (T)0.0, (T)-1000.0) == doctest::Approx((T)-1500.0));
}
+TEST_CASE_TEMPLATE("[Math] angle_difference", T, float, double) {
+ // Loops around, should return 0.0.
+ CHECK(Math::angle_difference((T)0.0, (T)Math_TAU) == doctest::Approx((T)0.0));
+ CHECK(Math::angle_difference((T)Math_PI, (T)-Math_PI) == doctest::Approx((T)0.0));
+ CHECK(Math::angle_difference((T)0.0, (T)Math_TAU * (T)4.0) == doctest::Approx((T)0.0));
+
+ // Rotation is clockwise, so it should return -PI.
+ CHECK(Math::angle_difference((T)0.0, (T)Math_PI) == doctest::Approx((T)-Math_PI));
+ CHECK(Math::angle_difference((T)0.0, (T)-Math_PI) == doctest::Approx((T)Math_PI));
+ CHECK(Math::angle_difference((T)Math_PI, (T)0.0) == doctest::Approx((T)Math_PI));
+ CHECK(Math::angle_difference((T)-Math_PI, (T)0.0) == doctest::Approx((T)-Math_PI));
+
+ CHECK(Math::angle_difference((T)0.0, (T)3.0) == doctest::Approx((T)3.0));
+ CHECK(Math::angle_difference((T)1.0, (T)-2.0) == doctest::Approx((T)-3.0));
+ CHECK(Math::angle_difference((T)-1.0, (T)2.0) == doctest::Approx((T)3.0));
+ CHECK(Math::angle_difference((T)-2.0, (T)-4.5) == doctest::Approx((T)-2.5));
+ CHECK(Math::angle_difference((T)100.0, (T)102.5) == doctest::Approx((T)2.5));
+}
+
TEST_CASE_TEMPLATE("[Math] lerp_angle", T, float, double) {
// Counter-clockwise rotation.
CHECK(Math::lerp_angle((T)0.24 * Math_TAU, 0.75 * Math_TAU, 0.5) == doctest::Approx((T)-0.005 * Math_TAU));
@@ -357,6 +432,23 @@ TEST_CASE_TEMPLATE("[Math] move_toward", T, float, double) {
CHECK(Math::move_toward(-2.0, -5.0, 4.0) == doctest::Approx((T)-5.0));
}
+TEST_CASE_TEMPLATE("[Math] rotate_toward", T, float, double) {
+ // Rotate toward.
+ CHECK(Math::rotate_toward((T)0.0, (T)Math_PI * (T)0.75, (T)1.5) == doctest::Approx((T)1.5));
+ CHECK(Math::rotate_toward((T)-2.0, (T)1.0, (T)2.5) == doctest::Approx((T)0.5));
+ CHECK(Math::rotate_toward((T)-2.0, (T)Math_PI, (T)Math_PI) == doctest::Approx((T)-Math_PI));
+ CHECK(Math::rotate_toward((T)1.0, (T)Math_PI, (T)20.0) == doctest::Approx((T)Math_PI));
+
+ // Rotate away.
+ CHECK(Math::rotate_toward((T)0.0, (T)0.0, (T)-1.5) == doctest::Approx((T)-1.5));
+ CHECK(Math::rotate_toward((T)0.0, (T)0.0, (T)-Math_PI) == doctest::Approx((T)-Math_PI));
+ CHECK(Math::rotate_toward((T)3.0, (T)Math_PI, (T)-Math_PI) == doctest::Approx((T)0.0));
+ CHECK(Math::rotate_toward((T)2.0, (T)Math_PI, (T)-1.5) == doctest::Approx((T)0.5));
+ CHECK(Math::rotate_toward((T)1.0, (T)2.0, (T)-0.5) == doctest::Approx((T)0.5));
+ CHECK(Math::rotate_toward((T)2.5, (T)2.0, (T)-0.5) == doctest::Approx((T)3.0));
+ CHECK(Math::rotate_toward((T)-1.0, (T)1.0, (T)-1.0) == doctest::Approx((T)-2.0));
+}
+
TEST_CASE_TEMPLATE("[Math] smoothstep", T, float, double) {
CHECK(Math::smoothstep((T)0.0, (T)2.0, (T)-5.0) == doctest::Approx((T)0.0));
CHECK(Math::smoothstep((T)0.0, (T)2.0, (T)0.5) == doctest::Approx((T)0.15625));
diff --git a/tests/core/math/test_transform_2d.h b/tests/core/math/test_transform_2d.h
index ca27776180..36d27ce7a9 100644
--- a/tests/core/math/test_transform_2d.h
+++ b/tests/core/math/test_transform_2d.h
@@ -130,6 +130,36 @@ TEST_CASE("[Transform2D] Finite number checks") {
"Transform2D with three components infinite should not be finite.");
}
+TEST_CASE("[Transform2D] Is conformal checks") {
+ CHECK_MESSAGE(
+ Transform2D().is_conformal(),
+ "Identity Transform2D should be conformal.");
+
+ CHECK_MESSAGE(
+ Transform2D(1.2, Vector2()).is_conformal(),
+ "Transform2D with only rotation should be conformal.");
+
+ CHECK_MESSAGE(
+ Transform2D(Vector2(1, 0), Vector2(0, -1), Vector2()).is_conformal(),
+ "Transform2D with only a flip should be conformal.");
+
+ CHECK_MESSAGE(
+ Transform2D(Vector2(1.2, 0), Vector2(0, 1.2), Vector2()).is_conformal(),
+ "Transform2D with only uniform scale should be conformal.");
+
+ CHECK_MESSAGE(
+ Transform2D(Vector2(1.2, 3.4), Vector2(3.4, -1.2), Vector2()).is_conformal(),
+ "Transform2D with a flip, rotation, and uniform scale should be conformal.");
+
+ CHECK_FALSE_MESSAGE(
+ Transform2D(Vector2(1.2, 0), Vector2(0, 3.4), Vector2()).is_conformal(),
+ "Transform2D with non-uniform scale should not be conformal.");
+
+ CHECK_FALSE_MESSAGE(
+ Transform2D(Vector2(Math_SQRT12, Math_SQRT12), Vector2(0, 1), Vector2()).is_conformal(),
+ "Transform2D with the X axis skewed 45 degrees should not be conformal.");
+}
+
} // namespace TestTransform2D
#endif // TEST_TRANSFORM_2D_H
diff --git a/tests/core/math/test_vector2i.h b/tests/core/math/test_vector2i.h
index 743c87f486..0f33400f7f 100644
--- a/tests/core/math/test_vector2i.h
+++ b/tests/core/math/test_vector2i.h
@@ -87,6 +87,12 @@ TEST_CASE("[Vector2i] Length methods") {
CHECK_MESSAGE(
vector2.length() == doctest::Approx(36.05551275463989293119),
"Vector2i length should work as expected.");
+ CHECK_MESSAGE(
+ vector1.distance_squared_to(vector2) == 500,
+ "Vector2i distance_squared_to should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ vector1.distance_to(vector2) == doctest::Approx(22.36067977499789696409),
+ "Vector2i distance_to should work as expected.");
}
TEST_CASE("[Vector2i] Operators") {
diff --git a/tests/core/math/test_vector3i.h b/tests/core/math/test_vector3i.h
index 485a500715..3914b85a75 100644
--- a/tests/core/math/test_vector3i.h
+++ b/tests/core/math/test_vector3i.h
@@ -90,6 +90,12 @@ TEST_CASE("[Vector3i] Length methods") {
CHECK_MESSAGE(
vector2.length() == doctest::Approx(53.8516480713450403125),
"Vector3i length should work as expected.");
+ CHECK_MESSAGE(
+ vector1.distance_squared_to(vector2) == 1400,
+ "Vector3i distance_squared_to should work as expected and return exact result.");
+ CHECK_MESSAGE(
+ vector1.distance_to(vector2) == doctest::Approx(37.41657386773941385584),
+ "Vector3i distance_to should work as expected.");
}
TEST_CASE("[Vector3i] Operators") {
diff --git a/tests/core/math/test_vector4i.h b/tests/core/math/test_vector4i.h
index 5fda6f1778..31f68696c0 100644
--- a/tests/core/math/test_vector4i.h
+++ b/tests/core/math/test_vector4i.h
@@ -90,6 +90,12 @@ TEST_CASE("[Vector4i] Length methods") {
CHECK_MESSAGE(
vector2.length() == doctest::Approx(73.4846922835),
"Vector4i length should work as expected.");
+ CHECK_MESSAGE(
+ vector1.distance_squared_to(vector2) == 3000,
+ "Vector4i distance_squared_to should work as expected.");
+ CHECK_MESSAGE(
+ vector1.distance_to(vector2) == doctest::Approx(54.772255750517),
+ "Vector4i distance_to should work as expected.");
}
TEST_CASE("[Vector4i] Operators") {
diff --git a/tests/core/object/test_object.h b/tests/core/object/test_object.h
index 8ab6221a1c..e5d91db650 100644
--- a/tests/core/object/test_object.h
+++ b/tests/core/object/test_object.h
@@ -82,6 +82,8 @@ public:
Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid) const override {
return Variant::PACKED_FLOAT32_ARRAY;
}
+ virtual void validate_property(PropertyInfo &p_property) const override {
+ }
bool property_can_revert(const StringName &p_name) const override {
return false;
};
@@ -96,7 +98,7 @@ public:
Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
return Variant();
}
- void notification(int p_notification) override {
+ void notification(int p_notification, bool p_reversed = false) override {
}
Ref<Script> get_script() const override {
return Ref<Script>();
@@ -424,6 +426,74 @@ TEST_CASE("[Object] Signals") {
}
}
+class NotificationObject1 : public Object {
+ GDCLASS(NotificationObject1, Object);
+
+protected:
+ void _notification(int p_what) {
+ switch (p_what) {
+ case 12345: {
+ order_internal1 = order_global++;
+ } break;
+ }
+ }
+
+public:
+ static int order_global;
+ int order_internal1 = -1;
+
+ void reset_order() {
+ order_internal1 = -1;
+ order_global = 1;
+ }
+};
+
+int NotificationObject1::order_global = 1;
+
+class NotificationObject2 : public NotificationObject1 {
+ GDCLASS(NotificationObject2, NotificationObject1);
+
+protected:
+ void _notification(int p_what) {
+ switch (p_what) {
+ case 12345: {
+ order_internal2 = order_global++;
+ } break;
+ }
+ }
+
+public:
+ int order_internal2 = -1;
+ void reset_order() {
+ NotificationObject1::reset_order();
+ order_internal2 = -1;
+ }
+};
+
+TEST_CASE("[Object] Notification order") { // GH-52325
+ NotificationObject2 *test_notification_object = memnew(NotificationObject2);
+
+ SUBCASE("regular order") {
+ test_notification_object->notification(12345, false);
+
+ CHECK_EQ(test_notification_object->order_internal1, 1);
+ CHECK_EQ(test_notification_object->order_internal2, 2);
+
+ test_notification_object->reset_order();
+ }
+
+ SUBCASE("reverse order") {
+ test_notification_object->notification(12345, true);
+
+ CHECK_EQ(test_notification_object->order_internal1, 2);
+ CHECK_EQ(test_notification_object->order_internal2, 1);
+
+ test_notification_object->reset_order();
+ }
+
+ memdelete(test_notification_object);
+}
+
} // namespace TestObject
#endif // TEST_OBJECT_H
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index 659fb003d3..8a11491bb2 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -456,30 +456,93 @@ TEST_CASE("[String] Number to string") {
}
TEST_CASE("[String] String to integer") {
- static const char *nums[4] = { "1237461283", "- 22", "0", " - 1123412" };
- static const int num[4] = { 1237461283, -22, 0, -1123412 };
+ static const char *nums[14] = { "1237461283", "- 22", "0", " - 1123412", "", "10_000_000", "-1_2_3_4", "10__000", " 1 2 34 ", "-0", "007", "--45", "---46", "-7-2" };
+ static const int num[14] = { 1237461283, -22, 0, -1123412, 0, 10000000, -1234, 10000, 1234, 0, 7, 45, -46, -72 };
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < 14; i++) {
CHECK(String(nums[i]).to_int() == num[i]);
}
+ CHECK(String("0b1011").to_int() == 1011); // Looks like a binary number, but to_int() handles this as a base-10 number, "b" is just ignored.
+ CHECK(String("0x1012").to_int() == 1012); // Looks like a hexadecimal number, but to_int() handles this as a base-10 number, "x" is just ignored.
+
+ ERR_PRINT_OFF
+ CHECK(String("999999999999999999999999999999999999999999999999999999999").to_int() == INT64_MAX); // Too large, largest possible is returned.
+ CHECK(String("-999999999999999999999999999999999999999999999999999999999").to_int() == INT64_MIN); // Too small, smallest possible is returned.
+ ERR_PRINT_ON
}
TEST_CASE("[String] Hex to integer") {
- static const char *nums[4] = { "0xFFAE", "22", "0", "AADDAD" };
- static const int64_t num[4] = { 0xFFAE, 0x22, 0, 0xAADDAD };
+ static const char *nums[12] = { "0xFFAE", "22", "0", "AADDAD", "0x7FFFFFFFFFFFFFFF", "-0xf", "", "000", "000f", "0xaA", "-ff", "-" };
+ static const int64_t num[12] = { 0xFFAE, 0x22, 0, 0xAADDAD, 0x7FFFFFFFFFFFFFFF, -0xf, 0, 0, 0xf, 0xaa, -0xff, 0x0 };
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < 12; i++) {
CHECK(String(nums[i]).hex_to_int() == num[i]);
}
+
+ // Invalid hex strings should return 0.
+ static const char *invalid_nums[15] = { "qwerty", "QWERTY", "0xqwerty", "0x00qwerty", "qwerty00", "0x", "0x__", "__", "x12", "+", " ff", "ff ", "f f", "+ff", "--0x78" };
+
+ ERR_PRINT_OFF
+ for (int i = 0; i < 15; i++) {
+ CHECK(String(invalid_nums[i]).hex_to_int() == 0);
+ }
+
+ CHECK(String("0xFFFFFFFFFFFFFFFFFFFFFFF").hex_to_int() == INT64_MAX); // Too large, largest possible is returned.
+ CHECK(String("-0xFFFFFFFFFFFFFFFFFFFFFFF").hex_to_int() == INT64_MIN); // Too small, smallest possible is returned.
+ ERR_PRINT_ON
+}
+
+TEST_CASE("[String] Bin to integer") {
+ static const char *nums[10] = { "", "0", "0b0", "0b1", "0b", "1", "0b1010", "-0b11", "-1010", "0b0111111111111111111111111111111111111111111111111111111111111111" };
+ static const int64_t num[10] = { 0, 0, 0, 1, 0, 1, 10, -3, -10, 0x7FFFFFFFFFFFFFFF };
+
+ for (int i = 0; i < 10; i++) {
+ CHECK(String(nums[i]).bin_to_int() == num[i]);
+ }
+
+ // Invalid bin strings should return 0. The long "0x11...11" is just too long for a 64 bit int.
+ static const char *invalid_nums[16] = { "qwerty", "QWERTY", "0bqwerty", "0b00qwerty", "qwerty00", "0x__", "0b__", "__", "b12", "+", "-", "0x12ab", " 11", "11 ", "1 1", "--0b11" };
+
+ for (int i = 0; i < 16; i++) {
+ CHECK(String(invalid_nums[i]).bin_to_int() == 0);
+ }
+
+ ERR_PRINT_OFF
+ CHECK(String("0b111111111111111111111111111111111111111111111111111111111111111111111111111111111").bin_to_int() == INT64_MAX); // Too large, largest possible is returned.
+ CHECK(String("-0b111111111111111111111111111111111111111111111111111111111111111111111111111111111").bin_to_int() == INT64_MIN); // Too small, smallest possible is returned.
+ ERR_PRINT_ON
}
TEST_CASE("[String] String to float") {
- static const char *nums[4] = { "-12348298412.2", "0.05", "2.0002", " -0.0001" };
- static const double num[4] = { -12348298412.2, 0.05, 2.0002, -0.0001 };
+ static const char *nums[12] = { "-12348298412.2", "0.05", "2.0002", " -0.0001", "0", "000", "123", "0.0", "000.000", "000.007", "234__", "3..14" };
+ static const double num[12] = { -12348298412.2, 0.05, 2.0002, -0.0001, 0.0, 0.0, 123.0, 0.0, 0.0, 0.007, 234.0, 3.0 };
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < 12; i++) {
CHECK(!(ABS(String(nums[i]).to_float() - num[i]) > 0.00001));
}
+
+ // Invalid float strings should return 0.
+ static const char *invalid_nums[6] = { "qwerty", "qwerty123", "0xffff", "0b1010", "--3.13", "__345" };
+
+ for (int i = 0; i < 6; i++) {
+ CHECK(String(invalid_nums[i]).to_float() == 0);
+ }
+
+ // Very large exponents.
+ CHECK(String("1e308").to_float() == 1e308);
+ CHECK(String("-1e308").to_float() == -1e308);
+
+ // Exponent is so high that value is INFINITY/-INFINITY.
+ CHECK(String("1e309").to_float() == INFINITY);
+ CHECK(String("1e511").to_float() == INFINITY);
+ CHECK(String("-1e309").to_float() == -INFINITY);
+ CHECK(String("-1e511").to_float() == -INFINITY);
+
+ // Exponent is so high that a warning message is printed. Value is INFINITY/-INFINITY.
+ ERR_PRINT_OFF
+ CHECK(String("1e512").to_float() == INFINITY);
+ CHECK(String("-1e512").to_float() == -INFINITY);
+ ERR_PRINT_ON
}
TEST_CASE("[String] Slicing") {
@@ -805,6 +868,22 @@ TEST_CASE("[String] sprintf") {
REQUIRE(error == false);
CHECK(output == String("fish +99.990000 frog"));
+ // Real with sign (negative zero).
+ format = "fish %+f frog";
+ args.clear();
+ args.push_back(-0.0);
+ output = format.sprintf(args, &error);
+ REQUIRE(error == false);
+ CHECK(output == String("fish -0.000000 frog"));
+
+ // Real with sign (positive zero).
+ format = "fish %+f frog";
+ args.clear();
+ args.push_back(0.0);
+ output = format.sprintf(args, &error);
+ REQUIRE(error == false);
+ CHECK(output == String("fish +0.000000 frog"));
+
// Real with 1 decimal.
format = "fish %.1f frog";
args.clear();
@@ -1700,7 +1779,7 @@ TEST_CASE("[String] validate_identifier") {
CHECK(empty_string.validate_identifier() == "_");
String numeric_only = "12345";
- CHECK(numeric_only.validate_identifier() == "_2345");
+ CHECK(numeric_only.validate_identifier() == "_12345");
String name_with_spaces = "Name with spaces";
CHECK(name_with_spaces.validate_identifier() == "Name_with_spaces");
diff --git a/tests/core/variant/test_dictionary.h b/tests/core/variant/test_dictionary.h
index 4571de6487..5bc56075da 100644
--- a/tests/core/variant/test_dictionary.h
+++ b/tests/core/variant/test_dictionary.h
@@ -90,7 +90,7 @@ TEST_CASE("[Dictionary] Assignment using bracket notation ([])") {
CHECK(int(map[false]) == 128);
// Ensure read-only maps aren't modified by non-existing keys.
- const auto length = map.size();
+ const int length = map.size();
map.make_read_only();
CHECK(int(map["This key does not exist"].get_type()) == Variant::NIL);
CHECK(map.size() == length);
diff --git a/tests/core/variant/test_variant.h b/tests/core/variant/test_variant.h
index 66b58620d2..54ca06c6c4 100644
--- a/tests/core/variant/test_variant.h
+++ b/tests/core/variant/test_variant.h
@@ -126,7 +126,7 @@ TEST_CASE("[Variant] Writer and parser Variant::FLOAT") {
CHECK_MESSAGE(float_parsed == 1.0e+100, "Should match the double literal.");
}
-TEST_CASE("[Variant] Assignment To Bool from Int,Float,String,Vec2,Vec2i,Vec3,Vec3i and Color") {
+TEST_CASE("[Variant] Assignment To Bool from Int,Float,String,Vec2,Vec2i,Vec3,Vec3i,Vec4,Vec4i,Rect2,Rect2i,Trans2d,Trans3d,Color,Call,Plane,Basis,AABB,Quant,Proj,RID,and Object") {
Variant int_v = 0;
Variant bool_v = true;
int_v = bool_v; // int_v is now a bool
@@ -183,6 +183,54 @@ TEST_CASE("[Variant] Assignment To Bool from Int,Float,String,Vec2,Vec2i,Vec3,Ve
vec3i_v = bool_v;
CHECK(vec3i_v.get_type() == Variant::BOOL);
+ Variant vec4_v = Vector4(0, 0, 0, 0);
+ bool_v = true;
+ vec4_v = bool_v;
+ CHECK(vec4_v == Variant(true));
+ bool_v = false;
+ vec4_v = bool_v;
+ CHECK(vec4_v.get_type() == Variant::BOOL);
+
+ Variant vec4i_v = Vector4i(0, 0, 0, 0);
+ bool_v = true;
+ vec4i_v = bool_v;
+ CHECK(vec4i_v == Variant(true));
+ bool_v = false;
+ vec4i_v = bool_v;
+ CHECK(vec4i_v.get_type() == Variant::BOOL);
+
+ Variant rect2_v = Rect2();
+ bool_v = true;
+ rect2_v = bool_v;
+ CHECK(rect2_v == Variant(true));
+ bool_v = false;
+ rect2_v = bool_v;
+ CHECK(rect2_v.get_type() == Variant::BOOL);
+
+ Variant rect2i_v = Rect2i();
+ bool_v = true;
+ rect2i_v = bool_v;
+ CHECK(rect2i_v == Variant(true));
+ bool_v = false;
+ rect2i_v = bool_v;
+ CHECK(rect2i_v.get_type() == Variant::BOOL);
+
+ Variant transform2d_v = Transform2D();
+ bool_v = true;
+ transform2d_v = bool_v;
+ CHECK(transform2d_v == Variant(true));
+ bool_v = false;
+ transform2d_v = bool_v;
+ CHECK(transform2d_v.get_type() == Variant::BOOL);
+
+ Variant transform3d_v = Transform3D();
+ bool_v = true;
+ transform3d_v = bool_v;
+ CHECK(transform3d_v == Variant(true));
+ bool_v = false;
+ transform3d_v = bool_v;
+ CHECK(transform3d_v.get_type() == Variant::BOOL);
+
Variant col_v = Color(0.5f, 0.2f, 0.75f);
bool_v = true;
col_v = bool_v;
@@ -190,9 +238,74 @@ TEST_CASE("[Variant] Assignment To Bool from Int,Float,String,Vec2,Vec2i,Vec3,Ve
bool_v = false;
col_v = bool_v;
CHECK(col_v.get_type() == Variant::BOOL);
+
+ Variant call_v = Callable();
+ bool_v = true;
+ call_v = bool_v;
+ CHECK(call_v == Variant(true));
+ bool_v = false;
+ call_v = bool_v;
+ CHECK(call_v.get_type() == Variant::BOOL);
+
+ Variant plane_v = Plane();
+ bool_v = true;
+ plane_v = bool_v;
+ CHECK(plane_v == Variant(true));
+ bool_v = false;
+ plane_v = bool_v;
+ CHECK(plane_v.get_type() == Variant::BOOL);
+
+ Variant basis_v = Basis();
+ bool_v = true;
+ basis_v = bool_v;
+ CHECK(basis_v == Variant(true));
+ bool_v = false;
+ basis_v = bool_v;
+ CHECK(basis_v.get_type() == Variant::BOOL);
+
+ Variant aabb_v = AABB();
+ bool_v = true;
+ aabb_v = bool_v;
+ CHECK(aabb_v == Variant(true));
+ bool_v = false;
+ aabb_v = bool_v;
+ CHECK(aabb_v.get_type() == Variant::BOOL);
+
+ Variant quaternion_v = Quaternion();
+ bool_v = true;
+ quaternion_v = bool_v;
+ CHECK(quaternion_v == Variant(true));
+ bool_v = false;
+ quaternion_v = bool_v;
+ CHECK(quaternion_v.get_type() == Variant::BOOL);
+
+ Variant projection_v = Projection();
+ bool_v = true;
+ projection_v = bool_v;
+ CHECK(projection_v == Variant(true));
+ bool_v = false;
+ projection_v = bool_v;
+ CHECK(projection_v.get_type() == Variant::BOOL);
+
+ Variant rid_v = RID();
+ bool_v = true;
+ rid_v = bool_v;
+ CHECK(rid_v == Variant(true));
+ bool_v = false;
+ rid_v = bool_v;
+ CHECK(rid_v.get_type() == Variant::BOOL);
+
+ Object obj_one = Object();
+ Variant object_v = &obj_one;
+ bool_v = true;
+ object_v = bool_v;
+ CHECK(object_v == Variant(true));
+ bool_v = false;
+ object_v = bool_v;
+ CHECK(object_v.get_type() == Variant::BOOL);
}
-TEST_CASE("[Variant] Assignment To Int from Bool,Float,String,Vec2,Vec2i,Vec3,Vec3i and Color") {
+TEST_CASE("[Variant] Assignment To Int from Bool,Float,String,Vec2,Vec2i,Vec3,Vec3i Vec4,Vec4i,Rect2,Rect2i,Trans2d,Trans3d,Color,Call,Plane,Basis,AABB,Quant,Proj,RID,and Object") {
Variant bool_v = false;
Variant int_v = 2;
bool_v = int_v; // Now bool_v is int
@@ -249,6 +362,54 @@ TEST_CASE("[Variant] Assignment To Int from Bool,Float,String,Vec2,Vec2i,Vec3,Ve
vec3i_v = int_v;
CHECK(vec3i_v.get_type() == Variant::INT);
+ Variant vec4_v = Vector4(0, 0, 0, 0);
+ int_v = 2;
+ vec4_v = int_v;
+ CHECK(vec4_v == Variant(2));
+ int_v = -3;
+ vec4_v = int_v;
+ CHECK(vec4_v.get_type() == Variant::INT);
+
+ Variant vec4i_v = Vector4i(0, 0, 0, 0);
+ int_v = 2;
+ vec4i_v = int_v;
+ CHECK(vec4i_v == Variant(2));
+ int_v = -3;
+ vec4i_v = int_v;
+ CHECK(vec4i_v.get_type() == Variant::INT);
+
+ Variant rect2_v = Rect2();
+ int_v = 2;
+ rect2_v = int_v;
+ CHECK(rect2_v == Variant(2));
+ int_v = -3;
+ rect2_v = int_v;
+ CHECK(rect2_v.get_type() == Variant::INT);
+
+ Variant rect2i_v = Rect2i();
+ int_v = 2;
+ rect2i_v = int_v;
+ CHECK(rect2i_v == Variant(2));
+ int_v = -3;
+ rect2i_v = int_v;
+ CHECK(rect2i_v.get_type() == Variant::INT);
+
+ Variant transform2d_v = Transform2D();
+ int_v = 2;
+ transform2d_v = int_v;
+ CHECK(transform2d_v == Variant(2));
+ int_v = -3;
+ transform2d_v = int_v;
+ CHECK(transform2d_v.get_type() == Variant::INT);
+
+ Variant transform3d_v = Transform3D();
+ int_v = 2;
+ transform3d_v = int_v;
+ CHECK(transform3d_v == Variant(2));
+ int_v = -3;
+ transform3d_v = int_v;
+ CHECK(transform3d_v.get_type() == Variant::INT);
+
Variant col_v = Color(0.5f, 0.2f, 0.75f);
int_v = 2;
col_v = int_v;
@@ -256,9 +417,74 @@ TEST_CASE("[Variant] Assignment To Int from Bool,Float,String,Vec2,Vec2i,Vec3,Ve
int_v = -3;
col_v = int_v;
CHECK(col_v.get_type() == Variant::INT);
+
+ Variant call_v = Callable();
+ int_v = 2;
+ call_v = int_v;
+ CHECK(call_v == Variant(2));
+ int_v = -3;
+ call_v = int_v;
+ CHECK(call_v.get_type() == Variant::INT);
+
+ Variant plane_v = Plane();
+ int_v = 2;
+ plane_v = int_v;
+ CHECK(plane_v == Variant(2));
+ int_v = -3;
+ plane_v = int_v;
+ CHECK(plane_v.get_type() == Variant::INT);
+
+ Variant basis_v = Basis();
+ int_v = 2;
+ basis_v = int_v;
+ CHECK(basis_v == Variant(2));
+ int_v = -3;
+ basis_v = int_v;
+ CHECK(basis_v.get_type() == Variant::INT);
+
+ Variant aabb_v = AABB();
+ int_v = 2;
+ aabb_v = int_v;
+ CHECK(aabb_v == Variant(2));
+ int_v = -3;
+ aabb_v = int_v;
+ CHECK(aabb_v.get_type() == Variant::INT);
+
+ Variant quaternion_v = Quaternion();
+ int_v = 2;
+ quaternion_v = int_v;
+ CHECK(quaternion_v == Variant(2));
+ int_v = -3;
+ quaternion_v = int_v;
+ CHECK(quaternion_v.get_type() == Variant::INT);
+
+ Variant projection_v = Projection();
+ int_v = 2;
+ projection_v = int_v;
+ CHECK(projection_v == Variant(2));
+ int_v = -3;
+ projection_v = int_v;
+ CHECK(projection_v.get_type() == Variant::INT);
+
+ Variant rid_v = RID();
+ int_v = 2;
+ rid_v = int_v;
+ CHECK(rid_v == Variant(2));
+ bool_v = -3;
+ rid_v = int_v;
+ CHECK(rid_v.get_type() == Variant::INT);
+
+ Object obj_one = Object();
+ Variant object_v = &obj_one;
+ int_v = 2;
+ object_v = int_v;
+ CHECK(object_v == Variant(2));
+ int_v = -3;
+ object_v = int_v;
+ CHECK(object_v.get_type() == Variant::INT);
}
-TEST_CASE("[Variant] Assignment To Float from Bool,Int,String,Vec2,Vec2i,Vec3,Vec3i and Color") {
+TEST_CASE("[Variant] Assignment To Float from Bool,Int,String,Vec2,Vec2i,Vec3,Vec3i,Vec4,Vec4i,Rect2,Rect2i,Trans2d,Trans3d,Color,Call,Plane,Basis,AABB,Quant,Proj,RID,and Object") {
Variant bool_v = false;
Variant float_v = 1.5f;
bool_v = float_v; // Now bool_v is float
@@ -315,6 +541,54 @@ TEST_CASE("[Variant] Assignment To Float from Bool,Int,String,Vec2,Vec2i,Vec3,Ve
vec3i_v = float_v;
CHECK(vec3i_v.get_type() == Variant::FLOAT);
+ Variant vec4_v = Vector4(0, 0, 0, 0);
+ float_v = 1.5f;
+ vec4_v = float_v;
+ CHECK(vec4_v == Variant(1.5f));
+ float_v = -4.6f;
+ vec4_v = float_v;
+ CHECK(vec4_v.get_type() == Variant::FLOAT);
+
+ Variant vec4i_v = Vector4i(0, 0, 0, 0);
+ float_v = 1.5f;
+ vec4i_v = float_v;
+ CHECK(vec4i_v == Variant(1.5f));
+ float_v = -4.6f;
+ vec4i_v = float_v;
+ CHECK(vec4i_v.get_type() == Variant::FLOAT);
+
+ Variant rect2_v = Rect2();
+ float_v = 1.5f;
+ rect2_v = float_v;
+ CHECK(rect2_v == Variant(1.5f));
+ float_v = -4.6f;
+ rect2_v = float_v;
+ CHECK(rect2_v.get_type() == Variant::FLOAT);
+
+ Variant rect2i_v = Rect2i();
+ float_v = 1.5f;
+ rect2i_v = float_v;
+ CHECK(rect2i_v == Variant(1.5f));
+ float_v = -4.6f;
+ rect2i_v = float_v;
+ CHECK(rect2i_v.get_type() == Variant::FLOAT);
+
+ Variant transform2d_v = Transform2D();
+ float_v = 1.5f;
+ transform2d_v = float_v;
+ CHECK(transform2d_v == Variant(1.5f));
+ float_v = -4.6f;
+ transform2d_v = float_v;
+ CHECK(transform2d_v.get_type() == Variant::FLOAT);
+
+ Variant transform3d_v = Transform3D();
+ float_v = 1.5f;
+ transform3d_v = float_v;
+ CHECK(transform3d_v == Variant(1.5f));
+ float_v = -4.6f;
+ transform3d_v = float_v;
+ CHECK(transform2d_v.get_type() == Variant::FLOAT);
+
Variant col_v = Color(0.5f, 0.2f, 0.75f);
float_v = 1.5f;
col_v = float_v;
@@ -322,9 +596,74 @@ TEST_CASE("[Variant] Assignment To Float from Bool,Int,String,Vec2,Vec2i,Vec3,Ve
float_v = -4.6f;
col_v = float_v;
CHECK(col_v.get_type() == Variant::FLOAT);
+
+ Variant call_v = Callable();
+ float_v = 1.5f;
+ call_v = float_v;
+ CHECK(call_v == Variant(1.5f));
+ float_v = -4.6f;
+ call_v = float_v;
+ CHECK(call_v.get_type() == Variant::FLOAT);
+
+ Variant plane_v = Plane();
+ float_v = 1.5f;
+ plane_v = float_v;
+ CHECK(plane_v == Variant(1.5f));
+ float_v = -4.6f;
+ plane_v = float_v;
+ CHECK(plane_v.get_type() == Variant::FLOAT);
+
+ Variant basis_v = Basis();
+ float_v = 1.5f;
+ basis_v = float_v;
+ CHECK(basis_v == Variant(1.5f));
+ float_v = -4.6f;
+ basis_v = float_v;
+ CHECK(basis_v.get_type() == Variant::FLOAT);
+
+ Variant aabb_v = AABB();
+ float_v = 1.5f;
+ aabb_v = float_v;
+ CHECK(aabb_v == Variant(1.5f));
+ float_v = -4.6f;
+ aabb_v = float_v;
+ CHECK(aabb_v.get_type() == Variant::FLOAT);
+
+ Variant quaternion_v = Quaternion();
+ float_v = 1.5f;
+ quaternion_v = float_v;
+ CHECK(quaternion_v == Variant(1.5f));
+ float_v = -4.6f;
+ quaternion_v = float_v;
+ CHECK(quaternion_v.get_type() == Variant::FLOAT);
+
+ Variant projection_v = Projection();
+ float_v = 1.5f;
+ projection_v = float_v;
+ CHECK(projection_v == Variant(1.5f));
+ float_v = -4.6f;
+ projection_v = float_v;
+ CHECK(projection_v.get_type() == Variant::FLOAT);
+
+ Variant rid_v = RID();
+ float_v = 1.5f;
+ rid_v = float_v;
+ CHECK(rid_v == Variant(1.5f));
+ float_v = -4.6f;
+ rid_v = float_v;
+ CHECK(rid_v.get_type() == Variant::FLOAT);
+
+ Object obj_one = Object();
+ Variant object_v = &obj_one;
+ float_v = 1.5f;
+ object_v = float_v;
+ CHECK(object_v == Variant(1.5f));
+ float_v = -4.6f;
+ object_v = float_v;
+ CHECK(object_v.get_type() == Variant::FLOAT);
}
-TEST_CASE("[Variant] Assignment To String from Bool,Int,Float,Vec2,Vec2i,Vec3,Vec3i and Color") {
+TEST_CASE("[Variant] Assignment To String from Bool,Int,Float,Vec2,Vec2i,Vec3,Vec3i,Vec4,Vec4i,Rect2,Rect2i,Trans2d,Trans3d,Color,Call,Plane,Basis,AABB,Quant,Proj,RID,and Object") {
Variant bool_v = false;
Variant string_v = "Hello";
bool_v = string_v; // Now bool_v is string
@@ -381,6 +720,54 @@ TEST_CASE("[Variant] Assignment To String from Bool,Int,Float,Vec2,Vec2i,Vec3,Ve
vec3i_v = string_v;
CHECK(vec3i_v.get_type() == Variant::STRING);
+ Variant vec4_v = Vector4(0, 0, 0, 0);
+ string_v = "Hello";
+ vec4_v = string_v;
+ CHECK(vec4_v == Variant("Hello"));
+ string_v = "Hello there";
+ vec4_v = string_v;
+ CHECK(vec4_v.get_type() == Variant::STRING);
+
+ Variant vec4i_v = Vector4i(0, 0, 0, 0);
+ string_v = "Hello";
+ vec4i_v = string_v;
+ CHECK(vec4i_v == Variant("Hello"));
+ string_v = "Hello there";
+ vec4i_v = string_v;
+ CHECK(vec4i_v.get_type() == Variant::STRING);
+
+ Variant rect2_v = Rect2();
+ string_v = "Hello";
+ rect2_v = string_v;
+ CHECK(rect2_v == Variant("Hello"));
+ string_v = "Hello there";
+ rect2_v = string_v;
+ CHECK(rect2_v.get_type() == Variant::STRING);
+
+ Variant rect2i_v = Rect2i();
+ string_v = "Hello";
+ rect2i_v = string_v;
+ CHECK(rect2i_v == Variant("Hello"));
+ string_v = "Hello there";
+ rect2i_v = string_v;
+ CHECK(rect2i_v.get_type() == Variant::STRING);
+
+ Variant transform2d_v = Transform2D();
+ string_v = "Hello";
+ transform2d_v = string_v;
+ CHECK(transform2d_v == Variant("Hello"));
+ string_v = "Hello there";
+ transform2d_v = string_v;
+ CHECK(transform2d_v.get_type() == Variant::STRING);
+
+ Variant transform3d_v = Transform3D();
+ string_v = "Hello";
+ transform3d_v = string_v;
+ CHECK(transform3d_v == Variant("Hello"));
+ string_v = "Hello there";
+ transform3d_v = string_v;
+ CHECK(transform3d_v.get_type() == Variant::STRING);
+
Variant col_v = Color(0.5f, 0.2f, 0.75f);
string_v = "Hello";
col_v = string_v;
@@ -388,9 +775,74 @@ TEST_CASE("[Variant] Assignment To String from Bool,Int,Float,Vec2,Vec2i,Vec3,Ve
string_v = "Hello there";
col_v = string_v;
CHECK(col_v.get_type() == Variant::STRING);
+
+ Variant call_v = Callable();
+ string_v = "Hello";
+ call_v = string_v;
+ CHECK(call_v == Variant("Hello"));
+ string_v = "Hello there";
+ call_v = string_v;
+ CHECK(call_v.get_type() == Variant::STRING);
+
+ Variant plane_v = Plane();
+ string_v = "Hello";
+ plane_v = string_v;
+ CHECK(plane_v == Variant("Hello"));
+ string_v = "Hello there";
+ plane_v = string_v;
+ CHECK(plane_v.get_type() == Variant::STRING);
+
+ Variant basis_v = Basis();
+ string_v = "Hello";
+ basis_v = string_v;
+ CHECK(basis_v == Variant("Hello"));
+ string_v = "Hello there";
+ basis_v = string_v;
+ CHECK(basis_v.get_type() == Variant::STRING);
+
+ Variant aabb_v = AABB();
+ string_v = "Hello";
+ aabb_v = string_v;
+ CHECK(aabb_v == Variant("Hello"));
+ string_v = "Hello there";
+ aabb_v = string_v;
+ CHECK(aabb_v.get_type() == Variant::STRING);
+
+ Variant quaternion_v = Quaternion();
+ string_v = "Hello";
+ quaternion_v = string_v;
+ CHECK(quaternion_v == Variant("Hello"));
+ string_v = "Hello there";
+ quaternion_v = string_v;
+ CHECK(quaternion_v.get_type() == Variant::STRING);
+
+ Variant projection_v = Projection();
+ string_v = "Hello";
+ projection_v = string_v;
+ CHECK(projection_v == Variant("Hello"));
+ string_v = "Hello there";
+ projection_v = string_v;
+ CHECK(projection_v.get_type() == Variant::STRING);
+
+ Variant rid_v = RID();
+ string_v = "Hello";
+ rid_v = string_v;
+ CHECK(rid_v == Variant("Hello"));
+ string_v = "Hello there";
+ rid_v = string_v;
+ CHECK(rid_v.get_type() == Variant::STRING);
+
+ Object obj_one = Object();
+ Variant object_v = &obj_one;
+ string_v = "Hello";
+ object_v = string_v;
+ CHECK(object_v == Variant("Hello"));
+ string_v = "Hello there";
+ object_v = string_v;
+ CHECK(object_v.get_type() == Variant::STRING);
}
-TEST_CASE("[Variant] Assignment To Vec2 from Bool,Int,Float,String,Vec2i,Vec3,Vec3i and Color") {
+TEST_CASE("[Variant] Assignment To Vec2 from Bool,Int,Float,String,Vec2i,Vec3,Vec3i,Vec4,Vec4i,Rect2,Rect2i,Trans2d,Trans3d,Color,Call,Plane,Basis,AABB,Quant,Proj,RID,and Object") {
Variant bool_v = false;
Variant vec2_v = Vector2(2.2f, 3.5f);
bool_v = vec2_v; // Now bool_v is Vector2
@@ -447,6 +899,54 @@ TEST_CASE("[Variant] Assignment To Vec2 from Bool,Int,Float,String,Vec2i,Vec3,Ve
vec3i_v = vec2_v;
CHECK(vec3i_v.get_type() == Variant::VECTOR2);
+ Variant vec4_v = Vector4(0, 0, 0, 0);
+ vec2_v = Vector2(2.2f, 3.5f);
+ vec4_v = vec2_v;
+ CHECK(vec4_v == Variant(Vector2(2.2f, 3.5f)));
+ vec2_v = Vector2(-5.4f, -7.9f);
+ vec4_v = vec2_v;
+ CHECK(vec4_v.get_type() == Variant::VECTOR2);
+
+ Variant vec4i_v = Vector4i(0, 0, 0, 0);
+ vec2_v = Vector2(2.2f, 3.5f);
+ vec4i_v = vec2_v;
+ CHECK(vec4i_v == Variant(Vector2(2.2f, 3.5f)));
+ vec2_v = Vector2(-5.4f, -7.9f);
+ vec4i_v = vec2_v;
+ CHECK(vec4i_v.get_type() == Variant::VECTOR2);
+
+ Variant rect2_v = Rect2();
+ vec2_v = Vector2(2.2f, 3.5f);
+ rect2_v = vec2_v;
+ CHECK(rect2_v == Variant(Vector2(2.2f, 3.5f)));
+ vec2_v = Vector2(-5.4f, -7.9f);
+ rect2_v = vec2_v;
+ CHECK(rect2_v.get_type() == Variant::VECTOR2);
+
+ Variant rect2i_v = Rect2i();
+ vec2_v = Vector2(2.2f, 3.5f);
+ rect2i_v = vec2_v;
+ CHECK(rect2i_v == Variant(Vector2(2.2f, 3.5f)));
+ vec2_v = Vector2(-5.4f, -7.9f);
+ rect2i_v = vec2_v;
+ CHECK(rect2i_v.get_type() == Variant::VECTOR2);
+
+ Variant transform2d_v = Transform2D();
+ vec2_v = Vector2(2.2f, 3.5f);
+ transform2d_v = vec2_v;
+ CHECK(transform2d_v == Variant(Vector2(2.2f, 3.5f)));
+ vec2_v = Vector2(-5.4f, -7.9f);
+ transform2d_v = vec2_v;
+ CHECK(transform2d_v.get_type() == Variant::VECTOR2);
+
+ Variant transform3d_v = Transform3D();
+ vec2_v = Vector2(2.2f, 3.5f);
+ transform3d_v = vec2_v;
+ CHECK(transform3d_v == Variant(Vector2(2.2f, 3.5f)));
+ vec2_v = Vector2(-5.4f, -7.9f);
+ transform3d_v = vec2_v;
+ CHECK(transform3d_v.get_type() == Variant::VECTOR2);
+
Variant col_v = Color(0.5f, 0.2f, 0.75f);
vec2_v = Vector2(2.2f, 3.5f);
col_v = vec2_v;
@@ -454,9 +954,74 @@ TEST_CASE("[Variant] Assignment To Vec2 from Bool,Int,Float,String,Vec2i,Vec3,Ve
vec2_v = Vector2(-5.4f, -7.9f);
col_v = vec2_v;
CHECK(col_v.get_type() == Variant::VECTOR2);
+
+ Variant call_v = Callable();
+ vec2_v = Vector2(2.2f, 3.5f);
+ call_v = vec2_v;
+ CHECK(call_v == Variant(Vector2(2.2f, 3.5f)));
+ vec2_v = Vector2(-5.4f, -7.9f);
+ call_v = vec2_v;
+ CHECK(call_v.get_type() == Variant::VECTOR2);
+
+ Variant plane_v = Plane();
+ vec2_v = Vector2(2.2f, 3.5f);
+ plane_v = vec2_v;
+ CHECK(plane_v == Variant(Vector2(2.2f, 3.5f)));
+ vec2_v = Vector2(-5.4f, -7.9f);
+ plane_v = vec2_v;
+ CHECK(plane_v.get_type() == Variant::VECTOR2);
+
+ Variant basis_v = Basis();
+ vec2_v = Vector2(2.2f, 3.5f);
+ basis_v = vec2_v;
+ CHECK(basis_v == Variant(Vector2(2.2f, 3.5f)));
+ vec2_v = Vector2(-5.4f, -7.9f);
+ basis_v = vec2_v;
+ CHECK(basis_v.get_type() == Variant::VECTOR2);
+
+ Variant aabb_v = AABB();
+ vec2_v = Vector2(2.2f, 3.5f);
+ aabb_v = vec2_v;
+ CHECK(aabb_v == Variant(Vector2(2.2f, 3.5f)));
+ vec2_v = Vector2(-5.4f, -7.9f);
+ aabb_v = vec2_v;
+ CHECK(aabb_v.get_type() == Variant::VECTOR2);
+
+ Variant quaternion_v = Quaternion();
+ vec2_v = Vector2(2.2f, 3.5f);
+ quaternion_v = vec2_v;
+ CHECK(quaternion_v == Variant(Vector2(2.2f, 3.5f)));
+ vec2_v = Vector2(-5.4f, -7.9f);
+ quaternion_v = vec2_v;
+ CHECK(quaternion_v.get_type() == Variant::VECTOR2);
+
+ Variant projection_v = Projection();
+ vec2_v = Vector2(2.2f, 3.5f);
+ projection_v = vec2_v;
+ CHECK(projection_v == Variant(Vector2(2.2f, 3.5f)));
+ vec2_v = Vector2(-5.4f, -7.9f);
+ projection_v = vec2_v;
+ CHECK(projection_v.get_type() == Variant::VECTOR2);
+
+ Variant rid_v = RID();
+ vec2_v = Vector2(2.2f, 3.5f);
+ rid_v = vec2_v;
+ CHECK(rid_v == Variant(Vector2(2.2f, 3.5f)));
+ vec2_v = Vector2(-5.4f, -7.9f);
+ rid_v = vec2_v;
+ CHECK(rid_v.get_type() == Variant::VECTOR2);
+
+ Object obj_one = Object();
+ Variant object_v = &obj_one;
+ vec2_v = Vector2(2.2f, 3.5f);
+ object_v = vec2_v;
+ CHECK(object_v == Variant(Vector2(2.2f, 3.5f)));
+ vec2_v = Vector2(-5.4f, -7.9f);
+ object_v = vec2_v;
+ CHECK(object_v.get_type() == Variant::VECTOR2);
}
-TEST_CASE("[Variant] Assignment To Vec2i from Bool,Int,Float,String,Vec2,Vec3,Vec3i and Color") {
+TEST_CASE("[Variant] Assignment To Vec2i from Bool,Int,Float,String,Vec2,Vec3,Vec3i,Vec4,Vec4i,Rect2,Rect2i,Trans2d,Trans3d,Color,Call,Plane,Basis,AABB,Quant,Proj,RID,and Object") {
Variant bool_v = false;
Variant vec2i_v = Vector2i(2, 3);
bool_v = vec2i_v; // Now bool_v is Vector2i
@@ -513,6 +1078,54 @@ TEST_CASE("[Variant] Assignment To Vec2i from Bool,Int,Float,String,Vec2,Vec3,Ve
vec3i_v = vec2i_v;
CHECK(vec3i_v.get_type() == Variant::VECTOR2I);
+ Variant vec4_v = Vector4(0, 0, 0, 0);
+ vec2i_v = Vector2i(2, 3);
+ vec4_v = vec2i_v;
+ CHECK(vec4_v == Variant(Vector2i(2, 3)));
+ vec2i_v = Vector2i(-5, -7);
+ vec4_v = vec2i_v;
+ CHECK(vec4_v.get_type() == Variant::VECTOR2I);
+
+ Variant vec4i_v = Vector4i(0, 0, 0, 0);
+ vec2i_v = Vector2i(2, 3);
+ vec4i_v = vec2i_v;
+ CHECK(vec4i_v == Variant(Vector2i(2, 3)));
+ vec2i_v = Vector2i(-5, -7);
+ vec4i_v = vec2i_v;
+ CHECK(vec4i_v.get_type() == Variant::VECTOR2I);
+
+ Variant rect2_v = Rect2();
+ vec2i_v = Vector2i(2, 3);
+ rect2_v = vec2i_v;
+ CHECK(rect2_v == Variant(Vector2i(2, 3)));
+ vec2i_v = Vector2i(-5, -7);
+ rect2_v = vec2i_v;
+ CHECK(rect2_v.get_type() == Variant::VECTOR2I);
+
+ Variant rect2i_v = Rect2i();
+ vec2i_v = Vector2i(2, 3);
+ rect2i_v = vec2i_v;
+ CHECK(rect2i_v == Variant(Vector2i(2, 3)));
+ vec2i_v = Vector2i(-5, -7);
+ rect2i_v = vec2i_v;
+ CHECK(rect2i_v.get_type() == Variant::VECTOR2I);
+
+ Variant transform2d_v = Transform2D();
+ vec2i_v = Vector2i(2, 3);
+ transform2d_v = vec2i_v;
+ CHECK(transform2d_v == Variant(Vector2i(2, 3)));
+ vec2i_v = Vector2i(-5, -7);
+ transform2d_v = vec2i_v;
+ CHECK(transform2d_v.get_type() == Variant::VECTOR2I);
+
+ Variant transform3d_v = Transform3D();
+ vec2i_v = Vector2i(2, 3);
+ transform3d_v = vec2i_v;
+ CHECK(transform3d_v == Variant(Vector2i(2, 3)));
+ vec2i_v = Vector2i(-5, -7);
+ transform3d_v = vec2i_v;
+ CHECK(transform3d_v.get_type() == Variant::VECTOR2I);
+
Variant col_v = Color(0.5f, 0.2f, 0.75f);
vec2i_v = Vector2i(2, 3);
col_v = vec2i_v;
@@ -520,9 +1133,74 @@ TEST_CASE("[Variant] Assignment To Vec2i from Bool,Int,Float,String,Vec2,Vec3,Ve
vec2i_v = Vector2i(-5, -7);
col_v = vec2i_v;
CHECK(col_v.get_type() == Variant::VECTOR2I);
+
+ Variant call_v = Callable();
+ vec2i_v = Vector2i(2, 3);
+ call_v = vec2i_v;
+ CHECK(call_v == Variant(Vector2i(2, 3)));
+ vec2i_v = Vector2i(-5, -7);
+ call_v = vec2i_v;
+ CHECK(call_v.get_type() == Variant::VECTOR2I);
+
+ Variant plane_v = Plane();
+ vec2i_v = Vector2i(2, 3);
+ plane_v = vec2i_v;
+ CHECK(plane_v == Variant(Vector2i(2, 3)));
+ vec2i_v = Vector2i(-5, -7);
+ plane_v = vec2i_v;
+ CHECK(plane_v.get_type() == Variant::VECTOR2I);
+
+ Variant basis_v = Basis();
+ vec2i_v = Vector2i(2, 3);
+ basis_v = vec2i_v;
+ CHECK(basis_v == Variant(Vector2i(2, 3)));
+ vec2i_v = Vector2i(-5, -7);
+ basis_v = vec2i_v;
+ CHECK(basis_v.get_type() == Variant::VECTOR2I);
+
+ Variant aabb_v = AABB();
+ vec2i_v = Vector2i(2, 3);
+ aabb_v = vec2i_v;
+ CHECK(aabb_v == Variant(Vector2i(2, 3)));
+ vec2i_v = Vector2i(-5, -7);
+ aabb_v = vec2i_v;
+ CHECK(aabb_v.get_type() == Variant::VECTOR2I);
+
+ Variant quaternion_v = Quaternion();
+ vec2i_v = Vector2i(2, 3);
+ quaternion_v = vec2i_v;
+ CHECK(quaternion_v == Variant(Vector2i(2, 3)));
+ vec2i_v = Vector2i(-5, -7);
+ quaternion_v = vec2i_v;
+ CHECK(quaternion_v.get_type() == Variant::VECTOR2I);
+
+ Variant projection_v = Projection();
+ vec2i_v = Vector2i(2, 3);
+ projection_v = vec2i_v;
+ CHECK(projection_v == Variant(Vector2i(2, 3)));
+ vec2i_v = Vector2i(-5, -7);
+ projection_v = vec2i_v;
+ CHECK(projection_v.get_type() == Variant::VECTOR2I);
+
+ Variant rid_v = RID();
+ vec2i_v = Vector2i(2, 3);
+ rid_v = vec2i_v;
+ CHECK(rid_v == Variant(Vector2i(2, 3)));
+ vec2i_v = Vector2i(-5, -7);
+ rid_v = vec2i_v;
+ CHECK(rid_v.get_type() == Variant::VECTOR2I);
+
+ Object obj_one = Object();
+ Variant object_v = &obj_one;
+ vec2i_v = Vector2i(2, 3);
+ object_v = vec2i_v;
+ CHECK(object_v == Variant(Vector2i(2, 3)));
+ vec2i_v = Vector2i(-5, -7);
+ object_v = vec2i_v;
+ CHECK(object_v.get_type() == Variant::VECTOR2I);
}
-TEST_CASE("[Variant] Assignment To Vec3 from Bool,Int,Float,String,Vec2,Vec2i,Vec3i and Color") {
+TEST_CASE("[Variant] Assignment To Vec3 from Bool,Int,Float,String,Vec2,Vec2i,Vec3i,Vec4,Vec4i,Rect2,Rect2i,Trans2d,Trans3d,Color,Call,Plane,Basis,AABB,Quant,Proj,RID,and Object") {
Variant bool_v = false;
Variant vec3_v = Vector3(2.2f, 3.5f, 5.3f);
bool_v = vec3_v; // Now bool_v is Vector3
@@ -579,6 +1257,54 @@ TEST_CASE("[Variant] Assignment To Vec3 from Bool,Int,Float,String,Vec2,Vec2i,Ve
vec3i_v = vec3_v;
CHECK(vec3i_v.get_type() == Variant::VECTOR3);
+ Variant vec4_v = Vector4(0, 0, 0, 0);
+ vec3_v = Vector3(2.2f, 3.5f, 5.3f);
+ vec4_v = vec3_v;
+ CHECK(vec4_v == Variant(Vector3(2.2f, 3.5f, 5.3f)));
+ vec3_v = Vector3(-5.4f, -7.9f, -2.1f);
+ vec4_v = vec3_v;
+ CHECK(vec4_v.get_type() == Variant::VECTOR3);
+
+ Variant vec4i_v = Vector4i(0, 0, 0, 0);
+ vec3_v = Vector3(2.2f, 3.5f, 5.3f);
+ vec4i_v = vec3_v;
+ CHECK(vec4i_v == Variant(Vector3(2.2f, 3.5f, 5.3f)));
+ vec3_v = Vector3(-5.4f, -7.9f, -2.1f);
+ vec4i_v = vec3_v;
+ CHECK(vec4i_v.get_type() == Variant::VECTOR3);
+
+ Variant rect2_v = Rect2();
+ vec3_v = Vector3(2.2f, 3.5f, 5.3f);
+ rect2_v = vec3_v;
+ CHECK(rect2_v == Variant(Vector3(2.2f, 3.5f, 5.3f)));
+ vec3_v = Vector3(-5.4f, -7.9f, -2.1f);
+ rect2_v = vec3_v;
+ CHECK(rect2_v.get_type() == Variant::VECTOR3);
+
+ Variant rect2i_v = Rect2i();
+ vec3_v = Vector3(2.2f, 3.5f, 5.3f);
+ rect2i_v = vec3_v;
+ CHECK(rect2i_v == Variant(Vector3(2.2f, 3.5f, 5.3f)));
+ vec3_v = Vector3(-5.4f, -7.9f, -2.1f);
+ rect2i_v = vec3_v;
+ CHECK(rect2i_v.get_type() == Variant::VECTOR3);
+
+ Variant transform2d_v = Transform2D();
+ vec3_v = Vector3(2.2f, 3.5f, 5.3f);
+ transform2d_v = vec3_v;
+ CHECK(transform2d_v == Variant(Vector3(2.2f, 3.5f, 5.3f)));
+ vec3_v = Vector3(-5.4f, -7.9f, -2.1f);
+ transform2d_v = vec3_v;
+ CHECK(transform2d_v.get_type() == Variant::VECTOR3);
+
+ Variant transform3d_v = Transform3D();
+ vec3_v = Vector3(2.2f, 3.5f, 5.3f);
+ transform3d_v = vec3_v;
+ CHECK(transform3d_v == Variant(Vector3(2.2f, 3.5f, 5.3f)));
+ vec3_v = Vector3(-5.4f, -7.9f, -2.1f);
+ transform3d_v = vec3_v;
+ CHECK(transform3d_v.get_type() == Variant::VECTOR3);
+
Variant col_v = Color(0.5f, 0.2f, 0.75f);
vec3_v = Vector3(2.2f, 3.5f, 5.3f);
col_v = vec3_v;
@@ -586,6 +1312,71 @@ TEST_CASE("[Variant] Assignment To Vec3 from Bool,Int,Float,String,Vec2,Vec2i,Ve
vec3_v = Vector3(-5.4f, -7.9f, -2.1f);
col_v = vec3_v;
CHECK(col_v.get_type() == Variant::VECTOR3);
+
+ Variant call_v = Callable();
+ vec3_v = Vector3(2.2f, 3.5f, 5.3f);
+ call_v = vec3_v;
+ CHECK(call_v == Variant(Vector3(2.2f, 3.5f, 5.3f)));
+ vec3_v = Vector3(-5.4f, -7.9f, -2.1f);
+ call_v = vec3_v;
+ CHECK(call_v.get_type() == Variant::VECTOR3);
+
+ Variant plane_v = Plane();
+ vec3_v = Vector3(2.2f, 3.5f, 5.3f);
+ plane_v = vec3_v;
+ CHECK(plane_v == Variant(Vector3(2.2f, 3.5f, 5.3f)));
+ vec3_v = Vector3(-5.4f, -7.9f, -2.1f);
+ plane_v = vec3_v;
+ CHECK(plane_v.get_type() == Variant::VECTOR3);
+
+ Variant basis_v = Basis();
+ vec3_v = Vector3(2.2f, 3.5f, 5.3f);
+ basis_v = vec3_v;
+ CHECK(basis_v == Variant(Vector3(2.2f, 3.5f, 5.3f)));
+ vec3_v = Vector3(-5.4f, -7.9f, -2.1f);
+ basis_v = vec3_v;
+ CHECK(basis_v.get_type() == Variant::VECTOR3);
+
+ Variant aabb_v = AABB();
+ vec3_v = Vector3(2.2f, 3.5f, 5.3f);
+ aabb_v = vec3_v;
+ CHECK(aabb_v == Variant(Vector3(2.2f, 3.5f, 5.3f)));
+ vec3_v = Vector3(-5.4f, -7.9f, -2.1f);
+ aabb_v = vec3_v;
+ CHECK(aabb_v.get_type() == Variant::VECTOR3);
+
+ Variant quaternion_v = Quaternion();
+ vec3_v = Vector3(2.2f, 3.5f, 5.3f);
+ quaternion_v = vec3_v;
+ CHECK(quaternion_v == Variant(Vector3(2.2f, 3.5f, 5.3f)));
+ vec3_v = Vector3(-5.4f, -7.9f, -2.1f);
+ quaternion_v = vec3_v;
+ CHECK(quaternion_v.get_type() == Variant::VECTOR3);
+
+ Variant projection_v = Projection();
+ vec3_v = Vector3(2.2f, 3.5f, 5.3f);
+ quaternion_v = vec3_v;
+ CHECK(quaternion_v == Variant(Vector3(2.2f, 3.5f, 5.3f)));
+ vec3_v = Vector3(-5.4f, -7.9f, -2.1f);
+ quaternion_v = vec3_v;
+ CHECK(quaternion_v.get_type() == Variant::VECTOR3);
+
+ Variant rid_v = RID();
+ vec3_v = Vector3(2.2f, 3.5f, 5.3f);
+ rid_v = vec3_v;
+ CHECK(rid_v == Variant(Vector3(2.2f, 3.5f, 5.3f)));
+ vec3_v = Vector3(-5.4f, -7.9f, -2.1f);
+ rid_v = vec3_v;
+ CHECK(rid_v.get_type() == Variant::VECTOR3);
+
+ Object obj_one = Object();
+ Variant object_v = &obj_one;
+ vec3_v = Vector3(2.2f, 3.5f, 5.3f);
+ object_v = vec3_v;
+ CHECK(object_v == Variant(Vector3(2.2f, 3.5f, 5.3f)));
+ vec3_v = Vector3(-5.4f, -7.9f, -2.1f);
+ object_v = vec3_v;
+ CHECK(object_v.get_type() == Variant::VECTOR3);
}
TEST_CASE("[Variant] Assignment To Vec3i from Bool,Int,Float,String,Vec2,Vec2i,Vec3 and Color") {
@@ -645,6 +1436,54 @@ TEST_CASE("[Variant] Assignment To Vec3i from Bool,Int,Float,String,Vec2,Vec2i,V
vec3_v = vec3i_v;
CHECK(vec3_v.get_type() == Variant::VECTOR3I);
+ Variant vec4_v = Vector4(0, 0, 0, 0);
+ vec3i_v = Vector3i(2, 3, 5);
+ vec4_v = vec3i_v;
+ CHECK(vec4_v == Variant(Vector3i(2, 3, 5)));
+ vec3i_v = Vector3i(-5, -7, -2);
+ vec4_v = vec3i_v;
+ CHECK(vec4_v.get_type() == Variant::VECTOR3I);
+
+ Variant vec4i_v = Vector4i(0, 0, 0, 0);
+ vec3i_v = Vector3i(2, 3, 5);
+ vec4i_v = vec3i_v;
+ CHECK(vec4i_v == Variant(Vector3i(2, 3, 5)));
+ vec3i_v = Vector3i(-5, -7, -2);
+ vec4i_v = vec3i_v;
+ CHECK(vec4i_v.get_type() == Variant::VECTOR3I);
+
+ Variant rect2_v = Rect2();
+ vec3i_v = Vector3i(2, 3, 5);
+ rect2_v = vec3i_v;
+ CHECK(rect2_v == Variant(Vector3i(2, 3, 5)));
+ vec3i_v = Vector3i(-5, -7, -2);
+ rect2_v = vec3i_v;
+ CHECK(rect2_v.get_type() == Variant::VECTOR3I);
+
+ Variant rect2i_v = Rect2i();
+ vec3i_v = Vector3i(2, 3, 5);
+ rect2i_v = vec3i_v;
+ CHECK(rect2i_v == Variant(Vector3i(2, 3, 5)));
+ vec3i_v = Vector3i(-5, -7, -2);
+ rect2i_v = vec3i_v;
+ CHECK(rect2i_v.get_type() == Variant::VECTOR3I);
+
+ Variant transform2d_v = Transform2D();
+ vec3i_v = Vector3i(2, 3, 5);
+ transform2d_v = vec3i_v;
+ CHECK(transform2d_v == Variant(Vector3i(2, 3, 5)));
+ vec3i_v = Vector3i(-5, -7, -2);
+ transform2d_v = vec3i_v;
+ CHECK(transform2d_v.get_type() == Variant::VECTOR3I);
+
+ Variant transform3d_v = Transform3D();
+ vec3i_v = Vector3i(2, 3, 5);
+ transform3d_v = vec3i_v;
+ CHECK(transform3d_v == Variant(Vector3i(2, 3, 5)));
+ vec3i_v = Vector3i(-5, -7, -2);
+ transform3d_v = vec3i_v;
+ CHECK(transform3d_v.get_type() == Variant::VECTOR3I);
+
Variant col_v = Color(0.5f, 0.2f, 0.75f);
vec3i_v = Vector3i(2, 3, 5);
col_v = vec3i_v;
@@ -652,9 +1491,74 @@ TEST_CASE("[Variant] Assignment To Vec3i from Bool,Int,Float,String,Vec2,Vec2i,V
vec3i_v = Vector3i(-5, -7, -2);
col_v = vec3i_v;
CHECK(col_v.get_type() == Variant::VECTOR3I);
+
+ Variant call_v = Callable();
+ vec3i_v = Vector3i(2, 3, 5);
+ call_v = vec3i_v;
+ CHECK(call_v == Variant(Vector3i(2, 3, 5)));
+ vec3i_v = Vector3i(-5, -7, -2);
+ call_v = vec3i_v;
+ CHECK(call_v.get_type() == Variant::VECTOR3I);
+
+ Variant plane_v = Plane();
+ vec3i_v = Vector3i(2, 3, 5);
+ plane_v = vec3i_v;
+ CHECK(plane_v == Variant(Vector3i(2, 3, 5)));
+ vec3i_v = Vector3i(-5, -7, -2);
+ plane_v = vec3i_v;
+ CHECK(plane_v.get_type() == Variant::VECTOR3I);
+
+ Variant basis_v = Basis();
+ vec3i_v = Vector3i(2, 3, 5);
+ basis_v = vec3i_v;
+ CHECK(basis_v == Variant(Vector3i(2, 3, 5)));
+ vec3i_v = Vector3i(-5, -7, -2);
+ basis_v = vec3i_v;
+ CHECK(basis_v.get_type() == Variant::VECTOR3I);
+
+ Variant aabb_v = AABB();
+ vec3i_v = Vector3i(2, 3, 5);
+ aabb_v = vec3i_v;
+ CHECK(aabb_v == Variant(Vector3i(2, 3, 5)));
+ vec3i_v = Vector3i(-5, -7, -2);
+ aabb_v = vec3i_v;
+ CHECK(aabb_v.get_type() == Variant::VECTOR3I);
+
+ Variant quaternion_v = Quaternion();
+ vec3i_v = Vector3i(2, 3, 5);
+ quaternion_v = vec3i_v;
+ CHECK(quaternion_v == Variant(Vector3i(2, 3, 5)));
+ vec3i_v = Vector3i(-5, -7, -2);
+ quaternion_v = vec3i_v;
+ CHECK(quaternion_v.get_type() == Variant::VECTOR3I);
+
+ Variant projection_v = Projection();
+ vec3i_v = Vector3i(2, 3, 5);
+ projection_v = vec3i_v;
+ CHECK(projection_v == Variant(Vector3i(2, 3, 5)));
+ vec3i_v = Vector3i(-5, -7, -2);
+ projection_v = vec3i_v;
+ CHECK(projection_v.get_type() == Variant::VECTOR3I);
+
+ Variant rid_v = RID();
+ vec3i_v = Vector3i(2, 3, 5);
+ rid_v = vec3i_v;
+ CHECK(rid_v == Variant(Vector3i(2, 3, 5)));
+ vec3i_v = Vector3i(-5, -7, -2);
+ rid_v = vec3i_v;
+ CHECK(rid_v.get_type() == Variant::VECTOR3I);
+
+ Object obj_one = Object();
+ Variant object_v = &obj_one;
+ vec3i_v = Vector3i(2, 3, 5);
+ object_v = vec3i_v;
+ CHECK(object_v == Variant(Vector3i(2, 3, 5)));
+ vec3i_v = Vector3i(-5, -7, -2);
+ object_v = vec3i_v;
+ CHECK(object_v.get_type() == Variant::VECTOR3I);
}
-TEST_CASE("[Variant] Assignment To Color from Bool,Int,Float,String,Vec2,Vec2i,Vec3 and Vec3i") {
+TEST_CASE("[Variant] Assignment To Color from Bool,Int,Float,String,Vec2,Vec2i,Vec3,Vec3i,Vec4,Vec4i,Rect2,Rect2i,Trans2d,Trans3d,Color,Call,Plane,Basis,AABB,Quant,Proj,RID,and Object") {
Variant bool_v = false;
Variant col_v = Color(0.25f, 0.4f, 0.78f);
bool_v = col_v; // Now bool_v is Color
@@ -718,6 +1622,119 @@ TEST_CASE("[Variant] Assignment To Color from Bool,Int,Float,String,Vec2,Vec2i,V
col_v = Color(0.33f, 0.75f, 0.21f);
vec3i_v = col_v;
CHECK(vec3i_v.get_type() == Variant::COLOR);
+
+ Variant vec4_v = Vector4(0, 0, 0, 0);
+ col_v = Color(0.25f, 0.4f, 0.78f);
+ vec4_v = col_v;
+ CHECK(vec4_v == Variant(Color(0.25f, 0.4f, 0.78f)));
+ col_v = Color(0.33f, 0.75f, 0.21f);
+ vec4_v = col_v;
+ CHECK(vec4_v.get_type() == Variant::COLOR);
+
+ Variant vec4i_v = Vector4i(0, 0, 0, 0);
+ col_v = Color(0.25f, 0.4f, 0.78f);
+ vec4i_v = col_v;
+ CHECK(vec4i_v == Variant(Color(0.25f, 0.4f, 0.78f)));
+ col_v = Color(0.33f, 0.75f, 0.21f);
+ vec4i_v = col_v;
+ CHECK(vec4i_v.get_type() == Variant::COLOR);
+
+ Variant rect2_v = Rect2();
+ col_v = Color(0.25f, 0.4f, 0.78f);
+ rect2_v = col_v;
+ CHECK(rect2_v == Variant(Color(0.25f, 0.4f, 0.78f)));
+ col_v = Color(0.33f, 0.75f, 0.21f);
+ rect2_v = col_v;
+ CHECK(rect2_v.get_type() == Variant::COLOR);
+
+ Variant rect2i_v = Rect2i();
+ col_v = Color(0.25f, 0.4f, 0.78f);
+ rect2i_v = col_v;
+ CHECK(rect2i_v == Variant(Color(0.25f, 0.4f, 0.78f)));
+ col_v = Color(0.33f, 0.75f, 0.21f);
+ rect2i_v = col_v;
+ CHECK(rect2i_v.get_type() == Variant::COLOR);
+
+ Variant transform2d_v = Transform2D();
+ col_v = Color(0.25f, 0.4f, 0.78f);
+ transform2d_v = col_v;
+ CHECK(transform2d_v == Variant(Color(0.25f, 0.4f, 0.78f)));
+ col_v = Color(0.33f, 0.75f, 0.21f);
+ transform2d_v = col_v;
+ CHECK(transform2d_v.get_type() == Variant::COLOR);
+
+ Variant transform3d_v = Transform3D();
+ col_v = Color(0.25f, 0.4f, 0.78f);
+ transform3d_v = col_v;
+ CHECK(transform3d_v == Variant(Color(0.25f, 0.4f, 0.78f)));
+ col_v = Color(0.33f, 0.75f, 0.21f);
+ transform3d_v = col_v;
+ CHECK(transform3d_v.get_type() == Variant::COLOR);
+
+ Variant call_v = Callable();
+ col_v = Color(0.25f, 0.4f, 0.78f);
+ call_v = col_v;
+ CHECK(call_v == Variant(Color(0.25f, 0.4f, 0.78f)));
+ col_v = Color(0.33f, 0.75f, 0.21f);
+ call_v = col_v;
+ CHECK(call_v.get_type() == Variant::COLOR);
+
+ Variant plane_v = Plane();
+ col_v = Color(0.25f, 0.4f, 0.78f);
+ plane_v = col_v;
+ CHECK(plane_v == Variant(Color(0.25f, 0.4f, 0.78f)));
+ col_v = Color(0.33f, 0.75f, 0.21f);
+ plane_v = col_v;
+ CHECK(plane_v.get_type() == Variant::COLOR);
+
+ Variant basis_v = Basis();
+ col_v = Color(0.25f, 0.4f, 0.78f);
+ basis_v = col_v;
+ CHECK(basis_v == Variant(Color(0.25f, 0.4f, 0.78f)));
+ col_v = Color(0.33f, 0.75f, 0.21f);
+ basis_v = col_v;
+ CHECK(basis_v.get_type() == Variant::COLOR);
+
+ Variant aabb_v = AABB();
+ col_v = Color(0.25f, 0.4f, 0.78f);
+ aabb_v = col_v;
+ CHECK(aabb_v == Variant(Color(0.25f, 0.4f, 0.78f)));
+ col_v = Color(0.33f, 0.75f, 0.21f);
+ aabb_v = col_v;
+ CHECK(aabb_v.get_type() == Variant::COLOR);
+
+ Variant quaternion_v = Quaternion();
+ col_v = Color(0.25f, 0.4f, 0.78f);
+ quaternion_v = col_v;
+ CHECK(quaternion_v == Variant(Color(0.25f, 0.4f, 0.78f)));
+ col_v = Color(0.33f, 0.75f, 0.21f);
+ quaternion_v = col_v;
+ CHECK(quaternion_v.get_type() == Variant::COLOR);
+
+ Variant projection_v = Projection();
+ col_v = Color(0.25f, 0.4f, 0.78f);
+ projection_v = col_v;
+ CHECK(projection_v == Variant(Color(0.25f, 0.4f, 0.78f)));
+ col_v = Color(0.33f, 0.75f, 0.21f);
+ projection_v = col_v;
+ CHECK(projection_v.get_type() == Variant::COLOR);
+
+ Variant rid_v = RID();
+ col_v = Color(0.25f, 0.4f, 0.78f);
+ rid_v = col_v;
+ CHECK(rid_v == Variant(Color(0.25f, 0.4f, 0.78f)));
+ col_v = Color(0.33f, 0.75f, 0.21f);
+ rid_v = col_v;
+ CHECK(rid_v.get_type() == Variant::COLOR);
+
+ Object obj_one = Object();
+ Variant object_v = &obj_one;
+ col_v = Color(0.25f, 0.4f, 0.78f);
+ object_v = col_v;
+ CHECK(object_v == Variant(Color(0.25f, 0.4f, 0.78f)));
+ col_v = Color(0.33f, 0.75f, 0.21f);
+ object_v = col_v;
+ CHECK(object_v.get_type() == Variant::COLOR);
}
TEST_CASE("[Variant] Writer and parser array") {
diff --git a/tests/core/variant/test_variant_utility.h b/tests/core/variant/test_variant_utility.h
new file mode 100644
index 0000000000..93458b63f4
--- /dev/null
+++ b/tests/core/variant/test_variant_utility.h
@@ -0,0 +1,141 @@
+/**************************************************************************/
+/* test_variant_utility.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_VARIANT_UTILITY_H
+#define TEST_VARIANT_UTILITY_H
+
+#include "core/variant/variant_utility.h"
+
+#include "tests/test_macros.h"
+
+namespace TestVariantUtility {
+
+TEST_CASE("[VariantUtility] Type conversion") {
+ Variant converted;
+ converted = VariantUtilityFunctions::type_convert("Hi!", Variant::Type::NIL);
+ CHECK(converted.get_type() == Variant::Type::NIL);
+ CHECK(converted == Variant());
+
+ converted = VariantUtilityFunctions::type_convert("Hi!", Variant::Type::INT);
+ CHECK(converted.get_type() == Variant::Type::INT);
+ CHECK(converted == Variant(0));
+
+ converted = VariantUtilityFunctions::type_convert("123", Variant::Type::INT);
+ CHECK(converted.get_type() == Variant::Type::INT);
+ CHECK(converted == Variant(123));
+
+ converted = VariantUtilityFunctions::type_convert(123, Variant::Type::STRING);
+ CHECK(converted.get_type() == Variant::Type::STRING);
+ CHECK(converted == Variant("123"));
+
+ converted = VariantUtilityFunctions::type_convert(123.4, Variant::Type::INT);
+ CHECK(converted.get_type() == Variant::Type::INT);
+ CHECK(converted == Variant(123));
+
+ converted = VariantUtilityFunctions::type_convert(5, Variant::Type::VECTOR2);
+ CHECK(converted.get_type() == Variant::Type::VECTOR2);
+ CHECK(converted == Variant(Vector2(0, 0)));
+
+ converted = VariantUtilityFunctions::type_convert(Vector3(1, 2, 3), Variant::Type::VECTOR2);
+ CHECK(converted.get_type() == Variant::Type::VECTOR2);
+ CHECK(converted == Variant(Vector2(1, 2)));
+
+ converted = VariantUtilityFunctions::type_convert(Vector2(1, 2), Variant::Type::VECTOR4);
+ CHECK(converted.get_type() == Variant::Type::VECTOR4);
+ CHECK(converted == Variant(Vector4(1, 2, 0, 0)));
+
+ converted = VariantUtilityFunctions::type_convert(Vector4(1.2, 3.4, 5.6, 7.8), Variant::Type::VECTOR3I);
+ CHECK(converted.get_type() == Variant::Type::VECTOR3I);
+ CHECK(converted == Variant(Vector3i(1, 3, 5)));
+
+ {
+ Basis basis = Basis::from_scale(Vector3(1.2, 3.4, 5.6));
+ Transform3D transform = Transform3D(basis, Vector3());
+
+ converted = VariantUtilityFunctions::type_convert(transform, Variant::Type::BASIS);
+ CHECK(converted.get_type() == Variant::Type::BASIS);
+ CHECK(converted == basis);
+
+ converted = VariantUtilityFunctions::type_convert(basis, Variant::Type::TRANSFORM3D);
+ CHECK(converted.get_type() == Variant::Type::TRANSFORM3D);
+ CHECK(converted == transform);
+
+ converted = VariantUtilityFunctions::type_convert(basis, Variant::Type::STRING);
+ CHECK(converted.get_type() == Variant::Type::STRING);
+ CHECK(converted == Variant("[X: (1.2, 0, 0), Y: (0, 3.4, 0), Z: (0, 0, 5.6)]"));
+ }
+
+ {
+ Array arr;
+ arr.push_back(1.2);
+ arr.push_back(3.4);
+ arr.push_back(5.6);
+
+ PackedFloat64Array packed;
+ packed.push_back(1.2);
+ packed.push_back(3.4);
+ packed.push_back(5.6);
+
+ converted = VariantUtilityFunctions::type_convert(arr, Variant::Type::PACKED_FLOAT64_ARRAY);
+ CHECK(converted.get_type() == Variant::Type::PACKED_FLOAT64_ARRAY);
+ CHECK(converted == packed);
+
+ converted = VariantUtilityFunctions::type_convert(packed, Variant::Type::ARRAY);
+ CHECK(converted.get_type() == Variant::Type::ARRAY);
+ CHECK(converted == arr);
+ }
+
+ {
+ // Check that using Variant::call_utility_function also works.
+ Vector<const Variant *> args;
+ Variant data_arg = "Hi!";
+ args.push_back(&data_arg);
+ Variant type_arg = Variant::Type::NIL;
+ args.push_back(&type_arg);
+ Callable::CallError call_error;
+ Variant::call_utility_function("type_convert", &converted, (const Variant **)args.ptr(), 2, call_error);
+ CHECK(converted.get_type() == Variant::Type::NIL);
+ CHECK(converted == Variant());
+
+ type_arg = Variant::Type::INT;
+ Variant::call_utility_function("type_convert", &converted, (const Variant **)args.ptr(), 2, call_error);
+ CHECK(converted.get_type() == Variant::Type::INT);
+ CHECK(converted == Variant(0));
+
+ data_arg = "123";
+ Variant::call_utility_function("type_convert", &converted, (const Variant **)args.ptr(), 2, call_error);
+ CHECK(converted.get_type() == Variant::Type::INT);
+ CHECK(converted == Variant(123));
+ }
+}
+
+} // namespace TestVariantUtility
+
+#endif // TEST_VARIANT_UTILITY_H
diff --git a/tests/data/images/icon.dds b/tests/data/images/icon.dds
deleted file mode 100644
index 8a9de402cb..0000000000
--- a/tests/data/images/icon.dds
+++ /dev/null
Binary files differ
diff --git a/tests/display_server_mock.h b/tests/display_server_mock.h
index fe36fa0b69..8d8a678e20 100644
--- a/tests/display_server_mock.h
+++ b/tests/display_server_mock.h
@@ -64,13 +64,8 @@ private:
}
void _dispatch_input_event(const Ref<InputEvent> &p_event) {
- Variant ev = p_event;
- Variant *evp = &ev;
- Variant ret;
- Callable::CallError ce;
-
if (input_event_callback.is_valid()) {
- input_event_callback.callp((const Variant **)&evp, 1, ret, ce);
+ input_event_callback.call(p_event);
}
}
@@ -93,10 +88,7 @@ private:
void _send_window_event(WindowEvent p_event) {
if (!event_callback.is_null()) {
Variant event = int(p_event);
- Variant *eventp = &event;
- Variant ret;
- Callable::CallError ce;
- event_callback.callp((const Variant **)&eventp, 1, ret, ce);
+ event_callback.call(event);
}
}
diff --git a/tests/scene/test_arraymesh.h b/tests/scene/test_arraymesh.h
index b2a2ecc3bf..1623b41300 100644
--- a/tests/scene/test_arraymesh.h
+++ b/tests/scene/test_arraymesh.h
@@ -154,7 +154,7 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") {
}
}
-TEST_CASE("[SceneTree][ArrayMesh] Surface meta data tests.") {
+TEST_CASE("[SceneTree][ArrayMesh] Surface metadata tests.") {
Ref<ArrayMesh> mesh = memnew(ArrayMesh);
Ref<CylinderMesh> cylinder = memnew(CylinderMesh);
Array cylinder_array{};
@@ -195,7 +195,7 @@ TEST_CASE("[SceneTree][ArrayMesh] Surface meta data tests.") {
}
SUBCASE("Returns correct format for the mesh") {
- auto format = RS::ARRAY_FORMAT_BLEND_SHAPE_MASK | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX;
+ int format = RS::ARRAY_FORMAT_BLEND_SHAPE_MASK | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX;
CHECK((mesh->surface_get_format(0) & format) != 0);
CHECK((mesh->surface_get_format(1) & format) != 0);
}
diff --git a/tests/scene/test_camera_3d.h b/tests/scene/test_camera_3d.h
new file mode 100644
index 0000000000..169486d108
--- /dev/null
+++ b/tests/scene/test_camera_3d.h
@@ -0,0 +1,370 @@
+/**************************************************************************/
+/* test_camera_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_CAMERA_3D_H
+#define TEST_CAMERA_3D_H
+
+#include "scene/3d/camera_3d.h"
+#include "scene/main/viewport.h"
+#include "scene/main/window.h"
+
+#include "tests/test_macros.h"
+
+// Constants.
+#define SQRT3 (1.7320508f)
+
+TEST_CASE("[SceneTree][Camera3D] Getters and setters") {
+ Camera3D *test_camera = memnew(Camera3D);
+
+ SUBCASE("Cull mask") {
+ constexpr int cull_mask = (1 << 5) | (1 << 7) | (1 << 9);
+ constexpr int set_enable_layer = 3;
+ constexpr int set_disable_layer = 5;
+ test_camera->set_cull_mask(cull_mask);
+ CHECK(test_camera->get_cull_mask() == cull_mask);
+ test_camera->set_cull_mask_value(set_enable_layer, true);
+ CHECK(test_camera->get_cull_mask_value(set_enable_layer));
+ test_camera->set_cull_mask_value(set_disable_layer, false);
+ CHECK_FALSE(test_camera->get_cull_mask_value(set_disable_layer));
+ }
+
+ SUBCASE("Attributes") {
+ Ref<CameraAttributes> attributes = memnew(CameraAttributes);
+ test_camera->set_attributes(attributes);
+ CHECK(test_camera->get_attributes() == attributes);
+ Ref<CameraAttributesPhysical> physical_attributes = memnew(CameraAttributesPhysical);
+ test_camera->set_attributes(physical_attributes);
+ CHECK(test_camera->get_attributes() == physical_attributes);
+ }
+
+ SUBCASE("Camera frustum properties") {
+ constexpr float near = 0.2f;
+ constexpr float far = 995.0f;
+ constexpr float fov = 120.0f;
+ constexpr float size = 7.0f;
+ constexpr float h_offset = 1.1f;
+ constexpr float v_offset = -1.6f;
+ const Vector2 frustum_offset(5, 7);
+ test_camera->set_near(near);
+ CHECK(test_camera->get_near() == near);
+ test_camera->set_far(far);
+ CHECK(test_camera->get_far() == far);
+ test_camera->set_fov(fov);
+ CHECK(test_camera->get_fov() == fov);
+ test_camera->set_size(size);
+ CHECK(test_camera->get_size() == size);
+ test_camera->set_h_offset(h_offset);
+ CHECK(test_camera->get_h_offset() == h_offset);
+ test_camera->set_v_offset(v_offset);
+ CHECK(test_camera->get_v_offset() == v_offset);
+ test_camera->set_frustum_offset(frustum_offset);
+ CHECK(test_camera->get_frustum_offset() == frustum_offset);
+ test_camera->set_keep_aspect_mode(Camera3D::KeepAspect::KEEP_HEIGHT);
+ CHECK(test_camera->get_keep_aspect_mode() == Camera3D::KeepAspect::KEEP_HEIGHT);
+ test_camera->set_keep_aspect_mode(Camera3D::KeepAspect::KEEP_WIDTH);
+ CHECK(test_camera->get_keep_aspect_mode() == Camera3D::KeepAspect::KEEP_WIDTH);
+ }
+
+ SUBCASE("Projection mode") {
+ test_camera->set_projection(Camera3D::ProjectionType::PROJECTION_ORTHOGONAL);
+ CHECK(test_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_ORTHOGONAL);
+ test_camera->set_projection(Camera3D::ProjectionType::PROJECTION_PERSPECTIVE);
+ CHECK(test_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE);
+ }
+
+ SUBCASE("Helper setters") {
+ constexpr float fov = 90.0f, size = 6.0f;
+ constexpr float near1 = 0.1f, near2 = 0.5f;
+ constexpr float far1 = 1001.0f, far2 = 1005.0f;
+ test_camera->set_perspective(fov, near1, far1);
+ CHECK(test_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE);
+ CHECK(test_camera->get_near() == near1);
+ CHECK(test_camera->get_far() == far1);
+ CHECK(test_camera->get_fov() == fov);
+ test_camera->set_orthogonal(size, near2, far2);
+ CHECK(test_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_ORTHOGONAL);
+ CHECK(test_camera->get_near() == near2);
+ CHECK(test_camera->get_far() == far2);
+ CHECK(test_camera->get_size() == size);
+ }
+
+ SUBCASE("Doppler tracking") {
+ test_camera->set_doppler_tracking(Camera3D::DopplerTracking::DOPPLER_TRACKING_IDLE_STEP);
+ CHECK(test_camera->get_doppler_tracking() == Camera3D::DopplerTracking::DOPPLER_TRACKING_IDLE_STEP);
+ test_camera->set_doppler_tracking(Camera3D::DopplerTracking::DOPPLER_TRACKING_PHYSICS_STEP);
+ CHECK(test_camera->get_doppler_tracking() == Camera3D::DopplerTracking::DOPPLER_TRACKING_PHYSICS_STEP);
+ test_camera->set_doppler_tracking(Camera3D::DopplerTracking::DOPPLER_TRACKING_DISABLED);
+ CHECK(test_camera->get_doppler_tracking() == Camera3D::DopplerTracking::DOPPLER_TRACKING_DISABLED);
+ }
+
+ memdelete(test_camera);
+}
+
+TEST_CASE("[SceneTree][Camera3D] Position queries") {
+ // Cameras need a viewport to know how to compute their frustums, so we make a fake one here.
+ Camera3D *test_camera = memnew(Camera3D);
+ SubViewport *mock_viewport = memnew(SubViewport);
+ // 4:2.
+ mock_viewport->set_size(Vector2(400, 200));
+ SceneTree::get_singleton()->get_root()->add_child(mock_viewport);
+ mock_viewport->add_child(test_camera);
+ test_camera->set_keep_aspect_mode(Camera3D::KeepAspect::KEEP_WIDTH);
+ REQUIRE_MESSAGE(test_camera->is_current(), "Camera3D should be made current upon entering tree.");
+
+ SUBCASE("Orthogonal projection") {
+ test_camera->set_projection(Camera3D::ProjectionType::PROJECTION_ORTHOGONAL);
+ // The orthogonal case is simpler, so we test a more random position + rotation combination here.
+ // For the other cases we'll use zero translation and rotation instead.
+ test_camera->set_global_position(Vector3(1, 2, 3));
+ test_camera->look_at(Vector3(-4, 5, 1));
+ // Width = 5, Aspect Ratio = 400 / 200 = 2, so Height is 2.5.
+ test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f);
+ const Basis basis = test_camera->get_global_basis();
+ // Subtract near so offset starts from the near plane.
+ const Vector3 offset1 = basis.xform(Vector3(-1.5f, 3.5f, 0.2f - test_camera->get_near()));
+ const Vector3 offset2 = basis.xform(Vector3(2.0f, -0.5f, -0.6f - test_camera->get_near()));
+ const Vector3 offset3 = basis.xform(Vector3(-3.0f, 1.0f, -0.6f - test_camera->get_near()));
+ const Vector3 offset4 = basis.xform(Vector3(-2.0f, 1.5f, -0.6f - test_camera->get_near()));
+ const Vector3 offset5 = basis.xform(Vector3(0, 0, 10000.0f - test_camera->get_near()));
+
+ SUBCASE("is_position_behind") {
+ CHECK(test_camera->is_position_behind(test_camera->get_global_position() + offset1));
+ CHECK_FALSE(test_camera->is_position_behind(test_camera->get_global_position() + offset2));
+
+ SUBCASE("h/v offset should have no effect on the result of is_position_behind") {
+ test_camera->set_h_offset(-11.0f);
+ test_camera->set_v_offset(22.1f);
+ CHECK(test_camera->is_position_behind(test_camera->get_global_position() + offset1));
+ test_camera->set_h_offset(4.7f);
+ test_camera->set_v_offset(-3.0f);
+ CHECK_FALSE(test_camera->is_position_behind(test_camera->get_global_position() + offset2));
+ }
+ // Reset h/v offsets.
+ test_camera->set_h_offset(0);
+ test_camera->set_v_offset(0);
+ }
+
+ SUBCASE("is_position_in_frustum") {
+ // If the point is behind the near plane, it is outside the camera frustum.
+ // So offset1 is not in frustum.
+ CHECK_FALSE(test_camera->is_position_in_frustum(test_camera->get_global_position() + offset1));
+ // If |right| > 5 / 2 or |up| > 2.5 / 2, the point is outside the camera frustum.
+ // So offset2 is in frustum and offset3 and offset4 are not.
+ CHECK(test_camera->is_position_in_frustum(test_camera->get_global_position() + offset2));
+ CHECK_FALSE(test_camera->is_position_in_frustum(test_camera->get_global_position() + offset3));
+ CHECK_FALSE(test_camera->is_position_in_frustum(test_camera->get_global_position() + offset4));
+ // offset5 is beyond the far plane, so it is not in frustum.
+ CHECK_FALSE(test_camera->is_position_in_frustum(test_camera->get_global_position() + offset5));
+ }
+ }
+
+ SUBCASE("Perspective projection") {
+ test_camera->set_projection(Camera3D::ProjectionType::PROJECTION_PERSPECTIVE);
+ // Camera at origin, looking at +Z.
+ test_camera->set_global_position(Vector3(0, 0, 0));
+ test_camera->set_global_rotation(Vector3(0, 0, 0));
+ // Keep width, so horizontal fov = 120.
+ // Since the near plane distance is 1,
+ // with trig we know the near plane's width is 2 * sqrt(3), so its height is sqrt(3).
+ test_camera->set_perspective(120.0f, 1.0f, 1000.0f);
+
+ SUBCASE("is_position_behind") {
+ CHECK_FALSE(test_camera->is_position_behind(Vector3(0, 0, -1.5f)));
+ CHECK(test_camera->is_position_behind(Vector3(2, 0, -0.2f)));
+ }
+
+ SUBCASE("is_position_in_frustum") {
+ CHECK(test_camera->is_position_in_frustum(Vector3(-1.3f, 0, -1.1f)));
+ CHECK_FALSE(test_camera->is_position_in_frustum(Vector3(2, 0, -1.1f)));
+ CHECK(test_camera->is_position_in_frustum(Vector3(1, 0.5f, -1.1f)));
+ CHECK_FALSE(test_camera->is_position_in_frustum(Vector3(1, 1, -1.1f)));
+ CHECK(test_camera->is_position_in_frustum(Vector3(0, 0, -1.5f)));
+ CHECK_FALSE(test_camera->is_position_in_frustum(Vector3(0, 0, -0.5f)));
+ }
+ }
+
+ memdelete(test_camera);
+ memdelete(mock_viewport);
+}
+
+TEST_CASE("[SceneTree][Camera3D] Project/Unproject position") {
+ // Cameras need a viewport to know how to compute their frustums, so we make a fake one here.
+ Camera3D *test_camera = memnew(Camera3D);
+ SubViewport *mock_viewport = memnew(SubViewport);
+ // 4:2.
+ mock_viewport->set_size(Vector2(400, 200));
+ SceneTree::get_singleton()->get_root()->add_child(mock_viewport);
+ mock_viewport->add_child(test_camera);
+ test_camera->set_global_position(Vector3(0, 0, 0));
+ test_camera->set_global_rotation(Vector3(0, 0, 0));
+ test_camera->set_keep_aspect_mode(Camera3D::KeepAspect::KEEP_HEIGHT);
+
+ SUBCASE("project_position") {
+ SUBCASE("Orthogonal projection") {
+ test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->project_position(Vector2(200, 100), 0.5f).is_equal_approx(Vector3(0, 0, -0.5f)));
+ // Top left.
+ CHECK(test_camera->project_position(Vector2(0, 0), 1.5f).is_equal_approx(Vector3(-5.0f, 2.5f, -1.5f)));
+ // Bottom right.
+ CHECK(test_camera->project_position(Vector2(400, 200), 5.0f).is_equal_approx(Vector3(5.0f, -2.5f, -5.0f)));
+ }
+
+ SUBCASE("Perspective projection") {
+ test_camera->set_perspective(120.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->project_position(Vector2(200, 100), 0.5f).is_equal_approx(Vector3(0, 0, -0.5f)));
+ CHECK(test_camera->project_position(Vector2(200, 100), 100.0f).is_equal_approx(Vector3(0, 0, -100.0f)));
+ // 3/4th way to Top left.
+ CHECK(test_camera->project_position(Vector2(100, 50), 0.5f).is_equal_approx(Vector3(-SQRT3 * 0.5f, SQRT3 * 0.25f, -0.5f)));
+ CHECK(test_camera->project_position(Vector2(100, 50), 1.0f).is_equal_approx(Vector3(-SQRT3, SQRT3 * 0.5f, -1.0f)));
+ // 3/4th way to Bottom right.
+ CHECK(test_camera->project_position(Vector2(300, 150), 0.5f).is_equal_approx(Vector3(SQRT3 * 0.5f, -SQRT3 * 0.25f, -0.5f)));
+ CHECK(test_camera->project_position(Vector2(300, 150), 1.0f).is_equal_approx(Vector3(SQRT3, -SQRT3 * 0.5f, -1.0f)));
+ }
+ }
+
+ // Uses cases that are the inverse of the above sub-case.
+ SUBCASE("unproject_position") {
+ SUBCASE("Orthogonal projection") {
+ test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f);
+ // Center
+ CHECK(test_camera->unproject_position(Vector3(0, 0, -0.5f)).is_equal_approx(Vector2(200, 100)));
+ // Top left
+ CHECK(test_camera->unproject_position(Vector3(-5.0f, 2.5f, -1.5f)).is_equal_approx(Vector2(0, 0)));
+ // Bottom right
+ CHECK(test_camera->unproject_position(Vector3(5.0f, -2.5f, -5.0f)).is_equal_approx(Vector2(400, 200)));
+ }
+
+ SUBCASE("Perspective projection") {
+ test_camera->set_perspective(120.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->unproject_position(Vector3(0, 0, -0.5f)).is_equal_approx(Vector2(200, 100)));
+ CHECK(test_camera->unproject_position(Vector3(0, 0, -100.0f)).is_equal_approx(Vector2(200, 100)));
+ // 3/4th way to Top left.
+ WARN(test_camera->unproject_position(Vector3(-SQRT3 * 0.5f, SQRT3 * 0.25f, -0.5f)).is_equal_approx(Vector2(100, 50)));
+ WARN(test_camera->unproject_position(Vector3(-SQRT3, SQRT3 * 0.5f, -1.0f)).is_equal_approx(Vector2(100, 50)));
+ // 3/4th way to Bottom right.
+ CHECK(test_camera->unproject_position(Vector3(SQRT3 * 0.5f, -SQRT3 * 0.25f, -0.5f)).is_equal_approx(Vector2(300, 150)));
+ CHECK(test_camera->unproject_position(Vector3(SQRT3, -SQRT3 * 0.5f, -1.0f)).is_equal_approx(Vector2(300, 150)));
+ }
+ }
+
+ memdelete(test_camera);
+ memdelete(mock_viewport);
+}
+
+TEST_CASE("[SceneTree][Camera3D] Project ray") {
+ // Cameras need a viewport to know how to compute their frustums, so we make a fake one here.
+ Camera3D *test_camera = memnew(Camera3D);
+ SubViewport *mock_viewport = memnew(SubViewport);
+ // 4:2.
+ mock_viewport->set_size(Vector2(400, 200));
+ SceneTree::get_singleton()->get_root()->add_child(mock_viewport);
+ mock_viewport->add_child(test_camera);
+ test_camera->set_global_position(Vector3(0, 0, 0));
+ test_camera->set_global_rotation(Vector3(0, 0, 0));
+ test_camera->set_keep_aspect_mode(Camera3D::KeepAspect::KEEP_HEIGHT);
+
+ SUBCASE("project_ray_origin") {
+ SUBCASE("Orthogonal projection") {
+ test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->project_ray_origin(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, -0.5f)));
+ // Top left.
+ CHECK(test_camera->project_ray_origin(Vector2(0, 0)).is_equal_approx(Vector3(-5.0f, 2.5f, -0.5f)));
+ // Bottom right.
+ CHECK(test_camera->project_ray_origin(Vector2(400, 200)).is_equal_approx(Vector3(5.0f, -2.5f, -0.5f)));
+ }
+
+ SUBCASE("Perspective projection") {
+ test_camera->set_perspective(120.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->project_ray_origin(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, 0)));
+ // Top left.
+ CHECK(test_camera->project_ray_origin(Vector2(0, 0)).is_equal_approx(Vector3(0, 0, 0)));
+ // Bottom right.
+ CHECK(test_camera->project_ray_origin(Vector2(400, 200)).is_equal_approx(Vector3(0, 0, 0)));
+ }
+ }
+
+ SUBCASE("project_ray_normal") {
+ SUBCASE("Orthogonal projection") {
+ test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->project_ray_normal(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, -1)));
+ // Top left.
+ CHECK(test_camera->project_ray_normal(Vector2(0, 0)).is_equal_approx(Vector3(0, 0, -1)));
+ // Bottom right.
+ CHECK(test_camera->project_ray_normal(Vector2(400, 200)).is_equal_approx(Vector3(0, 0, -1)));
+ }
+
+ SUBCASE("Perspective projection") {
+ test_camera->set_perspective(120.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->project_ray_normal(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, -1)));
+ // Top left.
+ CHECK(test_camera->project_ray_normal(Vector2(0, 0)).is_equal_approx(Vector3(-SQRT3, SQRT3 / 2, -0.5f).normalized()));
+ // Bottom right.
+ CHECK(test_camera->project_ray_normal(Vector2(400, 200)).is_equal_approx(Vector3(SQRT3, -SQRT3 / 2, -0.5f).normalized()));
+ }
+ }
+
+ SUBCASE("project_local_ray_normal") {
+ test_camera->set_rotation_degrees(Vector3(60, 60, 60));
+
+ SUBCASE("Orthogonal projection") {
+ test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->project_local_ray_normal(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, -1)));
+ // Top left.
+ CHECK(test_camera->project_local_ray_normal(Vector2(0, 0)).is_equal_approx(Vector3(0, 0, -1)));
+ // Bottom right.
+ CHECK(test_camera->project_local_ray_normal(Vector2(400, 200)).is_equal_approx(Vector3(0, 0, -1)));
+ }
+
+ SUBCASE("Perspective projection") {
+ test_camera->set_perspective(120.0f, 0.5f, 1000.0f);
+ // Center.
+ CHECK(test_camera->project_local_ray_normal(Vector2(200, 100)).is_equal_approx(Vector3(0, 0, -1)));
+ // Top left.
+ CHECK(test_camera->project_local_ray_normal(Vector2(0, 0)).is_equal_approx(Vector3(-SQRT3, SQRT3 / 2, -0.5f).normalized()));
+ // Bottom right.
+ CHECK(test_camera->project_local_ray_normal(Vector2(400, 200)).is_equal_approx(Vector3(SQRT3, -SQRT3 / 2, -0.5f).normalized()));
+ }
+ }
+
+ memdelete(test_camera);
+ memdelete(mock_viewport);
+}
+
+#undef SQRT3
+
+#endif // TEST_CAMERA_3D_H
diff --git a/tests/scene/test_code_edit.h b/tests/scene/test_code_edit.h
index d6858fbcb4..7d98372327 100644
--- a/tests/scene/test_code_edit.h
+++ b/tests/scene/test_code_edit.h
@@ -1503,6 +1503,19 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
CHECK(code_edit->is_in_string(1) == -1);
CHECK(code_edit->is_in_string(2) != -1);
CHECK(code_edit->is_in_string(3) == -1);
+
+ /* Next check updating the delimiter cache while typing. */
+ code_edit->set_text("\n\n");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(0);
+ CHECK(code_edit->is_in_string(0) == -1);
+ CHECK(code_edit->is_in_string(1) == -1);
+ code_edit->insert_text_at_caret("#");
+ CHECK(code_edit->is_in_string(0) != -1);
+ CHECK(code_edit->is_in_string(1) != -1);
+ code_edit->insert_text_at_caret("#");
+ CHECK(code_edit->is_in_string(0) != -1);
+ CHECK(code_edit->is_in_string(1) == -1);
}
SUBCASE("[CodeEdit] multiline comment delimiters") {
@@ -1692,6 +1705,19 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
CHECK(code_edit->is_in_comment(1) == -1);
CHECK(code_edit->is_in_comment(2) != -1);
CHECK(code_edit->is_in_comment(3) == -1);
+
+ /* Next check updating the delimiter cache while typing. */
+ code_edit->set_text("\n\n");
+ code_edit->set_caret_line(0);
+ code_edit->set_caret_column(0);
+ CHECK(code_edit->is_in_comment(0) == -1);
+ CHECK(code_edit->is_in_comment(1) == -1);
+ code_edit->insert_text_at_caret("#");
+ CHECK(code_edit->is_in_comment(0) != -1);
+ CHECK(code_edit->is_in_comment(1) != -1);
+ code_edit->insert_text_at_caret("#");
+ CHECK(code_edit->is_in_comment(0) != -1);
+ CHECK(code_edit->is_in_comment(1) == -1);
}
SUBCASE("[CodeEdit] multiline mixed delimiters") {
@@ -2839,6 +2865,194 @@ TEST_CASE("[SceneTree][CodeEdit] folding") {
memdelete(code_edit);
}
+TEST_CASE("[SceneTree][CodeEdit] region folding") {
+ CodeEdit *code_edit = memnew(CodeEdit);
+ SceneTree::get_singleton()->get_root()->add_child(code_edit);
+ code_edit->grab_focus();
+
+ SUBCASE("[CodeEdit] region folding") {
+ code_edit->set_line_folding_enabled(true);
+
+ // Region tag detection.
+ code_edit->set_text("#region region_name\nline2\n#endregion");
+ code_edit->clear_comment_delimiters();
+ code_edit->add_comment_delimiter("#", "");
+ CHECK(code_edit->is_line_code_region_start(0));
+ CHECK_FALSE(code_edit->is_line_code_region_start(1));
+ CHECK_FALSE(code_edit->is_line_code_region_start(2));
+ CHECK_FALSE(code_edit->is_line_code_region_end(0));
+ CHECK_FALSE(code_edit->is_line_code_region_end(1));
+ CHECK(code_edit->is_line_code_region_end(2));
+
+ // Region tag customization.
+ code_edit->set_text("#region region_name\nline2\n#endregion\n#open region_name\nline2\n#close");
+ code_edit->clear_comment_delimiters();
+ code_edit->add_comment_delimiter("#", "");
+ CHECK(code_edit->is_line_code_region_start(0));
+ CHECK(code_edit->is_line_code_region_end(2));
+ CHECK_FALSE(code_edit->is_line_code_region_start(3));
+ CHECK_FALSE(code_edit->is_line_code_region_end(5));
+ code_edit->set_code_region_tags("open", "close");
+ CHECK_FALSE(code_edit->is_line_code_region_start(0));
+ CHECK_FALSE(code_edit->is_line_code_region_end(2));
+ CHECK(code_edit->is_line_code_region_start(3));
+ CHECK(code_edit->is_line_code_region_end(5));
+ code_edit->set_code_region_tags("region", "endregion");
+
+ // Setting identical start and end region tags should fail.
+ CHECK(code_edit->get_code_region_start_tag() == "region");
+ CHECK(code_edit->get_code_region_end_tag() == "endregion");
+ ERR_PRINT_OFF;
+ code_edit->set_code_region_tags("same_tag", "same_tag");
+ ERR_PRINT_ON;
+ CHECK(code_edit->get_code_region_start_tag() == "region");
+ CHECK(code_edit->get_code_region_end_tag() == "endregion");
+
+ // Region creation with selection adds start / close region lines.
+ code_edit->set_text("line1\nline2\nline3");
+ code_edit->clear_comment_delimiters();
+ code_edit->add_comment_delimiter("#", "");
+ code_edit->select(1, 0, 1, 4);
+ code_edit->create_code_region();
+ CHECK(code_edit->is_line_code_region_start(1));
+ CHECK(code_edit->get_line(2).contains("line2"));
+ CHECK(code_edit->is_line_code_region_end(3));
+
+ // Region creation without any selection has no effect.
+ code_edit->set_text("line1\nline2\nline3");
+ code_edit->clear_comment_delimiters();
+ code_edit->add_comment_delimiter("#", "");
+ code_edit->create_code_region();
+ CHECK(code_edit->get_text() == "line1\nline2\nline3");
+
+ // Region creation with multiple selections.
+ code_edit->set_text("line1\nline2\nline3");
+ code_edit->clear_comment_delimiters();
+ code_edit->add_comment_delimiter("#", "");
+ code_edit->select(0, 0, 0, 4, 0);
+ code_edit->add_caret(2, 5);
+ code_edit->select(2, 0, 2, 5, 1);
+ code_edit->create_code_region();
+ CHECK(code_edit->get_text() == "#region New Code Region\nline1\n#endregion\nline2\n#region New Code Region\nline3\n#endregion");
+
+ // Two selections on the same line create only one region.
+ code_edit->set_text("test line1\ntest line2\ntest line3");
+ code_edit->clear_comment_delimiters();
+ code_edit->add_comment_delimiter("#", "");
+ code_edit->select(0, 0, 1, 2, 0);
+ code_edit->add_caret(1, 4);
+ code_edit->select(1, 4, 2, 5, 1);
+ code_edit->create_code_region();
+ CHECK(code_edit->get_text() == "#region New Code Region\ntest line1\ntest line2\ntest line3\n#endregion");
+
+ // Region tag with // comment delimiter.
+ code_edit->set_text("//region region_name\nline2\n//endregion");
+ code_edit->clear_comment_delimiters();
+ code_edit->add_comment_delimiter("//", "");
+ CHECK(code_edit->is_line_code_region_start(0));
+ CHECK(code_edit->is_line_code_region_end(2));
+
+ // Creating region with no valid one line comment delimiter has no effect.
+ code_edit->set_text("line1\nline2\nline3");
+ code_edit->clear_comment_delimiters();
+ code_edit->create_code_region();
+ CHECK(code_edit->get_text() == "line1\nline2\nline3");
+ code_edit->add_comment_delimiter("/*", "*/");
+ code_edit->create_code_region();
+ CHECK(code_edit->get_text() == "line1\nline2\nline3");
+
+ // Choose one line comment delimiter.
+ code_edit->set_text("//region region_name\nline2\n//endregion");
+ code_edit->clear_comment_delimiters();
+ code_edit->add_comment_delimiter("/*", "*/");
+ code_edit->add_comment_delimiter("//", "");
+ CHECK(code_edit->is_line_code_region_start(0));
+ CHECK(code_edit->is_line_code_region_end(2));
+
+ // Update code region delimiter when removing comment delimiter.
+ code_edit->set_text("#region region_name\nline2\n#endregion\n//region region_name\nline2\n//endregion");
+ code_edit->clear_comment_delimiters();
+ code_edit->add_comment_delimiter("//", "");
+ code_edit->add_comment_delimiter("#", ""); // A shorter delimiter has higher priority.
+ CHECK(code_edit->is_line_code_region_start(0));
+ CHECK(code_edit->is_line_code_region_end(2));
+ CHECK_FALSE(code_edit->is_line_code_region_start(3));
+ CHECK_FALSE(code_edit->is_line_code_region_end(5));
+ code_edit->remove_comment_delimiter("#");
+ CHECK_FALSE(code_edit->is_line_code_region_start(0));
+ CHECK_FALSE(code_edit->is_line_code_region_end(2));
+ CHECK(code_edit->is_line_code_region_start(3));
+ CHECK(code_edit->is_line_code_region_end(5));
+
+ // Update code region delimiter when clearing comment delimiters.
+ code_edit->set_text("//region region_name\nline2\n//endregion");
+ code_edit->clear_comment_delimiters();
+ code_edit->add_comment_delimiter("//", "");
+ CHECK(code_edit->is_line_code_region_start(0));
+ CHECK(code_edit->is_line_code_region_end(2));
+ code_edit->clear_comment_delimiters();
+ CHECK_FALSE(code_edit->is_line_code_region_start(0));
+ CHECK_FALSE(code_edit->is_line_code_region_end(2));
+
+ // Fold region.
+ code_edit->clear_comment_delimiters();
+ code_edit->add_comment_delimiter("#", "");
+ code_edit->set_text("#region region_name\nline2\nline3\n#endregion\nvisible line");
+ CHECK(code_edit->can_fold_line(0));
+ for (int i = 1; i < 5; i++) {
+ CHECK_FALSE(code_edit->can_fold_line(i));
+ }
+ for (int i = 0; i < 5; i++) {
+ CHECK_FALSE(code_edit->is_line_folded(i));
+ }
+ code_edit->fold_line(0);
+ CHECK(code_edit->is_line_folded(0));
+ CHECK(code_edit->get_next_visible_line_offset_from(1, 1) == 4);
+
+ // Region with no end can't be folded.
+ ERR_PRINT_OFF;
+ code_edit->clear_comment_delimiters();
+ code_edit->add_comment_delimiter("#", "");
+ code_edit->set_text("#region region_name\nline2\nline3\n#bad_end_tag\nvisible line");
+ CHECK_FALSE(code_edit->can_fold_line(0));
+ ERR_PRINT_ON;
+
+ // Bad nested region can't be folded.
+ ERR_PRINT_OFF;
+ code_edit->clear_comment_delimiters();
+ code_edit->add_comment_delimiter("#", "");
+ code_edit->set_text("#region without end\n#region region2\nline3\n#endregion\n#no_end");
+ CHECK_FALSE(code_edit->can_fold_line(0));
+ CHECK(code_edit->can_fold_line(1));
+ ERR_PRINT_ON;
+
+ // Nested region folding.
+ ERR_PRINT_OFF;
+ code_edit->clear_comment_delimiters();
+ code_edit->add_comment_delimiter("#", "");
+ code_edit->set_text("#region region1\n#region region2\nline3\n#endregion\n#endregion");
+ CHECK(code_edit->can_fold_line(0));
+ CHECK(code_edit->can_fold_line(1));
+ code_edit->fold_line(1);
+ CHECK(code_edit->get_next_visible_line_offset_from(2, 1) == 3);
+ code_edit->fold_line(0);
+ CHECK(code_edit->get_next_visible_line_offset_from(1, 1) == 4);
+ ERR_PRINT_ON;
+
+ // Unfolding a line inside a region unfold whole region.
+ code_edit->clear_comment_delimiters();
+ code_edit->add_comment_delimiter("#", "");
+ code_edit->set_text("#region region\ninside\nline3\n#endregion\nvisible");
+ code_edit->fold_line(0);
+ CHECK(code_edit->is_line_folded(0));
+ CHECK(code_edit->get_next_visible_line_offset_from(1, 1) == 4);
+ code_edit->unfold_line(1);
+ CHECK_FALSE(code_edit->is_line_folded(0));
+ }
+
+ memdelete(code_edit);
+}
+
TEST_CASE("[SceneTree][CodeEdit] completion") {
CodeEdit *code_edit = memnew(CodeEdit);
SceneTree::get_singleton()->get_root()->add_child(code_edit);
@@ -3176,7 +3390,9 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
/* After update, pending add should not be counted, */
/* also does not work on col 0 */
+ int before_text_caret_column = code_edit->get_caret_column();
code_edit->insert_text_at_caret("i");
+
code_edit->update_code_completion_options();
code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_CLASS, "item_0.", "item_0", Color(1, 0, 0), Ref<Resource>(), Color(1, 0, 0));
code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "item_1.", "item_1");
@@ -3212,12 +3428,40 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
/* Set size for mouse input. */
code_edit->set_size(Size2(100, 100));
+ /* Test home and end keys close the completion and move the caret */
+ /* => ui_text_caret_line_start */
+ code_edit->set_caret_column(before_text_caret_column + 1);
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_CLASS, "item_0.", "item_0", Color(1, 0, 0), Ref<Resource>(), Color(1, 0, 0));
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "item_1.", "item_1");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "item_2.", "item_2");
+
+ code_edit->update_code_completion_options();
+
+ SEND_GUI_ACTION("ui_text_caret_line_start");
+ code_edit->update_code_completion_options();
+ CHECK(code_edit->get_code_completion_selected_index() == -1);
+ CHECK(code_edit->get_caret_column() == 0);
+
+ /* => ui_text_caret_line_end */
+ code_edit->set_caret_column(before_text_caret_column + 1);
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_CLASS, "item_0.", "item_0", Color(1, 0, 0), Ref<Resource>(), Color(1, 0, 0));
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "item_1.", "item_1");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "item_2.", "item_2");
+
+ code_edit->update_code_completion_options();
+
+ SEND_GUI_ACTION("ui_text_caret_line_end");
+ code_edit->update_code_completion_options();
+ CHECK(code_edit->get_code_completion_selected_index() == -1);
+ CHECK(code_edit->get_caret_column() == before_text_caret_column + 1);
+
/* Check input. */
- SEND_GUI_ACTION("ui_end");
- CHECK(code_edit->get_code_completion_selected_index() == 2);
+ code_edit->set_caret_column(before_text_caret_column + 1);
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_CLASS, "item_0.", "item_0", Color(1, 0, 0), Ref<Resource>(), Color(1, 0, 0));
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "item_1.", "item_1");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "item_2.", "item_2");
- SEND_GUI_ACTION("ui_home");
- CHECK(code_edit->get_code_completion_selected_index() == 0);
+ code_edit->update_code_completion_options();
SEND_GUI_ACTION("ui_page_down");
CHECK(code_edit->get_code_completion_selected_index() == 2);
@@ -3446,7 +3690,7 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
}
SUBCASE("[CodeEdit] autocomplete suggestion order") {
- /* Favorize less fragmented suggestion. */
+ /* Prefer less fragmented suggestion. */
code_edit->clear();
code_edit->insert_text_at_caret("te");
code_edit->set_caret_column(2);
@@ -3456,7 +3700,7 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
code_edit->confirm_code_completion();
CHECK(code_edit->get_line(0) == "test");
- /* Favorize suggestion starting from the string to complete (matching start). */
+ /* Prefer suggestion starting with the string to complete (matching start). */
code_edit->clear();
code_edit->insert_text_at_caret("te");
code_edit->set_caret_column(2);
@@ -3466,7 +3710,7 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
code_edit->confirm_code_completion();
CHECK(code_edit->get_line(0) == "test");
- /* Favorize less fragment to matching start. */
+ /* Prefer less fragment over matching start. */
code_edit->clear();
code_edit->insert_text_at_caret("te");
code_edit->set_caret_column(2);
@@ -3476,37 +3720,37 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
code_edit->confirm_code_completion();
CHECK(code_edit->get_line(0) == "stest");
- /* Favorize closer location. */
+ /* Prefer good capitalization. */
code_edit->clear();
code_edit->insert_text_at_caret("te");
code_edit->set_caret_column(2);
code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "test", "test");
- code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "test_bis", "test_bis", Color(1, 1, 1), Ref<Resource>(), Variant::NIL, CodeEdit::LOCATION_LOCAL);
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "Test", "Test");
code_edit->update_code_completion_options();
code_edit->confirm_code_completion();
- CHECK(code_edit->get_line(0) == "test_bis");
+ CHECK(code_edit->get_line(0) == "test");
- /* Favorize matching start to location. */
+ /* Prefer matching start over good capitalization. */
code_edit->clear();
code_edit->insert_text_at_caret("te");
code_edit->set_caret_column(2);
- code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "test", "test");
- code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "stest_bis", "test_bis", Color(1, 1, 1), Ref<Resource>(), Variant::NIL, CodeEdit::LOCATION_LOCAL);
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "Test", "Test");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "stest_bis", "test_bis");
code_edit->update_code_completion_options();
code_edit->confirm_code_completion();
- CHECK(code_edit->get_line(0) == "test");
+ CHECK(code_edit->get_line(0) == "Test");
- /* Favorize good capitalization. */
+ /* Prefer closer location. */
code_edit->clear();
code_edit->insert_text_at_caret("te");
code_edit->set_caret_column(2);
code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "test", "test");
- code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "Test", "Test");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "test_bis", "test_bis", Color(1, 1, 1), Ref<Resource>(), Variant::NIL, CodeEdit::LOCATION_LOCAL);
code_edit->update_code_completion_options();
code_edit->confirm_code_completion();
- CHECK(code_edit->get_line(0) == "test");
+ CHECK(code_edit->get_line(0) == "test_bis");
- /* Favorize location to good capitalization. */
+ /* Prefer good capitalization over location. */
code_edit->clear();
code_edit->insert_text_at_caret("te");
code_edit->set_caret_column(2);
@@ -3514,9 +3758,9 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "Test", "Test", Color(1, 1, 1), Ref<Resource>(), Variant::NIL, CodeEdit::LOCATION_LOCAL);
code_edit->update_code_completion_options();
code_edit->confirm_code_completion();
- CHECK(code_edit->get_line(0) == "Test");
+ CHECK(code_edit->get_line(0) == "test");
- /* Favorize string to complete being closest to the start of the suggestion (closest to start). */
+ /* Prefer the start of the string to complete being closest to the start of the suggestion (closest to start). */
code_edit->clear();
code_edit->insert_text_at_caret("te");
code_edit->set_caret_column(2);
@@ -3526,17 +3770,78 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
code_edit->confirm_code_completion();
CHECK(code_edit->get_line(0) == "stest");
- /* Favorize good capitalization to closest to start. */
+ /* Prefer location over closest to start. */
code_edit->clear();
code_edit->insert_text_at_caret("te");
code_edit->set_caret_column(2);
- code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "sTest", "stest");
- code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "sstest", "sstest");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "stest", "stest");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "sstest", "sstest", Color(1, 1, 1), Ref<Resource>(), Variant::NIL, CodeEdit::LOCATION_LOCAL);
code_edit->update_code_completion_options();
code_edit->confirm_code_completion();
CHECK(code_edit->get_line(0) == "sstest");
}
+ SUBCASE("[CodeEdit] autocomplete currently selected option") {
+ code_edit->set_code_completion_enabled(true);
+ REQUIRE(code_edit->is_code_completion_enabled());
+
+ // Initially select item 0.
+ code_edit->insert_text_at_caret("te");
+ code_edit->set_caret_column(2);
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te1", "te1");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te3", "te3");
+ code_edit->update_code_completion_options();
+ CHECK_MESSAGE(code_edit->get_code_completion_selected_index() == 0, "Initially selected item should be 0.");
+
+ // After adding later options shouldn't update selection.
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te1", "te1");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te3", "te3");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te4", "te4"); // Added te4.
+ code_edit->update_code_completion_options();
+ CHECK_MESSAGE(code_edit->get_code_completion_selected_index() == 0, "Adding later options shouldn't update selection.");
+
+ code_edit->set_code_completion_selected_index(2);
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te1", "te1");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te3", "te3");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te4", "te4");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te5", "te5"); // Added te5.
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te6", "te6"); // Added te6.
+ code_edit->update_code_completion_options();
+ CHECK_MESSAGE(code_edit->get_code_completion_selected_index() == 2, "Adding later options shouldn't update selection.");
+
+ // Removing elements after selected element shouldn't update selection.
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te1", "te1");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te3", "te3");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te4", "te4");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te5", "te5"); // Removed te6.
+ code_edit->update_code_completion_options();
+ CHECK_MESSAGE(code_edit->get_code_completion_selected_index() == 2, "Removing elements after selected element shouldn't update selection.");
+
+ // Changing elements after selected element shouldn't update selection.
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te1", "te1");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te3", "te3");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te4", "te4");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te6", "te6"); // Changed te5->te6.
+ code_edit->update_code_completion_options();
+ CHECK_MESSAGE(code_edit->get_code_completion_selected_index() == 2, "Changing elements after selected element shouldn't update selection.");
+
+ // Changing elements before selected element should reset selection.
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te2", "te2"); // Changed te1->te2.
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te3", "te3");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te4", "te4");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te6", "te6");
+ code_edit->update_code_completion_options();
+ CHECK_MESSAGE(code_edit->get_code_completion_selected_index() == 0, "Changing elements before selected element should reset selection.");
+
+ // Removing elements before selected element should reset selection.
+ code_edit->set_code_completion_selected_index(2);
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te3", "te3"); // Removed te2.
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te4", "te4");
+ code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te6", "te6");
+ code_edit->update_code_completion_options();
+ CHECK_MESSAGE(code_edit->get_code_completion_selected_index() == 0, "Removing elements before selected element should reset selection.");
+ }
+
memdelete(code_edit);
}
@@ -3698,6 +4003,84 @@ TEST_CASE("[SceneTree][CodeEdit] New Line") {
memdelete(code_edit);
}
+TEST_CASE("[SceneTree][CodeEdit] Duplicate Lines") {
+ CodeEdit *code_edit = memnew(CodeEdit);
+ SceneTree::get_singleton()->get_root()->add_child(code_edit);
+ code_edit->grab_focus();
+
+ code_edit->set_text(R"(extends Node
+
+func _ready():
+ var a := len(OS.get_cmdline_args())
+ var b := get_child_count()
+ var c := a + b
+ for i in range(c):
+ print("This is the solution: ", sin(i))
+ var pos = get_index() - 1
+ print("Make sure this exits: %b" % pos)
+)");
+
+ /* Duplicate a single line without selection. */
+ code_edit->set_caret_line(0);
+ code_edit->duplicate_lines();
+ CHECK(code_edit->get_line(0) == "extends Node");
+ CHECK(code_edit->get_line(1) == "extends Node");
+ CHECK(code_edit->get_line(2) == "");
+
+ /* Duplicate multiple lines with selection. */
+ code_edit->set_caret_line(6);
+ code_edit->set_caret_column(15);
+ code_edit->select(4, 8, 6, 15);
+ code_edit->duplicate_lines();
+ CHECK(code_edit->get_line(6) == "\tvar c := a + b");
+ CHECK(code_edit->get_line(7) == "\tvar a := len(OS.get_cmdline_args())");
+ CHECK(code_edit->get_line(8) == "\tvar b := get_child_count()");
+ CHECK(code_edit->get_line(9) == "\tvar c := a + b");
+ CHECK(code_edit->get_line(10) == "\tfor i in range(c):");
+
+ /* Duplicate single lines with multiple carets. */
+ code_edit->deselect();
+ code_edit->set_caret_line(10);
+ code_edit->set_caret_column(1);
+ code_edit->add_caret(11, 2);
+ code_edit->add_caret(12, 1);
+ code_edit->duplicate_lines();
+ CHECK(code_edit->get_line(9) == "\tvar c := a + b");
+ CHECK(code_edit->get_line(10) == "\tfor i in range(c):");
+ CHECK(code_edit->get_line(11) == "\tfor i in range(c):");
+ CHECK(code_edit->get_line(12) == "\t\tprint(\"This is the solution: \", sin(i))");
+ CHECK(code_edit->get_line(13) == "\t\tprint(\"This is the solution: \", sin(i))");
+ CHECK(code_edit->get_line(14) == "\tvar pos = get_index() - 1");
+ CHECK(code_edit->get_line(15) == "\tvar pos = get_index() - 1");
+ CHECK(code_edit->get_line(16) == "\tprint(\"Make sure this exits: %b\" % pos)");
+
+ /* Duplicate multiple lines with multiple carets. */
+ code_edit->select(0, 0, 1, 2, 0);
+ code_edit->select(3, 0, 4, 2, 1);
+ code_edit->select(16, 0, 17, 0, 2);
+ code_edit->set_caret_line(1, false, true, 0, 0);
+ code_edit->set_caret_column(2, false, 0);
+ code_edit->set_caret_line(4, false, true, 0, 1);
+ code_edit->set_caret_column(2, false, 1);
+ code_edit->set_caret_line(17, false, true, 0, 2);
+ code_edit->set_caret_column(0, false, 2);
+ code_edit->duplicate_lines();
+ CHECK(code_edit->get_line(1) == "extends Node");
+ CHECK(code_edit->get_line(2) == "extends Node");
+ CHECK(code_edit->get_line(3) == "extends Node");
+ CHECK(code_edit->get_line(4) == "");
+ CHECK(code_edit->get_line(6) == "\tvar a := len(OS.get_cmdline_args())");
+ CHECK(code_edit->get_line(7) == "func _ready():");
+ CHECK(code_edit->get_line(8) == "\tvar a := len(OS.get_cmdline_args())");
+ CHECK(code_edit->get_line(9) == "\tvar b := get_child_count()");
+ CHECK(code_edit->get_line(20) == "\tprint(\"Make sure this exits: %b\" % pos)");
+ CHECK(code_edit->get_line(21) == "");
+ CHECK(code_edit->get_line(22) == "\tprint(\"Make sure this exits: %b\" % pos)");
+ CHECK(code_edit->get_line(23) == "");
+
+ memdelete(code_edit);
+}
+
} // namespace TestCodeEdit
#endif // TEST_CODE_EDIT_H
diff --git a/tests/scene/test_control.h b/tests/scene/test_control.h
new file mode 100644
index 0000000000..3d7e389e0a
--- /dev/null
+++ b/tests/scene/test_control.h
@@ -0,0 +1,66 @@
+/**************************************************************************/
+/* test_control.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_CONTROL_H
+#define TEST_CONTROL_H
+
+#include "scene/gui/control.h"
+
+#include "tests/test_macros.h"
+
+namespace TestControl {
+
+TEST_CASE("[SceneTree][Control]") {
+ SUBCASE("[Control][Global Transform] Global Transform should be accessible while not in SceneTree.") { // GH-79453
+ Control *test_node = memnew(Control);
+ Control *test_child = memnew(Control);
+ test_node->add_child(test_child);
+
+ test_node->set_global_position(Point2(1, 1));
+ CHECK_EQ(test_node->get_global_position(), Point2(1, 1));
+ CHECK_EQ(test_child->get_global_position(), Point2(1, 1));
+ test_node->set_global_position(Point2(2, 2));
+ CHECK_EQ(test_node->get_global_position(), Point2(2, 2));
+ test_node->set_scale(Vector2(4, 4));
+ CHECK_EQ(test_node->get_global_transform(), Transform2D(0, Size2(4, 4), 0, Vector2(2, 2)));
+ test_node->set_scale(Vector2(1, 1));
+ test_node->set_rotation_degrees(90);
+ CHECK_EQ(test_node->get_global_transform(), Transform2D(Math_PI / 2, Vector2(2, 2)));
+ test_node->set_pivot_offset(Vector2(1, 0));
+ CHECK_EQ(test_node->get_global_transform(), Transform2D(Math_PI / 2, Vector2(3, 1)));
+
+ memdelete(test_child);
+ memdelete(test_node);
+ }
+}
+
+} // namespace TestControl
+
+#endif // TEST_CONTROL_H
diff --git a/tests/scene/test_curve_3d.h b/tests/scene/test_curve_3d.h
index 4d7b718d7e..d73bb1ad35 100644
--- a/tests/scene/test_curve_3d.h
+++ b/tests/scene/test_curve_3d.h
@@ -209,6 +209,14 @@ TEST_CASE("[Curve3D] Sampling") {
CHECK(curve->get_closest_point(Vector3(50, 50, 0)) == Vector3(0, 50, 0));
CHECK(curve->get_closest_point(Vector3(0, 100, 0)) == Vector3(0, 50, 0));
}
+
+ SUBCASE("sample_baked_up_vector, off-axis") {
+ // Regression test for issue #81879
+ Ref<Curve3D> c = memnew(Curve3D);
+ c->add_point(Vector3());
+ c->add_point(Vector3(0, .1, 1));
+ CHECK_LT((c->sample_baked_up_vector(c->get_closest_offset(Vector3(0, 0, .9))) - Vector3(0, 0.995037, -0.099504)).length(), 0.01);
+ }
}
TEST_CASE("[Curve3D] Tessellation") {
diff --git a/tests/scene/test_node_2d.h b/tests/scene/test_node_2d.h
new file mode 100644
index 0000000000..7e93c77e22
--- /dev/null
+++ b/tests/scene/test_node_2d.h
@@ -0,0 +1,63 @@
+/**************************************************************************/
+/* test_node_2d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_NODE_2D_H
+#define TEST_NODE_2D_H
+
+#include "scene/2d/node_2d.h"
+
+#include "tests/test_macros.h"
+
+namespace TestNode2D {
+
+TEST_CASE("[SceneTree][Node2D]") {
+ SUBCASE("[Node2D][Global Transform] Global Transform should be accessible while not in SceneTree.") { // GH-79453
+ Node2D *test_node = memnew(Node2D);
+ test_node->set_name("node");
+ Node2D *test_child = memnew(Node2D);
+ test_child->set_name("child");
+ test_node->add_child(test_child);
+
+ test_node->set_global_position(Point2(1, 1));
+ CHECK_EQ(test_node->get_global_position(), Point2(1, 1));
+ CHECK_EQ(test_child->get_global_position(), Point2(1, 1));
+ test_node->set_global_position(Point2(2, 2));
+ CHECK_EQ(test_node->get_global_position(), Point2(2, 2));
+ test_node->set_global_transform(Transform2D(0, Point2(3, 3)));
+ CHECK_EQ(test_node->get_global_position(), Point2(3, 3));
+
+ memdelete(test_child);
+ memdelete(test_node);
+ }
+}
+
+} // namespace TestNode2D
+
+#endif // TEST_NODE_2D_H
diff --git a/tests/scene/test_primitives.h b/tests/scene/test_primitives.h
index 9232a3020d..552f722d24 100644
--- a/tests/scene/test_primitives.h
+++ b/tests/scene/test_primitives.h
@@ -232,7 +232,7 @@ TEST_CASE("[SceneTree][Primitive][Cylinder] Cylinder Primitive") {
CHECK(cylinder->get_bottom_radius() > 0);
CHECK(cylinder->get_height() > 0);
CHECK(cylinder->get_radial_segments() > 0);
- CHECK(cylinder->get_rings() > 0);
+ CHECK(cylinder->get_rings() >= 0);
}
SUBCASE("[SceneTree][Primitive][Cylinder] Set properties and get them") {
diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h
index 79766cd919..8f603c698d 100644
--- a/tests/scene/test_text_edit.h
+++ b/tests/scene/test_text_edit.h
@@ -3186,6 +3186,60 @@ TEST_CASE("[SceneTree][TextEdit] versioning") {
CHECK(text_edit->get_version() == 3); // Should this be cleared?
CHECK(text_edit->get_saved_version() == 0);
+ SUBCASE("[TextEdit] versioning selection") {
+ text_edit->set_text("Godot Engine\nWaiting for Godot\nTest Text for multi carat\nLine 4 Text");
+ text_edit->set_multiple_carets_enabled(true);
+
+ text_edit->remove_secondary_carets();
+ text_edit->deselect();
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(0);
+
+ CHECK(text_edit->get_caret_count() == 1);
+
+ Array caret_index;
+ caret_index.push_back(0);
+
+ for (int i = 1; i < 4; i++) {
+ caret_index.push_back(text_edit->add_caret(i, 0));
+ CHECK((int)caret_index.back() >= 0);
+ }
+
+ CHECK(text_edit->get_caret_count() == 4);
+
+ for (int i = 0; i < 4; i++) {
+ text_edit->select(i, 0, i, 5, caret_index[i]);
+ }
+
+ CHECK(text_edit->get_caret_count() == 4);
+ for (int i = 0; i < 4; i++) {
+ CHECK(text_edit->has_selection(caret_index[i]));
+ CHECK(text_edit->get_selection_from_line(caret_index[i]) == i);
+ CHECK(text_edit->get_selection_from_column(caret_index[i]) == 0);
+ CHECK(text_edit->get_selection_to_line(caret_index[i]) == i);
+ CHECK(text_edit->get_selection_to_column(caret_index[i]) == 5);
+ }
+ text_edit->begin_complex_operation();
+ text_edit->deselect();
+ text_edit->set_text("New Line Text");
+ text_edit->select(0, 0, 0, 7, 0);
+ text_edit->end_complex_operation();
+
+ CHECK(text_edit->get_caret_count() == 1);
+ CHECK(text_edit->get_selected_text(0) == "New Lin");
+
+ text_edit->undo();
+
+ CHECK(text_edit->get_caret_count() == 4);
+ for (int i = 0; i < 4; i++) {
+ CHECK(text_edit->has_selection(caret_index[i]));
+ CHECK(text_edit->get_selection_from_line(caret_index[i]) == i);
+ CHECK(text_edit->get_selection_from_column(caret_index[i]) == 0);
+ CHECK(text_edit->get_selection_to_line(caret_index[i]) == i);
+ CHECK(text_edit->get_selection_to_column(caret_index[i]) == 5);
+ }
+ }
+
memdelete(text_edit);
}
@@ -3193,7 +3247,7 @@ TEST_CASE("[SceneTree][TextEdit] search") {
TextEdit *text_edit = memnew(TextEdit);
SceneTree::get_singleton()->get_root()->add_child(text_edit);
- text_edit->set_text("hay needle, hay\nHAY NEEDLE, HAY");
+ text_edit->set_text("hay needle, hay\nHAY NEEDLE, HAY\nwordword.word.word");
int length = text_edit->get_line(1).length();
CHECK(text_edit->search("test", 0, 0, 0) == Point2i(-1, -1));
@@ -3225,6 +3279,11 @@ TEST_CASE("[SceneTree][TextEdit] search") {
CHECK(text_edit->search("need", TextEdit::SEARCH_WHOLE_WORDS | TextEdit::SEARCH_MATCH_CASE, 0, 0) == Point2i(-1, -1));
CHECK(text_edit->search("need", TextEdit::SEARCH_WHOLE_WORDS | TextEdit::SEARCH_MATCH_CASE | TextEdit::SEARCH_BACKWARDS, 0, 0) == Point2i(-1, -1));
+ CHECK(text_edit->search("word", TextEdit::SEARCH_WHOLE_WORDS, 2, 0) == Point2i(9, 2));
+ CHECK(text_edit->search("word", TextEdit::SEARCH_WHOLE_WORDS, 2, 10) == Point2i(14, 2));
+ CHECK(text_edit->search(".word", TextEdit::SEARCH_WHOLE_WORDS, 2, 0) == Point2i(8, 2));
+ CHECK(text_edit->search("word.", TextEdit::SEARCH_WHOLE_WORDS, 2, 0) == Point2i(9, 2));
+
ERR_PRINT_OFF;
CHECK(text_edit->search("", 0, 0, 0) == Point2i(-1, -1));
CHECK(text_edit->search("needle", 0, -1, 0) == Point2i(-1, -1));
@@ -3241,6 +3300,15 @@ TEST_CASE("[SceneTree][TextEdit] mouse") {
SceneTree::get_singleton()->get_root()->add_child(text_edit);
text_edit->set_size(Size2(800, 200));
+
+ CHECK(text_edit->get_rect_at_line_column(0, 0).get_position() == Point2i(0, 0));
+
+ text_edit->set_line(0, "A");
+ MessageQueue::get_singleton()->flush();
+ CHECK(text_edit->get_rect_at_line_column(0, 1).get_position().x > 0);
+
+ text_edit->clear(); // Necessary, otherwise the following test cases fail.
+
text_edit->set_line(0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vasius mattis leo, sed porta ex lacinia bibendum. Nunc bibendum pellentesque.");
MessageQueue::get_singleton()->flush();
@@ -3899,7 +3967,8 @@ TEST_CASE("[SceneTree][TextEdit] viewport") {
CHECK(text_edit->get_h_scroll() == 0);
text_edit->set_h_scroll(10000000);
- CHECK(text_edit->get_h_scroll() == 314);
+ CHECK(text_edit->get_h_scroll() == 306);
+ CHECK(text_edit->get_h_scroll_bar()->get_combined_minimum_size().x == 8);
text_edit->set_h_scroll(-100);
CHECK(text_edit->get_h_scroll() == 0);
@@ -4103,7 +4172,7 @@ TEST_CASE("[SceneTree][TextEdit] setter getters") {
CHECK_FALSE(text_edit->is_drawing_spaces());
}
- SUBCASE("[TextEdit] draw minimao") {
+ SUBCASE("[TextEdit] draw minimap") {
text_edit->set_draw_minimap(true);
CHECK(text_edit->is_drawing_minimap());
text_edit->set_draw_minimap(false);
diff --git a/tests/scene/test_viewport.h b/tests/scene/test_viewport.h
index dd4786977e..1afae66ee0 100644
--- a/tests/scene/test_viewport.h
+++ b/tests/scene/test_viewport.h
@@ -43,31 +43,53 @@
namespace TestViewport {
-class NotificationControl : public Control {
- GDCLASS(NotificationControl, Control);
+class NotificationControlViewport : public Control {
+ GDCLASS(NotificationControlViewport, Control);
protected:
void _notification(int p_what) {
switch (p_what) {
case NOTIFICATION_MOUSE_ENTER: {
+ if (mouse_over) {
+ invalid_order = true;
+ }
mouse_over = true;
} break;
case NOTIFICATION_MOUSE_EXIT: {
+ if (!mouse_over) {
+ invalid_order = true;
+ }
mouse_over = false;
} break;
+
+ case NOTIFICATION_MOUSE_ENTER_SELF: {
+ if (mouse_over_self) {
+ invalid_order = true;
+ }
+ mouse_over_self = true;
+ } break;
+
+ case NOTIFICATION_MOUSE_EXIT_SELF: {
+ if (!mouse_over_self) {
+ invalid_order = true;
+ }
+ mouse_over_self = false;
+ } break;
}
}
public:
bool mouse_over = false;
+ bool mouse_over_self = false;
+ bool invalid_order = false;
};
-// `NotificationControl`-derived class that additionally
+// `NotificationControlViewport`-derived class that additionally
// - allows start Dragging
// - stores mouse information of last event
-class DragStart : public NotificationControl {
- GDCLASS(DragStart, NotificationControl);
+class DragStart : public NotificationControlViewport {
+ GDCLASS(DragStart, NotificationControlViewport);
public:
MouseButton last_mouse_button;
@@ -93,9 +115,9 @@ public:
}
};
-// `NotificationControl`-derived class that acts as a Drag and Drop target.
-class DragTarget : public NotificationControl {
- GDCLASS(DragTarget, NotificationControl);
+// `NotificationControlViewport`-derived class that acts as a Drag and Drop target.
+class DragTarget : public NotificationControlViewport {
+ GDCLASS(DragTarget, NotificationControlViewport);
public:
Variant drag_data;
@@ -119,12 +141,15 @@ public:
TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
DragStart *node_a = memnew(DragStart);
- Control *node_b = memnew(Control);
+ NotificationControlViewport *node_b = memnew(NotificationControlViewport);
Node2D *node_c = memnew(Node2D);
DragTarget *node_d = memnew(DragTarget);
- Control *node_e = memnew(Control);
+ NotificationControlViewport *node_e = memnew(NotificationControlViewport);
Node *node_f = memnew(Node);
- Control *node_g = memnew(Control);
+ NotificationControlViewport *node_g = memnew(NotificationControlViewport);
+ NotificationControlViewport *node_h = memnew(NotificationControlViewport);
+ NotificationControlViewport *node_i = memnew(NotificationControlViewport);
+ NotificationControlViewport *node_j = memnew(NotificationControlViewport);
node_a->set_name(SNAME("NodeA"));
node_b->set_name(SNAME("NodeB"));
@@ -133,6 +158,9 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
node_e->set_name(SNAME("NodeE"));
node_f->set_name(SNAME("NodeF"));
node_g->set_name(SNAME("NodeG"));
+ node_h->set_name(SNAME("NodeH"));
+ node_i->set_name(SNAME("NodeI"));
+ node_j->set_name(SNAME("NodeJ"));
node_a->set_position(Point2i(0, 0));
node_b->set_position(Point2i(10, 10));
@@ -140,16 +168,25 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
node_d->set_position(Point2i(10, 10));
node_e->set_position(Point2i(10, 100));
node_g->set_position(Point2i(10, 100));
+ node_h->set_position(Point2i(10, 120));
+ node_i->set_position(Point2i(2, 0));
+ node_j->set_position(Point2i(2, 0));
node_a->set_size(Point2i(30, 30));
node_b->set_size(Point2i(30, 30));
node_d->set_size(Point2i(30, 30));
node_e->set_size(Point2i(10, 10));
node_g->set_size(Point2i(10, 10));
+ node_h->set_size(Point2i(10, 10));
+ node_i->set_size(Point2i(10, 10));
+ node_j->set_size(Point2i(10, 10));
node_a->set_focus_mode(Control::FOCUS_CLICK);
node_b->set_focus_mode(Control::FOCUS_CLICK);
node_d->set_focus_mode(Control::FOCUS_CLICK);
node_e->set_focus_mode(Control::FOCUS_CLICK);
node_g->set_focus_mode(Control::FOCUS_CLICK);
+ node_h->set_focus_mode(Control::FOCUS_CLICK);
+ node_i->set_focus_mode(Control::FOCUS_CLICK);
+ node_j->set_focus_mode(Control::FOCUS_CLICK);
Window *root = SceneTree::get_singleton()->get_root();
DisplayServerMock *DS = (DisplayServerMock *)(DisplayServer::get_singleton());
@@ -162,6 +199,9 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
// - e (Control)
// - f (Node)
// - g (Control)
+ // - h (Control)
+ // - i (Control)
+ // - j (Control)
root->add_child(node_a);
root->add_child(node_b);
node_b->add_child(node_c);
@@ -169,12 +209,17 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
root->add_child(node_e);
node_e->add_child(node_f);
node_f->add_child(node_g);
+ root->add_child(node_h);
+ node_h->add_child(node_i);
+ node_i->add_child(node_j);
Point2i on_a = Point2i(5, 5);
Point2i on_b = Point2i(15, 15);
Point2i on_d = Point2i(25, 25);
Point2i on_e = Point2i(15, 105);
Point2i on_g = Point2i(15, 105);
+ Point2i on_i = Point2i(13, 125);
+ Point2i on_j = Point2i(15, 125);
Point2i on_background = Point2i(500, 500);
Point2i on_outside = Point2i(-1, -1);
@@ -419,26 +464,612 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
SUBCASE("[Viewport][GuiInputEvent] Mouse Motion") {
// FIXME: Tooltips are not yet tested. They likely require an internal clock.
- SUBCASE("[Viewport][GuiInputEvent] Mouse Motion changes the Control, that it is over.") {
+ SUBCASE("[Viewport][GuiInputEvent] Mouse Motion changes the Control that it is over.") {
SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
CHECK_FALSE(node_a->mouse_over);
+ CHECK_FALSE(node_a->mouse_over_self);
// Move over Control.
SEND_GUI_MOUSE_MOTION_EVENT(on_a, MouseButtonMask::NONE, Key::NONE);
CHECK(node_a->mouse_over);
+ CHECK(node_a->mouse_over_self);
// No change.
SEND_GUI_MOUSE_MOTION_EVENT(on_a + Point2i(1, 1), MouseButtonMask::NONE, Key::NONE);
CHECK(node_a->mouse_over);
+ CHECK(node_a->mouse_over_self);
// Move over other Control.
SEND_GUI_MOUSE_MOTION_EVENT(on_d, MouseButtonMask::NONE, Key::NONE);
CHECK_FALSE(node_a->mouse_over);
+ CHECK_FALSE(node_a->mouse_over_self);
CHECK(node_d->mouse_over);
+ CHECK(node_d->mouse_over_self);
- // Move to background
+ // Move to background.
SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
CHECK_FALSE(node_d->mouse_over);
+ CHECK_FALSE(node_d->mouse_over_self);
+
+ CHECK_FALSE(node_a->invalid_order);
+ CHECK_FALSE(node_d->invalid_order);
+ }
+
+ SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification propagation.") {
+ node_d->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ node_g->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(node_b->mouse_over);
+ CHECK_FALSE(node_b->mouse_over_self);
+ CHECK_FALSE(node_d->mouse_over);
+ CHECK_FALSE(node_d->mouse_over_self);
+
+ // Move to Control node_d. node_b receives mouse over since it is only separated by a CanvasItem.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_d, MouseButtonMask::NONE, Key::NONE);
+ CHECK(node_b->mouse_over);
+ CHECK_FALSE(node_b->mouse_over_self);
+ CHECK(node_d->mouse_over);
+ CHECK(node_d->mouse_over_self);
+
+ // Move to background.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(node_b->mouse_over);
+ CHECK_FALSE(node_b->mouse_over_self);
+ CHECK_FALSE(node_d->mouse_over);
+ CHECK_FALSE(node_d->mouse_over_self);
+
+ CHECK_FALSE(node_e->mouse_over);
+ CHECK_FALSE(node_e->mouse_over_self);
+ CHECK_FALSE(node_g->mouse_over);
+ CHECK_FALSE(node_g->mouse_over_self);
+
+ // Move to Control node_g. node_g receives mouse over but node_e does not since it is separated by a non-CanvasItem.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_g, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(node_e->mouse_over);
+ CHECK_FALSE(node_e->mouse_over_self);
+ CHECK(node_g->mouse_over);
+ CHECK(node_g->mouse_over_self);
+
+ // Move to background.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(node_e->mouse_over);
+ CHECK_FALSE(node_e->mouse_over_self);
+ CHECK_FALSE(node_g->mouse_over);
+ CHECK_FALSE(node_g->mouse_over_self);
+
+ CHECK_FALSE(node_b->invalid_order);
+ CHECK_FALSE(node_d->invalid_order);
+ CHECK_FALSE(node_e->invalid_order);
+ CHECK_FALSE(node_g->invalid_order);
+
+ node_d->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ node_g->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ }
+
+ SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification propagation when moving into child.") {
+ SIGNAL_WATCH(node_i, SNAME("mouse_entered"));
+ SIGNAL_WATCH(node_i, SNAME("mouse_exited"));
+ Array signal_args;
+ signal_args.push_back(Array());
+
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+
+ // Move to background.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK_FALSE(node_j->mouse_over);
+ CHECK_FALSE(node_j->mouse_over_self);
+
+ // Move to Control node_i.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_i, MouseButtonMask::NONE, Key::NONE);
+ CHECK(node_i->mouse_over);
+ CHECK(node_i->mouse_over_self);
+ CHECK_FALSE(node_j->mouse_over);
+ CHECK_FALSE(node_j->mouse_over_self);
+ SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ // Move to child Control node_j. node_i should not receive any new Mouse Enter signals.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
+ CHECK(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ // Move to parent Control node_i. node_i should not receive any new Mouse Enter signals.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_i, MouseButtonMask::NONE, Key::NONE);
+ CHECK(node_i->mouse_over);
+ CHECK(node_i->mouse_over_self);
+ CHECK_FALSE(node_j->mouse_over);
+ CHECK_FALSE(node_j->mouse_over_self);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ // Move to background.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK_FALSE(node_j->mouse_over);
+ CHECK_FALSE(node_j->mouse_over_self);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
+ SIGNAL_CHECK(SNAME("mouse_exited"), signal_args);
+
+ CHECK_FALSE(node_i->invalid_order);
+ CHECK_FALSE(node_j->invalid_order);
+
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+
+ SIGNAL_UNWATCH(node_i, SNAME("mouse_entered"));
+ SIGNAL_UNWATCH(node_i, SNAME("mouse_exited"));
+ }
+
+ SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification propagation with top level.") {
+ node_c->set_as_top_level(true);
+ node_i->set_as_top_level(true);
+ node_c->set_position(node_b->get_global_position());
+ node_i->set_position(node_h->get_global_position());
+ node_d->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(node_b->mouse_over);
+ CHECK_FALSE(node_b->mouse_over_self);
+ CHECK_FALSE(node_d->mouse_over);
+ CHECK_FALSE(node_d->mouse_over_self);
+
+ // Move to Control node_d. node_b does not receive mouse over since node_c is top level.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_d, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(node_b->mouse_over);
+ CHECK_FALSE(node_b->mouse_over_self);
+ CHECK(node_d->mouse_over);
+ CHECK(node_d->mouse_over_self);
+
+ // Move to background.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(node_b->mouse_over);
+ CHECK_FALSE(node_b->mouse_over_self);
+ CHECK_FALSE(node_d->mouse_over);
+ CHECK_FALSE(node_d->mouse_over_self);
+
+ CHECK_FALSE(node_g->mouse_over);
+ CHECK_FALSE(node_g->mouse_over_self);
+ CHECK_FALSE(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK_FALSE(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+
+ // Move to Control node_j. node_h does not receive mouse over since node_i is top level.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+
+ // Move to background.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK_FALSE(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK_FALSE(node_j->mouse_over);
+ CHECK_FALSE(node_j->mouse_over_self);
+
+ CHECK_FALSE(node_b->invalid_order);
+ CHECK_FALSE(node_d->invalid_order);
+ CHECK_FALSE(node_e->invalid_order);
+ CHECK_FALSE(node_h->invalid_order);
+ CHECK_FALSE(node_i->invalid_order);
+ CHECK_FALSE(node_j->invalid_order);
+
+ node_c->set_as_top_level(false);
+ node_i->set_as_top_level(false);
+ node_c->set_position(Point2i(0, 0));
+ node_i->set_position(Point2i(0, 0));
+ node_d->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ }
+
+ SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification propagation with mouse filter stop.") {
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+
+ // Move to background.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK_FALSE(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK_FALSE(node_j->mouse_over);
+ CHECK_FALSE(node_j->mouse_over_self);
+
+ // Move to Control node_j. node_h does not receive mouse over since node_i is MOUSE_FILTER_STOP.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+
+ // Move to background.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK_FALSE(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK_FALSE(node_j->mouse_over);
+ CHECK_FALSE(node_j->mouse_over_self);
+
+ CHECK_FALSE(node_h->invalid_order);
+ CHECK_FALSE(node_i->invalid_order);
+ CHECK_FALSE(node_j->invalid_order);
+
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ }
+
+ SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification propagation with mouse filter ignore.") {
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+
+ // Move to background.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK_FALSE(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK_FALSE(node_j->mouse_over);
+ CHECK_FALSE(node_j->mouse_over_self);
+
+ // Move to Control node_j. node_i does not receive mouse over since node_i is MOUSE_FILTER_IGNORE.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
+ CHECK(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK_FALSE(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+
+ // Move to background.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
+ CHECK_FALSE(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK_FALSE(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK_FALSE(node_j->mouse_over);
+ CHECK_FALSE(node_j->mouse_over_self);
+
+ CHECK_FALSE(node_h->invalid_order);
+ CHECK_FALSE(node_i->invalid_order);
+ CHECK_FALSE(node_j->invalid_order);
+
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ }
+
+ SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when changing top level.") {
+ SIGNAL_WATCH(node_i, SNAME("mouse_entered"));
+ SIGNAL_WATCH(node_i, SNAME("mouse_exited"));
+ Array signal_args;
+ signal_args.push_back(Array());
+
+ node_d->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+
+ // Move to Control node_d.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_d, MouseButtonMask::NONE, Key::NONE);
+ CHECK(node_b->mouse_over);
+ CHECK_FALSE(node_b->mouse_over_self);
+ CHECK(node_d->mouse_over);
+ CHECK(node_d->mouse_over_self);
+
+ // Change node_c to be top level. node_b should receive Mouse Exit.
+ node_c->set_as_top_level(true);
+ CHECK_FALSE(node_b->mouse_over);
+ CHECK_FALSE(node_b->mouse_over_self);
+ CHECK(node_d->mouse_over);
+ CHECK(node_d->mouse_over_self);
+
+ // Change node_c to be not top level. node_b should receive Mouse Enter.
+ node_c->set_as_top_level(false);
+ CHECK(node_b->mouse_over);
+ CHECK_FALSE(node_b->mouse_over_self);
+ CHECK(node_d->mouse_over);
+ CHECK(node_d->mouse_over_self);
+
+ // Move to Control node_j.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
+ CHECK(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+ SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ // Change node_i to top level. node_h should receive Mouse Exit. node_i should not receive any new signals.
+ node_i->set_as_top_level(true);
+ CHECK_FALSE(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ // Change node_i to not top level. node_h should receive Mouse Enter. node_i should not receive any new signals.
+ node_i->set_as_top_level(false);
+ CHECK(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ CHECK_FALSE(node_b->invalid_order);
+ CHECK_FALSE(node_d->invalid_order);
+ CHECK_FALSE(node_e->invalid_order);
+ CHECK_FALSE(node_h->invalid_order);
+ CHECK_FALSE(node_i->invalid_order);
+ CHECK_FALSE(node_j->invalid_order);
+
+ node_d->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+
+ SIGNAL_UNWATCH(node_i, SNAME("mouse_entered"));
+ SIGNAL_UNWATCH(node_i, SNAME("mouse_exited"));
+ }
+
+ SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when changing the mouse filter to stop.") {
+ SIGNAL_WATCH(node_i, SNAME("mouse_entered"));
+ SIGNAL_WATCH(node_i, SNAME("mouse_exited"));
+ Array signal_args;
+ signal_args.push_back(Array());
+
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+
+ // Move to Control node_j.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
+ CHECK(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+ SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ // Change node_i to MOUSE_FILTER_STOP. node_h should receive Mouse Exit. node_i should not receive any new signals.
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ CHECK_FALSE(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ // Change node_i to MOUSE_FILTER_PASS. node_h should receive Mouse Enter. node_i should not receive any new signals.
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ CHECK(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ CHECK_FALSE(node_h->invalid_order);
+ CHECK_FALSE(node_i->invalid_order);
+ CHECK_FALSE(node_j->invalid_order);
+
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+
+ SIGNAL_UNWATCH(node_i, SNAME("mouse_entered"));
+ SIGNAL_UNWATCH(node_i, SNAME("mouse_exited"));
+ }
+
+ SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when changing the mouse filter to ignore.") {
+ SIGNAL_WATCH(node_i, SNAME("mouse_entered"));
+ SIGNAL_WATCH(node_i, SNAME("mouse_exited"));
+ Array signal_args;
+ signal_args.push_back(Array());
+
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+
+ // Move to Control node_j.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
+ CHECK(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+ SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ // Change node_i to MOUSE_FILTER_IGNORE. node_i should receive Mouse Exit.
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ CHECK(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK_FALSE(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
+ SIGNAL_CHECK(SNAME("mouse_exited"), signal_args);
+
+ // Change node_i to MOUSE_FILTER_PASS. node_i should receive Mouse Enter.
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ CHECK(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+ SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ // Change node_j to MOUSE_FILTER_IGNORE. After updating the mouse motion, node_i should now have mouse_over_self.
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
+ CHECK(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK(node_i->mouse_over);
+ CHECK(node_i->mouse_over_self);
+ CHECK_FALSE(node_j->mouse_over);
+ CHECK_FALSE(node_j->mouse_over_self);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ // Change node_j to MOUSE_FILTER_PASS. After updating the mouse motion, node_j should now have mouse_over_self.
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
+ CHECK(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ CHECK_FALSE(node_h->invalid_order);
+ CHECK_FALSE(node_i->invalid_order);
+ CHECK_FALSE(node_j->invalid_order);
+
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+
+ SIGNAL_UNWATCH(node_i, SNAME("mouse_entered"));
+ SIGNAL_UNWATCH(node_i, SNAME("mouse_exited"));
+ }
+
+ SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when removing the hovered Control.") {
+ SIGNAL_WATCH(node_h, SNAME("mouse_entered"));
+ SIGNAL_WATCH(node_h, SNAME("mouse_exited"));
+ Array signal_args;
+ signal_args.push_back(Array());
+
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+
+ // Move to Control node_j.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
+ CHECK(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+ SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ // Remove node_i from the tree. node_i and node_j should receive Mouse Exit. node_h should not receive any new signals.
+ node_h->remove_child(node_i);
+ CHECK(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK_FALSE(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK_FALSE(node_j->mouse_over);
+ CHECK_FALSE(node_j->mouse_over_self);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ // Add node_i to the tree and update the mouse. node_i and node_j should receive Mouse Enter. node_h should not receive any new signals.
+ node_h->add_child(node_i);
+ SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
+ CHECK(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ CHECK_FALSE(node_h->invalid_order);
+ CHECK_FALSE(node_i->invalid_order);
+ CHECK_FALSE(node_j->invalid_order);
+
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+
+ SIGNAL_UNWATCH(node_h, SNAME("mouse_entered"));
+ SIGNAL_UNWATCH(node_h, SNAME("mouse_exited"));
+ }
+
+ SUBCASE("[Viewport][GuiInputEvent] Mouse Enter/Exit notification when hiding the hovered Control.") {
+ SIGNAL_WATCH(node_h, SNAME("mouse_entered"));
+ SIGNAL_WATCH(node_h, SNAME("mouse_exited"));
+ Array signal_args;
+ signal_args.push_back(Array());
+
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+
+ // Move to Control node_j.
+ SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
+ CHECK(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+ SIGNAL_CHECK(SNAME("mouse_entered"), signal_args);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ // Hide node_i. node_i and node_j should receive Mouse Exit. node_h should not receive any new signals.
+ node_i->hide();
+ CHECK(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK_FALSE(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK_FALSE(node_j->mouse_over);
+ CHECK_FALSE(node_j->mouse_over_self);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ // Show node_i and update the mouse. node_i and node_j should receive Mouse Enter. node_h should not receive any new signals.
+ node_i->show();
+ SEND_GUI_MOUSE_MOTION_EVENT(on_j, MouseButtonMask::NONE, Key::NONE);
+ CHECK(node_h->mouse_over);
+ CHECK_FALSE(node_h->mouse_over_self);
+ CHECK(node_i->mouse_over);
+ CHECK_FALSE(node_i->mouse_over_self);
+ CHECK(node_j->mouse_over);
+ CHECK(node_j->mouse_over_self);
+ SIGNAL_CHECK_FALSE(SNAME("mouse_entered"));
+ SIGNAL_CHECK_FALSE(SNAME("mouse_exited"));
+
+ CHECK_FALSE(node_h->invalid_order);
+ CHECK_FALSE(node_i->invalid_order);
+ CHECK_FALSE(node_j->invalid_order);
+
+ node_i->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+ node_j->set_mouse_filter(Control::MOUSE_FILTER_STOP);
+
+ SIGNAL_UNWATCH(node_h, SNAME("mouse_entered"));
+ SIGNAL_UNWATCH(node_h, SNAME("mouse_exited"));
}
SUBCASE("[Viewport][GuiInputEvent] Window Mouse Enter/Exit signals.") {
@@ -710,6 +1341,9 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
}
}
+ memdelete(node_j);
+ memdelete(node_i);
+ memdelete(node_h);
memdelete(node_g);
memdelete(node_f);
memdelete(node_e);
diff --git a/tests/scene/test_window.h b/tests/scene/test_window.h
index e0c55101de..592cccfd7e 100644
--- a/tests/scene/test_window.h
+++ b/tests/scene/test_window.h
@@ -38,8 +38,8 @@
namespace TestWindow {
-class NotificationControl : public Control {
- GDCLASS(NotificationControl, Control);
+class NotificationControlWindow : public Control {
+ GDCLASS(NotificationControlWindow, Control);
protected:
void _notification(int p_what) {
@@ -69,7 +69,7 @@ TEST_CASE("[SceneTree][Window]") {
w->set_content_scale_size(Size2i(200, 200));
w->set_content_scale_mode(Window::CONTENT_SCALE_MODE_CANVAS_ITEMS);
w->set_content_scale_aspect(Window::CONTENT_SCALE_ASPECT_KEEP);
- NotificationControl *c = memnew(NotificationControl);
+ NotificationControlWindow *c = memnew(NotificationControlWindow);
w->add_child(c);
c->set_size(Size2i(100, 100));
c->set_position(Size2i(-50, -50));
diff --git a/tests/servers/rendering/test_shader_preprocessor.h b/tests/servers/rendering/test_shader_preprocessor.h
index d65eb522e8..e12da8a2db 100644
--- a/tests/servers/rendering/test_shader_preprocessor.h
+++ b/tests/servers/rendering/test_shader_preprocessor.h
@@ -66,7 +66,7 @@ String remove_spaces(String &p_str) {
for (int n = 0; n < p_str.size(); n++) {
// These test cases only use ASCII.
- auto c = static_cast<unsigned char>(p_str[n]);
+ unsigned char c = static_cast<unsigned char>(p_str[n]);
if (std::isblank(c)) {
has_removed = true;
} else {
@@ -92,7 +92,7 @@ String remove_spaces(String &p_str) {
String compact_spaces(String &p_str) {
Vector<String> lines = p_str.split("\n", false);
erase_all_empty(lines);
- for (auto &line : lines) {
+ for (String &line : lines) {
line = remove_spaces(line);
}
return String("\n").join(lines);
@@ -294,7 +294,7 @@ TEST_CASE("[ShaderPreprocessor] Concatenation sorting network") {
CHECK_SHADER_EQ(result, expected);
}
-TEST_CASE("[ShaderPreprocessor] Undefined behaviour") {
+TEST_CASE("[ShaderPreprocessor] Undefined behavior") {
// None of these are valid concatenation, nor valid shader code.
// Don't care about results, just make sure there's no crash.
const String filename("somefile.gdshader");
diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h
index a116559cb2..5ab2975b74 100644
--- a/tests/servers/test_navigation_server_3d.h
+++ b/tests/servers/test_navigation_server_3d.h
@@ -123,6 +123,7 @@ TEST_SUITE("[Navigation]") {
CHECK_FALSE(map.is_valid());
SUBCASE("Queries against invalid map should return empty or invalid values") {
+ ERR_PRINT_OFF;
CHECK_EQ(navigation_server->map_get_closest_point(map, Vector3(7, 7, 7)), Vector3());
CHECK_EQ(navigation_server->map_get_closest_point_normal(map, Vector3(7, 7, 7)), Vector3());
CHECK_FALSE(navigation_server->map_get_closest_point_owner(map, Vector3(7, 7, 7)).is_valid());
@@ -141,6 +142,7 @@ TEST_SUITE("[Navigation]") {
CHECK_EQ(query_result->get_path_types().size(), 0);
CHECK_EQ(query_result->get_path_rids().size(), 0);
CHECK_EQ(query_result->get_path_owner_ids().size(), 0);
+ ERR_PRINT_ON;
}
map = navigation_server->map_create();
@@ -225,6 +227,7 @@ TEST_SUITE("[Navigation]") {
navigation_server->map_set_active(map, true);
navigation_server->process(0.0); // Give server some cycles to commit.
+ ERR_PRINT_OFF;
CHECK_EQ(navigation_server->map_get_closest_point(map, Vector3(7, 7, 7)), Vector3());
CHECK_EQ(navigation_server->map_get_closest_point_normal(map, Vector3(7, 7, 7)), Vector3());
CHECK_FALSE(navigation_server->map_get_closest_point_owner(map, Vector3(7, 7, 7)).is_valid());
@@ -243,6 +246,7 @@ TEST_SUITE("[Navigation]") {
CHECK_EQ(query_result->get_path_types().size(), 0);
CHECK_EQ(query_result->get_path_rids().size(), 0);
CHECK_EQ(query_result->get_path_owner_ids().size(), 0);
+ ERR_PRINT_ON;
navigation_server->map_set_active(map, false);
navigation_server->process(0.0); // Give server some cycles to commit.
@@ -350,9 +354,11 @@ TEST_SUITE("[Navigation]") {
}
SUBCASE("Queries against empty region should return empty or invalid values") {
+ ERR_PRINT_OFF;
CHECK_EQ(navigation_server->region_get_connections_count(region), 0);
CHECK_EQ(navigation_server->region_get_connection_pathway_end(region, 55), Vector3());
CHECK_EQ(navigation_server->region_get_connection_pathway_start(region, 55), Vector3());
+ ERR_PRINT_ON;
}
navigation_server->free(region);
@@ -423,6 +429,105 @@ TEST_SUITE("[Navigation]") {
navigation_server->free(map);
}
+ TEST_CASE("[NavigationServer3D] Server should make agents avoid dynamic obstacles when avoidance enabled") {
+ NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
+
+ RID map = navigation_server->map_create();
+ RID agent_1 = navigation_server->agent_create();
+ RID obstacle_1 = navigation_server->obstacle_create();
+
+ navigation_server->map_set_active(map, true);
+
+ navigation_server->agent_set_map(agent_1, map);
+ navigation_server->agent_set_avoidance_enabled(agent_1, true);
+ navigation_server->agent_set_position(agent_1, Vector3(0, 0, 0));
+ navigation_server->agent_set_radius(agent_1, 1);
+ navigation_server->agent_set_velocity(agent_1, Vector3(1, 0, 0));
+ CallableMock agent_1_avoidance_callback_mock;
+ navigation_server->agent_set_avoidance_callback(agent_1, callable_mp(&agent_1_avoidance_callback_mock, &CallableMock::function1));
+
+ navigation_server->obstacle_set_map(obstacle_1, map);
+ navigation_server->obstacle_set_avoidance_enabled(obstacle_1, true);
+ navigation_server->obstacle_set_position(obstacle_1, Vector3(2.5, 0, 0.5));
+ navigation_server->obstacle_set_radius(obstacle_1, 1);
+
+ CHECK_EQ(agent_1_avoidance_callback_mock.function1_calls, 0);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ CHECK_EQ(agent_1_avoidance_callback_mock.function1_calls, 1);
+ Vector3 agent_1_safe_velocity = agent_1_avoidance_callback_mock.function1_latest_arg0;
+ CHECK_MESSAGE(agent_1_safe_velocity.x > 0, "Agent 1 should move a bit along desired velocity (+X).");
+ CHECK_MESSAGE(agent_1_safe_velocity.z < 0, "Agent 1 should move a bit to the side so that it avoids obstacle.");
+
+ navigation_server->free(obstacle_1);
+ navigation_server->free(agent_1);
+ navigation_server->free(map);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ }
+
+ TEST_CASE("[NavigationServer3D] Server should make agents avoid static obstacles when avoidance enabled") {
+ NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
+
+ RID map = navigation_server->map_create();
+ RID agent_1 = navigation_server->agent_create();
+ RID agent_2 = navigation_server->agent_create();
+ RID obstacle_1 = navigation_server->obstacle_create();
+
+ navigation_server->map_set_active(map, true);
+
+ navigation_server->agent_set_map(agent_1, map);
+ navigation_server->agent_set_avoidance_enabled(agent_1, true);
+ navigation_server->agent_set_radius(agent_1, 1.6); // Have hit the obstacle already.
+ navigation_server->agent_set_velocity(agent_1, Vector3(1, 0, 0));
+ CallableMock agent_1_avoidance_callback_mock;
+ navigation_server->agent_set_avoidance_callback(agent_1, callable_mp(&agent_1_avoidance_callback_mock, &CallableMock::function1));
+
+ navigation_server->agent_set_map(agent_2, map);
+ navigation_server->agent_set_avoidance_enabled(agent_2, true);
+ navigation_server->agent_set_radius(agent_2, 1.4); // Haven't hit the obstacle yet.
+ navigation_server->agent_set_velocity(agent_2, Vector3(1, 0, 0));
+ CallableMock agent_2_avoidance_callback_mock;
+ navigation_server->agent_set_avoidance_callback(agent_2, callable_mp(&agent_2_avoidance_callback_mock, &CallableMock::function1));
+
+ navigation_server->obstacle_set_map(obstacle_1, map);
+ navigation_server->obstacle_set_avoidance_enabled(obstacle_1, true);
+ PackedVector3Array obstacle_1_vertices;
+
+ SUBCASE("Static obstacles should work on ground level") {
+ navigation_server->agent_set_position(agent_1, Vector3(0, 0, 0));
+ navigation_server->agent_set_position(agent_2, Vector3(0, 0, 5));
+ obstacle_1_vertices.push_back(Vector3(1.5, 0, 0.5));
+ obstacle_1_vertices.push_back(Vector3(1.5, 0, 4.5));
+ }
+
+ SUBCASE("Static obstacles should work when elevated") {
+ navigation_server->agent_set_position(agent_1, Vector3(0, 5, 0));
+ navigation_server->agent_set_position(agent_2, Vector3(0, 5, 5));
+ obstacle_1_vertices.push_back(Vector3(1.5, 0, 0.5));
+ obstacle_1_vertices.push_back(Vector3(1.5, 0, 4.5));
+ navigation_server->obstacle_set_position(obstacle_1, Vector3(0, 5, 0));
+ }
+
+ navigation_server->obstacle_set_vertices(obstacle_1, obstacle_1_vertices);
+
+ CHECK_EQ(agent_1_avoidance_callback_mock.function1_calls, 0);
+ CHECK_EQ(agent_2_avoidance_callback_mock.function1_calls, 0);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ CHECK_EQ(agent_1_avoidance_callback_mock.function1_calls, 1);
+ CHECK_EQ(agent_2_avoidance_callback_mock.function1_calls, 1);
+ Vector3 agent_1_safe_velocity = agent_1_avoidance_callback_mock.function1_latest_arg0;
+ Vector3 agent_2_safe_velocity = agent_2_avoidance_callback_mock.function1_latest_arg0;
+ CHECK_MESSAGE(agent_1_safe_velocity.x > 0, "Agent 1 should move a bit along desired velocity (+X).");
+ CHECK_MESSAGE(agent_1_safe_velocity.z < 0, "Agent 1 should move a bit to the side so that it avoids obstacle.");
+ CHECK_MESSAGE(agent_2_safe_velocity.x > 0, "Agent 2 should move a bit along desired velocity (+X).");
+ CHECK_MESSAGE(agent_2_safe_velocity.z == 0, "Agent 2 should not move to the side.");
+
+ navigation_server->free(obstacle_1);
+ navigation_server->free(agent_2);
+ navigation_server->free(agent_1);
+ navigation_server->free(map);
+ navigation_server->process(0.0); // Give server some cycles to commit.
+ }
+
#ifndef DISABLE_DEPRECATED
// This test case uses only public APIs on purpose - other test cases use simplified baking.
// FIXME: Remove once deprecated `region_bake_navigation_mesh()` is removed.
@@ -450,7 +555,9 @@ TEST_SUITE("[Navigation]") {
CHECK_EQ(navigation_mesh->get_polygon_count(), 0);
CHECK_EQ(navigation_mesh->get_vertices().size(), 0);
+ ERR_PRINT_OFF;
navigation_server->region_bake_navigation_mesh(navigation_mesh, node_3d);
+ ERR_PRINT_ON;
// FIXME: The above line should trigger the update (line below) under the hood.
navigation_server->region_set_navigation_mesh(region, navigation_mesh); // Force update.
CHECK_EQ(navigation_mesh->get_polygon_count(), 2);
diff --git a/tests/servers/test_text_server.h b/tests/servers/test_text_server.h
index eef5b850ca..0f23929e1e 100644
--- a/tests/servers/test_text_server.h
+++ b/tests/servers/test_text_server.h
@@ -70,7 +70,7 @@ TEST_SUITE("[TextServer]") {
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
ts->font_set_allow_system_fallback(font1, false);
RID font2 = ts->create_font();
- ts->font_set_data_ptr(font2, _font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size);
+ ts->font_set_data_ptr(font2, _font_NotoSansThai_Regular, _font_NotoSansThai_Regular_size);
ts->font_set_allow_system_fallback(font2, false);
Array font;
@@ -177,7 +177,7 @@ TEST_SUITE("[TextServer]") {
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
ts->font_set_allow_system_fallback(font1, false);
RID font2 = ts->create_font();
- ts->font_set_data_ptr(font2, _font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size);
+ ts->font_set_data_ptr(font2, _font_NotoSansThai_Regular, _font_NotoSansThai_Regular_size);
ts->font_set_allow_system_fallback(font2, false);
RID font3 = ts->create_font();
ts->font_set_data_ptr(font3, _font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size);
@@ -511,7 +511,7 @@ TEST_SUITE("[TextServer]") {
RID font1 = ts->create_font();
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
RID font2 = ts->create_font();
- ts->font_set_data_ptr(font2, _font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size);
+ ts->font_set_data_ptr(font2, _font_NotoSansThai_Regular, _font_NotoSansThai_Regular_size);
Array font;
font.push_back(font1);
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index f1e348345b..5187ebd00f 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -89,12 +89,15 @@
#include "tests/core/variant/test_array.h"
#include "tests/core/variant/test_dictionary.h"
#include "tests/core/variant/test_variant.h"
+#include "tests/core/variant/test_variant_utility.h"
#include "tests/scene/test_animation.h"
#include "tests/scene/test_arraymesh.h"
#include "tests/scene/test_audio_stream_wav.h"
#include "tests/scene/test_bit_map.h"
+#include "tests/scene/test_camera_3d.h"
#include "tests/scene/test_code_edit.h"
#include "tests/scene/test_color_picker.h"
+#include "tests/scene/test_control.h"
#include "tests/scene/test_curve.h"
#include "tests/scene/test_curve_2d.h"
#include "tests/scene/test_curve_3d.h"
@@ -106,6 +109,7 @@
#include "tests/scene/test_navigation_region_2d.h"
#include "tests/scene/test_navigation_region_3d.h"
#include "tests/scene/test_node.h"
+#include "tests/scene/test_node_2d.h"
#include "tests/scene/test_packed_scene.h"
#include "tests/scene/test_path_2d.h"
#include "tests/scene/test_path_3d.h"
@@ -210,7 +214,6 @@ struct GodotTestCaseListener : public doctest::IReporter {
PhysicsServer2D *physics_server_2d = nullptr;
NavigationServer3D *navigation_server_3d = nullptr;
NavigationServer2D *navigation_server_2d = nullptr;
- ThemeDB *theme_db = nullptr;
void test_case_start(const doctest::TestCaseData &p_in) override {
reinitialize();
@@ -236,21 +239,26 @@ struct GodotTestCaseListener : public doctest::IReporter {
RenderingServerDefault::get_singleton()->init();
RenderingServerDefault::get_singleton()->set_render_loop_enabled(false);
+ // ThemeDB requires RenderingServer to initialize the default theme.
+ // So we have to do this for each test case. Also make sure there is
+ // no residual theme from something else.
+ ThemeDB::get_singleton()->finalize_theme();
+ ThemeDB::get_singleton()->initialize_theme_noproject();
+
physics_server_3d = PhysicsServer3DManager::get_singleton()->new_default_server();
physics_server_3d->init();
physics_server_2d = PhysicsServer2DManager::get_singleton()->new_default_server();
physics_server_2d->init();
+ ERR_PRINT_OFF;
navigation_server_3d = NavigationServer3DManager::new_default_server();
- navigation_server_2d = memnew(NavigationServer2D);
+ navigation_server_2d = NavigationServer2DManager::new_default_server();
+ ERR_PRINT_ON;
memnew(InputMap);
InputMap::get_singleton()->load_default();
- theme_db = memnew(ThemeDB);
- theme_db->initialize_theme_noproject();
-
memnew(SceneTree);
SceneTree::get_singleton()->initialize();
if (!DisplayServer::get_singleton()->has_feature(DisplayServer::Feature::FEATURE_SUBWINDOWS)) {
@@ -269,8 +277,10 @@ struct GodotTestCaseListener : public doctest::IReporter {
}
if (suite_name.find("[Navigation]") != -1 && navigation_server_2d == nullptr && navigation_server_3d == nullptr) {
+ ERR_PRINT_OFF;
navigation_server_3d = NavigationServer3DManager::new_default_server();
- navigation_server_2d = memnew(NavigationServer2D);
+ navigation_server_2d = NavigationServer2DManager::new_default_server();
+ ERR_PRINT_ON;
return;
}
}
@@ -288,11 +298,6 @@ struct GodotTestCaseListener : public doctest::IReporter {
memdelete(SceneTree::get_singleton());
}
- if (theme_db) {
- memdelete(theme_db);
- theme_db = nullptr;
- }
-
if (navigation_server_3d) {
memdelete(navigation_server_3d);
navigation_server_3d = nullptr;
@@ -320,6 +325,10 @@ struct GodotTestCaseListener : public doctest::IReporter {
}
if (RenderingServer::get_singleton()) {
+ // ThemeDB requires RenderingServer to finalize the default theme.
+ // So we have to do this for each test case.
+ ThemeDB::get_singleton()->finalize_theme();
+
RenderingServer::get_singleton()->sync();
RenderingServer::get_singleton()->global_shader_parameters_clear();
RenderingServer::get_singleton()->finish();