43 #include <google/protobuf/unittest.pb.h>
44 #include <google/protobuf/unittest_custom_options.pb.h>
63 class MockErrorCollector :
public io::ErrorCollector {
65 MockErrorCollector() =
default;
66 ~MockErrorCollector()
override =
default;
82 class MockValidationErrorCollector :
public DescriptorPool::ErrorCollector {
84 MockValidationErrorCollector(
const SourceLocationTable& source_locations,
85 io::ErrorCollector* wrapped_collector)
88 ~MockValidationErrorCollector() {}
113 void SetupParser(
const char* text) {
114 raw_input_.reset(
new io::ArrayInputStream(text, strlen(text)));
124 void ExpectParsesTo(
const char*
input,
const char*
output) {
143 EXPECT_EQ(expected.DebugString(), actual.DebugString());
147 void ExpectHasErrors(
const char* text,
const char* expected_errors) {
148 ExpectHasEarlyExitErrors(text, expected_errors);
154 void ExpectHasEarlyExitErrors(
const char* text,
const char* expected_errors) {
163 void ExpectHasValidationErrors(
const char* text,
164 const char* expected_errors) {
166 SourceLocationTable source_locations;
167 parser_->RecordSourceLocationsTo(&source_locations);
175 MockValidationErrorCollector validation_error_collector(source_locations,
178 file, &validation_error_collector) ==
NULL);
193 TEST_F(ParserTest, StopAfterSyntaxIdentifier) {
196 "syntax = \"foobar\";\n"
197 "this line will not be parsed\n");
198 parser_->SetStopAfterSyntaxIdentifier(
true);
204 TEST_F(ParserTest, StopAfterOmittedSyntaxIdentifier) {
207 "this line will not be parsed\n");
208 parser_->SetStopAfterSyntaxIdentifier(
true);
214 TEST_F(ParserTest, StopAfterSyntaxIdentifierWithErrors) {
217 "syntax = error;\n");
218 parser_->SetStopAfterSyntaxIdentifier(
true);
223 TEST_F(ParserTest, WarnIfSyntaxIdentifierOmmitted) {
224 SetupParser(
"message A {}");
232 TEST_F(ParserTest, WarnIfFieldNameIsNotUpperCamel) {
234 "syntax = \"proto2\";"
239 "Message name should be in UpperCamelCase. Found: abc.") !=
243 TEST_F(ParserTest, WarnIfFieldNameIsNotLowerUnderscore) {
245 "syntax = \"proto2\";"
247 " optional string SongName = 1;"
252 "Field name should be lowercase. Found: SongName") !=
256 TEST_F(ParserTest, WarnIfFieldNameContainsNumberImmediatelyFollowUnderscore) {
258 "syntax = \"proto2\";"
260 " optional string song_name_1 = 1;"
265 "Number should not come right after an underscore. Found: "
266 "song_name_1.") != string::npos);
271 typedef ParserTest ParseMessageTest;
273 TEST_F(ParseMessageTest, IgnoreBOM) {
275 " message TestMessage {\n"
276 " required int32 foo = 1;\n"
279 input[0] = (char)0xEF;
280 input[1] = (char)0xBB;
281 input[2] = (char)0xBF;
285 " name: \"TestMessage\""
286 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }"
290 TEST_F(ParseMessageTest, BOMError) {
292 " message TestMessage {\n"
293 " required int32 foo = 1;\n"
295 input[0] = (char)0xEF;
296 ExpectHasErrors(
input,
297 "0:1: Proto file starts with 0xEF but not UTF-8 BOM. "
298 "Only UTF-8 is accepted for proto file.\n"
299 "0:0: Expected top-level statement (e.g. \"message\").\n");
302 TEST_F(ParseMessageTest, SimpleMessage) {
304 "message TestMessage {\n"
305 " required int32 foo = 1;\n"
309 " name: \"TestMessage\""
310 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }"
314 TEST_F(ParseMessageTest, ImplicitSyntaxIdentifier) {
317 "message TestMessage {\n"
318 " required int32 foo = 1;\n"
322 " name: \"TestMessage\""
323 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }"
328 TEST_F(ParseMessageTest, ExplicitSyntaxIdentifier) {
330 "syntax = \"proto2\";\n"
331 "message TestMessage {\n"
332 " required int32 foo = 1;\n"
337 " name: \"TestMessage\""
338 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }"
343 TEST_F(ParseMessageTest, ExplicitRequiredSyntaxIdentifier) {
346 "syntax = \"proto2\";\n"
347 "message TestMessage {\n"
348 " required int32 foo = 1;\n"
353 " name: \"TestMessage\""
354 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }"
359 TEST_F(ParseMessageTest, SimpleFields) {
361 "message TestMessage {\n"
362 " required int32 foo = 15;\n"
363 " optional int32 bar = 34;\n"
364 " repeated int32 baz = 3;\n"
368 " name: \"TestMessage\""
369 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:15 }"
370 " field { name:\"bar\" label:LABEL_OPTIONAL type:TYPE_INT32 number:34 }"
371 " field { name:\"baz\" label:LABEL_REPEATED type:TYPE_INT32 number:3 }"
375 TEST_F(ParseMessageTest, PrimitiveFieldTypes) {
377 "message TestMessage {\n"
378 " required int32 foo = 1;\n"
379 " required int64 foo = 1;\n"
380 " required uint32 foo = 1;\n"
381 " required uint64 foo = 1;\n"
382 " required sint32 foo = 1;\n"
383 " required sint64 foo = 1;\n"
384 " required fixed32 foo = 1;\n"
385 " required fixed64 foo = 1;\n"
386 " required sfixed32 foo = 1;\n"
387 " required sfixed64 foo = 1;\n"
388 " required float foo = 1;\n"
389 " required double foo = 1;\n"
390 " required string foo = 1;\n"
391 " required bytes foo = 1;\n"
392 " required bool foo = 1;\n"
396 " name: \"TestMessage\""
397 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 "
399 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT64 number:1 "
401 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_UINT32 number:1 "
403 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_UINT64 number:1 "
405 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_SINT32 number:1 "
407 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_SINT64 number:1 "
409 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_FIXED32 number:1 "
411 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_FIXED64 number:1 "
413 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_SFIXED32 number:1 "
415 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_SFIXED64 number:1 "
417 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_FLOAT number:1 "
419 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_DOUBLE number:1 "
421 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_STRING number:1 "
423 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_BYTES number:1 "
425 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_BOOL number:1 "
430 TEST_F(ParseMessageTest, FieldDefaults) {
432 "message TestMessage {\n"
433 " required int32 foo = 1 [default= 1 ];\n"
434 " required int32 foo = 1 [default= -2 ];\n"
435 " required int64 foo = 1 [default= 3 ];\n"
436 " required int64 foo = 1 [default= -4 ];\n"
437 " required uint32 foo = 1 [default= 5 ];\n"
438 " required uint64 foo = 1 [default= 6 ];\n"
439 " required float foo = 1 [default= 7.5];\n"
440 " required float foo = 1 [default= -8.5];\n"
441 " required float foo = 1 [default= 9 ];\n"
442 " required double foo = 1 [default= 10.5];\n"
443 " required double foo = 1 [default=-11.5];\n"
444 " required double foo = 1 [default= 12 ];\n"
445 " required double foo = 1 [default= inf ];\n"
446 " required double foo = 1 [default=-inf ];\n"
447 " required double foo = 1 [default= nan ];\n"
448 " required string foo = 1 [default='13\\001'];\n"
449 " required string foo = 1 [default='a' \"b\" \n \"c\"];\n"
450 " required bytes foo = 1 [default='14\\002'];\n"
451 " required bytes foo = 1 [default='a' \"b\" \n 'c'];\n"
452 " required bool foo = 1 [default=true ];\n"
453 " required Foo foo = 1 [default=FOO ];\n"
455 " required int32 foo = 1 [default= 0x7FFFFFFF];\n"
456 " required int32 foo = 1 [default=-0x80000000];\n"
457 " required uint32 foo = 1 [default= 0xFFFFFFFF];\n"
458 " required int64 foo = 1 [default= 0x7FFFFFFFFFFFFFFF];\n"
459 " required int64 foo = 1 [default=-0x8000000000000000];\n"
460 " required uint64 foo = 1 [default= 0xFFFFFFFFFFFFFFFF];\n"
461 " required double foo = 1 [default= 0xabcd];\n"
464 #define
ETC "name:\"foo\" label:LABEL_REQUIRED number:1"
466 " name: \"TestMessage\""
467 " field { type:TYPE_INT32 default_value:\"1\" " ETC
469 " field { type:TYPE_INT32 default_value:\"-2\" " ETC
471 " field { type:TYPE_INT64 default_value:\"3\" " ETC
473 " field { type:TYPE_INT64 default_value:\"-4\" " ETC
475 " field { type:TYPE_UINT32 default_value:\"5\" " ETC
477 " field { type:TYPE_UINT64 default_value:\"6\" " ETC
479 " field { type:TYPE_FLOAT default_value:\"7.5\" " ETC
481 " field { type:TYPE_FLOAT default_value:\"-8.5\" " ETC
483 " field { type:TYPE_FLOAT default_value:\"9\" " ETC
485 " field { type:TYPE_DOUBLE default_value:\"10.5\" " ETC
487 " field { type:TYPE_DOUBLE default_value:\"-11.5\" " ETC
489 " field { type:TYPE_DOUBLE default_value:\"12\" " ETC
491 " field { type:TYPE_DOUBLE default_value:\"inf\" " ETC
493 " field { type:TYPE_DOUBLE default_value:\"-inf\" " ETC
495 " field { type:TYPE_DOUBLE default_value:\"nan\" " ETC
497 " field { type:TYPE_STRING default_value:\"13\\001\" " ETC
499 " field { type:TYPE_STRING default_value:\"abc\" " ETC
501 " field { type:TYPE_BYTES default_value:\"14\\\\002\" " ETC
503 " field { type:TYPE_BYTES default_value:\"abc\" " ETC
505 " field { type:TYPE_BOOL default_value:\"true\" " ETC
507 " field { type_name:\"Foo\" default_value:\"FOO\" " ETC
511 " type:TYPE_INT32 default_value:\"2147483647\" " ETC
514 " type:TYPE_INT32 default_value:\"-2147483648\" " ETC
517 " type:TYPE_UINT32 default_value:\"4294967295\" " ETC
520 " type:TYPE_INT64 default_value:\"9223372036854775807\" " ETC
523 " type:TYPE_INT64 default_value:\"-9223372036854775808\" " ETC
526 " type:TYPE_UINT64 default_value:\"18446744073709551615\" " ETC
529 " type:TYPE_DOUBLE default_value:\"43981\" " ETC
535 TEST_F(ParseMessageTest, FieldJsonName) {
537 "message TestMessage {\n"
538 " optional string foo = 1 [json_name = \"@type\"];\n"
541 " name: \"TestMessage\""
543 " name: \"foo\" label: LABEL_OPTIONAL type: TYPE_STRING number: 1"
544 " json_name: \"@type\"\n"
551 "message TestMessage {\n"
552 " optional string foo = 1\n"
553 " [ctype=CORD, (foo)=7, foo.(.bar.baz).qux.quux.(corge)=-33, \n"
554 " (quux)=\"x\040y\", (baz.qux)=hey];\n"
558 " name: \"TestMessage\""
559 " field { name: \"foo\" label: LABEL_OPTIONAL type: TYPE_STRING number: "
561 " options { uninterpreted_option: { name { name_part: \"ctype\" "
562 " is_extension: false "
564 " identifier_value: \"CORD\" "
566 " uninterpreted_option: { name { name_part: \"foo\" "
567 " is_extension: true } "
568 " positive_int_value: 7 }"
569 " uninterpreted_option: { name { name_part: \"foo\" "
570 " is_extension: false "
572 " name { name_part: "
574 " is_extension: true } "
575 " name { name_part: \"qux\" "
576 " is_extension: false "
578 " name { name_part: \"quux\" "
579 " is_extension: false "
581 " name { name_part: \"corge\" "
582 " is_extension: true } "
583 " negative_int_value: -33 }"
584 " uninterpreted_option: { name { name_part: \"quux\" "
585 " is_extension: true } "
586 " string_value: \"x y\" }"
587 " uninterpreted_option: { name { name_part: "
589 " is_extension: true } "
590 " identifier_value: \"hey\" }"
598 "message TestMessage {\n"
602 " TestMessage c = 3;\n"
603 " group D = 4 { optional int32 i = 5; }\n"
608 " name: \"TestMessage\""
609 " field { name:\"a\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 "
611 " field { name:\"b\" label:LABEL_OPTIONAL type:TYPE_STRING number:2 "
613 " field { name:\"c\" label:LABEL_OPTIONAL type_name:\"TestMessage\" "
614 " number:3 oneof_index:0 }"
615 " field { name:\"d\" label:LABEL_OPTIONAL type:TYPE_GROUP "
616 " type_name:\"D\" number:4 oneof_index:0 }"
622 " field { name:\"i\" label:LABEL_OPTIONAL type:TYPE_INT32 number:5 }"
627 TEST_F(ParseMessageTest, MultipleOneofs) {
629 "message TestMessage {\n"
641 " name: \"TestMessage\""
642 " field { name:\"a\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 "
644 " field { name:\"b\" label:LABEL_OPTIONAL type:TYPE_STRING number:2 "
646 " field { name:\"c\" label:LABEL_OPTIONAL type:TYPE_INT32 number:3 "
648 " field { name:\"d\" label:LABEL_OPTIONAL type:TYPE_STRING number:4 "
659 TEST_F(ParseMessageTest, Maps) {
661 "message TestMessage {\n"
662 " map<int32, string> primitive_type_map = 1;\n"
663 " map<KeyType, ValueType> composite_type_map = 2;\n"
667 " name: \"TestMessage\""
669 " name: \"PrimitiveTypeMapEntry\""
671 " name: \"key\" number: 1 label:LABEL_OPTIONAL"
675 " name: \"value\" number: 2 label:LABEL_OPTIONAL"
678 " options { map_entry: true }"
681 " name: \"CompositeTypeMapEntry\""
683 " name: \"key\" number: 1 label:LABEL_OPTIONAL"
684 " type_name: \"KeyType\""
687 " name: \"value\" number: 2 label:LABEL_OPTIONAL"
688 " type_name: \"ValueType\""
690 " options { map_entry: true }"
693 " name: \"primitive_type_map\""
694 " label: LABEL_REPEATED"
695 " type_name: \"PrimitiveTypeMapEntry\""
699 " name: \"composite_type_map\""
700 " label: LABEL_REPEATED"
701 " type_name: \"CompositeTypeMapEntry\""
707 TEST_F(ParseMessageTest, Group) {
709 "message TestMessage {\n"
710 " optional group TestGroup = 1 {};\n"
714 " name: \"TestMessage\""
715 " nested_type { name: \"TestGroup\" }"
716 " field { name:\"testgroup\" label:LABEL_OPTIONAL number:1"
717 " type:TYPE_GROUP type_name: \"TestGroup\" }"
721 TEST_F(ParseMessageTest, NestedMessage) {
723 "message TestMessage {\n"
724 " message Nested {}\n"
725 " optional Nested test_nested = 1;\n"
729 " name: \"TestMessage\""
730 " nested_type { name: \"Nested\" }"
731 " field { name:\"test_nested\" label:LABEL_OPTIONAL number:1"
732 " type_name: \"Nested\" }"
736 TEST_F(ParseMessageTest, NestedEnum) {
738 "message TestMessage {\n"
739 " enum NestedEnum {}\n"
740 " optional NestedEnum test_enum = 1;\n"
744 " name: \"TestMessage\""
745 " enum_type { name: \"NestedEnum\" }"
746 " field { name:\"test_enum\" label:LABEL_OPTIONAL number:1"
747 " type_name: \"NestedEnum\" }"
751 TEST_F(ParseMessageTest, ReservedRange) {
753 "message TestMessage {\n"
754 " required int32 foo = 1;\n"
755 " reserved 2, 15, 9 to 11, 3, 20 to max;\n"
759 " name: \"TestMessage\""
760 " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }"
761 " reserved_range { start:2 end:3 }"
762 " reserved_range { start:15 end:16 }"
763 " reserved_range { start:9 end:12 }"
764 " reserved_range { start:3 end:4 }"
765 " reserved_range { start:20 end:536870912 }"
769 TEST_F(ParseMessageTest, ReservedRangeOnMessageSet) {
771 "message TestMessage {\n"
772 " option message_set_wire_format = true;\n"
773 " reserved 20 to max;\n"
777 " name: \"TestMessage\""
779 " uninterpreted_option {"
781 " name_part: \"message_set_wire_format\""
782 " is_extension: false"
784 " identifier_value: \"true\""
787 " reserved_range { start:20 end:2147483647 }"
791 TEST_F(ParseMessageTest, ReservedNames) {
793 "message TestMessage {\n"
794 " reserved \"foo\", \"bar\";\n"
798 " name: \"TestMessage\""
799 " reserved_name: \"foo\""
800 " reserved_name: \"bar\""
804 TEST_F(ParseMessageTest, ExtensionRange) {
806 "message TestMessage {\n"
807 " extensions 10 to 19;\n"
808 " extensions 30 to max;\n"
812 " name: \"TestMessage\""
813 " extension_range { start:10 end:20 }"
814 " extension_range { start:30 end:536870912 }"
818 TEST_F(ParseMessageTest, ExtensionRangeWithOptions) {
820 "message TestMessage {\n"
821 " extensions 10 to 19 [(i) = 5];\n"
825 " name: \"TestMessage\""
830 " uninterpreted_option {"
833 " is_extension: true"
835 " positive_int_value: 5"
842 TEST_F(ParseMessageTest, CompoundExtensionRange) {
844 "message TestMessage {\n"
845 " extensions 2, 15, 9 to 11, 100 to max, 3;\n"
849 " name: \"TestMessage\""
850 " extension_range { start:2 end:3 }"
851 " extension_range { start:15 end:16 }"
852 " extension_range { start:9 end:12 }"
853 " extension_range { start:100 end:536870912 }"
854 " extension_range { start:3 end:4 }"
858 TEST_F(ParseMessageTest, CompoundExtensionRangeWithOptions) {
860 "message TestMessage {\n"
861 " extensions 2, 15, 9 to 11, 100 to max, 3 [(i) = 5];\n"
865 " name: \"TestMessage\""
870 " uninterpreted_option {"
873 " is_extension: true"
875 " positive_int_value: 5"
883 " uninterpreted_option {"
886 " is_extension: true"
888 " positive_int_value: 5"
896 " uninterpreted_option {"
899 " is_extension: true"
901 " positive_int_value: 5"
909 " uninterpreted_option {"
912 " is_extension: true"
914 " positive_int_value: 5"
922 " uninterpreted_option {"
925 " is_extension: true"
927 " positive_int_value: 5"
934 TEST_F(ParseMessageTest, LargerMaxForMessageSetWireFormatMessages) {
939 "message TestMessage {\n"
940 " extensions 4 to max;\n"
941 " option message_set_wire_format = true;\n"
945 " name: \"TestMessage\""
946 " extension_range { start:4 end: 0x7fffffff }"
948 " uninterpreted_option { \n"
950 " name_part: \"message_set_wire_format\"\n"
951 " is_extension: false\n"
953 " identifier_value: \"true\"\n"
961 "extend Extendee1 { optional int32 foo = 12; }\n"
962 "extend Extendee2 { repeated TestMessage bar = 22; }\n",
964 "extension { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:12"
965 " extendee: \"Extendee1\" } "
966 "extension { name:\"bar\" label:LABEL_REPEATED number:22"
967 " type_name:\"TestMessage\" extendee: \"Extendee2\" }");
970 TEST_F(ParseMessageTest, ExtensionsInMessageScope) {
972 "message TestMessage {\n"
973 " extend Extendee1 { optional int32 foo = 12; }\n"
974 " extend Extendee2 { repeated TestMessage bar = 22; }\n"
978 " name: \"TestMessage\""
979 " extension { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 "
981 " extendee: \"Extendee1\" }"
982 " extension { name:\"bar\" label:LABEL_REPEATED number:22"
983 " type_name:\"TestMessage\" extendee: \"Extendee2\" }"
987 TEST_F(ParseMessageTest, MultipleExtensionsOneExtendee) {
989 "extend Extendee1 {\n"
990 " optional int32 foo = 12;\n"
991 " repeated TestMessage bar = 22;\n"
994 "extension { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:12"
995 " extendee: \"Extendee1\" } "
996 "extension { name:\"bar\" label:LABEL_REPEATED number:22"
997 " type_name:\"TestMessage\" extendee: \"Extendee1\" }");
1000 TEST_F(ParseMessageTest, OptionalLabelProto3) {
1002 "syntax = \"proto3\";\n"
1003 "message TestMessage {\n"
1007 "syntax: \"proto3\" "
1009 " name: \"TestMessage\""
1010 " field { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 } "
1016 typedef ParserTest ParseEnumTest;
1018 TEST_F(ParseEnumTest, SimpleEnum) {
1025 " name: \"TestEnum\""
1026 " value { name:\"FOO\" number:0 }"
1036 " HEX_MAX = 0x7FFFFFFF;\n"
1037 " HEX_MIN = -0x80000000;\n"
1038 " INT_MAX = 2147483647;\n"
1039 " INT_MIN = -2147483648;\n"
1043 " name: \"TestEnum\""
1044 " value { name:\"FOO\" number:13 }"
1045 " value { name:\"BAR\" number:-10 }"
1046 " value { name:\"BAZ\" number:500 }"
1047 " value { name:\"HEX_MAX\" number:2147483647 }"
1048 " value { name:\"HEX_MIN\" number:-2147483648 }"
1049 " value { name:\"INT_MAX\" number:2147483647 }"
1050 " value { name:\"INT_MIN\" number:-2147483648 }"
1054 TEST_F(ParseEnumTest, ValueOptions) {
1058 " BAR = -10 [ (something.text) = 'abc' ];\n"
1059 " BAZ = 500 [ (something.text) = 'def', other = 1 ];\n"
1063 " name: \"TestEnum\""
1064 " value { name: \"FOO\" number: 13 }"
1065 " value { name: \"BAR\" number: -10 "
1067 " uninterpreted_option { "
1068 " name { name_part: \"something.text\" is_extension: true } "
1069 " string_value: \"abc\" "
1073 " value { name: \"BAZ\" number: 500 "
1075 " uninterpreted_option { "
1076 " name { name_part: \"something.text\" is_extension: true } "
1077 " string_value: \"def\" "
1079 " uninterpreted_option { "
1080 " name { name_part: \"other\" is_extension: false } "
1081 " positive_int_value: 1 "
1088 TEST_F(ParseEnumTest, ReservedRange) {
1092 " reserved -2147483648, -6 to -4, -1 to 1, 2, 15, 9 to 11, 3, 20 to "
1097 " name: \"TestEnum\""
1098 " value { name:\"FOO\" number:0 }"
1099 " reserved_range { start:-2147483648 end:-2147483648 }"
1100 " reserved_range { start:-6 end:-4 }"
1101 " reserved_range { start:-1 end:1 }"
1102 " reserved_range { start:2 end:2 }"
1103 " reserved_range { start:15 end:15 }"
1104 " reserved_range { start:9 end:11 }"
1105 " reserved_range { start:3 end:3 }"
1106 " reserved_range { start:20 end:2147483647 }"
1110 TEST_F(ParseEnumTest, ReservedNames) {
1114 " reserved \"foo\", \"bar\";\n"
1118 " name: \"TestEnum\""
1119 " value { name:\"FOO\" number:0 }"
1120 " reserved_name: \"foo\""
1121 " reserved_name: \"bar\""
1127 typedef ParserTest ParseServiceTest;
1129 TEST_F(ParseServiceTest, SimpleService) {
1131 "service TestService {\n"
1132 " rpc Foo(In) returns (Out);\n"
1136 " name: \"TestService\""
1137 " method { name:\"Foo\" input_type:\"In\" output_type:\"Out\" }"
1141 TEST_F(ParseServiceTest, MethodsAndStreams) {
1143 "service TestService {\n"
1144 " rpc Foo(In1) returns (Out1);\n"
1145 " rpc Bar(In2) returns (Out2);\n"
1146 " rpc Baz(In3) returns (Out3);\n"
1150 " name: \"TestService\""
1151 " method { name:\"Foo\" input_type:\"In1\" output_type:\"Out1\" }"
1152 " method { name:\"Bar\" input_type:\"In2\" output_type:\"Out2\" }"
1153 " method { name:\"Baz\" input_type:\"In3\" output_type:\"Out3\" }"
1161 typedef ParserTest ParseMiscTest;
1163 TEST_F(ParseMiscTest, ParseImport) {
1164 ExpectParsesTo(
"import \"foo/bar/baz.proto\";\n",
1165 "dependency: \"foo/bar/baz.proto\"");
1168 TEST_F(ParseMiscTest, ParseMultipleImports) {
1170 "import \"foo.proto\";\n"
1171 "import \"bar.proto\";\n"
1172 "import \"baz.proto\";\n",
1173 "dependency: \"foo.proto\""
1174 "dependency: \"bar.proto\""
1175 "dependency: \"baz.proto\"");
1178 TEST_F(ParseMiscTest, ParsePublicImports) {
1180 "import \"foo.proto\";\n"
1181 "import public \"bar.proto\";\n"
1182 "import \"baz.proto\";\n"
1183 "import public \"qux.proto\";\n",
1184 "dependency: \"foo.proto\""
1185 "dependency: \"bar.proto\""
1186 "dependency: \"baz.proto\""
1187 "dependency: \"qux.proto\""
1188 "public_dependency: 1 "
1189 "public_dependency: 3 ");
1192 TEST_F(ParseMiscTest, ParsePackage) {
1193 ExpectParsesTo(
"package foo.bar.baz;\n",
"package: \"foo.bar.baz\"");
1196 TEST_F(ParseMiscTest, ParsePackageWithSpaces) {
1198 "package foo . bar. \n"
1200 "package: \"foo.bar.baz\"");
1206 TEST_F(ParseMiscTest, ParseFileOptions) {
1208 "option java_package = \"com.google.foo\";\n"
1209 "option optimize_for = CODE_SIZE;",
1212 "uninterpreted_option { name { name_part: \"java_package\" "
1213 " is_extension: false }"
1214 " string_value: \"com.google.foo\"} "
1215 "uninterpreted_option { name { name_part: \"optimize_for\" "
1216 " is_extension: false }"
1217 " identifier_value: \"CODE_SIZE\" } "
1231 typedef ParserTest ParseErrorTest;
1233 TEST_F(ParseErrorTest, MissingSyntaxIdentifier) {
1235 ExpectHasEarlyExitErrors(
"message TestMessage {}",
1236 "0:0: File must begin with a syntax statement, e.g. "
1237 "'syntax = \"proto2\";'.\n");
1241 TEST_F(ParseErrorTest, UnknownSyntaxIdentifier) {
1242 ExpectHasEarlyExitErrors(
1243 "syntax = \"no_such_syntax\";",
1244 "0:9: Unrecognized syntax identifier \"no_such_syntax\". This parser "
1245 "only recognizes \"proto2\" and \"proto3\".\n");
1249 TEST_F(ParseErrorTest, SimpleSyntaxError) {
1250 ExpectHasErrors(
"message TestMessage @#$ { blah }",
1251 "0:20: Expected \"{\".\n");
1255 TEST_F(ParseErrorTest, ExpectedTopLevel) {
1256 ExpectHasErrors(
"blah;",
1257 "0:0: Expected top-level statement (e.g. \"message\").\n");
1260 TEST_F(ParseErrorTest, UnmatchedCloseBrace) {
1262 ExpectHasErrors(
"}",
1263 "0:0: Expected top-level statement (e.g. \"message\").\n"
1264 "0:0: Unmatched \"}\".\n");
1270 TEST_F(ParseErrorTest, MessageMissingName) {
1271 ExpectHasErrors(
"message {}",
"0:8: Expected message name.\n");
1274 TEST_F(ParseErrorTest, MessageMissingBody) {
1275 ExpectHasErrors(
"message TestMessage;",
"0:19: Expected \"{\".\n");
1278 TEST_F(ParseErrorTest, EofInMessage) {
1280 "message TestMessage {",
1281 "0:21: Reached end of input in message definition (missing '}').\n");
1284 TEST_F(ParseErrorTest, MissingFieldNumber) {
1286 "message TestMessage {\n"
1287 " optional int32 foo;\n"
1289 "1:20: Missing field number.\n");
1292 TEST_F(ParseErrorTest, ExpectedFieldNumber) {
1294 "message TestMessage {\n"
1295 " optional int32 foo = ;\n"
1297 "1:23: Expected field number.\n");
1300 TEST_F(ParseErrorTest, FieldNumberOutOfRange) {
1302 "message TestMessage {\n"
1303 " optional int32 foo = 0x100000000;\n"
1305 "1:23: Integer out of range.\n");
1308 TEST_F(ParseErrorTest, MissingLabel) {
1310 "message TestMessage {\n"
1313 "1:2: Expected \"required\", \"optional\", or \"repeated\".\n");
1316 TEST_F(ParseErrorTest, ExpectedOptionName) {
1318 "message TestMessage {\n"
1319 " optional uint32 foo = 1 [];\n"
1321 "1:27: Expected identifier.\n");
1324 TEST_F(ParseErrorTest, NonExtensionOptionNameBeginningWithDot) {
1326 "message TestMessage {\n"
1327 " optional uint32 foo = 1 [.foo=1];\n"
1329 "1:27: Expected identifier.\n");
1332 TEST_F(ParseErrorTest, DefaultValueTypeMismatch) {
1334 "message TestMessage {\n"
1335 " optional uint32 foo = 1 [default=true];\n"
1337 "1:35: Expected integer for field default value.\n");
1340 TEST_F(ParseErrorTest, DefaultValueNotBoolean) {
1342 "message TestMessage {\n"
1343 " optional bool foo = 1 [default=blah];\n"
1345 "1:33: Expected \"true\" or \"false\".\n");
1348 TEST_F(ParseErrorTest, DefaultValueNotString) {
1350 "message TestMessage {\n"
1351 " optional string foo = 1 [default=1];\n"
1353 "1:35: Expected string for field default value.\n");
1356 TEST_F(ParseErrorTest, DefaultValueUnsignedNegative) {
1358 "message TestMessage {\n"
1359 " optional uint32 foo = 1 [default=-1];\n"
1361 "1:36: Unsigned field can't have negative default value.\n");
1364 TEST_F(ParseErrorTest, DefaultValueTooLarge) {
1366 "message TestMessage {\n"
1367 " optional int32 foo = 1 [default= 0x80000000];\n"
1368 " optional int32 foo = 1 [default=-0x80000001];\n"
1369 " optional uint32 foo = 1 [default= 0x100000000];\n"
1370 " optional int64 foo = 1 [default= 0x80000000000000000];\n"
1371 " optional int64 foo = 1 [default=-0x80000000000000001];\n"
1372 " optional uint64 foo = 1 [default= 0x100000000000000000];\n"
1374 "1:36: Integer out of range.\n"
1375 "2:36: Integer out of range.\n"
1376 "3:36: Integer out of range.\n"
1377 "4:36: Integer out of range.\n"
1378 "5:36: Integer out of range.\n"
1379 "6:36: Integer out of range.\n");
1382 TEST_F(ParseErrorTest, JsonNameNotString) {
1384 "message TestMessage {\n"
1385 " optional string foo = 1 [json_name=1];\n"
1387 "1:37: Expected string for JSON name.\n");
1390 TEST_F(ParseErrorTest, DuplicateJsonName) {
1392 "message TestMessage {\n"
1393 " optional uint32 foo = 1 [json_name=\"a\",json_name=\"b\"];\n"
1395 "1:41: Already set option \"json_name\".\n");
1398 TEST_F(ParseErrorTest, EnumValueOutOfRange) {
1401 " HEX_TOO_BIG = 0x80000000;\n"
1402 " HEX_TOO_SMALL = -0x80000001;\n"
1403 " INT_TOO_BIG = 2147483648;\n"
1404 " INT_TOO_SMALL = -2147483649;\n"
1406 "1:19: Integer out of range.\n"
1407 "2:19: Integer out of range.\n"
1408 "3:19: Integer out of range.\n"
1409 "4:19: Integer out of range.\n");
1412 TEST_F(ParseErrorTest, EnumAllowAliasFalse) {
1415 " option allow_alias = false;\n"
1419 "5:0: \"Foo\" declares 'option allow_alias = false;' which has no "
1421 "Please remove the declaration.\n");
1424 TEST_F(ParseErrorTest, UnnecessaryEnumAllowAlias) {
1427 " option allow_alias = true;\n"
1431 "5:0: \"Foo\" declares support for enum aliases but no enum values share "
1432 "field numbers. Please remove the unnecessary 'option allow_alias = "
1437 TEST_F(ParseErrorTest, DefaultValueMissing) {
1439 "message TestMessage {\n"
1440 " optional uint32 foo = 1 [default=];\n"
1442 "1:35: Expected integer for field default value.\n");
1445 TEST_F(ParseErrorTest, DefaultValueForGroup) {
1447 "message TestMessage {\n"
1448 " optional group Foo = 1 [default=blah] {}\n"
1450 "1:34: Messages can't have default values.\n");
1453 TEST_F(ParseErrorTest, DuplicateDefaultValue) {
1455 "message TestMessage {\n"
1456 " optional uint32 foo = 1 [default=1,default=2];\n"
1458 "1:37: Already set option \"default\".\n");
1461 TEST_F(ParseErrorTest, MissingOneofName) {
1463 "message TestMessage {\n"
1468 "1:8: Expected oneof name.\n");
1471 TEST_F(ParseErrorTest, LabelInOneof) {
1473 "message TestMessage {\n"
1475 " optional int32 bar = 1;\n"
1478 "2:4: Fields in oneofs must not have labels (required / optional "
1482 TEST_F(ParseErrorTest, MapInOneof) {
1484 "message TestMessage {\n"
1486 " map<int32, int32> foo_map = 1;\n"
1487 " map message_field = 2;\n"
1490 "2:7: Map fields are not allowed in oneofs.\n");
1493 TEST_F(ParseErrorTest, LabelForMap) {
1495 "message TestMessage {\n"
1496 " optional map<int32, int32> int_map = 1;\n"
1497 " required map<int32, int32> int_map2 = 2;\n"
1498 " repeated map<int32, int32> int_map3 = 3;\n"
1499 " optional map map_message = 4;\n"
1501 "1:14: Field labels (required/optional/repeated) are not allowed on map "
1503 "2:14: Field labels (required/optional/repeated) are not allowed on map "
1505 "3:14: Field labels (required/optional/repeated) are not allowed on map "
1509 TEST_F(ParseErrorTest, MalformedMaps) {
1511 "message TestMessage {\n"
1512 " map map_message = 1;\n"
1513 " map<string> str_map = 2;\n"
1514 " map<string,> str_map2 = 3;\n"
1515 " map<,string> str_map3 = 4;\n"
1516 " map<> empty_map = 5;\n"
1517 " map<string,string str_map6 = 6;\n"
1519 "extend SomeMessage {\n"
1520 " map<int32, int32> int_map = 1;\n"
1522 "1:6: Expected \"required\", \"optional\", or \"repeated\".\n"
1523 "2:12: Expected \",\".\n"
1524 "3:13: Expected type name.\n"
1525 "4:6: Expected type name.\n"
1526 "5:6: Expected type name.\n"
1527 "6:20: Expected \">\".\n"
1528 "8:5: Map fields are not allowed to be extensions.\n");
1531 TEST_F(ParseErrorTest, GroupNotCapitalized) {
1533 "message TestMessage {\n"
1534 " optional group foo = 1 {}\n"
1536 "1:17: Group names must start with a capital letter.\n");
1539 TEST_F(ParseErrorTest, GroupMissingBody) {
1541 "message TestMessage {\n"
1542 " optional group Foo = 1;\n"
1544 "1:24: Missing group body.\n");
1547 TEST_F(ParseErrorTest, ExtendingPrimitive) {
1548 ExpectHasErrors(
"extend int32 { optional string foo = 4; }\n",
1549 "0:7: Expected message type.\n");
1552 TEST_F(ParseErrorTest, ErrorInExtension) {
1554 "message Foo { extensions 100 to 199; }\n"
1555 "extend Foo { optional string foo; }\n",
1556 "1:32: Missing field number.\n");
1559 TEST_F(ParseErrorTest, MultipleParseErrors) {
1563 "message TestMessage {\n"
1564 " optional int32 foo;\n"
1565 " !invalid statement ending in a block { blah blah { blah } blah }\n"
1566 " optional int32 bar = 3 {}\n"
1568 "1:20: Missing field number.\n"
1569 "2:2: Expected \"required\", \"optional\", or \"repeated\".\n"
1570 "2:2: Expected type name.\n"
1571 "3:25: Expected \";\".\n");
1574 TEST_F(ParseErrorTest, EofInAggregateValue) {
1576 "option (fileopt) = { i:100\n",
1577 "1:0: Unexpected end of stream while parsing aggregate value.\n");
1580 TEST_F(ParseErrorTest, ExplicitOptionalLabelProto3) {
1582 "syntax = 'proto3';\n"
1583 "message TestMessage {\n"
1584 " optional int32 foo = 1;\n"
1586 "2:11: Explicit 'optional' labels are disallowed in the Proto3 syntax. "
1587 "To define 'optional' fields in Proto3, simply remove the 'optional' "
1588 "label, as fields are 'optional' by default.\n");
1594 TEST_F(ParseErrorTest, EofInEnum) {
1597 "0:15: Reached end of input in enum definition (missing '}').\n");
1600 TEST_F(ParseErrorTest, EnumValueMissingNumber) {
1605 "1:5: Missing numeric value for enum constant.\n");
1608 TEST_F(ParseErrorTest, EnumReservedStandaloneMaxNotAllowed) {
1614 "2:11: Expected enum value or number range.\n");
1617 TEST_F(ParseErrorTest, EnumReservedMixNameAndNumber) {
1621 " reserved 10, \"foo\";\n"
1623 "2:15: Expected enum number range.\n");
1626 TEST_F(ParseErrorTest, EnumReservedPositiveNumberOutOfRange) {
1630 " reserved 2147483648;\n"
1632 "2:11: Integer out of range.\n");
1635 TEST_F(ParseErrorTest, EnumReservedNegativeNumberOutOfRange) {
1639 " reserved -2147483649;\n"
1641 "2:12: Integer out of range.\n");
1644 TEST_F(ParseErrorTest, EnumReservedMissingQuotes) {
1650 "2:11: Expected enum value or number range.\n");
1656 TEST_F(ParseErrorTest, ReservedStandaloneMaxNotAllowed) {
1661 "1:11: Expected field name or number range.\n");
1664 TEST_F(ParseErrorTest, ReservedMixNameAndNumber) {
1667 " reserved 10, \"foo\";\n"
1669 "1:15: Expected field number range.\n");
1672 TEST_F(ParseErrorTest, ReservedMissingQuotes) {
1677 "1:11: Expected field name or number range.\n");
1680 TEST_F(ParseErrorTest, ReservedNegativeNumber) {
1685 "1:11: Expected field name or number range.\n");
1688 TEST_F(ParseErrorTest, ReservedNumberOutOfRange) {
1691 " reserved 2147483648;\n"
1693 "1:11: Integer out of range.\n");
1699 TEST_F(ParseErrorTest, EofInService) {
1701 "service TestService {",
1702 "0:21: Reached end of input in service definition (missing '}').\n");
1705 TEST_F(ParseErrorTest, ServiceMethodPrimitiveParams) {
1707 "service TestService {\n"
1708 " rpc Foo(int32) returns (string);\n"
1710 "1:10: Expected message type.\n"
1711 "1:26: Expected message type.\n");
1715 TEST_F(ParseErrorTest, EofInMethodOptions) {
1717 "service TestService {\n"
1718 " rpc Foo(Bar) returns(Bar) {",
1719 "1:29: Reached end of input in method options (missing '}').\n"
1720 "1:29: Reached end of input in service definition (missing '}').\n");
1724 TEST_F(ParseErrorTest, PrimitiveMethodInput) {
1726 "service TestService {\n"
1727 " rpc Foo(int32) returns(Bar);\n"
1729 "1:10: Expected message type.\n");
1733 TEST_F(ParseErrorTest, MethodOptionTypeError) {
1738 " rpc Bar(Baz) returns(Baz) { option invalid syntax; }\n"
1740 "2:45: Expected \"=\".\n");
1747 TEST_F(ParseErrorTest, ImportNotQuoted) {
1748 ExpectHasErrors(
"import foo;\n",
1749 "0:7: Expected a string naming the file to import.\n");
1752 TEST_F(ParseErrorTest, MultiplePackagesInFile) {
1756 "1:0: Multiple package definitions.\n");
1764 typedef ParserTest ParserValidationErrorTest;
1766 TEST_F(ParserValidationErrorTest, PackageNameError) {
1774 ExpectHasValidationErrors(
1776 "0:0: \"foo\" is already defined (as something other than a package) "
1777 "in file \"bar.proto\".\n");
1780 TEST_F(ParserValidationErrorTest, ImportUnloadedError) {
1781 ExpectHasValidationErrors(
1784 "import \"unloaded.proto\";",
1785 "2:0: Import \"unloaded.proto\" has not been loaded.\n");
1788 TEST_F(ParserValidationErrorTest, ImportTwice) {
1794 ExpectHasValidationErrors(
1797 "import \"bar.proto\";\n"
1798 " import \"bar.proto\";",
1799 "3:2: Import \"bar.proto\" was listed twice.\n");
1802 TEST_F(ParserValidationErrorTest, DuplicateFileError) {
1807 ExpectHasValidationErrors(
1808 "package test;",
"0:0: A file with this name is already in the pool.\n");
1811 TEST_F(ParserValidationErrorTest, MessageNameError) {
1812 ExpectHasValidationErrors(
1815 "1:8: \"Foo\" is already defined.\n");
1818 TEST_F(ParserValidationErrorTest, FieldNameError) {
1819 ExpectHasValidationErrors(
1821 " optional int32 bar = 1;\n"
1822 " optional int32 bar = 2;\n"
1824 "2:17: \"bar\" is already defined in \"Foo\".\n");
1827 TEST_F(ParserValidationErrorTest, FieldTypeError) {
1828 ExpectHasValidationErrors(
1830 " optional Baz bar = 1;\n"
1832 "1:11: \"Baz\" is not defined.\n");
1835 TEST_F(ParserValidationErrorTest, FieldNumberError) {
1836 ExpectHasValidationErrors(
1838 " optional int32 bar = 0;\n"
1840 "1:23: Field numbers must be positive integers.\n");
1843 TEST_F(ParserValidationErrorTest, FieldExtendeeError) {
1844 ExpectHasValidationErrors(
"extend Baz { optional int32 bar = 1; }\n",
1845 "0:7: \"Baz\" is not defined.\n");
1848 TEST_F(ParserValidationErrorTest, ExtensionJsonNameError) {
1849 ExpectHasValidationErrors(
1850 "message TestMessage {\n"
1851 " extensions 1 to 100;\n"
1853 "extend TestMessage {\n"
1854 " optional int32 foo = 12 [json_name = \"bar\"];\n"
1856 "4:27: option json_name is not allowed on extension fields.\n");
1859 TEST_F(ParserValidationErrorTest, FieldDefaultValueError) {
1860 ExpectHasValidationErrors(
1861 "enum Baz { QUX = 1; }\n"
1863 " optional Baz bar = 1 [default=NO_SUCH_VALUE];\n"
1865 "2:32: Enum type \"Baz\" has no value named \"NO_SUCH_VALUE\".\n");
1868 TEST_F(ParserValidationErrorTest, FileOptionNameError) {
1869 ExpectHasValidationErrors(
1871 "0:7: Option \"foo\" unknown. Ensure that your proto definition file "
1872 "imports the proto which defines the option.\n");
1875 TEST_F(ParserValidationErrorTest, FileOptionValueError) {
1876 ExpectHasValidationErrors(
1877 "option java_outer_classname = 5;",
1878 "0:30: Value must be quoted string for string option "
1879 "\"google.protobuf.FileOptions.java_outer_classname\".\n");
1882 TEST_F(ParserValidationErrorTest, FieldOptionNameError) {
1883 ExpectHasValidationErrors(
1885 " optional bool bar = 1 [foo=1];\n"
1887 "1:25: Option \"foo\" unknown. Ensure that your proto definition file "
1888 "imports the proto which defines the option.\n");
1891 TEST_F(ParserValidationErrorTest, FieldOptionValueError) {
1892 ExpectHasValidationErrors(
1894 " optional int32 bar = 1 [ctype=1];\n"
1896 "1:32: Value must be identifier for enum-valued option "
1897 "\"google.protobuf.FieldOptions.ctype\".\n");
1900 TEST_F(ParserValidationErrorTest, ExtensionRangeNumberError) {
1901 ExpectHasValidationErrors(
1905 "1:13: Extension numbers must be positive integers.\n");
1908 TEST_F(ParserValidationErrorTest, Proto3ExtensionError) {
1909 ExpectHasValidationErrors(
1910 "syntax = 'proto3';\n"
1912 " extensions 100 to 199;\n"
1914 "extend Foo { string foo = 101; }\n",
1915 "4:7: Extensions in proto3 are only allowed for defining options.\n"
1916 "2:13: Extension ranges are not allowed in proto3.\n");
1919 TEST_F(ParserValidationErrorTest, Proto3MessageSet) {
1920 ExpectHasValidationErrors(
1921 "syntax = 'proto3';\n"
1923 " option message_set_wire_format = true;\n"
1925 "1:8: MessageSet is not supported in proto3.\n");
1928 TEST_F(ParserValidationErrorTest, Proto3Required) {
1929 ExpectHasValidationErrors(
1930 "syntax = 'proto3';\n"
1932 " required int32 field = 1;"
1934 "2:11: Required fields are not allowed in proto3.\n");
1937 TEST_F(ParserValidationErrorTest, Proto3Default) {
1938 ExpectHasValidationErrors(
1939 "syntax = 'proto3';\n"
1941 " int32 field = 1 [default = 12];"
1943 "2:29: Explicit default values are not allowed in proto3.\n");
1946 TEST_F(ParserValidationErrorTest, Proto3JsonConflictError) {
1947 ExpectHasValidationErrors(
1948 "syntax = 'proto3';\n"
1949 "message TestMessage {\n"
1950 " uint32 foo = 1;\n"
1951 " uint32 Foo = 2;\n"
1953 "3:9: The JSON camel-case name of field \"Foo\" conflicts with field "
1954 "\"foo\". This is not allowed in proto3.\n");
1957 TEST_F(ParserValidationErrorTest, EnumNameError) {
1958 ExpectHasValidationErrors(
1959 "enum Foo {A = 1;}\n"
1960 "enum Foo {B = 1;}\n",
1961 "1:5: \"Foo\" is already defined.\n");
1964 TEST_F(ParserValidationErrorTest, Proto3EnumError) {
1965 ExpectHasValidationErrors(
1966 "syntax = 'proto3';\n"
1967 "enum Foo {A = 1;}\n",
1968 "1:14: The first enum value must be zero in proto3.\n");
1971 TEST_F(ParserValidationErrorTest, EnumValueNameError) {
1972 ExpectHasValidationErrors(
1977 "2:2: \"BAR\" is already defined.\n");
1980 TEST_F(ParserValidationErrorTest, EnumValueAliasError) {
1981 ExpectHasValidationErrors(
1986 "2:8: \"BAZ\" uses the same enum value as \"BAR\". If this is "
1987 "intended, set 'option allow_alias = true;' to the enum "
1991 TEST_F(ParserValidationErrorTest, ExplicitlyMapEntryError) {
1992 ExpectHasValidationErrors(
1994 " message ValueEntry {\n"
1995 " option map_entry = true;\n"
1996 " optional int32 key = 1;\n"
1997 " optional int32 value = 2;\n"
1998 " extensions 99 to 999;\n"
2000 " repeated ValueEntry value = 1;\n"
2002 "7:11: map_entry should not be set explicitly. Use "
2003 "map<KeyType, ValueType> instead.\n");
2006 TEST_F(ParserValidationErrorTest, ServiceNameError) {
2007 ExpectHasValidationErrors(
2010 "1:8: \"Foo\" is already defined.\n");
2013 TEST_F(ParserValidationErrorTest, MethodNameError) {
2014 ExpectHasValidationErrors(
2017 " rpc Bar(Baz) returns(Baz);\n"
2018 " rpc Bar(Baz) returns(Baz);\n"
2020 "3:6: \"Bar\" is already defined in \"Foo\".\n");
2024 TEST_F(ParserValidationErrorTest, MethodInputTypeError) {
2025 ExpectHasValidationErrors(
2028 " rpc Bar(Qux) returns(Baz);\n"
2030 "2:10: \"Qux\" is not defined.\n");
2034 TEST_F(ParserValidationErrorTest, MethodOutputTypeError) {
2035 ExpectHasValidationErrors(
2038 " rpc Bar(Baz) returns(Qux);\n"
2040 "2:23: \"Qux\" is not defined.\n");
2044 TEST_F(ParserValidationErrorTest, ResovledUndefinedError) {
2054 ExpectHasValidationErrors(
2055 "package foo.base;\n"
2056 "import \"base.proto\";\n"
2058 " optional base.bar baz = 1;\n"
2059 " optional .base.bar quz = 2;\n"
2061 "3:11: \"base.bar\" is resolved to \"foo.base.bar\","
2062 " which is not defined. The innermost scope is searched first "
2063 "in name resolution. Consider using a leading '.'(i.e., \".base.bar\")"
2064 " to start from the outermost scope.\n");
2067 TEST_F(ParserValidationErrorTest, ResovledUndefinedOptionError) {
2079 other_file.
set_name(
"base2.proto");
2087 field->set_name(
"foo");
2088 field->set_number(1);
2098 extension->set_extendee(
"google.protobuf.FileOptions");
2109 ExpectHasValidationErrors(
2110 "package qux.baz;\n"
2111 "import \"base2.proto\";\n"
2112 "option (baz.bar).foo = 1;\n",
2113 "2:7: Option \"(baz.bar)\" is resolved to \"(qux.baz.bar)\","
2114 " which is not defined. The innermost scope is searched first "
2115 "in name resolution. Consider using a leading '.'(i.e., \"(.baz.bar)\")"
2116 " to start from the outermost scope.\n");
2125 typedef ParserTest ParseDescriptorDebugTest;
2127 class CompareDescriptorNames {
2131 return left->name() < right->
name();
2139 for (
int i = 0;
i <
size; ++
i) {
2144 std::sort(
data,
data +
size, CompareDescriptorNames());
2151 for (
int i = 0;
i <
size; ++
i) {
2156 std::sort(
data,
data +
size, CompareDescriptorNames());
2163 std::string::size_type pos = type_name.find_last_of(
".");
2164 if (pos != std::string::npos) {
2166 type_name.begin() + pos + 1, type_name.end());
2180 TEST_F(ParseDescriptorDebugTest, TestAllDescriptorTypes) {
2184 original_file->CopyTo(&expected);
2188 std::string debug_string = original_file->DebugString();
2191 SetupParser(debug_string.c_str());
2206 public_import->CopyTo(&public_import_proto);
2211 import->CopyTo(&import_proto);
2215 ASSERT_TRUE(actual !=
NULL) <<
"Failed to validate:\n" << debug_string;
2216 actual->CopyTo(&parsed);
2222 SortMessages(&expected);
2223 SortMessages(&parsed);
2228 EXPECT_EQ(expected.DebugString(), parsed.DebugString());
2231 TEST_F(ParseDescriptorDebugTest, TestCustomOptions) {
2235 original_file->CopyTo(&expected);
2237 std::string debug_string = original_file->DebugString();
2240 SetupParser(debug_string.c_str());
2249 parsed.
set_name(original_file->name());
2254 import->CopyTo(&import_proto);
2259 actual->CopyTo(&parsed);
2264 SortMessages(&expected);
2265 SortMessages(&parsed);
2267 EXPECT_EQ(expected.DebugString(), parsed.DebugString());
2273 TEST_F(ParseDescriptorDebugTest, TestCommentsInDebugString) {
2275 "// Detached comment before syntax.\n"
2277 "// Syntax comment.\n"
2278 "syntax = \"proto2\";\n"
2280 "// Detached comment before package.\n"
2282 "// Package comment.\n"
2283 "package comment_test;\n"
2285 "// Detached comment before TestMessage1.\n"
2287 "// Message comment.\n"
2289 "// More detail in message comment.\n"
2290 "message TestMessage1 {\n"
2292 " // Detached comment before foo.\n"
2294 " // Field comment.\n"
2295 " optional int32 foo = 1;\n"
2297 " // Detached comment before NestedMessage.\n"
2299 " // Nested-message comment.\n"
2300 " message NestedMessage {\n"
2301 " optional int32 bar = 1;\n"
2305 "// Detached comment before MyEnumType.\n"
2307 "// Enum comment.\n"
2308 "enum MyEnumType {\n"
2310 " // Detached comment before ASDF.\n"
2312 " // Enum-value comment.\n"
2316 "// Detached comment before MyService.\n"
2318 "// Service comment.\n"
2319 "service MyService {\n"
2321 " // Detached comment before MyRPCCall.\n"
2323 " // RPC comment.\n"
2324 " rpc MyRPCCall(TestMessage1) returns (TestMessage1) { }\n"
2329 SourceLocationTable source_locations;
2330 parser_->RecordSourceLocationsTo(&source_locations);
2336 MockValidationErrorCollector collector(source_locations, &
error_collector_);
2338 pool_.BuildFileCollectingErrors(parsed_desc, &collector);
2344 const char* expected_comments[] = {
2345 "Detached comment before syntax.",
2347 "Detached comment before package.",
2349 "Detached comment before TestMessage1.",
2351 "More detail in message comment.",
2352 "Detached comment before foo.",
2354 "Detached comment before NestedMessage.",
2355 "Nested-message comment",
2356 "Detached comment before MyEnumType.",
2358 "Detached comment before ASDF.",
2359 "Enum-value comment",
2360 "Detached comment before MyService.",
2362 "Detached comment before MyRPCCall.",
2366 DebugStringOptions debug_string_options;
2367 debug_string_options.include_comments =
true;
2371 descriptor->DebugStringWithOptions(debug_string_options);
2374 std::string::size_type found_pos =
2375 debug_string.find(expected_comments[
i]);
2377 <<
"\"" << expected_comments[
i] <<
"\" not found.";
2381 SetupParser(debug_string.c_str());
2391 TEST_F(ParseDescriptorDebugTest, TestMaps) {
2393 "syntax = \"proto3\"; "
2396 " map<int32, Bar> enum_message_map = 1; "
2397 " map<string, float> primitive_map = 2; "
2408 EXPECT_TRUE(debug_string.find(
"map<") != string::npos);
2409 EXPECT_TRUE(debug_string.find(
"option map_entry") == string::npos);
2410 EXPECT_TRUE(debug_string.find(
"MapEntry") == string::npos);
2414 SetupParser(debug_string.c_str());
2420 StripFieldTypeName(&original);
2421 StripFieldTypeName(&parsed);
2422 EXPECT_EQ(original.DebugString(), parsed.DebugString());
2440 bool FollowPath(
const Message&
root,
const int* path_begin,
const int* path_end,
2441 const Message** output_message,
2443 if (path_begin == path_end) {
2445 *output_message = &
root;
2446 *output_field =
NULL;
2452 const Reflection* reflection =
root.GetReflection();
2458 <<
" has no field number: " << *path_begin;
2464 if (
field->is_repeated()) {
2465 if (path_begin == path_end) {
2467 *output_message = &
root;
2468 *output_field =
field;
2473 int index = *path_begin++;
2478 <<
" has size " <<
size
2479 <<
", but path contained index: " <<
index;
2486 return FollowPath(
child, path_begin, path_end, output_message,
2487 output_field, output_index);
2488 }
else if (path_begin == path_end) {
2490 *output_message = &
root;
2491 *output_field =
field;
2492 *output_index =
index;
2496 <<
" is not a message; cannot descend into it.";
2502 return FollowPath(
child, path_begin, path_end, output_message,
2503 output_field, output_index);
2504 }
else if (path_begin == path_end) {
2506 *output_message = &
root;
2507 *output_field =
field;
2512 <<
" is not a message; cannot descend into it.";
2521 if (span1.
size() != span2.
size())
return false;
2522 for (
int i = 0;
i < span1.
size();
i++) {
2523 if (span1.Get(
i) != span2.Get(
i))
return false;
2530 class SourceInfoTest :
public ParserTest {
2537 bool Parse(
const char* text) {
2538 ExtractMarkers(text);
2562 virtual void TearDown() {
2564 <<
spans_.begin()->second->DebugString();
2573 bool HasSpan(
char start_marker,
char end_marker,
2579 bool HasSpanWithComment(
char start_marker,
char end_marker,
2581 const char* expected_leading_comments,
2582 const char* expected_trailing_comments,
2583 const char* expected_leading_detached_comments) {
2585 -1, expected_leading_comments,
2586 expected_trailing_comments,
2587 expected_leading_detached_comments);
2590 bool HasSpan(
char start_marker,
char end_marker,
2592 return HasSpan(start_marker, end_marker,
descriptor_proto, field_name, -1);
2595 bool HasSpan(
char start_marker,
char end_marker,
2602 bool HasSpan(
char start_marker,
char end_marker,
2604 int index,
const char* expected_leading_comments,
2605 const char* expected_trailing_comments,
2606 const char* expected_leading_detached_comments) {
2611 <<
" has no such field: " << field_name;
2616 index, expected_leading_comments,
2617 expected_trailing_comments,
2618 expected_leading_detached_comments);
2630 bool HasSpanWithComment(
char start_marker,
char end_marker,
2633 const char* expected_leading_comments,
2634 const char* expected_trailing_comments,
2635 const char* expected_leading_detached_comments) {
2636 std::pair<SpanMap::iterator, SpanMap::iterator>
range =
2639 if (start_marker ==
'\0') {
2651 expected_span.Add(start_pos.first);
2652 expected_span.Add(start_pos.second);
2653 if (end_pos.first != start_pos.first) {
2654 expected_span.Add(end_pos.first);
2656 expected_span.Add(end_pos.second);
2658 for (SpanMap::iterator iter =
range.first; iter !=
range.second; ++iter) {
2659 if (CompareSpans(expected_span, iter->second->span())) {
2660 if (expected_leading_comments ==
NULL) {
2663 EXPECT_TRUE(iter->second->has_leading_comments());
2665 iter->second->leading_comments());
2667 if (expected_trailing_comments ==
NULL) {
2670 EXPECT_TRUE(iter->second->has_trailing_comments());
2672 iter->second->trailing_comments());
2674 if (expected_leading_detached_comments ==
NULL) {
2675 EXPECT_EQ(0, iter->second->leading_detached_comments_size());
2678 expected_leading_detached_comments,
2679 Join(iter->second->leading_detached_comments(),
"\n"));
2698 inline SpanKey(
const Message& descriptor_proto_param,
2702 index(index_param) {}
2704 inline bool operator<(
const SpanKey& other)
const {
2707 if (
field < other.field)
return true;
2708 if (
field > other.field)
return false;
2709 return index < other.index;
2713 typedef std::multimap<SpanKey, const SourceCodeInfo::Location*> SpanMap;
2718 void ExtractMarkers(
const char* text) {
2723 while (*text !=
'\0') {
2731 markers_[*text] = std::make_pair(line, column);
2735 }
else if (*text ==
'\n') {
2748 TEST_F(SourceInfoTest, BasicFileDecls) {
2750 Parse(
"$a$syntax = \"proto2\";$i$\n"
2751 "$b$package foo.bar;$c$\n"
2752 "$d$import \"baz.proto\";$e$\n"
2753 "$f$import\"qux.proto\";$h$\n"
2754 "$j$import $k$public$l$ \"bar.proto\";$m$\n"
2755 "$n$import $o$weak$p$ \"bar.proto\";$q$\n"
2757 "// comment ignored\n"));
2770 TEST_F(SourceInfoTest, Messages) {
2772 Parse(
"$a$message $b$Foo$c$ {}$d$\n"
2773 "$e$message $f$Bar$g$ {}$h$\n"));
2784 TEST_F(SourceInfoTest, Fields) {
2786 Parse(
"message Foo {\n"
2787 " $a$optional$b$ $c$int32$d$ $e$bar$f$ = $g$1$h$;$i$\n"
2788 " $j$repeated$k$ $l$X.Y$m$ $n$baz$o$ = $p$2$q$;$r$\n"
2802 EXPECT_TRUE(HasSpan(
'l',
'm', field2,
"type_name"));
2814 Parse(
"$a$extend $b$Foo$c$ {\n"
2815 " $d$optional$e$ int32 bar = 1;$f$\n"
2816 " $g$repeated$h$ X.Y baz = 2;$i$\n"
2818 "$k$extend $l$Bar$m$ {\n"
2819 " $n$optional int32 qux = 1;$o$\n"
2831 EXPECT_TRUE(HasSpan(
'b',
'c', field1,
"extendee"));
2835 EXPECT_TRUE(HasSpan(
'b',
'c', field2,
"extendee"));
2838 EXPECT_TRUE(HasSpan(
'l',
'm', field3,
"extendee"));
2854 TEST_F(SourceInfoTest, NestedExtensions) {
2856 Parse(
"message Message {\n"
2857 " $a$extend $b$Foo$c$ {\n"
2858 " $d$optional$e$ int32 bar = 1;$f$\n"
2859 " $g$repeated$h$ X.Y baz = 2;$i$\n"
2861 " $k$extend $l$Bar$m$ {\n"
2862 " $n$optional int32 qux = 1;$o$\n"
2875 EXPECT_TRUE(HasSpan(
'b',
'c', field1,
"extendee"));
2879 EXPECT_TRUE(HasSpan(
'b',
'c', field2,
"extendee"));
2882 EXPECT_TRUE(HasSpan(
'l',
'm', field3,
"extendee"));
2900 TEST_F(SourceInfoTest, ExtensionRanges) {
2902 Parse(
"message Message {\n"
2903 " $a$extensions $b$1$c$ to $d$4$e$, $f$6$g$;$h$\n"
2904 " $i$extensions $j$8$k$ to $l$max$m$;$n$\n"
2935 TEST_F(SourceInfoTest, ReservedRanges) {
2937 Parse(
"message Message {\n"
2938 " $a$reserved $b$1$c$ to $d$4$e$, $f$6$g$;$h$\n"
2962 TEST_F(SourceInfoTest, Oneofs) {
2964 Parse(
"message Foo {\n"
2965 " $a$oneof $c$foo$d$ {\n"
2966 " $e$int32$f$ $g$a$h$ = $i$1$j$;$k$\n"
2974 EXPECT_TRUE(HasSpan(
'c',
'd', oneof_decl,
"name"));
2987 TEST_F(SourceInfoTest, NestedMessages) {
2989 Parse(
"message Foo {\n"
2990 " $a$message $b$Bar$c$ {\n"
2991 " $d$message $e$Baz$f$ {}$g$\n"
2993 " $i$message $j$Qux$k$ {}$l$\n"
3013 TEST_F(SourceInfoTest, Groups) {
3015 Parse(
"message Foo {\n"
3017 " $a$optional$b$ $c$group$d$ $e$Baz$f$ = $g$1$h$ {\n"
3018 " $i$message Qux {}$j$\n"
3047 TEST_F(SourceInfoTest, Enums) {
3049 Parse(
"$a$enum $b$Foo$c$ {}$d$\n"
3050 "$e$enum $f$Bar$g$ {}$h$\n"));
3061 TEST_F(SourceInfoTest, EnumValues) {
3063 Parse(
"enum Foo {\n"
3064 " $a$BAR$b$ = $c$1$d$;$e$\n"
3065 " $f$BAZ$g$ = $h$2$i$;$j$\n"
3084 TEST_F(SourceInfoTest, NestedEnums) {
3086 Parse(
"message Foo {\n"
3087 " $a$enum $b$Bar$c$ {}$d$\n"
3088 " $e$enum $f$Baz$g$ {}$h$\n"
3105 TEST_F(SourceInfoTest, Services) {
3107 Parse(
"$a$service $b$Foo$c$ {}$d$\n"
3108 "$e$service $f$Bar$g$ {}$h$\n"));
3119 TEST_F(SourceInfoTest, MethodsAndStreams) {
3121 Parse(
"service Foo {\n"
3122 " $a$rpc $b$Bar$c$($d$X$e$) returns($f$Y$g$);$h$"
3123 " $i$rpc $j$Baz$k$($l$Z$m$) returns($n$W$o$);$p$"
3136 EXPECT_TRUE(HasSpan(
'l',
'm', baz,
"input_type"));
3137 EXPECT_TRUE(HasSpan(
'n',
'o', baz,
"output_type"));
3146 TEST_F(SourceInfoTest, Options) {
3148 Parse(
"$a$option $b$foo$c$.$d$($e$bar.baz$f$)$g$ = "
3150 "$k$option qux = $l$-123$m$;$n$\n"
3151 "$o$option corge = $p$abc$q$;$r$\n"
3152 "$s$option grault = $t$'blah'$u$;$v$\n"
3153 "$w$option garply = $x${ yadda yadda }$y$;$z$\n"
3154 "$0$option waldo = $1$123.0$2$;$3$\n"));
3170 EXPECT_TRUE(HasSpan(
'h',
'i', option1,
"positive_int_value"));
3173 EXPECT_TRUE(HasSpan(
'l',
'm', option2,
"negative_int_value"));
3176 EXPECT_TRUE(HasSpan(
'p',
'q', option3,
"identifier_value"));
3179 EXPECT_TRUE(HasSpan(
't',
'u', option4,
"string_value"));
3182 EXPECT_TRUE(HasSpan(
'x',
'y', option5,
"aggregate_value"));
3185 EXPECT_TRUE(HasSpan(
'1',
'2', option6,
"double_value"));
3211 TEST_F(SourceInfoTest, ScopedOptions) {
3213 Parse(
"message Foo {\n"
3214 " $a$option mopt = 1;$b$\n"
3217 " $c$option eopt = 1;$d$\n"
3220 " $e$option sopt = 1;$f$\n"
3221 " rpc M(X) returns(Y) {\n"
3222 " $g$option mopt = 1;$h$\n"
3224 " rpc MS4($1$stream$2$ X) returns($3$stream$4$ Y) {\n"
3225 " $k$option mopt = 1;$l$\n"
3247 "positive_int_value"));
3252 HasSpan(
file_.
enum_type(0).options().uninterpreted_option(0),
"name"));
3254 HasSpan(
file_.
enum_type(0).options().uninterpreted_option(0).name(0)));
3256 HasSpan(
file_.
enum_type(0).options().uninterpreted_option(0).name(0),
3259 "positive_int_value"));
3265 HasSpan(
file_.
service(0).options().uninterpreted_option(0),
"name"));
3267 HasSpan(
file_.
service(0).options().uninterpreted_option(0).name(0)));
3269 file_.
service(0).options().uninterpreted_option(0).name(0),
"name_part"));
3271 "positive_int_value"));
3276 HasSpan(
file_.
service(0).method(0).options().uninterpreted_option(0)));
3278 file_.
service(0).method(0).options().uninterpreted_option(0),
"name"));
3280 file_.
service(0).method(0).options().uninterpreted_option(0).name(0)));
3282 file_.
service(0).method(0).options().uninterpreted_option(0).name(0),
3285 HasSpan(
file_.
service(0).method(0).options().uninterpreted_option(0),
3286 "positive_int_value"));
3294 HasSpan(
file_.
service(0).method(1).options().uninterpreted_option(0)));
3296 file_.
service(0).method(1).options().uninterpreted_option(0),
"name"));
3298 file_.
service(0).method(1).options().uninterpreted_option(0).name(0)));
3300 file_.
service(0).method(1).options().uninterpreted_option(0).name(0),
3303 HasSpan(
file_.
service(0).method(1).options().uninterpreted_option(0),
3304 "positive_int_value"));
3306 HasSpan(
'1',
'2',
file_.
service(0).method(1),
"client_streaming"));
3308 HasSpan(
'3',
'4',
file_.
service(0).method(1),
"server_streaming"));
3316 Parse(
"message Foo {"
3317 " optional int32 bar = 1 "
3318 "$a$[default=$b$123$c$,$d$opt1=123$e$,"
3319 "$f$opt2='hi'$g$]$h$;"
3346 EXPECT_TRUE(HasSpan(option1,
"positive_int_value"));
3356 " BAR = 1 $a$[$b$opt1=123$c$,$d$opt2='hi'$e$]$f$;"
3380 EXPECT_TRUE(HasSpan(option1,
"positive_int_value"));
3384 TEST_F(SourceInfoTest, DocComments) {
3386 Parse(
"// Foo leading\n"
3388 "$a$message Foo {\n"
3389 " // Foo trailing\n"
3395 " $b$optional int32 bar = 1;$c$\n"
3396 " // bar trailing\n"
3403 EXPECT_TRUE(HasSpanWithComment(
'a',
'd',
foo,
" Foo leading\n line 2\n",
3404 " Foo trailing\n line 2\n",
NULL));
3406 " bar trailing\n",
" detached\n"));
3417 TEST_F(SourceInfoTest, DocComments2) {
3419 Parse(
"// detached before message.\n"
3423 "$a$message Foo {\n"
3424 " /* Foo trailing\n"
3429 " $b$optional int32 bar = 1;$c$ // bar trailing\n"
3430 " // ignored detached\n"
3434 "// detached before option\n"
3436 "// option leading\n"
3437 "$e$option baz = 123;$f$\n"
3438 "// option trailing\n"));
3444 EXPECT_TRUE(HasSpanWithComment(
'a',
'd',
foo,
" Foo leading\n line 2\n",
3445 " Foo trailing\n line 2 ",
3446 " detached before message.\n"));
3448 " bar trailing\n",
" detached\n"));
3449 EXPECT_TRUE(HasSpanWithComment(
'e',
'f', baz,
" option leading\n",
3450 " option trailing\n",
3451 " detached before option\n"));
3467 TEST_F(SourceInfoTest, DocComments3) {
3469 Parse(
"$a$message Foo {\n"
3471 " $b$optional int32 bar = 1 [(baz.qux) = {}];$c$\n"
3472 " // bar trailing\n"
3480 " bar trailing\n",
NULL));
3492 EXPECT_TRUE(HasSpan(
bar.options().uninterpreted_option(0),
"name"));
3493 EXPECT_TRUE(HasSpan(
bar.options().uninterpreted_option(0).name(0)));
3495 HasSpan(
bar.options().uninterpreted_option(0).name(0),
"name_part"));
3497 HasSpan(
bar.options().uninterpreted_option(0),
"aggregate_value"));
3500 TEST_F(SourceInfoTest, DocCommentsTopLevel) {
3502 Parse(
"// detached before syntax paragraph 1\n"
3504 "// detached before syntax paragraph 2\n"
3506 "// syntax leading\n"
3507 "$a$syntax = \"proto2\";$b$\n"
3508 "// syntax trailing\n"
3510 "// syntax-package detached comments\n"
3514 "// detached after empty before package\n"
3516 "// package leading\n"
3517 "$c$package foo;$d$\n"
3518 "// package trailing\n"
3520 "// ignored detach\n"
3524 " syntax trailing\n",
3525 " detached before syntax paragraph 1\n"
3527 " detached before syntax paragraph 2\n"));
3529 " package trailing\n",
3530 " syntax-package detached comments\n"
3532 " detached after empty before package\n"));
3538 TEST_F(SourceInfoTest, DocCommentsOneof) {
3540 Parse(
"// Foo leading\n"
3541 "$a$message Foo {\n"
3542 " /* Foo trailing\n"
3544 " // detached before oneof\n"
3548 " /* bar trailing\n"
3550 " // detached before bar_int\n"
3551 " /* bar_int leading\n"
3553 " $c$int32 bar_int = 1;$d$ // bar_int trailing\n"
3554 " // detach comment ignored\n"
3563 " Foo trailing\n",
NULL));
3564 EXPECT_TRUE(HasSpanWithComment(
'b',
'e',
bar,
" bar leading\n line 2 ",
3565 " bar trailing\n line 2 ",
3566 " detached before oneof\n"));
3567 EXPECT_TRUE(HasSpanWithComment(
'c',
'd', bar_int,
" bar_int leading\n",
3568 " bar_int trailing\n",
3569 " detached before bar_int\n"));