1 /** 2 * Copyright: (c) 2015-2020, 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_node; 9 10 private 11 { 12 import std.algorithm.iteration : map; 13 import std.traits : isArray, isAssociativeArray; 14 import std.exception : assertThrown, collectException; 15 import std.meta : AliasSeq, allSatisfy; 16 import std.array : array; 17 import std.range : iota; 18 import std.conv : text; 19 20 import uninode.node; 21 } 22 23 24 version (unittest) 25 { 26 void testInitNode(T)(T val, bool function(UniNode) @safe checker) @safe 27 { 28 UniNode node = UniNode(val); 29 assert (checker(node)); 30 31 const T cv = val; 32 const cnode = UniNode(cv); 33 assert (checker(cnode)); 34 35 static if (isArray!T) 36 immutable T iv = val.idup; 37 else static if (isAssociativeArray!T) 38 immutable T iv = () @trusted { return cast(immutable) val; } (); 39 else 40 immutable T iv = val; 41 immutable inode = UniNode(iv); 42 assert (checker(inode)); 43 } 44 } 45 46 47 @("Should size normal") 48 @safe unittest 49 { 50 const node = UniNode(1); 51 assert (node.sizeof == 24); 52 } 53 54 55 @("Should allow create node") 56 @safe unittest 57 { 58 const node = UniNode(null); 59 assert (node.canNil); 60 61 testInitNode!(int)(11, (n) => n.canInteger); 62 testInitNode!(uint)(11u, (n) => n.canUinteger); 63 testInitNode!(float)(1.1, (n) => n.canFloating); 64 testInitNode!(double)(1.1, (n) => n.canFloating); 65 testInitNode!(string)("hello", (n) => n.canText); 66 ubyte[2] bytes = [1, 2]; 67 testInitNode(bytes, (n) => n.canRaw); 68 ubyte[] abytes = [1, 2]; 69 testInitNode(abytes, (n) => n.canRaw); 70 71 testInitNode([UniNode(1), UniNode(2)], (n) => n.canSequence); 72 testInitNode(["one": UniNode(1), "two": UniNode(2)], (n) => n.canMapping); 73 74 const anode = UniNode(1, "one", 1.2); 75 assert (anode.canSequence); 76 77 const arr = [UniNode(1), UniNode(2)]; 78 const canode = UniNode(arr); 79 assert (canode.canSequence); 80 } 81 82 83 @("Should allow create sequence node") 84 @safe unittest 85 { 86 const start = iota(5).map!(i => UniNode(i)).array; 87 88 const seq = UniNode(start); 89 static assert (is(typeof(seq.getSequence()) == const(UniNode[]))); 90 assert (seq.canSequence); 91 assert (seq[0].get!int == 0); // opIndex 92 static assert (!__traits(compiles, () { seq[0] = 2; })); // no modify const 93 94 auto mutSeq = UniNode(start); 95 static assert (is(typeof(mutSeq.getSequence()) == UniNode[])); 96 UniNode[] refArr = mutSeq.getSequence; // ref array 97 98 assert (mutSeq[0].get!int == 0); // opIndex 99 mutSeq[0] = 101; // opIndexAssign 100 assert (mutSeq[0].get!int == 101); // opIndex 101 assert (refArr[0].get!int == 101); // array modify too 102 103 mutSeq[0] = UniNode([UniNode(2), UniNode(3)]); 104 assert (mutSeq[0][1].get!int == 3); // nested opIndex 105 106 mutSeq[0][1] = 4; // nested opIndexAssign 107 assert (mutSeq[0][1].get!int == 4); 108 assert (refArr[0].getSequence[1].get!int == 4); // ref array 109 110 assert (mutSeq.getSequence.length == 5); 111 mutSeq ~= UniNode(5); // opOpAssign 112 assert (mutSeq.getSequence.length == 6); 113 assert (refArr.length == 5); // not modify ref array 114 assert (mutSeq[5].get!int == 5); 115 116 mutSeq ~= [UniNode(55), UniNode(44)]; // opOpAssign array 117 assert (mutSeq.getSequence.length == 7); 118 assert (mutSeq[6][1].get!int == 44); 119 120 const eNode = UniNode.emptySequence; 121 assert (eNode.canSequence); 122 123 assert (start.length == 5); 124 } 125 126 127 @("Should allow create mapping node") 128 @safe unittest 129 { 130 const mapp = ["one": UniNode(1), "two": UniNode(2), "three": UniNode(3)]; 131 132 const cMapp = UniNode(mapp); 133 static assert (is(typeof(cMapp.getMapping()) == const(UniNode[string]))); 134 assert (cMapp.canMapping); 135 assert (cMapp["one"].get!int == 1); 136 static assert (!__traits(compiles, () { cMapp["four"] = 4; })); 137 138 auto mutMapp = UniNode(mapp); 139 static assert (is(typeof(mutMapp.getMapping()) == UniNode[string])); 140 assert (mutMapp.canMapping); 141 assert (mutMapp["two"].get!int == 2); 142 UniNode[string] refMapp = mutMapp.getMapping; 143 144 assert (mutMapp["one"].get!int == 1); // opIndex 145 mutMapp["one"] = 101; 146 assert (mutMapp["one"].get!int == 101); 147 assert (refMapp["one"].get!int == 101); 148 149 mutMapp["one"] = UniNode(["one": UniNode(11), "two": UniNode(12)]); 150 assert (mutMapp["one"]["two"].get!int == 12); 151 152 mutMapp["one"]["one"] = 1010; 153 assert (mutMapp["one"]["one"].get!int == 1010); 154 assert (refMapp["one"].getMapping["one"].get!int == 1010); 155 156 assert (mutMapp.getMapping.length == 3); 157 mutMapp["four"] = UniNode(4); 158 assert (mutMapp.getMapping.length == 4); 159 assert (refMapp.length == 4); // ref modify too 160 assert (mutMapp["four"].get!int == 4); 161 assert (mapp.length == 3); // start not modify 162 163 const eMap = UniNode.emptyMapping; 164 assert (eMap.canMapping); 165 166 mutMapp.remove("four"); 167 assert (mutMapp.getMapping.length == 3); 168 assert (refMapp.length == 3); // ref modify too 169 170 assert ("one" in cMapp); 171 auto cNode = "one" in cMapp; 172 assert (cNode.get!int == 1); 173 static assert (is(typeof(cNode) == const(UniNode)*)); 174 175 auto context = UniNode.emptyMapping; 176 context["loop"] = UniNode.emptyMapping; 177 context["loop"]["length"] = UniNode(1); 178 } 179 180 181 @("Should work protect number overflow") 182 @safe unittest 183 { 184 const minNode = UniNode(long.min); 185 auto e = collectException!UniNodeException(minNode.get!ubyte); 186 assert (e.msg == "Signed value less zero"); 187 188 const bigNode = UniNode(long.max); 189 e = collectException!UniNodeException(bigNode.get!ubyte); 190 assert (e.msg == "Conversion positive overflow"); 191 192 const uNode = UniNode(ulong.max); 193 e = collectException!UniNodeException(uNode.get!ubyte); 194 assert (e.msg == "Conversion positive overflow"); 195 196 assertThrown!UniNodeException(uNode.get!long); 197 assertThrown!UniNodeException(bigNode.get!(byte)); 198 } 199 200 201 @("Should allow create unsigned integer node") 202 @safe unittest 203 { 204 foreach (TT; AliasSeq!(ubyte, ushort, uint, ulong)) 205 { 206 TT v = cast(TT)11U; 207 const node = UniNode(v); 208 assert (node.canUinteger); 209 assert (node.get!TT == 11U); 210 } 211 } 212 213 214 @("Should allow create integer node") 215 @safe unittest 216 { 217 foreach (TT; AliasSeq!(byte, short, int, long)) 218 { 219 TT v = -11; 220 const node = UniNode(v); 221 assert (node.canInteger); 222 assert (node.get!TT == -11); 223 } 224 } 225 226 227 @("Should allow boolean node") 228 @safe unittest 229 { 230 const node = UniNode(true); 231 assert (node.canBoolean); 232 assert (node.get!bool == true); 233 234 const nodei = UniNode(0); 235 assert (nodei.canInteger); 236 assert (nodei.get!bool == false); 237 238 const nodeu = UniNode(ulong.max); 239 assert (nodeu.canUinteger); 240 assert (nodeu.get!bool == true); 241 } 242 243 244 @("Should allow create floating node") 245 @safe unittest 246 { 247 foreach (TT; AliasSeq!(float, double)) 248 { 249 TT v = 11.11; 250 const node = UniNode(v); 251 assert (node.canFloating); 252 assert (node.get!TT == cast(TT)11.11); 253 } 254 255 const nodeu = UniNode(11u); 256 assert (nodeu.get!double == 11.0); 257 258 const nodei = UniNode(-11); 259 assert (nodei.get!double == -11.0); 260 } 261 262 263 @("Should allow create string node") 264 @safe unittest 265 { 266 enum text = "hello"; 267 268 const node = UniNode(text); 269 assert(node.canText); 270 assert (node.get!string == text); 271 272 ubyte[] bytes = new ubyte[text.length]; 273 foreach (i, c; text) 274 bytes[i] = c; 275 276 const nodeb = UniNode(bytes); 277 assert(nodeb.get!string == text); 278 } 279 280 281 @("Should allow create raw node") 282 @safe unittest 283 { 284 ubyte[] dynArr = [1, 2, 3]; 285 auto node = UniNode(dynArr); 286 assert (node.canRaw); 287 assert (node.get!(ubyte[]) == [1, 2, 3]); 288 289 ubyte[3] stArr = [1, 2, 3]; 290 node = UniNode(stArr); 291 assert (node.canRaw); 292 assert (node.get!(ubyte[3]) == [1, 2, 3]); 293 294 Bytes bb = [1, 2, 3]; 295 node = UniNode(bb); 296 assert (node.canRaw); 297 assert (node.get!(ubyte[]) == [1, 2, 3]); 298 } 299 300 301 @("Should work numeric node") 302 @safe unittest 303 { 304 auto snode = UniNode(-10); 305 const exc = collectException!UniNodeException(snode.get!ulong); 306 assert (exc && exc.msg == "Signed value less zero"); 307 308 auto unode = UniNode(ulong.max); 309 const exc2 = collectException!UniNodeException(unode.get!long); 310 assert (exc2 && exc2.msg == "Unsigned value great max"); 311 } 312 313 314 @("Should work system code") 315 @system unittest 316 { 317 const node = UniNode(1); 318 auto mnode = UniNode(["one": node, "two": node]); 319 const anode = UniNode([node, node]); 320 321 ulong counter; 322 foreach (string key, const(UniNode) n; mnode.getMapping) 323 counter++; 324 assert (counter == mnode.getMapping.length); 325 326 counter = 0; 327 foreach (ulong idx, const(UniNode) n; anode.getSequence) 328 counter++; 329 assert (counter == anode.getSequence.length); 330 331 counter = 0; 332 foreach (const(UniNode) n; anode.getSequence) 333 counter++; 334 assert (counter == anode.getSequence.length); 335 } 336 337 338 @("Should work opApply for object") 339 @safe unittest 340 { 341 import std.algorithm.searching : canFind, all; 342 343 string[] keys; 344 UniNode[] values; 345 346 const cNode = UniNode(["one": UniNode(1), "two": UniNode(2)]); 347 foreach (string k, const(UniNode) n; cNode) 348 { 349 keys ~= k; 350 values ~= n; 351 } 352 353 UniNode[string] mapp = ["tree": UniNode(3), "four": UniNode(4)]; 354 UniNode mNode = UniNode(mapp); 355 foreach (string k, UniNode n; mNode) 356 { 357 keys ~= k; 358 values ~= n; 359 } 360 361 assert (["one", "two", "tree", "four"].all!((i) => keys.canFind(i))); 362 assert ([UniNode(1), UniNode(2), UniNode(3), UniNode(4)].all!( 363 (i) => values.canFind(i))); 364 } 365 366 367 @("Should work opApply for array") 368 @safe unittest 369 { 370 import std.algorithm.searching : canFind, all; 371 372 auto arr = [UniNode(5), UniNode(7)]; 373 auto node = UniNode(arr); 374 UniNode[] values; 375 foreach (const(UniNode) n; node) 376 values ~= n; 377 378 assert (arr.all!((i) => values.canFind(i))); 379 } 380 381 382 @("Should work opApply for array with idx") 383 @safe unittest 384 { 385 import std.algorithm.searching : canFind, all; 386 387 auto arr = [UniNode(5), UniNode(7), UniNode(3)]; 388 auto node = UniNode(arr); 389 size_t summ; 390 UniNode[] values; 391 foreach (size_t i, UniNode n; node) 392 { 393 values ~= n; 394 summ += i; 395 } 396 397 assert (arr.all!((i) => values.canFind(i))); 398 assert (summ == 3); 399 } 400 401 402 @("Should work opApply for array @system") 403 @system unittest 404 { 405 import std.algorithm.searching : canFind, all; 406 407 auto arr = [UniNode(5), UniNode(7)]; 408 const node = UniNode(arr); 409 UniNode[] values; 410 foreach (UniNode n; node) 411 values ~= n; 412 413 assert (arr.all!((i) => values.canFind(i))); 414 } 415 416 417 @("Should work toHash") 418 @safe unittest 419 { 420 const node1 = UniNode(1); 421 assert (node1.toHash == 1); 422 423 auto val = "hello"; 424 const nodes = UniNode(val); 425 assert (nodes.toHash == val.hashOf); 426 } 427 428 429 @("Should work function default value") 430 @safe unittest 431 { 432 string val; 433 void fun(UniNode node = UniNode("")) 434 { 435 val = node.get!string; 436 } 437 438 fun(); 439 assert (val == ""); 440 fun(UniNode("1")); 441 assert (val == "1"); 442 } 443 444 445 @("Should work any memory types") 446 @safe unittest 447 { 448 immutable inode = UniNode("immutable"); 449 const cnode = UniNode("const"); 450 const node = UniNode("auto"); 451 assert (inode.length == 9); 452 assert (cnode.length == 5); 453 assert (node.length == 4); 454 } 455 456 457 @("Should allow match node") 458 @safe unittest 459 { 460 import std.format : fmt = format; 461 462 alias allMatch = match!( 463 (bool val) => fmt!"got %s"(val), 464 (byte val) => fmt!"got integer %s"(val), 465 (ubyte val) => fmt!"got unsigned integer %s"(val), 466 (float val) => fmt!"got float val %s"(val), 467 (Bytes val) => fmt!"got bytes %s"(val), 468 (string val) => fmt!"got string '%s'"(val), 469 (const(UniNode)[] seq) => fmt!"got seq len %s"(seq.length), 470 (const(UniNode[string]) mapp) => fmt!"got mapping len %s"(mapp.length), 471 () { return "got empty"; } 472 ); 473 474 void testNode(UniNode node, string msg) 475 { 476 const val = allMatch(node); 477 assert (val == msg); 478 } 479 480 testNode(UniNode(), "got empty"); 481 testNode(UniNode(12), "got integer 12"); 482 testNode(UniNode(11u), "got unsigned integer 11"); 483 testNode(UniNode(1.1), "got float val 1.1"); 484 ubyte[2] bytes = [1, 2]; 485 testNode(UniNode(bytes), "got bytes [1, 2]"); 486 testNode(UniNode("hello"), "got string 'hello'"); 487 testNode(UniNode([UniNode(1), UniNode(2)]), "got seq len 2"); 488 testNode(UniNode(["one": UniNode(1), "two": UniNode(2)]), "got mapping len 2"); 489 } 490 491 492 @("Should work length operator") 493 @safe unittest 494 { 495 assert (UniNode("hello").length == 5); 496 ubyte[] bytes = [1, 2, 3]; 497 assert (UniNode(bytes).length == 3); 498 assert (UniNode([UniNode(1), UniNode(2)]).length == 2); 499 assert (UniNode(["one": UniNode(1), "two": UniNode(2)]).length == 2); 500 } 501 502 503 @("Should allow template match node") 504 @safe unittest 505 { 506 const node = UniNode(); 507 const val = node.match!( 508 (val) => "tpl", 509 ); 510 assert (val == "tpl"); 511 512 bool flag = false; 513 node.match!( 514 (val) { flag = true; }, 515 ); 516 assert (flag); 517 } 518 519 520 @("Should work opEquals") 521 @safe unittest 522 { 523 const node1 = UniNode(1); 524 const node2 = UniNode(1u); 525 assert (node1.opEquals(node2)); 526 assert (node2.opEquals(node1)); 527 assert (node1 == node2); 528 529 auto n1 = UniNode(1); 530 auto n2 = UniNode("1"); 531 auto n3 = UniNode(1); 532 533 assert (n1 == n3); 534 assert (n1 != n2); 535 assert (n1 != UniNode(3)); 536 537 assert (UniNode([n1, n2, n3]) != UniNode([n2, n1, n3])); 538 assert (UniNode([n1, n2, n3]) == UniNode([n1, n2, n3])); 539 540 assert (UniNode(["one": n1, "two": n2]) == UniNode(["one": n1, "two": n2])); 541 } 542 543 544 @("Should work toString method") 545 @safe unittest 546 { 547 auto intNode = UniNode(int.max); 548 auto uintNode = UniNode(uint.max); 549 auto fNode = UniNode(float.nan); 550 auto textNode = UniNode("node"); 551 auto boolNode = UniNode(true); 552 ubyte[] bytes = [1, 2, 3]; 553 auto binNode = UniNode(bytes); 554 auto nilNode = UniNode(); 555 556 auto arrNode = UniNode([intNode, fNode, textNode, nilNode]); 557 const objNode = UniNode([ 558 "i": intNode, 559 "ui": uintNode, 560 "f": fNode, 561 "text": textNode, 562 "bool": boolNode, 563 "bin": binNode, 564 "nil": nilNode, 565 "arr": arrNode]); 566 567 assert (objNode.text == "{i:int(2147483647), bool:bool(true), " 568 ~ "text:text(node), arr:[int(2147483647), float(nan), text(node), " 569 ~ "nil], nil:nil, ui:uint(4294967295), bin:raw([1, 2, 3]), f:float(nan)}"); 570 } 571 572 573 @("Should work function parameters") 574 @safe unittest 575 { 576 static int initializer(int start, int end, out UniNode result) 577 { 578 result = UniNode.emptySequence; 579 foreach (int i; start .. end) 580 result ~= UniNode(i); 581 return 0; 582 } 583 584 UniNode ret; 585 assert (!initializer(1, 5, ret)); 586 assert (ret.canSequence); 587 assert (ret.length == 4); 588 589 ret.match!( 590 (ref UniNode[] arr) { arr ~= UniNode(6); }, 591 () {} 592 ); 593 assert (ret.length == 5); 594 595 auto mapp = UniNode.emptyMapping; 596 mapp["two"] = UniNode(33); 597 mapp.match!( 598 (ref UniNode[string] mapp) { mapp = ["one": UniNode(1)]; }, 599 () {} 600 ); 601 assert (mapp["one"].get!int == 1); 602 } 603 604 605 @("Should work require") 606 @safe unittest 607 { 608 UniNode mapp = UniNode.emptyMapping; 609 auto req = mapp.require("one", 1); 610 assert (req == UniNode(1)); 611 req = mapp.require("one", 11); 612 assert (req == UniNode(1)); 613 } 614 615 616 @("Should work opApply modify") 617 @safe unittest 618 { 619 UniNode mapp = UniNode(["one": UniNode(1), "two": UniNode(2)]); 620 foreach (string k, ref UniNode n; mapp) 621 n = UniNode(k); 622 assert (mapp["one"] == UniNode("one")); 623 624 UniNode seq = UniNode([UniNode(1), UniNode(2)]); 625 foreach (size_t idx, ref UniNode n; seq) 626 n = UniNode(idx * 4); 627 assert (seq[1] == UniNode(4)); 628 } 629 630 631 @("Should work opt method") 632 @system unittest 633 { 634 immutable node = UniNode(1); 635 assert (!node.opt!int.isNull); 636 assert (node.opt!string.isNull); 637 assert (node.opt!(UniNode[]).isNull); 638 assert (node.opt!(UniNode[string]).isNull); 639 640 UniNode anode = UniNode([UniNode(1), UniNode(2)]); 641 assert (!anode.opt!(UniNode[]).isNull); 642 assert (!anode.optSequence.isNull); 643 assert (node.optSequence.isNull); 644 645 UniNode mnode = UniNode(["one": UniNode(1), "two": UniNode(2)]); 646 assert (!mnode.opt!(UniNode[string]).isNull); 647 assert (!mnode.optMapping.isNull); 648 assert (node.optMapping.isNull); 649 } 650 651 652 @("Should work getOrElse method") 653 @safe unittest 654 { 655 immutable node = UniNode(1); 656 assert (node.getOrElse(2) == 1); 657 assert (node.getOrElse("h") == "h"); 658 } 659