aboutsummaryrefslogtreecommitdiff
path: root/src/google/protobuf/descriptor_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/google/protobuf/descriptor_unittest.cc')
-rw-r--r--src/google/protobuf/descriptor_unittest.cc1181
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