diff options
Diffstat (limited to 'tests')
29 files changed, 1029 insertions, 79 deletions
diff --git a/tests/core/config/test_project_settings.h b/tests/core/config/test_project_settings.h index 9bd072f511..8fc2489f8b 100644 --- a/tests/core/config/test_project_settings.h +++ b/tests/core/config/test_project_settings.h @@ -45,6 +45,8 @@ public: namespace TestProjectSettings { +// TODO: Handle some cases failing on release builds. See: https://github.com/godotengine/godot/pull/88452 +#ifdef TOOLS_ENABLED TEST_CASE("[ProjectSettings] Get existing setting") { CHECK(ProjectSettings::get_singleton()->has_setting("application/config/name")); @@ -64,6 +66,7 @@ TEST_CASE("[ProjectSettings] Default value is ignored if setting exists") { String name = variant; CHECK_EQ(name, "GDScript Integration Test Suite"); } +#endif // TOOLS_ENABLED TEST_CASE("[ProjectSettings] Non existing setting is null") { CHECK_FALSE(ProjectSettings::get_singleton()->has_setting("not_existing_setting")); diff --git a/tests/core/input/test_input_event_key.h b/tests/core/input/test_input_event_key.h index 3317941fad..80918542ce 100644 --- a/tests/core/input/test_input_event_key.h +++ b/tests/core/input/test_input_event_key.h @@ -85,6 +85,16 @@ TEST_CASE("[InputEventKey] Key correctly stores and retrieves unicode") { CHECK(key.get_unicode() != 'y'); } +TEST_CASE("[InputEventKey] Key correctly stores and retrieves location") { + InputEventKey key; + + CHECK(key.get_location() == KeyLocation::UNSPECIFIED); + + key.set_location(KeyLocation::LEFT); + CHECK(key.get_location() == KeyLocation::LEFT); + CHECK(key.get_location() != KeyLocation::RIGHT); +} + TEST_CASE("[InputEventKey] Key correctly stores and checks echo") { InputEventKey key; @@ -144,32 +154,36 @@ TEST_CASE("[InputEventKey] Key correctly converts itself to text") { TEST_CASE("[InputEventKey] Key correctly converts its state to a string representation") { InputEventKey none_key; - CHECK(none_key.to_string() == "InputEventKey: keycode=(Unset), mods=none, physical=false, pressed=false, echo=false"); + CHECK(none_key.to_string() == "InputEventKey: keycode=(Unset), mods=none, physical=false, location=unspecified, pressed=false, echo=false"); // Set physical key to Escape. none_key.set_physical_keycode(Key::ESCAPE); - CHECK(none_key.to_string() == "InputEventKey: keycode=4194305 (Escape), mods=none, physical=true, pressed=false, echo=false"); + CHECK(none_key.to_string() == "InputEventKey: keycode=4194305 (Escape), mods=none, physical=true, location=unspecified, pressed=false, echo=false"); InputEventKey key; // Set physical to None, set keycode to Space. key.set_keycode(Key::SPACE); - CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=none, physical=false, pressed=false, echo=false"); + CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=none, physical=false, location=unspecified, pressed=false, echo=false"); + + // Set location + key.set_location(KeyLocation::RIGHT); + CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=none, physical=false, location=right, pressed=false, echo=false"); // Set pressed to true. key.set_pressed(true); - CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=none, physical=false, pressed=true, echo=false"); + CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=none, physical=false, location=right, pressed=true, echo=false"); // set echo to true. key.set_echo(true); - CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=none, physical=false, pressed=true, echo=true"); + CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=none, physical=false, location=right, pressed=true, echo=true"); // Press Ctrl and Alt. key.set_ctrl_pressed(true); key.set_alt_pressed(true); #ifdef MACOS_ENABLED - CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=Ctrl+Option, physical=false, pressed=true, echo=true"); + CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=Ctrl+Option, physical=false, location=right, pressed=true, echo=true"); #else - CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=Ctrl+Alt, physical=false, pressed=true, echo=true"); + CHECK(key.to_string() == "InputEventKey: keycode=32 (Space), mods=Ctrl+Alt, physical=false, location=right, pressed=true, echo=true"); #endif } @@ -291,6 +305,34 @@ TEST_CASE("[IsMatch] Keys are correctly matched") { CHECK(key2.is_match(match, true) == true); CHECK(key2.is_match(no_match, true) == false); + + // Physical key with location. + InputEventKey key3; + key3.set_keycode(Key::NONE); + key3.set_physical_keycode(Key::SHIFT); + + Ref<InputEventKey> loc_ref = key.create_reference(Key::NONE); + + loc_ref->set_keycode(Key::SHIFT); + loc_ref->set_physical_keycode(Key::SHIFT); + + CHECK(key3.is_match(loc_ref, false) == true); + key3.set_location(KeyLocation::UNSPECIFIED); + CHECK(key3.is_match(loc_ref, false) == true); + + loc_ref->set_location(KeyLocation::LEFT); + CHECK(key3.is_match(loc_ref, false) == true); + + key3.set_location(KeyLocation::LEFT); + CHECK(key3.is_match(loc_ref, false) == true); + + key3.set_location(KeyLocation::RIGHT); + CHECK(key3.is_match(loc_ref, false) == false); + + // Keycode key with location. + key3.set_physical_keycode(Key::NONE); + key3.set_keycode(Key::SHIFT); + CHECK(key3.is_match(loc_ref, false) == true); } } // namespace TestInputEventKey diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h index 07c7c04e36..7a0cbb13f9 100644 --- a/tests/core/io/test_image.h +++ b/tests/core/io/test_image.h @@ -88,11 +88,14 @@ TEST_CASE("[Image] Saving and loading") { err == OK, "The image should be saved successfully as a .png file."); + // Only available on editor builds. +#ifdef TOOLS_ENABLED // Save EXR err = image->save_exr(save_path_exr, false); CHECK_MESSAGE( err == OK, "The image should be saved successfully as an .exr file."); +#endif // TOOLS_ENABLED // Load using load() Ref<Image> image_load = memnew(Image()); @@ -403,6 +406,32 @@ 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(); + ERR_PRINT_OFF; + image->convert((Image::Format)-1); + ERR_PRINT_ON; + 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(); + ERR_PRINT_OFF; + image2->convert((Image::Format)(Image::FORMAT_MAX + 1)); + ERR_PRINT_ON; + 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_aabb.h b/tests/core/math/test_aabb.h index d3560ff6b6..b9f84cca24 100644 --- a/tests/core/math/test_aabb.h +++ b/tests/core/math/test_aabb.h @@ -228,11 +228,20 @@ TEST_CASE("[AABB] Merging") { TEST_CASE("[AABB] Encloses") { const AABB aabb_big = AABB(Vector3(-1.5, 2, -2.5), Vector3(4, 5, 6)); + CHECK_MESSAGE( + aabb_big.encloses(aabb_big), + "encloses() with itself should return the expected result."); + AABB aabb_small = AABB(Vector3(-1.5, 2, -2.5), Vector3(1, 1, 1)); CHECK_MESSAGE( aabb_big.encloses(aabb_small), "encloses() with fully contained AABB (touching the edge) should return the expected result."); + aabb_small = AABB(Vector3(1.5, 6, 2.5), Vector3(1, 1, 1)); + CHECK_MESSAGE( + aabb_big.encloses(aabb_small), + "encloses() with fully contained AABB (touching the edge) should return the expected result."); + aabb_small = AABB(Vector3(0.5, 1.5, -2), Vector3(1, 1, 1)); CHECK_MESSAGE( !aabb_big.encloses(aabb_small), 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/math/test_vector2i.h b/tests/core/math/test_vector2i.h index 743c87f486..0f33400f7f 100644 --- a/tests/core/math/test_vector2i.h +++ b/tests/core/math/test_vector2i.h @@ -87,6 +87,12 @@ TEST_CASE("[Vector2i] Length methods") { CHECK_MESSAGE( vector2.length() == doctest::Approx(36.05551275463989293119), "Vector2i length should work as expected."); + CHECK_MESSAGE( + vector1.distance_squared_to(vector2) == 500, + "Vector2i distance_squared_to should work as expected and return exact result."); + CHECK_MESSAGE( + vector1.distance_to(vector2) == doctest::Approx(22.36067977499789696409), + "Vector2i distance_to should work as expected."); } TEST_CASE("[Vector2i] Operators") { diff --git a/tests/core/math/test_vector3i.h b/tests/core/math/test_vector3i.h index 485a500715..3914b85a75 100644 --- a/tests/core/math/test_vector3i.h +++ b/tests/core/math/test_vector3i.h @@ -90,6 +90,12 @@ TEST_CASE("[Vector3i] Length methods") { CHECK_MESSAGE( vector2.length() == doctest::Approx(53.8516480713450403125), "Vector3i length should work as expected."); + CHECK_MESSAGE( + vector1.distance_squared_to(vector2) == 1400, + "Vector3i distance_squared_to should work as expected and return exact result."); + CHECK_MESSAGE( + vector1.distance_to(vector2) == doctest::Approx(37.41657386773941385584), + "Vector3i distance_to should work as expected."); } TEST_CASE("[Vector3i] Operators") { diff --git a/tests/core/math/test_vector4i.h b/tests/core/math/test_vector4i.h index 5fda6f1778..31f68696c0 100644 --- a/tests/core/math/test_vector4i.h +++ b/tests/core/math/test_vector4i.h @@ -90,6 +90,12 @@ TEST_CASE("[Vector4i] Length methods") { CHECK_MESSAGE( vector2.length() == doctest::Approx(73.4846922835), "Vector4i length should work as expected."); + CHECK_MESSAGE( + vector1.distance_squared_to(vector2) == 3000, + "Vector4i distance_squared_to should work as expected."); + CHECK_MESSAGE( + vector1.distance_to(vector2) == doctest::Approx(54.772255750517), + "Vector4i distance_to should work as expected."); } TEST_CASE("[Vector4i] Operators") { diff --git a/tests/core/os/test_os.h b/tests/core/os/test_os.h index ef8216685f..1a5d360f57 100644 --- a/tests/core/os/test_os.h +++ b/tests/core/os/test_os.h @@ -93,6 +93,7 @@ TEST_CASE("[OS] Ticks") { } TEST_CASE("[OS] Feature tags") { +#ifdef TOOLS_ENABLED CHECK_MESSAGE( OS::get_singleton()->has_feature("editor"), "The binary has the \"editor\" feature tag."); @@ -105,6 +106,29 @@ TEST_CASE("[OS] Feature tags") { CHECK_MESSAGE( !OS::get_singleton()->has_feature("template_release"), "The binary does not have the \"template_release\" feature tag."); +#else + CHECK_MESSAGE( + !OS::get_singleton()->has_feature("editor"), + "The binary does not have the \"editor\" feature tag."); + CHECK_MESSAGE( + OS::get_singleton()->has_feature("template"), + "The binary has the \"template\" feature tag."); +#ifdef DEBUG_ENABLED + CHECK_MESSAGE( + OS::get_singleton()->has_feature("template_debug"), + "The binary has the \"template_debug\" feature tag."); + CHECK_MESSAGE( + !OS::get_singleton()->has_feature("template_release"), + "The binary does not have the \"template_release\" feature tag."); +#else + CHECK_MESSAGE( + !OS::get_singleton()->has_feature("template_debug"), + "The binary does not have the \"template_debug\" feature tag."); + CHECK_MESSAGE( + OS::get_singleton()->has_feature("template_release"), + "The binary has the \"template_release\" feature tag."); +#endif // DEBUG_ENABLED +#endif // TOOLS_ENABLED } TEST_CASE("[OS] Process ID") { diff --git a/tests/core/string/test_node_path.h b/tests/core/string/test_node_path.h index 9afda38611..bdbc578e85 100644 --- a/tests/core/string/test_node_path.h +++ b/tests/core/string/test_node_path.h @@ -98,44 +98,44 @@ TEST_CASE("[NodePath] Relative path") { } TEST_CASE("[NodePath] Absolute path") { - const NodePath node_path_aboslute = NodePath("/root/Sprite2D"); + const NodePath node_path_absolute = NodePath("/root/Sprite2D"); CHECK_MESSAGE( - node_path_aboslute.get_as_property_path() == NodePath(":root/Sprite2D"), + node_path_absolute.get_as_property_path() == NodePath(":root/Sprite2D"), "The returned property path should match the expected value."); CHECK_MESSAGE( - node_path_aboslute.get_concatenated_subnames() == "", + node_path_absolute.get_concatenated_subnames() == "", "The returned concatenated subnames should match the expected value."); CHECK_MESSAGE( - node_path_aboslute.get_name(0) == "root", + node_path_absolute.get_name(0) == "root", "The returned name at index 0 should match the expected value."); CHECK_MESSAGE( - node_path_aboslute.get_name(1) == "Sprite2D", + node_path_absolute.get_name(1) == "Sprite2D", "The returned name at index 1 should match the expected value."); ERR_PRINT_OFF; CHECK_MESSAGE( - node_path_aboslute.get_name(2) == "", + node_path_absolute.get_name(2) == "", "The returned name at invalid index 2 should match the expected value."); CHECK_MESSAGE( - node_path_aboslute.get_name(-1) == "", + node_path_absolute.get_name(-1) == "", "The returned name at invalid index -1 should match the expected value."); ERR_PRINT_ON; CHECK_MESSAGE( - node_path_aboslute.get_name_count() == 2, + node_path_absolute.get_name_count() == 2, "The returned number of names should match the expected value."); CHECK_MESSAGE( - node_path_aboslute.get_subname_count() == 0, + node_path_absolute.get_subname_count() == 0, "The returned number of subnames should match the expected value."); CHECK_MESSAGE( - node_path_aboslute.is_absolute(), + node_path_absolute.is_absolute(), "The node path should be considered absolute."); CHECK_MESSAGE( - !node_path_aboslute.is_empty(), + !node_path_absolute.is_empty(), "The node path shouldn't be considered empty."); } diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index 8a11491bb2..8d2607b874 100644 --- a/tests/core/string/test_string.h +++ b/tests/core/string/test_string.h @@ -1300,39 +1300,54 @@ TEST_CASE("[String] Capitalize against many strings") { input = "snake_case_function( snake_case_arg )"; output = "Snake Case Function( Snake Case Arg )"; CHECK(input.capitalize() == output); + + input = U"словоСлово_слово слово"; + output = U"Слово Слово Слово Слово"; + CHECK(input.capitalize() == output); + + input = U"λέξηΛέξη_λέξη λέξη"; + output = U"Λέξη Λέξη Λέξη Λέξη"; + CHECK(input.capitalize() == output); + + input = U"բառԲառ_բառ բառ"; + output = U"Բառ Բառ Բառ Բառ"; + CHECK(input.capitalize() == output); } struct StringCasesTestCase { - const char *input; - const char *camel_case; - const char *pascal_case; - const char *snake_case; + const char32_t *input; + const char32_t *camel_case; + const char32_t *pascal_case; + const char32_t *snake_case; }; TEST_CASE("[String] Checking case conversion methods") { StringCasesTestCase test_cases[] = { /* clang-format off */ - { "2D", "2d", "2d", "2d" }, - { "2d", "2d", "2d", "2d" }, - { "2db", "2Db", "2Db", "2_db" }, - { "Vector3", "vector3", "Vector3", "vector_3" }, - { "sha256", "sha256", "Sha256", "sha_256" }, - { "Node2D", "node2d", "Node2d", "node_2d" }, - { "RichTextLabel", "richTextLabel", "RichTextLabel", "rich_text_label" }, - { "HTML5", "html5", "Html5", "html_5" }, - { "Node2DPosition", "node2dPosition", "Node2dPosition", "node_2d_position" }, - { "Number2Digits", "number2Digits", "Number2Digits", "number_2_digits" }, - { "get_property_list", "getPropertyList", "GetPropertyList", "get_property_list" }, - { "get_camera_2d", "getCamera2d", "GetCamera2d", "get_camera_2d" }, - { "_physics_process", "physicsProcess", "PhysicsProcess", "_physics_process" }, - { "bytes2var", "bytes2Var", "Bytes2Var", "bytes_2_var" }, - { "linear2db", "linear2Db", "Linear2Db", "linear_2_db" }, - { "sha256sum", "sha256Sum", "Sha256Sum", "sha_256_sum" }, - { "camelCase", "camelCase", "CamelCase", "camel_case" }, - { "PascalCase", "pascalCase", "PascalCase", "pascal_case" }, - { "snake_case", "snakeCase", "SnakeCase", "snake_case" }, - { "Test TEST test", "testTestTest", "TestTestTest", "test_test_test" }, - { nullptr, nullptr, nullptr, nullptr }, + { U"2D", U"2d", U"2d", U"2d" }, + { U"2d", U"2d", U"2d", U"2d" }, + { U"2db", U"2Db", U"2Db", U"2_db" }, + { U"Vector3", U"vector3", U"Vector3", U"vector_3" }, + { U"sha256", U"sha256", U"Sha256", U"sha_256" }, + { U"Node2D", U"node2d", U"Node2d", U"node_2d" }, + { U"RichTextLabel", U"richTextLabel", U"RichTextLabel", U"rich_text_label" }, + { U"HTML5", U"html5", U"Html5", U"html_5" }, + { U"Node2DPosition", U"node2dPosition", U"Node2dPosition", U"node_2d_position" }, + { U"Number2Digits", U"number2Digits", U"Number2Digits", U"number_2_digits" }, + { U"get_property_list", U"getPropertyList", U"GetPropertyList", U"get_property_list" }, + { U"get_camera_2d", U"getCamera2d", U"GetCamera2d", U"get_camera_2d" }, + { U"_physics_process", U"physicsProcess", U"PhysicsProcess", U"_physics_process" }, + { U"bytes2var", U"bytes2Var", U"Bytes2Var", U"bytes_2_var" }, + { U"linear2db", U"linear2Db", U"Linear2Db", U"linear_2_db" }, + { U"sha256sum", U"sha256Sum", U"Sha256Sum", U"sha_256_sum" }, + { U"camelCase", U"camelCase", U"CamelCase", U"camel_case" }, + { U"PascalCase", U"pascalCase", U"PascalCase", U"pascal_case" }, + { U"snake_case", U"snakeCase", U"SnakeCase", U"snake_case" }, + { U"Test TEST test", U"testTestTest", U"TestTestTest", U"test_test_test" }, + { U"словоСлово_слово слово", U"словоСловоСловоСлово", U"СловоСловоСловоСлово", U"слово_слово_слово_слово" }, + { U"λέξηΛέξη_λέξη λέξη", U"λέξηΛέξηΛέξηΛέξη", U"ΛέξηΛέξηΛέξηΛέξη", U"λέξη_λέξη_λέξη_λέξη" }, + { U"բառԲառ_բառ բառ", U"բառԲառԲառԲառ", U"ԲառԲառԲառԲառ", U"բառ_բառ_բառ_բառ" }, + { nullptr, nullptr, nullptr, nullptr }, /* clang-format on */ }; diff --git a/tests/core/string/test_translation.h b/tests/core/string/test_translation.h index bf9674d6b1..acdd851b29 100644 --- a/tests/core/string/test_translation.h +++ b/tests/core/string/test_translation.h @@ -129,6 +129,7 @@ TEST_CASE("[TranslationPO] Plural messages") { CHECK(vformat(translation->get_plural_message("There are %d apples", "", 2), 2) == "Il y a 2 pommes"); } +#ifdef TOOLS_ENABLED TEST_CASE("[OptimizedTranslation] Generate from Translation and read messages") { Ref<Translation> translation = memnew(Translation); translation->set_locale("fr"); @@ -150,7 +151,6 @@ TEST_CASE("[OptimizedTranslation] Generate from Translation and read messages") CHECK(messages.size() == 0); } -#ifdef TOOLS_ENABLED TEST_CASE("[TranslationCSV] CSV import") { Ref<ResourceImporterCSVTranslation> import_csv_translation = memnew(ResourceImporterCSVTranslation); diff --git a/tests/core/test_crypto.h b/tests/core/test_crypto.h index 2fd26c3d6b..a7c2fce589 100644 --- a/tests/core/test_crypto.h +++ b/tests/core/test_crypto.h @@ -39,13 +39,13 @@ namespace TestCrypto { class _MockCrypto : public Crypto { virtual PackedByteArray generate_random_bytes(int p_bytes) { return PackedByteArray(); } virtual Ref<CryptoKey> generate_rsa(int p_bytes) { return nullptr; } - virtual Ref<X509Certificate> generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after) { return nullptr; } + virtual Ref<X509Certificate> generate_self_signed_certificate(Ref<CryptoKey> p_key, const String &p_issuer_name, const String &p_not_before, const String &p_not_after) { return nullptr; } - virtual Vector<uint8_t> sign(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Ref<CryptoKey> p_key) { return Vector<uint8_t>(); } - virtual bool verify(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Vector<uint8_t> p_signature, Ref<CryptoKey> p_key) { return false; } - virtual Vector<uint8_t> encrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_plaintext) { return Vector<uint8_t>(); } - virtual Vector<uint8_t> decrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_ciphertext) { return Vector<uint8_t>(); } - virtual PackedByteArray hmac_digest(HashingContext::HashType p_hash_type, PackedByteArray p_key, PackedByteArray p_msg) { return PackedByteArray(); } + virtual Vector<uint8_t> sign(HashingContext::HashType p_hash_type, const Vector<uint8_t> &p_hash, Ref<CryptoKey> p_key) { return Vector<uint8_t>(); } + virtual bool verify(HashingContext::HashType p_hash_type, const Vector<uint8_t> &p_hash, const Vector<uint8_t> &p_signature, Ref<CryptoKey> p_key) { return false; } + virtual Vector<uint8_t> encrypt(Ref<CryptoKey> p_key, const Vector<uint8_t> &p_plaintext) { return Vector<uint8_t>(); } + virtual Vector<uint8_t> decrypt(Ref<CryptoKey> p_key, const Vector<uint8_t> &p_ciphertext) { return Vector<uint8_t>(); } + virtual PackedByteArray hmac_digest(HashingContext::HashType p_hash_type, const PackedByteArray &p_key, const PackedByteArray &p_msg) { return PackedByteArray(); } }; PackedByteArray raw_to_pba(const uint8_t *arr, size_t len) { diff --git a/tests/core/variant/test_array.h b/tests/core/variant/test_array.h index 228d77b3b5..ea61ae2a02 100644 --- a/tests/core/variant/test_array.h +++ b/tests/core/variant/test_array.h @@ -367,7 +367,7 @@ TEST_CASE("[Array] Duplicate recursive array") { Array a_shallow = a.duplicate(false); CHECK_EQ(a, a_shallow); - // Deep copy of recursive array endup with recursion limit and return + // Deep copy of recursive array ends up with recursion limit and return // an invalid result (multiple nested arrays), the point is we should // not end up with a segfault and an error log should be printed ERR_PRINT_OFF; diff --git a/tests/scene/test_arraymesh.h b/tests/scene/test_arraymesh.h index 1623b41300..67aa19c5d3 100644 --- a/tests/scene/test_arraymesh.h +++ b/tests/scene/test_arraymesh.h @@ -31,8 +31,8 @@ #ifndef TEST_ARRAYMESH_H #define TEST_ARRAYMESH_H +#include "scene/resources/3d/primitive_meshes.h" #include "scene/resources/mesh.h" -#include "scene/resources/primitive_meshes.h" #include "tests/test_macros.h" diff --git a/tests/scene/test_audio_stream_wav.h b/tests/scene/test_audio_stream_wav.h index e36f049136..ed1697929e 100644 --- a/tests/scene/test_audio_stream_wav.h +++ b/tests/scene/test_audio_stream_wav.h @@ -148,7 +148,7 @@ void run_test(String file_name, AudioStreamWAV::Format data_format, bool stereo, Ref<FileAccess> wav_file = FileAccess::open(save_path, FileAccess::READ, &error); REQUIRE(error == OK); -#if TOOLS_ENABLED +#ifdef TOOLS_ENABLED // The WAV importer can be used if enabled to check that the saved file is valid. Ref<ResourceImporterWAV> wav_importer = memnew(ResourceImporterWAV); diff --git a/tests/scene/test_camera_3d.h b/tests/scene/test_camera_3d.h new file mode 100644 index 0000000000..830c667257 --- /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 depth_near = 0.2f; + constexpr float depth_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(depth_near); + CHECK(test_camera->get_near() == depth_near); + test_camera->set_far(depth_far); + CHECK(test_camera->get_far() == depth_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 c3a374b5cd..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") { diff --git a/tests/scene/test_curve_2d.h b/tests/scene/test_curve_2d.h index fc141f3d09..099f6fefa9 100644 --- a/tests/scene/test_curve_2d.h +++ b/tests/scene/test_curve_2d.h @@ -155,17 +155,37 @@ TEST_CASE("[Curve2D] Sampling") { SUBCASE("sample_baked_with_rotation") { const real_t pi = 3.14159; - Transform2D t = curve->sample_baked_with_rotation(curve->get_closest_offset(Vector2(0, 0))); - CHECK(t.get_origin() == Vector2(0, 0)); - CHECK(Math::is_equal_approx(t.get_rotation(), pi)); - - t = curve->sample_baked_with_rotation(curve->get_closest_offset(Vector2(0, 25))); + const real_t half_pi = pi * 0.5; + Ref<Curve2D> rot_curve = memnew(Curve2D); + Transform2D t; + + rot_curve->clear_points(); + rot_curve->add_point(Vector2()); + rot_curve->add_point(Vector2(50, 0)); + t = rot_curve->sample_baked_with_rotation(25); + CHECK(t.get_origin() == Vector2(25, 0)); + CHECK(Math::is_equal_approx(t.get_rotation(), 0)); + + rot_curve->clear_points(); + rot_curve->add_point(Vector2()); + rot_curve->add_point(Vector2(0, 50)); + t = rot_curve->sample_baked_with_rotation(25); CHECK(t.get_origin() == Vector2(0, 25)); - CHECK(Math::is_equal_approx(t.get_rotation(), pi)); + CHECK(Math::is_equal_approx(t.get_rotation(), half_pi)); - t = curve->sample_baked_with_rotation(curve->get_closest_offset(Vector2(0, 50))); - CHECK(t.get_origin() == Vector2(0, 50)); + rot_curve->clear_points(); + rot_curve->add_point(Vector2()); + rot_curve->add_point(Vector2(-50, 0)); + t = rot_curve->sample_baked_with_rotation(25); + CHECK(t.get_origin() == Vector2(-25, 0)); CHECK(Math::is_equal_approx(t.get_rotation(), pi)); + + rot_curve->clear_points(); + rot_curve->add_point(Vector2()); + rot_curve->add_point(Vector2(0, -50)); + t = rot_curve->sample_baked_with_rotation(25); + CHECK(t.get_origin() == Vector2(0, -25)); + CHECK(Math::is_equal_approx(t.get_rotation(), -half_pi)); } SUBCASE("get_closest_point") { diff --git a/tests/scene/test_image_texture.h b/tests/scene/test_image_texture.h new file mode 100644 index 0000000000..c9282165a6 --- /dev/null +++ b/tests/scene/test_image_texture.h @@ -0,0 +1,111 @@ +/**************************************************************************/ +/* test_image_texture.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_IMAGE_TEXTURE_H +#define TEST_IMAGE_TEXTURE_H + +#include "core/io/image.h" +#include "scene/resources/image_texture.h" + +#include "tests/test_macros.h" +#include "tests/test_utils.h" + +namespace TestImageTexture { + +// [SceneTree] in a test case name enables initializing a mock render server, +// which ImageTexture is dependent on. +TEST_CASE("[SceneTree][ImageTexture] constructor") { + Ref<ImageTexture> image_texture = memnew(ImageTexture); + CHECK(image_texture->get_width() == 0); + CHECK(image_texture->get_height() == 0); + CHECK(image_texture->get_format() == 0); + CHECK(image_texture->has_alpha() == false); + CHECK(image_texture->get_image() == Ref<Image>()); +} + +TEST_CASE("[SceneTree][ImageTexture] create_from_image") { + Ref<Image> image = memnew(Image(16, 8, true, Image::FORMAT_RGBA8)); + Ref<ImageTexture> image_texture = ImageTexture::create_from_image(image); + CHECK(image_texture->get_width() == 16); + CHECK(image_texture->get_height() == 8); + CHECK(image_texture->get_format() == Image::FORMAT_RGBA8); + CHECK(image_texture->has_alpha() == true); + CHECK(image_texture->get_rid().is_valid() == true); +} + +TEST_CASE("[SceneTree][ImageTexture] set_image") { + Ref<ImageTexture> image_texture = memnew(ImageTexture); + Ref<Image> image = memnew(Image(8, 4, false, Image::FORMAT_RGB8)); + image_texture->set_image(image); + CHECK(image_texture->get_width() == 8); + CHECK(image_texture->get_height() == 4); + CHECK(image_texture->get_format() == Image::FORMAT_RGB8); + CHECK(image_texture->has_alpha() == false); + CHECK(image_texture->get_width() == image_texture->get_image()->get_width()); + CHECK(image_texture->get_height() == image_texture->get_image()->get_height()); + CHECK(image_texture->get_format() == image_texture->get_image()->get_format()); +} + +TEST_CASE("[SceneTree][ImageTexture] set_size_override") { + Ref<Image> image = memnew(Image(16, 8, false, Image::FORMAT_RGB8)); + Ref<ImageTexture> image_texture = ImageTexture::create_from_image(image); + CHECK(image_texture->get_width() == 16); + CHECK(image_texture->get_height() == 8); + image_texture->set_size_override(Size2i(32, 16)); + CHECK(image_texture->get_width() == 32); + CHECK(image_texture->get_height() == 16); +} + +TEST_CASE("[SceneTree][ImageTexture] is_pixel_opaque") { + Ref<Image> image = memnew(Image(8, 8, false, Image::FORMAT_RGBA8)); + image->set_pixel(0, 0, Color(0.0, 0.0, 0.0, 0.0)); // not opaque + image->set_pixel(0, 1, Color(0.0, 0.0, 0.0, 0.1)); // not opaque + image->set_pixel(0, 2, Color(0.0, 0.0, 0.0, 0.5)); // opaque + image->set_pixel(0, 3, Color(0.0, 0.0, 0.0, 0.9)); // opaque + image->set_pixel(0, 4, Color(0.0, 0.0, 0.0, 1.0)); // opaque + + Ref<ImageTexture> image_texture = ImageTexture::create_from_image(image); + CHECK(image_texture->is_pixel_opaque(0, 0) == false); + CHECK(image_texture->is_pixel_opaque(0, 1) == false); + CHECK(image_texture->is_pixel_opaque(0, 2) == true); + CHECK(image_texture->is_pixel_opaque(0, 3) == true); + CHECK(image_texture->is_pixel_opaque(0, 4) == true); +} + +TEST_CASE("[SceneTree][ImageTexture] set_path") { + Ref<ImageTexture> image_texture = memnew(ImageTexture); + String path = TestUtils::get_data_path("images/icon.png"); + image_texture->set_path(path, true); + CHECK(image_texture->get_path() == path); +} + +} //namespace TestImageTexture + +#endif // TEST_IMAGE_TEXTURE_H diff --git a/tests/scene/test_navigation_region_3d.h b/tests/scene/test_navigation_region_3d.h index 4c5ae34615..372f6dc505 100644 --- a/tests/scene/test_navigation_region_3d.h +++ b/tests/scene/test_navigation_region_3d.h @@ -31,8 +31,10 @@ #ifndef TEST_NAVIGATION_REGION_3D_H #define TEST_NAVIGATION_REGION_3D_H +#include "scene/3d/mesh_instance_3d.h" #include "scene/3d/navigation_region_3d.h" #include "scene/main/window.h" +#include "scene/resources/3d/primitive_meshes.h" #include "tests/test_macros.h" @@ -44,6 +46,44 @@ TEST_SUITE("[Navigation]") { CHECK(region_node->get_region_rid().is_valid()); memdelete(region_node); } + + TEST_CASE("[SceneTree][NavigationRegion3D] Region should bake successfully from valid geometry") { + Node3D *node_3d = memnew(Node3D); + SceneTree::get_singleton()->get_root()->add_child(node_3d); + Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh); + NavigationRegion3D *navigation_region = memnew(NavigationRegion3D); + navigation_region->set_navigation_mesh(navigation_mesh); + node_3d->add_child(navigation_region); + Ref<PlaneMesh> plane_mesh = memnew(PlaneMesh); + plane_mesh->set_size(Size2(10.0, 10.0)); + MeshInstance3D *mesh_instance = memnew(MeshInstance3D); + mesh_instance->set_mesh(plane_mesh); + navigation_region->add_child(mesh_instance); + + CHECK_FALSE(navigation_region->is_baking()); + CHECK_EQ(navigation_mesh->get_polygon_count(), 0); + CHECK_EQ(navigation_mesh->get_vertices().size(), 0); + + SUBCASE("Synchronous bake should have immediate effects") { + navigation_region->bake_navigation_mesh(false); + CHECK_FALSE(navigation_region->is_baking()); + CHECK_NE(navigation_mesh->get_polygon_count(), 0); + CHECK_NE(navigation_mesh->get_vertices().size(), 0); + } + + // Race condition is present in the below subcase, but baking should take many + // orders of magnitude longer than basic checks on the main thread, so it's fine. + SUBCASE("Asynchronous bake should not be immediate") { + navigation_region->bake_navigation_mesh(true); + CHECK(navigation_region->is_baking()); + CHECK_EQ(navigation_mesh->get_polygon_count(), 0); + CHECK_EQ(navigation_mesh->get_vertices().size(), 0); + } + + memdelete(mesh_instance); + memdelete(navigation_region); + memdelete(node_3d); + } } } //namespace TestNavigationRegion3D diff --git a/tests/scene/test_node_2d.h b/tests/scene/test_node_2d.h index 7e93c77e22..8cf6408438 100644 --- a/tests/scene/test_node_2d.h +++ b/tests/scene/test_node_2d.h @@ -32,6 +32,7 @@ #define TEST_NODE_2D_H #include "scene/2d/node_2d.h" +#include "scene/main/window.h" #include "tests/test_macros.h" @@ -56,6 +57,33 @@ TEST_CASE("[SceneTree][Node2D]") { memdelete(test_child); memdelete(test_node); } + + SUBCASE("[Node2D][Global Transform] Global Transform should be correct after inserting node from detached tree into SceneTree.") { // GH-86841 + Node2D *main = memnew(Node2D); + Node2D *outer = memnew(Node2D); + Node2D *inner = memnew(Node2D); + SceneTree::get_singleton()->get_root()->add_child(main); + + main->set_position(Point2(100, 100)); + outer->set_position(Point2(10, 0)); + inner->set_position(Point2(0, 10)); + + outer->add_child(inner); + // `inner` is still detached. + CHECK_EQ(inner->get_global_position(), Point2(10, 10)); + + main->add_child(outer); + // `inner` is in scene tree. + CHECK_EQ(inner->get_global_position(), Point2(110, 110)); + + main->remove_child(outer); + // `inner` is detached again. + CHECK_EQ(inner->get_global_position(), Point2(10, 10)); + + memdelete(inner); + memdelete(outer); + memdelete(main); + } } } // namespace TestNode2D diff --git a/tests/scene/test_packed_scene.h b/tests/scene/test_packed_scene.h index 3517aba31f..1e784c199d 100644 --- a/tests/scene/test_packed_scene.h +++ b/tests/scene/test_packed_scene.h @@ -150,6 +150,71 @@ TEST_CASE("[PackedScene] Instantiate Packed Scene With Children") { memdelete(instance); } +TEST_CASE("[PackedScene] Set Path") { + // Create a scene to pack. + Node *scene = memnew(Node); + scene->set_name("TestScene"); + + // Pack the scene. + PackedScene packed_scene; + packed_scene.pack(scene); + + // Set a new path for the packed scene. + const String new_path = "NewTestPath"; + packed_scene.set_path(new_path); + + // Check if the path has been set correctly. + Ref<SceneState> state = packed_scene.get_state(); + CHECK(state.is_valid()); + CHECK(state->get_path() == new_path); + + memdelete(scene); +} + +TEST_CASE("[PackedScene] Replace State") { + // Create a scene to pack. + Node *scene = memnew(Node); + scene->set_name("TestScene"); + + // Pack the scene. + PackedScene packed_scene; + packed_scene.pack(scene); + + // Create another scene state to replace with. + Ref<SceneState> new_state = memnew(SceneState); + new_state->set_path("NewPath"); + + // Replace the state. + packed_scene.replace_state(new_state); + + // Check if the state has been replaced. + Ref<SceneState> state = packed_scene.get_state(); + CHECK(state.is_valid()); + CHECK(state == new_state); + + memdelete(scene); +} + +TEST_CASE("[PackedScene] Recreate State") { + // Create a scene to pack. + Node *scene = memnew(Node); + scene->set_name("TestScene"); + + // Pack the scene. + PackedScene packed_scene; + packed_scene.pack(scene); + + // Recreate the state. + packed_scene.recreate_state(); + + // Check if the state has been recreated. + Ref<SceneState> state = packed_scene.get_state(); + CHECK(state.is_valid()); + CHECK(state->get_node_count() == 0); // Since the state was recreated, it should be empty. + + memdelete(scene); +} + } // namespace TestPackedScene #endif // TEST_PACKED_SCENE_H diff --git a/tests/scene/test_primitives.h b/tests/scene/test_primitives.h index 552f722d24..f105e1ac04 100644 --- a/tests/scene/test_primitives.h +++ b/tests/scene/test_primitives.h @@ -31,7 +31,7 @@ #ifndef TEST_PRIMITIVES_H #define TEST_PRIMITIVES_H -#include "scene/resources/primitive_meshes.h" +#include "scene/resources/3d/primitive_meshes.h" #include "tests/test_macros.h" @@ -68,8 +68,10 @@ TEST_CASE("[SceneTree][Primitive][Capsule] Capsule Primitive") { } SUBCASE("[SceneTree][Primitive][Capsule] If set segments negative, default to at least 0") { + ERR_PRINT_OFF; capsule->set_radial_segments(-5); capsule->set_rings(-17); + ERR_PRINT_ON; CHECK_MESSAGE(capsule->get_radial_segments() >= 0, "Ensure number of radial segments is >= 0."); @@ -165,9 +167,11 @@ TEST_CASE("[SceneTree][Primitive][Box] Box Primitive") { } SUBCASE("[SceneTree][Primitive][Box] Set subdivides to negative and ensure they are >= 0") { + ERR_PRINT_OFF; box->set_subdivide_width(-2); box->set_subdivide_height(-2); box->set_subdivide_depth(-2); + ERR_PRINT_ON; CHECK(box->get_subdivide_width() >= 0); CHECK(box->get_subdivide_height() >= 0); @@ -254,8 +258,10 @@ TEST_CASE("[SceneTree][Primitive][Cylinder] Cylinder Primitive") { } SUBCASE("[SceneTree][Primitive][Cylinder] Ensure num segments is >= 0") { + ERR_PRINT_OFF; cylinder->set_radial_segments(-12); cylinder->set_rings(-16); + ERR_PRINT_ON; CHECK(cylinder->get_radial_segments() >= 0); CHECK(cylinder->get_rings() >= 0); @@ -441,8 +447,10 @@ TEST_CASE("[SceneTree][Primitive][Plane] Plane Primitive") { } SUBCASE("[SceneTree][Primitive][Plane] Ensure number of segments is >= 0.") { + ERR_PRINT_OFF; plane->set_subdivide_width(-15); plane->set_subdivide_depth(-29); + ERR_PRINT_ON; CHECK(plane->get_subdivide_width() >= 0); CHECK(plane->get_subdivide_depth() >= 0); @@ -486,9 +494,11 @@ TEST_CASE("[SceneTree][Primitive][Prism] Prism Primitive") { } SUBCASE("[Primitive][Prism] Ensure number of segments always >= 0") { + ERR_PRINT_OFF; prism->set_subdivide_width(-36); prism->set_subdivide_height(-5); prism->set_subdivide_depth(-64); + ERR_PRINT_ON; CHECK(prism->get_subdivide_width() >= 0); CHECK(prism->get_subdivide_height() >= 0); @@ -521,8 +531,10 @@ TEST_CASE("[SceneTree][Primitive][Sphere] Sphere Primitive") { } SUBCASE("[Primitive][Sphere] Ensure number of segments always >= 0") { + ERR_PRINT_OFF; sphere->set_radial_segments(-36); sphere->set_rings(-5); + ERR_PRINT_ON; CHECK(sphere->get_radial_segments() >= 0); CHECK(sphere->get_rings() >= 0); diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index e81578a862..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); } diff --git a/tests/scene/test_viewport.h b/tests/scene/test_viewport.h index 1afae66ee0..66b0f438cc 100644 --- a/tests/scene/test_viewport.h +++ b/tests/scene/test_viewport.h @@ -31,13 +31,13 @@ #ifndef TEST_VIEWPORT_H #define TEST_VIEWPORT_H -#include "scene/2d/area_2d.h" -#include "scene/2d/collision_shape_2d.h" +#include "scene/2d/physics/area_2d.h" +#include "scene/2d/physics/collision_shape_2d.h" #include "scene/gui/control.h" #include "scene/gui/subviewport_container.h" #include "scene/main/canvas_layer.h" #include "scene/main/window.h" -#include "scene/resources/rectangle_shape_2d.h" +#include "scene/resources/2d/rectangle_shape_2d.h" #include "tests/test_macros.h" diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h index 5ab2975b74..b5547f2c94 100644 --- a/tests/servers/test_navigation_server_3d.h +++ b/tests/servers/test_navigation_server_3d.h @@ -32,7 +32,7 @@ #define TEST_NAVIGATION_SERVER_3D_H #include "scene/3d/mesh_instance_3d.h" -#include "scene/resources/primitive_meshes.h" +#include "scene/resources/3d/primitive_meshes.h" #include "servers/navigation_server_3d.h" #include "tests/test_macros.h" @@ -720,6 +720,24 @@ TEST_SUITE("[Navigation]") { navigation_server->free(map); navigation_server->process(0.0); // Give server some cycles to commit. } + + TEST_CASE("[NavigationServer3D] Server should be able to bake asynchronously") { + NavigationServer3D *navigation_server = NavigationServer3D::get_singleton(); + Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh); + Ref<NavigationMeshSourceGeometryData3D> source_geometry = memnew(NavigationMeshSourceGeometryData3D); + + Array arr; + arr.resize(RS::ARRAY_MAX); + BoxMesh::create_mesh_array(arr, Vector3(10.0, 0.001, 10.0)); + source_geometry->add_mesh_array(arr, Transform3D()); + + // Race condition is present below, but baking should take many orders of magnitude + // longer than basic checks on the main thread, so it's fine. + navigation_server->bake_from_source_geometry_data_async(navigation_mesh, source_geometry, Callable()); + CHECK(navigation_server->is_baking_navigation_mesh(navigation_mesh)); + CHECK_EQ(navigation_mesh->get_polygon_count(), 0); + CHECK_EQ(navigation_mesh->get_vertices().size(), 0); + } } } //namespace TestNavigationServer3D diff --git a/tests/servers/test_text_server.h b/tests/servers/test_text_server.h index 0f23929e1e..334c642d26 100644 --- a/tests/servers/test_text_server.h +++ b/tests/servers/test_text_server.h @@ -33,7 +33,7 @@ #ifdef TOOLS_ENABLED -#include "editor/builtin_fonts.gen.h" +#include "editor/themes/builtin_fonts.gen.h" #include "servers/text_server.h" #include "tests/test_macros.h" diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 8c120f6d3a..f40f562973 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -30,6 +30,11 @@ #include "test_main.h" +#ifdef TOOLS_ENABLED +#include "editor/editor_paths.h" +#include "editor/editor_settings.h" +#endif // TOOLS_ENABLED + #include "tests/core/config/test_project_settings.h" #include "tests/core/input/test_input_event.h" #include "tests/core/input/test_input_event_key.h" @@ -101,18 +106,11 @@ #include "tests/scene/test_curve_2d.h" #include "tests/scene/test_curve_3d.h" #include "tests/scene/test_gradient.h" -#include "tests/scene/test_navigation_agent_2d.h" -#include "tests/scene/test_navigation_agent_3d.h" -#include "tests/scene/test_navigation_obstacle_2d.h" -#include "tests/scene/test_navigation_obstacle_3d.h" -#include "tests/scene/test_navigation_region_2d.h" -#include "tests/scene/test_navigation_region_3d.h" +#include "tests/scene/test_image_texture.h" #include "tests/scene/test_node.h" #include "tests/scene/test_node_2d.h" #include "tests/scene/test_packed_scene.h" #include "tests/scene/test_path_2d.h" -#include "tests/scene/test_path_3d.h" -#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" @@ -120,21 +118,37 @@ #include "tests/scene/test_visual_shader.h" #include "tests/scene/test_window.h" #include "tests/servers/rendering/test_shader_preprocessor.h" -#include "tests/servers/test_navigation_server_2d.h" -#include "tests/servers/test_navigation_server_3d.h" #include "tests/servers/test_text_server.h" #include "tests/test_validate_testing.h" +#ifndef _3D_DISABLED +#include "tests/scene/test_camera_3d.h" +#include "tests/scene/test_navigation_agent_2d.h" +#include "tests/scene/test_navigation_agent_3d.h" +#include "tests/scene/test_navigation_obstacle_2d.h" +#include "tests/scene/test_navigation_obstacle_3d.h" +#include "tests/scene/test_navigation_region_2d.h" +#include "tests/scene/test_navigation_region_3d.h" +#include "tests/scene/test_path_3d.h" +#include "tests/scene/test_primitives.h" +#include "tests/servers/test_navigation_server_2d.h" +#include "tests/servers/test_navigation_server_3d.h" +#endif // _3D_DISABLED + #include "modules/modules_tests.gen.h" #include "tests/display_server_mock.h" #include "tests/test_macros.h" #include "scene/theme/theme_db.h" +#ifndef _3D_DISABLED #include "servers/navigation_server_2d.h" #include "servers/navigation_server_3d.h" +#endif // _3D_DISABLED #include "servers/physics_server_2d.h" +#ifndef _3D_DISABLED #include "servers/physics_server_3d.h" +#endif // _3D_DISABLED #include "servers/rendering/rendering_server_default.h" int test_main(int argc, char *argv[]) { @@ -209,10 +223,12 @@ struct GodotTestCaseListener : public doctest::IReporter { SignalWatcher *signal_watcher = nullptr; - PhysicsServer3D *physics_server_3d = nullptr; PhysicsServer2D *physics_server_2d = nullptr; +#ifndef _3D_DISABLED + PhysicsServer3D *physics_server_3d = nullptr; NavigationServer3D *navigation_server_3d = nullptr; NavigationServer2D *navigation_server_2d = nullptr; +#endif // _3D_DISABLED void test_case_start(const doctest::TestCaseData &p_in) override { reinitialize(); @@ -220,7 +236,7 @@ struct GodotTestCaseListener : public doctest::IReporter { String name = String(p_in.m_name); String suite_name = String(p_in.m_test_suite); - if (name.find("[SceneTree]") != -1) { + if (name.find("[SceneTree]") != -1 || name.find("[Editor]") != -1) { memnew(MessageQueue); memnew(Input); @@ -244,16 +260,20 @@ struct GodotTestCaseListener : public doctest::IReporter { ThemeDB::get_singleton()->finalize_theme(); ThemeDB::get_singleton()->initialize_theme_noproject(); +#ifndef _3D_DISABLED physics_server_3d = PhysicsServer3DManager::get_singleton()->new_default_server(); physics_server_3d->init(); +#endif // _3D_DISABLED physics_server_2d = PhysicsServer2DManager::get_singleton()->new_default_server(); physics_server_2d->init(); +#ifndef _3D_DISABLED ERR_PRINT_OFF; navigation_server_3d = NavigationServer3DManager::new_default_server(); navigation_server_2d = NavigationServer2DManager::new_default_server(); ERR_PRINT_ON; +#endif // _3D_DISABLED memnew(InputMap); InputMap::get_singleton()->load_default(); @@ -263,6 +283,15 @@ struct GodotTestCaseListener : public doctest::IReporter { if (!DisplayServer::get_singleton()->has_feature(DisplayServer::Feature::FEATURE_SUBWINDOWS)) { SceneTree::get_singleton()->get_root()->set_embedding_subwindows(true); } + +#ifdef TOOLS_ENABLED + if (name.find("[Editor]") != -1) { + Engine::get_singleton()->set_editor_hint(true); + EditorPaths::create(); + EditorSettings::create(); + } +#endif // TOOLS_ENABLED + return; } @@ -275,6 +304,7 @@ struct GodotTestCaseListener : public doctest::IReporter { return; } +#ifndef _3D_DISABLED if (suite_name.find("[Navigation]") != -1 && navigation_server_2d == nullptr && navigation_server_3d == nullptr) { ERR_PRINT_OFF; navigation_server_3d = NavigationServer3DManager::new_default_server(); @@ -282,9 +312,18 @@ struct GodotTestCaseListener : public doctest::IReporter { ERR_PRINT_ON; return; } +#endif // _3D_DISABLED } void test_case_end(const doctest::CurrentTestCaseStats &) override { +#ifdef TOOLS_ENABLED + if (EditorSettings::get_singleton()) { + EditorSettings::destroy(); + } +#endif // TOOLS_ENABLED + + Engine::get_singleton()->set_editor_hint(false); + if (SceneTree::get_singleton()) { SceneTree::get_singleton()->finalize(); } @@ -297,6 +336,7 @@ struct GodotTestCaseListener : public doctest::IReporter { memdelete(SceneTree::get_singleton()); } +#ifndef _3D_DISABLED if (navigation_server_3d) { memdelete(navigation_server_3d); navigation_server_3d = nullptr; @@ -306,12 +346,15 @@ struct GodotTestCaseListener : public doctest::IReporter { memdelete(navigation_server_2d); navigation_server_2d = nullptr; } +#endif // _3D_DISABLED +#ifndef _3D_DISABLED if (physics_server_3d) { physics_server_3d->finish(); memdelete(physics_server_3d); physics_server_3d = nullptr; } +#endif // _3D_DISABLED if (physics_server_2d) { physics_server_2d->finish(); |