diff options
Diffstat (limited to 'src/google/protobuf/descriptor_unittest.cc')
-rw-r--r-- | src/google/protobuf/descriptor_unittest.cc | 1181 |
1 files changed, 1155 insertions, 26 deletions
diff --git a/src/google/protobuf/descriptor_unittest.cc b/src/google/protobuf/descriptor_unittest.cc index 7ec75156..54da095a 100644 --- a/src/google/protobuf/descriptor_unittest.cc +++ b/src/google/protobuf/descriptor_unittest.cc @@ -34,16 +34,15 @@ // // This file makes extensive use of RFC 3092. :) +#include <limits> #include <memory> -#ifndef _SHARED_PTR_H -#include <google/protobuf/stubs/shared_ptr.h> -#endif #include <vector> #include <google/protobuf/compiler/importer.h> #include <google/protobuf/compiler/parser.h> #include <google/protobuf/unittest.pb.h> #include <google/protobuf/unittest_custom_options.pb.h> +#include <google/protobuf/unittest_lazy_dependencies.pb.h> #include <google/protobuf/unittest_proto3_arena.pb.h> #include <google/protobuf/io/tokenizer.h> #include <google/protobuf/io/zero_copy_stream_impl.h> @@ -52,7 +51,6 @@ #include <google/protobuf/descriptor_database.h> #include <google/protobuf/dynamic_message.h> #include <google/protobuf/text_format.h> -#include <google/protobuf/stubs/strutil.h> #include <google/protobuf/stubs/substitute.h> #include <google/protobuf/stubs/common.h> @@ -62,6 +60,7 @@ #include <google/protobuf/testing/googletest.h> #include <gtest/gtest.h> + namespace google { namespace protobuf { @@ -157,6 +156,14 @@ DescriptorProto::ReservedRange* AddReservedRange(DescriptorProto* parent, return result; } +EnumDescriptorProto::EnumReservedRange* AddReservedRange( + EnumDescriptorProto* parent, int start, int end) { + EnumDescriptorProto::EnumReservedRange* result = parent->add_reserved_range(); + result->set_start(start); + result->set_end(end); + return result; +} + EnumValueDescriptorProto* AddEnumValue(EnumDescriptorProto* enum_proto, const string& name, int number) { EnumValueDescriptorProto* result = enum_proto->add_value(); @@ -421,6 +428,7 @@ TEST_F(FileDescriptorTest, FindExtensionByNumber) { EXPECT_TRUE(pool_.FindExtensionByNumber(foo_message_, 2) == NULL); } + TEST_F(FileDescriptorTest, BuildAgain) { // Test that if te call BuildFile again on the same input we get the same // FileDescriptor back. @@ -501,7 +509,7 @@ void ExtractDebugString( for (int i = 0; i < file->dependency_count(); ++i) { ExtractDebugString(file->dependency(i), visited, debug_strings); } - debug_strings->push_back(make_pair(file->name(), file->DebugString())); + debug_strings->push_back(std::make_pair(file->name(), file->DebugString())); } class SimpleErrorCollector : public google::protobuf::io::ErrorCollector { @@ -963,6 +971,7 @@ TEST_F(DescriptorTest, FieldEnumType) { EXPECT_EQ(enum_, bar_->enum_type()); } + // =================================================================== // Test simple flat messages and fields. @@ -1933,6 +1942,7 @@ TEST_F(ExtensionDescriptorTest, FindAllExtensions) { EXPECT_EQ(39, extensions[3]->number()); } + TEST_F(ExtensionDescriptorTest, DuplicateFieldNumber) { DescriptorPool pool; FileDescriptorProto file_proto; @@ -2047,6 +2057,137 @@ TEST_F(ReservedDescriptorTest, IsReservedName) { // =================================================================== +// Test reserved enum fields. +class ReservedEnumDescriptorTest : public testing::Test { + protected: + virtual void SetUp() { + // Build descriptors for the following definitions: + // + // enum Foo { + // BAR = 1; + // reserved 2, 9 to 11, 15; + // reserved "foo", "bar"; + // } + + FileDescriptorProto foo_file; + foo_file.set_name("foo.proto"); + + EnumDescriptorProto* foo = AddEnum(&foo_file, "Foo"); + EnumDescriptorProto* edge1 = AddEnum(&foo_file, "Edge1"); + EnumDescriptorProto* edge2 = AddEnum(&foo_file, "Edge2"); + + AddEnumValue(foo, "BAR", 4); + AddReservedRange(foo, -5, -3); + AddReservedRange(foo, -2, 1); + AddReservedRange(foo, 2, 3); + AddReservedRange(foo, 9, 12); + AddReservedRange(foo, 15, 16); + + foo->add_reserved_name("foo"); + foo->add_reserved_name("bar"); + + // Some additional edge cases that cover most or all of the range of enum + // values + + // Note: We use INT_MAX as the maximum reserved range upper bound, + // inclusive. + AddEnumValue(edge1, "EDGE1", 1); + AddReservedRange(edge1, 10, INT_MAX); + AddEnumValue(edge2, "EDGE2", 15); + AddReservedRange(edge2, INT_MIN, 10); + + // Build the descriptors and get the pointers. + foo_file_ = pool_.BuildFile(foo_file); + ASSERT_TRUE(foo_file_ != NULL); + + ASSERT_EQ(3, foo_file_->enum_type_count()); + foo_ = foo_file_->enum_type(0); + edge1_ = foo_file_->enum_type(1); + edge2_ = foo_file_->enum_type(2); + } + + DescriptorPool pool_; + const FileDescriptor* foo_file_; + const EnumDescriptor* foo_; + const EnumDescriptor* edge1_; + const EnumDescriptor* edge2_; +}; + +TEST_F(ReservedEnumDescriptorTest, ReservedRanges) { + ASSERT_EQ(5, foo_->reserved_range_count()); + + EXPECT_EQ(-5, foo_->reserved_range(0)->start); + EXPECT_EQ(-3, foo_->reserved_range(0)->end); + + EXPECT_EQ(-2, foo_->reserved_range(1)->start); + EXPECT_EQ(1, foo_->reserved_range(1)->end); + + EXPECT_EQ(2, foo_->reserved_range(2)->start); + EXPECT_EQ(3, foo_->reserved_range(2)->end); + + EXPECT_EQ(9, foo_->reserved_range(3)->start); + EXPECT_EQ(12, foo_->reserved_range(3)->end); + + EXPECT_EQ(15, foo_->reserved_range(4)->start); + EXPECT_EQ(16, foo_->reserved_range(4)->end); + + ASSERT_EQ(1, edge1_->reserved_range_count()); + EXPECT_EQ(10, edge1_->reserved_range(0)->start); + EXPECT_EQ(INT_MAX, edge1_->reserved_range(0)->end); + + ASSERT_EQ(1, edge2_->reserved_range_count()); + EXPECT_EQ(INT_MIN, edge2_->reserved_range(0)->start); + EXPECT_EQ(10, edge2_->reserved_range(0)->end); +} + +TEST_F(ReservedEnumDescriptorTest, IsReservedNumber) { + EXPECT_TRUE(foo_->IsReservedNumber(-5)); + EXPECT_TRUE(foo_->IsReservedNumber(-4)); + EXPECT_TRUE(foo_->IsReservedNumber(-3)); + EXPECT_TRUE(foo_->IsReservedNumber(-2)); + EXPECT_TRUE(foo_->IsReservedNumber(-1)); + EXPECT_TRUE(foo_->IsReservedNumber(0)); + EXPECT_TRUE(foo_->IsReservedNumber(1)); + EXPECT_TRUE (foo_->IsReservedNumber(2)); + EXPECT_TRUE(foo_->IsReservedNumber(3)); + EXPECT_FALSE(foo_->IsReservedNumber(8)); + EXPECT_TRUE (foo_->IsReservedNumber(9)); + EXPECT_TRUE (foo_->IsReservedNumber(10)); + EXPECT_TRUE (foo_->IsReservedNumber(11)); + EXPECT_TRUE(foo_->IsReservedNumber(12)); + EXPECT_FALSE(foo_->IsReservedNumber(13)); + EXPECT_FALSE(foo_->IsReservedNumber(13)); + EXPECT_FALSE(foo_->IsReservedNumber(14)); + EXPECT_TRUE (foo_->IsReservedNumber(15)); + EXPECT_TRUE(foo_->IsReservedNumber(16)); + EXPECT_FALSE(foo_->IsReservedNumber(17)); + + EXPECT_FALSE(edge1_->IsReservedNumber(9)); + EXPECT_TRUE(edge1_->IsReservedNumber(10)); + EXPECT_TRUE(edge1_->IsReservedNumber(INT_MAX - 1)); + EXPECT_TRUE(edge1_->IsReservedNumber(INT_MAX)); + + EXPECT_TRUE(edge2_->IsReservedNumber(INT_MIN)); + EXPECT_TRUE(edge2_->IsReservedNumber(9)); + EXPECT_TRUE(edge2_->IsReservedNumber(10)); + EXPECT_FALSE(edge2_->IsReservedNumber(11)); +} + +TEST_F(ReservedEnumDescriptorTest, ReservedNames) { + ASSERT_EQ(2, foo_->reserved_name_count()); + + EXPECT_EQ("foo", foo_->reserved_name(0)); + EXPECT_EQ("bar", foo_->reserved_name(1)); +} + +TEST_F(ReservedEnumDescriptorTest, IsReservedName) { + EXPECT_TRUE (foo_->IsReservedName("foo")); + EXPECT_TRUE (foo_->IsReservedName("bar")); + EXPECT_FALSE(foo_->IsReservedName("baz")); +} + +// =================================================================== + class MiscTest : public testing::Test { protected: // Function which makes a field descriptor of the given type. @@ -2108,7 +2249,7 @@ class MiscTest : public testing::Test { return field != NULL ? field->enum_type() : NULL; } - google::protobuf::scoped_ptr<DescriptorPool> pool_; + std::unique_ptr<DescriptorPool> pool_; }; TEST_F(MiscTest, TypeNames) { @@ -2538,7 +2679,7 @@ class AllowUnknownDependenciesTest const FieldDescriptor* qux_field_; SimpleDescriptorDatabase db_; // used if in FALLBACK_DATABASE mode. - google::protobuf::scoped_ptr<DescriptorPool> pool_; + std::unique_ptr<DescriptorPool> pool_; }; TEST_P(AllowUnknownDependenciesTest, PlaceholderFile) { @@ -3774,6 +3915,166 @@ TEST_F(ValidationErrorTest, ReservedFieldsDebugString) { file->DebugString()); } +TEST_F(ValidationErrorTest, EnumReservedFieldError) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type {" + " name: \"Foo\"" + " value { name:\"BAR\" number:15 }" + " reserved_range { start: 10 end: 20 }" + "}", + + "foo.proto: BAR: NUMBER: Enum value \"BAR\" uses reserved number 15.\n"); +} + +TEST_F(ValidationErrorTest, EnumNegativeReservedFieldError) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type {" + " name: \"Foo\"" + " value { name:\"BAR\" number:-15 }" + " reserved_range { start: -20 end: -10 }" + "}", + + "foo.proto: BAR: NUMBER: Enum value \"BAR\" uses reserved number -15.\n"); +} + +TEST_F(ValidationErrorTest, EnumReservedRangeOverlap) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type {" + " name: \"Foo\"" + " value { name:\"BAR\" number:0 }" + " reserved_range { start: 10 end: 20 }" + " reserved_range { start: 5 end: 15 }" + "}", + + "foo.proto: Foo: NUMBER: Reserved range 5 to 15" + " overlaps with already-defined range 10 to 20.\n"); +} + +TEST_F(ValidationErrorTest, EnumReservedRangeOverlapByOne) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type {" + " name: \"Foo\"" + " value { name:\"BAR\" number:0 }" + " reserved_range { start: 10 end: 20 }" + " reserved_range { start: 5 end: 10 }" + "}", + + "foo.proto: Foo: NUMBER: Reserved range 5 to 10" + " overlaps with already-defined range 10 to 20.\n"); +} + +TEST_F(ValidationErrorTest, EnumNegativeReservedRangeOverlap) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type {" + " name: \"Foo\"" + " value { name:\"BAR\" number:0 }" + " reserved_range { start: -20 end: -10 }" + " reserved_range { start: -15 end: -5 }" + "}", + + "foo.proto: Foo: NUMBER: Reserved range -15 to -5" + " overlaps with already-defined range -20 to -10.\n"); +} + +TEST_F(ValidationErrorTest, EnumMixedReservedRangeOverlap) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type {" + " name: \"Foo\"" + " value { name:\"BAR\" number:20 }" + " reserved_range { start: -20 end: 10 }" + " reserved_range { start: -15 end: 5 }" + "}", + + "foo.proto: Foo: NUMBER: Reserved range -15 to 5" + " overlaps with already-defined range -20 to 10.\n"); +} + +TEST_F(ValidationErrorTest, EnumMixedReservedRangeOverlap2) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type {" + " name: \"Foo\"" + " value { name:\"BAR\" number:20 }" + " reserved_range { start: -20 end: 10 }" + " reserved_range { start: 10 end: 10 }" + "}", + + "foo.proto: Foo: NUMBER: Reserved range 10 to 10" + " overlaps with already-defined range -20 to 10.\n"); +} + +TEST_F(ValidationErrorTest, EnumReservedRangeStartGreaterThanEnd) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type {" + " name: \"Foo\"" + " value { name:\"BAR\" number:20 }" + " reserved_range { start: 11 end: 10 }" + "}", + + "foo.proto: Foo: NUMBER: Reserved range end number must be greater" + " than start number.\n"); +} + +TEST_F(ValidationErrorTest, EnumReservedNameError) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type {" + " name: \"Foo\"" + " value { name:\"FOO\" number:15 }" + " value { name:\"BAR\" number:15 }" + " reserved_name: \"FOO\"" + " reserved_name: \"BAR\"" + "}", + + "foo.proto: FOO: NAME: Enum value \"FOO\" is reserved.\n" + "foo.proto: BAR: NAME: Enum value \"BAR\" is reserved.\n"); +} + +TEST_F(ValidationErrorTest, EnumReservedNameRedundant) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type {" + " name: \"Foo\"" + " value { name:\"FOO\" number:15 }" + " reserved_name: \"foo\"" + " reserved_name: \"foo\"" + "}", + + "foo.proto: foo: NAME: Enum value \"foo\" is reserved multiple times.\n"); +} + +TEST_F(ValidationErrorTest, EnumReservedFieldsDebugString) { + const FileDescriptor* file = BuildFile( + "name: \"foo.proto\" " + "enum_type {" + " name: \"Foo\"" + " value { name:\"FOO\" number:3 }" + " reserved_name: \"foo\"" + " reserved_name: \"bar\"" + " reserved_range { start: -6 end: -6 }" + " reserved_range { start: -5 end: -4 }" + " reserved_range { start: -1 end: 1 }" + " reserved_range { start: 5 end: 5 }" + " reserved_range { start: 10 end: 19 }" + "}"); + + ASSERT_EQ( + "syntax = \"proto2\";\n\n" + "enum Foo {\n" + " FOO = 3;\n" + " reserved -6, -5 to -4, -1 to 1, 5, 10 to 19;\n" + " reserved \"foo\", \"bar\";\n" + "}\n\n", + file->DebugString()); +} + TEST_F(ValidationErrorTest, InvalidDefaults) { BuildFileWithErrors( "name: \"foo.proto\" " @@ -6066,6 +6367,7 @@ TEST_F(ValidationErrorTest, ValidateProto3JsonName) { "conflicts with field \"ab\". This is not allowed in proto3.\n"); } + // =================================================================== // DescriptorDatabase @@ -6616,40 +6918,95 @@ class SingletonSourceTree : public compiler::SourceTree { const char *const kSourceLocationTestInput = "syntax = \"proto2\";\n" + "option java_package = \"com.foo.bar\";\n" + "option (test_file_opt) = \"foobar\";\n" "message A {\n" - " optional int32 a = 1;\n" + " option (test_msg_opt) = \"foobar\";\n" + " optional int32 a = 1 [deprecated = true];\n" " message B {\n" - " required double b = 1;\n" + " required double b = 1 [(test_field_opt) = \"foobar\"];\n" + " }\n" + " oneof c {\n" + " option (test_oneof_opt) = \"foobar\";\n" + " string d = 2;\n" + " string e = 3;\n" + " string f = 4;\n" " }\n" "}\n" "enum Indecision {\n" - " YES = 1;\n" - " NO = 2;\n" + " option (test_enum_opt) = 21;\n" + " option (test_enum_opt) = 42;\n" + " option (test_enum_opt) = 63;\n" + " YES = 1 [(test_enumval_opt).a = 100];\n" + " NO = 2 [(test_enumval_opt) = {a:200}];\n" " MAYBE = 3;\n" "}\n" "service S {\n" + " option (test_svc_opt) = {a:100};\n" + " option (test_svc_opt) = {a:200};\n" + " option (test_svc_opt) = {a:300};\n" " rpc Method(A) returns (A.B);\n" // Put an empty line here to make the source location range match. "\n" + " rpc OtherMethod(A) returns (A) {\n" + " option deprecated = true;\n" + " option (test_method_opt) = \"foobar\";\n" + " }\n" "}\n" "message MessageWithExtensions {\n" - " extensions 1000 to max;\n" + " extensions 1000 to 2000, 2001 to max [(test_ext_opt) = \"foobar\"];\n" "}\n" "extend MessageWithExtensions {\n" - " optional int32 int32_extension = 1001;\n" + " repeated int32 int32_extension = 1001 [packed=true];\n" "}\n" "message C {\n" " extend MessageWithExtensions {\n" " optional C message_extension = 1002;\n" " }\n" - "}\n"; + "}\n" + "import \"google/protobuf/descriptor.proto\";\n" + "extend google.protobuf.FileOptions {\n" + " optional string test_file_opt = 10101;\n" + "}\n" + "extend google.protobuf.MessageOptions {\n" + " optional string test_msg_opt = 10101;\n" + "}\n" + "extend google.protobuf.FieldOptions {\n" + " optional string test_field_opt = 10101;\n" + "}\n" + "extend google.protobuf.EnumOptions {\n" + " repeated int32 test_enum_opt = 10101;\n" + "}\n" + "extend google.protobuf.EnumValueOptions {\n" + " optional A test_enumval_opt = 10101;\n" + "}\n" + "extend google.protobuf.ServiceOptions {\n" + " repeated A test_svc_opt = 10101;\n" + "}\n" + "extend google.protobuf.MethodOptions {\n" + " optional string test_method_opt = 10101;\n" + "}\n" + "extend google.protobuf.OneofOptions {\n" + " optional string test_oneof_opt = 10101;\n" + "}\n" + "extend google.protobuf.ExtensionRangeOptions {\n" + " optional string test_ext_opt = 10101;\n" + "}\n" + ; class SourceLocationTest : public testing::Test { public: SourceLocationTest() : source_tree_("/test/test.proto", kSourceLocationTestInput), - db_(&source_tree_), - pool_(&db_, &collector_) {} + simple_db_(), + source_tree_db_(&source_tree_), + merged_db_(&simple_db_, &source_tree_db_), + pool_(&merged_db_, &collector_) { + // we need descriptor.proto to be accessible by the pool + // since our test file imports it + FileDescriptorProto::descriptor()->file()->CopyTo(&file_proto_); + simple_db_.Add(file_proto_); + } static string PrintSourceLocation(const SourceLocation &loc) { return strings::Substitute("$0:$1-$2:$3", @@ -6660,12 +7017,20 @@ class SourceLocationTest : public testing::Test { } private: + FileDescriptorProto file_proto_; AbortingErrorCollector collector_; SingletonSourceTree source_tree_; - compiler::SourceTreeDescriptorDatabase db_; + SimpleDescriptorDatabase simple_db_; // contains descriptor.proto + compiler::SourceTreeDescriptorDatabase source_tree_db_; // loads test.proto + MergedDescriptorDatabase merged_db_; // combines above two dbs protected: DescriptorPool pool_; + + // tag number of all custom options in above test file + static const int kCustomOptionFieldNumber = 10101; + // tag number of field "a" in message type "A" in above test file + static const int kA_aFieldNumber = 1; }; // TODO(adonovan): implement support for option fields and for @@ -6679,27 +7044,27 @@ TEST_F(SourceLocationTest, GetSourceLocation) { const Descriptor *a_desc = file_desc->FindMessageTypeByName("A"); EXPECT_TRUE(a_desc->GetSourceLocation(&loc)); - EXPECT_EQ("2:1-7:2", PrintSourceLocation(loc)); + EXPECT_EQ("4:1-16:2", PrintSourceLocation(loc)); const Descriptor *a_b_desc = a_desc->FindNestedTypeByName("B"); EXPECT_TRUE(a_b_desc->GetSourceLocation(&loc)); - EXPECT_EQ("4:3-6:4", PrintSourceLocation(loc)); + EXPECT_EQ("7:3-9:4", PrintSourceLocation(loc)); const EnumDescriptor *e_desc = file_desc->FindEnumTypeByName("Indecision"); EXPECT_TRUE(e_desc->GetSourceLocation(&loc)); - EXPECT_EQ("8:1-12:2", PrintSourceLocation(loc)); + EXPECT_EQ("17:1-24:2", PrintSourceLocation(loc)); const EnumValueDescriptor *yes_desc = e_desc->FindValueByName("YES"); EXPECT_TRUE(yes_desc->GetSourceLocation(&loc)); - EXPECT_EQ("9:3-9:13", PrintSourceLocation(loc)); + EXPECT_EQ("21:3-21:42", PrintSourceLocation(loc)); const ServiceDescriptor *s_desc = file_desc->FindServiceByName("S"); EXPECT_TRUE(s_desc->GetSourceLocation(&loc)); - EXPECT_EQ("13:1-16:2", PrintSourceLocation(loc)); + EXPECT_EQ("25:1-35:2", PrintSourceLocation(loc)); const MethodDescriptor *m_desc = s_desc->FindMethodByName("Method"); EXPECT_TRUE(m_desc->GetSourceLocation(&loc)); - EXPECT_EQ("14:3-14:31", PrintSourceLocation(loc)); + EXPECT_EQ("29:3-29:31", PrintSourceLocation(loc)); } @@ -6712,16 +7077,426 @@ TEST_F(SourceLocationTest, ExtensionSourceLocation) { const FieldDescriptor *int32_extension_desc = file_desc->FindExtensionByName("int32_extension"); EXPECT_TRUE(int32_extension_desc->GetSourceLocation(&loc)); - EXPECT_EQ("21:3-21:41", PrintSourceLocation(loc)); + EXPECT_EQ("40:3-40:55", PrintSourceLocation(loc)); const Descriptor *c_desc = file_desc->FindMessageTypeByName("C"); EXPECT_TRUE(c_desc->GetSourceLocation(&loc)); - EXPECT_EQ("23:1-27:2", PrintSourceLocation(loc)); + EXPECT_EQ("42:1-46:2", PrintSourceLocation(loc)); const FieldDescriptor *message_extension_desc = c_desc->FindExtensionByName("message_extension"); EXPECT_TRUE(message_extension_desc->GetSourceLocation(&loc)); - EXPECT_EQ("25:5-25:41", PrintSourceLocation(loc)); + EXPECT_EQ("44:5-44:41", PrintSourceLocation(loc)); +} + +TEST_F(SourceLocationTest, InterpretedOptionSourceLocation) { + // This one's a doozy. It checks every kind of option, including + // extension range options. + + // We are verifying that the file's source info contains correct + // info for interpreted options and that it does *not* contain + // any info for corresponding uninterpreted option path. + + SourceLocation loc; + + const FileDescriptor *file_desc = + GOOGLE_CHECK_NOTNULL(pool_.FindFileByName("/test/test.proto")); + + // File options + { + int path[] = {FileDescriptorProto::kOptionsFieldNumber, + FileOptions::kJavaPackageFieldNumber}; + int unint[] = {FileDescriptorProto::kOptionsFieldNumber, + FileOptions::kUninterpretedOptionFieldNumber, + 0}; + + std::vector<int> vpath(path, path + 2); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("2:1-2:37", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 3); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + { + int path[] = {FileDescriptorProto::kOptionsFieldNumber, + kCustomOptionFieldNumber}; + int unint[] = {FileDescriptorProto::kOptionsFieldNumber, + FileOptions::kUninterpretedOptionFieldNumber, + 1}; + std::vector<int> vpath(path, path + 2); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("3:1-3:35", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 3); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + + // Message option + { + int path[] = {FileDescriptorProto::kMessageTypeFieldNumber, + 0, + DescriptorProto::kOptionsFieldNumber, + kCustomOptionFieldNumber}; + int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber, + 0, + DescriptorProto::kOptionsFieldNumber, + MessageOptions::kUninterpretedOptionFieldNumber, + 0}; + std::vector<int> vpath(path, path + 4); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("5:3-5:36", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 5); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + + // Field option + { + int path[] = {FileDescriptorProto::kMessageTypeFieldNumber, + 0, + DescriptorProto::kFieldFieldNumber, + 0, + FieldDescriptorProto::kOptionsFieldNumber, + FieldOptions::kDeprecatedFieldNumber}; + int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber, + 0, + DescriptorProto::kFieldFieldNumber, + 0, + FieldDescriptorProto::kOptionsFieldNumber, + FieldOptions::kUninterpretedOptionFieldNumber, + 0}; + std::vector<int> vpath(path, path + 6); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("6:25-6:42", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 7); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + + // Nested message option + { + int path[] = {FileDescriptorProto::kMessageTypeFieldNumber, + 0, + DescriptorProto::kNestedTypeFieldNumber, + 0, + DescriptorProto::kFieldFieldNumber, + 0, + FieldDescriptorProto::kOptionsFieldNumber, + kCustomOptionFieldNumber}; + int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber, + 0, + DescriptorProto::kNestedTypeFieldNumber, + 0, + DescriptorProto::kFieldFieldNumber, + 0, + FieldDescriptorProto::kOptionsFieldNumber, + FieldOptions::kUninterpretedOptionFieldNumber, + 0}; + std::vector<int> vpath(path, path + 8); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("8:28-8:55", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 9); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + + // One-of option + { + int path[] = {FileDescriptorProto::kMessageTypeFieldNumber, + 0, + DescriptorProto::kOneofDeclFieldNumber, + 0, + OneofDescriptorProto::kOptionsFieldNumber, + kCustomOptionFieldNumber}; + int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber, + 0, + DescriptorProto::kOneofDeclFieldNumber, + 0, + OneofDescriptorProto::kOptionsFieldNumber, + OneofOptions::kUninterpretedOptionFieldNumber, + 0}; + std::vector<int> vpath(path, path + 6); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("11:5-11:40", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 7); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + + // Enum option, repeated options + { + int path[] = {FileDescriptorProto::kEnumTypeFieldNumber, + 0, + EnumDescriptorProto::kOptionsFieldNumber, + kCustomOptionFieldNumber, + 0}; + int unint[] = {FileDescriptorProto::kEnumTypeFieldNumber, + 0, + EnumDescriptorProto::kOptionsFieldNumber, + EnumOptions::kUninterpretedOptionFieldNumber, + 0}; + std::vector<int> vpath(path, path + 5); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("18:3-18:31", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 5); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + { + int path[] = {FileDescriptorProto::kEnumTypeFieldNumber, + 0, + EnumDescriptorProto::kOptionsFieldNumber, + kCustomOptionFieldNumber, + 1}; + int unint[] = {FileDescriptorProto::kEnumTypeFieldNumber, + 0, + EnumDescriptorProto::kOptionsFieldNumber, + EnumOptions::kUninterpretedOptionFieldNumber, + 1}; + std::vector<int> vpath(path, path + 5); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("19:3-19:31", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 5); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + { + int path[] = {FileDescriptorProto::kEnumTypeFieldNumber, + 0, + EnumDescriptorProto::kOptionsFieldNumber, + kCustomOptionFieldNumber, + 2}; + int unint[] = {FileDescriptorProto::kEnumTypeFieldNumber, + 0, + EnumDescriptorProto::kOptionsFieldNumber, + OneofOptions::kUninterpretedOptionFieldNumber, + 2}; + std::vector<int> vpath(path, path + 5); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("20:3-20:31", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 5); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + + // Enum value options + { + // option w/ message type that directly sets field + int path[] = {FileDescriptorProto::kEnumTypeFieldNumber, + 0, + EnumDescriptorProto::kValueFieldNumber, + 0, + EnumValueDescriptorProto::kOptionsFieldNumber, + kCustomOptionFieldNumber, + kA_aFieldNumber}; + int unint[] = {FileDescriptorProto::kEnumTypeFieldNumber, + 0, + EnumDescriptorProto::kValueFieldNumber, + 0, + EnumValueDescriptorProto::kOptionsFieldNumber, + EnumValueOptions::kUninterpretedOptionFieldNumber, + 0}; + std::vector<int> vpath(path, path + 7); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("21:14-21:40", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 7); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + { + int path[] = {FileDescriptorProto::kEnumTypeFieldNumber, + 0, + EnumDescriptorProto::kValueFieldNumber, + 1, + EnumValueDescriptorProto::kOptionsFieldNumber, + kCustomOptionFieldNumber}; + int unint[] = {FileDescriptorProto::kEnumTypeFieldNumber, + 0, + EnumDescriptorProto::kValueFieldNumber, + 1, + EnumValueDescriptorProto::kOptionsFieldNumber, + EnumValueOptions::kUninterpretedOptionFieldNumber, + 0}; + std::vector<int> vpath(path, path + 6); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("22:14-22:42", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 7); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + + // Service option, repeated options + { + int path[] = {FileDescriptorProto::kServiceFieldNumber, + 0, + ServiceDescriptorProto::kOptionsFieldNumber, + kCustomOptionFieldNumber, + 0}; + int unint[] = {FileDescriptorProto::kServiceFieldNumber, + 0, + ServiceDescriptorProto::kOptionsFieldNumber, + ServiceOptions::kUninterpretedOptionFieldNumber, + 0}; + std::vector<int> vpath(path, path + 5); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("26:3-26:35", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 5); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + { + int path[] = {FileDescriptorProto::kServiceFieldNumber, + 0, + ServiceDescriptorProto::kOptionsFieldNumber, + kCustomOptionFieldNumber, + 1}; + int unint[] = {FileDescriptorProto::kServiceFieldNumber, + 0, + ServiceDescriptorProto::kOptionsFieldNumber, + ServiceOptions::kUninterpretedOptionFieldNumber, + 1}; + std::vector<int> vpath(path, path + 5); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("27:3-27:35", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 5); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + { + int path[] = {FileDescriptorProto::kServiceFieldNumber, + 0, + ServiceDescriptorProto::kOptionsFieldNumber, + kCustomOptionFieldNumber, + 2}; + int unint[] = {FileDescriptorProto::kServiceFieldNumber, + 0, + ServiceDescriptorProto::kOptionsFieldNumber, + ServiceOptions::kUninterpretedOptionFieldNumber, + 2}; + std::vector<int> vpath(path, path + 5); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("28:3-28:35", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 5); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + + // Method options + { + int path[] = {FileDescriptorProto::kServiceFieldNumber, + 0, + ServiceDescriptorProto::kMethodFieldNumber, + 1, + MethodDescriptorProto::kOptionsFieldNumber, + MethodOptions::kDeprecatedFieldNumber}; + int unint[] = {FileDescriptorProto::kServiceFieldNumber, + 0, + ServiceDescriptorProto::kMethodFieldNumber, + 1, + MethodDescriptorProto::kOptionsFieldNumber, + MethodOptions::kUninterpretedOptionFieldNumber, + 0}; + std::vector<int> vpath(path, path + 6); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("32:5-32:30", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 7); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + { + int path[] = {FileDescriptorProto::kServiceFieldNumber, + 0, + ServiceDescriptorProto::kMethodFieldNumber, + 1, + MethodDescriptorProto::kOptionsFieldNumber, + kCustomOptionFieldNumber}; + int unint[] = {FileDescriptorProto::kServiceFieldNumber, + 0, + ServiceDescriptorProto::kMethodFieldNumber, + 1, + MethodDescriptorProto::kOptionsFieldNumber, + MethodOptions::kUninterpretedOptionFieldNumber, + 1}; + std::vector<int> vpath(path, path + 6); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("33:5-33:41", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 7); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + + // Extension range options + { + int path[] = {FileDescriptorProto::kMessageTypeFieldNumber, + 1, + DescriptorProto::kExtensionRangeFieldNumber, + 0, + DescriptorProto_ExtensionRange::kOptionsFieldNumber}; + std::vector<int> vpath(path, path + 5); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("37:40-37:67", PrintSourceLocation(loc)); + } + { + int path[] = {FileDescriptorProto::kMessageTypeFieldNumber, + 1, + DescriptorProto::kExtensionRangeFieldNumber, + 0, + DescriptorProto_ExtensionRange::kOptionsFieldNumber, + kCustomOptionFieldNumber}; + int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber, + 1, + DescriptorProto::kExtensionRangeFieldNumber, + 0, + DescriptorProto_ExtensionRange::kOptionsFieldNumber, + ExtensionRangeOptions::kUninterpretedOptionFieldNumber, + 0}; + std::vector<int> vpath(path, path + 6); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("37:41-37:66", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 7); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + { + int path[] = {FileDescriptorProto::kMessageTypeFieldNumber, + 1, + DescriptorProto::kExtensionRangeFieldNumber, + 1, + DescriptorProto_ExtensionRange::kOptionsFieldNumber, + kCustomOptionFieldNumber}; + int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber, + 1, + DescriptorProto::kExtensionRangeFieldNumber, + 1, + DescriptorProto_ExtensionRange::kOptionsFieldNumber, + ExtensionRangeOptions::kUninterpretedOptionFieldNumber, + 0}; + std::vector<int> vpath(path, path + 6); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("37:41-37:66", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 7); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } + + // Field option on extension + { + int path[] = {FileDescriptorProto::kExtensionFieldNumber, + 0, + FieldDescriptorProto::kOptionsFieldNumber, + FieldOptions::kPackedFieldNumber}; + int unint[] = {FileDescriptorProto::kExtensionFieldNumber, + 0, + FieldDescriptorProto::kOptionsFieldNumber, + FieldOptions::kUninterpretedOptionFieldNumber, + 0}; + std::vector<int> vpath(path, path + 4); + EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc)); + EXPECT_EQ("40:42-40:53", PrintSourceLocation(loc)); + + std::vector<int> vunint(unint, unint + 5); + EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc)); + } } // Missing SourceCodeInfo doesn't cause crash: @@ -6824,6 +7599,360 @@ TEST_F(CopySourceCodeInfoToTest, CopySourceCodeInfoTo) { // =================================================================== +class LazilyBuildDependenciesTest : public testing::Test { + public: + LazilyBuildDependenciesTest() : pool_(&db_, NULL) { + pool_.InternalSetLazilyBuildDependencies(); + } + + void ParseProtoAndAddToDb(const char* proto) { + FileDescriptorProto tmp; + ASSERT_TRUE(TextFormat::ParseFromString(proto, &tmp)); + db_.Add(tmp); + } + + void ParseProtoAndAddToDb(const string& proto) { + FileDescriptorProto tmp; + ASSERT_TRUE(TextFormat::ParseFromString(proto, &tmp)); + db_.Add(tmp); + } + + void AddSimpleMessageProtoFileToDb(const char* file_name, + const char* message_name) { + ParseProtoAndAddToDb("name: '" + string(file_name) + + ".proto' " + "package: \"protobuf_unittest\" " + "message_type { " + " name:'" + + string(message_name) + + "' " + " field { name:'a' number:1 " + " label:LABEL_OPTIONAL " + " type_name:'int32' } " + "}"); + } + + void AddSimpleEnumProtoFileToDb(const char* file_name, const char* enum_name, + const char* enum_value_name) { + ParseProtoAndAddToDb("name: '" + string(file_name) + + ".proto' " + "package: 'protobuf_unittest' " + "enum_type { " + " name:'" + + string(enum_name) + + "' " + " value { name:'" + + string(enum_value_name) + + "' number:1 } " + "}"); + } + + protected: + SimpleDescriptorDatabase db_; + DescriptorPool pool_; +}; + +TEST_F(LazilyBuildDependenciesTest, Message) { + ParseProtoAndAddToDb( + "name: 'foo.proto' " + "package: 'protobuf_unittest' " + "dependency: 'bar.proto' " + "message_type { " + " name:'Foo' " + " field { name:'bar' number:1 label:LABEL_OPTIONAL " + "type_name:'.protobuf_unittest.Bar' } " + "}"); + AddSimpleMessageProtoFileToDb("bar", "Bar"); + + // Verify neither has been built yet. + EXPECT_FALSE(pool_.InternalIsFileLoaded("foo.proto")); + EXPECT_FALSE(pool_.InternalIsFileLoaded("bar.proto")); + + const FileDescriptor* file = pool_.FindFileByName("foo.proto"); + + // Verify only foo gets built when asking for foo.proto + EXPECT_TRUE(file != NULL); + EXPECT_TRUE(pool_.InternalIsFileLoaded("foo.proto")); + EXPECT_FALSE(pool_.InternalIsFileLoaded("bar.proto")); + + // Verify calling FindFieldBy* works when the type of the field was + // not built at cross link time. Verify this doesn't build the file + // the field's type is defined in, as well. + const Descriptor* desc = file->FindMessageTypeByName("Foo"); + const FieldDescriptor* field = desc->FindFieldByName("bar"); + EXPECT_TRUE(field != NULL); + EXPECT_EQ(field, desc->FindFieldByNumber(1)); + EXPECT_EQ(field, desc->FindFieldByLowercaseName("bar")); + EXPECT_EQ(field, desc->FindFieldByCamelcaseName("bar")); + EXPECT_FALSE(pool_.InternalIsFileLoaded("bar.proto")); + + // Finally, verify that if we call message_type() on the field, we will + // buid the file where the message is defined, and get a valid descriptor + EXPECT_TRUE(field->message_type() != NULL); + EXPECT_TRUE(pool_.InternalIsFileLoaded("bar.proto")); +} + +TEST_F(LazilyBuildDependenciesTest, Enum) { + ParseProtoAndAddToDb( + "name: 'foo.proto' " + "package: 'protobuf_unittest' " + "dependency: 'enum1.proto' " + "dependency: 'enum2.proto' " + "message_type { " + " name:'Lazy' " + " field { name:'enum1' number:1 label:LABEL_OPTIONAL " + "type_name:'.protobuf_unittest.Enum1' } " + " field { name:'enum2' number:1 label:LABEL_OPTIONAL " + "type_name:'.protobuf_unittest.Enum2' } " + "}"); + AddSimpleEnumProtoFileToDb("enum1", "Enum1", "ENUM1"); + AddSimpleEnumProtoFileToDb("enum2", "Enum2", "ENUM2"); + + const FileDescriptor* file = pool_.FindFileByName("foo.proto"); + + // Verify calling enum_type() on a field whose definition is not + // yet built will build the file and return a descriptor. + EXPECT_FALSE(pool_.InternalIsFileLoaded("enum1.proto")); + const Descriptor* desc = file->FindMessageTypeByName("Lazy"); + EXPECT_TRUE(desc != NULL); + const FieldDescriptor* field = desc->FindFieldByName("enum1"); + EXPECT_TRUE(field != NULL); + EXPECT_TRUE(field->enum_type() != NULL); + EXPECT_TRUE(pool_.InternalIsFileLoaded("enum1.proto")); + + // Verify calling default_value_enum() on a field whose definition is not + // yet built will build the file and return a descriptor to the value. + EXPECT_FALSE(pool_.InternalIsFileLoaded("enum2.proto")); + field = desc->FindFieldByName("enum2"); + EXPECT_TRUE(field != NULL); + EXPECT_TRUE(field->default_value_enum() != NULL); + EXPECT_TRUE(pool_.InternalIsFileLoaded("enum2.proto")); +} + +TEST_F(LazilyBuildDependenciesTest, Type) { + ParseProtoAndAddToDb( + "name: 'foo.proto' " + "package: 'protobuf_unittest' " + "dependency: 'message1.proto' " + "dependency: 'message2.proto' " + "dependency: 'enum1.proto' " + "dependency: 'enum2.proto' " + "message_type { " + " name:'Lazy' " + " field { name:'message1' number:1 label:LABEL_OPTIONAL " + "type_name:'.protobuf_unittest.Message1' } " + " field { name:'message2' number:1 label:LABEL_OPTIONAL " + "type_name:'.protobuf_unittest.Message2' } " + " field { name:'enum1' number:1 label:LABEL_OPTIONAL " + "type_name:'.protobuf_unittest.Enum1' } " + " field { name:'enum2' number:1 label:LABEL_OPTIONAL " + "type_name:'.protobuf_unittest.Enum2' } " + "}"); + AddSimpleMessageProtoFileToDb("message1", "Message1"); + AddSimpleMessageProtoFileToDb("message2", "Message2"); + AddSimpleEnumProtoFileToDb("enum1", "Enum1", "ENUM1"); + AddSimpleEnumProtoFileToDb("enum2", "Enum2", "ENUM2"); + + const FileDescriptor* file = pool_.FindFileByName("foo.proto"); + + // Verify calling type() on a field that is a message type will + // build the type defined in another file. + EXPECT_FALSE(pool_.InternalIsFileLoaded("message1.proto")); + const Descriptor* desc = file->FindMessageTypeByName("Lazy"); + EXPECT_TRUE(desc != NULL); + const FieldDescriptor* field = desc->FindFieldByName("message1"); + EXPECT_TRUE(field != NULL); + EXPECT_EQ(field->type(), FieldDescriptor::TYPE_MESSAGE); + EXPECT_TRUE(pool_.InternalIsFileLoaded("message1.proto")); + + // Verify calling cpp_type() on a field that is a message type will + // build the type defined in another file. + EXPECT_FALSE(pool_.InternalIsFileLoaded("message2.proto")); + field = desc->FindFieldByName("message2"); + EXPECT_TRUE(field != NULL); + EXPECT_EQ(field->cpp_type(), FieldDescriptor::CPPTYPE_MESSAGE); + EXPECT_TRUE(pool_.InternalIsFileLoaded("message2.proto")); + + // Verify calling type() on a field that is an enum type will + // build the type defined in another file. + EXPECT_FALSE(pool_.InternalIsFileLoaded("enum1.proto")); + field = desc->FindFieldByName("enum1"); + EXPECT_TRUE(field != NULL); + EXPECT_EQ(field->type(), FieldDescriptor::TYPE_ENUM); + EXPECT_TRUE(pool_.InternalIsFileLoaded("enum1.proto")); + + // Verify calling cpp_type() on a field that is an enum type will + // build the type defined in another file. + EXPECT_FALSE(pool_.InternalIsFileLoaded("enum2.proto")); + field = desc->FindFieldByName("enum2"); + EXPECT_TRUE(field != NULL); + EXPECT_EQ(field->cpp_type(), FieldDescriptor::CPPTYPE_ENUM); + EXPECT_TRUE(pool_.InternalIsFileLoaded("enum2.proto")); +} + +TEST_F(LazilyBuildDependenciesTest, Extension) { + ParseProtoAndAddToDb( + "name: 'foo.proto' " + "package: 'protobuf_unittest' " + "dependency: 'bar.proto' " + "dependency: 'baz.proto' " + "extension { extendee: '.protobuf_unittest.Bar' name:'bar' number:11" + " label:LABEL_OPTIONAL type_name:'.protobuf_unittest.Baz' }"); + ParseProtoAndAddToDb( + "name: 'bar.proto' " + "package: 'protobuf_unittest' " + "message_type { " + " name:'Bar' " + " extension_range { start: 10 end: 20 }" + "}"); + AddSimpleMessageProtoFileToDb("baz", "Baz"); + + // Verify none have been built yet. + EXPECT_FALSE(pool_.InternalIsFileLoaded("foo.proto")); + EXPECT_FALSE(pool_.InternalIsFileLoaded("bar.proto")); + EXPECT_FALSE(pool_.InternalIsFileLoaded("baz.proto")); + + const FileDescriptor* file = pool_.FindFileByName("foo.proto"); + + // Verify foo.bar gets loaded, and bar.proto gets loaded + // to register the extension. baz.proto should not get loaded. + EXPECT_TRUE(file != NULL); + EXPECT_TRUE(pool_.InternalIsFileLoaded("foo.proto")); + EXPECT_TRUE(pool_.InternalIsFileLoaded("bar.proto")); + EXPECT_FALSE(pool_.InternalIsFileLoaded("baz.proto")); +} + +TEST_F(LazilyBuildDependenciesTest, Service) { + ParseProtoAndAddToDb( + "name: 'foo.proto' " + "package: 'protobuf_unittest' " + "dependency: 'message1.proto' " + "dependency: 'message2.proto' " + "dependency: 'message3.proto' " + "dependency: 'message4.proto' " + "service {" + " name: 'LazyService'" + " method { name: 'A' input_type: '.protobuf_unittest.Message1' " + " output_type: '.protobuf_unittest.Message2' }" + "}"); + AddSimpleMessageProtoFileToDb("message1", "Message1"); + AddSimpleMessageProtoFileToDb("message2", "Message2"); + AddSimpleMessageProtoFileToDb("message3", "Message3"); + AddSimpleMessageProtoFileToDb("message4", "Message4"); + + const FileDescriptor* file = pool_.FindFileByName("foo.proto"); + + // Verify calling FindServiceByName or FindMethodByName doesn't build the + // files defining the input and output type, and input_type() and + // output_type() does indeed build the appropriate files. + const ServiceDescriptor* service = file->FindServiceByName("LazyService"); + EXPECT_TRUE(service != NULL); + const MethodDescriptor* method = service->FindMethodByName("A"); + EXPECT_FALSE(pool_.InternalIsFileLoaded("message1.proto")); + EXPECT_FALSE(pool_.InternalIsFileLoaded("message2.proto")); + EXPECT_TRUE(method != NULL); + EXPECT_TRUE(method->input_type() != NULL); + EXPECT_TRUE(pool_.InternalIsFileLoaded("message1.proto")); + EXPECT_FALSE(pool_.InternalIsFileLoaded("message2.proto")); + EXPECT_TRUE(method->output_type() != NULL); + EXPECT_TRUE(pool_.InternalIsFileLoaded("message2.proto")); +} + + +TEST_F(LazilyBuildDependenciesTest, GeneratedFile) { + // Most testing is done with custom pools with lazy dependencies forced on, + // do some sanity checking that lazy imports is on by default for the + // generated pool, and do custom options testing with generated to + // be able to use the GetExtension ids for the custom options. + + // Verify none of the files are loaded yet. + EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded( + "google/protobuf/unittest_lazy_dependencies.proto")); + EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded( + "google/protobuf/unittest_lazy_dependencies_custom_option.proto")); + EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded( + "google/protobuf/unittest_lazy_dependencies_enum.proto")); + + // Verify calling autogenerated function to get a descriptor in the base + // file will build that file but none of it's imports. This verifies that + // lazily_build_dependencies_ is set on the generated pool, and also that + // the generated function "descriptor()" doesn't somehow subvert the laziness + // by manually loading the dependencies or something. + EXPECT_TRUE(protobuf_unittest::lazy_imports::ImportedMessage::descriptor() != + NULL); + EXPECT_TRUE(DescriptorPool::generated_pool()->InternalIsFileLoaded( + "google/protobuf/unittest_lazy_dependencies.proto")); + EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded( + "google/protobuf/unittest_lazy_dependencies_custom_option.proto")); + EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded( + "google/protobuf/unittest_lazy_dependencies_enum.proto")); + + // Verify custom options work when defined in an import that isn't loaded, + // and that a non-default value of a custom option doesn't load the file + // where that enum is defined. + const google::protobuf::MessageOptions& options = + protobuf_unittest::lazy_imports::MessageCustomOption::descriptor() + ->options(); + protobuf_unittest::lazy_imports::LazyEnum custom_option_value = + options.GetExtension(protobuf_unittest::lazy_imports::lazy_enum_option); + + EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded( + "google/protobuf/unittest_lazy_dependencies_custom_option.proto")); + EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded( + "google/protobuf/unittest_lazy_dependencies_enum.proto")); + EXPECT_EQ(custom_option_value, protobuf_unittest::lazy_imports::LAZY_ENUM_1); + + const google::protobuf::MessageOptions& options2 = + protobuf_unittest::lazy_imports::MessageCustomOption2::descriptor() + ->options(); + custom_option_value = + options2.GetExtension(protobuf_unittest::lazy_imports::lazy_enum_option); + + EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded( + "google/protobuf/unittest_lazy_dependencies_custom_option.proto")); + EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded( + "google/protobuf/unittest_lazy_dependencies_enum.proto")); + EXPECT_EQ(custom_option_value, protobuf_unittest::lazy_imports::LAZY_ENUM_0); +} + +TEST_F(LazilyBuildDependenciesTest, Dependency) { + ParseProtoAndAddToDb( + "name: 'foo.proto' " + "package: 'protobuf_unittest' " + "dependency: 'bar.proto' " + "message_type { " + " name:'Foo' " + " field { name:'bar' number:1 label:LABEL_OPTIONAL " + "type_name:'.protobuf_unittest.Bar' } " + "}"); + ParseProtoAndAddToDb( + "name: 'bar.proto' " + "package: 'protobuf_unittest' " + "dependency: 'baz.proto' " + "message_type { " + " name:'Bar' " + " field { name:'baz' number:1 label:LABEL_OPTIONAL " + "type_name:'.protobuf_unittest.Baz' } " + "}"); + AddSimpleMessageProtoFileToDb("baz", "Baz"); + + const FileDescriptor* foo_file = pool_.FindFileByName("foo.proto"); + EXPECT_TRUE(foo_file != NULL); + // As expected, requesting foo.proto shouldn't build it's dependencies + EXPECT_TRUE(pool_.InternalIsFileLoaded("foo.proto")); + EXPECT_FALSE(pool_.InternalIsFileLoaded("bar.proto")); + EXPECT_FALSE(pool_.InternalIsFileLoaded("baz.proto")); + + // Verify calling dependency(N) will build the dependency, but + // not that file's dependencies. + const FileDescriptor* bar_file = foo_file->dependency(0); + EXPECT_TRUE(bar_file != NULL); + EXPECT_TRUE(pool_.InternalIsFileLoaded("bar.proto")); + EXPECT_FALSE(pool_.InternalIsFileLoaded("baz.proto")); +} + +// =================================================================== + } // namespace descriptor_unittest } // namespace protobuf |