diff options
Diffstat (limited to 'tests/core')
49 files changed, 2874 insertions, 1594 deletions
diff --git a/tests/core/io/test_config_file.h b/tests/core/io/test_config_file.h index f6fbaf9a88..355aca479e 100644 --- a/tests/core/io/test_config_file.h +++ b/tests/core/io/test_config_file.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -155,7 +155,7 @@ antiAliasing=false "a=b"=7 )"); - FileAccessRef file = FileAccess::open(config_path, FileAccess::READ); + Ref<FileAccess> file = FileAccess::open(config_path, FileAccess::READ); CHECK_MESSAGE(file->get_as_utf8_string() == contents, "The saved configuration file should match the expected format."); } diff --git a/tests/core/io/test_file_access.h b/tests/core/io/test_file_access.h index f566899c9b..f0e1cceacf 100644 --- a/tests/core/io/test_file_access.h +++ b/tests/core/io/test_file_access.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,7 +38,7 @@ namespace TestFileAccess { TEST_CASE("[FileAccess] CSV read") { - FileAccessRef f = FileAccess::open(TestUtils::get_data_path("translations.csv"), FileAccess::READ); + Ref<FileAccess> f = FileAccess::open(TestUtils::get_data_path("translations.csv"), FileAccess::READ); Vector<String> header = f->get_csv_line(); // Default delimiter: ",". REQUIRE(header.size() == 3); @@ -77,8 +77,6 @@ TEST_CASE("[FileAccess] CSV read") { CHECK(row5[0] == "What about"); CHECK(row5[1] == "tab separated"); CHECK(row5[2] == "lines, good?"); - - f->close(); } } // namespace TestFileAccess diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h index 643d2f31ec..36e6b83bfd 100644 --- a/tests/core/io/test_image.h +++ b/tests/core/io/test_image.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -106,7 +106,7 @@ TEST_CASE("[Image] Saving and loading") { // Load BMP Ref<Image> image_bmp = memnew(Image()); - FileAccessRef f_bmp = FileAccess::open(TestUtils::get_data_path("images/icon.bmp"), FileAccess::READ, &err); + Ref<FileAccess> f_bmp = FileAccess::open(TestUtils::get_data_path("images/icon.bmp"), FileAccess::READ, &err); PackedByteArray data_bmp; data_bmp.resize(f_bmp->get_length() + 1); f_bmp->get_buffer(data_bmp.ptrw(), f_bmp->get_length()); @@ -116,7 +116,7 @@ TEST_CASE("[Image] Saving and loading") { // Load JPG Ref<Image> image_jpg = memnew(Image()); - FileAccessRef f_jpg = FileAccess::open(TestUtils::get_data_path("images/icon.jpg"), FileAccess::READ, &err); + Ref<FileAccess> f_jpg = FileAccess::open(TestUtils::get_data_path("images/icon.jpg"), FileAccess::READ, &err); PackedByteArray data_jpg; data_jpg.resize(f_jpg->get_length() + 1); f_jpg->get_buffer(data_jpg.ptrw(), f_jpg->get_length()); @@ -126,7 +126,7 @@ TEST_CASE("[Image] Saving and loading") { // Load WEBP Ref<Image> image_webp = memnew(Image()); - FileAccessRef f_webp = FileAccess::open(TestUtils::get_data_path("images/icon.webp"), FileAccess::READ, &err); + Ref<FileAccess> f_webp = FileAccess::open(TestUtils::get_data_path("images/icon.webp"), FileAccess::READ, &err); PackedByteArray data_webp; data_webp.resize(f_webp->get_length() + 1); f_webp->get_buffer(data_webp.ptrw(), f_webp->get_length()); @@ -136,7 +136,7 @@ TEST_CASE("[Image] Saving and loading") { // Load PNG Ref<Image> image_png = memnew(Image()); - FileAccessRef f_png = FileAccess::open(TestUtils::get_data_path("images/icon.png"), FileAccess::READ, &err); + Ref<FileAccess> f_png = FileAccess::open(TestUtils::get_data_path("images/icon.png"), FileAccess::READ, &err); PackedByteArray data_png; data_png.resize(f_png->get_length() + 1); f_png->get_buffer(data_png.ptrw(), f_png->get_length()); @@ -146,7 +146,7 @@ TEST_CASE("[Image] Saving and loading") { // Load TGA Ref<Image> image_tga = memnew(Image()); - FileAccessRef f_tga = FileAccess::open(TestUtils::get_data_path("images/icon.tga"), FileAccess::READ, &err); + Ref<FileAccess> f_tga = FileAccess::open(TestUtils::get_data_path("images/icon.tga"), FileAccess::READ, &err); PackedByteArray data_tga; data_tga.resize(f_tga->get_length() + 1); f_tga->get_buffer(data_tga.ptrw(), f_tga->get_length()); @@ -161,8 +161,8 @@ TEST_CASE("[Image] Basic getters") { CHECK(image->get_height() == 4); CHECK(image->get_size() == Vector2(8, 4)); CHECK(image->get_format() == Image::FORMAT_LA8); - CHECK(image->get_used_rect() == Rect2(0, 0, 0, 0)); - Ref<Image> image_get_rect = image->get_rect(Rect2(0, 0, 2, 1)); + CHECK(image->get_used_rect() == Rect2i(0, 0, 0, 0)); + Ref<Image> image_get_rect = image->get_rect(Rect2i(0, 0, 2, 1)); CHECK(image_get_rect->get_size() == Vector2(2, 1)); } @@ -213,8 +213,8 @@ TEST_CASE("[Image] Modifying pixels of an image") { image->get_pixelv(Vector2(0, 0)).is_equal_approx(Color(1, 1, 1, 1)), "Image's get_pixel() should return the same color value as the one being set with set_pixel() in the same position."); CHECK_MESSAGE( - image->get_used_rect() == Rect2(0, 0, 1, 1), - "Image's get_used_rect should return the expected value, larger than Rect2(0, 0, 0, 0) if it's visible."); + image->get_used_rect() == Rect2i(0, 0, 1, 1), + "Image's get_used_rect should return the expected value, larger than Rect2i(0, 0, 0, 0) if it's visible."); image->set_pixelv(Vector2(0, 0), Color(0.5, 0.5, 0.5, 0.5)); Ref<Image> image2 = memnew(Image(3, 3, false, Image::FORMAT_RGBA8)); @@ -233,19 +233,19 @@ TEST_CASE("[Image] Modifying pixels of an image") { { const int img_width = 3; const int img_height = 3; - Vector<Rect2> rects; - rects.push_back(Rect2()); - rects.push_back(Rect2(-5, -5, 3, 3)); - rects.push_back(Rect2(img_width, 0, 12, 12)); - rects.push_back(Rect2(0, img_height, 12, 12)); - rects.push_back(Rect2(img_width + 1, img_height + 2, 12, 12)); - rects.push_back(Rect2(1, 1, 1, 1)); - rects.push_back(Rect2(0, 1, 2, 3)); - rects.push_back(Rect2(-5, 0, img_width + 10, 2)); - rects.push_back(Rect2(0, -5, 2, img_height + 10)); - rects.push_back(Rect2(-1, -1, img_width + 1, img_height + 1)); - - for (const Rect2 &rect : rects) { + Vector<Rect2i> rects; + rects.push_back(Rect2i()); + rects.push_back(Rect2i(-5, -5, 3, 3)); + rects.push_back(Rect2i(img_width, 0, 12, 12)); + rects.push_back(Rect2i(0, img_height, 12, 12)); + rects.push_back(Rect2i(img_width + 1, img_height + 2, 12, 12)); + rects.push_back(Rect2i(1, 1, 1, 1)); + rects.push_back(Rect2i(0, 1, 2, 3)); + rects.push_back(Rect2i(-5, 0, img_width + 10, 2)); + rects.push_back(Rect2i(0, -5, 2, img_height + 10)); + rects.push_back(Rect2i(-1, -1, img_width + 1, img_height + 1)); + + for (const Rect2i &rect : rects) { Ref<Image> img = memnew(Image(img_width, img_height, false, Image::FORMAT_RGBA8)); CHECK_NOTHROW_MESSAGE( img->fill_rect(rect, Color(1, 1, 1, 1)), @@ -267,7 +267,7 @@ TEST_CASE("[Image] Modifying pixels of an image") { } // Blend two images together - image->blend_rect(image2, Rect2(Vector2(0, 0), image2->get_size()), Vector2(0, 0)); + image->blend_rect(image2, Rect2i(Vector2i(0, 0), image2->get_size()), Vector2i(0, 0)); CHECK_MESSAGE( image->get_pixel(0, 0).a > 0.7, "blend_rect() should blend the alpha values of the two images."); @@ -279,7 +279,7 @@ TEST_CASE("[Image] Modifying pixels of an image") { image3->set_pixel(0, 0, Color(0, 1, 0, 1)); //blit_rect() two images together - image->blit_rect(image3, Rect2(Vector2(0, 0), image3->get_size()), Vector2(0, 0)); + image->blit_rect(image3, Rect2i(Vector2i(0, 0), image3->get_size()), Vector2i(0, 0)); CHECK_MESSAGE( image->get_pixel(0, 0).is_equal_approx(Color(0, 1, 0, 1)), "blit_rect() should replace old colors and not blend them."); @@ -300,4 +300,5 @@ TEST_CASE("[Image] Modifying pixels of an image") { "flip_y() should not leave old pixels behind."); } } // namespace TestImage + #endif // TEST_IMAGE_H diff --git a/tests/core/io/test_json.h b/tests/core/io/test_json.h index 3af58dfa1c..478cf1766e 100644 --- a/tests/core/io/test_json.h +++ b/tests/core/io/test_json.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tests/core/io/test_marshalls.h b/tests/core/io/test_marshalls.h index 6bd916164e..546a2e9358 100644 --- a/tests/core/io/test_marshalls.h +++ b/tests/core/io/test_marshalls.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tests/core/io/test_pck_packer.h b/tests/core/io/test_pck_packer.h index 75a4abffbe..d21fbdaf50 100644 --- a/tests/core/io/test_pck_packer.h +++ b/tests/core/io/test_pck_packer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -52,7 +52,7 @@ TEST_CASE("[PCKPacker] Pack an empty PCK file") { "Flushing the PCK should return an OK error code."); Error err; - FileAccessRef f = FileAccess::open(output_pck_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(output_pck_path, FileAccess::READ, &err); CHECK_MESSAGE( err == OK, "The generated empty PCK file should be opened successfully."); @@ -106,7 +106,7 @@ TEST_CASE("[PCKPacker] Pack a PCK file with some files and directories") { "Flushing the PCK should return an OK error code."); Error err; - FileAccessRef f = FileAccess::open(output_pck_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(output_pck_path, FileAccess::READ, &err); CHECK_MESSAGE( err == OK, "The generated non-empty PCK file should be opened successfully."); diff --git a/tests/core/io/test_resource.h b/tests/core/io/test_resource.h index cee3281995..c880ca7d2a 100644 --- a/tests/core/io/test_resource.h +++ b/tests/core/io/test_resource.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef TEST_RESOURCE -#define TEST_RESOURCE +#ifndef TEST_RESOURCE_H +#define TEST_RESOURCE_H #include "core/io/resource.h" #include "core/io/resource_loader.h" @@ -69,22 +69,22 @@ TEST_CASE("[Resource] Duplication") { TEST_CASE("[Resource] Saving and loading") { Ref<Resource> resource = memnew(Resource); resource->set_name("Hello world"); - resource->set_meta(" ExampleMetadata ", Vector2i(40, 80)); + resource->set_meta("ExampleMetadata", Vector2i(40, 80)); resource->set_meta("string", "The\nstring\nwith\nunnecessary\nline\n\t\\\nbreaks"); Ref<Resource> child_resource = memnew(Resource); child_resource->set_name("I'm a child resource"); resource->set_meta("other_resource", child_resource); const String save_path_binary = OS::get_singleton()->get_cache_path().plus_file("resource.res"); const String save_path_text = OS::get_singleton()->get_cache_path().plus_file("resource.tres"); - ResourceSaver::save(save_path_binary, resource); - ResourceSaver::save(save_path_text, resource); + ResourceSaver::save(resource, save_path_binary); + ResourceSaver::save(resource, save_path_text); const Ref<Resource> &loaded_resource_binary = ResourceLoader::load(save_path_binary); CHECK_MESSAGE( loaded_resource_binary->get_name() == "Hello world", "The loaded resource name should be equal to the expected value."); CHECK_MESSAGE( - loaded_resource_binary->get_meta(" ExampleMetadata ") == Vector2i(40, 80), + loaded_resource_binary->get_meta("ExampleMetadata") == Vector2i(40, 80), "The loaded resource metadata should be equal to the expected value."); CHECK_MESSAGE( loaded_resource_binary->get_meta("string") == "The\nstring\nwith\nunnecessary\nline\n\t\\\nbreaks", @@ -99,7 +99,7 @@ TEST_CASE("[Resource] Saving and loading") { loaded_resource_text->get_name() == "Hello world", "The loaded resource name should be equal to the expected value."); CHECK_MESSAGE( - loaded_resource_text->get_meta(" ExampleMetadata ") == Vector2i(40, 80), + loaded_resource_text->get_meta("ExampleMetadata") == Vector2i(40, 80), "The loaded resource metadata should be equal to the expected value."); CHECK_MESSAGE( loaded_resource_text->get_meta("string") == "The\nstring\nwith\nunnecessary\nline\n\t\\\nbreaks", @@ -111,4 +111,4 @@ TEST_CASE("[Resource] Saving and loading") { } } // namespace TestResource -#endif // TEST_RESOURCE +#endif // TEST_RESOURCE_H diff --git a/tests/core/io/test_xml_parser.h b/tests/core/io/test_xml_parser.h index 2d00f29ddf..87592b56ce 100644 --- a/tests/core/io/test_xml_parser.h +++ b/tests/core/io/test_xml_parser.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tests/core/math/test_aabb.h b/tests/core/math/test_aabb.h index f5076ce1ed..526972a82f 100644 --- a/tests/core/math/test_aabb.h +++ b/tests/core/math/test_aabb.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tests/core/math/test_astar.h b/tests/core/math/test_astar.h index 2c183374ac..9f5e98ef94 100644 --- a/tests/core/math/test_astar.h +++ b/tests/core/math/test_astar.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,7 +37,7 @@ namespace TestAStar { -class ABCX : public AStar { +class ABCX : public AStar3D { public: enum { A, @@ -58,7 +58,7 @@ public: } // Disable heuristic completely. - real_t _compute_cost(int p_from, int p_to) { + real_t _compute_cost(int64_t p_from, int64_t p_to) { if (p_from == A && p_to == C) { return 1000; } @@ -66,18 +66,18 @@ public: } }; -TEST_CASE("[AStar] ABC path") { +TEST_CASE("[AStar3D] ABC path") { ABCX abcx; - Vector<int> path = abcx.get_id_path(ABCX::A, ABCX::C); + Vector<int64_t> path = abcx.get_id_path(ABCX::A, ABCX::C); REQUIRE(path.size() == 3); CHECK(path[0] == ABCX::A); CHECK(path[1] == ABCX::B); CHECK(path[2] == ABCX::C); } -TEST_CASE("[AStar] ABCX path") { +TEST_CASE("[AStar3D] ABCX path") { ABCX abcx; - Vector<int> path = abcx.get_id_path(ABCX::X, ABCX::C); + Vector<int64_t> path = abcx.get_id_path(ABCX::X, ABCX::C); REQUIRE(path.size() == 4); CHECK(path[0] == ABCX::X); CHECK(path[1] == ABCX::A); @@ -85,8 +85,8 @@ TEST_CASE("[AStar] ABCX path") { CHECK(path[3] == ABCX::C); } -TEST_CASE("[AStar] Add/Remove") { - AStar a; +TEST_CASE("[AStar3D] Add/Remove") { + AStar3D a; // Manual tests. a.add_point(1, Vector3(0, 0, 0)); @@ -213,13 +213,13 @@ TEST_CASE("[AStar] Add/Remove") { // It's been great work, cheers. \(^ ^)/ } -TEST_CASE("[Stress][AStar] Find paths") { +TEST_CASE("[Stress][AStar3D] Find paths") { // Random stress tests with Floyd-Warshall. const int N = 30; Math::seed(0); for (int test = 0; test < 1000; test++) { - AStar a; + AStar3D a; Vector3 p[N]; bool adj[N][N] = { { false } }; @@ -318,7 +318,7 @@ TEST_CASE("[Stress][AStar] Find paths") { for (int u = 0; u < N; u++) { for (int v = 0; v < N; v++) { if (u != v) { - Vector<int> route = a.get_id_path(u, v); + Vector<int64_t> route = a.get_id_path(u, v); if (!Math::is_inf(d[u][v])) { // Reachable. if (route.size() == 0) { diff --git a/tests/core/math/test_basis.h b/tests/core/math/test_basis.h index 500c069a33..ae8ca4acde 100644 --- a/tests/core/math/test_basis.h +++ b/tests/core/math/test_basis.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -164,9 +164,9 @@ void test_rotation(Vector3 deg_original_euler, RotOrder rot_order) { Basis res = to_rotation.inverse() * rotation_from_computed_euler; - CHECK_MESSAGE((res.get_axis(0) - Vector3(1.0, 0.0, 0.0)).length() <= 0.1, vformat("Fail due to X %s\n", String(res.get_axis(0))).utf8().ptr()); - CHECK_MESSAGE((res.get_axis(1) - Vector3(0.0, 1.0, 0.0)).length() <= 0.1, vformat("Fail due to Y %s\n", String(res.get_axis(1))).utf8().ptr()); - CHECK_MESSAGE((res.get_axis(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.1, vformat("Fail due to Z %s\n", String(res.get_axis(2))).utf8().ptr()); + CHECK_MESSAGE((res.get_column(0) - Vector3(1.0, 0.0, 0.0)).length() <= 0.1, vformat("Fail due to X %s\n", String(res.get_column(0))).utf8().ptr()); + CHECK_MESSAGE((res.get_column(1) - Vector3(0.0, 1.0, 0.0)).length() <= 0.1, vformat("Fail due to Y %s\n", String(res.get_column(1))).utf8().ptr()); + CHECK_MESSAGE((res.get_column(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.1, vformat("Fail due to Z %s\n", String(res.get_column(2))).utf8().ptr()); // Double check `to_rotation` decomposing with XYZ rotation order. const Vector3 euler_xyz_from_rotation = to_rotation.get_euler(Basis::EULER_ORDER_XYZ); @@ -175,9 +175,9 @@ void test_rotation(Vector3 deg_original_euler, RotOrder rot_order) { res = to_rotation.inverse() * rotation_from_xyz_computed_euler; - CHECK_MESSAGE((res.get_axis(0) - Vector3(1.0, 0.0, 0.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to X %s\n", String(res.get_axis(0))).utf8().ptr()); - CHECK_MESSAGE((res.get_axis(1) - Vector3(0.0, 1.0, 0.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to Y %s\n", String(res.get_axis(1))).utf8().ptr()); - CHECK_MESSAGE((res.get_axis(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to Z %s\n", String(res.get_axis(2))).utf8().ptr()); + CHECK_MESSAGE((res.get_column(0) - Vector3(1.0, 0.0, 0.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to X %s\n", String(res.get_column(0))).utf8().ptr()); + CHECK_MESSAGE((res.get_column(1) - Vector3(0.0, 1.0, 0.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to Y %s\n", String(res.get_column(1))).utf8().ptr()); + CHECK_MESSAGE((res.get_column(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to Z %s\n", String(res.get_column(2))).utf8().ptr()); INFO(vformat("Rotation order: %s\n.", get_rot_order_name(rot_order)).utf8().ptr()); INFO(vformat("Original Rotation: %s\n", String(deg_original_euler)).utf8().ptr()); @@ -283,4 +283,4 @@ TEST_CASE("[Stress][Basis] Euler conversions") { } } // namespace TestBasis -#endif +#endif // TEST_BASIS_H diff --git a/tests/core/math/test_color.h b/tests/core/math/test_color.h index 82cf786f7a..51c3bc8bdc 100644 --- a/tests/core/math/test_color.h +++ b/tests/core/math/test_color.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -144,17 +144,35 @@ TEST_CASE("[Color] Conversion methods") { "The string representation should match the expected value."); } +TEST_CASE("[Color] Linear <-> sRGB conversion") { + const Color color = Color(0.35, 0.5, 0.6, 0.7); + const Color color_linear = color.srgb_to_linear(); + const Color color_srgb = color.linear_to_srgb(); + CHECK_MESSAGE( + color_linear.is_equal_approx(Color(0.100481, 0.214041, 0.318547, 0.7)), + "The color converted to linear color space should match the expected value."); + CHECK_MESSAGE( + color_srgb.is_equal_approx(Color(0.62621, 0.735357, 0.797738, 0.7)), + "The color converted to sRGB color space should match the expected value."); + CHECK_MESSAGE( + color_linear.linear_to_srgb().is_equal_approx(Color(0.35, 0.5, 0.6, 0.7)), + "The linear color converted back to sRGB color space should match the expected value."); + CHECK_MESSAGE( + color_srgb.srgb_to_linear().is_equal_approx(Color(0.35, 0.5, 0.6, 0.7)), + "The sRGB color converted back to linear color space should match the expected value."); +} + TEST_CASE("[Color] Named colors") { CHECK_MESSAGE( - Color::named("red").is_equal_approx(Color(1, 0, 0)), + Color::named("red").is_equal_approx(Color::hex(0xFF0000FF)), "The named color \"red\" should match the expected value."); // Named colors have their names automatically normalized. CHECK_MESSAGE( - Color::named("white_smoke").is_equal_approx(Color(0.96, 0.96, 0.96)), + Color::named("white_smoke").is_equal_approx(Color::hex(0xF5F5F5FF)), "The named color \"white_smoke\" should match the expected value."); CHECK_MESSAGE( - Color::named("Slate Blue").is_equal_approx(Color(0.42, 0.35, 0.80)), + Color::named("Slate Blue").is_equal_approx(Color::hex(0x6A5ACDFF)), "The named color \"Slate Blue\" should match the expected value."); ERR_PRINT_OFF; diff --git a/tests/core/math/test_expression.h b/tests/core/math/test_expression.h index cb1d29389f..6e3be541b0 100644 --- a/tests/core/math/test_expression.h +++ b/tests/core/math/test_expression.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -137,7 +137,7 @@ TEST_CASE("[Expression] Scientific notation") { expression.parse("2e5") == OK, "The expression should parse successfully."); CHECK_MESSAGE( - Math::is_equal_approx(double(expression.execute()), 25), + Math::is_equal_approx(double(expression.execute()), 2e5), "The expression should return the expected result."); CHECK_MESSAGE( diff --git a/tests/core/math/test_geometry_2d.h b/tests/core/math/test_geometry_2d.h index 8f6669b572..db4e6e2177 100644 --- a/tests/core/math/test_geometry_2d.h +++ b/tests/core/math/test_geometry_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -135,7 +135,7 @@ TEST_CASE("[Geometry2D] Line intersection") { "Parallel lines should not intersect."); } -TEST_CASE("[Geometry2D] Segment intersection.") { +TEST_CASE("[Geometry2D] Segment intersection") { Vector2 r; CHECK(Geometry2D::segment_intersects_segment(Vector2(-1, 1), Vector2(1, -1), Vector2(1, 1), Vector2(-1, -1), &r)); @@ -148,6 +148,10 @@ TEST_CASE("[Geometry2D] Segment intersection.") { Geometry2D::segment_intersects_segment(Vector2(-1, 1), Vector2(1, -1), Vector2(0, 1), Vector2(2, -1), &r), "Parallel segments should not intersect."); + CHECK_FALSE_MESSAGE( + Geometry2D::segment_intersects_segment(Vector2(1, 2), Vector2(3, 2), Vector2(0, 2), Vector2(-2, 2), &r), + "Non-overlapping collinear segments should not intersect."); + CHECK_MESSAGE( Geometry2D::segment_intersects_segment(Vector2(0, 0), Vector2(0, 1), Vector2(0, 0), Vector2(1, 0), &r), "Touching segments should intersect."); @@ -159,11 +163,114 @@ TEST_CASE("[Geometry2D] Segment intersection.") { CHECK(r.is_equal_approx(Vector2(0, 0))); } +TEST_CASE("[Geometry2D] Segment intersection with circle") { + real_t minus_one = -1.0; + real_t zero = 0.0; + real_t one_quarter = 0.25; + real_t three_quarters = 0.75; + real_t one = 1.0; + + CHECK_MESSAGE( + Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(0, 0), Vector2(4, 0), Vector2(0, 0), 1.0), one_quarter), + "Segment from inside to outside of circle should intersect it."); + CHECK_MESSAGE( + Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(4, 0), Vector2(0, 0), Vector2(0, 0), 1.0), three_quarters), + "Segment from outside to inside of circle should intersect it."); + + CHECK_MESSAGE( + Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(-2, 0), Vector2(2, 0), Vector2(0, 0), 1.0), one_quarter), + "Segment running through circle should intersect it."); + CHECK_MESSAGE( + Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(-2, 0), Vector2(0, 0), 1.0), one_quarter), + "Segment running through circle should intersect it."); + + CHECK_MESSAGE( + Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(0, 0), Vector2(1, 0), Vector2(0, 0), 1.0), one), + "Segment starting inside the circle and ending on the circle should intersect it"); + CHECK_MESSAGE( + Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(0, 0), Vector2(0, 0), 1.0), zero), + "Segment starting on the circle and going inwards should intersect it"); + CHECK_MESSAGE( + Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(2, 0), Vector2(0, 0), 1.0), zero), + "Segment starting on the circle and going outwards should intersect it"); + CHECK_MESSAGE( + Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(1, 0), Vector2(0, 0), 1.0), one), + "Segment starting outside the circle and ending on the circle intersect it"); + + CHECK_MESSAGE( + Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(-1, 0), Vector2(1, 0), Vector2(0, 0), 2.0), minus_one), + "Segment completely within the circle should not intersect it"); + CHECK_MESSAGE( + Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(1, 0), Vector2(-1, 0), Vector2(0, 0), 2.0), minus_one), + "Segment completely within the circle should not intersect it"); + CHECK_MESSAGE( + Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(2, 0), Vector2(3, 0), Vector2(0, 0), 1.0), minus_one), + "Segment completely outside the circle should not intersect it"); + CHECK_MESSAGE( + Math::is_equal_approx(Geometry2D::segment_intersects_circle(Vector2(3, 0), Vector2(2, 0), Vector2(0, 0), 1.0), minus_one), + "Segment completely outside the circle should not intersect it"); +} + +TEST_CASE("[Geometry2D] Segment intersection with polygon") { + Vector<Point2> a; + + a.push_back(Point2(-2, 2)); + a.push_back(Point2(3, 4)); + a.push_back(Point2(1, 1)); + a.push_back(Point2(2, -2)); + a.push_back(Point2(-1, -1)); + + CHECK_MESSAGE( + Geometry2D::is_segment_intersecting_polygon(Vector2(0, 2), Vector2(2, 2), a), + "Segment from inside to outside of polygon should intersect it."); + CHECK_MESSAGE( + Geometry2D::is_segment_intersecting_polygon(Vector2(2, 2), Vector2(0, 2), a), + "Segment from outside to inside of polygon should intersect it."); + + CHECK_MESSAGE( + Geometry2D::is_segment_intersecting_polygon(Vector2(2, 4), Vector2(3, 3), a), + "Segment running through polygon should intersect it."); + CHECK_MESSAGE( + Geometry2D::is_segment_intersecting_polygon(Vector2(3, 3), Vector2(2, 4), a), + "Segment running through polygon should intersect it."); + + CHECK_MESSAGE( + Geometry2D::is_segment_intersecting_polygon(Vector2(0, 0), Vector2(1, 1), a), + "Segment starting inside the polygon and ending on the polygon should intersect it"); + CHECK_MESSAGE( + Geometry2D::is_segment_intersecting_polygon(Vector2(1, 1), Vector2(0, 0), a), + "Segment starting on the polygon and going inwards should intersect it"); + CHECK_MESSAGE( + Geometry2D::is_segment_intersecting_polygon(Vector2(-2, 2), Vector2(-2, -1), a), + "Segment starting on the polygon and going outwards should intersect it"); + CHECK_MESSAGE( + Geometry2D::is_segment_intersecting_polygon(Vector2(-2, 1), Vector2(-2, 2), a), + "Segment starting outside the polygon and ending on the polygon intersect it"); + + CHECK_FALSE_MESSAGE( + Geometry2D::is_segment_intersecting_polygon(Vector2(-1, 2), Vector2(1, -1), a), + "Segment completely within the polygon should not intersect it"); + CHECK_FALSE_MESSAGE( + Geometry2D::is_segment_intersecting_polygon(Vector2(1, -1), Vector2(-1, 2), a), + "Segment completely within the polygon should not intersect it"); + CHECK_FALSE_MESSAGE( + Geometry2D::is_segment_intersecting_polygon(Vector2(2, 2), Vector2(2, -1), a), + "Segment completely outside the polygon should not intersect it"); + CHECK_FALSE_MESSAGE( + Geometry2D::is_segment_intersecting_polygon(Vector2(2, -1), Vector2(2, 2), a), + "Segment completely outside the polygon should not intersect it"); +} + TEST_CASE("[Geometry2D] Closest point to segment") { Vector2 s[] = { Vector2(-4, -4), Vector2(4, 4) }; CHECK(Geometry2D::get_closest_point_to_segment(Vector2(4.1, 4.1), s).is_equal_approx(Vector2(4, 4))); CHECK(Geometry2D::get_closest_point_to_segment(Vector2(-4.1, -4.1), s).is_equal_approx(Vector2(-4, -4))); CHECK(Geometry2D::get_closest_point_to_segment(Vector2(-1, 1), s).is_equal_approx(Vector2(0, 0))); + + Vector2 t[] = { Vector2(1, -2), Vector2(1, -2) }; + CHECK_MESSAGE( + Geometry2D::get_closest_point_to_segment(Vector2(-3, 4), t).is_equal_approx(Vector2(1, -2)), + "Line segment is only a single point. This point should be the closest."); } TEST_CASE("[Geometry2D] Closest point to uncapped segment") { @@ -186,6 +293,30 @@ TEST_CASE("[Geometry2D] Closest points between segments") { Geometry2D::get_closest_points_between_segments(Vector2(-1, 1), Vector2(1, -1), Vector2(1, 1), Vector2(-1, -1), c1, c2); CHECK(c1.is_equal_approx(Vector2(0, 0))); CHECK(c2.is_equal_approx(Vector2(0, 0))); + + Geometry2D::get_closest_points_between_segments(Vector2(-3, 4), Vector2(-3, 4), Vector2(-4, 3), Vector2(-2, 3), c1, c2); + CHECK_MESSAGE( + c1.is_equal_approx(Vector2(-3, 4)), + "1st line segment is only a point, this point should be the closest point to the 2nd line segment."); + CHECK_MESSAGE( + c2.is_equal_approx(Vector2(-3, 3)), + "1st line segment is only a point, this should not matter when determining the closest point on the 2nd line segment."); + + Geometry2D::get_closest_points_between_segments(Vector2(-4, 3), Vector2(-2, 3), Vector2(-3, 4), Vector2(-3, 4), c1, c2); + CHECK_MESSAGE( + c1.is_equal_approx(Vector2(-3, 3)), + "2nd line segment is only a point, this should not matter when determining the closest point on the 1st line segment."); + CHECK_MESSAGE( + c2.is_equal_approx(Vector2(-3, 4)), + "2nd line segment is only a point, this point should be the closest point to the 1st line segment."); + + Geometry2D::get_closest_points_between_segments(Vector2(5, -4), Vector2(5, -4), Vector2(-2, 1), Vector2(-2, 1), c1, c2); + CHECK_MESSAGE( + c1.is_equal_approx(Vector2(5, -4)), + "Both line segments are only a point. On the 1st line segment, that point should be the closest point to the 2nd line segment."); + CHECK_MESSAGE( + c2.is_equal_approx(Vector2(-2, 1)), + "Both line segments are only a point. On the 2nd line segment, that point should be the closest point to the 1st line segment."); } TEST_CASE("[Geometry2D] Make atlas") { @@ -562,6 +693,174 @@ TEST_CASE("[Geometry2D] Clip polyline with polygon") { CHECK(r[1][1].is_equal_approx(Vector2(55, 70))); } } + +TEST_CASE("[Geometry2D] Convex hull") { + Vector<Point2> a; + Vector<Point2> r; + + a.push_back(Point2(-4, -8)); + a.push_back(Point2(-10, -4)); + a.push_back(Point2(8, 2)); + a.push_back(Point2(-6, 10)); + a.push_back(Point2(-12, 4)); + a.push_back(Point2(10, -8)); + a.push_back(Point2(4, 8)); + + SUBCASE("[Geometry2D] No points") { + r = Geometry2D::convex_hull(Vector<Vector2>()); + + CHECK_MESSAGE(r.is_empty(), "The convex hull should be empty if there are no input points."); + } + + SUBCASE("[Geometry2D] Single point") { + Vector<Point2> b; + b.push_back(Point2(4, -3)); + + r = Geometry2D::convex_hull(b); + REQUIRE_MESSAGE(r.size() == 1, "Convex hull should contain 1 point."); + CHECK(r[0].is_equal_approx(b[0])); + } + + SUBCASE("[Geometry2D] All points form the convex hull") { + r = Geometry2D::convex_hull(a); + REQUIRE_MESSAGE(r.size() == 8, "Convex hull should contain 8 points."); + CHECK(r[0].is_equal_approx(Point2(-12, 4))); + CHECK(r[1].is_equal_approx(Point2(-10, -4))); + CHECK(r[2].is_equal_approx(Point2(-4, -8))); + CHECK(r[3].is_equal_approx(Point2(10, -8))); + CHECK(r[4].is_equal_approx(Point2(8, 2))); + CHECK(r[5].is_equal_approx(Point2(4, 8))); + CHECK(r[6].is_equal_approx(Point2(-6, 10))); + CHECK(r[7].is_equal_approx(Point2(-12, 4))); + } + + SUBCASE("[Geometry2D] Add extra points inside original convex hull") { + a.push_back(Point2(-4, -8)); + a.push_back(Point2(0, 0)); + a.push_back(Point2(0, 8)); + a.push_back(Point2(-10, -3)); + a.push_back(Point2(9, -4)); + a.push_back(Point2(6, 4)); + + r = Geometry2D::convex_hull(a); + REQUIRE_MESSAGE(r.size() == 8, "Convex hull should contain 8 points."); + CHECK(r[0].is_equal_approx(Point2(-12, 4))); + CHECK(r[1].is_equal_approx(Point2(-10, -4))); + CHECK(r[2].is_equal_approx(Point2(-4, -8))); + CHECK(r[3].is_equal_approx(Point2(10, -8))); + CHECK(r[4].is_equal_approx(Point2(8, 2))); + CHECK(r[5].is_equal_approx(Point2(4, 8))); + CHECK(r[6].is_equal_approx(Point2(-6, 10))); + CHECK(r[7].is_equal_approx(Point2(-12, 4))); + } + + SUBCASE("[Geometry2D] Add extra points on border of original convex hull") { + a.push_back(Point2(9, -3)); + a.push_back(Point2(-2, -8)); + + r = Geometry2D::convex_hull(a); + REQUIRE_MESSAGE(r.size() == 8, "Convex hull should contain 8 points."); + CHECK(r[0].is_equal_approx(Point2(-12, 4))); + CHECK(r[1].is_equal_approx(Point2(-10, -4))); + CHECK(r[2].is_equal_approx(Point2(-4, -8))); + CHECK(r[3].is_equal_approx(Point2(10, -8))); + CHECK(r[4].is_equal_approx(Point2(8, 2))); + CHECK(r[5].is_equal_approx(Point2(4, 8))); + CHECK(r[6].is_equal_approx(Point2(-6, 10))); + CHECK(r[7].is_equal_approx(Point2(-12, 4))); + } + + SUBCASE("[Geometry2D] Add extra points outside border of original convex hull") { + a.push_back(Point2(-11, -1)); + a.push_back(Point2(7, 6)); + + r = Geometry2D::convex_hull(a); + REQUIRE_MESSAGE(r.size() == 10, "Convex hull should contain 10 points."); + CHECK(r[0].is_equal_approx(Point2(-12, 4))); + CHECK(r[1].is_equal_approx(Point2(-11, -1))); + CHECK(r[2].is_equal_approx(Point2(-10, -4))); + CHECK(r[3].is_equal_approx(Point2(-4, -8))); + CHECK(r[4].is_equal_approx(Point2(10, -8))); + CHECK(r[5].is_equal_approx(Point2(8, 2))); + CHECK(r[6].is_equal_approx(Point2(7, 6))); + CHECK(r[7].is_equal_approx(Point2(4, 8))); + CHECK(r[8].is_equal_approx(Point2(-6, 10))); + CHECK(r[9].is_equal_approx(Point2(-12, 4))); + } +} + +TEST_CASE("[Geometry2D] Bresenham line") { + Vector<Vector2i> r; + + SUBCASE("[Geometry2D] Single point") { + r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(0, 0)); + + REQUIRE_MESSAGE(r.size() == 1, "The Bresenham line should contain exactly one point."); + CHECK(r[0] == Vector2i(0, 0)); + } + + SUBCASE("[Geometry2D] Line parallel to x-axis") { + r = Geometry2D::bresenham_line(Point2i(1, 2), Point2i(5, 2)); + + REQUIRE_MESSAGE(r.size() == 5, "The Bresenham line should contain exactly five points."); + CHECK(r[0] == Vector2i(1, 2)); + CHECK(r[1] == Vector2i(2, 2)); + CHECK(r[2] == Vector2i(3, 2)); + CHECK(r[3] == Vector2i(4, 2)); + CHECK(r[4] == Vector2i(5, 2)); + } + + SUBCASE("[Geometry2D] 45 degree line from the origin") { + r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(4, 4)); + + REQUIRE_MESSAGE(r.size() == 5, "The Bresenham line should contain exactly five points."); + CHECK(r[0] == Vector2i(0, 0)); + CHECK(r[1] == Vector2i(1, 1)); + CHECK(r[2] == Vector2i(2, 2)); + CHECK(r[3] == Vector2i(3, 3)); + CHECK(r[4] == Vector2i(4, 4)); + } + + SUBCASE("[Geometry2D] Sloped line going up one unit") { + r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(4, 1)); + + REQUIRE_MESSAGE(r.size() == 5, "The Bresenham line should contain exactly five points."); + CHECK(r[0] == Vector2i(0, 0)); + CHECK(r[1] == Vector2i(1, 0)); + CHECK(r[2] == Vector2i(2, 0)); + CHECK(r[3] == Vector2i(3, 1)); + CHECK(r[4] == Vector2i(4, 1)); + } + + SUBCASE("[Geometry2D] Sloped line going up two units") { + r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(4, 2)); + + REQUIRE_MESSAGE(r.size() == 5, "The Bresenham line should contain exactly five points."); + CHECK(r[0] == Vector2i(0, 0)); + CHECK(r[1] == Vector2i(1, 0)); + CHECK(r[2] == Vector2i(2, 1)); + CHECK(r[3] == Vector2i(3, 1)); + CHECK(r[4] == Vector2i(4, 2)); + } + + SUBCASE("[Geometry2D] Long sloped line") { + r = Geometry2D::bresenham_line(Point2i(0, 0), Point2i(11, 5)); + + REQUIRE_MESSAGE(r.size() == 12, "The Bresenham line should contain exactly twelve points."); + CHECK(r[0] == Vector2i(0, 0)); + CHECK(r[1] == Vector2i(1, 0)); + CHECK(r[2] == Vector2i(2, 1)); + CHECK(r[3] == Vector2i(3, 1)); + CHECK(r[4] == Vector2i(4, 2)); + CHECK(r[5] == Vector2i(5, 2)); + CHECK(r[6] == Vector2i(6, 3)); + CHECK(r[7] == Vector2i(7, 3)); + CHECK(r[8] == Vector2i(8, 4)); + CHECK(r[9] == Vector2i(9, 4)); + CHECK(r[10] == Vector2i(10, 5)); + CHECK(r[11] == Vector2i(11, 5)); + } +} } // namespace TestGeometry2D #endif // TEST_GEOMETRY_2D_H diff --git a/tests/core/math/test_geometry_3d.h b/tests/core/math/test_geometry_3d.h index f42003ffcf..99a4ef2d46 100644 --- a/tests/core/math/test_geometry_3d.h +++ b/tests/core/math/test_geometry_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -288,7 +288,7 @@ TEST_CASE("[Geometry3D] Is Point in Projected Triangle") { TEST_CASE("[Geometry3D] Does Ray Intersect Triangle") { struct Case { Vector3 from, direction, v_1, v_2, v_3; - Vector3 *result; + Vector3 *result = nullptr; bool want; Case(){}; Case(Vector3 p_from, Vector3 p_direction, Vector3 p_v_1, Vector3 p_v_2, Vector3 p_v_3, bool p_want) : @@ -390,7 +390,7 @@ TEST_CASE("[Geometry3D] Triangle and Box Overlap") { struct Case { Vector3 box_centre; Vector3 box_half_size; - Vector3 *tri_verts; + Vector3 *tri_verts = nullptr; bool want; Case(){}; Case(Vector3 p_centre, Vector3 p_half_size, Vector3 *p_verts, bool p_want) : diff --git a/tests/core/math/test_math.cpp b/tests/core/math/test_math.cpp deleted file mode 100644 index 97e6055130..0000000000 --- a/tests/core/math/test_math.cpp +++ /dev/null @@ -1,690 +0,0 @@ -/*************************************************************************/ -/* test_math.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "test_math.h" - -#include "core/math/camera_matrix.h" -#include "core/math/delaunay_3d.h" -#include "core/math/geometry_2d.h" -#include "core/os/main_loop.h" -#include "core/os/os.h" - -namespace TestMath { - -class GetClassAndNamespace { - String code; - int idx; - int line; - String error_str; - bool error; - Variant value; - - String class_name; - - enum Token { - TK_BRACKET_OPEN, - TK_BRACKET_CLOSE, - TK_CURLY_BRACKET_OPEN, - TK_CURLY_BRACKET_CLOSE, - TK_PERIOD, - TK_COLON, - TK_COMMA, - TK_SYMBOL, - TK_IDENTIFIER, - TK_STRING, - TK_NUMBER, - TK_EOF, - TK_ERROR - }; - - Token get_token() { - while (true) { - switch (code[idx]) { - case '\n': { - line++; - idx++; - break; - }; - case 0: { - return TK_EOF; - - } break; - case '{': { - idx++; - return TK_CURLY_BRACKET_OPEN; - }; - case '}': { - idx++; - return TK_CURLY_BRACKET_CLOSE; - }; - case '[': { - idx++; - return TK_BRACKET_OPEN; - }; - case ']': { - idx++; - return TK_BRACKET_CLOSE; - }; - case ':': { - idx++; - return TK_COLON; - }; - case ',': { - idx++; - return TK_COMMA; - }; - case '.': { - idx++; - return TK_PERIOD; - }; - case '#': { - //compiler directive - while (code[idx] != '\n' && code[idx] != 0) { - idx++; - } - continue; - } break; - case '/': { - switch (code[idx + 1]) { - case '*': { // block comment - - idx += 2; - while (true) { - if (code[idx] == 0) { - error_str = "Unterminated comment"; - error = true; - return TK_ERROR; - } else if (code[idx] == '*' && code[idx + 1] == '/') { - idx += 2; - break; - } else if (code[idx] == '\n') { - line++; - } - - idx++; - } - - } break; - case '/': { // line comment skip - - while (code[idx] != '\n' && code[idx] != 0) { - idx++; - } - - } break; - default: { - value = "/"; - idx++; - return TK_SYMBOL; - } - } - - continue; // a comment - } break; - case '\'': - case '"': { - char32_t begin_str = code[idx]; - idx++; - String tk_string = String(); - while (true) { - if (code[idx] == 0) { - error_str = "Unterminated String"; - error = true; - return TK_ERROR; - } else if (code[idx] == begin_str) { - idx++; - break; - } else if (code[idx] == '\\') { - //escaped characters... - idx++; - char32_t next = code[idx]; - if (next == 0) { - error_str = "Unterminated String"; - error = true; - return TK_ERROR; - } - char32_t res = 0; - - switch (next) { - case 'b': - res = 8; - break; - case 't': - res = 9; - break; - case 'n': - res = 10; - break; - case 'f': - res = 12; - break; - case 'r': - res = 13; - break; - case '\"': - res = '\"'; - break; - case '\\': - res = '\\'; - break; - default: { - res = next; - } break; - } - - tk_string += res; - - } else { - if (code[idx] == '\n') { - line++; - } - tk_string += code[idx]; - } - idx++; - } - - value = tk_string; - - return TK_STRING; - - } break; - default: { - if (code[idx] <= 32) { - idx++; - break; - } - - if ((code[idx] >= 33 && code[idx] <= 47) || (code[idx] >= 58 && code[idx] <= 64) || (code[idx] >= 91 && code[idx] <= 96) || (code[idx] >= 123 && code[idx] <= 127)) { - value = String::chr(code[idx]); - idx++; - return TK_SYMBOL; - } - - if (code[idx] == '-' || (code[idx] >= '0' && code[idx] <= '9')) { - //a number - const char32_t *rptr; - double number = String::to_float(&code[idx], &rptr); - idx += (rptr - &code[idx]); - value = number; - return TK_NUMBER; - - } else if ((code[idx] >= 'A' && code[idx] <= 'Z') || (code[idx] >= 'a' && code[idx] <= 'z') || code[idx] > 127) { - String id; - - while ((code[idx] >= 'A' && code[idx] <= 'Z') || (code[idx] >= 'a' && code[idx] <= 'z') || code[idx] > 127) { - id += code[idx]; - idx++; - } - - value = id; - return TK_IDENTIFIER; - } else { - error_str = "Unexpected character."; - error = true; - return TK_ERROR; - } - } - } - } - } - -public: - Error parse(const String &p_code, const String &p_known_class_name = String()) { - code = p_code; - idx = 0; - line = 0; - error_str = String(); - error = false; - value = Variant(); - class_name = String(); - - bool use_next_class = false; - Token tk = get_token(); - - Map<int, String> namespace_stack; - int curly_stack = 0; - - while (!error || tk != TK_EOF) { - if (tk == TK_BRACKET_OPEN) { - tk = get_token(); - if (tk == TK_IDENTIFIER && String(value) == "ScriptClass") { - if (get_token() == TK_BRACKET_CLOSE) { - use_next_class = true; - } - } - } else if (tk == TK_IDENTIFIER && String(value) == "class") { - tk = get_token(); - if (tk == TK_IDENTIFIER) { - String name = value; - if (use_next_class || p_known_class_name == name) { - for (const KeyValue<int, String> &E : namespace_stack) { - class_name += E.value + "."; - } - class_name += String(value); - break; - } - } - - } else if (tk == TK_IDENTIFIER && String(value) == "namespace") { - String name; - int at_level = curly_stack; - while (true) { - tk = get_token(); - if (tk == TK_IDENTIFIER) { - name += String(value); - } - - tk = get_token(); - if (tk == TK_PERIOD) { - name += "."; - } else if (tk == TK_CURLY_BRACKET_OPEN) { - curly_stack++; - break; - } else { - break; //whathever else - } - } - - if (!name.is_empty()) { - namespace_stack[at_level] = name; - } - - } else if (tk == TK_CURLY_BRACKET_OPEN) { - curly_stack++; - } else if (tk == TK_CURLY_BRACKET_CLOSE) { - curly_stack--; - if (namespace_stack.has(curly_stack)) { - namespace_stack.erase(curly_stack); - } - } - - tk = get_token(); - } - - if (error) { - return ERR_PARSE_ERROR; - } - - return OK; - } - - String get_error() { - return error_str; - } - - String get_class() { - return class_name; - } -}; - -void test_vec(Plane p_vec) { - CameraMatrix cm; - cm.set_perspective(45, 1, 0, 100); - Plane v0 = cm.xform4(p_vec); - - print_line("out: " + v0); - v0.normal.z = (v0.d / 100.0 * 2.0 - 1.0) * v0.d; - print_line("out_F: " + v0); -} - -uint32_t ihash(uint32_t a) { - a = (a + 0x7ed55d16) + (a << 12); - a = (a ^ 0xc761c23c) ^ (a >> 19); - a = (a + 0x165667b1) + (a << 5); - a = (a + 0xd3a2646c) ^ (a << 9); - a = (a + 0xfd7046c5) + (a << 3); - a = (a ^ 0xb55a4f09) ^ (a >> 16); - return a; -} - -uint32_t ihash2(uint32_t a) { - a = (a ^ 61) ^ (a >> 16); - a = a + (a << 3); - a = a ^ (a >> 4); - a = a * 0x27d4eb2d; - a = a ^ (a >> 15); - return a; -} - -uint32_t ihash3(uint32_t a) { - a = (a + 0x479ab41d) + (a << 8); - a = (a ^ 0xe4aa10ce) ^ (a >> 5); - a = (a + 0x9942f0a6) - (a << 14); - a = (a ^ 0x5aedd67d) ^ (a >> 3); - a = (a + 0x17bea992) + (a << 7); - return a; -} - -MainLoop *test() { - { - Vector<Vector3> points; - points.push_back(Vector3(0, 0, 0)); - points.push_back(Vector3(0, 0, 1)); - points.push_back(Vector3(0, 1, 0)); - points.push_back(Vector3(0, 1, 1)); - points.push_back(Vector3(1, 1, 0)); - points.push_back(Vector3(1, 0, 0)); - points.push_back(Vector3(1, 0, 1)); - points.push_back(Vector3(1, 1, 1)); - - for (int i = 0; i < 800; i++) { - points.push_back(Vector3(Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0) * Vector3(25, 30, 33)); - } - - Vector<Delaunay3D::OutputSimplex> os = Delaunay3D::tetrahedralize(points); - print_line("simplices in the end: " + itos(os.size())); - for (int i = 0; i < os.size(); i++) { - print_line("Simplex " + itos(i) + ": "); - print_line(points[os[i].points[0]]); - print_line(points[os[i].points[1]]); - print_line(points[os[i].points[2]]); - print_line(points[os[i].points[3]]); - } - - { - FileAccessRef f = FileAccess::open("res://bsp.obj", FileAccess::WRITE); - for (int i = 0; i < os.size(); i++) { - f->store_line("o Simplex" + itos(i)); - for (int j = 0; j < 4; j++) { - f->store_line(vformat("v %f %f %f", points[os[i].points[j]].x, points[os[i].points[j]].y, points[os[i].points[j]].z)); - } - static const int face_order[4][3] = { - { 1, 2, 3 }, - { 1, 3, 4 }, - { 1, 2, 4 }, - { 2, 3, 4 } - }; - - for (int j = 0; j < 4; j++) { - f->store_line(vformat("f %d %d %d", 4 * i + face_order[j][0], 4 * i + face_order[j][1], 4 * i + face_order[j][2])); - } - } - f->close(); - } - - return nullptr; - } - - { - float r = 1; - float g = 0.5; - float b = 0.1; - - const float pow2to9 = 512.0f; - const float B = 15.0f; - const float N = 9.0f; - - float sharedexp = 65408.000f; - - float cRed = MAX(0.0f, MIN(sharedexp, r)); - float cGreen = MAX(0.0f, MIN(sharedexp, g)); - float cBlue = MAX(0.0f, MIN(sharedexp, b)); - - float cMax = MAX(cRed, MAX(cGreen, cBlue)); - - float expp = MAX(-B - 1.0f, floor(Math::log(cMax) / Math_LN2)) + 1.0f + B; - - float sMax = (float)floor((cMax / Math::pow(2.0f, expp - B - N)) + 0.5f); - - float exps = expp + 1.0f; - - if (0.0 <= sMax && sMax < pow2to9) { - exps = expp; - } - - float sRed = Math::floor((cRed / pow(2.0f, exps - B - N)) + 0.5f); - float sGreen = Math::floor((cGreen / pow(2.0f, exps - B - N)) + 0.5f); - float sBlue = Math::floor((cBlue / pow(2.0f, exps - B - N)) + 0.5f); - - print_line("R: " + rtos(sRed) + " G: " + rtos(sGreen) + " B: " + rtos(sBlue) + " EXP: " + rtos(exps)); - - uint32_t rgbe = (Math::fast_ftoi(sRed) & 0x1FF) | ((Math::fast_ftoi(sGreen) & 0x1FF) << 9) | ((Math::fast_ftoi(sBlue) & 0x1FF) << 18) | ((Math::fast_ftoi(exps) & 0x1F) << 27); - - float rb = rgbe & 0x1ff; - float gb = (rgbe >> 9) & 0x1ff; - float bb = (rgbe >> 18) & 0x1ff; - float eb = (rgbe >> 27); - float mb = Math::pow(2.0, eb - 15.0 - 9.0); - float rd = rb * mb; - float gd = gb * mb; - float bd = bb * mb; - - print_line("RGBE: " + Color(rd, gd, bd)); - } - - Vector<int> ints; - ints.resize(20); - - { - int *w; - w = ints.ptrw(); - for (int i = 0; i < ints.size(); i++) { - w[i] = i; - } - } - - Vector<int> posho = ints; - - { - const int *r = posho.ptr(); - for (int i = 0; i < posho.size(); i++) { - print_line(itos(i) + " : " + itos(r[i])); - } - } - - List<String> cmdlargs = OS::get_singleton()->get_cmdline_args(); - - if (cmdlargs.is_empty()) { - //try editor! - return nullptr; - } - - String test = cmdlargs.back()->get(); - if (test == "math") { - // Not a file name but the test name, abort. - // FIXME: This test is ugly as heck, needs fixing :) - return nullptr; - } - - FileAccess *fa = FileAccess::open(test, FileAccess::READ); - ERR_FAIL_COND_V_MSG(!fa, nullptr, "Could not open file: " + test); - - Vector<uint8_t> buf; - uint64_t flen = fa->get_length(); - buf.resize(fa->get_length() + 1); - fa->get_buffer(buf.ptrw(), flen); - buf.write[flen] = 0; - - String code; - code.parse_utf8((const char *)&buf[0]); - - GetClassAndNamespace getclass; - if (getclass.parse(code)) { - print_line("Parse error: " + getclass.get_error()); - } else { - print_line("Found class: " + getclass.get_class()); - } - - { - Vector<int> hashes; - List<StringName> tl; - ClassDB::get_class_list(&tl); - - for (const StringName &E : tl) { - Vector<uint8_t> m5b = E.operator String().md5_buffer(); - hashes.push_back(hashes.size()); - } - - for (int i = nearest_shift(hashes.size()); i < 20; i++) { - bool success = true; - for (int s = 0; s < 10000; s++) { - Set<uint32_t> existing; - success = true; - - for (int j = 0; j < hashes.size(); j++) { - uint32_t eh = ihash2(ihash3(hashes[j] + ihash(s) + s)) & ((1 << i) - 1); - if (existing.has(eh)) { - success = false; - break; - } - existing.insert(eh); - } - - if (success) { - print_line("success at " + itos(i) + "/" + itos(nearest_shift(hashes.size())) + " shift " + itos(s)); - break; - } - } - if (success) { - break; - } - } - - print_line("DONE"); - } - - { - print_line("NUM: " + itos(-128)); - } - - { - Vector3 v(1, 2, 3); - v.normalize(); - real_t a = 0.3; - - Basis m(v, a); - - Vector3 v2(7, 3, 1); - v2.normalize(); - real_t a2 = 0.8; - - Basis m2(v2, a2); - - Quaternion q = m; - Quaternion q2 = m2; - - Basis m3 = m.inverse() * m2; - Quaternion q3 = (q.inverse() * q2); //.normalized(); - - print_line(Quaternion(m3)); - print_line(q3); - - print_line("before v: " + v + " a: " + rtos(a)); - q.get_axis_angle(v, a); - print_line("after v: " + v + " a: " + rtos(a)); - } - - String ret; - - List<String> args; - args.push_back("-l"); - Error err = OS::get_singleton()->execute("/bin/ls", args, &ret); - print_line("error: " + itos(err)); - print_line(ret); - - Basis m3; - m3.rotate(Vector3(1, 0, 0), 0.2); - m3.rotate(Vector3(0, 1, 0), 1.77); - m3.rotate(Vector3(0, 0, 1), 212); - Basis m32; - m32.set_euler(m3.get_euler()); - print_line("ELEULEEEEEEEEEEEEEEEEEER: " + m3.get_euler() + " vs " + m32.get_euler()); - - { - Dictionary d; - d["momo"] = 1; - Dictionary b = d; - b["44"] = 4; - } - - print_line("inters: " + rtos(Geometry2D::segment_intersects_circle(Vector2(-5, 0), Vector2(-2, 0), Vector2(), 1.0))); - - print_line("cross: " + Vector3(1, 2, 3).cross(Vector3(4, 5, 7))); - print_line("dot: " + rtos(Vector3(1, 2, 3).dot(Vector3(4, 5, 7)))); - print_line("abs: " + Vector3(-1, 2, -3).abs()); - print_line("distance_to: " + rtos(Vector3(1, 2, 3).distance_to(Vector3(4, 5, 7)))); - print_line("distance_squared_to: " + rtos(Vector3(1, 2, 3).distance_squared_to(Vector3(4, 5, 7)))); - print_line("plus: " + (Vector3(1, 2, 3) + Vector3(Vector3(4, 5, 7)))); - print_line("minus: " + (Vector3(1, 2, 3) - Vector3(Vector3(4, 5, 7)))); - print_line("mul: " + (Vector3(1, 2, 3) * Vector3(Vector3(4, 5, 7)))); - print_line("div: " + (Vector3(1, 2, 3) / Vector3(Vector3(4, 5, 7)))); - print_line("mul scalar: " + (Vector3(1, 2, 3) * 2.0)); - print_line("premul scalar: " + (2.0 * Vector3(1, 2, 3))); - print_line("div scalar: " + (Vector3(1, 2, 3) / 3.0)); - print_line("length: " + rtos(Vector3(1, 2, 3).length())); - print_line("length squared: " + rtos(Vector3(1, 2, 3).length_squared())); - print_line("normalized: " + Vector3(1, 2, 3).normalized()); - print_line("inverse: " + Vector3(1, 2, 3).inverse()); - - { - Vector3 v(4, 5, 7); - v.normalize(); - print_line("normalize: " + v); - } - - { - Vector3 v(4, 5, 7); - v += Vector3(1, 2, 3); - print_line("+=: " + v); - } - - { - Vector3 v(4, 5, 7); - v -= Vector3(1, 2, 3); - print_line("-=: " + v); - } - - { - Vector3 v(4, 5, 7); - v *= Vector3(1, 2, 3); - print_line("*=: " + v); - } - - { - Vector3 v(4, 5, 7); - v /= Vector3(1, 2, 3); - print_line("/=: " + v); - } - - { - Vector3 v(4, 5, 7); - v *= 2.0; - print_line("scalar *=: " + v); - } - - { - Vector3 v(4, 5, 7); - v /= 2.0; - print_line("scalar /=: " + v); - } - - return nullptr; -} -} // namespace TestMath diff --git a/tests/core/math/test_math.h b/tests/core/math/test_math.h deleted file mode 100644 index ab5fb6a050..0000000000 --- a/tests/core/math/test_math.h +++ /dev/null @@ -1,41 +0,0 @@ -/*************************************************************************/ -/* test_math.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef TEST_MATH_H -#define TEST_MATH_H - -class MainLoop; - -namespace TestMath { - -MainLoop *test(); -} - -#endif diff --git a/tests/core/math/test_plane.h b/tests/core/math/test_plane.h new file mode 100644 index 0000000000..d81a5af1ce --- /dev/null +++ b/tests/core/math/test_plane.h @@ -0,0 +1,172 @@ +/*************************************************************************/ +/* test_plane.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_PLANE_H +#define TEST_PLANE_H + +#include "core/math/plane.h" + +#include "thirdparty/doctest/doctest.h" + +namespace TestPlane { + +// Plane + +TEST_CASE("[Plane] Constructor methods") { + const Plane plane = Plane(32, 22, 16, 3); + const Plane plane_vector = Plane(Vector3(32, 22, 16), 3); + const Plane plane_copy_plane = Plane(plane); + + CHECK_MESSAGE( + plane == plane_vector, + "Planes created with same values but different methods should be equal."); + + CHECK_MESSAGE( + plane == plane_copy_plane, + "Planes created with same values but different methods should be equal."); +} + +TEST_CASE("[Plane] Basic getters") { + const Plane plane = Plane(32, 22, 16, 3); + const Plane plane_normalized = Plane(32.0 / 42, 22.0 / 42, 16.0 / 42, 3.0 / 42); + + CHECK_MESSAGE( + plane.get_normal().is_equal_approx(Vector3(32, 22, 16)), + "get_normal() should return the expected value."); + + CHECK_MESSAGE( + plane.normalized().is_equal_approx(plane_normalized), + "normalized() should return a copy of the normalized value."); +} + +TEST_CASE("[Plane] Basic setters") { + Plane plane = Plane(32, 22, 16, 3); + plane.set_normal(Vector3(4, 2, 3)); + + CHECK_MESSAGE( + plane.is_equal_approx(Plane(4, 2, 3, 3)), + "set_normal() should result in the expected plane."); + + plane = Plane(32, 22, 16, 3); + plane.normalize(); + + CHECK_MESSAGE( + plane.is_equal_approx(Plane(32.0 / 42, 22.0 / 42, 16.0 / 42, 3.0 / 42)), + "normalize() should result in the expected plane."); +} + +TEST_CASE("[Plane] Plane-point operations") { + const Plane plane = Plane(32, 22, 16, 3); + const Plane y_facing_plane = Plane(0, 1, 0, 4); + + CHECK_MESSAGE( + plane.center().is_equal_approx(Vector3(32 * 3, 22 * 3, 16 * 3)), + "center() should return a vector pointing to the center of the plane."); + + CHECK_MESSAGE( + y_facing_plane.is_point_over(Vector3(0, 5, 0)), + "is_point_over() should return the expected result."); + + CHECK_MESSAGE( + y_facing_plane.get_any_perpendicular_normal().is_equal_approx(Vector3(1, 0, 0)), + "get_any_perpindicular_normal() should return the expected result."); + + // TODO distance_to() +} + +TEST_CASE("[Plane] Has point") { + const Plane x_facing_plane = Plane(1, 0, 0, 0); + const Plane y_facing_plane = Plane(0, 1, 0, 0); + const Plane z_facing_plane = Plane(0, 0, 1, 0); + + const Vector3 x_axis_point = Vector3(10, 0, 0); + const Vector3 y_axis_point = Vector3(0, 10, 0); + const Vector3 z_axis_point = Vector3(0, 0, 10); + + const Plane x_facing_plane_with_d_offset = Plane(1, 0, 0, 1); + const Vector3 y_axis_point_with_d_offset = Vector3(1, 10, 0); + + CHECK_MESSAGE( + x_facing_plane.has_point(y_axis_point), + "has_point() with contained Vector3 should return the expected result."); + CHECK_MESSAGE( + x_facing_plane.has_point(z_axis_point), + "has_point() with contained Vector3 should return the expected result."); + + CHECK_MESSAGE( + y_facing_plane.has_point(x_axis_point), + "has_point() with contained Vector3 should return the expected result."); + CHECK_MESSAGE( + y_facing_plane.has_point(z_axis_point), + "has_point() with contained Vector3 should return the expected result."); + + CHECK_MESSAGE( + z_facing_plane.has_point(y_axis_point), + "has_point() with contained Vector3 should return the expected result."); + CHECK_MESSAGE( + z_facing_plane.has_point(x_axis_point), + "has_point() with contained Vector3 should return the expected result."); + + CHECK_MESSAGE( + x_facing_plane_with_d_offset.has_point(y_axis_point_with_d_offset), + "has_point() with passed Vector3 should return the expected result."); +} + +TEST_CASE("[Plane] Intersection") { + const Plane x_facing_plane = Plane(1, 0, 0, 1); + const Plane y_facing_plane = Plane(0, 1, 0, 2); + const Plane z_facing_plane = Plane(0, 0, 1, 3); + + Vector3 vec_out; + + CHECK_MESSAGE( + x_facing_plane.intersect_3(y_facing_plane, z_facing_plane, &vec_out), + "intersect_3() should return the expected result."); + CHECK_MESSAGE( + vec_out.is_equal_approx(Vector3(1, 2, 3)), + "intersect_3() should modify vec_out to the expected result."); + + CHECK_MESSAGE( + x_facing_plane.intersects_ray(Vector3(0, 1, 1), Vector3(2, 0, 0), &vec_out), + "intersects_ray() should return the expected result."); + CHECK_MESSAGE( + vec_out.is_equal_approx(Vector3(1, 1, 1)), + "intersects_ray() should modify vec_out to the expected result."); + + CHECK_MESSAGE( + x_facing_plane.intersects_segment(Vector3(0, 1, 1), Vector3(2, 1, 1), &vec_out), + "intersects_segment() should return the expected result."); + CHECK_MESSAGE( + vec_out.is_equal_approx(Vector3(1, 1, 1)), + "intersects_segment() should modify vec_out to the expected result."); +} +} // namespace TestPlane + +#endif // TEST_PLANE_H diff --git a/tests/core/math/test_random_number_generator.h b/tests/core/math/test_random_number_generator.h index 39c4771c19..e8cd47b9d7 100644 --- a/tests/core/math/test_random_number_generator.h +++ b/tests/core/math/test_random_number_generator.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tests/core/math/test_rect2.h b/tests/core/math/test_rect2.h index aabb950461..0b1106ac3c 100644 --- a/tests/core/math/test_rect2.h +++ b/tests/core/math/test_rect2.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,15 +32,11 @@ #define TEST_RECT2_H #include "core/math/rect2.h" +#include "core/math/rect2i.h" #include "thirdparty/doctest/doctest.h" namespace TestRect2 { -// We also test Rect2i here, for consistency with the source code where Rect2 -// and Rect2i are defined in the same file. - -// Rect2 - TEST_CASE("[Rect2] Constructor methods") { const Rect2 rect = Rect2(0, 100, 1280, 720); const Rect2 rect_vector = Rect2(Vector2(0, 100), Vector2(1280, 720)); @@ -304,272 +300,6 @@ TEST_CASE("[Rect2] Merging") { Rect2(0, 100, 1280, 720).merge(Rect2(-4000, -4000, 100, 100)).is_equal_approx(Rect2(-4000, -4000, 5280, 4820)), "merge() with non-enclosed Rect2 should return the expected result."); } - -// Rect2i - -TEST_CASE("[Rect2i] Constructor methods") { - Rect2i recti = Rect2i(0, 100, 1280, 720); - Rect2i recti_vector = Rect2i(Vector2i(0, 100), Vector2i(1280, 720)); - Rect2i recti_copy_recti = Rect2i(recti); - Rect2i recti_copy_rect = Rect2i(Rect2(0, 100, 1280, 720)); - - CHECK_MESSAGE( - recti == recti_vector, - "Rect2is created with the same dimensions but by different methods should be equal."); - CHECK_MESSAGE( - recti == recti_copy_recti, - "Rect2is created with the same dimensions but by different methods should be equal."); - CHECK_MESSAGE( - recti == recti_copy_rect, - "Rect2is created with the same dimensions but by different methods should be equal."); -} - -TEST_CASE("[Rect2i] String conversion") { - // Note: This also depends on the Vector2 string representation. - CHECK_MESSAGE( - String(Rect2i(0, 100, 1280, 720)) == "[P: (0, 100), S: (1280, 720)]", - "The string representation should match the expected value."); -} - -TEST_CASE("[Rect2i] Basic getters") { - const Rect2i rect = Rect2i(0, 100, 1280, 720); - CHECK_MESSAGE( - rect.get_position() == Vector2i(0, 100), - "get_position() should return the expected value."); - CHECK_MESSAGE( - rect.get_size() == Vector2i(1280, 720), - "get_size() should return the expected value."); - CHECK_MESSAGE( - rect.get_end() == Vector2i(1280, 820), - "get_end() should return the expected value."); - CHECK_MESSAGE( - rect.get_center() == Vector2i(640, 460), - "get_center() should return the expected value."); - CHECK_MESSAGE( - Rect2i(0, 100, 1281, 721).get_center() == Vector2i(640, 460), - "get_center() should return the expected value."); -} - -TEST_CASE("[Rect2i] Basic setters") { - Rect2i rect = Rect2i(0, 100, 1280, 720); - rect.set_end(Vector2i(4000, 4000)); - CHECK_MESSAGE( - rect == Rect2i(0, 100, 4000, 3900), - "set_end() should result in the expected Rect2i."); - - rect = Rect2i(0, 100, 1280, 720); - rect.set_position(Vector2i(4000, 4000)); - CHECK_MESSAGE( - rect == Rect2i(4000, 4000, 1280, 720), - "set_position() should result in the expected Rect2i."); - - rect = Rect2i(0, 100, 1280, 720); - rect.set_size(Vector2i(4000, 4000)); - CHECK_MESSAGE( - rect == Rect2i(0, 100, 4000, 4000), - "set_size() should result in the expected Rect2i."); -} - -TEST_CASE("[Rect2i] Area getters") { - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).get_area() == 921'600, - "get_area() should return the expected value."); - CHECK_MESSAGE( - Rect2i(0, 100, -1280, -720).get_area() == 921'600, - "get_area() should return the expected value."); - CHECK_MESSAGE( - Rect2i(0, 100, 1280, -720).get_area() == -921'600, - "get_area() should return the expected value."); - CHECK_MESSAGE( - Rect2i(0, 100, -1280, 720).get_area() == -921'600, - "get_area() should return the expected value."); - CHECK_MESSAGE( - Rect2i(0, 100, 0, 720).get_area() == 0, - "get_area() should return the expected value."); - - CHECK_MESSAGE( - !Rect2i(0, 100, 1280, 720).has_no_area(), - "has_no_area() should return the expected value on Rect2i with an area."); - CHECK_MESSAGE( - Rect2i(0, 100, 0, 500).has_no_area(), - "has_no_area() should return the expected value on Rect2i with no area."); - CHECK_MESSAGE( - Rect2i(0, 100, 500, 0).has_no_area(), - "has_no_area() should return the expected value on Rect2i with no area."); - CHECK_MESSAGE( - Rect2i(0, 100, 0, 0).has_no_area(), - "has_no_area() should return the expected value on Rect2i with no area."); -} - -TEST_CASE("[Rect2i] Absolute coordinates") { - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).abs() == Rect2i(0, 100, 1280, 720), - "abs() should return the expected Rect2i."); - CHECK_MESSAGE( - Rect2i(0, -100, 1280, 720).abs() == Rect2i(0, -100, 1280, 720), - "abs() should return the expected Rect2i."); - CHECK_MESSAGE( - Rect2i(0, -100, -1280, -720).abs() == Rect2i(-1280, -820, 1280, 720), - "abs() should return the expected Rect2i."); - CHECK_MESSAGE( - Rect2i(0, 100, -1280, 720).abs() == Rect2i(-1280, 100, 1280, 720), - "abs() should return the expected Rect2i."); -} - -TEST_CASE("[Rect2i] Intersection") { - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).intersection(Rect2i(0, 300, 100, 100)) == Rect2i(0, 300, 100, 100), - "intersection() with fully enclosed Rect2i should return the expected result."); - // The resulting Rect2i is 100 pixels high because the first Rect2i is vertically offset by 100 pixels. - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).intersection(Rect2i(1200, 700, 100, 100)) == Rect2i(1200, 700, 80, 100), - "intersection() with partially enclosed Rect2i should return the expected result."); - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).intersection(Rect2i(-4000, -4000, 100, 100)) == Rect2i(), - "intersection() with non-enclosed Rect2i should return the expected result."); -} - -TEST_CASE("[Rect2i] Enclosing") { - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).encloses(Rect2i(0, 300, 100, 100)), - "encloses() with fully contained Rect2i should return the expected result."); - CHECK_MESSAGE( - !Rect2i(0, 100, 1280, 720).encloses(Rect2i(1200, 700, 100, 100)), - "encloses() with partially contained Rect2i should return the expected result."); - CHECK_MESSAGE( - !Rect2i(0, 100, 1280, 720).encloses(Rect2i(-4000, -4000, 100, 100)), - "encloses() with non-contained Rect2i should return the expected result."); -} - -TEST_CASE("[Rect2i] Expanding") { - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).expand(Vector2i(500, 600)) == Rect2i(0, 100, 1280, 720), - "expand() with contained Vector2i should return the expected result."); - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).expand(Vector2i(0, 0)) == Rect2i(0, 0, 1280, 820), - "expand() with non-contained Vector2i should return the expected result."); -} - -TEST_CASE("[Rect2i] Growing") { - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).grow(100) == Rect2i(-100, 0, 1480, 920), - "grow() with positive value should return the expected Rect2i."); - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).grow(-100) == Rect2i(100, 200, 1080, 520), - "grow() with negative value should return the expected Rect2i."); - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).grow(-4000) == Rect2i(4000, 4100, -6720, -7280), - "grow() with large negative value should return the expected Rect2i."); - - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).grow_individual(100, 200, 300, 400) == Rect2i(-100, -100, 1680, 1320), - "grow_individual() with positive values should return the expected Rect2i."); - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).grow_individual(-100, 200, 300, -400) == Rect2i(100, -100, 1480, 520), - "grow_individual() with positive and negative values should return the expected Rect2i."); - - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).grow_side(SIDE_TOP, 500) == Rect2i(0, -400, 1280, 1220), - "grow_side() with positive value should return the expected Rect2i."); - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).grow_side(SIDE_TOP, -500) == Rect2i(0, 600, 1280, 220), - "grow_side() with negative value should return the expected Rect2i."); -} - -TEST_CASE("[Rect2i] Has point") { - Rect2i rect = Rect2i(0, 100, 1280, 720); - CHECK_MESSAGE( - rect.has_point(Vector2i(500, 600)), - "has_point() with contained Vector2i should return the expected result."); - CHECK_MESSAGE( - !rect.has_point(Vector2i(0, 0)), - "has_point() with non-contained Vector2i should return the expected result."); - - CHECK_MESSAGE( - rect.has_point(rect.position), - "has_point() with positive size should include `position`."); - CHECK_MESSAGE( - rect.has_point(rect.position + Vector2i(1, 1)), - "has_point() with positive size should include `position + (1, 1)`."); - CHECK_MESSAGE( - !rect.has_point(rect.position + Vector2i(1, -1)), - "has_point() with positive size should not include `position + (1, -1)`."); - CHECK_MESSAGE( - !rect.has_point(rect.position + rect.size), - "has_point() with positive size should not include `position + size`."); - CHECK_MESSAGE( - !rect.has_point(rect.position + rect.size + Vector2i(1, 1)), - "has_point() with positive size should not include `position + size + (1, 1)`."); - CHECK_MESSAGE( - rect.has_point(rect.position + rect.size + Vector2i(-1, -1)), - "has_point() with positive size should include `position + size + (-1, -1)`."); - CHECK_MESSAGE( - !rect.has_point(rect.position + rect.size + Vector2i(-1, 1)), - "has_point() with positive size should not include `position + size + (-1, 1)`."); - - CHECK_MESSAGE( - rect.has_point(rect.position + Vector2i(0, 10)), - "has_point() with point located on left edge should return true."); - CHECK_MESSAGE( - !rect.has_point(rect.position + Vector2i(rect.size.x, 10)), - "has_point() with point located on right edge should return false."); - CHECK_MESSAGE( - rect.has_point(rect.position + Vector2i(10, 0)), - "has_point() with point located on top edge should return true."); - CHECK_MESSAGE( - !rect.has_point(rect.position + Vector2i(10, rect.size.y)), - "has_point() with point located on bottom edge should return false."); - - /* - // FIXME: Disabled for now until GH-37617 is fixed one way or another. - // More tests should then be written like for the positive size case. - rect = Rect2i(0, 100, -1280, -720); - CHECK_MESSAGE( - rect.has_point(rect.position), - "has_point() with negative size should include `position`."); - CHECK_MESSAGE( - !rect.has_point(rect.position + rect.size), - "has_point() with negative size should not include `position + size`."); - */ - - rect = Rect2i(-4000, -200, 1280, 720); - CHECK_MESSAGE( - rect.has_point(rect.position + Vector2i(0, 10)), - "has_point() with negative position and point located on left edge should return true."); - CHECK_MESSAGE( - !rect.has_point(rect.position + Vector2i(rect.size.x, 10)), - "has_point() with negative position and point located on right edge should return false."); - CHECK_MESSAGE( - rect.has_point(rect.position + Vector2i(10, 0)), - "has_point() with negative position and point located on top edge should return true."); - CHECK_MESSAGE( - !rect.has_point(rect.position + Vector2i(10, rect.size.y)), - "has_point() with negative position and point located on bottom edge should return false."); -} - -TEST_CASE("[Rect2i] Intersection") { - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).intersects(Rect2i(0, 300, 100, 100)), - "intersects() with fully enclosed Rect2i should return the expected result."); - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).intersects(Rect2i(1200, 700, 100, 100)), - "intersects() with partially enclosed Rect2i should return the expected result."); - CHECK_MESSAGE( - !Rect2i(0, 100, 1280, 720).intersects(Rect2i(-4000, -4000, 100, 100)), - "intersects() with non-enclosed Rect2i should return the expected result."); -} - -TEST_CASE("[Rect2i] Merging") { - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).merge(Rect2i(0, 300, 100, 100)) == Rect2i(0, 100, 1280, 720), - "merge() with fully enclosed Rect2i should return the expected result."); - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).merge(Rect2i(1200, 700, 100, 100)) == Rect2i(0, 100, 1300, 720), - "merge() with partially enclosed Rect2i should return the expected result."); - CHECK_MESSAGE( - Rect2i(0, 100, 1280, 720).merge(Rect2i(-4000, -4000, 100, 100)) == Rect2i(-4000, -4000, 5280, 4820), - "merge() with non-enclosed Rect2i should return the expected result."); -} } // namespace TestRect2 #endif // TEST_RECT2_H diff --git a/tests/core/math/test_rect2i.h b/tests/core/math/test_rect2i.h new file mode 100644 index 0000000000..0d1a088a66 --- /dev/null +++ b/tests/core/math/test_rect2i.h @@ -0,0 +1,311 @@ +/*************************************************************************/ +/* test_rect2i.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_RECT2I_H +#define TEST_RECT2I_H + +#include "core/math/rect2.h" +#include "core/math/rect2i.h" + +#include "thirdparty/doctest/doctest.h" + +namespace TestRect2i { +TEST_CASE("[Rect2i] Constructor methods") { + Rect2i recti = Rect2i(0, 100, 1280, 720); + Rect2i recti_vector = Rect2i(Vector2i(0, 100), Vector2i(1280, 720)); + Rect2i recti_copy_recti = Rect2i(recti); + Rect2i recti_copy_rect = Rect2i(Rect2(0, 100, 1280, 720)); + + CHECK_MESSAGE( + recti == recti_vector, + "Rect2is created with the same dimensions but by different methods should be equal."); + CHECK_MESSAGE( + recti == recti_copy_recti, + "Rect2is created with the same dimensions but by different methods should be equal."); + CHECK_MESSAGE( + recti == recti_copy_rect, + "Rect2is created with the same dimensions but by different methods should be equal."); +} + +TEST_CASE("[Rect2i] String conversion") { + // Note: This also depends on the Vector2 string representation. + CHECK_MESSAGE( + String(Rect2i(0, 100, 1280, 720)) == "[P: (0, 100), S: (1280, 720)]", + "The string representation should match the expected value."); +} + +TEST_CASE("[Rect2i] Basic getters") { + const Rect2i rect = Rect2i(0, 100, 1280, 720); + CHECK_MESSAGE( + rect.get_position() == Vector2i(0, 100), + "get_position() should return the expected value."); + CHECK_MESSAGE( + rect.get_size() == Vector2i(1280, 720), + "get_size() should return the expected value."); + CHECK_MESSAGE( + rect.get_end() == Vector2i(1280, 820), + "get_end() should return the expected value."); + CHECK_MESSAGE( + rect.get_center() == Vector2i(640, 460), + "get_center() should return the expected value."); + CHECK_MESSAGE( + Rect2i(0, 100, 1281, 721).get_center() == Vector2i(640, 460), + "get_center() should return the expected value."); +} + +TEST_CASE("[Rect2i] Basic setters") { + Rect2i rect = Rect2i(0, 100, 1280, 720); + rect.set_end(Vector2i(4000, 4000)); + CHECK_MESSAGE( + rect == Rect2i(0, 100, 4000, 3900), + "set_end() should result in the expected Rect2i."); + + rect = Rect2i(0, 100, 1280, 720); + rect.set_position(Vector2i(4000, 4000)); + CHECK_MESSAGE( + rect == Rect2i(4000, 4000, 1280, 720), + "set_position() should result in the expected Rect2i."); + + rect = Rect2i(0, 100, 1280, 720); + rect.set_size(Vector2i(4000, 4000)); + CHECK_MESSAGE( + rect == Rect2i(0, 100, 4000, 4000), + "set_size() should result in the expected Rect2i."); +} + +TEST_CASE("[Rect2i] Area getters") { + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).get_area() == 921'600, + "get_area() should return the expected value."); + CHECK_MESSAGE( + Rect2i(0, 100, -1280, -720).get_area() == 921'600, + "get_area() should return the expected value."); + CHECK_MESSAGE( + Rect2i(0, 100, 1280, -720).get_area() == -921'600, + "get_area() should return the expected value."); + CHECK_MESSAGE( + Rect2i(0, 100, -1280, 720).get_area() == -921'600, + "get_area() should return the expected value."); + CHECK_MESSAGE( + Rect2i(0, 100, 0, 720).get_area() == 0, + "get_area() should return the expected value."); + + CHECK_MESSAGE( + !Rect2i(0, 100, 1280, 720).has_no_area(), + "has_no_area() should return the expected value on Rect2i with an area."); + CHECK_MESSAGE( + Rect2i(0, 100, 0, 500).has_no_area(), + "has_no_area() should return the expected value on Rect2i with no area."); + CHECK_MESSAGE( + Rect2i(0, 100, 500, 0).has_no_area(), + "has_no_area() should return the expected value on Rect2i with no area."); + CHECK_MESSAGE( + Rect2i(0, 100, 0, 0).has_no_area(), + "has_no_area() should return the expected value on Rect2i with no area."); +} + +TEST_CASE("[Rect2i] Absolute coordinates") { + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).abs() == Rect2i(0, 100, 1280, 720), + "abs() should return the expected Rect2i."); + CHECK_MESSAGE( + Rect2i(0, -100, 1280, 720).abs() == Rect2i(0, -100, 1280, 720), + "abs() should return the expected Rect2i."); + CHECK_MESSAGE( + Rect2i(0, -100, -1280, -720).abs() == Rect2i(-1280, -820, 1280, 720), + "abs() should return the expected Rect2i."); + CHECK_MESSAGE( + Rect2i(0, 100, -1280, 720).abs() == Rect2i(-1280, 100, 1280, 720), + "abs() should return the expected Rect2i."); +} + +TEST_CASE("[Rect2i] Intersection") { + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).intersection(Rect2i(0, 300, 100, 100)) == Rect2i(0, 300, 100, 100), + "intersection() with fully enclosed Rect2i should return the expected result."); + // The resulting Rect2i is 100 pixels high because the first Rect2i is vertically offset by 100 pixels. + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).intersection(Rect2i(1200, 700, 100, 100)) == Rect2i(1200, 700, 80, 100), + "intersection() with partially enclosed Rect2i should return the expected result."); + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).intersection(Rect2i(-4000, -4000, 100, 100)) == Rect2i(), + "intersection() with non-enclosed Rect2i should return the expected result."); +} + +TEST_CASE("[Rect2i] Enclosing") { + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).encloses(Rect2i(0, 300, 100, 100)), + "encloses() with fully contained Rect2i should return the expected result."); + CHECK_MESSAGE( + !Rect2i(0, 100, 1280, 720).encloses(Rect2i(1200, 700, 100, 100)), + "encloses() with partially contained Rect2i should return the expected result."); + CHECK_MESSAGE( + !Rect2i(0, 100, 1280, 720).encloses(Rect2i(-4000, -4000, 100, 100)), + "encloses() with non-contained Rect2i should return the expected result."); + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).encloses(Rect2i(0, 100, 1280, 720)), + "encloses() with identical Rect2i should return the expected result."); +} + +TEST_CASE("[Rect2i] Expanding") { + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).expand(Vector2i(500, 600)) == Rect2i(0, 100, 1280, 720), + "expand() with contained Vector2i should return the expected result."); + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).expand(Vector2i(0, 0)) == Rect2i(0, 0, 1280, 820), + "expand() with non-contained Vector2i should return the expected result."); +} + +TEST_CASE("[Rect2i] Growing") { + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).grow(100) == Rect2i(-100, 0, 1480, 920), + "grow() with positive value should return the expected Rect2i."); + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).grow(-100) == Rect2i(100, 200, 1080, 520), + "grow() with negative value should return the expected Rect2i."); + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).grow(-4000) == Rect2i(4000, 4100, -6720, -7280), + "grow() with large negative value should return the expected Rect2i."); + + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).grow_individual(100, 200, 300, 400) == Rect2i(-100, -100, 1680, 1320), + "grow_individual() with positive values should return the expected Rect2i."); + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).grow_individual(-100, 200, 300, -400) == Rect2i(100, -100, 1480, 520), + "grow_individual() with positive and negative values should return the expected Rect2i."); + + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).grow_side(SIDE_TOP, 500) == Rect2i(0, -400, 1280, 1220), + "grow_side() with positive value should return the expected Rect2i."); + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).grow_side(SIDE_TOP, -500) == Rect2i(0, 600, 1280, 220), + "grow_side() with negative value should return the expected Rect2i."); +} + +TEST_CASE("[Rect2i] Has point") { + Rect2i rect = Rect2i(0, 100, 1280, 720); + CHECK_MESSAGE( + rect.has_point(Vector2i(500, 600)), + "has_point() with contained Vector2i should return the expected result."); + CHECK_MESSAGE( + !rect.has_point(Vector2i(0, 0)), + "has_point() with non-contained Vector2i should return the expected result."); + + CHECK_MESSAGE( + rect.has_point(rect.position), + "has_point() with positive size should include `position`."); + CHECK_MESSAGE( + rect.has_point(rect.position + Vector2i(1, 1)), + "has_point() with positive size should include `position + (1, 1)`."); + CHECK_MESSAGE( + !rect.has_point(rect.position + Vector2i(1, -1)), + "has_point() with positive size should not include `position + (1, -1)`."); + CHECK_MESSAGE( + !rect.has_point(rect.position + rect.size), + "has_point() with positive size should not include `position + size`."); + CHECK_MESSAGE( + !rect.has_point(rect.position + rect.size + Vector2i(1, 1)), + "has_point() with positive size should not include `position + size + (1, 1)`."); + CHECK_MESSAGE( + rect.has_point(rect.position + rect.size + Vector2i(-1, -1)), + "has_point() with positive size should include `position + size + (-1, -1)`."); + CHECK_MESSAGE( + !rect.has_point(rect.position + rect.size + Vector2i(-1, 1)), + "has_point() with positive size should not include `position + size + (-1, 1)`."); + + CHECK_MESSAGE( + rect.has_point(rect.position + Vector2i(0, 10)), + "has_point() with point located on left edge should return true."); + CHECK_MESSAGE( + !rect.has_point(rect.position + Vector2i(rect.size.x, 10)), + "has_point() with point located on right edge should return false."); + CHECK_MESSAGE( + rect.has_point(rect.position + Vector2i(10, 0)), + "has_point() with point located on top edge should return true."); + CHECK_MESSAGE( + !rect.has_point(rect.position + Vector2i(10, rect.size.y)), + "has_point() with point located on bottom edge should return false."); + + /* + // FIXME: Disabled for now until GH-37617 is fixed one way or another. + // More tests should then be written like for the positive size case. + rect = Rect2i(0, 100, -1280, -720); + CHECK_MESSAGE( + rect.has_point(rect.position), + "has_point() with negative size should include `position`."); + CHECK_MESSAGE( + !rect.has_point(rect.position + rect.size), + "has_point() with negative size should not include `position + size`."); + */ + + rect = Rect2i(-4000, -200, 1280, 720); + CHECK_MESSAGE( + rect.has_point(rect.position + Vector2i(0, 10)), + "has_point() with negative position and point located on left edge should return true."); + CHECK_MESSAGE( + !rect.has_point(rect.position + Vector2i(rect.size.x, 10)), + "has_point() with negative position and point located on right edge should return false."); + CHECK_MESSAGE( + rect.has_point(rect.position + Vector2i(10, 0)), + "has_point() with negative position and point located on top edge should return true."); + CHECK_MESSAGE( + !rect.has_point(rect.position + Vector2i(10, rect.size.y)), + "has_point() with negative position and point located on bottom edge should return false."); +} + +TEST_CASE("[Rect2i] Intersection") { + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).intersects(Rect2i(0, 300, 100, 100)), + "intersects() with fully enclosed Rect2i should return the expected result."); + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).intersects(Rect2i(1200, 700, 100, 100)), + "intersects() with partially enclosed Rect2i should return the expected result."); + CHECK_MESSAGE( + !Rect2i(0, 100, 1280, 720).intersects(Rect2i(-4000, -4000, 100, 100)), + "intersects() with non-enclosed Rect2i should return the expected result."); + CHECK_MESSAGE( + !Rect2i(0, 0, 2, 2).intersects(Rect2i(2, 2, 2, 2)), + "intersects() with adjacent Rect2i should return the expected result."); +} + +TEST_CASE("[Rect2i] Merging") { + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).merge(Rect2i(0, 300, 100, 100)) == Rect2i(0, 100, 1280, 720), + "merge() with fully enclosed Rect2i should return the expected result."); + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).merge(Rect2i(1200, 700, 100, 100)) == Rect2i(0, 100, 1300, 720), + "merge() with partially enclosed Rect2i should return the expected result."); + CHECK_MESSAGE( + Rect2i(0, 100, 1280, 720).merge(Rect2i(-4000, -4000, 100, 100)) == Rect2i(-4000, -4000, 5280, 4820), + "merge() with non-enclosed Rect2i should return the expected result."); +} +} // namespace TestRect2i + +#endif // TEST_RECT2I_H diff --git a/tests/core/math/test_vector2.h b/tests/core/math/test_vector2.h new file mode 100644 index 0000000000..9b7800164a --- /dev/null +++ b/tests/core/math/test_vector2.h @@ -0,0 +1,389 @@ +/*************************************************************************/ +/* test_vector2.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_VECTOR2_H +#define TEST_VECTOR2_H + +#include "core/math/vector2.h" +#include "core/math/vector2i.h" +#include "tests/test_macros.h" + +namespace TestVector2 { + +TEST_CASE("[Vector2] Angle methods") { + const Vector2 vector_x = Vector2(1, 0); + const Vector2 vector_y = Vector2(0, 1); + CHECK_MESSAGE( + Math::is_equal_approx(vector_x.angle_to(vector_y), (real_t)Math_TAU / 4), + "Vector2 angle_to should work as expected."); + CHECK_MESSAGE( + Math::is_equal_approx(vector_y.angle_to(vector_x), (real_t)-Math_TAU / 4), + "Vector2 angle_to should work as expected."); + CHECK_MESSAGE( + Math::is_equal_approx(vector_x.angle_to_point(vector_y), (real_t)Math_TAU * 3 / 8), + "Vector2 angle_to_point should work as expected."); + CHECK_MESSAGE( + Math::is_equal_approx(vector_y.angle_to_point(vector_x), (real_t)-Math_TAU / 8), + "Vector2 angle_to_point should work as expected."); +} + +TEST_CASE("[Vector2] Axis methods") { + Vector2 vector = Vector2(1.2, 3.4); + CHECK_MESSAGE( + vector.max_axis_index() == Vector2::Axis::AXIS_Y, + "Vector2 max_axis_index should work as expected."); + CHECK_MESSAGE( + vector.min_axis_index() == Vector2::Axis::AXIS_X, + "Vector2 min_axis_index should work as expected."); + CHECK_MESSAGE( + vector[vector.min_axis_index()] == (real_t)1.2, + "Vector2 array operator should work as expected."); + vector[Vector2::Axis::AXIS_Y] = 3.7; + CHECK_MESSAGE( + vector[Vector2::Axis::AXIS_Y] == (real_t)3.7, + "Vector2 array operator setter should work as expected."); +} + +TEST_CASE("[Vector2] Interpolation methods") { + const Vector2 vector1 = Vector2(1, 2); + const Vector2 vector2 = Vector2(4, 5); + CHECK_MESSAGE( + vector1.lerp(vector2, 0.5) == Vector2(2.5, 3.5), + "Vector2 lerp should work as expected."); + CHECK_MESSAGE( + vector1.lerp(vector2, 1.0 / 3.0).is_equal_approx(Vector2(2, 3)), + "Vector2 lerp should work as expected."); + CHECK_MESSAGE( + vector1.normalized().slerp(vector2.normalized(), 0.5).is_equal_approx(Vector2(0.538953602313995361, 0.84233558177947998)), + "Vector2 slerp should work as expected."); + CHECK_MESSAGE( + vector1.normalized().slerp(vector2.normalized(), 1.0 / 3.0).is_equal_approx(Vector2(0.508990883827209473, 0.860771894454956055)), + "Vector2 slerp should work as expected."); + CHECK_MESSAGE( + Vector2(5, 0).slerp(Vector2(0, 5), 0.5).is_equal_approx(Vector2(5, 5) * Math_SQRT12), + "Vector2 slerp with non-normalized values should work as expected."); + CHECK_MESSAGE( + Vector2(1, 1).slerp(Vector2(2, 2), 0.5).is_equal_approx(Vector2(1.5, 1.5)), + "Vector2 slerp with colinear inputs should behave as expected."); + CHECK_MESSAGE( + Vector2().slerp(Vector2(), 0.5) == Vector2(), + "Vector2 slerp with both inputs as zero vectors should return a zero vector."); + CHECK_MESSAGE( + Vector2().slerp(Vector2(1, 1), 0.5) == Vector2(0.5, 0.5), + "Vector2 slerp with one input as zero should behave like a regular lerp."); + CHECK_MESSAGE( + Vector2(1, 1).slerp(Vector2(), 0.5) == Vector2(0.5, 0.5), + "Vector2 slerp with one input as zero should behave like a regular lerp."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.slerp(vector2, 0.5).length(), (real_t)4.31959610746631919), + "Vector2 slerp with different length input should return a vector with an interpolated length."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.angle_to(vector1.slerp(vector2, 0.5)) * 2, vector1.angle_to(vector2)), + "Vector2 slerp with different length input should return a vector with an interpolated angle."); + CHECK_MESSAGE( + vector1.cubic_interpolate(vector2, Vector2(), Vector2(7, 7), 0.5) == Vector2(2.375, 3.5), + "Vector2 cubic_interpolate should work as expected."); + CHECK_MESSAGE( + vector1.cubic_interpolate(vector2, Vector2(), Vector2(7, 7), 1.0 / 3.0).is_equal_approx(Vector2(1.851851940155029297, 2.962963104248046875)), + "Vector2 cubic_interpolate should work as expected."); + CHECK_MESSAGE( + Vector2(1, 0).move_toward(Vector2(10, 0), 3) == Vector2(4, 0), + "Vector2 move_toward should work as expected."); +} + +TEST_CASE("[Vector2] Length methods") { + const Vector2 vector1 = Vector2(10, 10); + const Vector2 vector2 = Vector2(20, 30); + CHECK_MESSAGE( + vector1.length_squared() == 200, + "Vector2 length_squared should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.length(), 10 * (real_t)Math_SQRT2), + "Vector2 length should work as expected."); + CHECK_MESSAGE( + vector2.length_squared() == 1300, + "Vector2 length_squared should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector2.length(), (real_t)36.05551275463989293119), + "Vector2 length should work as expected."); + CHECK_MESSAGE( + vector1.distance_squared_to(vector2) == 500, + "Vector2 distance_squared_to should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.distance_to(vector2), (real_t)22.36067977499789696409), + "Vector2 distance_to should work as expected."); +} + +TEST_CASE("[Vector2] Limiting methods") { + const Vector2 vector = Vector2(10, 10); + CHECK_MESSAGE( + vector.limit_length().is_equal_approx(Vector2(Math_SQRT12, Math_SQRT12)), + "Vector2 limit_length should work as expected."); + CHECK_MESSAGE( + vector.limit_length(5).is_equal_approx(5 * Vector2(Math_SQRT12, Math_SQRT12)), + "Vector2 limit_length should work as expected."); + + CHECK_MESSAGE( + Vector2(-5, 15).clamp(Vector2(), vector).is_equal_approx(Vector2(0, 10)), + "Vector2 clamp should work as expected."); + CHECK_MESSAGE( + vector.clamp(Vector2(0, 15), Vector2(5, 20)).is_equal_approx(Vector2(5, 15)), + "Vector2 clamp should work as expected."); +} + +TEST_CASE("[Vector2] Normalization methods") { + CHECK_MESSAGE( + Vector2(1, 0).is_normalized() == true, + "Vector2 is_normalized should return true for a normalized vector."); + CHECK_MESSAGE( + Vector2(1, 1).is_normalized() == false, + "Vector2 is_normalized should return false for a non-normalized vector."); + CHECK_MESSAGE( + Vector2(1, 0).normalized() == Vector2(1, 0), + "Vector2 normalized should return the same vector for a normalized vector."); + CHECK_MESSAGE( + Vector2(1, 1).normalized().is_equal_approx(Vector2(Math_SQRT12, Math_SQRT12)), + "Vector2 normalized should work as expected."); +} + +TEST_CASE("[Vector2] Operators") { + const Vector2 decimal1 = Vector2(2.3, 4.9); + const Vector2 decimal2 = Vector2(1.2, 3.4); + const Vector2 power1 = Vector2(0.75, 1.5); + const Vector2 power2 = Vector2(0.5, 0.125); + const Vector2 int1 = Vector2(4, 5); + const Vector2 int2 = Vector2(1, 2); + + CHECK_MESSAGE( + (decimal1 + decimal2).is_equal_approx(Vector2(3.5, 8.3)), + "Vector2 addition should behave as expected."); + CHECK_MESSAGE( + (power1 + power2) == Vector2(1.25, 1.625), + "Vector2 addition with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 + int2) == Vector2(5, 7), + "Vector2 addition with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 - decimal2).is_equal_approx(Vector2(1.1, 1.5)), + "Vector2 subtraction should behave as expected."); + CHECK_MESSAGE( + (power1 - power2) == Vector2(0.25, 1.375), + "Vector2 subtraction with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 - int2) == Vector2(3, 3), + "Vector2 subtraction with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 * decimal2).is_equal_approx(Vector2(2.76, 16.66)), + "Vector2 multiplication should behave as expected."); + CHECK_MESSAGE( + (power1 * power2) == Vector2(0.375, 0.1875), + "Vector2 multiplication with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 * int2) == Vector2(4, 10), + "Vector2 multiplication with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 / decimal2).is_equal_approx(Vector2(1.91666666666666666, 1.44117647058823529)), + "Vector2 division should behave as expected."); + CHECK_MESSAGE( + (power1 / power2) == Vector2(1.5, 12.0), + "Vector2 division with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 / int2) == Vector2(4, 2.5), + "Vector2 division with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 * 2).is_equal_approx(Vector2(4.6, 9.8)), + "Vector2 multiplication should behave as expected."); + CHECK_MESSAGE( + (power1 * 2) == Vector2(1.5, 3), + "Vector2 multiplication with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 * 2) == Vector2(8, 10), + "Vector2 multiplication with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 / 2).is_equal_approx(Vector2(1.15, 2.45)), + "Vector2 division should behave as expected."); + CHECK_MESSAGE( + (power1 / 2) == Vector2(0.375, 0.75), + "Vector2 division with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 / 2) == Vector2(2, 2.5), + "Vector2 division with integers should give exact results."); + + CHECK_MESSAGE( + ((Vector2i)decimal1) == Vector2i(2, 4), + "Vector2 cast to Vector2i should work as expected."); + CHECK_MESSAGE( + ((Vector2i)decimal2) == Vector2i(1, 3), + "Vector2 cast to Vector2i should work as expected."); + CHECK_MESSAGE( + Vector2(Vector2i(1, 2)) == Vector2(1, 2), + "Vector2 constructed from Vector2i should work as expected."); + + CHECK_MESSAGE( + ((String)decimal1) == "(2.3, 4.9)", + "Vector2 cast to String should work as expected."); + CHECK_MESSAGE( + ((String)decimal2) == "(1.2, 3.4)", + "Vector2 cast to String should work as expected."); + CHECK_MESSAGE( + ((String)Vector2(9.8, 9.9)) == "(9.8, 9.9)", + "Vector2 cast to String should work as expected."); +#ifdef REAL_T_IS_DOUBLE + CHECK_MESSAGE( + ((String)Vector2(Math_PI, Math_TAU)) == "(3.14159265358979, 6.28318530717959)", + "Vector2 cast to String should print the correct amount of digits for real_t = double."); +#else + CHECK_MESSAGE( + ((String)Vector2(Math_PI, Math_TAU)) == "(3.141593, 6.283185)", + "Vector2 cast to String should print the correct amount of digits for real_t = float."); +#endif // REAL_T_IS_DOUBLE +} + +TEST_CASE("[Vector2] Other methods") { + const Vector2 vector = Vector2(1.2, 3.4); + CHECK_MESSAGE( + Math::is_equal_approx(vector.aspect(), (real_t)1.2 / (real_t)3.4), + "Vector2 aspect should work as expected."); + CHECK_MESSAGE( + vector.direction_to(Vector2()).is_equal_approx(-vector.normalized()), + "Vector2 direction_to should work as expected."); + CHECK_MESSAGE( + Vector2(1, 1).direction_to(Vector2(2, 2)).is_equal_approx(Vector2(Math_SQRT12, Math_SQRT12)), + "Vector2 direction_to should work as expected."); + CHECK_MESSAGE( + vector.posmod(2).is_equal_approx(Vector2(1.2, 1.4)), + "Vector2 posmod should work as expected."); + CHECK_MESSAGE( + (-vector).posmod(2).is_equal_approx(Vector2(0.8, 0.6)), + "Vector2 posmod should work as expected."); + CHECK_MESSAGE( + vector.posmodv(Vector2(1, 2)).is_equal_approx(Vector2(0.2, 1.4)), + "Vector2 posmodv should work as expected."); + CHECK_MESSAGE( + (-vector).posmodv(Vector2(2, 3)).is_equal_approx(Vector2(0.8, 2.6)), + "Vector2 posmodv should work as expected."); + CHECK_MESSAGE( + vector.rotated(Math_TAU / 4).is_equal_approx(Vector2(-3.4, 1.2)), + "Vector2 rotated should work as expected."); + CHECK_MESSAGE( + vector.snapped(Vector2(1, 1)) == Vector2(1, 3), + "Vector2 snapped to integers should be the same as rounding."); + CHECK_MESSAGE( + Vector2(3.4, 5.6).snapped(Vector2(1, 1)) == Vector2(3, 6), + "Vector2 snapped to integers should be the same as rounding."); + CHECK_MESSAGE( + vector.snapped(Vector2(0.25, 0.25)) == Vector2(1.25, 3.5), + "Vector2 snapped to 0.25 should give exact results."); +} + +TEST_CASE("[Vector2] Plane methods") { + const Vector2 vector = Vector2(1.2, 3.4); + const Vector2 vector_y = Vector2(0, 1); + CHECK_MESSAGE( + vector.bounce(vector_y) == Vector2(1.2, -3.4), + "Vector2 bounce on a plane with normal of the Y axis should."); + CHECK_MESSAGE( + vector.reflect(vector_y) == Vector2(-1.2, 3.4), + "Vector2 reflect on a plane with normal of the Y axis should."); + CHECK_MESSAGE( + vector.project(vector_y) == Vector2(0, 3.4), + "Vector2 projected on the X axis should only give the Y component."); + CHECK_MESSAGE( + vector.slide(vector_y) == Vector2(1.2, 0), + "Vector2 slide on a plane with normal of the Y axis should set the Y to zero."); +} + +TEST_CASE("[Vector2] Rounding methods") { + const Vector2 vector1 = Vector2(1.2, 5.6); + const Vector2 vector2 = Vector2(1.2, -5.6); + CHECK_MESSAGE( + vector1.abs() == vector1, + "Vector2 abs should work as expected."); + CHECK_MESSAGE( + vector2.abs() == vector1, + "Vector2 abs should work as expected."); + + CHECK_MESSAGE( + vector1.ceil() == Vector2(2, 6), + "Vector2 ceil should work as expected."); + CHECK_MESSAGE( + vector2.ceil() == Vector2(2, -5), + "Vector2 ceil should work as expected."); + + CHECK_MESSAGE( + vector1.floor() == Vector2(1, 5), + "Vector2 floor should work as expected."); + CHECK_MESSAGE( + vector2.floor() == Vector2(1, -6), + "Vector2 floor should work as expected."); + + CHECK_MESSAGE( + vector1.round() == Vector2(1, 6), + "Vector2 round should work as expected."); + CHECK_MESSAGE( + vector2.round() == Vector2(1, -6), + "Vector2 round should work as expected."); + + CHECK_MESSAGE( + vector1.sign() == Vector2(1, 1), + "Vector2 sign should work as expected."); + CHECK_MESSAGE( + vector2.sign() == Vector2(1, -1), + "Vector2 sign should work as expected."); +} + +TEST_CASE("[Vector2] Linear algebra methods") { + const Vector2 vector_x = Vector2(1, 0); + const Vector2 vector_y = Vector2(0, 1); + CHECK_MESSAGE( + vector_x.cross(vector_y) == 1, + "Vector2 cross product of X and Y should give 1."); + CHECK_MESSAGE( + vector_y.cross(vector_x) == -1, + "Vector2 cross product of Y and X should give negative 1."); + + CHECK_MESSAGE( + vector_x.dot(vector_y) == 0.0, + "Vector2 dot product of perpendicular vectors should be zero."); + CHECK_MESSAGE( + vector_x.dot(vector_x) == 1.0, + "Vector2 dot product of identical unit vectors should be one."); + CHECK_MESSAGE( + (vector_x * 10).dot(vector_x * 10) == 100.0, + "Vector2 dot product of same direction vectors should behave as expected."); +} +} // namespace TestVector2 + +#endif // TEST_VECTOR2_H diff --git a/tests/core/math/test_vector2i.h b/tests/core/math/test_vector2i.h new file mode 100644 index 0000000000..841bb793a4 --- /dev/null +++ b/tests/core/math/test_vector2i.h @@ -0,0 +1,145 @@ +/*************************************************************************/ +/* test_vector2i.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_VECTOR2I_H +#define TEST_VECTOR2I_H + +#include "core/math/vector2.h" +#include "core/math/vector2i.h" +#include "tests/test_macros.h" + +namespace TestVector2i { + +TEST_CASE("[Vector2i] Axis methods") { + Vector2i vector = Vector2i(2, 3); + CHECK_MESSAGE( + vector.max_axis_index() == Vector2i::Axis::AXIS_Y, + "Vector2i max_axis_index should work as expected."); + CHECK_MESSAGE( + vector.min_axis_index() == Vector2i::Axis::AXIS_X, + "Vector2i min_axis_index should work as expected."); + CHECK_MESSAGE( + vector[vector.min_axis_index()] == 2, + "Vector2i array operator should work as expected."); + vector[Vector2i::Axis::AXIS_Y] = 5; + CHECK_MESSAGE( + vector[Vector2i::Axis::AXIS_Y] == 5, + "Vector2i array operator setter should work as expected."); +} + +TEST_CASE("[Vector2i] Clamp method") { + const Vector2i vector = Vector2i(10, 10); + CHECK_MESSAGE( + Vector2i(-5, 15).clamp(Vector2i(), vector) == Vector2i(0, 10), + "Vector2i clamp should work as expected."); + CHECK_MESSAGE( + vector.clamp(Vector2i(0, 15), Vector2i(5, 20)) == Vector2i(5, 15), + "Vector2i clamp should work as expected."); +} + +TEST_CASE("[Vector2i] Length methods") { + const Vector2i vector1 = Vector2i(10, 10); + const Vector2i vector2 = Vector2i(20, 30); + CHECK_MESSAGE( + vector1.length_squared() == 200, + "Vector2i length_squared should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.length(), 10 * Math_SQRT2), + "Vector2i length should work as expected."); + CHECK_MESSAGE( + vector2.length_squared() == 1300, + "Vector2i length_squared should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector2.length(), 36.05551275463989293119), + "Vector2i length should work as expected."); +} + +TEST_CASE("[Vector2i] Operators") { + const Vector2i vector1 = Vector2i(5, 9); + const Vector2i vector2 = Vector2i(2, 3); + + CHECK_MESSAGE( + (vector1 + vector2) == Vector2i(7, 12), + "Vector2i addition with integers should give exact results."); + CHECK_MESSAGE( + (vector1 - vector2) == Vector2i(3, 6), + "Vector2i subtraction with integers should give exact results."); + CHECK_MESSAGE( + (vector1 * vector2) == Vector2i(10, 27), + "Vector2i multiplication with integers should give exact results."); + CHECK_MESSAGE( + (vector1 / vector2) == Vector2i(2, 3), + "Vector2i division with integers should give exact results."); + + CHECK_MESSAGE( + (vector1 * 2) == Vector2i(10, 18), + "Vector2i multiplication with integers should give exact results."); + CHECK_MESSAGE( + (vector1 / 2) == Vector2i(2, 4), + "Vector2i division with integers should give exact results."); + + CHECK_MESSAGE( + ((Vector2)vector1) == Vector2(5, 9), + "Vector2i cast to Vector2 should work as expected."); + CHECK_MESSAGE( + ((Vector2)vector2) == Vector2(2, 3), + "Vector2i cast to Vector2 should work as expected."); + CHECK_MESSAGE( + Vector2i(Vector2(1.1, 2.9)) == Vector2i(1, 2), + "Vector2i constructed from Vector2 should work as expected."); +} + +TEST_CASE("[Vector2i] Other methods") { + const Vector2i vector = Vector2i(1, 3); + CHECK_MESSAGE( + Math::is_equal_approx(vector.aspect(), (real_t)1.0 / (real_t)3.0), + "Vector2i aspect should work as expected."); +} + +TEST_CASE("[Vector2i] Abs and sign methods") { + const Vector2i vector1 = Vector2i(1, 3); + const Vector2i vector2 = Vector2i(1, -3); + CHECK_MESSAGE( + vector1.abs() == vector1, + "Vector2i abs should work as expected."); + CHECK_MESSAGE( + vector2.abs() == vector1, + "Vector2i abs should work as expected."); + + CHECK_MESSAGE( + vector1.sign() == Vector2i(1, 1), + "Vector2i sign should work as expected."); + CHECK_MESSAGE( + vector2.sign() == Vector2i(1, -1), + "Vector2i sign should work as expected."); +} +} // namespace TestVector2i + +#endif // TEST_VECTOR2I_H diff --git a/tests/core/math/test_vector3.h b/tests/core/math/test_vector3.h new file mode 100644 index 0000000000..6f99fada2b --- /dev/null +++ b/tests/core/math/test_vector3.h @@ -0,0 +1,417 @@ +/*************************************************************************/ +/* test_vector3.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_VECTOR3_H +#define TEST_VECTOR3_H + +#include "core/math/vector3.h" +#include "tests/test_macros.h" + +#define Math_SQRT13 0.57735026918962576450914878050196 +#define Math_SQRT3 1.7320508075688772935274463415059 + +namespace TestVector3 { + +TEST_CASE("[Vector3] Angle methods") { + const Vector3 vector_x = Vector3(1, 0, 0); + const Vector3 vector_y = Vector3(0, 1, 0); + const Vector3 vector_yz = Vector3(0, 1, 1); + CHECK_MESSAGE( + Math::is_equal_approx(vector_x.angle_to(vector_y), (real_t)Math_TAU / 4), + "Vector3 angle_to should work as expected."); + CHECK_MESSAGE( + Math::is_equal_approx(vector_x.angle_to(vector_yz), (real_t)Math_TAU / 4), + "Vector3 angle_to should work as expected."); + CHECK_MESSAGE( + Math::is_equal_approx(vector_yz.angle_to(vector_x), (real_t)Math_TAU / 4), + "Vector3 angle_to should work as expected."); + CHECK_MESSAGE( + Math::is_equal_approx(vector_y.angle_to(vector_yz), (real_t)Math_TAU / 8), + "Vector3 angle_to should work as expected."); + + CHECK_MESSAGE( + Math::is_equal_approx(vector_x.signed_angle_to(vector_y, vector_y), (real_t)Math_TAU / 4), + "Vector3 signed_angle_to edge case should be positive."); + CHECK_MESSAGE( + Math::is_equal_approx(vector_x.signed_angle_to(vector_yz, vector_y), (real_t)Math_TAU / -4), + "Vector3 signed_angle_to should work as expected."); + CHECK_MESSAGE( + Math::is_equal_approx(vector_yz.signed_angle_to(vector_x, vector_y), (real_t)Math_TAU / 4), + "Vector3 signed_angle_to should work as expected."); +} + +TEST_CASE("[Vector3] Axis methods") { + Vector3 vector = Vector3(1.2, 3.4, 5.6); + CHECK_MESSAGE( + vector.max_axis_index() == Vector3::Axis::AXIS_Z, + "Vector3 max_axis_index should work as expected."); + CHECK_MESSAGE( + vector.min_axis_index() == Vector3::Axis::AXIS_X, + "Vector3 min_axis_index should work as expected."); + CHECK_MESSAGE( + vector.get_axis(vector.max_axis_index()) == (real_t)5.6, + "Vector3 get_axis should work as expected."); + CHECK_MESSAGE( + vector[vector.min_axis_index()] == (real_t)1.2, + "Vector3 array operator should work as expected."); + + vector.set_axis(Vector3::Axis::AXIS_Y, 4.7); + CHECK_MESSAGE( + vector.get_axis(Vector3::Axis::AXIS_Y) == (real_t)4.7, + "Vector3 set_axis should work as expected."); + vector[Vector3::Axis::AXIS_Y] = 3.7; + CHECK_MESSAGE( + vector[Vector3::Axis::AXIS_Y] == (real_t)3.7, + "Vector3 array operator setter should work as expected."); +} + +TEST_CASE("[Vector3] Interpolation methods") { + const Vector3 vector1 = Vector3(1, 2, 3); + const Vector3 vector2 = Vector3(4, 5, 6); + CHECK_MESSAGE( + vector1.lerp(vector2, 0.5) == Vector3(2.5, 3.5, 4.5), + "Vector3 lerp should work as expected."); + CHECK_MESSAGE( + vector1.lerp(vector2, 1.0 / 3.0).is_equal_approx(Vector3(2, 3, 4)), + "Vector3 lerp should work as expected."); + CHECK_MESSAGE( + vector1.normalized().slerp(vector2.normalized(), 0.5).is_equal_approx(Vector3(0.363866806030273438, 0.555698215961456299, 0.747529566287994385)), + "Vector3 slerp should work as expected."); + CHECK_MESSAGE( + vector1.normalized().slerp(vector2.normalized(), 1.0 / 3.0).is_equal_approx(Vector3(0.332119762897491455, 0.549413740634918213, 0.766707837581634521)), + "Vector3 slerp should work as expected."); + CHECK_MESSAGE( + Vector3(5, 0, 0).slerp(Vector3(0, 3, 4), 0.5).is_equal_approx(Vector3(3.535533905029296875, 2.121320486068725586, 2.828427314758300781)), + "Vector3 slerp with non-normalized values should work as expected."); + CHECK_MESSAGE( + Vector3(1, 1, 1).slerp(Vector3(2, 2, 2), 0.5).is_equal_approx(Vector3(1.5, 1.5, 1.5)), + "Vector3 slerp with colinear inputs should behave as expected."); + CHECK_MESSAGE( + Vector3().slerp(Vector3(), 0.5) == Vector3(), + "Vector3 slerp with both inputs as zero vectors should return a zero vector."); + CHECK_MESSAGE( + Vector3().slerp(Vector3(1, 1, 1), 0.5) == Vector3(0.5, 0.5, 0.5), + "Vector3 slerp with one input as zero should behave like a regular lerp."); + CHECK_MESSAGE( + Vector3(1, 1, 1).slerp(Vector3(), 0.5) == Vector3(0.5, 0.5, 0.5), + "Vector3 slerp with one input as zero should behave like a regular lerp."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.slerp(vector2, 0.5).length(), (real_t)6.25831088708303172), + "Vector3 slerp with different length input should return a vector with an interpolated length."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.angle_to(vector1.slerp(vector2, 0.5)) * 2, vector1.angle_to(vector2)), + "Vector3 slerp with different length input should return a vector with an interpolated angle."); + CHECK_MESSAGE( + vector1.cubic_interpolate(vector2, Vector3(), Vector3(7, 7, 7), 0.5) == Vector3(2.375, 3.5, 4.625), + "Vector3 cubic_interpolate should work as expected."); + CHECK_MESSAGE( + vector1.cubic_interpolate(vector2, Vector3(), Vector3(7, 7, 7), 1.0 / 3.0).is_equal_approx(Vector3(1.851851940155029297, 2.962963104248046875, 4.074074268341064453)), + "Vector3 cubic_interpolate should work as expected."); + CHECK_MESSAGE( + Vector3(1, 0, 0).move_toward(Vector3(10, 0, 0), 3) == Vector3(4, 0, 0), + "Vector3 move_toward should work as expected."); +} + +TEST_CASE("[Vector3] Length methods") { + const Vector3 vector1 = Vector3(10, 10, 10); + const Vector3 vector2 = Vector3(20, 30, 40); + CHECK_MESSAGE( + vector1.length_squared() == 300, + "Vector3 length_squared should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.length(), 10 * (real_t)Math_SQRT3), + "Vector3 length should work as expected."); + CHECK_MESSAGE( + vector2.length_squared() == 2900, + "Vector3 length_squared should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector2.length(), (real_t)53.8516480713450403125), + "Vector3 length should work as expected."); + CHECK_MESSAGE( + vector1.distance_squared_to(vector2) == 1400, + "Vector3 distance_squared_to should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.distance_to(vector2), (real_t)37.41657386773941385584), + "Vector3 distance_to should work as expected."); +} + +TEST_CASE("[Vector3] Limiting methods") { + const Vector3 vector = Vector3(10, 10, 10); + CHECK_MESSAGE( + vector.limit_length().is_equal_approx(Vector3(Math_SQRT13, Math_SQRT13, Math_SQRT13)), + "Vector3 limit_length should work as expected."); + CHECK_MESSAGE( + vector.limit_length(5).is_equal_approx(5 * Vector3(Math_SQRT13, Math_SQRT13, Math_SQRT13)), + "Vector3 limit_length should work as expected."); + + CHECK_MESSAGE( + Vector3(-5, 5, 15).clamp(Vector3(), vector) == Vector3(0, 5, 10), + "Vector3 clamp should work as expected."); + CHECK_MESSAGE( + vector.clamp(Vector3(0, 10, 15), Vector3(5, 10, 20)) == Vector3(5, 10, 15), + "Vector3 clamp should work as expected."); +} + +TEST_CASE("[Vector3] Normalization methods") { + CHECK_MESSAGE( + Vector3(1, 0, 0).is_normalized() == true, + "Vector3 is_normalized should return true for a normalized vector."); + CHECK_MESSAGE( + Vector3(1, 1, 1).is_normalized() == false, + "Vector3 is_normalized should return false for a non-normalized vector."); + CHECK_MESSAGE( + Vector3(1, 0, 0).normalized() == Vector3(1, 0, 0), + "Vector3 normalized should return the same vector for a normalized vector."); + CHECK_MESSAGE( + Vector3(1, 1, 0).normalized().is_equal_approx(Vector3(Math_SQRT12, Math_SQRT12, 0)), + "Vector3 normalized should work as expected."); + CHECK_MESSAGE( + Vector3(1, 1, 1).normalized().is_equal_approx(Vector3(Math_SQRT13, Math_SQRT13, Math_SQRT13)), + "Vector3 normalized should work as expected."); +} + +TEST_CASE("[Vector3] Operators") { + const Vector3 decimal1 = Vector3(2.3, 4.9, 7.8); + const Vector3 decimal2 = Vector3(1.2, 3.4, 5.6); + const Vector3 power1 = Vector3(0.75, 1.5, 0.625); + const Vector3 power2 = Vector3(0.5, 0.125, 0.25); + const Vector3 int1 = Vector3(4, 5, 9); + const Vector3 int2 = Vector3(1, 2, 3); + + CHECK_MESSAGE( + (decimal1 + decimal2).is_equal_approx(Vector3(3.5, 8.3, 13.4)), + "Vector3 addition should behave as expected."); + CHECK_MESSAGE( + (power1 + power2) == Vector3(1.25, 1.625, 0.875), + "Vector3 addition with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 + int2) == Vector3(5, 7, 12), + "Vector3 addition with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 - decimal2).is_equal_approx(Vector3(1.1, 1.5, 2.2)), + "Vector3 subtraction should behave as expected."); + CHECK_MESSAGE( + (power1 - power2) == Vector3(0.25, 1.375, 0.375), + "Vector3 subtraction with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 - int2) == Vector3(3, 3, 6), + "Vector3 subtraction with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 * decimal2).is_equal_approx(Vector3(2.76, 16.66, 43.68)), + "Vector3 multiplication should behave as expected."); + CHECK_MESSAGE( + (power1 * power2) == Vector3(0.375, 0.1875, 0.15625), + "Vector3 multiplication with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 * int2) == Vector3(4, 10, 27), + "Vector3 multiplication with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 / decimal2).is_equal_approx(Vector3(1.91666666666666666, 1.44117647058823529, 1.39285714285714286)), + "Vector3 division should behave as expected."); + CHECK_MESSAGE( + (power1 / power2) == Vector3(1.5, 12.0, 2.5), + "Vector3 division with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 / int2) == Vector3(4, 2.5, 3), + "Vector3 division with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 * 2).is_equal_approx(Vector3(4.6, 9.8, 15.6)), + "Vector3 multiplication should behave as expected."); + CHECK_MESSAGE( + (power1 * 2) == Vector3(1.5, 3, 1.25), + "Vector3 multiplication with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 * 2) == Vector3(8, 10, 18), + "Vector3 multiplication with integers should give exact results."); + + CHECK_MESSAGE( + (decimal1 / 2).is_equal_approx(Vector3(1.15, 2.45, 3.9)), + "Vector3 division should behave as expected."); + CHECK_MESSAGE( + (power1 / 2) == Vector3(0.375, 0.75, 0.3125), + "Vector3 division with powers of two should give exact results."); + CHECK_MESSAGE( + (int1 / 2) == Vector3(2, 2.5, 4.5), + "Vector3 division with integers should give exact results."); + + CHECK_MESSAGE( + ((Vector3i)decimal1) == Vector3i(2, 4, 7), + "Vector3 cast to Vector3i should work as expected."); + CHECK_MESSAGE( + ((Vector3i)decimal2) == Vector3i(1, 3, 5), + "Vector3 cast to Vector3i should work as expected."); + CHECK_MESSAGE( + Vector3(Vector3i(1, 2, 3)) == Vector3(1, 2, 3), + "Vector3 constructed from Vector3i should work as expected."); + + CHECK_MESSAGE( + ((String)decimal1) == "(2.3, 4.9, 7.8)", + "Vector3 cast to String should work as expected."); + CHECK_MESSAGE( + ((String)decimal2) == "(1.2, 3.4, 5.6)", + "Vector3 cast to String should work as expected."); + CHECK_MESSAGE( + ((String)Vector3(9.7, 9.8, 9.9)) == "(9.7, 9.8, 9.9)", + "Vector3 cast to String should work as expected."); +#ifdef REAL_T_IS_DOUBLE + CHECK_MESSAGE( + ((String)Vector3(Math_E, Math_SQRT2, Math_SQRT3)) == "(2.71828182845905, 1.4142135623731, 1.73205080756888)", + "Vector3 cast to String should print the correct amount of digits for real_t = double."); +#else + CHECK_MESSAGE( + ((String)Vector3(Math_E, Math_SQRT2, Math_SQRT3)) == "(2.718282, 1.414214, 1.732051)", + "Vector3 cast to String should print the correct amount of digits for real_t = float."); +#endif // REAL_T_IS_DOUBLE +} + +TEST_CASE("[Vector3] Other methods") { + const Vector3 vector = Vector3(1.2, 3.4, 5.6); + CHECK_MESSAGE( + vector.direction_to(Vector3()).is_equal_approx(-vector.normalized()), + "Vector3 direction_to should work as expected."); + CHECK_MESSAGE( + Vector3(1, 1, 1).direction_to(Vector3(2, 2, 2)).is_equal_approx(Vector3(Math_SQRT13, Math_SQRT13, Math_SQRT13)), + "Vector3 direction_to should work as expected."); + CHECK_MESSAGE( + vector.inverse().is_equal_approx(Vector3(1 / 1.2, 1 / 3.4, 1 / 5.6)), + "Vector3 inverse should work as expected."); + CHECK_MESSAGE( + vector.posmod(2).is_equal_approx(Vector3(1.2, 1.4, 1.6)), + "Vector3 posmod should work as expected."); + CHECK_MESSAGE( + (-vector).posmod(2).is_equal_approx(Vector3(0.8, 0.6, 0.4)), + "Vector3 posmod should work as expected."); + CHECK_MESSAGE( + vector.posmodv(Vector3(1, 2, 3)).is_equal_approx(Vector3(0.2, 1.4, 2.6)), + "Vector3 posmodv should work as expected."); + CHECK_MESSAGE( + (-vector).posmodv(Vector3(2, 3, 4)).is_equal_approx(Vector3(0.8, 2.6, 2.4)), + "Vector3 posmodv should work as expected."); + CHECK_MESSAGE( + vector.rotated(Vector3(0, 1, 0), Math_TAU / 4).is_equal_approx(Vector3(5.6, 3.4, -1.2)), + "Vector3 rotated should work as expected."); + CHECK_MESSAGE( + vector.snapped(Vector3(1, 1, 1)) == Vector3(1, 3, 6), + "Vector3 snapped to integers should be the same as rounding."); + CHECK_MESSAGE( + vector.snapped(Vector3(0.25, 0.25, 0.25)) == Vector3(1.25, 3.5, 5.5), + "Vector3 snapped to 0.25 should give exact results."); +} + +TEST_CASE("[Vector3] Plane methods") { + const Vector3 vector = Vector3(1.2, 3.4, 5.6); + const Vector3 vector_y = Vector3(0, 1, 0); + CHECK_MESSAGE( + vector.bounce(vector_y) == Vector3(1.2, -3.4, 5.6), + "Vector3 bounce on a plane with normal of the Y axis should."); + CHECK_MESSAGE( + vector.reflect(vector_y) == Vector3(-1.2, 3.4, -5.6), + "Vector3 reflect on a plane with normal of the Y axis should."); + CHECK_MESSAGE( + vector.project(vector_y) == Vector3(0, 3.4, 0), + "Vector3 projected on the X axis should only give the Y component."); + CHECK_MESSAGE( + vector.slide(vector_y) == Vector3(1.2, 0, 5.6), + "Vector3 slide on a plane with normal of the Y axis should set the Y to zero."); +} + +TEST_CASE("[Vector3] Rounding methods") { + const Vector3 vector1 = Vector3(1.2, 3.4, 5.6); + const Vector3 vector2 = Vector3(1.2, -3.4, -5.6); + CHECK_MESSAGE( + vector1.abs() == vector1, + "Vector3 abs should work as expected."); + CHECK_MESSAGE( + vector2.abs() == vector1, + "Vector3 abs should work as expected."); + + CHECK_MESSAGE( + vector1.ceil() == Vector3(2, 4, 6), + "Vector3 ceil should work as expected."); + CHECK_MESSAGE( + vector2.ceil() == Vector3(2, -3, -5), + "Vector3 ceil should work as expected."); + + CHECK_MESSAGE( + vector1.floor() == Vector3(1, 3, 5), + "Vector3 floor should work as expected."); + CHECK_MESSAGE( + vector2.floor() == Vector3(1, -4, -6), + "Vector3 floor should work as expected."); + + CHECK_MESSAGE( + vector1.round() == Vector3(1, 3, 6), + "Vector3 round should work as expected."); + CHECK_MESSAGE( + vector2.round() == Vector3(1, -3, -6), + "Vector3 round should work as expected."); + + CHECK_MESSAGE( + vector1.sign() == Vector3(1, 1, 1), + "Vector3 sign should work as expected."); + CHECK_MESSAGE( + vector2.sign() == Vector3(1, -1, -1), + "Vector3 sign should work as expected."); +} + +TEST_CASE("[Vector3] Linear algebra methods") { + const Vector3 vector_x = Vector3(1, 0, 0); + const Vector3 vector_y = Vector3(0, 1, 0); + const Vector3 vector_z = Vector3(0, 0, 1); + CHECK_MESSAGE( + vector_x.cross(vector_y) == vector_z, + "Vector3 cross product of X and Y should give Z."); + CHECK_MESSAGE( + vector_y.cross(vector_x) == -vector_z, + "Vector3 cross product of Y and X should give negative Z."); + CHECK_MESSAGE( + vector_y.cross(vector_z) == vector_x, + "Vector3 cross product of Y and Z should give X."); + CHECK_MESSAGE( + vector_z.cross(vector_x) == vector_y, + "Vector3 cross product of Z and X should give Y."); + + CHECK_MESSAGE( + vector_x.dot(vector_y) == 0.0, + "Vector3 dot product of perpendicular vectors should be zero."); + CHECK_MESSAGE( + vector_x.dot(vector_x) == 1.0, + "Vector3 dot product of identical unit vectors should be one."); + CHECK_MESSAGE( + (vector_x * 10).dot(vector_x * 10) == 100.0, + "Vector3 dot product of same direction vectors should behave as expected."); +} +} // namespace TestVector3 + +#endif // TEST_VECTOR3_H diff --git a/tests/core/math/test_vector3i.h b/tests/core/math/test_vector3i.h new file mode 100644 index 0000000000..b1c6944eba --- /dev/null +++ b/tests/core/math/test_vector3i.h @@ -0,0 +1,145 @@ +/*************************************************************************/ +/* test_vector3i.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_VECTOR3I_H +#define TEST_VECTOR3I_H + +#include "core/math/vector3i.h" +#include "tests/test_macros.h" + +namespace TestVector3i { + +TEST_CASE("[Vector3i] Axis methods") { + Vector3i vector = Vector3i(1, 2, 3); + CHECK_MESSAGE( + vector.max_axis_index() == Vector3i::Axis::AXIS_Z, + "Vector3i max_axis_index should work as expected."); + CHECK_MESSAGE( + vector.min_axis_index() == Vector3i::Axis::AXIS_X, + "Vector3i min_axis_index should work as expected."); + CHECK_MESSAGE( + vector.get_axis(vector.max_axis_index()) == 3, + "Vector3i get_axis should work as expected."); + CHECK_MESSAGE( + vector[vector.min_axis_index()] == 1, + "Vector3i array operator should work as expected."); + + vector.set_axis(Vector3i::Axis::AXIS_Y, 4); + CHECK_MESSAGE( + vector.get_axis(Vector3i::Axis::AXIS_Y) == 4, + "Vector3i set_axis should work as expected."); + vector[Vector3i::Axis::AXIS_Y] = 5; + CHECK_MESSAGE( + vector[Vector3i::Axis::AXIS_Y] == 5, + "Vector3i array operator setter should work as expected."); +} + +TEST_CASE("[Vector3i] Clamp method") { + const Vector3i vector = Vector3i(10, 10, 10); + CHECK_MESSAGE( + Vector3i(-5, 5, 15).clamp(Vector3i(), vector) == Vector3i(0, 5, 10), + "Vector3i clamp should work as expected."); + CHECK_MESSAGE( + vector.clamp(Vector3i(0, 10, 15), Vector3i(5, 10, 20)) == Vector3i(5, 10, 15), + "Vector3i clamp should work as expected."); +} + +TEST_CASE("[Vector3i] Length methods") { + const Vector3i vector1 = Vector3i(10, 10, 10); + const Vector3i vector2 = Vector3i(20, 30, 40); + CHECK_MESSAGE( + vector1.length_squared() == 300, + "Vector3i length_squared should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector1.length(), 10 * Math_SQRT3), + "Vector3i length should work as expected."); + CHECK_MESSAGE( + vector2.length_squared() == 2900, + "Vector3i length_squared should work as expected and return exact result."); + CHECK_MESSAGE( + Math::is_equal_approx(vector2.length(), 53.8516480713450403125), + "Vector3i length should work as expected."); +} + +TEST_CASE("[Vector3i] Operators") { + const Vector3i vector1 = Vector3i(4, 5, 9); + const Vector3i vector2 = Vector3i(1, 2, 3); + + CHECK_MESSAGE( + (vector1 + vector2) == Vector3i(5, 7, 12), + "Vector3i addition with integers should give exact results."); + CHECK_MESSAGE( + (vector1 - vector2) == Vector3i(3, 3, 6), + "Vector3i subtraction with integers should give exact results."); + CHECK_MESSAGE( + (vector1 * vector2) == Vector3i(4, 10, 27), + "Vector3i multiplication with integers should give exact results."); + CHECK_MESSAGE( + (vector1 / vector2) == Vector3i(4, 2, 3), + "Vector3i division with integers should give exact results."); + + CHECK_MESSAGE( + (vector1 * 2) == Vector3i(8, 10, 18), + "Vector3i multiplication with integers should give exact results."); + CHECK_MESSAGE( + (vector1 / 2) == Vector3i(2, 2, 4), + "Vector3i division with integers should give exact results."); + + CHECK_MESSAGE( + ((Vector3)vector1) == Vector3(4, 5, 9), + "Vector3i cast to Vector3 should work as expected."); + CHECK_MESSAGE( + ((Vector3)vector2) == Vector3(1, 2, 3), + "Vector3i cast to Vector3 should work as expected."); + CHECK_MESSAGE( + Vector3i(Vector3(1.1, 2.9, 3.9)) == Vector3i(1, 2, 3), + "Vector3i constructed from Vector3 should work as expected."); +} + +TEST_CASE("[Vector3i] Abs and sign methods") { + const Vector3i vector1 = Vector3i(1, 3, 5); + const Vector3i vector2 = Vector3i(1, -3, -5); + CHECK_MESSAGE( + vector1.abs() == vector1, + "Vector3i abs should work as expected."); + CHECK_MESSAGE( + vector2.abs() == vector1, + "Vector3i abs should work as expected."); + + CHECK_MESSAGE( + vector1.sign() == Vector3i(1, 1, 1), + "Vector3i sign should work as expected."); + CHECK_MESSAGE( + vector2.sign() == Vector3i(1, -1, -1), + "Vector3i sign should work as expected."); +} +} // namespace TestVector3i + +#endif // TEST_VECTOR3I_H diff --git a/tests/core/object/test_class_db.h b/tests/core/object/test_class_db.h index 4b27905485..c7535426df 100644 --- a/tests/core/object/test_class_db.h +++ b/tests/core/object/test_class_db.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -46,7 +46,7 @@ struct TypeReference { struct ConstantData { String name; - int value = 0; + int64_t value = 0; }; struct EnumData { @@ -173,7 +173,7 @@ struct NamesCache { } }; -typedef OrderedHashMap<StringName, ExposedClass> ExposedClasses; +typedef HashMap<StringName, ExposedClass> ExposedClasses; struct Context { Vector<StringName> enum_types; @@ -183,13 +183,13 @@ struct Context { NamesCache names_cache; const ExposedClass *find_exposed_class(const StringName &p_name) const { - ExposedClasses::ConstElement elem = exposed_classes.find(p_name); - return elem ? &elem.value() : nullptr; + ExposedClasses::ConstIterator elem = exposed_classes.find(p_name); + return elem ? &elem->value : nullptr; } const ExposedClass *find_exposed_class(const TypeReference &p_type_ref) const { - ExposedClasses::ConstElement elem = exposed_classes.find(p_type_ref.name); - return elem ? &elem.value() : nullptr; + ExposedClasses::ConstIterator elem = exposed_classes.find(p_type_ref.name); + return elem ? &elem->value : nullptr; } bool has_type(const TypeReference &p_type_ref) const { @@ -519,7 +519,7 @@ void add_exposed_classes(Context &r_context) { List<PropertyInfo> property_list; ClassDB::get_property_list(class_name, &property_list, true); - Map<StringName, StringName> accessor_methods; + HashMap<StringName, StringName> accessor_methods; for (const PropertyInfo &property : property_list) { if (property.usage & PROPERTY_USAGE_GROUP || property.usage & PROPERTY_USAGE_SUBGROUP || property.usage & PROPERTY_USAGE_CATEGORY || (property.type == Variant::NIL && property.usage & PROPERTY_USAGE_ARRAY)) { @@ -597,7 +597,7 @@ void add_exposed_classes(Context &r_context) { (exposed_class.name != r_context.names_cache.object_class || String(method.name) != "free"), warn_msg.utf8().get_data()); - } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { + } else if (return_info.type == Variant::INT && return_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) { method.return_type.name = return_info.class_name; method.return_type.is_enum = true; } else if (return_info.class_name != StringName()) { @@ -626,7 +626,7 @@ void add_exposed_classes(Context &r_context) { ArgumentData arg; arg.name = orig_arg_name; - if (arg_info.type == Variant::INT && arg_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { + if (arg_info.type == Variant::INT && arg_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) { arg.type.name = arg_info.class_name; arg.type.is_enum = true; } else if (arg_info.class_name != StringName()) { @@ -671,21 +671,16 @@ void add_exposed_classes(Context &r_context) { } else { exposed_class.methods.push_back(method); } - - if (method.is_virtual) { - TEST_COND(String(method.name)[0] != '_', "Virtual method ", String(method.name), " does not start with underscore."); - } } // Add signals const HashMap<StringName, MethodInfo> &signal_map = class_info->signal_map; - const StringName *k = nullptr; - while ((k = signal_map.next(k))) { + for (const KeyValue<StringName, MethodInfo> &K : signal_map) { SignalData signal; - const MethodInfo &method_info = signal_map.get(*k); + const MethodInfo &method_info = signal_map.get(K.key); signal.name = method_info.name; @@ -699,7 +694,7 @@ void add_exposed_classes(Context &r_context) { ArgumentData arg; arg.name = orig_arg_name; - if (arg_info.type == Variant::INT && arg_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { + if (arg_info.type == Variant::INT && arg_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) { arg.type.name = arg_info.class_name; arg.type.is_enum = true; } else if (arg_info.class_name != StringName()) { @@ -737,20 +732,18 @@ void add_exposed_classes(Context &r_context) { List<String> constants; ClassDB::get_integer_constant_list(class_name, &constants, true); - const HashMap<StringName, List<StringName>> &enum_map = class_info->enum_map; - k = nullptr; + const HashMap<StringName, ClassDB::ClassInfo::EnumInfo> &enum_map = class_info->enum_map; - while ((k = enum_map.next(k))) { + for (const KeyValue<StringName, ClassDB::ClassInfo::EnumInfo> &K : enum_map) { EnumData enum_; - enum_.name = *k; + enum_.name = K.key; - const List<StringName> &enum_constants = enum_map.get(*k); - for (const StringName &E : enum_constants) { + for (const StringName &E : K.value.constants) { const StringName &constant_name = E; TEST_FAIL_COND(String(constant_name).find("::") != -1, "Enum constant contains '::', check bindings to remove the scope: '", String(class_name), ".", String(enum_.name), ".", String(constant_name), "'."); - int *value = class_info->constant_map.getptr(constant_name); + int64_t *value = class_info->constant_map.getptr(constant_name); TEST_FAIL_COND(!value, "Missing enum constant value: '", String(class_name), ".", String(enum_.name), ".", String(constant_name), "'."); constants.erase(constant_name); @@ -764,7 +757,7 @@ void add_exposed_classes(Context &r_context) { exposed_class.enums.push_back(enum_); - r_context.enum_types.push_back(String(class_name) + "." + String(*k)); + r_context.enum_types.push_back(String(class_name) + "." + String(K.key)); } for (const String &E : constants) { @@ -772,7 +765,7 @@ void add_exposed_classes(Context &r_context) { TEST_FAIL_COND(constant_name.find("::") != -1, "Constant contains '::', check bindings to remove the scope: '", String(class_name), ".", constant_name, "'."); - int *value = class_info->constant_map.getptr(StringName(E)); + int64_t *value = class_info->constant_map.getptr(StringName(E)); TEST_FAIL_COND(!value, "Missing constant value: '", String(class_name), ".", String(constant_name), "'."); ConstantData constant; @@ -854,8 +847,8 @@ TEST_SUITE("[ClassDB]") { TEST_FAIL_COND(object_class->base != StringName(), "Object class derives from another class: '", object_class->base, "'."); - for (ExposedClasses::Element E = context.exposed_classes.front(); E; E = E.next()) { - validate_class(context, E.value()); + for (const KeyValue<StringName, ExposedClass> &E : context.exposed_classes) { + validate_class(context, E.value); } } } diff --git a/tests/core/object/test_method_bind.h b/tests/core/object/test_method_bind.h index c3a869a8a4..350a08b6e2 100644 --- a/tests/core/object/test_method_bind.h +++ b/tests/core/object/test_method_bind.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -51,9 +51,15 @@ public: TEST_METHODRC, TEST_METHODRC_ARGS, TEST_METHOD_DEFARGS, + TEST_METHOD_OBJECT_CAST, TEST_MAX }; + class ObjectSubclass : public Object { + public: + int value = 1; + }; + int test_num = 0; bool test_valid[TEST_MAX]; @@ -98,6 +104,10 @@ public: test_valid[TEST_METHOD_DEFARGS] = p_arg1 == 1 && p_arg2 == 2 && p_arg3 == 3 && p_arg4 == 4 && p_arg5 == 5; //temporary } + void test_method_object_cast(ObjectSubclass *p_object) { + test_valid[TEST_METHOD_OBJECT_CAST] = p_object->value == 1; + } + static void _bind_methods() { ClassDB::bind_method(D_METHOD("test_method"), &MethodBindTester::test_method); ClassDB::bind_method(D_METHOD("test_method_args"), &MethodBindTester::test_method_args); @@ -108,6 +118,7 @@ public: ClassDB::bind_method(D_METHOD("test_methodrc"), &MethodBindTester::test_methodrc); ClassDB::bind_method(D_METHOD("test_methodrc_args"), &MethodBindTester::test_methodrc_args); ClassDB::bind_method(D_METHOD("test_method_default_args"), &MethodBindTester::test_method_default_args, DEFVAL(9) /* wrong on purpose */, DEFVAL(4), DEFVAL(5)); + ClassDB::bind_method(D_METHOD("test_method_object_cast", "object"), &MethodBindTester::test_method_object_cast); } virtual void run_tests() { @@ -134,6 +145,10 @@ public: test_valid[TEST_METHODRC_ARGS] = int(call("test_methodrc_args", test_num)) == test_num && test_valid[TEST_METHODRC_ARGS]; call("test_method_default_args", 1, 2, 3, 4); + + ObjectSubclass *obj = memnew(ObjectSubclass); + call("test_method_object_cast", obj); + memdelete(obj); } }; @@ -152,6 +167,7 @@ TEST_CASE("[MethodBind] check all method binds") { CHECK(mbt->test_valid[MethodBindTester::TEST_METHODRC]); CHECK(mbt->test_valid[MethodBindTester::TEST_METHODRC_ARGS]); CHECK(mbt->test_valid[MethodBindTester::TEST_METHOD_DEFARGS]); + CHECK(mbt->test_valid[MethodBindTester::TEST_METHOD_OBJECT_CAST]); memdelete(mbt); } diff --git a/tests/core/object/test_object.h b/tests/core/object/test_object.h index 4109ea521a..88a3e4ccad 100644 --- a/tests/core/object/test_object.h +++ b/tests/core/object/test_object.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -87,7 +87,7 @@ public: bool has_method(const StringName &p_method) const override { return false; } - Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override { + Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override { return Variant(); } void notification(int p_notification) override { @@ -95,8 +95,8 @@ public: Ref<Script> get_script() const override { return Ref<Script>(); } - const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override { - return Vector<Multiplayer::RPCConfig>(); + const Variant get_rpc_config() const override { + return Variant(); } ScriptLanguage *get_language() override { return nullptr; @@ -133,7 +133,7 @@ TEST_CASE("[Object] Core getters") { } TEST_CASE("[Object] Metadata") { - const String meta_path = "hello/world complex métadata\n\n\t\tpath"; + const String meta_path = "complex_metadata_path"; Object object; object.set_meta(meta_path, Color(0, 1, 0)); diff --git a/tests/core/os/test_os.h b/tests/core/os/test_os.h new file mode 100644 index 0000000000..c46da5e156 --- /dev/null +++ b/tests/core/os/test_os.h @@ -0,0 +1,158 @@ +/*************************************************************************/ +/* test_os.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_OS_H +#define TEST_OS_H + +#include "core/os/os.h" + +#include "thirdparty/doctest/doctest.h" + +namespace TestOS { + +TEST_CASE("[OS] Environment variables") { +#ifdef WINDOWS_ENABLED + CHECK_MESSAGE( + OS::get_singleton()->has_environment("USERPROFILE"), + "The USERPROFILE environment variable should be present."); +#else + CHECK_MESSAGE( + OS::get_singleton()->has_environment("HOME"), + "The HOME environment variable should be present."); +#endif + + OS::get_singleton()->set_environment("HELLO", "world"); + CHECK_MESSAGE( + OS::get_singleton()->get_environment("HELLO") == "world", + "The previously-set HELLO environment variable should return the expected value."); +} + +TEST_CASE("[OS] Command line arguments") { + List<String> arguments = OS::get_singleton()->get_cmdline_args(); + bool found = false; + for (int i = 0; i < arguments.size(); i++) { + if (arguments[i] == "--test") { + found = true; + break; + } + } + CHECK_MESSAGE( + found, + "The `--test` option must be present in the list of command line arguments."); +} + +TEST_CASE("[OS] Executable and data paths") { + CHECK_MESSAGE( + OS::get_singleton()->get_executable_path().is_absolute_path(), + "The executable path returned should be an absolute path."); + CHECK_MESSAGE( + OS::get_singleton()->get_data_path().is_absolute_path(), + "The user data path returned should be an absolute path."); + CHECK_MESSAGE( + OS::get_singleton()->get_config_path().is_absolute_path(), + "The user configuration path returned should be an absolute path."); + CHECK_MESSAGE( + OS::get_singleton()->get_cache_path().is_absolute_path(), + "The cache path returned should be an absolute path."); +} + +TEST_CASE("[OS] Ticks") { + CHECK_MESSAGE( + OS::get_singleton()->get_ticks_usec() > 1000, + "The returned ticks (in microseconds) must be greater than 1,000."); + CHECK_MESSAGE( + OS::get_singleton()->get_ticks_msec() > 1, + "The returned ticks (in milliseconds) must be greater than 1."); +} + +TEST_CASE("[OS] Feature tags") { + CHECK_MESSAGE( + OS::get_singleton()->has_feature("editor"), + "The binary has the \"editor\" feature tag."); + CHECK_MESSAGE( + !OS::get_singleton()->has_feature("standalone"), + "The binary does not have the \"standalone\" feature tag."); + CHECK_MESSAGE( + OS::get_singleton()->has_feature("debug"), + "The binary has the \"debug\" feature tag."); + CHECK_MESSAGE( + !OS::get_singleton()->has_feature("release"), + "The binary does not have the \"release\" feature tag."); +} + +TEST_CASE("[OS] Process ID") { + CHECK_MESSAGE( + OS::get_singleton()->get_process_id() >= 1, + "The returned process ID should be greater than zero."); +} + +TEST_CASE("[OS] Processor count and memory information") { + CHECK_MESSAGE( + OS::get_singleton()->get_processor_count() >= 1, + "The returned processor count should be greater than zero."); + CHECK_MESSAGE( + OS::get_singleton()->get_static_memory_usage() >= 1, + "The returned static memory usage should be greater than zero."); + CHECK_MESSAGE( + OS::get_singleton()->get_static_memory_peak_usage() >= 1, + "The returned static memory peak usage should be greater than zero."); +} + +TEST_CASE("[OS] Execute") { +#ifdef WINDOWS_ENABLED + List<String> arguments; + arguments.push_back("/C"); + arguments.push_back("dir > NUL"); + int exit_code; + const Error err = OS::get_singleton()->execute("cmd", arguments, nullptr, &exit_code); + CHECK_MESSAGE( + err == OK, + "(Running the command `cmd /C \"dir > NUL\"` returns the expected Godot error code (OK)."); + CHECK_MESSAGE( + exit_code == 0, + "Running the command `cmd /C \"dir > NUL\"` returns a zero (successful) exit code."); +#else + List<String> arguments; + arguments.push_back("-c"); + arguments.push_back("ls > /dev/null"); + int exit_code; + const Error err = OS::get_singleton()->execute("sh", arguments, nullptr, &exit_code); + CHECK_MESSAGE( + err == OK, + "(Running the command `sh -c \"ls > /dev/null\"` returns the expected Godot error code (OK)."); + CHECK_MESSAGE( + exit_code == 0, + "Running the command `sh -c \"ls > /dev/null\"` returns a zero (successful) exit code."); +#endif +} + +} // namespace TestOS + +#endif // TEST_OS_H diff --git a/tests/core/string/test_node_path.h b/tests/core/string/test_node_path.h index 0216a30f8f..d2de766889 100644 --- a/tests/core/string/test_node_path.h +++ b/tests/core/string/test_node_path.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index eef1cac894..0c5704d6c9 100644 --- a/tests/core/string/test_string.h +++ b/tests/core/string/test_string.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,8 +38,9 @@ namespace TestString { int u32scmp(const char32_t *l, const char32_t *r) { - for (; *l == *r && *l && *r; l++, r++) - ; + for (; *l == *r && *l && *r; l++, r++) { + // Continue. + } return *l - *r; } @@ -88,12 +89,12 @@ TEST_CASE("[String] UTF8") { static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 }; static const uint8_t u8str[] = { 0x45, 0x20, 0xE3, 0x81, 0x8A, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0 }; String s = u32str; - bool err = s.parse_utf8(s.utf8().get_data()); - CHECK(!err); + Error err = s.parse_utf8(s.utf8().get_data()); + CHECK(err == OK); CHECK(s == u32str); err = s.parse_utf8((const char *)u8str); - CHECK(!err); + CHECK(err == OK); CHECK(s == u32str); CharString cs = (const char *)u8str; @@ -105,12 +106,12 @@ TEST_CASE("[String] UTF16") { static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 }; static const char16_t u16str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0xD83C, 0xDFA4, 0 }; String s = u32str; - bool err = s.parse_utf16(s.utf16().get_data()); - CHECK(!err); + Error err = s.parse_utf16(s.utf16().get_data()); + CHECK(err == OK); CHECK(s == u32str); err = s.parse_utf16(u16str); - CHECK(!err); + CHECK(err == OK); CHECK(s == u32str); Char16String cs = u16str; @@ -122,8 +123,8 @@ TEST_CASE("[String] UTF8 with BOM") { static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 }; static const uint8_t u8str[] = { 0xEF, 0xBB, 0xBF, 0x45, 0x20, 0xE3, 0x81, 0x8A, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0 }; String s; - bool err = s.parse_utf8((const char *)u8str); - CHECK(!err); + Error err = s.parse_utf8((const char *)u8str); + CHECK(err == OK); CHECK(s == u32str); CharString cs = (const char *)u8str; @@ -136,12 +137,12 @@ TEST_CASE("[String] UTF16 with BOM") { static const char16_t u16str[] = { 0xFEFF, 0x0020, 0x0045, 0x304A, 0x360F, 0x3088, 0x3046, 0xD83C, 0xDFA4, 0 }; static const char16_t u16str_swap[] = { 0xFFFE, 0x2000, 0x4500, 0x4A30, 0x0F36, 0x8830, 0x4630, 0x3CD8, 0xA4DF, 0 }; String s; - bool err = s.parse_utf16(u16str); - CHECK(!err); + Error err = s.parse_utf16(u16str); + CHECK(err == OK); CHECK(s == u32str); err = s.parse_utf16(u16str_swap); - CHECK(!err); + CHECK(err == OK); CHECK(s == u32str); Char16String cs = u16str; @@ -151,29 +152,48 @@ TEST_CASE("[String] UTF16 with BOM") { CHECK(String::utf16(cs) == s); } -TEST_CASE("[String] Invalid UTF8") { +TEST_CASE("[String] Invalid UTF8 (non-standard)") { ERR_PRINT_OFF - static const uint8_t u8str[] = { 0x45, 0xE3, 0x81, 0x8A, 0x8F, 0xE3, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0 }; + static const uint8_t u8str[] = { 0x45, 0xE3, 0x81, 0x8A, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0xF0, 0x82, 0x82, 0xAC, 0xED, 0xA0, 0x81, 0 }; + // + +2 +2 +2 +3 overlong +3 unpaired +2 + static const char32_t u32str[] = { 0x45, 0x304A, 0x3088, 0x3046, 0x1F3A4, 0x20AC, 0xD801, 0 }; String s; - bool err = s.parse_utf8((const char *)u8str); - CHECK(err); - CHECK(s.is_empty()); + Error err = s.parse_utf8((const char *)u8str); + CHECK(err == ERR_PARSE_ERROR); + CHECK(s == u32str); CharString cs = (const char *)u8str; - CHECK(String::utf8(cs).is_empty()); + CHECK(String::utf8(cs) == s); ERR_PRINT_ON } -TEST_CASE("[String] Invalid UTF16") { +TEST_CASE("[String] Invalid UTF8 (unrecoverable)") { + ERR_PRINT_OFF + static const uint8_t u8str[] = { 0x45, 0xE3, 0x81, 0x8A, 0x8F, 0xE3, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xC0, 0x80, 0xF0, 0x9F, 0x8E, 0xA4, 0xF0, 0x82, 0x82, 0xAC, 0xED, 0xA0, 0x81, 0 }; + // + +2 inv +2 inv inv inv +2 +2 ovl NUL +1 +3 overlong +3 unpaired +2 + static const char32_t u32str[] = { 0x45, 0x304A, 0x20, 0x20, 0x20, 0x20, 0x3088, 0x3046, 0x20, 0x1F3A4, 0x20AC, 0xD801, 0 }; + String s; + Error err = s.parse_utf8((const char *)u8str); + CHECK(err == ERR_INVALID_DATA); + CHECK(s == u32str); + + CharString cs = (const char *)u8str; + CHECK(String::utf8(cs) == s); + ERR_PRINT_ON +} + +TEST_CASE("[String] Invalid UTF16 (non-standard)") { ERR_PRINT_OFF static const char16_t u16str[] = { 0x0045, 0x304A, 0x3088, 0x3046, 0xDFA4, 0 }; + // + + + + unpaired + static const char32_t u32str[] = { 0x0045, 0x304A, 0x3088, 0x3046, 0xDFA4, 0 }; String s; - bool err = s.parse_utf16(u16str); - CHECK(err); - CHECK(s.is_empty()); + Error err = s.parse_utf16(u16str); + CHECK(err == ERR_PARSE_ERROR); + CHECK(s == u32str); Char16String cs = u16str; - CHECK(String::utf16(cs).is_empty()); + CHECK(String::utf16(cs) == s); ERR_PRINT_ON } @@ -244,12 +264,25 @@ TEST_CASE("[String] Testing for empty string") { CHECK(String("").is_empty()); } +TEST_CASE("[String] Contains") { + String s = "C:\\Godot\\project\\string_test.tscn"; + CHECK(s.contains(":\\")); + CHECK(s.contains("Godot")); + CHECK(s.contains(String("project\\string_test"))); + CHECK(s.contains(String("\\string_test.tscn"))); + + CHECK(!s.contains("://")); + CHECK(!s.contains("Godoh")); + CHECK(!s.contains(String("project\\string test"))); + CHECK(!s.contains(String("\\char_test.tscn"))); +} + TEST_CASE("[String] Test chr") { CHECK(String::chr('H') == "H"); CHECK(String::chr(0x3012)[0] == 0x3012); ERR_PRINT_OFF - CHECK(String::chr(0xd812)[0] == 0xfffd); // Unpaired UTF-16 surrogate - CHECK(String::chr(0x20d812)[0] == 0xfffd); // Outside UTF-32 range + CHECK(String::chr(0xd812)[0] == 0xd812); // Unpaired UTF-16 surrogate + CHECK(String::chr(0x20d812)[0] == 0x20d812); // Outside UTF-32 range ERR_PRINT_ON } @@ -355,11 +388,17 @@ TEST_CASE("[String] Number to string") { CHECK(String::num(42.100023, 4) == "42.1"); // No trailing zeros. // String::num_real tests. + CHECK(String::num_real(1.0) == "1.0"); + CHECK(String::num_real(1.0, false) == "1"); + CHECK(String::num_real(9.9) == "9.9"); + CHECK(String::num_real(9.99) == "9.99"); + CHECK(String::num_real(9.999) == "9.999"); + CHECK(String::num_real(9.9999) == "9.9999"); CHECK(String::num_real(3.141593) == "3.141593"); CHECK(String::num_real(3.141) == "3.141"); // No trailing zeros. #ifdef REAL_T_IS_DOUBLE CHECK_MESSAGE(String::num_real(Math_PI) == "3.14159265358979", "Prints the appropriate amount of digits for real_t = double."); - CHECK_MESSAGE(String::num_real(3.1415f) == "3.14149999618530", "Prints more digits of 32-bit float when real_t = double (ones that would be reliable for double)."); + CHECK_MESSAGE(String::num_real(3.1415f) == "3.1414999961853", "Prints more digits of 32-bit float when real_t = double (ones that would be reliable for double) and no trailing zero."); #else CHECK_MESSAGE(String::num_real(Math_PI) == "3.141593", "Prints the appropriate amount of digits for real_t = float."); CHECK_MESSAGE(String::num_real(3.1415f) == "3.1415", "Prints only reliable digits of 32-bit float when real_t = float."); @@ -616,6 +655,38 @@ TEST_CASE("[String] sprintf") { REQUIRE(error == false); CHECK(output == String("fish -5 frog")); + // Negative int left padded with spaces. + format = "fish %5d frog"; + args.clear(); + args.push_back(-5); + output = format.sprintf(args, &error); + REQUIRE(error == false); + CHECK(output == String("fish -5 frog")); + + // Negative int left padded with zeros. + format = "fish %05d frog"; + args.clear(); + args.push_back(-5); + output = format.sprintf(args, &error); + REQUIRE(error == false); + CHECK(output == String("fish -0005 frog")); + + // Negative int right padded with spaces. + format = "fish %-5d frog"; + args.clear(); + args.push_back(-5); + output = format.sprintf(args, &error); + REQUIRE(error == false); + CHECK(output == String("fish -5 frog")); + + // Negative int right padded with zeros. (0 ignored) + format = "fish %-05d frog"; + args.clear(); + args.push_back(-5); + output = format.sprintf(args, &error); + REQUIRE(error == false); + CHECK(output == String("fish -5 frog")); + // Hex (lower) format = "fish %x frog"; args.clear(); @@ -706,6 +777,14 @@ TEST_CASE("[String] sprintf") { REQUIRE(error == false); CHECK(output == String("fish 100 frog")); + // Negative real right padded with zeros. (0 ignored) + format = "fish %-011f frog"; + args.clear(); + args.push_back(-99.99); + output = format.sprintf(args, &error); + REQUIRE(error == false); + CHECK(output == String("fish -99.990000 frog")); + /////// Strings. // String @@ -871,7 +950,7 @@ TEST_CASE("[String] is_subsequence_of") { String a = "is subsequence of"; CHECK(String("sub").is_subsequence_of(a)); CHECK(!String("Sub").is_subsequence_of(a)); - CHECK(String("Sub").is_subsequence_ofi(a)); + CHECK(String("Sub").is_subsequence_ofn(a)); } TEST_CASE("[String] match") { @@ -1065,9 +1144,9 @@ TEST_CASE("[String] lstrip and rstrip") { #undef STRIP_TEST } -TEST_CASE("[String] ensuring empty string into parse_utf8 passes empty string") { +TEST_CASE("[String] Ensuring empty string into parse_utf8 passes empty string") { String empty; - CHECK(empty.parse_utf8(nullptr, -1)); + CHECK(empty.parse_utf8(nullptr, -1) == ERR_INVALID_DATA); } TEST_CASE("[String] Cyrillic to_lower()") { @@ -1132,6 +1211,25 @@ TEST_CASE("[String] c-escape/unescape") { CHECK(s.c_escape().c_unescape() == s); } +TEST_CASE("[String] indent") { + static const char *input[] = { + "", + "aaa\nbbb", + "\tcontains\n\tindent", + "empty\n\nline", + }; + static const char *expected[] = { + "", + "\taaa\n\tbbb", + "\t\tcontains\n\t\tindent", + "\tempty\n\n\tline", + }; + + for (int i = 0; i < 3; i++) { + CHECK(String(input[i]).indent("\t") == expected[i]); + } +} + TEST_CASE("[String] dedent") { String s = " aaa\n bbb"; String t = "aaa\nbbb"; @@ -1361,13 +1459,27 @@ TEST_CASE("[String] validate_node_name") { String name_with_spaces = "Name with spaces"; CHECK(name_with_spaces.validate_node_name() == "Name with spaces"); - String name_with_kana = "Name with kana ゴドツ"; - CHECK(name_with_kana.validate_node_name() == "Name with kana ゴドツ"); + String name_with_kana = U"Name with kana ゴドツ"; + CHECK(name_with_kana.validate_node_name() == U"Name with kana ゴドツ"); String name_with_invalid_chars = "Name with invalid characters :.@removed!"; CHECK(name_with_invalid_chars.validate_node_name() == "Name with invalid characters removed!"); } +TEST_CASE("[String] validate_identifier") { + String empty_string; + CHECK(empty_string.validate_identifier() == "_"); + + String numeric_only = "12345"; + CHECK(numeric_only.validate_identifier() == "_2345"); + + String name_with_spaces = "Name with spaces"; + CHECK(name_with_spaces.validate_identifier() == "Name_with_spaces"); + + String name_with_invalid_chars = String::utf8("Invalid characters:@*#&世界"); + CHECK(name_with_invalid_chars.validate_identifier() == "Invalid_characters_______"); +} + TEST_CASE("[String] Variant indexed get") { Variant s = String("abcd"); bool valid = false; diff --git a/tests/core/string/test_translation.h b/tests/core/string/test_translation.h index 47e06add40..0a1903ccbf 100644 --- a/tests/core/string/test_translation.h +++ b/tests/core/string/test_translation.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -154,7 +154,7 @@ TEST_CASE("[OptimizedTranslation] Generate from Translation and read messages") TEST_CASE("[Translation] CSV import") { Ref<ResourceImporterCSVTranslation> import_csv_translation = memnew(ResourceImporterCSVTranslation); - Map<StringName, Variant> options; + HashMap<StringName, Variant> options; options["compress"] = false; options["delimiter"] = 0; diff --git a/tests/core/templates/test_command_queue.h b/tests/core/templates/test_command_queue.h index 5d228f2bf6..0d016f5d06 100644 --- a/tests/core/templates/test_command_queue.h +++ b/tests/core/templates/test_command_queue.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tests/core/templates/test_ordered_hash_map.h b/tests/core/templates/test_hash_map.h index 35ce0fc656..7a3d5f5d47 100644 --- a/tests/core/templates/test_ordered_hash_map.h +++ b/tests/core/templates/test_hash_map.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* test_ordered_hash_map.h */ +/* test_hash_map.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,56 +28,53 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef TEST_ORDERED_HASH_MAP_H -#define TEST_ORDERED_HASH_MAP_H +#ifndef TEST_HASH_MAP_H +#define TEST_HASH_MAP_H -#include "core/templates/ordered_hash_map.h" +#include "core/templates/hash_map.h" #include "tests/test_macros.h" -namespace TestOrderedHashMap { +namespace TestHashMap { -TEST_CASE("[OrderedHashMap] Insert element") { - OrderedHashMap<int, int> map; - OrderedHashMap<int, int>::Element e = map.insert(42, 84); +TEST_CASE("[HashMap] Insert element") { + HashMap<int, int> map; + HashMap<int, int>::Iterator e = map.insert(42, 84); CHECK(e); - CHECK(e.key() == 42); - CHECK(e.get() == 84); - CHECK(e.value() == 84); + CHECK(e->key == 42); + CHECK(e->value == 84); CHECK(map[42] == 84); CHECK(map.has(42)); CHECK(map.find(42)); } -TEST_CASE("[OrderedHashMap] Overwrite element") { - OrderedHashMap<int, int> map; +TEST_CASE("[HashMap] Overwrite element") { + HashMap<int, int> map; map.insert(42, 84); map.insert(42, 1234); CHECK(map[42] == 1234); } -TEST_CASE("[OrderedHashMap] Erase via element") { - OrderedHashMap<int, int> map; - OrderedHashMap<int, int>::Element e = map.insert(42, 84); - - map.erase(e); - CHECK(!e); +TEST_CASE("[HashMap] Erase via element") { + HashMap<int, int> map; + HashMap<int, int>::Iterator e = map.insert(42, 84); + map.remove(e); CHECK(!map.has(42)); CHECK(!map.find(42)); } -TEST_CASE("[OrderedHashMap] Erase via key") { - OrderedHashMap<int, int> map; +TEST_CASE("[HashMap] Erase via key") { + HashMap<int, int> map; map.insert(42, 84); map.erase(42); CHECK(!map.has(42)); CHECK(!map.find(42)); } -TEST_CASE("[OrderedHashMap] Size") { - OrderedHashMap<int, int> map; +TEST_CASE("[HashMap] Size") { + HashMap<int, int> map; map.insert(42, 84); map.insert(123, 84); map.insert(123, 84); @@ -87,8 +84,8 @@ TEST_CASE("[OrderedHashMap] Size") { CHECK(map.size() == 4); } -TEST_CASE("[OrderedHashMap] Iteration") { - OrderedHashMap<int, int> map; +TEST_CASE("[HashMap] Iteration") { + HashMap<int, int> map; map.insert(42, 84); map.insert(123, 12385); map.insert(0, 12934); @@ -102,34 +99,35 @@ TEST_CASE("[OrderedHashMap] Iteration") { expected.push_back(Pair<int, int>(123485, 1238888)); int idx = 0; - for (OrderedHashMap<int, int>::Element E = map.front(); E; E = E.next()) { - CHECK(expected[idx] == Pair<int, int>(E.key(), E.value())); + for (const KeyValue<int, int> &E : map) { + CHECK(expected[idx] == Pair<int, int>(E.key, E.value)); ++idx; } } -TEST_CASE("[OrderedHashMap] Const iteration") { - OrderedHashMap<int, int> map; +TEST_CASE("[HashMap] Const iteration") { + HashMap<int, int> map; map.insert(42, 84); map.insert(123, 12385); map.insert(0, 12934); map.insert(123485, 1238888); map.insert(123, 111111); - const OrderedHashMap<int, int> const_map = map; + const HashMap<int, int> const_map = map; Vector<Pair<int, int>> expected; expected.push_back(Pair<int, int>(42, 84)); expected.push_back(Pair<int, int>(123, 111111)); expected.push_back(Pair<int, int>(0, 12934)); expected.push_back(Pair<int, int>(123485, 1238888)); + expected.push_back(Pair<int, int>(123, 111111)); int idx = 0; - for (OrderedHashMap<int, int>::ConstElement E = const_map.front(); E; E = E.next()) { - CHECK(expected[idx] == Pair<int, int>(E.key(), E.value())); + for (const KeyValue<int, int> &E : const_map) { + CHECK(expected[idx] == Pair<int, int>(E.key, E.value)); ++idx; } } -} // namespace TestOrderedHashMap +} // namespace TestHashMap -#endif // TEST_ORDERED_HASH_MAP_H +#endif // TEST_HASH_MAP_H diff --git a/tests/core/templates/test_hash_set.h b/tests/core/templates/test_hash_set.h new file mode 100644 index 0000000000..3b9a800641 --- /dev/null +++ b/tests/core/templates/test_hash_set.h @@ -0,0 +1,228 @@ +/*************************************************************************/ +/* test_hash_set.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_HASH_SET_H +#define TEST_HASH_SET_H + +#include "core/templates/hash_set.h" + +#include "tests/test_macros.h" + +namespace TestHashSet { + +TEST_CASE("[HashSet] Insert element") { + print_line("SMALL BEGIN MEM: ", Memory::get_mem_usage()); + HashSet<int> set; + HashSet<int>::Iterator e = set.insert(42); + + CHECK(e); + CHECK(*e == 42); + CHECK(set.has(42)); + CHECK(set.find(42)); + set.reset(); + print_line("SMALL END MEM: ", Memory::get_mem_usage()); +} + +TEST_CASE("[HashSet] Insert existing element") { + HashSet<int> set; + set.insert(42); + set.insert(42); + + CHECK(set.has(42)); + CHECK(set.size() == 1); +} + +TEST_CASE("[HashSet] Insert, iterate and remove many elements") { + const int elem_max = 12343; + HashSet<int> set; + for (int i = 0; i < elem_max; i++) { + set.insert(i); + } + + //insert order should have been kept + int idx = 0; + for (const int &K : set) { + CHECK(idx == K); + CHECK(set.has(idx)); + idx++; + } + + Vector<int> elems_still_valid; + + for (int i = 0; i < elem_max; i++) { + if ((i % 5) == 0) { + set.erase(i); + } else { + elems_still_valid.push_back(i); + } + } + + CHECK(elems_still_valid.size() == set.size()); + + for (int i = 0; i < elems_still_valid.size(); i++) { + CHECK(set.has(elems_still_valid[i])); + } +} + +TEST_CASE("[HashSet] Insert, iterate and remove many strings") { + // This tests a key that uses allocation, to see if any leaks occur + + uint64_t pre_mem = Memory::get_mem_usage(); + const int elem_max = 4018; + HashSet<String> set; + for (int i = 0; i < elem_max; i++) { + set.insert(itos(i)); + } + + //insert order should have been kept + int idx = 0; + for (const String &K : set) { + CHECK(itos(idx) == K); + CHECK(set.has(itos(idx))); + idx++; + } + + Vector<String> elems_still_valid; + + for (int i = 0; i < elem_max; i++) { + if ((i % 5) == 0) { + set.erase(itos(i)); + } else { + elems_still_valid.push_back(itos(i)); + } + } + + CHECK(elems_still_valid.size() == set.size()); + + for (int i = 0; i < elems_still_valid.size(); i++) { + CHECK(set.has(elems_still_valid[i])); + } + + elems_still_valid.clear(); + set.reset(); + + CHECK(Memory::get_mem_usage() == pre_mem); +} + +TEST_CASE("[HashSet] Erase via element") { + HashSet<int> set; + HashSet<int>::Iterator e = set.insert(42); + set.remove(e); + CHECK(!set.has(42)); + CHECK(!set.find(42)); +} + +TEST_CASE("[HashSet] Erase via key") { + HashSet<int> set; + set.insert(42); + set.insert(49); + set.erase(42); + CHECK(!set.has(42)); + CHECK(!set.find(42)); +} + +TEST_CASE("[HashSet] Insert and erase half elements") { + HashSet<int> set; + set.insert(1); + set.insert(2); + set.insert(3); + set.insert(4); + set.erase(1); + set.erase(3); + + CHECK(set.size() == 2); + CHECK(set.has(2)); + CHECK(set.has(4)); +} + +TEST_CASE("[HashSet] Size") { + HashSet<int> set; + set.insert(42); + set.insert(123); + set.insert(123); + set.insert(0); + set.insert(123485); + + CHECK(set.size() == 4); +} + +TEST_CASE("[HashSet] Iteration") { + HashSet<int> set; + set.insert(42); + set.insert(123); + set.insert(0); + set.insert(123485); + + Vector<int> expected; + expected.push_back(42); + expected.push_back(123); + expected.push_back(0); + expected.push_back(123485); + + int idx = 0; + for (const int &E : set) { + CHECK(expected[idx] == E); + ++idx; + } +} + +TEST_CASE("[HashSet] Copy") { + HashSet<int> set; + set.insert(42); + set.insert(123); + set.insert(0); + set.insert(123485); + + Vector<int> expected; + expected.push_back(42); + expected.push_back(123); + expected.push_back(0); + expected.push_back(123485); + + HashSet<int> copy_assign = set; + + int idx = 0; + for (const int &E : copy_assign) { + CHECK(expected[idx] == E); + ++idx; + } + + HashSet<int> copy_construct(set); + + idx = 0; + for (const int &E : copy_construct) { + CHECK(expected[idx] == E); + ++idx; + } +} + +} // namespace TestHashSet + +#endif // TEST_HASH_SET_H diff --git a/tests/core/templates/test_list.h b/tests/core/templates/test_list.h index 52d5edff70..49da0b8aad 100644 --- a/tests/core/templates/test_list.h +++ b/tests/core/templates/test_list.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tests/core/templates/test_local_vector.h b/tests/core/templates/test_local_vector.h index 67bcf515f9..b2464c3914 100644 --- a/tests/core/templates/test_local_vector.h +++ b/tests/core/templates/test_local_vector.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,6 +37,17 @@ namespace TestLocalVector { +TEST_CASE("[LocalVector] List Initialization.") { + LocalVector<int> vector{ 0, 1, 2, 3, 4 }; + + CHECK(vector.size() == 5); + CHECK(vector[0] == 0); + CHECK(vector[1] == 1); + CHECK(vector[2] == 2); + CHECK(vector[3] == 3); + CHECK(vector[4] == 4); +} + TEST_CASE("[LocalVector] Push Back.") { LocalVector<int> vector; vector.push_back(0); diff --git a/tests/core/templates/test_lru.h b/tests/core/templates/test_lru.h index 9359909c53..354f53e164 100644 --- a/tests/core/templates/test_lru.h +++ b/tests/core/templates/test_lru.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tests/core/templates/test_oa_hash_map.cpp b/tests/core/templates/test_oa_hash_map.cpp deleted file mode 100644 index f7b2b7cdb0..0000000000 --- a/tests/core/templates/test_oa_hash_map.cpp +++ /dev/null @@ -1,301 +0,0 @@ -/*************************************************************************/ -/* test_oa_hash_map.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "test_oa_hash_map.h" - -#include "core/os/os.h" -#include "core/templates/oa_hash_map.h" - -namespace TestOAHashMap { - -struct CountedItem { - static int count; - - int id = -1; - bool destroyed = false; - - CountedItem() { - count++; - } - - CountedItem(int p_id) : - id(p_id) { - count++; - } - - CountedItem(const CountedItem &p_other) : - id(p_other.id) { - count++; - } - - void operator=(const CountedItem &p_other) { - id = p_other.id; - count++; - } - - ~CountedItem() { - CRASH_COND(destroyed); - count--; - destroyed = true; - } -}; - -int CountedItem::count; - -MainLoop *test() { - OS::get_singleton()->print("\n\n\nHello from test\n"); - - // test element tracking. - { - OAHashMap<int, int> map; - - map.set(42, 1337); - map.set(1337, 21); - map.set(42, 11880); - - int value = 0; - map.lookup(42, value); - - OS::get_singleton()->print("capacity %d\n", map.get_capacity()); - OS::get_singleton()->print("elements %d\n", map.get_num_elements()); - - OS::get_singleton()->print("map[42] = %d\n", value); - } - - // rehashing and deletion - { - OAHashMap<int, int> map; - - for (int i = 0; i < 500; i++) { - map.set(i, i * 2); - } - - for (int i = 0; i < 500; i += 2) { - map.remove(i); - } - - uint32_t num_elems = 0; - for (int i = 0; i < 500; i++) { - int tmp; - if (map.lookup(i, tmp) && tmp == i * 2) { - num_elems++; - } - } - - OS::get_singleton()->print("elements %d == %d.\n", map.get_num_elements(), num_elems); - } - - // iteration - { - OAHashMap<String, int> map; - - map.set("Hello", 1); - map.set("World", 2); - map.set("Godot rocks", 42); - - for (OAHashMap<String, int>::Iterator it = map.iter(); it.valid; it = map.next_iter(it)) { - OS::get_singleton()->print("map[\"%s\"] = %d\n", it.key->utf8().get_data(), *it.value); - } - } - - // stress test / test for issue #22928 - { - OAHashMap<int, int> map; - int dummy = 0; - const int N = 1000; - uint32_t *keys = new uint32_t[N]; - - Math::seed(0); - - // insert a couple of random keys (with a dummy value, which is ignored) - for (int i = 0; i < N; i++) { - keys[i] = Math::rand(); - map.set(keys[i], dummy); - - if (!map.lookup(keys[i], dummy)) { - OS::get_singleton()->print("could not find 0x%X despite it was just inserted!\n", unsigned(keys[i])); - } - } - - // check whether the keys are still present - for (int i = 0; i < N; i++) { - if (!map.lookup(keys[i], dummy)) { - OS::get_singleton()->print("could not find 0x%X despite it has been inserted previously! (not checking the other keys, breaking...)\n", unsigned(keys[i])); - break; - } - } - - delete[] keys; - } - - // regression test / test for issue related to #31402 - { - OS::get_singleton()->print("test for issue #31402 started...\n"); - - const int num_test_values = 12; - int test_values[num_test_values] = { 0, 24, 48, 72, 96, 120, 144, 168, 192, 216, 240, 264 }; - - int dummy = 0; - OAHashMap<int, int> map; - map.clear(); - - for (int i = 0; i < num_test_values; ++i) { - map.set(test_values[i], dummy); - } - - OS::get_singleton()->print("test for issue #31402 passed.\n"); - } - - // test collision resolution, should not crash or run indefinitely - { - OAHashMap<int, int> map(4); - map.set(1, 1); - map.set(5, 1); - map.set(9, 1); - map.set(13, 1); - map.remove(5); - map.remove(9); - map.remove(13); - map.set(5, 1); - } - - // test memory management of items, should not crash or leak items - { - // Exercise different patterns of removal - for (int i = 0; i < 4; ++i) { - { - OAHashMap<String, CountedItem> map; - int id = 0; - for (int j = 0; j < 100; ++j) { - map.insert(itos(j), CountedItem(id)); - } - if (i <= 1) { - for (int j = 0; j < 100; ++j) { - map.remove(itos(j)); - } - } - if (i % 2 == 0) { - map.clear(); - } - } - - if (CountedItem::count != 0) { - OS::get_singleton()->print("%d != 0 (not performing the other test sub-cases, breaking...)\n", CountedItem::count); - break; - } - } - } - - // Test map with 0 capacity. - { - OAHashMap<int, String> original_map(0); - original_map.set(1, "1"); - OS::get_singleton()->print("OAHashMap 0 capacity initialization passed.\n"); - } - - // Test copy constructor. - { - OAHashMap<int, String> original_map; - original_map.set(1, "1"); - original_map.set(2, "2"); - original_map.set(3, "3"); - original_map.set(4, "4"); - original_map.set(5, "5"); - - OAHashMap<int, String> map_copy(original_map); - - bool pass = true; - for ( - OAHashMap<int, String>::Iterator it = original_map.iter(); - it.valid; - it = original_map.next_iter(it)) { - if (map_copy.lookup_ptr(*it.key) == nullptr) { - pass = false; - } - if (*it.value != *map_copy.lookup_ptr(*it.key)) { - pass = false; - } - } - if (pass) { - OS::get_singleton()->print("OAHashMap copy constructor test passed.\n"); - } else { - OS::get_singleton()->print("OAHashMap copy constructor test FAILED.\n"); - } - - map_copy.set(1, "Random String"); - if (*map_copy.lookup_ptr(1) == *original_map.lookup_ptr(1)) { - OS::get_singleton()->print("OAHashMap copy constructor, atomic copy test FAILED.\n"); - } else { - OS::get_singleton()->print("OAHashMap copy constructor, atomic copy test passed.\n"); - } - } - - // Test assign operator. - { - OAHashMap<int, String> original_map; - original_map.set(1, "1"); - original_map.set(2, "2"); - original_map.set(3, "3"); - original_map.set(4, "4"); - original_map.set(5, "5"); - - OAHashMap<int, String> map_copy(100000); - map_copy.set(1, "Just a string."); - map_copy = original_map; - - bool pass = true; - for ( - OAHashMap<int, String>::Iterator it = map_copy.iter(); - it.valid; - it = map_copy.next_iter(it)) { - if (original_map.lookup_ptr(*it.key) == nullptr) { - pass = false; - } - if (*it.value != *original_map.lookup_ptr(*it.key)) { - pass = false; - } - } - if (pass) { - OS::get_singleton()->print("OAHashMap assign operation test passed.\n"); - } else { - OS::get_singleton()->print("OAHashMap assign operation test FAILED.\n"); - } - - map_copy.set(1, "Random String"); - if (*map_copy.lookup_ptr(1) == *original_map.lookup_ptr(1)) { - OS::get_singleton()->print("OAHashMap assign operation atomic copy test FAILED.\n"); - } else { - OS::get_singleton()->print("OAHashMap assign operation atomic copy test passed.\n"); - } - } - - return nullptr; -} -} // namespace TestOAHashMap diff --git a/tests/core/templates/test_oa_hash_map.h b/tests/core/templates/test_oa_hash_map.h deleted file mode 100644 index f229ac94ea..0000000000 --- a/tests/core/templates/test_oa_hash_map.h +++ /dev/null @@ -1,41 +0,0 @@ -/*************************************************************************/ -/* test_oa_hash_map.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef TEST_OA_HASH_MAP_H -#define TEST_OA_HASH_MAP_H - -class MainLoop; - -namespace TestOAHashMap { - -MainLoop *test(); -} - -#endif // TEST_OA_HASH_MAP_H diff --git a/tests/core/templates/test_paged_array.h b/tests/core/templates/test_paged_array.h index 7efd3799f3..86cf3a2dfc 100644 --- a/tests/core/templates/test_paged_array.h +++ b/tests/core/templates/test_paged_array.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tests/core/templates/test_vector.h b/tests/core/templates/test_vector.h index 6ea865dacc..f27d6a332e 100644 --- a/tests/core/templates/test_vector.h +++ b/tests/core/templates/test_vector.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,6 +37,17 @@ namespace TestVector { +TEST_CASE("[Vector] List initialization") { + Vector<int> vector{ 0, 1, 2, 3, 4 }; + + CHECK(vector.size() == 5); + CHECK(vector[0] == 0); + CHECK(vector[1] == 1); + CHECK(vector[2] == 2); + CHECK(vector[3] == 3); + CHECK(vector[4] == 4); +} + TEST_CASE("[Vector] Push back and append") { Vector<int> vector; vector.push_back(0); @@ -246,27 +257,42 @@ TEST_CASE("[Vector] Slice") { vector.push_back(3); vector.push_back(4); + Vector<int> slice0 = vector.slice(0, 0); + CHECK(slice0.size() == 0); + Vector<int> slice1 = vector.slice(1, 3); CHECK(slice1.size() == 2); CHECK(slice1[0] == 1); CHECK(slice1[1] == 2); Vector<int> slice2 = vector.slice(1, -1); - CHECK(slice2.size() == 4); + CHECK(slice2.size() == 3); CHECK(slice2[0] == 1); CHECK(slice2[1] == 2); CHECK(slice2[2] == 3); - CHECK(slice2[3] == 4); - Vector<int> slice3 = vector.slice(3, -1); + Vector<int> slice3 = vector.slice(3); CHECK(slice3.size() == 2); CHECK(slice3[0] == 3); CHECK(slice3[1] == 4); Vector<int> slice4 = vector.slice(2, -2); - CHECK(slice4.size() == 2); + CHECK(slice4.size() == 1); CHECK(slice4[0] == 2); - CHECK(slice4[1] == 3); + + Vector<int> slice5 = vector.slice(-2); + CHECK(slice5.size() == 2); + CHECK(slice5[0] == 3); + CHECK(slice5[1] == 4); + + Vector<int> slice6 = vector.slice(2, 42); + CHECK(slice6.size() == 3); + CHECK(slice6[0] == 2); + CHECK(slice6[1] == 3); + CHECK(slice6[2] == 4); + + Vector<int> slice7 = vector.slice(5, 1); + CHECK(slice7.size() == 0); } TEST_CASE("[Vector] Find, has") { diff --git a/tests/core/test_crypto.h b/tests/core/test_crypto.h index 3b909c7df8..ce4edc71ae 100644 --- a/tests/core/test_crypto.h +++ b/tests/core/test_crypto.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tests/core/test_hashing_context.h b/tests/core/test_hashing_context.h index 728a5f2cfa..4795d24103 100644 --- a/tests/core/test_hashing_context.h +++ b/tests/core/test_hashing_context.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/tests/core/test_time.h b/tests/core/test_time.h index 28f1cb2f20..177512c832 100644 --- a/tests/core/test_time.h +++ b/tests/core/test_time.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -79,6 +79,9 @@ TEST_CASE("[Time] Unix time conversion to/from datetime string") { CHECK_MESSAGE(time->get_date_string_from_unix_time(1391904000) == "2014-02-09", "Time get_date_string_from_unix_time: The date for GODOT IS OPEN SOURCE without time is as expected."); CHECK_MESSAGE(time->get_time_string_from_unix_time(79830) == "22:10:30", "Time get_time_string_from_unix_time: The time for GODOT IS OPEN SOURCE without date is as expected."); CHECK_MESSAGE(time->get_datetime_string_from_unix_time(31494784780800) == "1000000-01-01T00:00:00", "Time get_datetime_string_from_unix_time: The timestamp for the year a million is as expected."); + CHECK_MESSAGE(time->get_offset_string_from_offset_minutes(0) == "+00:00", "Time get_offset_string_from_offset_minutes: The offset string is as expected."); + CHECK_MESSAGE(time->get_offset_string_from_offset_minutes(-600) == "-10:00", "Time get_offset_string_from_offset_minutes: The offset string is as expected."); + CHECK_MESSAGE(time->get_offset_string_from_offset_minutes(345) == "+05:45", "Time get_offset_string_from_offset_minutes: The offset string is as expected."); } TEST_CASE("[Time] Datetime dictionary conversion methods") { @@ -115,11 +118,11 @@ TEST_CASE("[Time] Datetime dictionary conversion methods") { CHECK_MESSAGE((Time::Weekday)(int)time->get_datetime_dict_from_unix_time(0)[WEEKDAY_KEY] == Time::Weekday::WEEKDAY_THURSDAY, "Time get_datetime_dict_from_unix_time: The weekday for the Unix epoch is a Thursday as expected."); CHECK_MESSAGE((Time::Weekday)(int)time->get_datetime_dict_from_unix_time(1391983830)[WEEKDAY_KEY] == Time::Weekday::WEEKDAY_SUNDAY, "Time get_datetime_dict_from_unix_time: The weekday for GODOT IS OPEN SOURCE is a Sunday as expected."); - CHECK_MESSAGE(time->get_datetime_dict_from_string("2014-02-09T22:10:30").hash() == datetime.hash(), "Time get_datetime_dict_from_string: The dictionary from string for GODOT IS OPEN SOURCE works as expected."); - CHECK_MESSAGE(!time->get_datetime_dict_from_string("2014-02-09T22:10:30", false).has(WEEKDAY_KEY), "Time get_datetime_dict_from_string: The dictionary from string for GODOT IS OPEN SOURCE without weekday doesn't contain the weekday key as expected."); - CHECK_MESSAGE(time->get_datetime_string_from_dict(datetime) == "2014-02-09T22:10:30", "Time get_datetime_string_from_dict: The string from dictionary for GODOT IS OPEN SOURCE works as expected."); - CHECK_MESSAGE(time->get_datetime_string_from_dict(time->get_datetime_dict_from_string("2014-02-09T22:10:30")) == "2014-02-09T22:10:30", "Time get_datetime_string_from_dict: The round-trip string to dict to string GODOT IS OPEN SOURCE works as expected."); - CHECK_MESSAGE(time->get_datetime_string_from_dict(time->get_datetime_dict_from_string("2014-02-09 22:10:30"), true) == "2014-02-09 22:10:30", "Time get_datetime_string_from_dict: The round-trip string to dict to string GODOT IS OPEN SOURCE with spaces works as expected."); + CHECK_MESSAGE(time->get_datetime_dict_from_datetime_string("2014-02-09T22:10:30").hash() == datetime.hash(), "Time get_datetime_dict_from_string: The dictionary from string for GODOT IS OPEN SOURCE works as expected."); + CHECK_MESSAGE(!time->get_datetime_dict_from_datetime_string("2014-02-09T22:10:30", false).has(WEEKDAY_KEY), "Time get_datetime_dict_from_string: The dictionary from string for GODOT IS OPEN SOURCE without weekday doesn't contain the weekday key as expected."); + CHECK_MESSAGE(time->get_datetime_string_from_datetime_dict(datetime) == "2014-02-09T22:10:30", "Time get_datetime_string_from_dict: The string from dictionary for GODOT IS OPEN SOURCE works as expected."); + CHECK_MESSAGE(time->get_datetime_string_from_datetime_dict(time->get_datetime_dict_from_datetime_string("2014-02-09T22:10:30")) == "2014-02-09T22:10:30", "Time get_datetime_string_from_dict: The round-trip string to dict to string GODOT IS OPEN SOURCE works as expected."); + CHECK_MESSAGE(time->get_datetime_string_from_datetime_dict(time->get_datetime_dict_from_datetime_string("2014-02-09 22:10:30"), true) == "2014-02-09 22:10:30", "Time get_datetime_string_from_dict: The round-trip string to dict to string GODOT IS OPEN SOURCE with spaces works as expected."); } TEST_CASE("[Time] System time methods") { diff --git a/tests/core/threads/test_worker_thread_pool.h b/tests/core/threads/test_worker_thread_pool.h new file mode 100644 index 0000000000..641b293c8a --- /dev/null +++ b/tests/core/threads/test_worker_thread_pool.h @@ -0,0 +1,158 @@ +/*************************************************************************/ +/* test_worker_thread_pool.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_WORKER_THREAD_POOL_H +#define TEST_WORKER_THREAD_POOL_H + +#include "core/object/worker_thread_pool.h" + +#include "tests/test_macros.h" + +namespace TestWorkerThreadPool { + +int u32scmp(const char32_t *l, const char32_t *r) { + for (; *l == *r && *l && *r; l++, r++) { + // Continue. + } + return *l - *r; +} + +static void static_test(void *p_arg) { + SafeNumeric<uint32_t> *counter = (SafeNumeric<uint32_t> *)p_arg; + counter->increment(); +} + +static SafeNumeric<uint32_t> callable_counter; + +static void static_callable_test() { + callable_counter.increment(); +} + +TEST_CASE("[WorkerThreadPool] Process 256 threads using native task") { + const int count = 256; + SafeNumeric<uint32_t> counter; + WorkerThreadPool::TaskID tasks[count]; + for (int i = 0; i < count; i++) { + tasks[i] = WorkerThreadPool::get_singleton()->add_native_task(static_test, &counter, true); + } + for (int i = 0; i < count; i++) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(tasks[i]); + } + + CHECK(counter.get() == count); +} + +TEST_CASE("[WorkerThreadPool] Process 256 threads using native low priority") { + const int count = 256; + SafeNumeric<uint32_t> counter = SafeNumeric<uint32_t>(0); + WorkerThreadPool::TaskID tasks[count]; + for (int i = 0; i < count; i++) { + tasks[i] = WorkerThreadPool::get_singleton()->add_native_task(static_test, &counter, false); + } + for (int i = 0; i < count; i++) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(tasks[i]); + } + + CHECK(counter.get() == count); +} + +TEST_CASE("[WorkerThreadPool] Process 256 threads using callable") { + const int count = 256; + WorkerThreadPool::TaskID tasks[count]; + callable_counter.set(0); + for (int i = 0; i < count; i++) { + tasks[i] = WorkerThreadPool::get_singleton()->add_task(callable_mp_static(static_callable_test), true); + } + for (int i = 0; i < count; i++) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(tasks[i]); + } + + CHECK(callable_counter.get() == count); +} + +TEST_CASE("[WorkerThreadPool] Process 256 threads using callable low priority") { + const int count = 256; + WorkerThreadPool::TaskID tasks[count]; + callable_counter.set(0); + for (int i = 0; i < count; i++) { + tasks[i] = WorkerThreadPool::get_singleton()->add_task(callable_mp_static(static_callable_test), false); + } + for (int i = 0; i < count; i++) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(tasks[i]); + } + + CHECK(callable_counter.get() == count); +} + +static void static_group_test(void *p_arg, uint32_t p_index) { + SafeNumeric<uint32_t> *counter = (SafeNumeric<uint32_t> *)p_arg; + counter->exchange_if_greater(p_index); +} + +TEST_CASE("[WorkerThreadPool] Process 256 elements on native task group") { + const int count = 256; + SafeNumeric<uint32_t> counter; + WorkerThreadPool::GroupID group = WorkerThreadPool::get_singleton()->add_native_group_task(static_group_test, &counter, count, -1, true); + WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group); + CHECK(counter.get() == count - 1); +} + +TEST_CASE("[WorkerThreadPool] Process 256 elements on native task group low priority") { + const int count = 256; + SafeNumeric<uint32_t> counter; + WorkerThreadPool::GroupID group = WorkerThreadPool::get_singleton()->add_native_group_task(static_group_test, &counter, count, -1, false); + WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group); + CHECK(counter.get() == count - 1); +} + +static SafeNumeric<uint32_t> callable_group_counter; + +static void static_callable_group_test(uint32_t p_index) { + callable_group_counter.exchange_if_greater(p_index); +} + +TEST_CASE("[WorkerThreadPool] Process 256 elements on native task group") { + const int count = 256; + WorkerThreadPool::GroupID group = WorkerThreadPool::get_singleton()->add_group_task(callable_mp_static(static_callable_group_test), count, -1, true); + WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group); + CHECK(callable_group_counter.get() == count - 1); +} + +TEST_CASE("[WorkerThreadPool] Process 256 elements on native task group low priority") { + const int count = 256; + callable_group_counter.set(0); + WorkerThreadPool::GroupID group = WorkerThreadPool::get_singleton()->add_group_task(callable_mp_static(static_callable_group_test), count, -1, false); + WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group); + CHECK(callable_group_counter.get() == count - 1); +} + +} // namespace TestWorkerThreadPool + +#endif // TEST_WORKER_THREAD_POOL_H diff --git a/tests/core/variant/test_array.h b/tests/core/variant/test_array.h index d02b3d0e39..6093048307 100644 --- a/tests/core/variant/test_array.h +++ b/tests/core/variant/test_array.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -254,27 +254,52 @@ TEST_CASE("[Array] slice()") { array.push_back(3); array.push_back(4); + Array slice0 = array.slice(0, 0); + CHECK(slice0.size() == 0); + Array slice1 = array.slice(1, 3); CHECK(slice1.size() == 2); CHECK(slice1[0] == Variant(1)); CHECK(slice1[1] == Variant(2)); Array slice2 = array.slice(1, -1); - CHECK(slice2.size() == 4); + CHECK(slice2.size() == 3); CHECK(slice2[0] == Variant(1)); CHECK(slice2[1] == Variant(2)); CHECK(slice2[2] == Variant(3)); - CHECK(slice2[3] == Variant(4)); - Array slice3 = array.slice(3, -1); + Array slice3 = array.slice(3); CHECK(slice3.size() == 2); CHECK(slice3[0] == Variant(3)); CHECK(slice3[1] == Variant(4)); Array slice4 = array.slice(2, -2); - CHECK(slice4.size() == 2); + CHECK(slice4.size() == 1); CHECK(slice4[0] == Variant(2)); - CHECK(slice4[1] == Variant(3)); + + Array slice5 = array.slice(-2); + CHECK(slice5.size() == 2); + CHECK(slice5[0] == Variant(3)); + CHECK(slice5[1] == Variant(4)); + + Array slice6 = array.slice(2, 42); + CHECK(slice6.size() == 3); + CHECK(slice6[0] == Variant(2)); + CHECK(slice6[1] == Variant(3)); + CHECK(slice6[2] == Variant(4)); + + Array slice7 = array.slice(4, 0, -2); + CHECK(slice7.size() == 2); + CHECK(slice7[0] == Variant(4)); + CHECK(slice7[1] == Variant(2)); + + ERR_PRINT_OFF; + Array slice8 = array.slice(4, 1); + CHECK(slice8.size() == 0); + + Array slice9 = array.slice(3, -4); + CHECK(slice9.size() == 0); + ERR_PRINT_ON; } TEST_CASE("[Array] Duplicate array") { diff --git a/tests/core/variant/test_dictionary.h b/tests/core/variant/test_dictionary.h index 65079698a3..729035919d 100644 --- a/tests/core/variant/test_dictionary.h +++ b/tests/core/variant/test_dictionary.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -196,7 +196,7 @@ TEST_CASE("[Dictionary] Duplicate dictionary") { Dictionary shallow_d = d.duplicate(false); CHECK_MESSAGE(shallow_d.id() != d.id(), "Should create a new array"); CHECK_MESSAGE(Dictionary(shallow_d[1]).id() == Dictionary(d[1]).id(), "Should keep nested dictionary"); - CHECK_MESSAGE(Array(shallow_d[2]).id() == Array(d[2]).id(), "Should keep nested array"); + CHECK_MESSAGE(Array(shallow_d[k2]).id() == Array(d[k2]).id(), "Should keep nested array"); CHECK_EQ(shallow_d, d); shallow_d[0] = 0; CHECK_NE(shallow_d, d); diff --git a/tests/core/variant/test_variant.h b/tests/core/variant/test_variant.h index 0d16fa092c..916686d7c1 100644 --- a/tests/core/variant/test_variant.h +++ b/tests/core/variant/test_variant.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ |