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