feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -39,7 +39,8 @@
|
|||
namespace TestArrayMesh {
|
||||
|
||||
TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") {
|
||||
Ref<ArrayMesh> mesh = memnew(ArrayMesh);
|
||||
Ref<ArrayMesh> mesh;
|
||||
mesh.instantiate();
|
||||
StringName name_a{ "ShapeA" };
|
||||
StringName name_b{ "ShapeB" };
|
||||
|
||||
|
|
@ -76,8 +77,9 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") {
|
|||
}
|
||||
|
||||
SUBCASE("Adding blend shape after surface is added causes error") {
|
||||
Ref<CylinderMesh> cylinder = memnew(CylinderMesh);
|
||||
Array cylinder_array{};
|
||||
Ref<CylinderMesh> cylinder;
|
||||
cylinder.instantiate();
|
||||
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);
|
||||
|
|
@ -88,6 +90,26 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") {
|
|||
CHECK(mesh->get_blend_shape_count() == 0);
|
||||
}
|
||||
|
||||
SUBCASE("Adding blend shapes once all surfaces have been removed is allowed") {
|
||||
Ref<CylinderMesh> cylinder;
|
||||
cylinder.instantiate();
|
||||
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);
|
||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array);
|
||||
|
||||
mesh->surface_remove(0);
|
||||
ERR_PRINT_OFF
|
||||
mesh->add_blend_shape(name_a);
|
||||
ERR_PRINT_ON
|
||||
CHECK(mesh->get_blend_shape_count() == 0);
|
||||
|
||||
mesh->surface_remove(0);
|
||||
mesh->add_blend_shape(name_a);
|
||||
CHECK(mesh->get_blend_shape_count() == 1);
|
||||
}
|
||||
|
||||
SUBCASE("Change blend shape name after adding.") {
|
||||
mesh->add_blend_shape(name_a);
|
||||
mesh->set_blend_shape_name(0, name_b);
|
||||
|
|
@ -114,11 +136,40 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") {
|
|||
CHECK(mesh->get_blend_shape_count() == 0);
|
||||
}
|
||||
|
||||
SUBCASE("Clearing all blend shapes once all surfaces have been removed is allowed") {
|
||||
mesh->add_blend_shape(name_a);
|
||||
mesh->add_blend_shape(name_b);
|
||||
Ref<CylinderMesh> cylinder;
|
||||
cylinder.instantiate();
|
||||
Array cylinder_array;
|
||||
cylinder_array.resize(Mesh::ARRAY_MAX);
|
||||
cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f);
|
||||
Array blend_shape;
|
||||
blend_shape.resize(Mesh::ARRAY_MAX);
|
||||
blend_shape[Mesh::ARRAY_VERTEX] = cylinder_array[Mesh::ARRAY_VERTEX];
|
||||
blend_shape[Mesh::ARRAY_NORMAL] = cylinder_array[Mesh::ARRAY_NORMAL];
|
||||
blend_shape[Mesh::ARRAY_TANGENT] = cylinder_array[Mesh::ARRAY_TANGENT];
|
||||
Array blend_shapes;
|
||||
blend_shapes.push_back(blend_shape);
|
||||
blend_shapes.push_back(blend_shape);
|
||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, blend_shapes);
|
||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, blend_shapes);
|
||||
|
||||
mesh->surface_remove(0);
|
||||
ERR_PRINT_OFF
|
||||
mesh->clear_blend_shapes();
|
||||
ERR_PRINT_ON
|
||||
CHECK(mesh->get_blend_shape_count() == 2);
|
||||
|
||||
mesh->surface_remove(0);
|
||||
mesh->clear_blend_shapes();
|
||||
CHECK(mesh->get_blend_shape_count() == 0);
|
||||
}
|
||||
|
||||
SUBCASE("Can't add surface with incorrect number of blend shapes.") {
|
||||
mesh->add_blend_shape(name_a);
|
||||
mesh->add_blend_shape(name_b);
|
||||
Ref<CylinderMesh> cylinder = memnew(CylinderMesh);
|
||||
Array cylinder_array{};
|
||||
Array cylinder_array;
|
||||
ERR_PRINT_OFF
|
||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array);
|
||||
ERR_PRINT_ON
|
||||
|
|
@ -128,16 +179,17 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") {
|
|||
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{};
|
||||
Ref<CylinderMesh> cylinder;
|
||||
cylinder.instantiate();
|
||||
Array cylinder_array;
|
||||
cylinder_array.resize(Mesh::ARRAY_MAX);
|
||||
cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f);
|
||||
Array blend_shape{};
|
||||
Array blend_shape;
|
||||
blend_shape.resize(Mesh::ARRAY_MAX);
|
||||
blend_shape[Mesh::ARRAY_VERTEX] = cylinder_array[Mesh::ARRAY_VERTEX];
|
||||
blend_shape[Mesh::ARRAY_NORMAL] = cylinder_array[Mesh::ARRAY_NORMAL];
|
||||
blend_shape[Mesh::ARRAY_TANGENT] = cylinder_array[Mesh::ARRAY_TANGENT];
|
||||
Array blend_shapes{};
|
||||
Array blend_shapes;
|
||||
blend_shapes.push_back(blend_shape);
|
||||
blend_shapes.push_back(blend_shape);
|
||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, blend_shapes);
|
||||
|
|
@ -155,15 +207,18 @@ TEST_CASE("[SceneTree][ArrayMesh] Adding and modifying blendshapes.") {
|
|||
}
|
||||
|
||||
TEST_CASE("[SceneTree][ArrayMesh] Surface metadata tests.") {
|
||||
Ref<ArrayMesh> mesh = memnew(ArrayMesh);
|
||||
Ref<CylinderMesh> cylinder = memnew(CylinderMesh);
|
||||
Array cylinder_array{};
|
||||
Ref<ArrayMesh> mesh;
|
||||
mesh.instantiate();
|
||||
Ref<CylinderMesh> cylinder;
|
||||
cylinder.instantiate();
|
||||
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{};
|
||||
Ref<BoxMesh> box;
|
||||
box.instantiate();
|
||||
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);
|
||||
|
|
@ -207,7 +262,8 @@ TEST_CASE("[SceneTree][ArrayMesh] Surface metadata tests.") {
|
|||
}
|
||||
|
||||
SUBCASE("Set material to two different surfaces.") {
|
||||
Ref<Material> mat = memnew(Material);
|
||||
Ref<Material> mat;
|
||||
mat.instantiate();
|
||||
mesh->surface_set_material(0, mat);
|
||||
CHECK(mesh->surface_get_material(0) == mat);
|
||||
mesh->surface_set_material(1, mat);
|
||||
|
|
@ -215,7 +271,8 @@ TEST_CASE("[SceneTree][ArrayMesh] Surface metadata tests.") {
|
|||
}
|
||||
|
||||
SUBCASE("Set same material multiple times doesn't change material of surface.") {
|
||||
Ref<Material> mat = memnew(Material);
|
||||
Ref<Material> mat;
|
||||
mat.instantiate();
|
||||
mesh->surface_set_material(0, mat);
|
||||
mesh->surface_set_material(0, mat);
|
||||
mesh->surface_set_material(0, mat);
|
||||
|
|
@ -223,8 +280,10 @@ TEST_CASE("[SceneTree][ArrayMesh] Surface metadata tests.") {
|
|||
}
|
||||
|
||||
SUBCASE("Set material of surface then change to different material.") {
|
||||
Ref<Material> mat1 = memnew(Material);
|
||||
Ref<Material> mat2 = memnew(Material);
|
||||
Ref<Material> mat1;
|
||||
mat1.instantiate();
|
||||
Ref<Material> mat2;
|
||||
mat2.instantiate();
|
||||
mesh->surface_set_material(1, mat1);
|
||||
CHECK(mesh->surface_get_material(1) == mat1);
|
||||
mesh->surface_set_material(1, mat2);
|
||||
|
|
@ -232,40 +291,48 @@ TEST_CASE("[SceneTree][ArrayMesh] Surface metadata tests.") {
|
|||
}
|
||||
|
||||
SUBCASE("Get the LOD of the mesh.") {
|
||||
Dictionary lod{};
|
||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array, TypedArray<Array>{}, lod);
|
||||
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{};
|
||||
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{};
|
||||
Ref<ArrayMesh> mesh;
|
||||
mesh.instantiate();
|
||||
Ref<CylinderMesh> cylinder;
|
||||
cylinder.instantiate();
|
||||
Array cylinder_array;
|
||||
cylinder_array.resize(Mesh::ARRAY_MAX);
|
||||
cylinder->create_mesh_array(cylinder_array, 3.f, 3.f, 5.f);
|
||||
constexpr float cylinder_radius = 3.f;
|
||||
constexpr float cylinder_height = 5.f;
|
||||
cylinder->create_mesh_array(cylinder_array, cylinder_radius, cylinder_radius, cylinder_height);
|
||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array);
|
||||
|
||||
Ref<BoxMesh> box = memnew(BoxMesh);
|
||||
Array box_array{};
|
||||
Ref<BoxMesh> box;
|
||||
box.instantiate();
|
||||
Array box_array;
|
||||
box_array.resize(Mesh::ARRAY_MAX);
|
||||
box->create_mesh_array(box_array, Vector3(2.f, 1.2f, 1.6f));
|
||||
const Vector3 box_size = Vector3(2.f, 1.2f, 1.6f);
|
||||
box->create_mesh_array(box_array, box_size);
|
||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, box_array);
|
||||
|
||||
SUBCASE("Set the shadow mesh.") {
|
||||
Ref<ArrayMesh> shadow = memnew(ArrayMesh);
|
||||
Ref<ArrayMesh> shadow;
|
||||
shadow.instantiate();
|
||||
mesh->set_shadow_mesh(shadow);
|
||||
CHECK(mesh->get_shadow_mesh() == shadow);
|
||||
}
|
||||
|
||||
SUBCASE("Set the shadow mesh multiple times.") {
|
||||
Ref<ArrayMesh> shadow = memnew(ArrayMesh);
|
||||
Ref<ArrayMesh> shadow;
|
||||
shadow.instantiate();
|
||||
mesh->set_shadow_mesh(shadow);
|
||||
mesh->set_shadow_mesh(shadow);
|
||||
mesh->set_shadow_mesh(shadow);
|
||||
|
|
@ -274,8 +341,10 @@ TEST_CASE("[SceneTree][ArrayMesh] Get/Set mesh metadata and actions") {
|
|||
}
|
||||
|
||||
SUBCASE("Set the same shadow mesh on multiple meshes.") {
|
||||
Ref<ArrayMesh> shadow = memnew(ArrayMesh);
|
||||
Ref<ArrayMesh> mesh2 = memnew(ArrayMesh);
|
||||
Ref<ArrayMesh> shadow;
|
||||
shadow.instantiate();
|
||||
Ref<ArrayMesh> mesh2;
|
||||
mesh2.instantiate();
|
||||
mesh->set_shadow_mesh(shadow);
|
||||
mesh2->set_shadow_mesh(shadow);
|
||||
|
||||
|
|
@ -284,22 +353,24 @@ TEST_CASE("[SceneTree][ArrayMesh] Get/Set mesh metadata and actions") {
|
|||
}
|
||||
|
||||
SUBCASE("Set the shadow mesh and then change it.") {
|
||||
Ref<ArrayMesh> shadow = memnew(ArrayMesh);
|
||||
Ref<ArrayMesh> shadow;
|
||||
shadow.instantiate();
|
||||
mesh->set_shadow_mesh(shadow);
|
||||
CHECK(mesh->get_shadow_mesh() == shadow);
|
||||
Ref<ArrayMesh> shadow2 = memnew(ArrayMesh);
|
||||
Ref<ArrayMesh> shadow2;
|
||||
shadow2.instantiate();
|
||||
mesh->set_shadow_mesh(shadow2);
|
||||
CHECK(mesh->get_shadow_mesh() == shadow2);
|
||||
}
|
||||
|
||||
SUBCASE("Set custom AABB.") {
|
||||
AABB bound{};
|
||||
AABB bound;
|
||||
mesh->set_custom_aabb(bound);
|
||||
CHECK(mesh->get_custom_aabb() == bound);
|
||||
}
|
||||
|
||||
SUBCASE("Set custom AABB multiple times.") {
|
||||
AABB bound{};
|
||||
AABB bound;
|
||||
mesh->set_custom_aabb(bound);
|
||||
mesh->set_custom_aabb(bound);
|
||||
mesh->set_custom_aabb(bound);
|
||||
|
|
@ -308,8 +379,8 @@ TEST_CASE("[SceneTree][ArrayMesh] Get/Set mesh metadata and actions") {
|
|||
}
|
||||
|
||||
SUBCASE("Set custom AABB then change to another AABB.") {
|
||||
AABB bound{};
|
||||
AABB bound2{};
|
||||
AABB bound;
|
||||
AABB bound2;
|
||||
mesh->set_custom_aabb(bound);
|
||||
CHECK(mesh->get_custom_aabb() == bound);
|
||||
mesh->set_custom_aabb(bound2);
|
||||
|
|
@ -329,7 +400,8 @@ TEST_CASE("[SceneTree][ArrayMesh] Get/Set mesh metadata and actions") {
|
|||
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);
|
||||
Ref<ArrayMesh> mesh2;
|
||||
mesh2.instantiate();
|
||||
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);
|
||||
|
|
@ -337,6 +409,43 @@ TEST_CASE("[SceneTree][ArrayMesh] Get/Set mesh metadata and actions") {
|
|||
CHECK((mesh2->surface_get_format(0) & surface_data.format) != 0);
|
||||
CHECK(mesh2->get_aabb().is_equal_approx(surface_data.aabb));
|
||||
}
|
||||
|
||||
SUBCASE("Removing a surface decreases surface count.") {
|
||||
REQUIRE(mesh->get_surface_count() == 2);
|
||||
mesh->surface_remove(0);
|
||||
CHECK(mesh->get_surface_count() == 1);
|
||||
mesh->surface_remove(0);
|
||||
CHECK(mesh->get_surface_count() == 0);
|
||||
}
|
||||
|
||||
SUBCASE("Remove the first surface and check the mesh's AABB.") {
|
||||
REQUIRE(mesh->get_surface_count() >= 1);
|
||||
mesh->surface_remove(0);
|
||||
const AABB box_aabb = AABB(-box_size / 2, box_size);
|
||||
CHECK(mesh->get_aabb().is_equal_approx(box_aabb));
|
||||
}
|
||||
|
||||
SUBCASE("Remove the last surface and check the mesh's AABB.") {
|
||||
REQUIRE(mesh->get_surface_count() >= 1);
|
||||
mesh->surface_remove(mesh->get_surface_count() - 1);
|
||||
const AABB cylinder_aabb = AABB(Vector3(-cylinder_radius, -cylinder_height / 2, -cylinder_radius),
|
||||
Vector3(2 * cylinder_radius, cylinder_height, 2 * cylinder_radius));
|
||||
CHECK(mesh->get_aabb().is_equal_approx(cylinder_aabb));
|
||||
}
|
||||
|
||||
SUBCASE("Remove all surfaces and check the mesh's AABB.") {
|
||||
while (mesh->get_surface_count()) {
|
||||
mesh->surface_remove(0);
|
||||
}
|
||||
CHECK(mesh->get_aabb() == AABB());
|
||||
}
|
||||
|
||||
SUBCASE("Removing a non-existent surface causes error.") {
|
||||
ERR_PRINT_OFF
|
||||
mesh->surface_remove(42);
|
||||
ERR_PRINT_ON
|
||||
CHECK(mesh->get_surface_count() == 2);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TestArrayMesh
|
||||
|
|
|
|||
|
|
@ -37,11 +37,6 @@
|
|||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "editor/import/resource_importer_wav.h"
|
||||
#endif
|
||||
|
||||
namespace TestAudioStreamWAV {
|
||||
|
||||
// Default wav rate for test cases.
|
||||
|
|
@ -148,23 +143,8 @@ void run_test(String file_name, AudioStreamWAV::Format data_format, bool stereo,
|
|||
Ref<FileAccess> wav_file = FileAccess::open(save_path, FileAccess::READ, &error);
|
||||
REQUIRE(error == OK);
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// The WAV importer can be used if enabled to check that the saved file is valid.
|
||||
Ref<ResourceImporterWAV> wav_importer = memnew(ResourceImporterWAV);
|
||||
|
||||
List<ResourceImporter::ImportOption> options_list;
|
||||
wav_importer->get_import_options("", &options_list);
|
||||
|
||||
HashMap<StringName, Variant> options_map;
|
||||
for (const ResourceImporter::ImportOption &E : options_list) {
|
||||
options_map[E.option.name] = E.default_value;
|
||||
}
|
||||
|
||||
REQUIRE(wav_importer->import(save_path, save_path, options_map, nullptr) == OK);
|
||||
|
||||
String load_path = save_path + "." + wav_importer->get_save_extension();
|
||||
Ref<AudioStreamWAV> loaded_stream = ResourceLoader::load(load_path, "AudioStreamWAV", ResourceFormatImporter::CACHE_MODE_IGNORE, &error);
|
||||
REQUIRE(error == OK);
|
||||
Dictionary options;
|
||||
Ref<AudioStreamWAV> loaded_stream = AudioStreamWAV::load_from_file(save_path, options);
|
||||
|
||||
CHECK(loaded_stream->get_format() == stream->get_format());
|
||||
CHECK(loaded_stream->get_loop_mode() == stream->get_loop_mode());
|
||||
|
|
@ -175,31 +155,30 @@ void run_test(String file_name, AudioStreamWAV::Format data_format, bool stereo,
|
|||
CHECK(loaded_stream->get_length() == stream->get_length());
|
||||
CHECK(loaded_stream->is_monophonic() == stream->is_monophonic());
|
||||
CHECK(loaded_stream->get_data() == stream->get_data());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioStreamWAV] Mono PCM8 format") {
|
||||
TEST_CASE("[Audio][AudioStreamWAV] Mono PCM8 format") {
|
||||
run_test("test_pcm8_mono.wav", AudioStreamWAV::FORMAT_8_BITS, false, WAV_RATE, WAV_COUNT);
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioStreamWAV] Mono PCM16 format") {
|
||||
TEST_CASE("[Audio][AudioStreamWAV] Mono PCM16 format") {
|
||||
run_test("test_pcm16_mono.wav", AudioStreamWAV::FORMAT_16_BITS, false, WAV_RATE, WAV_COUNT);
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioStreamWAV] Stereo PCM8 format") {
|
||||
TEST_CASE("[Audio][AudioStreamWAV] Stereo PCM8 format") {
|
||||
run_test("test_pcm8_stereo.wav", AudioStreamWAV::FORMAT_8_BITS, true, WAV_RATE, WAV_COUNT);
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioStreamWAV] Stereo PCM16 format") {
|
||||
TEST_CASE("[Audio][AudioStreamWAV] Stereo PCM16 format") {
|
||||
run_test("test_pcm16_stereo.wav", AudioStreamWAV::FORMAT_16_BITS, true, WAV_RATE, WAV_COUNT);
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioStreamWAV] Alternate mix rate") {
|
||||
TEST_CASE("[Audio][AudioStreamWAV] Alternate mix rate") {
|
||||
run_test("test_pcm16_stereo_38000Hz.wav", AudioStreamWAV::FORMAT_16_BITS, true, 38000, 38000);
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioStreamWAV] save_to_wav() adds '.wav' file extension automatically") {
|
||||
TEST_CASE("[Audio][AudioStreamWAV] save_to_wav() adds '.wav' file extension automatically") {
|
||||
String save_path = TestUtils::get_temp_path("test_wav_extension");
|
||||
Vector<uint8_t> test_data = gen_pcm8_test(WAV_RATE, WAV_COUNT, false);
|
||||
Ref<AudioStreamWAV> stream = memnew(AudioStreamWAV);
|
||||
|
|
@ -211,7 +190,7 @@ TEST_CASE("[AudioStreamWAV] save_to_wav() adds '.wav' file extension automatical
|
|||
CHECK(error == OK);
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioStreamWAV] Default values") {
|
||||
TEST_CASE("[Audio][AudioStreamWAV] Default values") {
|
||||
Ref<AudioStreamWAV> stream = memnew(AudioStreamWAV);
|
||||
CHECK(stream->get_format() == AudioStreamWAV::FORMAT_8_BITS);
|
||||
CHECK(stream->get_loop_mode() == AudioStreamWAV::LOOP_DISABLED);
|
||||
|
|
@ -225,11 +204,11 @@ TEST_CASE("[AudioStreamWAV] Default values") {
|
|||
CHECK(stream->get_stream_name() == "");
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioStreamWAV] Save empty file") {
|
||||
TEST_CASE("[Audio][AudioStreamWAV] Save empty file") {
|
||||
run_test("test_empty.wav", AudioStreamWAV::FORMAT_8_BITS, false, WAV_RATE, 0);
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioStreamWAV] Saving IMA ADPCM is not supported") {
|
||||
TEST_CASE("[Audio][AudioStreamWAV] Saving IMA ADPCM is not supported") {
|
||||
String save_path = TestUtils::get_temp_path("test_adpcm.wav");
|
||||
Ref<AudioStreamWAV> stream = memnew(AudioStreamWAV);
|
||||
stream->set_format(AudioStreamWAV::FORMAT_IMA_ADPCM);
|
||||
|
|
|
|||
66
engine/tests/scene/test_button.h
Normal file
66
engine/tests/scene/test_button.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/**************************************************************************/
|
||||
/* test_button.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_BUTTON_H
|
||||
#define TEST_BUTTON_H
|
||||
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/main/window.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestButton {
|
||||
TEST_CASE("[SceneTree][Button] is_hovered()") {
|
||||
// Create new button instance.
|
||||
Button *button = memnew(Button);
|
||||
CHECK(button != nullptr);
|
||||
Window *root = SceneTree::get_singleton()->get_root();
|
||||
root->add_child(button);
|
||||
|
||||
// Set up button's size and position.
|
||||
button->set_size(Size2i(50, 50));
|
||||
button->set_position(Size2i(10, 10));
|
||||
|
||||
// Button should initially be not hovered.
|
||||
CHECK(button->is_hovered() == false);
|
||||
|
||||
// Simulate mouse entering the button.
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(Point2i(25, 25), MouseButtonMask::NONE, Key::NONE);
|
||||
CHECK(button->is_hovered() == true);
|
||||
|
||||
// Simulate mouse exiting the button.
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(Point2i(150, 150), MouseButtonMask::NONE, Key::NONE);
|
||||
CHECK(button->is_hovered() == false);
|
||||
|
||||
memdelete(button);
|
||||
}
|
||||
|
||||
} //namespace TestButton
|
||||
#endif // TEST_BUTTON_H
|
||||
|
|
@ -231,10 +231,13 @@ TEST_CASE("[SceneTree][Camera3D] Project/Unproject position") {
|
|||
test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f);
|
||||
// Center.
|
||||
CHECK(test_camera->project_position(Vector2(200, 100), 0.5f).is_equal_approx(Vector3(0, 0, -0.5f)));
|
||||
CHECK(test_camera->project_position(Vector2(200, 100), test_camera->get_far()).is_equal_approx(Vector3(0, 0, -test_camera->get_far())));
|
||||
// Top left.
|
||||
CHECK(test_camera->project_position(Vector2(0, 0), 1.5f).is_equal_approx(Vector3(-5.0f, 2.5f, -1.5f)));
|
||||
CHECK(test_camera->project_position(Vector2(0, 0), test_camera->get_near()).is_equal_approx(Vector3(-5.0f, 2.5f, -test_camera->get_near())));
|
||||
// Bottom right.
|
||||
CHECK(test_camera->project_position(Vector2(400, 200), 5.0f).is_equal_approx(Vector3(5.0f, -2.5f, -5.0f)));
|
||||
CHECK(test_camera->project_position(Vector2(400, 200), test_camera->get_far()).is_equal_approx(Vector3(5.0f, -2.5f, -test_camera->get_far())));
|
||||
}
|
||||
|
||||
SUBCASE("Perspective projection") {
|
||||
|
|
@ -242,12 +245,15 @@ TEST_CASE("[SceneTree][Camera3D] Project/Unproject position") {
|
|||
// Center.
|
||||
CHECK(test_camera->project_position(Vector2(200, 100), 0.5f).is_equal_approx(Vector3(0, 0, -0.5f)));
|
||||
CHECK(test_camera->project_position(Vector2(200, 100), 100.0f).is_equal_approx(Vector3(0, 0, -100.0f)));
|
||||
CHECK(test_camera->project_position(Vector2(200, 100), test_camera->get_far()).is_equal_approx(Vector3(0, 0, -1.0f) * test_camera->get_far()));
|
||||
// 3/4th way to Top left.
|
||||
CHECK(test_camera->project_position(Vector2(100, 50), 0.5f).is_equal_approx(Vector3(-SQRT3 * 0.5f, SQRT3 * 0.25f, -0.5f)));
|
||||
CHECK(test_camera->project_position(Vector2(100, 50), 1.0f).is_equal_approx(Vector3(-SQRT3, SQRT3 * 0.5f, -1.0f)));
|
||||
CHECK(test_camera->project_position(Vector2(100, 50), test_camera->get_near()).is_equal_approx(Vector3(-SQRT3, SQRT3 * 0.5f, -1.0f) * test_camera->get_near()));
|
||||
// 3/4th way to Bottom right.
|
||||
CHECK(test_camera->project_position(Vector2(300, 150), 0.5f).is_equal_approx(Vector3(SQRT3 * 0.5f, -SQRT3 * 0.25f, -0.5f)));
|
||||
CHECK(test_camera->project_position(Vector2(300, 150), 1.0f).is_equal_approx(Vector3(SQRT3, -SQRT3 * 0.5f, -1.0f)));
|
||||
CHECK(test_camera->project_position(Vector2(300, 150), test_camera->get_far()).is_equal_approx(Vector3(SQRT3, -SQRT3 * 0.5f, -1.0f) * test_camera->get_far()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -848,7 +848,7 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
SceneTree::get_singleton()->get_root()->add_child(code_edit);
|
||||
code_edit->grab_focus();
|
||||
|
||||
const Point2 OUTSIDE_DELIMETER = Point2(-1, -1);
|
||||
const Point2 OUTSIDE_DELIMITER = Point2(-1, -1);
|
||||
|
||||
code_edit->clear_string_delimiters();
|
||||
code_edit->clear_comment_delimiters();
|
||||
|
|
@ -1047,13 +1047,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line above is not in string. */
|
||||
CHECK(code_edit->is_in_string(0, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column before start key is not in string. */
|
||||
CHECK(code_edit->is_in_string(1, 0) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column after start key is in string and start / end positions are correct. */
|
||||
CHECK(code_edit->is_in_string(1, 1) != -1);
|
||||
|
|
@ -1062,8 +1062,8 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line after is not in string. */
|
||||
CHECK(code_edit->is_in_string(2, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(2, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(2, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(2, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(2, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check region metadata. */
|
||||
int idx = code_edit->is_in_string(1, 1);
|
||||
|
|
@ -1075,13 +1075,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line above is not in string. */
|
||||
CHECK(code_edit->is_in_string(0, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column before first start key is not in string. */
|
||||
CHECK(code_edit->is_in_string(1, 0) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column after the first start key is in string and start / end positions are correct. */
|
||||
CHECK(code_edit->is_in_string(1, 1) != -1);
|
||||
|
|
@ -1095,8 +1095,8 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line after is not in string. */
|
||||
CHECK(code_edit->is_in_string(2, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(2, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(2, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(2, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(2, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check is in string with no column returns true if entire line is comment excluding whitespace. */
|
||||
code_edit->set_text(" \n # # \n ");
|
||||
|
|
@ -1138,13 +1138,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line above is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(0, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column before start key is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(1, 0) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column after start key is in comment and start / end positions are correct. */
|
||||
CHECK(code_edit->is_in_comment(1, 1) != -1);
|
||||
|
|
@ -1153,8 +1153,8 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line after is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(2, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(2, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(2, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(2, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(2, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check region metadata. */
|
||||
int idx = code_edit->is_in_comment(1, 1);
|
||||
|
|
@ -1166,13 +1166,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line above is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(0, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column before first start key is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(1, 0) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column after the first start key is in comment and start / end positions are correct. */
|
||||
CHECK(code_edit->is_in_comment(1, 1) != -1);
|
||||
|
|
@ -1186,8 +1186,8 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line after is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(2, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(2, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(2, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(2, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(2, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check is in comment with no column returns true if entire line is comment excluding whitespace. */
|
||||
code_edit->set_text(" \n # # \n ");
|
||||
|
|
@ -1235,13 +1235,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line above is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(0, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column before first start key is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(1, 0) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column after the first start key is in comment and start / end positions are correct. */
|
||||
CHECK(code_edit->is_in_comment(1, 1) != -1);
|
||||
|
|
@ -1256,8 +1256,8 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line after is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(2, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(2, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(2, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(2, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(2, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Remove the comment delimiter. */
|
||||
code_edit->remove_comment_delimiter("#");
|
||||
|
|
@ -1266,8 +1266,8 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* The "first" comment region is no longer valid. */
|
||||
CHECK(code_edit->is_in_comment(1, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* The "second" region as string is now valid. */
|
||||
CHECK(code_edit->is_in_string(1, 5) != -1);
|
||||
|
|
@ -1291,13 +1291,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line above is not in string. */
|
||||
CHECK(code_edit->is_in_string(0, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column before start key is not in string. */
|
||||
CHECK(code_edit->is_in_string(1, 0) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column before closing delimiter is in string. */
|
||||
CHECK(code_edit->is_in_string(1, 2) != -1);
|
||||
|
|
@ -1306,13 +1306,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check column after end key is not in string. */
|
||||
CHECK(code_edit->is_in_string(1, 6) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 6) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 6) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 6) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 6) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check line after is not in string. */
|
||||
CHECK(code_edit->is_in_string(2, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(2, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(2, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(2, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(2, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check the region metadata. */
|
||||
int idx = code_edit->is_in_string(1, 2);
|
||||
|
|
@ -1324,13 +1324,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line above is not in string. */
|
||||
CHECK(code_edit->is_in_string(0, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column before start key is not in string. */
|
||||
CHECK(code_edit->is_in_string(1, 0) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column just after start key is in string. */
|
||||
CHECK(code_edit->is_in_string(1, 2) != -1);
|
||||
|
|
@ -1349,26 +1349,26 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check column after end key is not in string. */
|
||||
CHECK(code_edit->is_in_string(3, 3) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(3, 3) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(3, 3) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(3, 3) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(3, 3) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check line after is not in string. */
|
||||
CHECK(code_edit->is_in_string(4, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(4, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(4, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(4, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(4, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Next test over a multiple non-blank lines. */
|
||||
code_edit->set_text(" \n # \n \n # \n ");
|
||||
|
||||
/* Check line above is not in string. */
|
||||
CHECK(code_edit->is_in_string(0, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column before start key is not in string. */
|
||||
CHECK(code_edit->is_in_string(1, 0) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column just after start key is in string. */
|
||||
CHECK(code_edit->is_in_string(1, 2) != -1);
|
||||
|
|
@ -1387,13 +1387,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check column after end key is not in string. */
|
||||
CHECK(code_edit->is_in_string(3, 3) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(3, 3) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(3, 3) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(3, 3) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(3, 3) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check line after is not in string. */
|
||||
CHECK(code_edit->is_in_string(4, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(4, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(4, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(4, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(4, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* check the region metadata. */
|
||||
idx = code_edit->is_in_string(1, 2);
|
||||
|
|
@ -1409,13 +1409,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line above is not in string. */
|
||||
CHECK(code_edit->is_in_string(0, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column before start key is not in string. */
|
||||
CHECK(code_edit->is_in_string(1, 0) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column just after start key is in string. */
|
||||
CHECK(code_edit->is_in_string(1, 2) != -1);
|
||||
|
|
@ -1434,13 +1434,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check column after end key is not in string. */
|
||||
CHECK(code_edit->is_in_string(3, 3) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(3, 3) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(3, 3) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(3, 3) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(3, 3) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check line after is not in string. */
|
||||
CHECK(code_edit->is_in_string(4, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(4, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(4, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(4, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(4, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* check the region metadata. */
|
||||
idx = code_edit->is_in_string(1, 2);
|
||||
|
|
@ -1493,13 +1493,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line above is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(0, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column before start key is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(1, 0) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column before closing delimiter is in comment. */
|
||||
CHECK(code_edit->is_in_comment(1, 2) != -1);
|
||||
|
|
@ -1508,13 +1508,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check column after end key is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(1, 6) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 6) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 6) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 6) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 6) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check line after is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(2, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(2, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(2, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(2, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(2, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check the region metadata. */
|
||||
int idx = code_edit->is_in_comment(1, 2);
|
||||
|
|
@ -1526,13 +1526,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line above is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(0, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column before start key is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(1, 0) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column just after start key is in comment. */
|
||||
CHECK(code_edit->is_in_comment(1, 2) != -1);
|
||||
|
|
@ -1551,26 +1551,26 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check column after end key is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(3, 3) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(3, 3) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(3, 3) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(3, 3) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(3, 3) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check line after is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(4, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(4, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(4, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(4, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(4, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Next test over a multiple non-blank lines. */
|
||||
code_edit->set_text(" \n # \n \n # \n ");
|
||||
|
||||
/* Check line above is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(0, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column before start key is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(1, 0) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column just after start key is in comment. */
|
||||
CHECK(code_edit->is_in_comment(1, 2) != -1);
|
||||
|
|
@ -1589,13 +1589,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check column after end key is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(3, 3) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(3, 3) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(3, 3) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(3, 3) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(3, 3) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check line after is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(4, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(4, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(4, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(4, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(4, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* check the region metadata. */
|
||||
idx = code_edit->is_in_comment(1, 2);
|
||||
|
|
@ -1611,13 +1611,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line above is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(0, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column before start key is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(1, 0) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column just after start key is in comment. */
|
||||
CHECK(code_edit->is_in_comment(1, 2) != -1);
|
||||
|
|
@ -1636,13 +1636,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check column after end key is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(3, 3) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(3, 3) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(3, 3) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(3, 3) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(3, 3) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check line after is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(4, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(4, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(4, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(4, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(4, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* check the region metadata. */
|
||||
idx = code_edit->is_in_comment(1, 2);
|
||||
|
|
@ -1700,13 +1700,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check line above is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(0, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(0, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column before start key is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(1, 0) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(1, 0) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check column just after start key is in comment. */
|
||||
CHECK(code_edit->is_in_comment(1, 2) != -1);
|
||||
|
|
@ -1725,13 +1725,13 @@ TEST_CASE("[SceneTree][CodeEdit] delimiters") {
|
|||
|
||||
/* Check column after end key is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(3, 3) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(3, 3) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(3, 3) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(3, 3) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(3, 3) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* Check line after is not in comment. */
|
||||
CHECK(code_edit->is_in_comment(4, 1) == -1);
|
||||
CHECK(code_edit->get_delimiter_start_position(4, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_end_position(4, 1) == OUTSIDE_DELIMETER);
|
||||
CHECK(code_edit->get_delimiter_start_position(4, 1) == OUTSIDE_DELIMITER);
|
||||
CHECK(code_edit->get_delimiter_end_position(4, 1) == OUTSIDE_DELIMITER);
|
||||
|
||||
/* check the region metadata. */
|
||||
int idx = code_edit->is_in_comment(1, 2);
|
||||
|
|
@ -4609,6 +4609,26 @@ TEST_CASE("[SceneTree][CodeEdit] text manipulation") {
|
|||
CHECK(code_edit->get_text() == "line 1\nline 2\nline 3");
|
||||
CHECK(code_edit->get_caret_line() == 0);
|
||||
CHECK(code_edit->get_caret_column() == 0);
|
||||
|
||||
// Unfold previous folded line on backspace if the caret is at the first column.
|
||||
code_edit->set_line_folding_enabled(true);
|
||||
code_edit->set_text("line 1\n\tline 2\nline 3");
|
||||
code_edit->set_caret_line(2);
|
||||
code_edit->set_caret_column(0);
|
||||
code_edit->fold_line(0);
|
||||
code_edit->backspace();
|
||||
CHECK_FALSE(code_edit->is_line_folded(0));
|
||||
code_edit->set_line_folding_enabled(false);
|
||||
|
||||
// Do not unfold previous line on backspace if the caret is not at the first column.
|
||||
code_edit->set_line_folding_enabled(true);
|
||||
code_edit->set_text("line 1\n\tline 2\nline 3");
|
||||
code_edit->set_caret_line(2);
|
||||
code_edit->set_caret_column(4);
|
||||
code_edit->fold_line(0);
|
||||
code_edit->backspace();
|
||||
CHECK(code_edit->is_line_folded(0));
|
||||
code_edit->set_line_folding_enabled(false);
|
||||
}
|
||||
|
||||
SUBCASE("[TextEdit] cut") {
|
||||
|
|
@ -4759,6 +4779,31 @@ TEST_CASE("[SceneTree][CodeEdit] text manipulation") {
|
|||
CHECK(code_edit->get_caret_column(3) == 0);
|
||||
}
|
||||
|
||||
SUBCASE("[SceneTree][CodeEdit] cut when empty selection clipboard disabled") {
|
||||
DisplayServerMock *DS = (DisplayServerMock *)(DisplayServer::get_singleton());
|
||||
code_edit->set_empty_selection_clipboard_enabled(false);
|
||||
DS->clipboard_set("");
|
||||
|
||||
code_edit->set_text("this is\nsome\n");
|
||||
code_edit->set_caret_line(0);
|
||||
code_edit->set_caret_column(6);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
SIGNAL_DISCARD("text_set");
|
||||
SIGNAL_DISCARD("text_changed");
|
||||
SIGNAL_DISCARD("lines_edited_from");
|
||||
SIGNAL_DISCARD("caret_changed");
|
||||
|
||||
code_edit->cut();
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(DS->clipboard_get() == "");
|
||||
CHECK(code_edit->get_text() == "this is\nsome\n");
|
||||
CHECK(code_edit->get_caret_line() == 0);
|
||||
CHECK(code_edit->get_caret_column() == 6);
|
||||
SIGNAL_CHECK_FALSE("caret_changed");
|
||||
SIGNAL_CHECK_FALSE("text_changed");
|
||||
SIGNAL_CHECK_FALSE("lines_edited_from");
|
||||
}
|
||||
|
||||
SUBCASE("[SceneTree][CodeEdit] new line") {
|
||||
// Add a new line.
|
||||
code_edit->set_text("test new line");
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
namespace TestControl {
|
||||
|
||||
TEST_CASE("[SceneTree][Control]") {
|
||||
TEST_CASE("[SceneTree][Control] Transforms") {
|
||||
SUBCASE("[Control][Global Transform] Global Transform should be accessible while not in SceneTree.") { // GH-79453
|
||||
Control *test_node = memnew(Control);
|
||||
Control *test_child = memnew(Control);
|
||||
|
|
@ -61,6 +61,265 @@ TEST_CASE("[SceneTree][Control]") {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Control] Focus") {
|
||||
Control *ctrl = memnew(Control);
|
||||
SceneTree::get_singleton()->get_root()->add_child(ctrl);
|
||||
|
||||
SUBCASE("[SceneTree][Control] Default focus") {
|
||||
CHECK_FALSE(ctrl->has_focus());
|
||||
}
|
||||
|
||||
SUBCASE("[SceneTree][Control] Can't grab focus with default focus mode") {
|
||||
ERR_PRINT_OFF
|
||||
ctrl->grab_focus();
|
||||
ERR_PRINT_ON
|
||||
|
||||
CHECK_FALSE(ctrl->has_focus());
|
||||
}
|
||||
|
||||
SUBCASE("[SceneTree][Control] Can grab focus") {
|
||||
ctrl->set_focus_mode(Control::FocusMode::FOCUS_ALL);
|
||||
ctrl->grab_focus();
|
||||
|
||||
CHECK(ctrl->has_focus());
|
||||
}
|
||||
|
||||
SUBCASE("[SceneTree][Control] Can release focus") {
|
||||
ctrl->set_focus_mode(Control::FocusMode::FOCUS_ALL);
|
||||
ctrl->grab_focus();
|
||||
CHECK(ctrl->has_focus());
|
||||
|
||||
ctrl->release_focus();
|
||||
CHECK_FALSE(ctrl->has_focus());
|
||||
}
|
||||
|
||||
SUBCASE("[SceneTree][Control] Only one can grab focus at the same time") {
|
||||
ctrl->set_focus_mode(Control::FocusMode::FOCUS_ALL);
|
||||
ctrl->grab_focus();
|
||||
CHECK(ctrl->has_focus());
|
||||
|
||||
Control *other_ctrl = memnew(Control);
|
||||
SceneTree::get_singleton()->get_root()->add_child(other_ctrl);
|
||||
other_ctrl->set_focus_mode(Control::FocusMode::FOCUS_ALL);
|
||||
other_ctrl->grab_focus();
|
||||
|
||||
CHECK(other_ctrl->has_focus());
|
||||
CHECK_FALSE(ctrl->has_focus());
|
||||
|
||||
memdelete(other_ctrl);
|
||||
}
|
||||
|
||||
memdelete(ctrl);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Control] Anchoring") {
|
||||
Control *test_control = memnew(Control);
|
||||
Control *test_child = memnew(Control);
|
||||
test_control->add_child(test_child);
|
||||
test_control->set_size(Size2(2, 2));
|
||||
Window *root = SceneTree::get_singleton()->get_root();
|
||||
root->add_child(test_control);
|
||||
|
||||
SUBCASE("Anchoring without offsets") {
|
||||
test_child->set_anchor(SIDE_RIGHT, 0.75);
|
||||
test_child->set_anchor(SIDE_BOTTOM, 0.1);
|
||||
CHECK_MESSAGE(
|
||||
test_child->get_size().is_equal_approx(Vector2(1.5, 0.2)),
|
||||
"With no LEFT or TOP anchors, positive RIGHT and BOTTOM anchors should be proportional to the size.");
|
||||
CHECK_MESSAGE(
|
||||
test_child->get_position().is_equal_approx(Vector2(0, 0)),
|
||||
"With positive RIGHT and BOTTOM anchors set and no LEFT or TOP anchors, the position should not change.");
|
||||
|
||||
test_child->set_anchor(SIDE_LEFT, 0.5);
|
||||
test_child->set_anchor(SIDE_TOP, 0.01);
|
||||
CHECK_MESSAGE(
|
||||
test_child->get_size().is_equal_approx(Vector2(0.5, 0.18)),
|
||||
"With all anchors set, the size should fit between all four anchors.");
|
||||
CHECK_MESSAGE(
|
||||
test_child->get_position().is_equal_approx(Vector2(1, 0.02)),
|
||||
"With all anchors set, the LEFT and TOP anchors should proportional to the position.");
|
||||
}
|
||||
|
||||
SUBCASE("Anchoring with offsets") {
|
||||
test_child->set_offset(SIDE_RIGHT, 0.33);
|
||||
test_child->set_offset(SIDE_BOTTOM, 0.2);
|
||||
CHECK_MESSAGE(
|
||||
test_child->get_size().is_equal_approx(Vector2(0.33, 0.2)),
|
||||
"With no anchors or LEFT or TOP offsets set, the RIGHT and BOTTOM offsets should be equal to size.");
|
||||
CHECK_MESSAGE(
|
||||
test_child->get_position().is_equal_approx(Vector2(0, 0)),
|
||||
"With only positive RIGHT and BOTTOM offsets set, the position should not change.");
|
||||
|
||||
test_child->set_offset(SIDE_LEFT, 0.1);
|
||||
test_child->set_offset(SIDE_TOP, 0.05);
|
||||
CHECK_MESSAGE(
|
||||
test_child->get_size().is_equal_approx(Vector2(0.23, 0.15)),
|
||||
"With no anchors set, the size should fit between all four offsets.");
|
||||
CHECK_MESSAGE(
|
||||
test_child->get_position().is_equal_approx(Vector2(0.1, 0.05)),
|
||||
"With no anchors set, the LEFT and TOP offsets should be equal to the position.");
|
||||
|
||||
test_child->set_anchor(SIDE_RIGHT, 0.5);
|
||||
test_child->set_anchor(SIDE_BOTTOM, 0.3);
|
||||
test_child->set_anchor(SIDE_LEFT, 0.2);
|
||||
test_child->set_anchor(SIDE_TOP, 0.1);
|
||||
CHECK_MESSAGE(
|
||||
test_child->get_size().is_equal_approx(Vector2(0.83, 0.55)),
|
||||
"Anchors adjust size first then it is affected by offsets.");
|
||||
CHECK_MESSAGE(
|
||||
test_child->get_position().is_equal_approx(Vector2(0.5, 0.25)),
|
||||
"Anchors adjust positions first then it is affected by offsets.");
|
||||
|
||||
test_child->set_offset(SIDE_RIGHT, -0.1);
|
||||
test_child->set_offset(SIDE_BOTTOM, -0.01);
|
||||
test_child->set_offset(SIDE_LEFT, -0.33);
|
||||
test_child->set_offset(SIDE_TOP, -0.16);
|
||||
CHECK_MESSAGE(
|
||||
test_child->get_size().is_equal_approx(Vector2(0.83, 0.55)),
|
||||
"Keeping offset distance equal when changing offsets, keeps size equal.");
|
||||
CHECK_MESSAGE(
|
||||
test_child->get_position().is_equal_approx(Vector2(0.07, 0.04)),
|
||||
"Negative offsets move position in top left direction.");
|
||||
}
|
||||
|
||||
SUBCASE("Anchoring is preserved on parent size changed") {
|
||||
test_child->set_offset(SIDE_RIGHT, -0.05);
|
||||
test_child->set_offset(SIDE_BOTTOM, 0.1);
|
||||
test_child->set_offset(SIDE_LEFT, 0.05);
|
||||
test_child->set_offset(SIDE_TOP, 0.1);
|
||||
test_child->set_anchor(SIDE_RIGHT, 0.3);
|
||||
test_child->set_anchor(SIDE_BOTTOM, 0.85);
|
||||
test_child->set_anchor(SIDE_LEFT, 0.2);
|
||||
test_child->set_anchor(SIDE_TOP, 0.55);
|
||||
CHECK(test_child->get_rect().is_equal_approx(
|
||||
Rect2(Vector2(0.45, 1.2), Size2(0.1, 0.6))));
|
||||
|
||||
test_control->set_size(Size2(4, 1));
|
||||
CHECK(test_child->get_rect().is_equal_approx(
|
||||
Rect2(Vector2(0.85, 0.65), Size2(0.3, 0.3))));
|
||||
}
|
||||
|
||||
memdelete(test_child);
|
||||
memdelete(test_control);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Control] Custom minimum size") {
|
||||
Control *test_control = memnew(Control);
|
||||
test_control->set_custom_minimum_size(Size2(4, 2));
|
||||
Window *root = SceneTree::get_singleton()->get_root();
|
||||
root->add_child(test_control);
|
||||
CHECK_MESSAGE(
|
||||
test_control->get_size().is_equal_approx(Vector2(4, 2)),
|
||||
"Size increases to match custom minimum size.");
|
||||
|
||||
test_control->set_size(Size2(5, 4));
|
||||
CHECK_MESSAGE(
|
||||
test_control->get_size().is_equal_approx(Vector2(5, 4)),
|
||||
"Size does not change if above custom minimum size.");
|
||||
|
||||
test_control->set_size(Size2(1, 1));
|
||||
CHECK_MESSAGE(
|
||||
test_control->get_size().is_equal_approx(Vector2(4, 2)),
|
||||
"Size matches minimum size if set below custom minimum size.");
|
||||
|
||||
test_control->set_size(Size2(3, 3));
|
||||
CHECK_MESSAGE(
|
||||
test_control->get_size().is_equal_approx(Vector2(4, 3)),
|
||||
"Adjusts only x axis size if x is below custom minimum size.");
|
||||
|
||||
test_control->set_size(Size2(10, 0.1));
|
||||
CHECK_MESSAGE(
|
||||
test_control->get_size().is_equal_approx(Vector2(10, 2)),
|
||||
"Adjusts only y axis size if y is below custom minimum size.");
|
||||
|
||||
memdelete(test_control);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Control] Grow direction") {
|
||||
Control *test_control = memnew(Control);
|
||||
test_control->set_size(Size2(1, 1));
|
||||
Window *root = SceneTree::get_singleton()->get_root();
|
||||
root->add_child(test_control);
|
||||
|
||||
SUBCASE("Defaults") {
|
||||
CHECK(test_control->get_h_grow_direction() == Control::GROW_DIRECTION_END);
|
||||
CHECK(test_control->get_v_grow_direction() == Control::GROW_DIRECTION_END);
|
||||
}
|
||||
|
||||
SIGNAL_WATCH(test_control, SNAME("minimum_size_changed"))
|
||||
Array signal_args;
|
||||
signal_args.push_back(Array());
|
||||
|
||||
SUBCASE("Horizontal grow direction begin") {
|
||||
test_control->set_h_grow_direction(Control::GROW_DIRECTION_BEGIN);
|
||||
test_control->set_custom_minimum_size(Size2(2, 2));
|
||||
SceneTree::get_singleton()->process(0);
|
||||
SIGNAL_CHECK("minimum_size_changed", signal_args)
|
||||
CHECK_MESSAGE(
|
||||
test_control->get_rect().is_equal_approx(
|
||||
Rect2(Vector2(-1, 0), Size2(2, 2))),
|
||||
"Expand leftwards.");
|
||||
}
|
||||
|
||||
SUBCASE("Vertical grow direction begin") {
|
||||
test_control->set_v_grow_direction(Control::GROW_DIRECTION_BEGIN);
|
||||
test_control->set_custom_minimum_size(Size2(4, 3));
|
||||
SceneTree::get_singleton()->process(0);
|
||||
SIGNAL_CHECK("minimum_size_changed", signal_args);
|
||||
CHECK_MESSAGE(
|
||||
test_control->get_rect().is_equal_approx(
|
||||
Rect2(Vector2(0, -2), Size2(4, 3))),
|
||||
"Expand upwards.");
|
||||
}
|
||||
|
||||
SUBCASE("Horizontal grow direction end") {
|
||||
test_control->set_h_grow_direction(Control::GROW_DIRECTION_END);
|
||||
test_control->set_custom_minimum_size(Size2(5, 3));
|
||||
SceneTree::get_singleton()->process(0);
|
||||
SIGNAL_CHECK("minimum_size_changed", signal_args);
|
||||
CHECK_MESSAGE(
|
||||
test_control->get_rect().is_equal_approx(
|
||||
Rect2(Vector2(0, 0), Size2(5, 3))),
|
||||
"Expand rightwards.");
|
||||
}
|
||||
|
||||
SUBCASE("Vertical grow direction end") {
|
||||
test_control->set_v_grow_direction(Control::GROW_DIRECTION_END);
|
||||
test_control->set_custom_minimum_size(Size2(4, 4));
|
||||
SceneTree::get_singleton()->process(0);
|
||||
SIGNAL_CHECK("minimum_size_changed", signal_args);
|
||||
CHECK_MESSAGE(
|
||||
test_control->get_rect().is_equal_approx(
|
||||
Rect2(Vector2(0, 0), Size2(4, 4))),
|
||||
"Expand downwards.");
|
||||
;
|
||||
}
|
||||
|
||||
SUBCASE("Horizontal grow direction both") {
|
||||
test_control->set_h_grow_direction(Control::GROW_DIRECTION_BOTH);
|
||||
test_control->set_custom_minimum_size(Size2(2, 4));
|
||||
SceneTree::get_singleton()->process(0);
|
||||
SIGNAL_CHECK("minimum_size_changed", signal_args);
|
||||
CHECK_MESSAGE(
|
||||
test_control->get_rect().is_equal_approx(
|
||||
Rect2(Vector2(-0.5, 0), Size2(2, 4))),
|
||||
"Expand equally leftwards and rightwards.");
|
||||
}
|
||||
|
||||
SUBCASE("Vertical grow direction both") {
|
||||
test_control->set_v_grow_direction(Control::GROW_DIRECTION_BOTH);
|
||||
test_control->set_custom_minimum_size(Size2(6, 3));
|
||||
SceneTree::get_singleton()->process(0);
|
||||
SIGNAL_CHECK("minimum_size_changed", signal_args);
|
||||
CHECK_MESSAGE(
|
||||
test_control->get_rect().is_equal_approx(
|
||||
Rect2(Vector2(0, -1), Size2(6, 3))),
|
||||
"Expand equally upwards and downwards.");
|
||||
}
|
||||
|
||||
memdelete(test_control);
|
||||
}
|
||||
|
||||
} // namespace TestControl
|
||||
|
||||
#endif // TEST_CONTROL_H
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ TEST_CASE("[Curve] Default curve") {
|
|||
"Default curve should return the expected value at offset 1.0.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Curve] Custom curve with free tangents") {
|
||||
TEST_CASE("[Curve] Custom unit curve with free tangents") {
|
||||
Ref<Curve> curve = memnew(Curve);
|
||||
// "Sawtooth" curve with an open ending towards the 1.0 offset.
|
||||
curve->add_point(Vector2(0, 0));
|
||||
|
|
@ -136,7 +136,90 @@ TEST_CASE("[Curve] Custom curve with free tangents") {
|
|||
"Custom free curve should return the expected baked value at offset 0.6 after clearing all points.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Curve] Custom curve with linear tangents") {
|
||||
TEST_CASE("[Curve] Custom non-unit curve with free tangents") {
|
||||
Ref<Curve> curve = memnew(Curve);
|
||||
curve->set_min_domain(-100.0);
|
||||
curve->set_max_domain(100.0);
|
||||
// "Sawtooth" curve with an open ending towards the 100 offset.
|
||||
curve->add_point(Vector2(-100, 0));
|
||||
curve->add_point(Vector2(-50, 1));
|
||||
curve->add_point(Vector2(0, 0));
|
||||
curve->add_point(Vector2(50, 1));
|
||||
curve->set_bake_resolution(11);
|
||||
|
||||
CHECK_MESSAGE(
|
||||
Math::is_zero_approx(curve->get_point_left_tangent(0)),
|
||||
"get_point_left_tangent() should return the expected value for point index 0.");
|
||||
CHECK_MESSAGE(
|
||||
Math::is_zero_approx(curve->get_point_right_tangent(0)),
|
||||
"get_point_right_tangent() should return the expected value for point index 0.");
|
||||
CHECK_MESSAGE(
|
||||
curve->get_point_left_mode(0) == Curve::TangentMode::TANGENT_FREE,
|
||||
"get_point_left_mode() should return the expected value for point index 0.");
|
||||
CHECK_MESSAGE(
|
||||
curve->get_point_right_mode(0) == Curve::TangentMode::TANGENT_FREE,
|
||||
"get_point_right_mode() should return the expected value for point index 0.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
curve->get_point_count() == 4,
|
||||
"Custom free curve should contain the expected number of points.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
Math::is_zero_approx(curve->sample(-200)),
|
||||
"Custom free curve should return the expected value at offset -200.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.352),
|
||||
"Custom free curve should return the expected value at offset equivalent to a unit curve's 0.1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.4 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.352),
|
||||
"Custom free curve should return the expected value at offset equivalent to a unit curve's 0.4.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.7 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.896),
|
||||
"Custom free curve should return the expected value at offset equivalent to a unit curve's 0.7.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(1),
|
||||
"Custom free curve should return the expected value at offset equivalent to a unit curve's 1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(2 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(1),
|
||||
"Custom free curve should return the expected value at offset equivalent to a unit curve's 2.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
Math::is_zero_approx(curve->sample_baked(-200)),
|
||||
"Custom free curve should return the expected baked value at offset equivalent to a unit curve's -0.1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.352),
|
||||
"Custom free curve should return the expected baked value at offset equivalent to a unit curve's 0.1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.4 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.352),
|
||||
"Custom free curve should return the expected baked value at offset equivalent to a unit curve's 0.4.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.7 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.896),
|
||||
"Custom free curve should return the expected baked value at offset equivalent to a unit curve's 0.7.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(1),
|
||||
"Custom free curve should return the expected baked value at offset equivalent to a unit curve's 1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(2 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(1),
|
||||
"Custom free curve should return the expected baked value at offset equivalent to a unit curve's 2.");
|
||||
|
||||
curve->remove_point(1);
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(0),
|
||||
"Custom free curve should return the expected value at offset equivalent to a unit curve's 0.1 after removing point at index 1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(0),
|
||||
"Custom free curve should return the expected baked value at offset equivalent to a unit curve's 0.1 after removing point at index 1.");
|
||||
|
||||
curve->clear_points();
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.6 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(0),
|
||||
"Custom free curve should return the expected value at offset 0.6 after clearing all points.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.6 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(0),
|
||||
"Custom free curve should return the expected baked value at offset 0.6 after clearing all points.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Curve] Custom unit curve with linear tangents") {
|
||||
Ref<Curve> curve = memnew(Curve);
|
||||
// "Sawtooth" curve with an open ending towards the 1.0 offset.
|
||||
curve->add_point(Vector2(0, 0), 0, 0, Curve::TangentMode::TANGENT_LINEAR, Curve::TangentMode::TANGENT_LINEAR);
|
||||
|
|
@ -219,6 +302,91 @@ TEST_CASE("[Curve] Custom curve with linear tangents") {
|
|||
"Custom free curve should return the expected baked value at offset 0.7 after removing point at invalid index 10.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Curve] Custom non-unit curve with linear tangents") {
|
||||
Ref<Curve> curve = memnew(Curve);
|
||||
curve->set_min_domain(-100.0);
|
||||
curve->set_max_domain(100.0);
|
||||
// "Sawtooth" curve with an open ending towards the 100 offset.
|
||||
curve->add_point(Vector2(-100, 0), 0, 0, Curve::TangentMode::TANGENT_LINEAR, Curve::TangentMode::TANGENT_LINEAR);
|
||||
curve->add_point(Vector2(-50, 1), 0, 0, Curve::TangentMode::TANGENT_LINEAR, Curve::TangentMode::TANGENT_LINEAR);
|
||||
curve->add_point(Vector2(0, 0), 0, 0, Curve::TangentMode::TANGENT_LINEAR, Curve::TangentMode::TANGENT_LINEAR);
|
||||
curve->add_point(Vector2(50, 1), 0, 0, Curve::TangentMode::TANGENT_LINEAR, Curve::TangentMode::TANGENT_LINEAR);
|
||||
|
||||
CHECK_MESSAGE(
|
||||
curve->get_point_left_tangent(3) == doctest::Approx(1.f / 50),
|
||||
"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)),
|
||||
"get_point_right_tangent() should return the expected value for point index 3.");
|
||||
CHECK_MESSAGE(
|
||||
curve->get_point_left_mode(3) == Curve::TangentMode::TANGENT_LINEAR,
|
||||
"get_point_left_mode() should return the expected value for point index 3.");
|
||||
CHECK_MESSAGE(
|
||||
curve->get_point_right_mode(3) == Curve::TangentMode::TANGENT_LINEAR,
|
||||
"get_point_right_mode() should return the expected value for point index 3.");
|
||||
|
||||
ERR_PRINT_OFF;
|
||||
CHECK_MESSAGE(
|
||||
Math::is_zero_approx(curve->get_point_right_tangent(300)),
|
||||
"get_point_right_tangent() should return the expected value for invalid point index 300.");
|
||||
CHECK_MESSAGE(
|
||||
curve->get_point_left_mode(-12345) == Curve::TangentMode::TANGENT_FREE,
|
||||
"get_point_left_mode() should return the expected value for invalid point index -12345.");
|
||||
ERR_PRINT_ON;
|
||||
|
||||
CHECK_MESSAGE(
|
||||
curve->get_point_count() == 4,
|
||||
"Custom linear unit curve should contain the expected number of points.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
Math::is_zero_approx(curve->sample(-0.1 * curve->get_domain_range() + curve->get_min_domain())),
|
||||
"Custom linear curve should return the expected value at offset equivalent to a unit curve's -0.1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.4),
|
||||
"Custom linear curve should return the expected value at offset equivalent to a unit curve's 0.1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.4 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.4),
|
||||
"Custom linear curve should return the expected value at offset equivalent to a unit curve's 0.4.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.7 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.8),
|
||||
"Custom linear curve should return the expected value at offset equivalent to a unit curve's 0.7.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(1),
|
||||
"Custom linear curve should return the expected value at offset equivalent to a unit curve's 1.0.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(2 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(1),
|
||||
"Custom linear curve should return the expected value at offset equivalent to a unit curve's 2.0.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
Math::is_zero_approx(curve->sample_baked(-0.1 * curve->get_domain_range() + curve->get_min_domain())),
|
||||
"Custom linear curve should return the expected baked value at offset equivalent to a unit curve's -0.1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.4),
|
||||
"Custom linear curve should return the expected baked value at offset equivalent to a unit curve's 0.1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.4 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.4),
|
||||
"Custom linear curve should return the expected baked value at offset equivalent to a unit curve's 0.4.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.7 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.8),
|
||||
"Custom linear curve should return the expected baked value at offset equivalent to a unit curve's 0.7.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(1),
|
||||
"Custom linear curve should return the expected baked value at offset equivalent to a unit curve's 1.0.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(2 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(1),
|
||||
"Custom linear curve should return the expected baked value at offset equivalent to a unit curve's 2.0.");
|
||||
|
||||
ERR_PRINT_OFF;
|
||||
curve->remove_point(10);
|
||||
ERR_PRINT_ON;
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.7 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.8),
|
||||
"Custom free curve should return the expected value at offset equivalent to a unit curve's 0.7 after removing point at invalid index 10.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.7 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.8),
|
||||
"Custom free curve should return the expected baked value at offset equivalent to a unit curve's 0.7 after removing point at invalid index 10.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Curve] Straight line offset test") {
|
||||
Ref<Curve> curve = memnew(Curve);
|
||||
curve->add_point(Vector2(0, 0));
|
||||
|
|
|
|||
82
engine/tests/scene/test_fontfile.h
Normal file
82
engine/tests/scene/test_fontfile.h
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/**************************************************************************/
|
||||
/* test_fontfile.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_FONTFILE_H
|
||||
#define TEST_FONTFILE_H
|
||||
|
||||
#include "modules/modules_enabled.gen.h"
|
||||
|
||||
#include "scene/resources/font.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestFontfile {
|
||||
|
||||
TEST_CASE("[FontFile] Create font file and check data") {
|
||||
// Create test instance.
|
||||
Ref<FontFile> font_file;
|
||||
font_file.instantiate();
|
||||
|
||||
#ifdef MODULE_FREETYPE_ENABLED
|
||||
// Try to load non-existent files.
|
||||
ERR_PRINT_OFF
|
||||
CHECK(font_file->load_dynamic_font("") == OK);
|
||||
CHECK_MESSAGE(font_file->get_data().is_empty() == true, "Invalid fontfile should not be loaded.");
|
||||
|
||||
CHECK(font_file->load_dynamic_font("thirdparty/fonts/nofonthasthisname.woff2") == OK);
|
||||
CHECK_MESSAGE(font_file->get_data().is_empty() == true, "Invalid fontfile should not be loaded.");
|
||||
ERR_PRINT_ON
|
||||
|
||||
// Load a valid file.
|
||||
CHECK(font_file->load_dynamic_font("thirdparty/fonts/NotoSans_Regular.woff2") == OK);
|
||||
|
||||
// Check fontfile data.
|
||||
CHECK_MESSAGE(font_file->get_data().is_empty() == false, "Fontfile should have been loaded.");
|
||||
CHECK_MESSAGE(font_file->get_font_name() == "Noto Sans", "Loaded correct font name.");
|
||||
CHECK_MESSAGE(font_file->get_font_style_name() == "Regular", "Loaded correct font style.");
|
||||
CHECK_MESSAGE(font_file->get_data().size() == 148480llu, "Whole fontfile was loaded.");
|
||||
|
||||
// Valid glyphs.
|
||||
CHECK_MESSAGE(font_file->get_glyph_index(2, 'a', 0) != 0, "Glyph index for 'a' is valid.");
|
||||
CHECK_MESSAGE(font_file->get_glyph_index(2, 'b', 0) != 0, "Glyph index for 'b' is valid.");
|
||||
CHECK_MESSAGE(font_file->get_glyph_index(2, 0x0103, 0) != 0, "Glyph index for 'latin small letter a with breve' is valid.");
|
||||
CHECK_MESSAGE(font_file->get_glyph_index(2, 0x03a8, 0) != 0, "Glyph index for 'Greek psi' is valid.");
|
||||
CHECK_MESSAGE(font_file->get_glyph_index(2, 0x0416, 0) != 0, "Glyph index for 'Cyrillic zhe' is valid.");
|
||||
CHECK_MESSAGE(font_file->get_glyph_index(2, '&', 0) != 0, "Glyph index for '&' is valid.");
|
||||
|
||||
// Invalid glyphs.
|
||||
CHECK_MESSAGE(font_file->get_glyph_index(2, 0x4416, 0) == 0, "Glyph index is invalid.");
|
||||
CHECK_MESSAGE(font_file->get_glyph_index(2, 0x5555, 0) == 0, "Glyph index is invalid.");
|
||||
CHECK_MESSAGE(font_file->get_glyph_index(2, 0x2901, 0) == 0, "Glyph index is invalid.");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace TestFontfile
|
||||
|
||||
#endif // TEST_FONTFILE_H
|
||||
250
engine/tests/scene/test_gltf_document.h
Normal file
250
engine/tests/scene/test_gltf_document.h
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
/**************************************************************************/
|
||||
/* test_gltf_document.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_GLTF_DOCUMENT_H
|
||||
#define TEST_GLTF_DOCUMENT_H
|
||||
|
||||
#include "modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h"
|
||||
#include "modules/gltf/gltf_document.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
#include "tests/test_utils.h"
|
||||
|
||||
namespace TestGLTFDocument {
|
||||
|
||||
struct GLTFArraySize {
|
||||
String key;
|
||||
int val;
|
||||
};
|
||||
|
||||
struct GLTFKeyValue {
|
||||
String key;
|
||||
Variant val;
|
||||
};
|
||||
|
||||
struct GLTFTestCase {
|
||||
String filename;
|
||||
String copyright;
|
||||
String generator;
|
||||
String version;
|
||||
Vector<GLTFArraySize> array_sizes;
|
||||
Vector<GLTFArraySize> json_array_sizes;
|
||||
Vector<GLTFKeyValue> keyvalues;
|
||||
};
|
||||
|
||||
const GLTFTestCase glTF_test_cases[] = {
|
||||
{ "models/cube.gltf",
|
||||
"",
|
||||
"Khronos glTF Blender I/O v4.3.47",
|
||||
"2.0",
|
||||
// Here are the array sizes.
|
||||
{
|
||||
{ "nodes", 1 },
|
||||
{ "buffers", 1 },
|
||||
{ "buffer_views", 13 },
|
||||
{ "accessors", 13 },
|
||||
{ "meshes", 1 },
|
||||
{ "materials", 2 },
|
||||
{ "root_nodes", 1 },
|
||||
{ "textures", 0 },
|
||||
{ "texture_samplers", 0 },
|
||||
{ "images", 0 },
|
||||
{ "skins", 0 },
|
||||
{ "cameras", 0 },
|
||||
{ "lights", 0 },
|
||||
{ "skeletons", 0 },
|
||||
{ "animations", 1 },
|
||||
},
|
||||
// Here are the json array sizes.
|
||||
{
|
||||
{ "scenes", 1 },
|
||||
{ "nodes", 1 },
|
||||
{ "animations", 1 },
|
||||
{ "meshes", 1 },
|
||||
{ "accessors", 13 },
|
||||
{ "bufferViews", 13 },
|
||||
{ "buffers", 1 },
|
||||
},
|
||||
// Here are the key-value pairs.
|
||||
{
|
||||
{ "major_version", 2 },
|
||||
{ "minor_version", 0 },
|
||||
{ "scene_name", "cube" },
|
||||
{ "filename", "cube" } } },
|
||||
{ "models/suzanne.glb",
|
||||
"this is example text",
|
||||
"Khronos glTF Blender I/O v4.3.47",
|
||||
"2.0",
|
||||
// Here are the array sizes.
|
||||
{
|
||||
{ "glb_data", 68908 },
|
||||
{ "nodes", 2 },
|
||||
{ "buffers", 1 },
|
||||
{ "buffer_views", 5 },
|
||||
{ "accessors", 4 },
|
||||
{ "meshes", 1 },
|
||||
{ "materials", 1 },
|
||||
{ "root_nodes", 2 },
|
||||
{ "textures", 1 },
|
||||
{ "texture_samplers", 1 },
|
||||
{ "images", 1 },
|
||||
{ "skins", 0 },
|
||||
{ "cameras", 1 },
|
||||
{ "lights", 0 },
|
||||
{ "unique_names", 4 },
|
||||
{ "skeletons", 0 },
|
||||
{ "animations", 0 },
|
||||
},
|
||||
// Here are the json array sizes.
|
||||
{
|
||||
{ "scenes", 1 },
|
||||
{ "nodes", 2 },
|
||||
{ "cameras", 1 },
|
||||
{ "materials", 1 },
|
||||
{ "meshes", 1 },
|
||||
{ "textures", 1 },
|
||||
{ "images", 1 },
|
||||
{ "accessors", 4 },
|
||||
{ "bufferViews", 5 },
|
||||
{ "buffers", 1 },
|
||||
},
|
||||
// Here are the key-value pairs.
|
||||
{
|
||||
{ "major_version", 2 },
|
||||
{ "minor_version", 0 },
|
||||
{ "scene_name", "suzanne" },
|
||||
{ "filename", "suzanne" } } },
|
||||
};
|
||||
|
||||
void register_gltf_extension() {
|
||||
GLTFDocument::unregister_all_gltf_document_extensions();
|
||||
|
||||
// Ensures meshes become a MeshInstance3D and not an ImporterMeshInstance3D.
|
||||
Ref<GLTFDocumentExtensionConvertImporterMesh> extension_GLTFDocumentExtensionConvertImporterMesh;
|
||||
extension_GLTFDocumentExtensionConvertImporterMesh.instantiate();
|
||||
GLTFDocument::register_gltf_document_extension(extension_GLTFDocumentExtensionConvertImporterMesh);
|
||||
}
|
||||
|
||||
void test_gltf_document_values(Ref<GLTFDocument> &p_gltf_document, Ref<GLTFState> &p_gltf_state, const GLTFTestCase &p_test_case) {
|
||||
const Error err = p_gltf_document->append_from_file(TestUtils::get_data_path(p_test_case.filename), p_gltf_state);
|
||||
REQUIRE(err == OK);
|
||||
|
||||
for (GLTFArraySize array_size : p_test_case.array_sizes) {
|
||||
CHECK_MESSAGE(((Array)(p_gltf_state->getvar(array_size.key))).size() == array_size.val, "Expected \"", array_size.key, "\" to have ", array_size.val, " elements.");
|
||||
}
|
||||
|
||||
for (GLTFArraySize array_size : p_test_case.json_array_sizes) {
|
||||
CHECK(p_gltf_state->get_json().has(array_size.key));
|
||||
CHECK_MESSAGE(((Array)(p_gltf_state->get_json()[array_size.key])).size() == array_size.val, "Expected \"", array_size.key, "\" to have ", array_size.val, " elements.");
|
||||
}
|
||||
|
||||
for (GLTFKeyValue key_value : p_test_case.keyvalues) {
|
||||
CHECK_MESSAGE(p_gltf_state->getvar(key_value.key) == key_value.val, "Expected \"", key_value.key, "\" to be \"", key_value.val, "\".");
|
||||
}
|
||||
|
||||
CHECK(p_gltf_state->get_copyright() == p_test_case.copyright);
|
||||
CHECK(((Dictionary)p_gltf_state->get_json()["asset"])["generator"] == p_test_case.generator);
|
||||
CHECK(((Dictionary)p_gltf_state->get_json()["asset"])["version"] == p_test_case.version);
|
||||
}
|
||||
|
||||
void test_gltf_save(Node *p_node) {
|
||||
Ref<GLTFDocument> gltf_document_save;
|
||||
gltf_document_save.instantiate();
|
||||
Ref<GLTFState> gltf_state_save;
|
||||
gltf_state_save.instantiate();
|
||||
|
||||
gltf_document_save->append_from_scene(p_node, gltf_state_save);
|
||||
|
||||
// Check saving the scene to gltf and glb.
|
||||
const Error err_save_gltf = gltf_document_save->write_to_filesystem(gltf_state_save, TestUtils::get_temp_path("cube.gltf"));
|
||||
const Error err_save_glb = gltf_document_save->write_to_filesystem(gltf_state_save, TestUtils::get_temp_path("cube.glb"));
|
||||
CHECK(err_save_gltf == OK);
|
||||
CHECK(err_save_glb == OK);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][GLTFDocument] Load cube.gltf") {
|
||||
register_gltf_extension();
|
||||
|
||||
Ref<GLTFDocument> gltf_document;
|
||||
gltf_document.instantiate();
|
||||
Ref<GLTFState> gltf_state;
|
||||
gltf_state.instantiate();
|
||||
|
||||
test_gltf_document_values(gltf_document, gltf_state, glTF_test_cases[0]);
|
||||
|
||||
Node *node = gltf_document->generate_scene(gltf_state);
|
||||
|
||||
// Check the loaded scene.
|
||||
CHECK(node->is_class("Node3D"));
|
||||
CHECK(node->get_name() == "cube");
|
||||
|
||||
CHECK(node->get_child(0)->is_class("MeshInstance3D"));
|
||||
CHECK(node->get_child(0)->get_name() == "Cube");
|
||||
|
||||
CHECK(node->get_child(1)->is_class("AnimationPlayer"));
|
||||
CHECK(node->get_child(1)->get_name() == "AnimationPlayer");
|
||||
|
||||
test_gltf_save(node);
|
||||
|
||||
// Clean up the node.
|
||||
memdelete(node);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][GLTFDocument] Load suzanne.glb") {
|
||||
register_gltf_extension();
|
||||
|
||||
Ref<GLTFDocument> gltf_document;
|
||||
gltf_document.instantiate();
|
||||
Ref<GLTFState> gltf_state;
|
||||
gltf_state.instantiate();
|
||||
|
||||
test_gltf_document_values(gltf_document, gltf_state, glTF_test_cases[1]);
|
||||
|
||||
Node *node = gltf_document->generate_scene(gltf_state);
|
||||
|
||||
// Check the loaded scene.
|
||||
CHECK(node->is_class("Node3D"));
|
||||
CHECK(node->get_name() == "suzanne");
|
||||
|
||||
CHECK(node->get_child(0)->is_class("MeshInstance3D"));
|
||||
CHECK(node->get_child(0)->get_name() == "Suzanne");
|
||||
|
||||
CHECK(node->get_child(1)->is_class("Camera3D"));
|
||||
CHECK(node->get_child(1)->get_name() == "Camera");
|
||||
|
||||
test_gltf_save(node);
|
||||
|
||||
// Clean up the node.
|
||||
memdelete(node);
|
||||
}
|
||||
|
||||
} // namespace TestGLTFDocument
|
||||
|
||||
#endif // TEST_GLTF_DOCUMENT_H
|
||||
|
|
@ -70,11 +70,11 @@ TEST_CASE("[Gradient] Default gradient") {
|
|||
TEST_CASE("[Gradient] Custom gradient (points specified in order)") {
|
||||
// Red-yellow-green gradient (with overbright green).
|
||||
Ref<Gradient> gradient = memnew(Gradient);
|
||||
Vector<Gradient::Point> points;
|
||||
points.push_back({ 0.0, Color(1, 0, 0) });
|
||||
points.push_back({ 0.5, Color(1, 1, 0) });
|
||||
points.push_back({ 1.0, Color(0, 2, 0) });
|
||||
gradient->set_points(points);
|
||||
Vector<float> offsets = { 0.0, 0.5, 1.0 };
|
||||
Vector<Color> colors = { Color(1, 0, 0), Color(1, 1, 0), Color(0, 2, 0) };
|
||||
|
||||
gradient->set_offsets(offsets);
|
||||
gradient->set_colors(colors);
|
||||
|
||||
CHECK_MESSAGE(
|
||||
gradient->get_point_count() == 3,
|
||||
|
|
@ -109,14 +109,12 @@ TEST_CASE("[Gradient] Custom gradient (points specified out-of-order)") {
|
|||
// HSL rainbow with points specified out of order.
|
||||
// These should be sorted automatically when adding points.
|
||||
Ref<Gradient> gradient = memnew(Gradient);
|
||||
Vector<Gradient::Point> points;
|
||||
points.push_back({ 0.2, Color(1, 0, 0) });
|
||||
points.push_back({ 0.0, Color(1, 1, 0) });
|
||||
points.push_back({ 0.8, Color(0, 1, 0) });
|
||||
points.push_back({ 0.4, Color(0, 1, 1) });
|
||||
points.push_back({ 1.0, Color(0, 0, 1) });
|
||||
points.push_back({ 0.6, Color(1, 0, 1) });
|
||||
gradient->set_points(points);
|
||||
LocalVector<Gradient::Point> points;
|
||||
Vector<float> offsets = { 0.2, 0.0, 0.8, 0.4, 1.0, 0.6 };
|
||||
Vector<Color> colors = { Color(1, 0, 0), Color(1, 1, 0), Color(0, 1, 0), Color(0, 1, 1), Color(0, 0, 1), Color(1, 0, 1) };
|
||||
|
||||
gradient->set_offsets(offsets);
|
||||
gradient->set_colors(colors);
|
||||
|
||||
CHECK_MESSAGE(
|
||||
gradient->get_point_count() == 6,
|
||||
|
|
|
|||
87
engine/tests/scene/test_gradient_texture.h
Normal file
87
engine/tests/scene/test_gradient_texture.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/**************************************************************************/
|
||||
/* test_gradient_texture.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TEST_GRADIENT_TEXTURE_H
|
||||
#define TEST_GRADIENT_TEXTURE_H
|
||||
|
||||
#include "scene/resources/gradient_texture.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestGradientTexture {
|
||||
|
||||
// [SceneTree] in a test case name enables initializing a mock render server,
|
||||
// which ImageTexture is dependent on.
|
||||
TEST_CASE("[SceneTree][GradientTexture1D] Create GradientTexture1D") {
|
||||
Ref<GradientTexture1D> gradient_texture = memnew(GradientTexture1D);
|
||||
|
||||
Ref<Gradient> test_gradient = memnew(Gradient);
|
||||
gradient_texture->set_gradient(test_gradient);
|
||||
CHECK(gradient_texture->get_gradient() == test_gradient);
|
||||
|
||||
gradient_texture->set_width(83);
|
||||
CHECK(gradient_texture->get_width() == 83);
|
||||
|
||||
gradient_texture->set_use_hdr(true);
|
||||
CHECK(gradient_texture->is_using_hdr());
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][GradientTexture2D] Create GradientTexture2D") {
|
||||
Ref<GradientTexture2D> gradient_texture = memnew(GradientTexture2D);
|
||||
|
||||
Ref<Gradient> test_gradient = memnew(Gradient);
|
||||
gradient_texture->set_gradient(test_gradient);
|
||||
CHECK(gradient_texture->get_gradient() == test_gradient);
|
||||
|
||||
gradient_texture->set_width(82);
|
||||
CHECK(gradient_texture->get_width() == 82);
|
||||
|
||||
gradient_texture->set_height(81);
|
||||
CHECK(gradient_texture->get_height() == 81);
|
||||
|
||||
gradient_texture->set_use_hdr(true);
|
||||
CHECK(gradient_texture->is_using_hdr());
|
||||
|
||||
gradient_texture->set_fill(GradientTexture2D::Fill::FILL_SQUARE);
|
||||
CHECK(gradient_texture->get_fill() == GradientTexture2D::Fill::FILL_SQUARE);
|
||||
|
||||
gradient_texture->set_fill_from(Vector2(0.2, 0.25));
|
||||
CHECK(gradient_texture->get_fill_from() == Vector2(0.2, 0.25));
|
||||
|
||||
gradient_texture->set_fill_to(Vector2(0.35, 0.5));
|
||||
CHECK(gradient_texture->get_fill_to() == Vector2(0.35, 0.5));
|
||||
|
||||
gradient_texture->set_repeat(GradientTexture2D::Repeat::REPEAT);
|
||||
CHECK(gradient_texture->get_repeat() == GradientTexture2D::Repeat::REPEAT);
|
||||
}
|
||||
|
||||
} //namespace TestGradientTexture
|
||||
|
||||
#endif // TEST_GRADIENT_TEXTURE_H
|
||||
122
engine/tests/scene/test_height_map_shape_3d.h
Normal file
122
engine/tests/scene/test_height_map_shape_3d.h
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/**************************************************************************/
|
||||
/* test_height_map_shape_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_HEIGHT_MAP_SHAPE_3D_H
|
||||
#define TEST_HEIGHT_MAP_SHAPE_3D_H
|
||||
|
||||
#include "scene/resources/3d/height_map_shape_3d.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
#include "tests/test_utils.h"
|
||||
|
||||
namespace TestHeightMapShape3D {
|
||||
|
||||
TEST_CASE("[SceneTree][HeightMapShape3D] Constructor") {
|
||||
Ref<HeightMapShape3D> height_map_shape = memnew(HeightMapShape3D);
|
||||
CHECK(height_map_shape->get_map_width() == 2);
|
||||
CHECK(height_map_shape->get_map_depth() == 2);
|
||||
CHECK(height_map_shape->get_map_data().size() == 4);
|
||||
CHECK(height_map_shape->get_min_height() == 0.0);
|
||||
CHECK(height_map_shape->get_max_height() == 0.0);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][HeightMapShape3D] set_map_width and get_map_width") {
|
||||
Ref<HeightMapShape3D> height_map_shape = memnew(HeightMapShape3D);
|
||||
height_map_shape->set_map_width(10);
|
||||
CHECK(height_map_shape->get_map_width() == 10);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][HeightMapShape3D] set_map_depth and get_map_depth") {
|
||||
Ref<HeightMapShape3D> height_map_shape = memnew(HeightMapShape3D);
|
||||
height_map_shape->set_map_depth(15);
|
||||
CHECK(height_map_shape->get_map_depth() == 15);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][HeightMapShape3D] set_map_data and get_map_data") {
|
||||
Ref<HeightMapShape3D> height_map_shape = memnew(HeightMapShape3D);
|
||||
Vector<real_t> map_data;
|
||||
map_data.push_back(1.0);
|
||||
map_data.push_back(2.0);
|
||||
height_map_shape->set_map_data(map_data);
|
||||
CHECK(height_map_shape->get_map_data().size() == 4.0);
|
||||
CHECK(height_map_shape->get_map_data()[0] == 0.0);
|
||||
CHECK(height_map_shape->get_map_data()[1] == 0.0);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][HeightMapShape3D] get_min_height") {
|
||||
Ref<HeightMapShape3D> height_map_shape = memnew(HeightMapShape3D);
|
||||
height_map_shape->set_map_width(3);
|
||||
height_map_shape->set_map_depth(1);
|
||||
height_map_shape->set_map_data(Vector<real_t>{ 1.0, 2.0, 0.5 });
|
||||
CHECK(height_map_shape->get_min_height() == 0.5);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][HeightMapShape3D] get_max_height") {
|
||||
Ref<HeightMapShape3D> height_map_shape = memnew(HeightMapShape3D);
|
||||
height_map_shape->set_map_width(3);
|
||||
height_map_shape->set_map_depth(1);
|
||||
height_map_shape->set_map_data(Vector<real_t>{ 1.0, 2.0, 0.5 });
|
||||
CHECK(height_map_shape->get_max_height() == 2.0);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][HeightMapShape3D] update_map_data_from_image") {
|
||||
// Create a HeightMapShape3D instance.
|
||||
Ref<HeightMapShape3D> height_map_shape = memnew(HeightMapShape3D);
|
||||
|
||||
// Create a mock image with FORMAT_R8 and set its data.
|
||||
Vector<uint8_t> image_data;
|
||||
image_data.push_back(0);
|
||||
image_data.push_back(128);
|
||||
image_data.push_back(255);
|
||||
image_data.push_back(64);
|
||||
|
||||
Ref<Image> image = memnew(Image);
|
||||
image->set_data(2, 2, false, Image::FORMAT_R8, image_data);
|
||||
|
||||
height_map_shape->update_map_data_from_image(image, 0.0, 10.0);
|
||||
|
||||
// Check the map data.
|
||||
Vector<real_t> expected_map_data = { 0.0, 5.0, 10.0, 2.5 };
|
||||
Vector<real_t> actual_map_data = height_map_shape->get_map_data();
|
||||
real_t tolerance = 0.1;
|
||||
|
||||
for (int i = 0; i < expected_map_data.size(); ++i) {
|
||||
CHECK(Math::abs(actual_map_data[i] - expected_map_data[i]) < tolerance);
|
||||
}
|
||||
|
||||
// Check the min and max heights.
|
||||
CHECK(height_map_shape->get_min_height() == 0.0);
|
||||
CHECK(height_map_shape->get_max_height() == 10.0);
|
||||
}
|
||||
|
||||
} // namespace TestHeightMapShape3D
|
||||
|
||||
#endif // TEST_HEIGHT_MAP_SHAPE_3D_H
|
||||
|
|
@ -86,6 +86,131 @@ TEST_CASE("[SceneTree][Node2D]") {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Node2D] Utility methods") {
|
||||
Node2D *test_node1 = memnew(Node2D);
|
||||
Node2D *test_node2 = memnew(Node2D);
|
||||
Node2D *test_node3 = memnew(Node2D);
|
||||
Node2D *test_sibling = memnew(Node2D);
|
||||
SceneTree::get_singleton()->get_root()->add_child(test_node1);
|
||||
|
||||
test_node1->set_position(Point2(100, 100));
|
||||
test_node1->set_rotation(Math::deg_to_rad(30.0));
|
||||
test_node1->set_scale(Size2(1, 1));
|
||||
test_node1->add_child(test_node2);
|
||||
|
||||
test_node2->set_position(Point2(10, 0));
|
||||
test_node2->set_rotation(Math::deg_to_rad(60.0));
|
||||
test_node2->set_scale(Size2(1, 1));
|
||||
test_node2->add_child(test_node3);
|
||||
test_node2->add_child(test_sibling);
|
||||
|
||||
test_node3->set_position(Point2(0, 10));
|
||||
test_node3->set_rotation(Math::deg_to_rad(0.0));
|
||||
test_node3->set_scale(Size2(2, 2));
|
||||
|
||||
test_sibling->set_position(Point2(5, 10));
|
||||
test_sibling->set_rotation(Math::deg_to_rad(90.0));
|
||||
test_sibling->set_scale(Size2(2, 1));
|
||||
|
||||
SUBCASE("[Node2D] look_at") {
|
||||
test_node3->look_at(Vector2(1, 1));
|
||||
|
||||
CHECK(test_node3->get_global_position().is_equal_approx(Point2(98.66026, 105)));
|
||||
CHECK(Math::is_equal_approx(test_node3->get_global_rotation(), real_t(-2.32477)));
|
||||
CHECK(test_node3->get_global_scale().is_equal_approx(Vector2(2, 2)));
|
||||
|
||||
CHECK(test_node3->get_position().is_equal_approx(Vector2(0, 10)));
|
||||
CHECK(Math::is_equal_approx(test_node3->get_rotation(), real_t(2.38762)));
|
||||
CHECK(test_node3->get_scale().is_equal_approx(Vector2(2, 2)));
|
||||
|
||||
test_node3->look_at(Vector2(0, 10));
|
||||
|
||||
CHECK(test_node3->get_global_position().is_equal_approx(Vector2(98.66026, 105)));
|
||||
CHECK(Math::is_equal_approx(test_node3->get_global_rotation(), real_t(-2.37509)));
|
||||
CHECK(test_node3->get_global_scale().is_equal_approx(Vector2(2, 2)));
|
||||
|
||||
CHECK(test_node3->get_position().is_equal_approx(Vector2(0, 10)));
|
||||
CHECK(Math::is_equal_approx(test_node3->get_rotation(), real_t(2.3373)));
|
||||
CHECK(test_node3->get_scale().is_equal_approx(Vector2(2, 2)));
|
||||
|
||||
// Don't do anything if look_at own position.
|
||||
test_node3->look_at(test_node3->get_global_position());
|
||||
|
||||
CHECK(test_node3->get_global_position().is_equal_approx(Vector2(98.66026, 105)));
|
||||
CHECK(Math::is_equal_approx(test_node3->get_global_rotation(), real_t(-2.37509)));
|
||||
CHECK(test_node3->get_global_scale().is_equal_approx(Vector2(2, 2)));
|
||||
|
||||
CHECK(test_node3->get_position().is_equal_approx(Vector2(0, 10)));
|
||||
CHECK(Math::is_equal_approx(test_node3->get_rotation(), real_t(2.3373)));
|
||||
CHECK(test_node3->get_scale().is_equal_approx(Vector2(2, 2)));
|
||||
|
||||
// Revert any rotation caused by look_at, must run after look_at tests
|
||||
test_node3->set_rotation(Math::deg_to_rad(0.0));
|
||||
}
|
||||
|
||||
SUBCASE("[Node2D] get_angle_to") {
|
||||
CHECK(Math::is_equal_approx(test_node3->get_angle_to(Vector2(1, 1)), real_t(2.38762)));
|
||||
CHECK(Math::is_equal_approx(test_node3->get_angle_to(Vector2(0, 10)), real_t(2.3373)));
|
||||
CHECK(Math::is_equal_approx(test_node3->get_angle_to(Vector2(2, -5)), real_t(2.42065)));
|
||||
CHECK(Math::is_equal_approx(test_node3->get_angle_to(Vector2(-2, 5)), real_t(2.3529)));
|
||||
|
||||
// Return 0 when get_angle_to own position.
|
||||
CHECK(Math::is_equal_approx(test_node3->get_angle_to(test_node3->get_global_position()), real_t(0)));
|
||||
}
|
||||
|
||||
SUBCASE("[Node2D] to_local") {
|
||||
Point2 node3_local = test_node3->to_local(Point2(1, 2));
|
||||
CHECK(node3_local.is_equal_approx(Point2(-51.5, 48.83013)));
|
||||
|
||||
node3_local = test_node3->to_local(Point2(-2, 1));
|
||||
CHECK(node3_local.is_equal_approx(Point2(-52, 50.33013)));
|
||||
|
||||
node3_local = test_node3->to_local(Point2(0, 0));
|
||||
CHECK(node3_local.is_equal_approx(Point2(-52.5, 49.33013)));
|
||||
|
||||
node3_local = test_node3->to_local(test_node3->get_global_position());
|
||||
CHECK(node3_local.is_equal_approx(Point2(0, 0)));
|
||||
}
|
||||
|
||||
SUBCASE("[Node2D] to_global") {
|
||||
Point2 node3_global = test_node3->to_global(Point2(1, 2));
|
||||
CHECK(node3_global.is_equal_approx(Point2(94.66026, 107)));
|
||||
|
||||
node3_global = test_node3->to_global(Point2(-2, 1));
|
||||
CHECK(node3_global.is_equal_approx(Point2(96.66026, 101)));
|
||||
|
||||
node3_global = test_node3->to_global(Point2(0, 0));
|
||||
CHECK(node3_global.is_equal_approx(test_node3->get_global_position()));
|
||||
}
|
||||
|
||||
SUBCASE("[Node2D] get_relative_transform_to_parent") {
|
||||
Transform2D relative_xform = test_node3->get_relative_transform_to_parent(test_node3);
|
||||
CHECK(relative_xform.is_equal_approx(Transform2D()));
|
||||
|
||||
relative_xform = test_node3->get_relative_transform_to_parent(test_node2);
|
||||
CHECK(relative_xform.get_origin().is_equal_approx(Vector2(0, 10)));
|
||||
CHECK(Math::is_equal_approx(relative_xform.get_rotation(), real_t(0)));
|
||||
CHECK(relative_xform.get_scale().is_equal_approx(Vector2(2, 2)));
|
||||
|
||||
relative_xform = test_node3->get_relative_transform_to_parent(test_node1);
|
||||
CHECK(relative_xform.get_origin().is_equal_approx(Vector2(1.339746, 5)));
|
||||
CHECK(Math::is_equal_approx(relative_xform.get_rotation(), real_t(1.0472)));
|
||||
CHECK(relative_xform.get_scale().is_equal_approx(Vector2(2, 2)));
|
||||
|
||||
ERR_PRINT_OFF;
|
||||
// In case of a sibling all transforms until the root are accumulated.
|
||||
Transform2D xform = test_node3->get_relative_transform_to_parent(test_sibling);
|
||||
Transform2D return_xform = test_node1->get_global_transform().inverse() * test_node3->get_global_transform();
|
||||
CHECK(xform.is_equal_approx(return_xform));
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
memdelete(test_sibling);
|
||||
memdelete(test_node3);
|
||||
memdelete(test_node2);
|
||||
memdelete(test_node1);
|
||||
}
|
||||
|
||||
} // namespace TestNode2D
|
||||
|
||||
#endif // TEST_NODE_2D_H
|
||||
|
|
|
|||
136
engine/tests/scene/test_option_button.h
Normal file
136
engine/tests/scene/test_option_button.h
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/**************************************************************************/
|
||||
/* test_option_button.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_OPTION_BUTTON_H
|
||||
#define TEST_OPTION_BUTTON_H
|
||||
|
||||
#include "scene/gui/option_button.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestOptionButton {
|
||||
|
||||
TEST_CASE("[SceneTree][OptionButton] Initialization") {
|
||||
OptionButton *test_opt = memnew(OptionButton);
|
||||
|
||||
SUBCASE("There should be no options right after initialization") {
|
||||
CHECK_FALSE(test_opt->has_selectable_items());
|
||||
CHECK(test_opt->get_item_count() == 0);
|
||||
}
|
||||
|
||||
memdelete(test_opt);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][OptionButton] Single item") {
|
||||
OptionButton *test_opt = memnew(OptionButton);
|
||||
|
||||
SUBCASE("There should a single item after after adding one") {
|
||||
test_opt->add_item("single", 1013);
|
||||
|
||||
CHECK(test_opt->has_selectable_items());
|
||||
CHECK(test_opt->get_item_count() == 1);
|
||||
CHECK(test_opt->get_item_index(1013) == 0);
|
||||
CHECK(test_opt->get_item_id(0) == 1013);
|
||||
|
||||
test_opt->remove_item(0);
|
||||
|
||||
CHECK_FALSE(test_opt->has_selectable_items());
|
||||
CHECK(test_opt->get_item_count() == 0);
|
||||
}
|
||||
|
||||
SUBCASE("There should a single item after after adding an icon") {
|
||||
Ref<Texture2D> test_icon = memnew(Texture2D);
|
||||
test_opt->add_icon_item(test_icon, "icon", 345);
|
||||
|
||||
CHECK(test_opt->has_selectable_items());
|
||||
CHECK(test_opt->get_item_count() == 1);
|
||||
CHECK(test_opt->get_item_index(345) == 0);
|
||||
CHECK(test_opt->get_item_id(0) == 345);
|
||||
|
||||
test_opt->remove_item(0);
|
||||
|
||||
CHECK_FALSE(test_opt->has_selectable_items());
|
||||
CHECK(test_opt->get_item_count() == 0);
|
||||
}
|
||||
|
||||
memdelete(test_opt);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][OptionButton] Complex structure") {
|
||||
OptionButton *test_opt = memnew(OptionButton);
|
||||
|
||||
SUBCASE("Creating a complex structure and checking getters") {
|
||||
// Regular item at index 0.
|
||||
Ref<Texture2D> test_icon1 = memnew(Texture2D);
|
||||
Ref<Texture2D> test_icon2 = memnew(Texture2D);
|
||||
// Regular item at index 3.
|
||||
Ref<Texture2D> test_icon4 = memnew(Texture2D);
|
||||
|
||||
test_opt->add_item("first", 100);
|
||||
test_opt->add_icon_item(test_icon1, "second_icon", 101);
|
||||
test_opt->add_icon_item(test_icon2, "third_icon", 102);
|
||||
test_opt->add_item("fourth", 104);
|
||||
test_opt->add_icon_item(test_icon4, "fifth_icon", 104);
|
||||
|
||||
// Disable test_icon4.
|
||||
test_opt->set_item_disabled(4, true);
|
||||
|
||||
CHECK(test_opt->has_selectable_items());
|
||||
CHECK(test_opt->get_item_count() == 5);
|
||||
|
||||
// Check for test_icon2.
|
||||
CHECK(test_opt->get_item_index(102) == 2);
|
||||
CHECK(test_opt->get_item_id(2) == 102);
|
||||
|
||||
// Remove the two regular items.
|
||||
test_opt->remove_item(3);
|
||||
test_opt->remove_item(0);
|
||||
|
||||
CHECK(test_opt->has_selectable_items());
|
||||
CHECK(test_opt->get_item_count() == 3);
|
||||
|
||||
// Check test_icon4.
|
||||
CHECK(test_opt->get_item_index(104) == 2);
|
||||
CHECK(test_opt->get_item_id(2) == 104);
|
||||
|
||||
// Remove the two non-disabled icon items.
|
||||
test_opt->remove_item(1);
|
||||
test_opt->remove_item(0);
|
||||
|
||||
CHECK_FALSE(test_opt->has_selectable_items());
|
||||
CHECK(test_opt->get_item_count() == 1);
|
||||
}
|
||||
|
||||
memdelete(test_opt);
|
||||
}
|
||||
|
||||
} // namespace TestOptionButton
|
||||
|
||||
#endif // TEST_OPTION_BUTTON_H
|
||||
|
|
@ -56,6 +56,72 @@ TEST_CASE("[PackedScene] Pack Scene and Retrieve State") {
|
|||
memdelete(scene);
|
||||
}
|
||||
|
||||
TEST_CASE("[PackedScene] Signals Preserved when Packing Scene") {
|
||||
// Create main scene
|
||||
// root
|
||||
// `- sub_node (local)
|
||||
// `- sub_scene (instance of another scene)
|
||||
// `- sub_scene_node (owned by sub_scene)
|
||||
Node *main_scene_root = memnew(Node);
|
||||
Node *sub_node = memnew(Node);
|
||||
Node *sub_scene_root = memnew(Node);
|
||||
Node *sub_scene_node = memnew(Node);
|
||||
|
||||
main_scene_root->add_child(sub_node);
|
||||
sub_node->set_owner(main_scene_root);
|
||||
|
||||
sub_scene_root->add_child(sub_scene_node);
|
||||
sub_scene_node->set_owner(sub_scene_root);
|
||||
|
||||
main_scene_root->add_child(sub_scene_root);
|
||||
sub_scene_root->set_owner(main_scene_root);
|
||||
|
||||
SUBCASE("Signals that should be saved") {
|
||||
int main_flags = Object::CONNECT_PERSIST;
|
||||
// sub node to a node in main scene
|
||||
sub_node->connect("ready", callable_mp(main_scene_root, &Node::is_ready), main_flags);
|
||||
// subscene root to a node in main scene
|
||||
sub_scene_root->connect("ready", callable_mp(main_scene_root, &Node::is_ready), main_flags);
|
||||
//subscene root to subscene root (connected within main scene)
|
||||
sub_scene_root->connect("ready", callable_mp(sub_scene_root, &Node::is_ready), main_flags);
|
||||
|
||||
// Pack the scene.
|
||||
Ref<PackedScene> packed_scene;
|
||||
packed_scene.instantiate();
|
||||
const Error err = packed_scene->pack(main_scene_root);
|
||||
CHECK(err == OK);
|
||||
|
||||
// Make sure the right connections are in packed scene.
|
||||
Ref<SceneState> state = packed_scene->get_state();
|
||||
CHECK_EQ(state->get_connection_count(), 3);
|
||||
}
|
||||
|
||||
/*
|
||||
// FIXME: This subcase requires GH-48064 to be fixed.
|
||||
SUBCASE("Signals that should not be saved") {
|
||||
int subscene_flags = Object::CONNECT_PERSIST | Object::CONNECT_INHERITED;
|
||||
// subscene node to itself
|
||||
sub_scene_node->connect("ready", callable_mp(sub_scene_node, &Node::is_ready), subscene_flags);
|
||||
// subscene node to subscene root
|
||||
sub_scene_node->connect("ready", callable_mp(sub_scene_root, &Node::is_ready), subscene_flags);
|
||||
//subscene root to subscene root (connected within sub scene)
|
||||
sub_scene_root->connect("ready", callable_mp(sub_scene_root, &Node::is_ready), subscene_flags);
|
||||
|
||||
// Pack the scene.
|
||||
Ref<PackedScene> packed_scene;
|
||||
packed_scene.instantiate();
|
||||
const Error err = packed_scene->pack(main_scene_root);
|
||||
CHECK(err == OK);
|
||||
|
||||
// Make sure the right connections are in packed scene.
|
||||
Ref<SceneState> state = packed_scene->get_state();
|
||||
CHECK_EQ(state->get_connection_count(), 0);
|
||||
}
|
||||
*/
|
||||
|
||||
memdelete(main_scene_root);
|
||||
}
|
||||
|
||||
TEST_CASE("[PackedScene] Clear Packed Scene") {
|
||||
// Create a scene to pack.
|
||||
Node *scene = memnew(Node);
|
||||
|
|
|
|||
131
engine/tests/scene/test_parallax_2d.h
Normal file
131
engine/tests/scene/test_parallax_2d.h
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/**************************************************************************/
|
||||
/* test_parallax_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_PARALLAX_2D_H
|
||||
#define TEST_PARALLAX_2D_H
|
||||
|
||||
#include "scene/2d/parallax_2d.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestParallax2D {
|
||||
|
||||
// Test cases for the Parallax2D class to ensure its properties are set and retrieved correctly.
|
||||
|
||||
TEST_CASE("[SceneTree][Parallax2D] Scroll Scale") {
|
||||
// Test setting and getting the scroll scale.
|
||||
Parallax2D *parallax = memnew(Parallax2D);
|
||||
Size2 scale(2, 2);
|
||||
parallax->set_scroll_scale(scale);
|
||||
CHECK(parallax->get_scroll_scale() == scale);
|
||||
memdelete(parallax);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Parallax2D] Repeat Size") {
|
||||
// Test setting and getting the repeat size.
|
||||
Parallax2D *parallax = memnew(Parallax2D);
|
||||
Size2 size(100, 100);
|
||||
parallax->set_repeat_size(size);
|
||||
CHECK(parallax->get_repeat_size() == size);
|
||||
memdelete(parallax);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Parallax2D] Repeat Times") {
|
||||
// Test setting and getting the repeat times.
|
||||
Parallax2D *parallax = memnew(Parallax2D);
|
||||
int times = 5;
|
||||
parallax->set_repeat_times(times);
|
||||
CHECK(parallax->get_repeat_times() == times);
|
||||
memdelete(parallax);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Parallax2D] Autoscroll") {
|
||||
// Test setting and getting the autoscroll values.
|
||||
Parallax2D *parallax = memnew(Parallax2D);
|
||||
Point2 autoscroll(1, 1);
|
||||
parallax->set_autoscroll(autoscroll);
|
||||
CHECK(parallax->get_autoscroll() == autoscroll);
|
||||
memdelete(parallax);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Parallax2D] Scroll Offset") {
|
||||
// Test setting and getting the scroll offset.
|
||||
Parallax2D *parallax = memnew(Parallax2D);
|
||||
Point2 offset(10, 10);
|
||||
parallax->set_scroll_offset(offset);
|
||||
CHECK(parallax->get_scroll_offset() == offset);
|
||||
memdelete(parallax);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Parallax2D] Screen Offset") {
|
||||
// Test setting and getting the screen offset.
|
||||
Parallax2D *parallax = memnew(Parallax2D);
|
||||
Point2 offset(20, 20);
|
||||
parallax->set_screen_offset(offset);
|
||||
CHECK(parallax->get_screen_offset() == offset);
|
||||
memdelete(parallax);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Parallax2D] Limit Begin") {
|
||||
// Test setting and getting the limit begin values.
|
||||
Parallax2D *parallax = memnew(Parallax2D);
|
||||
Point2 limit_begin(-100, -100);
|
||||
parallax->set_limit_begin(limit_begin);
|
||||
CHECK(parallax->get_limit_begin() == limit_begin);
|
||||
memdelete(parallax);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Parallax2D] Limit End") {
|
||||
// Test setting and getting the limit end values.
|
||||
Parallax2D *parallax = memnew(Parallax2D);
|
||||
Point2 limit_end(100, 100);
|
||||
parallax->set_limit_end(limit_end);
|
||||
CHECK(parallax->get_limit_end() == limit_end);
|
||||
memdelete(parallax);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Parallax2D] Follow Viewport") {
|
||||
// Test setting and getting the follow viewport flag.
|
||||
Parallax2D *parallax = memnew(Parallax2D);
|
||||
parallax->set_follow_viewport(false);
|
||||
CHECK_FALSE(parallax->get_follow_viewport());
|
||||
memdelete(parallax);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Parallax2D] Ignore Camera Scroll") {
|
||||
// Test setting and getting the ignore camera scroll flag.
|
||||
Parallax2D *parallax = memnew(Parallax2D);
|
||||
parallax->set_ignore_camera_scroll(true);
|
||||
CHECK(parallax->is_ignore_camera_scroll());
|
||||
memdelete(parallax);
|
||||
}
|
||||
|
||||
} // namespace TestParallax2D
|
||||
|
||||
#endif // TEST_PARALLAX_2D_H
|
||||
|
|
@ -40,7 +40,7 @@ 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);
|
||||
CHECK(test_path->get_curve().is_null());
|
||||
memdelete(test_path);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ namespace TestPath3D {
|
|||
TEST_CASE("[Path3D] Initialization") {
|
||||
SUBCASE("Path should be empty right after initialization") {
|
||||
Path3D *test_path = memnew(Path3D);
|
||||
CHECK(test_path->get_curve() == nullptr);
|
||||
CHECK(test_path->get_curve().is_null());
|
||||
memdelete(test_path);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,39 +60,30 @@ TEST_CASE("[SceneTree][PathFollow3D] Sampling with progress ratio") {
|
|||
SceneTree::get_singleton()->get_root()->add_child(path);
|
||||
|
||||
path_follow_3d->set_progress_ratio(0);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(0, 0, 0), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
path_follow_3d->set_progress_ratio(0.125);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(50, 0, 0), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
path_follow_3d->set_progress_ratio(0.25);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(100, 0, 0), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
path_follow_3d->set_progress_ratio(0.375);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(100, 50, 0), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
path_follow_3d->set_progress_ratio(0.5);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(100, 100, 0), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
path_follow_3d->set_progress_ratio(0.625);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(100, 100, 50), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
path_follow_3d->set_progress_ratio(0.75);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(100, 100, 100), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
path_follow_3d->set_progress_ratio(0.875);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(100, 50, 100), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
path_follow_3d->set_progress_ratio(1);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(100, 0, 100), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
memdelete(path);
|
||||
|
|
@ -113,39 +104,30 @@ TEST_CASE("[SceneTree][PathFollow3D] Sampling with progress") {
|
|||
SceneTree::get_singleton()->get_root()->add_child(path);
|
||||
|
||||
path_follow_3d->set_progress(0);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(0, 0, 0), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
path_follow_3d->set_progress(50);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(50, 0, 0), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
path_follow_3d->set_progress(100);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(100, 0, 0), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
path_follow_3d->set_progress(150);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(100, 50, 0), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
path_follow_3d->set_progress(200);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(100, 100, 0), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
path_follow_3d->set_progress(250);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(100, 100, 50), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
path_follow_3d->set_progress(300);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(100, 100, 100), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
path_follow_3d->set_progress(350);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(100, 50, 100), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
path_follow_3d->set_progress(400);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(100, 0, 100), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
memdelete(path);
|
||||
|
|
@ -163,13 +145,11 @@ TEST_CASE("[SceneTree][PathFollow3D] Removal of a point in curve") {
|
|||
SceneTree::get_singleton()->get_root()->add_child(path);
|
||||
|
||||
path_follow_3d->set_progress_ratio(0.5);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(100, 0, 0), path_follow_3d->get_transform().get_origin()));
|
||||
|
||||
curve->remove_point(1);
|
||||
|
||||
path_follow_3d->set_progress_ratio(0.5);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK_MESSAGE(
|
||||
is_equal_approx(Vector3(50, 50, 0), path_follow_3d->get_transform().get_origin()),
|
||||
"Path follow's position should be updated after removing a point from the curve");
|
||||
|
|
@ -270,47 +250,36 @@ TEST_CASE("[SceneTree][PathFollow3D] Calculate forward vector") {
|
|||
path_follow_3d->set_rotation_mode(PathFollow3D::RotationMode::ROTATION_ORIENTED);
|
||||
|
||||
path_follow_3d->set_progress(-50);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(-1, 0, 0), path_follow_3d->get_transform().get_basis().get_column(2)));
|
||||
|
||||
path_follow_3d->set_progress(0);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(-1, 0, 0), path_follow_3d->get_transform().get_basis().get_column(2)));
|
||||
|
||||
path_follow_3d->set_progress(50);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(-1, 0, 0), path_follow_3d->get_transform().get_basis().get_column(2)));
|
||||
|
||||
path_follow_3d->set_progress(100);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(-1, 0, 0), path_follow_3d->get_transform().get_basis().get_column(2)));
|
||||
|
||||
path_follow_3d->set_progress(100 + dist_cube_100 / 2);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(-0.577348, -0.577348, 0.577348), path_follow_3d->get_transform().get_basis().get_column(2)));
|
||||
|
||||
path_follow_3d->set_progress(100 + dist_cube_100 - 0.01);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(-0.577348, -0.577348, 0.577348), path_follow_3d->get_transform().get_basis().get_column(2)));
|
||||
|
||||
path_follow_3d->set_progress(250 + dist_cube_100);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(0, 0, -1), path_follow_3d->get_transform().get_basis().get_column(2)));
|
||||
|
||||
path_follow_3d->set_progress(400 + dist_cube_100 - 0.01);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(0, 0, -1), path_follow_3d->get_transform().get_basis().get_column(2)));
|
||||
|
||||
path_follow_3d->set_progress(400 + 1.5 * dist_cube_100);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(0.577348, 0.577348, 0.577348), path_follow_3d->get_transform().get_basis().get_column(2)));
|
||||
|
||||
path_follow_3d->set_progress(400 + 2 * dist_cube_100 - 0.01);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(0.577348, 0.577348, 0.577348), path_follow_3d->get_transform().get_basis().get_column(2)));
|
||||
|
||||
path_follow_3d->set_progress(500 + 2 * dist_cube_100);
|
||||
path_follow_3d->update_transform(true);
|
||||
CHECK(is_equal_approx(Vector3(1, 0, 0), path_follow_3d->get_transform().get_basis().get_column(2)));
|
||||
|
||||
memdelete(path);
|
||||
|
|
|
|||
107
engine/tests/scene/test_physics_material.h
Normal file
107
engine/tests/scene/test_physics_material.h
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/**************************************************************************/
|
||||
/* test_physics_material.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_PHYSICS_MATERIAL_H
|
||||
#define TEST_PHYSICS_MATERIAL_H
|
||||
|
||||
#include "scene/resources/physics_material.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestPhysics_material {
|
||||
|
||||
TEST_CASE("[Physics_material] Defaults") {
|
||||
Ref<PhysicsMaterial> physics_material;
|
||||
physics_material.instantiate();
|
||||
|
||||
CHECK(physics_material->get_friction() == 1.);
|
||||
CHECK(physics_material->is_rough() == false);
|
||||
CHECK(physics_material->get_bounce() == 0.);
|
||||
CHECK(physics_material->is_absorbent() == false);
|
||||
}
|
||||
|
||||
TEST_CASE("[Physics_material] Friction") {
|
||||
Ref<PhysicsMaterial> physics_material;
|
||||
physics_material.instantiate();
|
||||
|
||||
real_t friction = 0.314;
|
||||
physics_material->set_friction(friction);
|
||||
CHECK(physics_material->get_friction() == friction);
|
||||
}
|
||||
|
||||
TEST_CASE("[Physics_material] Rough") {
|
||||
Ref<PhysicsMaterial> physics_material;
|
||||
physics_material.instantiate();
|
||||
|
||||
bool rough = true;
|
||||
physics_material->set_rough(rough);
|
||||
CHECK(physics_material->is_rough() == rough);
|
||||
|
||||
real_t friction = 0.314;
|
||||
physics_material->set_friction(friction);
|
||||
CHECK(physics_material->computed_friction() == -friction);
|
||||
|
||||
rough = false;
|
||||
physics_material->set_rough(rough);
|
||||
CHECK(physics_material->is_rough() == rough);
|
||||
|
||||
CHECK(physics_material->computed_friction() == friction);
|
||||
}
|
||||
|
||||
TEST_CASE("[Physics_material] Bounce") {
|
||||
Ref<PhysicsMaterial> physics_material;
|
||||
physics_material.instantiate();
|
||||
|
||||
real_t bounce = 0.271;
|
||||
physics_material->set_bounce(bounce);
|
||||
CHECK(physics_material->get_bounce() == bounce);
|
||||
}
|
||||
|
||||
TEST_CASE("[Physics_material] Absorbent") {
|
||||
Ref<PhysicsMaterial> physics_material;
|
||||
physics_material.instantiate();
|
||||
|
||||
bool absorbent = true;
|
||||
physics_material->set_absorbent(absorbent);
|
||||
CHECK(physics_material->is_absorbent() == absorbent);
|
||||
|
||||
real_t bounce = 0.271;
|
||||
physics_material->set_bounce(bounce);
|
||||
CHECK(physics_material->computed_bounce() == -bounce);
|
||||
|
||||
absorbent = false;
|
||||
physics_material->set_absorbent(absorbent);
|
||||
CHECK(physics_material->is_absorbent() == absorbent);
|
||||
|
||||
CHECK(physics_material->computed_bounce() == bounce);
|
||||
}
|
||||
|
||||
} // namespace TestPhysics_material
|
||||
|
||||
#endif // TEST_PHYSICS_MATERIAL_H
|
||||
|
|
@ -104,8 +104,9 @@ TEST_CASE("[SceneTree][Primitive][Capsule] Capsule Primitive") {
|
|||
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)
|
||||
if (new_dist_to_y > dist_to_yaxis) {
|
||||
dist_to_yaxis = new_dist_to_y;
|
||||
}
|
||||
}
|
||||
|
||||
CHECK(dist_to_yaxis <= radius * radius);
|
||||
|
|
@ -114,10 +115,12 @@ TEST_CASE("[SceneTree][Primitive][Capsule] Capsule Primitive") {
|
|||
float max_y{ 0.f };
|
||||
float min_y{ 0.f };
|
||||
for (Vector3 point : points) {
|
||||
if (point.y > max_y)
|
||||
if (point.y > max_y) {
|
||||
max_y = point.y;
|
||||
if (point.y < min_y)
|
||||
}
|
||||
if (point.y < min_y) {
|
||||
min_y = point.y;
|
||||
}
|
||||
}
|
||||
|
||||
CHECK(max_y - min_y <= height);
|
||||
|
|
@ -196,12 +199,14 @@ TEST_CASE("[SceneTree][Primitive][Box] Box Primitive") {
|
|||
for (const Vector3 &normal : normals) {
|
||||
bool add_normal{ true };
|
||||
for (const Vector3 &vec : distinct_normals) {
|
||||
if (vec.is_equal_approx(normal))
|
||||
if (vec.is_equal_approx(normal)) {
|
||||
add_normal = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (add_normal)
|
||||
if (add_normal) {
|
||||
distinct_normals.push_back(normal);
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_MESSAGE(distinct_normals.size() == 6,
|
||||
|
|
@ -218,8 +223,9 @@ TEST_CASE("[SceneTree][Primitive][Box] Box Primitive") {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (!normal_correct_direction)
|
||||
if (!normal_correct_direction) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_MESSAGE(normal_correct_direction,
|
||||
|
|
@ -609,7 +615,7 @@ TEST_CASE("[SceneTree][Primitive][TubeTrail] TubeTrail Primitive") {
|
|||
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_curve().is_null());
|
||||
CHECK(tube->get_builtin_bind_pose_count() >= 0);
|
||||
}
|
||||
|
||||
|
|
@ -669,7 +675,7 @@ TEST_CASE("[SceneTree][Primitive][RibbonTrail] RibbonTrail Primitive") {
|
|||
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_curve().is_null());
|
||||
CHECK((ribbon->get_shape() == RibbonTrailMesh::SHAPE_CROSS ||
|
||||
ribbon->get_shape() == RibbonTrailMesh::SHAPE_FLAT));
|
||||
}
|
||||
|
|
@ -731,7 +737,7 @@ TEST_CASE("[SceneTree][Primitive][Text] Text Primitive") {
|
|||
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().is_null());
|
||||
CHECK(text->get_font_size() > 0);
|
||||
CHECK(text->get_line_spacing() >= 0);
|
||||
CHECK((text->get_autowrap_mode() == TextServer::AUTOWRAP_OFF ||
|
||||
|
|
|
|||
78
engine/tests/scene/test_skeleton_3d.h
Normal file
78
engine/tests/scene/test_skeleton_3d.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/**************************************************************************/
|
||||
/* test_skeleton_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_SKELETON_3D_H
|
||||
#define TEST_SKELETON_3D_H
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
|
||||
namespace TestSkeleton3D {
|
||||
|
||||
TEST_CASE("[Skeleton3D] Test per-bone meta") {
|
||||
Skeleton3D *skeleton = memnew(Skeleton3D);
|
||||
skeleton->add_bone("root");
|
||||
skeleton->set_bone_rest(0, Transform3D());
|
||||
|
||||
// Adding meta to bone.
|
||||
skeleton->set_bone_meta(0, "key1", "value1");
|
||||
skeleton->set_bone_meta(0, "key2", 12345);
|
||||
CHECK_MESSAGE(skeleton->get_bone_meta(0, "key1") == "value1", "Bone meta missing.");
|
||||
CHECK_MESSAGE(skeleton->get_bone_meta(0, "key2") == Variant(12345), "Bone meta missing.");
|
||||
|
||||
// Rename bone and check if meta persists.
|
||||
skeleton->set_bone_name(0, "renamed_root");
|
||||
CHECK_MESSAGE(skeleton->get_bone_meta(0, "key1") == "value1", "Bone meta missing.");
|
||||
CHECK_MESSAGE(skeleton->get_bone_meta(0, "key2") == Variant(12345), "Bone meta missing.");
|
||||
|
||||
// Retrieve list of keys.
|
||||
List<StringName> keys;
|
||||
skeleton->get_bone_meta_list(0, &keys);
|
||||
CHECK_MESSAGE(keys.size() == 2, "Wrong number of bone meta keys.");
|
||||
CHECK_MESSAGE(keys.find("key1"), "key1 not found in bone meta list");
|
||||
CHECK_MESSAGE(keys.find("key2"), "key2 not found in bone meta list");
|
||||
|
||||
// Removing meta.
|
||||
skeleton->set_bone_meta(0, "key1", Variant());
|
||||
skeleton->set_bone_meta(0, "key2", Variant());
|
||||
CHECK_MESSAGE(!skeleton->has_bone_meta(0, "key1"), "Bone meta key1 should be deleted.");
|
||||
CHECK_MESSAGE(!skeleton->has_bone_meta(0, "key2"), "Bone meta key2 should be deleted.");
|
||||
List<StringName> should_be_empty_keys;
|
||||
skeleton->get_bone_meta_list(0, &should_be_empty_keys);
|
||||
CHECK_MESSAGE(should_be_empty_keys.size() == 0, "Wrong number of bone meta keys.");
|
||||
|
||||
// Deleting non-existing key should succeed.
|
||||
skeleton->set_bone_meta(0, "non-existing-key", Variant());
|
||||
memdelete(skeleton);
|
||||
}
|
||||
} // namespace TestSkeleton3D
|
||||
|
||||
#endif // TEST_SKELETON_3D_H
|
||||
222
engine/tests/scene/test_sky.h
Normal file
222
engine/tests/scene/test_sky.h
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
/**************************************************************************/
|
||||
/* test_sky.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_SKY_H
|
||||
#define TEST_SKY_H
|
||||
|
||||
#include "scene/resources/sky.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestSky {
|
||||
|
||||
TEST_CASE("[SceneTree][Sky] Constructor") {
|
||||
Ref<Sky> test_sky;
|
||||
test_sky.instantiate();
|
||||
|
||||
CHECK(test_sky->get_process_mode() == Sky::PROCESS_MODE_AUTOMATIC);
|
||||
CHECK(test_sky->get_radiance_size() == Sky::RADIANCE_SIZE_256);
|
||||
CHECK(test_sky->get_material().is_null());
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Sky] Radiance size setter and getter") {
|
||||
Ref<Sky> test_sky;
|
||||
test_sky.instantiate();
|
||||
|
||||
// Check default.
|
||||
CHECK(test_sky->get_radiance_size() == Sky::RADIANCE_SIZE_256);
|
||||
|
||||
test_sky->set_radiance_size(Sky::RADIANCE_SIZE_1024);
|
||||
CHECK(test_sky->get_radiance_size() == Sky::RADIANCE_SIZE_1024);
|
||||
|
||||
ERR_PRINT_OFF;
|
||||
// Check setting invalid radiance size.
|
||||
test_sky->set_radiance_size(Sky::RADIANCE_SIZE_MAX);
|
||||
ERR_PRINT_ON;
|
||||
|
||||
CHECK(test_sky->get_radiance_size() == Sky::RADIANCE_SIZE_1024);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Sky] Process mode setter and getter") {
|
||||
Ref<Sky> test_sky;
|
||||
test_sky.instantiate();
|
||||
|
||||
// Check default.
|
||||
CHECK(test_sky->get_process_mode() == Sky::PROCESS_MODE_AUTOMATIC);
|
||||
|
||||
test_sky->set_process_mode(Sky::PROCESS_MODE_INCREMENTAL);
|
||||
CHECK(test_sky->get_process_mode() == Sky::PROCESS_MODE_INCREMENTAL);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Sky] Material setter and getter") {
|
||||
Ref<Sky> test_sky;
|
||||
test_sky.instantiate();
|
||||
|
||||
Ref<Material> material;
|
||||
material.instantiate();
|
||||
|
||||
SUBCASE("Material passed to the class should remain the same") {
|
||||
test_sky->set_material(material);
|
||||
CHECK(test_sky->get_material() == material);
|
||||
}
|
||||
SUBCASE("Material passed many times to the class should remain the same") {
|
||||
test_sky->set_material(material);
|
||||
test_sky->set_material(material);
|
||||
test_sky->set_material(material);
|
||||
CHECK(test_sky->get_material() == material);
|
||||
}
|
||||
SUBCASE("Material rewrite testing") {
|
||||
Ref<Material> material1;
|
||||
Ref<Material> material2;
|
||||
material1.instantiate();
|
||||
material2.instantiate();
|
||||
|
||||
test_sky->set_material(material1);
|
||||
test_sky->set_material(material2);
|
||||
CHECK_MESSAGE(test_sky->get_material() != material1,
|
||||
"After rewrite, second material should be in class.");
|
||||
CHECK_MESSAGE(test_sky->get_material() == material2,
|
||||
"After rewrite, second material should be in class.");
|
||||
}
|
||||
|
||||
SUBCASE("Assign same material to two skys") {
|
||||
Ref<Sky> sky2;
|
||||
sky2.instantiate();
|
||||
|
||||
test_sky->set_material(material);
|
||||
sky2->set_material(material);
|
||||
CHECK_MESSAGE(test_sky->get_material() == sky2->get_material(),
|
||||
"Both skys should have the same material.");
|
||||
}
|
||||
|
||||
SUBCASE("Swapping materials between two skys") {
|
||||
Ref<Sky> sky2;
|
||||
sky2.instantiate();
|
||||
Ref<Material> material1;
|
||||
Ref<Material> material2;
|
||||
material1.instantiate();
|
||||
material2.instantiate();
|
||||
|
||||
test_sky->set_material(material1);
|
||||
sky2->set_material(material2);
|
||||
CHECK(test_sky->get_material() == material1);
|
||||
CHECK(sky2->get_material() == material2);
|
||||
|
||||
// Do the swap.
|
||||
Ref<Material> temp = test_sky->get_material();
|
||||
test_sky->set_material(sky2->get_material());
|
||||
sky2->set_material(temp);
|
||||
|
||||
CHECK(test_sky->get_material() == material2);
|
||||
CHECK(sky2->get_material() == material1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Sky] Invalid radiance size handling") {
|
||||
Ref<Sky> test_sky;
|
||||
test_sky.instantiate();
|
||||
|
||||
// Attempt to set an invalid radiance size.
|
||||
ERR_PRINT_OFF;
|
||||
test_sky->set_radiance_size(Sky::RADIANCE_SIZE_MAX);
|
||||
ERR_PRINT_ON;
|
||||
|
||||
// Verify that the radiance size remains unchanged.
|
||||
CHECK(test_sky->get_radiance_size() == Sky::RADIANCE_SIZE_256);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Sky] Process mode variations") {
|
||||
Ref<Sky> test_sky;
|
||||
test_sky.instantiate();
|
||||
|
||||
// Test all process modes.
|
||||
const Sky::ProcessMode process_modes[] = {
|
||||
Sky::PROCESS_MODE_AUTOMATIC,
|
||||
Sky::PROCESS_MODE_QUALITY,
|
||||
Sky::PROCESS_MODE_INCREMENTAL,
|
||||
Sky::PROCESS_MODE_REALTIME
|
||||
};
|
||||
|
||||
for (Sky::ProcessMode mode : process_modes) {
|
||||
test_sky->set_process_mode(mode);
|
||||
CHECK(test_sky->get_process_mode() == mode);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Sky] Radiance size variations") {
|
||||
Ref<Sky> test_sky;
|
||||
test_sky.instantiate();
|
||||
|
||||
// Test all radiance sizes except MAX.
|
||||
const Sky::RadianceSize radiance_sizes[] = {
|
||||
Sky::RADIANCE_SIZE_32,
|
||||
Sky::RADIANCE_SIZE_64,
|
||||
Sky::RADIANCE_SIZE_128,
|
||||
Sky::RADIANCE_SIZE_256,
|
||||
Sky::RADIANCE_SIZE_512,
|
||||
Sky::RADIANCE_SIZE_1024,
|
||||
Sky::RADIANCE_SIZE_2048
|
||||
};
|
||||
|
||||
for (Sky::RadianceSize size : radiance_sizes) {
|
||||
test_sky->set_radiance_size(size);
|
||||
CHECK(test_sky->get_radiance_size() == size);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Sky] Null material handling") {
|
||||
Ref<Sky> test_sky;
|
||||
test_sky.instantiate();
|
||||
|
||||
SUBCASE("Setting null material") {
|
||||
test_sky->set_material(Ref<Material>());
|
||||
CHECK(test_sky->get_material().is_null());
|
||||
}
|
||||
|
||||
SUBCASE("Overwriting existing material with null") {
|
||||
Ref<Material> material;
|
||||
material.instantiate();
|
||||
test_sky->set_material(material);
|
||||
test_sky->set_material(Ref<Material>());
|
||||
|
||||
CHECK(test_sky->get_material().is_null());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][Sky] RID generation") {
|
||||
Ref<Sky> test_sky;
|
||||
test_sky.instantiate();
|
||||
// Check validity.
|
||||
CHECK(!test_sky->get_rid().is_valid());
|
||||
}
|
||||
|
||||
} // namespace TestSky
|
||||
|
||||
#endif // TEST_SKY_H
|
||||
1480
engine/tests/scene/test_split_container.h
Normal file
1480
engine/tests/scene/test_split_container.h
Normal file
File diff suppressed because it is too large
Load diff
194
engine/tests/scene/test_style_box_texture.h
Normal file
194
engine/tests/scene/test_style_box_texture.h
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
/**************************************************************************/
|
||||
/* test_style_box_texture.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TEST_STYLE_BOX_TEXTURE_H
|
||||
#define TEST_STYLE_BOX_TEXTURE_H
|
||||
|
||||
#include "scene/resources/style_box_texture.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestStyleBoxTexture {
|
||||
|
||||
TEST_CASE("[StyleBoxTexture] Constructor") {
|
||||
Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
|
||||
|
||||
CHECK(style_box_texture->get_h_axis_stretch_mode() == style_box_texture->AXIS_STRETCH_MODE_STRETCH);
|
||||
CHECK(style_box_texture->get_v_axis_stretch_mode() == style_box_texture->AXIS_STRETCH_MODE_STRETCH);
|
||||
CHECK(style_box_texture->is_draw_center_enabled() == true);
|
||||
|
||||
CHECK(style_box_texture->get_expand_margin(SIDE_LEFT) == 0);
|
||||
CHECK(style_box_texture->get_expand_margin(SIDE_TOP) == 0);
|
||||
CHECK(style_box_texture->get_expand_margin(SIDE_RIGHT) == 0);
|
||||
CHECK(style_box_texture->get_expand_margin(SIDE_BOTTOM) == 0);
|
||||
|
||||
CHECK(style_box_texture->get_modulate() == Color(1, 1, 1, 1));
|
||||
CHECK(style_box_texture->get_region_rect() == Rect2(0, 0, 0, 0));
|
||||
CHECK(style_box_texture->get_texture() == Ref<Texture2D>());
|
||||
|
||||
CHECK(style_box_texture->get_texture_margin(SIDE_LEFT) == 0);
|
||||
CHECK(style_box_texture->get_texture_margin(SIDE_TOP) == 0);
|
||||
CHECK(style_box_texture->get_texture_margin(SIDE_RIGHT) == 0);
|
||||
CHECK(style_box_texture->get_texture_margin(SIDE_BOTTOM) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("[StyleBoxTexture] set_texture, get_texture") {
|
||||
Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
|
||||
Ref<Texture2D> texture = memnew(Texture2D);
|
||||
|
||||
style_box_texture->set_texture(texture);
|
||||
CHECK(style_box_texture->get_texture() == texture);
|
||||
}
|
||||
|
||||
TEST_CASE("[StyleBoxTexture] set_texture_margin, set_texture_margin_all, set_texture_margin_individual, get_texture_margin") {
|
||||
Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
|
||||
|
||||
SUBCASE("set_texture_margin, get_texture_margin") {
|
||||
style_box_texture->set_texture_margin(SIDE_LEFT, 1);
|
||||
style_box_texture->set_texture_margin(SIDE_TOP, 1);
|
||||
style_box_texture->set_texture_margin(SIDE_RIGHT, 1);
|
||||
style_box_texture->set_texture_margin(SIDE_BOTTOM, 1);
|
||||
|
||||
CHECK(style_box_texture->get_texture_margin(SIDE_LEFT) == 1);
|
||||
CHECK(style_box_texture->get_texture_margin(SIDE_TOP) == 1);
|
||||
CHECK(style_box_texture->get_texture_margin(SIDE_RIGHT) == 1);
|
||||
CHECK(style_box_texture->get_texture_margin(SIDE_BOTTOM) == 1);
|
||||
}
|
||||
|
||||
SUBCASE("set_texture_margin_all") {
|
||||
style_box_texture->set_texture_margin_all(2);
|
||||
|
||||
CHECK(style_box_texture->get_texture_margin(SIDE_LEFT) == 2);
|
||||
CHECK(style_box_texture->get_texture_margin(SIDE_TOP) == 2);
|
||||
CHECK(style_box_texture->get_texture_margin(SIDE_RIGHT) == 2);
|
||||
CHECK(style_box_texture->get_texture_margin(SIDE_BOTTOM) == 2);
|
||||
}
|
||||
|
||||
SUBCASE("set_texture_margin_individual") {
|
||||
style_box_texture->set_texture_margin_individual(3, 4, 5, 6);
|
||||
|
||||
CHECK(style_box_texture->get_texture_margin(SIDE_LEFT) == 3);
|
||||
CHECK(style_box_texture->get_texture_margin(SIDE_TOP) == 4);
|
||||
CHECK(style_box_texture->get_texture_margin(SIDE_RIGHT) == 5);
|
||||
CHECK(style_box_texture->get_texture_margin(SIDE_BOTTOM) == 6);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[StyleBoxTexture] set_expand_margin, set_expand_margin_all, set_expand_margin_individual") {
|
||||
Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
|
||||
|
||||
SUBCASE("set_expand_margin, get_expand_margin") {
|
||||
style_box_texture->set_expand_margin(SIDE_LEFT, 1);
|
||||
style_box_texture->set_expand_margin(SIDE_TOP, 1);
|
||||
style_box_texture->set_expand_margin(SIDE_RIGHT, 1);
|
||||
style_box_texture->set_expand_margin(SIDE_BOTTOM, 1);
|
||||
|
||||
CHECK(style_box_texture->get_expand_margin(SIDE_LEFT) == 1);
|
||||
CHECK(style_box_texture->get_expand_margin(SIDE_TOP) == 1);
|
||||
CHECK(style_box_texture->get_expand_margin(SIDE_RIGHT) == 1);
|
||||
CHECK(style_box_texture->get_expand_margin(SIDE_BOTTOM) == 1);
|
||||
}
|
||||
|
||||
SUBCASE("set_expand_margin_all") {
|
||||
style_box_texture->set_expand_margin_all(2);
|
||||
|
||||
CHECK(style_box_texture->get_expand_margin(SIDE_LEFT) == 2);
|
||||
CHECK(style_box_texture->get_expand_margin(SIDE_TOP) == 2);
|
||||
CHECK(style_box_texture->get_expand_margin(SIDE_RIGHT) == 2);
|
||||
CHECK(style_box_texture->get_expand_margin(SIDE_BOTTOM) == 2);
|
||||
}
|
||||
|
||||
SUBCASE("set_expand_margin_individual") {
|
||||
style_box_texture->set_expand_margin_individual(3, 4, 5, 6);
|
||||
|
||||
CHECK(style_box_texture->get_expand_margin(SIDE_LEFT) == 3);
|
||||
CHECK(style_box_texture->get_expand_margin(SIDE_TOP) == 4);
|
||||
CHECK(style_box_texture->get_expand_margin(SIDE_RIGHT) == 5);
|
||||
CHECK(style_box_texture->get_expand_margin(SIDE_BOTTOM) == 6);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[StyleBoxTexture] set_region_rect, get_region_rect") {
|
||||
Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
|
||||
|
||||
style_box_texture->set_region_rect(Rect2(1, 1, 1, 1));
|
||||
CHECK(style_box_texture->get_region_rect() == Rect2(1, 1, 1, 1));
|
||||
}
|
||||
|
||||
TEST_CASE("[StyleBoxTexture] set_draw_center, get_draw_center") {
|
||||
Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
|
||||
|
||||
style_box_texture->set_draw_center(false);
|
||||
CHECK(style_box_texture->is_draw_center_enabled() == false);
|
||||
}
|
||||
|
||||
TEST_CASE("[StyleBoxTexture] set_h_axis_stretch_mode, set_v_axis_stretch_mode, get_h_axis_stretch_mode, get_v_axis_stretch_mode") {
|
||||
Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
|
||||
|
||||
SUBCASE("set_h_axis_stretch_mode, get_h_axis_stretch_mode") {
|
||||
style_box_texture->set_h_axis_stretch_mode(style_box_texture->AXIS_STRETCH_MODE_TILE);
|
||||
CHECK(style_box_texture->get_h_axis_stretch_mode() == style_box_texture->AXIS_STRETCH_MODE_TILE);
|
||||
|
||||
style_box_texture->set_h_axis_stretch_mode(style_box_texture->AXIS_STRETCH_MODE_TILE_FIT);
|
||||
CHECK(style_box_texture->get_h_axis_stretch_mode() == style_box_texture->AXIS_STRETCH_MODE_TILE_FIT);
|
||||
|
||||
style_box_texture->set_h_axis_stretch_mode(style_box_texture->AXIS_STRETCH_MODE_STRETCH);
|
||||
CHECK(style_box_texture->get_h_axis_stretch_mode() == style_box_texture->AXIS_STRETCH_MODE_STRETCH);
|
||||
}
|
||||
|
||||
SUBCASE("set_v_axis_stretch_mode, get_v_axis_stretch_mode") {
|
||||
style_box_texture->set_v_axis_stretch_mode(style_box_texture->AXIS_STRETCH_MODE_TILE);
|
||||
CHECK(style_box_texture->get_v_axis_stretch_mode() == style_box_texture->AXIS_STRETCH_MODE_TILE);
|
||||
|
||||
style_box_texture->set_v_axis_stretch_mode(style_box_texture->AXIS_STRETCH_MODE_TILE_FIT);
|
||||
CHECK(style_box_texture->get_v_axis_stretch_mode() == style_box_texture->AXIS_STRETCH_MODE_TILE_FIT);
|
||||
|
||||
style_box_texture->set_v_axis_stretch_mode(style_box_texture->AXIS_STRETCH_MODE_STRETCH);
|
||||
CHECK(style_box_texture->get_v_axis_stretch_mode() == style_box_texture->AXIS_STRETCH_MODE_STRETCH);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[StyleBoxTexture] set_modulate, get_modulate") {
|
||||
Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
|
||||
|
||||
style_box_texture->set_modulate(Color(0, 0, 0, 0));
|
||||
CHECK(style_box_texture->get_modulate() == Color(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
TEST_CASE("[StyleBoxTexture] get_draw_rect") {
|
||||
Ref<StyleBoxTexture> style_box_texture = memnew(StyleBoxTexture);
|
||||
|
||||
style_box_texture->set_expand_margin_all(5);
|
||||
CHECK(style_box_texture->get_draw_rect(Rect2(0, 0, 1, 1)) == Rect2(-5, -5, 11, 11));
|
||||
}
|
||||
|
||||
} // namespace TestStyleBoxTexture
|
||||
|
||||
#endif // TEST_STYLE_BOX_TEXTURE_H
|
||||
864
engine/tests/scene/test_tab_bar.h
Normal file
864
engine/tests/scene/test_tab_bar.h
Normal file
|
|
@ -0,0 +1,864 @@
|
|||
/**************************************************************************/
|
||||
/* test_tab_bar.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_TAB_BAR_H
|
||||
#define TEST_TAB_BAR_H
|
||||
|
||||
#include "scene/gui/tab_bar.h"
|
||||
#include "scene/main/window.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestTabBar {
|
||||
static inline Array build_array() {
|
||||
return Array();
|
||||
}
|
||||
template <typename... Targs>
|
||||
static inline Array build_array(Variant item, Targs... Fargs) {
|
||||
Array a = build_array(Fargs...);
|
||||
a.push_front(item);
|
||||
return a;
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][TabBar] tab operations") {
|
||||
TabBar *tab_bar = memnew(TabBar);
|
||||
SceneTree::get_singleton()->get_root()->add_child(tab_bar);
|
||||
tab_bar->set_clip_tabs(false);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
|
||||
SIGNAL_WATCH(tab_bar, "tab_selected");
|
||||
SIGNAL_WATCH(tab_bar, "tab_changed");
|
||||
|
||||
SUBCASE("[TabBar] no tabs") {
|
||||
CHECK(tab_bar->get_tab_count() == 0);
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] add tabs") {
|
||||
tab_bar->add_tab("tab0");
|
||||
CHECK(tab_bar->get_tab_count() == 1);
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(0)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(0)));
|
||||
|
||||
tab_bar->add_tab("tab1");
|
||||
CHECK(tab_bar->get_tab_count() == 2);
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
tab_bar->add_tab("tab2");
|
||||
CHECK(tab_bar->get_tab_count() == 3);
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
CHECK(tab_bar->get_tab_title(0) == "tab0");
|
||||
CHECK(tab_bar->get_tab_tooltip(0) == "");
|
||||
CHECK(tab_bar->get_tab_text_direction(0) == Control::TEXT_DIRECTION_INHERITED);
|
||||
CHECK_FALSE(tab_bar->is_tab_disabled(0));
|
||||
CHECK_FALSE(tab_bar->is_tab_hidden(0));
|
||||
|
||||
CHECK(tab_bar->get_tab_title(1) == "tab1");
|
||||
CHECK(tab_bar->get_tab_tooltip(1) == "");
|
||||
CHECK(tab_bar->get_tab_text_direction(1) == Control::TEXT_DIRECTION_INHERITED);
|
||||
CHECK_FALSE(tab_bar->is_tab_disabled(1));
|
||||
CHECK_FALSE(tab_bar->is_tab_hidden(1));
|
||||
|
||||
CHECK(tab_bar->get_tab_title(2) == "tab2");
|
||||
CHECK(tab_bar->get_tab_tooltip(2) == "");
|
||||
CHECK(tab_bar->get_tab_text_direction(2) == Control::TEXT_DIRECTION_INHERITED);
|
||||
CHECK_FALSE(tab_bar->is_tab_disabled(2));
|
||||
CHECK_FALSE(tab_bar->is_tab_hidden(2));
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] set tab count") {
|
||||
// Adds multiple tabs at once.
|
||||
tab_bar->set_tab_count(3);
|
||||
CHECK(tab_bar->get_tab_count() == 3);
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
CHECK(tab_bar->get_tab_title(0) == "");
|
||||
CHECK(tab_bar->get_tab_tooltip(0) == "");
|
||||
CHECK(tab_bar->get_tab_text_direction(0) == Control::TEXT_DIRECTION_INHERITED);
|
||||
CHECK_FALSE(tab_bar->is_tab_disabled(0));
|
||||
CHECK_FALSE(tab_bar->is_tab_hidden(0));
|
||||
|
||||
CHECK(tab_bar->get_tab_title(1) == "");
|
||||
CHECK(tab_bar->get_tab_tooltip(1) == "");
|
||||
CHECK(tab_bar->get_tab_text_direction(1) == Control::TEXT_DIRECTION_INHERITED);
|
||||
CHECK_FALSE(tab_bar->is_tab_disabled(1));
|
||||
CHECK_FALSE(tab_bar->is_tab_hidden(1));
|
||||
|
||||
CHECK(tab_bar->get_tab_title(2) == "");
|
||||
CHECK(tab_bar->get_tab_tooltip(2) == "");
|
||||
CHECK(tab_bar->get_tab_text_direction(2) == Control::TEXT_DIRECTION_INHERITED);
|
||||
CHECK_FALSE(tab_bar->is_tab_disabled(2));
|
||||
CHECK_FALSE(tab_bar->is_tab_hidden(2));
|
||||
|
||||
// Setting to less tabs than there are removes from the end.
|
||||
tab_bar->set_tab_title(0, "tab0");
|
||||
tab_bar->set_tab_title(1, "tab1");
|
||||
tab_bar->set_tab_title(2, "tab2");
|
||||
|
||||
tab_bar->set_tab_count(2);
|
||||
CHECK(tab_bar->get_tab_count() == 2);
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
CHECK(tab_bar->get_tab_title(0) == "tab0");
|
||||
CHECK(tab_bar->get_tab_title(1) == "tab1");
|
||||
|
||||
// Remove all tabs.
|
||||
tab_bar->set_tab_count(0);
|
||||
CHECK(tab_bar->get_tab_count() == 0);
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] clear tabs") {
|
||||
CHECK(tab_bar->get_tab_count() == 0);
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
|
||||
tab_bar->set_tab_count(2);
|
||||
CHECK(tab_bar->get_tab_count() == 2);
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
tab_bar->clear_tabs();
|
||||
CHECK(tab_bar->get_tab_count() == 0);
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] remove tabs") {
|
||||
tab_bar->add_tab("tab0");
|
||||
tab_bar->add_tab("tab1");
|
||||
tab_bar->add_tab("tab2");
|
||||
tab_bar->set_current_tab(1);
|
||||
CHECK(tab_bar->get_tab_count() == 3);
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
// Remove first tab.
|
||||
tab_bar->remove_tab(0);
|
||||
CHECK(tab_bar->get_tab_count() == 2);
|
||||
CHECK(tab_bar->get_tab_title(0) == "tab1");
|
||||
CHECK(tab_bar->get_tab_title(1) == "tab2");
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Remove last tab.
|
||||
tab_bar->remove_tab(1);
|
||||
CHECK(tab_bar->get_tab_count() == 1);
|
||||
CHECK(tab_bar->get_tab_title(0) == "tab1");
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Remove only tab.
|
||||
tab_bar->remove_tab(0);
|
||||
CHECK(tab_bar->get_tab_count() == 0);
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(-1)));
|
||||
|
||||
// Remove current tab when there are other tabs.
|
||||
tab_bar->add_tab("tab0");
|
||||
tab_bar->add_tab("tab1");
|
||||
tab_bar->add_tab("tab2");
|
||||
tab_bar->set_current_tab(1);
|
||||
tab_bar->set_current_tab(2);
|
||||
CHECK(tab_bar->get_tab_count() == 3);
|
||||
CHECK(tab_bar->get_current_tab() == 2);
|
||||
CHECK(tab_bar->get_previous_tab() == 1);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
tab_bar->remove_tab(2);
|
||||
CHECK(tab_bar->get_tab_count() == 2);
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] move tabs") {
|
||||
tab_bar->add_tab("tab0");
|
||||
tab_bar->add_tab("tab1");
|
||||
tab_bar->add_tab("tab2");
|
||||
tab_bar->set_current_tab(1);
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
// Don't move if index is the same.
|
||||
tab_bar->move_tab(0, 0);
|
||||
CHECK(tab_bar->get_tab_title(0) == "tab0");
|
||||
CHECK(tab_bar->get_tab_title(1) == "tab1");
|
||||
CHECK(tab_bar->get_tab_title(2) == "tab2");
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Move the first tab to the end.
|
||||
tab_bar->move_tab(0, 2);
|
||||
CHECK(tab_bar->get_tab_title(0) == "tab1");
|
||||
CHECK(tab_bar->get_tab_title(1) == "tab2");
|
||||
CHECK(tab_bar->get_tab_title(2) == "tab0");
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == 2);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Move the second tab to the front.
|
||||
tab_bar->move_tab(1, 0);
|
||||
CHECK(tab_bar->get_tab_title(0) == "tab2");
|
||||
CHECK(tab_bar->get_tab_title(1) == "tab1");
|
||||
CHECK(tab_bar->get_tab_title(2) == "tab0");
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 2);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] set current tab") {
|
||||
tab_bar->add_tab("tab0");
|
||||
tab_bar->add_tab("tab1");
|
||||
tab_bar->add_tab("tab2");
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(0)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(0)));
|
||||
|
||||
// Set the current tab.
|
||||
tab_bar->set_current_tab(1);
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
|
||||
|
||||
// Set to same tab.
|
||||
tab_bar->set_current_tab(1);
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 1);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Out of bounds.
|
||||
ERR_PRINT_OFF;
|
||||
tab_bar->set_current_tab(-5);
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
tab_bar->set_current_tab(5);
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] deselection enabled") {
|
||||
tab_bar->add_tab("tab0");
|
||||
tab_bar->add_tab("tab1");
|
||||
tab_bar->add_tab("tab2");
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
// Setting deselect enabled doesn't change current tab.
|
||||
tab_bar->set_deselect_enabled(true);
|
||||
CHECK(tab_bar->get_deselect_enabled());
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Can deselect all tabs by setting current to -1.
|
||||
tab_bar->set_current_tab(-1);
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(-1)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(-1)));
|
||||
|
||||
// Adding a tab will still set the current tab to 0.
|
||||
tab_bar->clear_tabs();
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
|
||||
tab_bar->add_tab("tab0");
|
||||
tab_bar->add_tab("tab1");
|
||||
tab_bar->add_tab("tab2");
|
||||
CHECK(tab_bar->get_tab_count() == 3);
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(0)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(0)));
|
||||
|
||||
tab_bar->set_current_tab(-1);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
// Disabling while at -1 will select the first available tab.
|
||||
tab_bar->set_deselect_enabled(false);
|
||||
CHECK_FALSE(tab_bar->get_deselect_enabled());
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(0)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(0)));
|
||||
|
||||
// Cannot set to -1 if disabled.
|
||||
ERR_PRINT_OFF;
|
||||
tab_bar->set_current_tab(-1);
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
ERR_PRINT_ON;
|
||||
|
||||
// Disabling while at -1 skips any disabled or hidden tabs.
|
||||
tab_bar->set_deselect_enabled(true);
|
||||
tab_bar->set_tab_disabled(0, true);
|
||||
tab_bar->set_tab_hidden(1, true);
|
||||
tab_bar->set_current_tab(-1);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
tab_bar->set_deselect_enabled(false);
|
||||
CHECK(tab_bar->get_current_tab() == 2);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(2)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(2)));
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] hidden tabs") {
|
||||
tab_bar->add_tab("tab0");
|
||||
tab_bar->add_tab("tab1");
|
||||
tab_bar->add_tab("tab2");
|
||||
tab_bar->set_current_tab(1);
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
CHECK_FALSE(tab_bar->is_tab_hidden(1));
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
MessageQueue::get_singleton()->flush();
|
||||
Vector<Rect2> tab_rects = {
|
||||
tab_bar->get_tab_rect(0),
|
||||
tab_bar->get_tab_rect(1),
|
||||
tab_bar->get_tab_rect(2)
|
||||
};
|
||||
|
||||
// Hiding a tab does not affect current tab.
|
||||
tab_bar->set_tab_hidden(1, true);
|
||||
CHECK(tab_bar->is_tab_hidden(1));
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// The tabs after are moved over.
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab_bar->get_tab_rect(0) == tab_rects[0]);
|
||||
CHECK(tab_bar->get_tab_rect(2) == tab_rects[1]);
|
||||
|
||||
// Unhiding a tab does not affect current tab.
|
||||
tab_bar->set_tab_hidden(1, false);
|
||||
CHECK_FALSE(tab_bar->is_tab_hidden(1));
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// The tabs are back where they were.
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab_bar->get_tab_rect(0) == tab_rects[0]);
|
||||
CHECK(tab_bar->get_tab_rect(1) == tab_rects[1]);
|
||||
CHECK(tab_bar->get_tab_rect(2) == tab_rects[2]);
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] disabled tabs") {
|
||||
tab_bar->add_tab("tab0");
|
||||
tab_bar->add_tab("tab1");
|
||||
tab_bar->add_tab("tab2");
|
||||
CHECK_FALSE(tab_bar->is_tab_disabled(1));
|
||||
tab_bar->set_current_tab(1);
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
CHECK_FALSE(tab_bar->is_tab_hidden(1));
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
// Disabling a tab does not affect current tab.
|
||||
tab_bar->set_tab_disabled(1, true);
|
||||
CHECK(tab_bar->is_tab_disabled(1));
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Enabling a tab does not affect current tab.
|
||||
tab_bar->set_tab_disabled(1, false);
|
||||
CHECK_FALSE(tab_bar->is_tab_disabled(1));
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] select next available") {
|
||||
tab_bar->add_tab("tab0");
|
||||
tab_bar->add_tab("tab1");
|
||||
tab_bar->add_tab("tab2");
|
||||
tab_bar->add_tab("tab3");
|
||||
tab_bar->add_tab("tab4");
|
||||
tab_bar->set_tab_disabled(2, true);
|
||||
tab_bar->set_tab_hidden(3, true);
|
||||
tab_bar->set_current_tab(0);
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
// Selects the next tab.
|
||||
CHECK(tab_bar->select_next_available());
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
|
||||
|
||||
// Skips over disabled and hidden tabs.
|
||||
CHECK(tab_bar->select_next_available());
|
||||
CHECK(tab_bar->get_current_tab() == 4);
|
||||
CHECK(tab_bar->get_previous_tab() == 1);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(4)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(4)));
|
||||
|
||||
// Does not wrap around.
|
||||
CHECK_FALSE(tab_bar->select_next_available());
|
||||
CHECK(tab_bar->get_current_tab() == 4);
|
||||
CHECK(tab_bar->get_previous_tab() == 1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Fails if there is only one valid tab.
|
||||
tab_bar->remove_tab(0);
|
||||
tab_bar->remove_tab(3);
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
CHECK_FALSE(tab_bar->select_next_available());
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Fails if there are no valid tabs.
|
||||
tab_bar->remove_tab(0);
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
CHECK_FALSE(tab_bar->select_next_available());
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Fails if there are no tabs.
|
||||
tab_bar->clear_tabs();
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
CHECK_FALSE(tab_bar->select_next_available());
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] select previous available") {
|
||||
tab_bar->add_tab("tab0");
|
||||
tab_bar->add_tab("tab1");
|
||||
tab_bar->add_tab("tab2");
|
||||
tab_bar->add_tab("tab3");
|
||||
tab_bar->add_tab("tab4");
|
||||
tab_bar->set_tab_disabled(1, true);
|
||||
tab_bar->set_tab_hidden(2, true);
|
||||
tab_bar->set_current_tab(4);
|
||||
CHECK(tab_bar->get_current_tab() == 4);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
// Selects the previous tab.
|
||||
CHECK(tab_bar->select_previous_available());
|
||||
CHECK(tab_bar->get_current_tab() == 3);
|
||||
CHECK(tab_bar->get_previous_tab() == 4);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(3)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(3)));
|
||||
|
||||
// Skips over disabled and hidden tabs.
|
||||
CHECK(tab_bar->select_previous_available());
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == 3);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(0)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(0)));
|
||||
|
||||
// Does not wrap around.
|
||||
CHECK_FALSE(tab_bar->select_previous_available());
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == 3);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Fails if there is only one valid tab.
|
||||
tab_bar->remove_tab(4);
|
||||
tab_bar->remove_tab(3);
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == 2);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
CHECK_FALSE(tab_bar->select_previous_available());
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == 2);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Fails if there are no valid tabs.
|
||||
tab_bar->remove_tab(0);
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == 1);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
CHECK_FALSE(tab_bar->select_previous_available());
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == 1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Fails if there are no tabs.
|
||||
tab_bar->clear_tabs();
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
CHECK_FALSE(tab_bar->select_previous_available());
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
}
|
||||
|
||||
SIGNAL_UNWATCH(tab_bar, "tab_selected");
|
||||
SIGNAL_UNWATCH(tab_bar, "tab_changed");
|
||||
|
||||
memdelete(tab_bar);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][TabBar] initialization") {
|
||||
TabBar *tab_bar = memnew(TabBar);
|
||||
|
||||
SIGNAL_WATCH(tab_bar, "tab_selected");
|
||||
SIGNAL_WATCH(tab_bar, "tab_changed");
|
||||
|
||||
SUBCASE("[TabBar] current tab can be set before tabs are set") {
|
||||
// This queues the current tab to update on when tabs are set.
|
||||
tab_bar->set_current_tab(1);
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
tab_bar->set_tab_count(2);
|
||||
CHECK(tab_bar->get_tab_count() == 2);
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
|
||||
|
||||
// Does not work again.
|
||||
ERR_PRINT_OFF;
|
||||
tab_bar->set_current_tab(2);
|
||||
CHECK(tab_bar->get_tab_count() == 2);
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
tab_bar->set_tab_count(3);
|
||||
CHECK(tab_bar->get_tab_count() == 3);
|
||||
CHECK(tab_bar->get_current_tab() == 1);
|
||||
CHECK(tab_bar->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] setting tabs works normally if no current tab was set") {
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
|
||||
tab_bar->set_tab_count(2);
|
||||
CHECK(tab_bar->get_tab_count() == 2);
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] cannot set current tab to an invalid value before tabs are set") {
|
||||
tab_bar->set_current_tab(100);
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// This will print an error message as if `set_current_tab` was called after.
|
||||
ERR_PRINT_OFF;
|
||||
tab_bar->set_tab_count(2);
|
||||
CHECK(tab_bar->get_tab_count() == 2);
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] setting the current tab before tabs only works when out of tree") {
|
||||
tab_bar->set_current_tab(1);
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
SceneTree::get_singleton()->get_root()->add_child(tab_bar);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab_bar->get_tab_count() == 0);
|
||||
CHECK(tab_bar->get_current_tab() == -1);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Works normally.
|
||||
tab_bar->set_tab_count(2);
|
||||
CHECK(tab_bar->get_tab_count() == 2);
|
||||
CHECK(tab_bar->get_current_tab() == 0);
|
||||
CHECK(tab_bar->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
}
|
||||
|
||||
SIGNAL_UNWATCH(tab_bar, "tab_selected");
|
||||
SIGNAL_UNWATCH(tab_bar, "tab_changed");
|
||||
|
||||
memdelete(tab_bar);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][TabBar] layout and offset") {
|
||||
TabBar *tab_bar = memnew(TabBar);
|
||||
SceneTree::get_singleton()->get_root()->add_child(tab_bar);
|
||||
|
||||
tab_bar->set_clip_tabs(false);
|
||||
tab_bar->add_tab("tab0");
|
||||
tab_bar->add_tab("tab1 ");
|
||||
tab_bar->add_tab("tab2 ");
|
||||
MessageQueue::get_singleton()->flush();
|
||||
Size2 all_tabs_size = tab_bar->get_size();
|
||||
|
||||
Vector<Rect2> tab_rects = {
|
||||
tab_bar->get_tab_rect(0),
|
||||
tab_bar->get_tab_rect(1),
|
||||
tab_bar->get_tab_rect(2)
|
||||
};
|
||||
|
||||
SUBCASE("[TabBar] tabs are arranged next to each other") {
|
||||
// Horizontal positions are next to each other.
|
||||
CHECK(tab_rects[0].position.x == 0);
|
||||
CHECK(tab_rects[1].position.x == tab_rects[0].size.x);
|
||||
CHECK(tab_rects[2].position.x == tab_rects[1].position.x + tab_rects[1].size.x);
|
||||
|
||||
// Fills the entire width.
|
||||
CHECK(tab_rects[2].position.x + tab_rects[2].size.x == all_tabs_size.x);
|
||||
|
||||
// Horizontal sizes are positive.
|
||||
CHECK(tab_rects[0].size.x > 0);
|
||||
CHECK(tab_rects[1].size.x > 0);
|
||||
CHECK(tab_rects[2].size.x > 0);
|
||||
|
||||
// Vertical positions are at 0.
|
||||
CHECK(tab_rects[0].position.y == 0);
|
||||
CHECK(tab_rects[1].position.y == 0);
|
||||
CHECK(tab_rects[2].position.y == 0);
|
||||
|
||||
// Vertical sizes are the same.
|
||||
CHECK(tab_rects[0].size.y == tab_rects[1].size.y);
|
||||
CHECK(tab_rects[1].size.y == tab_rects[2].size.y);
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] tab alignment") {
|
||||
// Add extra space so the alignment can be seen.
|
||||
tab_bar->set_size(Size2(all_tabs_size.x + 100, all_tabs_size.y));
|
||||
|
||||
// Left alignment.
|
||||
tab_bar->set_tab_alignment(TabBar::ALIGNMENT_LEFT);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
tab_rects = {
|
||||
tab_bar->get_tab_rect(0),
|
||||
tab_bar->get_tab_rect(1),
|
||||
tab_bar->get_tab_rect(2)
|
||||
};
|
||||
CHECK(tab_bar->get_tab_alignment() == TabBar::ALIGNMENT_LEFT);
|
||||
CHECK(tab_rects[0].position.x == 0);
|
||||
CHECK(tab_rects[1].position.x == tab_rects[0].size.x);
|
||||
CHECK(tab_rects[2].position.x == tab_rects[1].position.x + tab_rects[1].size.x);
|
||||
|
||||
// Right alignment.
|
||||
tab_bar->set_tab_alignment(TabBar::ALIGNMENT_RIGHT);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
tab_rects = {
|
||||
tab_bar->get_tab_rect(0),
|
||||
tab_bar->get_tab_rect(1),
|
||||
tab_bar->get_tab_rect(2)
|
||||
};
|
||||
CHECK(tab_bar->get_tab_alignment() == TabBar::ALIGNMENT_RIGHT);
|
||||
CHECK(tab_rects[2].position.x == tab_bar->get_size().x - tab_rects[2].size.x);
|
||||
CHECK(tab_rects[1].position.x == tab_rects[2].position.x - tab_rects[1].size.x);
|
||||
CHECK(tab_rects[0].position.x == tab_rects[1].position.x - tab_rects[0].size.x);
|
||||
|
||||
// Center alignment.
|
||||
tab_bar->set_tab_alignment(TabBar::ALIGNMENT_CENTER);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
tab_rects = {
|
||||
tab_bar->get_tab_rect(0),
|
||||
tab_bar->get_tab_rect(1),
|
||||
tab_bar->get_tab_rect(2)
|
||||
};
|
||||
CHECK(tab_bar->get_tab_alignment() == TabBar::ALIGNMENT_CENTER);
|
||||
float center_pos = tab_bar->get_size().x / 2;
|
||||
CHECK(tab_rects[0].position.x == center_pos - all_tabs_size.x / 2);
|
||||
CHECK(tab_rects[1].position.x == tab_rects[0].position.x + tab_rects[0].size.x);
|
||||
CHECK(tab_rects[2].position.x == tab_rects[1].position.x + tab_rects[1].size.x);
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] clip tabs") {
|
||||
// Clip tabs disabled means all tabs are visible and the minimum size holds all of them.
|
||||
tab_bar->set_clip_tabs(false);
|
||||
CHECK_FALSE(tab_bar->get_clip_tabs());
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab_bar->get_tab_offset() == 0);
|
||||
CHECK(tab_bar->get_minimum_size() == tab_bar->get_size());
|
||||
CHECK(tab_bar->get_size().x == tab_rects[0].size.x + tab_rects[1].size.x + tab_rects[2].size.x);
|
||||
CHECK(tab_bar->get_size().y == MAX(tab_rects[0].size.y, MAX(tab_rects[1].size.y, tab_rects[2].size.y)));
|
||||
|
||||
tab_bar->set_clip_tabs(true);
|
||||
CHECK(tab_bar->get_clip_tabs());
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab_bar->get_tab_offset() == 0);
|
||||
|
||||
// Horizontal size and minimum size get set to 0.
|
||||
CHECK(tab_bar->get_minimum_size().x == 0);
|
||||
CHECK(tab_bar->get_minimum_size().y == all_tabs_size.y);
|
||||
CHECK(tab_bar->get_size().x == 0);
|
||||
CHECK(tab_bar->get_size().y == all_tabs_size.y);
|
||||
}
|
||||
|
||||
SUBCASE("[TabBar] ensure tab visible") {
|
||||
tab_bar->set_scroll_to_selected(false);
|
||||
tab_bar->set_clip_tabs(true);
|
||||
|
||||
// Resize tab bar to only be able to fit 2 tabs.
|
||||
const float offset_button_size = tab_bar->get_theme_icon("decrement_icon")->get_width() + tab_bar->get_theme_icon("increment_icon")->get_width();
|
||||
tab_bar->set_size(Size2(tab_rects[2].size.x + tab_rects[1].size.x + offset_button_size, all_tabs_size.y));
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab_bar->get_tab_offset() == 0);
|
||||
CHECK(tab_bar->get_offset_buttons_visible());
|
||||
|
||||
// Scroll right to a tab that is not visible.
|
||||
tab_bar->ensure_tab_visible(2);
|
||||
CHECK(tab_bar->get_tab_offset() == 1);
|
||||
CHECK(tab_bar->get_tab_rect(1).position.x == 0);
|
||||
CHECK(tab_bar->get_tab_rect(2).position.x == tab_rects[1].size.x);
|
||||
|
||||
tab_bar->set_tab_offset(2);
|
||||
CHECK(tab_bar->get_tab_offset() == 2);
|
||||
CHECK(tab_bar->get_tab_rect(2).position.x == 0);
|
||||
|
||||
// Scroll left to a previous tab.
|
||||
tab_bar->ensure_tab_visible(1);
|
||||
CHECK(tab_bar->get_tab_offset() == 1);
|
||||
CHECK(tab_bar->get_tab_rect(1).position.x == 0);
|
||||
CHECK(tab_bar->get_tab_rect(2).position.x == tab_rects[1].size.x);
|
||||
|
||||
// Will not scroll if the tab is already visible.
|
||||
tab_bar->ensure_tab_visible(2);
|
||||
CHECK(tab_bar->get_tab_offset() == 1);
|
||||
CHECK(tab_bar->get_tab_rect(1).position.x == 0);
|
||||
CHECK(tab_bar->get_tab_rect(2).position.x == tab_rects[1].size.x);
|
||||
}
|
||||
|
||||
memdelete(tab_bar);
|
||||
}
|
||||
|
||||
// FIXME: Add tests for mouse click, keyboard navigation, and drag and drop.
|
||||
|
||||
} // namespace TestTabBar
|
||||
|
||||
#endif // TEST_TAB_BAR_H
|
||||
671
engine/tests/scene/test_tab_container.h
Normal file
671
engine/tests/scene/test_tab_container.h
Normal file
|
|
@ -0,0 +1,671 @@
|
|||
/**************************************************************************/
|
||||
/* test_tab_container.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_TAB_CONTAINER_H
|
||||
#define TEST_TAB_CONTAINER_H
|
||||
|
||||
#include "scene/gui/tab_container.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestTabContainer {
|
||||
static inline Array build_array() {
|
||||
return Array();
|
||||
}
|
||||
template <typename... Targs>
|
||||
static inline Array build_array(Variant item, Targs... Fargs) {
|
||||
Array a = build_array(Fargs...);
|
||||
a.push_front(item);
|
||||
return a;
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][TabContainer] tab operations") {
|
||||
TabContainer *tab_container = memnew(TabContainer);
|
||||
SceneTree::get_singleton()->get_root()->add_child(tab_container);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
SIGNAL_WATCH(tab_container, "tab_selected");
|
||||
SIGNAL_WATCH(tab_container, "tab_changed");
|
||||
|
||||
Control *tab0 = memnew(Control);
|
||||
tab0->set_name("tab0");
|
||||
Control *tab1 = memnew(Control);
|
||||
tab1->set_name("tab1");
|
||||
Control *tab2 = memnew(Control);
|
||||
tab2->set_name("tab2");
|
||||
|
||||
SUBCASE("[TabContainer] add tabs by adding children") {
|
||||
CHECK(tab_container->get_tab_count() == 0);
|
||||
CHECK(tab_container->get_current_tab() == -1);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
|
||||
// Add first tab child.
|
||||
tab_container->add_child(tab0);
|
||||
// MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab_container->get_tab_count() == 1);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(0)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(0)));
|
||||
|
||||
// Add second tab child.
|
||||
tab_container->add_child(tab1);
|
||||
CHECK(tab_container->get_tab_count() == 2);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Check default values, the title is the name of the child.
|
||||
CHECK(tab_container->get_tab_control(0) == tab0);
|
||||
CHECK(tab_container->get_tab_idx_from_control(tab0) == 0);
|
||||
CHECK(tab_container->get_tab_title(0) == "tab0");
|
||||
CHECK(tab_container->get_tab_tooltip(0) == "");
|
||||
CHECK_FALSE(tab_container->is_tab_disabled(0));
|
||||
CHECK_FALSE(tab_container->is_tab_hidden(0));
|
||||
|
||||
CHECK(tab_container->get_tab_control(1) == tab1);
|
||||
CHECK(tab_container->get_tab_idx_from_control(tab1) == 1);
|
||||
CHECK(tab_container->get_tab_title(1) == "tab1");
|
||||
CHECK(tab_container->get_tab_tooltip(1) == "");
|
||||
CHECK_FALSE(tab_container->is_tab_disabled(1));
|
||||
CHECK_FALSE(tab_container->is_tab_hidden(1));
|
||||
}
|
||||
|
||||
SUBCASE("[TabContainer] remove tabs by removing children") {
|
||||
tab_container->add_child(tab0);
|
||||
tab_container->add_child(tab1);
|
||||
tab_container->add_child(tab2);
|
||||
tab_container->set_current_tab(1);
|
||||
CHECK(tab_container->get_tab_count() == 3);
|
||||
CHECK(tab_container->get_current_tab() == 1);
|
||||
CHECK(tab_container->get_previous_tab() == 0);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
// Remove first tab.
|
||||
tab_container->remove_child(tab0);
|
||||
CHECK(tab_container->get_tab_count() == 2);
|
||||
CHECK(tab_container->get_tab_title(0) == "tab1");
|
||||
CHECK(tab_container->get_tab_title(1) == "tab2");
|
||||
CHECK(tab_container->get_tab_idx_from_control(tab1) == 0);
|
||||
CHECK(tab_container->get_tab_idx_from_control(tab2) == 1);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Remove last tab.
|
||||
tab_container->remove_child(tab2);
|
||||
CHECK(tab_container->get_tab_count() == 1);
|
||||
CHECK(tab_container->get_tab_title(0) == "tab1");
|
||||
CHECK(tab_container->get_tab_idx_from_control(tab1) == 0);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Remove only tab.
|
||||
tab_container->remove_child(tab1);
|
||||
CHECK(tab_container->get_tab_count() == 0);
|
||||
CHECK(tab_container->get_current_tab() == -1);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(-1)));
|
||||
|
||||
// Remove current tab when there are other tabs.
|
||||
tab_container->add_child(tab0);
|
||||
tab_container->add_child(tab1);
|
||||
tab_container->add_child(tab2);
|
||||
tab_container->set_current_tab(1);
|
||||
tab_container->set_current_tab(2);
|
||||
CHECK(tab_container->get_tab_count() == 3);
|
||||
CHECK(tab_container->get_current_tab() == 2);
|
||||
CHECK(tab_container->get_previous_tab() == 1);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
tab_container->remove_child(tab2);
|
||||
CHECK(tab_container->get_tab_count() == 2);
|
||||
CHECK(tab_container->get_current_tab() == 1);
|
||||
CHECK(tab_container->get_previous_tab() == 1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
|
||||
}
|
||||
|
||||
SUBCASE("[TabContainer] move tabs by moving children") {
|
||||
tab_container->add_child(tab0);
|
||||
tab_container->add_child(tab1);
|
||||
tab_container->add_child(tab2);
|
||||
tab_container->set_current_tab(1);
|
||||
CHECK(tab_container->get_current_tab() == 1);
|
||||
CHECK(tab_container->get_previous_tab() == 0);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
|
||||
// Move the first tab to the end.
|
||||
tab_container->move_child(tab0, 2);
|
||||
CHECK(tab_container->get_tab_idx_from_control(tab0) == 2);
|
||||
CHECK(tab_container->get_tab_idx_from_control(tab1) == 0);
|
||||
CHECK(tab_container->get_tab_idx_from_control(tab2) == 1);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == 2);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Move the second tab to the front.
|
||||
tab_container->move_child(tab2, 0);
|
||||
CHECK(tab_container->get_tab_idx_from_control(tab0) == 2);
|
||||
CHECK(tab_container->get_tab_idx_from_control(tab1) == 1);
|
||||
CHECK(tab_container->get_tab_idx_from_control(tab2) == 0);
|
||||
CHECK(tab_container->get_current_tab() == 1);
|
||||
CHECK(tab_container->get_previous_tab() == 2);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
}
|
||||
|
||||
SUBCASE("[TabContainer] set current tab") {
|
||||
tab_container->add_child(tab0);
|
||||
tab_container->add_child(tab1);
|
||||
tab_container->add_child(tab2);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(0)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(0)));
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab0->is_visible());
|
||||
CHECK_FALSE(tab1->is_visible());
|
||||
CHECK_FALSE(tab2->is_visible());
|
||||
|
||||
// Set the current tab.
|
||||
tab_container->set_current_tab(1);
|
||||
CHECK(tab_container->get_current_tab() == 1);
|
||||
CHECK(tab_container->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK_FALSE(tab0->is_visible());
|
||||
CHECK(tab1->is_visible());
|
||||
CHECK_FALSE(tab2->is_visible());
|
||||
|
||||
// Set to same tab.
|
||||
tab_container->set_current_tab(1);
|
||||
CHECK(tab_container->get_current_tab() == 1);
|
||||
CHECK(tab_container->get_previous_tab() == 1);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK_FALSE(tab0->is_visible());
|
||||
CHECK(tab1->is_visible());
|
||||
CHECK_FALSE(tab2->is_visible());
|
||||
|
||||
// Out of bounds.
|
||||
ERR_PRINT_OFF;
|
||||
tab_container->set_current_tab(-5);
|
||||
CHECK(tab_container->get_current_tab() == 1);
|
||||
CHECK(tab_container->get_previous_tab() == 1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK_FALSE(tab0->is_visible());
|
||||
CHECK(tab1->is_visible());
|
||||
CHECK_FALSE(tab2->is_visible());
|
||||
|
||||
tab_container->set_current_tab(5);
|
||||
CHECK(tab_container->get_current_tab() == 1);
|
||||
CHECK(tab_container->get_previous_tab() == 1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK_FALSE(tab0->is_visible());
|
||||
CHECK(tab1->is_visible());
|
||||
CHECK_FALSE(tab2->is_visible());
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
SUBCASE("[TabContainer] change current tab by changing visibility of children") {
|
||||
tab_container->add_child(tab0);
|
||||
tab_container->add_child(tab1);
|
||||
tab_container->add_child(tab2);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab0->is_visible());
|
||||
CHECK_FALSE(tab1->is_visible());
|
||||
CHECK_FALSE(tab2->is_visible());
|
||||
|
||||
// Show a child to make it the current tab.
|
||||
tab1->show();
|
||||
CHECK(tab_container->get_current_tab() == 1);
|
||||
CHECK(tab_container->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK_FALSE(tab0->is_visible());
|
||||
CHECK(tab1->is_visible());
|
||||
CHECK_FALSE(tab2->is_visible());
|
||||
|
||||
// Hide the visible child to select the next tab.
|
||||
tab1->hide();
|
||||
CHECK(tab_container->get_current_tab() == 2);
|
||||
CHECK(tab_container->get_previous_tab() == 1);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(2)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(2)));
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK_FALSE(tab0->is_visible());
|
||||
CHECK_FALSE(tab1->is_visible());
|
||||
CHECK(tab2->is_visible());
|
||||
|
||||
// Hide the visible child to select the previous tab if there is no next.
|
||||
tab2->hide();
|
||||
CHECK(tab_container->get_current_tab() == 1);
|
||||
CHECK(tab_container->get_previous_tab() == 2);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK_FALSE(tab0->is_visible());
|
||||
CHECK(tab1->is_visible());
|
||||
CHECK_FALSE(tab2->is_visible());
|
||||
|
||||
// Cannot hide if there is only one valid child since deselection is not enabled.
|
||||
tab_container->remove_child(tab1);
|
||||
tab_container->remove_child(tab2);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == 0);
|
||||
SIGNAL_DISCARD("tab_selected");
|
||||
SIGNAL_DISCARD("tab_changed");
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab0->is_visible());
|
||||
|
||||
tab0->hide();
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab0->is_visible());
|
||||
|
||||
// Can hide the last tab if deselection is enabled.
|
||||
tab_container->set_deselect_enabled(true);
|
||||
tab0->hide();
|
||||
CHECK(tab_container->get_current_tab() == -1);
|
||||
CHECK(tab_container->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(-1)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(-1)));
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK_FALSE(tab0->is_visible());
|
||||
}
|
||||
|
||||
SIGNAL_UNWATCH(tab_container, "tab_selected");
|
||||
SIGNAL_UNWATCH(tab_container, "tab_changed");
|
||||
|
||||
memdelete(tab2);
|
||||
memdelete(tab1);
|
||||
memdelete(tab0);
|
||||
memdelete(tab_container);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][TabContainer] initialization") {
|
||||
TabContainer *tab_container = memnew(TabContainer);
|
||||
|
||||
Control *tab0 = memnew(Control);
|
||||
tab0->set_name("tab0");
|
||||
Control *tab1 = memnew(Control);
|
||||
tab1->set_name("tab1 ");
|
||||
Control *tab2 = memnew(Control);
|
||||
tab2->set_name("tab2 ");
|
||||
|
||||
SIGNAL_WATCH(tab_container, "tab_selected");
|
||||
SIGNAL_WATCH(tab_container, "tab_changed");
|
||||
|
||||
SUBCASE("[TabContainer] add children before entering tree") {
|
||||
CHECK(tab_container->get_current_tab() == -1);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
|
||||
tab_container->add_child(tab0);
|
||||
CHECK(tab_container->get_tab_count() == 1);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
|
||||
tab_container->add_child(tab1);
|
||||
CHECK(tab_container->get_tab_count() == 2);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
|
||||
SceneTree::get_singleton()->get_root()->add_child(tab_container);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab_container->get_tab_count() == 2);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
CHECK(tab0->is_visible());
|
||||
CHECK_FALSE(tab1->is_visible());
|
||||
}
|
||||
|
||||
SUBCASE("[TabContainer] current tab can be set before children are added") {
|
||||
// Set the current tab before there are any tabs.
|
||||
// This queues the current tab to update on entering the tree.
|
||||
tab_container->set_current_tab(1);
|
||||
CHECK(tab_container->get_current_tab() == -1);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
tab_container->add_child(tab0);
|
||||
CHECK(tab_container->get_tab_count() == 1);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
|
||||
tab_container->add_child(tab1);
|
||||
CHECK(tab_container->get_tab_count() == 2);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
|
||||
tab_container->add_child(tab2);
|
||||
CHECK(tab_container->get_tab_count() == 3);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// Current tab is set when entering the tree.
|
||||
SceneTree::get_singleton()->get_root()->add_child(tab_container);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab_container->get_tab_count() == 3);
|
||||
CHECK(tab_container->get_current_tab() == 1);
|
||||
CHECK(tab_container->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
|
||||
CHECK_FALSE(tab0->is_visible());
|
||||
CHECK(tab1->is_visible());
|
||||
CHECK_FALSE(tab2->is_visible());
|
||||
}
|
||||
|
||||
SUBCASE("[TabContainer] cannot set current tab to an invalid value before tabs are set") {
|
||||
tab_container->set_current_tab(100);
|
||||
CHECK(tab_container->get_current_tab() == -1);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
tab_container->add_child(tab0);
|
||||
CHECK(tab_container->get_tab_count() == 1);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
tab_container->add_child(tab1);
|
||||
CHECK(tab_container->get_tab_count() == 2);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
|
||||
// This will print an error message as if `set_current_tab` was called after.
|
||||
ERR_PRINT_OFF;
|
||||
SceneTree::get_singleton()->get_root()->add_child(tab_container);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab_container->get_tab_count() == 2);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
SUBCASE("[TabContainer] children visibility before entering tree") {
|
||||
CHECK(tab_container->get_current_tab() == -1);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
|
||||
// Adding a hidden child first will change visibility because it is the current tab.
|
||||
tab0->hide();
|
||||
tab_container->add_child(tab0);
|
||||
CHECK(tab_container->get_tab_count() == 1);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab0->is_visible());
|
||||
|
||||
// Adding a visible child after will hide it because it is not the current tab.
|
||||
tab_container->add_child(tab1);
|
||||
CHECK(tab_container->get_tab_count() == 2);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab0->is_visible());
|
||||
CHECK_FALSE(tab1->is_visible());
|
||||
|
||||
// Can change current by showing child now after children have been added.
|
||||
// This queues the current tab to update on entering the tree.
|
||||
tab1->show();
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab_container->get_tab_count() == 2);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
CHECK(tab0->is_visible());
|
||||
CHECK(tab1->is_visible());
|
||||
|
||||
SceneTree::get_singleton()->get_root()->add_child(tab_container);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab_container->get_tab_count() == 2);
|
||||
CHECK(tab_container->get_current_tab() == 1);
|
||||
CHECK(tab_container->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
|
||||
CHECK_FALSE(tab0->is_visible());
|
||||
CHECK(tab1->is_visible());
|
||||
}
|
||||
|
||||
SUBCASE("[TabContainer] setting current tab and changing child visibility after adding") {
|
||||
tab_container->add_child(tab0);
|
||||
tab_container->add_child(tab1);
|
||||
tab_container->add_child(tab2);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
|
||||
tab2->show();
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab_container->get_tab_count() == 3);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
SIGNAL_CHECK_FALSE("tab_selected");
|
||||
SIGNAL_CHECK_FALSE("tab_changed");
|
||||
CHECK(tab0->is_visible());
|
||||
CHECK_FALSE(tab1->is_visible());
|
||||
CHECK(tab2->is_visible());
|
||||
|
||||
// Whichever happens last will have priority.
|
||||
tab_container->set_current_tab(1);
|
||||
CHECK(tab_container->get_current_tab() == 0);
|
||||
CHECK(tab_container->get_previous_tab() == -1);
|
||||
|
||||
// Current tab is set when entering the tree.
|
||||
SceneTree::get_singleton()->get_root()->add_child(tab_container);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(tab_container->get_tab_count() == 3);
|
||||
CHECK(tab_container->get_current_tab() == 1);
|
||||
CHECK(tab_container->get_previous_tab() == 0);
|
||||
SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
|
||||
SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
|
||||
CHECK_FALSE(tab0->is_visible());
|
||||
CHECK(tab1->is_visible());
|
||||
CHECK_FALSE(tab2->is_visible());
|
||||
}
|
||||
|
||||
SIGNAL_UNWATCH(tab_container, "tab_selected");
|
||||
SIGNAL_UNWATCH(tab_container, "tab_changed");
|
||||
|
||||
memdelete(tab2);
|
||||
memdelete(tab1);
|
||||
memdelete(tab0);
|
||||
memdelete(tab_container);
|
||||
}
|
||||
|
||||
TEST_CASE("[SceneTree][TabContainer] layout and offset") {
|
||||
TabContainer *tab_container = memnew(TabContainer);
|
||||
SceneTree::get_singleton()->get_root()->add_child(tab_container);
|
||||
tab_container->set_clip_tabs(false);
|
||||
|
||||
Control *tab0 = memnew(Control);
|
||||
tab0->set_name("tab0");
|
||||
Control *tab1 = memnew(Control);
|
||||
tab1->set_name("tab1 ");
|
||||
Control *tab2 = memnew(Control);
|
||||
tab2->set_name("tab2 ");
|
||||
|
||||
tab_container->add_child(tab0);
|
||||
tab_container->add_child(tab1);
|
||||
tab_container->add_child(tab2);
|
||||
|
||||
MessageQueue::get_singleton()->flush();
|
||||
|
||||
Size2 all_tabs_size = tab_container->get_size();
|
||||
const float side_margin = tab_container->get_theme_constant("side_margin");
|
||||
|
||||
TabBar *tab_bar = tab_container->get_tab_bar();
|
||||
|
||||
Vector<Rect2> tab_rects = {
|
||||
tab_bar->get_tab_rect(0),
|
||||
tab_bar->get_tab_rect(1),
|
||||
tab_bar->get_tab_rect(2)
|
||||
};
|
||||
|
||||
SUBCASE("[TabContainer] tabs are arranged next to each other") {
|
||||
// Horizontal positions are next to each other.
|
||||
CHECK(tab_rects[0].position.x == 0);
|
||||
CHECK(tab_rects[1].position.x == tab_rects[0].size.x);
|
||||
CHECK(tab_rects[2].position.x == tab_rects[1].position.x + tab_rects[1].size.x);
|
||||
|
||||
// Fills the entire width.
|
||||
CHECK(tab_rects[2].position.x + tab_rects[2].size.x == all_tabs_size.x - side_margin);
|
||||
|
||||
// Horizontal sizes are positive.
|
||||
CHECK(tab_rects[0].size.x > 0);
|
||||
CHECK(tab_rects[1].size.x > 0);
|
||||
CHECK(tab_rects[2].size.x > 0);
|
||||
|
||||
// Vertical positions are at 0.
|
||||
CHECK(tab_rects[0].position.y == 0);
|
||||
CHECK(tab_rects[1].position.y == 0);
|
||||
CHECK(tab_rects[2].position.y == 0);
|
||||
|
||||
// Vertical sizes are the same.
|
||||
CHECK(tab_rects[0].size.y == tab_rects[1].size.y);
|
||||
CHECK(tab_rects[1].size.y == tab_rects[2].size.y);
|
||||
}
|
||||
|
||||
SUBCASE("[TabContainer] tab position") {
|
||||
float tab_height = tab_rects[0].size.y;
|
||||
Ref<StyleBox> panel_style = tab_container->get_theme_stylebox("panel_style");
|
||||
|
||||
// Initial position, same as top position.
|
||||
// Tab bar is at the top.
|
||||
CHECK(tab_bar->get_anchor(SIDE_TOP) == 0);
|
||||
CHECK(tab_bar->get_anchor(SIDE_BOTTOM) == 0);
|
||||
CHECK(tab_bar->get_anchor(SIDE_LEFT) == 0);
|
||||
CHECK(tab_bar->get_anchor(SIDE_RIGHT) == 1);
|
||||
CHECK(tab_bar->get_offset(SIDE_TOP) == 0);
|
||||
CHECK(tab_bar->get_offset(SIDE_BOTTOM) == tab_height);
|
||||
CHECK(tab_bar->get_offset(SIDE_LEFT) == side_margin);
|
||||
CHECK(tab_bar->get_offset(SIDE_RIGHT) == 0);
|
||||
|
||||
// Child is expanded and below the tab bar.
|
||||
CHECK(tab0->get_anchor(SIDE_TOP) == 0);
|
||||
CHECK(tab0->get_anchor(SIDE_BOTTOM) == 1);
|
||||
CHECK(tab0->get_anchor(SIDE_LEFT) == 0);
|
||||
CHECK(tab0->get_anchor(SIDE_RIGHT) == 1);
|
||||
CHECK(tab0->get_offset(SIDE_TOP) == tab_height);
|
||||
CHECK(tab0->get_offset(SIDE_BOTTOM) == 0);
|
||||
CHECK(tab0->get_offset(SIDE_LEFT) == 0);
|
||||
CHECK(tab0->get_offset(SIDE_RIGHT) == 0);
|
||||
|
||||
// Bottom position.
|
||||
tab_container->set_tabs_position(TabContainer::POSITION_BOTTOM);
|
||||
CHECK(tab_container->get_tabs_position() == TabContainer::POSITION_BOTTOM);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
|
||||
// Tab bar is at the bottom.
|
||||
CHECK(tab_bar->get_anchor(SIDE_TOP) == 1);
|
||||
CHECK(tab_bar->get_anchor(SIDE_BOTTOM) == 1);
|
||||
CHECK(tab_bar->get_anchor(SIDE_LEFT) == 0);
|
||||
CHECK(tab_bar->get_anchor(SIDE_RIGHT) == 1);
|
||||
CHECK(tab_bar->get_offset(SIDE_TOP) == -tab_height);
|
||||
CHECK(tab_bar->get_offset(SIDE_BOTTOM) == 0);
|
||||
CHECK(tab_bar->get_offset(SIDE_LEFT) == side_margin);
|
||||
CHECK(tab_bar->get_offset(SIDE_RIGHT) == 0);
|
||||
|
||||
// Child is expanded and above the tab bar.
|
||||
CHECK(tab0->get_anchor(SIDE_TOP) == 0);
|
||||
CHECK(tab0->get_anchor(SIDE_BOTTOM) == 1);
|
||||
CHECK(tab0->get_anchor(SIDE_LEFT) == 0);
|
||||
CHECK(tab0->get_anchor(SIDE_RIGHT) == 1);
|
||||
CHECK(tab0->get_offset(SIDE_TOP) == 0);
|
||||
CHECK(tab0->get_offset(SIDE_BOTTOM) == -tab_height);
|
||||
CHECK(tab0->get_offset(SIDE_LEFT) == 0);
|
||||
CHECK(tab0->get_offset(SIDE_RIGHT) == 0);
|
||||
|
||||
// Top position.
|
||||
tab_container->set_tabs_position(TabContainer::POSITION_TOP);
|
||||
CHECK(tab_container->get_tabs_position() == TabContainer::POSITION_TOP);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
|
||||
// Tab bar is at the top.
|
||||
CHECK(tab_bar->get_anchor(SIDE_TOP) == 0);
|
||||
CHECK(tab_bar->get_anchor(SIDE_BOTTOM) == 0);
|
||||
CHECK(tab_bar->get_anchor(SIDE_LEFT) == 0);
|
||||
CHECK(tab_bar->get_anchor(SIDE_RIGHT) == 1);
|
||||
CHECK(tab_bar->get_offset(SIDE_TOP) == 0);
|
||||
CHECK(tab_bar->get_offset(SIDE_BOTTOM) == tab_height);
|
||||
CHECK(tab_bar->get_offset(SIDE_LEFT) == side_margin);
|
||||
CHECK(tab_bar->get_offset(SIDE_RIGHT) == 0);
|
||||
|
||||
// Child is expanded and below the tab bar.
|
||||
CHECK(tab0->get_anchor(SIDE_TOP) == 0);
|
||||
CHECK(tab0->get_anchor(SIDE_BOTTOM) == 1);
|
||||
CHECK(tab0->get_anchor(SIDE_LEFT) == 0);
|
||||
CHECK(tab0->get_anchor(SIDE_RIGHT) == 1);
|
||||
CHECK(tab0->get_offset(SIDE_TOP) == tab_height);
|
||||
CHECK(tab0->get_offset(SIDE_BOTTOM) == 0);
|
||||
CHECK(tab0->get_offset(SIDE_LEFT) == 0);
|
||||
CHECK(tab0->get_offset(SIDE_RIGHT) == 0);
|
||||
}
|
||||
|
||||
memdelete(tab_container);
|
||||
}
|
||||
|
||||
// FIXME: Add tests for mouse click, keyboard navigation, and drag and drop.
|
||||
|
||||
} // namespace TestTabContainer
|
||||
|
||||
#endif // TEST_TAB_CONTAINER_H
|
||||
|
|
@ -1763,6 +1763,28 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
|
|||
CHECK_FALSE(text_edit->has_selection());
|
||||
CHECK(text_edit->get_caret_line() == 0);
|
||||
CHECK(text_edit->get_caret_column() == 4);
|
||||
|
||||
// Wrapped lines.
|
||||
text_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY);
|
||||
text_edit->set_text("this is some text\nfor selection");
|
||||
text_edit->set_size(Size2(110, 100));
|
||||
MessageQueue::get_singleton()->flush();
|
||||
|
||||
// Line 0 wraps: 'this is ', 'some text'.
|
||||
// Line 1 wraps: 'for ', 'selection'.
|
||||
CHECK(text_edit->is_line_wrapped(0));
|
||||
|
||||
// Select to the first character of a wrapped line.
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 11).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 8).get_center(), MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK(text_edit->has_selection());
|
||||
CHECK(text_edit->get_selected_text() == "so");
|
||||
CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_POINTER);
|
||||
CHECK(text_edit->get_selection_origin_line() == 0);
|
||||
CHECK(text_edit->get_selection_origin_column() == 10);
|
||||
CHECK(text_edit->get_caret_line() == 0);
|
||||
CHECK(text_edit->get_caret_column() == 8);
|
||||
CHECK(text_edit->is_dragging_cursor());
|
||||
}
|
||||
|
||||
SUBCASE("[TextEdit] mouse word select") {
|
||||
|
|
@ -3563,6 +3585,54 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
|
|||
SIGNAL_CHECK_FALSE("lines_edited_from");
|
||||
}
|
||||
|
||||
SUBCASE("[TextEdit] cut when empty selection clipboard disabled") {
|
||||
text_edit->set_empty_selection_clipboard_enabled(false);
|
||||
DS->clipboard_set("");
|
||||
|
||||
text_edit->set_text("this is\nsome\n");
|
||||
text_edit->set_caret_line(0);
|
||||
text_edit->set_caret_column(6);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
SIGNAL_DISCARD("text_set");
|
||||
SIGNAL_DISCARD("text_changed");
|
||||
SIGNAL_DISCARD("lines_edited_from");
|
||||
SIGNAL_DISCARD("caret_changed");
|
||||
|
||||
text_edit->cut();
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(DS->clipboard_get() == "");
|
||||
CHECK(text_edit->get_text() == "this is\nsome\n");
|
||||
CHECK(text_edit->get_caret_line() == 0);
|
||||
CHECK(text_edit->get_caret_column() == 6);
|
||||
SIGNAL_CHECK_FALSE("caret_changed");
|
||||
SIGNAL_CHECK_FALSE("text_changed");
|
||||
SIGNAL_CHECK_FALSE("lines_edited_from");
|
||||
}
|
||||
|
||||
SUBCASE("[TextEdit] copy when empty selection clipboard disabled") {
|
||||
text_edit->set_empty_selection_clipboard_enabled(false);
|
||||
DS->clipboard_set("");
|
||||
|
||||
text_edit->set_text("this is\nsome\n");
|
||||
text_edit->set_caret_line(0);
|
||||
text_edit->set_caret_column(6);
|
||||
MessageQueue::get_singleton()->flush();
|
||||
SIGNAL_DISCARD("text_set");
|
||||
SIGNAL_DISCARD("text_changed");
|
||||
SIGNAL_DISCARD("lines_edited_from");
|
||||
SIGNAL_DISCARD("caret_changed");
|
||||
|
||||
text_edit->copy();
|
||||
MessageQueue::get_singleton()->flush();
|
||||
CHECK(DS->clipboard_get() == "");
|
||||
CHECK(text_edit->get_text() == "this is\nsome\n");
|
||||
CHECK(text_edit->get_caret_line() == 0);
|
||||
CHECK(text_edit->get_caret_column() == 6);
|
||||
SIGNAL_CHECK_FALSE("caret_changed");
|
||||
SIGNAL_CHECK_FALSE("text_changed");
|
||||
SIGNAL_CHECK_FALSE("lines_edited_from");
|
||||
}
|
||||
|
||||
SIGNAL_UNWATCH(text_edit, "text_set");
|
||||
SIGNAL_UNWATCH(text_edit, "text_changed");
|
||||
SIGNAL_UNWATCH(text_edit, "lines_edited_from");
|
||||
|
|
@ -4210,6 +4280,18 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
|
|||
CHECK(text_edit->get_caret_line(0) == 0);
|
||||
CHECK(text_edit->get_caret_column(0) == 4);
|
||||
text_edit->remove_secondary_carets();
|
||||
|
||||
// Remove when there are no words, only symbols.
|
||||
text_edit->set_text("#{}");
|
||||
text_edit->set_caret_line(0);
|
||||
text_edit->set_caret_column(3);
|
||||
|
||||
SEND_GUI_ACTION("ui_text_backspace_word");
|
||||
CHECK(text_edit->get_viewport()->is_input_handled());
|
||||
CHECK_FALSE(text_edit->has_selection());
|
||||
CHECK(text_edit->get_text() == "");
|
||||
CHECK(text_edit->get_caret_line(0) == 0);
|
||||
CHECK(text_edit->get_caret_column(0) == 0);
|
||||
}
|
||||
|
||||
SUBCASE("[TextEdit] ui_text_backspace_word same line") {
|
||||
|
|
@ -4869,6 +4951,18 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
|
|||
CHECK(text_edit->get_caret_line(0) == 0);
|
||||
CHECK(text_edit->get_caret_column(0) == 2);
|
||||
text_edit->remove_secondary_carets();
|
||||
|
||||
// Remove when there are no words, only symbols.
|
||||
text_edit->set_text("#{}");
|
||||
text_edit->set_caret_line(0);
|
||||
text_edit->set_caret_column(0);
|
||||
|
||||
SEND_GUI_ACTION("ui_text_delete_word");
|
||||
CHECK(text_edit->get_viewport()->is_input_handled());
|
||||
CHECK_FALSE(text_edit->has_selection());
|
||||
CHECK(text_edit->get_text() == "");
|
||||
CHECK(text_edit->get_caret_line(0) == 0);
|
||||
CHECK(text_edit->get_caret_column(0) == 0);
|
||||
}
|
||||
|
||||
SUBCASE("[TextEdit] ui_text_delete_word same line") {
|
||||
|
|
@ -5279,6 +5373,16 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
|
|||
SIGNAL_CHECK("caret_changed", empty_signal_args);
|
||||
SIGNAL_CHECK_FALSE("text_changed");
|
||||
SIGNAL_CHECK_FALSE("lines_edited_from");
|
||||
|
||||
// Move when there are no words, only symbols.
|
||||
text_edit->set_text("#{}");
|
||||
text_edit->set_caret_line(0);
|
||||
text_edit->set_caret_column(3);
|
||||
|
||||
SEND_GUI_ACTION("ui_text_caret_word_left");
|
||||
CHECK(text_edit->get_viewport()->is_input_handled());
|
||||
CHECK(text_edit->get_caret_line(0) == 0);
|
||||
CHECK(text_edit->get_caret_column(0) == 0);
|
||||
}
|
||||
|
||||
SUBCASE("[TextEdit] ui_text_caret_left") {
|
||||
|
|
@ -5541,6 +5645,16 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
|
|||
SIGNAL_CHECK("caret_changed", empty_signal_args);
|
||||
SIGNAL_CHECK_FALSE("text_changed");
|
||||
SIGNAL_CHECK_FALSE("lines_edited_from");
|
||||
|
||||
// Move when there are no words, only symbols.
|
||||
text_edit->set_text("#{}");
|
||||
text_edit->set_caret_line(0);
|
||||
text_edit->set_caret_column(0);
|
||||
|
||||
SEND_GUI_ACTION("ui_text_caret_word_right");
|
||||
CHECK(text_edit->get_viewport()->is_input_handled());
|
||||
CHECK(text_edit->get_caret_line(0) == 0);
|
||||
CHECK(text_edit->get_caret_column(0) == 3);
|
||||
}
|
||||
|
||||
SUBCASE("[TextEdit] ui_text_caret_right") {
|
||||
|
|
@ -5713,6 +5827,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
|
|||
CHECK(text_edit->get_caret_count() == 2);
|
||||
|
||||
MessageQueue::get_singleton()->flush();
|
||||
// Lines 0 and 4 are wrapped into 2 parts: 'this is ' and 'some'.
|
||||
CHECK(text_edit->is_line_wrapped(0));
|
||||
SIGNAL_DISCARD("text_set");
|
||||
SIGNAL_DISCARD("text_changed");
|
||||
|
|
@ -5762,9 +5877,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
|
|||
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.
|
||||
text_edit->set_caret_column(12, false);
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
CHECK(text_edit->get_viewport()->is_input_handled());
|
||||
CHECK(text_edit->get_caret_line() == 0);
|
||||
|
|
@ -5777,6 +5892,23 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
|
|||
SIGNAL_CHECK("caret_changed", empty_signal_args);
|
||||
SIGNAL_CHECK_FALSE("text_changed");
|
||||
SIGNAL_CHECK_FALSE("lines_edited_from");
|
||||
|
||||
// Normal up from column 0 to a wrapped line.
|
||||
text_edit->remove_secondary_carets();
|
||||
text_edit->set_caret_line(5);
|
||||
text_edit->set_caret_column(0);
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
CHECK(text_edit->get_viewport()->is_input_handled());
|
||||
CHECK(text_edit->get_caret_line() == 4);
|
||||
CHECK(text_edit->get_caret_column() == 8);
|
||||
CHECK_FALSE(text_edit->has_selection(0));
|
||||
|
||||
// Normal up to column 0 of a wrapped line.
|
||||
SEND_GUI_ACTION("ui_text_caret_up");
|
||||
CHECK(text_edit->get_viewport()->is_input_handled());
|
||||
CHECK(text_edit->get_caret_line() == 4);
|
||||
CHECK(text_edit->get_caret_column() == 0);
|
||||
CHECK_FALSE(text_edit->has_selection(0));
|
||||
}
|
||||
|
||||
SUBCASE("[TextEdit] ui_text_caret_down") {
|
||||
|
|
@ -5792,6 +5924,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
|
|||
|
||||
MessageQueue::get_singleton()->flush();
|
||||
|
||||
// Lines 3 and 7 are wrapped into 2 parts: 'this is ' and 'some'.
|
||||
CHECK(text_edit->is_line_wrapped(3));
|
||||
SIGNAL_DISCARD("text_set");
|
||||
SIGNAL_DISCARD("text_changed");
|
||||
|
|
@ -5841,9 +5974,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
|
|||
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.
|
||||
text_edit->set_caret_column(7, false);
|
||||
SEND_GUI_ACTION("ui_text_caret_down");
|
||||
CHECK(text_edit->get_viewport()->is_input_handled());
|
||||
CHECK(text_edit->get_caret_line() == 3);
|
||||
|
|
@ -5856,6 +5989,23 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
|
|||
SIGNAL_CHECK("caret_changed", empty_signal_args);
|
||||
SIGNAL_CHECK_FALSE("text_changed");
|
||||
SIGNAL_CHECK_FALSE("lines_edited_from");
|
||||
|
||||
// Normal down to column 0 of a wrapped line.
|
||||
text_edit->remove_secondary_carets();
|
||||
text_edit->set_caret_line(3);
|
||||
text_edit->set_caret_column(0);
|
||||
SEND_GUI_ACTION("ui_text_caret_down");
|
||||
CHECK(text_edit->get_viewport()->is_input_handled());
|
||||
CHECK(text_edit->get_caret_line() == 3);
|
||||
CHECK(text_edit->get_caret_column() == 8);
|
||||
CHECK_FALSE(text_edit->has_selection(0));
|
||||
|
||||
// Normal down out of visual column 0 of a wrapped line moves to start of next line.
|
||||
SEND_GUI_ACTION("ui_text_caret_down");
|
||||
CHECK(text_edit->get_viewport()->is_input_handled());
|
||||
CHECK(text_edit->get_caret_line() == 4);
|
||||
CHECK(text_edit->get_caret_column() == 0);
|
||||
CHECK_FALSE(text_edit->has_selection(0));
|
||||
}
|
||||
|
||||
SUBCASE("[TextEdit] ui_text_caret_document_start") {
|
||||
|
|
@ -7162,7 +7312,7 @@ TEST_CASE("[SceneTree][TextEdit] multicaret") {
|
|||
CHECK(text_edit->get_caret_line(0) == 2);
|
||||
CHECK(text_edit->get_caret_column(0) == 5);
|
||||
CHECK(text_edit->get_caret_line(1) == 2);
|
||||
CHECK(text_edit->get_caret_column(1) == 10);
|
||||
CHECK(text_edit->get_caret_column(1) == 6);
|
||||
|
||||
// Cannot add caret below from last line last line wrap.
|
||||
text_edit->add_caret_at_carets(true);
|
||||
|
|
@ -7171,7 +7321,7 @@ TEST_CASE("[SceneTree][TextEdit] multicaret") {
|
|||
CHECK(text_edit->get_caret_line(0) == 2);
|
||||
CHECK(text_edit->get_caret_column(0) == 5);
|
||||
CHECK(text_edit->get_caret_line(1) == 2);
|
||||
CHECK(text_edit->get_caret_column(1) == 10);
|
||||
CHECK(text_edit->get_caret_column(1) == 6);
|
||||
|
||||
// Add caret above from not first line wrap.
|
||||
text_edit->remove_secondary_carets();
|
||||
|
|
@ -8001,6 +8151,93 @@ TEST_CASE("[SceneTree][TextEdit] gutters") {
|
|||
// Merging tested via CodeEdit gutters.
|
||||
}
|
||||
|
||||
SUBCASE("[TextEdit] gutter mouse") {
|
||||
DisplayServerMock *DS = (DisplayServerMock *)(DisplayServer::get_singleton());
|
||||
// Set size for mouse input.
|
||||
text_edit->set_size(Size2(200, 200));
|
||||
|
||||
text_edit->set_text("test1\ntest2\ntest3\ntest4");
|
||||
text_edit->grab_focus();
|
||||
|
||||
text_edit->add_gutter();
|
||||
text_edit->set_gutter_name(0, "test_gutter");
|
||||
text_edit->set_gutter_width(0, 10);
|
||||
text_edit->set_gutter_clickable(0, true);
|
||||
|
||||
text_edit->add_gutter();
|
||||
text_edit->set_gutter_name(1, "test_gutter_not_clickable");
|
||||
text_edit->set_gutter_width(1, 10);
|
||||
text_edit->set_gutter_clickable(1, false);
|
||||
|
||||
text_edit->add_gutter();
|
||||
CHECK(text_edit->get_gutter_count() == 3);
|
||||
text_edit->set_gutter_name(2, "test_gutter_3");
|
||||
text_edit->set_gutter_width(2, 10);
|
||||
text_edit->set_gutter_clickable(2, true);
|
||||
|
||||
MessageQueue::get_singleton()->flush();
|
||||
const int line_height = text_edit->get_line_height();
|
||||
|
||||
// Defaults to none.
|
||||
CHECK(text_edit->get_hovered_gutter() == Vector2i(-1, -1));
|
||||
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_ARROW);
|
||||
|
||||
// Hover over gutter.
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(Point2(5, line_height + line_height / 2), MouseButtonMask::NONE, Key::NONE);
|
||||
CHECK(text_edit->get_hovered_gutter() == Vector2i(0, 1));
|
||||
SIGNAL_CHECK_FALSE("gutter_clicked");
|
||||
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_POINTING_HAND);
|
||||
|
||||
// Click on gutter.
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(Point2(5, line_height / 2), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK(text_edit->get_hovered_gutter() == Vector2i(0, 0));
|
||||
SIGNAL_CHECK("gutter_clicked", build_array(build_array(0, 0)));
|
||||
|
||||
// Click on gutter on another line.
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(Point2(5, line_height * 3 + line_height / 2), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK(text_edit->get_hovered_gutter() == Vector2i(0, 3));
|
||||
SIGNAL_CHECK("gutter_clicked", build_array(build_array(3, 0)));
|
||||
|
||||
// Unclickable gutter can be hovered.
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(Point2(15, line_height + line_height / 2), MouseButtonMask::NONE, Key::NONE);
|
||||
CHECK(text_edit->get_hovered_gutter() == Vector2i(1, 1));
|
||||
SIGNAL_CHECK_FALSE("gutter_clicked");
|
||||
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_ARROW);
|
||||
|
||||
// Unclickable gutter can be clicked.
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(Point2(15, line_height * 2 + line_height / 2), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK(text_edit->get_hovered_gutter() == Vector2i(1, 2));
|
||||
SIGNAL_CHECK("gutter_clicked", build_array(build_array(2, 1)));
|
||||
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_ARROW);
|
||||
|
||||
// Hover past last line.
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(Point2(5, line_height * 5), MouseButtonMask::NONE, Key::NONE);
|
||||
CHECK(text_edit->get_hovered_gutter() == Vector2i(-1, -1));
|
||||
SIGNAL_CHECK_FALSE("gutter_clicked");
|
||||
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_ARROW);
|
||||
|
||||
// Click on gutter past last line.
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(Point2(5, line_height * 5), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK(text_edit->get_hovered_gutter() == Vector2i(-1, -1));
|
||||
SIGNAL_CHECK_FALSE("gutter_clicked");
|
||||
|
||||
// Mouse exit resets hover.
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(Point2(5, line_height + line_height / 2), MouseButtonMask::NONE, Key::NONE);
|
||||
CHECK(text_edit->get_hovered_gutter() == Vector2i(0, 1));
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(Point2(-1, -1), MouseButtonMask::NONE, Key::NONE);
|
||||
CHECK(text_edit->get_hovered_gutter() == Vector2i(-1, -1));
|
||||
|
||||
// Removing gutter updates hover.
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(Point2(25, line_height + line_height / 2), MouseButtonMask::NONE, Key::NONE);
|
||||
CHECK(text_edit->get_hovered_gutter() == Vector2i(2, 1));
|
||||
text_edit->remove_gutter(2);
|
||||
CHECK(text_edit->get_hovered_gutter() == Vector2i(-1, -1));
|
||||
|
||||
// Updating size updates hover.
|
||||
text_edit->set_gutter_width(1, 20);
|
||||
CHECK(text_edit->get_hovered_gutter() == Vector2i(1, 1));
|
||||
}
|
||||
|
||||
SIGNAL_UNWATCH(text_edit, "gutter_clicked");
|
||||
SIGNAL_UNWATCH(text_edit, "gutter_added");
|
||||
SIGNAL_UNWATCH(text_edit, "gutter_removed");
|
||||
|
|
|
|||
92
engine/tests/scene/test_texture_progress_bar.h
Normal file
92
engine/tests/scene/test_texture_progress_bar.h
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/**************************************************************************/
|
||||
/* test_texture_progress_bar.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_TEXTURE_PROGRESS_BAR_H
|
||||
#define TEST_TEXTURE_PROGRESS_BAR_H
|
||||
|
||||
#include "scene/gui/texture_progress_bar.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestTextureProgressBar {
|
||||
|
||||
TEST_CASE("[SceneTree][TextureProgressBar]") {
|
||||
TextureProgressBar *texture_progress_bar = memnew(TextureProgressBar);
|
||||
|
||||
SUBCASE("[TextureProgressBar] set_radial_initial_angle() should wrap angle between 0 and 360 degrees (inclusive).") {
|
||||
texture_progress_bar->set_radial_initial_angle(0.0);
|
||||
CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)0.0));
|
||||
|
||||
texture_progress_bar->set_radial_initial_angle(360.0);
|
||||
CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)360.0));
|
||||
|
||||
texture_progress_bar->set_radial_initial_angle(30.5);
|
||||
CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)30.5));
|
||||
|
||||
texture_progress_bar->set_radial_initial_angle(-30.5);
|
||||
CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)(360 - 30.5)));
|
||||
|
||||
texture_progress_bar->set_radial_initial_angle(36000 + 30.5);
|
||||
CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)30.5));
|
||||
|
||||
texture_progress_bar->set_radial_initial_angle(-(36000 + 30.5));
|
||||
CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)(360 - 30.5)));
|
||||
}
|
||||
|
||||
SUBCASE("[TextureProgressBar] set_radial_initial_angle() should not set non-finite values.") {
|
||||
texture_progress_bar->set_radial_initial_angle(30.5);
|
||||
|
||||
ERR_PRINT_OFF;
|
||||
texture_progress_bar->set_radial_initial_angle(INFINITY);
|
||||
ERR_PRINT_ON;
|
||||
CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)30.5));
|
||||
|
||||
ERR_PRINT_OFF;
|
||||
texture_progress_bar->set_radial_initial_angle(-INFINITY);
|
||||
ERR_PRINT_ON;
|
||||
CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)30.5));
|
||||
|
||||
ERR_PRINT_OFF;
|
||||
texture_progress_bar->set_radial_initial_angle(NAN);
|
||||
ERR_PRINT_ON;
|
||||
CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)30.5));
|
||||
|
||||
ERR_PRINT_OFF;
|
||||
texture_progress_bar->set_radial_initial_angle(-NAN);
|
||||
ERR_PRINT_ON;
|
||||
CHECK(Math::is_equal_approx(texture_progress_bar->get_radial_initial_angle(), (float)30.5));
|
||||
}
|
||||
|
||||
memdelete(texture_progress_bar);
|
||||
}
|
||||
|
||||
} // namespace TestTextureProgressBar
|
||||
|
||||
#endif // TEST_TEXTURE_PROGRESS_BAR_H
|
||||
301
engine/tests/scene/test_tree.h
Normal file
301
engine/tests/scene/test_tree.h
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
/**************************************************************************/
|
||||
/* test_tree.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_TREE_H
|
||||
#define TEST_TREE_H
|
||||
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestTree {
|
||||
|
||||
TEST_CASE("[SceneTree][Tree]") {
|
||||
SUBCASE("[Tree] Create and remove items.") {
|
||||
Tree *tree = memnew(Tree);
|
||||
TreeItem *root = tree->create_item();
|
||||
|
||||
TreeItem *child1 = tree->create_item();
|
||||
CHECK_EQ(root->get_child_count(), 1);
|
||||
|
||||
TreeItem *child2 = tree->create_item(root);
|
||||
CHECK_EQ(root->get_child_count(), 2);
|
||||
|
||||
TreeItem *child3 = tree->create_item(root, 0);
|
||||
CHECK_EQ(root->get_child_count(), 3);
|
||||
|
||||
CHECK_EQ(root->get_child(0), child3);
|
||||
CHECK_EQ(root->get_child(1), child1);
|
||||
CHECK_EQ(root->get_child(2), child2);
|
||||
|
||||
root->remove_child(child3);
|
||||
CHECK_EQ(root->get_child_count(), 2);
|
||||
|
||||
root->add_child(child3);
|
||||
CHECK_EQ(root->get_child_count(), 3);
|
||||
|
||||
TreeItem *child4 = root->create_child();
|
||||
CHECK_EQ(root->get_child_count(), 4);
|
||||
|
||||
CHECK_EQ(root->get_child(0), child1);
|
||||
CHECK_EQ(root->get_child(1), child2);
|
||||
CHECK_EQ(root->get_child(2), child3);
|
||||
CHECK_EQ(root->get_child(3), child4);
|
||||
|
||||
memdelete(tree);
|
||||
}
|
||||
|
||||
SUBCASE("[Tree] Clear items.") {
|
||||
Tree *tree = memnew(Tree);
|
||||
TreeItem *root = tree->create_item();
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
tree->create_item();
|
||||
}
|
||||
CHECK_EQ(root->get_child_count(), 10);
|
||||
|
||||
root->clear_children();
|
||||
CHECK_EQ(root->get_child_count(), 0);
|
||||
|
||||
memdelete(tree);
|
||||
}
|
||||
|
||||
SUBCASE("[Tree] Get last item.") {
|
||||
Tree *tree = memnew(Tree);
|
||||
TreeItem *root = tree->create_item();
|
||||
|
||||
TreeItem *last;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
last = tree->create_item();
|
||||
}
|
||||
CHECK_EQ(root->get_child_count(), 10);
|
||||
CHECK_EQ(tree->get_last_item(), last);
|
||||
|
||||
// Check nested.
|
||||
TreeItem *old_last = last;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
last = tree->create_item(old_last);
|
||||
}
|
||||
CHECK_EQ(tree->get_last_item(), last);
|
||||
|
||||
memdelete(tree);
|
||||
}
|
||||
|
||||
// https://github.com/godotengine/godot/issues/96205
|
||||
SUBCASE("[Tree] Get last item after removal.") {
|
||||
Tree *tree = memnew(Tree);
|
||||
TreeItem *root = tree->create_item();
|
||||
|
||||
TreeItem *child1 = tree->create_item(root);
|
||||
TreeItem *child2 = tree->create_item(root);
|
||||
|
||||
CHECK_EQ(root->get_child_count(), 2);
|
||||
CHECK_EQ(tree->get_last_item(), child2);
|
||||
|
||||
root->remove_child(child2);
|
||||
|
||||
CHECK_EQ(root->get_child_count(), 1);
|
||||
CHECK_EQ(tree->get_last_item(), child1);
|
||||
|
||||
root->add_child(child2);
|
||||
|
||||
CHECK_EQ(root->get_child_count(), 2);
|
||||
CHECK_EQ(tree->get_last_item(), child2);
|
||||
|
||||
memdelete(tree);
|
||||
}
|
||||
|
||||
SUBCASE("[Tree] Previous and Next items.") {
|
||||
Tree *tree = memnew(Tree);
|
||||
TreeItem *root = tree->create_item();
|
||||
|
||||
TreeItem *child1 = tree->create_item();
|
||||
TreeItem *child2 = tree->create_item();
|
||||
TreeItem *child3 = tree->create_item();
|
||||
CHECK_EQ(root->get_next(), nullptr);
|
||||
CHECK_EQ(root->get_next_visible(), child1);
|
||||
CHECK_EQ(root->get_next_in_tree(), child1);
|
||||
CHECK_EQ(child1->get_next(), child2);
|
||||
CHECK_EQ(child1->get_next_visible(), child2);
|
||||
CHECK_EQ(child1->get_next_in_tree(), child2);
|
||||
CHECK_EQ(child2->get_next(), child3);
|
||||
CHECK_EQ(child2->get_next_visible(), child3);
|
||||
CHECK_EQ(child2->get_next_in_tree(), child3);
|
||||
CHECK_EQ(child3->get_next(), nullptr);
|
||||
CHECK_EQ(child3->get_next_visible(), nullptr);
|
||||
CHECK_EQ(child3->get_next_in_tree(), nullptr);
|
||||
|
||||
CHECK_EQ(root->get_prev(), nullptr);
|
||||
CHECK_EQ(root->get_prev_visible(), nullptr);
|
||||
CHECK_EQ(root->get_prev_in_tree(), nullptr);
|
||||
CHECK_EQ(child1->get_prev(), nullptr);
|
||||
CHECK_EQ(child1->get_prev_visible(), root);
|
||||
CHECK_EQ(child1->get_prev_in_tree(), root);
|
||||
CHECK_EQ(child2->get_prev(), child1);
|
||||
CHECK_EQ(child2->get_prev_visible(), child1);
|
||||
CHECK_EQ(child2->get_prev_in_tree(), child1);
|
||||
CHECK_EQ(child3->get_prev(), child2);
|
||||
CHECK_EQ(child3->get_prev_visible(), child2);
|
||||
CHECK_EQ(child3->get_prev_in_tree(), child2);
|
||||
|
||||
TreeItem *nested1 = tree->create_item(child2);
|
||||
TreeItem *nested2 = tree->create_item(child2);
|
||||
TreeItem *nested3 = tree->create_item(child2);
|
||||
|
||||
CHECK_EQ(child1->get_next(), child2);
|
||||
CHECK_EQ(child1->get_next_visible(), child2);
|
||||
CHECK_EQ(child1->get_next_in_tree(), child2);
|
||||
CHECK_EQ(child2->get_next(), child3);
|
||||
CHECK_EQ(child2->get_next_visible(), nested1);
|
||||
CHECK_EQ(child2->get_next_in_tree(), nested1);
|
||||
CHECK_EQ(child3->get_prev(), child2);
|
||||
CHECK_EQ(child3->get_prev_visible(), nested3);
|
||||
CHECK_EQ(child3->get_prev_in_tree(), nested3);
|
||||
CHECK_EQ(nested1->get_prev_in_tree(), child2);
|
||||
CHECK_EQ(nested1->get_next_in_tree(), nested2);
|
||||
CHECK_EQ(nested3->get_next_in_tree(), child3);
|
||||
|
||||
memdelete(tree);
|
||||
}
|
||||
|
||||
SUBCASE("[Tree] Previous and Next items with hide root.") {
|
||||
Tree *tree = memnew(Tree);
|
||||
tree->set_hide_root(true);
|
||||
TreeItem *root = tree->create_item();
|
||||
|
||||
TreeItem *child1 = tree->create_item();
|
||||
TreeItem *child2 = tree->create_item();
|
||||
TreeItem *child3 = tree->create_item();
|
||||
CHECK_EQ(root->get_next(), nullptr);
|
||||
CHECK_EQ(root->get_next_visible(), child1);
|
||||
CHECK_EQ(root->get_next_in_tree(), child1);
|
||||
CHECK_EQ(child1->get_next(), child2);
|
||||
CHECK_EQ(child1->get_next_visible(), child2);
|
||||
CHECK_EQ(child1->get_next_in_tree(), child2);
|
||||
CHECK_EQ(child2->get_next(), child3);
|
||||
CHECK_EQ(child2->get_next_visible(), child3);
|
||||
CHECK_EQ(child2->get_next_in_tree(), child3);
|
||||
CHECK_EQ(child3->get_next(), nullptr);
|
||||
CHECK_EQ(child3->get_next_visible(), nullptr);
|
||||
CHECK_EQ(child3->get_next_in_tree(), nullptr);
|
||||
|
||||
CHECK_EQ(root->get_prev(), nullptr);
|
||||
CHECK_EQ(root->get_prev_visible(), nullptr);
|
||||
CHECK_EQ(root->get_prev_in_tree(), nullptr);
|
||||
CHECK_EQ(child1->get_prev(), nullptr);
|
||||
CHECK_EQ(child1->get_prev_visible(), nullptr);
|
||||
CHECK_EQ(child1->get_prev_in_tree(), nullptr);
|
||||
CHECK_EQ(child2->get_prev(), child1);
|
||||
CHECK_EQ(child2->get_prev_visible(), child1);
|
||||
CHECK_EQ(child2->get_prev_in_tree(), child1);
|
||||
CHECK_EQ(child3->get_prev(), child2);
|
||||
CHECK_EQ(child3->get_prev_visible(), child2);
|
||||
CHECK_EQ(child3->get_prev_in_tree(), child2);
|
||||
|
||||
memdelete(tree);
|
||||
}
|
||||
|
||||
SUBCASE("[Tree] Previous and Next items wrapping.") {
|
||||
Tree *tree = memnew(Tree);
|
||||
TreeItem *root = tree->create_item();
|
||||
|
||||
TreeItem *child1 = tree->create_item();
|
||||
TreeItem *child2 = tree->create_item();
|
||||
TreeItem *child3 = tree->create_item();
|
||||
CHECK_EQ(root->get_next_visible(true), child1);
|
||||
CHECK_EQ(root->get_next_in_tree(true), child1);
|
||||
CHECK_EQ(child1->get_next_visible(true), child2);
|
||||
CHECK_EQ(child1->get_next_in_tree(true), child2);
|
||||
CHECK_EQ(child2->get_next_visible(true), child3);
|
||||
CHECK_EQ(child2->get_next_in_tree(true), child3);
|
||||
CHECK_EQ(child3->get_next_visible(true), root);
|
||||
CHECK_EQ(child3->get_next_in_tree(true), root);
|
||||
|
||||
CHECK_EQ(root->get_prev_visible(true), child3);
|
||||
CHECK_EQ(root->get_prev_in_tree(true), child3);
|
||||
CHECK_EQ(child1->get_prev_visible(true), root);
|
||||
CHECK_EQ(child1->get_prev_in_tree(true), root);
|
||||
CHECK_EQ(child2->get_prev_visible(true), child1);
|
||||
CHECK_EQ(child2->get_prev_in_tree(true), child1);
|
||||
CHECK_EQ(child3->get_prev_visible(true), child2);
|
||||
CHECK_EQ(child3->get_prev_in_tree(true), child2);
|
||||
|
||||
TreeItem *nested1 = tree->create_item(child2);
|
||||
TreeItem *nested2 = tree->create_item(child2);
|
||||
TreeItem *nested3 = tree->create_item(child2);
|
||||
|
||||
CHECK_EQ(child1->get_next_visible(true), child2);
|
||||
CHECK_EQ(child1->get_next_in_tree(true), child2);
|
||||
CHECK_EQ(child2->get_next_visible(true), nested1);
|
||||
CHECK_EQ(child2->get_next_in_tree(true), nested1);
|
||||
CHECK_EQ(nested3->get_next_visible(true), child3);
|
||||
CHECK_EQ(nested3->get_next_in_tree(true), child3);
|
||||
CHECK_EQ(child3->get_prev_visible(true), nested3);
|
||||
CHECK_EQ(child3->get_prev_in_tree(true), nested3);
|
||||
CHECK_EQ(nested1->get_prev_in_tree(true), child2);
|
||||
CHECK_EQ(nested1->get_next_in_tree(true), nested2);
|
||||
CHECK_EQ(nested3->get_next_in_tree(true), child3);
|
||||
|
||||
memdelete(tree);
|
||||
}
|
||||
|
||||
SUBCASE("[Tree] Previous and Next items wrapping with hide root.") {
|
||||
Tree *tree = memnew(Tree);
|
||||
tree->set_hide_root(true);
|
||||
TreeItem *root = tree->create_item();
|
||||
|
||||
TreeItem *child1 = tree->create_item();
|
||||
TreeItem *child2 = tree->create_item();
|
||||
TreeItem *child3 = tree->create_item();
|
||||
CHECK_EQ(root->get_next_visible(true), child1);
|
||||
CHECK_EQ(root->get_next_in_tree(true), child1);
|
||||
CHECK_EQ(child1->get_next_visible(true), child2);
|
||||
CHECK_EQ(child1->get_next_in_tree(true), child2);
|
||||
CHECK_EQ(child2->get_next_visible(true), child3);
|
||||
CHECK_EQ(child2->get_next_in_tree(true), child3);
|
||||
CHECK_EQ(child3->get_next_visible(true), root);
|
||||
CHECK_EQ(child3->get_next_in_tree(true), root);
|
||||
|
||||
CHECK_EQ(root->get_prev_visible(true), child3);
|
||||
CHECK_EQ(root->get_prev_in_tree(true), child3);
|
||||
CHECK_EQ(child1->get_prev_visible(true), child3);
|
||||
CHECK_EQ(child1->get_prev_in_tree(true), child3);
|
||||
CHECK_EQ(child2->get_prev_visible(true), child1);
|
||||
CHECK_EQ(child2->get_prev_in_tree(true), child1);
|
||||
CHECK_EQ(child3->get_prev_visible(true), child2);
|
||||
CHECK_EQ(child3->get_prev_in_tree(true), child2);
|
||||
|
||||
memdelete(tree);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TestTree
|
||||
|
||||
#endif // TEST_TREE_H
|
||||
|
|
@ -38,6 +38,7 @@
|
|||
#include "scene/main/canvas_layer.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/resources/2d/rectangle_shape_2d.h"
|
||||
#include "servers/physics_server_2d_dummy.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
|
|
@ -119,8 +120,23 @@ public:
|
|||
class DragTarget : public NotificationControlViewport {
|
||||
GDCLASS(DragTarget, NotificationControlViewport);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_DRAG_BEGIN: {
|
||||
during_drag = true;
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_DRAG_END: {
|
||||
during_drag = false;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Variant drag_data;
|
||||
bool valid_drop = false;
|
||||
bool during_drag = false;
|
||||
virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override {
|
||||
StringName string_data = p_data;
|
||||
// Verify drag data is compatible.
|
||||
|
|
@ -136,6 +152,7 @@ public:
|
|||
|
||||
virtual void drop_data(const Point2 &p_point, const Variant &p_data) override {
|
||||
drag_data = p_data;
|
||||
valid_drop = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1107,12 +1124,10 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
|
|||
SUBCASE("[Viewport][GuiInputEvent] Drag and Drop") {
|
||||
// FIXME: Drag-Preview will likely change. Tests for this part would have to be rewritten anyway.
|
||||
// See https://github.com/godotengine/godot/pull/67531#issuecomment-1385353430 for details.
|
||||
// FIXME: Testing Drag and Drop with non-embedded windows would require DisplayServerMock additions
|
||||
// FIXME: Drag and Drop currently doesn't work with embedded Windows and SubViewports - not testing.
|
||||
// See https://github.com/godotengine/godot/issues/28522 for example.
|
||||
// Note: Testing Drag and Drop with non-embedded windows would require DisplayServerMock additions.
|
||||
int min_grab_movement = 11;
|
||||
SUBCASE("[Viewport][GuiInputEvent] Drag from one Control to another in the same viewport.") {
|
||||
SUBCASE("[Viewport][GuiInputEvent] Perform successful Drag and Drop on a different Control.") {
|
||||
SUBCASE("[Viewport][GuiInputEvent][DnD] Drag from one Control to another in the same viewport.") {
|
||||
SUBCASE("[Viewport][GuiInputEvent][DnD] Perform successful Drag and Drop on a different Control.") {
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(on_a, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK_FALSE(root->gui_is_dragging());
|
||||
|
||||
|
|
@ -1131,7 +1146,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
|
|||
CHECK((StringName)node_d->drag_data == SNAME("Drag Data"));
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][GuiInputEvent] Perform unsuccessful drop on Control.") {
|
||||
SUBCASE("[Viewport][GuiInputEvent][DnD] Perform unsuccessful drop on Control.") {
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(on_a, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK_FALSE(root->gui_is_dragging());
|
||||
|
||||
|
|
@ -1157,7 +1172,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
|
|||
CHECK_FALSE(root->gui_is_drag_successful());
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][GuiInputEvent] Perform unsuccessful drop on No-Control.") {
|
||||
SUBCASE("[Viewport][GuiInputEvent][DnD] Perform unsuccessful drop on No-Control.") {
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(on_a, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK_FALSE(root->gui_is_dragging());
|
||||
|
||||
|
|
@ -1171,7 +1186,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
|
|||
|
||||
// Move away from Controls.
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_ARROW); // This could also be CURSOR_FORBIDDEN.
|
||||
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_ARROW);
|
||||
|
||||
CHECK(root->gui_is_dragging());
|
||||
SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_background, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
|
||||
|
|
@ -1179,7 +1194,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
|
|||
CHECK_FALSE(root->gui_is_drag_successful());
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][GuiInputEvent] Perform unsuccessful drop outside of window.") {
|
||||
SUBCASE("[Viewport][GuiInputEvent][DnD] Perform unsuccessful drop outside of window.") {
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(on_a, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK_FALSE(root->gui_is_dragging());
|
||||
|
||||
|
|
@ -1192,7 +1207,6 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
|
|||
|
||||
// Move outside of window.
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_outside, MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_ARROW);
|
||||
CHECK(root->gui_is_dragging());
|
||||
|
||||
SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_outside, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
|
||||
|
|
@ -1200,7 +1214,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
|
|||
CHECK_FALSE(root->gui_is_drag_successful());
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][GuiInputEvent] Drag and Drop doesn't work with other Mouse Buttons than LMB.") {
|
||||
SUBCASE("[Viewport][GuiInputEvent][DnD] Drag and Drop doesn't work with other Mouse Buttons than LMB.") {
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(on_a, MouseButton::MIDDLE, MouseButtonMask::MIDDLE, Key::NONE);
|
||||
CHECK_FALSE(root->gui_is_dragging());
|
||||
|
||||
|
|
@ -1209,7 +1223,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
|
|||
SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_a, MouseButton::MIDDLE, MouseButtonMask::NONE, Key::NONE);
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][GuiInputEvent] Drag and Drop parent propagation.") {
|
||||
SUBCASE("[Viewport][GuiInputEvent][DnD] Drag and Drop parent propagation.") {
|
||||
Node2D *node_aa = memnew(Node2D);
|
||||
Control *node_aaa = memnew(Control);
|
||||
Node2D *node_dd = memnew(Node2D);
|
||||
|
|
@ -1318,7 +1332,7 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
|
|||
memdelete(node_aa);
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][GuiInputEvent] Force Drag and Drop.") {
|
||||
SUBCASE("[Viewport][GuiInputEvent][DnD] Force Drag and Drop.") {
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_background, MouseButtonMask::NONE, Key::NONE);
|
||||
CHECK_FALSE(root->gui_is_dragging());
|
||||
node_a->force_drag(SNAME("Drag Data"), nullptr);
|
||||
|
|
@ -1327,8 +1341,8 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
|
|||
SEND_GUI_MOUSE_MOTION_EVENT(on_d, MouseButtonMask::NONE, Key::NONE);
|
||||
|
||||
// Force Drop doesn't get triggered by mouse Buttons other than LMB.
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(on_d, MouseButton::RIGHT, MouseButtonMask::RIGHT, Key::NONE);
|
||||
SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_a, MouseButton::RIGHT, MouseButtonMask::NONE, Key::NONE);
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(on_d, MouseButton::MIDDLE, MouseButtonMask::MIDDLE, Key::NONE);
|
||||
SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_a, MouseButton::MIDDLE, MouseButtonMask::NONE, Key::NONE);
|
||||
CHECK(root->gui_is_dragging());
|
||||
|
||||
// Force Drop with LMB-Down.
|
||||
|
|
@ -1337,8 +1351,122 @@ TEST_CASE("[SceneTree][Viewport] Controls and InputEvent handling") {
|
|||
CHECK(root->gui_is_drag_successful());
|
||||
|
||||
SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_d, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
|
||||
|
||||
node_a->force_drag(SNAME("Drag Data"), nullptr);
|
||||
CHECK(root->gui_is_dragging());
|
||||
|
||||
// Cancel with RMB.
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(on_d, MouseButton::RIGHT, MouseButtonMask::RIGHT, Key::NONE);
|
||||
CHECK_FALSE(root->gui_is_dragging());
|
||||
CHECK_FALSE(root->gui_is_drag_successful());
|
||||
SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_a, MouseButton::RIGHT, MouseButtonMask::NONE, Key::NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][GuiInputEvent][DnD] Drag to a different Viewport.") {
|
||||
SubViewportContainer *svc = memnew(SubViewportContainer);
|
||||
svc->set_size(Size2(100, 100));
|
||||
svc->set_position(Point2(200, 50));
|
||||
root->add_child(svc);
|
||||
|
||||
SubViewport *sv = memnew(SubViewport);
|
||||
sv->set_embedding_subwindows(true);
|
||||
sv->set_size(Size2i(100, 100));
|
||||
svc->add_child(sv);
|
||||
|
||||
DragStart *sv_a = memnew(DragStart);
|
||||
sv_a->set_position(Point2(10, 10));
|
||||
sv_a->set_size(Size2(10, 10));
|
||||
sv->add_child(sv_a);
|
||||
Point2i on_sva = Point2i(215, 65);
|
||||
|
||||
DragTarget *sv_b = memnew(DragTarget);
|
||||
sv_b->set_position(Point2(30, 30));
|
||||
sv_b->set_size(Size2(20, 20));
|
||||
sv->add_child(sv_b);
|
||||
Point2i on_svb = Point2i(235, 85);
|
||||
|
||||
Window *ew = memnew(Window);
|
||||
ew->set_position(Point2(50, 200));
|
||||
ew->set_size(Size2(100, 100));
|
||||
root->add_child(ew);
|
||||
|
||||
DragStart *ew_a = memnew(DragStart);
|
||||
ew_a->set_position(Point2(10, 10));
|
||||
ew_a->set_size(Size2(10, 10));
|
||||
ew->add_child(ew_a);
|
||||
Point2i on_ewa = Point2i(65, 215);
|
||||
|
||||
DragTarget *ew_b = memnew(DragTarget);
|
||||
ew_b->set_position(Point2(30, 30));
|
||||
ew_b->set_size(Size2(20, 20));
|
||||
ew->add_child(ew_b);
|
||||
Point2i on_ewb = Point2i(85, 235);
|
||||
|
||||
SUBCASE("[Viewport][GuiInputEvent][DnD] Drag to SubViewport") {
|
||||
sv_b->valid_drop = false;
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(on_a, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_a + Point2i(min_grab_movement, 0), MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK(root->gui_is_dragging());
|
||||
CHECK(sv_b->during_drag);
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_svb, MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_CAN_DROP);
|
||||
|
||||
SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_svb, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
|
||||
CHECK(sv_b->valid_drop);
|
||||
CHECK(!sv_b->during_drag);
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][GuiInputEvent][DnD] Drag from SubViewport") {
|
||||
node_d->valid_drop = false;
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(on_sva, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_sva + Point2i(min_grab_movement, 0), MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK(sv->gui_is_dragging());
|
||||
CHECK(node_d->during_drag);
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_d, MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_CAN_DROP);
|
||||
|
||||
SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_d, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
|
||||
CHECK(node_d->valid_drop);
|
||||
CHECK(!node_d->during_drag);
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][GuiInputEvent][DnD] Drag to embedded Window") {
|
||||
ew_b->valid_drop = false;
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(on_a, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_a + Point2i(min_grab_movement, 0), MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK(root->gui_is_dragging());
|
||||
CHECK(ew_b->during_drag);
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_ewb, MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_CAN_DROP);
|
||||
|
||||
SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_ewb, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
|
||||
CHECK(ew_b->valid_drop);
|
||||
CHECK(!ew_b->during_drag);
|
||||
}
|
||||
|
||||
SUBCASE("[Viewport][GuiInputEvent][DnD] Drag from embedded Window") {
|
||||
node_d->valid_drop = false;
|
||||
SEND_GUI_MOUSE_BUTTON_EVENT(on_ewa, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_ewa + Point2i(min_grab_movement, 0), MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK(ew->gui_is_dragging());
|
||||
CHECK(node_d->during_drag);
|
||||
SEND_GUI_MOUSE_MOTION_EVENT(on_d, MouseButtonMask::LEFT, Key::NONE);
|
||||
CHECK(DS->get_cursor_shape() == DisplayServer::CURSOR_CAN_DROP);
|
||||
|
||||
SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(on_d, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
|
||||
CHECK(node_d->valid_drop);
|
||||
CHECK(!node_d->during_drag);
|
||||
}
|
||||
|
||||
memdelete(ew_a);
|
||||
memdelete(ew_b);
|
||||
memdelete(ew);
|
||||
memdelete(sv_a);
|
||||
memdelete(sv_b);
|
||||
memdelete(sv);
|
||||
memdelete(svc);
|
||||
}
|
||||
}
|
||||
|
||||
memdelete(node_j);
|
||||
|
|
@ -1432,6 +1560,12 @@ int TestArea2D::counter = 0;
|
|||
TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") {
|
||||
// FIXME: MOUSE_MODE_CAPTURED if-conditions are not testable, because DisplayServerMock doesn't support it.
|
||||
|
||||
// NOTE: This test requires a real physics server.
|
||||
PhysicsServer2DDummy *physics_server_2d_dummy = Object::cast_to<PhysicsServer2DDummy>(PhysicsServer2D::get_singleton());
|
||||
if (physics_server_2d_dummy) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct PickingCollider {
|
||||
TestArea2D *a;
|
||||
CollisionShape2D *c;
|
||||
|
|
@ -1452,7 +1586,7 @@ TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") {
|
|||
PickingCollider pc;
|
||||
pc.a = memnew(TestArea2D);
|
||||
pc.c = memnew(CollisionShape2D);
|
||||
pc.r = Ref<RectangleShape2D>(memnew(RectangleShape2D));
|
||||
pc.r.instantiate();
|
||||
pc.r->set_size(Size2(150, 150));
|
||||
pc.c->set_shape(pc.r);
|
||||
pc.a->add_child(pc.c);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue