diff options
Diffstat (limited to 'tests/scene')
-rw-r--r-- | tests/scene/test_animation.h | 148 | ||||
-rw-r--r-- | tests/scene/test_arraymesh.h | 325 | ||||
-rw-r--r-- | tests/scene/test_audio_stream_wav.h | 60 | ||||
-rw-r--r-- | tests/scene/test_bit_map.h | 148 | ||||
-rw-r--r-- | tests/scene/test_code_edit.h | 152 | ||||
-rw-r--r-- | tests/scene/test_curve.h | 120 | ||||
-rw-r--r-- | tests/scene/test_gradient.h | 68 | ||||
-rw-r--r-- | tests/scene/test_node.h | 415 | ||||
-rw-r--r-- | tests/scene/test_path_2d.h | 109 | ||||
-rw-r--r-- | tests/scene/test_path_3d.h | 58 | ||||
-rw-r--r-- | tests/scene/test_path_follow_2d.h | 58 | ||||
-rw-r--r-- | tests/scene/test_path_follow_3d.h | 58 | ||||
-rw-r--r-- | tests/scene/test_primitives.h | 850 | ||||
-rw-r--r-- | tests/scene/test_sprite_frames.h | 66 | ||||
-rw-r--r-- | tests/scene/test_text_edit.h | 1362 | ||||
-rw-r--r-- | tests/scene/test_theme.h | 58 | ||||
-rw-r--r-- | tests/scene/test_visual_shader.h | 151 |
17 files changed, 3480 insertions, 726 deletions
diff --git a/tests/scene/test_animation.h b/tests/scene/test_animation.h index 9199713fd9..0e16fc104c 100644 --- a/tests/scene/test_animation.h +++ b/tests/scene/test_animation.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* test_animation.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. */ -/*************************************************************************/ +/**************************************************************************/ +/* test_animation.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef TEST_ANIMATION_H #define TEST_ANIMATION_H @@ -40,8 +40,8 @@ namespace TestAnimation { TEST_CASE("[Animation] Empty animation getters") { const Ref<Animation> animation = memnew(Animation); - CHECK(Math::is_equal_approx(animation->get_length(), real_t(1.0))); - CHECK(Math::is_equal_approx(animation->get_step(), real_t(0.1))); + CHECK(animation->get_length() == doctest::Approx(real_t(1.0))); + CHECK(animation->get_step() == doctest::Approx(real_t(0.1))); } TEST_CASE("[Animation] Create value track") { @@ -59,33 +59,33 @@ TEST_CASE("[Animation] Create value track") { CHECK(int(animation->track_get_key_value(0, 0)) == 0); CHECK(int(animation->track_get_key_value(0, 1)) == 100); - CHECK(Math::is_equal_approx(animation->value_track_interpolate(0, -0.2), 0.0)); - CHECK(Math::is_equal_approx(animation->value_track_interpolate(0, 0.0), 0.0)); - CHECK(Math::is_equal_approx(animation->value_track_interpolate(0, 0.2), 40.0)); - CHECK(Math::is_equal_approx(animation->value_track_interpolate(0, 0.4), 80.0)); - CHECK(Math::is_equal_approx(animation->value_track_interpolate(0, 0.5), 100.0)); - CHECK(Math::is_equal_approx(animation->value_track_interpolate(0, 0.6), 100.0)); + CHECK(animation->value_track_interpolate(0, -0.2) == doctest::Approx(0.0)); + CHECK(animation->value_track_interpolate(0, 0.0) == doctest::Approx(0.0)); + CHECK(animation->value_track_interpolate(0, 0.2) == doctest::Approx(40.0)); + CHECK(animation->value_track_interpolate(0, 0.4) == doctest::Approx(80.0)); + CHECK(animation->value_track_interpolate(0, 0.5) == doctest::Approx(100.0)); + CHECK(animation->value_track_interpolate(0, 0.6) == doctest::Approx(100.0)); - CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 0), real_t(1.0))); - CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 1), real_t(1.0))); + CHECK(animation->track_get_key_transition(0, 0) == doctest::Approx(real_t(1.0))); + CHECK(animation->track_get_key_transition(0, 1) == doctest::Approx(real_t(1.0))); ERR_PRINT_OFF; // Nonexistent keys. CHECK(animation->track_get_key_value(0, 2).is_null()); CHECK(animation->track_get_key_value(0, -1).is_null()); - CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 2), real_t(-1.0))); + CHECK(animation->track_get_key_transition(0, 2) == doctest::Approx(real_t(-1.0))); // Nonexistent track (and keys). CHECK(animation->track_get_key_value(1, 0).is_null()); CHECK(animation->track_get_key_value(1, 1).is_null()); CHECK(animation->track_get_key_value(1, 2).is_null()); CHECK(animation->track_get_key_value(1, -1).is_null()); - CHECK(Math::is_equal_approx(animation->track_get_key_transition(1, 0), real_t(-1.0))); + CHECK(animation->track_get_key_transition(1, 0) == doctest::Approx(real_t(-1.0))); // This is a value track, so the methods below should return errors. CHECK(animation->position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER); CHECK(animation->rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER); CHECK(animation->scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER); - CHECK(Math::is_zero_approx(animation->bezier_track_interpolate(0, 0.0))); + CHECK(animation->bezier_track_interpolate(0, 0.0) == doctest::Approx(0.0)); CHECK(animation->blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER); ERR_PRINT_ON; } @@ -123,15 +123,15 @@ TEST_CASE("[Animation] Create 3D position track") { CHECK(r_interpolation.is_equal_approx(Vector3(3.5, 4, 5))); // 3D position tracks always use linear interpolation for performance reasons. - CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 0), real_t(1.0))); - CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 1), real_t(1.0))); + CHECK(animation->track_get_key_transition(0, 0) == doctest::Approx(real_t(1.0))); + CHECK(animation->track_get_key_transition(0, 1) == doctest::Approx(real_t(1.0))); // This is a 3D position track, so the methods below should return errors. ERR_PRINT_OFF; CHECK(animation->value_track_interpolate(0, 0.0).is_null()); CHECK(animation->rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER); CHECK(animation->scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER); - CHECK(Math::is_zero_approx(animation->bezier_track_interpolate(0, 0.0))); + CHECK(animation->bezier_track_interpolate(0, 0.0) == doctest::Approx(0.0)); CHECK(animation->blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER); ERR_PRINT_ON; } @@ -140,13 +140,13 @@ TEST_CASE("[Animation] Create 3D rotation track") { Ref<Animation> animation = memnew(Animation); const int track_index = animation->add_track(Animation::TYPE_ROTATION_3D); animation->track_set_path(track_index, NodePath("Enemy:rotation")); - animation->rotation_track_insert_key(track_index, 0.0, Quaternion(Vector3(0, 1, 2))); - animation->rotation_track_insert_key(track_index, 0.5, Quaternion(Vector3(3.5, 4, 5))); + animation->rotation_track_insert_key(track_index, 0.0, Quaternion::from_euler(Vector3(0, 1, 2))); + animation->rotation_track_insert_key(track_index, 0.5, Quaternion::from_euler(Vector3(3.5, 4, 5))); CHECK(animation->get_track_count() == 1); CHECK(!animation->track_is_compressed(0)); - CHECK(Quaternion(animation->track_get_key_value(0, 0)).is_equal_approx(Quaternion(Vector3(0, 1, 2)))); - CHECK(Quaternion(animation->track_get_key_value(0, 1)).is_equal_approx(Quaternion(Vector3(3.5, 4, 5)))); + CHECK(Quaternion(animation->track_get_key_value(0, 0)).is_equal_approx(Quaternion::from_euler(Vector3(0, 1, 2)))); + CHECK(Quaternion(animation->track_get_key_value(0, 1)).is_equal_approx(Quaternion::from_euler(Vector3(3.5, 4, 5)))); Quaternion r_interpolation; @@ -169,15 +169,15 @@ TEST_CASE("[Animation] Create 3D rotation track") { CHECK(r_interpolation.is_equal_approx(Quaternion(0.231055, 0.374912, 0.761204, 0.476048))); // 3D rotation tracks always use linear interpolation for performance reasons. - CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 0), real_t(1.0))); - CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 1), real_t(1.0))); + CHECK(animation->track_get_key_transition(0, 0) == doctest::Approx(real_t(1.0))); + CHECK(animation->track_get_key_transition(0, 1) == doctest::Approx(real_t(1.0))); // This is a 3D rotation track, so the methods below should return errors. ERR_PRINT_OFF; CHECK(animation->value_track_interpolate(0, 0.0).is_null()); CHECK(animation->position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER); CHECK(animation->scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER); - CHECK(Math::is_zero_approx(animation->bezier_track_interpolate(0, 0.0))); + CHECK(animation->bezier_track_interpolate(0, 0.0) == doctest::Approx(real_t(0.0))); CHECK(animation->blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER); ERR_PRINT_ON; } @@ -215,15 +215,15 @@ TEST_CASE("[Animation] Create 3D scale track") { CHECK(r_interpolation.is_equal_approx(Vector3(3.5, 4, 5))); // 3D scale tracks always use linear interpolation for performance reasons. - CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 0), real_t(1.0))); - CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 1), real_t(1.0))); + CHECK(animation->track_get_key_transition(0, 0) == doctest::Approx(1.0)); + CHECK(animation->track_get_key_transition(0, 1) == doctest::Approx(1.0)); // This is a 3D scale track, so the methods below should return errors. ERR_PRINT_OFF; CHECK(animation->value_track_interpolate(0, 0.0).is_null()); CHECK(animation->position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER); CHECK(animation->rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER); - CHECK(Math::is_zero_approx(animation->bezier_track_interpolate(0, 0.0))); + CHECK(animation->bezier_track_interpolate(0, 0.0) == doctest::Approx(0.0)); CHECK(animation->blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER); ERR_PRINT_ON; } @@ -242,32 +242,32 @@ TEST_CASE("[Animation] Create blend shape track") { float r_blend = 0.0f; CHECK(animation->blend_shape_track_get_key(0, 0, &r_blend) == OK); - CHECK(Math::is_equal_approx(r_blend, -1.0f)); + CHECK(r_blend == doctest::Approx(-1.0f)); CHECK(animation->blend_shape_track_get_key(0, 1, &r_blend) == OK); - CHECK(Math::is_equal_approx(r_blend, 1.0f)); + CHECK(r_blend == doctest::Approx(1.0f)); CHECK(animation->blend_shape_track_interpolate(0, -0.2, &r_blend) == OK); - CHECK(Math::is_equal_approx(r_blend, -1.0f)); + CHECK(r_blend == doctest::Approx(-1.0f)); CHECK(animation->blend_shape_track_interpolate(0, 0.0, &r_blend) == OK); - CHECK(Math::is_equal_approx(r_blend, -1.0f)); + CHECK(r_blend == doctest::Approx(-1.0f)); CHECK(animation->blend_shape_track_interpolate(0, 0.2, &r_blend) == OK); - CHECK(Math::is_equal_approx(r_blend, -0.2f)); + CHECK(r_blend == doctest::Approx(-0.2f)); CHECK(animation->blend_shape_track_interpolate(0, 0.4, &r_blend) == OK); - CHECK(Math::is_equal_approx(r_blend, 0.6f)); + CHECK(r_blend == doctest::Approx(0.6f)); CHECK(animation->blend_shape_track_interpolate(0, 0.5, &r_blend) == OK); - CHECK(Math::is_equal_approx(r_blend, 1.0f)); + CHECK(r_blend == doctest::Approx(1.0f)); CHECK(animation->blend_shape_track_interpolate(0, 0.6, &r_blend) == OK); - CHECK(Math::is_equal_approx(r_blend, 1.0f)); + CHECK(r_blend == doctest::Approx(1.0f)); // Blend shape tracks always use linear interpolation for performance reasons. - CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 0), real_t(1.0))); - CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 1), real_t(1.0))); + CHECK(animation->track_get_key_transition(0, 0) == doctest::Approx(real_t(1.0))); + CHECK(animation->track_get_key_transition(0, 1) == doctest::Approx(real_t(1.0))); // This is a blend shape track, so the methods below should return errors. ERR_PRINT_OFF; @@ -275,7 +275,7 @@ TEST_CASE("[Animation] Create blend shape track") { CHECK(animation->position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER); CHECK(animation->rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER); CHECK(animation->scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER); - CHECK(Math::is_zero_approx(animation->bezier_track_interpolate(0, 0.0))); + CHECK(animation->bezier_track_interpolate(0, 0.0) == doctest::Approx(0.0)); ERR_PRINT_ON; } @@ -289,15 +289,15 @@ TEST_CASE("[Animation] Create Bezier track") { CHECK(animation->get_track_count() == 1); CHECK(!animation->track_is_compressed(0)); - CHECK(Math::is_equal_approx(animation->bezier_track_get_key_value(0, 0), real_t(-1.0))); - CHECK(Math::is_equal_approx(animation->bezier_track_get_key_value(0, 1), real_t(1.0))); + CHECK(animation->bezier_track_get_key_value(0, 0) == doctest::Approx(real_t(-1.0))); + CHECK(animation->bezier_track_get_key_value(0, 1) == doctest::Approx(real_t(1.0))); - CHECK(Math::is_equal_approx(animation->bezier_track_interpolate(0, -0.2), real_t(-1.0))); - CHECK(Math::is_equal_approx(animation->bezier_track_interpolate(0, 0.0), real_t(-1.0))); - CHECK(Math::is_equal_approx(animation->bezier_track_interpolate(0, 0.2), real_t(-0.76057207584381))); - CHECK(Math::is_equal_approx(animation->bezier_track_interpolate(0, 0.4), real_t(-0.39975279569626))); - CHECK(Math::is_equal_approx(animation->bezier_track_interpolate(0, 0.5), real_t(1.0))); - CHECK(Math::is_equal_approx(animation->bezier_track_interpolate(0, 0.6), real_t(1.0))); + CHECK(animation->bezier_track_interpolate(0, -0.2) == doctest::Approx(real_t(-1.0))); + CHECK(animation->bezier_track_interpolate(0, 0.0) == doctest::Approx(real_t(-1.0))); + CHECK(animation->bezier_track_interpolate(0, 0.2) == doctest::Approx(real_t(-0.76057207584381))); + CHECK(animation->bezier_track_interpolate(0, 0.4) == doctest::Approx(real_t(-0.39975279569626))); + CHECK(animation->bezier_track_interpolate(0, 0.5) == doctest::Approx(real_t(1.0))); + CHECK(animation->bezier_track_interpolate(0, 0.6) == doctest::Approx(real_t(1.0))); // This is a bezier track, so the methods below should return errors. ERR_PRINT_OFF; diff --git a/tests/scene/test_arraymesh.h b/tests/scene/test_arraymesh.h new file mode 100644 index 0000000000..4d9feeb4fa --- /dev/null +++ b/tests/scene/test_arraymesh.h @@ -0,0 +1,325 @@ +/**************************************************************************/ +/* test_arraymesh.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TEST_ARRAYMESH_H +#define TEST_ARRAYMESH_H + +#include "scene/resources/mesh.h" +#include "scene/resources/primitive_meshes.h" + +#include "tests/test_macros.h" + +namespace TestArrayMesh { + +TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") { + Ref<ArrayMesh> mesh = memnew(ArrayMesh); + StringName name_a{ "ShapeA" }; + StringName name_b{ "ShapeB" }; + + SUBCASE("Adding a blend shape to the mesh before a surface is added.") { + mesh->add_blend_shape(name_a); + mesh->add_blend_shape(name_b); + + CHECK(mesh->get_blend_shape_name(0) == name_a); + CHECK(mesh->get_blend_shape_name(1) == name_b); + } + + SUBCASE("Add same blend shape multiple times appends name with number.") { + mesh->add_blend_shape(name_a); + mesh->add_blend_shape(name_a); + mesh->add_blend_shape(name_a); + + CHECK(mesh->get_blend_shape_name(0) == "ShapeA"); + bool all_different = (static_cast<String>(mesh->get_blend_shape_name(0)) != static_cast<String>(mesh->get_blend_shape_name(1))) && + (static_cast<String>(mesh->get_blend_shape_name(1)) != static_cast<String>(mesh->get_blend_shape_name(2))) && + (static_cast<String>(mesh->get_blend_shape_name(0)) != static_cast<String>(mesh->get_blend_shape_name(2))); + bool all_have_name = static_cast<String>(mesh->get_blend_shape_name(1)).contains("ShapeA") && + static_cast<String>(mesh->get_blend_shape_name(2)).contains("ShapeA"); + CHECK((all_different && all_have_name)); + } + + SUBCASE("ArrayMesh keeps correct count of number of blend shapes") { + mesh->add_blend_shape(name_a); + mesh->add_blend_shape(name_a); + mesh->add_blend_shape(name_b); + mesh->add_blend_shape(name_b); + mesh->add_blend_shape(name_b); + + REQUIRE(mesh->get_blend_shape_count() == 5); + } + + SUBCASE("Adding blend shape after surface is added causes error") { + Ref<CylinderMesh> cylinder = memnew(CylinderMesh); + Array cylinder_array{}; + cylinder_array.resize(Mesh::ARRAY_MAX); + cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array); + + ERR_PRINT_OFF + mesh->add_blend_shape(name_a); + ERR_PRINT_ON + CHECK(mesh->get_blend_shape_count() == 0); + } + + SUBCASE("Change blend shape name after adding.") { + mesh->add_blend_shape(name_a); + mesh->set_blend_shape_name(0, name_b); + + CHECK(mesh->get_blend_shape_name(0) == name_b); + } + + SUBCASE("Change blend shape name to the name of one already there, should append number to end") { + mesh->add_blend_shape(name_a); + mesh->add_blend_shape(name_b); + mesh->set_blend_shape_name(0, name_b); + + String name_string = mesh->get_blend_shape_name(0); + CHECK(name_string.contains("ShapeB")); + CHECK(name_string.length() > static_cast<String>(name_b).size()); + } + + SUBCASE("Clear all blend shapes before surface has been added.") { + mesh->add_blend_shape(name_a); + mesh->add_blend_shape(name_b); + CHECK(mesh->get_blend_shape_count() == 2); + + mesh->clear_blend_shapes(); + CHECK(mesh->get_blend_shape_count() == 0); + } + + SUBCASE("Can't clear blend shapes after surface had been added.") { + mesh->add_blend_shape(name_a); + mesh->add_blend_shape(name_b); + Ref<CylinderMesh> cylinder = memnew(CylinderMesh); + Array cylinder_array{}; + cylinder_array.resize(Mesh::ARRAY_MAX); + cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array); + + ERR_PRINT_OFF + mesh->clear_blend_shapes(); + ERR_PRINT_ON + CHECK(mesh->get_blend_shape_count() == 2); + } + + SUBCASE("Set the blend shape mode of ArrayMesh and underlying mesh RID.") { + mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_RELATIVE); + CHECK(mesh->get_blend_shape_mode() == Mesh::BLEND_SHAPE_MODE_RELATIVE); + } +} + +TEST_CASE("[SceneTree][ArrayMesh] Surface meta data tests.") { + Ref<ArrayMesh> mesh = memnew(ArrayMesh); + Ref<CylinderMesh> cylinder = memnew(CylinderMesh); + Array cylinder_array{}; + cylinder_array.resize(Mesh::ARRAY_MAX); + cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array); + + Ref<BoxMesh> box = memnew(BoxMesh); + Array box_array{}; + box_array.resize(Mesh::ARRAY_MAX); + box->create_mesh_array(box_array, Vector3(2.f, 1.2f, 1.6f)); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, box_array); + + SUBCASE("Add 2 surfaces and count the number of surfaces in the mesh.") { + REQUIRE(mesh->get_surface_count() == 2); + } + + SUBCASE("Get the surface array from mesh.") { + REQUIRE(mesh->surface_get_arrays(0)[0] == cylinder_array[0]); + REQUIRE(mesh->surface_get_arrays(1)[0] == box_array[0]); + } + + SUBCASE("Get the array length of a particular surface.") { + CHECK(mesh->surface_get_array_len(0) == static_cast<Vector<Vector3>>(cylinder_array[RenderingServer::ARRAY_VERTEX]).size()); + CHECK(mesh->surface_get_array_len(1) == static_cast<Vector<Vector3>>(box_array[RenderingServer::ARRAY_VERTEX]).size()); + } + + SUBCASE("Get the index array length of a particular surface.") { + CHECK(mesh->surface_get_array_index_len(0) == static_cast<Vector<Vector3>>(cylinder_array[RenderingServer::ARRAY_INDEX]).size()); + CHECK(mesh->surface_get_array_index_len(1) == static_cast<Vector<Vector3>>(box_array[RenderingServer::ARRAY_INDEX]).size()); + } + + SUBCASE("Get correct primitive type") { + CHECK(mesh->surface_get_primitive_type(0) == Mesh::PRIMITIVE_TRIANGLES); + CHECK(mesh->surface_get_primitive_type(1) == Mesh::PRIMITIVE_TRIANGLES); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLE_STRIP, box_array); + CHECK(mesh->surface_get_primitive_type(2) == Mesh::PRIMITIVE_TRIANGLE_STRIP); + } + + SUBCASE("Returns correct format for the mesh") { + auto format = RS::ARRAY_FORMAT_BLEND_SHAPE_MASK | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX; + CHECK((mesh->surface_get_format(0) & format) != 0); + CHECK((mesh->surface_get_format(1) & format) != 0); + } + + SUBCASE("Set a surface name and retrieve it by name.") { + mesh->surface_set_name(0, "surf1"); + CHECK(mesh->surface_find_by_name("surf1") == 0); + CHECK(mesh->surface_get_name(0) == "surf1"); + } + + SUBCASE("Set material to two different surfaces.") { + Ref<Material> mat = memnew(Material); + mesh->surface_set_material(0, mat); + CHECK(mesh->surface_get_material(0) == mat); + mesh->surface_set_material(1, mat); + CHECK(mesh->surface_get_material(1) == mat); + } + + SUBCASE("Set same material multiple times doesn't change material of surface.") { + Ref<Material> mat = memnew(Material); + mesh->surface_set_material(0, mat); + mesh->surface_set_material(0, mat); + mesh->surface_set_material(0, mat); + CHECK(mesh->surface_get_material(0) == mat); + } + + SUBCASE("Set material of surface then change to different material.") { + Ref<Material> mat1 = memnew(Material); + Ref<Material> mat2 = memnew(Material); + mesh->surface_set_material(1, mat1); + CHECK(mesh->surface_get_material(1) == mat1); + mesh->surface_set_material(1, mat2); + CHECK(mesh->surface_get_material(1) == mat2); + } + + SUBCASE("Get the LOD of the mesh.") { + Dictionary lod{}; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, TypedArray<Array>{}, lod); + CHECK(mesh->surface_get_lods(2) == lod); + } + + SUBCASE("Get the blend shape arrays from the mesh.") { + TypedArray<Array> blend{}; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, blend); + CHECK(mesh->surface_get_blend_shape_arrays(2) == blend); + } +} + +TEST_CASE("[SceneTree][ArrayMesh] Get/Set mesh metadata and actions") { + Ref<ArrayMesh> mesh = memnew(ArrayMesh); + Ref<CylinderMesh> cylinder = memnew(CylinderMesh); + Array cylinder_array{}; + cylinder_array.resize(Mesh::ARRAY_MAX); + cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array); + + Ref<BoxMesh> box = memnew(BoxMesh); + Array box_array{}; + box_array.resize(Mesh::ARRAY_MAX); + box->create_mesh_array(box_array, Vector3(2.f, 1.2f, 1.6f)); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, box_array); + + SUBCASE("Set the shadow mesh.") { + Ref<ArrayMesh> shadow = memnew(ArrayMesh); + mesh->set_shadow_mesh(shadow); + CHECK(mesh->get_shadow_mesh() == shadow); + } + + SUBCASE("Set the shadow mesh multiple times.") { + Ref<ArrayMesh> shadow = memnew(ArrayMesh); + mesh->set_shadow_mesh(shadow); + mesh->set_shadow_mesh(shadow); + mesh->set_shadow_mesh(shadow); + mesh->set_shadow_mesh(shadow); + CHECK(mesh->get_shadow_mesh() == shadow); + } + + SUBCASE("Set the same shadow mesh on multiple meshes.") { + Ref<ArrayMesh> shadow = memnew(ArrayMesh); + Ref<ArrayMesh> mesh2 = memnew(ArrayMesh); + mesh->set_shadow_mesh(shadow); + mesh2->set_shadow_mesh(shadow); + + CHECK(mesh->get_shadow_mesh() == shadow); + CHECK(mesh2->get_shadow_mesh() == shadow); + } + + SUBCASE("Set the shadow mesh and then change it.") { + Ref<ArrayMesh> shadow = memnew(ArrayMesh); + mesh->set_shadow_mesh(shadow); + CHECK(mesh->get_shadow_mesh() == shadow); + Ref<ArrayMesh> shadow2 = memnew(ArrayMesh); + mesh->set_shadow_mesh(shadow2); + CHECK(mesh->get_shadow_mesh() == shadow2); + } + + SUBCASE("Set custom AABB.") { + AABB bound{}; + mesh->set_custom_aabb(bound); + CHECK(mesh->get_custom_aabb() == bound); + } + + SUBCASE("Set custom AABB multiple times.") { + AABB bound{}; + mesh->set_custom_aabb(bound); + mesh->set_custom_aabb(bound); + mesh->set_custom_aabb(bound); + mesh->set_custom_aabb(bound); + CHECK(mesh->get_custom_aabb() == bound); + } + + SUBCASE("Set custom AABB then change to another AABB.") { + AABB bound{}; + AABB bound2{}; + mesh->set_custom_aabb(bound); + CHECK(mesh->get_custom_aabb() == bound); + mesh->set_custom_aabb(bound2); + CHECK(mesh->get_custom_aabb() == bound2); + } + + SUBCASE("Clear all surfaces should leave zero count.") { + mesh->clear_surfaces(); + CHECK(mesh->get_surface_count() == 0); + } + + SUBCASE("Able to get correct mesh RID.") { + RID rid = mesh->get_rid(); + CHECK(RS::get_singleton()->mesh_get_surface_count(rid) == 2); + } + + SUBCASE("Create surface from raw SurfaceData data.") { + RID mesh_rid = mesh->get_rid(); + RS::SurfaceData surface_data = RS::get_singleton()->mesh_get_surface(mesh_rid, 0); + Ref<ArrayMesh> mesh2 = memnew(ArrayMesh); + mesh2->add_surface(surface_data.format, Mesh::PRIMITIVE_TRIANGLES, surface_data.vertex_data, surface_data.attribute_data, + surface_data.skin_data, surface_data.vertex_count, surface_data.index_data, surface_data.index_count, surface_data.aabb); + CHECK(mesh2->get_surface_count() == 1); + CHECK(mesh2->surface_get_primitive_type(0) == Mesh::PRIMITIVE_TRIANGLES); + CHECK((mesh2->surface_get_format(0) & surface_data.format) != 0); + CHECK(mesh2->get_aabb().is_equal_approx(surface_data.aabb)); + } +} + +} // namespace TestArrayMesh + +#endif // TEST_ARRAYMESH_H diff --git a/tests/scene/test_audio_stream_wav.h b/tests/scene/test_audio_stream_wav.h index cf369c115b..e36f049136 100644 --- a/tests/scene/test_audio_stream_wav.h +++ b/tests/scene/test_audio_stream_wav.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* test_audio_stream_wav.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. */ -/*************************************************************************/ +/**************************************************************************/ +/* test_audio_stream_wav.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef TEST_AUDIO_STREAM_WAV_H #define TEST_AUDIO_STREAM_WAV_H @@ -138,7 +138,7 @@ void run_test(String file_name, AudioStreamWAV::Format data_format, bool stereo, CHECK(stream->get_data() == test_data); SUBCASE("Stream length is computed properly") { - CHECK(Math::is_equal_approx(stream->get_length(), wav_count / wav_rate)); + CHECK(stream->get_length() == doctest::Approx(double(wav_count / wav_rate))); } SUBCASE("Stream can be saved as .wav") { diff --git a/tests/scene/test_bit_map.h b/tests/scene/test_bit_map.h index 53afdc38f7..3c372b2ac3 100644 --- a/tests/scene/test_bit_map.h +++ b/tests/scene/test_bit_map.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* test_bit_map.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. */ -/*************************************************************************/ +/**************************************************************************/ +/* test_bit_map.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef TEST_BIT_MAP_H #define TEST_BIT_MAP_H @@ -49,6 +49,8 @@ TEST_CASE("[BitMap] Create bit map") { CHECK(bit_map.get_size() == Size2i(256, 512)); CHECK_MESSAGE(bit_map.get_true_bit_count() == 0, "This will go through the entire bitmask inside of bitmap, thus hopefully checking if the bitmask was correctly set up."); + ERR_PRINT_OFF + dim = Size2i(0, 256); bit_map.create(dim); CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 512), "We should still have the same dimensions as before, because the new dimension is invalid."); @@ -60,6 +62,8 @@ TEST_CASE("[BitMap] Create bit map") { dim = Size2i(46341, 46341); bit_map.create(dim); CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 512), "We should still have the same dimensions as before, because the new dimension is too large (46341*46341=2147488281)."); + + ERR_PRINT_ON } TEST_CASE("[BitMap] Create bit map from image alpha") { @@ -67,6 +71,8 @@ TEST_CASE("[BitMap] Create bit map from image alpha") { BitMap bit_map{}; bit_map.create(dim); + ERR_PRINT_OFF + const Ref<Image> null_img = nullptr; bit_map.create_from_image_alpha(null_img); CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 256), "Bitmap should have its old values because bitmap creation from a nullptr should fail."); @@ -76,15 +82,13 @@ TEST_CASE("[BitMap] Create bit map from image alpha") { bit_map.create_from_image_alpha(empty_img); CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 256), "Bitmap should have its old values because bitmap creation from an empty image should fail."); - Ref<Image> wrong_format_img; - wrong_format_img.instantiate(); - wrong_format_img->create(3, 3, false, Image::Format::FORMAT_DXT1); + Ref<Image> wrong_format_img = Image::create_empty(3, 3, false, Image::Format::FORMAT_DXT1); bit_map.create_from_image_alpha(wrong_format_img); CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 256), "Bitmap should have its old values because converting from a compressed image should fail."); - Ref<Image> img; - img.instantiate(); - img->create(3, 3, false, Image::Format::FORMAT_RGBA8); + ERR_PRINT_ON + + Ref<Image> img = Image::create_empty(3, 3, false, Image::Format::FORMAT_RGBA8); img->set_pixel(0, 0, Color(0, 0, 0, 0)); img->set_pixel(0, 1, Color(0, 0, 0, 0.09f)); img->set_pixel(0, 2, Color(0, 0, 0, 0.25f)); @@ -109,6 +113,8 @@ TEST_CASE("[BitMap] Set bit") { BitMap bit_map{}; // Setting a point before a bit map is created should not crash, because there are checks to see if we are out of bounds. + ERR_PRINT_OFF + bit_map.set_bitv(Point2i(128, 128), true); bit_map.create(dim); @@ -124,12 +130,16 @@ TEST_CASE("[BitMap] Set bit") { bit_map.create(dim); bit_map.set_bitv(Point2i(512, 512), true); CHECK_MESSAGE(bit_map.get_true_bit_count() == 0, "Nothing should change as we were trying to edit a bit outside of the correct range."); + + ERR_PRINT_ON } TEST_CASE("[BitMap] Get bit") { const Size2i dim{ 256, 256 }; BitMap bit_map{}; + ERR_PRINT_OFF + CHECK_MESSAGE(bit_map.get_bitv(Point2i(128, 128)) == false, "Trying to access a bit outside of the BitMap's range should always return false"); bit_map.create(dim); @@ -144,6 +154,8 @@ TEST_CASE("[BitMap] Get bit") { CHECK(bit_map.get_bitv(Point2i(255, 255)) == true); CHECK(bit_map.get_bitv(Point2i(256, 256)) == false); CHECK(bit_map.get_bitv(Point2i(257, 257)) == false); + + ERR_PRINT_ON } TEST_CASE("[BitMap] Set bit rect") { @@ -162,6 +174,9 @@ TEST_CASE("[BitMap] Set bit rect") { reset_bit_map(bit_map); // Checking out of bounds handling. + + ERR_PRINT_OFF + bit_map.set_bit_rect(Rect2i{ 128, 128, 256, 256 }, true); CHECK(bit_map.get_true_bit_count() == 16384); @@ -174,6 +189,8 @@ TEST_CASE("[BitMap] Set bit rect") { bit_map.set_bit_rect(Rect2i{ -128, -128, 512, 512 }, true); CHECK(bit_map.get_true_bit_count() == 65536); + + ERR_PRINT_ON } TEST_CASE("[BitMap] Get true bit count") { @@ -183,7 +200,7 @@ TEST_CASE("[BitMap] Get true bit count") { CHECK(bit_map.get_true_bit_count() == 0); bit_map.create(dim); - CHECK_MESSAGE(bit_map.get_true_bit_count() == 0, "Unitialized bit map should have no true bits"); + CHECK_MESSAGE(bit_map.get_true_bit_count() == 0, "Uninitialized bit map should have no true bits"); bit_map.set_bit_rect(Rect2i{ 0, 0, 256, 256 }, true); CHECK(bit_map.get_true_bit_count() == 65536); bit_map.set_bitv(Point2i{ 0, 0 }, false); @@ -196,12 +213,14 @@ TEST_CASE("[BitMap] Get size") { const Size2i dim{ 256, 256 }; BitMap bit_map{}; - CHECK_MESSAGE(bit_map.get_size() == Size2i(0, 0), "Unitialized bit map should have a size of 0x0"); + CHECK_MESSAGE(bit_map.get_size() == Size2i(0, 0), "Uninitialized bit map should have a size of 0x0"); bit_map.create(dim); CHECK(bit_map.get_size() == Size2i(256, 256)); + ERR_PRINT_OFF bit_map.create(Size2i(-1, 0)); + ERR_PRINT_ON CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 256), "Invalid size should not be accepted by create"); bit_map.create(Size2i(256, 128)); @@ -223,7 +242,9 @@ TEST_CASE("[BitMap] Resize") { CHECK_MESSAGE(bit_map.get_true_bit_count() == 50, "There should be 25 bits in the top left corner, and 25 bits in the bottom right corner"); bit_map.create(dim); + ERR_PRINT_OFF bit_map.resize(Size2i(-1, 128)); + ERR_PRINT_ON CHECK_MESSAGE(bit_map.get_size() == Size2i(128, 128), "When an invalid size is given the bit map will keep its size"); bit_map.create(dim); @@ -238,7 +259,9 @@ TEST_CASE("[BitMap] Resize") { TEST_CASE("[BitMap] Grow and shrink mask") { const Size2i dim{ 256, 256 }; BitMap bit_map{}; - bit_map.grow_mask(100, Rect2i(0, 0, 128, 128)); // Check if method does not crash when working with an uninitialised bit map. + ERR_PRINT_OFF + bit_map.grow_mask(100, Rect2i(0, 0, 128, 128)); // Check if method does not crash when working with an uninitialized bit map. + ERR_PRINT_ON CHECK_MESSAGE(bit_map.get_size() == Size2i(0, 0), "Size should still be equal to 0x0"); bit_map.create(dim); @@ -335,25 +358,27 @@ TEST_CASE("[BitMap] Blit") { Ref<BitMap> blit_bit_map{}; // Testing null reference to blit bit map. + ERR_PRINT_OFF bit_map.blit(blit_pos, blit_bit_map); + ERR_PRINT_ON blit_bit_map.instantiate(); - // Testing if uninitialised blit bit map and uninitialised bit map does not crash + // Testing if uninitialized blit bit map and uninitialized bit map does not crash bit_map.blit(blit_pos, blit_bit_map); - // Testing if uninitialised bit map does not crash + // Testing if uninitialized bit map does not crash blit_bit_map->create(blit_size); bit_map.blit(blit_pos, blit_bit_map); - // Testing if uninitialised bit map does not crash + // Testing if uninitialized bit map does not crash blit_bit_map.unref(); blit_bit_map.instantiate(); CHECK_MESSAGE(blit_bit_map->get_size() == Point2i(0, 0), "Size should be cleared by unref and instance calls."); bit_map.create(bit_map_size); bit_map.blit(Point2i(128, 128), blit_bit_map); - // Testing if both initialised does not crash. + // Testing if both initialized does not crash. blit_bit_map->create(blit_size); bit_map.blit(blit_pos, blit_bit_map); @@ -382,7 +407,9 @@ TEST_CASE("[BitMap] Convert to image") { BitMap bit_map{}; Ref<Image> img; + ERR_PRINT_OFF img = bit_map.convert_to_image(); + ERR_PRINT_ON CHECK_MESSAGE(img.is_valid(), "We should receive a valid Image Object even if BitMap is not created yet"); CHECK_MESSAGE(img->get_format() == Image::FORMAT_L8, "We should receive a valid Image Object even if BitMap is not created yet"); CHECK_MESSAGE(img->get_size() == (Size2i(0, 0)), "Image should have no width or height, because BitMap has not yet been created"); @@ -390,13 +417,13 @@ TEST_CASE("[BitMap] Convert to image") { bit_map.create(dim); img = bit_map.convert_to_image(); CHECK_MESSAGE(img->get_size() == dim, "Image should have the same dimensions as the BitMap"); - CHECK_MESSAGE(img->get_pixel(0, 0).is_equal_approx(Color(0, 0, 0)), "BitMap is intialized to all 0's, so Image should be all black"); + CHECK_MESSAGE(img->get_pixel(0, 0).is_equal_approx(Color(0, 0, 0)), "BitMap is initialized to all 0's, so Image should be all black"); reset_bit_map(bit_map); bit_map.set_bit_rect(Rect2i(0, 0, 128, 128), true); img = bit_map.convert_to_image(); CHECK_MESSAGE(img->get_pixel(0, 0).is_equal_approx(Color(1, 1, 1)), "BitMap's top-left quadrant is all 1's, so Image should be white"); - CHECK_MESSAGE(img->get_pixel(256, 256).is_equal_approx(Color(0, 0, 0)), "All other quadrants were 0's, so these should be black"); + CHECK_MESSAGE(img->get_pixel(255, 255).is_equal_approx(Color(0, 0, 0)), "All other quadrants were 0's, so these should be black"); } TEST_CASE("[BitMap] Clip to polygon") { @@ -404,7 +431,9 @@ TEST_CASE("[BitMap] Clip to polygon") { BitMap bit_map{}; Vector<Vector<Vector2>> polygons; + ERR_PRINT_OFF polygons = bit_map.clip_opaque_to_polygons(Rect2i(0, 0, 128, 128)); + ERR_PRINT_ON CHECK_MESSAGE(polygons.size() == 0, "We should have no polygons, because the BitMap was not initialized"); bit_map.create(dim); @@ -438,6 +467,37 @@ TEST_CASE("[BitMap] Clip to polygon") { polygons = bit_map.clip_opaque_to_polygons(Rect2i(0, 0, 128, 128)); CHECK_MESSAGE(polygons.size() == 1, "We should have exactly 1 polygon"); CHECK_MESSAGE(polygons[0].size() == 6, "The polygon should have exactly 6 points"); + + reset_bit_map(bit_map); + bit_map.set_bit_rect(Rect2i(0, 0, 64, 64), true); + bit_map.set_bit_rect(Rect2i(64, 64, 64, 64), true); + bit_map.set_bit_rect(Rect2i(192, 128, 64, 64), true); + bit_map.set_bit_rect(Rect2i(128, 192, 64, 64), true); + polygons = bit_map.clip_opaque_to_polygons(Rect2i(0, 0, 256, 256)); + CHECK_MESSAGE(polygons.size() == 4, "We should have exactly 4 polygons"); + CHECK_MESSAGE(polygons[0].size() == 4, "The polygon should have exactly 4 points"); + CHECK_MESSAGE(polygons[1].size() == 4, "The polygon should have exactly 4 points"); + CHECK_MESSAGE(polygons[2].size() == 4, "The polygon should have exactly 4 points"); + CHECK_MESSAGE(polygons[3].size() == 4, "The polygon should have exactly 4 points"); + + reset_bit_map(bit_map); + bit_map.set_bit(0, 0, true); + bit_map.set_bit(2, 0, true); + bit_map.set_bit_rect(Rect2i(1, 1, 1, 2), true); + polygons = bit_map.clip_opaque_to_polygons(Rect2i(0, 0, 3, 3)); + CHECK_MESSAGE(polygons.size() == 3, "We should have exactly 3 polygons"); + CHECK_MESSAGE(polygons[0].size() == 4, "The polygon should have exactly 4 points"); + CHECK_MESSAGE(polygons[1].size() == 4, "The polygon should have exactly 4 points"); + CHECK_MESSAGE(polygons[2].size() == 4, "The polygon should have exactly 4 points"); + + reset_bit_map(bit_map); + bit_map.set_bit_rect(Rect2i(0, 0, 2, 1), true); + bit_map.set_bit_rect(Rect2i(0, 2, 3, 1), true); + bit_map.set_bit(0, 1, true); + bit_map.set_bit(2, 1, true); + polygons = bit_map.clip_opaque_to_polygons(Rect2i(0, 0, 4, 4)); + CHECK_MESSAGE(polygons.size() == 1, "We should have exactly 1 polygon"); + CHECK_MESSAGE(polygons[0].size() == 6, "The polygon should have exactly 6 points"); } } // namespace TestBitmap diff --git a/tests/scene/test_code_edit.h b/tests/scene/test_code_edit.h index 3940bdb37a..c68560000c 100644 --- a/tests/scene/test_code_edit.h +++ b/tests/scene/test_code_edit.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* test_code_edit.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. */ -/*************************************************************************/ +/**************************************************************************/ +/* test_code_edit.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef TEST_CODE_EDIT_H #define TEST_CODE_EDIT_H @@ -2871,6 +2871,89 @@ TEST_CASE("[SceneTree][CodeEdit] completion") { CHECK(code_edit->get_text() == "\'\'\'"); } + SUBCASE("[CodeEdit] autocomplete with brace completion") { + code_edit->set_auto_brace_completion_enabled(true); + CHECK(code_edit->is_auto_brace_completion_enabled()); + + code_edit->insert_text_at_caret("(te)"); + code_edit->set_caret_column(3); + + // Full completion. + code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_FUNCTION, "test()", "test()"); + code_edit->update_code_completion_options(); + code_edit->confirm_code_completion(); + CHECK(code_edit->get_line(0) == "(test())"); + CHECK(code_edit->get_caret_column() == 7); + code_edit->undo(); + + // With "arg". + code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_FUNCTION, "test(", "test("); + code_edit->update_code_completion_options(); + code_edit->confirm_code_completion(); + CHECK(code_edit->get_line(0) == "(test())"); + CHECK(code_edit->get_caret_column() == 6); + code_edit->undo(); + + // brace completion disabled + code_edit->set_auto_brace_completion_enabled(false); + + // Full completion. + code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_FUNCTION, "test()", "test()"); + code_edit->update_code_completion_options(); + code_edit->confirm_code_completion(); + CHECK(code_edit->get_line(0) == "(test())"); + CHECK(code_edit->get_caret_column() == 7); + code_edit->undo(); + + // With "arg". + code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_FUNCTION, "test(", "test("); + code_edit->update_code_completion_options(); + code_edit->confirm_code_completion(); + CHECK(code_edit->get_line(0) == "(test()"); + CHECK(code_edit->get_caret_column() == 6); + + // String + code_edit->set_auto_brace_completion_enabled(true); + code_edit->clear(); + code_edit->insert_text_at_caret("\"\""); + code_edit->set_caret_column(1); + + // Full completion. + code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_NODE_PATH, "\"test\"", "\"test\""); + code_edit->update_code_completion_options(); + code_edit->confirm_code_completion(); + CHECK(code_edit->get_line(0) == "\"test\""); + CHECK(code_edit->get_caret_column() == 6); + code_edit->undo(); + + // With "arg". + code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_NODE_PATH, "\"test", "\"test"); + code_edit->update_code_completion_options(); + code_edit->confirm_code_completion(); + CHECK(code_edit->get_line(0) == "\"\"test\"\""); + CHECK(code_edit->get_caret_column() == 7); + code_edit->undo(); + + // brace completion disabled + code_edit->set_auto_brace_completion_enabled(false); + + // Full completion. + code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_NODE_PATH, "\"test\"", "\"test\""); + code_edit->update_code_completion_options(); + code_edit->confirm_code_completion(); + CHECK(code_edit->get_line(0) == "\"test\""); + CHECK(code_edit->get_caret_column() == 6); + code_edit->undo(); + + // With "arg". + code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_NODE_PATH, "\"test", "\"test"); + code_edit->update_code_completion_options(); + code_edit->confirm_code_completion(); + CHECK(code_edit->get_line(0) == "\"\"test\""); + CHECK(code_edit->get_caret_column() == 7); + code_edit->undo(); + } + SUBCASE("[CodeEdit] autocomplete") { code_edit->set_code_completion_enabled(true); CHECK(code_edit->is_code_completion_enabled()); @@ -3027,15 +3110,16 @@ TEST_CASE("[SceneTree][CodeEdit] completion") { CHECK(code_edit->get_code_completion_selected_index() == 0); Point2 caret_pos = code_edit->get_caret_draw_pos(); - caret_pos.y -= code_edit->get_line_height(); - SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::WHEEL_DOWN, MouseButton::NONE, Key::NONE); + caret_pos.y += code_edit->get_line_height(); + SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::WHEEL_DOWN, 0, Key::NONE); CHECK(code_edit->get_code_completion_selected_index() == 1); - SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::WHEEL_UP, MouseButton::NONE, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::WHEEL_UP, 0, Key::NONE); CHECK(code_edit->get_code_completion_selected_index() == 0); /* Single click selects. */ - SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE); + caret_pos.y += code_edit->get_line_height() * 2; + SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); CHECK(code_edit->get_code_completion_selected_index() == 2); /* Double click inserts. */ @@ -3246,7 +3330,7 @@ TEST_CASE("[SceneTree][CodeEdit] symbol lookup") { Point2 caret_pos = code_edit->get_caret_draw_pos(); caret_pos.x += 60; - SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::NONE, MouseButton::NONE, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(code_edit, caret_pos, MouseButton::NONE, 0, Key::NONE); CHECK(code_edit->get_text_for_symbol_lookup() == "this is s" + String::chr(0xFFFF) + "ome text"); SIGNAL_WATCH(code_edit, "symbol_validate"); diff --git a/tests/scene/test_curve.h b/tests/scene/test_curve.h index ad7625ddc5..bfa37e8f69 100644 --- a/tests/scene/test_curve.h +++ b/tests/scene/test_curve.h @@ -1,36 +1,37 @@ -/*************************************************************************/ -/* test_curve.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. */ -/*************************************************************************/ +/**************************************************************************/ +/* test_curve.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef TEST_CURVE_H #define TEST_CURVE_H +#include "core/math/math_funcs.h" #include "scene/resources/curve.h" #include "tests/test_macros.h" @@ -83,54 +84,54 @@ TEST_CASE("[Curve] Custom curve with free tangents") { Math::is_zero_approx(curve->sample(-0.1)), "Custom free curve should return the expected value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample(0.1), (real_t)0.352), + curve->sample(0.1) == doctest::Approx((real_t)0.352), "Custom free curve should return the expected value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample(0.4), (real_t)0.352), + curve->sample(0.4) == doctest::Approx((real_t)0.352), "Custom free curve should return the expected value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample(0.7), (real_t)0.896), + curve->sample(0.7) == doctest::Approx((real_t)0.896), "Custom free curve should return the expected value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample(1), 1), + curve->sample(1) == doctest::Approx(1), "Custom free curve should return the expected value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample(2), 1), + curve->sample(2) == doctest::Approx(1), "Custom free curve should return the expected value at offset 0.1."); CHECK_MESSAGE( Math::is_zero_approx(curve->sample_baked(-0.1)), "Custom free curve should return the expected baked value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample_baked(0.1), (real_t)0.352), + curve->sample_baked(0.1) == doctest::Approx((real_t)0.352), "Custom free curve should return the expected baked value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample_baked(0.4), (real_t)0.352), + curve->sample_baked(0.4) == doctest::Approx((real_t)0.352), "Custom free curve should return the expected baked value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample_baked(0.7), (real_t)0.896), + curve->sample_baked(0.7) == doctest::Approx((real_t)0.896), "Custom free curve should return the expected baked value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample_baked(1), 1), + curve->sample_baked(1) == doctest::Approx(1), "Custom free curve should return the expected baked value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample_baked(2), 1), + curve->sample_baked(2) == doctest::Approx(1), "Custom free curve should return the expected baked value at offset 0.1."); curve->remove_point(1); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample(0.1), 0), + curve->sample(0.1) == doctest::Approx(0), "Custom free curve should return the expected value at offset 0.1 after removing point at index 1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample_baked(0.1), 0), + curve->sample_baked(0.1) == doctest::Approx(0), "Custom free curve should return the expected baked value at offset 0.1 after removing point at index 1."); curve->clear_points(); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample(0.6), 0), + curve->sample(0.6) == doctest::Approx(0), "Custom free curve should return the expected value at offset 0.6 after clearing all points."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample_baked(0.6), 0), + curve->sample_baked(0.6) == doctest::Approx(0), "Custom free curve should return the expected baked value at offset 0.6 after clearing all points."); } @@ -143,7 +144,7 @@ TEST_CASE("[Curve] Custom curve with linear tangents") { curve->add_point(Vector2(0.75, 1), 0, 0, Curve::TangentMode::TANGENT_LINEAR, Curve::TangentMode::TANGENT_LINEAR); CHECK_MESSAGE( - Math::is_equal_approx(curve->get_point_left_tangent(3), 4), + curve->get_point_left_tangent(3) == doctest::Approx(4), "get_point_left_tangent() should return the expected value for point index 3."); CHECK_MESSAGE( Math::is_zero_approx(curve->get_point_right_tangent(3)), @@ -172,48 +173,48 @@ TEST_CASE("[Curve] Custom curve with linear tangents") { Math::is_zero_approx(curve->sample(-0.1)), "Custom linear curve should return the expected value at offset -0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample(0.1), (real_t)0.4), + curve->sample(0.1) == doctest::Approx((real_t)0.4), "Custom linear curve should return the expected value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample(0.4), (real_t)0.4), + curve->sample(0.4) == doctest::Approx((real_t)0.4), "Custom linear curve should return the expected value at offset 0.4."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample(0.7), (real_t)0.8), + curve->sample(0.7) == doctest::Approx((real_t)0.8), "Custom linear curve should return the expected value at offset 0.7."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample(1), 1), + curve->sample(1) == doctest::Approx(1), "Custom linear curve should return the expected value at offset 1.0."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample(2), 1), + curve->sample(2) == doctest::Approx(1), "Custom linear curve should return the expected value at offset 2.0."); CHECK_MESSAGE( Math::is_zero_approx(curve->sample_baked(-0.1)), "Custom linear curve should return the expected baked value at offset -0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample_baked(0.1), (real_t)0.4), + curve->sample_baked(0.1) == doctest::Approx((real_t)0.4), "Custom linear curve should return the expected baked value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample_baked(0.4), (real_t)0.4), + curve->sample_baked(0.4) == doctest::Approx((real_t)0.4), "Custom linear curve should return the expected baked value at offset 0.4."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample_baked(0.7), (real_t)0.8), + curve->sample_baked(0.7) == doctest::Approx((real_t)0.8), "Custom linear curve should return the expected baked value at offset 0.7."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample_baked(1), 1), + curve->sample_baked(1) == doctest::Approx(1), "Custom linear curve should return the expected baked value at offset 1.0."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample_baked(2), 1), + curve->sample_baked(2) == doctest::Approx(1), "Custom linear curve should return the expected baked value at offset 2.0."); ERR_PRINT_OFF; curve->remove_point(10); ERR_PRINT_ON; CHECK_MESSAGE( - Math::is_equal_approx(curve->sample(0.7), (real_t)0.8), + curve->sample(0.7) == doctest::Approx((real_t)0.8), "Custom free curve should return the expected value at offset 0.7 after removing point at invalid index 10."); CHECK_MESSAGE( - Math::is_equal_approx(curve->sample_baked(0.7), (real_t)0.8), + curve->sample_baked(0.7) == doctest::Approx((real_t)0.8), "Custom free curve should return the expected baked value at offset 0.7 after removing point at invalid index 10."); } @@ -229,7 +230,7 @@ TEST_CASE("[Curve2D] Linear sampling should return exact value") { for (int i = 0; i < len; i++) { Vector2 pos = curve->sample_baked(i); - CHECK_MESSAGE(pos.x == i, "sample_baked should return exact value"); + CHECK_MESSAGE(Math::is_equal_approx(pos.x, i), "sample_baked should return exact value"); } } @@ -239,13 +240,14 @@ TEST_CASE("[Curve3D] Linear sampling should return exact value") { curve->add_point(Vector3(0, 0, 0)); curve->add_point(Vector3(len, 0, 0)); - + ERR_PRINT_OFF real_t baked_length = curve->get_baked_length(); + ERR_PRINT_ON CHECK(len == baked_length); for (int i = 0; i < len; i++) { Vector3 pos = curve->sample_baked(i); - CHECK_MESSAGE(pos.x == i, "sample_baked should return exact value"); + CHECK_MESSAGE(Math::is_equal_approx(pos.x, i), "sample_baked should return exact value"); } } diff --git a/tests/scene/test_gradient.h b/tests/scene/test_gradient.h index b0e6128932..4fbdaa8125 100644 --- a/tests/scene/test_gradient.h +++ b/tests/scene/test_gradient.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* test_gradient.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. */ -/*************************************************************************/ +/**************************************************************************/ +/* test_gradient.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef TEST_GRADIENT_H #define TEST_GRADIENT_H @@ -42,7 +42,7 @@ TEST_CASE("[Gradient] Default gradient") { Ref<Gradient> gradient = memnew(Gradient); CHECK_MESSAGE( - gradient->get_points_count() == 2, + gradient->get_point_count() == 2, "Default gradient should contain the expected number of points."); CHECK_MESSAGE( @@ -77,7 +77,7 @@ TEST_CASE("[Gradient] Custom gradient (points specified in order)") { gradient->set_points(points); CHECK_MESSAGE( - gradient->get_points_count() == 3, + gradient->get_point_count() == 3, "Custom gradient should contain the expected number of points."); CHECK_MESSAGE( @@ -98,7 +98,7 @@ TEST_CASE("[Gradient] Custom gradient (points specified in order)") { gradient->remove_point(1); CHECK_MESSAGE( - gradient->get_points_count() == 2, + gradient->get_point_count() == 2, "Custom gradient should contain the expected number of points after removing one point."); CHECK_MESSAGE( gradient->get_color_at_offset(0.5).is_equal_approx(Color(0.5, 1, 0)), @@ -119,7 +119,7 @@ TEST_CASE("[Gradient] Custom gradient (points specified out-of-order)") { gradient->set_points(points); CHECK_MESSAGE( - gradient->get_points_count() == 6, + gradient->get_point_count() == 6, "Custom out-of-order gradient should contain the expected number of points."); CHECK_MESSAGE( @@ -137,7 +137,7 @@ TEST_CASE("[Gradient] Custom gradient (points specified out-of-order)") { gradient->remove_point(0); CHECK_MESSAGE( - gradient->get_points_count() == 5, + gradient->get_point_count() == 5, "Custom out-of-order gradient should contain the expected number of points after removing one point."); // The color will be clamped to the nearest point (which is at offset 0.2). CHECK_MESSAGE( diff --git a/tests/scene/test_node.h b/tests/scene/test_node.h new file mode 100644 index 0000000000..20c42d9fc2 --- /dev/null +++ b/tests/scene/test_node.h @@ -0,0 +1,415 @@ +/**************************************************************************/ +/* test_node.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TEST_NODE_H +#define TEST_NODE_H + +#include "scene/main/node.h" + +#include "tests/test_macros.h" + +namespace TestNode { + +TEST_CASE("[SceneTree][Node] Simple Add/Remove/Move/Find") { + Node *node = memnew(Node); + + // Check initial scene tree setup. + CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 0); + CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 1); + + // Check initial node setup. + CHECK(node->get_name() == StringName()); + CHECK_FALSE(node->is_inside_tree()); + CHECK_EQ(node->get_parent(), nullptr); + ERR_PRINT_OFF; + CHECK(node->get_path().is_empty()); + ERR_PRINT_ON; + CHECK_EQ(node->get_child_count(), 0); + + SceneTree::get_singleton()->get_root()->add_child(node); + + CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 1); + CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 2); + + CHECK(node->get_name() != StringName()); + CHECK(node->is_inside_tree()); + CHECK_EQ(SceneTree::get_singleton()->get_root(), node->get_parent()); + CHECK_FALSE(node->get_path().is_empty()); + CHECK_EQ(node->get_child_count(), 0); + + SUBCASE("Node should be accessible as first child") { + Node *child = SceneTree::get_singleton()->get_root()->get_child(0); + CHECK_EQ(child, node); + } + + SUBCASE("Node should be accessible via the node path") { + Node *child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node->get_path()); + CHECK_EQ(child_by_path, node); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("Node")); + CHECK_EQ(child_by_path, nullptr); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("/root/Node")); + CHECK_EQ(child_by_path, nullptr); + + node->set_name("Node"); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node->get_path()); + CHECK_EQ(child_by_path, node); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("Node")); + CHECK_EQ(child_by_path, node); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("/root/Node")); + CHECK_EQ(child_by_path, node); + } + + SUBCASE("Node should be accessible via group") { + List<Node *> nodes; + SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes); + CHECK(nodes.is_empty()); + + node->add_to_group("nodes"); + + SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes); + CHECK_EQ(nodes.size(), 1); + List<Node *>::Element *E = nodes.front(); + CHECK_EQ(E->get(), node); + } + + SUBCASE("Node should be possible to find") { + Node *child = SceneTree::get_singleton()->get_root()->find_child("Node", true, false); + CHECK_EQ(child, nullptr); + + node->set_name("Node"); + + child = SceneTree::get_singleton()->get_root()->find_child("Node", true, false); + CHECK_EQ(child, node); + } + + SUBCASE("Node should be possible to remove") { + SceneTree::get_singleton()->get_root()->remove_child(node); + + CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 0); + CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 1); + + CHECK_FALSE(node->is_inside_tree()); + CHECK_EQ(node->get_parent(), nullptr); + ERR_PRINT_OFF; + CHECK(node->get_path().is_empty()); + ERR_PRINT_ON; + } + + SUBCASE("Node should be possible to move") { + SceneTree::get_singleton()->get_root()->move_child(node, 0); + + Node *child = SceneTree::get_singleton()->get_root()->get_child(0); + CHECK_EQ(child, node); + CHECK(node->is_inside_tree()); + } + + memdelete(node); +} + +TEST_CASE("[SceneTree][Node] Nested Add/Remove/Move/Find") { + Node *node1 = memnew(Node); + Node *node2 = memnew(Node); + Node *node1_1 = memnew(Node); + + SceneTree::get_singleton()->get_root()->add_child(node1); + SceneTree::get_singleton()->get_root()->add_child(node2); + + node1->add_child(node1_1); + + CHECK(node1_1->is_inside_tree()); + CHECK_EQ(node1_1->get_parent(), node1); + CHECK_EQ(node1->get_child_count(), 1); + + CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 2); + CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 4); + + SUBCASE("Nodes should be accessible via get_child(..)") { + Node *child1 = SceneTree::get_singleton()->get_root()->get_child(0); + CHECK_EQ(child1, node1); + + Node *child2 = SceneTree::get_singleton()->get_root()->get_child(1); + CHECK_EQ(child2, node2); + + Node *child1_1 = node1->get_child(0); + CHECK_EQ(child1_1, node1_1); + } + + SUBCASE("Removed nodes should also remove their children from the scene tree") { + // Should also remove node1_1 from the scene tree. + SceneTree::get_singleton()->get_root()->remove_child(node1); + + CHECK_EQ(node1->get_child_count(), 1); + + CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 1); + CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 2); + + // First child should now be the second node. + Node *child1 = SceneTree::get_singleton()->get_root()->get_child(0); + CHECK_EQ(child1, node2); + } + + SUBCASE("Removed children nodes should not affect their parent in the scene tree") { + node1->remove_child(node1_1); + + CHECK_EQ(node1_1->get_parent(), nullptr); + CHECK_EQ(node1->get_child_count(), 0); + + CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 3); + } + + SUBCASE("Nodes should be in the expected order when a node is moved to the back") { + SceneTree::get_singleton()->get_root()->move_child(node1, 1); + + Node *child1 = SceneTree::get_singleton()->get_root()->get_child(0); + CHECK_EQ(child1, node2); + + Node *child2 = SceneTree::get_singleton()->get_root()->get_child(1); + CHECK_EQ(child2, node1); + } + + SUBCASE("Nodes should be in the expected order when a node is moved to the front") { + SceneTree::get_singleton()->get_root()->move_child(node2, 0); + + Node *child1 = SceneTree::get_singleton()->get_root()->get_child(0); + CHECK_EQ(child1, node2); + + Node *child2 = SceneTree::get_singleton()->get_root()->get_child(1); + CHECK_EQ(child2, node1); + } + + SUBCASE("Nodes should be in the expected order when reparented") { + CHECK_EQ(node2->get_child_count(), 0); + + node1->remove_child(node1_1); + CHECK_EQ(node1->get_child_count(), 0); + CHECK_EQ(node1_1->get_parent(), nullptr); + + node2->add_child(node1_1); + CHECK_EQ(node2->get_child_count(), 1); + CHECK_EQ(node1_1->get_parent(), node2); + + Node *child = node2->get_child(0); + CHECK_EQ(child, node1_1); + + CHECK_EQ(SceneTree::get_singleton()->get_root()->get_child_count(), 2); + CHECK_EQ(SceneTree::get_singleton()->get_node_count(), 4); + } + + SUBCASE("Nodes should be possible to find") { + Node *child = SceneTree::get_singleton()->get_root()->find_child("NestedNode", true, false); + CHECK_EQ(child, nullptr); + + node1->set_name("Node1"); + node2->set_name("Node2"); + node1_1->set_name("NestedNode"); + + child = SceneTree::get_singleton()->get_root()->find_child("NestedNode", true, false); + CHECK_EQ(child, node1_1); + + // First node that matches with the name is node1. + child = SceneTree::get_singleton()->get_root()->find_child("Node?", true, false); + CHECK_EQ(child, node1); + + SceneTree::get_singleton()->get_root()->move_child(node2, 0); + + // It should be node2, as it is now the first one in the tree. + child = SceneTree::get_singleton()->get_root()->find_child("Node?", true, false); + CHECK_EQ(child, node2); + } + + SUBCASE("Nodes should be accessible via their node path") { + Node *child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node1->get_path()); + CHECK_EQ(child_by_path, node1); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node2->get_path()); + CHECK_EQ(child_by_path, node2); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(node1_1->get_path()); + CHECK_EQ(child_by_path, node1_1); + + node1->set_name("Node1"); + node1_1->set_name("NestedNode"); + + child_by_path = node1->get_node_or_null(NodePath("NestedNode")); + CHECK_EQ(child_by_path, node1_1); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("/root/Node1/NestedNode")); + CHECK_EQ(child_by_path, node1_1); + + child_by_path = SceneTree::get_singleton()->get_root()->get_node_or_null(NodePath("Node1/NestedNode")); + CHECK_EQ(child_by_path, node1_1); + } + + SUBCASE("Nodes should be accessible via their groups") { + List<Node *> nodes; + SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes); + CHECK(nodes.is_empty()); + + SceneTree::get_singleton()->get_nodes_in_group("other_nodes", &nodes); + CHECK(nodes.is_empty()); + + node1->add_to_group("nodes"); + node2->add_to_group("other_nodes"); + node1_1->add_to_group("nodes"); + node1_1->add_to_group("other_nodes"); + + SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes); + CHECK_EQ(nodes.size(), 2); + + List<Node *>::Element *E = nodes.front(); + CHECK_EQ(E->get(), node1); + E = E->next(); + CHECK_EQ(E->get(), node1_1); + + // Clear and try again with the other group. + nodes.clear(); + + SceneTree::get_singleton()->get_nodes_in_group("other_nodes", &nodes); + CHECK_EQ(nodes.size(), 2); + + E = nodes.front(); + CHECK_EQ(E->get(), node1_1); + E = E->next(); + CHECK_EQ(E->get(), node2); + + // Clear and try again with the other group and one node removed. + nodes.clear(); + + node1->remove_from_group("nodes"); + SceneTree::get_singleton()->get_nodes_in_group("nodes", &nodes); + CHECK_EQ(nodes.size(), 1); + + E = nodes.front(); + CHECK_EQ(E->get(), node1_1); + } + + memdelete(node1_1); + memdelete(node1); + memdelete(node2); +} + +TEST_CASE("[Node] Processing") { + Node *node = memnew(Node); + + SUBCASE("Processing") { + CHECK_FALSE(node->is_processing()); + + node->set_process(true); + + CHECK(node->is_processing()); + + node->set_process(false); + + CHECK_FALSE(node->is_processing()); + } + + SUBCASE("Physics processing") { + CHECK_FALSE(node->is_physics_processing()); + + node->set_physics_process(true); + + CHECK(node->is_physics_processing()); + + node->set_physics_process(false); + + CHECK_FALSE(node->is_physics_processing()); + } + + SUBCASE("Unhandled input processing") { + CHECK_FALSE(node->is_processing_unhandled_input()); + + node->set_process_unhandled_input(true); + + CHECK(node->is_processing_unhandled_input()); + + node->set_process_unhandled_input(false); + + CHECK_FALSE(node->is_processing_unhandled_input()); + } + + SUBCASE("Input processing") { + CHECK_FALSE(node->is_processing_input()); + + node->set_process_input(true); + + CHECK(node->is_processing_input()); + + node->set_process_input(false); + + CHECK_FALSE(node->is_processing_input()); + } + + SUBCASE("Unhandled key input processing") { + CHECK_FALSE(node->is_processing_unhandled_key_input()); + + node->set_process_unhandled_key_input(true); + + CHECK(node->is_processing_unhandled_key_input()); + + node->set_process_unhandled_key_input(false); + + CHECK_FALSE(node->is_processing_unhandled_key_input()); + } + + SUBCASE("Shortcut input processing") { + CHECK_FALSE(node->is_processing_shortcut_input()); + + node->set_process_shortcut_input(true); + + CHECK(node->is_processing_shortcut_input()); + + node->set_process_shortcut_input(false); + + CHECK_FALSE(node->is_processing_shortcut_input()); + } + + SUBCASE("Internal processing") { + CHECK_FALSE(node->is_processing_internal()); + + node->set_process_internal(true); + + CHECK(node->is_processing_internal()); + + node->set_process_internal(false); + + CHECK_FALSE(node->is_processing_internal()); + } + + memdelete(node); +} + +} // namespace TestNode + +#endif // TEST_NODE_H diff --git a/tests/scene/test_path_2d.h b/tests/scene/test_path_2d.h new file mode 100644 index 0000000000..7b6cec96db --- /dev/null +++ b/tests/scene/test_path_2d.h @@ -0,0 +1,109 @@ +/**************************************************************************/ +/* test_path_2d.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TEST_PATH_2D_H +#define TEST_PATH_2D_H + +#include "scene/2d/path_2d.h" + +#include "tests/test_macros.h" + +namespace TestPath2D { + +TEST_CASE("[SceneTree][Path2D] Initialization") { + SUBCASE("Path should be empty right after initialization") { + Path2D *test_path = memnew(Path2D); + CHECK(test_path->get_curve() == nullptr); + memdelete(test_path); + } +} + +TEST_CASE("[SceneTree][Path2D] Curve setter and getter") { + Path2D *test_path = memnew(Path2D); + const Ref<Curve2D> &curve = memnew(Curve2D); + + SUBCASE("Curve passed to the class should remain the same") { + test_path->set_curve(curve); + CHECK(test_path->get_curve() == curve); + } + SUBCASE("Curve passed many times to the class should remain the same") { + test_path->set_curve(curve); + test_path->set_curve(curve); + test_path->set_curve(curve); + CHECK(test_path->get_curve() == curve); + } + SUBCASE("Curve rewrite testing") { + const Ref<Curve2D> &curve1 = memnew(Curve2D); + const Ref<Curve2D> &curve2 = memnew(Curve2D); + + test_path->set_curve(curve1); + test_path->set_curve(curve2); + CHECK_MESSAGE(test_path->get_curve() != curve1, + "After rewrite, second curve should be in class"); + CHECK_MESSAGE(test_path->get_curve() == curve2, + "After rewrite, second curve should be in class"); + } + + SUBCASE("Assign same curve to two paths") { + Path2D *path2 = memnew(Path2D); + + test_path->set_curve(curve); + path2->set_curve(curve); + CHECK_MESSAGE(test_path->get_curve() == path2->get_curve(), + "Both paths have the same curve."); + memdelete(path2); + } + + SUBCASE("Swapping curves between two paths") { + Path2D *path2 = memnew(Path2D); + const Ref<Curve2D> &curve1 = memnew(Curve2D); + const Ref<Curve2D> &curve2 = memnew(Curve2D); + + test_path->set_curve(curve1); + path2->set_curve(curve2); + CHECK(test_path->get_curve() == curve1); + CHECK(path2->get_curve() == curve2); + + // Do the swap + Ref<Curve2D> temp = test_path->get_curve(); + test_path->set_curve(path2->get_curve()); + path2->set_curve(temp); + + CHECK(test_path->get_curve() == curve2); + CHECK(path2->get_curve() == curve1); + memdelete(path2); + } + + memdelete(test_path); +} + +} // namespace TestPath2D + +#endif // TEST_PATH_2D_H diff --git a/tests/scene/test_path_3d.h b/tests/scene/test_path_3d.h index 8ac3d7b5b4..f779f514a4 100644 --- a/tests/scene/test_path_3d.h +++ b/tests/scene/test_path_3d.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* test_path_3d.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. */ -/*************************************************************************/ +/**************************************************************************/ +/* test_path_3d.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef TEST_PATH_3D_H #define TEST_PATH_3D_H diff --git a/tests/scene/test_path_follow_2d.h b/tests/scene/test_path_follow_2d.h index 57261116a2..1958befa18 100644 --- a/tests/scene/test_path_follow_2d.h +++ b/tests/scene/test_path_follow_2d.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* test_path_follow_2d.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* test_path_follow_2d.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef TEST_PATH_FOLLOW_2D_H #define TEST_PATH_FOLLOW_2D_H diff --git a/tests/scene/test_path_follow_3d.h b/tests/scene/test_path_follow_3d.h index 6334fa56de..7595fddd2f 100644 --- a/tests/scene/test_path_follow_3d.h +++ b/tests/scene/test_path_follow_3d.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* test_path_follow_3d.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. */ -/*************************************************************************/ +/**************************************************************************/ +/* test_path_follow_3d.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef TEST_PATH_FOLLOW_3D_H #define TEST_PATH_FOLLOW_3D_H diff --git a/tests/scene/test_primitives.h b/tests/scene/test_primitives.h new file mode 100644 index 0000000000..6cdb5fb0a5 --- /dev/null +++ b/tests/scene/test_primitives.h @@ -0,0 +1,850 @@ +/**************************************************************************/ +/* test_primitives.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TEST_PRIMITIVES_H +#define TEST_PRIMITIVES_H + +#include "scene/resources/primitive_meshes.h" + +#include "tests/test_macros.h" + +namespace TestPrimitives { + +TEST_CASE("[SceneTree][Primitive][Capsule] Capsule Primitive") { + Ref<CapsuleMesh> capsule = memnew(CapsuleMesh); + + SUBCASE("[SceneTree][Primitive][Capsule] Default values should be valid") { + CHECK_MESSAGE(capsule->get_radius() > 0, + "Radius of default capsule positive."); + CHECK_MESSAGE(capsule->get_height() > 0, + "Height of default capsule positive."); + CHECK_MESSAGE(capsule->get_radial_segments() >= 0, + "Radius Segments of default capsule positive."); + CHECK_MESSAGE(capsule->get_rings() >= 0, + "Number of rings of default capsule positive."); + } + + SUBCASE("[SceneTree][Primitive][Capsule] Set properties of the capsule and get them with accessor methods") { + capsule->set_height(7.1f); + capsule->set_radius(1.3f); + capsule->set_radial_segments(16); + capsule->set_rings(32); + + CHECK_MESSAGE(capsule->get_radius() == doctest::Approx(1.3f), + "Get/Set radius work with one set."); + CHECK_MESSAGE(capsule->get_height() == doctest::Approx(7.1f), + "Get/Set radius work with one set."); + CHECK_MESSAGE(capsule->get_radial_segments() == 16, + "Get/Set radius work with one set."); + CHECK_MESSAGE(capsule->get_rings() == 32, + "Get/Set radius work with one set."); + } + + SUBCASE("[SceneTree][Primitive][Capsule] If set segments negative, default to at least 0") { + capsule->set_radial_segments(-5); + capsule->set_rings(-17); + + CHECK_MESSAGE(capsule->get_radial_segments() >= 0, + "Ensure number of radial segments is >= 0."); + CHECK_MESSAGE(capsule->get_rings() >= 0, + "Ensure number of rings is >= 0."); + } + + SUBCASE("[SceneTree][Primitive][Capsule] If set height < 2*radius, adjust radius and height to radius=height*0.5") { + capsule->set_radius(1.f); + capsule->set_height(0.5f); + + CHECK_MESSAGE(capsule->get_radius() >= capsule->get_height() * 0.5, + "Ensure radius >= height * 0.5 (needed for capsule to exist)."); + } + + SUBCASE("[Primitive][Capsule] Check mesh is correct") { + Array data{}; + data.resize(RS::ARRAY_MAX); + float radius{ 0.5f }; + float height{ 4.f }; + int num_radial_segments{ 4 }; + int num_rings{ 8 }; + CapsuleMesh::create_mesh_array(data, radius, height, num_radial_segments, num_rings); + Vector<Vector3> points = data[RS::ARRAY_VERTEX]; + + SUBCASE("[Primitive][Capsule] Ensure all vertices positions are within bounding radius and height") { + // Get mesh data + + // Check all points within radius of capsule + float dist_to_yaxis = 0.f; + for (Vector3 point : points) { + float new_dist_to_y = point.x * point.x + point.z * point.z; + if (new_dist_to_y > dist_to_yaxis) + dist_to_yaxis = new_dist_to_y; + } + + CHECK(dist_to_yaxis <= radius * radius); + + // Check highest point and lowest point are within height of each other + float max_y{ 0.f }; + float min_y{ 0.f }; + for (Vector3 point : points) { + if (point.y > max_y) + max_y = point.y; + if (point.y < min_y) + min_y = point.y; + } + + CHECK(max_y - min_y <= height); + } + + SUBCASE("[Primitive][Capsule] If normal.y == 0, then mesh makes a cylinder.") { + Vector<Vector3> normals = data[RS::ARRAY_NORMAL]; + for (int ii = 0; ii < points.size(); ++ii) { + float point_dist_from_yaxis = Math::sqrt(points[ii].x * points[ii].x + points[ii].z * points[ii].z); + Vector3 yaxis_to_point{ points[ii].x / point_dist_from_yaxis, 0.f, points[ii].z / point_dist_from_yaxis }; + if (normals[ii].y == 0.f) { + float mag_of_normal = Math::sqrt(normals[ii].x * normals[ii].x + normals[ii].z * normals[ii].z); + Vector3 normalized_normal = normals[ii] / mag_of_normal; + CHECK_MESSAGE(point_dist_from_yaxis == doctest::Approx(radius), + "Points on the tube of the capsule are radius away from y-axis."); + CHECK_MESSAGE(normalized_normal.is_equal_approx(yaxis_to_point), + "Normal points orthogonal from mid cylinder."); + } + } + } + } +} // End capsule tests + +TEST_CASE("[SceneTree][Primitive][Box] Box Primitive") { + Ref<BoxMesh> box = memnew(BoxMesh); + + SUBCASE("[SceneTree][Primitive][Box] Default values should be valid") { + CHECK(box->get_size().x > 0); + CHECK(box->get_size().y > 0); + CHECK(box->get_size().z > 0); + CHECK(box->get_subdivide_width() >= 0); + CHECK(box->get_subdivide_height() >= 0); + CHECK(box->get_subdivide_depth() >= 0); + } + + SUBCASE("[SceneTree][Primitive][Box] Set properties and get them with accessor methods") { + Vector3 size{ 2.1, 3.3, 1.7 }; + box->set_size(size); + box->set_subdivide_width(3); + box->set_subdivide_height(2); + box->set_subdivide_depth(4); + + CHECK(box->get_size().is_equal_approx(size)); + CHECK(box->get_subdivide_width() == 3); + CHECK(box->get_subdivide_height() == 2); + CHECK(box->get_subdivide_depth() == 4); + } + + SUBCASE("[SceneTree][Primitive][Box] Set subdivides to negative and ensure they are >= 0") { + box->set_subdivide_width(-2); + box->set_subdivide_height(-2); + box->set_subdivide_depth(-2); + + CHECK(box->get_subdivide_width() >= 0); + CHECK(box->get_subdivide_height() >= 0); + CHECK(box->get_subdivide_depth() >= 0); + } + + SUBCASE("[Primitive][Box] Check mesh is correct.") { + Array data{}; + data.resize(RS::ARRAY_MAX); + Vector3 size{ 0.5f, 1.2f, .9f }; + int subdivide_width{ 3 }; + int subdivide_height{ 2 }; + int subdivide_depth{ 8 }; + BoxMesh::create_mesh_array(data, size, subdivide_width, subdivide_height, subdivide_depth); + Vector<Vector3> points = data[RS::ARRAY_VERTEX]; + Vector<Vector3> normals = data[RS::ARRAY_NORMAL]; + + SUBCASE("Only 6 distinct normals.") { + Vector<Vector3> distinct_normals{}; + distinct_normals.push_back(normals[0]); + + for (const Vector3 &normal : normals) { + bool add_normal{ true }; + for (const Vector3 &vec : distinct_normals) { + if (vec.is_equal_approx(normal)) + add_normal = false; + } + + if (add_normal) + distinct_normals.push_back(normal); + } + + CHECK_MESSAGE(distinct_normals.size() == 6, + "There are exactly 6 distinct normals in the mesh data."); + + // All normals are orthogonal, or pointing in same direction. + bool normal_correct_direction{ true }; + for (int rowIndex = 0; rowIndex < distinct_normals.size(); ++rowIndex) { + for (int colIndex = rowIndex + 1; colIndex < distinct_normals.size(); ++colIndex) { + if (!Math::is_equal_approx(distinct_normals[rowIndex].normalized().dot(distinct_normals[colIndex].normalized()), 0) && + !Math::is_equal_approx(distinct_normals[rowIndex].normalized().dot(distinct_normals[colIndex].normalized()), 1) && + !Math::is_equal_approx(distinct_normals[rowIndex].normalized().dot(distinct_normals[colIndex].normalized()), -1)) { + normal_correct_direction = false; + break; + } + } + if (!normal_correct_direction) + break; + } + + CHECK_MESSAGE(normal_correct_direction, + "All normals are either orthogonal or colinear."); + } + } +} // End box tests + +TEST_CASE("[SceneTree][Primitive][Cylinder] Cylinder Primitive") { + Ref<CylinderMesh> cylinder = memnew(CylinderMesh); + + SUBCASE("[SceneTree][Primitive][Cylinder] Default values should be valid") { + CHECK(cylinder->get_top_radius() > 0); + CHECK(cylinder->get_bottom_radius() > 0); + CHECK(cylinder->get_height() > 0); + CHECK(cylinder->get_radial_segments() > 0); + CHECK(cylinder->get_rings() > 0); + } + + SUBCASE("[SceneTree][Primitive][Cylinder] Set properties and get them") { + cylinder->set_top_radius(4.3f); + cylinder->set_bottom_radius(1.2f); + cylinder->set_height(9.77f); + cylinder->set_radial_segments(12); + cylinder->set_rings(16); + cylinder->set_cap_top(false); + cylinder->set_cap_bottom(false); + + CHECK(cylinder->get_top_radius() == doctest::Approx(4.3f)); + CHECK(cylinder->get_bottom_radius() == doctest::Approx(1.2f)); + CHECK(cylinder->get_height() == doctest::Approx(9.77f)); + CHECK(cylinder->get_radial_segments() == 12); + CHECK(cylinder->get_rings() == 16); + CHECK(!cylinder->is_cap_top()); + CHECK(!cylinder->is_cap_bottom()); + } + + SUBCASE("[SceneTree][Primitive][Cylinder] Ensure num segments is >= 0") { + cylinder->set_radial_segments(-12); + cylinder->set_rings(-16); + + CHECK(cylinder->get_radial_segments() >= 0); + CHECK(cylinder->get_rings() >= 0); + } + + SUBCASE("[Primitive][Cylinder] Actual cylinder mesh tests (top and bottom radius the same).") { + Array data{}; + data.resize(RS::ARRAY_MAX); + real_t radius = .9f; + real_t height = 3.2f; + int radial_segments = 8; + int rings = 5; + bool top_cap = true; + bool bottom_cap = true; + CylinderMesh::create_mesh_array(data, radius, radius, height, radial_segments, rings, top_cap, bottom_cap); + Vector<Vector3> points = data[RS::ARRAY_VERTEX]; + Vector<Vector3> normals = data[RS::ARRAY_NORMAL]; + + SUBCASE("[Primitive][Cylinder] Side points are radius away from y-axis.") { + bool is_radius_correct{ true }; + for (int index = 0; index < normals.size(); ++index) { + if (Math::is_equal_approx(normals[index].y, 0)) { + if (!Math::is_equal_approx((points[index] - Vector3(0, points[index].y, 0)).length_squared(), radius * radius)) { + is_radius_correct = false; + break; + } + } + } + + CHECK(is_radius_correct); + } + + SUBCASE("[Primitive][Cylinder] Only possible normals point in direction of point or in positive/negative y direction.") { + bool is_correct_normals{ true }; + for (int index = 0; index < normals.size(); ++index) { + Vector3 yaxis_to_point = points[index] - Vector3(0.f, points[index].y, 0.f); + Vector3 point_to_normal = normals[index].normalized() - yaxis_to_point.normalized(); + // std::cout << "<" << point_to_normal.x << ", " << point_to_normal.y << ", " << point_to_normal.z << ">\n"; + if (!(point_to_normal.is_equal_approx(Vector3(0, 0, 0))) && + (!Math::is_equal_approx(Math::abs(normals[index].normalized().y), 1))) { + is_correct_normals = false; + break; + } + } + + CHECK(is_correct_normals); + } + + SUBCASE("[Primitive][Cylinder] Points on top and bottom are height/2 away from origin.") { + bool is_height_correct{ true }; + real_t half_height = 0.5 * height; + for (int index = 0; index < normals.size(); ++index) { + if (Math::is_equal_approx(normals[index].x, 0) && + Math::is_equal_approx(normals[index].z, 0) && + normals[index].y > 0) { + if (!Math::is_equal_approx(points[index].y, half_height)) { + is_height_correct = false; + break; + } + } + if (Math::is_equal_approx(normals[index].x, 0) && + Math::is_equal_approx(normals[index].z, 0) && + normals[index].y < 0) { + if (!Math::is_equal_approx(points[index].y, -half_height)) { + is_height_correct = false; + break; + } + } + } + + CHECK(is_height_correct); + } + + SUBCASE("[Primitive][Cylinder] Does mesh obey cap parameters?") { + CylinderMesh::create_mesh_array(data, radius, radius, height, radial_segments, rings, top_cap, false); + points = data[RS::ARRAY_VERTEX]; + normals = data[RS::ARRAY_NORMAL]; + bool no_bottom_cap{ true }; + + for (int index = 0; index < normals.size(); ++index) { + if (Math::is_equal_approx(normals[index].x, 0) && + Math::is_equal_approx(normals[index].z, 0) && + normals[index].y < 0) { + no_bottom_cap = false; + break; + } + } + + CHECK_MESSAGE(no_bottom_cap, + "Check there is no bottom cap."); + + CylinderMesh::create_mesh_array(data, radius, radius, height, radial_segments, rings, false, bottom_cap); + points = data[RS::ARRAY_VERTEX]; + normals = data[RS::ARRAY_NORMAL]; + bool no_top_cap{ true }; + + for (int index = 0; index < normals.size(); ++index) { + if (Math::is_equal_approx(normals[index].x, 0) && + Math::is_equal_approx(normals[index].z, 0) && + normals[index].y > 0) { + no_top_cap = false; + break; + } + } + + CHECK_MESSAGE(no_top_cap, + "Check there is no top cap."); + } + } + + SUBCASE("[Primitive][Cylinder] Slanted cylinder mesh (top and bottom radius different).") { + Array data{}; + data.resize(RS::ARRAY_MAX); + real_t top_radius = 2.f; + real_t bottom_radius = 1.f; + real_t height = 1.f; + int radial_segments = 8; + int rings = 5; + CylinderMesh::create_mesh_array(data, top_radius, bottom_radius, height, radial_segments, rings, false, false); + Vector<Vector3> points = data[RS::ARRAY_VERTEX]; + Vector<Vector3> normals = data[RS::ARRAY_NORMAL]; + + SUBCASE("[Primitive][Cylinder] Side points lie correct distance from y-axis") { + bool is_radius_correct{ true }; + for (int index = 0; index < points.size(); ++index) { + real_t radius = ((top_radius - bottom_radius) / height) * (points[index].y - 0.5 * height) + top_radius; + Vector3 distance_to_yaxis = points[index] - Vector3(0.f, points[index].y, 0.f); + if (!Math::is_equal_approx(distance_to_yaxis.length_squared(), radius * radius)) { + is_radius_correct = false; + break; + } + } + + CHECK(is_radius_correct); + } + + SUBCASE("[Primitive][Cylinder] Normal on side is orthogonal to side tangent vector") { + bool is_normal_correct{ true }; + for (int index = 0; index < points.size(); ++index) { + Vector3 yaxis_to_point = points[index] - Vector3(0.f, points[index].y, 0.f); + Vector3 yaxis_to_rb = yaxis_to_point.normalized() * bottom_radius; + Vector3 rb_to_point = yaxis_to_point - yaxis_to_rb; + Vector3 y_to_bottom = -Vector3(0.f, points[index].y + 0.5 * height, 0.f); + Vector3 side_tangent = rb_to_point - y_to_bottom; + + if (!Math::is_equal_approx(normals[index].dot(side_tangent), 0)) { + is_normal_correct = false; + break; + } + } + + CHECK(is_normal_correct); + } + } + +} // End cylinder tests + +TEST_CASE("[SceneTree][Primitive][Plane] Plane Primitive") { + Ref<PlaneMesh> plane = memnew(PlaneMesh); + + SUBCASE("[SceneTree][Primitive][Plane] Default values should be valid") { + CHECK(plane->get_size().x > 0); + CHECK(plane->get_size().y > 0); + CHECK(plane->get_subdivide_width() >= 0); + CHECK(plane->get_subdivide_depth() >= 0); + CHECK((plane->get_orientation() == PlaneMesh::FACE_X || plane->get_orientation() == PlaneMesh::FACE_Y || plane->get_orientation() == PlaneMesh::FACE_Z)); + } + + SUBCASE("[SceneTree][Primitive][Plane] Set properties and get them.") { + Size2 size{ 3.2, 1.8 }; + Vector3 offset{ -7.3, 0.4, -1.7 }; + plane->set_size(size); + plane->set_subdivide_width(15); + plane->set_subdivide_depth(29); + plane->set_center_offset(offset); + plane->set_orientation(PlaneMesh::FACE_X); + + CHECK(plane->get_size().is_equal_approx(size)); + CHECK(plane->get_subdivide_width() == 15); + CHECK(plane->get_subdivide_depth() == 29); + CHECK(plane->get_center_offset().is_equal_approx(offset)); + CHECK(plane->get_orientation() == PlaneMesh::FACE_X); + } + + SUBCASE("[SceneTree][Primitive][Plane] Ensure number of segments is >= 0.") { + plane->set_subdivide_width(-15); + plane->set_subdivide_depth(-29); + + CHECK(plane->get_subdivide_width() >= 0); + CHECK(plane->get_subdivide_depth() >= 0); + } +} + +TEST_CASE("[SceneTree][Primitive][Quad] QuadMesh Primitive") { + Ref<QuadMesh> quad = memnew(QuadMesh); + + SUBCASE("[Primitive][Quad] Orientation on initialization is in z direction") { + CHECK(quad->get_orientation() == PlaneMesh::FACE_Z); + } +} + +TEST_CASE("[SceneTree][Primitive][Prism] Prism Primitive") { + Ref<PrismMesh> prism = memnew(PrismMesh); + + SUBCASE("[Primitive][Prism] There are valid values of properties on initialization.") { + CHECK(prism->get_left_to_right() >= 0); + CHECK(prism->get_size().x >= 0); + CHECK(prism->get_size().y >= 0); + CHECK(prism->get_size().z >= 0); + CHECK(prism->get_subdivide_width() >= 0); + CHECK(prism->get_subdivide_height() >= 0); + CHECK(prism->get_subdivide_depth() >= 0); + } + + SUBCASE("[Primitive][Prism] Are able to change prism properties.") { + Vector3 size{ 4.3, 9.1, 0.43 }; + prism->set_left_to_right(3.4f); + prism->set_size(size); + prism->set_subdivide_width(36); + prism->set_subdivide_height(5); + prism->set_subdivide_depth(64); + + CHECK(prism->get_left_to_right() == doctest::Approx(3.4f)); + CHECK(prism->get_size().is_equal_approx(size)); + CHECK(prism->get_subdivide_width() == 36); + CHECK(prism->get_subdivide_height() == 5); + CHECK(prism->get_subdivide_depth() == 64); + } + + SUBCASE("[Primitive][Prism] Ensure number of segments always >= 0") { + prism->set_subdivide_width(-36); + prism->set_subdivide_height(-5); + prism->set_subdivide_depth(-64); + + CHECK(prism->get_subdivide_width() >= 0); + CHECK(prism->get_subdivide_height() >= 0); + CHECK(prism->get_subdivide_depth() >= 0); + } +} + +TEST_CASE("[SceneTree][Primitive][Sphere] Sphere Primitive") { + Ref<SphereMesh> sphere = memnew(SphereMesh); + + SUBCASE("[Primitive][Sphere] There are valid values of properties on initialization.") { + CHECK(sphere->get_radius() >= 0); + CHECK(sphere->get_height() >= 0); + CHECK(sphere->get_radial_segments() >= 0); + CHECK(sphere->get_rings() >= 0); + } + + SUBCASE("[Primitive][Sphere] Are able to change prism properties.") { + sphere->set_radius(3.4f); + sphere->set_height(2.2f); + sphere->set_radial_segments(36); + sphere->set_rings(5); + sphere->set_is_hemisphere(true); + + CHECK(sphere->get_radius() == doctest::Approx(3.4f)); + CHECK(sphere->get_height() == doctest::Approx(2.2f)); + CHECK(sphere->get_radial_segments() == 36); + CHECK(sphere->get_rings() == 5); + CHECK(sphere->get_is_hemisphere()); + } + + SUBCASE("[Primitive][Sphere] Ensure number of segments always >= 0") { + sphere->set_radial_segments(-36); + sphere->set_rings(-5); + + CHECK(sphere->get_radial_segments() >= 0); + CHECK(sphere->get_rings() >= 0); + } + + SUBCASE("[Primitive][Sphere] Sphere mesh tests.") { + Array data{}; + data.resize(RS::ARRAY_MAX); + real_t radius = 1.1f; + int radial_segments = 8; + int rings = 5; + SphereMesh::create_mesh_array(data, radius, 2 * radius, radial_segments, rings); + Vector<Vector3> points = data[RS::ARRAY_VERTEX]; + Vector<Vector3> normals = data[RS::ARRAY_NORMAL]; + + SUBCASE("[Primitive][Sphere] All points lie radius away from origin.") { + bool is_radius_correct = true; + for (Vector3 point : points) { + if (!Math::is_equal_approx(point.length_squared(), radius * radius)) { + is_radius_correct = false; + break; + } + } + + CHECK(is_radius_correct); + } + + SUBCASE("[Primitive][Sphere] All normals lie in direction of corresponding point.") { + bool is_normals_correct = true; + for (int index = 0; index < points.size(); ++index) { + if (!Math::is_equal_approx(normals[index].normalized().dot(points[index].normalized()), 1)) { + is_normals_correct = false; + break; + } + } + + CHECK(is_normals_correct); + } + } +} + +TEST_CASE("[SceneTree][Primitive][Torus] Torus Primitive") { + Ref<TorusMesh> torus = memnew(TorusMesh); + Ref<PrimitiveMesh> prim = memnew(PrimitiveMesh); + + SUBCASE("[Primitive][Torus] There are valid values of properties on initialization.") { + CHECK(torus->get_inner_radius() > 0); + CHECK(torus->get_outer_radius() > 0); + CHECK(torus->get_rings() >= 0); + CHECK(torus->get_ring_segments() >= 0); + } + + SUBCASE("[Primitive][Torus] Are able to change properties.") { + torus->set_inner_radius(3.2f); + torus->set_outer_radius(9.5f); + torus->set_rings(19); + torus->set_ring_segments(43); + + CHECK(torus->get_inner_radius() == doctest::Approx(3.2f)); + CHECK(torus->get_outer_radius() == doctest::Approx(9.5f)); + CHECK(torus->get_rings() == 19); + CHECK(torus->get_ring_segments() == 43); + } +} + +TEST_CASE("[SceneTree][Primitive][TubeTrail] TubeTrail Primitive") { + Ref<TubeTrailMesh> tube = memnew(TubeTrailMesh); + + SUBCASE("[Primitive][TubeTrail] There are valid values of properties on initialization.") { + CHECK(tube->get_radius() > 0); + CHECK(tube->get_radial_steps() >= 0); + CHECK(tube->get_sections() >= 0); + CHECK(tube->get_section_length() > 0); + CHECK(tube->get_section_rings() >= 0); + CHECK(tube->get_curve() == nullptr); + CHECK(tube->get_builtin_bind_pose_count() >= 0); + } + + SUBCASE("[Primitive][TubeTrail] Are able to change properties.") { + tube->set_radius(7.2f); + tube->set_radial_steps(9); + tube->set_sections(33); + tube->set_section_length(5.5f); + tube->set_section_rings(12); + Ref<Curve> curve = memnew(Curve); + tube->set_curve(curve); + + CHECK(tube->get_radius() == doctest::Approx(7.2f)); + CHECK(tube->get_section_length() == doctest::Approx(5.5f)); + CHECK(tube->get_radial_steps() == 9); + CHECK(tube->get_sections() == 33); + CHECK(tube->get_section_rings() == 12); + CHECK(tube->get_curve() == curve); + } + + SUBCASE("[Primitive][TubeTrail] Setting same curve more than once, it remains the same.") { + Ref<Curve> curve = memnew(Curve); + tube->set_curve(curve); + tube->set_curve(curve); + tube->set_curve(curve); + + CHECK(tube->get_curve() == curve); + } + + SUBCASE("[Primitive][TubeTrail] Setting curve, then changing to different curve.") { + Ref<Curve> curve1 = memnew(Curve); + Ref<Curve> curve2 = memnew(Curve); + tube->set_curve(curve1); + CHECK(tube->get_curve() == curve1); + + tube->set_curve(curve2); + CHECK(tube->get_curve() == curve2); + } + + SUBCASE("[Primitive][TubeTrail] Assign same curve to two different tube trails") { + Ref<TubeTrailMesh> tube2 = memnew(TubeTrailMesh); + Ref<Curve> curve = memnew(Curve); + tube->set_curve(curve); + tube2->set_curve(curve); + + CHECK(tube->get_curve() == curve); + CHECK(tube2->get_curve() == curve); + } +} + +TEST_CASE("[SceneTree][Primitive][RibbonTrail] RibbonTrail Primitive") { + Ref<RibbonTrailMesh> ribbon = memnew(RibbonTrailMesh); + + SUBCASE("[Primitive][RibbonTrail] There are valid values of properties on initialization.") { + CHECK(ribbon->get_size() > 0); + CHECK(ribbon->get_sections() >= 0); + CHECK(ribbon->get_section_length() > 0); + CHECK(ribbon->get_section_segments() >= 0); + CHECK(ribbon->get_builtin_bind_pose_count() >= 0); + CHECK(ribbon->get_curve() == nullptr); + CHECK((ribbon->get_shape() == RibbonTrailMesh::SHAPE_CROSS || + ribbon->get_shape() == RibbonTrailMesh::SHAPE_FLAT)); + } + + SUBCASE("[Primitive][RibbonTrail] Able to change properties.") { + Ref<Curve> curve = memnew(Curve); + ribbon->set_size(4.3f); + ribbon->set_sections(16); + ribbon->set_section_length(1.3f); + ribbon->set_section_segments(9); + ribbon->set_curve(curve); + + CHECK(ribbon->get_size() == doctest::Approx(4.3f)); + CHECK(ribbon->get_section_length() == doctest::Approx(1.3f)); + CHECK(ribbon->get_sections() == 16); + CHECK(ribbon->get_section_segments() == 9); + CHECK(ribbon->get_curve() == curve); + } + + SUBCASE("[Primitive][RibbonTrail] Setting same curve more than once, it remains the same.") { + Ref<Curve> curve = memnew(Curve); + ribbon->set_curve(curve); + ribbon->set_curve(curve); + ribbon->set_curve(curve); + + CHECK(ribbon->get_curve() == curve); + } + + SUBCASE("[Primitive][RibbonTrail] Setting curve, then changing to different curve.") { + Ref<Curve> curve1 = memnew(Curve); + Ref<Curve> curve2 = memnew(Curve); + ribbon->set_curve(curve1); + CHECK(ribbon->get_curve() == curve1); + + ribbon->set_curve(curve2); + CHECK(ribbon->get_curve() == curve2); + } + + SUBCASE("[Primitive][RibbonTrail] Assign same curve to two different ribbon trails") { + Ref<RibbonTrailMesh> ribbon2 = memnew(RibbonTrailMesh); + Ref<Curve> curve = memnew(Curve); + ribbon->set_curve(curve); + ribbon2->set_curve(curve); + + CHECK(ribbon->get_curve() == curve); + CHECK(ribbon2->get_curve() == curve); + } +} + +TEST_CASE("[SceneTree][Primitive][Text] Text Primitive") { + Ref<TextMesh> text = memnew(TextMesh); + + SUBCASE("[Primitive][Text] There are valid values of properties on initialization.") { + CHECK((text->get_horizontal_alignment() == HORIZONTAL_ALIGNMENT_CENTER || + text->get_horizontal_alignment() == HORIZONTAL_ALIGNMENT_LEFT || + text->get_horizontal_alignment() == HORIZONTAL_ALIGNMENT_RIGHT || + text->get_horizontal_alignment() == HORIZONTAL_ALIGNMENT_FILL)); + CHECK((text->get_vertical_alignment() == VERTICAL_ALIGNMENT_BOTTOM || + text->get_vertical_alignment() == VERTICAL_ALIGNMENT_TOP || + text->get_vertical_alignment() == VERTICAL_ALIGNMENT_CENTER || + text->get_vertical_alignment() == VERTICAL_ALIGNMENT_FILL)); + CHECK(text->get_font() == nullptr); + CHECK(text->get_font_size() > 0); + CHECK(text->get_line_spacing() >= 0); + CHECK((text->get_autowrap_mode() == TextServer::AUTOWRAP_OFF || + text->get_autowrap_mode() == TextServer::AUTOWRAP_ARBITRARY || + text->get_autowrap_mode() == TextServer::AUTOWRAP_WORD || + text->get_autowrap_mode() == TextServer::AUTOWRAP_WORD_SMART)); + CHECK((text->get_text_direction() == TextServer::DIRECTION_AUTO || + text->get_text_direction() == TextServer::DIRECTION_LTR || + text->get_text_direction() == TextServer::DIRECTION_RTL)); + CHECK((text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_DEFAULT || + text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_URI || + text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_FILE || + text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_EMAIL || + text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_LIST || + text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_NONE || + text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_CUSTOM)); + CHECK(text->get_structured_text_bidi_override_options().size() >= 0); + CHECK(text->get_width() > 0); + CHECK(text->get_depth() > 0); + CHECK(text->get_curve_step() > 0); + CHECK(text->get_pixel_size() > 0); + } + + SUBCASE("[Primitive][Text] Change the properties of the mesh.") { + Ref<Font> font = memnew(Font); + Array options{}; + Point2 offset{ 30.8, 104.23 }; + text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); + text->set_vertical_alignment(VERTICAL_ALIGNMENT_BOTTOM); + text->set_text("Hello"); + text->set_font(font); + text->set_font_size(12); + text->set_line_spacing(1.7f); + text->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); + text->set_text_direction(TextServer::DIRECTION_RTL); + text->set_language("French"); + text->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_EMAIL); + text->set_structured_text_bidi_override_options(options); + text->set_uppercase(true); + real_t width{ 0.6 }; + real_t depth{ 1.7 }; + real_t pixel_size{ 2.8 }; + real_t curve_step{ 4.8 }; + text->set_width(width); + text->set_depth(depth); + text->set_curve_step(curve_step); + text->set_pixel_size(pixel_size); + text->set_offset(offset); + + CHECK(text->get_horizontal_alignment() == HORIZONTAL_ALIGNMENT_RIGHT); + CHECK(text->get_vertical_alignment() == VERTICAL_ALIGNMENT_BOTTOM); + CHECK(text->get_text_direction() == TextServer::DIRECTION_RTL); + CHECK(text->get_text() == "Hello"); + CHECK(text->get_font() == font); + CHECK(text->get_font_size() == 12); + CHECK(text->get_autowrap_mode() == TextServer::AUTOWRAP_WORD_SMART); + CHECK(text->get_language() == "French"); + CHECK(text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_EMAIL); + CHECK(text->get_structured_text_bidi_override_options() == options); + CHECK(text->is_uppercase() == true); + CHECK(text->get_offset() == offset); + CHECK(text->get_line_spacing() == doctest::Approx(1.7f)); + CHECK(text->get_width() == doctest::Approx(width)); + CHECK(text->get_depth() == doctest::Approx(depth)); + CHECK(text->get_curve_step() == doctest::Approx(curve_step)); + CHECK(text->get_pixel_size() == doctest::Approx(pixel_size)); + } + + SUBCASE("[Primitive][Text] Set objects multiple times.") { + Ref<Font> font = memnew(Font); + Array options{}; + Point2 offset{ 30.8, 104.23 }; + + text->set_font(font); + text->set_font(font); + text->set_font(font); + text->set_structured_text_bidi_override_options(options); + text->set_structured_text_bidi_override_options(options); + text->set_structured_text_bidi_override_options(options); + text->set_offset(offset); + text->set_offset(offset); + text->set_offset(offset); + + CHECK(text->get_font() == font); + CHECK(text->get_structured_text_bidi_override_options() == options); + CHECK(text->get_offset() == offset); + } + + SUBCASE("[Primitive][Text] Set then change objects.") { + Ref<Font> font1 = memnew(Font); + Ref<Font> font2 = memnew(Font); + Array options1{}; + Array options2{}; + Point2 offset1{ 30.8, 104.23 }; + Point2 offset2{ -30.8, -104.23 }; + + text->set_font(font1); + text->set_structured_text_bidi_override_options(options1); + text->set_offset(offset1); + + CHECK(text->get_font() == font1); + CHECK(text->get_structured_text_bidi_override_options() == options1); + CHECK(text->get_offset() == offset1); + + text->set_font(font2); + text->set_structured_text_bidi_override_options(options2); + text->set_offset(offset2); + + CHECK(text->get_font() == font2); + CHECK(text->get_structured_text_bidi_override_options() == options2); + CHECK(text->get_offset() == offset2); + } + + SUBCASE("[Primitive][Text] Assign same font to two Textmeshes.") { + Ref<TextMesh> text2 = memnew(TextMesh); + Ref<Font> font = memnew(Font); + + text->set_font(font); + text2->set_font(font); + + CHECK(text->get_font() == font); + CHECK(text2->get_font() == font); + } +} + +} // namespace TestPrimitives + +#endif // TEST_PRIMITIVES_H diff --git a/tests/scene/test_sprite_frames.h b/tests/scene/test_sprite_frames.h index 61bbd16493..bf127cd42c 100644 --- a/tests/scene/test_sprite_frames.h +++ b/tests/scene/test_sprite_frames.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* test_sprite_frames.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. */ -/*************************************************************************/ +/**************************************************************************/ +/* test_sprite_frames.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef TEST_SPRITE_FRAMES_H #define TEST_SPRITE_FRAMES_H @@ -210,9 +210,9 @@ TEST_CASE("[SpriteFrames] Frame addition, removal, and retrieval") { frames.get_frame_count(test_animation_name) == 0, "Animation has a default frame count of 0"); - frames.add_frame(test_animation_name, dummy_frame1, 0); - frames.add_frame(test_animation_name, dummy_frame1, 1); - frames.add_frame(test_animation_name, dummy_frame1, 2); + frames.add_frame(test_animation_name, dummy_frame1, 1.0, 0); + frames.add_frame(test_animation_name, dummy_frame1, 1.0, 1); + frames.add_frame(test_animation_name, dummy_frame1, 1.0, 2); CHECK_MESSAGE( frames.get_frame_count(test_animation_name) == 3, @@ -227,7 +227,7 @@ TEST_CASE("[SpriteFrames] Frame addition, removal, and retrieval") { // These error handling cases should not crash. ERR_PRINT_OFF; - frames.add_frame("does not exist", dummy_frame1, 0); + frames.add_frame("does not exist", dummy_frame1, 1.0, 0); frames.remove_frame(test_animation_name, -99); frames.remove_frame("does not exist", 0); ERR_PRINT_ON; diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index 7550c12a0f..944f9cb9f6 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* test_text_edit.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. */ -/*************************************************************************/ +/**************************************************************************/ +/* test_text_edit.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef TEST_TEXT_EDIT_H #define TEST_TEXT_EDIT_H @@ -93,10 +93,10 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->undo(); MessageQueue::get_singleton()->flush(); CHECK(text_edit->get_text() == "test text"); - CHECK(text_edit->get_caret_column() == 9); + CHECK(text_edit->get_caret_column() == 0); SIGNAL_CHECK("lines_edited_from", lines_edited_args); - SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); + SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_set"); text_edit->redo(); @@ -104,18 +104,18 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK(text_edit->get_text() == ""); CHECK(text_edit->get_caret_column() == 0); SIGNAL_CHECK("lines_edited_from", lines_edited_args); - SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); + SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_set"); // Cannot undo when not-editable but should still clear. text_edit->undo(); MessageQueue::get_singleton()->flush(); CHECK(text_edit->get_text() == "test text"); - CHECK(text_edit->get_caret_column() == 9); + CHECK(text_edit->get_caret_column() == 0); SIGNAL_CHECK("lines_edited_from", lines_edited_args); - SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); + SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_set"); // Clear. @@ -131,8 +131,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK(text_edit->get_text() == ""); CHECK(text_edit->get_caret_column() == 0); SIGNAL_CHECK("text_set", empty_signal_args); - SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_clear_args); + SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_changed"); text_edit->set_editable(true); @@ -252,6 +252,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK(text_edit->get_line(0) == "te"); CHECK(text_edit->has_selection()); CHECK(text_edit->get_selected_text() == "te"); + CHECK(text_edit->get_caret_column() == 2); SIGNAL_CHECK("lines_edited_from", lines_edited_args); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); @@ -263,6 +264,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { MessageQueue::get_singleton()->flush(); CHECK(text_edit->get_line(0) == "test text"); CHECK(text_edit->has_selection()); + CHECK(text_edit->get_selected_text() == "test"); + CHECK(text_edit->get_caret_column() == 4); SIGNAL_CHECK("lines_edited_from", lines_edited_args); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); @@ -271,7 +274,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->redo(); MessageQueue::get_singleton()->flush(); CHECK(text_edit->get_line(0) == "te"); - CHECK_FALSE(text_edit->has_selection()); // Currently not handled. + CHECK(text_edit->has_selection()); + CHECK(text_edit->get_caret_column() == 2); SIGNAL_CHECK("lines_edited_from", lines_edited_args); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); @@ -425,7 +429,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->undo(); MessageQueue::get_singleton()->flush(); CHECK(text_edit->get_text() == "testing\nswap"); - CHECK_FALSE(text_edit->has_selection()); // Not currently handled. + CHECK(text_edit->has_selection()); SIGNAL_CHECK("lines_edited_from", lines_edited_args); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); @@ -436,7 +440,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->redo(); MessageQueue::get_singleton()->flush(); CHECK(text_edit->get_text() == "new\ntesting\nswap"); - CHECK_FALSE(text_edit->has_selection()); // Not currently handled. + CHECK(text_edit->has_selection()); SIGNAL_CHECK("lines_edited_from", lines_edited_args); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); @@ -448,7 +452,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK(text_edit->has_selection()); CHECK(text_edit->get_selection_from_line() == 0); CHECK(text_edit->get_selection_to_line() == 2); - SIGNAL_CHECK("caret_changed", empty_signal_args); + SIGNAL_CHECK_FALSE("caret_changed"); ((Array)lines_edited_args[0])[0] = 2; ((Array)lines_edited_args[0])[1] = 3; @@ -533,7 +537,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { MessageQueue::get_singleton()->flush(); CHECK(text_edit->get_text() == "temidsting\nswap"); CHECK(text_edit->get_caret_line() == 0); - CHECK(text_edit->get_caret_column() == 10); + CHECK(text_edit->get_caret_column() == 5); CHECK(text_edit->has_selection()); SIGNAL_CHECK("lines_edited_from", lines_edited_args); SIGNAL_CHECK("text_changed", empty_signal_args); @@ -633,17 +637,42 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { } SUBCASE("[TextEdit] select word under caret") { - text_edit->set_text("test test"); + text_edit->set_text("\ntest test\ntest test"); + text_edit->set_caret_column(0); + text_edit->set_caret_line(1); + + text_edit->add_caret(2, 0); + text_edit->add_caret(2, 2); + CHECK(text_edit->get_caret_count() == 3); + + MessageQueue::get_singleton()->flush(); + + SIGNAL_DISCARD("text_set"); + SIGNAL_DISCARD("text_changed"); + SIGNAL_DISCARD("lines_edited_from"); + SIGNAL_DISCARD("caret_changed"); + text_edit->select_word_under_caret(); - CHECK(text_edit->get_selected_text() == "test"); - CHECK(text_edit->has_selection()); - CHECK(text_edit->get_selection_from_line() == 0); - CHECK(text_edit->get_selection_from_column() == 0); - CHECK(text_edit->get_selection_to_line() == 0); - CHECK(text_edit->get_selection_to_column() == 4); - CHECK(text_edit->get_caret_line() == 0); - CHECK(text_edit->get_caret_column() == 4); + CHECK(text_edit->has_selection(0)); + CHECK(text_edit->get_selected_text(0) == "test"); + CHECK(text_edit->get_selection_from_line(0) == 1); + CHECK(text_edit->get_selection_from_column(0) == 0); + CHECK(text_edit->get_selection_to_line(0) == 1); + CHECK(text_edit->get_selection_to_column(0) == 4); + CHECK(text_edit->get_caret_line(0) == 1); + CHECK(text_edit->get_caret_column(0) == 4); + + CHECK(text_edit->has_selection(1)); + CHECK(text_edit->get_selected_text(1) == "test"); + CHECK(text_edit->get_selection_from_line(1) == 2); + CHECK(text_edit->get_selection_from_column(1) == 0); + CHECK(text_edit->get_selection_to_line(1) == 2); + CHECK(text_edit->get_selection_to_column(1) == 4); + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 4); + + CHECK(text_edit->get_caret_count() == 2); text_edit->select_word_under_caret(); CHECK_FALSE(text_edit->has_selection()); @@ -652,27 +681,44 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SEND_GUI_ACTION(text_edit, "ui_text_select_word_under_caret"); CHECK(text_edit->get_viewport()->is_input_handled()); MessageQueue::get_singleton()->flush(); - CHECK(text_edit->has_selection()); - CHECK(text_edit->get_selected_text() == "test"); - CHECK(text_edit->get_selection_from_line() == 0); - CHECK(text_edit->get_selection_from_column() == 0); - CHECK(text_edit->get_selection_to_line() == 0); - CHECK(text_edit->get_selection_to_column() == 4); - CHECK(text_edit->get_caret_line() == 0); - CHECK(text_edit->get_caret_column() == 4); + CHECK(text_edit->has_selection(0)); + CHECK(text_edit->get_selected_text(0) == "test"); + CHECK(text_edit->get_selection_from_line(0) == 1); + CHECK(text_edit->get_selection_from_column(0) == 0); + CHECK(text_edit->get_selection_to_line(0) == 1); + CHECK(text_edit->get_selection_to_column(0) == 4); + CHECK(text_edit->get_caret_line(0) == 1); + CHECK(text_edit->get_caret_column(0) == 4); + + CHECK(text_edit->has_selection(1)); + CHECK(text_edit->get_selected_text(1) == "test"); + CHECK(text_edit->get_selection_from_line(1) == 2); + CHECK(text_edit->get_selection_from_column(1) == 0); + CHECK(text_edit->get_selection_to_line(1) == 2); + CHECK(text_edit->get_selection_to_column(1) == 4); + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 4); + + CHECK(text_edit->get_selected_text() == "test\ntest"); SIGNAL_CHECK("caret_changed", empty_signal_args); text_edit->set_selecting_enabled(false); text_edit->select_word_under_caret(); CHECK_FALSE(text_edit->has_selection()); CHECK(text_edit->get_selected_text() == ""); - CHECK(text_edit->get_caret_line() == 0); - CHECK(text_edit->get_caret_column() == 4); + CHECK(text_edit->get_caret_line(0) == 1); + CHECK(text_edit->get_caret_column(0) == 4); + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 4); SIGNAL_CHECK_FALSE("caret_changed"); text_edit->set_selecting_enabled(true); - text_edit->set_caret_line(0); - text_edit->set_caret_column(5); + text_edit->set_caret_line(1, false, true, 0, 0); + text_edit->set_caret_column(5, false, 0); + + text_edit->set_caret_line(2, false, true, 0, 1); + text_edit->set_caret_column(5, false, 1); + text_edit->select_word_under_caret(); CHECK_FALSE(text_edit->has_selection()); CHECK(text_edit->get_selected_text() == ""); @@ -680,11 +726,81 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->select_word_under_caret(); CHECK_FALSE(text_edit->has_selection()); CHECK(text_edit->get_selected_text() == ""); - CHECK(text_edit->get_caret_line() == 0); - CHECK(text_edit->get_caret_column() == 5); + CHECK(text_edit->get_caret_line(0) == 1); + CHECK(text_edit->get_caret_column(0) == 5); + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 5); SIGNAL_CHECK_FALSE("caret_changed"); } + SUBCASE("[TextEdit] add selection for next occurrence") { + text_edit->set_text("\ntest other_test\nrandom test\nword test word nonrandom"); + text_edit->set_caret_column(0); + text_edit->set_caret_line(1); + + // First selection made by the implicit select_word_under_caret call + text_edit->add_selection_for_next_occurrence(); + CHECK(text_edit->get_caret_count() == 1); + CHECK(text_edit->get_selected_text(0) == "test"); + CHECK(text_edit->get_selection_from_line(0) == 1); + CHECK(text_edit->get_selection_from_column(0) == 0); + CHECK(text_edit->get_selection_to_line(0) == 1); + CHECK(text_edit->get_selection_to_column(0) == 4); + CHECK(text_edit->get_caret_line(0) == 1); + CHECK(text_edit->get_caret_column(0) == 4); + + text_edit->add_selection_for_next_occurrence(); + CHECK(text_edit->get_caret_count() == 2); + CHECK(text_edit->get_selected_text(1) == "test"); + CHECK(text_edit->get_selection_from_line(1) == 1); + CHECK(text_edit->get_selection_from_column(1) == 13); + CHECK(text_edit->get_selection_to_line(1) == 1); + CHECK(text_edit->get_selection_to_column(1) == 17); + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 17); + + text_edit->add_selection_for_next_occurrence(); + CHECK(text_edit->get_caret_count() == 3); + CHECK(text_edit->get_selected_text(2) == "test"); + CHECK(text_edit->get_selection_from_line(2) == 2); + CHECK(text_edit->get_selection_from_column(2) == 9); + CHECK(text_edit->get_selection_to_line(2) == 2); + CHECK(text_edit->get_selection_to_column(2) == 13); + CHECK(text_edit->get_caret_line(2) == 2); + CHECK(text_edit->get_caret_column(2) == 13); + + text_edit->add_selection_for_next_occurrence(); + CHECK(text_edit->get_caret_count() == 4); + CHECK(text_edit->get_selected_text(3) == "test"); + CHECK(text_edit->get_selection_from_line(3) == 3); + CHECK(text_edit->get_selection_from_column(3) == 5); + CHECK(text_edit->get_selection_to_line(3) == 3); + CHECK(text_edit->get_selection_to_column(3) == 9); + CHECK(text_edit->get_caret_line(3) == 3); + CHECK(text_edit->get_caret_column(3) == 9); + + // A different word with a new manually added caret + text_edit->add_caret(2, 1); + text_edit->select(2, 0, 2, 4, 4); + CHECK(text_edit->get_selected_text(4) == "rand"); + + text_edit->add_selection_for_next_occurrence(); + CHECK(text_edit->get_caret_count() == 6); + CHECK(text_edit->get_selected_text(5) == "rand"); + CHECK(text_edit->get_selection_from_line(5) == 3); + CHECK(text_edit->get_selection_from_column(5) == 18); + CHECK(text_edit->get_selection_to_line(5) == 3); + CHECK(text_edit->get_selection_to_column(5) == 22); + CHECK(text_edit->get_caret_line(5) == 3); + CHECK(text_edit->get_caret_column(5) == 22); + + // Make sure the previous selections are still active + CHECK(text_edit->get_selected_text(0) == "test"); + CHECK(text_edit->get_selected_text(1) == "test"); + CHECK(text_edit->get_selected_text(2) == "test"); + CHECK(text_edit->get_selected_text(3) == "test"); + } + SUBCASE("[TextEdit] deselect on focus loss") { text_edit->set_text("test"); @@ -727,7 +843,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { #ifdef MACOS_ENABLED SEND_GUI_KEY_EVENT(text_edit, Key::RIGHT | KeyModifierMask::SHIFT | KeyModifierMask::ALT) #else - SEND_GUI_KEY_EVENT(text_edit, Key::RIGHT | KeyModifierMask::SHIFT | KeyModifierMask::CMD) + SEND_GUI_KEY_EVENT(text_edit, Key::RIGHT | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL) #endif CHECK(text_edit->has_selection()); CHECK(text_edit->get_selected_text() == "test"); @@ -739,7 +855,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { #ifdef MACOS_ENABLED SEND_GUI_KEY_EVENT(text_edit, Key::LEFT | KeyModifierMask::SHIFT | KeyModifierMask::ALT) #else - SEND_GUI_KEY_EVENT(text_edit, Key::LEFT | KeyModifierMask::SHIFT | KeyModifierMask::CMD) + SEND_GUI_KEY_EVENT(text_edit, Key::LEFT | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL) #endif CHECK_FALSE(text_edit->has_selection()); CHECK(text_edit->get_selected_text() == ""); @@ -775,8 +891,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->grab_focus(); MessageQueue::get_singleton()->flush(); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 1), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE); - SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::MASK_LEFT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 1), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); + SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButtonMask::LEFT, Key::NONE); CHECK(text_edit->has_selection()); CHECK(text_edit->get_selected_text() == "for s"); CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_POINTER); @@ -787,12 +903,12 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 5); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 9), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 9), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); CHECK_FALSE(text_edit->has_selection()); text_edit->set_selecting_enabled(false); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 1), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE); - SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::MASK_LEFT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 1), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); + SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButtonMask::LEFT, Key::NONE); CHECK_FALSE(text_edit->has_selection()); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 5); @@ -819,7 +935,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK(text_edit->get_caret_column() == 3); SIGNAL_CHECK("caret_changed", empty_signal_args); - SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::MASK_LEFT, Key::NONE); + SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButtonMask::LEFT, Key::NONE); CHECK(text_edit->has_selection()); CHECK(text_edit->get_selected_text() == "for selection"); CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_WORD); @@ -833,7 +949,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { Point2i line_0 = text_edit->get_pos_at_line_column(0, 0); line_0.y /= 2; - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, line_0, MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, line_0, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); CHECK_FALSE(text_edit->has_selection()); text_edit->set_selecting_enabled(false); @@ -852,7 +968,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { MessageQueue::get_singleton()->flush(); SEND_GUI_DOUBLE_CLICK(text_edit, text_edit->get_pos_at_line_column(0, 2), Key::NONE); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 2), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 2), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); CHECK(text_edit->has_selection()); CHECK(text_edit->get_selected_text() == "for selection"); CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_LINE); @@ -865,12 +981,12 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { Point2i line_0 = text_edit->get_pos_at_line_column(0, 0); line_0.y /= 2; - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, line_0, MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, line_0, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); CHECK_FALSE(text_edit->has_selection()); text_edit->set_selecting_enabled(false); SEND_GUI_DOUBLE_CLICK(text_edit, text_edit->get_pos_at_line_column(0, 2), Key::NONE); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 2), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 2), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); CHECK_FALSE(text_edit->has_selection()); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 0); @@ -884,8 +1000,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_text("this is some text\nfor selection"); MessageQueue::get_singleton()->flush(); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 0), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE | KeyModifierMask::SHIFT); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE | KeyModifierMask::SHIFT); CHECK(text_edit->has_selection()); CHECK(text_edit->get_selected_text() == "for s"); CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_POINTER); @@ -896,12 +1012,12 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 5); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 9), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 9), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); CHECK_FALSE(text_edit->has_selection()); text_edit->set_selecting_enabled(false); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 0), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE | KeyModifierMask::SHIFT); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE | KeyModifierMask::SHIFT); CHECK_FALSE(text_edit->has_selection()); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 5); @@ -950,11 +1066,15 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->delete_selection(); CHECK(text_edit->get_text() == "this is some text\nfor selection"); + CHECK(text_edit->get_caret_line() == 0); + CHECK(text_edit->get_caret_column() == 8); text_edit->select(0, 8, 0, 4); CHECK(text_edit->has_selection()); SEND_GUI_ACTION(text_edit, "ui_text_backspace"); CHECK(text_edit->get_text() == "thissome text\nfor selection"); + CHECK(text_edit->get_caret_line() == 0); + CHECK(text_edit->get_caret_column() == 4); text_edit->undo(); CHECK(text_edit->has_selection()); @@ -966,7 +1086,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK_FALSE(text_edit->has_selection()); CHECK(text_edit->get_text() == "thissome text\nfor selection"); CHECK(text_edit->get_caret_line() == 0); - CHECK(text_edit->get_caret_column() == 8); + CHECK(text_edit->get_caret_column() == 4); text_edit->undo(); CHECK(text_edit->has_selection()); @@ -991,7 +1111,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK_FALSE(text_edit->has_selection()); CHECK(text_edit->get_text() == "thissome text\nfor selection"); CHECK(text_edit->get_caret_line() == 0); - CHECK(text_edit->get_caret_column() == 8); + CHECK(text_edit->get_caret_column() == 4); text_edit->undo(); CHECK(text_edit->has_selection()); @@ -1029,19 +1149,19 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { Point2i line_0 = text_edit->get_pos_at_line_column(0, 0); line_0.y /= 2; - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, line_0, MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, line_0, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); CHECK(text_edit->is_mouse_over_selection()); - SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButton::MASK_LEFT, Key::NONE); + SEND_GUI_MOUSE_MOTION_EVENT(text_edit, text_edit->get_pos_at_line_column(0, 7), MouseButtonMask::LEFT, Key::NONE); CHECK(text_edit->get_viewport()->gui_is_dragging()); CHECK(text_edit->get_viewport()->gui_get_drag_data() == "drag me"); line_0 = target_text_edit->get_pos_at_line_column(0, 0); line_0.y /= 2; line_0.x += 401; // As empty add one. - SEND_GUI_MOUSE_MOTION_EVENT(target_text_edit, line_0, MouseButton::MASK_LEFT, Key::NONE); + SEND_GUI_MOUSE_MOTION_EVENT(target_text_edit, line_0, MouseButtonMask::LEFT, Key::NONE); CHECK(text_edit->get_viewport()->gui_is_dragging()); - SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(target_text_edit, line_0, MouseButton::LEFT, MouseButton::MASK_LEFT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(target_text_edit, line_0, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE); CHECK_FALSE(text_edit->get_viewport()->gui_is_dragging()); CHECK(text_edit->get_text() == ""); @@ -1176,8 +1296,8 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->undo(); MessageQueue::get_singleton()->flush(); CHECK(text_edit->get_text() == "this is\nsome\n"); - CHECK(text_edit->get_caret_line() == 1); - CHECK(text_edit->get_caret_column() == 0); + CHECK(text_edit->get_caret_line() == 0); + CHECK(text_edit->get_caret_column() == 6); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); @@ -1187,9 +1307,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->redo(); MessageQueue::get_singleton()->flush(); CHECK(text_edit->get_text() == "some\n"); - CHECK(text_edit->get_caret_line() == 1); - CHECK(text_edit->get_caret_column() == 0); - SIGNAL_CHECK_FALSE("caret_changed"); + CHECK(text_edit->get_caret_line() == 0); + CHECK(text_edit->get_caret_column() == 4); + SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); @@ -1259,9 +1379,14 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { lines_edited_args.push_back(args1); SUBCASE("[TextEdit] ui_text_newline_above") { - text_edit->set_text("this is some test text."); + text_edit->set_text("this is some test text.\nthis is some test text."); text_edit->select(0, 0, 0, 4); text_edit->set_caret_column(4); + + text_edit->add_caret(1, 4); + text_edit->select(1, 0, 1, 4, 1); + CHECK(text_edit->get_caret_count() == 2); + MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1269,50 +1394,78 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); - ((Array)lines_edited_args[0])[1] = 1; + // For the second caret. + Array args2; + args2.push_back(0); + args2.push_back(1); + lines_edited_args.push_front(args2); + + ((Array)lines_edited_args[1])[1] = 1; SEND_GUI_ACTION(text_edit, "ui_text_newline_above"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\nthis is some test text."); + CHECK(text_edit->get_text() == "\nthis is some test text.\n\nthis is some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); text_edit->set_caret_line(1); text_edit->set_caret_column(4); - text_edit->select(0, 0, 0, 4); + + text_edit->set_caret_line(3, false, true, 0, 1); + text_edit->set_caret_column(4, false, 1); MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("caret_changed"); text_edit->set_editable(false); SEND_GUI_ACTION(text_edit, "ui_text_newline_above"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\nthis is some test text."); + CHECK(text_edit->get_text() == "\nthis is some test text.\n\nthis is some test text."); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 4); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 3); + CHECK(text_edit->get_caret_column(1) == 4); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); text_edit->set_editable(true); + ((Array)lines_edited_args[0])[0] = 2; + ((Array)lines_edited_args[0])[1] = 3; + SEND_GUI_ACTION(text_edit, "ui_text_newline_above"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\n\nthis is some test text."); + CHECK(text_edit->get_text() == "\n\nthis is some test text.\n\n\nthis is some test text."); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 4); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); } SUBCASE("[TextEdit] ui_text_newline_blank") { - text_edit->set_text("this is some test text."); + text_edit->set_text("this is some test text.\nthis is some test text."); text_edit->select(0, 0, 0, 4); text_edit->set_caret_column(4); + + text_edit->add_caret(1, 4); + text_edit->select(1, 0, 1, 4, 1); + CHECK(text_edit->get_caret_count() == 2); + MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1320,13 +1473,23 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); - ((Array)lines_edited_args[0])[1] = 1; + // For the second caret. + Array args2; + args2.push_back(1); + args2.push_back(2); + lines_edited_args.push_front(args2); + + ((Array)lines_edited_args[1])[1] = 1; SEND_GUI_ACTION(text_edit, "ui_text_newline_blank"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "this is some test text.\n"); + CHECK(text_edit->get_text() == "this is some test text.\n\nthis is some test text.\n"); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 3); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); @@ -1334,10 +1497,14 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_editable(false); SEND_GUI_ACTION(text_edit, "ui_text_newline_blank"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "this is some test text.\n"); + CHECK(text_edit->get_text() == "this is some test text.\n\nthis is some test text.\n"); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 3); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); @@ -1345,9 +1512,14 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { } SUBCASE("[TextEdit] ui_text_newline") { - text_edit->set_text("this is some test text."); + text_edit->set_text("this is some test text.\nthis is some test text."); text_edit->select(0, 0, 0, 4); text_edit->set_caret_column(4); + + text_edit->add_caret(1, 4); + text_edit->select(1, 0, 1, 4, 1); + CHECK(text_edit->get_caret_count() == 2); + MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1355,14 +1527,27 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); - lines_edited_args.push_back(lines_edited_args[0].duplicate()); - ((Array)lines_edited_args[1])[1] = 1; + // For the second caret. + Array args2; + args2.push_back(1); + args2.push_back(1); + lines_edited_args.push_front(args2); + lines_edited_args.push_front(args2.duplicate()); + ((Array)lines_edited_args[1])[1] = 2; + + lines_edited_args.push_back(lines_edited_args[2].duplicate()); + ((Array)lines_edited_args[3])[1] = 1; + SEND_GUI_ACTION(text_edit, "ui_text_newline"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\n is some test text."); + CHECK(text_edit->get_text() == "\n is some test text.\n\n is some test text."); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 3); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); @@ -1370,10 +1555,14 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_editable(false); SEND_GUI_ACTION(text_edit, "ui_text_newline"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\n is some test text."); + CHECK(text_edit->get_text() == "\n is some test text.\n\n is some test text."); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 3); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); @@ -1381,13 +1570,18 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { } SUBCASE("[TextEdit] ui_text_backspace_all_to_left") { - text_edit->set_text("\nthis is some test text."); + text_edit->set_text("\nthis is some test text.\n\nthis is some test text."); text_edit->select(1, 0, 1, 4); text_edit->set_caret_line(1); text_edit->set_caret_column(4); + + text_edit->add_caret(3, 4); + text_edit->select(3, 0, 3, 4, 1); + CHECK(text_edit->get_caret_count() == 2); + MessageQueue::get_singleton()->flush(); - Ref<InputEvent> tmpevent = InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::ALT | KeyModifierMask::CMD); + Ref<InputEvent> tmpevent = InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::ALT | KeyModifierMask::CMD_OR_CTRL); InputMap::get_singleton()->action_add_event("ui_text_backspace_all_to_left", tmpevent); SIGNAL_DISCARD("text_set"); @@ -1395,34 +1589,50 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); + // For the second caret. + Array args2; + args2.push_back(3); + args2.push_back(3); + lines_edited_args.push_front(args2); + // With selection should be a normal backspace. - ((Array)lines_edited_args[0])[0] = 1; - ((Array)lines_edited_args[0])[1] = 1; + ((Array)lines_edited_args[1])[0] = 1; + ((Array)lines_edited_args[1])[1] = 1; SEND_GUI_ACTION(text_edit, "ui_text_backspace_all_to_left"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\n is some test text."); + CHECK(text_edit->get_text() == "\n is some test text.\n\n is some test text."); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 3); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); - ((Array)lines_edited_args[0])[1] = 0; + ((Array)lines_edited_args[0])[1] = 2; + ((Array)lines_edited_args[1])[1] = 0; // Start of line should also be a normal backspace. SEND_GUI_ACTION(text_edit, "ui_text_backspace_all_to_left"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " is some test text."); + CHECK(text_edit->get_text() == " is some test text.\n is some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); text_edit->set_caret_column(text_edit->get_line(0).length()); + text_edit->set_caret_column(text_edit->get_line(1).length(), false, 1); MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1433,23 +1643,33 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_editable(false); SEND_GUI_ACTION(text_edit, "ui_text_backspace_all_to_left"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " is some test text."); + CHECK(text_edit->get_text() == " is some test text.\n is some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == text_edit->get_line(0).length()); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == text_edit->get_line(1).length()); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); text_edit->set_editable(true); - ((Array)lines_edited_args[0])[0] = 0; + ((Array)lines_edited_args[0])[0] = 1; + ((Array)lines_edited_args[0])[1] = 1; + ((Array)lines_edited_args[1])[0] = 0; SEND_GUI_ACTION(text_edit, "ui_text_backspace_all_to_left"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == ""); + CHECK(text_edit->get_text() == "\n"); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); @@ -1458,10 +1678,14 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { } SUBCASE("[TextEdit] ui_text_backspace_word") { - text_edit->set_text("\nthis is some test text."); + text_edit->set_text("\nthis is some test text.\n\nthis is some test text."); text_edit->select(1, 0, 1, 4); text_edit->set_caret_line(1); text_edit->set_caret_column(4); + + text_edit->add_caret(3, 4); + text_edit->select(3, 0, 3, 4, 1); + CHECK(text_edit->get_caret_count() == 2); MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1469,30 +1693,45 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); + // For the second caret. + Array args2; + args2.push_back(3); + args2.push_back(3); + lines_edited_args.push_front(args2); + // With selection should be a normal backspace. - ((Array)lines_edited_args[0])[0] = 1; - ((Array)lines_edited_args[0])[1] = 1; + ((Array)lines_edited_args[1])[0] = 1; + ((Array)lines_edited_args[1])[1] = 1; SEND_GUI_ACTION(text_edit, "ui_text_backspace_word"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\n is some test text."); + CHECK(text_edit->get_text() == "\n is some test text.\n\n is some test text."); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 3); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); text_edit->end_complex_operation(); - ((Array)lines_edited_args[0])[1] = 0; + ((Array)lines_edited_args[0])[1] = 2; + ((Array)lines_edited_args[1])[1] = 0; // Start of line should also be a normal backspace. SEND_GUI_ACTION(text_edit, "ui_text_backspace_word"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " is some test text."); + CHECK(text_edit->get_text() == " is some test text.\n is some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); @@ -1500,16 +1739,21 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_editable(false); SEND_GUI_ACTION(text_edit, "ui_text_backspace_word"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " is some test text."); + CHECK(text_edit->get_text() == " is some test text.\n is some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); text_edit->set_editable(true); text_edit->set_caret_column(text_edit->get_line(0).length()); + text_edit->set_caret_column(text_edit->get_line(1).length(), false, 1); MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1517,24 +1761,35 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); - ((Array)lines_edited_args[0])[0] = 0; + ((Array)lines_edited_args[0])[0] = 1; + ((Array)lines_edited_args[0])[1] = 1; + ((Array)lines_edited_args[1])[0] = 0; SEND_GUI_ACTION(text_edit, "ui_text_backspace_word"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " is some test "); + CHECK(text_edit->get_text() == " is some test \n is some test "); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 14); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 14); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); } SUBCASE("[TextEdit] ui_text_backspace") { - text_edit->set_text("\nthis is some test text."); + text_edit->set_text("\nthis is some test text.\n\nthis is some test text."); text_edit->select(1, 0, 1, 4); text_edit->set_caret_line(1); text_edit->set_caret_column(4); + + text_edit->add_caret(3, 4); + text_edit->select(3, 0, 3, 4, 1); + CHECK(text_edit->get_caret_count() == 2); + MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1542,34 +1797,50 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); + // For the second caret. + Array args2; + args2.push_back(3); + args2.push_back(3); + lines_edited_args.push_front(args2); + // With selection should be a normal backspace. - ((Array)lines_edited_args[0])[0] = 1; - ((Array)lines_edited_args[0])[1] = 1; + ((Array)lines_edited_args[1])[0] = 1; + ((Array)lines_edited_args[1])[1] = 1; SEND_GUI_ACTION(text_edit, "ui_text_backspace"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\n is some test text."); + CHECK(text_edit->get_text() == "\n is some test text.\n\n is some test text."); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 3); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); - ((Array)lines_edited_args[0])[1] = 0; + ((Array)lines_edited_args[0])[1] = 2; + ((Array)lines_edited_args[1])[1] = 0; // Start of line should also be a normal backspace. SEND_GUI_ACTION(text_edit, "ui_text_backspace"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " is some test text."); + CHECK(text_edit->get_text() == " is some test text.\n is some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); text_edit->set_caret_column(text_edit->get_line(0).length()); + text_edit->set_caret_column(text_edit->get_line(1).length(), false, 1); MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1580,23 +1851,33 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_editable(false); SEND_GUI_ACTION(text_edit, "ui_text_backspace"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " is some test text."); + CHECK(text_edit->get_text() == " is some test text.\n is some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == text_edit->get_line(0).length()); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == text_edit->get_line(1).length()); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); text_edit->set_editable(true); - ((Array)lines_edited_args[0])[0] = 0; + ((Array)lines_edited_args[0])[0] = 1; + ((Array)lines_edited_args[0])[1] = 1; + ((Array)lines_edited_args[1])[0] = 0; SEND_GUI_ACTION(text_edit, "ui_text_backspace"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " is some test text"); + CHECK(text_edit->get_text() == " is some test text\n is some test text"); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 18); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 18); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); @@ -1605,6 +1886,10 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->select(0, 18, 0, 0); text_edit->set_caret_line(0); text_edit->set_caret_column(0); + + text_edit->select(1, 18, 1, 0, 1); + text_edit->set_caret_line(1, false, true, 0, 1); + text_edit->set_caret_column(0, false, 1); MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1612,25 +1897,30 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); - ((Array)lines_edited_args[0])[0] = 0; - SEND_GUI_ACTION(text_edit, "ui_text_backspace"); - CHECK(text_edit->get_text() == ""); + CHECK(text_edit->get_text() == "\n"); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 0); SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); } SUBCASE("[TextEdit] ui_text_delete_all_to_right") { - Ref<InputEvent> tmpevent = InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::ALT | KeyModifierMask::CMD); + Ref<InputEvent> tmpevent = InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::ALT | KeyModifierMask::CMD_OR_CTRL); InputMap::get_singleton()->action_add_event("ui_text_delete_all_to_right", tmpevent); - text_edit->set_text("this is some test text.\n"); + text_edit->set_text("this is some test text.\nthis is some test text.\n"); text_edit->select(0, 0, 0, 4); text_edit->set_caret_line(0); text_edit->set_caret_column(4); + + text_edit->add_caret(1, 4); + text_edit->select(1, 0, 1, 4, 1); + CHECK(text_edit->get_caret_count() == 2); + MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1638,19 +1928,30 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); + // For the second caret. + Array args2; + args2.push_back(1); + args2.push_back(1); + lines_edited_args.push_front(args2); + // With selection should be a normal delete. SEND_GUI_ACTION(text_edit, "ui_text_delete_all_to_right"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " is some test text.\n"); + CHECK(text_edit->get_text() == " is some test text.\n is some test text.\n"); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); // End of line should not do anything. text_edit->set_caret_column(text_edit->get_line(0).length()); + text_edit->set_caret_column(text_edit->get_line(1).length(), false, 1); MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1660,15 +1961,20 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SEND_GUI_ACTION(text_edit, "ui_text_delete_all_to_right"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " is some test text.\n"); + CHECK(text_edit->get_text() == " is some test text.\n is some test text.\n"); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == text_edit->get_line(0).length()); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == text_edit->get_line(1).length()); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); text_edit->set_caret_column(0); + text_edit->set_caret_column(0, false, 1); MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1679,10 +1985,14 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_editable(false); SEND_GUI_ACTION(text_edit, "ui_text_delete_all_to_right"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " is some test text.\n"); + CHECK(text_edit->get_text() == " is some test text.\n is some test text.\n"); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); @@ -1690,10 +2000,14 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SEND_GUI_ACTION(text_edit, "ui_text_delete_all_to_right"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\n"); + CHECK(text_edit->get_text() == "\n\n"); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); @@ -1705,10 +2019,15 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_caret_mid_grapheme_enabled(true); CHECK(text_edit->is_caret_mid_grapheme_enabled()); - text_edit->set_text("this ffi some test text.\n"); + text_edit->set_text("this ffi some test text.\n\nthis ffi some test text.\n"); text_edit->select(0, 0, 0, 4); text_edit->set_caret_line(0); text_edit->set_caret_column(4); + + text_edit->add_caret(2, 4); + text_edit->select(2, 0, 2, 4, 1); + CHECK(text_edit->get_caret_count() == 2); + MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1716,20 +2035,32 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); + // For the second caret. + Array args2; + args2.push_back(2); + args2.push_back(2); + lines_edited_args.push_front(args2); + // With selection should be a normal delete. SEND_GUI_ACTION(text_edit, "ui_text_delete_word"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " ffi some test text.\n"); + CHECK(text_edit->get_text() == " ffi some test text.\n\n ffi some test text.\n"); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); // With selection should be a normal delete. - ((Array)lines_edited_args[0])[0] = 1; + ((Array)lines_edited_args[0])[0] = 3; + ((Array)lines_edited_args[1])[0] = 1; text_edit->set_caret_column(text_edit->get_line(0).length()); + text_edit->set_caret_column(text_edit->get_line(2).length(), false, 1); MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1739,16 +2070,23 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SEND_GUI_ACTION(text_edit, "ui_text_delete_word"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " ffi some test text."); + CHECK(text_edit->get_text() == " ffi some test text.\n ffi some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == text_edit->get_line(0).length()); CHECK_FALSE(text_edit->has_selection()); - SIGNAL_CHECK_FALSE("caret_changed"); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == text_edit->get_line(1).length()); + CHECK_FALSE(text_edit->has_selection(0)); + SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); - ((Array)lines_edited_args[0])[0] = 0; + ((Array)lines_edited_args[1])[0] = 0; + ((Array)lines_edited_args[0])[0] = 1; + ((Array)lines_edited_args[0])[1] = 1; text_edit->set_caret_column(0); + text_edit->set_caret_column(0, false, 1); MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1759,10 +2097,14 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_editable(false); SEND_GUI_ACTION(text_edit, "ui_text_delete_word"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " ffi some test text."); + CHECK(text_edit->get_text() == " ffi some test text.\n ffi some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); @@ -1770,10 +2112,14 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SEND_GUI_ACTION(text_edit, "ui_text_delete_word"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " some test text."); + CHECK(text_edit->get_text() == " some test text.\n some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); @@ -1783,10 +2129,15 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_caret_mid_grapheme_enabled(true); CHECK(text_edit->is_caret_mid_grapheme_enabled()); - text_edit->set_text("this ffi some test text.\n"); + text_edit->set_text("this ffi some test text.\nthis ffi some test text."); text_edit->select(0, 0, 0, 4); text_edit->set_caret_line(0); text_edit->set_caret_column(4); + + text_edit->add_caret(1, 4); + text_edit->select(1, 0, 1, 4, 1); + CHECK(text_edit->get_caret_count() == 2); + MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1794,19 +2145,31 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); + // For the second caret. + Array args2; + args2.push_back(1); + args2.push_back(1); + lines_edited_args.push_front(args2); + // With selection should be a normal delete. SEND_GUI_ACTION(text_edit, "ui_text_delete"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " ffi some test text.\n"); + CHECK(text_edit->get_text() == " ffi some test text.\n ffi some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); // With selection should be a normal delete. + lines_edited_args.remove_at(0); ((Array)lines_edited_args[0])[0] = 1; + text_edit->set_caret_column(text_edit->get_line(1).length(), false, 1); text_edit->set_caret_column(text_edit->get_line(0).length()); MessageQueue::get_singleton()->flush(); @@ -1817,16 +2180,25 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SEND_GUI_ACTION(text_edit, "ui_text_delete"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " ffi some test text."); + CHECK(text_edit->get_text() == " ffi some test text. ffi some test text."); CHECK(text_edit->get_caret_line() == 0); - CHECK(text_edit->get_caret_column() == text_edit->get_line(0).length()); - CHECK_FALSE(text_edit->has_selection()); - SIGNAL_CHECK_FALSE("caret_changed"); + CHECK(text_edit->get_caret_column() == 20); + CHECK_FALSE(text_edit->has_selection(0)); + SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); - ((Array)lines_edited_args[0])[0] = 0; + // Caret should be removed due to column preservation. + CHECK(text_edit->get_caret_count() == 1); + + // Lets add it back. text_edit->set_caret_column(0); + text_edit->add_caret(0, 20); + + ((Array)lines_edited_args[0])[0] = 0; + lines_edited_args.push_back(args2); + ((Array)lines_edited_args[1])[0] = 0; + ((Array)lines_edited_args[1])[1] = 0; MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1837,43 +2209,59 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_editable(false); SEND_GUI_ACTION(text_edit, "ui_text_delete"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " ffi some test text."); + CHECK(text_edit->get_text() == " ffi some test text. ffi some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 0); + CHECK(text_edit->get_caret_column(1) == 20); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); text_edit->set_editable(true); + text_edit->start_action(TextEdit::EditAction::ACTION_NONE); + SEND_GUI_ACTION(text_edit, "ui_text_delete"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "ffi some test text."); + CHECK(text_edit->get_text() == "ffi some test text.ffi some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); - SIGNAL_CHECK_FALSE("caret_changed"); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 0); + CHECK(text_edit->get_caret_column(1) == 19); + CHECK_FALSE(text_edit->has_selection(0)); + SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); + text_edit->start_action(TextEdit::EditAction::ACTION_NONE); + SEND_GUI_ACTION(text_edit, "ui_text_delete"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "fi some test text."); + CHECK(text_edit->get_text() == "fi some test text.fi some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); - SIGNAL_CHECK_FALSE("caret_changed"); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 0); + CHECK(text_edit->get_caret_column(1) == 18); + CHECK_FALSE(text_edit->has_selection(1)); + SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); text_edit->set_caret_mid_grapheme_enabled(false); CHECK_FALSE(text_edit->is_caret_mid_grapheme_enabled()); + text_edit->start_action(TextEdit::EditAction::ACTION_NONE); + text_edit->undo(); - text_edit->set_caret_line(0); - text_edit->set_caret_column(0); MessageQueue::get_singleton()->flush(); - CHECK(text_edit->get_text() == "ffi some test text."); + CHECK(text_edit->get_text() == "ffi some test text.ffi some test text."); SIGNAL_DISCARD("text_set"); SIGNAL_DISCARD("text_changed"); @@ -1882,19 +2270,26 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SEND_GUI_ACTION(text_edit, "ui_text_delete"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == " some test text."); + CHECK(text_edit->get_text() == " some test text. some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); - SIGNAL_CHECK_FALSE("caret_changed"); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 0); + CHECK(text_edit->get_caret_column(1) == 16); + CHECK_FALSE(text_edit->has_selection(1)); + SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); } SUBCASE("[TextEdit] ui_text_caret_word_left") { - text_edit->set_text("\nthis is some test text."); + text_edit->set_text("\nthis is some test text.\nthis is some test text."); text_edit->set_caret_line(1); text_edit->set_caret_column(7); + + text_edit->add_caret(2, 7); + CHECK(text_edit->get_caret_count() == 2); MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1902,47 +2297,67 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); + // Shift should select. #ifdef MACOS_ENABLED SEND_GUI_KEY_EVENT(text_edit, Key::LEFT | KeyModifierMask::ALT | KeyModifierMask::SHIFT); #else - SEND_GUI_KEY_EVENT(text_edit, Key::LEFT | KeyModifierMask::CMD | KeyModifierMask::SHIFT); + SEND_GUI_KEY_EVENT(text_edit, Key::LEFT | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT); #endif CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\nthis is some test text."); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 5); - CHECK(text_edit->get_selected_text() == "is"); - CHECK(text_edit->has_selection()); + CHECK(text_edit->get_selected_text(0) == "is"); + CHECK(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 5); + CHECK(text_edit->get_selected_text(1) == "is"); + CHECK(text_edit->has_selection(1)); + SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + // Should still move caret with selection. SEND_GUI_ACTION(text_edit, "ui_text_caret_word_left"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\nthis is some test text."); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); + SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + // Normal word left. SEND_GUI_ACTION(text_edit, "ui_text_caret_word_left"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\nthis is some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 23); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); } SUBCASE("[TextEdit] ui_text_caret_left") { - text_edit->set_text("\nthis is some test text."); + text_edit->set_text("\nthis is some test text.\nthis is some test text."); text_edit->set_caret_line(1); text_edit->set_caret_column(7); text_edit->select(1, 2, 1, 7); + + text_edit->add_caret(2, 7); + text_edit->select(2, 2, 2, 7, 1); + CHECK(text_edit->get_caret_count() == 2); + MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -1950,62 +2365,91 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); + // Normal left should deselect and place at selection start. SEND_GUI_ACTION(text_edit, "ui_text_caret_left"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\nthis is some test text."); + CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 2); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 2); + CHECK_FALSE(text_edit->has_selection(1)); + SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + // With shift should select. SEND_GUI_KEY_EVENT(text_edit, Key::LEFT | KeyModifierMask::SHIFT); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\nthis is some test text."); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 1); - CHECK(text_edit->get_selected_text() == "h"); - CHECK(text_edit->has_selection()); + CHECK(text_edit->get_selected_text(0) == "h"); + CHECK(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 1); + CHECK(text_edit->get_selected_text(1) == "h"); + CHECK(text_edit->has_selection(1)); + SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + // All ready at select left, should only deselect. SEND_GUI_ACTION(text_edit, "ui_text_caret_left"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\nthis is some test text."); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 1); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 1); + CHECK_FALSE(text_edit->has_selection(1)); + SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + // Normal left. SEND_GUI_ACTION(text_edit, "ui_text_caret_left"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\nthis is some test text."); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 0); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 0); CHECK_FALSE(text_edit->has_selection()); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + // Left at col 0 should go up a line. SEND_GUI_ACTION(text_edit, "ui_text_caret_left"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "\nthis is some test text."); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 23); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); } SUBCASE("[TextEdit] ui_text_caret_word_right") { - text_edit->set_text("this is some test text\n"); + text_edit->set_text("this is some test text\n\nthis is some test text\n"); text_edit->set_caret_line(0); text_edit->set_caret_column(13); + + text_edit->add_caret(2, 13); + CHECK(text_edit->get_caret_count() == 2); + MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -2013,47 +2457,67 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); + // Shift should select. #ifdef MACOS_ENABLED SEND_GUI_KEY_EVENT(text_edit, Key::RIGHT | KeyModifierMask::ALT | KeyModifierMask::SHIFT); #else - SEND_GUI_KEY_EVENT(text_edit, Key::RIGHT | KeyModifierMask::CMD | KeyModifierMask::SHIFT); + SEND_GUI_KEY_EVENT(text_edit, Key::RIGHT | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT); #endif CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "this is some test text\n"); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 17); - CHECK(text_edit->get_selected_text() == "test"); - CHECK(text_edit->has_selection()); + CHECK(text_edit->get_selected_text(0) == "test"); + CHECK(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 17); + CHECK(text_edit->get_selected_text(1) == "test"); + CHECK(text_edit->has_selection(1)); + SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + // Should still move caret with selection. SEND_GUI_ACTION(text_edit, "ui_text_caret_word_right"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "this is some test text\n"); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 22); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 22); + CHECK_FALSE(text_edit->has_selection(1)); + SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + // Normal word right. SEND_GUI_ACTION(text_edit, "ui_text_caret_word_right"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "this is some test text\n"); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 3); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); } SUBCASE("[TextEdit] ui_text_caret_right") { - text_edit->set_text("this is some test text\n"); + text_edit->set_text("this is some test text\n\nthis is some test text\n"); text_edit->set_caret_line(0); text_edit->set_caret_column(16); text_edit->select(0, 16, 0, 20); + + text_edit->add_caret(2, 16); + text_edit->select(2, 16, 2, 20, 1); + CHECK(text_edit->get_caret_count() == 2); + MessageQueue::get_singleton()->flush(); SIGNAL_DISCARD("text_set"); @@ -2061,53 +2525,76 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); + // Normal right should deselect and place at selection start. SEND_GUI_ACTION(text_edit, "ui_text_caret_right"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "this is some test text\n"); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 20); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 20); + CHECK_FALSE(text_edit->has_selection(1)); + SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + // With shift should select. SEND_GUI_KEY_EVENT(text_edit, Key::RIGHT | KeyModifierMask::SHIFT); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "this is some test text\n"); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 21); - CHECK(text_edit->get_selected_text() == "x"); - CHECK(text_edit->has_selection()); + CHECK(text_edit->get_selected_text(0) == "x"); + CHECK(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 21); + CHECK(text_edit->get_selected_text(1) == "x"); + CHECK(text_edit->has_selection(1)); + SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + // All ready at select right, should only deselect. SEND_GUI_ACTION(text_edit, "ui_text_caret_right"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "this is some test text\n"); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 21); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 21); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + // Normal right. SEND_GUI_ACTION(text_edit, "ui_text_caret_right"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "this is some test text\n"); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 22); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 22); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + // Right at end col should go down a line. SEND_GUI_ACTION(text_edit, "ui_text_caret_right"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "this is some test text\n"); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 3); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); @@ -2117,9 +2604,13 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); text_edit->set_size(Size2(110, 100)); - text_edit->set_text("this is some\nother test\nlines\ngo here"); - text_edit->set_caret_line(4); + text_edit->set_text("this is some\nother test\nlines\ngo here\nthis is some\nother test\nlines\ngo here"); + text_edit->set_caret_line(3); text_edit->set_caret_column(7); + + text_edit->add_caret(7, 7); + CHECK(text_edit->get_caret_count() == 2); + MessageQueue::get_singleton()->flush(); CHECK(text_edit->is_line_wrapped(0)); @@ -2128,44 +2619,61 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); + // Select + up should select everything to the left on that line. SEND_GUI_KEY_EVENT(text_edit, Key::UP | KeyModifierMask::SHIFT); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "this is some\nother test\nlines\ngo here"); CHECK(text_edit->get_caret_line() == 2); CHECK(text_edit->get_caret_column() == 5); - CHECK(text_edit->get_selected_text() == "\ngo here"); - CHECK(text_edit->has_selection()); + CHECK(text_edit->get_selected_text(0) == "\ngo here"); + CHECK(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 6); + CHECK(text_edit->get_caret_column(1) == 5); + CHECK(text_edit->get_selected_text(1) == "\ngo here"); + CHECK(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + // Should deselect and move up. SEND_GUI_ACTION(text_edit, "ui_text_caret_up"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "this is some\nother test\nlines\ngo here"); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 8); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 5); + CHECK(text_edit->get_caret_column(1) == 8); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + // Normal up over wrapped line. SEND_GUI_ACTION(text_edit, "ui_text_caret_up"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "this is some\nother test\nlines\ngo here"); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 12); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 4); + CHECK(text_edit->get_caret_column(1) == 12); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); text_edit->set_caret_column(12, false); + // Normal up over wrapped line to line 0. SEND_GUI_ACTION(text_edit, "ui_text_caret_up"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "this is some\nother test\nlines\ngo here"); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 7); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 4); + CHECK(text_edit->get_caret_column(1) == 7); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); @@ -2175,9 +2683,13 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); text_edit->set_size(Size2(110, 100)); - text_edit->set_text("go here\nlines\nother test\nthis is some"); + text_edit->set_text("go here\nlines\nother test\nthis is some\ngo here\nlines\nother test\nthis is some"); text_edit->set_caret_line(0); text_edit->set_caret_column(7); + + text_edit->add_caret(4, 7); + CHECK(text_edit->get_caret_count() == 2); + MessageQueue::get_singleton()->flush(); CHECK(text_edit->is_line_wrapped(3)); @@ -2186,44 +2698,61 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); + // Select + down should select everything to the right on that line. SEND_GUI_KEY_EVENT(text_edit, Key::DOWN | KeyModifierMask::SHIFT); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "go here\nlines\nother test\nthis is some"); CHECK(text_edit->get_caret_line() == 1); CHECK(text_edit->get_caret_column() == 5); - CHECK(text_edit->get_selected_text() == "\nlines"); - CHECK(text_edit->has_selection()); + CHECK(text_edit->get_selected_text(0) == "\nlines"); + CHECK(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 5); + CHECK(text_edit->get_caret_column(1) == 5); + CHECK(text_edit->get_selected_text(1) == "\nlines"); + CHECK(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + // Should deselect and move down. SEND_GUI_ACTION(text_edit, "ui_text_caret_down"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "go here\nlines\nother test\nthis is some"); CHECK(text_edit->get_caret_line() == 2); CHECK(text_edit->get_caret_column() == 8); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 6); + CHECK(text_edit->get_caret_column(1) == 8); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + // Normal down over wrapped line. SEND_GUI_ACTION(text_edit, "ui_text_caret_down"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "go here\nlines\nother test\nthis is some"); CHECK(text_edit->get_caret_line() == 3); CHECK(text_edit->get_caret_column() == 7); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 7); + CHECK(text_edit->get_caret_column(1) == 7); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); text_edit->set_caret_column(7, false); + // Normal down over wrapped line to last wrapped line. SEND_GUI_ACTION(text_edit, "ui_text_caret_down"); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "go here\nlines\nother test\nthis is some"); CHECK(text_edit->get_caret_line() == 3); CHECK(text_edit->get_caret_column() == 12); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 7); + CHECK(text_edit->get_caret_column(1) == 12); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); @@ -2236,6 +2765,10 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_text("this is some\nother test\nlines\ngo here"); text_edit->set_caret_line(4); text_edit->set_caret_column(7); + + text_edit->add_caret(3, 2); + CHECK(text_edit->get_caret_count() == 2); + MessageQueue::get_singleton()->flush(); CHECK(text_edit->is_line_wrapped(0)); @@ -2245,9 +2778,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("caret_changed"); #ifdef MACOS_ENABLED - SEND_GUI_KEY_EVENT(text_edit, Key::UP | KeyModifierMask::CMD | KeyModifierMask::SHIFT); + SEND_GUI_KEY_EVENT(text_edit, Key::UP | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT); #else - SEND_GUI_KEY_EVENT(text_edit, Key::HOME | KeyModifierMask::CMD | KeyModifierMask::SHIFT); + SEND_GUI_KEY_EVENT(text_edit, Key::HOME | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT); #endif CHECK(text_edit->get_viewport()->is_input_handled()); CHECK(text_edit->get_text() == "this is some\nother test\nlines\ngo here"); @@ -2258,6 +2791,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + CHECK(text_edit->get_caret_count() == 1); SEND_GUI_ACTION(text_edit, "ui_text_caret_document_start"); CHECK(text_edit->get_viewport()->is_input_handled()); @@ -2277,6 +2811,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_text("go here\nlines\nother test\nthis is some"); text_edit->set_caret_line(0); text_edit->set_caret_column(0); + + text_edit->add_caret(1, 0); + CHECK(text_edit->get_caret_count() == 2); MessageQueue::get_singleton()->flush(); CHECK(text_edit->is_line_wrapped(3)); @@ -2286,9 +2823,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("caret_changed"); #ifdef MACOS_ENABLED - SEND_GUI_KEY_EVENT(text_edit, Key::DOWN | KeyModifierMask::CMD | KeyModifierMask::SHIFT); + SEND_GUI_KEY_EVENT(text_edit, Key::DOWN | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT); #else - SEND_GUI_KEY_EVENT(text_edit, Key::END | KeyModifierMask::CMD | KeyModifierMask::SHIFT); + SEND_GUI_KEY_EVENT(text_edit, Key::END | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT); #endif CHECK(text_edit->get_viewport()->is_input_handled()); CHECK(text_edit->get_text() == "go here\nlines\nother test\nthis is some"); @@ -2299,6 +2836,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); + CHECK(text_edit->get_caret_count() == 1); SEND_GUI_ACTION(text_edit, "ui_text_caret_document_end"); CHECK(text_edit->get_viewport()->is_input_handled()); @@ -2315,9 +2853,12 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); text_edit->set_size(Size2(110, 100)); - text_edit->set_text(" this is some"); + text_edit->set_text(" this is some\n this is some"); text_edit->set_caret_line(0); text_edit->set_caret_column(text_edit->get_line(0).length()); + + text_edit->add_caret(1, text_edit->get_line(1).length()); + CHECK(text_edit->get_caret_count() == 2); MessageQueue::get_singleton()->flush(); CHECK(text_edit->is_line_wrapped(0)); @@ -2327,15 +2868,20 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("caret_changed"); #ifdef MACOS_ENABLED - SEND_GUI_KEY_EVENT(text_edit, Key::LEFT | KeyModifierMask::CMD | KeyModifierMask::SHIFT); + SEND_GUI_KEY_EVENT(text_edit, Key::LEFT | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT); #else SEND_GUI_KEY_EVENT(text_edit, Key::HOME | KeyModifierMask::SHIFT); #endif CHECK(text_edit->get_viewport()->is_input_handled()); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 10); - CHECK(text_edit->has_selection()); - CHECK(text_edit->get_selected_text() == "some"); + CHECK(text_edit->has_selection(0)); + CHECK(text_edit->get_selected_text(0) == "some"); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 10); + CHECK(text_edit->has_selection(1)); + CHECK(text_edit->get_selected_text(1) == "some"); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); @@ -2344,7 +2890,11 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK(text_edit->get_viewport()->is_input_handled()); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 2); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 2); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); @@ -2353,7 +2903,11 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK(text_edit->get_viewport()->is_input_handled()); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 0); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 0); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); @@ -2362,7 +2916,11 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK(text_edit->get_viewport()->is_input_handled()); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 2); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 2); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); @@ -2372,9 +2930,12 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); text_edit->set_size(Size2(110, 100)); - text_edit->set_text(" this is some"); + text_edit->set_text(" this is some\n this is some"); text_edit->set_caret_line(0); text_edit->set_caret_column(0); + + text_edit->add_caret(1, 0); + CHECK(text_edit->get_caret_count() == 2); MessageQueue::get_singleton()->flush(); CHECK(text_edit->is_line_wrapped(0)); @@ -2384,15 +2945,20 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("caret_changed"); #ifdef MACOS_ENABLED - SEND_GUI_KEY_EVENT(text_edit, Key::RIGHT | KeyModifierMask::CMD | KeyModifierMask::SHIFT); + SEND_GUI_KEY_EVENT(text_edit, Key::RIGHT | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT); #else SEND_GUI_KEY_EVENT(text_edit, Key::END | KeyModifierMask::SHIFT); #endif CHECK(text_edit->get_viewport()->is_input_handled()); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == 9); - CHECK(text_edit->has_selection()); - CHECK(text_edit->get_selected_text() == " this is"); + CHECK(text_edit->has_selection(0)); + CHECK(text_edit->get_selected_text(0) == " this is"); + + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 9); + CHECK(text_edit->has_selection(1)); + CHECK(text_edit->get_selected_text(1) == " this is"); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); @@ -2401,13 +2967,24 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { CHECK(text_edit->get_viewport()->is_input_handled()); CHECK(text_edit->get_caret_line() == 0); CHECK(text_edit->get_caret_column() == text_edit->get_line(0).length()); - CHECK_FALSE(text_edit->has_selection()); + CHECK_FALSE(text_edit->has_selection(0)); + + CHECK(text_edit->get_viewport()->is_input_handled()); + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == text_edit->get_line(1).length()); + CHECK_FALSE(text_edit->has_selection(1)); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); } SUBCASE("[TextEdit] unicode") { + text_edit->set_text("\n"); + text_edit->set_caret_line(0); + text_edit->set_caret_column(0); + + text_edit->add_caret(1, 0); + CHECK(text_edit->get_caret_count() == 2); text_edit->insert_text_at_caret("a"); MessageQueue::get_singleton()->flush(); @@ -2416,10 +2993,17 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_DISCARD("lines_edited_from"); SIGNAL_DISCARD("caret_changed"); + // For the second caret. + Array args2; + args2.push_back(1); + args2.push_back(1); + lines_edited_args.push_front(args2); + SEND_GUI_KEY_EVENT(text_edit, Key::A); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "aA"); + CHECK(text_edit->get_text() == "aA\naA"); CHECK(text_edit->get_caret_column() == 2); + CHECK(text_edit->get_caret_column(1) == 2); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); @@ -2427,20 +3011,24 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { text_edit->set_editable(false); SEND_GUI_KEY_EVENT(text_edit, Key::A); CHECK_FALSE(text_edit->get_viewport()->is_input_handled()); // Should this be handled? - CHECK(text_edit->get_text() == "aA"); + CHECK(text_edit->get_text() == "aA\naA"); CHECK(text_edit->get_caret_column() == 2); + CHECK(text_edit->get_caret_column(1) == 2); SIGNAL_CHECK_FALSE("caret_changed"); SIGNAL_CHECK_FALSE("text_changed"); SIGNAL_CHECK_FALSE("lines_edited_from"); text_edit->set_editable(true); - lines_edited_args.push_back(lines_edited_args[0].duplicate()); + lines_edited_args.push_back(lines_edited_args[1].duplicate()); + lines_edited_args.push_front(args2.duplicate()); text_edit->select(0, 0, 0, 1); + text_edit->select(1, 0, 1, 1, 1); SEND_GUI_KEY_EVENT(text_edit, Key::B); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "BA"); + CHECK(text_edit->get_text() == "BA\nBA"); CHECK(text_edit->get_caret_column() == 1); + CHECK(text_edit->get_caret_column(1) == 1); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); @@ -2450,22 +3038,36 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SEND_GUI_KEY_EVENT(text_edit, Key::B); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "BB"); + CHECK(text_edit->get_text() == "BB\nBB"); CHECK(text_edit->get_caret_column() == 2); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); text_edit->select(0, 0, 0, 1); + text_edit->select(1, 0, 1, 1, 1); SEND_GUI_KEY_EVENT(text_edit, Key::A); CHECK(text_edit->get_viewport()->is_input_handled()); - CHECK(text_edit->get_text() == "AB"); + CHECK(text_edit->get_text() == "AB\nAB"); CHECK(text_edit->get_caret_column() == 1); + CHECK(text_edit->get_caret_column(1) == 1); SIGNAL_CHECK("caret_changed", empty_signal_args); SIGNAL_CHECK("text_changed", empty_signal_args); SIGNAL_CHECK("lines_edited_from", lines_edited_args); text_edit->set_overtype_mode_enabled(false); CHECK_FALSE(text_edit->is_overtype_mode_enabled()); + + lines_edited_args.remove_at(0); + lines_edited_args.remove_at(1); + + SEND_GUI_KEY_EVENT(text_edit, Key::TAB); + CHECK(text_edit->get_viewport()->is_input_handled()); + CHECK(text_edit->get_text() == "A\tB\nA\tB"); + CHECK(text_edit->get_caret_column() == 2); + CHECK(text_edit->get_caret_column(1) == 2); + SIGNAL_CHECK("caret_changed", empty_signal_args); + SIGNAL_CHECK("text_changed", empty_signal_args); + SIGNAL_CHECK("lines_edited_from", lines_edited_args); } SIGNAL_UNWATCH(text_edit, "text_set"); @@ -2491,14 +3093,14 @@ TEST_CASE("[SceneTree][TextEdit] context menu") { CHECK_FALSE(text_edit->is_context_menu_enabled()); CHECK_FALSE(text_edit->is_menu_visible()); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(600, 10), MouseButton::RIGHT, MouseButton::MASK_RIGHT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(600, 10), MouseButton::RIGHT, MouseButtonMask::RIGHT, Key::NONE); CHECK_FALSE(text_edit->is_menu_visible()); text_edit->set_context_menu_enabled(true); CHECK(text_edit->is_context_menu_enabled()); CHECK_FALSE(text_edit->is_menu_visible()); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(700, 10), MouseButton::RIGHT, MouseButton::MASK_RIGHT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(700, 10), MouseButton::RIGHT, MouseButtonMask::RIGHT, Key::NONE); CHECK(text_edit->is_menu_visible()); memdelete(text_edit); @@ -2558,7 +3160,7 @@ TEST_CASE("[SceneTree][TextEdit] versioning") { CHECK(text_edit->has_redo()); text_edit->redo(); - CHECK(text_edit->get_line(0) == "test nested ops"); + CHECK(text_edit->get_line(0) == "test ops nested"); CHECK(text_edit->get_version() == 3); CHECK(text_edit->get_saved_version() == 3); CHECK(text_edit->has_undo()); @@ -2659,8 +3261,8 @@ TEST_CASE("[SceneTree][TextEdit] mouse") { CHECK(text_edit->get_line_column_at_pos(Point2i(end_pos.x - 100, end_pos.y - 100), false) == Point2i(90, 0)); CHECK(text_edit->get_line_column_at_pos(Point2i(end_pos.x - 100, end_pos.y)) == Point2i(90, 0)); - CHECK(text_edit->get_line_column_at_pos(Point2i(end_pos.x, end_pos.y + 100)) == Point2i(141, 0)); - CHECK(text_edit->get_line_column_at_pos(Point2i(end_pos.x - 100, end_pos.y + 100)) == Point2i(141, 0)); + CHECK(text_edit->get_line_column_at_pos(Point2i(end_pos.x, end_pos.y + 100)) == Point2i(140, 0)); + CHECK(text_edit->get_line_column_at_pos(Point2i(end_pos.x - 100, end_pos.y + 100)) == Point2i(140, 0)); CHECK(text_edit->get_line_column_at_pos(Point2i(end_pos.x, end_pos.y - 100)) == Point2i(104, 0)); CHECK(text_edit->get_line_column_at_pos(Point2i(end_pos.x - 100, end_pos.y - 100)) == Point2i(90, 0)); @@ -2714,15 +3316,15 @@ TEST_CASE("[SceneTree][TextEdit] caret") { text_edit->set_caret_blink_enabled(true); CHECK(text_edit->is_caret_blink_enabled()); - text_edit->set_caret_blink_speed(10); - CHECK(text_edit->get_caret_blink_speed() == 10); + text_edit->set_caret_blink_interval(10); + CHECK(text_edit->get_caret_blink_interval() == 10); ERR_PRINT_OFF; - text_edit->set_caret_blink_speed(-1); - CHECK(text_edit->get_caret_blink_speed() == 10); + text_edit->set_caret_blink_interval(-1); + CHECK(text_edit->get_caret_blink_interval() == 10); - text_edit->set_caret_blink_speed(0); - CHECK(text_edit->get_caret_blink_speed() == 10); + text_edit->set_caret_blink_interval(0); + CHECK(text_edit->get_caret_blink_interval() == 10); ERR_PRINT_ON; text_edit->set_caret_type(TextEdit::CaretType::CARET_TYPE_LINE); @@ -2738,13 +3340,13 @@ TEST_CASE("[SceneTree][TextEdit] caret") { text_edit->set_move_caret_on_right_click_enabled(false); CHECK_FALSE(text_edit->is_move_caret_on_right_click_enabled()); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(100, 1), MouseButton::RIGHT, MouseButton::MASK_RIGHT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(100, 1), MouseButton::RIGHT, MouseButtonMask::RIGHT, Key::NONE); CHECK(text_edit->get_caret_column() == caret_col); text_edit->set_move_caret_on_right_click_enabled(true); CHECK(text_edit->is_move_caret_on_right_click_enabled()); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(100, 1), MouseButton::RIGHT, MouseButton::MASK_RIGHT, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(100, 1), MouseButton::RIGHT, MouseButtonMask::RIGHT, Key::NONE); CHECK(text_edit->get_caret_column() != caret_col); text_edit->set_move_caret_on_right_click_enabled(false); @@ -2779,6 +3381,136 @@ TEST_CASE("[SceneTree][TextEdit] caret") { memdelete(text_edit); } +TEST_CASE("[SceneTree][TextEdit] multicaret") { + TextEdit *text_edit = memnew(TextEdit); + SceneTree::get_singleton()->get_root()->add_child(text_edit); + text_edit->set_multiple_carets_enabled(true); + + Array empty_signal_args; + empty_signal_args.push_back(Array()); + + SIGNAL_WATCH(text_edit, "caret_changed"); + + text_edit->set_text("this is\nsome test\ntext"); + text_edit->set_caret_line(0); + text_edit->set_caret_column(0); + MessageQueue::get_singleton()->flush(); + SIGNAL_DISCARD("caret_changed"); + + SUBCASE("[TextEdit] add remove caret") { + // Overlapping + CHECK(text_edit->add_caret(0, 0) == -1); + MessageQueue::get_singleton()->flush(); + SIGNAL_CHECK_FALSE("caret_changed"); + + // Selection + text_edit->select(0, 0, 2, 4); + CHECK(text_edit->add_caret(0, 0) == -1); + CHECK(text_edit->add_caret(2, 4) == -1); + CHECK(text_edit->add_caret(1, 2) == -1); + + // Out of bounds + CHECK(text_edit->add_caret(-1, 0) == -1); + CHECK(text_edit->add_caret(5, 0) == -1); + CHECK(text_edit->add_caret(0, 100) == -1); + + MessageQueue::get_singleton()->flush(); + SIGNAL_CHECK_FALSE("caret_changed"); + + CHECK(text_edit->get_caret_count() == 1); + + text_edit->deselect(); + SIGNAL_CHECK_FALSE("caret_changed"); + + CHECK(text_edit->add_caret(0, 1) == 1); + MessageQueue::get_singleton()->flush(); + SIGNAL_CHECK("caret_changed", empty_signal_args); + CHECK(text_edit->get_caret_count() == 2); + + CHECK(text_edit->get_caret_line(0) == 0); + CHECK(text_edit->get_caret_column(0) == 0); + + CHECK(text_edit->get_caret_line(1) == 0); + CHECK(text_edit->get_caret_column(1) == 1); + + ERR_PRINT_OFF; + text_edit->remove_caret(-1); + text_edit->remove_caret(5); + ERR_PRINT_ON; + CHECK(text_edit->get_caret_count() == 2); + SIGNAL_CHECK_FALSE("caret_changed"); + + text_edit->remove_caret(0); + SIGNAL_CHECK_FALSE("caret_changed"); + CHECK(text_edit->get_caret_count() == 1); + CHECK(text_edit->get_caret_line(0) == 0); + CHECK(text_edit->get_caret_column(0) == 1); + + ERR_PRINT_OFF; + text_edit->remove_caret(0); + CHECK(text_edit->get_caret_count() == 1); + ERR_PRINT_ON; + } + + SUBCASE("[TextEdit] caret index edit order") { + Vector<int> caret_index_get_order; + caret_index_get_order.push_back(1); + caret_index_get_order.push_back(0); + + CHECK(text_edit->add_caret(1, 0)); + CHECK(text_edit->get_caret_count() == 2); + CHECK(text_edit->get_caret_index_edit_order() == caret_index_get_order); + + text_edit->remove_secondary_carets(); + text_edit->set_caret_line(1); + CHECK(text_edit->add_caret(0, 0)); + CHECK(text_edit->get_caret_count() == 2); + + caret_index_get_order.write[0] = 0; + caret_index_get_order.write[1] = 1; + CHECK(text_edit->get_caret_index_edit_order() == caret_index_get_order); + } + + SUBCASE("[TextEdit] add caret at carets") { + text_edit->remove_secondary_carets(); + text_edit->set_caret_line(1); + text_edit->set_caret_column(9); + + text_edit->add_caret_at_carets(true); + CHECK(text_edit->get_caret_count() == 2); + CHECK(text_edit->get_caret_line(1) == 2); + CHECK(text_edit->get_caret_column(1) == 4); + + text_edit->add_caret_at_carets(true); + CHECK(text_edit->get_caret_count() == 2); + + text_edit->add_caret_at_carets(false); + CHECK(text_edit->get_caret_count() == 3); + CHECK(text_edit->get_caret_line(2) == 0); + CHECK(text_edit->get_caret_column(2) == 7); + + text_edit->remove_secondary_carets(); + text_edit->set_caret_line(0); + text_edit->set_caret_column(4); + text_edit->select(0, 0, 0, 4); + text_edit->add_caret_at_carets(true); + CHECK(text_edit->get_caret_count() == 2); + CHECK(text_edit->get_selection_from_line(1) == 1); + CHECK(text_edit->get_selection_to_line(1) == 1); + CHECK(text_edit->get_selection_from_column(1) == 0); + CHECK(text_edit->get_selection_to_column(1) == 3); + + text_edit->add_caret_at_carets(true); + CHECK(text_edit->get_caret_count() == 3); + CHECK(text_edit->get_selection_from_line(2) == 2); + CHECK(text_edit->get_selection_to_line(2) == 2); + CHECK(text_edit->get_selection_from_column(2) == 0); + CHECK(text_edit->get_selection_to_column(2) == 4); + } + + memdelete(text_edit); +} + TEST_CASE("[SceneTree][TextEdit] line wrapping") { TextEdit *text_edit = memnew(TextEdit); SceneTree::get_singleton()->get_root()->add_child(text_edit); @@ -3128,28 +3860,28 @@ TEST_CASE("[SceneTree][TextEdit] viewport") { // Scroll. int v_scroll = text_edit->get_v_scroll(); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_DOWN, MouseButton::WHEEL_DOWN, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_DOWN, 0, Key::NONE); CHECK(text_edit->get_v_scroll() > v_scroll); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_UP, MouseButton::WHEEL_UP, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_UP, 0, Key::NONE); CHECK(text_edit->get_v_scroll() == v_scroll); // smooth scroll speed. text_edit->set_smooth_scroll_enabled(true); v_scroll = text_edit->get_v_scroll(); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_DOWN, MouseButton::WHEEL_DOWN, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_DOWN, 0, Key::NONE); text_edit->notification(TextEdit::NOTIFICATION_INTERNAL_PHYSICS_PROCESS); CHECK(text_edit->get_v_scroll() >= v_scroll); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_UP, MouseButton::WHEEL_UP, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_UP, 0, Key::NONE); text_edit->notification(TextEdit::NOTIFICATION_INTERNAL_PHYSICS_PROCESS); CHECK(text_edit->get_v_scroll() == v_scroll); v_scroll = text_edit->get_v_scroll(); text_edit->set_v_scroll_speed(10000); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_DOWN, MouseButton::WHEEL_DOWN, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_DOWN, 0, Key::NONE); text_edit->notification(TextEdit::NOTIFICATION_INTERNAL_PHYSICS_PROCESS); CHECK(text_edit->get_v_scroll() >= v_scroll); - SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_UP, MouseButton::WHEEL_UP, Key::NONE); + SEND_GUI_MOUSE_BUTTON_EVENT(text_edit, Point2i(10, 10), MouseButton::WHEEL_UP, 0, Key::NONE); text_edit->notification(TextEdit::NOTIFICATION_INTERNAL_PHYSICS_PROCESS); CHECK(text_edit->get_v_scroll() == v_scroll); @@ -3291,6 +4023,32 @@ TEST_CASE("[SceneTree][TextEdit] viewport") { CHECK(text_edit->get_last_full_visible_line_wrap_index() == 0); CHECK(text_edit->get_caret_wrap_index() == 0); + // Typing and undo / redo should adjust viewport + text_edit->set_caret_line(0); + text_edit->set_caret_column(0); + text_edit->set_line_as_first_visible(5); + MessageQueue::get_singleton()->flush(); + CHECK(text_edit->get_first_visible_line() == 5); + + SEND_GUI_KEY_EVENT(text_edit, Key::A); + CHECK(text_edit->get_first_visible_line() == 0); + + text_edit->set_line_as_first_visible(5); + MessageQueue::get_singleton()->flush(); + CHECK(text_edit->get_first_visible_line() == 5); + + text_edit->undo(); + MessageQueue::get_singleton()->flush(); + CHECK(text_edit->get_first_visible_line() == 0); + + text_edit->set_line_as_first_visible(5); + MessageQueue::get_singleton()->flush(); + CHECK(text_edit->get_first_visible_line() == 5); + + text_edit->redo(); + MessageQueue::get_singleton()->flush(); + CHECK(text_edit->get_first_visible_line() == 0); + memdelete(text_edit); } diff --git a/tests/scene/test_theme.h b/tests/scene/test_theme.h index f5b21eec32..69d29e5ca5 100644 --- a/tests/scene/test_theme.h +++ b/tests/scene/test_theme.h @@ -1,32 +1,32 @@ -/*************************************************************************/ -/* test_theme.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. */ -/*************************************************************************/ +/**************************************************************************/ +/* test_theme.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #ifndef TEST_THEME_H #define TEST_THEME_H diff --git a/tests/scene/test_visual_shader.h b/tests/scene/test_visual_shader.h new file mode 100644 index 0000000000..e54971768c --- /dev/null +++ b/tests/scene/test_visual_shader.h @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* test_visual_shader.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TEST_VISUAL_SHADER_H +#define TEST_VISUAL_SHADER_H + +#include "scene/resources/visual_shader.h" + +#include "tests/test_macros.h" + +namespace TestVisualArray { + +TEST_CASE("[SceneTree][VisualShader] Object creation and parameter") { + Ref<VisualShader> vs = memnew(VisualShader); + CHECK(vs.is_valid()); + + CHECK(vs->get_mode() == Shader::MODE_SPATIAL); + + for (int i = 1; i < Shader::MODE_MAX; i++) { + vs->set_mode((Shader::Mode)i); + CHECK(vs->get_mode() == i); + } +} + +TEST_CASE("[SceneTree][VisualShader] Testing VisualShaderNodes") { + SUBCASE("Testing Node Creation") { + Ref<VisualShader> vs = memnew(VisualShader); + CHECK(vs.is_valid()); + + for (int i = 0; i < VisualShader::TYPE_MAX; i++) { + Ref<VisualShaderNode> vsn = memnew(VisualShaderNodeInput); + CHECK(vsn.is_valid()); + vs->add_node(VisualShader::Type(i), vsn, Vector2(1, 10), i + 2); + CHECK(vs->get_node(VisualShader::Type(i), i + 2) == vsn); + } + + ERR_PRINT_OFF; + + // Testing for Invalid entries. + Ref<VisualShaderNode> vsn5 = memnew(VisualShaderNodeInput); + Ref<VisualShaderNode> vsn6 = memnew(VisualShaderNodeInput); + CHECK(vsn6.is_valid()); + CHECK(vsn5.is_valid()); + + vs->add_node(VisualShader::TYPE_SKY, vsn5, Vector2(1, 10), 0); + CHECK_FALSE(vs->get_node(VisualShader::TYPE_SKY, 0) == vsn6); + vs->add_node(VisualShader::TYPE_MAX, vsn6, Vector2(1, 10), 7); + CHECK_FALSE(vs->get_node(VisualShader::TYPE_SKY, 7) == vsn6); + + ERR_PRINT_ON; + } + + SUBCASE("Testing VisualShaderNode position getter and setter") { + Ref<VisualShader> vs = memnew(VisualShader); + CHECK(vs.is_valid()); + + Ref<VisualShaderNode> vsn1 = memnew(VisualShaderNodeInput); + CHECK(vsn1.is_valid()); + vs->add_node(VisualShader::TYPE_COLLIDE, vsn1, Vector2(0, 0), 3); + CHECK(vs->get_node_position(VisualShader::TYPE_COLLIDE, 3) == Vector2(0, 0)); + vs->set_node_position(VisualShader::TYPE_COLLIDE, 3, Vector2(1, 1)); + CHECK(vs->get_node_position(VisualShader::TYPE_COLLIDE, 3) == Vector2(1, 1)); + + Ref<VisualShaderNode> vsn2 = memnew(VisualShaderNodeInput); + CHECK(vsn2.is_valid()); + vs->add_node(VisualShader::TYPE_FOG, vsn2, Vector2(1, 2), 4); + CHECK(vs->get_node_position(VisualShader::TYPE_FOG, 4) == Vector2(1, 2)); + vs->set_node_position(VisualShader::TYPE_FOG, 4, Vector2(2, 2)); + CHECK(vs->get_node_position(VisualShader::TYPE_FOG, 4) == Vector2(2, 2)); + } + + SUBCASE("Testing VisualShaderNode ID") { + Ref<VisualShader> vs = memnew(VisualShader); + CHECK(vs.is_valid()); + + for (int i = 0; i < VisualShader::TYPE_MAX; i++) { + Ref<VisualShaderNode> vsn = memnew(VisualShaderNodeInput); + CHECK(vsn.is_valid()); + vs->add_node(VisualShader::Type(i), vsn, Vector2(1, 10), i + 2); + CHECK(vs->get_valid_node_id(VisualShader::Type(i)) - 1 == i + 2); + } + } + + SUBCASE("Testing remove and replace VisualShaderNode") { + Ref<VisualShader> vs = memnew(VisualShader); + CHECK(vs.is_valid()); + + ERR_PRINT_OFF; + + for (int i = 0; i < VisualShader::TYPE_MAX; i++) { + Ref<VisualShaderNode> vsn = memnew(VisualShaderNodeInput); + CHECK(vsn.is_valid()); + vs->add_node(VisualShader::Type(i), vsn, Vector2(1, 10), i + 2); + CHECK(vs->get_node(VisualShader::Type(i), i + 2) == vsn); + vs->remove_node(VisualShader::Type(i), i + 2); + CHECK_FALSE(vs->get_node(VisualShader::Type(i), i + 2) == vsn); + } + + ERR_PRINT_ON; + } +} + +TEST_CASE("[SceneTree][VisualShader] Testing Varyings") { + Ref<VisualShader> vs = memnew(VisualShader); + + vs->add_varying("Test1", VisualShader::VARYING_MODE_FRAG_TO_LIGHT, VisualShader::VARYING_TYPE_TRANSFORM); + CHECK(vs->has_varying("Test1") == true); + + vs->add_varying("Test2", VisualShader::VARYING_MODE_VERTEX_TO_FRAG_LIGHT, VisualShader::VARYING_TYPE_VECTOR_2D); + CHECK(vs->has_varying("Test2")); + + CHECK_FALSE(vs->has_varying("Does_not_exits")); + ERR_PRINT_OFF; + vs->add_varying("Test3", VisualShader::VARYING_MODE_MAX, VisualShader::VARYING_TYPE_INT); + CHECK_FALSE(vs->has_varying("Test3")); + + vs->add_varying("Test4", VisualShader::VARYING_MODE_FRAG_TO_LIGHT, VisualShader::VARYING_TYPE_MAX); + CHECK_FALSE(vs->has_varying("Test4")); + ERR_PRINT_ON; +} + +} //namespace TestVisualArray + +#endif // TEST_VISUAL_SHADER_H |