Improve accuracy and safety of float serialization
[oweals/minetest.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
25 class TestSerialization : public TestBase {
26 public:
27         TestSerialization() { TestManager::registerTestModule(this); }
28         const char *getName() { return "TestSerialization"; }
29
30         void runTests(IGameDef *gamedef);
31         void buildTestStrings();
32
33         void testSerializeString();
34         void testSerializeWideString();
35         void testSerializeLongString();
36         void testSerializeJsonString();
37         void testSerializeHex();
38         void testDeSerializeString();
39         void testDeSerializeWideString();
40         void testDeSerializeLongString();
41         void testStreamRead();
42         void testStreamWrite();
43
44         std::string teststring2;
45         std::wstring teststring2_w;
46         std::string teststring2_w_encoded;
47
48         static const u8 test_serialized_data[12 * 13];
49 };
50
51 static TestSerialization g_test_instance;
52
53 void TestSerialization::runTests(IGameDef *gamedef)
54 {
55         buildTestStrings();
56
57         TEST(testSerializeString);
58         TEST(testDeSerializeString);
59         TEST(testSerializeWideString);
60         TEST(testDeSerializeWideString);
61         TEST(testSerializeLongString);
62         TEST(testDeSerializeLongString);
63         TEST(testSerializeJsonString);
64         TEST(testSerializeHex);
65         TEST(testStreamRead);
66         TEST(testStreamWrite);
67 }
68
69 ////////////////////////////////////////////////////////////////////////////////
70
71 // To be used like this:
72 //   mkstr("Some\0string\0with\0embedded\0nuls")
73 // since std::string("...") doesn't work as expected in that case.
74 template<size_t N> std::string mkstr(const char (&s)[N])
75 {
76         return std::string(s, N - 1);
77 }
78
79 void TestSerialization::buildTestStrings()
80 {
81         std::ostringstream tmp_os;
82         std::wostringstream tmp_os_w;
83         std::ostringstream tmp_os_w_encoded;
84         for (int i = 0; i < 256; i++) {
85                 tmp_os << (char)i;
86                 tmp_os_w << (wchar_t)i;
87                 tmp_os_w_encoded << (char)0 << (char)i;
88         }
89         teststring2 = tmp_os.str();
90         teststring2_w = tmp_os_w.str();
91         teststring2_w_encoded = tmp_os_w_encoded.str();
92 }
93
94 void TestSerialization::testSerializeString()
95 {
96         // Test blank string
97         UASSERT(serializeString("") == mkstr("\0\0"));
98
99         // Test basic string
100         UASSERT(serializeString("Hello world!") == mkstr("\0\14Hello world!"));
101
102         // Test character range
103         UASSERT(serializeString(teststring2) == mkstr("\1\0") + teststring2);
104 }
105
106 void TestSerialization::testDeSerializeString()
107 {
108         // Test deserialize
109         {
110                 std::istringstream is(serializeString(teststring2), std::ios::binary);
111                 UASSERT(deSerializeString(is) == teststring2);
112                 UASSERT(!is.eof());
113                 is.get();
114                 UASSERT(is.eof());
115         }
116
117         // Test deserialize an incomplete length specifier
118         {
119                 std::istringstream is(mkstr("\x53"), std::ios::binary);
120                 EXCEPTION_CHECK(SerializationError, deSerializeString(is));
121         }
122
123         // Test deserialize a string with incomplete data
124         {
125                 std::istringstream is(mkstr("\x00\x55 abcdefg"), std::ios::binary);
126                 EXCEPTION_CHECK(SerializationError, deSerializeString(is));
127         }
128 }
129
130 void TestSerialization::testSerializeWideString()
131 {
132         // Test blank string
133         UASSERT(serializeWideString(L"") == mkstr("\0\0"));
134
135         // Test basic string
136         UASSERT(serializeWideString(utf8_to_wide("Hello world!")) ==
137                 mkstr("\0\14\0H\0e\0l\0l\0o\0 \0w\0o\0r\0l\0d\0!"));
138
139         // Test character range
140         UASSERT(serializeWideString(teststring2_w) ==
141                 mkstr("\1\0") + teststring2_w_encoded);
142 }
143
144 void TestSerialization::testDeSerializeWideString()
145 {
146         // Test deserialize
147         {
148                 std::istringstream is(serializeWideString(teststring2_w), std::ios::binary);
149                 UASSERT(deSerializeWideString(is) == teststring2_w);
150                 UASSERT(!is.eof());
151                 is.get();
152                 UASSERT(is.eof());
153         }
154
155         // Test deserialize an incomplete length specifier
156         {
157                 std::istringstream is(mkstr("\x53"), std::ios::binary);
158                 EXCEPTION_CHECK(SerializationError, deSerializeWideString(is));
159         }
160
161         // Test deserialize a string with an incomplete character
162         {
163                 std::istringstream is(mkstr("\x00\x07\0a\0b\0c\0d\0e\0f\0"), std::ios::binary);
164                 EXCEPTION_CHECK(SerializationError, deSerializeWideString(is));
165         }
166
167         // Test deserialize a string with incomplete data
168         {
169                 std::istringstream is(mkstr("\x00\x08\0a\0b\0c\0d\0e\0f"), std::ios::binary);
170                 EXCEPTION_CHECK(SerializationError, deSerializeWideString(is));
171         }
172 }
173
174 void TestSerialization::testSerializeLongString()
175 {
176         // Test blank string
177         UASSERT(serializeLongString("") == mkstr("\0\0\0\0"));
178
179         // Test basic string
180         UASSERT(serializeLongString("Hello world!") == mkstr("\0\0\0\14Hello world!"));
181
182         // Test character range
183         UASSERT(serializeLongString(teststring2) == mkstr("\0\0\1\0") + teststring2);
184 }
185
186 void TestSerialization::testDeSerializeLongString()
187 {
188         // Test deserialize
189         {
190                 std::istringstream is(serializeLongString(teststring2), std::ios::binary);
191                 UASSERT(deSerializeLongString(is) == teststring2);
192                 UASSERT(!is.eof());
193                 is.get();
194                 UASSERT(is.eof());
195         }
196
197         // Test deserialize an incomplete length specifier
198         {
199                 std::istringstream is(mkstr("\x53"), std::ios::binary);
200                 EXCEPTION_CHECK(SerializationError, deSerializeLongString(is));
201         }
202
203         // Test deserialize a string with incomplete data
204         {
205                 std::istringstream is(mkstr("\x00\x00\x00\x05 abc"), std::ios::binary);
206                 EXCEPTION_CHECK(SerializationError, deSerializeLongString(is));
207         }
208
209         // Test deserialize a string with a length too large
210         {
211                 std::istringstream is(mkstr("\xFF\xFF\xFF\xFF blah"), std::ios::binary);
212                 EXCEPTION_CHECK(SerializationError, deSerializeLongString(is));
213         }
214 }
215
216
217 void TestSerialization::testSerializeJsonString()
218 {
219         // Test blank string
220         UASSERT(serializeJsonString("") == "\"\"");
221
222         // Test basic string
223         UASSERT(serializeJsonString("Hello world!") == "\"Hello world!\"");
224
225         // MSVC fails when directly using "\\\\"
226         std::string backslash = "\\";
227         UASSERT(serializeJsonString(teststring2) ==
228                 mkstr("\"") +
229                 "\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007" +
230                 "\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f" +
231                 "\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" +
232                 "\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f" +
233                 " !\\\"" + teststring2.substr(0x23, 0x2f-0x23) +
234                 "\\/" + teststring2.substr(0x30, 0x5c-0x30) +
235                 backslash + backslash + teststring2.substr(0x5d, 0x7f-0x5d) + "\\u007f" +
236                 "\\u0080\\u0081\\u0082\\u0083\\u0084\\u0085\\u0086\\u0087" +
237                 "\\u0088\\u0089\\u008a\\u008b\\u008c\\u008d\\u008e\\u008f" +
238                 "\\u0090\\u0091\\u0092\\u0093\\u0094\\u0095\\u0096\\u0097" +
239                 "\\u0098\\u0099\\u009a\\u009b\\u009c\\u009d\\u009e\\u009f" +
240                 "\\u00a0\\u00a1\\u00a2\\u00a3\\u00a4\\u00a5\\u00a6\\u00a7" +
241                 "\\u00a8\\u00a9\\u00aa\\u00ab\\u00ac\\u00ad\\u00ae\\u00af" +
242                 "\\u00b0\\u00b1\\u00b2\\u00b3\\u00b4\\u00b5\\u00b6\\u00b7" +
243                 "\\u00b8\\u00b9\\u00ba\\u00bb\\u00bc\\u00bd\\u00be\\u00bf" +
244                 "\\u00c0\\u00c1\\u00c2\\u00c3\\u00c4\\u00c5\\u00c6\\u00c7" +
245                 "\\u00c8\\u00c9\\u00ca\\u00cb\\u00cc\\u00cd\\u00ce\\u00cf" +
246                 "\\u00d0\\u00d1\\u00d2\\u00d3\\u00d4\\u00d5\\u00d6\\u00d7" +
247                 "\\u00d8\\u00d9\\u00da\\u00db\\u00dc\\u00dd\\u00de\\u00df" +
248                 "\\u00e0\\u00e1\\u00e2\\u00e3\\u00e4\\u00e5\\u00e6\\u00e7" +
249                 "\\u00e8\\u00e9\\u00ea\\u00eb\\u00ec\\u00ed\\u00ee\\u00ef" +
250                 "\\u00f0\\u00f1\\u00f2\\u00f3\\u00f4\\u00f5\\u00f6\\u00f7" +
251                 "\\u00f8\\u00f9\\u00fa\\u00fb\\u00fc\\u00fd\\u00fe\\u00ff" +
252                 "\"");
253
254         // Test deserialize
255         std::istringstream is(serializeJsonString(teststring2), std::ios::binary);
256         UASSERT(deSerializeJsonString(is) == teststring2);
257         UASSERT(!is.eof());
258         is.get();
259         UASSERT(is.eof());
260 }
261
262 void TestSerialization::testSerializeHex()
263 {
264         // Test blank string
265         UASSERT(serializeHexString("") == "");
266         UASSERT(serializeHexString("", true) == "");
267
268         // Test basic string
269         UASSERT(serializeHexString("Hello world!") ==
270                 "48656c6c6f20776f726c6421");
271         UASSERT(serializeHexString("Hello world!", true) ==
272                 "48 65 6c 6c 6f 20 77 6f 72 6c 64 21");
273
274         // Test binary string
275         UASSERT(serializeHexString(mkstr("\x00\x0a\xb0\x63\x1f\x00\xff")) ==
276                 "000ab0631f00ff");
277         UASSERT(serializeHexString(mkstr("\x00\x0a\xb0\x63\x1f\x00\xff"), true) ==
278                 "00 0a b0 63 1f 00 ff");
279 }
280
281
282 void TestSerialization::testStreamRead()
283 {
284         std::string datastr(
285                 (const char *)test_serialized_data,
286                 sizeof(test_serialized_data));
287         std::istringstream is(datastr, std::ios_base::binary);
288
289         UASSERT(readU8(is) == 0x11);
290         UASSERT(readU16(is) == 0x2233);
291         UASSERT(readU32(is) == 0x44556677);
292         UASSERT(readU64(is) == 0x8899AABBCCDDEEFF);
293
294         UASSERT(readS8(is) == -128);
295         UASSERT(readS16(is) == 30000);
296         UASSERT(readS32(is) == -6);
297         UASSERT(readS64(is) == -43);
298
299         UASSERT(readF1000(is) == 53.534f);
300         UASSERT(readF1000(is) == -300000.32f);
301         UASSERT(readF1000(is) == F1000_MIN);
302         UASSERT(readF1000(is) == F1000_MAX);
303
304         UASSERT(deSerializeString(is) == "foobar!");
305
306         UASSERT(readV2S16(is) == v2s16(500, 500));
307         UASSERT(readV3S16(is) == v3s16(4207, 604, -30));
308         UASSERT(readV2S32(is) == v2s32(1920, 1080));
309         UASSERT(readV3S32(is) == v3s32(-400, 6400054, 290549855));
310         UASSERT(readV2F1000(is) == v2f(500.656f, 350.345f));
311
312         UASSERT(deSerializeWideString(is) == L"\x02~woof~\x5455");
313
314         UASSERT(readV3F1000(is) == v3f(500, 10024.2f, -192.54f));
315         UASSERT(readARGB8(is) == video::SColor(255, 128, 50, 128));
316
317         UASSERT(deSerializeLongString(is) == "some longer string here");
318
319         UASSERT(is.rdbuf()->in_avail() == 2);
320         UASSERT(readU16(is) == 0xF00D);
321         UASSERT(is.rdbuf()->in_avail() == 0);
322 }
323
324
325 void TestSerialization::testStreamWrite()
326 {
327         std::ostringstream os(std::ios_base::binary);
328         std::string data;
329
330         writeU8(os, 0x11);
331         writeU16(os, 0x2233);
332         writeU32(os, 0x44556677);
333         writeU64(os, 0x8899AABBCCDDEEFF);
334
335         writeS8(os, -128);
336         writeS16(os, 30000);
337         writeS32(os, -6);
338         writeS64(os, -43);
339
340         writeF1000(os, 53.53467f);
341         writeF1000(os, -300000.32f);
342         writeF1000(os, F1000_MIN);
343         writeF1000(os, F1000_MAX);
344
345         os << serializeString("foobar!");
346
347         data = os.str();
348         UASSERT(data.size() < sizeof(test_serialized_data));
349         UASSERT(!memcmp(&data[0], test_serialized_data, data.size()));
350
351         writeV2S16(os, v2s16(500, 500));
352         writeV3S16(os, v3s16(4207, 604, -30));
353         writeV2S32(os, v2s32(1920, 1080));
354         writeV3S32(os, v3s32(-400, 6400054, 290549855));
355         writeV2F1000(os, v2f(500.65661f, 350.34567f));
356
357         os << serializeWideString(L"\x02~woof~\x5455");
358
359         writeV3F1000(os, v3f(500, 10024.2f, -192.54f));
360         writeARGB8(os, video::SColor(255, 128, 50, 128));
361
362         os << serializeLongString("some longer string here");
363
364         writeU16(os, 0xF00D);
365
366         data = os.str();
367         UASSERT(data.size() == sizeof(test_serialized_data));
368         UASSERT(!memcmp(&data[0], test_serialized_data, sizeof(test_serialized_data)));
369 }
370
371
372 const u8 TestSerialization::test_serialized_data[12 * 13] = {
373         0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
374         0xdd, 0xee, 0xff, 0x80, 0x75, 0x30, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xff,
375         0xff, 0xff, 0xff, 0xff, 0xff, 0xd5, 0x00, 0x00, 0xd1, 0x1e, 0xee, 0x1e,
376         0x5b, 0xc0, 0x80, 0x00, 0x02, 0x80, 0x7F, 0xFF, 0xFD, 0x80, 0x00, 0x07,
377         0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x21, 0x01, 0xf4, 0x01, 0xf4, 0x10,
378         0x6f, 0x02, 0x5c, 0xff, 0xe2, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x04,
379         0x38, 0xff, 0xff, 0xfe, 0x70, 0x00, 0x61, 0xa8, 0x36, 0x11, 0x51, 0x70,
380         0x5f, 0x00, 0x07, 0xa3, 0xb0, 0x00, 0x05, 0x58, 0x89, 0x00, 0x08, 0x00,
381         0x02, 0x00, 0x7e, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x66, 0x00,
382         0x7e, 0x54, 0x55, 0x00, 0x07, 0xa1, 0x20, 0x00, 0x98, 0xf5, 0x08, 0xff,
383         0xfd, 0x0f, 0xe4, 0xff, 0x80, 0x32, 0x80, 0x00, 0x00, 0x00, 0x17, 0x73,
384         0x6f, 0x6d, 0x65, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x20, 0x73,
385         0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x65, 0x72, 0x65, 0xF0, 0x0D,
386 };