From 6ef984af4b0c63c1c33127a12dcfc8e6359f0c9e Mon Sep 17 00:00:00 2001 From: Feng Xiao Date: Mon, 10 Nov 2014 17:34:54 -0800 Subject: Down-integrate from internal code base. --- src/google/protobuf/compiler/parser_unittest.cc | 251 +++++++++++++++++++++++- 1 file changed, 244 insertions(+), 7 deletions(-) (limited to 'src/google/protobuf/compiler/parser_unittest.cc') diff --git a/src/google/protobuf/compiler/parser_unittest.cc b/src/google/protobuf/compiler/parser_unittest.cc index 9ec29a48..c2206ade 100644 --- a/src/google/protobuf/compiler/parser_unittest.cc +++ b/src/google/protobuf/compiler/parser_unittest.cc @@ -33,6 +33,9 @@ // Sanjay Ghemawat, Jeff Dean, and others. #include +#ifndef _SHARED_PTR_H +#include +#endif #include #include #include @@ -250,6 +253,7 @@ TEST_F(ParseMessageTest, ExplicitSyntaxIdentifier) { " required int32 foo = 1;\n" "}\n", + "syntax: 'proto2' " "message_type {" " name: \"TestMessage\"" " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" @@ -265,6 +269,7 @@ TEST_F(ParseMessageTest, ExplicitRequiredSyntaxIdentifier) { " required int32 foo = 1;\n" "}\n", + "syntax: 'proto2' " "message_type {" " name: \"TestMessage\"" " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" @@ -500,6 +505,54 @@ TEST_F(ParseMessageTest, MultipleOneofs) { "}"); } +TEST_F(ParseMessageTest, Maps) { + ExpectParsesTo( + "message TestMessage {\n" + " map primitive_type_map = 1;\n" + " map composite_type_map = 2;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " nested_type {" + " name: \"PrimitiveTypeMapEntry\"" + " field { " + " name: \"key\" number: 1 label:LABEL_OPTIONAL" + " type:TYPE_INT32" + " }" + " field { " + " name: \"value\" number: 2 label:LABEL_OPTIONAL" + " type:TYPE_STRING" + " }" + " options { map_entry: true }" + " }" + " nested_type {" + " name: \"CompositeTypeMapEntry\"" + " field { " + " name: \"key\" number: 1 label:LABEL_OPTIONAL" + " type_name: \"KeyType\"" + " }" + " field { " + " name: \"value\" number: 2 label:LABEL_OPTIONAL" + " type_name: \"ValueType\"" + " }" + " options { map_entry: true }" + " }" + " field {" + " name: \"primitive_type_map\"" + " label: LABEL_REPEATED" + " type_name: \"PrimitiveTypeMapEntry\"" + " number: 1" + " }" + " field {" + " name: \"composite_type_map\"" + " label: LABEL_REPEATED" + " type_name: \"CompositeTypeMapEntry\"" + " number: 2" + " }" + "}"); +} + TEST_F(ParseMessageTest, Group) { ExpectParsesTo( "message TestMessage {\n" @@ -639,6 +692,20 @@ TEST_F(ParseMessageTest, MultipleExtensionsOneExtendee) { " type_name:\"TestMessage\" extendee: \"Extendee1\" }"); } +TEST_F(ParseMessageTest, OptionalOptionalLabelProto3) { + ExpectParsesTo( + "syntax = \"proto3\";\n" + "message TestMessage {\n" + " int32 foo = 1;\n" + " optional int32 bar = 2;\n" + "}\n", + + "syntax: \"proto3\" " + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 }" + " field { name:\"bar\" label:LABEL_OPTIONAL type:TYPE_INT32 number:2 } }"); +} // =================================================================== @@ -828,9 +895,9 @@ typedef ParserTest ParseErrorTest; TEST_F(ParseErrorTest, MissingSyntaxIdentifier) { require_syntax_identifier_ = true; - ExpectHasEarlyExitErrors( - "message TestMessage {}", - "0:0: File must begin with 'syntax = \"proto2\";'.\n"); + ExpectHasEarlyExitErrors("message TestMessage {}", + "0:0: File must begin with a syntax statement, e.g. " + "'syntax = \"proto2\";'.\n"); EXPECT_EQ("", parser_->GetSyntaxIdentifier()); } @@ -838,7 +905,7 @@ TEST_F(ParseErrorTest, UnknownSyntaxIdentifier) { ExpectHasEarlyExitErrors( "syntax = \"no_such_syntax\";", "0:9: Unrecognized syntax identifier \"no_such_syntax\". This parser " - "only recognizes \"proto2\".\n"); + "only recognizes \"proto2\" and \"proto3\".\n"); EXPECT_EQ("no_such_syntax", parser_->GetSyntaxIdentifier()); } @@ -1041,6 +1108,55 @@ TEST_F(ParseErrorTest, LabelInOneof) { "/ repeated).\n"); } +TEST_F(ParseErrorTest, MapInOneof) { + ExpectHasErrors( + "message TestMessage {\n" + " oneof foo {\n" + " map foo_map = 1;\n" + " map message_field = 2;\n" // a normal message field is OK + " }\n" + "}\n", + "2:7: Map fields are not allowed in oneofs.\n"); +} + +TEST_F(ParseErrorTest, LabelForMap) { + ExpectHasErrors( + "message TestMessage {\n" + " optional map int_map = 1;\n" + " required map int_map2 = 2;\n" + " repeated map int_map3 = 3;\n" + " optional map map_message = 4;\n" // a normal message field is OK + "}\n", + "1:14: Field labels (required/optional/repeated) are not allowed on map " + "fields.\n" + "2:14: Field labels (required/optional/repeated) are not allowed on map " + "fields.\n" + "3:14: Field labels (required/optional/repeated) are not allowed on map " + "fields.\n"); +} + +TEST_F(ParseErrorTest, MalformedMaps) { + ExpectHasErrors( + "message TestMessage {\n" + " map map_message = 1;\n" // a normal message field lacking label + " map str_map = 2;\n" + " map str_map2 = 3;\n" + " map<,string> str_map3 = 4;\n" + " map<> empty_map = 5;\n" + " map int_map = 1;\n" + "}", + "1:6: Expected \"required\", \"optional\", or \"repeated\".\n" + "2:12: Expected \",\".\n" + "3:13: Expected type name.\n" + "4:6: Expected type name.\n" + "5:6: Expected type name.\n" + "6:20: Expected \">\".\n" + "8:5: Map fields are not allowed to be extensions.\n"); +} + TEST_F(ParseErrorTest, GroupNotCapitalized) { ExpectHasErrors( "message TestMessage {\n" @@ -1413,7 +1529,7 @@ TEST_F(ParserValidationErrorTest, ResovledUndefinedOptionError) { // definitions again afoter parsing (note, however, that the order of messages // cannot be guaranteed to be the same) -typedef ParserTest ParseDecriptorDebugTest; +typedef ParserTest ParseDescriptorDebugTest; class CompareDescriptorNames { public: @@ -1447,7 +1563,28 @@ void SortMessages(FileDescriptorProto *file_descriptor_proto) { sort(data, data + size, CompareDescriptorNames()); } -TEST_F(ParseDecriptorDebugTest, TestAllDescriptorTypes) { +// Strips the message and enum field type names for comparison purpose only. +void StripFieldTypeName(DescriptorProto* proto) { + for (int i = 0; i < proto->field_size(); ++i) { + string type_name = proto->field(i).type_name(); + string::size_type pos = type_name.find_last_of("."); + if (pos != string::npos) { + proto->mutable_field(i)->mutable_type_name()->assign( + type_name.begin() + pos + 1, type_name.end()); + } + } + for (int i = 0; i < proto->nested_type_size(); ++i) { + StripFieldTypeName(proto->mutable_nested_type(i)); + } +} + +void StripFieldTypeName(FileDescriptorProto* file_proto) { + for (int i = 0; i < file_proto->message_type_size(); ++i) { + StripFieldTypeName(file_proto->mutable_message_type(i)); + } +} + +TEST_F(ParseDescriptorDebugTest, TestAllDescriptorTypes) { const FileDescriptor* original_file = protobuf_unittest::TestAllTypes::descriptor()->file(); FileDescriptorProto expected; @@ -1499,7 +1636,7 @@ TEST_F(ParseDecriptorDebugTest, TestAllDescriptorTypes) { EXPECT_EQ(expected.DebugString(), parsed.DebugString()); } -TEST_F(ParseDecriptorDebugTest, TestCustomOptions) { +TEST_F(ParseDescriptorDebugTest, TestCustomOptions) { const FileDescriptor* original_file = protobuf_unittest::AggregateMessage::descriptor()->file(); FileDescriptorProto expected; @@ -1538,6 +1675,106 @@ TEST_F(ParseDecriptorDebugTest, TestCustomOptions) { EXPECT_EQ(expected.DebugString(), parsed.DebugString()); } +// Ensure that DebugStringWithOptions(), with |include_comments| set to true, +// includes comments from the original parser input in all of the appropriate +// places. +TEST_F(ParseDescriptorDebugTest, TestCommentsInDebugString) { + SetupParser( + "// Message comment.\n" + "message TestMessage1 {\n" + " // Field comment.\n" + " optional int32 foo = 1;\n" + "\n" + " // Nested-message comment.\n" + " message NestedMessage {\n" + " optional int32 bar = 1;\n" + " }\n" + "}\n" + "\n" + "// Enum comment.\n" + "enum MyEnumType {\n" + " // Enum-value comment.\n" + " ASDF = 1;\n" + "}\n" + "\n" + "// Service comment.\n" + "service MyService {\n" + " // RPC comment.\n" + " rpc MyRPCCall(TestMessage1) returns (TestMessage1) { }\n" + "}\n"); + + FileDescriptorProto parsed_desc; + parsed_desc.set_name("foo.proto"); + SourceLocationTable source_locations; + parser_->RecordSourceLocationsTo(&source_locations); + parser_->Parse(input_.get(), &parsed_desc); + EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type); + ASSERT_EQ("", error_collector_.text_); + + // We need to import the FileDescriptorProto to get a FileDescriptor. + MockValidationErrorCollector collector(source_locations, &error_collector_); + const FileDescriptor* descriptor = + pool_.BuildFileCollectingErrors(parsed_desc, &collector); + ASSERT_TRUE(descriptor != NULL); + + DebugStringOptions debug_string_options; + debug_string_options.include_comments = true; + const string debug_string = + descriptor->DebugStringWithOptions(debug_string_options); + + // Ensure that each of the comments appears somewhere in the DebugString(), + // and that these comments appear in order. We don't test the exact comment + // placement or formatting, because we do not want to be too fragile here. + const char* expected_comments[] = { + "Message comment.", + "Field comment", + "Nested-message comment", + "Enum comment", + "Enum-value comment", + "Service comment", + "RPC comment", + }; + + for (int i = 0; i < GOOGLE_ARRAYSIZE(expected_comments); ++i) { + string::size_type found_pos = debug_string.find(expected_comments[i]); + ASSERT_TRUE(found_pos != string::npos); + } +} + +TEST_F(ParseDescriptorDebugTest, TestMaps) { + SetupParser( + "syntax = \"proto3\"; " + "message Foo { " + " message Bar { } " + " map enum_message_map = 1; " + " map primitive_map = 2; " + "} "); + FileDescriptorProto original; + EXPECT_TRUE(parser_->Parse(input_.get(), &original)); + original.set_name("foo.proto"); + const FileDescriptor* file = pool_.BuildFile(original); + ASSERT_TRUE(file != NULL); + + // Make sure the debug string uses map syntax and does not have the auto + // generated entry. + string debug_string = file->DebugString(); + EXPECT_TRUE(debug_string.find("map<") != string::npos); + EXPECT_TRUE(debug_string.find("option map_entry") == string::npos); + EXPECT_TRUE(debug_string.find("MapEntry") == string::npos); + + // Make sure the descriptor debug string is parsable. + FileDescriptorProto parsed; + SetupParser(debug_string.c_str()); + parsed.set_name("foo.proto"); + ASSERT_TRUE(parser_->Parse(input_.get(), &parsed)); + + original.clear_source_code_info(); + parsed.clear_source_code_info(); + StripFieldTypeName(&original); + StripFieldTypeName(&parsed); + EXPECT_EQ(original.DebugString(), parsed.DebugString()); +} + // =================================================================== // SourceCodeInfo tests. -- cgit v1.2.3