diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/core/input/test_input_event_key.h | 2 | ||||
-rw-r--r-- | tests/core/io/test_image.h | 2 | ||||
-rw-r--r-- | tests/core/math/test_basis.h | 48 | ||||
-rw-r--r-- | tests/core/math/test_geometry_2d.h | 2 | ||||
-rw-r--r-- | tests/core/math/test_math_funcs.h | 556 | ||||
-rw-r--r-- | tests/core/math/test_quaternion.h | 57 | ||||
-rw-r--r-- | tests/core/math/test_vector2.h | 6 | ||||
-rw-r--r-- | tests/core/math/test_vector3.h | 6 | ||||
-rw-r--r-- | tests/scene/test_animation.h | 8 | ||||
-rw-r--r-- | tests/scene/test_bit_map.h | 10 | ||||
-rw-r--r-- | tests/scene/test_path_2d.h | 109 | ||||
-rw-r--r-- | tests/scene/test_primitives.h | 850 | ||||
-rw-r--r-- | tests/scene/test_text_edit.h | 4 | ||||
-rw-r--r-- | tests/test_main.cpp | 3 | ||||
-rw-r--r-- | tests/test_validate_testing.h | 2 |
15 files changed, 1600 insertions, 65 deletions
diff --git a/tests/core/input/test_input_event_key.h b/tests/core/input/test_input_event_key.h index ef0a656b18..b852f3ccb9 100644 --- a/tests/core/input/test_input_event_key.h +++ b/tests/core/input/test_input_event_key.h @@ -102,7 +102,7 @@ TEST_CASE("[InputEventKey] Key correctly converts itself to text") { // as text. These cases are a bit weird, since None has no textual representation // (find_keycode_name(Key::NONE) results in a nullptr). Thus, these tests look weird // with only (Physical) or a lonely modifier with (Physical) but (as far as I - // understand the code, that is intended behaviour. + // understand the code, that is intended behavior. // Key is None without a physical key. none_key.set_keycode(Key::NONE); diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h index 181d9a8a54..1559c59b5c 100644 --- a/tests/core/io/test_image.h +++ b/tests/core/io/test_image.h @@ -162,7 +162,7 @@ TEST_CASE("[Image] Basic getters") { CHECK(image->get_size() == Vector2(8, 4)); CHECK(image->get_format() == Image::FORMAT_LA8); CHECK(image->get_used_rect() == Rect2i(0, 0, 0, 0)); - Ref<Image> image_get_rect = image->get_rect(Rect2i(0, 0, 2, 1)); + Ref<Image> image_get_rect = image->get_region(Rect2i(0, 0, 2, 1)); CHECK(image_get_rect->get_size() == Vector2(2, 1)); } diff --git a/tests/core/math/test_basis.h b/tests/core/math/test_basis.h index f52b715cd7..a4099ebf7d 100644 --- a/tests/core/math/test_basis.h +++ b/tests/core/math/test_basis.h @@ -46,26 +46,26 @@ Vector3 rad2deg(const Vector3 &p_rotation) { return p_rotation / Math_PI * 180.0; } -String get_rot_order_name(Basis::EulerOrder ro) { +String get_rot_order_name(EulerOrder ro) { switch (ro) { - case Basis::EULER_ORDER_XYZ: + case EulerOrder::XYZ: return "XYZ"; - case Basis::EULER_ORDER_XZY: + case EulerOrder::XZY: return "XZY"; - case Basis::EULER_ORDER_YZX: + case EulerOrder::YZX: return "YZX"; - case Basis::EULER_ORDER_YXZ: + case EulerOrder::YXZ: return "YXZ"; - case Basis::EULER_ORDER_ZXY: + case EulerOrder::ZXY: return "ZXY"; - case Basis::EULER_ORDER_ZYX: + case EulerOrder::ZYX: return "ZYX"; default: return "[Not supported]"; } } -void test_rotation(Vector3 deg_original_euler, Basis::EulerOrder rot_order) { +void test_rotation(Vector3 deg_original_euler, EulerOrder rot_order) { // This test: // 1. Converts the rotation vector from deg to rad. // 2. Converts euler to basis. @@ -98,8 +98,8 @@ void test_rotation(Vector3 deg_original_euler, Basis::EulerOrder rot_order) { CHECK_MESSAGE((res.get_column(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.1, vformat("Fail due to Z %s\n", String(res.get_column(2))).utf8().ptr()); // Double check `to_rotation` decomposing with XYZ rotation order. - const Vector3 euler_xyz_from_rotation = to_rotation.get_euler(Basis::EULER_ORDER_XYZ); - Basis rotation_from_xyz_computed_euler = Basis::from_euler(euler_xyz_from_rotation, Basis::EULER_ORDER_XYZ); + const Vector3 euler_xyz_from_rotation = to_rotation.get_euler(EulerOrder::XYZ); + Basis rotation_from_xyz_computed_euler = Basis::from_euler(euler_xyz_from_rotation, EulerOrder::XYZ); res = to_rotation.inverse() * rotation_from_xyz_computed_euler; @@ -113,13 +113,13 @@ void test_rotation(Vector3 deg_original_euler, Basis::EulerOrder rot_order) { } TEST_CASE("[Basis] Euler conversions") { - Vector<Basis::EulerOrder> euler_order_to_test; - euler_order_to_test.push_back(Basis::EULER_ORDER_XYZ); - euler_order_to_test.push_back(Basis::EULER_ORDER_XZY); - euler_order_to_test.push_back(Basis::EULER_ORDER_YZX); - euler_order_to_test.push_back(Basis::EULER_ORDER_YXZ); - euler_order_to_test.push_back(Basis::EULER_ORDER_ZXY); - euler_order_to_test.push_back(Basis::EULER_ORDER_ZYX); + Vector<EulerOrder> euler_order_to_test; + euler_order_to_test.push_back(EulerOrder::XYZ); + euler_order_to_test.push_back(EulerOrder::XZY); + euler_order_to_test.push_back(EulerOrder::YZX); + euler_order_to_test.push_back(EulerOrder::YXZ); + euler_order_to_test.push_back(EulerOrder::ZXY); + euler_order_to_test.push_back(EulerOrder::ZYX); Vector<Vector3> vectors_to_test; @@ -185,13 +185,13 @@ TEST_CASE("[Basis] Euler conversions") { } TEST_CASE("[Stress][Basis] Euler conversions") { - Vector<Basis::EulerOrder> euler_order_to_test; - euler_order_to_test.push_back(Basis::EULER_ORDER_XYZ); - euler_order_to_test.push_back(Basis::EULER_ORDER_XZY); - euler_order_to_test.push_back(Basis::EULER_ORDER_YZX); - euler_order_to_test.push_back(Basis::EULER_ORDER_YXZ); - euler_order_to_test.push_back(Basis::EULER_ORDER_ZXY); - euler_order_to_test.push_back(Basis::EULER_ORDER_ZYX); + Vector<EulerOrder> euler_order_to_test; + euler_order_to_test.push_back(EulerOrder::XYZ); + euler_order_to_test.push_back(EulerOrder::XZY); + euler_order_to_test.push_back(EulerOrder::YZX); + euler_order_to_test.push_back(EulerOrder::YXZ); + euler_order_to_test.push_back(EulerOrder::ZXY); + euler_order_to_test.push_back(EulerOrder::ZYX); Vector<Vector3> vectors_to_test; // Add 1000 random vectors with weirds numbers. diff --git a/tests/core/math/test_geometry_2d.h b/tests/core/math/test_geometry_2d.h index db4e6e2177..54893a0b87 100644 --- a/tests/core/math/test_geometry_2d.h +++ b/tests/core/math/test_geometry_2d.h @@ -64,7 +64,7 @@ TEST_CASE("[Geometry2D] Point in triangle") { // This tests points on the edge of the triangle. They are treated as being outside the triangle. // In `is_point_in_circle` and `is_point_in_polygon` they are treated as being inside, so in order the make - // the behaviour consistent this may change in the future (see issue #44717 and PR #44274). + // the behavior consistent this may change in the future (see issue #44717 and PR #44274). CHECK_FALSE(Geometry2D::is_point_in_triangle(Vector2(1, 1), Vector2(-1, 1), Vector2(0, -1), Vector2(1, 1))); CHECK_FALSE(Geometry2D::is_point_in_triangle(Vector2(0, 1), Vector2(-1, 1), Vector2(0, -1), Vector2(1, 1))); } diff --git a/tests/core/math/test_math_funcs.h b/tests/core/math/test_math_funcs.h new file mode 100644 index 0000000000..c468e73b74 --- /dev/null +++ b/tests/core/math/test_math_funcs.h @@ -0,0 +1,556 @@ +/*************************************************************************/ +/* test_math_funcs.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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_MATH_FUNCS_H +#define TEST_MATH_FUNCS_H + +#include "tests/test_macros.h" + +namespace TestMath { + +TEST_CASE("[Math] C++ macros") { + CHECK(MIN(-2, 2) == -2); + CHECK(MIN(600, 2) == 2); + + CHECK(MAX(-2, 2) == 2); + CHECK(MAX(600, 2) == 600); + + CHECK(CLAMP(600, -2, 2) == 2); + CHECK(CLAMP(620, 600, 650) == 620); + // `max` is lower than `min`. + CHECK(CLAMP(620, 600, 50) == 50); + + CHECK(ABS(-5) == 5); + CHECK(ABS(0) == 0); + CHECK(ABS(5) == 5); + + CHECK(SIGN(-5) == -1.0); + CHECK(SIGN(0) == 0.0); + CHECK(SIGN(5) == 1.0); +} + +TEST_CASE("[Math] Power of two functions") { + CHECK(next_power_of_2(0) == 0); + CHECK(next_power_of_2(1) == 1); + CHECK(next_power_of_2(16) == 16); + CHECK(next_power_of_2(17) == 32); + CHECK(next_power_of_2(65535) == 65536); + + CHECK(previous_power_of_2(0) == 0); + CHECK(previous_power_of_2(1) == 1); + CHECK(previous_power_of_2(16) == 16); + CHECK(previous_power_of_2(17) == 16); + CHECK(previous_power_of_2(65535) == 32768); + + CHECK(closest_power_of_2(0) == 0); + CHECK(closest_power_of_2(1) == 1); + CHECK(closest_power_of_2(16) == 16); + CHECK(closest_power_of_2(17) == 16); + CHECK(closest_power_of_2(65535) == 65536); + + CHECK(get_shift_from_power_of_2(0) == -1); + CHECK(get_shift_from_power_of_2(1) == 0); + CHECK(get_shift_from_power_of_2(16) == 4); + CHECK(get_shift_from_power_of_2(17) == -1); + CHECK(get_shift_from_power_of_2(65535) == -1); + + CHECK(nearest_shift(0) == 0); + CHECK(nearest_shift(1) == 1); + CHECK(nearest_shift(16) == 5); + CHECK(nearest_shift(17) == 5); + CHECK(nearest_shift(65535) == 16); +} + +TEST_CASE("[Math] abs") { + // int + CHECK(Math::abs(-1) == 1); + CHECK(Math::abs(0) == 0); + CHECK(Math::abs(1) == 1); + + // double + CHECK(Math::abs(-0.1) == 0.1); + CHECK(Math::abs(0.0) == 0.0); + CHECK(Math::abs(0.1) == 0.1); + + // float + CHECK(Math::abs(-0.1f) == 0.1f); + CHECK(Math::abs(0.0f) == 0.0f); + CHECK(Math::abs(0.1f) == 0.1f); +} + +TEST_CASE("[Math] round/floor/ceil") { + CHECK(Math::round(1.5) == 2.0); + CHECK(Math::round(1.6) == 2.0); + CHECK(Math::round(-1.5) == -2.0); + CHECK(Math::round(-1.1) == -1.0); + + CHECK(Math::floor(1.5) == 1.0); + CHECK(Math::floor(-1.5) == -2.0); + + CHECK(Math::ceil(1.5) == 2.0); + CHECK(Math::ceil(-1.9) == -1.0); +} + +TEST_CASE("[Math] sin/cos/tan") { + CHECK(Math::sin(-0.1) == doctest::Approx(-0.0998334166)); + CHECK(Math::sin(0.1) == doctest::Approx(0.0998334166)); + CHECK(Math::sin(0.5) == doctest::Approx(0.4794255386)); + CHECK(Math::sin(1.0) == doctest::Approx(0.8414709848)); + CHECK(Math::sin(1.5) == doctest::Approx(0.9974949866)); + CHECK(Math::sin(450.0) == doctest::Approx(-0.683283725)); + + CHECK(Math::cos(-0.1) == doctest::Approx(0.99500416530)); + CHECK(Math::cos(0.1) == doctest::Approx(0.9950041653)); + CHECK(Math::cos(0.5) == doctest::Approx(0.8775825619)); + CHECK(Math::cos(1.0) == doctest::Approx(0.5403023059)); + CHECK(Math::cos(1.5) == doctest::Approx(0.0707372017)); + CHECK(Math::cos(450.0) == doctest::Approx(-0.7301529642)); + + CHECK(Math::tan(-0.1) == doctest::Approx(-0.1003346721)); + CHECK(Math::tan(0.1) == doctest::Approx(0.1003346721)); + CHECK(Math::tan(0.5) == doctest::Approx(0.5463024898)); + CHECK(Math::tan(1.0) == doctest::Approx(1.5574077247)); + CHECK(Math::tan(1.5) == doctest::Approx(14.1014199472)); + CHECK(Math::tan(450.0) == doctest::Approx(0.9358090134)); +} + +TEST_CASE("[Math] sinh/cosh/tanh") { + CHECK(Math::sinh(-0.1) == doctest::Approx(-0.10016675)); + CHECK(Math::sinh(0.1) == doctest::Approx(0.10016675)); + CHECK(Math::sinh(0.5) == doctest::Approx(0.5210953055)); + CHECK(Math::sinh(1.0) == doctest::Approx(1.1752011936)); + CHECK(Math::sinh(1.5) == doctest::Approx(2.1292794551)); + + CHECK(Math::cosh(-0.1) == doctest::Approx(1.0050041681)); + CHECK(Math::cosh(0.1) == doctest::Approx(1.0050041681)); + CHECK(Math::cosh(0.5) == doctest::Approx(1.1276259652)); + CHECK(Math::cosh(1.0) == doctest::Approx(1.5430806348)); + CHECK(Math::cosh(1.5) == doctest::Approx(2.3524096152)); + + CHECK(Math::tanh(-0.1) == doctest::Approx(-0.0996679946)); + CHECK(Math::tanh(0.1) == doctest::Approx(0.0996679946)); + CHECK(Math::tanh(0.5) == doctest::Approx(0.4621171573)); + CHECK(Math::tanh(1.0) == doctest::Approx(0.761594156)); + CHECK(Math::tanh(1.5) == doctest::Approx(0.9051482536)); + CHECK(Math::tanh(450.0) == doctest::Approx(1.0)); +} + +TEST_CASE("[Math] asin/acos/atan") { + CHECK(Math::asin(-0.1) == doctest::Approx(-0.1001674212)); + CHECK(Math::asin(0.1) == doctest::Approx(0.1001674212)); + CHECK(Math::asin(0.5) == doctest::Approx(0.5235987756)); + CHECK(Math::asin(1.0) == doctest::Approx(1.5707963268)); + CHECK(Math::is_nan(Math::asin(1.5))); + CHECK(Math::is_nan(Math::asin(450.0))); + + CHECK(Math::acos(-0.1) == doctest::Approx(1.670963748)); + CHECK(Math::acos(0.1) == doctest::Approx(1.4706289056)); + CHECK(Math::acos(0.5) == doctest::Approx(1.0471975512)); + CHECK(Math::acos(1.0) == doctest::Approx(0.0)); + CHECK(Math::is_nan(Math::acos(1.5))); + CHECK(Math::is_nan(Math::acos(450.0))); + + CHECK(Math::atan(-0.1) == doctest::Approx(-0.0996686525)); + CHECK(Math::atan(0.1) == doctest::Approx(0.0996686525)); + CHECK(Math::atan(0.5) == doctest::Approx(0.463647609)); + CHECK(Math::atan(1.0) == doctest::Approx(0.7853981634)); + CHECK(Math::atan(1.5) == doctest::Approx(0.9827937232)); + CHECK(Math::atan(450.0) == doctest::Approx(1.5685741082)); +} + +TEST_CASE("[Math] sinc/sincn/atan2") { + CHECK(Math::sinc(-0.1) == doctest::Approx(0.9983341665)); + CHECK(Math::sinc(0.1) == doctest::Approx(0.9983341665)); + CHECK(Math::sinc(0.5) == doctest::Approx(0.9588510772)); + CHECK(Math::sinc(1.0) == doctest::Approx(0.8414709848)); + CHECK(Math::sinc(1.5) == doctest::Approx(0.6649966577)); + CHECK(Math::sinc(450.0) == doctest::Approx(-0.0015184083)); + + CHECK(Math::sincn(-0.1) == doctest::Approx(0.9836316431)); + CHECK(Math::sincn(0.1) == doctest::Approx(0.9836316431)); + CHECK(Math::sincn(0.5) == doctest::Approx(0.6366197724)); + CHECK(Math::sincn(1.0) == doctest::Approx(0.0)); + CHECK(Math::sincn(1.5) == doctest::Approx(-0.2122065908)); + CHECK(Math::sincn(450.0) == doctest::Approx(0.0)); + + CHECK(Math::atan2(-0.1, 0.5) == doctest::Approx(-0.1973955598)); + CHECK(Math::atan2(0.1, -0.5) == doctest::Approx(2.9441970937)); + CHECK(Math::atan2(0.5, 1.5) == doctest::Approx(0.3217505544)); + CHECK(Math::atan2(1.0, 2.5) == doctest::Approx(0.3805063771)); + CHECK(Math::atan2(1.5, 1.0) == doctest::Approx(0.9827937232)); + CHECK(Math::atan2(450.0, 1.0) == doctest::Approx(1.5685741082)); +} + +TEST_CASE("[Math] pow/log/log2/exp/sqrt") { + CHECK(Math::pow(-0.1, 2.0) == doctest::Approx(0.01)); + CHECK(Math::pow(0.1, 2.5) == doctest::Approx(0.0031622777)); + CHECK(Math::pow(0.5, 0.5) == doctest::Approx(0.7071067812)); + CHECK(Math::pow(1.0, 1.0) == doctest::Approx(1.0)); + CHECK(Math::pow(1.5, -1.0) == doctest::Approx(0.6666666667)); + CHECK(Math::pow(450.0, -2.0) == doctest::Approx(0.0000049383)); + CHECK(Math::pow(450.0, 0.0) == doctest::Approx(1.0)); + + CHECK(Math::is_nan(Math::log(-0.1))); + CHECK(Math::log(0.1) == doctest::Approx(-2.302585093)); + CHECK(Math::log(0.5) == doctest::Approx(-0.6931471806)); + CHECK(Math::log(1.0) == doctest::Approx(0.0)); + CHECK(Math::log(1.5) == doctest::Approx(0.4054651081)); + CHECK(Math::log(450.0) == doctest::Approx(6.1092475828)); + + CHECK(Math::is_nan(Math::log2(-0.1))); + CHECK(Math::log2(0.1) == doctest::Approx(-3.3219280949)); + CHECK(Math::log2(0.5) == doctest::Approx(-1.0)); + CHECK(Math::log2(1.0) == doctest::Approx(0.0)); + CHECK(Math::log2(1.5) == doctest::Approx(0.5849625007)); + CHECK(Math::log2(450.0) == doctest::Approx(8.8137811912)); + + CHECK(Math::exp(-0.1) == doctest::Approx(0.904837418)); + CHECK(Math::exp(0.1) == doctest::Approx(1.1051709181)); + CHECK(Math::exp(0.5) == doctest::Approx(1.6487212707)); + CHECK(Math::exp(1.0) == doctest::Approx(2.7182818285)); + CHECK(Math::exp(1.5) == doctest::Approx(4.4816890703)); + + CHECK(Math::is_nan(Math::sqrt(-0.1))); + CHECK(Math::sqrt(0.1) == doctest::Approx(0.316228)); + CHECK(Math::sqrt(0.5) == doctest::Approx(0.707107)); + CHECK(Math::sqrt(1.0) == doctest::Approx(1.0)); + CHECK(Math::sqrt(1.5) == doctest::Approx(1.224745)); +} + +TEST_CASE("[Math] is_nan/is_inf") { + CHECK(!Math::is_nan(0.0)); + CHECK(Math::is_nan(NAN)); + + CHECK(!Math::is_inf(0.0)); + CHECK(Math::is_inf(INFINITY)); +} + +TEST_CASE("[Math] linear_to_db") { + CHECK(Math::linear_to_db(1.0) == doctest::Approx(0.0)); + CHECK(Math::linear_to_db(20.0) == doctest::Approx(26.0206)); + CHECK(Math::is_inf(Math::linear_to_db(0.0))); + CHECK(Math::is_nan(Math::linear_to_db(-20.0))); +} + +TEST_CASE("[Math] db_to_linear") { + CHECK(Math::db_to_linear(0.0) == doctest::Approx(1.0)); + CHECK(Math::db_to_linear(1.0) == doctest::Approx(1.122018)); + CHECK(Math::db_to_linear(20.0) == doctest::Approx(10.0)); + CHECK(Math::db_to_linear(-20.0) == doctest::Approx(0.1)); +} + +TEST_CASE("[Math] step_decimals") { + CHECK(Math::step_decimals(-0.5) == 1); + CHECK(Math::step_decimals(0) == 0); + CHECK(Math::step_decimals(1) == 0); + CHECK(Math::step_decimals(0.1) == 1); + CHECK(Math::step_decimals(0.01) == 2); + CHECK(Math::step_decimals(0.001) == 3); + CHECK(Math::step_decimals(0.0001) == 4); + CHECK(Math::step_decimals(0.00001) == 5); + CHECK(Math::step_decimals(0.000001) == 6); + CHECK(Math::step_decimals(0.0000001) == 7); + CHECK(Math::step_decimals(0.00000001) == 8); + CHECK(Math::step_decimals(0.000000001) == 9); + // Too many decimals to handle. + CHECK(Math::step_decimals(0.0000000001) == 0); +} + +TEST_CASE("[Math] range_step_decimals") { + CHECK(Math::range_step_decimals(0.000000001) == 9); + // Too many decimals to handle. + CHECK(Math::range_step_decimals(0.0000000001) == 0); + // Should be treated as a step of 0 for use by the editor. + CHECK(Math::range_step_decimals(0.0) == 16); + CHECK(Math::range_step_decimals(-0.5) == 16); +} + +TEST_CASE("[Math] lerp") { + CHECK(Math::lerp(2.0, 5.0, -0.1) == doctest::Approx(1.7)); + CHECK(Math::lerp(2.0, 5.0, 0.0) == doctest::Approx(2.0)); + CHECK(Math::lerp(2.0, 5.0, 0.1) == doctest::Approx(2.3)); + CHECK(Math::lerp(2.0, 5.0, 1.0) == doctest::Approx(5.0)); + CHECK(Math::lerp(2.0, 5.0, 2.0) == doctest::Approx(8.0)); + + CHECK(Math::lerp(-2.0, -5.0, -0.1) == doctest::Approx(-1.7)); + CHECK(Math::lerp(-2.0, -5.0, 0.0) == doctest::Approx(-2.0)); + CHECK(Math::lerp(-2.0, -5.0, 0.1) == doctest::Approx(-2.3)); + CHECK(Math::lerp(-2.0, -5.0, 1.0) == doctest::Approx(-5.0)); + CHECK(Math::lerp(-2.0, -5.0, 2.0) == doctest::Approx(-8.0)); +} + +TEST_CASE("[Math] inverse_lerp") { + CHECK(Math::inverse_lerp(2.0, 5.0, 1.7) == doctest::Approx(-0.1)); + CHECK(Math::inverse_lerp(2.0, 5.0, 2.0) == doctest::Approx(0.0)); + CHECK(Math::inverse_lerp(2.0, 5.0, 2.3) == doctest::Approx(0.1)); + CHECK(Math::inverse_lerp(2.0, 5.0, 5.0) == doctest::Approx(1.0)); + CHECK(Math::inverse_lerp(2.0, 5.0, 8.0) == doctest::Approx(2.0)); + + CHECK(Math::inverse_lerp(-2.0, -5.0, -1.7) == doctest::Approx(-0.1)); + CHECK(Math::inverse_lerp(-2.0, -5.0, -2.0) == doctest::Approx(0.0)); + CHECK(Math::inverse_lerp(-2.0, -5.0, -2.3) == doctest::Approx(0.1)); + CHECK(Math::inverse_lerp(-2.0, -5.0, -5.0) == doctest::Approx(1.0)); + CHECK(Math::inverse_lerp(-2.0, -5.0, -8.0) == doctest::Approx(2.0)); +} + +TEST_CASE("[Math] remap") { + CHECK(Math::remap(50.0, 100.0, 200.0, 0.0, 1000.0) == doctest::Approx(-500.0)); + CHECK(Math::remap(100.0, 100.0, 200.0, 0.0, 1000.0) == doctest::Approx(0.0)); + CHECK(Math::remap(200.0, 100.0, 200.0, 0.0, 1000.0) == doctest::Approx(1000.0)); + CHECK(Math::remap(250.0, 100.0, 200.0, 0.0, 1000.0) == doctest::Approx(1500.0)); + + CHECK(Math::remap(-50.0, -100.0, -200.0, 0.0, 1000.0) == doctest::Approx(-500.0)); + CHECK(Math::remap(-100.0, -100.0, -200.0, 0.0, 1000.0) == doctest::Approx(0.0)); + CHECK(Math::remap(-200.0, -100.0, -200.0, 0.0, 1000.0) == doctest::Approx(1000.0)); + CHECK(Math::remap(-250.0, -100.0, -200.0, 0.0, 1000.0) == doctest::Approx(1500.0)); + + CHECK(Math::remap(-50.0, -100.0, -200.0, 0.0, -1000.0) == doctest::Approx(500.0)); + CHECK(Math::remap(-100.0, -100.0, -200.0, 0.0, -1000.0) == doctest::Approx(0.0)); + CHECK(Math::remap(-200.0, -100.0, -200.0, 0.0, -1000.0) == doctest::Approx(-1000.0)); + CHECK(Math::remap(-250.0, -100.0, -200.0, 0.0, -1000.0) == doctest::Approx(-1500.0)); +} + +TEST_CASE("[Math] lerp_angle") { + // Counter-clockwise rotation. + CHECK(Math::lerp_angle(0.24 * Math_TAU, 0.75 * Math_TAU, 0.5) == doctest::Approx(-0.005 * Math_TAU)); + // Counter-clockwise rotation. + CHECK(Math::lerp_angle(0.25 * Math_TAU, 0.75 * Math_TAU, 0.5) == doctest::Approx(0.0)); + // Clockwise rotation. + CHECK(Math::lerp_angle(0.26 * Math_TAU, 0.75 * Math_TAU, 0.5) == doctest::Approx(0.505 * Math_TAU)); + + CHECK(Math::lerp_angle(-0.25 * Math_TAU, 1.25 * Math_TAU, 0.5) == doctest::Approx(-0.5 * Math_TAU)); + CHECK(Math::lerp_angle(0.72 * Math_TAU, 1.44 * Math_TAU, 0.96) == doctest::Approx(0.4512 * Math_TAU)); + CHECK(Math::lerp_angle(0.72 * Math_TAU, 1.44 * Math_TAU, 1.04) == doctest::Approx(0.4288 * Math_TAU)); + + // Initial and final angles are effectively identical, so the value returned + // should always be the same regardless of the `weight` parameter. + CHECK(Math::lerp_angle(-4 * Math_TAU, 4 * Math_TAU, -1.0) == doctest::Approx(-4.0 * Math_TAU)); + CHECK(Math::lerp_angle(-4 * Math_TAU, 4 * Math_TAU, 0.0) == doctest::Approx(-4.0 * Math_TAU)); + CHECK(Math::lerp_angle(-4 * Math_TAU, 4 * Math_TAU, 0.5) == doctest::Approx(-4.0 * Math_TAU)); + CHECK(Math::lerp_angle(-4 * Math_TAU, 4 * Math_TAU, 1.0) == doctest::Approx(-4.0 * Math_TAU)); + CHECK(Math::lerp_angle(-4 * Math_TAU, 4 * Math_TAU, 500.0) == doctest::Approx(-4.0 * Math_TAU)); +} + +TEST_CASE("[Math] move_toward") { + CHECK(Math::move_toward(2.0, 5.0, -1.0) == doctest::Approx(1.0)); + CHECK(Math::move_toward(2.0, 5.0, 2.5) == doctest::Approx(4.5)); + CHECK(Math::move_toward(2.0, 5.0, 4.0) == doctest::Approx(5.0)); + CHECK(Math::move_toward(-2.0, -5.0, -1.0) == doctest::Approx(-1.0)); + CHECK(Math::move_toward(-2.0, -5.0, 2.5) == doctest::Approx(-4.5)); + CHECK(Math::move_toward(-2.0, -5.0, 4.0) == doctest::Approx(-5.0)); +} + +TEST_CASE("[Math] smoothstep") { + CHECK(Math::smoothstep(0.0, 2.0, -5.0) == doctest::Approx(0.0)); + CHECK(Math::smoothstep(0.0, 2.0, 0.5) == doctest::Approx(0.15625)); + CHECK(Math::smoothstep(0.0, 2.0, 1.0) == doctest::Approx(0.5)); + CHECK(Math::smoothstep(0.0, 2.0, 2.0) == doctest::Approx(1.0)); +} + +TEST_CASE("[Math] ease") { + CHECK(Math::ease(0.1, 1.0) == doctest::Approx(0.1)); + CHECK(Math::ease(0.1, 2.0) == doctest::Approx(0.01)); + CHECK(Math::ease(0.1, 0.5) == doctest::Approx(0.19)); + CHECK(Math::ease(0.1, 0.0) == doctest::Approx(0)); + CHECK(Math::ease(0.1, -0.5) == doctest::Approx(0.2236067977)); + CHECK(Math::ease(0.1, -1.0) == doctest::Approx(0.1)); + CHECK(Math::ease(0.1, -2.0) == doctest::Approx(0.02)); + + CHECK(Math::ease(-1.0, 1.0) == doctest::Approx(0)); + CHECK(Math::ease(-1.0, 2.0) == doctest::Approx(0)); + CHECK(Math::ease(-1.0, 0.5) == doctest::Approx(0)); + CHECK(Math::ease(-1.0, 0.0) == doctest::Approx(0)); + CHECK(Math::ease(-1.0, -0.5) == doctest::Approx(0)); + CHECK(Math::ease(-1.0, -1.0) == doctest::Approx(0)); + CHECK(Math::ease(-1.0, -2.0) == doctest::Approx(0)); +} + +TEST_CASE("[Math] snapped") { + CHECK(Math::snapped(0.5, 0.04) == doctest::Approx(0.52)); + CHECK(Math::snapped(-0.5, 0.04) == doctest::Approx(-0.48)); + CHECK(Math::snapped(0.0, 0.04) == doctest::Approx(0)); + CHECK(Math::snapped(128'000.025, 0.04) == doctest::Approx(128'000.04)); + + CHECK(Math::snapped(0.5, 400) == doctest::Approx(0)); + CHECK(Math::snapped(-0.5, 400) == doctest::Approx(0)); + CHECK(Math::snapped(0.0, 400) == doctest::Approx(0)); + CHECK(Math::snapped(128'000.025, 400) == doctest::Approx(128'000.0)); + + CHECK(Math::snapped(0.5, 0.0) == doctest::Approx(0.5)); + CHECK(Math::snapped(-0.5, 0.0) == doctest::Approx(-0.5)); + CHECK(Math::snapped(0.0, 0.0) == doctest::Approx(0.0)); + CHECK(Math::snapped(128'000.025, 0.0) == doctest::Approx(128'000.0)); + + CHECK(Math::snapped(0.5, -1.0) == doctest::Approx(0)); + CHECK(Math::snapped(-0.5, -1.0) == doctest::Approx(-1.0)); + CHECK(Math::snapped(0.0, -1.0) == doctest::Approx(0)); + CHECK(Math::snapped(128'000.025, -1.0) == doctest::Approx(128'000.0)); +} + +TEST_CASE("[Math] larger_prime") { + CHECK(Math::larger_prime(0) == 5); + CHECK(Math::larger_prime(1) == 5); + CHECK(Math::larger_prime(2) == 5); + CHECK(Math::larger_prime(5) == 13); + CHECK(Math::larger_prime(500) == 769); + CHECK(Math::larger_prime(1'000'000) == 1'572'869); + CHECK(Math::larger_prime(1'000'000'000) == 1'610'612'741); + + // The next prime is larger than `INT32_MAX` and is not present in the built-in prime table. + ERR_PRINT_OFF; + CHECK(Math::larger_prime(2'000'000'000) == 0); + ERR_PRINT_ON; +} + +TEST_CASE("[Math] fmod") { + CHECK(Math::fmod(-2.0, 0.3) == doctest::Approx(-0.2)); + CHECK(Math::fmod(0.0, 0.3) == doctest::Approx(0.0)); + CHECK(Math::fmod(2.0, 0.3) == doctest::Approx(0.2)); + + CHECK(Math::fmod(-2.0, -0.3) == doctest::Approx(-0.2)); + CHECK(Math::fmod(0.0, -0.3) == doctest::Approx(0.0)); + CHECK(Math::fmod(2.0, -0.3) == doctest::Approx(0.2)); +} + +TEST_CASE("[Math] fposmod") { + CHECK(Math::fposmod(-2.0, 0.3) == doctest::Approx(0.1)); + CHECK(Math::fposmod(0.0, 0.3) == doctest::Approx(0.0)); + CHECK(Math::fposmod(2.0, 0.3) == doctest::Approx(0.2)); + + CHECK(Math::fposmod(-2.0, -0.3) == doctest::Approx(-0.2)); + CHECK(Math::fposmod(0.0, -0.3) == doctest::Approx(0.0)); + CHECK(Math::fposmod(2.0, -0.3) == doctest::Approx(-0.1)); +} + +TEST_CASE("[Math] fposmodp") { + CHECK(Math::fposmodp(-2.0, 0.3) == doctest::Approx(0.1)); + CHECK(Math::fposmodp(0.0, 0.3) == doctest::Approx(0.0)); + CHECK(Math::fposmodp(2.0, 0.3) == doctest::Approx(0.2)); + + CHECK(Math::fposmodp(-2.0, -0.3) == doctest::Approx(-0.5)); + CHECK(Math::fposmodp(0.0, -0.3) == doctest::Approx(0.0)); + CHECK(Math::fposmodp(2.0, -0.3) == doctest::Approx(0.2)); +} + +TEST_CASE("[Math] posmod") { + CHECK(Math::posmod(-20, 3) == 1); + CHECK(Math::posmod(0, 3) == 0); + CHECK(Math::posmod(20, 3) == 2); + CHECK(Math::posmod(-20, -3) == -2); + CHECK(Math::posmod(0, -3) == 0); + CHECK(Math::posmod(20, -3) == -1); +} + +TEST_CASE("[Math] wrapi") { + CHECK(Math::wrapi(-30, -20, 160) == 150); + CHECK(Math::wrapi(30, -20, 160) == 30); + CHECK(Math::wrapi(300, -20, 160) == 120); + CHECK(Math::wrapi(300'000'000'000, -20, 160) == 120); +} + +TEST_CASE("[Math] wrapf") { + CHECK(Math::wrapf(-30.0, -20.0, 160.0) == doctest::Approx(150.0)); + CHECK(Math::wrapf(30.0, -2.0, 160.0) == doctest::Approx(30.0)); + CHECK(Math::wrapf(300.0, -20.0, 160.0) == doctest::Approx(120.0)); + CHECK(Math::wrapf(300'000'000'000.0, -20.0, 160.0) == doctest::Approx(120.0)); +} + +TEST_CASE("[Math] fract") { + CHECK(Math::fract(1.0) == doctest::Approx(0.0)); + CHECK(Math::fract(77.8) == doctest::Approx(0.8)); + CHECK(Math::fract(-10.1) == doctest::Approx(0.9)); +} + +TEST_CASE("[Math] pingpong") { + CHECK(Math::pingpong(0.0, 0.0) == doctest::Approx(0.0)); + CHECK(Math::pingpong(1.0, 1.0) == doctest::Approx(1.0)); + CHECK(Math::pingpong(0.5, 2.0) == doctest::Approx(0.5)); + CHECK(Math::pingpong(3.5, 2.0) == doctest::Approx(0.5)); + CHECK(Math::pingpong(11.5, 2.0) == doctest::Approx(0.5)); + CHECK(Math::pingpong(-2.5, 2.0) == doctest::Approx(1.5)); +} + +TEST_CASE("[Math] deg_to_rad/rad_to_deg") { + CHECK(Math::deg_to_rad(180.0) == doctest::Approx(Math_PI)); + CHECK(Math::deg_to_rad(-27.0) == doctest::Approx(-0.471239)); + + CHECK(Math::rad_to_deg(Math_PI) == doctest::Approx(180.0)); + CHECK(Math::rad_to_deg(-1.5) == doctest::Approx(-85.94366927)); +} + +TEST_CASE("[Math] cubic_interpolate") { + CHECK(Math::cubic_interpolate(0.2, 0.8, 0.0, 1.0, 0.0) == doctest::Approx(0.2)); + CHECK(Math::cubic_interpolate(0.2, 0.8, 0.0, 1.0, 0.25) == doctest::Approx(0.33125)); + CHECK(Math::cubic_interpolate(0.2, 0.8, 0.0, 1.0, 0.5) == doctest::Approx(0.5)); + CHECK(Math::cubic_interpolate(0.2, 0.8, 0.0, 1.0, 0.75) == doctest::Approx(0.66875)); + CHECK(Math::cubic_interpolate(0.2, 0.8, 0.0, 1.0, 1.0) == doctest::Approx(0.8)); + + CHECK(Math::cubic_interpolate(20.2, 30.1, -100.0, 32.0, -50.0) == doctest::Approx(-6662732.3)); + CHECK(Math::cubic_interpolate(20.2, 30.1, -100.0, 32.0, -5.0) == doctest::Approx(-9356.3)); + CHECK(Math::cubic_interpolate(20.2, 30.1, -100.0, 32.0, 0.0) == doctest::Approx(20.2)); + CHECK(Math::cubic_interpolate(20.2, 30.1, -100.0, 32.0, 1.0) == doctest::Approx(30.1)); + CHECK(Math::cubic_interpolate(20.2, 30.1, -100.0, 32.0, 4.0) == doctest::Approx(1853.2)); +} + +TEST_CASE("[Math] cubic_interpolate_angle") { + CHECK(Math::cubic_interpolate_angle(Math_PI * (1.0 / 6.0), Math_PI * (5.0 / 6.0), 0.0, Math_PI, 0.0) == doctest::Approx(Math_PI * (1.0 / 6.0))); + CHECK(Math::cubic_interpolate_angle(Math_PI * (1.0 / 6.0), Math_PI * (5.0 / 6.0), 0.0, Math_PI, 0.25) == doctest::Approx(0.973566)); + CHECK(Math::cubic_interpolate_angle(Math_PI * (1.0 / 6.0), Math_PI * (5.0 / 6.0), 0.0, Math_PI, 0.5) == doctest::Approx(Math_PI / 2.0)); + CHECK(Math::cubic_interpolate_angle(Math_PI * (1.0 / 6.0), Math_PI * (5.0 / 6.0), 0.0, Math_PI, 0.75) == doctest::Approx(2.16803)); + CHECK(Math::cubic_interpolate_angle(Math_PI * (1.0 / 6.0), Math_PI * (5.0 / 6.0), 0.0, Math_PI, 1.0) == doctest::Approx(Math_PI * (5.0 / 6.0))); +} + +TEST_CASE("[Math] cubic_interpolate_in_time") { + CHECK(Math::cubic_interpolate_in_time(0.2, 0.8, 0.0, 1.0, 0.0, 0.5, 0.0, 1.0) == doctest::Approx(0.0)); + CHECK(Math::cubic_interpolate_in_time(0.2, 0.8, 0.0, 1.0, 0.25, 0.5, 0.0, 1.0) == doctest::Approx(0.1625)); + CHECK(Math::cubic_interpolate_in_time(0.2, 0.8, 0.0, 1.0, 0.5, 0.5, 0.0, 1.0) == doctest::Approx(0.4)); + CHECK(Math::cubic_interpolate_in_time(0.2, 0.8, 0.0, 1.0, 0.75, 0.5, 0.0, 1.0) == doctest::Approx(0.6375)); + CHECK(Math::cubic_interpolate_in_time(0.2, 0.8, 0.0, 1.0, 1.0, 0.5, 0.0, 1.0) == doctest::Approx(0.8)); +} + +TEST_CASE("[Math] cubic_interpolate_angle_in_time") { + CHECK(Math::cubic_interpolate_angle_in_time(Math_PI * (1.0 / 6.0), Math_PI * (5.0 / 6.0), 0.0, Math_PI, 0.0, 0.5, 0.0, 1.0) == doctest::Approx(0.0)); + CHECK(Math::cubic_interpolate_angle_in_time(Math_PI * (1.0 / 6.0), Math_PI * (5.0 / 6.0), 0.0, Math_PI, 0.25, 0.5, 0.0, 1.0) == doctest::Approx(0.494964)); + CHECK(Math::cubic_interpolate_angle_in_time(Math_PI * (1.0 / 6.0), Math_PI * (5.0 / 6.0), 0.0, Math_PI, 0.5, 0.5, 0.0, 1.0) == doctest::Approx(1.27627)); + CHECK(Math::cubic_interpolate_angle_in_time(Math_PI * (1.0 / 6.0), Math_PI * (5.0 / 6.0), 0.0, Math_PI, 0.75, 0.5, 0.0, 1.0) == doctest::Approx(2.07394)); + CHECK(Math::cubic_interpolate_angle_in_time(Math_PI * (1.0 / 6.0), Math_PI * (5.0 / 6.0), 0.0, Math_PI, 1.0, 0.5, 0.0, 1.0) == doctest::Approx(Math_PI * (5.0 / 6.0))); +} + +TEST_CASE("[Math] bezier_interpolate") { + CHECK(Math::bezier_interpolate(0.0, 0.2, 0.8, 1.0, 0.0) == doctest::Approx(0.0)); + CHECK(Math::bezier_interpolate(0.0, 0.2, 0.8, 1.0, 0.25) == doctest::Approx(0.2125)); + CHECK(Math::bezier_interpolate(0.0, 0.2, 0.8, 1.0, 0.5) == doctest::Approx(0.5)); + CHECK(Math::bezier_interpolate(0.0, 0.2, 0.8, 1.0, 0.75) == doctest::Approx(0.7875)); + CHECK(Math::bezier_interpolate(0.0, 0.2, 0.8, 1.0, 1.0) == doctest::Approx(1.0)); +} + +} // namespace TestMath + +#endif // TEST_MATH_FUNCS_H diff --git a/tests/core/math/test_quaternion.h b/tests/core/math/test_quaternion.h index eb5fea1d2d..c3ae322991 100644 --- a/tests/core/math/test_quaternion.h +++ b/tests/core/math/test_quaternion.h @@ -47,9 +47,9 @@ Quaternion quat_euler_yxz_deg(Vector3 angle) { // Generate YXZ (Z-then-X-then-Y) Quaternion using single-axis Euler // constructor and quaternion product, both tested separately. - Quaternion q_y(Vector3(0.0, yaw, 0.0)); - Quaternion q_p(Vector3(pitch, 0.0, 0.0)); - Quaternion q_r(Vector3(0.0, 0.0, roll)); + Quaternion q_y = Quaternion::from_euler(Vector3(0.0, yaw, 0.0)); + Quaternion q_p = Quaternion::from_euler(Vector3(pitch, 0.0, 0.0)); + Quaternion q_r = Quaternion::from_euler(Vector3(0.0, 0.0, roll)); // Roll-Z is followed by Pitch-X, then Yaw-Y. Quaternion q_yxz = q_y * q_p * q_r; @@ -134,21 +134,21 @@ TEST_CASE("[Quaternion] Construct Euler SingleAxis") { double roll = Math::deg_to_rad(10.0); Vector3 euler_y(0.0, yaw, 0.0); - Quaternion q_y(euler_y); + Quaternion q_y = Quaternion::from_euler(euler_y); CHECK(q_y[0] == doctest::Approx(0.0)); CHECK(q_y[1] == doctest::Approx(0.382684)); CHECK(q_y[2] == doctest::Approx(0.0)); CHECK(q_y[3] == doctest::Approx(0.923879)); Vector3 euler_p(pitch, 0.0, 0.0); - Quaternion q_p(euler_p); + Quaternion q_p = Quaternion::from_euler(euler_p); CHECK(q_p[0] == doctest::Approx(0.258819)); CHECK(q_p[1] == doctest::Approx(0.0)); CHECK(q_p[2] == doctest::Approx(0.0)); CHECK(q_p[3] == doctest::Approx(0.965926)); Vector3 euler_r(0.0, 0.0, roll); - Quaternion q_r(euler_r); + Quaternion q_r = Quaternion::from_euler(euler_r); CHECK(q_r[0] == doctest::Approx(0.0)); CHECK(q_r[1] == doctest::Approx(0.0)); CHECK(q_r[2] == doctest::Approx(0.0871558)); @@ -163,27 +163,27 @@ TEST_CASE("[Quaternion] Construct Euler YXZ dynamic axes") { // Generate YXZ comparison data (Z-then-X-then-Y) using single-axis Euler // constructor and quaternion product, both tested separately. Vector3 euler_y(0.0, yaw, 0.0); - Quaternion q_y(euler_y); + Quaternion q_y = Quaternion::from_euler(euler_y); Vector3 euler_p(pitch, 0.0, 0.0); - Quaternion q_p(euler_p); + Quaternion q_p = Quaternion::from_euler(euler_p); Vector3 euler_r(0.0, 0.0, roll); - Quaternion q_r(euler_r); + Quaternion q_r = Quaternion::from_euler(euler_r); - // Roll-Z is followed by Pitch-X. - Quaternion check_xz = q_p * q_r; - // Then Yaw-Y follows both. - Quaternion check_yxz = q_y * check_xz; + // Instrinsically, Yaw-Y then Pitch-X then Roll-Z. + // Extrinsically, Roll-Z is followed by Pitch-X, then Yaw-Y. + Quaternion check_yxz = q_y * q_p * q_r; // Test construction from YXZ Euler angles. Vector3 euler_yxz(pitch, yaw, roll); - Quaternion q(euler_yxz); + Quaternion q = Quaternion::from_euler(euler_yxz); CHECK(q[0] == doctest::Approx(check_yxz[0])); CHECK(q[1] == doctest::Approx(check_yxz[1])); CHECK(q[2] == doctest::Approx(check_yxz[2])); CHECK(q[3] == doctest::Approx(check_yxz[3])); - // Sneak in a test of is_equal_approx. CHECK(q.is_equal_approx(check_yxz)); + CHECK(q.get_euler().is_equal_approx(euler_yxz)); + CHECK(check_yxz.get_euler().is_equal_approx(euler_yxz)); } TEST_CASE("[Quaternion] Construct Basis Euler") { @@ -191,7 +191,7 @@ TEST_CASE("[Quaternion] Construct Basis Euler") { double pitch = Math::deg_to_rad(30.0); double roll = Math::deg_to_rad(10.0); Vector3 euler_yxz(pitch, yaw, roll); - Quaternion q_yxz(euler_yxz); + Quaternion q_yxz = Quaternion::from_euler(euler_yxz); Basis basis_axes = Basis::from_euler(euler_yxz); Quaternion q(basis_axes); CHECK(q.is_equal_approx(q_yxz)); @@ -209,7 +209,7 @@ TEST_CASE("[Quaternion] Construct Basis Axes") { // Quaternion from local calculation. Quaternion q_local = quat_euler_yxz_deg(Vector3(31.41, -49.16, 12.34)); // Quaternion from Euler angles constructor. - Quaternion q_euler(euler_yxz); + Quaternion q_euler = Quaternion::from_euler(euler_yxz); CHECK(q_calc.is_equal_approx(q_local)); CHECK(q_local.is_equal_approx(q_euler)); @@ -235,6 +235,23 @@ TEST_CASE("[Quaternion] Construct Basis Axes") { CHECK(q[3] == doctest::Approx(0.8582598)); } +TEST_CASE("[Quaternion] Get Euler Orders") { + double x = Math::deg_to_rad(30.0); + double y = Math::deg_to_rad(45.0); + double z = Math::deg_to_rad(10.0); + Vector3 euler(x, y, z); + for (int i = 0; i < 6; i++) { + EulerOrder order = (EulerOrder)i; + Basis basis = Basis::from_euler(euler, order); + Quaternion q = Quaternion(basis); + Vector3 check = q.get_euler(order); + CHECK_MESSAGE(check.is_equal_approx(euler), + "Quaternion get_euler method should return the original angles."); + CHECK_MESSAGE(check.is_equal_approx(basis.get_euler(order)), + "Quaternion get_euler method should behave the same as Basis get_euler."); + } +} + TEST_CASE("[Quaternion] Product (book)") { // Example from "Quaternions and Rotation Sequences" by Jack Kuipers, p. 108. Quaternion p(1.0, -2.0, 1.0, 3.0); @@ -253,21 +270,21 @@ TEST_CASE("[Quaternion] Product") { double roll = Math::deg_to_rad(10.0); Vector3 euler_y(0.0, yaw, 0.0); - Quaternion q_y(euler_y); + Quaternion q_y = Quaternion::from_euler(euler_y); CHECK(q_y[0] == doctest::Approx(0.0)); CHECK(q_y[1] == doctest::Approx(0.382684)); CHECK(q_y[2] == doctest::Approx(0.0)); CHECK(q_y[3] == doctest::Approx(0.923879)); Vector3 euler_p(pitch, 0.0, 0.0); - Quaternion q_p(euler_p); + Quaternion q_p = Quaternion::from_euler(euler_p); CHECK(q_p[0] == doctest::Approx(0.258819)); CHECK(q_p[1] == doctest::Approx(0.0)); CHECK(q_p[2] == doctest::Approx(0.0)); CHECK(q_p[3] == doctest::Approx(0.965926)); Vector3 euler_r(0.0, 0.0, roll); - Quaternion q_r(euler_r); + Quaternion q_r = Quaternion::from_euler(euler_r); CHECK(q_r[0] == doctest::Approx(0.0)); CHECK(q_r[1] == doctest::Approx(0.0)); CHECK(q_r[2] == doctest::Approx(0.0871558)); diff --git a/tests/core/math/test_vector2.h b/tests/core/math/test_vector2.h index a87b9ffc02..f7e9259329 100644 --- a/tests/core/math/test_vector2.h +++ b/tests/core/math/test_vector2.h @@ -382,13 +382,13 @@ TEST_CASE("[Vector2] Plane methods") { ERR_PRINT_OFF; CHECK_MESSAGE( vector.bounce(vector_non_normal).is_equal_approx(Vector2()), - "Vector2 bounce should return empty Vector2 with non-normalised input."); + "Vector2 bounce should return empty Vector2 with non-normalized input."); CHECK_MESSAGE( vector.reflect(vector_non_normal).is_equal_approx(Vector2()), - "Vector2 reflect should return empty Vector2 with non-normalised input."); + "Vector2 reflect should return empty Vector2 with non-normalized input."); CHECK_MESSAGE( vector.slide(vector_non_normal).is_equal_approx(Vector2()), - "Vector2 slide should return empty Vector2 with non-normalised input."); + "Vector2 slide should return empty Vector2 with non-normalized input."); ERR_PRINT_ON; } diff --git a/tests/core/math/test_vector3.h b/tests/core/math/test_vector3.h index 4932cd04db..77d3a9d93c 100644 --- a/tests/core/math/test_vector3.h +++ b/tests/core/math/test_vector3.h @@ -389,13 +389,13 @@ TEST_CASE("[Vector3] Plane methods") { ERR_PRINT_OFF; CHECK_MESSAGE( vector.bounce(vector_non_normal).is_equal_approx(Vector3()), - "Vector3 bounce should return empty Vector3 with non-normalised input."); + "Vector3 bounce should return empty Vector3 with non-normalized input."); CHECK_MESSAGE( vector.reflect(vector_non_normal).is_equal_approx(Vector3()), - "Vector3 reflect should return empty Vector3 with non-normalised input."); + "Vector3 reflect should return empty Vector3 with non-normalized input."); CHECK_MESSAGE( vector.slide(vector_non_normal).is_equal_approx(Vector3()), - "Vector3 slide should return empty Vector3 with non-normalised input."); + "Vector3 slide should return empty Vector3 with non-normalized input."); ERR_PRINT_ON; } diff --git a/tests/scene/test_animation.h b/tests/scene/test_animation.h index 9199713fd9..ca80f0ecab 100644 --- a/tests/scene/test_animation.h +++ b/tests/scene/test_animation.h @@ -140,13 +140,13 @@ TEST_CASE("[Animation] Create 3D rotation track") { Ref<Animation> animation = memnew(Animation); const int track_index = animation->add_track(Animation::TYPE_ROTATION_3D); animation->track_set_path(track_index, NodePath("Enemy:rotation")); - animation->rotation_track_insert_key(track_index, 0.0, Quaternion(Vector3(0, 1, 2))); - animation->rotation_track_insert_key(track_index, 0.5, Quaternion(Vector3(3.5, 4, 5))); + animation->rotation_track_insert_key(track_index, 0.0, Quaternion::from_euler(Vector3(0, 1, 2))); + animation->rotation_track_insert_key(track_index, 0.5, Quaternion::from_euler(Vector3(3.5, 4, 5))); CHECK(animation->get_track_count() == 1); CHECK(!animation->track_is_compressed(0)); - CHECK(Quaternion(animation->track_get_key_value(0, 0)).is_equal_approx(Quaternion(Vector3(0, 1, 2)))); - CHECK(Quaternion(animation->track_get_key_value(0, 1)).is_equal_approx(Quaternion(Vector3(3.5, 4, 5)))); + CHECK(Quaternion(animation->track_get_key_value(0, 0)).is_equal_approx(Quaternion::from_euler(Vector3(0, 1, 2)))); + CHECK(Quaternion(animation->track_get_key_value(0, 1)).is_equal_approx(Quaternion::from_euler(Vector3(3.5, 4, 5)))); Quaternion r_interpolation; diff --git a/tests/scene/test_bit_map.h b/tests/scene/test_bit_map.h index a102f40725..aca7e5fe22 100644 --- a/tests/scene/test_bit_map.h +++ b/tests/scene/test_bit_map.h @@ -234,7 +234,7 @@ TEST_CASE("[BitMap] Resize") { TEST_CASE("[BitMap] Grow and shrink mask") { const Size2i dim{ 256, 256 }; BitMap bit_map{}; - bit_map.grow_mask(100, Rect2i(0, 0, 128, 128)); // Check if method does not crash when working with an uninitialised bit map. + bit_map.grow_mask(100, Rect2i(0, 0, 128, 128)); // Check if method does not crash when working with an uninitialized bit map. CHECK_MESSAGE(bit_map.get_size() == Size2i(0, 0), "Size should still be equal to 0x0"); bit_map.create(dim); @@ -335,21 +335,21 @@ TEST_CASE("[BitMap] Blit") { blit_bit_map.instantiate(); - // Testing if uninitialised blit bit map and uninitialised bit map does not crash + // Testing if uninitialized blit bit map and uninitialized bit map does not crash bit_map.blit(blit_pos, blit_bit_map); - // Testing if uninitialised bit map does not crash + // Testing if uninitialized bit map does not crash blit_bit_map->create(blit_size); bit_map.blit(blit_pos, blit_bit_map); - // Testing if uninitialised bit map does not crash + // Testing if uninitialized bit map does not crash blit_bit_map.unref(); blit_bit_map.instantiate(); CHECK_MESSAGE(blit_bit_map->get_size() == Point2i(0, 0), "Size should be cleared by unref and instance calls."); bit_map.create(bit_map_size); bit_map.blit(Point2i(128, 128), blit_bit_map); - // Testing if both initialised does not crash. + // Testing if both initialized does not crash. blit_bit_map->create(blit_size); bit_map.blit(blit_pos, blit_bit_map); diff --git a/tests/scene/test_path_2d.h b/tests/scene/test_path_2d.h new file mode 100644 index 0000000000..dc5eee4012 --- /dev/null +++ b/tests/scene/test_path_2d.h @@ -0,0 +1,109 @@ +/*************************************************************************/ +/* test_path_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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_PATH_2D_H +#define TEST_PATH_2D_H + +#include "scene/2d/path_2d.h" + +#include "tests/test_macros.h" + +namespace TestPath2D { + +TEST_CASE("[SceneTree][Path2D] Initialization") { + SUBCASE("Path should be empty right after initialization") { + Path2D *test_path = memnew(Path2D); + CHECK(test_path->get_curve() == nullptr); + memdelete(test_path); + } +} + +TEST_CASE("[SceneTree][Path2D] Curve setter and getter") { + Path2D *test_path = memnew(Path2D); + const Ref<Curve2D> &curve = memnew(Curve2D); + + SUBCASE("Curve passed to the class should remain the same") { + test_path->set_curve(curve); + CHECK(test_path->get_curve() == curve); + } + SUBCASE("Curve passed many times to the class should remain the same") { + test_path->set_curve(curve); + test_path->set_curve(curve); + test_path->set_curve(curve); + CHECK(test_path->get_curve() == curve); + } + SUBCASE("Curve rewrite testing") { + const Ref<Curve2D> &curve1 = memnew(Curve2D); + const Ref<Curve2D> &curve2 = memnew(Curve2D); + + test_path->set_curve(curve1); + test_path->set_curve(curve2); + CHECK_MESSAGE(test_path->get_curve() != curve1, + "After rewrite, second curve should be in class"); + CHECK_MESSAGE(test_path->get_curve() == curve2, + "After rewrite, second curve should be in class"); + } + + SUBCASE("Assign same curve to two paths") { + Path2D *path2 = memnew(Path2D); + + test_path->set_curve(curve); + path2->set_curve(curve); + CHECK_MESSAGE(test_path->get_curve() == path2->get_curve(), + "Both paths have the same curve."); + memdelete(path2); + } + + SUBCASE("Swapping curves between two paths") { + Path2D *path2 = memnew(Path2D); + const Ref<Curve2D> &curve1 = memnew(Curve2D); + const Ref<Curve2D> &curve2 = memnew(Curve2D); + + test_path->set_curve(curve1); + path2->set_curve(curve2); + CHECK(test_path->get_curve() == curve1); + CHECK(path2->get_curve() == curve2); + + // Do the swap + Ref<Curve2D> temp = test_path->get_curve(); + test_path->set_curve(path2->get_curve()); + path2->set_curve(temp); + + CHECK(test_path->get_curve() == curve2); + CHECK(path2->get_curve() == curve1); + memdelete(path2); + } + + memdelete(test_path); +} + +} // namespace TestPath2D + +#endif // TEST_PATH_2D_H diff --git a/tests/scene/test_primitives.h b/tests/scene/test_primitives.h new file mode 100644 index 0000000000..ceec117700 --- /dev/null +++ b/tests/scene/test_primitives.h @@ -0,0 +1,850 @@ +/*************************************************************************/ +/* test_primitives.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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_PRIMITIVES_H +#define TEST_PRIMITIVES_H + +#include "scene/resources/primitive_meshes.h" + +#include "tests/test_macros.h" + +namespace TestPrimitives { + +TEST_CASE("[SceneTree][Primitive][Capsule] Capsule Primitive") { + Ref<CapsuleMesh> capsule = memnew(CapsuleMesh); + + SUBCASE("[SceneTree][Primitive][Capsule] Default values should be valid") { + CHECK_MESSAGE(capsule->get_radius() > 0, + "Radius of default capsule positive."); + CHECK_MESSAGE(capsule->get_height() > 0, + "Height of default capsule positive."); + CHECK_MESSAGE(capsule->get_radial_segments() >= 0, + "Radius Segments of default capsule positive."); + CHECK_MESSAGE(capsule->get_rings() >= 0, + "Number of rings of default capsule positive."); + } + + SUBCASE("[SceneTree][Primitive][Capsule] Set properties of the capsule and get them with accessor methods") { + capsule->set_height(7.1f); + capsule->set_radius(1.3f); + capsule->set_radial_segments(16); + capsule->set_rings(32); + + CHECK_MESSAGE(Math::is_equal_approx(capsule->get_radius(), 1.3f), + "Get/Set radius work with one set."); + CHECK_MESSAGE(Math::is_equal_approx(capsule->get_height(), 7.1f), + "Get/Set radius work with one set."); + CHECK_MESSAGE(capsule->get_radial_segments() == 16, + "Get/Set radius work with one set."); + CHECK_MESSAGE(capsule->get_rings() == 32, + "Get/Set radius work with one set."); + } + + SUBCASE("[SceneTree][Primitive][Capsule] If set segments negative, default to at least 0") { + capsule->set_radial_segments(-5); + capsule->set_rings(-17); + + CHECK_MESSAGE(capsule->get_radial_segments() >= 0, + "Ensure number of radial segments is >= 0."); + CHECK_MESSAGE(capsule->get_rings() >= 0, + "Ensure number of rings is >= 0."); + } + + SUBCASE("[SceneTree][Primitive][Capsule] If set height < 2*radius, adjust radius and height to radius=height*0.5") { + capsule->set_radius(1.f); + capsule->set_height(0.5f); + + CHECK_MESSAGE(capsule->get_radius() >= capsule->get_height() * 0.5, + "Ensure radius >= height * 0.5 (needed for capsule to exist)."); + } + + SUBCASE("[Primitive][Capsule] Check mesh is correct") { + Array data{}; + data.resize(RS::ARRAY_MAX); + float radius{ 0.5f }; + float height{ 4.f }; + int num_radial_segments{ 4 }; + int num_rings{ 8 }; + CapsuleMesh::create_mesh_array(data, radius, height, num_radial_segments, num_rings); + Vector<Vector3> points = data[RS::ARRAY_VERTEX]; + + SUBCASE("[Primitive][Capsule] Ensure all vertices positions are within bounding radius and height") { + // Get mesh data + + // Check all points within radius of capsule + float dist_to_yaxis = 0.f; + for (Vector3 point : points) { + float new_dist_to_y = point.x * point.x + point.z * point.z; + if (new_dist_to_y > dist_to_yaxis) + dist_to_yaxis = new_dist_to_y; + } + + CHECK(dist_to_yaxis <= radius * radius); + + // Check highest point and lowest point are within height of each other + float max_y{ 0.f }; + float min_y{ 0.f }; + for (Vector3 point : points) { + if (point.y > max_y) + max_y = point.y; + if (point.y < min_y) + min_y = point.y; + } + + CHECK(max_y - min_y <= height); + } + + SUBCASE("[Primitive][Capsule] If normal.y == 0, then mesh makes a cylinder.") { + Vector<Vector3> normals = data[RS::ARRAY_NORMAL]; + for (int ii = 0; ii < points.size(); ++ii) { + float point_dist_from_yaxis = Math::sqrt(points[ii].x * points[ii].x + points[ii].z * points[ii].z); + Vector3 yaxis_to_point{ points[ii].x / point_dist_from_yaxis, 0.f, points[ii].z / point_dist_from_yaxis }; + if (normals[ii].y == 0.f) { + float mag_of_normal = Math::sqrt(normals[ii].x * normals[ii].x + normals[ii].z * normals[ii].z); + Vector3 normalized_normal = normals[ii] / mag_of_normal; + CHECK_MESSAGE(Math::is_equal_approx(point_dist_from_yaxis, radius), + "Points on the tube of the capsule are radius away from y-axis."); + CHECK_MESSAGE(normalized_normal.is_equal_approx(yaxis_to_point), + "Normal points orthogonal from mid cylinder."); + } + } + } + } +} // End capsule tests + +TEST_CASE("[SceneTree][Primitive][Box] Box Primitive") { + Ref<BoxMesh> box = memnew(BoxMesh); + + SUBCASE("[SceneTree][Primitive][Box] Default values should be valid") { + CHECK(box->get_size().x > 0); + CHECK(box->get_size().y > 0); + CHECK(box->get_size().z > 0); + CHECK(box->get_subdivide_width() >= 0); + CHECK(box->get_subdivide_height() >= 0); + CHECK(box->get_subdivide_depth() >= 0); + } + + SUBCASE("[SceneTree][Primitive][Box] Set properties and get them with accessor methods") { + Vector3 size{ 2.1, 3.3, 1.7 }; + box->set_size(size); + box->set_subdivide_width(3); + box->set_subdivide_height(2); + box->set_subdivide_depth(4); + + CHECK(box->get_size().is_equal_approx(size)); + CHECK(box->get_subdivide_width() == 3); + CHECK(box->get_subdivide_height() == 2); + CHECK(box->get_subdivide_depth() == 4); + } + + SUBCASE("[SceneTree][Primitive][Box] Set subdivides to negative and ensure they are >= 0") { + box->set_subdivide_width(-2); + box->set_subdivide_height(-2); + box->set_subdivide_depth(-2); + + CHECK(box->get_subdivide_width() >= 0); + CHECK(box->get_subdivide_height() >= 0); + CHECK(box->get_subdivide_depth() >= 0); + } + + SUBCASE("[Primitive][Box] Check mesh is correct.") { + Array data{}; + data.resize(RS::ARRAY_MAX); + Vector3 size{ 0.5f, 1.2f, .9f }; + int subdivide_width{ 3 }; + int subdivide_height{ 2 }; + int subdivide_depth{ 8 }; + BoxMesh::create_mesh_array(data, size, subdivide_width, subdivide_height, subdivide_depth); + Vector<Vector3> points = data[RS::ARRAY_VERTEX]; + Vector<Vector3> normals = data[RS::ARRAY_NORMAL]; + + SUBCASE("Only 6 distinct normals.") { + Vector<Vector3> distinct_normals{}; + distinct_normals.push_back(normals[0]); + + for (const Vector3 &normal : normals) { + bool add_normal{ true }; + for (const Vector3 &vec : distinct_normals) { + if (vec.is_equal_approx(normal)) + add_normal = false; + } + + if (add_normal) + distinct_normals.push_back(normal); + } + + CHECK_MESSAGE(distinct_normals.size() == 6, + "There are exactly 6 distinct normals in the mesh data."); + + // All normals are orthogonal, or pointing in same direction. + bool normal_correct_direction{ true }; + for (int rowIndex = 0; rowIndex < distinct_normals.size(); ++rowIndex) { + for (int colIndex = rowIndex + 1; colIndex < distinct_normals.size(); ++colIndex) { + if (!Math::is_equal_approx(distinct_normals[rowIndex].normalized().dot(distinct_normals[colIndex].normalized()), 0) && + !Math::is_equal_approx(distinct_normals[rowIndex].normalized().dot(distinct_normals[colIndex].normalized()), 1) && + !Math::is_equal_approx(distinct_normals[rowIndex].normalized().dot(distinct_normals[colIndex].normalized()), -1)) { + normal_correct_direction = false; + break; + } + } + if (!normal_correct_direction) + break; + } + + CHECK_MESSAGE(normal_correct_direction, + "All normals are either orthogonal or colinear."); + } + } +} // End box tests + +TEST_CASE("[SceneTree][Primitive][Cylinder] Cylinder Primitive") { + Ref<CylinderMesh> cylinder = memnew(CylinderMesh); + + SUBCASE("[SceneTree][Primitive][Cylinder] Default values should be valid") { + CHECK(cylinder->get_top_radius() > 0); + CHECK(cylinder->get_bottom_radius() > 0); + CHECK(cylinder->get_height() > 0); + CHECK(cylinder->get_radial_segments() > 0); + CHECK(cylinder->get_rings() > 0); + } + + SUBCASE("[SceneTree][Primitive][Cylinder] Set properties and get them") { + cylinder->set_top_radius(4.3f); + cylinder->set_bottom_radius(1.2f); + cylinder->set_height(9.77f); + cylinder->set_radial_segments(12); + cylinder->set_rings(16); + cylinder->set_cap_top(false); + cylinder->set_cap_bottom(false); + + CHECK(Math::is_equal_approx(cylinder->get_top_radius(), 4.3f)); + CHECK(Math::is_equal_approx(cylinder->get_bottom_radius(), 1.2f)); + CHECK(Math::is_equal_approx(cylinder->get_height(), 9.77f)); + CHECK(cylinder->get_radial_segments() == 12); + CHECK(cylinder->get_rings() == 16); + CHECK(!cylinder->is_cap_top()); + CHECK(!cylinder->is_cap_bottom()); + } + + SUBCASE("[SceneTree][Primitive][Cylinder] Ensure num segments is >= 0") { + cylinder->set_radial_segments(-12); + cylinder->set_rings(-16); + + CHECK(cylinder->get_radial_segments() >= 0); + CHECK(cylinder->get_rings() >= 0); + } + + SUBCASE("[Primitive][Cylinder] Actual cylinder mesh tests (top and bottom radius the same).") { + Array data{}; + data.resize(RS::ARRAY_MAX); + real_t radius = .9f; + real_t height = 3.2f; + int radial_segments = 8; + int rings = 5; + bool top_cap = true; + bool bottom_cap = true; + CylinderMesh::create_mesh_array(data, radius, radius, height, radial_segments, rings, top_cap, bottom_cap); + Vector<Vector3> points = data[RS::ARRAY_VERTEX]; + Vector<Vector3> normals = data[RS::ARRAY_NORMAL]; + + SUBCASE("[Primitive][Cylinder] Side points are radius away from y-axis.") { + bool is_radius_correct{ true }; + for (int index = 0; index < normals.size(); ++index) { + if (Math::is_equal_approx(normals[index].y, 0)) { + if (!Math::is_equal_approx((points[index] - Vector3(0, points[index].y, 0)).length_squared(), radius * radius)) { + is_radius_correct = false; + break; + } + } + } + + CHECK(is_radius_correct); + } + + SUBCASE("[Primitive][Cylinder] Only possible normals point in direction of point or in positive/negative y direction.") { + bool is_correct_normals{ true }; + for (int index = 0; index < normals.size(); ++index) { + Vector3 yaxis_to_point = points[index] - Vector3(0.f, points[index].y, 0.f); + Vector3 point_to_normal = normals[index].normalized() - yaxis_to_point.normalized(); + // std::cout << "<" << point_to_normal.x << ", " << point_to_normal.y << ", " << point_to_normal.z << ">\n"; + if (!(point_to_normal.is_equal_approx(Vector3(0, 0, 0))) && + (!Math::is_equal_approx(Math::abs(normals[index].normalized().y), 1))) { + is_correct_normals = false; + break; + } + } + + CHECK(is_correct_normals); + } + + SUBCASE("[Primitive][Cylinder] Points on top and bottom are height/2 away from origin.") { + bool is_height_correct{ true }; + real_t half_height = 0.5 * height; + for (int index = 0; index < normals.size(); ++index) { + if (Math::is_equal_approx(normals[index].x, 0) && + Math::is_equal_approx(normals[index].z, 0) && + normals[index].y > 0) { + if (!Math::is_equal_approx(points[index].y, half_height)) { + is_height_correct = false; + break; + } + } + if (Math::is_equal_approx(normals[index].x, 0) && + Math::is_equal_approx(normals[index].z, 0) && + normals[index].y < 0) { + if (!Math::is_equal_approx(points[index].y, -half_height)) { + is_height_correct = false; + break; + } + } + } + + CHECK(is_height_correct); + } + + SUBCASE("[Primitive][Cylinder] Does mesh obey cap parameters?") { + CylinderMesh::create_mesh_array(data, radius, radius, height, radial_segments, rings, top_cap, false); + points = data[RS::ARRAY_VERTEX]; + normals = data[RS::ARRAY_NORMAL]; + bool no_bottom_cap{ true }; + + for (int index = 0; index < normals.size(); ++index) { + if (Math::is_equal_approx(normals[index].x, 0) && + Math::is_equal_approx(normals[index].z, 0) && + normals[index].y < 0) { + no_bottom_cap = false; + break; + } + } + + CHECK_MESSAGE(no_bottom_cap, + "Check there is no bottom cap."); + + CylinderMesh::create_mesh_array(data, radius, radius, height, radial_segments, rings, false, bottom_cap); + points = data[RS::ARRAY_VERTEX]; + normals = data[RS::ARRAY_NORMAL]; + bool no_top_cap{ true }; + + for (int index = 0; index < normals.size(); ++index) { + if (Math::is_equal_approx(normals[index].x, 0) && + Math::is_equal_approx(normals[index].z, 0) && + normals[index].y > 0) { + no_top_cap = false; + break; + } + } + + CHECK_MESSAGE(no_top_cap, + "Check there is no top cap."); + } + } + + SUBCASE("[Primitive][Cylinder] Slanted cylinder mesh (top and bottom radius different).") { + Array data{}; + data.resize(RS::ARRAY_MAX); + real_t top_radius = 2.f; + real_t bottom_radius = 1.f; + real_t height = 1.f; + int radial_segments = 8; + int rings = 5; + CylinderMesh::create_mesh_array(data, top_radius, bottom_radius, height, radial_segments, rings, false, false); + Vector<Vector3> points = data[RS::ARRAY_VERTEX]; + Vector<Vector3> normals = data[RS::ARRAY_NORMAL]; + + SUBCASE("[Primitive][Cylinder] Side points lie correct distance from y-axis") { + bool is_radius_correct{ true }; + for (int index = 0; index < points.size(); ++index) { + real_t radius = ((top_radius - bottom_radius) / height) * (points[index].y - 0.5 * height) + top_radius; + Vector3 distance_to_yaxis = points[index] - Vector3(0.f, points[index].y, 0.f); + if (!Math::is_equal_approx(distance_to_yaxis.length_squared(), radius * radius)) { + is_radius_correct = false; + break; + } + } + + CHECK(is_radius_correct); + } + + SUBCASE("[Primitive][Cylinder] Normal on side is orthogonal to side tangent vector") { + bool is_normal_correct{ true }; + for (int index = 0; index < points.size(); ++index) { + Vector3 yaxis_to_point = points[index] - Vector3(0.f, points[index].y, 0.f); + Vector3 yaxis_to_rb = yaxis_to_point.normalized() * bottom_radius; + Vector3 rb_to_point = yaxis_to_point - yaxis_to_rb; + Vector3 y_to_bottom = -Vector3(0.f, points[index].y + 0.5 * height, 0.f); + Vector3 side_tangent = rb_to_point - y_to_bottom; + + if (!Math::is_equal_approx(normals[index].dot(side_tangent), 0)) { + is_normal_correct = false; + break; + } + } + + CHECK(is_normal_correct); + } + } + +} // End cylinder tests + +TEST_CASE("[SceneTree][Primitive][Plane] Plane Primitive") { + Ref<PlaneMesh> plane = memnew(PlaneMesh); + + SUBCASE("[SceneTree][Primitive][Plane] Default values should be valid") { + CHECK(plane->get_size().x > 0); + CHECK(plane->get_size().y > 0); + CHECK(plane->get_subdivide_width() >= 0); + CHECK(plane->get_subdivide_depth() >= 0); + CHECK((plane->get_orientation() == PlaneMesh::FACE_X || plane->get_orientation() == PlaneMesh::FACE_Y || plane->get_orientation() == PlaneMesh::FACE_Z)); + } + + SUBCASE("[SceneTree][Primitive][Plane] Set properties and get them.") { + Size2 size{ 3.2, 1.8 }; + Vector3 offset{ -7.3, 0.4, -1.7 }; + plane->set_size(size); + plane->set_subdivide_width(15); + plane->set_subdivide_depth(29); + plane->set_center_offset(offset); + plane->set_orientation(PlaneMesh::FACE_X); + + CHECK(plane->get_size().is_equal_approx(size)); + CHECK(plane->get_subdivide_width() == 15); + CHECK(plane->get_subdivide_depth() == 29); + CHECK(plane->get_center_offset().is_equal_approx(offset)); + CHECK(plane->get_orientation() == PlaneMesh::FACE_X); + } + + SUBCASE("[SceneTree][Primitive][Plane] Ensure number of segments is >= 0.") { + plane->set_subdivide_width(-15); + plane->set_subdivide_depth(-29); + + CHECK(plane->get_subdivide_width() >= 0); + CHECK(plane->get_subdivide_depth() >= 0); + } +} + +TEST_CASE("[SceneTree][Primitive][Quad] QuadMesh Primitive") { + Ref<QuadMesh> quad = memnew(QuadMesh); + + SUBCASE("[Primitive][Quad] Orientation on initialization is in z direction") { + CHECK(quad->get_orientation() == PlaneMesh::FACE_Z); + } +} + +TEST_CASE("[SceneTree][Primitive][Prism] Prism Primitive") { + Ref<PrismMesh> prism = memnew(PrismMesh); + + SUBCASE("[Primitive][Prism] There are valid values of properties on initialization.") { + CHECK(prism->get_left_to_right() >= 0); + CHECK(prism->get_size().x >= 0); + CHECK(prism->get_size().y >= 0); + CHECK(prism->get_size().z >= 0); + CHECK(prism->get_subdivide_width() >= 0); + CHECK(prism->get_subdivide_height() >= 0); + CHECK(prism->get_subdivide_depth() >= 0); + } + + SUBCASE("[Primitive][Prism] Are able to change prism properties.") { + Vector3 size{ 4.3, 9.1, 0.43 }; + prism->set_left_to_right(3.4f); + prism->set_size(size); + prism->set_subdivide_width(36); + prism->set_subdivide_height(5); + prism->set_subdivide_depth(64); + + CHECK(Math::is_equal_approx(prism->get_left_to_right(), 3.4f)); + CHECK(prism->get_size().is_equal_approx(size)); + CHECK(prism->get_subdivide_width() == 36); + CHECK(prism->get_subdivide_height() == 5); + CHECK(prism->get_subdivide_depth() == 64); + } + + SUBCASE("[Primitive][Prism] Ensure number of segments always >= 0") { + prism->set_subdivide_width(-36); + prism->set_subdivide_height(-5); + prism->set_subdivide_depth(-64); + + CHECK(prism->get_subdivide_width() >= 0); + CHECK(prism->get_subdivide_height() >= 0); + CHECK(prism->get_subdivide_depth() >= 0); + } +} + +TEST_CASE("[SceneTree][Primitive][Sphere] Sphere Primitive") { + Ref<SphereMesh> sphere = memnew(SphereMesh); + + SUBCASE("[Primitive][Sphere] There are valid values of properties on initialization.") { + CHECK(sphere->get_radius() >= 0); + CHECK(sphere->get_height() >= 0); + CHECK(sphere->get_radial_segments() >= 0); + CHECK(sphere->get_rings() >= 0); + } + + SUBCASE("[Primitive][Sphere] Are able to change prism properties.") { + sphere->set_radius(3.4f); + sphere->set_height(2.2f); + sphere->set_radial_segments(36); + sphere->set_rings(5); + sphere->set_is_hemisphere(true); + + CHECK(Math::is_equal_approx(sphere->get_radius(), 3.4f)); + CHECK(Math::is_equal_approx(sphere->get_height(), 2.2f)); + CHECK(sphere->get_radial_segments() == 36); + CHECK(sphere->get_rings() == 5); + CHECK(sphere->get_is_hemisphere()); + } + + SUBCASE("[Primitive][Sphere] Ensure number of segments always >= 0") { + sphere->set_radial_segments(-36); + sphere->set_rings(-5); + + CHECK(sphere->get_radial_segments() >= 0); + CHECK(sphere->get_rings() >= 0); + } + + SUBCASE("[Primitive][Sphere] Sphere mesh tests.") { + Array data{}; + data.resize(RS::ARRAY_MAX); + real_t radius = 1.1f; + int radial_segments = 8; + int rings = 5; + SphereMesh::create_mesh_array(data, radius, 2 * radius, radial_segments, rings); + Vector<Vector3> points = data[RS::ARRAY_VERTEX]; + Vector<Vector3> normals = data[RS::ARRAY_NORMAL]; + + SUBCASE("[Primitive][Sphere] All points lie radius away from origin.") { + bool is_radius_correct = true; + for (Vector3 point : points) { + if (!Math::is_equal_approx(point.length_squared(), radius * radius)) { + is_radius_correct = false; + break; + } + } + + CHECK(is_radius_correct); + } + + SUBCASE("[Primitive][Sphere] All normals lie in direction of corresponding point.") { + bool is_normals_correct = true; + for (int index = 0; index < points.size(); ++index) { + if (!Math::is_equal_approx(normals[index].normalized().dot(points[index].normalized()), 1)) { + is_normals_correct = false; + break; + } + } + + CHECK(is_normals_correct); + } + } +} + +TEST_CASE("[SceneTree][Primitive][Torus] Torus Primitive") { + Ref<TorusMesh> torus = memnew(TorusMesh); + Ref<PrimitiveMesh> prim = memnew(PrimitiveMesh); + + SUBCASE("[Primitive][Torus] There are valid values of properties on initialization.") { + CHECK(torus->get_inner_radius() > 0); + CHECK(torus->get_outer_radius() > 0); + CHECK(torus->get_rings() >= 0); + CHECK(torus->get_ring_segments() >= 0); + } + + SUBCASE("[Primitive][Torus] Are able to change properties.") { + torus->set_inner_radius(3.2f); + torus->set_outer_radius(9.5f); + torus->set_rings(19); + torus->set_ring_segments(43); + + CHECK(Math::is_equal_approx(torus->get_inner_radius(), 3.2f)); + CHECK(Math::is_equal_approx(torus->get_outer_radius(), 9.5f)); + CHECK(torus->get_rings() == 19); + CHECK(torus->get_ring_segments() == 43); + } +} + +TEST_CASE("[SceneTree][Primitive][TubeTrail] TubeTrail Primitive") { + Ref<TubeTrailMesh> tube = memnew(TubeTrailMesh); + + SUBCASE("[Primitive][TubeTrail] There are valid values of properties on initialization.") { + CHECK(tube->get_radius() > 0); + CHECK(tube->get_radial_steps() >= 0); + CHECK(tube->get_sections() >= 0); + CHECK(tube->get_section_length() > 0); + CHECK(tube->get_section_rings() >= 0); + CHECK(tube->get_curve() == nullptr); + CHECK(tube->get_builtin_bind_pose_count() >= 0); + } + + SUBCASE("[Primitive][TubeTrail] Are able to change properties.") { + tube->set_radius(7.2f); + tube->set_radial_steps(9); + tube->set_sections(33); + tube->set_section_length(5.5f); + tube->set_section_rings(12); + Ref<Curve> curve = memnew(Curve); + tube->set_curve(curve); + + CHECK(Math::is_equal_approx(tube->get_radius(), 7.2f)); + CHECK(Math::is_equal_approx(tube->get_section_length(), 5.5f)); + CHECK(tube->get_radial_steps() == 9); + CHECK(tube->get_sections() == 33); + CHECK(tube->get_section_rings() == 12); + CHECK(tube->get_curve() == curve); + } + + SUBCASE("[Primitive][TubeTrail] Setting same curve more than once, it remains the same.") { + Ref<Curve> curve = memnew(Curve); + tube->set_curve(curve); + tube->set_curve(curve); + tube->set_curve(curve); + + CHECK(tube->get_curve() == curve); + } + + SUBCASE("[Primitive][TubeTrail] Setting curve, then changing to different curve.") { + Ref<Curve> curve1 = memnew(Curve); + Ref<Curve> curve2 = memnew(Curve); + tube->set_curve(curve1); + CHECK(tube->get_curve() == curve1); + + tube->set_curve(curve2); + CHECK(tube->get_curve() == curve2); + } + + SUBCASE("[Primitive][TubeTrail] Assign same curve to two different tube trails") { + Ref<TubeTrailMesh> tube2 = memnew(TubeTrailMesh); + Ref<Curve> curve = memnew(Curve); + tube->set_curve(curve); + tube2->set_curve(curve); + + CHECK(tube->get_curve() == curve); + CHECK(tube2->get_curve() == curve); + } +} + +TEST_CASE("[SceneTree][Primitive][RibbonTrail] RibbonTrail Primitive") { + Ref<RibbonTrailMesh> ribbon = memnew(RibbonTrailMesh); + + SUBCASE("[Primitive][RibbonTrail] There are valid values of properties on initialization.") { + CHECK(ribbon->get_size() > 0); + CHECK(ribbon->get_sections() >= 0); + CHECK(ribbon->get_section_length() > 0); + CHECK(ribbon->get_section_segments() >= 0); + CHECK(ribbon->get_builtin_bind_pose_count() >= 0); + CHECK(ribbon->get_curve() == nullptr); + CHECK((ribbon->get_shape() == RibbonTrailMesh::SHAPE_CROSS || + ribbon->get_shape() == RibbonTrailMesh::SHAPE_FLAT)); + } + + SUBCASE("[Primitive][RibbonTrail] Able to change properties.") { + Ref<Curve> curve = memnew(Curve); + ribbon->set_size(4.3f); + ribbon->set_sections(16); + ribbon->set_section_length(1.3f); + ribbon->set_section_segments(9); + ribbon->set_curve(curve); + + CHECK(Math::is_equal_approx(ribbon->get_size(), 4.3f)); + CHECK(Math::is_equal_approx(ribbon->get_section_length(), 1.3f)); + CHECK(ribbon->get_sections() == 16); + CHECK(ribbon->get_section_segments() == 9); + CHECK(ribbon->get_curve() == curve); + } + + SUBCASE("[Primitive][RibbonTrail] Setting same curve more than once, it remains the same.") { + Ref<Curve> curve = memnew(Curve); + ribbon->set_curve(curve); + ribbon->set_curve(curve); + ribbon->set_curve(curve); + + CHECK(ribbon->get_curve() == curve); + } + + SUBCASE("[Primitive][RibbonTrail] Setting curve, then changing to different curve.") { + Ref<Curve> curve1 = memnew(Curve); + Ref<Curve> curve2 = memnew(Curve); + ribbon->set_curve(curve1); + CHECK(ribbon->get_curve() == curve1); + + ribbon->set_curve(curve2); + CHECK(ribbon->get_curve() == curve2); + } + + SUBCASE("[Primitive][RibbonTrail] Assign same curve to two different ribbon trails") { + Ref<RibbonTrailMesh> ribbon2 = memnew(RibbonTrailMesh); + Ref<Curve> curve = memnew(Curve); + ribbon->set_curve(curve); + ribbon2->set_curve(curve); + + CHECK(ribbon->get_curve() == curve); + CHECK(ribbon2->get_curve() == curve); + } +} + +TEST_CASE("[SceneTree][Primitive][Text] Text Primitive") { + Ref<TextMesh> text = memnew(TextMesh); + + SUBCASE("[Primitive][Text] There are valid values of properties on initialization.") { + CHECK((text->get_horizontal_alignment() == HORIZONTAL_ALIGNMENT_CENTER || + text->get_horizontal_alignment() == HORIZONTAL_ALIGNMENT_LEFT || + text->get_horizontal_alignment() == HORIZONTAL_ALIGNMENT_RIGHT || + text->get_horizontal_alignment() == HORIZONTAL_ALIGNMENT_FILL)); + CHECK((text->get_vertical_alignment() == VERTICAL_ALIGNMENT_BOTTOM || + text->get_vertical_alignment() == VERTICAL_ALIGNMENT_TOP || + text->get_vertical_alignment() == VERTICAL_ALIGNMENT_CENTER || + text->get_vertical_alignment() == VERTICAL_ALIGNMENT_FILL)); + CHECK(text->get_font() == nullptr); + CHECK(text->get_font_size() > 0); + CHECK(text->get_line_spacing() >= 0); + CHECK((text->get_autowrap_mode() == TextServer::AUTOWRAP_OFF || + text->get_autowrap_mode() == TextServer::AUTOWRAP_ARBITRARY || + text->get_autowrap_mode() == TextServer::AUTOWRAP_WORD || + text->get_autowrap_mode() == TextServer::AUTOWRAP_WORD_SMART)); + CHECK((text->get_text_direction() == TextServer::DIRECTION_AUTO || + text->get_text_direction() == TextServer::DIRECTION_LTR || + text->get_text_direction() == TextServer::DIRECTION_RTL)); + CHECK((text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_DEFAULT || + text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_URI || + text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_FILE || + text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_EMAIL || + text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_LIST || + text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_NONE || + text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_CUSTOM)); + CHECK(text->get_structured_text_bidi_override_options().size() >= 0); + CHECK(text->get_width() > 0); + CHECK(text->get_depth() > 0); + CHECK(text->get_curve_step() > 0); + CHECK(text->get_pixel_size() > 0); + } + + SUBCASE("[Primitive][Text] Change the properties of the mesh.") { + Ref<Font> font = memnew(Font); + Array options{}; + Point2 offset{ 30.8, 104.23 }; + text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); + text->set_vertical_alignment(VERTICAL_ALIGNMENT_BOTTOM); + text->set_text("Hello"); + text->set_font(font); + text->set_font_size(12); + text->set_line_spacing(1.7f); + text->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); + text->set_text_direction(TextServer::DIRECTION_RTL); + text->set_language("French"); + text->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_EMAIL); + text->set_structured_text_bidi_override_options(options); + text->set_uppercase(true); + real_t width{ 0.6 }; + real_t depth{ 1.7 }; + real_t pixel_size{ 2.8 }; + real_t curve_step{ 4.8 }; + text->set_width(width); + text->set_depth(depth); + text->set_curve_step(curve_step); + text->set_pixel_size(pixel_size); + text->set_offset(offset); + + CHECK(text->get_horizontal_alignment() == HORIZONTAL_ALIGNMENT_RIGHT); + CHECK(text->get_vertical_alignment() == VERTICAL_ALIGNMENT_BOTTOM); + CHECK(text->get_text_direction() == TextServer::DIRECTION_RTL); + CHECK(text->get_text() == "Hello"); + CHECK(text->get_font() == font); + CHECK(text->get_font_size() == 12); + CHECK(text->get_autowrap_mode() == TextServer::AUTOWRAP_WORD_SMART); + CHECK(text->get_language() == "French"); + CHECK(text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_EMAIL); + CHECK(text->get_structured_text_bidi_override_options() == options); + CHECK(text->is_uppercase() == true); + CHECK(text->get_offset() == offset); + CHECK(Math::is_equal_approx(text->get_line_spacing(), 1.7f)); + CHECK(Math::is_equal_approx(text->get_width(), width)); + CHECK(Math::is_equal_approx(text->get_depth(), depth)); + CHECK(Math::is_equal_approx(text->get_curve_step(), curve_step)); + CHECK(Math::is_equal_approx(text->get_pixel_size(), pixel_size)); + } + + SUBCASE("[Primitive][Text] Set objects multiple times.") { + Ref<Font> font = memnew(Font); + Array options{}; + Point2 offset{ 30.8, 104.23 }; + + text->set_font(font); + text->set_font(font); + text->set_font(font); + text->set_structured_text_bidi_override_options(options); + text->set_structured_text_bidi_override_options(options); + text->set_structured_text_bidi_override_options(options); + text->set_offset(offset); + text->set_offset(offset); + text->set_offset(offset); + + CHECK(text->get_font() == font); + CHECK(text->get_structured_text_bidi_override_options() == options); + CHECK(text->get_offset() == offset); + } + + SUBCASE("[Primitive][Text] Set then change objects.") { + Ref<Font> font1 = memnew(Font); + Ref<Font> font2 = memnew(Font); + Array options1{}; + Array options2{}; + Point2 offset1{ 30.8, 104.23 }; + Point2 offset2{ -30.8, -104.23 }; + + text->set_font(font1); + text->set_structured_text_bidi_override_options(options1); + text->set_offset(offset1); + + CHECK(text->get_font() == font1); + CHECK(text->get_structured_text_bidi_override_options() == options1); + CHECK(text->get_offset() == offset1); + + text->set_font(font2); + text->set_structured_text_bidi_override_options(options2); + text->set_offset(offset2); + + CHECK(text->get_font() == font2); + CHECK(text->get_structured_text_bidi_override_options() == options2); + CHECK(text->get_offset() == offset2); + } + + SUBCASE("[Primitive][Text] Assign same font to two Textmeshes.") { + Ref<TextMesh> text2 = memnew(TextMesh); + Ref<Font> font = memnew(Font); + + text->set_font(font); + text2->set_font(font); + + CHECK(text->get_font() == font); + CHECK(text2->get_font() == font); + } +} + +} // namespace TestPrimitives + +#endif // TEST_PRIMITIVES_H diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index 652cbed6e8..e34f2970d4 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -2337,7 +2337,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); - // Normal left shoud deselect and place at selection start. + // Normal left should deselect and place at selection start. SEND_GUI_ACTION(text_edit, "ui_text_caret_left"); CHECK(text_edit->get_viewport()->is_input_handled()); @@ -2497,7 +2497,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); - // Normal right shoud deselect and place at selection start. + // Normal right should deselect and place at selection start. SEND_GUI_ACTION(text_edit, "ui_text_caret_right"); CHECK(text_edit->get_viewport()->is_input_handled()); CHECK(text_edit->get_caret_line() == 0); diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 80099d1dd4..cee110414e 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -47,6 +47,7 @@ #include "tests/core/math/test_expression.h" #include "tests/core/math/test_geometry_2d.h" #include "tests/core/math/test_geometry_3d.h" +#include "tests/core/math/test_math_funcs.h" #include "tests/core/math/test_plane.h" #include "tests/core/math/test_quaternion.h" #include "tests/core/math/test_random_number_generator.h" @@ -90,7 +91,9 @@ #include "tests/scene/test_code_edit.h" #include "tests/scene/test_curve.h" #include "tests/scene/test_gradient.h" +#include "tests/scene/test_path_2d.h" #include "tests/scene/test_path_3d.h" +#include "tests/scene/test_primitives.h" #include "tests/scene/test_sprite_frames.h" #include "tests/scene/test_text_edit.h" #include "tests/scene/test_theme.h" diff --git a/tests/test_validate_testing.h b/tests/test_validate_testing.h index 1471a952cd..41185a7d16 100644 --- a/tests/test_validate_testing.h +++ b/tests/test_validate_testing.h @@ -86,7 +86,7 @@ TEST_SUITE("Validate tests") { Plane plane(Vector3(1, 1, 1), 1.0); INFO(plane); - Quaternion quat(Vector3(0.5, 1.0, 2.0)); + Quaternion quat = Quaternion::from_euler(Vector3(0.5, 1.0, 2.0)); INFO(quat); AABB aabb(Vector3(), Vector3(100, 100, 100)); |