]> git.lizzy.rs Git - dragonfireclient.git/blob - src/unittest/test_serialization.cpp
660d77d02fe9eecf2d8e8597550bd9cec519cb08
[dragonfireclient.git] / src / unittest / test_serialization.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "test.h"
21
22 #include "util/string.h"
23 #include "util/serialize.h"
24 #include <cmath>
25
26 class TestSerialization : public TestBase {
27 public:
28         TestSerialization() { TestManager::registerTestModule(this); }
29         const char *getName() { return "TestSerialization"; }
30
31         void runTests(IGameDef *gamedef);
32         void buildTestStrings();
33
34         void testSerializeString();
35         void testSerializeLongString();
36         void testSerializeJsonString();
37         void testDeSerializeString();
38         void testDeSerializeLongString();
39         void testStreamRead();
40         void testStreamWrite();
41         void testFloatFormat();
42
43         std::string teststring2;
44         std::wstring teststring2_w;
45         std::string teststring2_w_encoded;
46
47         static const u8 test_serialized_data[12 * 11 - 2];
48 };
49
50 static TestSerialization g_test_instance;
51
52 void TestSerialization::runTests(IGameDef *gamedef)
53 {
54         buildTestStrings();
55
56         TEST(testSerializeString);
57         TEST(testDeSerializeString);
58         TEST(testSerializeLongString);
59         TEST(testDeSerializeLongString);
60         TEST(testSerializeJsonString);
61         TEST(testStreamRead);
62         TEST(testStreamWrite);
63         TEST(testFloatFormat);
64 }
65
66 ////////////////////////////////////////////////////////////////////////////////
67
68 // To be used like this:
69 //   mkstr("Some\0string\0with\0embedded\0nuls")
70 // since std::string("...") doesn't work as expected in that case.
71 template<size_t N> std::string mkstr(const char (&s)[N])
72 {
73         return std::string(s, N - 1);
74 }
75
76 void TestSerialization::buildTestStrings()
77 {
78         std::ostringstream tmp_os;
79         std::wostringstream tmp_os_w;
80         std::ostringstream tmp_os_w_encoded;
81         for (int i = 0; i < 256; i++) {
82                 tmp_os << (char)i;
83                 tmp_os_w << (wchar_t)i;
84                 tmp_os_w_encoded << (char)0 << (char)i;
85         }
86         teststring2 = tmp_os.str();
87         teststring2_w = tmp_os_w.str();
88         teststring2_w_encoded = tmp_os_w_encoded.str();
89 }
90
91 void TestSerialization::testSerializeString()
92 {
93         // Test blank string
94         UASSERT(serializeString16("") == mkstr("\0\0"));
95
96         // Test basic string
97         UASSERT(serializeString16("Hello world!") == mkstr("\0\14Hello world!"));
98
99         // Test character range
100         UASSERT(serializeString16(teststring2) == mkstr("\1\0") + teststring2);
101 }
102
103 void TestSerialization::testDeSerializeString()
104 {
105         // Test deserialize
106         {
107                 std::istringstream is(serializeString16(teststring2), std::ios::binary);
108                 UASSERT(deSerializeString16(is) == teststring2);
109                 UASSERT(!is.eof());
110                 is.get();
111                 UASSERT(is.eof());
112         }
113
114         // Test deserialize an incomplete length specifier
115         {
116                 std::istringstream is(mkstr("\x53"), std::ios::binary);
117                 EXCEPTION_CHECK(SerializationError, deSerializeString16(is));
118         }
119
120         // Test deserialize a string with incomplete data
121         {
122                 std::istringstream is(mkstr("\x00\x55 abcdefg"), std::ios::binary);
123                 EXCEPTION_CHECK(SerializationError, deSerializeString16(is));
124         }
125 }
126
127 void TestSerialization::testSerializeLongString()
128 {
129         // Test blank string
130         UASSERT(serializeString32("") == mkstr("\0\0\0\0"));
131
132         // Test basic string
133         UASSERT(serializeString32("Hello world!") == mkstr("\0\0\0\14Hello world!"));
134
135         // Test character range
136         UASSERT(serializeString32(teststring2) == mkstr("\0\0\1\0") + teststring2);
137 }
138
139 void TestSerialization::testDeSerializeLongString()
140 {
141         // Test deserialize
142         {
143                 std::istringstream is(serializeString32(teststring2), std::ios::binary);
144                 UASSERT(deSerializeString32(is) == teststring2);
145                 UASSERT(!is.eof());
146                 is.get();
147                 UASSERT(is.eof());
148         }
149
150         // Test deserialize an incomplete length specifier
151         {
152                 std::istringstream is(mkstr("\x53"), std::ios::binary);
153                 EXCEPTION_CHECK(SerializationError, deSerializeString32(is));
154         }
155
156         // Test deserialize a string with incomplete data
157         {
158                 std::istringstream is(mkstr("\x00\x00\x00\x05 abc"), std::ios::binary);
159                 EXCEPTION_CHECK(SerializationError, deSerializeString32(is));
160         }
161
162         // Test deserialize a string with a length too large
163         {
164                 std::istringstream is(mkstr("\xFF\xFF\xFF\xFF blah"), std::ios::binary);
165                 EXCEPTION_CHECK(SerializationError, deSerializeString32(is));
166         }
167 }
168
169
170 void TestSerialization::testSerializeJsonString()
171 {
172         // Test blank string
173         UASSERT(serializeJsonString("") == "\"\"");
174
175         // Test basic string
176         UASSERT(serializeJsonString("Hello world!") == "\"Hello world!\"");
177
178         // MSVC fails when directly using "\\\\"
179         std::string backslash = "\\";
180         UASSERT(serializeJsonString(teststring2) ==
181                 mkstr("\"") +
182                 "\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007" +
183                 "\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f" +
184                 "\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" +
185                 "\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f" +
186                 " !\\\"" + teststring2.substr(0x23, 0x2f-0x23) +
187                 "\\/" + teststring2.substr(0x30, 0x5c-0x30) +
188                 backslash + backslash + teststring2.substr(0x5d, 0x7f-0x5d) + "\\u007f" +
189                 "\\u0080\\u0081\\u0082\\u0083\\u0084\\u0085\\u0086\\u0087" +
190                 "\\u0088\\u0089\\u008a\\u008b\\u008c\\u008d\\u008e\\u008f" +
191                 "\\u0090\\u0091\\u0092\\u0093\\u0094\\u0095\\u0096\\u0097" +
192                 "\\u0098\\u0099\\u009a\\u009b\\u009c\\u009d\\u009e\\u009f" +
193                 "\\u00a0\\u00a1\\u00a2\\u00a3\\u00a4\\u00a5\\u00a6\\u00a7" +
194                 "\\u00a8\\u00a9\\u00aa\\u00ab\\u00ac\\u00ad\\u00ae\\u00af" +
195                 "\\u00b0\\u00b1\\u00b2\\u00b3\\u00b4\\u00b5\\u00b6\\u00b7" +
196                 "\\u00b8\\u00b9\\u00ba\\u00bb\\u00bc\\u00bd\\u00be\\u00bf" +
197                 "\\u00c0\\u00c1\\u00c2\\u00c3\\u00c4\\u00c5\\u00c6\\u00c7" +
198                 "\\u00c8\\u00c9\\u00ca\\u00cb\\u00cc\\u00cd\\u00ce\\u00cf" +
199                 "\\u00d0\\u00d1\\u00d2\\u00d3\\u00d4\\u00d5\\u00d6\\u00d7" +
200                 "\\u00d8\\u00d9\\u00da\\u00db\\u00dc\\u00dd\\u00de\\u00df" +
201                 "\\u00e0\\u00e1\\u00e2\\u00e3\\u00e4\\u00e5\\u00e6\\u00e7" +
202                 "\\u00e8\\u00e9\\u00ea\\u00eb\\u00ec\\u00ed\\u00ee\\u00ef" +
203                 "\\u00f0\\u00f1\\u00f2\\u00f3\\u00f4\\u00f5\\u00f6\\u00f7" +
204                 "\\u00f8\\u00f9\\u00fa\\u00fb\\u00fc\\u00fd\\u00fe\\u00ff" +
205                 "\"");
206
207         // Test deserialize
208         std::istringstream is(serializeJsonString(teststring2), std::ios::binary);
209         UASSERT(deSerializeJsonString(is) == teststring2);
210         UASSERT(!is.eof());
211         is.get();
212         UASSERT(is.eof());
213 }
214
215
216 void TestSerialization::testStreamRead()
217 {
218         std::string datastr(
219                 (const char *)test_serialized_data,
220                 sizeof(test_serialized_data));
221         std::istringstream is(datastr, std::ios_base::binary);
222
223         UASSERT(readU8(is) == 0x11);
224         UASSERT(readU16(is) == 0x2233);
225         UASSERT(readU32(is) == 0x44556677);
226         UASSERT(readU64(is) == 0x8899AABBCCDDEEFFLL);
227
228         UASSERT(readS8(is) == -128);
229         UASSERT(readS16(is) == 30000);
230         UASSERT(readS32(is) == -6);
231         UASSERT(readS64(is) == -43);
232
233         UASSERT(readF1000(is) == 53.534f);
234         UASSERT(readF1000(is) == -300000.32f);
235         UASSERT(readF1000(is) == F1000_MIN);
236         UASSERT(readF1000(is) == F1000_MAX);
237
238         UASSERT(deSerializeString16(is) == "foobar!");
239
240         UASSERT(readV2S16(is) == v2s16(500, 500));
241         UASSERT(readV3S16(is) == v3s16(4207, 604, -30));
242         UASSERT(readV2S32(is) == v2s32(1920, 1080));
243         UASSERT(readV3S32(is) == v3s32(-400, 6400054, 290549855));
244
245         UASSERT(readV3F1000(is) == v3f(500, 10024.2f, -192.54f));
246         UASSERT(readARGB8(is) == video::SColor(255, 128, 50, 128));
247
248         UASSERT(deSerializeString32(is) == "some longer string here");
249
250         UASSERT(is.rdbuf()->in_avail() == 2);
251         UASSERT(readU16(is) == 0xF00D);
252         UASSERT(is.rdbuf()->in_avail() == 0);
253 }
254
255
256 void TestSerialization::testStreamWrite()
257 {
258         std::ostringstream os(std::ios_base::binary);
259         std::string data;
260
261         writeU8(os, 0x11);
262         writeU16(os, 0x2233);
263         writeU32(os, 0x44556677);
264         writeU64(os, 0x8899AABBCCDDEEFFLL);
265
266         writeS8(os, -128);
267         writeS16(os, 30000);
268         writeS32(os, -6);
269         writeS64(os, -43);
270
271         writeF1000(os, 53.53467f);
272         writeF1000(os, -300000.32f);
273         writeF1000(os, F1000_MIN);
274         writeF1000(os, F1000_MAX);
275
276         os << serializeString16("foobar!");
277
278         data = os.str();
279         UASSERT(data.size() < sizeof(test_serialized_data));
280         UASSERT(!memcmp(&data[0], test_serialized_data, data.size()));
281
282         writeV2S16(os, v2s16(500, 500));
283         writeV3S16(os, v3s16(4207, 604, -30));
284         writeV2S32(os, v2s32(1920, 1080));
285         writeV3S32(os, v3s32(-400, 6400054, 290549855));
286
287         writeV3F1000(os, v3f(500, 10024.2f, -192.54f));
288         writeARGB8(os, video::SColor(255, 128, 50, 128));
289
290         os << serializeString32("some longer string here");
291
292         writeU16(os, 0xF00D);
293
294         data = os.str();
295         UASSERT(data.size() == sizeof(test_serialized_data));
296         UASSERT(!memcmp(&data[0], test_serialized_data, sizeof(test_serialized_data)));
297 }
298
299
300 void TestSerialization::testFloatFormat()
301 {
302         FloatType type = getFloatSerializationType();
303         u32 i;
304         f32 fs, fm;
305
306         // Check precision of float calculations on this platform
307         const std::unordered_map<f32, u32> float_results = {
308                 {  0.0f, 0x00000000UL },
309                 {  1.0f, 0x3F800000UL },
310                 { -1.0f, 0xBF800000UL },
311                 {  0.1f, 0x3DCCCCCDUL },
312                 { -0.1f, 0xBDCCCCCDUL },
313                 { 1945329.25f, 0x49ED778AUL },
314                 { -23298764.f, 0xCBB1C166UL },
315                 {  0.5f, 0x3F000000UL },
316                 { -0.5f, 0xBF000000UL }
317         };
318         for (const auto &v : float_results) {
319                 i = f32Tou32Slow(v.first);
320                 if (std::abs((s64)v.second - i) > 32) {
321                         printf("Inaccurate float values on %.9g, expected 0x%X, actual 0x%X\n",
322                                 v.first, v.second, i);
323                         UASSERT(false);
324                 }
325
326                 fs = u32Tof32Slow(v.second);
327                 if (std::fabs(v.first - fs) > std::fabs(v.first * 0.000005f)) {
328                         printf("Inaccurate float values on 0x%X, expected %.9g, actual 0x%.9g\n",
329                                 v.second, v.first, fs);
330                         UASSERT(false);
331                 }
332         }
333
334         if (type == FLOATTYPE_SLOW) {
335                 // conversion using memcpy is not possible
336                 // Skip exact float comparison checks below
337                 return;
338         }
339
340         // The code below compares the IEEE conversion functions with a
341         // known good IEC559/IEEE754 implementation. This test neeeds
342         // IEC559 compliance in the compiler.
343 #if defined(__GNUC__) && (!defined(__STDC_IEC_559__) || defined(__FAST_MATH__))
344         // GNU C++ lies about its IEC559 support when -ffast-math is active.
345         // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=84949
346         bool is_iec559 = false;
347 #else
348         bool is_iec559 = std::numeric_limits<f32>::is_iec559;
349 #endif
350         if (!is_iec559)
351                 return;
352
353         auto test_single = [&fs, &fm](const u32 &i) -> bool {
354                 memcpy(&fm, &i, 4);
355                 fs = u32Tof32Slow(i);
356                 if (fm != fs) {
357                         printf("u32Tof32Slow failed on 0x%X, expected %.9g, actual %.9g\n",
358                                 i, fm, fs);
359                         return false;
360                 }
361                 if (f32Tou32Slow(fs) != i) {
362                         printf("f32Tou32Slow failed on %.9g, expected 0x%X, actual 0x%X\n",
363                                 fs, i, f32Tou32Slow(fs));
364                         return false;
365                 }
366                 return true;
367         };
368
369         // Use step of prime 277 to speed things up from 3 minutes to a few seconds
370         // Test from 0 to 0xFF800000UL (positive)
371         for (i = 0x00000000UL; i <= 0x7F800000UL; i += 277)
372                 UASSERT(test_single(i));
373
374         // Ensure +inf and -inf are tested
375         UASSERT(test_single(0x7F800000UL));
376         UASSERT(test_single(0xFF800000UL));
377
378         // Test from 0x80000000UL to 0xFF800000UL (negative)
379         for (i = 0x80000000UL; i <= 0xFF800000UL; i += 277)
380                 UASSERT(test_single(i));
381 }
382
383 const u8 TestSerialization::test_serialized_data[12 * 11 - 2] = {
384         0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
385         0xdd, 0xee, 0xff, 0x80, 0x75, 0x30, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xff,
386         0xff, 0xff, 0xff, 0xff, 0xff, 0xd5, 0x00, 0x00, 0xd1, 0x1e, 0xee, 0x1e,
387         0x5b, 0xc0, 0x80, 0x00, 0x02, 0x80, 0x7F, 0xFF, 0xFD, 0x80, 0x00, 0x07,
388         0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x21, 0x01, 0xf4, 0x01, 0xf4, 0x10,
389         0x6f, 0x02, 0x5c, 0xff, 0xe2, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x04,
390         0x38, 0xff, 0xff, 0xfe, 0x70, 0x00, 0x61, 0xa8, 0x36, 0x11, 0x51, 0x70,
391         0x5f, 0x00, 0x07, 0xa1, 0x20, 0x00, 0x98, 0xf5, 0x08, 0xff,
392         0xfd, 0x0f, 0xe4, 0xff, 0x80, 0x32, 0x80, 0x00, 0x00, 0x00, 0x17, 0x73,
393         0x6f, 0x6d, 0x65, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x20, 0x73,
394         0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x65, 0x72, 0x65, 0xF0, 0x0D,
395 };