X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Funittest%2Ftest_serialization.cpp;h=ca4116413ccb2fe690a5d6d45b0c301bc2c97873;hb=8e4095f06832f4a786a77f4513f6fca9c1f6cc2b;hp=b180df54e2d7ee48d745edbf21aba8781d7f3c94;hpb=6f07f79c2f36b007b4c0385b7df2fc4612af7aba;p=minetest.git diff --git a/src/unittest/test_serialization.cpp b/src/unittest/test_serialization.cpp index b180df54e..ca4116413 100644 --- a/src/unittest/test_serialization.cpp +++ b/src/unittest/test_serialization.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/string.h" #include "util/serialize.h" +#include class TestSerialization : public TestBase { public: @@ -38,10 +39,18 @@ class TestSerialization : public TestBase { void testDeSerializeString(); void testDeSerializeWideString(); void testDeSerializeLongString(); + void testStreamRead(); + void testStreamWrite(); + void testVecPut(); + void testStringLengthLimits(); + void testBufReader(); + void testFloatFormat(); std::string teststring2; std::wstring teststring2_w; std::string teststring2_w_encoded; + + static const u8 test_serialized_data[12 * 13]; }; static TestSerialization g_test_instance; @@ -58,6 +67,12 @@ void TestSerialization::runTests(IGameDef *gamedef) TEST(testDeSerializeLongString); TEST(testSerializeJsonString); TEST(testSerializeHex); + TEST(testStreamRead); + TEST(testStreamWrite); + TEST(testVecPut); + TEST(testStringLengthLimits); + TEST(testBufReader); + TEST(testFloatFormat); } //////////////////////////////////////////////////////////////////////////////// @@ -271,3 +286,449 @@ void TestSerialization::testSerializeHex() UASSERT(serializeHexString(mkstr("\x00\x0a\xb0\x63\x1f\x00\xff"), true) == "00 0a b0 63 1f 00 ff"); } + + +void TestSerialization::testStreamRead() +{ + std::string datastr( + (const char *)test_serialized_data, + sizeof(test_serialized_data)); + std::istringstream is(datastr, std::ios_base::binary); + + UASSERT(readU8(is) == 0x11); + UASSERT(readU16(is) == 0x2233); + UASSERT(readU32(is) == 0x44556677); + UASSERT(readU64(is) == 0x8899AABBCCDDEEFFLL); + + UASSERT(readS8(is) == -128); + UASSERT(readS16(is) == 30000); + UASSERT(readS32(is) == -6); + UASSERT(readS64(is) == -43); + + UASSERT(readF1000(is) == 53.534f); + UASSERT(readF1000(is) == -300000.32f); + UASSERT(readF1000(is) == F1000_MIN); + UASSERT(readF1000(is) == F1000_MAX); + + UASSERT(deSerializeString(is) == "foobar!"); + + UASSERT(readV2S16(is) == v2s16(500, 500)); + UASSERT(readV3S16(is) == v3s16(4207, 604, -30)); + UASSERT(readV2S32(is) == v2s32(1920, 1080)); + UASSERT(readV3S32(is) == v3s32(-400, 6400054, 290549855)); + UASSERT(readV2F1000(is) == v2f(500.656f, 350.345f)); + + UASSERT(deSerializeWideString(is) == L"\x02~woof~\x5455"); + + UASSERT(readV3F1000(is) == v3f(500, 10024.2f, -192.54f)); + UASSERT(readARGB8(is) == video::SColor(255, 128, 50, 128)); + + UASSERT(deSerializeLongString(is) == "some longer string here"); + + UASSERT(is.rdbuf()->in_avail() == 2); + UASSERT(readU16(is) == 0xF00D); + UASSERT(is.rdbuf()->in_avail() == 0); +} + + +void TestSerialization::testStreamWrite() +{ + std::ostringstream os(std::ios_base::binary); + std::string data; + + writeU8(os, 0x11); + writeU16(os, 0x2233); + writeU32(os, 0x44556677); + writeU64(os, 0x8899AABBCCDDEEFFLL); + + writeS8(os, -128); + writeS16(os, 30000); + writeS32(os, -6); + writeS64(os, -43); + + writeF1000(os, 53.53467f); + writeF1000(os, -300000.32f); + writeF1000(os, F1000_MIN); + writeF1000(os, F1000_MAX); + + os << serializeString("foobar!"); + + data = os.str(); + UASSERT(data.size() < sizeof(test_serialized_data)); + UASSERT(!memcmp(&data[0], test_serialized_data, data.size())); + + writeV2S16(os, v2s16(500, 500)); + writeV3S16(os, v3s16(4207, 604, -30)); + writeV2S32(os, v2s32(1920, 1080)); + writeV3S32(os, v3s32(-400, 6400054, 290549855)); + writeV2F1000(os, v2f(500.65661f, 350.34567f)); + + os << serializeWideString(L"\x02~woof~\x5455"); + + writeV3F1000(os, v3f(500, 10024.2f, -192.54f)); + writeARGB8(os, video::SColor(255, 128, 50, 128)); + + os << serializeLongString("some longer string here"); + + writeU16(os, 0xF00D); + + data = os.str(); + UASSERT(data.size() == sizeof(test_serialized_data)); + UASSERT(!memcmp(&data[0], test_serialized_data, sizeof(test_serialized_data))); +} + + +void TestSerialization::testVecPut() +{ + std::vector buf; + + putU8(&buf, 0x11); + putU16(&buf, 0x2233); + putU32(&buf, 0x44556677); + putU64(&buf, 0x8899AABBCCDDEEFFLL); + + putS8(&buf, -128); + putS16(&buf, 30000); + putS32(&buf, -6); + putS64(&buf, -43); + + putF1000(&buf, 53.53467f); + putF1000(&buf, -300000.32f); + putF1000(&buf, F1000_MIN); + putF1000(&buf, F1000_MAX); + + putString(&buf, "foobar!"); + + putV2S16(&buf, v2s16(500, 500)); + putV3S16(&buf, v3s16(4207, 604, -30)); + putV2S32(&buf, v2s32(1920, 1080)); + putV3S32(&buf, v3s32(-400, 6400054, 290549855)); + putV2F1000(&buf, v2f(500.65661f, 350.34567f)); + + putWideString(&buf, L"\x02~woof~\x5455"); + + putV3F1000(&buf, v3f(500, 10024.2f, -192.54f)); + putARGB8(&buf, video::SColor(255, 128, 50, 128)); + + putLongString(&buf, "some longer string here"); + + putU16(&buf, 0xF00D); + + UASSERT(buf.size() == sizeof(test_serialized_data)); + UASSERT(!memcmp(&buf[0], test_serialized_data, sizeof(test_serialized_data))); +} + + +void TestSerialization::testStringLengthLimits() +{ + std::vector buf; + std::string too_long(STRING_MAX_LEN + 1, 'A'); + std::string way_too_large(LONG_STRING_MAX_LEN + 1, 'B'); + std::wstring too_long_wide(WIDE_STRING_MAX_LEN + 1, L'C'); + + EXCEPTION_CHECK(SerializationError, putString(&buf, too_long)); + + putLongString(&buf, too_long); + too_long.resize(too_long.size() - 1); + putString(&buf, too_long); + + EXCEPTION_CHECK(SerializationError, putWideString(&buf, too_long_wide)); + too_long_wide.resize(too_long_wide.size() - 1); + putWideString(&buf, too_long_wide); +} + + +void TestSerialization::testBufReader() +{ + u8 u8_data; + u16 u16_data; + u32 u32_data; + u64 u64_data; + s8 s8_data; + s16 s16_data; + s32 s32_data; + s64 s64_data; + f32 f32_data, f32_data2, f32_data3, f32_data4; + video::SColor scolor_data; + v2s16 v2s16_data; + v3s16 v3s16_data; + v2s32 v2s32_data; + v3s32 v3s32_data; + v2f v2f_data; + v3f v3f_data; + std::string string_data; + std::wstring widestring_data; + std::string longstring_data; + u8 raw_data[10] = {0}; + + BufReader buf(test_serialized_data, sizeof(test_serialized_data)); + + // Try reading data like normal + UASSERT(buf.getU8() == 0x11); + UASSERT(buf.getU16() == 0x2233); + UASSERT(buf.getU32() == 0x44556677); + UASSERT(buf.getU64() == 0x8899AABBCCDDEEFFLL); + UASSERT(buf.getS8() == -128); + UASSERT(buf.getS16() == 30000); + UASSERT(buf.getS32() == -6); + UASSERT(buf.getS64() == -43); + UASSERT(buf.getF1000() == 53.534f); + UASSERT(buf.getF1000() == -300000.32f); + UASSERT(buf.getF1000() == F1000_MIN); + UASSERT(buf.getF1000() == F1000_MAX); + UASSERT(buf.getString() == "foobar!"); + UASSERT(buf.getV2S16() == v2s16(500, 500)); + UASSERT(buf.getV3S16() == v3s16(4207, 604, -30)); + UASSERT(buf.getV2S32() == v2s32(1920, 1080)); + UASSERT(buf.getV3S32() == v3s32(-400, 6400054, 290549855)); + UASSERT(buf.getV2F1000() == v2f(500.656f, 350.345f)); + UASSERT(buf.getWideString() == L"\x02~woof~\x5455"); + UASSERT(buf.getV3F1000() == v3f(500, 10024.2f, -192.54f)); + UASSERT(buf.getARGB8() == video::SColor(255, 128, 50, 128)); + UASSERT(buf.getLongString() == "some longer string here"); + + // Verify the offset and data is unchanged after a failed read + size_t orig_pos = buf.pos; + u32_data = 0; + UASSERT(buf.getU32NoEx(&u32_data) == false); + UASSERT(buf.pos == orig_pos); + UASSERT(u32_data == 0); + + // Now try the same for a failed string read + UASSERT(buf.getStringNoEx(&string_data) == false); + UASSERT(buf.pos == orig_pos); + UASSERT(string_data == ""); + + // Now try the same for a failed string read + UASSERT(buf.getWideStringNoEx(&widestring_data) == false); + UASSERT(buf.pos == orig_pos); + UASSERT(widestring_data == L""); + + UASSERT(buf.getU16() == 0xF00D); + + UASSERT(buf.remaining() == 0); + + // Check to make sure these each blow exceptions as they're supposed to + EXCEPTION_CHECK(SerializationError, buf.getU8()); + EXCEPTION_CHECK(SerializationError, buf.getU16()); + EXCEPTION_CHECK(SerializationError, buf.getU32()); + EXCEPTION_CHECK(SerializationError, buf.getU64()); + + EXCEPTION_CHECK(SerializationError, buf.getS8()); + EXCEPTION_CHECK(SerializationError, buf.getS16()); + EXCEPTION_CHECK(SerializationError, buf.getS32()); + EXCEPTION_CHECK(SerializationError, buf.getS64()); + + EXCEPTION_CHECK(SerializationError, buf.getF1000()); + EXCEPTION_CHECK(SerializationError, buf.getARGB8()); + + EXCEPTION_CHECK(SerializationError, buf.getV2S16()); + EXCEPTION_CHECK(SerializationError, buf.getV3S16()); + EXCEPTION_CHECK(SerializationError, buf.getV2S32()); + EXCEPTION_CHECK(SerializationError, buf.getV3S32()); + EXCEPTION_CHECK(SerializationError, buf.getV2F1000()); + EXCEPTION_CHECK(SerializationError, buf.getV3F1000()); + + EXCEPTION_CHECK(SerializationError, buf.getString()); + EXCEPTION_CHECK(SerializationError, buf.getWideString()); + EXCEPTION_CHECK(SerializationError, buf.getLongString()); + EXCEPTION_CHECK(SerializationError, + buf.getRawData(raw_data, sizeof(raw_data))); + + // See if we can skip backwards + buf.pos = 5; + UASSERT(buf.getRawDataNoEx(raw_data, 3) == true); + UASSERT(raw_data[0] == 0x66); + UASSERT(raw_data[1] == 0x77); + UASSERT(raw_data[2] == 0x88); + + UASSERT(buf.getU32() == 0x99AABBCC); + UASSERT(buf.pos == 12); + + // Now let's try it all over again using the NoEx variants + buf.pos = 0; + + UASSERT(buf.getU8NoEx(&u8_data)); + UASSERT(buf.getU16NoEx(&u16_data)); + UASSERT(buf.getU32NoEx(&u32_data)); + UASSERT(buf.getU64NoEx(&u64_data)); + + UASSERT(buf.getS8NoEx(&s8_data)); + UASSERT(buf.getS16NoEx(&s16_data)); + UASSERT(buf.getS32NoEx(&s32_data)); + UASSERT(buf.getS64NoEx(&s64_data)); + + UASSERT(buf.getF1000NoEx(&f32_data)); + UASSERT(buf.getF1000NoEx(&f32_data2)); + UASSERT(buf.getF1000NoEx(&f32_data3)); + UASSERT(buf.getF1000NoEx(&f32_data4)); + + UASSERT(buf.getStringNoEx(&string_data)); + UASSERT(buf.getV2S16NoEx(&v2s16_data)); + UASSERT(buf.getV3S16NoEx(&v3s16_data)); + UASSERT(buf.getV2S32NoEx(&v2s32_data)); + UASSERT(buf.getV3S32NoEx(&v3s32_data)); + UASSERT(buf.getV2F1000NoEx(&v2f_data)); + UASSERT(buf.getWideStringNoEx(&widestring_data)); + UASSERT(buf.getV3F1000NoEx(&v3f_data)); + UASSERT(buf.getARGB8NoEx(&scolor_data)); + + UASSERT(buf.getLongStringNoEx(&longstring_data)); + + // and make sure we got the correct data + UASSERT(u8_data == 0x11); + UASSERT(u16_data == 0x2233); + UASSERT(u32_data == 0x44556677); + UASSERT(u64_data == 0x8899AABBCCDDEEFFLL); + UASSERT(s8_data == -128); + UASSERT(s16_data == 30000); + UASSERT(s32_data == -6); + UASSERT(s64_data == -43); + UASSERT(f32_data == 53.534f); + UASSERT(f32_data2 == -300000.32f); + UASSERT(f32_data3 == F1000_MIN); + UASSERT(f32_data4 == F1000_MAX); + UASSERT(string_data == "foobar!"); + UASSERT(v2s16_data == v2s16(500, 500)); + UASSERT(v3s16_data == v3s16(4207, 604, -30)); + UASSERT(v2s32_data == v2s32(1920, 1080)); + UASSERT(v3s32_data == v3s32(-400, 6400054, 290549855)); + UASSERT(v2f_data == v2f(500.656f, 350.345f)); + UASSERT(widestring_data == L"\x02~woof~\x5455"); + UASSERT(v3f_data == v3f(500, 10024.2f, -192.54f)); + UASSERT(scolor_data == video::SColor(255, 128, 50, 128)); + UASSERT(longstring_data == "some longer string here"); + + UASSERT(buf.remaining() == 2); + UASSERT(buf.getRawDataNoEx(raw_data, 3) == false); + UASSERT(buf.remaining() == 2); + UASSERT(buf.getRawDataNoEx(raw_data, 2) == true); + UASSERT(raw_data[0] == 0xF0); + UASSERT(raw_data[1] == 0x0D); + UASSERT(buf.remaining() == 0); + + // Make sure no more available data causes a failure + UASSERT(!buf.getU8NoEx(&u8_data)); + UASSERT(!buf.getU16NoEx(&u16_data)); + UASSERT(!buf.getU32NoEx(&u32_data)); + UASSERT(!buf.getU64NoEx(&u64_data)); + + UASSERT(!buf.getS8NoEx(&s8_data)); + UASSERT(!buf.getS16NoEx(&s16_data)); + UASSERT(!buf.getS32NoEx(&s32_data)); + UASSERT(!buf.getS64NoEx(&s64_data)); + + UASSERT(!buf.getF1000NoEx(&f32_data)); + UASSERT(!buf.getARGB8NoEx(&scolor_data)); + + UASSERT(!buf.getV2S16NoEx(&v2s16_data)); + UASSERT(!buf.getV3S16NoEx(&v3s16_data)); + UASSERT(!buf.getV2S32NoEx(&v2s32_data)); + UASSERT(!buf.getV3S32NoEx(&v3s32_data)); + UASSERT(!buf.getV2F1000NoEx(&v2f_data)); + UASSERT(!buf.getV3F1000NoEx(&v3f_data)); + + UASSERT(!buf.getStringNoEx(&string_data)); + UASSERT(!buf.getWideStringNoEx(&widestring_data)); + UASSERT(!buf.getLongStringNoEx(&longstring_data)); + UASSERT(!buf.getRawDataNoEx(raw_data, sizeof(raw_data))); +} + +void TestSerialization::testFloatFormat() +{ + FloatType type = getFloatSerializationType(); + u32 i; + f32 fs, fm; + + // Check precision of float calculations on this platform + const std::unordered_map float_results = { + { 0.0f, 0x00000000UL }, + { 1.0f, 0x3F800000UL }, + { -1.0f, 0xBF800000UL }, + { 0.1f, 0x3DCCCCCDUL }, + { -0.1f, 0xBDCCCCCDUL }, + { 1945329.25f, 0x49ED778AUL }, + { -23298764.f, 0xCBB1C166UL }, + { 0.5f, 0x3F000000UL }, + { -0.5f, 0xBF000000UL } + }; + for (const auto &v : float_results) { + i = f32Tou32Slow(v.first); + if (std::abs((s64)v.second - i) > 32) { + printf("Inaccurate float values on %.9g, expected 0x%X, actual 0x%X\n", + v.first, v.second, i); + UASSERT(false); + } + + fs = u32Tof32Slow(v.second); + if (std::fabs(v.first - fs) > std::fabs(v.first * 0.000005f)) { + printf("Inaccurate float values on 0x%X, expected %.9g, actual 0x%.9g\n", + v.second, v.first, fs); + UASSERT(false); + } + } + + if (type == FLOATTYPE_SLOW) { + // conversion using memcpy is not possible + // Skip exact float comparison checks below + return; + } + + // The code below compares the IEEE conversion functions with a + // known good IEC559/IEEE754 implementation. This test neeeds + // IEC559 compliance in the compiler. +#if defined(__GNUC__) && (!defined(__STDC_IEC_559__) || defined(__FAST_MATH__)) + // GNU C++ lies about its IEC559 support when -ffast-math is active. + // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=84949 + bool is_iec559 = false; +#else + bool is_iec559 = std::numeric_limits::is_iec559; +#endif + if (!is_iec559) + return; + + auto test_single = [&fs, &fm](const u32 &i) -> bool { + memcpy(&fm, &i, 4); + fs = u32Tof32Slow(i); + if (fm != fs) { + printf("u32Tof32Slow failed on 0x%X, expected %.9g, actual %.9g\n", + i, fm, fs); + return false; + } + if (f32Tou32Slow(fs) != i) { + printf("f32Tou32Slow failed on %.9g, expected 0x%X, actual 0x%X\n", + fs, i, f32Tou32Slow(fs)); + return false; + } + return true; + }; + + // Use step of prime 277 to speed things up from 3 minutes to a few seconds + // Test from 0 to 0xFF800000UL (positive) + for (i = 0x00000000UL; i <= 0x7F800000UL; i += 277) + UASSERT(test_single(i)); + + // Ensure +inf and -inf are tested + UASSERT(test_single(0x7F800000UL)); + UASSERT(test_single(0xFF800000UL)); + + // Test from 0x80000000UL to 0xFF800000UL (negative) + for (i = 0x80000000UL; i <= 0xFF800000UL; i += 277) + UASSERT(test_single(i)); +} + +const u8 TestSerialization::test_serialized_data[12 * 13] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, + 0xdd, 0xee, 0xff, 0x80, 0x75, 0x30, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xd5, 0x00, 0x00, 0xd1, 0x1e, 0xee, 0x1e, + 0x5b, 0xc0, 0x80, 0x00, 0x02, 0x80, 0x7F, 0xFF, 0xFD, 0x80, 0x00, 0x07, + 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x21, 0x01, 0xf4, 0x01, 0xf4, 0x10, + 0x6f, 0x02, 0x5c, 0xff, 0xe2, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x04, + 0x38, 0xff, 0xff, 0xfe, 0x70, 0x00, 0x61, 0xa8, 0x36, 0x11, 0x51, 0x70, + 0x5f, 0x00, 0x07, 0xa3, 0xb0, 0x00, 0x05, 0x58, 0x89, 0x00, 0x08, 0x00, + 0x02, 0x00, 0x7e, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x66, 0x00, + 0x7e, 0x54, 0x55, 0x00, 0x07, 0xa1, 0x20, 0x00, 0x98, 0xf5, 0x08, 0xff, + 0xfd, 0x0f, 0xe4, 0xff, 0x80, 0x32, 0x80, 0x00, 0x00, 0x00, 0x17, 0x73, + 0x6f, 0x6d, 0x65, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x20, 0x73, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x65, 0x72, 0x65, 0xF0, 0x0D, +};