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
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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue