1 /******************************************************************************* 2 * Library unit test file 3 * 4 * Authors: Matthew Soucy, dproto@msoucy.me 5 */ 6 module dproto.unittests; 7 8 import dproto.dproto; 9 10 unittest 11 { 12 assert(__traits(compiles, ProtocolBufferFromString!"message Test 13 { 14 optional string verySimple = 1; 15 }")); 16 } 17 18 unittest 19 { 20 assert(__traits(compiles, ProtocolBufferFromString!" 21 message Test 22 { 23 optional string verySimple = 1; 24 enum TestEnum 25 { 26 ONE = 1; 27 UNO = 1; 28 TOW = 2; 29 } 30 }")); 31 } 32 33 unittest 34 { 35 mixin ProtocolBufferFromString!" 36 message Test 37 { 38 required int32 id = 1; 39 optional string verySimple = 2; 40 enum TestEnum 41 { 42 ONE = 1; 43 UNO = 1; 44 TOW = 2; 45 } 46 optional TestEnum testValue = 3; 47 }"; 48 } 49 50 unittest 51 { 52 assert(__traits(compiles, ProtocolBufferFromString!"message Test 53 { 54 optional string verySimple = 1; 55 enum TestEnum 56 { 57 ONE = 1; 58 UNO = 1; 59 TOW = 2; 60 } 61 62 optional string testValue = 2; 63 }")); 64 } 65 66 unittest 67 { 68 assert(__traits(compiles, ProtocolBufferFromString!" 69 message Test 70 { 71 optional string verySimple = 1; 72 message NestedTest 73 { 74 optional string verySimple = 1; 75 } 76 77 optional NestedTest value = 2; 78 }")); 79 } 80 81 unittest 82 { 83 assert(__traits(compiles, ProtocolBufferFromString!" 84 message Test 85 { 86 optional string verySimple = 1; 87 message NestedTest 88 { 89 optional string verySimple2 = 1; 90 } 91 92 optional NestedTest value = 2; 93 }")); 94 } 95 96 unittest 97 { 98 assert(__traits(compiles, ProtocolBufferFromString!" 99 message Test 100 { 101 optional string verySimple = 1; 102 message NestedTest 103 { 104 optional string verySimple = 1; 105 } 106 107 repeated NestedTest value = 2; 108 }")); 109 } 110 111 unittest 112 { 113 assert(__traits(compiles, ProtocolBufferFromString!" 114 message Test 115 { 116 required int32 id = 3; 117 optional string verySimple = 1; 118 message NestedTest 119 { 120 required string verySimple = 1; 121 } 122 123 required NestedTest value = 2; 124 }")); 125 } 126 127 unittest 128 { 129 assert(__traits(compiles, ProtocolBufferFromString!" 130 message Test 131 { 132 required int32 id = 3; 133 optional string verySimple = 1; 134 message NestedTest 135 { 136 required string verySimple = 1; 137 } 138 139 repeated NestedTest value = 2; 140 }")); 141 } 142 143 unittest 144 { 145 assert(__traits(compiles, ProtocolBufferFromString!" 146 message Test 147 { 148 required int32 id = 3; 149 optional string verySimple = 1; 150 message NestedTest 151 { 152 required string verySimple = 1; 153 } 154 155 optional NestedTest value = 2; 156 }")); 157 } 158 159 unittest 160 { 161 assert(__traits(compiles, ProtocolBufferFromString!" 162 message Test 163 { 164 required int32 id = 3; 165 optional string verySimple = 1; 166 message NestedTest 167 { 168 required string verySimple = 1; 169 } 170 171 repeated NestedTest value = 2; 172 }")); 173 } 174 175 unittest 176 { 177 enum serviceDefinition = " 178 message ServiceRequest { 179 string request = 1; 180 } 181 message ServiceResponse { 182 string response = 1; 183 } 184 service TestService { 185 rpc TestMethod (ServiceRequest) returns (ServiceResponse); 186 } 187 "; 188 189 // Force code coverage in doveralls 190 import std..string; 191 import std.format; 192 import dproto.parse; 193 194 auto normalizedServiceDefinition = "%3.3p".format(ParseProtoSchema("<none>", serviceDefinition)); 195 196 assert(__traits(compiles, ProtocolBufferFromString!serviceDefinition)); 197 assert(__traits(compiles, ProtocolBufferInterface!serviceDefinition)); 198 assert(__traits(compiles, ProtocolBufferRpc!serviceDefinition)); 199 assert(__traits(compiles, ProtocolBufferImpl!serviceDefinition)); 200 assert(__traits(compiles, ProtocolBufferStruct!serviceDefinition)); 201 202 // Example from README.md. 203 mixin ProtocolBufferInterface!serviceDefinition; 204 205 class ServiceImplementation : TestService 206 { 207 ServiceResponse TestMethod(ServiceRequest input) 208 { 209 ServiceResponse output; 210 output.response = "received: " ~ input.request; 211 return output; 212 } 213 } 214 215 auto serviceTest = new ServiceImplementation; 216 ServiceRequest input; 217 input.request = "message"; 218 assert(serviceTest.TestMethod(input).response == "received: message"); 219 220 } 221 222 unittest 223 { 224 mixin ProtocolBufferFromString!" 225 enum PhoneType { 226 MOBILE = 0; 227 HOME = 0xf; 228 WORK = 071; 229 } 230 "; 231 232 assert(PhoneType.MOBILE == 0); 233 assert(PhoneType.HOME == 15); 234 assert(PhoneType.WORK == 57); 235 } 236 237 unittest 238 { 239 mixin ProtocolBufferFromString!" 240 enum PhoneType { 241 MOBILE = 0; 242 HOME = 0; 243 WORK = 2; 244 } 245 246 message Person { 247 required string name = 1; 248 required int32 id = 2; 249 optional string email = 3; 250 251 message PhoneNumber { 252 required string number = 1; 253 optional PhoneType type = 2 [default = HOME]; 254 } 255 256 repeated PhoneNumber phone = 4; 257 } 258 "; 259 260 Person t; 261 assert(t.name == ""); 262 assert(t.id == 0); 263 assert(t.phone.length == 0); 264 version (Have_painlessjson) 265 { 266 assert(t.toJson() == `{"email":"","id":0,"name":"","phone":[]}`); 267 } 268 269 t.name = "Max Musterman"; 270 assert(t.name == "Max Musterman"); 271 272 t.id = 3; 273 assert(t.id == 3); 274 275 t.email = "Max.Musterman@example.com"; 276 assert(t.email); 277 assert(t.email == "Max.Musterman@example.com"); 278 279 Person.PhoneNumber pn1; 280 pn1.number = "0123456789"; 281 assert(pn1.number == "0123456789"); 282 assert(pn1.type == PhoneType.HOME); 283 assert(pn1.type == PhoneType.MOBILE); 284 285 pn1.type = PhoneType.WORK; 286 assert(pn1.type == PhoneType.WORK); 287 assert(pn1.type); 288 assert(pn1.type == 2); 289 290 t.phone ~= pn1; 291 assert(t.phone[0] == pn1); 292 assert(t.phone.length == 1); 293 294 version (Have_painlessjson) 295 { 296 assert( 297 t.toJson() == `{"email":"Max.Musterman@example.com","id":3,"name":"Max Musterman","phone":[{"number":"0123456789","type":2}]}`); 298 } 299 300 pn1.type = pn1.type.init; 301 assert(pn1.type == PhoneType.HOME); 302 303 t.phone = t.phone.init; 304 assert(t.phone.length == 0); 305 306 t.email = t.email.init; 307 assert(t.email == ""); 308 } 309 310 unittest 311 { 312 mixin ProtocolBufferFromString!" 313 message Person { 314 required string name = 1; 315 required int32 id = 2; 316 optional string email = 3; 317 318 enum PhoneType { 319 MOBILE = 0; 320 HOME = 0; 321 WORK = 2; 322 } 323 324 message PhoneNumber { 325 required string number = 1; 326 optional PhoneType type = 2 [default = HOME]; 327 } 328 329 repeated PhoneNumber phone = 4; 330 } 331 332 message AddressBook { 333 repeated Person person = 1; 334 } 335 "; 336 337 Person t; 338 assert(t.name == ""); 339 assert(t.id == 0); 340 assert(t.phone.length == 0); 341 342 t.name = "Max Musterman"; 343 assert(t.name == "Max Musterman"); 344 345 t.id = 3; 346 assert(t.id == 3); 347 348 t.email = "Max.Musterman@example.com"; 349 assert(t.email); 350 assert(t.email == "Max.Musterman@example.com"); 351 352 Person.PhoneNumber pn1; 353 pn1.number = "0123456789"; 354 assert(pn1.number == "0123456789"); 355 356 t.phone ~= pn1; 357 assert(t.phone[0] == pn1); 358 assert(t.phone.length == 1); 359 360 t.phone = t.phone.init; 361 assert(t.phone.length == 0); 362 363 t.email = t.email.init; 364 assert(t.email == ""); 365 366 AddressBook addressbook; 367 assert(addressbook.person.length == 0); 368 addressbook.person ~= t; 369 addressbook.person ~= t; 370 assert(addressbook.person[0] == t); 371 assert(addressbook.person[0] == addressbook.person[1]); 372 assert(addressbook.person.length == 2); 373 } 374 375 unittest 376 { 377 mixin ProtocolBufferFromString!" 378 enum PhoneType { 379 MOBILE = 0; 380 HOME = 0; 381 WORK = 2; 382 } 383 384 message Person { 385 required string name = 1; 386 required int32 id = 2; 387 optional string email = 3; 388 389 message PhoneNumber { 390 required string number = 1; 391 optional PhoneType type = 2 [default = HOME]; 392 } 393 394 repeated PhoneNumber phone = 4; 395 } 396 397 message AddressBook { 398 repeated Person person = 1; 399 } 400 "; 401 402 Person t; 403 t.name = "Max Musterman"; 404 t.id = 3; 405 t.email = "test@example.com"; 406 407 Person.PhoneNumber pn1; 408 pn1.number = "0123456789"; 409 pn1.type = PhoneType.WORK; 410 411 Person.PhoneNumber pn2; 412 pn2.number = "0123456789"; 413 414 t.phone = [pn1, pn2]; 415 AddressBook addressbook; 416 addressbook.person ~= t; 417 addressbook.person ~= t; 418 419 ubyte[] serializedObject = addressbook.serialize(); 420 421 AddressBook addressbook2 = AddressBook.fromProto(serializedObject); 422 assert(addressbook2.person.length == 2); 423 foreach (t2; addressbook2.person[0 .. 1]) 424 { 425 assert(t2.name == "Max Musterman"); 426 assert(t2.id == 3); 427 assert(t2.email); 428 assert(t2.email == "test@example.com"); 429 assert(t2.phone[0].number == "0123456789"); 430 assert(t2.phone[0].type == PhoneType.WORK); 431 assert(t2.phone[1].number == "0123456789"); 432 assert(t2.phone[1].type == PhoneType.HOME); 433 assert(t2.phone[1].type == PhoneType.MOBILE); 434 assert(t2.phone.length == 2); 435 } 436 //the gdc-4.8 evaluates false here. Maybe an compiler bug. 437 version (DigitalMars) 438 { 439 assert(addressbook2.person[0] == addressbook.person[1]); 440 } 441 } 442 443 unittest 444 { 445 mixin ProtocolBufferFromString!" 446 message Person { 447 required string name = 1; 448 required int32 id = 2; 449 optional string email = 3; 450 451 enum PhoneType { 452 MOBILE = 0; 453 HOME = 0; 454 WORK = 2; 455 } 456 457 message PhoneNumber { 458 required string number = 1; 459 optional PhoneType type = 2 [default = HOME]; 460 } 461 462 repeated PhoneNumber phone = 4; 463 } 464 465 message AddressBook { 466 repeated Person person = 1; 467 } 468 "; 469 470 Person t; 471 assert(t.name == ""); 472 assert(t.id == 0); 473 assert(t.phone.length == 0); 474 475 t.name = "Max Musterman"; 476 assert(t.name == "Max Musterman"); 477 478 t.id = 3; 479 assert(t.id == 3); 480 481 t.email = "Max.Musterman@example.com"; 482 assert(t.email); 483 assert(t.email == "Max.Musterman@example.com"); 484 485 Person.PhoneNumber pn1; 486 pn1.number = "0123456789"; 487 assert(pn1.number == "0123456789"); 488 assert(pn1.type == Person.PhoneType.HOME); 489 assert(pn1.type == Person.PhoneType.MOBILE); 490 491 pn1.type = Person.PhoneType.WORK; 492 assert(pn1.type == Person.PhoneType.WORK); 493 assert(pn1.type == 2); 494 assert(pn1.type); 495 496 t.phone ~= pn1; 497 assert(t.phone[0] == pn1); 498 assert(t.phone.length == 1); 499 500 pn1.type = pn1.type.init; 501 assert(pn1.type == Person.PhoneType.HOME); 502 503 t.phone = t.phone.init; 504 assert(t.phone.length == 0); 505 506 t.email = t.email.init; 507 assert(t.email == ""); 508 509 AddressBook addressbook; 510 assert(addressbook.person.length == 0); 511 addressbook.person ~= t; 512 addressbook.person ~= t; 513 assert(addressbook.person[0] == t); 514 assert(addressbook.person[0] == addressbook.person[1]); 515 assert(addressbook.person.length == 2); 516 517 static struct OutBuf 518 { 519 @nogc: 520 @safe: 521 void put(in ubyte) 522 { 523 } 524 525 void put(in ubyte[]) 526 { 527 } 528 } 529 530 @nogc void testNoGC() 531 { 532 OutBuf buf; 533 addressbook.serializeTo(buf); 534 } 535 536 testNoGC(); 537 } 538 539 unittest 540 { 541 mixin ProtocolBufferFromString!" 542 message Person { 543 required string name = 1; 544 } 545 "; 546 547 static auto rvalue(in ubyte[] val) 548 { 549 return val; 550 } 551 552 enum data = cast(ubyte[])[1 << 3 | 2, "abc".length] ~ cast(ubyte[]) "abc"; 553 const(ubyte)[] val = data; 554 assert(val.length == 5); 555 assert(Person(rvalue(val)).name == "abc"); 556 assert(val.length == 5); 557 assert(Person(val).name == "abc"); 558 assert(val.length == 0); 559 Person p; 560 val = data; 561 assert(val.length == 5); 562 p.deserialize(rvalue(val)); 563 assert(val.length == 5); 564 assert(p.name == "abc"); 565 p.name = null; 566 p.deserialize(val); 567 assert(val.length == 0); 568 assert(p.name == "abc"); 569 } 570 571 unittest 572 { 573 mixin ProtocolBufferFromString!" 574 message Field_Name_Equals_Internal_Variable_Name { 575 required int32 r = 1; 576 required int32 data = 2; 577 required int32 msgdata = 3; 578 } 579 "; 580 } 581 582 unittest 583 { 584 import dproto.exception; 585 import dproto.serialize; 586 import dproto.parse; 587 import std..string : strip; 588 589 auto proto_src = `import "foo/baz.proto";`; 590 auto proto_struct = ParseProtoSchema("<none>", proto_src); 591 auto d_src = proto_struct.toD; 592 assert(`mixin ProtocolBuffer!"foo/baz.proto";` == d_src, 593 "Mixin string should not have two double quotes " ~ d_src); 594 assert(proto_src == proto_struct.toProto.strip, 595 "Round tripping to protobuf source should yield starting text " ~ proto_struct.toProto); 596 } 597 598 unittest 599 { 600 mixin ProtocolBufferFromString!` 601 enum RecordFlags 602 { 603 Announce = 1; 604 Cancel = 2; 605 SomeAnotherFlag = 4; // look at the enumeration! 606 } 607 message KeyValue 608 { 609 required bytes key = 1; 610 optional RecordFlags flags = 2; 611 optional bytes payload = 3; 612 } 613 message ECDSASignature 614 { 615 required bytes signature = 1; 616 required bytes pubKey = 2; 617 } 618 message Signed 619 { 620 required ECDSASignature esignature = 1; 621 required KeyValue keyValue = 2; 622 }`; 623 624 Signed d1; 625 d1.keyValue.key = cast(ubyte[]) "key data"; 626 d1.keyValue.payload = cast(ubyte[]) "value data"; 627 auto ser = d1.serialize(); 628 Signed d2 = ser; 629 assert(d1.keyValue.key == d2.keyValue.key); 630 assert(d1.keyValue.payload == d2.keyValue.payload); 631 } 632 633 unittest 634 { 635 636 mixin ProtocolBufferFromString!` 637 message DNSPayload 638 { 639 repeated bytes assignOwnerPubKeys = 1; 640 repeated bytes assignManagersPubKeys = 2; 641 642 repeated bytes ns = 3; 643 } 644 `; 645 646 DNSPayload p1; 647 p1.ns ~= [1, 2, 3]; 648 auto buf = p1.serialize(); 649 650 DNSPayload p2; 651 p2.deserialize(buf); 652 assert(p1 == p2); 653 } 654 655 unittest 656 { 657 mixin ProtocolBufferFromString!" 658 message Person { 659 required uint32 id = 1; 660 }"; 661 } 662 663 unittest 664 { 665 mixin ProtocolBufferFromString!` 666 message TestStructure 667 { 668 optional string optional_string = 1; 669 required string required_string = 2; 670 repeated string repeated_string = 3; 671 } 672 `; 673 import dproto.attributes : TagId; 674 675 assert(TagId!(TestStructure.optional_string) == 1); 676 assert(TagId!(TestStructure.required_string) == 2); 677 assert(TagId!(TestStructure.repeated_string) == 3); 678 } 679 680 unittest 681 { 682 mixin ProtocolBufferFromString!" 683 message Stats { 684 optional int32 agility = 1; 685 optional int32 stamina = 2; 686 } 687 message Character { 688 optional string name = 1; 689 optional Stats stats = 2; 690 } 691 message Account { 692 optional string owner = 1; 693 optional Character main = 2; 694 } 695 "; 696 const int agility = 200; 697 auto acct = Account(); 698 auto main = Character(); 699 main.name = "Hogan"; 700 main.stats = Stats(); 701 main.stats.agility = agility; 702 acct.main = main; 703 auto ser = acct.serialize(); 704 Account acct_rx; 705 acct_rx.deserialize(ser); 706 import std..string : format; 707 708 assert(acct_rx.main.stats.agility == agility, format("Expected %d, got %d", 709 agility, acct_rx.main.stats.agility)); 710 711 } 712 713 unittest 714 { 715 enum pbstring = q{ 716 enum Enum { 717 A = 0; 718 B = 1; 719 C = 2; 720 } 721 722 message Msg { 723 optional Enum unset = 1; 724 optional Enum isset_first = 2 [default = A]; 725 optional Enum isset_last = 3 [default = C]; 726 required Enum unset_required = 4; 727 required Enum isset_required = 5 [default = B]; 728 optional int32 i1 = 6 [default = 42]; 729 optional int32 i2 = 7; 730 required int32 i3 = 8 [default = 24]; 731 required int32 i4 = 9; 732 } 733 }; 734 735 // Force code coverage in doveralls 736 import std..string; 737 import std.format; 738 import dproto.parse; 739 740 auto normalizedServiceDefinition = "%3.3p".format(ParseProtoSchema("<none>", pbstring)); 741 742 mixin ProtocolBufferFromString!pbstring; 743 744 Msg msg; 745 assert(msg.unset == Enum.A); 746 assert(msg.isset_first == Enum.A); 747 assert(msg.isset_last == Enum.C); 748 assert(msg.unset_required == Enum.A); 749 assert(msg.isset_required == Enum.B); 750 assert(msg.i1 == 42); 751 assert(msg.i2 == typeof(msg.i2).init); 752 assert(msg.i3 == 24); 753 assert(msg.i4 == typeof(msg.i4).init); 754 } 755 756 unittest 757 { 758 import dproto.parse; 759 import dproto.exception; 760 import std.exception; 761 762 enum pbstring = q{ 763 message Info { 764 optional int32 version = 1 [default = -1]; 765 } 766 }; 767 assertThrown!DProtoReservedWordException(ParseProtoSchema( 768 "<none>", 769 `option dproto_reserved_fmt = "%s"; ` ~ pbstring)); 770 assertNotThrown!DProtoReservedWordException(ParseProtoSchema( 771 "<none>", 772 `option dproto_reserved_fmt = "%s_"; ` ~ pbstring)); 773 assertNotThrown!DProtoReservedWordException(ParseProtoSchema( 774 "<none>", pbstring)); 775 } 776 777 unittest 778 { 779 mixin ProtocolBufferFromString!` 780 message HeaderBBox { 781 required sint64 left = 1; 782 required sint64 right = 2; 783 required sint64 top = 3; 784 required sint64 bottom = 4; 785 }`; 786 HeaderBBox headerBBox; 787 788 headerBBox.left = 10; 789 headerBBox.right = 5; 790 headerBBox.top = -32; 791 headerBBox.bottom = -24; 792 793 auto hbb = headerBBox.serialize(); 794 headerBBox = HeaderBBox(hbb); // Error occurred here 795 796 assert(headerBBox.left == 10); 797 assert(headerBBox.right == 5); 798 assert(headerBBox.top == -32); 799 assert(headerBBox.bottom == -24); 800 } 801 802 unittest 803 { 804 assert(!__traits(compiles, mixin(`mixin ProtocolBufferFromString!q{ 805 message One { 806 required string a; 807 required int32 b; 808 } 809 };`)), 810 "Malformed proto structure accepted"); 811 } 812 813 unittest 814 { 815 import std.algorithm; 816 817 mixin ProtocolBufferFromString!` 818 message Foo { 819 repeated uint32 arr = 1 [packed=true]; 820 } 821 `; 822 823 Foo foo; 824 foo.arr = [1]; 825 826 auto serialized_foo = foo.serialize(); 827 828 auto foo2 = Foo(serialized_foo); 829 830 assert(equal(foo.arr, foo2.arr)); 831 } 832 833 unittest 834 { 835 // Issue #86 836 import dproto.parse; 837 import dproto.exception; 838 enum pbstring = q{ 839 message ReservedWordTest { 840 required bool notReservedWord = 1; 841 } 842 }; 843 mixin ProtocolBufferFromString!pbstring; 844 assert(ParseProtoSchema("<none>", pbstring).toD()); 845 } 846 847 unittest 848 { 849 import std.algorithm; 850 851 mixin ProtocolBufferFromString!` 852 message FooA { 853 repeated uint32 arr = 1 [packed=true]; 854 } 855 message FooB { 856 repeated uint32 arr = 1; 857 } 858 `; 859 860 FooA foo; 861 foo.arr = [1, 3, 5, 7, 2, 4, 6, 8]; 862 863 auto serialized_foo = foo.serialize(); 864 auto foo2 = FooB(serialized_foo); 865 assert(equal(foo.arr, foo2.arr)); 866 auto foo3 = FooA(foo2.serialize()); 867 assert(equal(foo2.arr, foo3.arr)); 868 } 869 870 unittest 871 { 872 // Issue #86 873 import dproto.parse; 874 import dproto.exception; 875 import std.exception; 876 877 enum pbstring = q{ 878 message ReservedWordTest { 879 required bool notReservedWord; 880 } 881 }; 882 assertThrown!DProtoSyntaxException(ParseProtoSchema("<none>", pbstring)); 883 } 884 885 unittest 886 { 887 // Issue #89 888 import dproto.dproto; 889 890 mixin ProtocolBufferFromString!q{ 891 message Test { 892 repeated double id = 1 [packed = true]; 893 } 894 895 }; 896 897 Test t; 898 899 t.id = [123]; 900 901 auto s = t.serialize(); 902 t = Test(s); 903 904 assert(t.id == [123]); 905 } 906 907 unittest 908 { 909 // Issue #92 910 import dproto.dproto : ProtocolBufferFromString; 911 import dproto.parse : ParseProtoSchema; 912 913 enum syntaxProto2 = ` 914 syntax = "proto2"; 915 `; 916 static assert(__traits(compiles, ProtocolBufferFromString!syntaxProto2)); 917 918 enum schemaProto2 = ParseProtoSchema("<none>", syntaxProto2); 919 static assert(schemaProto2.syntax == `"proto2"`); 920 921 enum syntaxProto3 = ` 922 syntax = "proto3"; 923 `; 924 static assert(__traits(compiles, ProtocolBufferFromString!syntaxProto3)); 925 926 enum schemaProto3 = ParseProtoSchema("<none>", syntaxProto3); 927 static assert(schemaProto3.syntax == `"proto3"`); 928 929 enum syntaxNoEquals = ` 930 syntax "proto2"; 931 `; 932 static assert(!__traits(compiles, ProtocolBufferFromString!syntaxNoEquals)); 933 934 enum syntaxNoQuotes = ` 935 syntax = proto2; 936 `; 937 static assert(!__traits(compiles, ProtocolBufferFromString!syntaxNoQuotes)); 938 939 enum syntaxNoLQuote = ` 940 syntax = proto2"; 941 `; 942 static assert(!__traits(compiles, ProtocolBufferFromString!syntaxNoLQuote)); 943 944 enum syntaxNoRQuote = ` 945 syntax = "proto2; 946 `; 947 static assert(!__traits(compiles, ProtocolBufferFromString!syntaxNoRQuote)); 948 949 enum syntaxNoSemicolon = ` 950 syntax = "proto2" 951 `; 952 static assert(!__traits(compiles, ProtocolBufferFromString!syntaxNoSemicolon)); 953 } 954 955 unittest 956 { 957 // Issue #26 958 import dproto.parse; 959 import dproto.exception; 960 import std.exception; 961 962 enum pbstring = q{ 963 import public; 964 }; 965 mixin ProtocolBufferFromString!pbstring; 966 } 967 968 unittest 969 { 970 // Issue #26 971 import dproto.parse; 972 import dproto.exception; 973 import std.exception; 974 975 enum pbstring = q{ 976 import public "proto/example.proto"; 977 }; 978 assert(ParseProtoSchema("<none>", pbstring).toD()); 979 } 980 981 unittest 982 { 983 import dproto.parse; 984 import dproto.exception; 985 import std.exception; 986 987 enum pbstring = q{ 988 enum Foo { 989 option allow_alias = false; 990 ONE = 1; 991 TWO = 1; 992 THREE = 3; 993 } 994 }; 995 assertThrown!DProtoSyntaxException(ParseProtoSchema("<none>", pbstring)); 996 997 enum pbstring2 = q{ 998 enum Foo { 999 ONE = 1; 1000 TWO = 1; 1001 THREE = 3; 1002 } 1003 }; 1004 assertNotThrown!DProtoSyntaxException(ParseProtoSchema("<none>", pbstring2)); 1005 } 1006 1007 unittest 1008 { 1009 // Issue #92 1010 1011 import dproto.parse; 1012 import dproto.exception; 1013 import std.exception; 1014 1015 enum pbstring = ` 1016 syntax = "proto3"; 1017 `; 1018 assertNotThrown!DProtoSyntaxException(ParseProtoSchema("<none>", pbstring)); 1019 } 1020 1021 unittest 1022 { 1023 enum pbstring = ` 1024 message MyMsgAux { 1025 optional string foo1=1; 1026 } 1027 1028 message MyMsg { 1029 required MyMsgAux a1=1; 1030 optional MyMsgAux a2=2; 1031 } 1032 `; 1033 mixin ProtocolBufferFromString!pbstring; 1034 1035 MyMsg proto; 1036 proto.a1.foo1 = "bar"; // ok 1037 proto.a2 = MyMsgAux(); 1038 proto.a2.foo1 = "bar"; // Called `get' on null Nullable!MymsgAux 1039 assert(proto.a1.foo1 == proto.a2.foo1); 1040 } 1041 1042 unittest 1043 { 1044 mixin ProtocolBufferFromString!" 1045 message Person { 1046 required string name = 1; 1047 required int32 id = 2; 1048 optional string email = 3; 1049 1050 enum PhoneType { 1051 MOBILE = 0; 1052 HOME = 0; 1053 WORK = 2; 1054 } 1055 1056 message PhoneNumber { 1057 required string number = 1; 1058 optional PhoneType type = 2 [default = HOME]; 1059 } 1060 1061 repeated PhoneNumber phone = 4; 1062 } 1063 "; 1064 1065 Person t; 1066 t.name = "Max Musterman"; 1067 t.id = 3; 1068 t.email = "Max.Musterman@example.com"; 1069 1070 Person.PhoneNumber pn1; 1071 pn1.number = "0123456789"; 1072 pn1.type = Person.PhoneType.WORK; 1073 t.phone ~= pn1; 1074 1075 Person t2 = t; 1076 t2.id = 5; 1077 t2.email = "Namretsum.Xam@example.com"; 1078 1079 Person.PhoneNumber pn2; 1080 pn2.number = "9876543210"; 1081 pn2.type = Person.PhoneType.HOME; 1082 t2.phone = [pn2]; 1083 1084 import std.stdio; 1085 t.mergeFrom(t2); 1086 assert(t.phone.length == 2); 1087 assert(t.phone[0] == pn1); 1088 assert(t.phone[1] == pn2); 1089 assert(t.id == t.id); 1090 assert(t.email == t2.email); 1091 1092 } 1093