diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/core/io/test_image.h | 23 | ||||
-rw-r--r-- | tests/core/math/test_basis.h | 94 | ||||
-rw-r--r-- | tests/core/math/test_math_funcs.h | 23 | ||||
-rw-r--r-- | tests/core/string/test_string.h | 81 | ||||
-rw-r--r-- | tests/core/variant/test_variant.h | 1033 | ||||
-rw-r--r-- | tests/scene/test_camera_3d.h | 370 | ||||
-rw-r--r-- | tests/scene/test_code_edit.h | 32 | ||||
-rw-r--r-- | tests/scene/test_primitives.h | 2 | ||||
-rw-r--r-- | tests/scene/test_text_edit.h | 59 | ||||
-rw-r--r-- | tests/scene/test_viewport.h | 644 | ||||
-rw-r--r-- | tests/servers/test_navigation_server_3d.h | 99 | ||||
-rw-r--r-- | tests/test_main.cpp | 1 |
12 files changed, 2433 insertions, 28 deletions
diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h index 07c7c04e36..945a7e1ba3 100644 --- a/tests/core/io/test_image.h +++ b/tests/core/io/test_image.h @@ -403,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/math/test_basis.h b/tests/core/math/test_basis.h index fcac9a6231..a9bc2e9b99 100644 --- a/tests/core/math/test_basis.h +++ b/tests/core/math/test_basis.h @@ -324,6 +324,100 @@ TEST_CASE("[Basis] Is conformal checks") { 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 diff --git a/tests/core/math/test_math_funcs.h b/tests/core/math/test_math_funcs.h index 5fbea4aece..0a9d9c97d9 100644 --- a/tests/core/math/test_math_funcs.h +++ b/tests/core/math/test_math_funcs.h @@ -110,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)); diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index 02349aedc0..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") { 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/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 e69ac448e4..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") { @@ -2944,15 +2970,15 @@ TEST_CASE("[SceneTree][CodeEdit] region folding") { 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->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("#", ""); + 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("//"); + 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)); 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 840a04becf..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); } @@ -3913,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); @@ -4117,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 0c53668c6d..1afae66ee0 100644 --- a/tests/scene/test_viewport.h +++ b/tests/scene/test_viewport.h @@ -50,17 +50,39 @@ 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; }; // `NotificationControlViewport`-derived class that additionally @@ -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/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h index 691536da8e..5ab2975b74 100644 --- a/tests/servers/test_navigation_server_3d.h +++ b/tests/servers/test_navigation_server_3d.h @@ -429,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. diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 8c120f6d3a..5187ebd00f 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -94,6 +94,7 @@ #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" |