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