feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -39,7 +39,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
|
||||
Loading…
Add table
Add a link
Reference in a new issue