summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/core/config/test_project_settings.h3
-rw-r--r--tests/core/input/test_input_event_key.h56
-rw-r--r--tests/core/io/test_image.h29
-rw-r--r--tests/core/math/test_aabb.h9
-rw-r--r--tests/core/math/test_math_funcs.h23
-rw-r--r--tests/core/math/test_vector2i.h6
-rw-r--r--tests/core/math/test_vector3i.h6
-rw-r--r--tests/core/math/test_vector4i.h6
-rw-r--r--tests/core/os/test_os.h24
-rw-r--r--tests/core/string/test_node_path.h22
-rw-r--r--tests/core/string/test_string.h65
-rw-r--r--tests/core/string/test_translation.h2
-rw-r--r--tests/core/test_crypto.h12
-rw-r--r--tests/core/variant/test_array.h2
-rw-r--r--tests/scene/test_arraymesh.h2
-rw-r--r--tests/scene/test_audio_stream_wav.h2
-rw-r--r--tests/scene/test_camera_3d.h370
-rw-r--r--tests/scene/test_code_edit.h26
-rw-r--r--tests/scene/test_curve_2d.h36
-rw-r--r--tests/scene/test_image_texture.h111
-rw-r--r--tests/scene/test_navigation_region_3d.h40
-rw-r--r--tests/scene/test_node_2d.h28
-rw-r--r--tests/scene/test_packed_scene.h65
-rw-r--r--tests/scene/test_primitives.h14
-rw-r--r--tests/scene/test_text_edit.h54
-rw-r--r--tests/scene/test_viewport.h6
-rw-r--r--tests/servers/test_navigation_server_3d.h20
-rw-r--r--tests/servers/test_text_server.h2
-rw-r--r--tests/test_main.cpp67
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();