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