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
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class TestProjectSettingsInternalsAccessor {
|
|||
public:
|
||||
static String &resource_path() {
|
||||
return ProjectSettings::get_singleton()->resource_path;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
namespace TestProjectSettings {
|
||||
|
|
@ -126,10 +126,9 @@ TEST_CASE("[ProjectSettings] localize_path") {
|
|||
CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path\\.\\filename"), "res://path/filename");
|
||||
#endif
|
||||
|
||||
// FIXME?: These checks pass, but that doesn't seems correct
|
||||
CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../filename"), "res://filename");
|
||||
CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../path/filename"), "res://path/filename");
|
||||
CHECK_EQ(ProjectSettings::get_singleton()->localize_path("..\\path\\filename"), "res://path/filename");
|
||||
CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../filename"), "../filename");
|
||||
CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../path/filename"), "../path/filename");
|
||||
CHECK_EQ(ProjectSettings::get_singleton()->localize_path("..\\path\\filename"), "../path/filename");
|
||||
|
||||
CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/filename"), "/testroot/filename");
|
||||
CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/path/filename"), "/testroot/path/filename");
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ namespace TestFileAccess {
|
|||
|
||||
TEST_CASE("[FileAccess] CSV read") {
|
||||
Ref<FileAccess> f = FileAccess::open(TestUtils::get_data_path("testdata.csv"), FileAccess::READ);
|
||||
REQUIRE(!f.is_null());
|
||||
REQUIRE(f.is_valid());
|
||||
|
||||
Vector<String> header = f->get_csv_line(); // Default delimiter: ",".
|
||||
REQUIRE(header.size() == 4);
|
||||
|
|
@ -84,7 +84,7 @@ TEST_CASE("[FileAccess] CSV read") {
|
|||
|
||||
TEST_CASE("[FileAccess] Get as UTF-8 String") {
|
||||
Ref<FileAccess> f_lf = FileAccess::open(TestUtils::get_data_path("line_endings_lf.test.txt"), FileAccess::READ);
|
||||
REQUIRE(!f_lf.is_null());
|
||||
REQUIRE(f_lf.is_valid());
|
||||
String s_lf = f_lf->get_as_utf8_string();
|
||||
f_lf->seek(0);
|
||||
String s_lf_nocr = f_lf->get_as_utf8_string(true);
|
||||
|
|
@ -92,7 +92,7 @@ TEST_CASE("[FileAccess] Get as UTF-8 String") {
|
|||
CHECK(s_lf_nocr == "Hello darkness\nMy old friend\nI've come to talk\nWith you again\n");
|
||||
|
||||
Ref<FileAccess> f_crlf = FileAccess::open(TestUtils::get_data_path("line_endings_crlf.test.txt"), FileAccess::READ);
|
||||
REQUIRE(!f_crlf.is_null());
|
||||
REQUIRE(f_crlf.is_valid());
|
||||
String s_crlf = f_crlf->get_as_utf8_string();
|
||||
f_crlf->seek(0);
|
||||
String s_crlf_nocr = f_crlf->get_as_utf8_string(true);
|
||||
|
|
@ -100,13 +100,105 @@ TEST_CASE("[FileAccess] Get as UTF-8 String") {
|
|||
CHECK(s_crlf_nocr == "Hello darkness\nMy old friend\nI've come to talk\nWith you again\n");
|
||||
|
||||
Ref<FileAccess> f_cr = FileAccess::open(TestUtils::get_data_path("line_endings_cr.test.txt"), FileAccess::READ);
|
||||
REQUIRE(!f_cr.is_null());
|
||||
REQUIRE(f_cr.is_valid());
|
||||
String s_cr = f_cr->get_as_utf8_string();
|
||||
f_cr->seek(0);
|
||||
String s_cr_nocr = f_cr->get_as_utf8_string(true);
|
||||
CHECK(s_cr == "Hello darkness\rMy old friend\rI've come to talk\rWith you again\r");
|
||||
CHECK(s_cr_nocr == "Hello darknessMy old friendI've come to talkWith you again");
|
||||
}
|
||||
|
||||
TEST_CASE("[FileAccess] Get/Store floating point values") {
|
||||
// BigEndian Hex: 0x40490E56
|
||||
// LittleEndian Hex: 0x560E4940
|
||||
float value = 3.1415f;
|
||||
|
||||
SUBCASE("Little Endian") {
|
||||
const String file_path = TestUtils::get_data_path("floating_point_little_endian.bin");
|
||||
const String file_path_new = TestUtils::get_data_path("floating_point_little_endian_new.bin");
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(file_path, FileAccess::READ);
|
||||
REQUIRE(f.is_valid());
|
||||
CHECK_EQ(f->get_float(), value);
|
||||
|
||||
Ref<FileAccess> fw = FileAccess::open(file_path_new, FileAccess::WRITE);
|
||||
REQUIRE(fw.is_valid());
|
||||
fw->store_float(value);
|
||||
fw->close();
|
||||
|
||||
CHECK_EQ(FileAccess::get_sha256(file_path_new), FileAccess::get_sha256(file_path));
|
||||
|
||||
DirAccess::remove_file_or_error(file_path_new);
|
||||
}
|
||||
|
||||
SUBCASE("Big Endian") {
|
||||
const String file_path = TestUtils::get_data_path("floating_point_big_endian.bin");
|
||||
const String file_path_new = TestUtils::get_data_path("floating_point_big_endian_new.bin");
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(file_path, FileAccess::READ);
|
||||
REQUIRE(f.is_valid());
|
||||
f->set_big_endian(true);
|
||||
CHECK_EQ(f->get_float(), value);
|
||||
|
||||
Ref<FileAccess> fw = FileAccess::open(file_path_new, FileAccess::WRITE);
|
||||
REQUIRE(fw.is_valid());
|
||||
fw->set_big_endian(true);
|
||||
fw->store_float(value);
|
||||
fw->close();
|
||||
|
||||
CHECK_EQ(FileAccess::get_sha256(file_path_new), FileAccess::get_sha256(file_path));
|
||||
|
||||
DirAccess::remove_file_or_error(file_path_new);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[FileAccess] Get/Store floating point half precision values") {
|
||||
// IEEE 754 half-precision binary floating-point format:
|
||||
// sign exponent (5 bits) fraction (10 bits)
|
||||
// 0 01101 0101010101
|
||||
// BigEndian Hex: 0x3555
|
||||
// LittleEndian Hex: 0x5535
|
||||
float value = 0.33325195f;
|
||||
|
||||
SUBCASE("Little Endian") {
|
||||
const String file_path = TestUtils::get_data_path("half_precision_floating_point_little_endian.bin");
|
||||
const String file_path_new = TestUtils::get_data_path("half_precision_floating_point_little_endian_new.bin");
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(file_path, FileAccess::READ);
|
||||
REQUIRE(f.is_valid());
|
||||
CHECK_EQ(f->get_half(), value);
|
||||
|
||||
Ref<FileAccess> fw = FileAccess::open(file_path_new, FileAccess::WRITE);
|
||||
REQUIRE(fw.is_valid());
|
||||
fw->store_half(value);
|
||||
fw->close();
|
||||
|
||||
CHECK_EQ(FileAccess::get_sha256(file_path_new), FileAccess::get_sha256(file_path));
|
||||
|
||||
DirAccess::remove_file_or_error(file_path_new);
|
||||
}
|
||||
|
||||
SUBCASE("Big Endian") {
|
||||
const String file_path = TestUtils::get_data_path("half_precision_floating_point_big_endian.bin");
|
||||
const String file_path_new = TestUtils::get_data_path("half_precision_floating_point_big_endian_new.bin");
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(file_path, FileAccess::READ);
|
||||
REQUIRE(f.is_valid());
|
||||
f->set_big_endian(true);
|
||||
CHECK_EQ(f->get_half(), value);
|
||||
|
||||
Ref<FileAccess> fw = FileAccess::open(file_path_new, FileAccess::WRITE);
|
||||
REQUIRE(fw.is_valid());
|
||||
fw->set_big_endian(true);
|
||||
fw->store_half(value);
|
||||
fw->close();
|
||||
|
||||
CHECK_EQ(FileAccess::get_sha256(file_path_new), FileAccess::get_sha256(file_path));
|
||||
|
||||
DirAccess::remove_file_or_error(file_path_new);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TestFileAccess
|
||||
|
||||
#endif // TEST_FILE_ACCESS_H
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ namespace TestHTTPClient {
|
|||
|
||||
TEST_CASE("[HTTPClient] Instantiation") {
|
||||
Ref<HTTPClient> client = HTTPClient::create();
|
||||
CHECK_MESSAGE(client != nullptr, "A HTTP Client created should not be a null pointer");
|
||||
CHECK_MESSAGE(client.is_valid(), "A HTTP Client created should not be a null pointer");
|
||||
}
|
||||
|
||||
TEST_CASE("[HTTPClient] query_string_from_dict") {
|
||||
|
|
|
|||
|
|
@ -35,10 +35,11 @@
|
|||
#include "core/os/os.h"
|
||||
|
||||
#include "tests/test_utils.h"
|
||||
#include "thirdparty/doctest/doctest.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h"
|
||||
|
||||
#include "thirdparty/doctest/doctest.h"
|
||||
|
||||
namespace TestImage {
|
||||
|
||||
TEST_CASE("[Image] Instantiation") {
|
||||
|
|
@ -113,7 +114,7 @@ TEST_CASE("[Image] Saving and loading") {
|
|||
// Load BMP
|
||||
Ref<Image> image_bmp = memnew(Image());
|
||||
Ref<FileAccess> f_bmp = FileAccess::open(TestUtils::get_data_path("images/icon.bmp"), FileAccess::READ, &err);
|
||||
REQUIRE(!f_bmp.is_null());
|
||||
REQUIRE(f_bmp.is_valid());
|
||||
PackedByteArray data_bmp;
|
||||
data_bmp.resize(f_bmp->get_length() + 1);
|
||||
f_bmp->get_buffer(data_bmp.ptrw(), f_bmp->get_length());
|
||||
|
|
@ -126,7 +127,7 @@ TEST_CASE("[Image] Saving and loading") {
|
|||
// Load JPG
|
||||
Ref<Image> image_jpg = memnew(Image());
|
||||
Ref<FileAccess> f_jpg = FileAccess::open(TestUtils::get_data_path("images/icon.jpg"), FileAccess::READ, &err);
|
||||
REQUIRE(!f_jpg.is_null());
|
||||
REQUIRE(f_jpg.is_valid());
|
||||
PackedByteArray data_jpg;
|
||||
data_jpg.resize(f_jpg->get_length() + 1);
|
||||
f_jpg->get_buffer(data_jpg.ptrw(), f_jpg->get_length());
|
||||
|
|
@ -139,7 +140,7 @@ TEST_CASE("[Image] Saving and loading") {
|
|||
// Load WebP
|
||||
Ref<Image> image_webp = memnew(Image());
|
||||
Ref<FileAccess> f_webp = FileAccess::open(TestUtils::get_data_path("images/icon.webp"), FileAccess::READ, &err);
|
||||
REQUIRE(!f_webp.is_null());
|
||||
REQUIRE(f_webp.is_valid());
|
||||
PackedByteArray data_webp;
|
||||
data_webp.resize(f_webp->get_length() + 1);
|
||||
f_webp->get_buffer(data_webp.ptrw(), f_webp->get_length());
|
||||
|
|
@ -151,7 +152,7 @@ TEST_CASE("[Image] Saving and loading") {
|
|||
// Load PNG
|
||||
Ref<Image> image_png = memnew(Image());
|
||||
Ref<FileAccess> f_png = FileAccess::open(TestUtils::get_data_path("images/icon.png"), FileAccess::READ, &err);
|
||||
REQUIRE(!f_png.is_null());
|
||||
REQUIRE(f_png.is_valid());
|
||||
PackedByteArray data_png;
|
||||
data_png.resize(f_png->get_length() + 1);
|
||||
f_png->get_buffer(data_png.ptrw(), f_png->get_length());
|
||||
|
|
@ -163,7 +164,7 @@ TEST_CASE("[Image] Saving and loading") {
|
|||
// Load TGA
|
||||
Ref<Image> image_tga = memnew(Image());
|
||||
Ref<FileAccess> f_tga = FileAccess::open(TestUtils::get_data_path("images/icon.tga"), FileAccess::READ, &err);
|
||||
REQUIRE(!f_tga.is_null());
|
||||
REQUIRE(f_tga.is_valid());
|
||||
PackedByteArray data_tga;
|
||||
data_tga.resize(f_tga->get_length() + 1);
|
||||
f_tga->get_buffer(data_tga.ptrw(), f_tga->get_length());
|
||||
|
|
|
|||
|
|
@ -232,6 +232,91 @@ TEST_CASE("[JSON] Parsing escape sequences") {
|
|||
ERR_PRINT_ON
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[JSON] Serialization") {
|
||||
JSON json;
|
||||
|
||||
struct FpTestCase {
|
||||
double number;
|
||||
String json;
|
||||
};
|
||||
|
||||
struct IntTestCase {
|
||||
int64_t number;
|
||||
String json;
|
||||
};
|
||||
|
||||
struct UIntTestCase {
|
||||
uint64_t number;
|
||||
String json;
|
||||
};
|
||||
|
||||
static FpTestCase fp_tests_default_precision[] = {
|
||||
{ 0.0, "0.0" },
|
||||
{ 1000.1234567890123456789, "1000.12345678901" },
|
||||
{ -1000.1234567890123456789, "-1000.12345678901" },
|
||||
{ DBL_MAX, "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0" },
|
||||
{ DBL_MAX - 1, "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0" },
|
||||
{ pow(2, 53), "9007199254740992.0" },
|
||||
{ -pow(2, 53), "-9007199254740992.0" },
|
||||
{ 0.00000000000000011, "0.00000000000000011" },
|
||||
{ -0.00000000000000011, "-0.00000000000000011" },
|
||||
{ 1.0 / 3.0, "0.333333333333333" },
|
||||
{ 0.9999999999999999, "1.0" },
|
||||
{ 1.0000000000000001, "1.0" },
|
||||
};
|
||||
|
||||
static FpTestCase fp_tests_full_precision[] = {
|
||||
{ 0.0, "0.0" },
|
||||
{ 1000.1234567890123456789, "1000.12345678901238" },
|
||||
{ -1000.1234567890123456789, "-1000.12345678901238" },
|
||||
{ DBL_MAX, "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0" },
|
||||
{ DBL_MAX - 1, "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0" },
|
||||
{ pow(2, 53), "9007199254740992.0" },
|
||||
{ -pow(2, 53), "-9007199254740992.0" },
|
||||
{ 0.00000000000000011, "0.00000000000000011" },
|
||||
{ -0.00000000000000011, "-0.00000000000000011" },
|
||||
{ 1.0 / 3.0, "0.333333333333333315" },
|
||||
{ 0.9999999999999999, "0.999999999999999889" },
|
||||
{ 1.0000000000000001, "1.0" },
|
||||
};
|
||||
|
||||
static IntTestCase int_tests[] = {
|
||||
{ 0, "0" },
|
||||
{ INT64_MAX, "9223372036854775807" },
|
||||
{ INT64_MIN, "-9223372036854775808" },
|
||||
};
|
||||
|
||||
SUBCASE("Floating point default precision") {
|
||||
for (FpTestCase &test : fp_tests_default_precision) {
|
||||
String json_value = json.stringify(test.number, "", true, false);
|
||||
|
||||
CHECK_MESSAGE(
|
||||
json_value == test.json,
|
||||
vformat("Serializing `%.20d` to JSON should return the expected value.", test.number));
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Floating point full precision") {
|
||||
for (FpTestCase &test : fp_tests_full_precision) {
|
||||
String json_value = json.stringify(test.number, "", true, true);
|
||||
|
||||
CHECK_MESSAGE(
|
||||
json_value == test.json,
|
||||
vformat("Serializing `%20f` to JSON should return the expected value.", test.number));
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Signed integer") {
|
||||
for (IntTestCase &test : int_tests) {
|
||||
String json_value = json.stringify(test.number, "", true, true);
|
||||
|
||||
CHECK_MESSAGE(
|
||||
json_value == test.json,
|
||||
vformat("Serializing `%d` to JSON should return the expected value.", test.number));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace TestJSON
|
||||
|
||||
#endif // TEST_JSON_H
|
||||
|
|
|
|||
227
engine/tests/core/io/test_json_native.h
Normal file
227
engine/tests/core/io/test_json_native.h
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
/**************************************************************************/
|
||||
/* test_json_native.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_JSON_NATIVE_H
|
||||
#define TEST_JSON_NATIVE_H
|
||||
|
||||
#include "core/io/json.h"
|
||||
|
||||
#include "core/variant/typed_array.h"
|
||||
#include "core/variant/typed_dictionary.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestJSONNative {
|
||||
|
||||
String encode(const Variant &p_variant, bool p_full_objects = false) {
|
||||
return JSON::stringify(JSON::from_native(p_variant, p_full_objects), "", false);
|
||||
}
|
||||
|
||||
Variant decode(const String &p_string, bool p_allow_objects = false) {
|
||||
return JSON::to_native(JSON::parse_string(p_string), p_allow_objects);
|
||||
}
|
||||
|
||||
void test(const Variant &p_variant, const String &p_string, bool p_with_objects = false) {
|
||||
CHECK(encode(p_variant, p_with_objects) == p_string);
|
||||
CHECK(decode(p_string, p_with_objects).get_construct_string() == p_variant.get_construct_string());
|
||||
}
|
||||
|
||||
TEST_CASE("[JSON][Native] Conversion between native and JSON formats") {
|
||||
// `Nil` and `bool` (represented as JSON keyword literals).
|
||||
test(Variant(), "null");
|
||||
test(false, "false");
|
||||
test(true, "true");
|
||||
|
||||
// Numbers and strings (represented as JSON strings).
|
||||
test(1, R"("i:1")");
|
||||
test(1.0, R"("f:1.0")");
|
||||
test(String("abc"), R"("s:abc")");
|
||||
test(StringName("abc"), R"("sn:abc")");
|
||||
test(NodePath("abc"), R"("np:abc")");
|
||||
|
||||
// Non-serializable types (always empty after deserialization).
|
||||
test(RID(), R"({"type":"RID"})");
|
||||
test(Callable(), R"({"type":"Callable"})");
|
||||
test(Signal(), R"({"type":"Signal"})");
|
||||
|
||||
// Math types.
|
||||
|
||||
test(Vector2(1, 2), R"({"type":"Vector2","args":[1.0,2.0]})");
|
||||
test(Vector2i(1, 2), R"({"type":"Vector2i","args":[1,2]})");
|
||||
test(Rect2(1, 2, 3, 4), R"({"type":"Rect2","args":[1.0,2.0,3.0,4.0]})");
|
||||
test(Rect2i(1, 2, 3, 4), R"({"type":"Rect2i","args":[1,2,3,4]})");
|
||||
test(Vector3(1, 2, 3), R"({"type":"Vector3","args":[1.0,2.0,3.0]})");
|
||||
test(Vector3i(1, 2, 3), R"({"type":"Vector3i","args":[1,2,3]})");
|
||||
test(Transform2D(1, 2, 3, 4, 5, 6), R"({"type":"Transform2D","args":[1.0,2.0,3.0,4.0,5.0,6.0]})");
|
||||
test(Vector4(1, 2, 3, 4), R"({"type":"Vector4","args":[1.0,2.0,3.0,4.0]})");
|
||||
test(Vector4i(1, 2, 3, 4), R"({"type":"Vector4i","args":[1,2,3,4]})");
|
||||
test(Plane(1, 2, 3, 4), R"({"type":"Plane","args":[1.0,2.0,3.0,4.0]})");
|
||||
test(Quaternion(1, 2, 3, 4), R"({"type":"Quaternion","args":[1.0,2.0,3.0,4.0]})");
|
||||
test(AABB(Vector3(1, 2, 3), Vector3(4, 5, 6)), R"({"type":"AABB","args":[1.0,2.0,3.0,4.0,5.0,6.0]})");
|
||||
|
||||
const Basis b(Vector3(1, 2, 3), Vector3(4, 5, 6), Vector3(7, 8, 9));
|
||||
test(b, R"({"type":"Basis","args":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0]})");
|
||||
|
||||
const Transform3D tr3d(Vector3(1, 2, 3), Vector3(4, 5, 6), Vector3(7, 8, 9), Vector3(10, 11, 12));
|
||||
test(tr3d, R"({"type":"Transform3D","args":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0]})");
|
||||
|
||||
const Projection p(Vector4(1, 2, 3, 4), Vector4(5, 6, 7, 8), Vector4(9, 10, 11, 12), Vector4(13, 14, 15, 16));
|
||||
test(p, R"({"type":"Projection","args":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0]})");
|
||||
|
||||
test(Color(1, 2, 3, 4), R"({"type":"Color","args":[1.0,2.0,3.0,4.0]})");
|
||||
|
||||
// `Object`.
|
||||
|
||||
Ref<Resource> res;
|
||||
res.instantiate();
|
||||
|
||||
// The properties are stored in an array because the order in which they are assigned may be important during initialization.
|
||||
const String res_repr = R"({"type":"Resource","props":["resource_local_to_scene",false,"resource_name","s:","script",null]})";
|
||||
|
||||
test(res, res_repr, true);
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(encode(res) == "null");
|
||||
CHECK(decode(res_repr).get_type() == Variant::NIL);
|
||||
ERR_PRINT_ON;
|
||||
|
||||
// `Dictionary`.
|
||||
|
||||
Dictionary dict;
|
||||
dict[false] = true;
|
||||
dict[0] = 1;
|
||||
dict[0.0] = 1.0;
|
||||
|
||||
// Godot dictionaries preserve insertion order, so an array is used for keys/values.
|
||||
test(dict, R"({"type":"Dictionary","args":[false,true,"i:0","i:1","f:0.0","f:1.0"]})");
|
||||
|
||||
TypedDictionary<int64_t, int64_t> int_int_dict;
|
||||
int_int_dict[1] = 2;
|
||||
int_int_dict[3] = 4;
|
||||
|
||||
test(int_int_dict, R"({"type":"Dictionary","key_type":"int","value_type":"int","args":["i:1","i:2","i:3","i:4"]})");
|
||||
|
||||
TypedDictionary<int64_t, Variant> int_var_dict;
|
||||
int_var_dict[1] = "2";
|
||||
int_var_dict[3] = "4";
|
||||
|
||||
test(int_var_dict, R"({"type":"Dictionary","key_type":"int","args":["i:1","s:2","i:3","s:4"]})");
|
||||
|
||||
TypedDictionary<Variant, int64_t> var_int_dict;
|
||||
var_int_dict["1"] = 2;
|
||||
var_int_dict["3"] = 4;
|
||||
|
||||
test(var_int_dict, R"({"type":"Dictionary","value_type":"int","args":["s:1","i:2","s:3","i:4"]})");
|
||||
|
||||
Dictionary dict2;
|
||||
dict2["x"] = res;
|
||||
|
||||
const String dict2_repr = vformat(R"({"type":"Dictionary","args":["s:x",%s]})", res_repr);
|
||||
|
||||
test(dict2, dict2_repr, true);
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(encode(dict2) == R"({"type":"Dictionary","args":["s:x",null]})");
|
||||
CHECK(decode(dict2_repr).get_construct_string() == "{\n\"x\": null\n}");
|
||||
ERR_PRINT_ON;
|
||||
|
||||
TypedDictionary<String, Resource> res_dict;
|
||||
res_dict["x"] = res;
|
||||
|
||||
const String res_dict_repr = vformat(R"({"type":"Dictionary","key_type":"String","value_type":"Resource","args":["s:x",%s]})", res_repr);
|
||||
|
||||
test(res_dict, res_dict_repr, true);
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(encode(res_dict) == "null");
|
||||
CHECK(decode(res_dict_repr).get_type() == Variant::NIL);
|
||||
ERR_PRINT_ON;
|
||||
|
||||
// `Array`.
|
||||
|
||||
Array arr;
|
||||
arr.push_back(true);
|
||||
arr.push_back(1);
|
||||
arr.push_back("abc");
|
||||
|
||||
test(arr, R"([true,"i:1","s:abc"])");
|
||||
|
||||
TypedArray<int64_t> int_arr;
|
||||
int_arr.push_back(1);
|
||||
int_arr.push_back(2);
|
||||
int_arr.push_back(3);
|
||||
|
||||
test(int_arr, R"({"type":"Array","elem_type":"int","args":["i:1","i:2","i:3"]})");
|
||||
|
||||
Array arr2;
|
||||
arr2.push_back(1);
|
||||
arr2.push_back(res);
|
||||
arr2.push_back(9);
|
||||
|
||||
const String arr2_repr = vformat(R"(["i:1",%s,"i:9"])", res_repr);
|
||||
|
||||
test(arr2, arr2_repr, true);
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(encode(arr2) == R"(["i:1",null,"i:9"])");
|
||||
CHECK(decode(arr2_repr).get_construct_string() == "[1, null, 9]");
|
||||
ERR_PRINT_ON;
|
||||
|
||||
TypedArray<Resource> res_arr;
|
||||
res_arr.push_back(res);
|
||||
|
||||
const String res_arr_repr = vformat(R"({"type":"Array","elem_type":"Resource","args":[%s]})", res_repr);
|
||||
|
||||
test(res_arr, res_arr_repr, true);
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(encode(res_arr) == "null");
|
||||
CHECK(decode(res_arr_repr).get_type() == Variant::NIL);
|
||||
ERR_PRINT_ON;
|
||||
|
||||
// Packed arrays.
|
||||
|
||||
test(PackedByteArray({ 1, 2, 3 }), R"({"type":"PackedByteArray","args":[1,2,3]})");
|
||||
test(PackedInt32Array({ 1, 2, 3 }), R"({"type":"PackedInt32Array","args":[1,2,3]})");
|
||||
test(PackedInt64Array({ 1, 2, 3 }), R"({"type":"PackedInt64Array","args":[1,2,3]})");
|
||||
test(PackedFloat32Array({ 1, 2, 3 }), R"({"type":"PackedFloat32Array","args":[1.0,2.0,3.0]})");
|
||||
test(PackedFloat64Array({ 1, 2, 3 }), R"({"type":"PackedFloat64Array","args":[1.0,2.0,3.0]})");
|
||||
test(PackedStringArray({ "a", "b", "c" }), R"({"type":"PackedStringArray","args":["a","b","c"]})");
|
||||
|
||||
const PackedVector2Array pv2arr({ Vector2(1, 2), Vector2(3, 4), Vector2(5, 6) });
|
||||
test(pv2arr, R"({"type":"PackedVector2Array","args":[1.0,2.0,3.0,4.0,5.0,6.0]})");
|
||||
|
||||
const PackedVector3Array pv3arr({ Vector3(1, 2, 3), Vector3(4, 5, 6), Vector3(7, 8, 9) });
|
||||
test(pv3arr, R"({"type":"PackedVector3Array","args":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0]})");
|
||||
|
||||
const PackedColorArray pcolarr({ Color(1, 2, 3, 4), Color(5, 6, 7, 8), Color(9, 10, 11, 12) });
|
||||
test(pcolarr, R"({"type":"PackedColorArray","args":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0]})");
|
||||
|
||||
const PackedVector4Array pv4arr({ Vector4(1, 2, 3, 4), Vector4(5, 6, 7, 8), Vector4(9, 10, 11, 12) });
|
||||
test(pv4arr, R"({"type":"PackedVector4Array","args":[1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0]})");
|
||||
}
|
||||
|
||||
} // namespace TestJSONNative
|
||||
|
||||
#endif // TEST_JSON_NATIVE_H
|
||||
170
engine/tests/core/io/test_logger.h
Normal file
170
engine/tests/core/io/test_logger.h
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
/**************************************************************************/
|
||||
/* test_logger.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_LOGGER_H
|
||||
#define TEST_LOGGER_H
|
||||
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/logger.h"
|
||||
#include "modules/regex/regex.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestLogger {
|
||||
|
||||
constexpr int sleep_duration = 1200000;
|
||||
|
||||
void initialize_logs() {
|
||||
ProjectSettings::get_singleton()->set_setting("application/config/name", "godot_tests");
|
||||
DirAccess::make_dir_recursive_absolute(OS::get_singleton()->get_user_data_dir().path_join("logs"));
|
||||
}
|
||||
|
||||
void cleanup_logs() {
|
||||
ProjectSettings::get_singleton()->set_setting("application/config/name", "godot_tests");
|
||||
Ref<DirAccess> dir = DirAccess::open("user://logs");
|
||||
dir->list_dir_begin();
|
||||
String file = dir->get_next();
|
||||
while (file != "") {
|
||||
if (file.match("*.log")) {
|
||||
dir->remove(file);
|
||||
}
|
||||
file = dir->get_next();
|
||||
}
|
||||
DirAccess::remove_absolute(OS::get_singleton()->get_user_data_dir().path_join("logs"));
|
||||
DirAccess::remove_absolute(OS::get_singleton()->get_user_data_dir());
|
||||
}
|
||||
|
||||
TEST_CASE("[Logger][RotatedFileLogger] Creates the first log file and logs on it") {
|
||||
initialize_logs();
|
||||
|
||||
String waiting_for_godot = "Waiting for Godot";
|
||||
RotatedFileLogger logger("user://logs/godot.log");
|
||||
logger.logf("%s", "Waiting for Godot");
|
||||
|
||||
Error err = Error::OK;
|
||||
Ref<FileAccess> log = FileAccess::open("user://logs/godot.log", FileAccess::READ, &err);
|
||||
CHECK_EQ(err, Error::OK);
|
||||
CHECK_EQ(log->get_as_text(), waiting_for_godot);
|
||||
|
||||
cleanup_logs();
|
||||
}
|
||||
|
||||
void get_log_files(Vector<String> &log_files) {
|
||||
Ref<DirAccess> dir = DirAccess::open("user://logs");
|
||||
dir->list_dir_begin();
|
||||
String file = dir->get_next();
|
||||
while (file != "") {
|
||||
// Filtering godot.log because ordered_insert will put it first and should be the last.
|
||||
if (file.match("*.log") && file != "godot.log") {
|
||||
log_files.ordered_insert(file);
|
||||
}
|
||||
file = dir->get_next();
|
||||
}
|
||||
if (FileAccess::exists("user://logs/godot.log")) {
|
||||
log_files.push_back("godot.log");
|
||||
}
|
||||
}
|
||||
|
||||
// All things related to log file rotation are in the same test because testing it require some sleeps.
|
||||
TEST_CASE("[Logger][RotatedFileLogger] Rotates logs files") {
|
||||
initialize_logs();
|
||||
|
||||
Vector<String> all_waiting_for_godot;
|
||||
|
||||
const int number_of_files = 3;
|
||||
for (int i = 0; i < number_of_files; i++) {
|
||||
String waiting_for_godot = "Waiting for Godot " + itos(i);
|
||||
RotatedFileLogger logger("user://logs/godot.log", number_of_files);
|
||||
logger.logf("%s", waiting_for_godot.ascii().get_data());
|
||||
all_waiting_for_godot.push_back(waiting_for_godot);
|
||||
|
||||
// Required to ensure the rotation of the log file.
|
||||
OS::get_singleton()->delay_usec(sleep_duration);
|
||||
}
|
||||
|
||||
Vector<String> log_files;
|
||||
get_log_files(log_files);
|
||||
CHECK_MESSAGE(log_files.size() == number_of_files, "Did not rotate all files");
|
||||
|
||||
for (int i = 0; i < log_files.size(); i++) {
|
||||
Error err = Error::OK;
|
||||
Ref<FileAccess> log_file = FileAccess::open("user://logs/" + log_files[i], FileAccess::READ, &err);
|
||||
REQUIRE_EQ(err, Error::OK);
|
||||
CHECK_EQ(log_file->get_as_text(), all_waiting_for_godot[i]);
|
||||
}
|
||||
|
||||
// Required to ensure the rotation of the log file.
|
||||
OS::get_singleton()->delay_usec(sleep_duration);
|
||||
|
||||
// This time the oldest log must be removed and godot.log updated.
|
||||
String new_waiting_for_godot = "Waiting for Godot " + itos(number_of_files);
|
||||
all_waiting_for_godot = all_waiting_for_godot.slice(1, all_waiting_for_godot.size());
|
||||
all_waiting_for_godot.push_back(new_waiting_for_godot);
|
||||
RotatedFileLogger logger("user://logs/godot.log", number_of_files);
|
||||
logger.logf("%s", new_waiting_for_godot.ascii().get_data());
|
||||
|
||||
log_files.clear();
|
||||
get_log_files(log_files);
|
||||
CHECK_MESSAGE(log_files.size() == number_of_files, "Did not remove old log file");
|
||||
|
||||
for (int i = 0; i < log_files.size(); i++) {
|
||||
Error err = Error::OK;
|
||||
Ref<FileAccess> log_file = FileAccess::open("user://logs/" + log_files[i], FileAccess::READ, &err);
|
||||
REQUIRE_EQ(err, Error::OK);
|
||||
CHECK_EQ(log_file->get_as_text(), all_waiting_for_godot[i]);
|
||||
}
|
||||
|
||||
cleanup_logs();
|
||||
}
|
||||
|
||||
TEST_CASE("[Logger][CompositeLogger] Logs the same into multiple loggers") {
|
||||
initialize_logs();
|
||||
|
||||
Vector<Logger *> all_loggers;
|
||||
all_loggers.push_back(memnew(RotatedFileLogger("user://logs/godot_logger_1.log", 1)));
|
||||
all_loggers.push_back(memnew(RotatedFileLogger("user://logs/godot_logger_2.log", 1)));
|
||||
|
||||
String waiting_for_godot = "Waiting for Godot";
|
||||
CompositeLogger logger(all_loggers);
|
||||
logger.logf("%s", "Waiting for Godot");
|
||||
|
||||
Error err = Error::OK;
|
||||
Ref<FileAccess> log = FileAccess::open("user://logs/godot_logger_1.log", FileAccess::READ, &err);
|
||||
CHECK_EQ(err, Error::OK);
|
||||
CHECK_EQ(log->get_as_text(), waiting_for_godot);
|
||||
log = FileAccess::open("user://logs/godot_logger_2.log", FileAccess::READ, &err);
|
||||
CHECK_EQ(err, Error::OK);
|
||||
CHECK_EQ(log->get_as_text(), waiting_for_godot);
|
||||
|
||||
cleanup_logs();
|
||||
}
|
||||
|
||||
} // namespace TestLogger
|
||||
|
||||
#endif // TEST_LOGGER_H
|
||||
|
|
@ -90,6 +90,20 @@ TEST_CASE("[Marshalls] Unsigned 64 bit integer decoding") {
|
|||
CHECK(decode_uint64(arr) == 0x0f123456789abcdef);
|
||||
}
|
||||
|
||||
TEST_CASE("[Marshalls] Floating point half precision encoding") {
|
||||
uint8_t arr[2];
|
||||
|
||||
// Decimal: 0.33325195
|
||||
// IEEE 754 half-precision binary floating-point format:
|
||||
// sign exponent (5 bits) fraction (10 bits)
|
||||
// 0 01101 0101010101
|
||||
// Hexadecimal: 0x3555
|
||||
unsigned int actual_size = encode_half(0.33325195f, arr);
|
||||
CHECK(actual_size == sizeof(uint16_t));
|
||||
CHECK(arr[0] == 0x55);
|
||||
CHECK(arr[1] == 0x35);
|
||||
}
|
||||
|
||||
TEST_CASE("[Marshalls] Floating point single precision encoding") {
|
||||
uint8_t arr[4];
|
||||
|
||||
|
|
@ -126,6 +140,13 @@ TEST_CASE("[Marshalls] Floating point double precision encoding") {
|
|||
CHECK(arr[7] == 0x3f);
|
||||
}
|
||||
|
||||
TEST_CASE("[Marshalls] Floating point half precision decoding") {
|
||||
uint8_t arr[] = { 0x55, 0x35 };
|
||||
|
||||
// See floating point half precision encoding test case for details behind expected values.
|
||||
CHECK(decode_half(arr) == 0.33325195f);
|
||||
}
|
||||
|
||||
TEST_CASE("[Marshalls] Floating point single precision decoding") {
|
||||
uint8_t arr[] = { 0x00, 0x00, 0x20, 0x3e };
|
||||
|
||||
|
|
@ -160,7 +181,7 @@ TEST_CASE("[Marshalls] NIL Variant encoding") {
|
|||
uint8_t buffer[4];
|
||||
|
||||
CHECK(encode_variant(variant, buffer, r_len) == OK);
|
||||
CHECK_MESSAGE(r_len == 4, "Length == 4 bytes for header");
|
||||
CHECK_MESSAGE(r_len == 4, "Length == 4 bytes for header.");
|
||||
CHECK_MESSAGE(buffer[0] == 0x00, "Variant::NIL");
|
||||
CHECK(buffer[1] == 0x00);
|
||||
CHECK(buffer[2] == 0x00);
|
||||
|
|
@ -174,7 +195,7 @@ TEST_CASE("[Marshalls] INT 32 bit Variant encoding") {
|
|||
uint8_t buffer[8];
|
||||
|
||||
CHECK(encode_variant(variant, buffer, r_len) == OK);
|
||||
CHECK_MESSAGE(r_len == 8, "Length == 4 bytes for header + 4 bytes for int32_t");
|
||||
CHECK_MESSAGE(r_len == 8, "Length == 4 bytes for header + 4 bytes for `int32_t`.");
|
||||
CHECK_MESSAGE(buffer[0] == 0x02, "Variant::INT");
|
||||
CHECK(buffer[1] == 0x00);
|
||||
CHECK(buffer[2] == 0x00);
|
||||
|
|
@ -192,7 +213,7 @@ TEST_CASE("[Marshalls] INT 64 bit Variant encoding") {
|
|||
uint8_t buffer[12];
|
||||
|
||||
CHECK(encode_variant(variant, buffer, r_len) == OK);
|
||||
CHECK_MESSAGE(r_len == 12, "Length == 4 bytes for header + 8 bytes for int64_t");
|
||||
CHECK_MESSAGE(r_len == 12, "Length == 4 bytes for header + 8 bytes for `int64_t`.");
|
||||
CHECK_MESSAGE(buffer[0] == 0x02, "Variant::INT");
|
||||
CHECK(buffer[1] == 0x00);
|
||||
CHECK_MESSAGE(buffer[2] == 0x01, "HEADER_DATA_FLAG_64");
|
||||
|
|
@ -214,7 +235,7 @@ TEST_CASE("[Marshalls] FLOAT single precision Variant encoding") {
|
|||
uint8_t buffer[8];
|
||||
|
||||
CHECK(encode_variant(variant, buffer, r_len) == OK);
|
||||
CHECK_MESSAGE(r_len == 8, "Length == 4 bytes for header + 4 bytes for float");
|
||||
CHECK_MESSAGE(r_len == 8, "Length == 4 bytes for header + 4 bytes for `float`.");
|
||||
CHECK_MESSAGE(buffer[0] == 0x03, "Variant::FLOAT");
|
||||
CHECK(buffer[1] == 0x00);
|
||||
CHECK(buffer[2] == 0x00);
|
||||
|
|
@ -232,7 +253,7 @@ TEST_CASE("[Marshalls] FLOAT double precision Variant encoding") {
|
|||
uint8_t buffer[12];
|
||||
|
||||
CHECK(encode_variant(variant, buffer, r_len) == OK);
|
||||
CHECK_MESSAGE(r_len == 12, "Length == 4 bytes for header + 8 bytes for double");
|
||||
CHECK_MESSAGE(r_len == 12, "Length == 4 bytes for header + 8 bytes for `double`.");
|
||||
CHECK_MESSAGE(buffer[0] == 0x03, "Variant::FLOAT");
|
||||
CHECK(buffer[1] == 0x00);
|
||||
CHECK_MESSAGE(buffer[2] == 0x01, "HEADER_DATA_FLAG_64");
|
||||
|
|
@ -335,10 +356,10 @@ TEST_CASE("[Marshalls] Typed array encoding") {
|
|||
uint8_t buffer[24];
|
||||
|
||||
CHECK(encode_variant(array, buffer, r_len) == OK);
|
||||
CHECK_MESSAGE(r_len == 24, "Length == 4 bytes for header + 4 bytes for array type + 4 bytes for array size + 12 bytes for element");
|
||||
CHECK_MESSAGE(r_len == 24, "Length == 4 bytes for header + 4 bytes for array type + 4 bytes for array size + 12 bytes for element.");
|
||||
CHECK_MESSAGE(buffer[0] == 0x1c, "Variant::ARRAY");
|
||||
CHECK(buffer[1] == 0x00);
|
||||
CHECK_MESSAGE(buffer[2] == 0x01, "HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN");
|
||||
CHECK_MESSAGE(buffer[2] == 0x01, "CONTAINER_TYPE_KIND_BUILTIN");
|
||||
CHECK(buffer[3] == 0x00);
|
||||
// Check array type.
|
||||
CHECK_MESSAGE(buffer[4] == 0x02, "Variant::INT");
|
||||
|
|
@ -370,7 +391,7 @@ TEST_CASE("[Marshalls] Typed array decoding") {
|
|||
Variant variant;
|
||||
int r_len;
|
||||
uint8_t buffer[] = {
|
||||
0x1c, 0x00, 0x01, 0x00, // Variant::ARRAY, HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN
|
||||
0x1c, 0x00, 0x01, 0x00, // Variant::ARRAY, CONTAINER_TYPE_KIND_BUILTIN
|
||||
0x02, 0x00, 0x00, 0x00, // Array type (Variant::INT).
|
||||
0x01, 0x00, 0x00, 0x00, // Array size.
|
||||
0x02, 0x00, 0x01, 0x00, // Element type (Variant::INT, HEADER_DATA_FLAG_64).
|
||||
|
|
@ -386,6 +407,89 @@ TEST_CASE("[Marshalls] Typed array decoding") {
|
|||
CHECK(array[0] == Variant(uint64_t(0x0f123456789abcdef)));
|
||||
}
|
||||
|
||||
TEST_CASE("[Marshalls] Typed dicttionary encoding") {
|
||||
int r_len;
|
||||
Dictionary dictionary;
|
||||
dictionary.set_typed(Variant::INT, StringName(), Ref<Script>(), Variant::INT, StringName(), Ref<Script>());
|
||||
dictionary[Variant(uint64_t(0x0f123456789abcdef))] = Variant(uint64_t(0x0f123456789abcdef));
|
||||
uint8_t buffer[40];
|
||||
|
||||
CHECK(encode_variant(dictionary, buffer, r_len) == OK);
|
||||
CHECK_MESSAGE(r_len == 40, "Length == 4 bytes for header + 8 bytes for dictionary type + 4 bytes for dictionary size + 24 bytes for key-value pair.");
|
||||
CHECK_MESSAGE(buffer[0] == 0x1b, "Variant::DICTIONARY");
|
||||
CHECK(buffer[1] == 0x00);
|
||||
CHECK_MESSAGE(buffer[2] == 0x05, "key: CONTAINER_TYPE_KIND_BUILTIN | value: CONTAINER_TYPE_KIND_BUILTIN");
|
||||
CHECK(buffer[3] == 0x00);
|
||||
// Check dictionary key type.
|
||||
CHECK_MESSAGE(buffer[4] == 0x02, "Variant::INT");
|
||||
CHECK(buffer[5] == 0x00);
|
||||
CHECK(buffer[6] == 0x00);
|
||||
CHECK(buffer[7] == 0x00);
|
||||
// Check dictionary value type.
|
||||
CHECK_MESSAGE(buffer[8] == 0x02, "Variant::INT");
|
||||
CHECK(buffer[9] == 0x00);
|
||||
CHECK(buffer[10] == 0x00);
|
||||
CHECK(buffer[11] == 0x00);
|
||||
// Check dictionary size.
|
||||
CHECK(buffer[12] == 0x01);
|
||||
CHECK(buffer[13] == 0x00);
|
||||
CHECK(buffer[14] == 0x00);
|
||||
CHECK(buffer[15] == 0x00);
|
||||
// Check key type.
|
||||
CHECK_MESSAGE(buffer[16] == 0x02, "Variant::INT");
|
||||
CHECK(buffer[17] == 0x00);
|
||||
CHECK_MESSAGE(buffer[18] == 0x01, "HEADER_DATA_FLAG_64");
|
||||
CHECK(buffer[19] == 0x00);
|
||||
// Check key value.
|
||||
CHECK(buffer[20] == 0xef);
|
||||
CHECK(buffer[21] == 0xcd);
|
||||
CHECK(buffer[22] == 0xab);
|
||||
CHECK(buffer[23] == 0x89);
|
||||
CHECK(buffer[24] == 0x67);
|
||||
CHECK(buffer[25] == 0x45);
|
||||
CHECK(buffer[26] == 0x23);
|
||||
CHECK(buffer[27] == 0xf1);
|
||||
// Check value type.
|
||||
CHECK_MESSAGE(buffer[28] == 0x02, "Variant::INT");
|
||||
CHECK(buffer[29] == 0x00);
|
||||
CHECK_MESSAGE(buffer[30] == 0x01, "HEADER_DATA_FLAG_64");
|
||||
CHECK(buffer[31] == 0x00);
|
||||
// Check value value.
|
||||
CHECK(buffer[32] == 0xef);
|
||||
CHECK(buffer[33] == 0xcd);
|
||||
CHECK(buffer[34] == 0xab);
|
||||
CHECK(buffer[35] == 0x89);
|
||||
CHECK(buffer[36] == 0x67);
|
||||
CHECK(buffer[37] == 0x45);
|
||||
CHECK(buffer[38] == 0x23);
|
||||
CHECK(buffer[39] == 0xf1);
|
||||
}
|
||||
|
||||
TEST_CASE("[Marshalls] Typed dictionary decoding") {
|
||||
Variant variant;
|
||||
int r_len;
|
||||
uint8_t buffer[] = {
|
||||
0x1b, 0x00, 0x05, 0x00, // Variant::DICTIONARY, key: CONTAINER_TYPE_KIND_BUILTIN | value: CONTAINER_TYPE_KIND_BUILTIN
|
||||
0x02, 0x00, 0x00, 0x00, // Dictionary key type (Variant::INT).
|
||||
0x02, 0x00, 0x00, 0x00, // Dictionary value type (Variant::INT).
|
||||
0x01, 0x00, 0x00, 0x00, // Dictionary size.
|
||||
0x02, 0x00, 0x01, 0x00, // Key type (Variant::INT, HEADER_DATA_FLAG_64).
|
||||
0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0xf1, // Key value.
|
||||
0x02, 0x00, 0x01, 0x00, // Value type (Variant::INT, HEADER_DATA_FLAG_64).
|
||||
0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0xf1, // Value value.
|
||||
};
|
||||
|
||||
CHECK(decode_variant(variant, buffer, 40, &r_len) == OK);
|
||||
CHECK(r_len == 40);
|
||||
CHECK(variant.get_type() == Variant::DICTIONARY);
|
||||
Dictionary dictionary = variant;
|
||||
CHECK(dictionary.get_typed_key_builtin() == Variant::INT);
|
||||
CHECK(dictionary.get_typed_value_builtin() == Variant::INT);
|
||||
CHECK(dictionary.size() == 1);
|
||||
CHECK(dictionary.has(Variant(uint64_t(0x0f123456789abcdef))));
|
||||
CHECK(dictionary[Variant(uint64_t(0x0f123456789abcdef))] == Variant(uint64_t(0x0f123456789abcdef)));
|
||||
}
|
||||
|
||||
} // namespace TestMarshalls
|
||||
|
||||
#endif // TEST_MARSHALLS_H
|
||||
|
|
|
|||
204
engine/tests/core/io/test_packet_peer.h
Normal file
204
engine/tests/core/io/test_packet_peer.h
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
/**************************************************************************/
|
||||
/* test_packet_peer.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_PACKET_PEER_H
|
||||
#define TEST_PACKET_PEER_H
|
||||
|
||||
#include "core/io/packet_peer.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestPacketPeer {
|
||||
|
||||
TEST_CASE("[PacketPeer][PacketPeerStream] Encode buffer max size") {
|
||||
Ref<PacketPeerStream> pps;
|
||||
pps.instantiate();
|
||||
|
||||
SUBCASE("Default value") {
|
||||
CHECK_EQ(pps->get_encode_buffer_max_size(), 8 * 1024 * 1024);
|
||||
}
|
||||
|
||||
SUBCASE("Max encode buffer must be at least 1024 bytes") {
|
||||
ERR_PRINT_OFF;
|
||||
pps->set_encode_buffer_max_size(42);
|
||||
ERR_PRINT_ON;
|
||||
|
||||
CHECK_EQ(pps->get_encode_buffer_max_size(), 8 * 1024 * 1024);
|
||||
}
|
||||
|
||||
SUBCASE("Max encode buffer cannot exceed 256 MiB") {
|
||||
ERR_PRINT_OFF;
|
||||
pps->set_encode_buffer_max_size((256 * 1024 * 1024) + 42);
|
||||
ERR_PRINT_ON;
|
||||
|
||||
CHECK_EQ(pps->get_encode_buffer_max_size(), 8 * 1024 * 1024);
|
||||
}
|
||||
|
||||
SUBCASE("Should be next power of two") {
|
||||
pps->set_encode_buffer_max_size(2000);
|
||||
|
||||
CHECK_EQ(pps->get_encode_buffer_max_size(), 2048);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[PacketPeer][PacketPeerStream] Read a variant from peer") {
|
||||
String godot_rules = "Godot Rules!!!";
|
||||
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
spb->put_var(godot_rules);
|
||||
spb->seek(0);
|
||||
|
||||
Ref<PacketPeerStream> pps;
|
||||
pps.instantiate();
|
||||
pps->set_stream_peer(spb);
|
||||
|
||||
Variant value;
|
||||
CHECK_EQ(pps->get_var(value), Error::OK);
|
||||
CHECK_EQ(String(value), godot_rules);
|
||||
}
|
||||
|
||||
TEST_CASE("[PacketPeer][PacketPeerStream] Read a variant from peer fails") {
|
||||
Ref<PacketPeerStream> pps;
|
||||
pps.instantiate();
|
||||
|
||||
Variant value;
|
||||
ERR_PRINT_OFF;
|
||||
CHECK_EQ(pps->get_var(value), Error::ERR_UNCONFIGURED);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
TEST_CASE("[PacketPeer][PacketPeerStream] Put a variant to peer") {
|
||||
String godot_rules = "Godot Rules!!!";
|
||||
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
|
||||
Ref<PacketPeerStream> pps;
|
||||
pps.instantiate();
|
||||
pps->set_stream_peer(spb);
|
||||
|
||||
CHECK_EQ(pps->put_var(godot_rules), Error::OK);
|
||||
|
||||
spb->seek(0);
|
||||
CHECK_EQ(String(spb->get_var()), godot_rules);
|
||||
}
|
||||
|
||||
TEST_CASE("[PacketPeer][PacketPeerStream] Put a variant to peer out of memory failure") {
|
||||
String more_than_1mb = String("*").repeat(1024 + 1);
|
||||
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
|
||||
Ref<PacketPeerStream> pps;
|
||||
pps.instantiate();
|
||||
pps->set_stream_peer(spb);
|
||||
pps->set_encode_buffer_max_size(1024);
|
||||
|
||||
ERR_PRINT_OFF;
|
||||
CHECK_EQ(pps->put_var(more_than_1mb), Error::ERR_OUT_OF_MEMORY);
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
TEST_CASE("[PacketPeer][PacketPeerStream] Get packet buffer") {
|
||||
String godot_rules = "Godot Rules!!!";
|
||||
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
// First 4 bytes are the length of the string.
|
||||
CharString cs = godot_rules.ascii();
|
||||
Vector<uint8_t> buffer = { (uint8_t)(cs.length() + 1), 0, 0, 0 };
|
||||
buffer.resize_zeroed(4 + cs.length() + 1);
|
||||
memcpy(buffer.ptrw() + 4, cs.get_data(), cs.length());
|
||||
spb->set_data_array(buffer);
|
||||
|
||||
Ref<PacketPeerStream> pps;
|
||||
pps.instantiate();
|
||||
pps->set_stream_peer(spb);
|
||||
|
||||
buffer.clear();
|
||||
CHECK_EQ(pps->get_packet_buffer(buffer), Error::OK);
|
||||
|
||||
CHECK_EQ(String(reinterpret_cast<const char *>(buffer.ptr())), godot_rules);
|
||||
}
|
||||
|
||||
TEST_CASE("[PacketPeer][PacketPeerStream] Get packet buffer from an empty peer") {
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
|
||||
Ref<PacketPeerStream> pps;
|
||||
pps.instantiate();
|
||||
pps->set_stream_peer(spb);
|
||||
|
||||
Vector<uint8_t> buffer;
|
||||
ERR_PRINT_OFF;
|
||||
CHECK_EQ(pps->get_packet_buffer(buffer), Error::ERR_UNAVAILABLE);
|
||||
ERR_PRINT_ON;
|
||||
CHECK_EQ(buffer.size(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE("[PacketPeer][PacketPeerStream] Put packet buffer") {
|
||||
String godot_rules = "Godot Rules!!!";
|
||||
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
|
||||
Ref<PacketPeerStream> pps;
|
||||
pps.instantiate();
|
||||
pps->set_stream_peer(spb);
|
||||
|
||||
CHECK_EQ(pps->put_packet_buffer(godot_rules.to_ascii_buffer()), Error::OK);
|
||||
|
||||
spb->seek(0);
|
||||
CHECK_EQ(spb->get_string(), godot_rules);
|
||||
// First 4 bytes are the length of the string.
|
||||
CharString cs = godot_rules.ascii();
|
||||
Vector<uint8_t> buffer = { (uint8_t)cs.length(), 0, 0, 0 };
|
||||
buffer.resize(4 + cs.length());
|
||||
memcpy(buffer.ptrw() + 4, cs.get_data(), cs.length());
|
||||
CHECK_EQ(spb->get_data_array(), buffer);
|
||||
}
|
||||
|
||||
TEST_CASE("[PacketPeer][PacketPeerStream] Put packet buffer when is empty") {
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
|
||||
Ref<PacketPeerStream> pps;
|
||||
pps.instantiate();
|
||||
pps->set_stream_peer(spb);
|
||||
|
||||
Vector<uint8_t> buffer;
|
||||
CHECK_EQ(pps->put_packet_buffer(buffer), Error::OK);
|
||||
|
||||
CHECK_EQ(spb->get_size(), 0);
|
||||
}
|
||||
|
||||
} // namespace TestPacketPeer
|
||||
|
||||
#endif // TEST_PACKET_PEER_H
|
||||
71
engine/tests/core/io/test_resource_uid.h
Normal file
71
engine/tests/core/io/test_resource_uid.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/**************************************************************************/
|
||||
/* test_resource_uid.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_RESOURCE_UID_H
|
||||
#define TEST_RESOURCE_UID_H
|
||||
|
||||
#include "core/io/resource_uid.h"
|
||||
|
||||
#include "thirdparty/doctest/doctest.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestResourceUID {
|
||||
|
||||
TEST_CASE("[ResourceUID] Must encode/decode maximum/minimum UID correctly") {
|
||||
CHECK_MESSAGE(ResourceUID::get_singleton()->id_to_text(0x7fffffffffffffff) == "uid://d4n4ub6itg400", "Maximum UID must encode correctly.");
|
||||
CHECK_MESSAGE(ResourceUID::get_singleton()->text_to_id("uid://d4n4ub6itg400") == 0x7fffffffffffffff, "Maximum UID must decode correctly.");
|
||||
|
||||
CHECK_MESSAGE(ResourceUID::get_singleton()->id_to_text(0) == "uid://a", "Minimum UID must encode correctly.");
|
||||
CHECK_MESSAGE(ResourceUID::get_singleton()->text_to_id("uid://a") == 0, "Minimum UID must decode correctly.");
|
||||
}
|
||||
|
||||
TEST_CASE("[ResourceUID] Must encode and decode invalid UIDs correctly") {
|
||||
ResourceUID *rid = ResourceUID::get_singleton();
|
||||
CHECK_MESSAGE(rid->id_to_text(-1) == "uid://<invalid>", "Invalid UID -1 must encode correctly.");
|
||||
CHECK_MESSAGE(rid->text_to_id("uid://<invalid>") == -1, "Invalid UID -1 must decode correctly.");
|
||||
|
||||
CHECK_MESSAGE(rid->id_to_text(-2) == rid->id_to_text(-1), "Invalid UID -2 must encode to the same as -1.");
|
||||
|
||||
CHECK_MESSAGE(rid->text_to_id("dm3rdgs30kfci") == -1, "UID without scheme must decode correctly.");
|
||||
}
|
||||
|
||||
TEST_CASE("[ResourceUID] Must encode and decode various UIDs correctly") {
|
||||
ResourceUID *rid = ResourceUID::get_singleton();
|
||||
CHECK_MESSAGE(rid->id_to_text(1) == "uid://b", "UID 1 must encode correctly.");
|
||||
CHECK_MESSAGE(rid->text_to_id("uid://b") == 1, "UID 1 must decode correctly.");
|
||||
|
||||
CHECK_MESSAGE(rid->id_to_text(8060368642360689600) == "uid://dm3rdgs30kfci", "A normal UID must encode correctly.");
|
||||
CHECK_MESSAGE(rid->text_to_id("uid://dm3rdgs30kfci") == 8060368642360689600, "A normal UID must decode correctly.");
|
||||
}
|
||||
|
||||
} // namespace TestResourceUID
|
||||
|
||||
#endif // TEST_RESOURCE_UID_H
|
||||
311
engine/tests/core/io/test_stream_peer.h
Normal file
311
engine/tests/core/io/test_stream_peer.h
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
/**************************************************************************/
|
||||
/* test_stream_peer.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_STREAM_PEER_H
|
||||
#define TEST_STREAM_PEER_H
|
||||
|
||||
#include "core/io/stream_peer.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestStreamPeer {
|
||||
|
||||
TEST_CASE("[StreamPeer] Initialization through StreamPeerBuffer") {
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
|
||||
CHECK_EQ(spb->is_big_endian_enabled(), false);
|
||||
}
|
||||
|
||||
TEST_CASE("[StreamPeer] Get and sets through StreamPeerBuffer") {
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
|
||||
SUBCASE("A int8_t value") {
|
||||
int8_t value = 42;
|
||||
|
||||
spb->clear();
|
||||
spb->put_8(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_8(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A uint8_t value") {
|
||||
uint8_t value = 42;
|
||||
|
||||
spb->clear();
|
||||
spb->put_u8(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_u8(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A int16_t value") {
|
||||
int16_t value = 42;
|
||||
|
||||
spb->clear();
|
||||
spb->put_16(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_16(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A uint16_t value") {
|
||||
uint16_t value = 42;
|
||||
|
||||
spb->clear();
|
||||
spb->put_u16(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_u16(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A int32_t value") {
|
||||
int32_t value = 42;
|
||||
|
||||
spb->clear();
|
||||
spb->put_32(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_32(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A uint32_t value") {
|
||||
uint32_t value = 42;
|
||||
|
||||
spb->clear();
|
||||
spb->put_u32(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_u32(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A int64_t value") {
|
||||
int64_t value = 42;
|
||||
|
||||
spb->clear();
|
||||
spb->put_64(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_64(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A int64_t value") {
|
||||
uint64_t value = 42;
|
||||
|
||||
spb->clear();
|
||||
spb->put_u64(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_u64(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A half-precision float value") {
|
||||
float value = 3.1415927f;
|
||||
float expected = 3.14062f;
|
||||
|
||||
spb->clear();
|
||||
spb->put_half(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK(spb->get_half() == doctest::Approx(expected));
|
||||
}
|
||||
|
||||
SUBCASE("A float value") {
|
||||
float value = 42.0f;
|
||||
|
||||
spb->clear();
|
||||
spb->put_float(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_float(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A double value") {
|
||||
double value = 42.0;
|
||||
|
||||
spb->clear();
|
||||
spb->put_double(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_double(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A string value") {
|
||||
String value = "Hello, World!";
|
||||
|
||||
spb->clear();
|
||||
spb->put_string(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_string(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A utf8 string value") {
|
||||
String value = String::utf8("Hello✩, World✩!");
|
||||
|
||||
spb->clear();
|
||||
spb->put_utf8_string(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_utf8_string(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A variant value") {
|
||||
Array value;
|
||||
value.push_front(42);
|
||||
value.push_front("Hello, World!");
|
||||
|
||||
spb->clear();
|
||||
spb->put_var(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_var(), value);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[StreamPeer] Get and sets big endian through StreamPeerBuffer") {
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
spb->set_big_endian(true);
|
||||
|
||||
SUBCASE("A int16_t value") {
|
||||
int16_t value = 42;
|
||||
|
||||
spb->clear();
|
||||
spb->put_16(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_16(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A uint16_t value") {
|
||||
uint16_t value = 42;
|
||||
|
||||
spb->clear();
|
||||
spb->put_u16(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_u16(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A int32_t value") {
|
||||
int32_t value = 42;
|
||||
|
||||
spb->clear();
|
||||
spb->put_32(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_32(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A uint32_t value") {
|
||||
uint32_t value = 42;
|
||||
|
||||
spb->clear();
|
||||
spb->put_u32(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_u32(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A int64_t value") {
|
||||
int64_t value = 42;
|
||||
|
||||
spb->clear();
|
||||
spb->put_64(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_64(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A int64_t value") {
|
||||
uint64_t value = 42;
|
||||
|
||||
spb->clear();
|
||||
spb->put_u64(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_u64(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A float value") {
|
||||
float value = 42.0f;
|
||||
|
||||
spb->clear();
|
||||
spb->put_float(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_float(), value);
|
||||
}
|
||||
|
||||
SUBCASE("A half-precision float value") {
|
||||
float value = 3.1415927f;
|
||||
float expected = 3.14062f;
|
||||
|
||||
spb->clear();
|
||||
spb->put_half(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK(spb->get_half() == doctest::Approx(expected));
|
||||
}
|
||||
|
||||
SUBCASE("A double value") {
|
||||
double value = 42.0;
|
||||
|
||||
spb->clear();
|
||||
spb->put_double(value);
|
||||
spb->seek(0);
|
||||
|
||||
CHECK_EQ(spb->get_double(), value);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[StreamPeer] Get string when there is no string") {
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
|
||||
ERR_PRINT_OFF;
|
||||
CHECK_EQ(spb->get_string(), "");
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
TEST_CASE("[StreamPeer] Get UTF8 string when there is no string") {
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
|
||||
ERR_PRINT_OFF;
|
||||
CHECK_EQ(spb->get_utf8_string(), "");
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
} // namespace TestStreamPeer
|
||||
|
||||
#endif // TEST_STREAM_PEER_H
|
||||
185
engine/tests/core/io/test_stream_peer_buffer.h
Normal file
185
engine/tests/core/io/test_stream_peer_buffer.h
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
/**************************************************************************/
|
||||
/* test_stream_peer_buffer.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_STREAM_PEER_BUFFER_H
|
||||
#define TEST_STREAM_PEER_BUFFER_H
|
||||
|
||||
#include "core/io/stream_peer.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestStreamPeerBuffer {
|
||||
|
||||
TEST_CASE("[StreamPeerBuffer] Initialization") {
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
CHECK_EQ(spb->get_size(), 0);
|
||||
CHECK_EQ(spb->get_position(), 0);
|
||||
CHECK_EQ(spb->get_available_bytes(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE("[StreamPeerBuffer] Seek") {
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
uint8_t first = 5;
|
||||
uint8_t second = 7;
|
||||
uint8_t third = 11;
|
||||
|
||||
spb->put_u8(first);
|
||||
spb->put_u8(second);
|
||||
spb->put_u8(third);
|
||||
|
||||
spb->seek(0);
|
||||
CHECK_EQ(spb->get_u8(), first);
|
||||
CHECK_EQ(spb->get_u8(), second);
|
||||
CHECK_EQ(spb->get_u8(), third);
|
||||
|
||||
spb->seek(1);
|
||||
CHECK_EQ(spb->get_position(), 1);
|
||||
CHECK_EQ(spb->get_u8(), second);
|
||||
|
||||
spb->seek(1);
|
||||
ERR_PRINT_OFF;
|
||||
spb->seek(-1);
|
||||
ERR_PRINT_ON;
|
||||
CHECK_EQ(spb->get_position(), 1);
|
||||
ERR_PRINT_OFF;
|
||||
spb->seek(5);
|
||||
ERR_PRINT_ON;
|
||||
CHECK_EQ(spb->get_position(), 1);
|
||||
}
|
||||
|
||||
TEST_CASE("[StreamPeerBuffer] Resize") {
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
CHECK_EQ(spb->get_size(), 0);
|
||||
CHECK_EQ(spb->get_position(), 0);
|
||||
CHECK_EQ(spb->get_available_bytes(), 0);
|
||||
|
||||
spb->resize(42);
|
||||
CHECK_EQ(spb->get_size(), 42);
|
||||
CHECK_EQ(spb->get_position(), 0);
|
||||
CHECK_EQ(spb->get_available_bytes(), 42);
|
||||
|
||||
spb->seek(21);
|
||||
CHECK_EQ(spb->get_size(), 42);
|
||||
CHECK_EQ(spb->get_position(), 21);
|
||||
CHECK_EQ(spb->get_available_bytes(), 21);
|
||||
}
|
||||
|
||||
TEST_CASE("[StreamPeerBuffer] Get underlying data array") {
|
||||
uint8_t first = 5;
|
||||
uint8_t second = 7;
|
||||
uint8_t third = 11;
|
||||
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
spb->put_u8(first);
|
||||
spb->put_u8(second);
|
||||
spb->put_u8(third);
|
||||
|
||||
Vector<uint8_t> data_array = spb->get_data_array();
|
||||
|
||||
CHECK_EQ(data_array[0], first);
|
||||
CHECK_EQ(data_array[1], second);
|
||||
CHECK_EQ(data_array[2], third);
|
||||
}
|
||||
|
||||
TEST_CASE("[StreamPeerBuffer] Set underlying data array") {
|
||||
uint8_t first = 5;
|
||||
uint8_t second = 7;
|
||||
uint8_t third = 11;
|
||||
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
spb->put_u8(1);
|
||||
spb->put_u8(2);
|
||||
spb->put_u8(3);
|
||||
|
||||
Vector<uint8_t> new_data_array;
|
||||
new_data_array.push_back(first);
|
||||
new_data_array.push_back(second);
|
||||
new_data_array.push_back(third);
|
||||
|
||||
spb->set_data_array(new_data_array);
|
||||
|
||||
CHECK_EQ(spb->get_u8(), first);
|
||||
CHECK_EQ(spb->get_u8(), second);
|
||||
CHECK_EQ(spb->get_u8(), third);
|
||||
}
|
||||
|
||||
TEST_CASE("[StreamPeerBuffer] Duplicate") {
|
||||
uint8_t first = 5;
|
||||
uint8_t second = 7;
|
||||
uint8_t third = 11;
|
||||
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
spb->put_u8(first);
|
||||
spb->put_u8(second);
|
||||
spb->put_u8(third);
|
||||
|
||||
Ref<StreamPeerBuffer> spb2 = spb->duplicate();
|
||||
|
||||
CHECK_EQ(spb2->get_u8(), first);
|
||||
CHECK_EQ(spb2->get_u8(), second);
|
||||
CHECK_EQ(spb2->get_u8(), third);
|
||||
}
|
||||
|
||||
TEST_CASE("[StreamPeerBuffer] Put data with size equal to zero does nothing") {
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
uint8_t data = 42;
|
||||
|
||||
Error error = spb->put_data((const uint8_t *)&data, 0);
|
||||
|
||||
CHECK_EQ(error, OK);
|
||||
CHECK_EQ(spb->get_size(), 0);
|
||||
CHECK_EQ(spb->get_position(), 0);
|
||||
CHECK_EQ(spb->get_available_bytes(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE("[StreamPeerBuffer] Get data with invalid size returns an error") {
|
||||
Ref<StreamPeerBuffer> spb;
|
||||
spb.instantiate();
|
||||
uint8_t data = 42;
|
||||
spb->put_u8(data);
|
||||
spb->seek(0);
|
||||
|
||||
uint8_t data_out = 0;
|
||||
Error error = spb->get_data(&data_out, 3);
|
||||
|
||||
CHECK_EQ(error, ERR_INVALID_PARAMETER);
|
||||
CHECK_EQ(spb->get_size(), 1);
|
||||
CHECK_EQ(spb->get_position(), 1);
|
||||
}
|
||||
|
||||
} // namespace TestStreamPeerBuffer
|
||||
|
||||
#endif // TEST_STREAM_PEER_BUFFER_H
|
||||
255
engine/tests/core/io/test_tcp_server.h
Normal file
255
engine/tests/core/io/test_tcp_server.h
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
/**************************************************************************/
|
||||
/* test_tcp_server.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_TCP_SERVER_H
|
||||
#define TEST_TCP_SERVER_H
|
||||
|
||||
#include "core/io/stream_peer_tcp.h"
|
||||
#include "core/io/tcp_server.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace TestTCPServer {
|
||||
|
||||
const int PORT = 12345;
|
||||
const IPAddress LOCALHOST("127.0.0.1");
|
||||
const uint32_t SLEEP_DURATION = 1000;
|
||||
const uint64_t MAX_WAIT_USEC = 2000000;
|
||||
|
||||
void wait_for_condition(std::function<bool()> f_test) {
|
||||
const uint64_t time = OS::get_singleton()->get_ticks_usec();
|
||||
while (!f_test() && (OS::get_singleton()->get_ticks_usec() - time) < MAX_WAIT_USEC) {
|
||||
OS::get_singleton()->delay_usec(SLEEP_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<TCPServer> create_server(const IPAddress &p_address, int p_port) {
|
||||
Ref<TCPServer> server;
|
||||
server.instantiate();
|
||||
|
||||
REQUIRE_EQ(server->listen(PORT, LOCALHOST), Error::OK);
|
||||
REQUIRE(server->is_listening());
|
||||
CHECK_FALSE(server->is_connection_available());
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
Ref<StreamPeerTCP> create_client(const IPAddress &p_address, int p_port) {
|
||||
Ref<StreamPeerTCP> client;
|
||||
client.instantiate();
|
||||
|
||||
REQUIRE_EQ(client->connect_to_host(LOCALHOST, PORT), Error::OK);
|
||||
CHECK_EQ(client->get_connected_host(), LOCALHOST);
|
||||
CHECK_EQ(client->get_connected_port(), PORT);
|
||||
CHECK_EQ(client->get_status(), StreamPeerTCP::STATUS_CONNECTING);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
Ref<StreamPeerTCP> accept_connection(Ref<TCPServer> &p_server) {
|
||||
wait_for_condition([&]() {
|
||||
return p_server->is_connection_available();
|
||||
});
|
||||
|
||||
REQUIRE(p_server->is_connection_available());
|
||||
Ref<StreamPeerTCP> client_from_server = p_server->take_connection();
|
||||
REQUIRE(client_from_server.is_valid());
|
||||
CHECK_EQ(client_from_server->get_connected_host(), LOCALHOST);
|
||||
CHECK_EQ(client_from_server->get_status(), StreamPeerTCP::STATUS_CONNECTED);
|
||||
|
||||
return client_from_server;
|
||||
}
|
||||
|
||||
TEST_CASE("[TCPServer] Instantiation") {
|
||||
Ref<TCPServer> server;
|
||||
server.instantiate();
|
||||
|
||||
REQUIRE(server.is_valid());
|
||||
CHECK_EQ(false, server->is_listening());
|
||||
}
|
||||
|
||||
TEST_CASE("[TCPServer] Accept a connection and receive/send data") {
|
||||
Ref<TCPServer> server = create_server(LOCALHOST, PORT);
|
||||
Ref<StreamPeerTCP> client = create_client(LOCALHOST, PORT);
|
||||
Ref<StreamPeerTCP> client_from_server = accept_connection(server);
|
||||
|
||||
wait_for_condition([&]() {
|
||||
return client->poll() != Error::OK || client->get_status() == StreamPeerTCP::STATUS_CONNECTED;
|
||||
});
|
||||
|
||||
CHECK_EQ(client->get_status(), StreamPeerTCP::STATUS_CONNECTED);
|
||||
|
||||
// Sending data from client to server.
|
||||
const String hello_world = "Hello World!";
|
||||
client->put_string(hello_world);
|
||||
CHECK_EQ(client_from_server->get_string(), hello_world);
|
||||
|
||||
// Sending data from server to client.
|
||||
const float pi = 3.1415;
|
||||
client_from_server->put_float(pi);
|
||||
CHECK_EQ(client->get_float(), pi);
|
||||
|
||||
client->disconnect_from_host();
|
||||
server->stop();
|
||||
CHECK_FALSE(server->is_listening());
|
||||
}
|
||||
|
||||
TEST_CASE("[TCPServer] Handle multiple clients at the same time") {
|
||||
Ref<TCPServer> server = create_server(LOCALHOST, PORT);
|
||||
|
||||
Vector<Ref<StreamPeerTCP>> clients;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
clients.push_back(create_client(LOCALHOST, PORT));
|
||||
}
|
||||
|
||||
Vector<Ref<StreamPeerTCP>> clients_from_server;
|
||||
for (int i = 0; i < clients.size(); i++) {
|
||||
clients_from_server.push_back(accept_connection(server));
|
||||
}
|
||||
|
||||
wait_for_condition([&]() {
|
||||
bool should_exit = true;
|
||||
for (Ref<StreamPeerTCP> &c : clients) {
|
||||
if (c->poll() != Error::OK) {
|
||||
return true;
|
||||
}
|
||||
StreamPeerTCP::Status status = c->get_status();
|
||||
if (status != StreamPeerTCP::STATUS_CONNECTED && status != StreamPeerTCP::STATUS_CONNECTING) {
|
||||
return true;
|
||||
}
|
||||
if (status != StreamPeerTCP::STATUS_CONNECTED) {
|
||||
should_exit = false;
|
||||
}
|
||||
}
|
||||
return should_exit;
|
||||
});
|
||||
|
||||
for (Ref<StreamPeerTCP> &c : clients) {
|
||||
REQUIRE_EQ(c->get_status(), StreamPeerTCP::STATUS_CONNECTED);
|
||||
}
|
||||
|
||||
// Sending data from each client to server.
|
||||
for (int i = 0; i < clients.size(); i++) {
|
||||
String hello_client = "Hello " + itos(i);
|
||||
clients[i]->put_string(hello_client);
|
||||
CHECK_EQ(clients_from_server[i]->get_string(), hello_client);
|
||||
}
|
||||
|
||||
for (Ref<StreamPeerTCP> &c : clients) {
|
||||
c->disconnect_from_host();
|
||||
}
|
||||
server->stop();
|
||||
}
|
||||
|
||||
TEST_CASE("[TCPServer] When stopped shouldn't accept new connections") {
|
||||
Ref<TCPServer> server = create_server(LOCALHOST, PORT);
|
||||
Ref<StreamPeerTCP> client = create_client(LOCALHOST, PORT);
|
||||
Ref<StreamPeerTCP> client_from_server = accept_connection(server);
|
||||
|
||||
wait_for_condition([&]() {
|
||||
return client->poll() != Error::OK || client->get_status() == StreamPeerTCP::STATUS_CONNECTED;
|
||||
});
|
||||
|
||||
CHECK_EQ(client->get_status(), StreamPeerTCP::STATUS_CONNECTED);
|
||||
|
||||
// Sending data from client to server.
|
||||
const String hello_world = "Hello World!";
|
||||
client->put_string(hello_world);
|
||||
CHECK_EQ(client_from_server->get_string(), hello_world);
|
||||
|
||||
client->disconnect_from_host();
|
||||
server->stop();
|
||||
CHECK_FALSE(server->is_listening());
|
||||
|
||||
// Make sure the client times out in less than the wait time.
|
||||
int timeout = ProjectSettings::get_singleton()->get_setting("network/limits/tcp/connect_timeout_seconds");
|
||||
ProjectSettings::get_singleton()->set_setting("network/limits/tcp/connect_timeout_seconds", 1);
|
||||
|
||||
Ref<StreamPeerTCP> new_client = create_client(LOCALHOST, PORT);
|
||||
|
||||
// Reset the timeout setting.
|
||||
ProjectSettings::get_singleton()->set_setting("network/limits/tcp/connect_timeout_seconds", timeout);
|
||||
|
||||
CHECK_FALSE(server->is_connection_available());
|
||||
|
||||
wait_for_condition([&]() {
|
||||
return new_client->poll() != Error::OK || new_client->get_status() == StreamPeerTCP::STATUS_ERROR;
|
||||
});
|
||||
|
||||
CHECK_FALSE(server->is_connection_available());
|
||||
|
||||
CHECK_EQ(new_client->get_status(), StreamPeerTCP::STATUS_ERROR);
|
||||
new_client->disconnect_from_host();
|
||||
CHECK_EQ(new_client->get_status(), StreamPeerTCP::STATUS_NONE);
|
||||
}
|
||||
|
||||
TEST_CASE("[TCPServer] Should disconnect client") {
|
||||
Ref<TCPServer> server = create_server(LOCALHOST, PORT);
|
||||
Ref<StreamPeerTCP> client = create_client(LOCALHOST, PORT);
|
||||
Ref<StreamPeerTCP> client_from_server = accept_connection(server);
|
||||
|
||||
wait_for_condition([&]() {
|
||||
return client->poll() != Error::OK || client->get_status() == StreamPeerTCP::STATUS_CONNECTED;
|
||||
});
|
||||
|
||||
CHECK_EQ(client->get_status(), StreamPeerTCP::STATUS_CONNECTED);
|
||||
|
||||
// Sending data from client to server.
|
||||
const String hello_world = "Hello World!";
|
||||
client->put_string(hello_world);
|
||||
CHECK_EQ(client_from_server->get_string(), hello_world);
|
||||
|
||||
client_from_server->disconnect_from_host();
|
||||
server->stop();
|
||||
CHECK_FALSE(server->is_listening());
|
||||
|
||||
// Wait for disconnection
|
||||
wait_for_condition([&]() {
|
||||
return client->poll() != Error::OK || client->get_status() == StreamPeerTCP::STATUS_NONE;
|
||||
});
|
||||
|
||||
// Wait for disconnection
|
||||
wait_for_condition([&]() {
|
||||
return client_from_server->poll() != Error::OK || client_from_server->get_status() == StreamPeerTCP::STATUS_NONE;
|
||||
});
|
||||
|
||||
CHECK_EQ(client->get_status(), StreamPeerTCP::STATUS_NONE);
|
||||
CHECK_EQ(client_from_server->get_status(), StreamPeerTCP::STATUS_NONE);
|
||||
|
||||
ERR_PRINT_OFF;
|
||||
CHECK_EQ(client->get_string(), String());
|
||||
CHECK_EQ(client_from_server->get_string(), String());
|
||||
ERR_PRINT_ON;
|
||||
}
|
||||
|
||||
} // namespace TestTCPServer
|
||||
|
||||
#endif // TEST_TCP_SERVER_H
|
||||
219
engine/tests/core/io/test_udp_server.h
Normal file
219
engine/tests/core/io/test_udp_server.h
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
/**************************************************************************/
|
||||
/* test_udp_server.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_UDP_SERVER_H
|
||||
#define TEST_UDP_SERVER_H
|
||||
|
||||
#include "core/io/packet_peer_udp.h"
|
||||
#include "core/io/udp_server.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestUDPServer {
|
||||
|
||||
const int PORT = 12345;
|
||||
const IPAddress LOCALHOST("127.0.0.1");
|
||||
const uint32_t SLEEP_DURATION = 1000;
|
||||
const uint64_t MAX_WAIT_USEC = 100000;
|
||||
|
||||
void wait_for_condition(std::function<bool()> f_test) {
|
||||
const uint64_t time = OS::get_singleton()->get_ticks_usec();
|
||||
while (!f_test() && (OS::get_singleton()->get_ticks_usec() - time) < MAX_WAIT_USEC) {
|
||||
OS::get_singleton()->delay_usec(SLEEP_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<UDPServer> create_server(const IPAddress &p_address, int p_port) {
|
||||
Ref<UDPServer> server;
|
||||
server.instantiate();
|
||||
|
||||
Error err = server->listen(PORT, LOCALHOST);
|
||||
REQUIRE_EQ(Error::OK, err);
|
||||
REQUIRE(server->is_listening());
|
||||
CHECK_FALSE(server->is_connection_available());
|
||||
CHECK_EQ(server->get_max_pending_connections(), 16);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
Ref<PacketPeerUDP> create_client(const IPAddress &p_address, int p_port) {
|
||||
Ref<PacketPeerUDP> client;
|
||||
client.instantiate();
|
||||
|
||||
Error err = client->connect_to_host(LOCALHOST, PORT);
|
||||
REQUIRE_EQ(Error::OK, err);
|
||||
CHECK(client->is_bound());
|
||||
CHECK(client->is_socket_connected());
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
Ref<PacketPeerUDP> accept_connection(Ref<UDPServer> &p_server) {
|
||||
wait_for_condition([&]() {
|
||||
return p_server->poll() != Error::OK || p_server->is_connection_available();
|
||||
});
|
||||
|
||||
CHECK_EQ(p_server->poll(), Error::OK);
|
||||
REQUIRE(p_server->is_connection_available());
|
||||
Ref<PacketPeerUDP> client_from_server = p_server->take_connection();
|
||||
REQUIRE(client_from_server.is_valid());
|
||||
CHECK(client_from_server->is_bound());
|
||||
CHECK(client_from_server->is_socket_connected());
|
||||
|
||||
return client_from_server;
|
||||
}
|
||||
|
||||
TEST_CASE("[UDPServer] Instantiation") {
|
||||
Ref<UDPServer> server;
|
||||
server.instantiate();
|
||||
|
||||
REQUIRE(server.is_valid());
|
||||
CHECK_EQ(false, server->is_listening());
|
||||
}
|
||||
|
||||
TEST_CASE("[UDPServer] Accept a connection and receive/send data") {
|
||||
Ref<UDPServer> server = create_server(LOCALHOST, PORT);
|
||||
Ref<PacketPeerUDP> client = create_client(LOCALHOST, PORT);
|
||||
|
||||
// Sending data from client to server.
|
||||
const String hello_world = "Hello World!";
|
||||
CHECK_EQ(client->put_var(hello_world), Error::OK);
|
||||
|
||||
Variant hello_world_received;
|
||||
Ref<PacketPeerUDP> client_from_server = accept_connection(server);
|
||||
CHECK_EQ(client_from_server->get_var(hello_world_received), Error::OK);
|
||||
CHECK_EQ(String(hello_world_received), hello_world);
|
||||
|
||||
// Sending data from server to client.
|
||||
const Variant pi = 3.1415;
|
||||
CHECK_EQ(client_from_server->put_var(pi), Error::OK);
|
||||
|
||||
wait_for_condition([&]() {
|
||||
return client->get_available_packet_count() > 0;
|
||||
});
|
||||
|
||||
CHECK_GT(client->get_available_packet_count(), 0);
|
||||
|
||||
Variant pi_received;
|
||||
CHECK_EQ(client->get_var(pi_received), Error::OK);
|
||||
CHECK_EQ(pi_received, pi);
|
||||
|
||||
client->close();
|
||||
server->stop();
|
||||
CHECK_FALSE(server->is_listening());
|
||||
}
|
||||
|
||||
TEST_CASE("[UDPServer] Handle multiple clients at the same time") {
|
||||
Ref<UDPServer> server = create_server(LOCALHOST, PORT);
|
||||
|
||||
Vector<Ref<PacketPeerUDP>> clients;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
Ref<PacketPeerUDP> c = create_client(LOCALHOST, PORT);
|
||||
|
||||
// Sending data from client to server.
|
||||
const String hello_client = itos(i);
|
||||
CHECK_EQ(c->put_var(hello_client), Error::OK);
|
||||
|
||||
clients.push_back(c);
|
||||
}
|
||||
|
||||
Array packets;
|
||||
for (int i = 0; i < clients.size(); i++) {
|
||||
Ref<PacketPeerUDP> cfs = accept_connection(server);
|
||||
|
||||
Variant received_var;
|
||||
CHECK_EQ(cfs->get_var(received_var), Error::OK);
|
||||
CHECK_EQ(received_var.get_type(), Variant::STRING);
|
||||
packets.push_back(received_var);
|
||||
|
||||
// Sending data from server to client.
|
||||
const float sent_float = 3.1415 + received_var.operator String().to_float();
|
||||
CHECK_EQ(cfs->put_var(sent_float), Error::OK);
|
||||
}
|
||||
|
||||
CHECK_EQ(packets.size(), clients.size());
|
||||
|
||||
packets.sort();
|
||||
for (int i = 0; i < clients.size(); i++) {
|
||||
CHECK_EQ(packets[i].operator String(), itos(i));
|
||||
}
|
||||
|
||||
wait_for_condition([&]() {
|
||||
bool should_exit = true;
|
||||
for (Ref<PacketPeerUDP> &c : clients) {
|
||||
int count = c->get_available_packet_count();
|
||||
if (count < 0) {
|
||||
return true;
|
||||
}
|
||||
if (count == 0) {
|
||||
should_exit = false;
|
||||
}
|
||||
}
|
||||
return should_exit;
|
||||
});
|
||||
|
||||
for (int i = 0; i < clients.size(); i++) {
|
||||
CHECK_GT(clients[i]->get_available_packet_count(), 0);
|
||||
|
||||
Variant received_var;
|
||||
const float expected = 3.1415 + i;
|
||||
CHECK_EQ(clients[i]->get_var(received_var), Error::OK);
|
||||
CHECK_EQ(received_var.get_type(), Variant::FLOAT);
|
||||
CHECK_EQ(received_var.operator float(), expected);
|
||||
}
|
||||
|
||||
for (Ref<PacketPeerUDP> &c : clients) {
|
||||
c->close();
|
||||
}
|
||||
server->stop();
|
||||
}
|
||||
|
||||
TEST_CASE("[UDPServer] Should not accept new connections after stop") {
|
||||
Ref<UDPServer> server = create_server(LOCALHOST, PORT);
|
||||
Ref<PacketPeerUDP> client = create_client(LOCALHOST, PORT);
|
||||
|
||||
// Sending data from client to server.
|
||||
const String hello_world = "Hello World!";
|
||||
CHECK_EQ(client->put_var(hello_world), Error::OK);
|
||||
|
||||
wait_for_condition([&]() {
|
||||
return server->poll() != Error::OK || server->is_connection_available();
|
||||
});
|
||||
|
||||
REQUIRE(server->is_connection_available());
|
||||
|
||||
server->stop();
|
||||
|
||||
CHECK_FALSE(server->is_listening());
|
||||
CHECK_FALSE(server->is_connection_available());
|
||||
}
|
||||
|
||||
} // namespace TestUDPServer
|
||||
|
||||
#endif // TEST_UDP_SERVER_H
|
||||
|
|
@ -48,7 +48,7 @@ TEST_CASE("[AABB] Constructor methods") {
|
|||
|
||||
TEST_CASE("[AABB] String conversion") {
|
||||
CHECK_MESSAGE(
|
||||
String(AABB(Vector3(-1.5, 2, -2.5), Vector3(4, 5, 6))) == "[P: (-1.5, 2, -2.5), S: (4, 5, 6)]",
|
||||
String(AABB(Vector3(-1.5, 2, -2.5), Vector3(4, 5, 6))) == "[P: (-1.5, 2.0, -2.5), S: (4.0, 5.0, 6.0)]",
|
||||
"The string representation should match the expected value.");
|
||||
}
|
||||
|
||||
|
|
@ -377,23 +377,23 @@ TEST_CASE("[AABB] Get longest/shortest axis") {
|
|||
TEST_CASE("[AABB] Get support") {
|
||||
const AABB aabb = AABB(Vector3(-1.5, 2, -2.5), Vector3(4, 5, 6));
|
||||
CHECK_MESSAGE(
|
||||
aabb.get_support(Vector3(1, 0, 0)).is_equal_approx(Vector3(2.5, 2, -2.5)),
|
||||
aabb.get_support(Vector3(1, 0, 0)) == Vector3(2.5, 2, -2.5),
|
||||
"get_support() should return the expected value.");
|
||||
CHECK_MESSAGE(
|
||||
aabb.get_support(Vector3(0.5, 1, 0)).is_equal_approx(Vector3(2.5, 7, -2.5)),
|
||||
aabb.get_support(Vector3(0.5, 1, 1)) == Vector3(2.5, 7, 3.5),
|
||||
"get_support() should return the expected value.");
|
||||
CHECK_MESSAGE(
|
||||
aabb.get_support(Vector3(0.5, 1, -400)).is_equal_approx(Vector3(2.5, 7, -2.5)),
|
||||
aabb.get_support(Vector3(0.5, 1, -400)) == Vector3(2.5, 7, -2.5),
|
||||
"get_support() should return the expected value.");
|
||||
CHECK_MESSAGE(
|
||||
aabb.get_support(Vector3(0, -1, 0)).is_equal_approx(Vector3(-1.5, 2, -2.5)),
|
||||
aabb.get_support(Vector3(0, -1, 0)) == Vector3(-1.5, 2, -2.5),
|
||||
"get_support() should return the expected value.");
|
||||
CHECK_MESSAGE(
|
||||
aabb.get_support(Vector3(0, -0.1, 0)).is_equal_approx(Vector3(-1.5, 2, -2.5)),
|
||||
aabb.get_support(Vector3(0, -0.1, 0)) == Vector3(-1.5, 2, -2.5),
|
||||
"get_support() should return the expected value.");
|
||||
CHECK_MESSAGE(
|
||||
aabb.get_support(Vector3()).is_equal_approx(Vector3(-1.5, 2, -2.5)),
|
||||
"get_support() should return the expected value with a null vector.");
|
||||
aabb.get_support(Vector3()) == Vector3(-1.5, 2, -2.5),
|
||||
"get_support() should return the AABB position when given a zero vector.");
|
||||
}
|
||||
|
||||
TEST_CASE("[AABB] Grow") {
|
||||
|
|
|
|||
|
|
@ -93,9 +93,9 @@ void test_rotation(Vector3 deg_original_euler, EulerOrder rot_order) {
|
|||
|
||||
Basis res = to_rotation.inverse() * rotation_from_computed_euler;
|
||||
|
||||
CHECK_MESSAGE((res.get_column(0) - Vector3(1.0, 0.0, 0.0)).length() <= 0.1, vformat("Fail due to X %s\n", String(res.get_column(0))));
|
||||
CHECK_MESSAGE((res.get_column(1) - Vector3(0.0, 1.0, 0.0)).length() <= 0.1, vformat("Fail due to Y %s\n", String(res.get_column(1))));
|
||||
CHECK_MESSAGE((res.get_column(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.1, vformat("Fail due to Z %s\n", String(res.get_column(2))));
|
||||
CHECK_MESSAGE((res.get_column(0) - Vector3(1.0, 0.0, 0.0)).length() <= 0.001, vformat("Fail due to X %s\n", String(res.get_column(0))));
|
||||
CHECK_MESSAGE((res.get_column(1) - Vector3(0.0, 1.0, 0.0)).length() <= 0.001, vformat("Fail due to Y %s\n", String(res.get_column(1))));
|
||||
CHECK_MESSAGE((res.get_column(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.001, vformat("Fail due to Z %s\n", String(res.get_column(2))));
|
||||
|
||||
// Double check `to_rotation` decomposing with XYZ rotation order.
|
||||
const Vector3 euler_xyz_from_rotation = to_rotation.get_euler(EulerOrder::XYZ);
|
||||
|
|
@ -103,9 +103,9 @@ void test_rotation(Vector3 deg_original_euler, EulerOrder rot_order) {
|
|||
|
||||
res = to_rotation.inverse() * rotation_from_xyz_computed_euler;
|
||||
|
||||
CHECK_MESSAGE((res.get_column(0) - Vector3(1.0, 0.0, 0.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to X %s\n", String(res.get_column(0))));
|
||||
CHECK_MESSAGE((res.get_column(1) - Vector3(0.0, 1.0, 0.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to Y %s\n", String(res.get_column(1))));
|
||||
CHECK_MESSAGE((res.get_column(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.1, vformat("Double check with XYZ rot order failed, due to Z %s\n", String(res.get_column(2))));
|
||||
CHECK_MESSAGE((res.get_column(0) - Vector3(1.0, 0.0, 0.0)).length() <= 0.001, vformat("Double check with XYZ rot order failed, due to X %s\n", String(res.get_column(0))));
|
||||
CHECK_MESSAGE((res.get_column(1) - Vector3(0.0, 1.0, 0.0)).length() <= 0.001, vformat("Double check with XYZ rot order failed, due to Y %s\n", String(res.get_column(1))));
|
||||
CHECK_MESSAGE((res.get_column(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.001, vformat("Double check with XYZ rot order failed, due to Z %s\n", String(res.get_column(2))));
|
||||
|
||||
INFO(vformat("Rotation order: %s\n.", get_rot_order_name(rot_order)));
|
||||
INFO(vformat("Original Rotation: %s\n", String(deg_original_euler)));
|
||||
|
|
@ -176,6 +176,12 @@ TEST_CASE("[Basis] Euler conversions") {
|
|||
vectors_to_test.push_back(Vector3(120.0, -150.0, -130.0));
|
||||
vectors_to_test.push_back(Vector3(120.0, 150.0, -130.0));
|
||||
vectors_to_test.push_back(Vector3(120.0, 150.0, 130.0));
|
||||
vectors_to_test.push_back(Vector3(89.9, 0.0, 0.0));
|
||||
vectors_to_test.push_back(Vector3(-89.9, 0.0, 0.0));
|
||||
vectors_to_test.push_back(Vector3(0.0, 89.9, 0.0));
|
||||
vectors_to_test.push_back(Vector3(0.0, -89.9, 0.0));
|
||||
vectors_to_test.push_back(Vector3(0.0, 0.0, 89.9));
|
||||
vectors_to_test.push_back(Vector3(0.0, 0.0, -89.9));
|
||||
|
||||
for (int h = 0; h < euler_order_to_test.size(); h += 1) {
|
||||
for (int i = 0; i < vectors_to_test.size(); i += 1) {
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ TEST_CASE("[Color] Conversion methods") {
|
|||
cyan.to_rgba64() == 0x0000'ffff'ffff'ffff,
|
||||
"The returned 64-bit BGR number should match the expected value.");
|
||||
CHECK_MESSAGE(
|
||||
String(cyan) == "(0, 1, 1, 1)",
|
||||
String(cyan) == "(0.0, 1.0, 1.0, 1.0)",
|
||||
"The string representation should match the expected value.");
|
||||
}
|
||||
|
||||
|
|
@ -160,6 +160,12 @@ TEST_CASE("[Color] Linear <-> sRGB conversion") {
|
|||
CHECK_MESSAGE(
|
||||
color_srgb.srgb_to_linear().is_equal_approx(Color(0.35, 0.5, 0.6, 0.7)),
|
||||
"The sRGB color converted back to linear color space should match the expected value.");
|
||||
CHECK_MESSAGE(
|
||||
Color(1.0, 1.0, 1.0, 1.0).srgb_to_linear() == (Color(1.0, 1.0, 1.0, 1.0)),
|
||||
"White converted from sRGB to linear should remain white.");
|
||||
CHECK_MESSAGE(
|
||||
Color(1.0, 1.0, 1.0, 1.0).linear_to_srgb() == (Color(1.0, 1.0, 1.0, 1.0)),
|
||||
"White converted from linear to sRGB should remain white.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Color] Named colors") {
|
||||
|
|
|
|||
|
|
@ -122,12 +122,68 @@ TEST_CASE("[Expression] Floating-point arithmetic") {
|
|||
"Float multiplication-addition-subtraction-division should return the expected result.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Expression] Floating-point notation") {
|
||||
Expression expression;
|
||||
|
||||
CHECK_MESSAGE(
|
||||
expression.parse("2.") == OK,
|
||||
"The expression should parse successfully.");
|
||||
CHECK_MESSAGE(
|
||||
double(expression.execute()) == doctest::Approx(2.0),
|
||||
"The expression should return the expected result.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
expression.parse("(2.)") == OK,
|
||||
"The expression should parse successfully.");
|
||||
CHECK_MESSAGE(
|
||||
double(expression.execute()) == doctest::Approx(2.0),
|
||||
"The expression should return the expected result.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
expression.parse(".3") == OK,
|
||||
"The expression should parse successfully.");
|
||||
CHECK_MESSAGE(
|
||||
double(expression.execute()) == doctest::Approx(0.3),
|
||||
"The expression should return the expected result.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
expression.parse("2.+5.") == OK,
|
||||
"The expression should parse successfully.");
|
||||
CHECK_MESSAGE(
|
||||
double(expression.execute()) == doctest::Approx(7.0),
|
||||
"The expression should return the expected result.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
expression.parse(".3-.8") == OK,
|
||||
"The expression should parse successfully.");
|
||||
CHECK_MESSAGE(
|
||||
double(expression.execute()) == doctest::Approx(-0.5),
|
||||
"The expression should return the expected result.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
expression.parse("2.+.2") == OK,
|
||||
"The expression should parse successfully.");
|
||||
CHECK_MESSAGE(
|
||||
double(expression.execute()) == doctest::Approx(2.2),
|
||||
"The expression should return the expected result.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
expression.parse(".0*0.") == OK,
|
||||
"The expression should parse successfully.");
|
||||
CHECK_MESSAGE(
|
||||
double(expression.execute()) == doctest::Approx(0.0),
|
||||
"The expression should return the expected result.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Expression] Scientific notation") {
|
||||
Expression expression;
|
||||
|
||||
CHECK_MESSAGE(
|
||||
expression.parse("2.e5") == OK,
|
||||
"The expression should parse successfully.");
|
||||
CHECK_MESSAGE(
|
||||
expression.parse("2.E5") == OK,
|
||||
"The expression should parse successfully.");
|
||||
CHECK_MESSAGE(
|
||||
double(expression.execute()) == doctest::Approx(200'000),
|
||||
"The expression should return the expected result.");
|
||||
|
|
@ -160,6 +216,15 @@ TEST_CASE("[Expression] Underscored numeric literals") {
|
|||
CHECK_MESSAGE(
|
||||
expression.parse("0xff_99_00") == OK,
|
||||
"The expression should parse successfully.");
|
||||
CHECK_MESSAGE(
|
||||
expression.parse("0Xff_99_00") == OK,
|
||||
"The expression should parse successfully.");
|
||||
CHECK_MESSAGE(
|
||||
expression.parse("0b10_11_00") == OK,
|
||||
"The expression should parse successfully.");
|
||||
CHECK_MESSAGE(
|
||||
expression.parse("0B10_11_00") == OK,
|
||||
"The expression should parse successfully.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Expression] Built-in functions") {
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ TEST_CASE("[Plane] Plane-point operations") {
|
|||
|
||||
CHECK_MESSAGE(
|
||||
y_facing_plane.get_any_perpendicular_normal().is_equal_approx(Vector3(1, 0, 0)),
|
||||
"get_any_perpindicular_normal() should return the expected result.");
|
||||
"get_any_perpendicular_normal() should return the expected result.");
|
||||
|
||||
// TODO distance_to()
|
||||
}
|
||||
|
|
|
|||
516
engine/tests/core/math/test_projection.h
Normal file
516
engine/tests/core/math/test_projection.h
Normal file
|
|
@ -0,0 +1,516 @@
|
|||
/**************************************************************************/
|
||||
/* test_projection.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_PROJECTION_H
|
||||
#define TEST_PROJECTION_H
|
||||
|
||||
#include "core/math/aabb.h"
|
||||
#include "core/math/plane.h"
|
||||
#include "core/math/projection.h"
|
||||
#include "core/math/rect2.h"
|
||||
#include "core/math/transform_3d.h"
|
||||
|
||||
#include "thirdparty/doctest/doctest.h"
|
||||
|
||||
namespace TestProjection {
|
||||
|
||||
TEST_CASE("[Projection] Construction") {
|
||||
Projection default_proj;
|
||||
|
||||
CHECK(default_proj[0].is_equal_approx(Vector4(1, 0, 0, 0)));
|
||||
CHECK(default_proj[1].is_equal_approx(Vector4(0, 1, 0, 0)));
|
||||
CHECK(default_proj[2].is_equal_approx(Vector4(0, 0, 1, 0)));
|
||||
CHECK(default_proj[3].is_equal_approx(Vector4(0, 0, 0, 1)));
|
||||
|
||||
Projection from_vec4(
|
||||
Vector4(1, 2, 3, 4),
|
||||
Vector4(5, 6, 7, 8),
|
||||
Vector4(9, 10, 11, 12),
|
||||
Vector4(13, 14, 15, 16));
|
||||
|
||||
CHECK(from_vec4[0].is_equal_approx(Vector4(1, 2, 3, 4)));
|
||||
CHECK(from_vec4[1].is_equal_approx(Vector4(5, 6, 7, 8)));
|
||||
CHECK(from_vec4[2].is_equal_approx(Vector4(9, 10, 11, 12)));
|
||||
CHECK(from_vec4[3].is_equal_approx(Vector4(13, 14, 15, 16)));
|
||||
|
||||
Transform3D transform(
|
||||
Basis(
|
||||
Vector3(1, 0, 0),
|
||||
Vector3(0, 2, 0),
|
||||
Vector3(0, 0, 3)),
|
||||
Vector3(4, 5, 6));
|
||||
|
||||
Projection from_transform(transform);
|
||||
|
||||
CHECK(from_transform[0].is_equal_approx(Vector4(1, 0, 0, 0)));
|
||||
CHECK(from_transform[1].is_equal_approx(Vector4(0, 2, 0, 0)));
|
||||
CHECK(from_transform[2].is_equal_approx(Vector4(0, 0, 3, 0)));
|
||||
CHECK(from_transform[3].is_equal_approx(Vector4(4, 5, 6, 1)));
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] set_zero()") {
|
||||
Projection proj;
|
||||
proj.set_zero();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
CHECK(proj.columns[i][j] == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] set_identity()") {
|
||||
Projection proj;
|
||||
proj.set_identity();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
CHECK(proj.columns[i][j] == (i == j ? 1 : 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] determinant()") {
|
||||
Projection proj(
|
||||
Vector4(1, 5, 9, 13),
|
||||
Vector4(2, 6, 11, 15),
|
||||
Vector4(4, 7, 11, 15),
|
||||
Vector4(4, 8, 12, 16));
|
||||
|
||||
CHECK(proj.determinant() == -12);
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Inverse and invert") {
|
||||
SUBCASE("[Projection] Arbitrary projection matrix inversion") {
|
||||
Projection proj(
|
||||
Vector4(1, 5, 9, 13),
|
||||
Vector4(2, 6, 11, 15),
|
||||
Vector4(4, 7, 11, 15),
|
||||
Vector4(4, 8, 12, 16));
|
||||
|
||||
Projection inverse_truth(
|
||||
Vector4(-4.0 / 12, 0, 1, -8.0 / 12),
|
||||
Vector4(8.0 / 12, -1, -1, 16.0 / 12),
|
||||
Vector4(-20.0 / 12, 2, -1, 5.0 / 12),
|
||||
Vector4(1, -1, 1, -0.75));
|
||||
|
||||
Projection inverse = proj.inverse();
|
||||
CHECK(inverse[0].is_equal_approx(inverse_truth[0]));
|
||||
CHECK(inverse[1].is_equal_approx(inverse_truth[1]));
|
||||
CHECK(inverse[2].is_equal_approx(inverse_truth[2]));
|
||||
CHECK(inverse[3].is_equal_approx(inverse_truth[3]));
|
||||
|
||||
proj.invert();
|
||||
CHECK(proj[0].is_equal_approx(inverse_truth[0]));
|
||||
CHECK(proj[1].is_equal_approx(inverse_truth[1]));
|
||||
CHECK(proj[2].is_equal_approx(inverse_truth[2]));
|
||||
CHECK(proj[3].is_equal_approx(inverse_truth[3]));
|
||||
}
|
||||
|
||||
SUBCASE("[Projection] Orthogonal projection matrix inversion") {
|
||||
Projection p = Projection::create_orthogonal(-125.0f, 125.0f, -125.0f, 125.0f, 0.01f, 25.0f);
|
||||
p = p.inverse() * p;
|
||||
|
||||
CHECK(p[0].is_equal_approx(Vector4(1, 0, 0, 0)));
|
||||
CHECK(p[1].is_equal_approx(Vector4(0, 1, 0, 0)));
|
||||
CHECK(p[2].is_equal_approx(Vector4(0, 0, 1, 0)));
|
||||
CHECK(p[3].is_equal_approx(Vector4(0, 0, 0, 1)));
|
||||
}
|
||||
|
||||
SUBCASE("[Projection] Perspective projection matrix inversion") {
|
||||
Projection p = Projection::create_perspective(90.0f, 1.77777f, 0.05f, 4000.0f);
|
||||
p = p.inverse() * p;
|
||||
|
||||
CHECK(p[0].is_equal_approx(Vector4(1, 0, 0, 0)));
|
||||
CHECK(p[1].is_equal_approx(Vector4(0, 1, 0, 0)));
|
||||
CHECK(p[2].is_equal_approx(Vector4(0, 0, 1, 0)));
|
||||
CHECK(p[3].is_equal_approx(Vector4(0, 0, 0, 1)));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Matrix product") {
|
||||
Projection proj1(
|
||||
Vector4(1, 5, 9, 13),
|
||||
Vector4(2, 6, 11, 15),
|
||||
Vector4(4, 7, 11, 15),
|
||||
Vector4(4, 8, 12, 16));
|
||||
|
||||
Projection proj2(
|
||||
Vector4(0, 1, 2, 3),
|
||||
Vector4(10, 11, 12, 13),
|
||||
Vector4(20, 21, 22, 23),
|
||||
Vector4(30, 31, 32, 33));
|
||||
|
||||
Projection prod = proj1 * proj2;
|
||||
|
||||
CHECK(prod[0].is_equal_approx(Vector4(22, 44, 69, 93)));
|
||||
CHECK(prod[1].is_equal_approx(Vector4(132, 304, 499, 683)));
|
||||
CHECK(prod[2].is_equal_approx(Vector4(242, 564, 929, 1273)));
|
||||
CHECK(prod[3].is_equal_approx(Vector4(352, 824, 1359, 1863)));
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Vector transformation") {
|
||||
Projection proj(
|
||||
Vector4(1, 5, 9, 13),
|
||||
Vector4(2, 6, 11, 15),
|
||||
Vector4(4, 7, 11, 15),
|
||||
Vector4(4, 8, 12, 16));
|
||||
|
||||
Projection inverse(
|
||||
Vector4(-4.0 / 12, 0, 1, -8.0 / 12),
|
||||
Vector4(8.0 / 12, -1, -1, 16.0 / 12),
|
||||
Vector4(-20.0 / 12, 2, -1, 5.0 / 12),
|
||||
Vector4(1, -1, 1, -0.75));
|
||||
|
||||
Vector4 vec4(1, 2, 3, 4);
|
||||
CHECK(proj.xform(vec4).is_equal_approx(Vector4(33, 70, 112, 152)));
|
||||
CHECK(proj.xform_inv(vec4).is_equal_approx(Vector4(90, 107, 111, 120)));
|
||||
|
||||
Vector3 vec3(1, 2, 3);
|
||||
CHECK(proj.xform(vec3).is_equal_approx(Vector3(21, 46, 76) / 104));
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Plane transformation") {
|
||||
Projection proj(
|
||||
Vector4(1, 5, 9, 13),
|
||||
Vector4(2, 6, 11, 15),
|
||||
Vector4(4, 7, 11, 15),
|
||||
Vector4(4, 8, 12, 16));
|
||||
|
||||
Plane plane(1, 2, 3, 4);
|
||||
CHECK(proj.xform4(plane).is_equal_approx(Plane(33, 70, 112, 152)));
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Values access") {
|
||||
Projection proj(
|
||||
Vector4(00, 01, 02, 03),
|
||||
Vector4(10, 11, 12, 13),
|
||||
Vector4(20, 21, 22, 23),
|
||||
Vector4(30, 31, 32, 33));
|
||||
|
||||
CHECK(proj[0] == Vector4(00, 01, 02, 03));
|
||||
CHECK(proj[1] == Vector4(10, 11, 12, 13));
|
||||
CHECK(proj[2] == Vector4(20, 21, 22, 23));
|
||||
CHECK(proj[3] == Vector4(30, 31, 32, 33));
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] flip_y() and flipped_y()") {
|
||||
Projection proj(
|
||||
Vector4(00, 01, 02, 03),
|
||||
Vector4(10, 11, 12, 13),
|
||||
Vector4(20, 21, 22, 23),
|
||||
Vector4(30, 31, 32, 33));
|
||||
|
||||
Projection flipped = proj.flipped_y();
|
||||
|
||||
CHECK(flipped[0] == proj[0]);
|
||||
CHECK(flipped[1] == -proj[1]);
|
||||
CHECK(flipped[2] == proj[2]);
|
||||
CHECK(flipped[3] == proj[3]);
|
||||
|
||||
proj.flip_y();
|
||||
|
||||
CHECK(proj[0] == flipped[0]);
|
||||
CHECK(proj[1] == flipped[1]);
|
||||
CHECK(proj[2] == flipped[2]);
|
||||
CHECK(proj[3] == flipped[3]);
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Jitter offset") {
|
||||
Projection proj(
|
||||
Vector4(00, 01, 02, 03),
|
||||
Vector4(10, 11, 12, 13),
|
||||
Vector4(20, 21, 22, 23),
|
||||
Vector4(30, 31, 32, 33));
|
||||
|
||||
Projection offsetted = proj.jitter_offseted(Vector2(1, 2));
|
||||
|
||||
CHECK(offsetted[0] == proj[0]);
|
||||
CHECK(offsetted[1] == proj[1]);
|
||||
CHECK(offsetted[2] == proj[2]);
|
||||
CHECK(offsetted[3] == proj[3] + Vector4(1, 2, 0, 0));
|
||||
|
||||
proj.add_jitter_offset(Vector2(1, 2));
|
||||
|
||||
CHECK(proj[0] == offsetted[0]);
|
||||
CHECK(proj[1] == offsetted[1]);
|
||||
CHECK(proj[2] == offsetted[2]);
|
||||
CHECK(proj[3] == offsetted[3]);
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Adjust znear") {
|
||||
Projection persp = Projection::create_perspective(90, 0.5, 1, 50, false);
|
||||
Projection adjusted = persp.perspective_znear_adjusted(2);
|
||||
|
||||
CHECK(adjusted[0] == persp[0]);
|
||||
CHECK(adjusted[1] == persp[1]);
|
||||
CHECK(adjusted[2].is_equal_approx(Vector4(persp[2][0], persp[2][1], -1.083333, persp[2][3])));
|
||||
CHECK(adjusted[3].is_equal_approx(Vector4(persp[3][0], persp[3][1], -4.166666, persp[3][3])));
|
||||
|
||||
persp.adjust_perspective_znear(2);
|
||||
|
||||
CHECK(persp[0] == adjusted[0]);
|
||||
CHECK(persp[1] == adjusted[1]);
|
||||
CHECK(persp[2] == adjusted[2]);
|
||||
CHECK(persp[3] == adjusted[3]);
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Set light bias") {
|
||||
Projection proj;
|
||||
proj.set_light_bias();
|
||||
|
||||
CHECK(proj[0] == Vector4(0.5, 0, 0, 0));
|
||||
CHECK(proj[1] == Vector4(0, 0.5, 0, 0));
|
||||
CHECK(proj[2] == Vector4(0, 0, 0.5, 0));
|
||||
CHECK(proj[3] == Vector4(0.5, 0.5, 0.5, 1));
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Depth correction") {
|
||||
Projection corrected = Projection::create_depth_correction(true);
|
||||
|
||||
CHECK(corrected[0] == Vector4(1, 0, 0, 0));
|
||||
CHECK(corrected[1] == Vector4(0, -1, 0, 0));
|
||||
CHECK(corrected[2] == Vector4(0, 0, -0.5, 0));
|
||||
CHECK(corrected[3] == Vector4(0, 0, 0.5, 1));
|
||||
|
||||
Projection proj;
|
||||
proj.set_depth_correction(true, true, true);
|
||||
|
||||
CHECK(proj[0] == corrected[0]);
|
||||
CHECK(proj[1] == corrected[1]);
|
||||
CHECK(proj[2] == corrected[2]);
|
||||
CHECK(proj[3] == corrected[3]);
|
||||
|
||||
proj.set_depth_correction(false, true, true);
|
||||
|
||||
CHECK(proj[0] == Vector4(1, 0, 0, 0));
|
||||
CHECK(proj[1] == Vector4(0, 1, 0, 0));
|
||||
CHECK(proj[2] == Vector4(0, 0, -0.5, 0));
|
||||
CHECK(proj[3] == Vector4(0, 0, 0.5, 1));
|
||||
|
||||
proj.set_depth_correction(false, false, true);
|
||||
|
||||
CHECK(proj[0] == Vector4(1, 0, 0, 0));
|
||||
CHECK(proj[1] == Vector4(0, 1, 0, 0));
|
||||
CHECK(proj[2] == Vector4(0, 0, 0.5, 0));
|
||||
CHECK(proj[3] == Vector4(0, 0, 0.5, 1));
|
||||
|
||||
proj.set_depth_correction(false, false, false);
|
||||
|
||||
CHECK(proj[0] == Vector4(1, 0, 0, 0));
|
||||
CHECK(proj[1] == Vector4(0, 1, 0, 0));
|
||||
CHECK(proj[2] == Vector4(0, 0, 1, 0));
|
||||
CHECK(proj[3] == Vector4(0, 0, 0, 1));
|
||||
|
||||
proj.set_depth_correction(true, true, false);
|
||||
|
||||
CHECK(proj[0] == Vector4(1, 0, 0, 0));
|
||||
CHECK(proj[1] == Vector4(0, -1, 0, 0));
|
||||
CHECK(proj[2] == Vector4(0, 0, -1, 0));
|
||||
CHECK(proj[3] == Vector4(0, 0, 0, 1));
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Light atlas rect") {
|
||||
Projection rect = Projection::create_light_atlas_rect(Rect2(1, 2, 30, 40));
|
||||
|
||||
CHECK(rect[0] == Vector4(30, 0, 0, 0));
|
||||
CHECK(rect[1] == Vector4(0, 40, 0, 0));
|
||||
CHECK(rect[2] == Vector4(0, 0, 1, 0));
|
||||
CHECK(rect[3] == Vector4(1, 2, 0, 1));
|
||||
|
||||
Projection proj;
|
||||
proj.set_light_atlas_rect(Rect2(1, 2, 30, 40));
|
||||
|
||||
CHECK(proj[0] == rect[0]);
|
||||
CHECK(proj[1] == rect[1]);
|
||||
CHECK(proj[2] == rect[2]);
|
||||
CHECK(proj[3] == rect[3]);
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Make scale") {
|
||||
Projection proj;
|
||||
proj.make_scale(Vector3(2, 3, 4));
|
||||
|
||||
CHECK(proj[0] == Vector4(2, 0, 0, 0));
|
||||
CHECK(proj[1] == Vector4(0, 3, 0, 0));
|
||||
CHECK(proj[2] == Vector4(0, 0, 4, 0));
|
||||
CHECK(proj[3] == Vector4(0, 0, 0, 1));
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Scale translate to fit aabb") {
|
||||
Projection fit = Projection::create_fit_aabb(AABB(Vector3(), Vector3(0.1, 0.2, 0.4)));
|
||||
|
||||
CHECK(fit[0] == Vector4(20, 0, 0, 0));
|
||||
CHECK(fit[1] == Vector4(0, 10, 0, 0));
|
||||
CHECK(fit[2] == Vector4(0, 0, 5, 0));
|
||||
CHECK(fit[3] == Vector4(-1, -1, -1, 1));
|
||||
|
||||
Projection proj;
|
||||
proj.scale_translate_to_fit(AABB(Vector3(), Vector3(0.1, 0.2, 0.4)));
|
||||
|
||||
CHECK(proj[0] == fit[0]);
|
||||
CHECK(proj[1] == fit[1]);
|
||||
CHECK(proj[2] == fit[2]);
|
||||
CHECK(proj[3] == fit[3]);
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Perspective") {
|
||||
Projection persp = Projection::create_perspective(90, 0.5, 5, 15, false);
|
||||
|
||||
CHECK(persp[0].is_equal_approx(Vector4(2, 0, 0, 0)));
|
||||
CHECK(persp[1].is_equal_approx(Vector4(0, 1, 0, 0)));
|
||||
CHECK(persp[2].is_equal_approx(Vector4(0, 0, -2, -1)));
|
||||
CHECK(persp[3].is_equal_approx(Vector4(0, 0, -15, 0)));
|
||||
|
||||
Projection proj;
|
||||
proj.set_perspective(90, 0.5, 5, 15, false);
|
||||
|
||||
CHECK(proj[0] == persp[0]);
|
||||
CHECK(proj[1] == persp[1]);
|
||||
CHECK(proj[2] == persp[2]);
|
||||
CHECK(proj[3] == persp[3]);
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Frustum") {
|
||||
Projection frustum = Projection::create_frustum(15, 20, 10, 12, 5, 15);
|
||||
|
||||
CHECK(frustum[0].is_equal_approx(Vector4(2, 0, 0, 0)));
|
||||
CHECK(frustum[1].is_equal_approx(Vector4(0, 5, 0, 0)));
|
||||
CHECK(frustum[2].is_equal_approx(Vector4(7, 11, -2, -1)));
|
||||
CHECK(frustum[3].is_equal_approx(Vector4(0, 0, -15, 0)));
|
||||
|
||||
Projection proj;
|
||||
proj.set_frustum(15, 20, 10, 12, 5, 15);
|
||||
|
||||
CHECK(proj[0] == frustum[0]);
|
||||
CHECK(proj[1] == frustum[1]);
|
||||
CHECK(proj[2] == frustum[2]);
|
||||
CHECK(proj[3] == frustum[3]);
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Ortho") {
|
||||
Projection ortho = Projection::create_orthogonal(15, 20, 10, 12, 5, 15);
|
||||
|
||||
CHECK(ortho[0].is_equal_approx(Vector4(0.4, 0, 0, 0)));
|
||||
CHECK(ortho[1].is_equal_approx(Vector4(0, 1, 0, 0)));
|
||||
CHECK(ortho[2].is_equal_approx(Vector4(0, 0, -0.2, 0)));
|
||||
CHECK(ortho[3].is_equal_approx(Vector4(-7, -11, -2, 1)));
|
||||
|
||||
Projection proj;
|
||||
proj.set_orthogonal(15, 20, 10, 12, 5, 15);
|
||||
|
||||
CHECK(proj[0] == ortho[0]);
|
||||
CHECK(proj[1] == ortho[1]);
|
||||
CHECK(proj[2] == ortho[2]);
|
||||
CHECK(proj[3] == ortho[3]);
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] get_fovy()") {
|
||||
double fov = Projection::get_fovy(90, 0.5);
|
||||
CHECK(fov == doctest::Approx(53.1301));
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Perspective values extraction") {
|
||||
Projection persp = Projection::create_perspective(90, 0.5, 1, 50, true);
|
||||
|
||||
double znear = persp.get_z_near();
|
||||
double zfar = persp.get_z_far();
|
||||
double aspect = persp.get_aspect();
|
||||
double fov = persp.get_fov();
|
||||
|
||||
CHECK(znear == doctest::Approx(1));
|
||||
CHECK(zfar == doctest::Approx(50));
|
||||
CHECK(aspect == doctest::Approx(0.5));
|
||||
CHECK(fov == doctest::Approx(90));
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Orthographic check") {
|
||||
Projection persp = Projection::create_perspective(90, 0.5, 1, 50, false);
|
||||
Projection ortho = Projection::create_orthogonal(15, 20, 10, 12, 5, 15);
|
||||
|
||||
CHECK(!persp.is_orthogonal());
|
||||
CHECK(ortho.is_orthogonal());
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Planes extraction") {
|
||||
Projection persp = Projection::create_perspective(90, 1, 1, 40, false);
|
||||
Vector<Plane> planes = persp.get_projection_planes(Transform3D());
|
||||
|
||||
CHECK(planes[Projection::PLANE_NEAR].normalized().is_equal_approx(Plane(0, 0, 1, -1)));
|
||||
CHECK(planes[Projection::PLANE_FAR].normalized().is_equal_approx(Plane(0, 0, -1, 40)));
|
||||
CHECK(planes[Projection::PLANE_LEFT].normalized().is_equal_approx(Plane(-0.707107, 0, 0.707107, 0)));
|
||||
CHECK(planes[Projection::PLANE_TOP].normalized().is_equal_approx(Plane(0, 0.707107, 0.707107, 0)));
|
||||
CHECK(planes[Projection::PLANE_RIGHT].normalized().is_equal_approx(Plane(0.707107, 0, 0.707107, 0)));
|
||||
CHECK(planes[Projection::PLANE_BOTTOM].normalized().is_equal_approx(Plane(0, -0.707107, 0.707107, 0)));
|
||||
|
||||
Plane plane_array[6]{
|
||||
persp.get_projection_plane(Projection::PLANE_NEAR),
|
||||
persp.get_projection_plane(Projection::PLANE_FAR),
|
||||
persp.get_projection_plane(Projection::PLANE_LEFT),
|
||||
persp.get_projection_plane(Projection::PLANE_TOP),
|
||||
persp.get_projection_plane(Projection::PLANE_RIGHT),
|
||||
persp.get_projection_plane(Projection::PLANE_BOTTOM)
|
||||
};
|
||||
|
||||
CHECK(plane_array[Projection::PLANE_NEAR].normalized().is_equal_approx(planes[Projection::PLANE_NEAR].normalized()));
|
||||
CHECK(plane_array[Projection::PLANE_FAR].normalized().is_equal_approx(planes[Projection::PLANE_FAR].normalized()));
|
||||
CHECK(plane_array[Projection::PLANE_LEFT].normalized().is_equal_approx(planes[Projection::PLANE_LEFT].normalized()));
|
||||
CHECK(plane_array[Projection::PLANE_TOP].normalized().is_equal_approx(planes[Projection::PLANE_TOP].normalized()));
|
||||
CHECK(plane_array[Projection::PLANE_RIGHT].normalized().is_equal_approx(planes[Projection::PLANE_RIGHT].normalized()));
|
||||
CHECK(plane_array[Projection::PLANE_BOTTOM].normalized().is_equal_approx(planes[Projection::PLANE_BOTTOM].normalized()));
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Half extents") {
|
||||
Projection persp = Projection::create_perspective(90, 1, 1, 40, false);
|
||||
Vector2 ne = persp.get_viewport_half_extents();
|
||||
Vector2 fe = persp.get_far_plane_half_extents();
|
||||
|
||||
CHECK(ne.is_equal_approx(Vector2(1, 1) * 1));
|
||||
CHECK(fe.is_equal_approx(Vector2(1, 1) * 40));
|
||||
}
|
||||
|
||||
TEST_CASE("[Projection] Endpoints") {
|
||||
Projection persp = Projection::create_perspective(90, 1, 1, 40, false);
|
||||
Vector3 ep[8];
|
||||
persp.get_endpoints(Transform3D(), ep);
|
||||
|
||||
CHECK(ep[0].is_equal_approx(Vector3(-1, 1, -1) * 40));
|
||||
CHECK(ep[1].is_equal_approx(Vector3(-1, -1, -1) * 40));
|
||||
CHECK(ep[2].is_equal_approx(Vector3(1, 1, -1) * 40));
|
||||
CHECK(ep[3].is_equal_approx(Vector3(1, -1, -1) * 40));
|
||||
CHECK(ep[4].is_equal_approx(Vector3(-1, 1, -1) * 1));
|
||||
CHECK(ep[5].is_equal_approx(Vector3(-1, -1, -1) * 1));
|
||||
CHECK(ep[6].is_equal_approx(Vector3(1, 1, -1) * 1));
|
||||
CHECK(ep[7].is_equal_approx(Vector3(1, -1, -1) * 1));
|
||||
}
|
||||
|
||||
} //namespace TestProjection
|
||||
|
||||
#endif // TEST_PROJECTION_H
|
||||
|
|
@ -235,6 +235,39 @@ TEST_CASE("[Quaternion] Construct Basis Axes") {
|
|||
CHECK(q[3] == doctest::Approx(0.8582598));
|
||||
}
|
||||
|
||||
TEST_CASE("[Quaternion] Construct Shortest Arc For 180 Degree Arc") {
|
||||
Vector3 up(0, 1, 0);
|
||||
Vector3 down(0, -1, 0);
|
||||
Vector3 left(-1, 0, 0);
|
||||
Vector3 right(1, 0, 0);
|
||||
Vector3 forward(0, 0, -1);
|
||||
Vector3 back(0, 0, 1);
|
||||
|
||||
// When we have a 180 degree rotation quaternion which was defined as
|
||||
// A to B, logically when we transform A we expect to get B.
|
||||
Quaternion left_to_right(left, right);
|
||||
Quaternion right_to_left(right, left);
|
||||
CHECK(left_to_right.xform(left).is_equal_approx(right));
|
||||
CHECK(Quaternion(right, left).xform(right).is_equal_approx(left));
|
||||
CHECK(Quaternion(up, down).xform(up).is_equal_approx(down));
|
||||
CHECK(Quaternion(down, up).xform(down).is_equal_approx(up));
|
||||
CHECK(Quaternion(forward, back).xform(forward).is_equal_approx(back));
|
||||
CHECK(Quaternion(back, forward).xform(back).is_equal_approx(forward));
|
||||
|
||||
// With (arbitrary) opposite vectors that are not axis-aligned as parameters.
|
||||
Vector3 diagonal_up = Vector3(1.2, 2.3, 4.5).normalized();
|
||||
Vector3 diagonal_down = -diagonal_up;
|
||||
Quaternion q1(diagonal_up, diagonal_down);
|
||||
CHECK(q1.xform(diagonal_down).is_equal_approx(diagonal_up));
|
||||
CHECK(q1.xform(diagonal_up).is_equal_approx(diagonal_down));
|
||||
|
||||
// For the consistency of the rotation direction, they should be symmetrical to the plane.
|
||||
CHECK(left_to_right.is_equal_approx(right_to_left.inverse()));
|
||||
|
||||
// If vectors are same, no rotation.
|
||||
CHECK(Quaternion(diagonal_up, diagonal_up).is_equal_approx(Quaternion()));
|
||||
}
|
||||
|
||||
TEST_CASE("[Quaternion] Get Euler Orders") {
|
||||
double x = Math::deg_to_rad(30.0);
|
||||
double y = Math::deg_to_rad(45.0);
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ TEST_CASE("[Rect2] Constructor methods") {
|
|||
TEST_CASE("[Rect2] String conversion") {
|
||||
// Note: This also depends on the Vector2 string representation.
|
||||
CHECK_MESSAGE(
|
||||
String(Rect2(0, 100, 1280, 720)) == "[P: (0, 100), S: (1280, 720)]",
|
||||
String(Rect2(0, 100, 1280, 720)) == "[P: (0.0, 100.0), S: (1280.0, 720.0)]",
|
||||
"The string representation should match the expected value.");
|
||||
}
|
||||
|
||||
|
|
@ -180,6 +180,28 @@ TEST_CASE("[Rect2] Expanding") {
|
|||
"expand() with non-contained Vector2 should return the expected result.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Rect2] Get support") {
|
||||
const Rect2 rect = Rect2(Vector2(-1.5, 2), Vector2(4, 5));
|
||||
CHECK_MESSAGE(
|
||||
rect.get_support(Vector2(1, 0)) == Vector2(2.5, 2),
|
||||
"get_support() should return the expected value.");
|
||||
CHECK_MESSAGE(
|
||||
rect.get_support(Vector2(0.5, 1)) == Vector2(2.5, 7),
|
||||
"get_support() should return the expected value.");
|
||||
CHECK_MESSAGE(
|
||||
rect.get_support(Vector2(0.5, 1)) == Vector2(2.5, 7),
|
||||
"get_support() should return the expected value.");
|
||||
CHECK_MESSAGE(
|
||||
rect.get_support(Vector2(0, -1)) == Vector2(-1.5, 2),
|
||||
"get_support() should return the expected value.");
|
||||
CHECK_MESSAGE(
|
||||
rect.get_support(Vector2(0, -0.1)) == Vector2(-1.5, 2),
|
||||
"get_support() should return the expected value.");
|
||||
CHECK_MESSAGE(
|
||||
rect.get_support(Vector2()) == Vector2(-1.5, 2),
|
||||
"get_support() should return the Rect2 position when given a zero vector.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Rect2] Growing") {
|
||||
CHECK_MESSAGE(
|
||||
Rect2(0, 100, 1280, 720).grow(100).is_equal_approx(Rect2(-100, 0, 1480, 920)),
|
||||
|
|
|
|||
|
|
@ -289,6 +289,38 @@ bool arg_default_value_is_assignable_to_type(const Context &p_context, const Var
|
|||
return false;
|
||||
}
|
||||
|
||||
bool arg_default_value_is_valid_data(const Variant &p_val, String *r_err_msg = nullptr) {
|
||||
switch (p_val.get_type()) {
|
||||
case Variant::RID:
|
||||
case Variant::ARRAY:
|
||||
case Variant::DICTIONARY:
|
||||
case Variant::PACKED_BYTE_ARRAY:
|
||||
case Variant::PACKED_INT32_ARRAY:
|
||||
case Variant::PACKED_INT64_ARRAY:
|
||||
case Variant::PACKED_FLOAT32_ARRAY:
|
||||
case Variant::PACKED_FLOAT64_ARRAY:
|
||||
case Variant::PACKED_STRING_ARRAY:
|
||||
case Variant::PACKED_VECTOR2_ARRAY:
|
||||
case Variant::PACKED_VECTOR3_ARRAY:
|
||||
case Variant::PACKED_COLOR_ARRAY:
|
||||
case Variant::PACKED_VECTOR4_ARRAY:
|
||||
case Variant::CALLABLE:
|
||||
case Variant::SIGNAL:
|
||||
case Variant::OBJECT:
|
||||
if (p_val.is_zero()) {
|
||||
return true;
|
||||
}
|
||||
if (r_err_msg) {
|
||||
*r_err_msg = "Must be zero.";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void validate_property(const Context &p_context, const ExposedClass &p_class, const PropertyData &p_prop) {
|
||||
const MethodData *setter = p_class.find_method_by_name(p_prop.setter);
|
||||
|
||||
|
|
@ -411,6 +443,14 @@ void validate_argument(const Context &p_context, const ExposedClass &p_class, co
|
|||
}
|
||||
|
||||
TEST_COND(!arg_defval_assignable_to_type, err_msg);
|
||||
|
||||
bool arg_defval_valid_data = arg_default_value_is_valid_data(p_arg.defval, &type_error_msg);
|
||||
|
||||
if (!type_error_msg.is_empty()) {
|
||||
err_msg += " " + type_error_msg;
|
||||
}
|
||||
|
||||
TEST_COND(!arg_defval_valid_data, err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -563,7 +603,7 @@ void add_exposed_classes(Context &r_context) {
|
|||
|
||||
MethodData method;
|
||||
method.name = method_info.name;
|
||||
TEST_FAIL_COND(!String(method.name).is_valid_identifier(),
|
||||
TEST_FAIL_COND(!String(method.name).is_valid_ascii_identifier(),
|
||||
"Method name is not a valid identifier: '", exposed_class.name, ".", method.name, "'.");
|
||||
|
||||
if (method_info.flags & METHOD_FLAG_VIRTUAL) {
|
||||
|
|
@ -689,7 +729,7 @@ void add_exposed_classes(Context &r_context) {
|
|||
const MethodInfo &method_info = signal_map.get(K.key);
|
||||
|
||||
signal.name = method_info.name;
|
||||
TEST_FAIL_COND(!String(signal.name).is_valid_identifier(),
|
||||
TEST_FAIL_COND(!String(signal.name).is_valid_ascii_identifier(),
|
||||
"Signal name is not a valid identifier: '", exposed_class.name, ".", signal.name, "'.");
|
||||
|
||||
int i = 0;
|
||||
|
|
@ -823,16 +863,19 @@ void add_global_enums(Context &r_context) {
|
|||
}
|
||||
}
|
||||
|
||||
// HARDCODED
|
||||
List<StringName> hardcoded_enums;
|
||||
hardcoded_enums.push_back("Vector2.Axis");
|
||||
hardcoded_enums.push_back("Vector2i.Axis");
|
||||
hardcoded_enums.push_back("Vector3.Axis");
|
||||
hardcoded_enums.push_back("Vector3i.Axis");
|
||||
for (const StringName &E : hardcoded_enums) {
|
||||
// These enums are not generated and must be written manually (e.g.: Vector3.Axis)
|
||||
// Here, we assume core types do not begin with underscore
|
||||
r_context.enum_types.push_back(E);
|
||||
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
|
||||
if (i == Variant::OBJECT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Variant::Type type = Variant::Type(i);
|
||||
|
||||
List<StringName> enum_names;
|
||||
Variant::get_enums_for_type(type, &enum_names);
|
||||
|
||||
for (const StringName &enum_name : enum_names) {
|
||||
r_context.enum_types.push_back(Variant::get_type_name(type) + "." + enum_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,16 @@
|
|||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
#ifdef SANITIZERS_ENABLED
|
||||
#ifdef __has_feature
|
||||
#if __has_feature(address_sanitizer) || __has_feature(thread_sanitizer)
|
||||
#define ASAN_OR_TSAN_ENABLED
|
||||
#endif
|
||||
#elif defined(__SANITIZE_ADDRESS__) || defined(__SANITIZE_THREAD__)
|
||||
#define ASAN_OR_TSAN_ENABLED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Declared in global namespace because of GDCLASS macro warning (Windows):
|
||||
// "Unqualified friend declaration referring to type outside of the nearest enclosing namespace
|
||||
// is a Microsoft extension; add a nested name specifier".
|
||||
|
|
@ -85,10 +95,10 @@ public:
|
|||
}
|
||||
bool property_can_revert(const StringName &p_name) const override {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
bool property_get_revert(const StringName &p_name, Variant &r_ret) const override {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
void get_method_list(List<MethodInfo> *p_list) const override {
|
||||
}
|
||||
bool has_method(const StringName &p_method) const override {
|
||||
|
|
@ -174,6 +184,31 @@ TEST_CASE("[Object] Metadata") {
|
|||
CHECK_MESSAGE(
|
||||
meta_list2.size() == 0,
|
||||
"The metadata list should contain 0 items after removing all metadata items.");
|
||||
|
||||
Object other;
|
||||
object.set_meta("conflicting_meta", "string");
|
||||
object.set_meta("not_conflicting_meta", 123);
|
||||
other.set_meta("conflicting_meta", Color(0, 1, 0));
|
||||
other.set_meta("other_meta", "other");
|
||||
object.merge_meta_from(&other);
|
||||
|
||||
CHECK_MESSAGE(
|
||||
Color(object.get_meta("conflicting_meta")).is_equal_approx(Color(0, 1, 0)),
|
||||
"String meta should be overwritten with Color after merging.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
int(object.get_meta("not_conflicting_meta")) == 123,
|
||||
"Not conflicting meta on destination should be kept intact.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
object.get_meta("other_meta", String()) == "other",
|
||||
"Not conflicting meta name on source should merged.");
|
||||
|
||||
List<StringName> meta_list3;
|
||||
object.get_meta_list(&meta_list3);
|
||||
CHECK_MESSAGE(
|
||||
meta_list3.size() == 3,
|
||||
"The metadata list should contain 3 items after merging meta from two objects.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Object] Construction") {
|
||||
|
|
@ -499,6 +534,74 @@ TEST_CASE("[Object] Notification order") { // GH-52325
|
|||
memdelete(test_notification_object);
|
||||
}
|
||||
|
||||
TEST_CASE("[Object] Destruction at the end of the call chain is safe") {
|
||||
Object *object = memnew(Object);
|
||||
ObjectID obj_id = object->get_instance_id();
|
||||
|
||||
class _SelfDestroyingScriptInstance : public _MockScriptInstance {
|
||||
Object *self = nullptr;
|
||||
|
||||
// This has to be static because ~Object() also destroys the script instance.
|
||||
static void free_self(Object *p_self) {
|
||||
#if defined(ASAN_OR_TSAN_ENABLED)
|
||||
// Regular deletion is enough becausa asan/tsan will catch a potential heap-after-use.
|
||||
memdelete(p_self);
|
||||
#else
|
||||
// Without asan/tsan, try at least to force a crash by replacing the otherwise seemingly good data with garbage.
|
||||
// Operations such as dereferencing pointers or decreasing a refcount would fail.
|
||||
// Unfortunately, we may not poison the memory after the deletion, because the memory would no longer belong to us
|
||||
// and on doing so we may cause a more generalized crash on some platforms (allocator implementations).
|
||||
p_self->~Object();
|
||||
memset((void *)p_self, 0, sizeof(Object));
|
||||
Memory::free_static(p_self, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
|
||||
free_self(self);
|
||||
return Variant();
|
||||
}
|
||||
Variant call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
|
||||
free_self(self);
|
||||
return Variant();
|
||||
}
|
||||
bool has_method(const StringName &p_method) const override {
|
||||
return p_method == "some_method";
|
||||
}
|
||||
|
||||
public:
|
||||
_SelfDestroyingScriptInstance(Object *p_self) :
|
||||
self(p_self) {}
|
||||
};
|
||||
|
||||
_SelfDestroyingScriptInstance *script_instance = memnew(_SelfDestroyingScriptInstance(object));
|
||||
object->set_script_instance(script_instance);
|
||||
|
||||
SUBCASE("Within callp()") {
|
||||
SUBCASE("Through call()") {
|
||||
object->call("some_method");
|
||||
}
|
||||
SUBCASE("Through callv()") {
|
||||
object->callv("some_method", Array());
|
||||
}
|
||||
}
|
||||
SUBCASE("Within call_const()") {
|
||||
Callable::CallError call_error;
|
||||
object->call_const("some_method", nullptr, 0, call_error);
|
||||
}
|
||||
SUBCASE("Within signal handling (from emit_signalp(), through emit_signal())") {
|
||||
Object emitter;
|
||||
emitter.add_user_signal(MethodInfo("some_signal"));
|
||||
emitter.connect("some_signal", Callable(object, "some_method"));
|
||||
emitter.emit_signal("some_signal");
|
||||
}
|
||||
|
||||
CHECK_MESSAGE(
|
||||
ObjectDB::get_instance(obj_id) == nullptr,
|
||||
"Object was tail-deleted without crashes.");
|
||||
}
|
||||
|
||||
} // namespace TestObject
|
||||
|
||||
#endif // TEST_OBJECT_H
|
||||
|
|
|
|||
83
engine/tests/core/string/test_fuzzy_search.h
Normal file
83
engine/tests/core/string/test_fuzzy_search.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/**************************************************************************/
|
||||
/* test_fuzzy_search.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_FUZZY_SEARCH_H
|
||||
#define TEST_FUZZY_SEARCH_H
|
||||
|
||||
#include "core/string/fuzzy_search.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestFuzzySearch {
|
||||
|
||||
struct FuzzySearchTestCase {
|
||||
String query;
|
||||
String expected;
|
||||
};
|
||||
|
||||
// Ideally each of these test queries should represent a different aspect, and potentially bottleneck, of the search process.
|
||||
const FuzzySearchTestCase test_cases[] = {
|
||||
// Short query, many matches, few adjacent characters
|
||||
{ "///gd", "./menu/hud/hud.gd" },
|
||||
// Filename match with typo
|
||||
{ "sm.png", "./entity/blood_sword/sam.png" },
|
||||
// Multipart filename word matches
|
||||
{ "ham ", "./entity/game_trap/ha_missed_me.wav" },
|
||||
// Single word token matches
|
||||
{ "push background", "./entity/background_zone1/background/push.png" },
|
||||
// Long token matches
|
||||
{ "background_freighter background png", "./entity/background_freighter/background/background.png" },
|
||||
// Many matches, many short tokens
|
||||
{ "menu menu characters wav", "./menu/menu/characters/smoker/0.wav" },
|
||||
// Maximize total matches
|
||||
{ "entity gd", "./entity/entity_man.gd" }
|
||||
};
|
||||
|
||||
Vector<String> load_test_data() {
|
||||
Ref<FileAccess> fp = FileAccess::open(TestUtils::get_data_path("fuzzy_search/project_dir_tree.txt"), FileAccess::READ);
|
||||
REQUIRE(fp.is_valid());
|
||||
return fp->get_as_utf8_string().split("\n");
|
||||
}
|
||||
|
||||
TEST_CASE("[FuzzySearch] Test fuzzy search results") {
|
||||
FuzzySearch search;
|
||||
Vector<FuzzySearchResult> results;
|
||||
Vector<String> targets = load_test_data();
|
||||
|
||||
for (FuzzySearchTestCase test_case : test_cases) {
|
||||
search.set_query(test_case.query);
|
||||
search.search_all(targets, results);
|
||||
CHECK_GT(results.size(), 0);
|
||||
CHECK_EQ(results[0].target, test_case.expected);
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace TestFuzzySearch
|
||||
|
||||
#endif // TEST_FUZZY_SEARCH_H
|
||||
|
|
@ -169,28 +169,31 @@ TEST_CASE("[NodePath] Empty path") {
|
|||
}
|
||||
|
||||
TEST_CASE("[NodePath] Slice") {
|
||||
const NodePath node_path_relative = NodePath("Parent/Child:prop");
|
||||
const NodePath node_path_relative = NodePath("Parent/Child:prop:subprop");
|
||||
const NodePath node_path_absolute = NodePath("/root/Parent/Child:prop");
|
||||
CHECK_MESSAGE(
|
||||
node_path_relative.slice(0, 2) == NodePath("Parent/Child"),
|
||||
"The slice lower bound should be inclusive and the slice upper bound should be exclusive.");
|
||||
CHECK_MESSAGE(
|
||||
node_path_relative.slice(3) == NodePath(":prop"),
|
||||
"Slicing on the length of the path should return the last entry.");
|
||||
node_path_relative.slice(3) == NodePath(":subprop"),
|
||||
"Slicing on the last index (length - 1) should return the last entry.");
|
||||
CHECK_MESSAGE(
|
||||
node_path_relative.slice(1) == NodePath("Child:prop:subprop"),
|
||||
"Slicing without upper bound should return remaining entries after index.");
|
||||
CHECK_MESSAGE(
|
||||
node_path_relative.slice(1, 3) == NodePath("Child:prop"),
|
||||
"Slicing should include names and subnames.");
|
||||
CHECK_MESSAGE(
|
||||
node_path_relative.slice(-1) == NodePath(":prop"),
|
||||
node_path_relative.slice(-1) == NodePath(":subprop"),
|
||||
"Slicing on -1 should return the last entry.");
|
||||
CHECK_MESSAGE(
|
||||
node_path_relative.slice(0, -1) == NodePath("Parent/Child"),
|
||||
node_path_relative.slice(0, -1) == NodePath("Parent/Child:prop"),
|
||||
"Slicing up to -1 should include the second-to-last entry.");
|
||||
CHECK_MESSAGE(
|
||||
node_path_relative.slice(-2, -1) == NodePath("Child"),
|
||||
node_path_relative.slice(-2, -1) == NodePath(":prop"),
|
||||
"Slicing from negative to negative should treat lower bound as inclusive and upper bound as exclusive.");
|
||||
CHECK_MESSAGE(
|
||||
node_path_relative.slice(0, 10) == NodePath("Parent/Child:prop"),
|
||||
node_path_relative.slice(0, 10) == NodePath("Parent/Child:prop:subprop"),
|
||||
"Slicing past the length of the path should work like slicing up to the last entry.");
|
||||
CHECK_MESSAGE(
|
||||
node_path_relative.slice(-10, 2) == NodePath("Parent/Child"),
|
||||
|
|
|
|||
|
|
@ -389,6 +389,19 @@ TEST_CASE("[String] Find") {
|
|||
MULTICHECK_STRING_INT_EQ(s, rfind, "", 15, -1);
|
||||
}
|
||||
|
||||
TEST_CASE("[String] Find character") {
|
||||
String s = "racecar";
|
||||
CHECK_EQ(s.find_char('r'), 0);
|
||||
CHECK_EQ(s.find_char('r', 1), 6);
|
||||
CHECK_EQ(s.find_char('e'), 3);
|
||||
CHECK_EQ(s.find_char('e', 4), -1);
|
||||
|
||||
CHECK_EQ(s.rfind_char('r'), 6);
|
||||
CHECK_EQ(s.rfind_char('r', 5), 0);
|
||||
CHECK_EQ(s.rfind_char('e'), 3);
|
||||
CHECK_EQ(s.rfind_char('e', 2), -1);
|
||||
}
|
||||
|
||||
TEST_CASE("[String] Find case insensitive") {
|
||||
String s = "Pretty Whale Whale";
|
||||
MULTICHECK_STRING_EQ(s, findn, "WHA", 7);
|
||||
|
|
@ -433,6 +446,19 @@ TEST_CASE("[String] Insertion") {
|
|||
String s = "Who is Frederic?";
|
||||
s = s.insert(s.find("?"), " Chopin");
|
||||
CHECK(s == "Who is Frederic Chopin?");
|
||||
|
||||
s = "foobar";
|
||||
CHECK(s.insert(0, "X") == "Xfoobar");
|
||||
CHECK(s.insert(-100, "X") == "foobar");
|
||||
CHECK(s.insert(6, "X") == "foobarX");
|
||||
CHECK(s.insert(100, "X") == "foobarX");
|
||||
CHECK(s.insert(2, "") == "foobar");
|
||||
|
||||
s = "";
|
||||
CHECK(s.insert(0, "abc") == "abc");
|
||||
CHECK(s.insert(100, "abc") == "abc");
|
||||
CHECK(s.insert(-100, "abc") == "");
|
||||
CHECK(s.insert(0, "") == "");
|
||||
}
|
||||
|
||||
TEST_CASE("[String] Erasing") {
|
||||
|
|
@ -442,16 +468,32 @@ TEST_CASE("[String] Erasing") {
|
|||
}
|
||||
|
||||
TEST_CASE("[String] Number to string") {
|
||||
CHECK(String::num(0) == "0");
|
||||
CHECK(String::num(0.0) == "0"); // No trailing zeros.
|
||||
CHECK(String::num(-0.0) == "-0"); // Includes sign even for zero.
|
||||
CHECK(String::num(0) == "0.0"); // The method takes double, so always add zeros.
|
||||
CHECK(String::num(0.0) == "0.0");
|
||||
CHECK(String::num(-0.0) == "-0.0"); // Includes sign even for zero.
|
||||
CHECK(String::num(3.141593) == "3.141593");
|
||||
CHECK(String::num(3.141593, 3) == "3.142");
|
||||
CHECK(String::num(42.100023, 4) == "42.1"); // No trailing zeros.
|
||||
CHECK(String::num_scientific(30000000) == "3e+07");
|
||||
|
||||
// String::num_int64 tests.
|
||||
CHECK(String::num_int64(3141593) == "3141593");
|
||||
CHECK(String::num_int64(-3141593) == "-3141593");
|
||||
CHECK(String::num_int64(0xA141593, 16) == "a141593");
|
||||
CHECK(String::num_int64(0xA141593, 16, true) == "A141593");
|
||||
CHECK(String::num(42.100023, 4) == "42.1"); // No trailing zeros.
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(String::num_int64(3141593, 1) == ""); // Invalid base < 2.
|
||||
CHECK(String::num_int64(3141593, 37) == ""); // Invalid base > 36.
|
||||
ERR_PRINT_ON;
|
||||
|
||||
// String::num_uint64 tests.
|
||||
CHECK(String::num_uint64(4294967295) == "4294967295");
|
||||
CHECK(String::num_uint64(0xF141593, 16) == "f141593");
|
||||
CHECK(String::num_uint64(0xF141593, 16, true) == "F141593");
|
||||
ERR_PRINT_OFF;
|
||||
CHECK(String::num_uint64(4294967295, 1) == ""); // Invalid base < 2.
|
||||
CHECK(String::num_uint64(4294967295, 37) == ""); // Invalid base > 36.
|
||||
ERR_PRINT_ON;
|
||||
|
||||
// String::num_real tests.
|
||||
CHECK(String::num_real(1.0) == "1.0");
|
||||
|
|
@ -463,15 +505,15 @@ TEST_CASE("[String] Number to string") {
|
|||
CHECK(String::num_real(3.141593) == "3.141593");
|
||||
CHECK(String::num_real(3.141) == "3.141"); // No trailing zeros.
|
||||
#ifdef REAL_T_IS_DOUBLE
|
||||
CHECK_MESSAGE(String::num_real(123.456789) == "123.456789", "Prints the appropriate amount of digits for real_t = double.");
|
||||
CHECK_MESSAGE(String::num_real(-123.456789) == "-123.456789", "Prints the appropriate amount of digits for real_t = double.");
|
||||
CHECK_MESSAGE(String::num_real(Math_PI) == "3.14159265358979", "Prints the appropriate amount of digits for real_t = double.");
|
||||
CHECK_MESSAGE(String::num_real(3.1415f) == "3.1414999961853", "Prints more digits of 32-bit float when real_t = double (ones that would be reliable for double) and no trailing zero.");
|
||||
CHECK_MESSAGE(String::num_real(real_t(123.456789)) == "123.456789", "Prints the appropriate amount of digits for real_t = double.");
|
||||
CHECK_MESSAGE(String::num_real(real_t(-123.456789)) == "-123.456789", "Prints the appropriate amount of digits for real_t = double.");
|
||||
CHECK_MESSAGE(String::num_real(real_t(Math_PI)) == "3.14159265358979", "Prints the appropriate amount of digits for real_t = double.");
|
||||
CHECK_MESSAGE(String::num_real(real_t(3.1415f)) == "3.1414999961853", "Prints more digits of 32-bit float when real_t = double (ones that would be reliable for double) and no trailing zero.");
|
||||
#else
|
||||
CHECK_MESSAGE(String::num_real(123.456789) == "123.4568", "Prints the appropriate amount of digits for real_t = float.");
|
||||
CHECK_MESSAGE(String::num_real(-123.456789) == "-123.4568", "Prints the appropriate amount of digits for real_t = float.");
|
||||
CHECK_MESSAGE(String::num_real(Math_PI) == "3.141593", "Prints the appropriate amount of digits for real_t = float.");
|
||||
CHECK_MESSAGE(String::num_real(3.1415f) == "3.1415", "Prints only reliable digits of 32-bit float when real_t = float.");
|
||||
CHECK_MESSAGE(String::num_real(real_t(123.456789)) == "123.4568", "Prints the appropriate amount of digits for real_t = float.");
|
||||
CHECK_MESSAGE(String::num_real(real_t(-123.456789)) == "-123.4568", "Prints the appropriate amount of digits for real_t = float.");
|
||||
CHECK_MESSAGE(String::num_real(real_t(Math_PI)) == "3.141593", "Prints the appropriate amount of digits for real_t = float.");
|
||||
CHECK_MESSAGE(String::num_real(real_t(3.1415f)) == "3.1415", "Prints only reliable digits of 32-bit float when real_t = float.");
|
||||
#endif // REAL_T_IS_DOUBLE
|
||||
|
||||
// Checks doubles with many decimal places.
|
||||
|
|
@ -480,7 +522,7 @@ TEST_CASE("[String] Number to string") {
|
|||
CHECK(String::num(-0.0000012345432123454321) == "-0.00000123454321");
|
||||
CHECK(String::num(-10000.0000012345432123454321) == "-10000.0000012345");
|
||||
CHECK(String::num(0.0000000000012345432123454321) == "0.00000000000123");
|
||||
CHECK(String::num(0.0000000000012345432123454321, 3) == "0");
|
||||
CHECK(String::num(0.0000000000012345432123454321, 3) == "0.0");
|
||||
|
||||
// Note: When relevant (remainder > 0.5), the last digit gets rounded up,
|
||||
// which can also lead to not include a trailing zero, e.g. "...89" -> "...9".
|
||||
|
|
@ -503,7 +545,10 @@ TEST_CASE("[String] String to integer") {
|
|||
CHECK(String(nums[i]).to_int() == num[i]);
|
||||
}
|
||||
CHECK(String("0b1011").to_int() == 1011); // Looks like a binary number, but to_int() handles this as a base-10 number, "b" is just ignored.
|
||||
CHECK(String("0B1011").to_int() == 1011);
|
||||
|
||||
CHECK(String("0x1012").to_int() == 1012); // Looks like a hexadecimal number, but to_int() handles this as a base-10 number, "x" is just ignored.
|
||||
CHECK(String("0X1012").to_int() == 1012);
|
||||
|
||||
ERR_PRINT_OFF
|
||||
CHECK(String("999999999999999999999999999999999999999999999999999999999").to_int() == INT64_MAX); // Too large, largest possible is returned.
|
||||
|
|
@ -512,10 +557,10 @@ TEST_CASE("[String] String to integer") {
|
|||
}
|
||||
|
||||
TEST_CASE("[String] Hex to integer") {
|
||||
static const char *nums[12] = { "0xFFAE", "22", "0", "AADDAD", "0x7FFFFFFFFFFFFFFF", "-0xf", "", "000", "000f", "0xaA", "-ff", "-" };
|
||||
static const int64_t num[12] = { 0xFFAE, 0x22, 0, 0xAADDAD, 0x7FFFFFFFFFFFFFFF, -0xf, 0, 0, 0xf, 0xaa, -0xff, 0x0 };
|
||||
static const char *nums[13] = { "0xFFAE", "22", "0", "AADDAD", "0x7FFFFFFFFFFFFFFF", "-0xf", "", "000", "000f", "0xaA", "-ff", "-", "0XFFAE" };
|
||||
static const int64_t num[13] = { 0xFFAE, 0x22, 0, 0xAADDAD, 0x7FFFFFFFFFFFFFFF, -0xf, 0, 0, 0xf, 0xaa, -0xff, 0x0, 0xFFAE };
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
for (int i = 0; i < 13; i++) {
|
||||
CHECK(String(nums[i]).hex_to_int() == num[i]);
|
||||
}
|
||||
|
||||
|
|
@ -533,10 +578,10 @@ TEST_CASE("[String] Hex to integer") {
|
|||
}
|
||||
|
||||
TEST_CASE("[String] Bin to integer") {
|
||||
static const char *nums[10] = { "", "0", "0b0", "0b1", "0b", "1", "0b1010", "-0b11", "-1010", "0b0111111111111111111111111111111111111111111111111111111111111111" };
|
||||
static const int64_t num[10] = { 0, 0, 0, 1, 0, 1, 10, -3, -10, 0x7FFFFFFFFFFFFFFF };
|
||||
static const char *nums[11] = { "", "0", "0b0", "0b1", "0b", "1", "0b1010", "-0b11", "-1010", "0b0111111111111111111111111111111111111111111111111111111111111111", "0B1010" };
|
||||
static const int64_t num[11] = { 0, 0, 0, 1, 0, 1, 10, -3, -10, 0x7FFFFFFFFFFFFFFF, 10 };
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int i = 0; i < 11; i++) {
|
||||
CHECK(String(nums[i]).bin_to_int() == num[i]);
|
||||
}
|
||||
|
||||
|
|
@ -638,64 +683,90 @@ TEST_CASE("[String] Ends with") {
|
|||
}
|
||||
|
||||
TEST_CASE("[String] Splitting") {
|
||||
String s = "Mars,Jupiter,Saturn,Uranus";
|
||||
const char *slices_l[3] = { "Mars", "Jupiter", "Saturn,Uranus" };
|
||||
MULTICHECK_SPLIT(s, split, ",", true, 2, slices_l, 3);
|
||||
{
|
||||
const String s = "Mars,Jupiter,Saturn,Uranus";
|
||||
|
||||
const char *slices_r[3] = { "Mars,Jupiter", "Saturn", "Uranus" };
|
||||
MULTICHECK_SPLIT(s, rsplit, ",", true, 2, slices_r, 3);
|
||||
const char *slices_l[3] = { "Mars", "Jupiter", "Saturn,Uranus" };
|
||||
MULTICHECK_SPLIT(s, split, ",", true, 2, slices_l, 3);
|
||||
|
||||
s = "test";
|
||||
const char *slices_3[4] = { "t", "e", "s", "t" };
|
||||
MULTICHECK_SPLIT(s, split, "", true, 0, slices_3, 4);
|
||||
|
||||
s = "";
|
||||
const char *slices_4[1] = { "" };
|
||||
MULTICHECK_SPLIT(s, split, "", true, 0, slices_4, 1);
|
||||
MULTICHECK_SPLIT(s, split, "", false, 0, slices_4, 0);
|
||||
|
||||
s = "Mars Jupiter Saturn Uranus";
|
||||
const char *slices_s[4] = { "Mars", "Jupiter", "Saturn", "Uranus" };
|
||||
Vector<String> l = s.split_spaces();
|
||||
for (int i = 0; i < l.size(); i++) {
|
||||
CHECK(l[i] == slices_s[i]);
|
||||
const char *slices_r[3] = { "Mars,Jupiter", "Saturn", "Uranus" };
|
||||
MULTICHECK_SPLIT(s, rsplit, ",", true, 2, slices_r, 3);
|
||||
}
|
||||
|
||||
s = "1.2;2.3 4.5";
|
||||
const double slices_d[3] = { 1.2, 2.3, 4.5 };
|
||||
|
||||
Vector<double> d_arr;
|
||||
d_arr = s.split_floats(";");
|
||||
CHECK(d_arr.size() == 2);
|
||||
for (int i = 0; i < d_arr.size(); i++) {
|
||||
CHECK(ABS(d_arr[i] - slices_d[i]) <= 0.00001);
|
||||
{
|
||||
const String s = "test";
|
||||
const char *slices[4] = { "t", "e", "s", "t" };
|
||||
MULTICHECK_SPLIT(s, split, "", true, 0, slices, 4);
|
||||
}
|
||||
|
||||
Vector<String> keys;
|
||||
keys.push_back(";");
|
||||
keys.push_back(" ");
|
||||
|
||||
Vector<float> f_arr;
|
||||
f_arr = s.split_floats_mk(keys);
|
||||
CHECK(f_arr.size() == 3);
|
||||
for (int i = 0; i < f_arr.size(); i++) {
|
||||
CHECK(ABS(f_arr[i] - slices_d[i]) <= 0.00001);
|
||||
{
|
||||
const String s = "";
|
||||
const char *slices[1] = { "" };
|
||||
MULTICHECK_SPLIT(s, split, "", true, 0, slices, 1);
|
||||
MULTICHECK_SPLIT(s, split, "", false, 0, slices, 0);
|
||||
}
|
||||
|
||||
s = "1;2 4";
|
||||
const int slices_i[3] = { 1, 2, 4 };
|
||||
|
||||
Vector<int> ii;
|
||||
ii = s.split_ints(";");
|
||||
CHECK(ii.size() == 2);
|
||||
for (int i = 0; i < ii.size(); i++) {
|
||||
CHECK(ii[i] == slices_i[i]);
|
||||
{
|
||||
const String s = "Mars Jupiter Saturn Uranus";
|
||||
const char *slices[4] = { "Mars", "Jupiter", "Saturn", "Uranus" };
|
||||
Vector<String> l = s.split_spaces();
|
||||
for (int i = 0; i < l.size(); i++) {
|
||||
CHECK(l[i] == slices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ii = s.split_ints_mk(keys);
|
||||
CHECK(ii.size() == 3);
|
||||
for (int i = 0; i < ii.size(); i++) {
|
||||
CHECK(ii[i] == slices_i[i]);
|
||||
{
|
||||
const String s = "1.2;2.3 4.5";
|
||||
const double slices[3] = { 1.2, 2.3, 4.5 };
|
||||
|
||||
const Vector<double> d_arr = s.split_floats(";");
|
||||
CHECK(d_arr.size() == 2);
|
||||
for (int i = 0; i < d_arr.size(); i++) {
|
||||
CHECK(ABS(d_arr[i] - slices[i]) <= 0.00001);
|
||||
}
|
||||
|
||||
const Vector<String> keys = { ";", " " };
|
||||
const Vector<float> f_arr = s.split_floats_mk(keys);
|
||||
CHECK(f_arr.size() == 3);
|
||||
for (int i = 0; i < f_arr.size(); i++) {
|
||||
CHECK(ABS(f_arr[i] - slices[i]) <= 0.00001);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const String s = " -2.0 5";
|
||||
const double slices[10] = { 0, -2, 0, 0, 0, 0, 0, 0, 0, 5 };
|
||||
|
||||
const Vector<double> arr = s.split_floats(" ");
|
||||
CHECK(arr.size() == 10);
|
||||
for (int i = 0; i < arr.size(); i++) {
|
||||
CHECK(ABS(arr[i] - slices[i]) <= 0.00001);
|
||||
}
|
||||
|
||||
const Vector<String> keys = { ";", " " };
|
||||
const Vector<float> mk = s.split_floats_mk(keys);
|
||||
CHECK(mk.size() == 10);
|
||||
for (int i = 0; i < mk.size(); i++) {
|
||||
CHECK(mk[i] == slices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const String s = "1;2 4";
|
||||
const int slices[3] = { 1, 2, 4 };
|
||||
|
||||
const Vector<int> arr = s.split_ints(";");
|
||||
CHECK(arr.size() == 2);
|
||||
for (int i = 0; i < arr.size(); i++) {
|
||||
CHECK(arr[i] == slices[i]);
|
||||
}
|
||||
|
||||
const Vector<String> keys = { ";", " " };
|
||||
const Vector<int> mk = s.split_ints_mk(keys);
|
||||
CHECK(mk.size() == 3);
|
||||
for (int i = 0; i < mk.size(); i++) {
|
||||
CHECK(mk[i] == slices[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1199,6 +1270,12 @@ TEST_CASE("[String] is_subsequence_of") {
|
|||
CHECK(String("Sub").is_subsequence_ofn(a));
|
||||
}
|
||||
|
||||
TEST_CASE("[String] is_lowercase") {
|
||||
CHECK(String("abcd1234 !@#$%^&*()_-=+,.<>/\\|[]{};':\"`~").is_lowercase());
|
||||
CHECK(String("").is_lowercase());
|
||||
CHECK(!String("abc_ABC").is_lowercase());
|
||||
}
|
||||
|
||||
TEST_CASE("[String] match") {
|
||||
CHECK(String("img1.png").match("*.png"));
|
||||
CHECK(!String("img1.jpeg").match("*.png"));
|
||||
|
|
@ -1594,7 +1671,7 @@ TEST_CASE("[String] Path functions") {
|
|||
static const char *base_name[8] = { "C:\\Godot\\project\\test", "/Godot/project/test", "../Godot/project/test", "Godot\\test", "C:\\test", "res://test", "user://test", "/" };
|
||||
static const char *ext[8] = { "tscn", "xscn", "scn", "doc", "", "", "", "test" };
|
||||
static const char *file[8] = { "test.tscn", "test.xscn", "test.scn", "test.doc", "test.", "test", "test", ".test" };
|
||||
static const char *simplified[8] = { "C:/Godot/project/test.tscn", "/Godot/project/test.xscn", "Godot/project/test.scn", "Godot/test.doc", "C:/test.", "res://test", "user://test", "/.test" };
|
||||
static const char *simplified[8] = { "C:/Godot/project/test.tscn", "/Godot/project/test.xscn", "../Godot/project/test.scn", "Godot/test.doc", "C:/test.", "res://test", "user://test", "/.test" };
|
||||
static const bool abs[8] = { true, true, false, false, true, true, true, true };
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
|
|
@ -1613,6 +1690,10 @@ TEST_CASE("[String] Path functions") {
|
|||
for (int i = 0; i < 3; i++) {
|
||||
CHECK(String(file_name[i]).is_valid_filename() == valid[i]);
|
||||
}
|
||||
|
||||
CHECK(String("res://texture.png") == String("res://folder/../folder/../texture.png").simplify_path());
|
||||
CHECK(String("res://texture.png") == String("res://folder/sub/../../texture.png").simplify_path());
|
||||
CHECK(String("res://../../texture.png") == String("res://../../texture.png").simplify_path());
|
||||
}
|
||||
|
||||
TEST_CASE("[String] hash") {
|
||||
|
|
@ -1785,31 +1866,45 @@ TEST_CASE("[String] SHA1/SHA256/MD5") {
|
|||
}
|
||||
|
||||
TEST_CASE("[String] Join") {
|
||||
String s = ", ";
|
||||
String comma = ", ";
|
||||
String empty = "";
|
||||
Vector<String> parts;
|
||||
|
||||
CHECK(comma.join(parts) == "");
|
||||
CHECK(empty.join(parts) == "");
|
||||
|
||||
parts.push_back("One");
|
||||
CHECK(comma.join(parts) == "One");
|
||||
CHECK(empty.join(parts) == "One");
|
||||
|
||||
parts.push_back("B");
|
||||
parts.push_back("C");
|
||||
String t = s.join(parts);
|
||||
CHECK(t == "One, B, C");
|
||||
CHECK(comma.join(parts) == "One, B, C");
|
||||
CHECK(empty.join(parts) == "OneBC");
|
||||
|
||||
parts.push_back("");
|
||||
CHECK(comma.join(parts) == "One, B, C, ");
|
||||
CHECK(empty.join(parts) == "OneBC");
|
||||
}
|
||||
|
||||
TEST_CASE("[String] Is_*") {
|
||||
static const char *data[12] = { "-30", "100", "10.1", "10,1", "1e2", "1e-2", "1e2e3", "0xAB", "AB", "Test1", "1Test", "Test*1" };
|
||||
static bool isnum[12] = { true, true, true, false, false, false, false, false, false, false, false, false };
|
||||
static bool isint[12] = { true, true, false, false, false, false, false, false, false, false, false, false };
|
||||
static bool ishex[12] = { true, true, false, false, true, false, true, false, true, false, false, false };
|
||||
static bool ishex_p[12] = { false, false, false, false, false, false, false, true, false, false, false, false };
|
||||
static bool isflt[12] = { true, true, true, false, true, true, false, false, false, false, false, false };
|
||||
static bool isid[12] = { false, false, false, false, false, false, false, false, true, true, false, false };
|
||||
for (int i = 0; i < 12; i++) {
|
||||
String s = String(data[i]);
|
||||
static const char *data[] = { "-30", "100", "10.1", "10,1", "1e2", "1e-2", "1e2e3", "0xAB", "AB", "Test1", "1Test", "Test*1", "文字", "1E2", "1E-2" };
|
||||
static bool isnum[] = { true, true, true, false, false, false, false, false, false, false, false, false, false, false, false };
|
||||
static bool isint[] = { true, true, false, false, false, false, false, false, false, false, false, false, false, false, false };
|
||||
static bool ishex[] = { true, true, false, false, true, false, true, false, true, false, false, false, false, true, false };
|
||||
static bool ishex_p[] = { false, false, false, false, false, false, false, true, false, false, false, false, false, false, false };
|
||||
static bool isflt[] = { true, true, true, false, true, true, false, false, false, false, false, false, false, true, true };
|
||||
static bool isaid[] = { false, false, false, false, false, false, false, false, true, true, false, false, false, false, false };
|
||||
static bool isuid[] = { false, false, false, false, false, false, false, false, true, true, false, false, true, false, false };
|
||||
for (unsigned int i = 0; i < sizeof(data) / sizeof(data[0]); i++) {
|
||||
String s = String::utf8(data[i]);
|
||||
CHECK(s.is_numeric() == isnum[i]);
|
||||
CHECK(s.is_valid_int() == isint[i]);
|
||||
CHECK(s.is_valid_hex_number(false) == ishex[i]);
|
||||
CHECK(s.is_valid_hex_number(true) == ishex_p[i]);
|
||||
CHECK(s.is_valid_float() == isflt[i]);
|
||||
CHECK(s.is_valid_identifier() == isid[i]);
|
||||
CHECK(s.is_valid_ascii_identifier() == isaid[i]);
|
||||
CHECK(s.is_valid_unicode_identifier() == isuid[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1835,18 +1930,32 @@ TEST_CASE("[String] validate_node_name") {
|
|||
CHECK(name_with_invalid_chars.validate_node_name() == "Name with invalid characters ____removed!");
|
||||
}
|
||||
|
||||
TEST_CASE("[String] validate_identifier") {
|
||||
TEST_CASE("[String] validate_ascii_identifier") {
|
||||
String empty_string;
|
||||
CHECK(empty_string.validate_identifier() == "_");
|
||||
CHECK(empty_string.validate_ascii_identifier() == "_");
|
||||
|
||||
String numeric_only = "12345";
|
||||
CHECK(numeric_only.validate_identifier() == "_12345");
|
||||
CHECK(numeric_only.validate_ascii_identifier() == "_12345");
|
||||
|
||||
String name_with_spaces = "Name with spaces";
|
||||
CHECK(name_with_spaces.validate_identifier() == "Name_with_spaces");
|
||||
CHECK(name_with_spaces.validate_ascii_identifier() == "Name_with_spaces");
|
||||
|
||||
String name_with_invalid_chars = U"Invalid characters:@*#&世界";
|
||||
CHECK(name_with_invalid_chars.validate_identifier() == "Invalid_characters_______");
|
||||
CHECK(name_with_invalid_chars.validate_ascii_identifier() == "Invalid_characters_______");
|
||||
}
|
||||
|
||||
TEST_CASE("[String] validate_unicode_identifier") {
|
||||
String empty_string;
|
||||
CHECK(empty_string.validate_unicode_identifier() == "_");
|
||||
|
||||
String numeric_only = "12345";
|
||||
CHECK(numeric_only.validate_unicode_identifier() == "_12345");
|
||||
|
||||
String name_with_spaces = "Name with spaces";
|
||||
CHECK(name_with_spaces.validate_unicode_identifier() == "Name_with_spaces");
|
||||
|
||||
String name_with_invalid_chars = U"Invalid characters:@*#&世界";
|
||||
CHECK(name_with_invalid_chars.validate_unicode_identifier() == U"Invalid_characters_____世界");
|
||||
}
|
||||
|
||||
TEST_CASE("[String] Variant indexed get") {
|
||||
|
|
@ -1921,6 +2030,61 @@ TEST_CASE("[String] Variant ptr indexed set") {
|
|||
CHECK_EQ(s, String("azcd"));
|
||||
}
|
||||
|
||||
TEST_CASE("[String][URL] Parse URL") {
|
||||
#define CHECK_URL(m_url_to_parse, m_expected_schema, m_expected_host, m_expected_port, m_expected_path, m_expected_fragment, m_expected_error) \
|
||||
if (true) { \
|
||||
int port; \
|
||||
String url(m_url_to_parse), schema, host, path, fragment; \
|
||||
\
|
||||
CHECK_EQ(url.parse_url(schema, host, port, path, fragment), m_expected_error); \
|
||||
CHECK_EQ(schema, m_expected_schema); \
|
||||
CHECK_EQ(host, m_expected_host); \
|
||||
CHECK_EQ(path, m_expected_path); \
|
||||
CHECK_EQ(fragment, m_expected_fragment); \
|
||||
CHECK_EQ(port, m_expected_port); \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
// All elements.
|
||||
CHECK_URL("https://www.example.com:8080/path/to/file.html#fragment", "https://", "www.example.com", 8080, "/path/to/file.html", "fragment", Error::OK);
|
||||
|
||||
// Valid URLs.
|
||||
CHECK_URL("https://godotengine.org", "https://", "godotengine.org", 0, "", "", Error::OK);
|
||||
CHECK_URL("https://godotengine.org/", "https://", "godotengine.org", 0, "/", "", Error::OK);
|
||||
CHECK_URL("godotengine.org/", "", "godotengine.org", 0, "/", "", Error::OK);
|
||||
CHECK_URL("HTTPS://godotengine.org/", "https://", "godotengine.org", 0, "/", "", Error::OK);
|
||||
CHECK_URL("https://GODOTENGINE.ORG/", "https://", "godotengine.org", 0, "/", "", Error::OK);
|
||||
CHECK_URL("http://godotengine.org", "http://", "godotengine.org", 0, "", "", Error::OK);
|
||||
CHECK_URL("https://godotengine.org:8080", "https://", "godotengine.org", 8080, "", "", Error::OK);
|
||||
CHECK_URL("https://godotengine.org/blog", "https://", "godotengine.org", 0, "/blog", "", Error::OK);
|
||||
CHECK_URL("https://godotengine.org/blog/", "https://", "godotengine.org", 0, "/blog/", "", Error::OK);
|
||||
CHECK_URL("https://docs.godotengine.org/en/stable", "https://", "docs.godotengine.org", 0, "/en/stable", "", Error::OK);
|
||||
CHECK_URL("https://docs.godotengine.org/en/stable/", "https://", "docs.godotengine.org", 0, "/en/stable/", "", Error::OK);
|
||||
CHECK_URL("https://me:secret@godotengine.org", "https://", "godotengine.org", 0, "", "", Error::OK);
|
||||
CHECK_URL("https://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]/ipv6", "https://", "fedc:ba98:7654:3210:fedc:ba98:7654:3210", 0, "/ipv6", "", Error::OK);
|
||||
|
||||
// Scheme vs Fragment.
|
||||
CHECK_URL("google.com/#goto=http://redirect_url/", "", "google.com", 0, "/", "goto=http://redirect_url/", Error::OK);
|
||||
|
||||
// Invalid URLs.
|
||||
|
||||
// Invalid Scheme.
|
||||
CHECK_URL("https_://godotengine.org", "", "https_", 0, "//godotengine.org", "", Error::ERR_INVALID_PARAMETER);
|
||||
|
||||
// Multiple ports.
|
||||
CHECK_URL("https://godotengine.org:8080:433", "https://", "", 0, "", "", Error::ERR_INVALID_PARAMETER);
|
||||
// Missing ] on literal IPv6.
|
||||
CHECK_URL("https://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/ipv6", "https://", "", 0, "/ipv6", "", Error::ERR_INVALID_PARAMETER);
|
||||
// Missing host.
|
||||
CHECK_URL("https:///blog", "https://", "", 0, "/blog", "", Error::ERR_INVALID_PARAMETER);
|
||||
// Invalid ports.
|
||||
CHECK_URL("https://godotengine.org:notaport", "https://", "godotengine.org", 0, "", "", Error::ERR_INVALID_PARAMETER);
|
||||
CHECK_URL("https://godotengine.org:-8080", "https://", "godotengine.org", -8080, "", "", Error::ERR_INVALID_PARAMETER);
|
||||
CHECK_URL("https://godotengine.org:88888", "https://", "godotengine.org", 88888, "", "", Error::ERR_INVALID_PARAMETER);
|
||||
|
||||
#undef CHECK_URL
|
||||
}
|
||||
|
||||
TEST_CASE("[Stress][String] Empty via ' == String()'") {
|
||||
for (int i = 0; i < 100000; ++i) {
|
||||
String str = "Hello World!";
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "core/string/optimized_translation.h"
|
||||
#include "core/string/translation.h"
|
||||
#include "core/string/translation_po.h"
|
||||
#include "core/string/translation_server.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/import/resource_importer_csv_translation.h"
|
||||
|
|
@ -160,7 +161,7 @@ TEST_CASE("[TranslationCSV] CSV import") {
|
|||
|
||||
List<String> gen_files;
|
||||
|
||||
Error result = import_csv_translation->import(TestUtils::get_data_path("translations.csv"),
|
||||
Error result = import_csv_translation->import(0, TestUtils::get_data_path("translations.csv"),
|
||||
"", options, nullptr, &gen_files);
|
||||
CHECK(result == OK);
|
||||
CHECK(gen_files.size() == 4);
|
||||
|
|
|
|||
|
|
@ -31,33 +31,43 @@
|
|||
#ifndef TEST_TRANSLATION_SERVER_H
|
||||
#define TEST_TRANSLATION_SERVER_H
|
||||
|
||||
#include "core/string/translation.h"
|
||||
#include "core/string/translation_server.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestTranslationServer {
|
||||
TEST_CASE("[TranslationServer] Translation operations") {
|
||||
Ref<Translation> t = memnew(Translation);
|
||||
t->set_locale("uk");
|
||||
t->add_message("Good Morning", String::utf8("Добрий ранок"));
|
||||
Ref<Translation> t1 = memnew(Translation);
|
||||
t1->set_locale("uk");
|
||||
t1->add_message("Good Morning", String(U"Добрий ранок"));
|
||||
|
||||
Ref<Translation> t2 = memnew(Translation);
|
||||
t2->set_locale("uk");
|
||||
t2->add_message("Hello Godot", String(U"你好戈多"));
|
||||
|
||||
TranslationServer *ts = TranslationServer::get_singleton();
|
||||
|
||||
// Adds translation for UK locale for the first time.
|
||||
int l_count_before = ts->get_loaded_locales().size();
|
||||
ts->add_translation(t);
|
||||
ts->add_translation(t1);
|
||||
int l_count_after = ts->get_loaded_locales().size();
|
||||
// Newly created Translation object should be added to the list, so the counter should increase, too.
|
||||
CHECK(l_count_after > l_count_before);
|
||||
|
||||
Ref<Translation> trans = ts->get_translation_object("uk");
|
||||
CHECK(trans.is_valid());
|
||||
// Adds translation for UK locale again.
|
||||
ts->add_translation(t2);
|
||||
CHECK_EQ(ts->get_loaded_locales().size(), l_count_after);
|
||||
|
||||
// Removing that translation.
|
||||
ts->remove_translation(t2);
|
||||
CHECK_EQ(ts->get_loaded_locales().size(), l_count_after);
|
||||
|
||||
CHECK(ts->get_translation_object("uk").is_valid());
|
||||
|
||||
ts->set_locale("uk");
|
||||
CHECK(ts->translate("Good Morning") == String::utf8("Добрий ранок"));
|
||||
|
||||
ts->remove_translation(t);
|
||||
trans = ts->get_translation_object("uk");
|
||||
CHECK(trans.is_null());
|
||||
ts->remove_translation(t1);
|
||||
CHECK(ts->get_translation_object("uk").is_null());
|
||||
// If no suitable Translation object has been found - the original message should be returned.
|
||||
CHECK(ts->translate("Good Morning") == "Good Morning");
|
||||
}
|
||||
|
|
@ -94,6 +104,36 @@ TEST_CASE("[TranslationServer] Locale operations") {
|
|||
res = ts->standardize_locale(loc);
|
||||
|
||||
CHECK(res == "de_DE");
|
||||
|
||||
// No added defaults.
|
||||
loc = "es_ES";
|
||||
res = ts->standardize_locale(loc, true);
|
||||
|
||||
CHECK(res == "es_ES");
|
||||
|
||||
// Add default script.
|
||||
loc = "az_AZ";
|
||||
res = ts->standardize_locale(loc, true);
|
||||
|
||||
CHECK(res == "az_Latn_AZ");
|
||||
|
||||
// Add default country.
|
||||
loc = "pa_Arab";
|
||||
res = ts->standardize_locale(loc, true);
|
||||
|
||||
CHECK(res == "pa_Arab_PK");
|
||||
|
||||
// Add default script and country.
|
||||
loc = "zh";
|
||||
res = ts->standardize_locale(loc, true);
|
||||
|
||||
CHECK(res == "zh_Hans_CN");
|
||||
|
||||
// Explicitly don't add defaults.
|
||||
loc = "zh";
|
||||
res = ts->standardize_locale(loc, false);
|
||||
|
||||
CHECK(res == "zh");
|
||||
}
|
||||
|
||||
TEST_CASE("[TranslationServer] Comparing locales") {
|
||||
|
|
@ -110,18 +150,50 @@ TEST_CASE("[TranslationServer] Comparing locales") {
|
|||
locale_a = "sr-Latn-CS";
|
||||
locale_b = "sr-Latn-RS";
|
||||
|
||||
// Two elements from locales match.
|
||||
// Script matches (+1) but country doesn't (-1).
|
||||
res = ts->compare_locales(locale_a, locale_b);
|
||||
|
||||
CHECK(res == 2);
|
||||
CHECK(res == 5);
|
||||
|
||||
locale_a = "uz-Cyrl-UZ";
|
||||
locale_b = "uz-Latn-UZ";
|
||||
|
||||
// Two elements match, but they are not sequentual.
|
||||
// Country matches (+1) but script doesn't (-1).
|
||||
res = ts->compare_locales(locale_a, locale_b);
|
||||
|
||||
CHECK(res == 2);
|
||||
CHECK(res == 5);
|
||||
|
||||
locale_a = "aa-Latn-ER";
|
||||
locale_b = "aa-Latn-ER-saaho";
|
||||
|
||||
// Script and country match (+2) with variant on one locale (+0).
|
||||
res = ts->compare_locales(locale_a, locale_b);
|
||||
|
||||
CHECK(res == 7);
|
||||
|
||||
locale_a = "uz-Cyrl-UZ";
|
||||
locale_b = "uz-Latn-KG";
|
||||
|
||||
// Both script and country mismatched (-2).
|
||||
res = ts->compare_locales(locale_a, locale_b);
|
||||
|
||||
CHECK(res == 3);
|
||||
|
||||
locale_a = "es-ES";
|
||||
locale_b = "es-AR";
|
||||
|
||||
// Mismatched country (-1).
|
||||
res = ts->compare_locales(locale_a, locale_b);
|
||||
|
||||
CHECK(res == 4);
|
||||
|
||||
locale_a = "es";
|
||||
locale_b = "es-AR";
|
||||
|
||||
// No country for one locale (+0).
|
||||
res = ts->compare_locales(locale_a, locale_b);
|
||||
|
||||
CHECK(res == 5);
|
||||
|
||||
locale_a = "es-EC";
|
||||
locale_b = "fr-LU";
|
||||
|
|
@ -130,6 +202,24 @@ TEST_CASE("[TranslationServer] Comparing locales") {
|
|||
res = ts->compare_locales(locale_a, locale_b);
|
||||
|
||||
CHECK(res == 0);
|
||||
|
||||
locale_a = "zh-HK";
|
||||
locale_b = "zh";
|
||||
|
||||
// In full standardization, zh-HK becomes zh_Hant_HK and zh becomes
|
||||
// zh_Hans_CN. Both script and country mismatch (-2).
|
||||
res = ts->compare_locales(locale_a, locale_b);
|
||||
|
||||
CHECK(res == 3);
|
||||
|
||||
locale_a = "zh-CN";
|
||||
locale_b = "zh";
|
||||
|
||||
// In full standardization, zh and zh-CN both become zh_Hans_CN for an
|
||||
// exact match.
|
||||
res = ts->compare_locales(locale_a, locale_b);
|
||||
|
||||
CHECK(res == 10);
|
||||
}
|
||||
} // namespace TestTranslationServer
|
||||
|
||||
|
|
|
|||
313
engine/tests/core/templates/test_a_hash_map.h
Normal file
313
engine/tests/core/templates/test_a_hash_map.h
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
/**************************************************************************/
|
||||
/* test_a_hash_map.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TEST_A_HASH_MAP_H
|
||||
#define TEST_A_HASH_MAP_H
|
||||
|
||||
#include "core/templates/a_hash_map.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestAHashMap {
|
||||
|
||||
TEST_CASE("[AHashMap] List initialization") {
|
||||
AHashMap<int, String> map{ { 0, "A" }, { 1, "B" }, { 2, "C" }, { 3, "D" }, { 4, "E" } };
|
||||
|
||||
CHECK(map.size() == 5);
|
||||
CHECK(map[0] == "A");
|
||||
CHECK(map[1] == "B");
|
||||
CHECK(map[2] == "C");
|
||||
CHECK(map[3] == "D");
|
||||
CHECK(map[4] == "E");
|
||||
}
|
||||
|
||||
TEST_CASE("[AHashMap] List initialization with existing elements") {
|
||||
AHashMap<int, String> map{ { 0, "A" }, { 0, "B" }, { 0, "C" }, { 0, "D" }, { 0, "E" } };
|
||||
|
||||
CHECK(map.size() == 1);
|
||||
CHECK(map[0] == "E");
|
||||
}
|
||||
|
||||
TEST_CASE("[AHashMap] Insert element") {
|
||||
AHashMap<int, int> map;
|
||||
AHashMap<int, int>::Iterator e = map.insert(42, 84);
|
||||
|
||||
CHECK(e);
|
||||
CHECK(e->key == 42);
|
||||
CHECK(e->value == 84);
|
||||
CHECK(map[42] == 84);
|
||||
CHECK(map.has(42));
|
||||
CHECK(map.find(42));
|
||||
}
|
||||
|
||||
TEST_CASE("[AHashMap] Overwrite element") {
|
||||
AHashMap<int, int> map;
|
||||
map.insert(42, 84);
|
||||
map.insert(42, 1234);
|
||||
|
||||
CHECK(map[42] == 1234);
|
||||
}
|
||||
|
||||
TEST_CASE("[AHashMap] Erase via element") {
|
||||
AHashMap<int, int> map;
|
||||
AHashMap<int, int>::Iterator e = map.insert(42, 84);
|
||||
map.remove(e);
|
||||
CHECK(!map.has(42));
|
||||
CHECK(!map.find(42));
|
||||
}
|
||||
|
||||
TEST_CASE("[AHashMap] Erase via key") {
|
||||
AHashMap<int, int> map;
|
||||
map.insert(42, 84);
|
||||
map.erase(42);
|
||||
CHECK(!map.has(42));
|
||||
CHECK(!map.find(42));
|
||||
}
|
||||
|
||||
TEST_CASE("[AHashMap] Size") {
|
||||
AHashMap<int, int> map;
|
||||
map.insert(42, 84);
|
||||
map.insert(123, 84);
|
||||
map.insert(123, 84);
|
||||
map.insert(0, 84);
|
||||
map.insert(123485, 84);
|
||||
|
||||
CHECK(map.size() == 4);
|
||||
}
|
||||
|
||||
TEST_CASE("[AHashMap] Iteration") {
|
||||
AHashMap<int, int> map;
|
||||
|
||||
map.insert(42, 84);
|
||||
map.insert(123, 12385);
|
||||
map.insert(0, 12934);
|
||||
map.insert(123485, 1238888);
|
||||
map.insert(123, 111111);
|
||||
|
||||
Vector<Pair<int, int>> expected;
|
||||
expected.push_back(Pair<int, int>(42, 84));
|
||||
expected.push_back(Pair<int, int>(123, 111111));
|
||||
expected.push_back(Pair<int, int>(0, 12934));
|
||||
expected.push_back(Pair<int, int>(123485, 1238888));
|
||||
|
||||
int idx = 0;
|
||||
for (const KeyValue<int, int> &E : map) {
|
||||
CHECK(expected[idx] == Pair<int, int>(E.key, E.value));
|
||||
idx++;
|
||||
}
|
||||
|
||||
idx--;
|
||||
for (AHashMap<int, int>::Iterator it = map.last(); it; --it) {
|
||||
CHECK(expected[idx] == Pair<int, int>(it->key, it->value));
|
||||
idx--;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[AHashMap] Const iteration") {
|
||||
AHashMap<int, int> map;
|
||||
map.insert(42, 84);
|
||||
map.insert(123, 12385);
|
||||
map.insert(0, 12934);
|
||||
map.insert(123485, 1238888);
|
||||
map.insert(123, 111111);
|
||||
|
||||
const AHashMap<int, int> const_map = map;
|
||||
|
||||
Vector<Pair<int, int>> expected;
|
||||
expected.push_back(Pair<int, int>(42, 84));
|
||||
expected.push_back(Pair<int, int>(123, 111111));
|
||||
expected.push_back(Pair<int, int>(0, 12934));
|
||||
expected.push_back(Pair<int, int>(123485, 1238888));
|
||||
expected.push_back(Pair<int, int>(123, 111111));
|
||||
|
||||
int idx = 0;
|
||||
for (const KeyValue<int, int> &E : const_map) {
|
||||
CHECK(expected[idx] == Pair<int, int>(E.key, E.value));
|
||||
idx++;
|
||||
}
|
||||
|
||||
idx--;
|
||||
for (AHashMap<int, int>::ConstIterator it = const_map.last(); it; --it) {
|
||||
CHECK(expected[idx] == Pair<int, int>(it->key, it->value));
|
||||
idx--;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[AHashMap] Replace key") {
|
||||
AHashMap<int, int> map;
|
||||
map.insert(42, 84);
|
||||
map.insert(0, 12934);
|
||||
CHECK(map.replace_key(0, 1));
|
||||
CHECK(map.has(1));
|
||||
CHECK(map[1] == 12934);
|
||||
}
|
||||
|
||||
TEST_CASE("[AHashMap] Clear") {
|
||||
AHashMap<int, int> map;
|
||||
map.insert(42, 84);
|
||||
map.insert(123, 12385);
|
||||
map.insert(0, 12934);
|
||||
|
||||
map.clear();
|
||||
CHECK(!map.has(42));
|
||||
CHECK(map.size() == 0);
|
||||
CHECK(map.is_empty());
|
||||
}
|
||||
|
||||
TEST_CASE("[AHashMap] Get") {
|
||||
AHashMap<int, int> map;
|
||||
map.insert(42, 84);
|
||||
map.insert(123, 12385);
|
||||
map.insert(0, 12934);
|
||||
|
||||
CHECK(map.get(123) == 12385);
|
||||
map.get(123) = 10;
|
||||
CHECK(map.get(123) == 10);
|
||||
|
||||
CHECK(*map.getptr(0) == 12934);
|
||||
*map.getptr(0) = 1;
|
||||
CHECK(*map.getptr(0) == 1);
|
||||
|
||||
CHECK(map.get(42) == 84);
|
||||
CHECK(map.getptr(-10) == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("[AHashMap] Insert, iterate and remove many elements") {
|
||||
const int elem_max = 1234;
|
||||
AHashMap<int, int> map;
|
||||
for (int i = 0; i < elem_max; i++) {
|
||||
map.insert(i, i);
|
||||
}
|
||||
|
||||
//insert order should have been kept
|
||||
int idx = 0;
|
||||
for (auto &K : map) {
|
||||
CHECK(idx == K.key);
|
||||
CHECK(idx == K.value);
|
||||
CHECK(map.has(idx));
|
||||
idx++;
|
||||
}
|
||||
|
||||
Vector<int> elems_still_valid;
|
||||
|
||||
for (int i = 0; i < elem_max; i++) {
|
||||
if ((i % 5) == 0) {
|
||||
map.erase(i);
|
||||
} else {
|
||||
elems_still_valid.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
CHECK(elems_still_valid.size() == map.size());
|
||||
|
||||
for (int i = 0; i < elems_still_valid.size(); i++) {
|
||||
CHECK(map.has(elems_still_valid[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[AHashMap] Insert, iterate and remove many strings") {
|
||||
const int elem_max = 432;
|
||||
AHashMap<String, String> map;
|
||||
for (int i = 0; i < elem_max; i++) {
|
||||
map.insert(itos(i), itos(i));
|
||||
}
|
||||
|
||||
//insert order should have been kept
|
||||
int idx = 0;
|
||||
for (auto &K : map) {
|
||||
CHECK(itos(idx) == K.key);
|
||||
CHECK(itos(idx) == K.value);
|
||||
CHECK(map.has(itos(idx)));
|
||||
idx++;
|
||||
}
|
||||
|
||||
Vector<String> elems_still_valid;
|
||||
|
||||
for (int i = 0; i < elem_max; i++) {
|
||||
if ((i % 5) == 0) {
|
||||
map.erase(itos(i));
|
||||
} else {
|
||||
elems_still_valid.push_back(itos(i));
|
||||
}
|
||||
}
|
||||
|
||||
CHECK(elems_still_valid.size() == map.size());
|
||||
|
||||
for (int i = 0; i < elems_still_valid.size(); i++) {
|
||||
CHECK(map.has(elems_still_valid[i]));
|
||||
}
|
||||
|
||||
elems_still_valid.clear();
|
||||
}
|
||||
|
||||
TEST_CASE("[AHashMap] Copy constructor") {
|
||||
AHashMap<int, int> map0;
|
||||
const uint32_t count = 5;
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
map0.insert(i, i);
|
||||
}
|
||||
AHashMap<int, int> map1(map0);
|
||||
CHECK(map0.size() == map1.size());
|
||||
CHECK(map0.get_capacity() == map1.get_capacity());
|
||||
CHECK(*map0.getptr(0) == *map1.getptr(0));
|
||||
}
|
||||
|
||||
TEST_CASE("[AHashMap] Operator =") {
|
||||
AHashMap<int, int> map0;
|
||||
AHashMap<int, int> map1;
|
||||
const uint32_t count = 5;
|
||||
map1.insert(1234, 1234);
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
map0.insert(i, i);
|
||||
}
|
||||
map1 = map0;
|
||||
CHECK(map0.size() == map1.size());
|
||||
CHECK(map0.get_capacity() == map1.get_capacity());
|
||||
CHECK(*map0.getptr(0) == *map1.getptr(0));
|
||||
}
|
||||
|
||||
TEST_CASE("[AHashMap] Array methods") {
|
||||
AHashMap<int, int> map;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
map.insert(100 - i, i);
|
||||
}
|
||||
for (int i = 0; i < 100; i++) {
|
||||
CHECK(map.get_by_index(i).value == i);
|
||||
}
|
||||
int index = map.get_index(1);
|
||||
CHECK(map.get_by_index(index).value == 99);
|
||||
CHECK(map.erase_by_index(index));
|
||||
CHECK(!map.erase_by_index(index));
|
||||
CHECK(map.get_index(1) == -1);
|
||||
}
|
||||
|
||||
} // namespace TestAHashMap
|
||||
|
||||
#endif // TEST_A_HASH_MAP_H
|
||||
|
|
@ -201,10 +201,10 @@ public:
|
|||
command_queue.push_and_sync(this, &SharedThreadState::func2, tr, f);
|
||||
break;
|
||||
case TEST_MSGRET_FUNC1_TRANSFORM:
|
||||
command_queue.push_and_ret(this, &SharedThreadState::func1r, tr, &otr);
|
||||
command_queue.push_and_ret(this, &SharedThreadState::func1r, &otr, tr);
|
||||
break;
|
||||
case TEST_MSGRET_FUNC2_TRANSFORM_FLOAT:
|
||||
command_queue.push_and_ret(this, &SharedThreadState::func2r, tr, f, &otr);
|
||||
command_queue.push_and_ret(this, &SharedThreadState::func2r, &otr, tr, f);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -244,6 +244,44 @@ public:
|
|||
}
|
||||
writer_thread.wait_to_finish();
|
||||
}
|
||||
|
||||
struct CopyMoveTestType {
|
||||
inline static int copy_count;
|
||||
inline static int move_count;
|
||||
int value = 0;
|
||||
|
||||
CopyMoveTestType(int p_value = 0) :
|
||||
value(p_value) {}
|
||||
|
||||
CopyMoveTestType(const CopyMoveTestType &p_other) :
|
||||
value(p_other.value) {
|
||||
copy_count++;
|
||||
}
|
||||
|
||||
CopyMoveTestType(CopyMoveTestType &&p_other) :
|
||||
value(p_other.value) {
|
||||
move_count++;
|
||||
}
|
||||
|
||||
CopyMoveTestType &operator=(const CopyMoveTestType &p_other) {
|
||||
value = p_other.value;
|
||||
copy_count++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CopyMoveTestType &operator=(CopyMoveTestType &&p_other) {
|
||||
value = p_other.value;
|
||||
move_count++;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
void copy_move_test_copy(CopyMoveTestType p_test_type) {
|
||||
}
|
||||
void copy_move_test_ref(const CopyMoveTestType &p_test_type) {
|
||||
}
|
||||
void copy_move_test_move(CopyMoveTestType &&p_test_type) {
|
||||
}
|
||||
};
|
||||
|
||||
static void test_command_queue_basic(bool p_use_thread_pool_sync) {
|
||||
|
|
@ -446,6 +484,83 @@ TEST_CASE("[Stress][CommandQueue] Stress test command queue") {
|
|||
ProjectSettings::get_singleton()->set_setting(COMMAND_QUEUE_SETTING,
|
||||
ProjectSettings::get_singleton()->property_get_revert(COMMAND_QUEUE_SETTING));
|
||||
}
|
||||
|
||||
TEST_CASE("[CommandQueue] Test Parameter Passing Semantics") {
|
||||
SharedThreadState sts;
|
||||
sts.init_threads();
|
||||
|
||||
SUBCASE("Testing with lvalue") {
|
||||
SharedThreadState::CopyMoveTestType::copy_count = 0;
|
||||
SharedThreadState::CopyMoveTestType::move_count = 0;
|
||||
|
||||
SharedThreadState::CopyMoveTestType lvalue(42);
|
||||
|
||||
SUBCASE("Pass by copy") {
|
||||
sts.command_queue.push(&sts, &SharedThreadState::copy_move_test_copy, lvalue);
|
||||
|
||||
sts.message_count_to_read = -1;
|
||||
sts.reader_threadwork.main_start_work();
|
||||
sts.reader_threadwork.main_wait_for_done();
|
||||
|
||||
CHECK(SharedThreadState::CopyMoveTestType::copy_count == 1);
|
||||
CHECK(SharedThreadState::CopyMoveTestType::move_count == 1);
|
||||
}
|
||||
|
||||
SUBCASE("Pass by reference") {
|
||||
sts.command_queue.push(&sts, &SharedThreadState::copy_move_test_ref, lvalue);
|
||||
|
||||
sts.message_count_to_read = -1;
|
||||
sts.reader_threadwork.main_start_work();
|
||||
sts.reader_threadwork.main_wait_for_done();
|
||||
|
||||
CHECK(SharedThreadState::CopyMoveTestType::copy_count == 1);
|
||||
CHECK(SharedThreadState::CopyMoveTestType::move_count == 0);
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Testing with rvalue") {
|
||||
SharedThreadState::CopyMoveTestType::copy_count = 0;
|
||||
SharedThreadState::CopyMoveTestType::move_count = 0;
|
||||
|
||||
SUBCASE("Pass by copy") {
|
||||
sts.command_queue.push(&sts, &SharedThreadState::copy_move_test_copy,
|
||||
SharedThreadState::CopyMoveTestType(43));
|
||||
|
||||
sts.message_count_to_read = -1;
|
||||
sts.reader_threadwork.main_start_work();
|
||||
sts.reader_threadwork.main_wait_for_done();
|
||||
|
||||
CHECK(SharedThreadState::CopyMoveTestType::copy_count == 0);
|
||||
CHECK(SharedThreadState::CopyMoveTestType::move_count == 2);
|
||||
}
|
||||
|
||||
SUBCASE("Pass by reference") {
|
||||
sts.command_queue.push(&sts, &SharedThreadState::copy_move_test_ref,
|
||||
SharedThreadState::CopyMoveTestType(43));
|
||||
|
||||
sts.message_count_to_read = -1;
|
||||
sts.reader_threadwork.main_start_work();
|
||||
sts.reader_threadwork.main_wait_for_done();
|
||||
|
||||
CHECK(SharedThreadState::CopyMoveTestType::copy_count == 0);
|
||||
CHECK(SharedThreadState::CopyMoveTestType::move_count == 1);
|
||||
}
|
||||
|
||||
SUBCASE("Pass by rvalue reference") {
|
||||
sts.command_queue.push(&sts, &SharedThreadState::copy_move_test_move,
|
||||
SharedThreadState::CopyMoveTestType(43));
|
||||
|
||||
sts.message_count_to_read = -1;
|
||||
sts.reader_threadwork.main_start_work();
|
||||
sts.reader_threadwork.main_wait_for_done();
|
||||
|
||||
CHECK(SharedThreadState::CopyMoveTestType::copy_count == 0);
|
||||
CHECK(SharedThreadState::CopyMoveTestType::move_count == 1);
|
||||
}
|
||||
}
|
||||
|
||||
sts.destroy_threads();
|
||||
}
|
||||
} // namespace TestCommandQueue
|
||||
|
||||
#endif // TEST_COMMAND_QUEUE_H
|
||||
|
|
|
|||
|
|
@ -37,6 +37,24 @@
|
|||
|
||||
namespace TestHashMap {
|
||||
|
||||
TEST_CASE("[HashMap] List initialization") {
|
||||
HashMap<int, String> map{ { 0, "A" }, { 1, "B" }, { 2, "C" }, { 3, "D" }, { 4, "E" } };
|
||||
|
||||
CHECK(map.size() == 5);
|
||||
CHECK(map[0] == "A");
|
||||
CHECK(map[1] == "B");
|
||||
CHECK(map[2] == "C");
|
||||
CHECK(map[3] == "D");
|
||||
CHECK(map[4] == "E");
|
||||
}
|
||||
|
||||
TEST_CASE("[HashMap] List initialization with existing elements") {
|
||||
HashMap<int, String> map{ { 0, "A" }, { 0, "B" }, { 0, "C" }, { 0, "D" }, { 0, "E" } };
|
||||
|
||||
CHECK(map.size() == 1);
|
||||
CHECK(map[0] == "E");
|
||||
}
|
||||
|
||||
TEST_CASE("[HashMap] Insert element") {
|
||||
HashMap<int, int> map;
|
||||
HashMap<int, int>::Iterator e = map.insert(42, 84);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,24 @@
|
|||
|
||||
namespace TestHashSet {
|
||||
|
||||
TEST_CASE("[HashSet] List initialization") {
|
||||
HashSet<int> set{ 0, 1, 2, 3, 4 };
|
||||
|
||||
CHECK(set.size() == 5);
|
||||
CHECK(set.has(0));
|
||||
CHECK(set.has(1));
|
||||
CHECK(set.has(2));
|
||||
CHECK(set.has(3));
|
||||
CHECK(set.has(4));
|
||||
}
|
||||
|
||||
TEST_CASE("[HashSet] List initialization with existing elements") {
|
||||
HashSet<int> set{ 0, 0, 0, 0, 0 };
|
||||
|
||||
CHECK(set.size() == 1);
|
||||
CHECK(set.has(0));
|
||||
}
|
||||
|
||||
TEST_CASE("[HashSet] Insert element") {
|
||||
HashSet<int> set;
|
||||
HashSet<int>::Iterator e = set.insert(42);
|
||||
|
|
|
|||
|
|
@ -45,6 +45,17 @@ static void populate_integers(List<int> &p_list, List<int>::Element *r_elements[
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[List] List initialization") {
|
||||
List<int> list{ 0, 1, 2, 3, 4 };
|
||||
|
||||
CHECK(list.size() == 5);
|
||||
CHECK(list.get(0) == 0);
|
||||
CHECK(list.get(1) == 1);
|
||||
CHECK(list.get(2) == 2);
|
||||
CHECK(list.get(3) == 3);
|
||||
CHECK(list.get(4) == 4);
|
||||
}
|
||||
|
||||
TEST_CASE("[List] Push/pop back") {
|
||||
List<String> list;
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,32 @@
|
|||
|
||||
namespace TestOAHashMap {
|
||||
|
||||
TEST_CASE("[OAHashMap] List initialization") {
|
||||
OAHashMap<int, String> map{ { 0, "A" }, { 1, "B" }, { 2, "C" }, { 3, "D" }, { 4, "E" } };
|
||||
|
||||
CHECK(map.get_num_elements() == 5);
|
||||
String value;
|
||||
CHECK(map.lookup(0, value));
|
||||
CHECK(value == "A");
|
||||
CHECK(map.lookup(1, value));
|
||||
CHECK(value == "B");
|
||||
CHECK(map.lookup(2, value));
|
||||
CHECK(value == "C");
|
||||
CHECK(map.lookup(3, value));
|
||||
CHECK(value == "D");
|
||||
CHECK(map.lookup(4, value));
|
||||
CHECK(value == "E");
|
||||
}
|
||||
|
||||
TEST_CASE("[OAHashMap] List initialization with existing elements") {
|
||||
OAHashMap<int, String> map{ { 0, "A" }, { 0, "B" }, { 0, "C" }, { 0, "D" }, { 0, "E" } };
|
||||
|
||||
CHECK(map.get_num_elements() == 1);
|
||||
String value;
|
||||
CHECK(map.lookup(0, value));
|
||||
CHECK(value == "E");
|
||||
}
|
||||
|
||||
TEST_CASE("[OAHashMap] Insert element") {
|
||||
OAHashMap<int, int> map;
|
||||
map.insert(42, 84);
|
||||
|
|
|
|||
|
|
@ -31,10 +31,27 @@
|
|||
#ifndef TEST_RID_H
|
||||
#define TEST_RID_H
|
||||
|
||||
#include "core/os/thread.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "core/templates/rid.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
#ifdef SANITIZERS_ENABLED
|
||||
#ifdef __has_feature
|
||||
#if __has_feature(thread_sanitizer)
|
||||
#define TSAN_ENABLED
|
||||
#endif
|
||||
#elif defined(__SANITIZE_THREAD__)
|
||||
#define TSAN_ENABLED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef TSAN_ENABLED
|
||||
#include <sanitizer/tsan_interface.h>
|
||||
#endif
|
||||
|
||||
namespace TestRID {
|
||||
TEST_CASE("[RID] Default Constructor") {
|
||||
RID rid;
|
||||
|
|
@ -96,6 +113,142 @@ TEST_CASE("[RID] 'get_local_index'") {
|
|||
CHECK(RID::from_uint64(4'294'967'295).get_local_index() == 4'294'967'295);
|
||||
CHECK(RID::from_uint64(4'294'967'297).get_local_index() == 1);
|
||||
}
|
||||
|
||||
// This case would let sanitizers realize data races.
|
||||
// Additionally, on purely weakly ordered architectures, it would detect synchronization issues
|
||||
// if RID_Alloc failed to impose proper memory ordering and the test's threads are distributed
|
||||
// among multiple L1 caches.
|
||||
TEST_CASE("[RID_Owner] Thread safety") {
|
||||
struct DataHolder {
|
||||
char data[Thread::CACHE_LINE_BYTES];
|
||||
};
|
||||
|
||||
struct RID_OwnerTester {
|
||||
uint32_t thread_count = 0;
|
||||
RID_Owner<DataHolder, true> rid_owner;
|
||||
TightLocalVector<Thread> threads;
|
||||
SafeNumeric<uint32_t> next_thread_idx;
|
||||
// Using std::atomic directly since SafeNumeric doesn't support relaxed ordering.
|
||||
TightLocalVector<std::atomic<uint64_t>> rids;
|
||||
std::atomic<uint32_t> sync[2] = {};
|
||||
std::atomic<uint32_t> correct = 0;
|
||||
|
||||
// A barrier that doesn't introduce memory ordering constraints, only compiler ones.
|
||||
// The idea is not to cause any sync traffic that would make the code we want to test
|
||||
// seem correct as a side effect.
|
||||
void lockstep(uint32_t p_step) {
|
||||
uint32_t buf_idx = p_step % 2;
|
||||
uint32_t target = (p_step / 2 + 1) * threads.size();
|
||||
sync[buf_idx].fetch_add(1, std::memory_order_relaxed);
|
||||
do {
|
||||
std::this_thread::yield();
|
||||
} while (sync[buf_idx].load(std::memory_order_relaxed) != target);
|
||||
}
|
||||
|
||||
explicit RID_OwnerTester(bool p_chunk_for_all, bool p_chunks_preallocated) :
|
||||
thread_count(OS::get_singleton()->get_processor_count()),
|
||||
rid_owner(sizeof(DataHolder) * (p_chunk_for_all ? thread_count : 1)) {
|
||||
threads.resize(thread_count);
|
||||
rids.resize(threads.size());
|
||||
if (p_chunks_preallocated) {
|
||||
LocalVector<RID> prealloc_rids;
|
||||
for (uint32_t i = 0; i < (p_chunk_for_all ? 1 : threads.size()); i++) {
|
||||
prealloc_rids.push_back(rid_owner.make_rid());
|
||||
}
|
||||
for (uint32_t i = 0; i < prealloc_rids.size(); i++) {
|
||||
rid_owner.free(prealloc_rids[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~RID_OwnerTester() {
|
||||
for (uint32_t i = 0; i < threads.size(); i++) {
|
||||
rid_owner.free(RID::from_uint64(rids[i].load(std::memory_order_relaxed)));
|
||||
}
|
||||
}
|
||||
|
||||
void test() {
|
||||
for (uint32_t i = 0; i < threads.size(); i++) {
|
||||
threads[i].start(
|
||||
[](void *p_data) {
|
||||
RID_OwnerTester *rot = (RID_OwnerTester *)p_data;
|
||||
|
||||
auto _compute_thread_unique_byte = [](uint32_t p_idx) -> char {
|
||||
return ((p_idx & 0xff) ^ (0b11111110 << (p_idx % 8)));
|
||||
};
|
||||
|
||||
// 1. Each thread gets a zero-based index.
|
||||
uint32_t self_th_idx = rot->next_thread_idx.postincrement();
|
||||
|
||||
rot->lockstep(0);
|
||||
|
||||
// 2. Each thread makes a RID holding unique data.
|
||||
DataHolder initial_data;
|
||||
memset(&initial_data, _compute_thread_unique_byte(self_th_idx), Thread::CACHE_LINE_BYTES);
|
||||
RID my_rid = rot->rid_owner.make_rid(initial_data);
|
||||
rot->rids[self_th_idx].store(my_rid.get_id(), std::memory_order_relaxed);
|
||||
|
||||
rot->lockstep(1);
|
||||
|
||||
// 3. Each thread verifies all the others.
|
||||
uint32_t local_correct = 0;
|
||||
for (uint32_t th_idx = 0; th_idx < rot->threads.size(); th_idx++) {
|
||||
if (th_idx == self_th_idx) {
|
||||
continue;
|
||||
}
|
||||
char expected_unique_byte = _compute_thread_unique_byte(th_idx);
|
||||
RID rid = RID::from_uint64(rot->rids[th_idx].load(std::memory_order_relaxed));
|
||||
DataHolder *data = rot->rid_owner.get_or_null(rid);
|
||||
#ifdef TSAN_ENABLED
|
||||
__tsan_acquire(data); // We know not a race in practice.
|
||||
#endif
|
||||
bool ok = true;
|
||||
for (uint32_t j = 0; j < Thread::CACHE_LINE_BYTES; j++) {
|
||||
if (data->data[j] != expected_unique_byte) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
local_correct++;
|
||||
}
|
||||
#ifdef TSAN_ENABLED
|
||||
__tsan_release(data);
|
||||
#endif
|
||||
}
|
||||
|
||||
rot->lockstep(2);
|
||||
|
||||
rot->correct.fetch_add(local_correct, std::memory_order_acq_rel);
|
||||
},
|
||||
this);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < threads.size(); i++) {
|
||||
threads[i].wait_to_finish();
|
||||
}
|
||||
|
||||
CHECK_EQ(correct.load(), threads.size() * (threads.size() - 1));
|
||||
}
|
||||
};
|
||||
|
||||
SUBCASE("All items in one chunk, pre-allocated") {
|
||||
RID_OwnerTester tester(true, true);
|
||||
tester.test();
|
||||
}
|
||||
SUBCASE("All items in one chunk, NOT pre-allocated") {
|
||||
RID_OwnerTester tester(true, false);
|
||||
tester.test();
|
||||
}
|
||||
SUBCASE("One item per chunk, pre-allocated") {
|
||||
RID_OwnerTester tester(false, true);
|
||||
tester.test();
|
||||
}
|
||||
SUBCASE("One item per chunk, NOT pre-allocated") {
|
||||
RID_OwnerTester tester(false, false);
|
||||
tester.test();
|
||||
}
|
||||
}
|
||||
} // namespace TestRID
|
||||
|
||||
#endif // TEST_RID_H
|
||||
|
|
|
|||
|
|
@ -215,6 +215,154 @@ TEST_CASE("[Vector] Get, set") {
|
|||
CHECK(vector.get(4) == 4);
|
||||
}
|
||||
|
||||
TEST_CASE("[Vector] To byte array (variant call)") {
|
||||
// PackedInt32Array.
|
||||
{
|
||||
PackedInt32Array vector[] = { { 0, -1, 2008 }, {} };
|
||||
PackedByteArray out[] = { { /* 0 */ 0x00, 0x00, 0x00, 0x00, /* -1 */ 0xFF, 0xFF, 0xFF, 0xFF, /* 2008 */ 0xD8, 0x07, 0x00, 0x00 }, {} };
|
||||
|
||||
for (size_t i = 0; i < std::size(vector); i++) {
|
||||
Callable::CallError err;
|
||||
Variant v_ret;
|
||||
Variant v_vector = vector[i];
|
||||
v_vector.callp("to_byte_array", nullptr, 0, v_ret, err);
|
||||
CHECK(v_ret.get_type() == Variant::PACKED_BYTE_ARRAY);
|
||||
CHECK(v_ret.operator PackedByteArray() == out[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// PackedInt64Array.
|
||||
{
|
||||
PackedInt64Array vector[] = { { 0, -1, 2008 }, {} };
|
||||
PackedByteArray out[] = { { /* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* -1 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 2008 */ 0xD8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, {} };
|
||||
|
||||
for (size_t i = 0; i < std::size(vector); i++) {
|
||||
Callable::CallError err;
|
||||
Variant v_ret;
|
||||
Variant v_vector = vector[i];
|
||||
v_vector.callp("to_byte_array", nullptr, 0, v_ret, err);
|
||||
CHECK(v_ret.get_type() == Variant::PACKED_BYTE_ARRAY);
|
||||
CHECK(v_ret.operator PackedByteArray() == out[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// PackedFloat32Array.
|
||||
{
|
||||
PackedFloat32Array vector[] = { { 0.0, -1.0, 200e24 }, {} };
|
||||
PackedByteArray out[] = { { /* 0.0 */ 0x00, 0x00, 0x00, 0x00, /* -1.0 */ 0x00, 0x00, 0x80, 0xBF, /* 200e24 */ 0xA6, 0x6F, 0x25, 0x6B }, {} };
|
||||
|
||||
for (size_t i = 0; i < std::size(vector); i++) {
|
||||
Callable::CallError err;
|
||||
Variant v_ret;
|
||||
Variant v_vector = vector[i];
|
||||
v_vector.callp("to_byte_array", nullptr, 0, v_ret, err);
|
||||
CHECK(v_ret.get_type() == Variant::PACKED_BYTE_ARRAY);
|
||||
CHECK(v_ret.operator PackedByteArray() == out[i]);
|
||||
}
|
||||
}
|
||||
// PackedFloat64Array.
|
||||
{
|
||||
PackedFloat64Array vector[] = { { 0.0, -1.0, 200e24 }, {} };
|
||||
PackedByteArray out[] = { { /* 0.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* -1.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xBF, /* 200e24 */ 0x35, 0x03, 0x32, 0xB7, 0xF4, 0xAD, 0x64, 0x45 }, {} };
|
||||
|
||||
for (size_t i = 0; i < std::size(vector); i++) {
|
||||
Callable::CallError err;
|
||||
Variant v_ret;
|
||||
Variant v_vector = vector[i];
|
||||
v_vector.callp("to_byte_array", nullptr, 0, v_ret, err);
|
||||
CHECK(v_ret.get_type() == Variant::PACKED_BYTE_ARRAY);
|
||||
CHECK(v_ret.operator PackedByteArray() == out[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// PackedStringArray.
|
||||
{
|
||||
PackedStringArray vector[] = { { "test", "string" }, {}, { "", "test" } };
|
||||
PackedByteArray out[] = { { /* test */ 0x74, 0x65, 0x73, 0x74, /* null */ 0x00, /* string */ 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, /* null */ 0x00 }, {}, { /* null */ 0x00, /* test */ 0x74, 0x65, 0x73, 0x74, /* null */ 0x00 } };
|
||||
|
||||
for (size_t i = 0; i < std::size(vector); i++) {
|
||||
Callable::CallError err;
|
||||
Variant v_ret;
|
||||
Variant v_vector = vector[i];
|
||||
v_vector.callp("to_byte_array", nullptr, 0, v_ret, err);
|
||||
CHECK(v_ret.get_type() == Variant::PACKED_BYTE_ARRAY);
|
||||
CHECK(v_ret.operator PackedByteArray() == out[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// PackedVector2Array.
|
||||
{
|
||||
PackedVector2Array vector[] = { { Vector2(), Vector2(1, -1) }, {} };
|
||||
#ifdef REAL_T_IS_DOUBLE
|
||||
PackedByteArray out[] = { { /* X=0.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Y=0.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* X=1.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, /* Y=-1.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xBF }, {} };
|
||||
#else
|
||||
PackedByteArray out[] = { { /* X=0.0 */ 0x00, 0x00, 0x00, 0x00, /* Y=0.0 */ 0x00, 0x00, 0x00, 0x00, /* X=1.0 */ 0x00, 0x00, 0x80, 0x3F, /* Y=-1.0 */ 0x00, 0x00, 0x80, 0xBF }, {} };
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < std::size(vector); i++) {
|
||||
Callable::CallError err;
|
||||
Variant v_ret;
|
||||
Variant v_vector = vector[i];
|
||||
v_vector.callp("to_byte_array", nullptr, 0, v_ret, err);
|
||||
CHECK(v_ret.get_type() == Variant::PACKED_BYTE_ARRAY);
|
||||
CHECK(v_ret.operator PackedByteArray() == out[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// PackedVector3Array.
|
||||
{
|
||||
PackedVector3Array vector[] = { { Vector3(), Vector3(1, 1, -1) }, {} };
|
||||
#ifdef REAL_T_IS_DOUBLE
|
||||
PackedByteArray out[] = { { /* X=0.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Y=0.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Z=0.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* X=1.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, /* Y=1.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, /* Z=-1.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xBF }, {} };
|
||||
#else
|
||||
PackedByteArray out[] = { { /* X=0.0 */ 0x00, 0x00, 0x00, 0x00, /* Y=0.0 */ 0x00, 0x00, 0x00, 0x00, /* Z=0.0 */ 0x00, 0x00, 0x00, 0x00, /* X=1.0 */ 0x00, 0x00, 0x80, 0x3F, /* Y=1.0 */ 0x00, 0x00, 0x80, 0x3F, /* Z=-1.0 */ 0x00, 0x00, 0x80, 0xBF }, {} };
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < std::size(vector); i++) {
|
||||
Callable::CallError err;
|
||||
Variant v_ret;
|
||||
Variant v_vector = vector[i];
|
||||
v_vector.callp("to_byte_array", nullptr, 0, v_ret, err);
|
||||
CHECK(v_ret.get_type() == Variant::PACKED_BYTE_ARRAY);
|
||||
CHECK(v_ret.operator PackedByteArray() == out[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// PackedColorArray.
|
||||
{
|
||||
PackedColorArray vector[] = { { Color(), Color(1, 1, 1) }, {} };
|
||||
PackedByteArray out[] = { { /* R=0.0 */ 0x00, 0x00, 0x00, 0x00, /* G=0.0 */ 0x00, 0x00, 0x00, 0x00, /* B=0.0 */ 0x00, 0x00, 0x00, 0x00, /* A=1.0 */ 0x00, 0x00, 0x80, 0x3F, /* R=1.0 */ 0x00, 0x00, 0x80, 0x3F, /* G=1.0 */ 0x00, 0x00, 0x80, 0x3F, /* B=1.0 */ 0x00, 0x00, 0x80, 0x3F, /* A=1.0 */ 0x00, 0x00, 0x80, 0x3F }, {} };
|
||||
|
||||
for (size_t i = 0; i < std::size(vector); i++) {
|
||||
Callable::CallError err;
|
||||
Variant v_ret;
|
||||
Variant v_vector = vector[i];
|
||||
v_vector.callp("to_byte_array", nullptr, 0, v_ret, err);
|
||||
CHECK(v_ret.get_type() == Variant::PACKED_BYTE_ARRAY);
|
||||
CHECK(v_ret.operator PackedByteArray() == out[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// PackedVector4Array.
|
||||
{
|
||||
PackedVector4Array vector[] = { { Vector4(), Vector4(1, -1, 1, -1) }, {} };
|
||||
#ifdef REAL_T_IS_DOUBLE
|
||||
PackedByteArray out[] = { { /* X=0.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Y=0.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Z 0.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* W=0.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* X=1.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, /* Y=-1.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xBF, /* Z=1.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F, /* W=-1.0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xBF }, {} };
|
||||
#else
|
||||
PackedByteArray out[] = { { /* X=0.0 */ 0x00, 0x00, 0x00, 0x00, /* Y=0.0 */ 0x00, 0x00, 0x00, 0x00, /* Z=0.0 */ 0x00, 0x00, 0x00, 0x00, /* W 0.0 */ 0x00, 0x00, 0x00, 0x00, /* X 1.0 */ 0x00, 0x00, 0x80, 0x3F, /* Y=-1.0 */ 0x00, 0x00, 0x80, 0xBF, /* Z=1.0 */ 0x00, 0x00, 0x80, 0x3F, /* W=-1.0 */ 0x00, 0x00, 0x80, 0xBF }, {} };
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < std::size(vector); i++) {
|
||||
Callable::CallError err;
|
||||
Variant v_ret;
|
||||
Variant v_vector = vector[i];
|
||||
v_vector.callp("to_byte_array", nullptr, 0, v_ret, err);
|
||||
CHECK(v_ret.get_type() == Variant::PACKED_BYTE_ARRAY);
|
||||
CHECK(v_ret.operator PackedByteArray() == out[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[Vector] To byte array") {
|
||||
Vector<int> vector;
|
||||
vector.push_back(0);
|
||||
|
|
@ -532,6 +680,31 @@ TEST_CASE("[Vector] Operators") {
|
|||
CHECK(vector != vector_other);
|
||||
}
|
||||
|
||||
struct CyclicVectorHolder {
|
||||
Vector<CyclicVectorHolder> *vector = nullptr;
|
||||
bool is_destructing = false;
|
||||
|
||||
~CyclicVectorHolder() {
|
||||
if (is_destructing) {
|
||||
// The vector must exist and not expose its backing array at this point.
|
||||
CHECK_NE(vector, nullptr);
|
||||
CHECK_EQ(vector->ptr(), nullptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("[Vector] Cyclic Reference") {
|
||||
// Create a stack-space vector.
|
||||
Vector<CyclicVectorHolder> vector;
|
||||
// Add a new (empty) element.
|
||||
vector.resize(1);
|
||||
// Expose the vector to its element through CyclicVectorHolder.
|
||||
// This is questionable behavior, but should still behave graciously.
|
||||
vector.ptrw()[0] = CyclicVectorHolder{ &vector };
|
||||
vector.ptrw()[0].is_destructing = true;
|
||||
// The vector goes out of scope and destructs, calling CyclicVectorHolder's destructor.
|
||||
}
|
||||
|
||||
} // namespace TestVector
|
||||
|
||||
#endif // TEST_VECTOR_H
|
||||
|
|
|
|||
|
|
@ -634,6 +634,24 @@ TEST_CASE("[Array] Typed copying") {
|
|||
a6.clear();
|
||||
}
|
||||
|
||||
static bool _find_custom_callable(const Variant &p_val) {
|
||||
return (int)p_val % 2 == 0;
|
||||
}
|
||||
|
||||
TEST_CASE("[Array] Test find_custom") {
|
||||
Array a1 = build_array(1, 3, 4, 5, 8, 9);
|
||||
// Find first even number.
|
||||
int index = a1.find_custom(callable_mp_static(_find_custom_callable));
|
||||
CHECK_EQ(index, 2);
|
||||
}
|
||||
|
||||
TEST_CASE("[Array] Test rfind_custom") {
|
||||
Array a1 = build_array(1, 3, 4, 5, 8, 9);
|
||||
// Find last even number.
|
||||
int index = a1.rfind_custom(callable_mp_static(_find_custom_callable));
|
||||
CHECK_EQ(index, 4);
|
||||
}
|
||||
|
||||
} // namespace TestArray
|
||||
|
||||
#endif // TEST_ARRAY_H
|
||||
|
|
|
|||
|
|
@ -135,6 +135,70 @@ TEST_CASE("[Callable] Argument count") {
|
|||
|
||||
memdelete(my_test);
|
||||
}
|
||||
|
||||
class TestBoundUnboundArgumentCount : public Object {
|
||||
GDCLASS(TestBoundUnboundArgumentCount, Object);
|
||||
|
||||
protected:
|
||||
static void _bind_methods() {
|
||||
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "test_func", &TestBoundUnboundArgumentCount::test_func, MethodInfo("test_func"));
|
||||
}
|
||||
|
||||
public:
|
||||
Variant test_func(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
|
||||
Array result;
|
||||
result.resize(p_argcount);
|
||||
for (int i = 0; i < p_argcount; i++) {
|
||||
result[i] = *p_args[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static String get_output(const Callable &p_callable) {
|
||||
Array effective_args;
|
||||
effective_args.push_back(7);
|
||||
effective_args.push_back(8);
|
||||
effective_args.push_back(9);
|
||||
|
||||
effective_args.resize(3 - p_callable.get_unbound_arguments_count());
|
||||
effective_args.append_array(p_callable.get_bound_arguments());
|
||||
|
||||
return vformat(
|
||||
"%d %d %s %s %s",
|
||||
p_callable.get_unbound_arguments_count(),
|
||||
p_callable.get_bound_arguments_count(),
|
||||
p_callable.get_bound_arguments(),
|
||||
p_callable.call(7, 8, 9),
|
||||
effective_args);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("[Callable] Bound and unbound argument count") {
|
||||
String (*get_output)(const Callable &) = TestBoundUnboundArgumentCount::get_output;
|
||||
|
||||
TestBoundUnboundArgumentCount *test_instance = memnew(TestBoundUnboundArgumentCount);
|
||||
|
||||
Callable test_func = Callable(test_instance, "test_func");
|
||||
|
||||
CHECK(get_output(test_func) == "0 0 [] [7, 8, 9] [7, 8, 9]");
|
||||
CHECK(get_output(test_func.bind(1, 2)) == "0 2 [1, 2] [7, 8, 9, 1, 2] [7, 8, 9, 1, 2]");
|
||||
CHECK(get_output(test_func.bind(1, 2).unbind(1)) == "1 2 [1, 2] [7, 8, 1, 2] [7, 8, 1, 2]");
|
||||
CHECK(get_output(test_func.bind(1, 2).unbind(1).bind(3, 4)) == "0 3 [3, 1, 2] [7, 8, 9, 3, 1, 2] [7, 8, 9, 3, 1, 2]");
|
||||
CHECK(get_output(test_func.bind(1, 2).unbind(1).bind(3, 4).unbind(1)) == "1 3 [3, 1, 2] [7, 8, 3, 1, 2] [7, 8, 3, 1, 2]");
|
||||
|
||||
CHECK(get_output(test_func.bind(1).bind(2).bind(3).unbind(1)) == "1 3 [3, 2, 1] [7, 8, 3, 2, 1] [7, 8, 3, 2, 1]");
|
||||
CHECK(get_output(test_func.bind(1).bind(2).unbind(1).bind(3)) == "0 2 [2, 1] [7, 8, 9, 2, 1] [7, 8, 9, 2, 1]");
|
||||
CHECK(get_output(test_func.bind(1).unbind(1).bind(2).bind(3)) == "0 2 [3, 1] [7, 8, 9, 3, 1] [7, 8, 9, 3, 1]");
|
||||
CHECK(get_output(test_func.unbind(1).bind(1).bind(2).bind(3)) == "0 2 [3, 2] [7, 8, 9, 3, 2] [7, 8, 9, 3, 2]");
|
||||
|
||||
CHECK(get_output(test_func.unbind(1).unbind(1).unbind(1).bind(1, 2, 3)) == "0 0 [] [7, 8, 9] [7, 8, 9]");
|
||||
CHECK(get_output(test_func.unbind(1).unbind(1).bind(1, 2, 3).unbind(1)) == "1 1 [1] [7, 8, 1] [7, 8, 1]");
|
||||
CHECK(get_output(test_func.unbind(1).bind(1, 2, 3).unbind(1).unbind(1)) == "2 2 [1, 2] [7, 1, 2] [7, 1, 2]");
|
||||
CHECK(get_output(test_func.bind(1, 2, 3).unbind(1).unbind(1).unbind(1)) == "3 3 [1, 2, 3] [1, 2, 3] [1, 2, 3]");
|
||||
|
||||
memdelete(test_instance);
|
||||
}
|
||||
|
||||
} // namespace TestCallable
|
||||
|
||||
#endif // TEST_CALLABLE_H
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
#ifndef TEST_DICTIONARY_H
|
||||
#define TEST_DICTIONARY_H
|
||||
|
||||
#include "core/variant/dictionary.h"
|
||||
#include "core/variant/typed_dictionary.h"
|
||||
#include "tests/test_macros.h"
|
||||
|
||||
namespace TestDictionary {
|
||||
|
|
@ -66,8 +66,7 @@ TEST_CASE("[Dictionary] Assignment using bracket notation ([])") {
|
|||
|
||||
map[StringName("HelloName")] = 6;
|
||||
CHECK(int(map[StringName("HelloName")]) == 6);
|
||||
// Check that StringName key is converted to String.
|
||||
CHECK(int(map.find_key(6).get_type()) == Variant::STRING);
|
||||
CHECK(int(map.find_key(6).get_type()) == Variant::STRING_NAME);
|
||||
map[StringName("HelloName")] = 7;
|
||||
CHECK(int(map[StringName("HelloName")]) == 7);
|
||||
|
||||
|
|
@ -96,6 +95,27 @@ TEST_CASE("[Dictionary] Assignment using bracket notation ([])") {
|
|||
CHECK(map.size() == length);
|
||||
}
|
||||
|
||||
TEST_CASE("[Dictionary] List init") {
|
||||
Dictionary dict{
|
||||
{ 0, "int" },
|
||||
{ "packed_string_array", PackedStringArray({ "array", "of", "values" }) },
|
||||
{ "key", Dictionary({ { "nested", 200 } }) },
|
||||
{ Vector2(), "v2" },
|
||||
};
|
||||
CHECK(dict.size() == 4);
|
||||
CHECK(dict[0] == "int");
|
||||
CHECK(PackedStringArray(dict["packed_string_array"])[2] == "values");
|
||||
CHECK(Dictionary(dict["key"])["nested"] == Variant(200));
|
||||
CHECK(dict[Vector2()] == "v2");
|
||||
|
||||
TypedDictionary<double, double> tdict{
|
||||
{ 0.0, 1.0 },
|
||||
{ 5.0, 2.0 },
|
||||
};
|
||||
CHECK_EQ(tdict[0.0], Variant(1.0));
|
||||
CHECK_EQ(tdict[5.0], Variant(2.0));
|
||||
}
|
||||
|
||||
TEST_CASE("[Dictionary] get_key_lists()") {
|
||||
Dictionary map;
|
||||
List<Variant> keys;
|
||||
|
|
@ -537,6 +557,43 @@ TEST_CASE("[Dictionary] Order and find") {
|
|||
CHECK_EQ(d.find_key("does not exist"), Variant());
|
||||
}
|
||||
|
||||
TEST_CASE("[Dictionary] Typed copying") {
|
||||
TypedDictionary<int, int> d1;
|
||||
d1[0] = 1;
|
||||
|
||||
TypedDictionary<double, double> d2;
|
||||
d2[0] = 1.0;
|
||||
|
||||
Dictionary d3 = d1;
|
||||
TypedDictionary<int, int> d4 = d3;
|
||||
|
||||
Dictionary d5 = d2;
|
||||
TypedDictionary<int, int> d6 = d5;
|
||||
|
||||
d3[0] = 2;
|
||||
d4[0] = 3;
|
||||
|
||||
// Same typed TypedDictionary should be shared.
|
||||
CHECK_EQ(d1[0], Variant(3));
|
||||
CHECK_EQ(d3[0], Variant(3));
|
||||
CHECK_EQ(d4[0], Variant(3));
|
||||
|
||||
d5[0] = 2.0;
|
||||
d6[0] = 3.0;
|
||||
|
||||
// Different typed TypedDictionary should not be shared.
|
||||
CHECK_EQ(d2[0], Variant(2.0));
|
||||
CHECK_EQ(d5[0], Variant(2.0));
|
||||
CHECK_EQ(d6[0], Variant(3.0));
|
||||
|
||||
d1.clear();
|
||||
d2.clear();
|
||||
d3.clear();
|
||||
d4.clear();
|
||||
d5.clear();
|
||||
d6.clear();
|
||||
}
|
||||
|
||||
} // namespace TestDictionary
|
||||
|
||||
#endif // TEST_DICTIONARY_H
|
||||
|
|
|
|||
|
|
@ -1806,6 +1806,14 @@ TEST_CASE("[Variant] Writer and parser dictionary") {
|
|||
CHECK_MESSAGE(d_parsed == Variant(d), "Should parse back.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Variant] Writer key sorting") {
|
||||
Dictionary d = build_dictionary(StringName("C"), 3, "A", 1, StringName("B"), 2, "D", 4);
|
||||
String d_str;
|
||||
VariantWriter::write_to_string(d, d_str);
|
||||
|
||||
CHECK_EQ(d_str, "{\n\"A\": 1,\n&\"B\": 2,\n&\"C\": 3,\n\"D\": 4\n}");
|
||||
}
|
||||
|
||||
TEST_CASE("[Variant] Writer recursive dictionary") {
|
||||
// There is no way to accurately represent a recursive dictionary,
|
||||
// the only thing we can do is make sure the writer doesn't blow up
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ TEST_CASE("[VariantUtility] Type conversion") {
|
|||
|
||||
converted = VariantUtilityFunctions::type_convert(basis, Variant::Type::STRING);
|
||||
CHECK(converted.get_type() == Variant::Type::STRING);
|
||||
CHECK(converted == Variant("[X: (1.2, 0, 0), Y: (0, 3.4, 0), Z: (0, 0, 5.6)]"));
|
||||
CHECK(converted == Variant("[X: (1.2, 0.0, 0.0), Y: (0.0, 3.4, 0.0), Z: (0.0, 0.0, 5.6)]"));
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,12 +13,16 @@ def main():
|
|||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
|
||||
parser = argparse.ArgumentParser(description="Creates a new unit test file.")
|
||||
parser.add_argument("name", type=str, help="The unit test name in PascalCase notation")
|
||||
parser.add_argument(
|
||||
"name",
|
||||
type=str,
|
||||
help="Specifies the class or component name to be tested, in PascalCase (e.g., MeshInstance3D). The name will be prefixed with 'test_' for the header file and 'Test' for the namespace.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"path",
|
||||
type=str,
|
||||
nargs="?",
|
||||
help="The path to the unit test file relative to the tests folder (default: .)",
|
||||
help="The path to the unit test file relative to the tests folder (e.g. core). This should correspond to the relative path of the class or component being tested. (default: .)",
|
||||
default=".",
|
||||
)
|
||||
parser.add_argument(
|
||||
|
|
@ -29,11 +33,15 @@ def main():
|
|||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
snake_case_regex = re.compile(r"(?<!^)(?=[A-Z])")
|
||||
name_snake_case = snake_case_regex.sub("_", args.name).lower()
|
||||
|
||||
snake_case_regex = re.compile(r"(?<!^)(?=[A-Z, 0-9])")
|
||||
# Replace 2D, 3D, and 4D with 2d, 3d, and 4d, respectively. This avoids undesired splits like node_3_d.
|
||||
prefiltered_name = re.sub(r"([234])D", lambda match: match.group(1).lower() + "d", args.name)
|
||||
name_snake_case = snake_case_regex.sub("_", prefiltered_name).lower()
|
||||
file_path = os.path.normpath(os.path.join(args.path, f"test_{name_snake_case}.h"))
|
||||
|
||||
# Ensure the directory exists.
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
|
||||
print(file_path)
|
||||
if os.path.isfile(file_path):
|
||||
print(f'ERROR: The file "{file_path}" already exists.')
|
||||
|
|
|
|||
1
engine/tests/data/floating_point_big_endian.bin
Normal file
1
engine/tests/data/floating_point_big_endian.bin
Normal file
|
|
@ -0,0 +1 @@
|
|||
@IV
|
||||
1
engine/tests/data/floating_point_little_endian.bin
Normal file
1
engine/tests/data/floating_point_little_endian.bin
Normal file
|
|
@ -0,0 +1 @@
|
|||
VI@
|
||||
999
engine/tests/data/fuzzy_search/project_dir_tree.txt
Normal file
999
engine/tests/data/fuzzy_search/project_dir_tree.txt
Normal file
|
|
@ -0,0 +1,999 @@
|
|||
./menu/home/home_menu.tscn
|
||||
./menu/tooltips/tooltip_server.tscn
|
||||
./menu/tooltips/tooltip_server.gd
|
||||
./menu/tooltips/tooltip.gd
|
||||
./menu/menu/characters/smoker/4.wav
|
||||
./menu/menu/characters/smoker/6.wav
|
||||
./menu/menu/characters/smoker/10.wav
|
||||
./menu/menu/characters/smoker/smoker.tscn
|
||||
./menu/menu/characters/smoker/8.wav
|
||||
./menu/menu/characters/smoker/type.gd
|
||||
./menu/menu/characters/smoker/9.wav
|
||||
./menu/menu/characters/smoker/5.wav
|
||||
./menu/menu/characters/smoker/0.wav
|
||||
./menu/menu/characters/smoker/back_light.png
|
||||
./menu/menu/characters/smoker/glasses.png
|
||||
./menu/menu/characters/smoker/smoker.gd
|
||||
./menu/menu/characters/smoker/cig.gd
|
||||
./menu/menu/characters/smoker/eyes.png
|
||||
./menu/menu/characters/smoker/3.wav
|
||||
./menu/menu/characters/smoker/to_pixelate.gd
|
||||
./menu/menu/characters/smoker/7.wav
|
||||
./menu/menu/characters/smoker/cig.png
|
||||
./menu/menu/characters/smoker/2.wav
|
||||
./menu/menu/characters/smoker/1.wav
|
||||
./menu/menu/characters/smoke.png
|
||||
./menu/menu/characters/space_bandit.tres
|
||||
./menu/menu/characters/dead_guy/blood_texture.png
|
||||
./menu/menu/characters/dead_guy/head_gibbed.png
|
||||
./menu/menu/characters/dead_guy/back_light.png
|
||||
./menu/menu/characters/dead_guy/smoker.gd
|
||||
./menu/menu/characters/dead_guy/eyes.png
|
||||
./menu/menu/characters/dead_guy/to_pixelate.gd
|
||||
./menu/menu/characters/dead_guy/dead_guy.gd
|
||||
./menu/menu/characters/dead_guy/eyes.gd
|
||||
./menu/menu/characters/dead_guy/x.png
|
||||
./menu/menu/characters/dead_guy/dead_guy.tscn
|
||||
./menu/menu/characters/dead_guy/mouth.png
|
||||
./menu/menu/characters/dead_guy/dead_guy.tres
|
||||
./menu/menu/characters/Label.gd
|
||||
./menu/menu/characters/guns2.png
|
||||
./menu/menu/characters/c.gd
|
||||
./menu/menu/characters/smoke.gd
|
||||
./menu/menu/characters/character.gd
|
||||
./menu/menu/characters/space_bandit/eyes.tres
|
||||
./menu/menu/characters/space_bandit/space_bandit_face_happy.png
|
||||
./menu/menu/characters/space_bandit/space_bandit.gd
|
||||
./menu/menu/characters/space_bandit/space_bandit.tscn
|
||||
./menu/menu/characters/boss/smoker.tscn
|
||||
./menu/menu/characters/boss/back_light.png
|
||||
./menu/menu/characters/boss/glasses.png
|
||||
./menu/menu/characters/boss/smoker.gd
|
||||
./menu/menu/characters/boss/cig.gd
|
||||
./menu/menu/characters/boss/eyes.png
|
||||
./menu/menu/characters/boss/to_pixelate.gd
|
||||
./menu/menu/characters/boss/x.png
|
||||
./menu/menu/characters/boss/cig.png
|
||||
./menu/menu/characters/eye.gd
|
||||
./menu/menu/characters/space_bandit_face_happy.png
|
||||
./menu/menu/characters/face.gd
|
||||
./menu/menu/characters/color.tres
|
||||
./menu/menu/characters/space_bandit.tscn
|
||||
./menu/menu/characters/space_bandit_face_bloody.png
|
||||
./menu/menu/characters/guns.png
|
||||
./menu/menu/characters/eyes2.tres
|
||||
./menu/options/controls/use.tres
|
||||
./menu/options/controls/input_map_button.gd
|
||||
./menu/options/controls/swap.tres
|
||||
./menu/options/controls/teleport.tres
|
||||
./menu/options/controls/joy_controls.tscn
|
||||
./menu/options/controls/mouse_and_keyboard_controls.tscn
|
||||
./menu/options/controls/input_map_button.tscn
|
||||
./menu/options/controls/special.tres
|
||||
./menu/options/controls/throw.tres
|
||||
./menu/options/controls/center.tres
|
||||
./menu/options/controls/input_action.gd
|
||||
./menu/options/controls/move.tres
|
||||
./menu/options/controls/melee.tres
|
||||
./menu/options/controls/controls.gd
|
||||
./menu/options/options.gd
|
||||
./menu/options/options.tscn
|
||||
./menu/options/graphics/graphics.tscn
|
||||
./menu/options/graphics/graphics.gd
|
||||
./menu/options/audio/audio.gd
|
||||
./menu/options/audio/audio.tscn
|
||||
./menu/options/game/game.gd
|
||||
./menu/options/game/game.tscn
|
||||
./menu/circle.tres
|
||||
./menu/fonts/keys.png
|
||||
./menu/fonts/rainbow_font.tres
|
||||
./menu/fonts/fallback_font.tres
|
||||
./menu/fonts/taxi_Driver.png
|
||||
./menu/fonts/NotoSansJP-Regular.ttf
|
||||
./menu/fonts/taxi_Driver_noise.png
|
||||
./menu/fonts/rainbow_font_shader.tres
|
||||
./menu/fonts/m5x7.ttf
|
||||
./menu/colors.gd
|
||||
./menu/toast_enter.wav
|
||||
./menu/ui_colors.tres
|
||||
./menu/pause/pause.gd
|
||||
./menu/pause/rainbow.tres
|
||||
./menu/pause/Label.gd
|
||||
./menu/pause/label.tscn
|
||||
./menu/pause/pause.tscn
|
||||
./menu/hoola.wav
|
||||
./menu/in_game_fallback.tres
|
||||
./menu/widgets/next_unlock.gd
|
||||
./menu/widgets/slider.gd
|
||||
./menu/widgets/fade.tscn
|
||||
./menu/widgets/background_hint.gd
|
||||
./menu/widgets/panel_container_smoke.gd
|
||||
./menu/widgets/wishlist_sticker.gd
|
||||
./menu/widgets/smoke.tres
|
||||
./menu/widgets/color_grade.gd
|
||||
./menu/widgets/rich_text_button.gd
|
||||
./menu/widgets/panel_container_smok2.tscn
|
||||
./menu/widgets/slider.tscn
|
||||
./menu/widgets/rich_text_heading.gd
|
||||
./menu/widgets/background_hint.tscn
|
||||
./menu/widgets/tip.tscn
|
||||
./menu/widgets/rich_text_button.tscn
|
||||
./menu/widgets/toggle.tscn
|
||||
./menu/widgets/heading.tscn
|
||||
./menu/widgets/hover.tscn
|
||||
./menu/widgets/toggle.gd
|
||||
./menu/widgets/smoke_panel_material.tres
|
||||
./menu/widgets/confirm.gd
|
||||
./menu/widgets/tip.gd
|
||||
./menu/widgets/panel.gd
|
||||
./menu/widgets/modal.gd
|
||||
./menu/widgets/NinePatchRect.gd
|
||||
./menu/widgets/smoke.shader
|
||||
./menu/widgets/9patch.png
|
||||
./menu/widgets/big_hint.gd
|
||||
./menu/widgets/TDVB1i.png
|
||||
./menu/widgets/color_grade.tscn
|
||||
./menu/widgets/text.gd
|
||||
./menu/widgets/panel_container_smoke.tscn
|
||||
./menu/widgets/1x1.png
|
||||
./menu/widgets/confirm.tscn
|
||||
./menu/widgets/RichTextPanel.tscn
|
||||
./menu/hud/cursor.png
|
||||
./menu/hud/inventory/draggable.gd
|
||||
./menu/hud/inventory/menu/characters/color.tres
|
||||
./menu/hud/inventory/drop_zone.tscn
|
||||
./menu/hud/inventory/RichTextLabel.gd
|
||||
./menu/hud/inventory/hud_icon_mutation.tscn
|
||||
./menu/hud/inventory/use_count.gd
|
||||
./menu/hud/inventory/draggable.tscn
|
||||
./menu/hud/inventory/black_shadow_font.tres
|
||||
./menu/hud/inventory/x.png
|
||||
./menu/hud/inventory/hud_icon_mutation.gd
|
||||
./menu/hud/inventory/flash_parent.gd
|
||||
./menu/hud/inventory/TextureRect4.gd
|
||||
./menu/hud/cursor.tscn
|
||||
./menu/hud/hud.tscn
|
||||
./menu/hud/cursor.gd
|
||||
./menu/hud/hud.gd
|
||||
./menu/metal_text.tres
|
||||
./menu/rich_text_effects/RichTextType.gd
|
||||
./menu/rich_text_effects/RichTextPanel.gd
|
||||
./menu/rich_text_effects/RichTextFlash.gd
|
||||
./menu/rich_text_effects/RichTextTranslate.gd
|
||||
./menu/in_game.tres
|
||||
./menu/lcd_screen_font.tres
|
||||
./menu/toast_exit.wav
|
||||
./menu/stack/ahses_material.tres
|
||||
./menu/stack/home.kra
|
||||
./menu/stack/fade.gd
|
||||
./menu/stack/stack.tscn
|
||||
./menu/stack/stack.gd
|
||||
./menu/stack/version.gd
|
||||
./menu/stack/art.kra
|
||||
./entity/unlock_skin_classic/icon.png
|
||||
./entity/use.gd
|
||||
./entity/chair/entity.tscn
|
||||
./entity/chair/icon.png
|
||||
./entity/chair/data.gd
|
||||
./entity/man_desert/entity.tscn
|
||||
./entity/man_desert/icon.png
|
||||
./entity/man_desert/teleprompts/need_medbay.wav
|
||||
./entity/man_desert/teleprompts/me_too.wav
|
||||
./entity/man_desert/teleprompts/get_up_alt.wav
|
||||
./entity/man_desert/teleprompts/getting_a_medpack.wav
|
||||
./entity/man_desert/teleprompts/firstaid-incoming.wav
|
||||
./entity/man_desert/teleprompts/batch_name.py
|
||||
./entity/man_desert/teleprompts/what.wav
|
||||
./entity/man_desert/teleprompts/oo.wav
|
||||
./entity/man_desert/teleprompts/yell.wav
|
||||
./entity/man_desert/teleprompts/rushing.wav
|
||||
./entity/man_desert/teleprompts/ooo.wav
|
||||
./entity/man_desert/teleprompts/coming_to_heal_ya.wav
|
||||
./entity/man_desert/teleprompts/where_is_the_medpack.wav
|
||||
./entity/man_desert/teleprompts/ah.wav
|
||||
./entity/man_desert/teleprompts/no.wav
|
||||
./entity/man_desert/teleprompts/going_to_camp_medbay.wav
|
||||
./entity/man_desert/teleprompts/aa.wav
|
||||
./entity/man_desert/teleprompts/pirate_alt.wav
|
||||
./entity/man_desert/teleprompts/take_morphine.wav
|
||||
./entity/man_desert/teleprompts/ee.wav
|
||||
./entity/man_desert/teleprompts/get_up.wav
|
||||
./entity/man_desert/teleprompts/aw.wav
|
||||
./entity/man_desert/teleprompts/easy.wav
|
||||
./entity/man_desert/teleprompts/intruder.wav
|
||||
./entity/man_desert/teleprompts/amateur.wav
|
||||
./entity/man_desert/teleprompts/hes_not_moving.wav
|
||||
./entity/man_desert/teleprompts/pirate.wav
|
||||
./entity/man_desert/teleprompts/i_dont_know.wav
|
||||
./entity/man_desert/teleprompts/index.txt
|
||||
./entity/man_desert/teleprompts/move.wav
|
||||
./entity/man_desert/teleprompts/hes_stuck.wav
|
||||
./entity/man_desert/teleprompts/how.wav
|
||||
./entity/man_desert/teleprompts/uu.wav
|
||||
./entity/man_desert/teleprompts/where_is_the_gun.wav
|
||||
./entity/man_desert/teleprompts/getting_a_gun.wav
|
||||
./entity/man_desert/data.gd
|
||||
./entity/man_desert/hand.png
|
||||
./entity/barrel_side_smoke/entity.tscn
|
||||
./entity/barrel_side_smoke/icon.png
|
||||
./entity/barrel_side_smoke/data.gd
|
||||
./entity/barrel_smoke/entity.tscn
|
||||
./entity/barrel_smoke/icon.png
|
||||
./entity/barrel_smoke/data.gd
|
||||
./entity/project_box/entity.tscn
|
||||
./entity/project_box/icon.png
|
||||
./entity/project_box/data.gd
|
||||
./entity/mutation_saw/entity.tscn
|
||||
./entity/mutation_saw/icon.png
|
||||
./entity/mutation_saw/special.gd
|
||||
./entity/mutation_saw/data.gd
|
||||
./entity/lift_entrance/entity.tscn
|
||||
./entity/lift_entrance/icon.png
|
||||
./entity/lift_entrance/special.gd
|
||||
./entity/lift_entrance/data.gd
|
||||
./entity/mutation_accuracy_boost_DELETE/entity.tscn
|
||||
./entity/mutation_accuracy_boost_DELETE/icon.png
|
||||
./entity/mutation_accuracy_boost_DELETE/special.gd
|
||||
./entity/mutation_accuracy_boost_DELETE/data.gd
|
||||
./entity/skin_ruffle/entity.tscn
|
||||
./entity/skin_ruffle/icon.png
|
||||
./entity/skin_ruffle/carried.png
|
||||
./entity/skin_ruffle/data.gd
|
||||
./entity/editor_only_icon.gd
|
||||
./entity/console_dark/entity.tscn
|
||||
./entity/console_dark/icon.png
|
||||
./entity/console_dark/data.gd
|
||||
./entity/console_dark/animation.png
|
||||
./entity/smg2/entity.tscn
|
||||
./entity/smg2/used.wav
|
||||
./entity/smg2/icon.png
|
||||
./entity/smg2/data.gd
|
||||
./entity/smg2/debug.gd
|
||||
./entity/grenade_launcher/entity.tscn
|
||||
./entity/grenade_launcher/used.wav
|
||||
./entity/grenade_launcher/icon.png
|
||||
./entity/grenade_launcher/special.gd
|
||||
./entity/grenade_launcher/data.gd
|
||||
./entity/floor_tile_full_square/entity.tscn
|
||||
./entity/floor_tile_full_square/icon.png
|
||||
./entity/floor_tile_full_square/data.gd
|
||||
./entity/grate_1/entity.tscn
|
||||
./entity/grate_1/icon.png
|
||||
./entity/grate_1/data.gd
|
||||
./entity/bed_bunk_corner/entity.tscn
|
||||
./entity/bed_bunk_corner/icon.png
|
||||
./entity/bed_bunk_corner/data.gd
|
||||
./entity/kill_streak_rail_gun_level_3/entity.tscn
|
||||
./entity/kill_streak_rail_gun_level_3/data.gd
|
||||
./entity/teleporter_random_weak/entity.tscn
|
||||
./entity/teleporter_random_weak/teleporter_model.gd
|
||||
./entity/teleporter_random_weak/used.wav
|
||||
./entity/teleporter_random_weak/icon.png
|
||||
./entity/teleporter_random_weak/special.gd
|
||||
./entity/teleporter_random_weak/ray.gd
|
||||
./entity/teleporter_random_weak/data.gd
|
||||
./entity/teleporter_random_weak/flap.png
|
||||
./entity/entities.kra
|
||||
./entity/jerry_can/entity.tscn
|
||||
./entity/jerry_can/icon.png
|
||||
./entity/jerry_can/data.gd
|
||||
./entity/kill_streak_helmet_full/entity.tscn
|
||||
./entity/kill_streak_helmet_full/data.gd
|
||||
./entity/background_derelict/background2.gd
|
||||
./entity/background_derelict/entity.tscn
|
||||
./entity/background_derelict/icon.png
|
||||
./entity/background_derelict/background/space.png
|
||||
./entity/background_derelict/background/line.png
|
||||
./entity/background_derelict/background/overlay.png
|
||||
./entity/background_derelict/background/background2.png
|
||||
./entity/background_derelict/background/background.png
|
||||
./entity/background_derelict/background/engine_glow.tscn
|
||||
./entity/background_derelict/background/lines3.png
|
||||
./entity/background_derelict/background/background.tscn
|
||||
./entity/background_derelict/background/lines.tres
|
||||
./entity/background_derelict/background/xx.gd
|
||||
./entity/background_derelict/background/background.gd
|
||||
./entity/background_derelict/background/bayer16tile2.png
|
||||
./entity/background_derelict/background/push.png
|
||||
./entity/background_derelict/background/palette_mono.png
|
||||
./entity/background_derelict/background/stars.gd
|
||||
./entity/background_derelict/background/lines2.png
|
||||
./entity/background_derelict/background/lines.shader
|
||||
./entity/background_derelict/background/ambience.gd
|
||||
./entity/background_derelict/background/space_ship_ambience.ogg
|
||||
./entity/background_derelict/background/stars.png
|
||||
./entity/background_derelict/data.gd
|
||||
./entity/smoker/entity.tscn
|
||||
./entity/smoker/right_hand.png
|
||||
./entity/smoker/eyes.png
|
||||
./entity/smoker/data.gd
|
||||
./entity/smoker/animate.gd
|
||||
./entity/smoker/left_hand.png
|
||||
./entity/EntityStatic.gd
|
||||
./entity/level_model.gd
|
||||
./entity/class_teleporter_drop_chance/entity.tscn
|
||||
./entity/class_teleporter_drop_chance/icon.png
|
||||
./entity/class_teleporter_drop_chance/special.gd
|
||||
./entity/class_teleporter_drop_chance/data.gd
|
||||
./entity/smg4/entity.tscn
|
||||
./entity/smg4/used.wav
|
||||
./entity/smg4/icon.png
|
||||
./entity/smg4/data.gd
|
||||
./entity/medpack/entity.tscn
|
||||
./entity/medpack/icon.png
|
||||
./entity/medpack/dead.png
|
||||
./entity/medpack/data.gd
|
||||
./entity/model.gd
|
||||
./entity/doom_transition/entity.tscn
|
||||
./entity/doom_transition/icon.png
|
||||
./entity/doom_transition/special.gd
|
||||
./entity/doom_transition/Screenshot from 2021-12-08 18-25-03.png
|
||||
./entity/doom_transition/data.gd
|
||||
./entity/glass_block_exploding/entity.tscn
|
||||
./entity/glass_block_exploding/icon.png
|
||||
./entity/glass_block_exploding/special.gd
|
||||
./entity/glass_block_exploding/dead.png
|
||||
./entity/glass_block_exploding/data.gd
|
||||
./entity/floor_ting/entity.tscn
|
||||
./entity/floor_ting/icon.png
|
||||
./entity/floor_ting/data.gd
|
||||
./entity/background_crashed_ship/entity.tscn
|
||||
./entity/background_crashed_ship/icon.png
|
||||
./entity/background_crashed_ship/background/background2.kra
|
||||
./entity/background_crashed_ship/background/dust_storm_negative.png
|
||||
./entity/background_crashed_ship/background/background2.png
|
||||
./entity/background_crashed_ship/background/background2 (copy 1).png
|
||||
./entity/background_crashed_ship/background/dust_bowl.ogg
|
||||
./entity/background_crashed_ship/background/background.tscn
|
||||
./entity/background_crashed_ship/background/background.kra
|
||||
./entity/background_crashed_ship/data.gd
|
||||
./entity/game_aim_hack_boss/entity.tscn
|
||||
./entity/game_aim_hack_boss/icon.png
|
||||
./entity/game_aim_hack_boss/special.gd
|
||||
./entity/game_aim_hack_boss/give_my_arm_back.wav
|
||||
./entity/game_aim_hack_boss/my_arm_came_off.wav
|
||||
./entity/game_aim_hack_boss/data.gd
|
||||
./entity/sink/entity.tscn
|
||||
./entity/sink/icon.png
|
||||
./entity/sink/data.gd
|
||||
./entity/grate_2/entity.tscn
|
||||
./entity/grate_2/icon.png
|
||||
./entity/grate_2/data.gd
|
||||
./entity/barrel_side/entity.tscn
|
||||
./entity/barrel_side/icon.png
|
||||
./entity/barrel_side/data.gd
|
||||
./entity/oxygen/entity.tscn
|
||||
./entity/oxygen/icon.png
|
||||
./entity/oxygen/shadow.png
|
||||
./entity/oxygen/data.gd
|
||||
./entity/oxygen/normal.png
|
||||
./entity/unlock_skin_robo/entity.tscn
|
||||
./entity/unlock_skin_robo/icon.png
|
||||
./entity/unlock_skin_robo/special.gd
|
||||
./entity/unlock_skin_robo/data.gd
|
||||
./entity/entity_agency_model.gd
|
||||
./entity/floor_tile_wood/entity.tscn
|
||||
./entity/floor_tile_wood/icon.png
|
||||
./entity/floor_tile_wood/data.gd
|
||||
./entity/qr_code/entity.tscn
|
||||
./entity/qr_code/icon.png
|
||||
./entity/qr_code/data.gd
|
||||
./entity/background_sun/overlay.png
|
||||
./entity/background_sun/entity.tscn
|
||||
./entity/background_sun/c.gd
|
||||
./entity/background_sun/kill.tscn
|
||||
./entity/background_sun/icon.png
|
||||
./entity/background_sun/special.gd
|
||||
./entity/background_sun/wtf.tres
|
||||
./entity/background_sun/background/background2.png
|
||||
./entity/background_sun/background/background.tscn
|
||||
./entity/background_sun/background/color2s.tres
|
||||
./entity/background_sun/background/background_glow.png
|
||||
./entity/background_sun/data.gd
|
||||
./entity/background_sun/kill.gd
|
||||
./entity/background_sun/stars.png
|
||||
./entity/background_zone_intro/overlay.png
|
||||
./entity/background_zone_intro/entity.tscn
|
||||
./entity/background_zone_intro/icon.png
|
||||
./entity/background_zone_intro/special.gd
|
||||
./entity/background_zone_intro/background/space.png
|
||||
./entity/background_zone_intro/background/line.png
|
||||
./entity/background_zone_intro/background/background2.png
|
||||
./entity/background_zone_intro/background/background.png
|
||||
./entity/background_zone_intro/background/engine_glow.tscn
|
||||
./entity/background_zone_intro/background/lines3.png
|
||||
./entity/background_zone_intro/background/background.tscn
|
||||
./entity/background_zone_intro/background/lines.tres
|
||||
./entity/background_zone_intro/background/background.gd
|
||||
./entity/background_zone_intro/background/bayer16tile2.png
|
||||
./entity/background_zone_intro/background/push.png
|
||||
./entity/background_zone_intro/background/palette_mono.png
|
||||
./entity/background_zone_intro/background/stars.gd
|
||||
./entity/background_zone_intro/background/lines2.png
|
||||
./entity/background_zone_intro/background/lines.shader
|
||||
./entity/background_zone_intro/background/ambience.gd
|
||||
./entity/background_zone_intro/background/space_ship_ambience.ogg
|
||||
./entity/background_zone_intro/background/stars.png
|
||||
./entity/background_zone_intro/background_end.png
|
||||
./entity/background_zone_intro/data.gd
|
||||
./entity/background_zone_intro/tinge.png
|
||||
./entity/closet_alt/entity.tscn
|
||||
./entity/closet_alt/icon.png
|
||||
./entity/closet_alt/data.gd
|
||||
./entity/meta_random_sound/entity.tscn
|
||||
./entity/meta_random_sound/giberish.wav
|
||||
./entity/meta_random_sound/icon.png
|
||||
./entity/meta_random_sound/special.gd
|
||||
./entity/meta_random_sound/who.wav
|
||||
./entity/meta_random_sound/data.gd
|
||||
./entity/meta_random_sound/hoola_boola.wav
|
||||
./entity/meta_random_sound/space_bandit.wav
|
||||
./entity/lines/entity.tscn
|
||||
./entity/lines/icon.png
|
||||
./entity/lines/data.gd
|
||||
./entity/teleporter_random_avoid_ray/entity.tscn
|
||||
./entity/teleporter_random_avoid_ray/used.wav
|
||||
./entity/teleporter_random_avoid_ray/icon.png
|
||||
./entity/teleporter_random_avoid_ray/ray.gd
|
||||
./entity/teleporter_random_avoid_ray/data.gd
|
||||
./entity/teleporter_random_avoid_ray/flap.png
|
||||
./entity/teleporter_random_avoid_ray/RayCast2D.gd
|
||||
./entity/teleporter_random_avoid_ray/area.gd
|
||||
./entity/teleporter_random_avoid_ray/flap.gd
|
||||
./entity/saw/blades.gd
|
||||
./entity/saw/entity.tscn
|
||||
./entity/saw/used.wav
|
||||
./entity/saw/icon.png
|
||||
./entity/saw/special.gd
|
||||
./entity/saw/carried.png
|
||||
./entity/saw/data.gd
|
||||
./entity/saw/used (copy 1).wav
|
||||
./entity/saw/saw.wav
|
||||
./entity/saw/carried_blades.png
|
||||
./entity/floor_tile_checkerdboard/damage.png
|
||||
./entity/floor_tile_checkerdboard/entity.tscn
|
||||
./entity/floor_tile_checkerdboard/icon.png
|
||||
./entity/floor_tile_checkerdboard/entity.tres
|
||||
./entity/floor_tile_checkerdboard/data.gd
|
||||
./entity/mutation_smoke_grenade_upgrade/entity.tscn
|
||||
./entity/mutation_smoke_grenade_upgrade/icon.png
|
||||
./entity/mutation_smoke_grenade_upgrade/special.gd
|
||||
./entity/mutation_smoke_grenade_upgrade/data.gd
|
||||
./entity/mutation_smoke_grenade_upgrade/mutation_model.gd
|
||||
./entity/helmet_full/entity.tscn
|
||||
./entity/helmet_full/pick_up.wav
|
||||
./entity/helmet_full/icon.png
|
||||
./entity/helmet_full/data.gd
|
||||
./entity/helmet_full/helmet-ping.wav
|
||||
./entity/barrel_explosive/entity.tscn
|
||||
./entity/barrel_explosive/icon.png
|
||||
./entity/barrel_explosive/data.gd
|
||||
./entity/bank/entity.tscn
|
||||
./entity/bank/icon.png
|
||||
./entity/bank/special.gd
|
||||
./entity/bank/data.gd
|
||||
./entity/kick/entity.tscn
|
||||
./entity/kick/swipe.png
|
||||
./entity/kick/used.wav
|
||||
./entity/kick/icon.png
|
||||
./entity/kick/AnimatedSprite.gd
|
||||
./entity/kick/data.gd
|
||||
./entity/battery/entity.tscn
|
||||
./entity/battery/icon.png
|
||||
./entity/battery/data.gd
|
||||
./entity/lift/entity.tscn
|
||||
./entity/lift/opening.wav
|
||||
./entity/lift/doors_open.png
|
||||
./entity/lift/RichTextLabel.gd
|
||||
./entity/lift/icon.png
|
||||
./entity/lift/open.wav
|
||||
./entity/lift/elevator_end.wav
|
||||
./entity/lift/lift_model.gd
|
||||
./entity/lift/label.tscn
|
||||
./entity/lift/rumble.gd
|
||||
./entity/lift/level_portal_model.gd
|
||||
./entity/lift/data.gd
|
||||
./entity/lift/doors.png
|
||||
./entity/lift/area.gd
|
||||
./entity/snes/entity.tscn
|
||||
./entity/snes/icon.png
|
||||
./entity/snes/data.gd
|
||||
./entity/passive_disarm/entity.tscn
|
||||
./entity/passive_disarm/icon.png
|
||||
./entity/passive_disarm/special.gd
|
||||
./entity/passive_disarm/data.gd
|
||||
./entity/mutation_lots_of_shot/entity.tscn
|
||||
./entity/mutation_lots_of_shot/icon.png
|
||||
./entity/mutation_lots_of_shot/special.gd
|
||||
./entity/mutation_lots_of_shot/data.gd
|
||||
./entity/pallet2/entity.tscn
|
||||
./entity/pallet2/icon.png
|
||||
./entity/pallet2/data.gd
|
||||
./entity/kill_streak_sword/entity.tscn
|
||||
./entity/kill_streak_sword/data.gd
|
||||
./entity/rain/entity.tscn
|
||||
./entity/rain/icon.png
|
||||
./entity/rain/special.gd
|
||||
./entity/rain/rain.png
|
||||
./entity/rain/rain.tscn
|
||||
./entity/rain/data.gd
|
||||
./entity/rain/rain.gd
|
||||
./entity/white_line/entity.tscn
|
||||
./entity/white_line/icon.png
|
||||
./entity/white_line/data.gd
|
||||
./entity/game_break_sword/entity.tscn
|
||||
./entity/game_break_sword/icon.png
|
||||
./entity/game_break_sword/special.gd
|
||||
./entity/game_break_sword/data.gd
|
||||
./entity/background_zone1/overlay.png
|
||||
./entity/background_zone1/entity.tscn
|
||||
./entity/background_zone1/icon.png
|
||||
./entity/background_zone1/special.gd
|
||||
./entity/background_zone1/background/space.png
|
||||
./entity/background_zone1/background/line.png
|
||||
./entity/background_zone1/background/background2.png
|
||||
./entity/background_zone1/background/background.png
|
||||
./entity/background_zone1/background/engine_glow.tscn
|
||||
./entity/background_zone1/background/lines3.png
|
||||
./entity/background_zone1/background/background.tscn
|
||||
./entity/background_zone1/background/lines.tres
|
||||
./entity/background_zone1/background/background.gd
|
||||
./entity/background_zone1/background/bayer16tile2.png
|
||||
./entity/background_zone1/background/push.png
|
||||
./entity/background_zone1/background/palette_mono.png
|
||||
./entity/background_zone1/background/stars.gd
|
||||
./entity/background_zone1/background/lines2.png
|
||||
./entity/background_zone1/background/lines.shader
|
||||
./entity/background_zone1/background/ambience.gd
|
||||
./entity/background_zone1/background/space_ship_ambience.ogg
|
||||
./entity/background_zone1/background/stars.png
|
||||
./entity/background_zone1/data.gd
|
||||
./entity/background_zone1/tinge.png
|
||||
./entity/mutation_throw_trap_DELETE/entity.tscn
|
||||
./entity/mutation_throw_trap_DELETE/icon.png
|
||||
./entity/mutation_throw_trap_DELETE/special.gd
|
||||
./entity/mutation_throw_trap_DELETE/data.gd
|
||||
./entity/agency.gd
|
||||
./entity/skin_cheese/entity.tscn
|
||||
./entity/skin_cheese/icon.png
|
||||
./entity/skin_cheese/carried.png
|
||||
./entity/skin_cheese/data.gd
|
||||
./entity/toilet/entity.tscn
|
||||
./entity/toilet/icon.png
|
||||
./entity/toilet/special.gd
|
||||
./entity/toilet/water.png
|
||||
./entity/toilet/drink.wav
|
||||
./entity/toilet/data.gd
|
||||
./entity/smg3/entity.tscn
|
||||
./entity/smg3/used.wav
|
||||
./entity/smg3/icon.png
|
||||
./entity/smg3/dead.png
|
||||
./entity/smg3/data.gd
|
||||
./entity/smg3/debug.gd
|
||||
./entity/teleporter_super/entity.tscn
|
||||
./entity/teleporter_super/icon.png
|
||||
./entity/teleporter_super/data.gd
|
||||
./entity/background_zone_end/overlay.png
|
||||
./entity/background_zone_end/entity.tscn
|
||||
./entity/background_zone_end/icon.png
|
||||
./entity/background_zone_end/special.gd
|
||||
./entity/background_zone_end/stars2.png
|
||||
./entity/background_zone_end/background_end.png
|
||||
./entity/background_zone_end/data.gd
|
||||
./entity/background_zone_end/tinge.png
|
||||
./entity/kill_streak_barricade/entity.tscn
|
||||
./entity/kill_streak_barricade/data.gd
|
||||
./entity/game_zone_4_boss_1/entity.tscn
|
||||
./entity/game_zone_4_boss_1/icon.png
|
||||
./entity/game_zone_4_boss_1/special.gd
|
||||
./entity/game_zone_4_boss_1/data.gd
|
||||
./entity/game_zone_4_boss_1/kill_me_and_explode_ship.wav
|
||||
./entity/mutation_remove_melee/entity.tscn
|
||||
./entity/mutation_remove_melee/icon.png
|
||||
./entity/mutation_remove_melee/special.gd
|
||||
./entity/mutation_remove_melee/data.gd
|
||||
./entity/he_grenade_level_2/entity.tscn
|
||||
./entity/he_grenade_level_2/icon.png
|
||||
./entity/he_grenade_level_2/data.gd
|
||||
./entity/background_zone_2/entity.tscn
|
||||
./entity/background_zone_2/icon.png
|
||||
./entity/background_zone_2/background/background2.kra
|
||||
./entity/background_zone_2/background/grad.png
|
||||
./entity/background_zone_2/background/background2.png
|
||||
./entity/background_zone_2/background/background.png
|
||||
./entity/background_zone_2/background/background2 (copy 1).png
|
||||
./entity/background_zone_2/background/backgrounds.gd
|
||||
./entity/background_zone_2/background/wall_overlay.png
|
||||
./entity/background_zone_2/background/background.tscn
|
||||
./entity/background_zone_2/background/Screenshot from 2022-07-07 10-58-48.png
|
||||
./entity/background_zone_2/background/background.gd
|
||||
./entity/background_zone_2/background/shadow.png
|
||||
./entity/background_zone_2/background/engine smoke.png
|
||||
./entity/background_zone_2/background/background.kra
|
||||
./entity/background_zone_2/background/sea.ogg
|
||||
./entity/background_zone_2/background/background2blur.png
|
||||
./entity/background_zone_2/background/test.gd
|
||||
./entity/background_zone_2/background/grad3.png
|
||||
./entity/background_zone_2/background/lines2.png
|
||||
./entity/background_zone_2/background/smoke.tscn
|
||||
./entity/background_zone_2/background/left_water.tscn
|
||||
./entity/background_zone_2/background/grad2.png
|
||||
./entity/background_zone_2/background/para.png
|
||||
./entity/background_zone_2/data.gd
|
||||
./entity/pipe_corner/entity.tscn
|
||||
./entity/pipe_corner/icon.png
|
||||
./entity/pipe_corner/data.gd
|
||||
./entity/floor_tile_metal_cow_trap/entity.tscn
|
||||
./entity/floor_tile_metal_cow_trap/icon.png
|
||||
./entity/floor_tile_metal_cow_trap/data.gd
|
||||
./entity/skin_naked/entity.tscn
|
||||
./entity/skin_naked/icon.png
|
||||
./entity/skin_naked/carried.png
|
||||
./entity/skin_naked/data.gd
|
||||
./entity/valve/entity.tscn
|
||||
./entity/valve/icon.png
|
||||
./entity/valve/.icon.png-autosave.kra
|
||||
./entity/valve/data.gd
|
||||
./entity/bed/entity.tscn
|
||||
./entity/bed/icon.png
|
||||
./entity/bed/data.gd
|
||||
./entity/game_invisible_guy/entity.tscn
|
||||
./entity/game_invisible_guy/icon.png
|
||||
./entity/game_invisible_guy/special.gd
|
||||
./entity/game_invisible_guy/data.gd
|
||||
./entity/smg/entity.tscn
|
||||
./entity/smg/used.wav
|
||||
./entity/smg/icon.png
|
||||
./entity/smg/data.gd
|
||||
./entity/skin_robo/entity.tscn
|
||||
./entity/skin_robo/icon.png
|
||||
./entity/skin_robo/carried.png
|
||||
./entity/skin_robo/data.gd
|
||||
./entity/bandana/entity.tscn
|
||||
./entity/bandana/bob.gd
|
||||
./entity/bandana/icon.png
|
||||
./entity/bandana/special.gd
|
||||
./entity/bandana/carried.png
|
||||
./entity/bandana/data.gd
|
||||
./entity/bandana/pixel.png
|
||||
./entity/floor_plug/entity.tscn
|
||||
./entity/floor_plug/icon.png
|
||||
./entity/floor_plug/data.gd
|
||||
./entity/bench/entity.tscn
|
||||
./entity/bench/icon.png
|
||||
./entity/bench/data.gd
|
||||
./entity/meta_strip_items/entity.tscn
|
||||
./entity/meta_strip_items/special.gd
|
||||
./entity/meta_strip_items/meta_strip_items_model.gd
|
||||
./entity/meta_strip_items/data.gd
|
||||
./entity/crate_teleporter/entity.tscn
|
||||
./entity/crate_teleporter/icon.png
|
||||
./entity/crate_teleporter/data.gd
|
||||
./entity/crate_teleporter/satellite.kra
|
||||
./entity/crate_garbage/entity.tscn
|
||||
./entity/crate_garbage/icon.png
|
||||
./entity/crate_garbage/data.gd
|
||||
./entity/crate_garbage/gibbed.png
|
||||
./entity/meta_stats/entity.tscn
|
||||
./entity/meta_stats/letters.tres
|
||||
./entity/meta_stats/icon.png
|
||||
./entity/meta_stats/special.gd
|
||||
./entity/meta_stats/data.gd
|
||||
./entity/meta_stats/meta_stats_model.gd
|
||||
./entity/rail_gun/entity.tscn
|
||||
./entity/rail_gun/used.wav
|
||||
./entity/rail_gun/icon.png
|
||||
./entity/rail_gun/special.gd
|
||||
./entity/rail_gun/carried.png
|
||||
./entity/rail_gun/data.gd
|
||||
./entity/drop_ship_door/entity.tscn
|
||||
./entity/drop_ship_door/icon.png
|
||||
./entity/drop_ship_door/data.gd
|
||||
./entity/floor_lines/entity.tscn
|
||||
./entity/floor_lines/icon.png
|
||||
./entity/floor_lines/data.gd
|
||||
./entity/game_trap/entity.tscn
|
||||
./entity/game_trap/you_blew_up_my_force_field.wav
|
||||
./entity/game_trap/droped_my_grenade_2.wav
|
||||
./entity/game_trap/icon.png
|
||||
./entity/game_trap/special.gd
|
||||
./entity/game_trap/droped_my_grenade_0.wav
|
||||
./entity/game_trap/shock.wav
|
||||
./entity/game_trap/uh_my_helmet.wav
|
||||
./entity/game_trap/ha_missed_me.wav
|
||||
./entity/game_trap/data.gd
|
||||
./entity/game_trap/try_beat_this_force_field.wav
|
||||
./entity/game_trap/droped_my_grenade_1.wav
|
||||
./entity/blood_sword/entity.tscn
|
||||
./entity/blood_sword/pick_up.wav
|
||||
./entity/blood_sword/used.wav
|
||||
./entity/blood_sword/sam2.png
|
||||
./entity/blood_sword/icon.png
|
||||
./entity/blood_sword/special.gd
|
||||
./entity/blood_sword/hit_bar.gd
|
||||
./entity/blood_sword/data.gd
|
||||
./entity/blood_sword/sam.png
|
||||
./entity/blood_sword/dead.wav
|
||||
./entity/blood_sword/animation.png
|
||||
./entity/auto_cables_thick/entity.tscn
|
||||
./entity/auto_cables_thick/data.gd
|
||||
./entity/auto_cables_thick/wires2.png
|
||||
./entity/shield/entity.tscn
|
||||
./entity/shield/pick_up.wav
|
||||
./entity/shield/icon.png
|
||||
./entity/shield/carried.png
|
||||
./entity/shield/data.gd
|
||||
./entity/shield/helmet-ping.wav
|
||||
./entity/game_teleport_in/entity.tscn
|
||||
./entity/game_teleport_in/icon.png
|
||||
./entity/game_teleport_in/special.gd
|
||||
./entity/game_teleport_in/data.gd
|
||||
./entity/shotgun_super/entity.tscn
|
||||
./entity/shotgun_super/icon.png
|
||||
./entity/shotgun_super/data.gd
|
||||
./entity/bottle/entity.tscn
|
||||
./entity/bottle/icon.png
|
||||
./entity/bottle/data.gd
|
||||
./entity/bottle/normal.png
|
||||
./entity/bottle/icon_shadow.png
|
||||
./entity/kill_streak_p90/entity.tscn
|
||||
./entity/kill_streak_p90/data.gd
|
||||
./entity/drain/entity.tscn
|
||||
./entity/drain/icon.png
|
||||
./entity/drain/data.gd
|
||||
./entity/auto_wires_three/entity.tscn
|
||||
./entity/auto_wires_three/data.gd
|
||||
./entity/light/entity.tscn
|
||||
./entity/light/icon.png
|
||||
./entity/light/special.gd
|
||||
./entity/light/light.wav
|
||||
./entity/light/data.gd
|
||||
./entity/debris/entity.tscn
|
||||
./entity/debris/icon.png
|
||||
./entity/debris/data.gd
|
||||
./entity/debris/gibbed.png
|
||||
./entity/mutation_rail_gun_upgrade/entity.tscn
|
||||
./entity/mutation_rail_gun_upgrade/icon.png
|
||||
./entity/mutation_rail_gun_upgrade/special.gd
|
||||
./entity/mutation_rail_gun_upgrade/data.gd
|
||||
./entity/mutation_rail_gun_upgrade/mutation_model.gd
|
||||
./entity/auto_cables/entity.tscn
|
||||
./entity/auto_cables/data.gd
|
||||
./entity/auto_cables/wires2.png
|
||||
./entity/stealth_camo/entity.tscn
|
||||
./entity/stealth_camo/special.gd
|
||||
./entity/stealth_camo/data.gd
|
||||
./entity/colt_45/entity.tscn
|
||||
./entity/colt_45/used.wav
|
||||
./entity/colt_45/icon.png
|
||||
./entity/colt_45/dead.png
|
||||
./entity/colt_45/data.gd
|
||||
./entity/quantum_suicide_drive/entity.tscn
|
||||
./entity/quantum_suicide_drive/heart.ogg
|
||||
./entity/quantum_suicide_drive/icon.png
|
||||
./entity/quantum_suicide_drive/special.gd
|
||||
./entity/quantum_suicide_drive/qsd_model.gd
|
||||
./entity/quantum_suicide_drive/multi.gd
|
||||
./entity/quantum_suicide_drive/multi.tscn
|
||||
./entity/quantum_suicide_drive/CenterContainer.gd
|
||||
./entity/quantum_suicide_drive/carried.png
|
||||
./entity/quantum_suicide_drive/data.gd
|
||||
./entity/helmet/entity.tscn
|
||||
./entity/helmet/pick_up.wav
|
||||
./entity/helmet/icon.png
|
||||
./entity/helmet/special.gd
|
||||
./entity/helmet/die.wav
|
||||
./entity/helmet/carried.png
|
||||
./entity/helmet/data.gd
|
||||
./entity/helmet/helmet-ping.wav
|
||||
./entity/ammo_box/entity.tscn
|
||||
./entity/ammo_box/icon.png
|
||||
./entity/ammo_box/data.gd
|
||||
./entity/rail_gun_level_2/entity.tscn
|
||||
./entity/rail_gun_level_2/icon.png
|
||||
./entity/rail_gun_level_2/data.gd
|
||||
./entity/glass_block_backup/entity.tscn
|
||||
./entity/glass_block_backup/icon.png
|
||||
./entity/glass_block_backup/data.gd
|
||||
./entity/closet/entity.tscn
|
||||
./entity/closet/icon.png
|
||||
./entity/closet/data.gd
|
||||
./entity/little_boxes/entity.tscn
|
||||
./entity/little_boxes/icon.png
|
||||
./entity/little_boxes/data.gd
|
||||
./entity/meta_health_bar/entity.tscn
|
||||
./entity/meta_health_bar/health_bar_model.gd
|
||||
./entity/meta_health_bar/icon.png
|
||||
./entity/meta_health_bar/special.gd
|
||||
./entity/meta_health_bar/invunerable.png
|
||||
./entity/meta_health_bar/data.gd
|
||||
./entity/night_stand/entity.tscn
|
||||
./entity/night_stand/icon_normal.png
|
||||
./entity/night_stand/icon.png
|
||||
./entity/night_stand/shadow.png
|
||||
./entity/night_stand/data.gd
|
||||
./entity/fan/entity.tscn
|
||||
./entity/fan/flap2.png
|
||||
./entity/fan/flaps.gd
|
||||
./entity/fan/icon.png
|
||||
./entity/fan/data.gd
|
||||
./entity/fan/flap.png
|
||||
./entity/fan/icon_shadow.png
|
||||
./entity/fan/animation.png
|
||||
./entity/fan/gibbed.png
|
||||
./entity/game_tutorial_end/entity.tscn
|
||||
./entity/game_tutorial_end/icon.png
|
||||
./entity/game_tutorial_end/special.gd
|
||||
./entity/game_tutorial_end/data.gd
|
||||
./entity/mutation_disarmament/entity.tscn
|
||||
./entity/mutation_disarmament/icon.png
|
||||
./entity/mutation_disarmament/special.gd
|
||||
./entity/mutation_disarmament/data.gd
|
||||
./entity/air_lock/icon_open.png
|
||||
./entity/air_lock/entity.tscn
|
||||
./entity/air_lock/door_close.wav
|
||||
./entity/air_lock/icon.png
|
||||
./entity/air_lock/special.gd
|
||||
./entity/air_lock/air_lock_model.gd
|
||||
./entity/air_lock/data.gd
|
||||
./entity/scorpion/entity.tscn
|
||||
./entity/scorpion/used.wav
|
||||
./entity/scorpion/laser.gd
|
||||
./entity/scorpion/icon.png
|
||||
./entity/scorpion/data.gd
|
||||
./entity/kill_streak_aim_hack/entity.tscn
|
||||
./entity/kill_streak_aim_hack/data.gd
|
||||
./entity/dungeon_proc_debug/entity.tscn
|
||||
./entity/dungeon_proc_debug/icon.png
|
||||
./entity/dungeon_proc_debug/data.gd
|
||||
./entity/dungeon_proc_debug/debug.gd
|
||||
./entity/dungeon_proc_debug/debug.tscn
|
||||
./entity/tarp/entity.tscn
|
||||
./entity/tarp/icon.png
|
||||
./entity/tarp/data.gd
|
||||
./entity/hit_indicator/entity.tscn
|
||||
./entity/hit_indicator/data.gd
|
||||
./entity/console_corner/entity.tscn
|
||||
./entity/console_corner/animation2.tscn
|
||||
./entity/console_corner/icon.png
|
||||
./entity/console_corner/data.gd
|
||||
./entity/console_corner/animation.tscn
|
||||
./entity/icon.png
|
||||
./entity/couch_corner/entity.tscn
|
||||
./entity/couch_corner/icon.png
|
||||
./entity/couch_corner/data.gd
|
||||
./entity/m4/entity.tscn
|
||||
./entity/m4/used.wav
|
||||
./entity/m4/icon.png
|
||||
./entity/m4/data.gd
|
||||
./entity/game_hud/entity.tscn
|
||||
./entity/game_hud/icon.png
|
||||
./entity/game_hud/data.gd
|
||||
./entity/game_hud/inventory_game.tscn
|
||||
./entity/prototypes.gd
|
||||
./entity/agent_chicken/emotes.png
|
||||
./entity/agent_chicken/entity.tscn
|
||||
./entity/agent_chicken/sound_board.gd
|
||||
./entity/agent_chicken/bones.tscn
|
||||
./entity/agent_chicken/bones.gd
|
||||
./entity/agent_chicken/barks.gd
|
||||
./entity/agent_chicken/emote.gd
|
||||
./entity/agent_chicken/icon.png
|
||||
./entity/agent_chicken/special.gd
|
||||
./entity/agent_chicken/bark.gd
|
||||
./entity/agent_chicken/deaad.png
|
||||
./entity/agent_chicken/icon.gd
|
||||
./entity/agent_chicken/data.gd
|
||||
./entity/agent_chicken/animation.tscn
|
||||
./entity/agent_chicken/emote.tscn
|
||||
./entity/agent_chicken/hand.png
|
||||
./entity/velocity/entity.tscn
|
||||
./entity/velocity/icon.png
|
||||
./entity/velocity/special.gd
|
||||
./entity/velocity/data.gd
|
||||
./entity/aircon/entity.tscn
|
||||
./entity/aircon/grate.png
|
||||
./entity/aircon/icon.png
|
||||
./entity/aircon/data.gd
|
||||
./entity/aircon/animation.png
|
||||
./entity/floor_tile_bricks/entity.tscn
|
||||
./entity/floor_tile_bricks/icon.png
|
||||
./entity/floor_tile_bricks/data.gd
|
||||
./entity/pallet/entity.tscn
|
||||
./entity/pallet/icon.png
|
||||
./entity/pallet/data.gd
|
||||
./entity/barricade_deployed/debug.png
|
||||
./entity/barricade_deployed/field.tscn
|
||||
./entity/barricade_deployed/entity.tscn
|
||||
./entity/barricade_deployed/ambience.ogg
|
||||
./entity/barricade_deployed/icon.png
|
||||
./entity/barricade_deployed/field.gd
|
||||
./entity/barricade_deployed/field_material.tres
|
||||
./entity/barricade_deployed/debug2.png
|
||||
./entity/barricade_deployed/data.gd
|
||||
./entity/barricade_deployed/field_material_invert.tres
|
||||
./entity/barricade_deployed/field_material.gd
|
||||
./entity/barricade_deployed/gibbed.png
|
||||
./entity/helmet_nv/entity.tscn
|
||||
./entity/helmet_nv/pick_up.wav
|
||||
./entity/helmet_nv/icon.png
|
||||
./entity/helmet_nv/special.gd
|
||||
./entity/helmet_nv/carried.png
|
||||
./entity/helmet_nv/eyes.png
|
||||
./entity/helmet_nv/data.gd
|
||||
./entity/helmet_nv/helmet-ping.wav
|
||||
./entity/helmet_nv/eyes.gd
|
||||
./entity/mutation_sword/entity.tscn
|
||||
./entity/mutation_sword/icon.png
|
||||
./entity/mutation_sword/special.gd
|
||||
./entity/mutation_sword/data.gd
|
||||
./entity/field_full_super/entity.tscn
|
||||
./entity/field_full_super/icon.png
|
||||
./entity/field_full_super/special.gd
|
||||
./entity/field_full_super/carried.png
|
||||
./entity/field_full_super/data.gd
|
||||
./entity/entity_man.gd
|
||||
./entity/couch/entity.tscn
|
||||
./entity/couch/icon.png
|
||||
./entity/couch/data.gd
|
||||
./entity/teleporter_lil_hunter/entity.tscn
|
||||
./entity/teleporter_lil_hunter/icon.png
|
||||
./entity/teleporter_lil_hunter/tubes.png
|
||||
./entity/teleporter_lil_hunter/osc_shader.tres
|
||||
./entity/teleporter_lil_hunter/eyes.png
|
||||
./entity/teleporter_lil_hunter/data.gd
|
||||
./entity/teleporter_lil_hunter/osc.tres
|
||||
./entity/game_tutorial_melee_zone/entity.tscn
|
||||
./entity/game_tutorial_melee_zone/icon.png
|
||||
./entity/game_tutorial_melee_zone/special.gd
|
||||
./entity/game_tutorial_melee_zone/data.gd
|
||||
./entity/kill_streak_glock/entity.tscn
|
||||
./entity/kill_streak_glock/data.gd
|
||||
./entity/skin_mime/entity.tscn
|
||||
./entity/skin_mime/icon.png
|
||||
./entity/skin_mime/special.gd
|
||||
./entity/skin_mime/carried.png
|
||||
./entity/skin_mime/data.gd
|
||||
./entity/medpack_hard/entity.tscn
|
||||
./entity/medpack_hard/icon.png
|
||||
./entity/medpack_hard/data.gd
|
||||
./entity/teleporter_overload/entity.tscn
|
||||
./entity/teleporter_overload/icon.png
|
||||
./entity/teleporter_overload/special.gd
|
||||
./entity/teleporter_overload/carried.png
|
||||
./entity/teleporter_overload/data.gd
|
||||
./entity/background_freighter/overlay.png
|
||||
./entity/background_freighter/entity.tscn
|
||||
./entity/background_freighter/icon.png
|
||||
./entity/background_freighter/Master.ogg
|
||||
./entity/background_freighter/background/space.png
|
||||
./entity/background_freighter/background/line.png
|
||||
./entity/background_freighter/background/background2.gd
|
||||
./entity/background_freighter/background/good create.png
|
||||
./entity/background_freighter/background/backgip.png
|
||||
./entity/background_freighter/background/background2.png
|
||||
./entity/background_freighter/background/background.png
|
||||
./entity/background_freighter/background/engine_glow.tscn
|
||||
./entity/background_freighter/background/gra2d.png
|
||||
./entity/background_freighter/background/lines3.png
|
||||
./entity/background_freighter/background/background.tscn
|
||||
./entity/background_freighter/background/lines.tres
|
||||
./entity/background_freighter/background/background.gd
|
||||
./entity/background_freighter/background/bayer16tile2.png
|
||||
./entity/background_freighter/background/goodcrate.png
|
||||
./entity/background_freighter/background/push.png
|
||||
./entity/background_freighter/background/background_floor.png
|
||||
./entity/background_freighter/background/palette_mono.png
|
||||
./entity/background_freighter/background/stars.gd
|
||||
./entity/background_freighter/background/lines2.png
|
||||
./entity/background_freighter/background/lines.shader
|
||||
./entity/background_freighter/background/ambience.gd
|
||||
./entity/background_freighter/background/bacsdas.png
|
||||
./entity/background_freighter/background/space_ship_ambience.ogg
|
||||
./entity/background_freighter/background/stars.png
|
||||
./entity/background_freighter/data.gd
|
||||
./entity/auto_wires/entity.tscn
|
||||
./entity/auto_wires/data.gd
|
||||
./entity/kill_streak/entity.tscn
|
||||
./entity/kill_streak/kill_streak_toast.tscn
|
||||
./entity/kill_streak/icon.png
|
||||
|
|
@ -0,0 +1 @@
|
|||
5U
|
||||
|
|
@ -0,0 +1 @@
|
|||
U5
|
||||
BIN
engine/tests/data/models/cube.bin
Normal file
BIN
engine/tests/data/models/cube.bin
Normal file
Binary file not shown.
312
engine/tests/data/models/cube.gltf
Normal file
312
engine/tests/data/models/cube.gltf
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
{
|
||||
"asset":{
|
||||
"generator":"Khronos glTF Blender I/O v4.3.47",
|
||||
"version":"2.0"
|
||||
},
|
||||
"scene":0,
|
||||
"scenes":[
|
||||
{
|
||||
"name":"Scene",
|
||||
"nodes":[
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes":[
|
||||
{
|
||||
"mesh":0,
|
||||
"name":"Cube"
|
||||
}
|
||||
],
|
||||
"animations":[
|
||||
{
|
||||
"channels":[
|
||||
{
|
||||
"sampler":0,
|
||||
"target":{
|
||||
"node":0,
|
||||
"path":"translation"
|
||||
}
|
||||
},
|
||||
{
|
||||
"sampler":1,
|
||||
"target":{
|
||||
"node":0,
|
||||
"path":"rotation"
|
||||
}
|
||||
},
|
||||
{
|
||||
"sampler":2,
|
||||
"target":{
|
||||
"node":0,
|
||||
"path":"scale"
|
||||
}
|
||||
}
|
||||
],
|
||||
"name":"CubeAction",
|
||||
"samplers":[
|
||||
{
|
||||
"input":8,
|
||||
"interpolation":"LINEAR",
|
||||
"output":9
|
||||
},
|
||||
{
|
||||
"input":10,
|
||||
"interpolation":"STEP",
|
||||
"output":11
|
||||
},
|
||||
{
|
||||
"input":10,
|
||||
"interpolation":"STEP",
|
||||
"output":12
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials":[
|
||||
{
|
||||
"doubleSided":true,
|
||||
"name":"Material1",
|
||||
"pbrMetallicRoughness":{
|
||||
"baseColorFactor":[
|
||||
1.9073486328125e-06,
|
||||
0,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"metallicFactor":0
|
||||
}
|
||||
},
|
||||
{
|
||||
"doubleSided":true,
|
||||
"name":"Material2",
|
||||
"pbrMetallicRoughness":{
|
||||
"baseColorFactor":[
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1
|
||||
],
|
||||
"roughnessFactor":0
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes":[
|
||||
{
|
||||
"name":"Cube.001",
|
||||
"primitives":[
|
||||
{
|
||||
"attributes":{
|
||||
"POSITION":0,
|
||||
"NORMAL":1,
|
||||
"TEXCOORD_0":2
|
||||
},
|
||||
"indices":3,
|
||||
"material":0
|
||||
},
|
||||
{
|
||||
"attributes":{
|
||||
"POSITION":4,
|
||||
"NORMAL":5,
|
||||
"TEXCOORD_0":6
|
||||
},
|
||||
"indices":7,
|
||||
"material":1
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"accessors":[
|
||||
{
|
||||
"bufferView":0,
|
||||
"componentType":5126,
|
||||
"count":4,
|
||||
"max":[
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"min":[
|
||||
-1,
|
||||
1,
|
||||
-1
|
||||
],
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":1,
|
||||
"componentType":5126,
|
||||
"count":4,
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":2,
|
||||
"componentType":5126,
|
||||
"count":4,
|
||||
"type":"VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView":3,
|
||||
"componentType":5123,
|
||||
"count":6,
|
||||
"type":"SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView":4,
|
||||
"componentType":5126,
|
||||
"count":20,
|
||||
"max":[
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"min":[
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":5,
|
||||
"componentType":5126,
|
||||
"count":20,
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":6,
|
||||
"componentType":5126,
|
||||
"count":20,
|
||||
"type":"VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView":7,
|
||||
"componentType":5123,
|
||||
"count":30,
|
||||
"type":"SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView":8,
|
||||
"componentType":5126,
|
||||
"count":20,
|
||||
"max":[
|
||||
0.8333333333333334
|
||||
],
|
||||
"min":[
|
||||
0.041666666666666664
|
||||
],
|
||||
"type":"SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView":9,
|
||||
"componentType":5126,
|
||||
"count":20,
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":10,
|
||||
"componentType":5126,
|
||||
"count":2,
|
||||
"max":[
|
||||
0.8333333333333334
|
||||
],
|
||||
"min":[
|
||||
0.041666666666666664
|
||||
],
|
||||
"type":"SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView":11,
|
||||
"componentType":5126,
|
||||
"count":2,
|
||||
"type":"VEC4"
|
||||
},
|
||||
{
|
||||
"bufferView":12,
|
||||
"componentType":5126,
|
||||
"count":2,
|
||||
"type":"VEC3"
|
||||
}
|
||||
],
|
||||
"bufferViews":[
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":48,
|
||||
"byteOffset":0,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":48,
|
||||
"byteOffset":48,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":32,
|
||||
"byteOffset":96,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":12,
|
||||
"byteOffset":128,
|
||||
"target":34963
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":240,
|
||||
"byteOffset":140,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":240,
|
||||
"byteOffset":380,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":160,
|
||||
"byteOffset":620,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":60,
|
||||
"byteOffset":780,
|
||||
"target":34963
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":80,
|
||||
"byteOffset":840
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":240,
|
||||
"byteOffset":920
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":8,
|
||||
"byteOffset":1160
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":32,
|
||||
"byteOffset":1168
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":24,
|
||||
"byteOffset":1200
|
||||
}
|
||||
],
|
||||
"buffers":[
|
||||
{
|
||||
"byteLength":1224,
|
||||
"uri":"cube.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
engine/tests/data/models/suzanne.glb
Normal file
BIN
engine/tests/data/models/suzanne.glb
Normal file
Binary file not shown.
|
|
@ -55,7 +55,7 @@ private:
|
|||
return drivers;
|
||||
}
|
||||
|
||||
static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
|
||||
static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
|
||||
r_error = OK;
|
||||
RasterizerDummy::make_current();
|
||||
return memnew(DisplayServerMock());
|
||||
|
|
@ -101,7 +101,7 @@ public:
|
|||
String get_name() const override { return "mock"; }
|
||||
|
||||
// You can simulate DisplayServer-events by calling this function.
|
||||
// The events will be deliverd to Godot's Input-system.
|
||||
// The events will be delivered to Godot's Input-system.
|
||||
// Mouse-events (Button & Motion) will additionally update the DisplayServer's mouse position.
|
||||
// For Mouse motion events, the `relative`-property is set based on the distance to the previous mouse position.
|
||||
void simulate_event(Ref<InputEvent> p_event) {
|
||||
|
|
|
|||
|
|
@ -37,10 +37,34 @@ protected:
|
|||
|
||||
static const Feedback* _feedbacks=nullptr;
|
||||
static const char _vertex_code[]={
|
||||
10,112,114,101,99,105,115,105,111,110,32,104,105,103,104,112,32,102,108,111,97,116,59,10,112,114,101,99,105,115,105,111,110,32,104,105,103,104,112,32,105,110,116,59,10,10,108,97,121,111,117,116,40,108,111,99,97,116,105,111,110,32,61,32,48,41,32,105,110,32,104,105,103,104,112,32,118,101,99,51,32,118,101,114,116,101,120,59,10,10,111,117,116,32,104,105,103,104,112,32,118,101,99,52,32,112,111,115,105,116,105,111,110,95,105,110,116,101,114,112,59,10,10,118,111,105,100,32,109,97,105,110,40,41,32,123,10,9,112,111,115,105,116,105,111,110,95,105,110,116,101,114,112,32,61,32,118,101,99,52,40,118,101,114,116,101,120,46,120,44,49,44,48,44,49,41,59,10,125,10,10, 0};
|
||||
R"<!>(
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
|
||||
layout(location = 0) in highp vec3 vertex;
|
||||
|
||||
out highp vec4 position_interp;
|
||||
|
||||
void main() {
|
||||
position_interp = vec4(vertex.x,1,0,1);
|
||||
}
|
||||
|
||||
)<!>"
|
||||
};
|
||||
|
||||
static const char _fragment_code[]={
|
||||
10,112,114,101,99,105,115,105,111,110,32,104,105,103,104,112,32,102,108,111,97,116,59,10,112,114,101,99,105,115,105,111,110,32,104,105,103,104,112,32,105,110,116,59,10,10,105,110,32,104,105,103,104,112,32,118,101,99,52,32,112,111,115,105,116,105,111,110,95,105,110,116,101,114,112,59,10,10,118,111,105,100,32,109,97,105,110,40,41,32,123,10,9,104,105,103,104,112,32,102,108,111,97,116,32,100,101,112,116,104,32,61,32,40,40,112,111,115,105,116,105,111,110,95,105,110,116,101,114,112,46,122,32,47,32,112,111,115,105,116,105,111,110,95,105,110,116,101,114,112,46,119,41,32,43,32,49,46,48,41,59,10,9,102,114,97,103,95,99,111,108,111,114,32,61,32,118,101,99,52,40,100,101,112,116,104,41,59,10,125,10, 0};
|
||||
R"<!>(
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
|
||||
in highp vec4 position_interp;
|
||||
|
||||
void main() {
|
||||
highp float depth = ((position_interp.z / position_interp.w) + 1.0);
|
||||
frag_color = vec4(depth);
|
||||
}
|
||||
)<!>"
|
||||
};
|
||||
|
||||
_setup(_vertex_code,_fragment_code,"VertexFragmentShaderGLES3",0,_uniform_strings,0,_ubo_pairs,0,_feedbacks,0,_texunit_pairs,1,_spec_pairs,1,_variant_defines);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,18 @@
|
|||
#define COMPUTE_SHADER_GLSL_RAW_H
|
||||
|
||||
static const char compute_shader_glsl[] = {
|
||||
35,91,99,111,109,112,117,116,101,93,10,10,35,118,101,114,115,105,111,110,32,52,53,48,10,10,35,86,69,82,83,73,79,78,95,68,69,70,73,78,69,83,10,10,10,35,100,101,102,105,110,101,32,77,95,80,73,32,51,46,49,52,49,53,57,50,54,53,51,53,57,10,10,118,111,105,100,32,109,97,105,110,40,41,32,123,10,9,118,101,99,51,32,115,116,97,116,105,99,95,108,105,103,104,116,32,61,32,118,101,99,51,40,48,44,32,49,44,32,48,41,59,10,125,10,0
|
||||
R"<!>(#[compute]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
|
||||
#define M_PI 3.14159265359
|
||||
|
||||
void main() {
|
||||
vec3 static_light = vec3(0, 1, 0);
|
||||
}
|
||||
)<!>"
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -3,6 +3,38 @@
|
|||
#define VERTEX_FRAGMENT_SHADER_GLSL_RAW_H
|
||||
|
||||
static const char vertex_fragment_shader_glsl[] = {
|
||||
35,91,118,101,114,115,105,111,110,115,93,10,10,108,105,110,101,115,32,61,32,34,35,100,101,102,105,110,101,32,77,79,68,69,95,76,73,78,69,83,34,59,10,10,35,91,118,101,114,116,101,120,93,10,10,35,118,101,114,115,105,111,110,32,52,53,48,10,10,35,86,69,82,83,73,79,78,95,68,69,70,73,78,69,83,10,10,108,97,121,111,117,116,40,108,111,99,97,116,105,111,110,32,61,32,48,41,32,111,117,116,32,118,101,99,51,32,117,118,95,105,110,116,101,114,112,59,10,10,118,111,105,100,32,109,97,105,110,40,41,32,123,10,10,35,105,102,100,101,102,32,77,79,68,69,95,76,73,78,69,83,10,9,117,118,95,105,110,116,101,114,112,32,61,32,118,101,99,51,40,48,44,48,44,49,41,59,10,35,101,110,100,105,102,10,125,10,10,35,91,102,114,97,103,109,101,110,116,93,10,10,35,118,101,114,115,105,111,110,32,52,53,48,10,10,35,86,69,82,83,73,79,78,95,68,69,70,73,78,69,83,10,10,35,100,101,102,105,110,101,32,77,95,80,73,32,51,46,49,52,49,53,57,50,54,53,51,53,57,10,10,108,97,121,111,117,116,40,108,111,99,97,116,105,111,110,32,61,32,48,41,32,111,117,116,32,118,101,99,52,32,100,115,116,95,99,111,108,111,114,59,10,10,118,111,105,100,32,109,97,105,110,40,41,32,123,10,9,100,115,116,95,99,111,108,111,114,32,61,32,118,101,99,52,40,49,44,49,44,48,44,48,41,59,10,125,10,0
|
||||
R"<!>(#[versions]
|
||||
|
||||
lines = "#define MODE_LINES";
|
||||
|
||||
#[vertex]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(location = 0) out vec3 uv_interp;
|
||||
|
||||
void main() {
|
||||
|
||||
#ifdef MODE_LINES
|
||||
uv_interp = vec3(0,0,1);
|
||||
#endif
|
||||
}
|
||||
|
||||
#[fragment]
|
||||
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#define M_PI 3.14159265359
|
||||
|
||||
layout(location = 0) out vec4 dst_color;
|
||||
|
||||
void main() {
|
||||
dst_color = vec4(1,1,0,0);
|
||||
}
|
||||
)<!>"
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -11,7 +11,19 @@ public:
|
|||
ComputeShaderRD() {
|
||||
|
||||
static const char _compute_code[] = {
|
||||
10,35,118,101,114,115,105,111,110,32,52,53,48,10,10,35,86,69,82,83,73,79,78,95,68,69,70,73,78,69,83,10,10,35,100,101,102,105,110,101,32,66,76,79,67,75,95,83,73,90,69,32,56,10,10,35,100,101,102,105,110,101,32,77,95,80,73,32,51,46,49,52,49,53,57,50,54,53,51,53,57,10,10,118,111,105,100,32,109,97,105,110,40,41,32,123,10,9,117,105,110,116,32,116,32,61,32,66,76,79,67,75,95,83,73,90,69,32,43,32,49,59,10,125,10,0
|
||||
R"<!>(
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#define BLOCK_SIZE 8
|
||||
|
||||
#define M_PI 3.14159265359
|
||||
|
||||
void main() {
|
||||
uint t = BLOCK_SIZE + 1;
|
||||
}
|
||||
)<!>"
|
||||
};
|
||||
setup(nullptr, nullptr, _compute_code, "ComputeShaderRD");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,10 +11,33 @@ public:
|
|||
VertexFragmentShaderRD() {
|
||||
|
||||
static const char _vertex_code[] = {
|
||||
10,35,118,101,114,115,105,111,110,32,52,53,48,10,10,35,86,69,82,83,73,79,78,95,68,69,70,73,78,69,83,10,10,35,100,101,102,105,110,101,32,77,95,80,73,32,51,46,49,52,49,53,57,50,54,53,51,53,57,10,10,108,97,121,111,117,116,40,108,111,99,97,116,105,111,110,32,61,32,48,41,32,111,117,116,32,118,101,99,50,32,117,118,95,105,110,116,101,114,112,59,10,10,118,111,105,100,32,109,97,105,110,40,41,32,123,10,9,117,118,95,105,110,116,101,114,112,32,61,32,118,101,99,50,40,48,44,32,49,41,59,10,125,10,10,0
|
||||
R"<!>(
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
#define M_PI 3.14159265359
|
||||
|
||||
layout(location = 0) out vec2 uv_interp;
|
||||
|
||||
void main() {
|
||||
uv_interp = vec2(0, 1);
|
||||
}
|
||||
|
||||
)<!>"
|
||||
};
|
||||
static const char _fragment_code[] = {
|
||||
10,35,118,101,114,115,105,111,110,32,52,53,48,10,10,35,86,69,82,83,73,79,78,95,68,69,70,73,78,69,83,10,10,108,97,121,111,117,116,40,108,111,99,97,116,105,111,110,32,61,32,48,41,32,105,110,32,118,101,99,50,32,117,118,95,105,110,116,101,114,112,59,10,10,118,111,105,100,32,109,97,105,110,40,41,32,123,10,9,117,118,95,105,110,116,101,114,112,32,61,32,118,101,99,50,40,49,44,32,48,41,59,10,125,10,0
|
||||
R"<!>(
|
||||
#version 450
|
||||
|
||||
#VERSION_DEFINES
|
||||
|
||||
layout(location = 0) in vec2 uv_interp;
|
||||
|
||||
void main() {
|
||||
uv_interp = vec2(1, 0);
|
||||
}
|
||||
)<!>"
|
||||
};
|
||||
setup(_vertex_code, _fragment_code, nullptr, "VertexFragmentShaderRD");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@
|
|||
#include "scene/resources/3d/primitive_meshes.h"
|
||||
#include "servers/navigation_server_3d.h"
|
||||
|
||||
#include "modules/navigation/nav_utils.h"
|
||||
|
||||
namespace TestNavigationServer3D {
|
||||
|
||||
// TODO: Find a more generic way to create `Callable` mocks.
|
||||
|
|
@ -48,7 +50,7 @@ public:
|
|||
}
|
||||
|
||||
unsigned function1_calls{ 0 };
|
||||
Variant function1_latest_arg0{};
|
||||
Variant function1_latest_arg0;
|
||||
};
|
||||
|
||||
static inline Array build_array() {
|
||||
|
|
@ -61,6 +63,32 @@ static inline Array build_array(Variant item, Targs... Fargs) {
|
|||
return a;
|
||||
}
|
||||
|
||||
struct GreaterThan {
|
||||
bool operator()(int p_a, int p_b) const { return p_a > p_b; }
|
||||
};
|
||||
|
||||
struct CompareArrayValues {
|
||||
const int *array;
|
||||
|
||||
CompareArrayValues(const int *p_array) :
|
||||
array(p_array) {}
|
||||
|
||||
bool operator()(uint32_t p_index_a, uint32_t p_index_b) const {
|
||||
return array[p_index_a] < array[p_index_b];
|
||||
}
|
||||
};
|
||||
|
||||
struct RegisterHeapIndexes {
|
||||
uint32_t *indexes;
|
||||
|
||||
RegisterHeapIndexes(uint32_t *p_indexes) :
|
||||
indexes(p_indexes) {}
|
||||
|
||||
void operator()(uint32_t p_vector_index, uint32_t p_heap_index) {
|
||||
indexes[p_vector_index] = p_heap_index;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_SUITE("[Navigation]") {
|
||||
TEST_CASE("[NavigationServer3D] Server should be empty when initialized") {
|
||||
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
|
||||
|
|
@ -545,6 +573,7 @@ TEST_SUITE("[Navigation]") {
|
|||
RID map = navigation_server->map_create();
|
||||
RID region = navigation_server->region_create();
|
||||
Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
|
||||
navigation_server->map_set_use_async_iterations(map, false);
|
||||
navigation_server->map_set_active(map, true);
|
||||
navigation_server->region_set_map(region, map);
|
||||
navigation_server->region_set_navigation_mesh(region, navigation_mesh);
|
||||
|
|
@ -605,7 +634,7 @@ TEST_SUITE("[Navigation]") {
|
|||
CHECK_EQ(source_geometry->get_indices().size(), 6);
|
||||
}
|
||||
|
||||
SUBCASE("Parsed geometry should be extendible with other geometry") {
|
||||
SUBCASE("Parsed geometry should be extendable with other geometry") {
|
||||
source_geometry->merge(source_geometry); // Merging with itself.
|
||||
const Vector<float> vertices = source_geometry->get_vertices();
|
||||
const Vector<int> indices = source_geometry->get_indices();
|
||||
|
|
@ -640,6 +669,7 @@ TEST_SUITE("[Navigation]") {
|
|||
RID map = navigation_server->map_create();
|
||||
RID region = navigation_server->region_create();
|
||||
Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
|
||||
navigation_server->map_set_use_async_iterations(map, false);
|
||||
navigation_server->map_set_active(map, true);
|
||||
navigation_server->region_set_map(region, map);
|
||||
navigation_server->region_set_navigation_mesh(region, navigation_mesh);
|
||||
|
|
@ -689,6 +719,7 @@ TEST_SUITE("[Navigation]") {
|
|||
RID map = navigation_server->map_create();
|
||||
RID region = navigation_server->region_create();
|
||||
navigation_server->map_set_active(map, true);
|
||||
navigation_server->map_set_use_async_iterations(map, false);
|
||||
navigation_server->region_set_map(region, map);
|
||||
navigation_server->region_set_navigation_mesh(region, navigation_mesh);
|
||||
navigation_server->process(0.0); // Give server some cycles to commit.
|
||||
|
|
@ -788,6 +819,154 @@ TEST_SUITE("[Navigation]") {
|
|||
CHECK_EQ(navigation_mesh->get_vertices().size(), 0);
|
||||
}
|
||||
*/
|
||||
|
||||
TEST_CASE("[NavigationServer3D] Server should simplify path properly") {
|
||||
real_t simplify_epsilon = 0.2;
|
||||
Vector<Vector3> source_path;
|
||||
source_path.resize(7);
|
||||
source_path.write[0] = Vector3(0.0, 0.0, 0.0);
|
||||
source_path.write[1] = Vector3(0.0, 0.0, 1.0); // This point needs to go.
|
||||
source_path.write[2] = Vector3(0.0, 0.0, 2.0); // This point needs to go.
|
||||
source_path.write[3] = Vector3(0.0, 0.0, 2.0);
|
||||
source_path.write[4] = Vector3(2.0, 1.0, 3.0);
|
||||
source_path.write[5] = Vector3(2.0, 1.5, 4.0); // This point needs to go.
|
||||
source_path.write[6] = Vector3(2.0, 2.0, 5.0);
|
||||
Vector<Vector3> simplified_path = NavigationServer3D::get_singleton()->simplify_path(source_path, simplify_epsilon);
|
||||
CHECK_EQ(simplified_path.size(), 4);
|
||||
}
|
||||
|
||||
TEST_CASE("[Heap] size") {
|
||||
gd::Heap<int> heap;
|
||||
|
||||
CHECK(heap.size() == 0);
|
||||
|
||||
heap.push(0);
|
||||
CHECK(heap.size() == 1);
|
||||
|
||||
heap.push(1);
|
||||
CHECK(heap.size() == 2);
|
||||
|
||||
heap.pop();
|
||||
CHECK(heap.size() == 1);
|
||||
|
||||
heap.pop();
|
||||
CHECK(heap.size() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("[Heap] is_empty") {
|
||||
gd::Heap<int> heap;
|
||||
|
||||
CHECK(heap.is_empty() == true);
|
||||
|
||||
heap.push(0);
|
||||
CHECK(heap.is_empty() == false);
|
||||
|
||||
heap.pop();
|
||||
CHECK(heap.is_empty() == true);
|
||||
}
|
||||
|
||||
TEST_CASE("[Heap] push/pop") {
|
||||
SUBCASE("Default comparator") {
|
||||
gd::Heap<int> heap;
|
||||
|
||||
heap.push(2);
|
||||
heap.push(7);
|
||||
heap.push(5);
|
||||
heap.push(3);
|
||||
heap.push(4);
|
||||
|
||||
CHECK(heap.pop() == 7);
|
||||
CHECK(heap.pop() == 5);
|
||||
CHECK(heap.pop() == 4);
|
||||
CHECK(heap.pop() == 3);
|
||||
CHECK(heap.pop() == 2);
|
||||
}
|
||||
|
||||
SUBCASE("Custom comparator") {
|
||||
GreaterThan greaterThan;
|
||||
gd::Heap<int, GreaterThan> heap(greaterThan);
|
||||
|
||||
heap.push(2);
|
||||
heap.push(7);
|
||||
heap.push(5);
|
||||
heap.push(3);
|
||||
heap.push(4);
|
||||
|
||||
CHECK(heap.pop() == 2);
|
||||
CHECK(heap.pop() == 3);
|
||||
CHECK(heap.pop() == 4);
|
||||
CHECK(heap.pop() == 5);
|
||||
CHECK(heap.pop() == 7);
|
||||
}
|
||||
|
||||
SUBCASE("Intermediate pops") {
|
||||
gd::Heap<int> heap;
|
||||
|
||||
heap.push(0);
|
||||
heap.push(3);
|
||||
heap.pop();
|
||||
heap.push(1);
|
||||
heap.push(2);
|
||||
|
||||
CHECK(heap.pop() == 2);
|
||||
CHECK(heap.pop() == 1);
|
||||
CHECK(heap.pop() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[Heap] shift") {
|
||||
int values[] = { 5, 3, 6, 7, 1 };
|
||||
uint32_t heap_indexes[] = { 0, 0, 0, 0, 0 };
|
||||
CompareArrayValues comparator(values);
|
||||
RegisterHeapIndexes indexer(heap_indexes);
|
||||
gd::Heap<uint32_t, CompareArrayValues, RegisterHeapIndexes> heap(comparator, indexer);
|
||||
|
||||
heap.push(0);
|
||||
heap.push(1);
|
||||
heap.push(2);
|
||||
heap.push(3);
|
||||
heap.push(4);
|
||||
|
||||
// Shift down: 6 -> 2
|
||||
values[2] = 2;
|
||||
heap.shift(heap_indexes[2]);
|
||||
|
||||
// Shift up: 5 -> 8
|
||||
values[0] = 8;
|
||||
heap.shift(heap_indexes[0]);
|
||||
|
||||
CHECK(heap.pop() == 0);
|
||||
CHECK(heap.pop() == 3);
|
||||
CHECK(heap.pop() == 1);
|
||||
CHECK(heap.pop() == 2);
|
||||
CHECK(heap.pop() == 4);
|
||||
|
||||
CHECK(heap_indexes[0] == UINT32_MAX);
|
||||
CHECK(heap_indexes[1] == UINT32_MAX);
|
||||
CHECK(heap_indexes[2] == UINT32_MAX);
|
||||
CHECK(heap_indexes[3] == UINT32_MAX);
|
||||
CHECK(heap_indexes[4] == UINT32_MAX);
|
||||
}
|
||||
|
||||
TEST_CASE("[Heap] clear") {
|
||||
uint32_t heap_indexes[] = { 0, 0, 0, 0 };
|
||||
RegisterHeapIndexes indexer(heap_indexes);
|
||||
gd::Heap<uint32_t, Comparator<uint32_t>, RegisterHeapIndexes> heap(indexer);
|
||||
|
||||
heap.push(0);
|
||||
heap.push(2);
|
||||
heap.push(1);
|
||||
heap.push(3);
|
||||
|
||||
heap.clear();
|
||||
|
||||
CHECK(heap.size() == 0);
|
||||
|
||||
CHECK(heap_indexes[0] == UINT32_MAX);
|
||||
CHECK(heap_indexes[1] == UINT32_MAX);
|
||||
CHECK(heap_indexes[2] == UINT32_MAX);
|
||||
CHECK(heap_indexes[3] == UINT32_MAX);
|
||||
}
|
||||
}
|
||||
} //namespace TestNavigationServer3D
|
||||
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ TEST_SUITE("[TextServer]") {
|
|||
RID font1 = ts->create_font();
|
||||
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
|
||||
RID font2 = ts->create_font();
|
||||
ts->font_set_data_ptr(font2, _font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size);
|
||||
ts->font_set_data_ptr(font2, _font_Vazirmatn_Regular, _font_Vazirmatn_Regular_size);
|
||||
|
||||
Array font;
|
||||
font.push_back(font1);
|
||||
|
|
@ -180,7 +180,7 @@ TEST_SUITE("[TextServer]") {
|
|||
ts->font_set_data_ptr(font2, _font_NotoSansThai_Regular, _font_NotoSansThai_Regular_size);
|
||||
ts->font_set_allow_system_fallback(font2, false);
|
||||
RID font3 = ts->create_font();
|
||||
ts->font_set_data_ptr(font3, _font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size);
|
||||
ts->font_set_data_ptr(font3, _font_Vazirmatn_Regular, _font_Vazirmatn_Regular_size);
|
||||
ts->font_set_allow_system_fallback(font3, false);
|
||||
|
||||
Array font;
|
||||
|
|
@ -461,7 +461,7 @@ TEST_SUITE("[TextServer]") {
|
|||
ts->free_rid(ctx);
|
||||
}
|
||||
|
||||
if (ts->has_feature(TextServer::FEATURE_BREAK_ITERATORS)) {
|
||||
if (ts->has_feature(TextServer::FEATURE_BREAK_ITERATORS)) { // Line breaking opportunities.
|
||||
String test = U"เป็นภาษาราชการและภาษา";
|
||||
RID ctx = ts->create_shaped_text();
|
||||
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
|
||||
|
|
@ -489,6 +489,67 @@ TEST_SUITE("[TextServer]") {
|
|||
ts->free_rid(ctx);
|
||||
}
|
||||
|
||||
if (ts->has_feature(TextServer::FEATURE_BREAK_ITERATORS)) { // Break line.
|
||||
struct TestCase {
|
||||
String text;
|
||||
PackedInt32Array breaks;
|
||||
};
|
||||
TestCase cases[] = {
|
||||
{ U" เมาส์ตัวนี้", { 0, 17, 17, 23 } },
|
||||
{ U" กู้ไฟล์", { 0, 17, 17, 21 } },
|
||||
{ U" ไม่มีคำ", { 0, 18, 18, 20 } },
|
||||
{ U" ไม่มีคำพูด", { 0, 18, 18, 23 } },
|
||||
{ U" ไม่มีคำ", { 0, 17, 17, 19 } },
|
||||
{ U" มีอุปกรณ์\nนี้", { 0, 11, 11, 19, 19, 22 } },
|
||||
{ U"الحمدا لحمدا لحمـــد", { 0, 13, 13, 20 } },
|
||||
{ U" الحمد test", { 0, 15, 15, 19 } },
|
||||
{ U"الحمـد الرياضي العربي", { 0, 7, 7, 15, 15, 21 } },
|
||||
{ U"test \rtest", { 0, 6, 6, 10 } },
|
||||
{ U"test\r test", { 0, 5, 5, 10 } },
|
||||
{ U"test\r test \r test", { 0, 5, 5, 12, 12, 17 } },
|
||||
};
|
||||
for (size_t j = 0; j < sizeof(cases) / sizeof(TestCase); j++) {
|
||||
RID ctx = ts->create_shaped_text();
|
||||
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
|
||||
bool ok = ts->shaped_text_add_string(ctx, cases[j].text, font, 16);
|
||||
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
|
||||
|
||||
PackedInt32Array breaks = ts->shaped_text_get_line_breaks(ctx, 90.0);
|
||||
CHECK_FALSE_MESSAGE(breaks != cases[j].breaks, "Invalid break points.");
|
||||
|
||||
breaks = ts->shaped_text_get_line_breaks_adv(ctx, { 90.0 }, 0, false);
|
||||
CHECK_FALSE_MESSAGE(breaks != cases[j].breaks, "Invalid break points.");
|
||||
|
||||
ts->free_rid(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
if (ts->has_feature(TextServer::FEATURE_BREAK_ITERATORS)) { // Break line and trim spaces.
|
||||
struct TestCase {
|
||||
String text;
|
||||
PackedInt32Array breaks;
|
||||
};
|
||||
TestCase cases[] = {
|
||||
{ U"test \rtest", { 0, 4, 6, 10 } },
|
||||
{ U"test\r test", { 0, 4, 6, 10 } },
|
||||
{ U"test\r test \r test", { 0, 4, 6, 10, 13, 17 } },
|
||||
};
|
||||
for (size_t j = 0; j < sizeof(cases) / sizeof(TestCase); j++) {
|
||||
RID ctx = ts->create_shaped_text();
|
||||
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
|
||||
bool ok = ts->shaped_text_add_string(ctx, cases[j].text, font, 16);
|
||||
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
|
||||
|
||||
PackedInt32Array breaks = ts->shaped_text_get_line_breaks(ctx, 90.0, 0, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES);
|
||||
CHECK_FALSE_MESSAGE(breaks != cases[j].breaks, "Invalid break points.");
|
||||
|
||||
breaks = ts->shaped_text_get_line_breaks_adv(ctx, { 90.0 }, 0, false, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES);
|
||||
CHECK_FALSE_MESSAGE(breaks != cases[j].breaks, "Invalid break points.");
|
||||
|
||||
ts->free_rid(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < font.size(); j++) {
|
||||
ts->free_rid(font[j]);
|
||||
}
|
||||
|
|
@ -535,6 +596,19 @@ TEST_SUITE("[TextServer]") {
|
|||
CHECK_FALSE_MESSAGE(brks[5] != 14, "Invalid line break position.");
|
||||
}
|
||||
|
||||
brks = ts->shaped_text_get_line_breaks(ctx, 35.0, 0, TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_EDGE_SPACES);
|
||||
CHECK_FALSE_MESSAGE(brks.size() != 6, "Invalid line breaks number.");
|
||||
if (brks.size() == 6) {
|
||||
CHECK_FALSE_MESSAGE(brks[0] != 0, "Invalid line break position.");
|
||||
CHECK_FALSE_MESSAGE(brks[1] != 4, "Invalid line break position.");
|
||||
|
||||
CHECK_FALSE_MESSAGE(brks[2] != 5, "Invalid line break position.");
|
||||
CHECK_FALSE_MESSAGE(brks[3] != 9, "Invalid line break position.");
|
||||
|
||||
CHECK_FALSE_MESSAGE(brks[4] != 10, "Invalid line break position.");
|
||||
CHECK_FALSE_MESSAGE(brks[5] != 14, "Invalid line break position.");
|
||||
}
|
||||
|
||||
ts->free_rid(ctx);
|
||||
|
||||
for (int j = 0; j < font.size(); j++) {
|
||||
|
|
@ -556,7 +630,7 @@ TEST_SUITE("[TextServer]") {
|
|||
RID font1 = ts->create_font();
|
||||
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
|
||||
RID font2 = ts->create_font();
|
||||
ts->font_set_data_ptr(font2, _font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size);
|
||||
ts->font_set_data_ptr(font2, _font_Vazirmatn_Regular, _font_Vazirmatn_Regular_size);
|
||||
|
||||
Array font;
|
||||
font.push_back(font1);
|
||||
|
|
@ -754,12 +828,12 @@ TEST_SUITE("[TextServer]") {
|
|||
SUBCASE("[TextServer] Word break") {
|
||||
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
|
||||
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
|
||||
CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
|
||||
|
||||
if (!ts->has_feature(TextServer::FEATURE_SIMPLE_LAYOUT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
|
||||
{
|
||||
String text1 = U"linguistically similar and effectively form";
|
||||
// 14^ 22^ 26^ 38^
|
||||
|
|
@ -857,6 +931,47 @@ TEST_SUITE("[TextServer]") {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("[TextServer] Buffer invalidation") {
|
||||
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
|
||||
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
|
||||
CHECK_FALSE_MESSAGE(ts.is_null(), "Invalid TS interface.");
|
||||
|
||||
if (!ts->has_feature(TextServer::FEATURE_SIMPLE_LAYOUT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RID font1 = ts->create_font();
|
||||
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
|
||||
|
||||
Array font;
|
||||
font.push_back(font1);
|
||||
|
||||
RID ctx = ts->create_shaped_text();
|
||||
CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed.");
|
||||
bool ok = ts->shaped_text_add_string(ctx, "T", font, 16);
|
||||
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
|
||||
int gl_size = ts->shaped_text_get_glyph_count(ctx);
|
||||
CHECK_MESSAGE(gl_size == 1, "Shaping failed, invalid glyph count");
|
||||
|
||||
ok = ts->shaped_text_add_object(ctx, "key", Size2(20, 20), INLINE_ALIGNMENT_CENTER, 1, 0.0);
|
||||
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
|
||||
gl_size = ts->shaped_text_get_glyph_count(ctx);
|
||||
CHECK_MESSAGE(gl_size == 2, "Shaping failed, invalid glyph count");
|
||||
|
||||
ok = ts->shaped_text_add_string(ctx, "B", font, 16);
|
||||
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
|
||||
gl_size = ts->shaped_text_get_glyph_count(ctx);
|
||||
CHECK_MESSAGE(gl_size == 3, "Shaping failed, invalid glyph count");
|
||||
|
||||
ts->free_rid(ctx);
|
||||
|
||||
for (int j = 0; j < font.size(); j++) {
|
||||
ts->free_rid(font[j]);
|
||||
}
|
||||
font.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}; // namespace TestTextServer
|
||||
|
|
|
|||
|
|
@ -93,8 +93,11 @@ DOCTEST_STRINGIFY_VARIANT(Rect2);
|
|||
DOCTEST_STRINGIFY_VARIANT(Rect2i);
|
||||
DOCTEST_STRINGIFY_VARIANT(Vector3);
|
||||
DOCTEST_STRINGIFY_VARIANT(Vector3i);
|
||||
DOCTEST_STRINGIFY_VARIANT(Vector4);
|
||||
DOCTEST_STRINGIFY_VARIANT(Vector4i);
|
||||
DOCTEST_STRINGIFY_VARIANT(Transform2D);
|
||||
DOCTEST_STRINGIFY_VARIANT(Plane);
|
||||
DOCTEST_STRINGIFY_VARIANT(Projection);
|
||||
DOCTEST_STRINGIFY_VARIANT(Quaternion);
|
||||
DOCTEST_STRINGIFY_VARIANT(AABB);
|
||||
DOCTEST_STRINGIFY_VARIANT(Basis);
|
||||
|
|
@ -171,20 +174,20 @@ int register_test_command(String p_command, TestFunc p_function);
|
|||
MessageQueue::get_singleton()->flush(); \
|
||||
}
|
||||
|
||||
#define _UPDATE_EVENT_MODIFERS(m_event, m_modifers) \
|
||||
m_event->set_shift_pressed(((m_modifers) & KeyModifierMask::SHIFT) != Key::NONE); \
|
||||
m_event->set_alt_pressed(((m_modifers) & KeyModifierMask::ALT) != Key::NONE); \
|
||||
m_event->set_ctrl_pressed(((m_modifers) & KeyModifierMask::CTRL) != Key::NONE); \
|
||||
m_event->set_meta_pressed(((m_modifers) & KeyModifierMask::META) != Key::NONE);
|
||||
#define _UPDATE_EVENT_MODIFIERS(m_event, m_modifiers) \
|
||||
m_event->set_shift_pressed(((m_modifiers) & KeyModifierMask::SHIFT) != Key::NONE); \
|
||||
m_event->set_alt_pressed(((m_modifiers) & KeyModifierMask::ALT) != Key::NONE); \
|
||||
m_event->set_ctrl_pressed(((m_modifiers) & KeyModifierMask::CTRL) != Key::NONE); \
|
||||
m_event->set_meta_pressed(((m_modifiers) & KeyModifierMask::META) != Key::NONE);
|
||||
|
||||
#define _CREATE_GUI_MOUSE_EVENT(m_screen_pos, m_input, m_mask, m_modifers) \
|
||||
Ref<InputEventMouseButton> event; \
|
||||
event.instantiate(); \
|
||||
event->set_position(m_screen_pos); \
|
||||
event->set_button_index(m_input); \
|
||||
event->set_button_mask(m_mask); \
|
||||
event->set_factor(1); \
|
||||
_UPDATE_EVENT_MODIFERS(event, m_modifers); \
|
||||
#define _CREATE_GUI_MOUSE_EVENT(m_screen_pos, m_input, m_mask, m_modifiers) \
|
||||
Ref<InputEventMouseButton> event; \
|
||||
event.instantiate(); \
|
||||
event->set_position(m_screen_pos); \
|
||||
event->set_button_index(m_input); \
|
||||
event->set_button_mask(m_mask); \
|
||||
event->set_factor(1); \
|
||||
_UPDATE_EVENT_MODIFIERS(event, m_modifiers); \
|
||||
event->set_pressed(true);
|
||||
|
||||
#define _CREATE_GUI_TOUCH_EVENT(m_screen_pos, m_pressed, m_double) \
|
||||
|
|
@ -194,42 +197,42 @@ int register_test_command(String p_command, TestFunc p_function);
|
|||
event->set_pressed(m_pressed); \
|
||||
event->set_double_tap(m_double);
|
||||
|
||||
#define SEND_GUI_MOUSE_BUTTON_EVENT(m_screen_pos, m_input, m_mask, m_modifers) \
|
||||
{ \
|
||||
_CREATE_GUI_MOUSE_EVENT(m_screen_pos, m_input, m_mask, m_modifers); \
|
||||
_SEND_DISPLAYSERVER_EVENT(event); \
|
||||
MessageQueue::get_singleton()->flush(); \
|
||||
#define SEND_GUI_MOUSE_BUTTON_EVENT(m_screen_pos, m_input, m_mask, m_modifiers) \
|
||||
{ \
|
||||
_CREATE_GUI_MOUSE_EVENT(m_screen_pos, m_input, m_mask, m_modifiers); \
|
||||
_SEND_DISPLAYSERVER_EVENT(event); \
|
||||
MessageQueue::get_singleton()->flush(); \
|
||||
}
|
||||
|
||||
#define SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(m_screen_pos, m_input, m_mask, m_modifers) \
|
||||
{ \
|
||||
_CREATE_GUI_MOUSE_EVENT(m_screen_pos, m_input, m_mask, m_modifers); \
|
||||
event->set_pressed(false); \
|
||||
_SEND_DISPLAYSERVER_EVENT(event); \
|
||||
MessageQueue::get_singleton()->flush(); \
|
||||
#define SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(m_screen_pos, m_input, m_mask, m_modifiers) \
|
||||
{ \
|
||||
_CREATE_GUI_MOUSE_EVENT(m_screen_pos, m_input, m_mask, m_modifiers); \
|
||||
event->set_pressed(false); \
|
||||
_SEND_DISPLAYSERVER_EVENT(event); \
|
||||
MessageQueue::get_singleton()->flush(); \
|
||||
}
|
||||
|
||||
#define SEND_GUI_DOUBLE_CLICK(m_screen_pos, m_modifers) \
|
||||
{ \
|
||||
_CREATE_GUI_MOUSE_EVENT(m_screen_pos, MouseButton::LEFT, 0, m_modifers); \
|
||||
event->set_double_click(true); \
|
||||
_SEND_DISPLAYSERVER_EVENT(event); \
|
||||
MessageQueue::get_singleton()->flush(); \
|
||||
#define SEND_GUI_DOUBLE_CLICK(m_screen_pos, m_modifiers) \
|
||||
{ \
|
||||
_CREATE_GUI_MOUSE_EVENT(m_screen_pos, MouseButton::LEFT, 0, m_modifiers); \
|
||||
event->set_double_click(true); \
|
||||
_SEND_DISPLAYSERVER_EVENT(event); \
|
||||
MessageQueue::get_singleton()->flush(); \
|
||||
}
|
||||
|
||||
// We toggle _print_error_enabled to prevent display server not supported warnings.
|
||||
#define SEND_GUI_MOUSE_MOTION_EVENT(m_screen_pos, m_mask, m_modifers) \
|
||||
{ \
|
||||
bool errors_enabled = CoreGlobals::print_error_enabled; \
|
||||
CoreGlobals::print_error_enabled = false; \
|
||||
Ref<InputEventMouseMotion> event; \
|
||||
event.instantiate(); \
|
||||
event->set_position(m_screen_pos); \
|
||||
event->set_button_mask(m_mask); \
|
||||
_UPDATE_EVENT_MODIFERS(event, m_modifers); \
|
||||
_SEND_DISPLAYSERVER_EVENT(event); \
|
||||
MessageQueue::get_singleton()->flush(); \
|
||||
CoreGlobals::print_error_enabled = errors_enabled; \
|
||||
#define SEND_GUI_MOUSE_MOTION_EVENT(m_screen_pos, m_mask, m_modifiers) \
|
||||
{ \
|
||||
bool errors_enabled = CoreGlobals::print_error_enabled; \
|
||||
CoreGlobals::print_error_enabled = false; \
|
||||
Ref<InputEventMouseMotion> event; \
|
||||
event.instantiate(); \
|
||||
event->set_position(m_screen_pos); \
|
||||
event->set_button_mask(m_mask); \
|
||||
_UPDATE_EVENT_MODIFIERS(event, m_modifiers); \
|
||||
_SEND_DISPLAYSERVER_EVENT(event); \
|
||||
MessageQueue::get_singleton()->flush(); \
|
||||
CoreGlobals::print_error_enabled = errors_enabled; \
|
||||
}
|
||||
|
||||
#define SEND_GUI_TOUCH_EVENT(m_screen_pos, m_pressed, m_double) \
|
||||
|
|
@ -376,6 +379,9 @@ public:
|
|||
|
||||
bool check_false(const String &p_name) {
|
||||
bool has = _signals.has(p_name);
|
||||
if (has) {
|
||||
MESSAGE("Signal has " << _signals[p_name] << " expected none.");
|
||||
}
|
||||
discard_signal(p_name);
|
||||
return !has;
|
||||
}
|
||||
|
|
@ -471,6 +477,6 @@ public:
|
|||
for (int i = 0; i < string_list.size(); ++i) { \
|
||||
CHECK(string_list[i] == m_slices[i]); \
|
||||
} \
|
||||
} while (0)
|
||||
} while (false)
|
||||
|
||||
#endif // TEST_MACROS_H
|
||||
|
|
|
|||
|
|
@ -48,9 +48,17 @@
|
|||
#include "tests/core/io/test_image.h"
|
||||
#include "tests/core/io/test_ip.h"
|
||||
#include "tests/core/io/test_json.h"
|
||||
#include "tests/core/io/test_json_native.h"
|
||||
#include "tests/core/io/test_logger.h"
|
||||
#include "tests/core/io/test_marshalls.h"
|
||||
#include "tests/core/io/test_packet_peer.h"
|
||||
#include "tests/core/io/test_pck_packer.h"
|
||||
#include "tests/core/io/test_resource.h"
|
||||
#include "tests/core/io/test_resource_uid.h"
|
||||
#include "tests/core/io/test_stream_peer.h"
|
||||
#include "tests/core/io/test_stream_peer_buffer.h"
|
||||
#include "tests/core/io/test_tcp_server.h"
|
||||
#include "tests/core/io/test_udp_server.h"
|
||||
#include "tests/core/io/test_xml_parser.h"
|
||||
#include "tests/core/math/test_aabb.h"
|
||||
#include "tests/core/math/test_astar.h"
|
||||
|
|
@ -61,6 +69,7 @@
|
|||
#include "tests/core/math/test_geometry_3d.h"
|
||||
#include "tests/core/math/test_math_funcs.h"
|
||||
#include "tests/core/math/test_plane.h"
|
||||
#include "tests/core/math/test_projection.h"
|
||||
#include "tests/core/math/test_quaternion.h"
|
||||
#include "tests/core/math/test_random_number_generator.h"
|
||||
#include "tests/core/math/test_rect2.h"
|
||||
|
|
@ -78,10 +87,12 @@
|
|||
#include "tests/core/object/test_object.h"
|
||||
#include "tests/core/object/test_undo_redo.h"
|
||||
#include "tests/core/os/test_os.h"
|
||||
#include "tests/core/string/test_fuzzy_search.h"
|
||||
#include "tests/core/string/test_node_path.h"
|
||||
#include "tests/core/string/test_string.h"
|
||||
#include "tests/core/string/test_translation.h"
|
||||
#include "tests/core/string/test_translation_server.h"
|
||||
#include "tests/core/templates/test_a_hash_map.h"
|
||||
#include "tests/core/templates/test_command_queue.h"
|
||||
#include "tests/core/templates/test_hash_map.h"
|
||||
#include "tests/core/templates/test_hash_set.h"
|
||||
|
|
@ -104,21 +115,28 @@
|
|||
#include "tests/scene/test_animation.h"
|
||||
#include "tests/scene/test_audio_stream_wav.h"
|
||||
#include "tests/scene/test_bit_map.h"
|
||||
#include "tests/scene/test_button.h"
|
||||
#include "tests/scene/test_camera_2d.h"
|
||||
#include "tests/scene/test_control.h"
|
||||
#include "tests/scene/test_curve.h"
|
||||
#include "tests/scene/test_curve_2d.h"
|
||||
#include "tests/scene/test_curve_3d.h"
|
||||
#include "tests/scene/test_fontfile.h"
|
||||
#include "tests/scene/test_gradient.h"
|
||||
#include "tests/scene/test_gradient_texture.h"
|
||||
#include "tests/scene/test_image_texture.h"
|
||||
#include "tests/scene/test_image_texture_3d.h"
|
||||
#include "tests/scene/test_instance_placeholder.h"
|
||||
#include "tests/scene/test_node.h"
|
||||
#include "tests/scene/test_node_2d.h"
|
||||
#include "tests/scene/test_packed_scene.h"
|
||||
#include "tests/scene/test_parallax_2d.h"
|
||||
#include "tests/scene/test_path_2d.h"
|
||||
#include "tests/scene/test_path_follow_2d.h"
|
||||
#include "tests/scene/test_physics_material.h"
|
||||
#include "tests/scene/test_sprite_frames.h"
|
||||
#include "tests/scene/test_style_box_texture.h"
|
||||
#include "tests/scene/test_texture_progress_bar.h"
|
||||
#include "tests/scene/test_theme.h"
|
||||
#include "tests/scene/test_timer.h"
|
||||
#include "tests/scene/test_viewport.h"
|
||||
|
|
@ -132,7 +150,12 @@
|
|||
#include "tests/scene/test_code_edit.h"
|
||||
#include "tests/scene/test_color_picker.h"
|
||||
#include "tests/scene/test_graph_node.h"
|
||||
#include "tests/scene/test_option_button.h"
|
||||
#include "tests/scene/test_split_container.h"
|
||||
#include "tests/scene/test_tab_bar.h"
|
||||
#include "tests/scene/test_tab_container.h"
|
||||
#include "tests/scene/test_text_edit.h"
|
||||
#include "tests/scene/test_tree.h"
|
||||
#endif // ADVANCED_GUI_DISABLED
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
|
|
@ -149,9 +172,13 @@
|
|||
|
||||
#include "tests/scene/test_arraymesh.h"
|
||||
#include "tests/scene/test_camera_3d.h"
|
||||
#include "tests/scene/test_gltf_document.h"
|
||||
#include "tests/scene/test_height_map_shape_3d.h"
|
||||
#include "tests/scene/test_path_3d.h"
|
||||
#include "tests/scene/test_path_follow_3d.h"
|
||||
#include "tests/scene/test_primitives.h"
|
||||
#include "tests/scene/test_skeleton_3d.h"
|
||||
#include "tests/scene/test_sky.h"
|
||||
#endif // _3D_DISABLED
|
||||
|
||||
#include "modules/modules_tests.gen.h"
|
||||
|
|
@ -165,8 +192,10 @@
|
|||
#include "servers/navigation_server_3d.h"
|
||||
#endif // _3D_DISABLED
|
||||
#include "servers/physics_server_2d.h"
|
||||
#include "servers/physics_server_2d_dummy.h"
|
||||
#ifndef _3D_DISABLED
|
||||
#include "servers/physics_server_3d.h"
|
||||
#include "servers/physics_server_3d_dummy.h"
|
||||
#endif // _3D_DISABLED
|
||||
#include "servers/rendering/rendering_server_default.h"
|
||||
|
||||
|
|
@ -265,7 +294,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
|
|||
OS::get_singleton()->set_has_server_feature_callback(nullptr);
|
||||
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
|
||||
if (String("mock") == DisplayServer::get_create_function_name(i)) {
|
||||
DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, nullptr, Vector2i(0, 0), DisplayServer::SCREEN_PRIMARY, DisplayServer::CONTEXT_EDITOR, err);
|
||||
DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, nullptr, Vector2i(0, 0), DisplayServer::SCREEN_PRIMARY, DisplayServer::CONTEXT_EDITOR, 0, err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -281,10 +310,16 @@ struct GodotTestCaseListener : public doctest::IReporter {
|
|||
|
||||
#ifndef _3D_DISABLED
|
||||
physics_server_3d = PhysicsServer3DManager::get_singleton()->new_default_server();
|
||||
if (!physics_server_3d) {
|
||||
physics_server_3d = memnew(PhysicsServer3DDummy);
|
||||
}
|
||||
physics_server_3d->init();
|
||||
#endif // _3D_DISABLED
|
||||
|
||||
physics_server_2d = PhysicsServer2DManager::get_singleton()->new_default_server();
|
||||
if (!physics_server_2d) {
|
||||
physics_server_2d = memnew(PhysicsServer2DDummy);
|
||||
}
|
||||
physics_server_2d->init();
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
|
|
@ -314,7 +349,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
|
|||
return;
|
||||
}
|
||||
|
||||
if (name.contains("Audio")) {
|
||||
if (name.contains("[Audio]")) {
|
||||
// The last driver index should always be the dummy driver.
|
||||
int dummy_idx = AudioDriverManager::get_driver_count() - 1;
|
||||
AudioDriverManager::initialize(dummy_idx);
|
||||
|
|
@ -338,6 +373,12 @@ struct GodotTestCaseListener : public doctest::IReporter {
|
|||
#ifdef TOOLS_ENABLED
|
||||
if (EditorSettings::get_singleton()) {
|
||||
EditorSettings::destroy();
|
||||
|
||||
// Instantiating the EditorSettings singleton sets the locale to the editor's language.
|
||||
TranslationServer::get_singleton()->set_locale("en");
|
||||
}
|
||||
if (EditorPaths::get_singleton()) {
|
||||
EditorPaths::free();
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue