1 /**
2  * Copyright: (c) 2015-2018, Milofon Project.
3  * License: Subject to the terms of the BSD 3-Clause License, as written in the included LICENSE.md file.
4  * Author: <m.galanin@milofon.pro> Maksim Galanin
5  * Date: 2018-10-08
6  */
7 
8 module uninode.test_serialization;
9 
10 private
11 {
12     import std.meta : AliasSeq;
13     import std.exception : assertThrown, collectException;
14     import std.traits : isPointer;
15     import std.conv : text;
16     import std.typecons;
17 
18     import uninode.node : Bytes;
19     import uninode.serialization;
20 }
21 
22 
23 version (unittest)
24 {
25     static void test(T)(auto ref T value, UniNode expected)
26     {
27         assert (serializeToUniNode(value) == expected, serializeToUniNode(value).text);
28         static if (isPointer!T)
29         {
30             if (value)
31                 assert (*deserializeUniNode!T(expected) == *value);
32             else
33                 assert (deserializeUniNode!T(expected) is null);
34         }
35         else
36             assert (deserializeUniNode!T(expected) == value);
37     }
38 }
39 
40 
41 @("Sould de/serialize simple types")
42 @safe unittest
43 {
44     test(null, UniNode());
45     test(cast(int*)null, UniNode());
46     int i = 42;
47     () @trusted { test(&i, UniNode(42)); }();
48 
49     foreach (T; AliasSeq!(byte, short, int, long))
50     {
51         T v = cast(T)11;
52         test(v, UniNode(v));
53     }
54 
55     foreach (T; AliasSeq!(ubyte, ushort, uint, ulong))
56     {
57         T v = cast(T)11u;
58         test(v, UniNode(v));
59     }
60 
61     foreach (T; AliasSeq!(float, double))
62     {
63         T v = cast(T)1.1;
64         test(v, UniNode(v));
65     }
66 
67     test("hello", UniNode("hello"));
68     test(true, UniNode(true));
69     test(false, UniNode(false));
70 
71     ubyte[3] bytes = [1, 2, 3];
72     test(bytes, UniNode(bytes));
73 }
74 
75 
76 @("Should de/serialize nullable types")
77 @safe unittest
78 {
79     assert (serializeToUniNode(Nullable!int.init).canNil);
80 
81     auto nNode = serializeToUniNode(Nullable!int(11));
82     assert (nNode == UniNode(11));
83     const nVal = deserializeUniNode!(Nullable!byte)(nNode);
84     assert (!nVal.isNull);
85     assert (nVal.get == 11);
86 
87     auto oNode = serializeToUniNode(Nullable!string("hello"));
88     assert (oNode == UniNode("hello"));
89     const oVal = deserializeUniNode!(Nullable!string)(oNode);
90     assert (!oVal.isNull);
91     assert (oVal.get == "hello");
92 }
93 
94 
95 @("Should de/serialization array")
96 @safe unittest
97 {
98     ubyte[] bytes = [1, 2, 3];
99     ulong[] numbers = [4, 5, 6];
100 
101     const bNode = serializeToUniNode(bytes);
102     assert (bNode.canRaw);
103     assert (deserializeUniNode!(ubyte[3])(bNode) == bytes);
104     assert (deserializeUniNode!(ubyte[])(bNode) == bytes);
105     assert (deserializeUniNode!(long[3])(bNode) == bytes);
106     assert (deserializeUniNode!(long[])(bNode) == bytes);
107 
108     const aNode = serializeToUniNode(numbers);
109     assert (aNode.getSequence[1] == UniNode(5));
110     assert (deserializeUniNode!(ubyte[3])(aNode) == numbers);
111     assert (deserializeUniNode!(ubyte[])(aNode) == numbers);
112     assert (deserializeUniNode!(ulong[3])(aNode) == numbers);
113     assert (deserializeUniNode!(ulong[])(aNode) == numbers);
114 
115     test(["hello", "world"], UniNode([UniNode("hello"), UniNode("world")]));
116 }
117 
118 
119 @("Should de/serialization associative array")
120 @safe unittest
121 {
122     auto aa = ["one": 1, "two": 2];
123     const aaNode = serializeToUniNode(aa);
124     assert (aaNode.getMapping["one"] == UniNode(1));
125     assert (deserializeUniNode!(int[string])(aaNode) == aa);
126 
127     test(["hello": "world"], UniNode(["hello": UniNode("world")]));
128 }
129 
130 
131 @("Should de/serialization typedef")
132 @safe unittest
133 {
134     alias TD = Typedef!int;
135     TD v = 20;
136     const tdNode = serializeToUniNode(v);
137     assert (tdNode == UniNode(20));
138     assert (deserializeUniNode!TD(tdNode) == 20);
139 }
140 
141 
142 @("Should de/serialization BitFlags")
143 @safe unittest
144 {
145     enum E
146     {
147         A = 1 << 0,
148         B = 1 << 2
149     }
150 
151     BitFlags!E flags = E.B;
152     const bfNode = serializeToUniNode(flags);
153     assert (bfNode == UniNode([UniNode(4)]));
154     assert (deserializeUniNode!(BitFlags!E)(bfNode) == flags);
155 }
156 
157 
158 @("Should de/serialization datetime")
159 @safe unittest
160 {
161     import std.datetime : SysTime, DateTime, Date;
162     auto date = Date(2000, 6, 1);
163     const dNode = serializeToUniNode(date);
164     assert (dNode == UniNode("2000-06-01"));
165     assert (deserializeUniNode!Date(dNode) == date);
166 }
167 
168 
169 @("Should de/serialization pointer")
170 @system unittest
171 {
172     int v = 11;
173     int* ptr = &v;
174     const ptrNode = serializeToUniNode(ptr);
175     assert (ptrNode == UniNode(11));
176     assert (*deserializeUniNode!(int*)(ptrNode) == v);
177     assert (!deserializeUniNode!(int*)(UniNode()));
178 }
179 
180 
181 @("Should de/serialization char")
182 @safe unittest
183 {
184     const c = 'c';
185     const cNode = serializeToUniNode(c);
186     assert (cNode == UniNode("c"));
187     assert (deserializeUniNode!char(cNode) == c);
188 }
189 
190 
191 @("Should de/serialization tuple")
192 @safe unittest
193 {
194     alias PairDict = Tuple!(int, "b", int, "a");
195     auto pd = PairDict(1, 2);
196     const pdNode = serializeToUniNode(pd);
197     assert (pdNode.getMapping["b"] == UniNode(1));
198     assert (pdNode.getMapping["a"] == UniNode(2));
199     assert (deserializeUniNode!PairDict(pdNode) == pd);
200 
201     const shortNode = UniNode(["a": UniNode(1)]);
202     assertThrown!UniNodeDeserializationException(deserializeUniNode!PairDict(shortNode));
203 
204     alias PairArr = Tuple!(int, int);
205     auto pa = PairArr(2, 3);
206     const paNode = serializeToUniNode(pa);
207 
208     assert (paNode.getSequence[0] == UniNode(2));
209     assert (paNode.getSequence[1] == UniNode(3));
210     assert (deserializeUniNode!PairArr(paNode) == pa);
211 
212     const sNodeArr = UniNode([UniNode(3)]);
213     assertThrown!UniNodeDeserializationException(deserializeUniNode!PairArr(sNodeArr));
214 
215     static struct S(T...)
216     {
217         @name("ff")
218         T f;
219     }
220 
221     const s = S!(int, string)(42, "hello");
222     const ss = serializeToUniNode(s);
223     assert (ss == UniNode(["ff": UniNode([UniNode(42), UniNode("hello")])]));
224 
225     const sc = deserializeUniNode!(S!(int, string))(ss);
226     assert (sc == s);
227 
228     static struct T
229     {
230         @asArray
231         S!(int, string) g;
232     }
233 
234     const t = T(s);
235     const st = serializeToUniNode(t);
236     assert (st == UniNode(["g": UniNode([UniNode(42), UniNode("hello")])]));
237 }
238 
239 
240 @("Testing the various UDAs")
241 @safe unittest
242 {
243     enum E { hello, world }
244     static struct S
245     {
246         @byName E e;
247         @ignore int i;
248         @optional float f;
249     }
250 
251     auto s = S(E.world, 42, 1.0f);
252     assert(serializeToUniNode(s) == UniNode(["e": UniNode("world"), "f": UniNode(1)]));
253 }
254 
255 
256 @("Custom serialization support")
257 @safe unittest
258 {
259     import std.datetime : TimeOfDay, Date, DateTime, SysTime, UTC;
260     auto t = TimeOfDay(6, 31, 23);
261     assert(serializeToUniNode(t) == UniNode("06:31:23"));
262     auto d = Date(1964, 1, 23);
263     assert(serializeToUniNode(d) == UniNode("1964-01-23"));
264     auto dt = DateTime(d, t);
265     assert(serializeToUniNode(dt) == UniNode("1964-01-23T06:31:23"));
266     auto st = SysTime(dt, UTC());
267     assert(serializeToUniNode(st) == UniNode("1964-01-23T06:31:23Z"));
268 }
269 
270 
271 @("Testing corner case: member function returning by ref")
272 @safe unittest
273 {
274     static struct S
275     {
276         int i;
277         ref int foo() { return i; }
278     }
279 
280     auto s = S(1);
281     assert(serializeToUniNode(s).deserializeUniNode!S == s);
282 }
283 
284 
285 @("Testing corner case: Variadic template constructors and methods")
286 @safe unittest
287 {
288     static struct S
289     {
290         int i;
291         this(Args...)(Args args) {}
292         int foo(Args...)(Args) { return i; }
293         ref int bar(Args...)(Args) { return i; }
294     }
295 
296     const s = S(1);
297     assert(s.serializeToUniNode.deserializeUniNode!S == s);
298 }
299 
300 
301 @("Make sure serializing through properties still works")
302 @safe unittest
303 {
304     static struct S
305     {
306         @safe:
307             public int i;
308         private int privateJ;
309 
310         @property int j() inout @safe { return privateJ; }
311         @property void j(int j) @safe { privateJ = j; }
312     }
313 
314     auto s = S(1, 2);
315     assert(s.serializeToUniNode().deserializeUniNode!S() == s);
316 }
317 
318 
319 @("Should custom serializationMethod struct")
320 @safe unittest
321 {
322     import std.conv : text, to;
323 
324     static struct Test
325     {
326         int a;
327 
328         @SerializationMethod
329         UniNode serializeToUniNode() inout
330         {
331             return UniNode(a.text);
332         }
333 
334         @DeserializationMethod
335         static Test deserializeUniNode(UniNode value)
336         {
337             return Test(value.get!string.to!int + 10);
338         }
339     }
340 
341     auto t = Test(1);
342     auto tNode = serializeToUniNode(t);
343     assert (tNode == UniNode("1"));
344     assert (deserializeUniNode!Test(tNode) == Test(11));
345 }
346 
347 
348 @("Should work de/serialization struct")
349 @system unittest
350 {
351     static struct Test
352     {
353         int a;
354         int b;
355         int c;
356     }
357 
358     auto t = Test(4, 5, 6);
359     const tNode = serializeToUniNode(t);
360     assert (tNode == UniNode(["a": UniNode(4),
361                 "b": UniNode(5), "c": UniNode(6)]));
362     assert (deserializeUniNode!Test(tNode) == t);
363 }
364 
365 
366 @("Should work attributes")
367 @system unittest
368 {
369     enum Color { red, green, blue }
370     alias Gradient = Tuple!(Color, "cs", Color, "ce");
371     alias Center = Tuple!(double, double);
372 
373     static struct Point
374     {
375         double x;
376         double y;
377     }
378 
379     static struct Pixel
380     {
381         @name("pivot")
382         Point point;
383         @name("col")
384         @byName
385         Color color;
386     }
387 
388 
389     static struct Surface
390     {
391         Pixel[] pixels;
392         Gradient gradFront;
393         @asArray
394         Gradient gradBack;
395         @asArray
396         Center center;
397         @ignore
398         Point left;
399         @masked
400         Point bottom;
401         @asString
402         int id;
403         @asArray
404         Point top;
405     }
406 
407     auto st = Surface([
408             Pixel(Point(1.1, 2.3), Color.red),
409             Pixel(Point(2.3, 3.3), Color.green),
410             Pixel(Point(3.3, 1.1), Color.blue),
411         ],
412         Gradient(Color.red, Color.blue),
413         Gradient(Color.green, Color.blue),
414         Center(4.1, 3.4));
415 
416     st.left = Point(0.1, 0.2);
417     st.bottom = Point(0.3, 0.7);
418     st.id = 100;
419     st.top = Point(30, 40);
420 
421     auto stNode = serializeToUniNode(st);
422     assert (stNode.canMapping);
423 
424     const pixels = stNode.getMapping["pixels"];
425     assert (pixels.length == 3);
426 
427     const p1 = pixels.getSequence[0];
428 
429     // name
430     assert ("pivot" in p1.getMapping);
431     assert ("col" in p1.getMapping);
432 
433     // byName
434     assert (p1.getMapping["col"] == UniNode("red"));
435 
436     // ignore
437     assert ("left" !in stNode.getMapping);
438 
439     // masked
440     assert ("bottom" !in stNode.getMapping);
441 
442     // asString
443     assert ("id" in stNode.getMapping);
444 
445     // asArray
446     assert (stNode.getMapping["top"].canSequence);
447 
448     const e = collectException!UniNodeDeserializationException(
449             deserializeUniNode!Surface(stNode));
450     assert (e.msg == "Missing non-optional field 'bottom' of type 'Surface'.");
451 
452     UniNode[string] fixUni = cast(UniNode[string])stNode.getMapping;
453     fixUni["bottom"] = serializeToUniNode(st.bottom);
454 
455     auto dsurf = deserializeUniNode!Surface(UniNode(fixUni));
456     assert (dsurf != st);
457 
458     dsurf.left = Point(0.1, 0.2);
459     dsurf.id = 100;
460     assert (dsurf == st);
461 
462     static struct Pair
463     {
464         @optional
465         int a;
466         int b;
467     }
468 
469     const optNode = UniNode(["a": UniNode(), "b": UniNode(2)]);
470     assert (deserializeUniNode!Pair(optNode) == Pair(0, 2));
471 
472     const oNode = UniNode(["a": UniNode(4), "b": UniNode(2)]);
473     assert (deserializeUniNode!Pair(oNode) == Pair(4, 2));
474 }
475 
476 
477 @("Should serializeToUniNode enum")
478 @safe unittest
479 {
480     enum Color { red, green, blue }
481 
482     @asArray
483     static struct TestArr
484     {
485         @byName
486         Color textColor;
487         Color numColor;
488 
489         Color col() @property inout
490         {
491             return Color.blue;
492         }
493 
494         void col(int c) @property
495         {
496             textColor = cast(Color)c;
497         }
498 
499         void col(Color c) @property
500         {
501             textColor = c;
502         }
503     }
504     auto ta = TestArr(Color.blue, Color.green);
505     const eNode = serializeToUniNode(ta);
506     assert (eNode.getSequence[0] == UniNode("blue"));
507     assert (eNode.getSequence[1] == UniNode(1));
508 
509     const eVal = deserializeUniNode!TestArr(eNode);
510     assert (eVal == ta);
511 
512     struct TestMap
513     {
514         @byName
515         Color textColor;
516         Color numColor;
517     }
518 
519     auto tm = TestMap(Color.blue, Color.green);
520     assert (serializeToUniNode(tm).getMapping["textColor"] == UniNode("blue"));
521     assert (serializeToUniNode(tm).getMapping["numColor"] == UniNode(1));
522 }
523 
524 
525 @("Should work serialization")
526 @system unittest
527 {
528     enum Flag {
529         a = 1<<0,
530         b = 1<<1,
531         c = 1<<2
532     }
533 
534     alias Flags = BitFlags!Flag;
535 
536     enum Gender
537     {
538         M,
539         F
540     }
541 
542     struct Point
543     {
544         int x;
545         int y;
546     }
547 
548     struct Rect
549     {
550         Point pivot;
551         int w;
552         int h;
553     }
554 
555     struct Face
556     {
557         Rect pos;
558         ubyte[] vector;
559         string id;
560         @byName
561         Gender gender;
562         Flags f;
563     }
564 
565     auto orig = Face(Rect(Point(2, 3), 5, 5), [1, 2, 4], "anno",
566             Gender.F, Flags(Flag.a, Flag.b));
567     const data = serializeToUniNode(orig);
568     assert (data.deserializeUniNode!Face == orig);
569 }
570 
571 
572 @("single-element tuples")
573 @safe unittest
574 {
575     static struct F { int field; }
576 
577     {
578         static struct S { typeof(F.init.tupleof) fields; }
579         auto b = serializeToUniNode(S(42));
580         auto a = deserializeUniNode!S(b);
581         assert(a.fields[0] == 42);
582     }
583 
584     {
585         static struct T { @asArray typeof(F.init.tupleof) fields; }
586         auto b = serializeToUniNode(T(42));
587         auto a = deserializeUniNode!(T)(b);
588         assert(a.fields[0] == 42);
589     }
590 }
591 
592 
593 @("@system property getters/setters does not compile")
594 @system unittest {
595     static class A
596     {
597         @property @name("foo")
598         {
599             string fooString() const { return a; }
600             void fooString(string a) { this.a = a; }
601         }
602 
603         private string a;
604     }
605 
606     auto a1 = new A;
607     a1.a = "hello";
608 
609     auto b = serializeToUniNode(a1);
610     const a2 = deserializeUniNode!(A)(b);
611     assert (a1.a == a2.a);
612 }
613 
614 
615 @("Should serialization builtin")
616 @system unittest
617 {
618     static struct Bar { Bar[] foos; int i; }
619     Bar b1 = {[{null, 2}], 1};
620     auto s = serializeToUniNode(b1);
621     const b = deserializeUniNode!(Bar)(s);
622     assert (b.i == 1);
623     assert (b.foos.length == 1);
624     assert (b.foos[0].i == 2);
625 }
626 
627 
628 @("Should de/serialization UniNode")
629 @system unittest
630 {
631     const node = UniNode(1);
632     const sNode = serializeToUniNode(node);
633     assert (node == sNode);
634 
635     const dNode = deserializeUniNode!UniNode(node);
636     assert (dNode == node);
637 }
638