| // Protocol Buffers - Google's data interchange format |
| // Copyright 2008 Google Inc. All rights reserved. |
| // |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file or at |
| // https://developers.google.com/open-source/licenses/bsd |
| |
| // Author: kenton@google.com (Kenton Varda) |
| // Based on original Protocol Buffers design by |
| // Sanjay Ghemawat, Jeff Dean, and others. |
| // |
| // This file makes extensive use of RFC 3092. :) |
| |
| #include "google/protobuf/descriptor.h" |
| |
| #include <limits.h> |
| |
| #include <atomic> |
| #include <cstdint> |
| #include <cstdlib> |
| #include <deque> |
| #include <functional> |
| #include <iostream> |
| #include <limits> |
| #include <memory> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| #include "google/protobuf/any.pb.h" |
| #include "google/protobuf/descriptor.pb.h" |
| #include "google/protobuf/descriptor.pb.h" |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include "absl/base/log_severity.h" |
| #include "absl/container/btree_set.h" |
| #include "absl/container/flat_hash_set.h" |
| #include "absl/flags/flag.h" |
| #include "absl/functional/any_invocable.h" |
| #include "absl/log/absl_check.h" |
| #include "absl/log/absl_log.h" |
| #include "absl/log/die_if_null.h" |
| #include "absl/log/scoped_mock_log.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/numbers.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_format.h" |
| #include "absl/strings/str_split.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/strings/strip.h" |
| #include "absl/strings/substitute.h" |
| #include "absl/synchronization/notification.h" |
| #include "google/protobuf/compiler/importer.h" |
| #include "google/protobuf/compiler/parser.h" |
| #include "google/protobuf/cpp_features.pb.h" |
| #include "google/protobuf/descriptor_database.h" |
| #include "google/protobuf/descriptor_legacy.h" |
| #include "google/protobuf/dynamic_message.h" |
| #include "google/protobuf/feature_resolver.h" |
| #include "google/protobuf/io/coded_stream.h" |
| #include "google/protobuf/io/tokenizer.h" |
| #include "google/protobuf/io/zero_copy_stream_impl_lite.h" |
| #include "google/protobuf/test_textproto.h" |
| #include "google/protobuf/text_format.h" |
| #include "google/protobuf/unittest.pb.h" |
| #include "google/protobuf/unittest_custom_options.pb.h" |
| #include "google/protobuf/unittest_delimited.pb.h" |
| #include "google/protobuf/unittest_delimited_import.pb.h" |
| #include "google/protobuf/unittest_features.pb.h" |
| #include "google/protobuf/unittest_lazy_dependencies.pb.h" |
| #include "google/protobuf/unittest_lazy_dependencies_custom_option.pb.h" |
| #include "google/protobuf/unittest_lazy_dependencies_enum.pb.h" |
| #include "google/protobuf/unittest_proto3_arena.pb.h" |
| |
| |
| // Must be included last. |
| #include "google/protobuf/port_def.inc" |
| |
| using ::google::protobuf::internal::cpp::GetUtf8CheckMode; |
| using ::google::protobuf::internal::cpp::HasPreservingUnknownEnumSemantics; |
| using ::google::protobuf::internal::cpp::Utf8CheckMode; |
| using ::testing::AnyOf; |
| using ::testing::AtLeast; |
| using ::testing::ElementsAre; |
| using ::testing::HasSubstr; |
| using ::testing::NotNull; |
| using ::testing::Return; |
| |
| absl::Status GetStatus(const absl::Status& s) { return s; } |
| template <typename T> |
| absl::Status GetStatus(const absl::StatusOr<T>& s) { |
| return s.status(); |
| } |
| MATCHER_P2(StatusIs, status, message, |
| absl::StrCat(".status() is ", testing::PrintToString(status))) { |
| return GetStatus(arg).code() == status && |
| testing::ExplainMatchResult(message, GetStatus(arg).message(), |
| result_listener); |
| } |
| #define EXPECT_OK(x) EXPECT_THAT(x, StatusIs(absl::StatusCode::kOk, testing::_)) |
| #define ASSERT_OK(x) ASSERT_THAT(x, StatusIs(absl::StatusCode::kOk, testing::_)) |
| |
| namespace google { |
| namespace protobuf { |
| |
| // Can't use an anonymous namespace here due to brokenness of Tru64 compiler. |
| namespace descriptor_unittest { |
| |
| // Some helpers to make assembling descriptors faster. |
| DescriptorProto* AddMessage(FileDescriptorProto* file, |
| const std::string& name) { |
| DescriptorProto* result = file->add_message_type(); |
| result->set_name(name); |
| return result; |
| } |
| |
| DescriptorProto* AddNestedMessage(DescriptorProto* parent, |
| const std::string& name) { |
| DescriptorProto* result = parent->add_nested_type(); |
| result->set_name(name); |
| return result; |
| } |
| |
| EnumDescriptorProto* AddEnum(FileDescriptorProto* file, |
| absl::string_view name) { |
| EnumDescriptorProto* result = file->add_enum_type(); |
| result->set_name(name); |
| return result; |
| } |
| |
| EnumDescriptorProto* AddNestedEnum(DescriptorProto* parent, |
| const std::string& name) { |
| EnumDescriptorProto* result = parent->add_enum_type(); |
| result->set_name(name); |
| return result; |
| } |
| |
| ServiceDescriptorProto* AddService(FileDescriptorProto* file, |
| const std::string& name) { |
| ServiceDescriptorProto* result = file->add_service(); |
| result->set_name(name); |
| return result; |
| } |
| |
| FieldDescriptorProto* AddField(DescriptorProto* parent, const std::string& name, |
| int number, FieldDescriptorProto::Label label, |
| FieldDescriptorProto::Type type) { |
| FieldDescriptorProto* result = parent->add_field(); |
| result->set_name(name); |
| result->set_number(number); |
| result->set_label(label); |
| result->set_type(type); |
| return result; |
| } |
| |
| FieldDescriptorProto* AddExtension(FileDescriptorProto* file, |
| const std::string& extendee, |
| const std::string& name, int number, |
| FieldDescriptorProto::Label label, |
| FieldDescriptorProto::Type type) { |
| FieldDescriptorProto* result = file->add_extension(); |
| result->set_name(name); |
| result->set_number(number); |
| result->set_label(label); |
| result->set_type(type); |
| result->set_extendee(extendee); |
| return result; |
| } |
| |
| FieldDescriptorProto* AddNestedExtension(DescriptorProto* parent, |
| const std::string& extendee, |
| const std::string& name, int number, |
| FieldDescriptorProto::Label label, |
| FieldDescriptorProto::Type type) { |
| FieldDescriptorProto* result = parent->add_extension(); |
| result->set_name(name); |
| result->set_number(number); |
| result->set_label(label); |
| result->set_type(type); |
| result->set_extendee(extendee); |
| return result; |
| } |
| |
| DescriptorProto::ExtensionRange* AddExtensionRange(DescriptorProto* parent, |
| int start, int end) { |
| DescriptorProto::ExtensionRange* result = parent->add_extension_range(); |
| result->set_start(start); |
| result->set_end(end); |
| return result; |
| } |
| |
| DescriptorProto::ReservedRange* AddReservedRange(DescriptorProto* parent, |
| int start, int end) { |
| DescriptorProto::ReservedRange* result = parent->add_reserved_range(); |
| result->set_start(start); |
| result->set_end(end); |
| 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 std::string& name, int number) { |
| EnumValueDescriptorProto* result = enum_proto->add_value(); |
| result->set_name(name); |
| result->set_number(number); |
| return result; |
| } |
| |
| MethodDescriptorProto* AddMethod(ServiceDescriptorProto* service, |
| const std::string& name, |
| const std::string& input_type, |
| const std::string& output_type) { |
| MethodDescriptorProto* result = service->add_method(); |
| result->set_name(name); |
| result->set_input_type(input_type); |
| result->set_output_type(output_type); |
| return result; |
| } |
| |
| // Empty enums technically aren't allowed. We need to insert a dummy value |
| // into them. |
| void AddEmptyEnum(FileDescriptorProto* file, absl::string_view name) { |
| AddEnumValue(AddEnum(file, name), absl::StrCat(name, "_DUMMY"), 1); |
| } |
| |
| class MockErrorCollector : public DescriptorPool::ErrorCollector { |
| public: |
| MockErrorCollector() = default; |
| ~MockErrorCollector() override = default; |
| |
| std::string text_; |
| std::string warning_text_; |
| |
| // implements ErrorCollector --------------------------------------- |
| void RecordError(absl::string_view filename, absl::string_view element_name, |
| const Message* descriptor, ErrorLocation location, |
| absl::string_view message) override { |
| absl::SubstituteAndAppend(&text_, "$0: $1: $2: $3\n", filename, |
| element_name, ErrorLocationName(location), |
| message); |
| } |
| |
| // implements ErrorCollector --------------------------------------- |
| void RecordWarning(absl::string_view filename, absl::string_view element_name, |
| const Message* descriptor, ErrorLocation location, |
| absl::string_view message) override { |
| absl::SubstituteAndAppend(&warning_text_, "$0: $1: $2: $3\n", filename, |
| element_name, ErrorLocationName(location), |
| message); |
| } |
| }; |
| |
| // =================================================================== |
| |
| // Test simple files. |
| class FileDescriptorTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| // Build descriptors for the following definitions: |
| // |
| // // in "foo.proto" |
| // message FooMessage { extensions 1; } |
| // enum FooEnum {FOO_ENUM_VALUE = 1;} |
| // service FooService {} |
| // extend FooMessage { optional int32 foo_extension = 1; } |
| // |
| // // in "bar.proto" |
| // package bar_package; |
| // message BarMessage { extensions 1; } |
| // enum BarEnum {BAR_ENUM_VALUE = 1;} |
| // service BarService {} |
| // extend BarMessage { optional int32 bar_extension = 1; } |
| // |
| // Also, we have an empty file "baz.proto". This file's purpose is to |
| // make sure that even though it has the same package as foo.proto, |
| // searching it for members of foo.proto won't work. |
| |
| FileDescriptorProto foo_file; |
| foo_file.set_name("foo.proto"); |
| AddExtensionRange(AddMessage(&foo_file, "FooMessage"), 1, 2); |
| AddEnumValue(AddEnum(&foo_file, "FooEnum"), "FOO_ENUM_VALUE", 1); |
| AddService(&foo_file, "FooService"); |
| AddExtension(&foo_file, "FooMessage", "foo_extension", 1, |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| |
| FileDescriptorProto bar_file; |
| bar_file.set_name("bar.proto"); |
| bar_file.set_package("bar_package"); |
| bar_file.add_dependency("foo.proto"); |
| AddExtensionRange(AddMessage(&bar_file, "BarMessage"), 1, 2); |
| AddEnumValue(AddEnum(&bar_file, "BarEnum"), "BAR_ENUM_VALUE", 1); |
| AddService(&bar_file, "BarService"); |
| AddExtension(&bar_file, "bar_package.BarMessage", "bar_extension", 1, |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| |
| FileDescriptorProto baz_file; |
| baz_file.set_name("baz.proto"); |
| |
| // Build the descriptors and get the pointers. |
| foo_file_ = pool_.BuildFile(foo_file); |
| ASSERT_TRUE(foo_file_ != nullptr); |
| |
| bar_file_ = pool_.BuildFile(bar_file); |
| ASSERT_TRUE(bar_file_ != nullptr); |
| |
| baz_file_ = pool_.BuildFile(baz_file); |
| ASSERT_TRUE(baz_file_ != nullptr); |
| |
| ASSERT_EQ(1, foo_file_->message_type_count()); |
| foo_message_ = foo_file_->message_type(0); |
| ASSERT_EQ(1, foo_file_->enum_type_count()); |
| foo_enum_ = foo_file_->enum_type(0); |
| ASSERT_EQ(1, foo_enum_->value_count()); |
| foo_enum_value_ = foo_enum_->value(0); |
| ASSERT_EQ(1, foo_file_->service_count()); |
| foo_service_ = foo_file_->service(0); |
| ASSERT_EQ(1, foo_file_->extension_count()); |
| foo_extension_ = foo_file_->extension(0); |
| |
| ASSERT_EQ(1, bar_file_->message_type_count()); |
| bar_message_ = bar_file_->message_type(0); |
| ASSERT_EQ(1, bar_file_->enum_type_count()); |
| bar_enum_ = bar_file_->enum_type(0); |
| ASSERT_EQ(1, bar_enum_->value_count()); |
| bar_enum_value_ = bar_enum_->value(0); |
| ASSERT_EQ(1, bar_file_->service_count()); |
| bar_service_ = bar_file_->service(0); |
| ASSERT_EQ(1, bar_file_->extension_count()); |
| bar_extension_ = bar_file_->extension(0); |
| } |
| |
| DescriptorPool pool_; |
| |
| const FileDescriptor* foo_file_; |
| const FileDescriptor* bar_file_; |
| const FileDescriptor* baz_file_; |
| |
| const Descriptor* foo_message_; |
| const EnumDescriptor* foo_enum_; |
| const EnumValueDescriptor* foo_enum_value_; |
| const ServiceDescriptor* foo_service_; |
| const FieldDescriptor* foo_extension_; |
| |
| const Descriptor* bar_message_; |
| const EnumDescriptor* bar_enum_; |
| const EnumValueDescriptor* bar_enum_value_; |
| const ServiceDescriptor* bar_service_; |
| const FieldDescriptor* bar_extension_; |
| }; |
| |
| TEST_F(FileDescriptorTest, Name) { |
| EXPECT_EQ("foo.proto", foo_file_->name()); |
| EXPECT_EQ("bar.proto", bar_file_->name()); |
| EXPECT_EQ("baz.proto", baz_file_->name()); |
| } |
| |
| TEST_F(FileDescriptorTest, Package) { |
| EXPECT_EQ("", foo_file_->package()); |
| EXPECT_EQ("bar_package", bar_file_->package()); |
| } |
| |
| TEST_F(FileDescriptorTest, Dependencies) { |
| EXPECT_EQ(0, foo_file_->dependency_count()); |
| EXPECT_EQ(1, bar_file_->dependency_count()); |
| EXPECT_EQ(foo_file_, bar_file_->dependency(0)); |
| } |
| |
| TEST_F(FileDescriptorTest, FindMessageTypeByName) { |
| EXPECT_EQ(foo_message_, foo_file_->FindMessageTypeByName("FooMessage")); |
| EXPECT_EQ(bar_message_, bar_file_->FindMessageTypeByName("BarMessage")); |
| |
| EXPECT_TRUE(foo_file_->FindMessageTypeByName("BarMessage") == nullptr); |
| EXPECT_TRUE(bar_file_->FindMessageTypeByName("FooMessage") == nullptr); |
| EXPECT_TRUE(baz_file_->FindMessageTypeByName("FooMessage") == nullptr); |
| |
| EXPECT_TRUE(foo_file_->FindMessageTypeByName("NoSuchMessage") == nullptr); |
| EXPECT_TRUE(foo_file_->FindMessageTypeByName("FooEnum") == nullptr); |
| } |
| |
| TEST_F(FileDescriptorTest, FindEnumTypeByName) { |
| EXPECT_EQ(foo_enum_, foo_file_->FindEnumTypeByName("FooEnum")); |
| EXPECT_EQ(bar_enum_, bar_file_->FindEnumTypeByName("BarEnum")); |
| |
| EXPECT_TRUE(foo_file_->FindEnumTypeByName("BarEnum") == nullptr); |
| EXPECT_TRUE(bar_file_->FindEnumTypeByName("FooEnum") == nullptr); |
| EXPECT_TRUE(baz_file_->FindEnumTypeByName("FooEnum") == nullptr); |
| |
| EXPECT_TRUE(foo_file_->FindEnumTypeByName("NoSuchEnum") == nullptr); |
| EXPECT_TRUE(foo_file_->FindEnumTypeByName("FooMessage") == nullptr); |
| } |
| |
| TEST_F(FileDescriptorTest, FindEnumValueByName) { |
| EXPECT_EQ(foo_enum_value_, foo_file_->FindEnumValueByName("FOO_ENUM_VALUE")); |
| EXPECT_EQ(bar_enum_value_, bar_file_->FindEnumValueByName("BAR_ENUM_VALUE")); |
| |
| EXPECT_TRUE(foo_file_->FindEnumValueByName("BAR_ENUM_VALUE") == nullptr); |
| EXPECT_TRUE(bar_file_->FindEnumValueByName("FOO_ENUM_VALUE") == nullptr); |
| EXPECT_TRUE(baz_file_->FindEnumValueByName("FOO_ENUM_VALUE") == nullptr); |
| |
| EXPECT_TRUE(foo_file_->FindEnumValueByName("NO_SUCH_VALUE") == nullptr); |
| EXPECT_TRUE(foo_file_->FindEnumValueByName("FooMessage") == nullptr); |
| } |
| |
| TEST_F(FileDescriptorTest, FindServiceByName) { |
| EXPECT_EQ(foo_service_, foo_file_->FindServiceByName("FooService")); |
| EXPECT_EQ(bar_service_, bar_file_->FindServiceByName("BarService")); |
| |
| EXPECT_TRUE(foo_file_->FindServiceByName("BarService") == nullptr); |
| EXPECT_TRUE(bar_file_->FindServiceByName("FooService") == nullptr); |
| EXPECT_TRUE(baz_file_->FindServiceByName("FooService") == nullptr); |
| |
| EXPECT_TRUE(foo_file_->FindServiceByName("NoSuchService") == nullptr); |
| EXPECT_TRUE(foo_file_->FindServiceByName("FooMessage") == nullptr); |
| } |
| |
| TEST_F(FileDescriptorTest, FindExtensionByName) { |
| EXPECT_EQ(foo_extension_, foo_file_->FindExtensionByName("foo_extension")); |
| EXPECT_EQ(bar_extension_, bar_file_->FindExtensionByName("bar_extension")); |
| |
| EXPECT_TRUE(foo_file_->FindExtensionByName("bar_extension") == nullptr); |
| EXPECT_TRUE(bar_file_->FindExtensionByName("foo_extension") == nullptr); |
| EXPECT_TRUE(baz_file_->FindExtensionByName("foo_extension") == nullptr); |
| |
| EXPECT_TRUE(foo_file_->FindExtensionByName("no_such_extension") == nullptr); |
| EXPECT_TRUE(foo_file_->FindExtensionByName("FooMessage") == nullptr); |
| } |
| |
| TEST_F(FileDescriptorTest, FindExtensionByNumber) { |
| EXPECT_EQ(foo_extension_, pool_.FindExtensionByNumber(foo_message_, 1)); |
| EXPECT_EQ(bar_extension_, pool_.FindExtensionByNumber(bar_message_, 1)); |
| |
| EXPECT_TRUE(pool_.FindExtensionByNumber(foo_message_, 2) == nullptr); |
| } |
| |
| |
| TEST_F(FileDescriptorTest, BuildAgain) { |
| // Test that if we call BuildFile again on the same input we get the same |
| // FileDescriptor back. |
| FileDescriptorProto file; |
| foo_file_->CopyTo(&file); |
| EXPECT_EQ(foo_file_, pool_.BuildFile(file)); |
| |
| // But if we change the file then it won't work. |
| file.set_package("some.other.package"); |
| EXPECT_TRUE(pool_.BuildFile(file) == nullptr); |
| } |
| |
| TEST_F(FileDescriptorTest, BuildAgainWithSyntax) { |
| // Test that if we call BuildFile again on the same input we get the same |
| // FileDescriptor back even if syntax param is specified. |
| FileDescriptorProto proto_syntax2; |
| proto_syntax2.set_name("foo_syntax2"); |
| proto_syntax2.set_syntax("proto2"); |
| |
| const FileDescriptor* proto2_descriptor = pool_.BuildFile(proto_syntax2); |
| EXPECT_TRUE(proto2_descriptor != nullptr); |
| EXPECT_EQ(proto2_descriptor, pool_.BuildFile(proto_syntax2)); |
| |
| FileDescriptorProto implicit_proto2; |
| implicit_proto2.set_name("foo_implicit_syntax2"); |
| |
| const FileDescriptor* implicit_proto2_descriptor = |
| pool_.BuildFile(implicit_proto2); |
| EXPECT_TRUE(implicit_proto2_descriptor != nullptr); |
| // We get the same FileDescriptor back if syntax param is explicitly |
| // specified. |
| implicit_proto2.set_syntax("proto2"); |
| EXPECT_EQ(implicit_proto2_descriptor, pool_.BuildFile(implicit_proto2)); |
| |
| FileDescriptorProto proto_syntax3; |
| proto_syntax3.set_name("foo_syntax3"); |
| proto_syntax3.set_syntax("proto3"); |
| |
| const FileDescriptor* proto3_descriptor = pool_.BuildFile(proto_syntax3); |
| EXPECT_TRUE(proto3_descriptor != nullptr); |
| EXPECT_EQ(proto3_descriptor, pool_.BuildFile(proto_syntax3)); |
| } |
| |
| TEST_F(FileDescriptorTest, Edition) { |
| FileDescriptorProto proto; |
| proto.set_name("foo"); |
| { |
| proto.set_syntax("proto2"); |
| DescriptorPool pool; |
| const FileDescriptor* file = pool.BuildFile(proto); |
| ASSERT_TRUE(file != nullptr); |
| EXPECT_EQ(FileDescriptorLegacy(file).edition(), Edition::EDITION_PROTO2); |
| FileDescriptorProto other; |
| file->CopyTo(&other); |
| EXPECT_EQ("", other.syntax()); |
| EXPECT_FALSE(other.has_edition()); |
| } |
| { |
| proto.set_syntax("proto3"); |
| DescriptorPool pool; |
| const FileDescriptor* file = pool.BuildFile(proto); |
| ASSERT_TRUE(file != nullptr); |
| EXPECT_EQ(FileDescriptorLegacy(file).edition(), Edition::EDITION_PROTO3); |
| FileDescriptorProto other; |
| file->CopyTo(&other); |
| EXPECT_EQ("proto3", other.syntax()); |
| EXPECT_FALSE(other.has_edition()); |
| } |
| { |
| proto.set_syntax("editions"); |
| proto.set_edition(EDITION_2023); |
| DescriptorPool pool; |
| const FileDescriptor* file = pool.BuildFile(proto); |
| ASSERT_TRUE(file != nullptr); |
| EXPECT_EQ(FileDescriptorLegacy(file).edition(), Edition::EDITION_2023); |
| FileDescriptorProto other; |
| file->CopyTo(&other); |
| EXPECT_EQ("editions", other.syntax()); |
| EXPECT_EQ(other.edition(), EDITION_2023); |
| } |
| } |
| |
| TEST_F(FileDescriptorTest, CopyHeadingTo) { |
| FileDescriptorProto proto; |
| proto.set_name("foo.proto"); |
| proto.set_package("foo.bar.baz"); |
| proto.set_syntax("proto3"); |
| proto.mutable_options()->set_java_package("foo.bar.baz"); |
| |
| // Won't be copied. |
| proto.add_message_type()->set_name("Foo"); |
| |
| DescriptorPool pool; |
| const FileDescriptor* file = pool.BuildFile(proto); |
| ASSERT_NE(file, nullptr); |
| |
| FileDescriptorProto other; |
| file->CopyHeadingTo(&other); |
| EXPECT_EQ(other.name(), "foo.proto"); |
| EXPECT_EQ(other.package(), "foo.bar.baz"); |
| EXPECT_EQ(other.syntax(), "proto3"); |
| EXPECT_EQ(other.options().java_package(), "foo.bar.baz"); |
| EXPECT_TRUE(other.message_type().empty()); |
| EXPECT_EQ(&other.options().features(), &FeatureSet::default_instance()); |
| { |
| proto.set_syntax("editions"); |
| proto.set_edition(EDITION_2023); |
| |
| DescriptorPool pool; |
| const FileDescriptor* file = pool.BuildFile(proto); |
| ASSERT_NE(file, nullptr); |
| |
| FileDescriptorProto other; |
| file->CopyHeadingTo(&other); |
| EXPECT_EQ(other.name(), "foo.proto"); |
| EXPECT_EQ(other.package(), "foo.bar.baz"); |
| EXPECT_EQ(other.syntax(), "editions"); |
| EXPECT_EQ(other.edition(), EDITION_2023); |
| EXPECT_EQ(other.options().java_package(), "foo.bar.baz"); |
| EXPECT_TRUE(other.message_type().empty()); |
| EXPECT_EQ(&other.options().features(), &FeatureSet::default_instance()); |
| } |
| } |
| |
| void ExtractDebugString( |
| const FileDescriptor* file, absl::flat_hash_set<std::string>* visited, |
| std::vector<std::pair<std::string, std::string>>* debug_strings) { |
| if (!visited->insert(file->name()).second) { |
| return; |
| } |
| for (int i = 0; i < file->dependency_count(); ++i) { |
| ExtractDebugString(file->dependency(i), visited, debug_strings); |
| } |
| debug_strings->push_back(std::make_pair(file->name(), file->DebugString())); |
| } |
| |
| class SimpleErrorCollector : public io::ErrorCollector { |
| public: |
| // implements ErrorCollector --------------------------------------- |
| void RecordError(int line, int column, absl::string_view message) override { |
| last_error_ = absl::StrFormat("%d:%d:%s", line, column, message); |
| } |
| |
| const std::string& last_error() { return last_error_; } |
| |
| private: |
| std::string last_error_; |
| }; |
| // Test that the result of FileDescriptor::DebugString() can be used to create |
| // the original descriptors. |
| TEST_F(FileDescriptorTest, DebugStringRoundTrip) { |
| absl::flat_hash_set<std::string> visited; |
| std::vector<std::pair<std::string, std::string>> debug_strings; |
| ExtractDebugString(protobuf_unittest::TestAllTypes::descriptor()->file(), |
| &visited, &debug_strings); |
| ExtractDebugString( |
| protobuf_unittest::TestMessageWithCustomOptions::descriptor()->file(), |
| &visited, &debug_strings); |
| ExtractDebugString(proto3_arena_unittest::TestAllTypes::descriptor()->file(), |
| &visited, &debug_strings); |
| ASSERT_GE(debug_strings.size(), 3); |
| |
| DescriptorPool pool; |
| for (size_t i = 0; i < debug_strings.size(); ++i) { |
| const std::string& name = debug_strings[i].first; |
| const std::string& content = debug_strings[i].second; |
| io::ArrayInputStream input_stream(content.data(), content.size()); |
| SimpleErrorCollector error_collector; |
| io::Tokenizer tokenizer(&input_stream, &error_collector); |
| compiler::Parser parser; |
| parser.RecordErrorsTo(&error_collector); |
| FileDescriptorProto proto; |
| ASSERT_TRUE(parser.Parse(&tokenizer, &proto)) |
| << error_collector.last_error() << "\n" |
| << content; |
| ASSERT_EQ("", error_collector.last_error()); |
| proto.set_name(name); |
| const FileDescriptor* descriptor = pool.BuildFile(proto); |
| ASSERT_TRUE(descriptor != nullptr) << error_collector.last_error(); |
| EXPECT_EQ(content, descriptor->DebugString()); |
| } |
| } |
| |
| TEST_F(FileDescriptorTest, AbslStringifyWorks) { |
| std::string s = absl::StrFormat( |
| "%v", |
| *protobuf_unittest::TestMessageWithCustomOptions::descriptor()->file()); |
| EXPECT_THAT(s, HasSubstr("TestMessageWithCustomOptions")); |
| } |
| |
| // =================================================================== |
| |
| // Test simple flat messages and fields. |
| class DescriptorTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| // Build descriptors for the following definitions: |
| // |
| // // in "foo.proto" |
| // message TestForeign {} |
| // enum TestEnum {} |
| // |
| // message TestMessage { |
| // required string foo = 1; |
| // optional TestEnum bar = 6; |
| // repeated TestForeign baz = 500000000; |
| // optional group moo = 15 {} |
| // } |
| // |
| // // in "bar.proto" |
| // package corge.grault; |
| // message TestMessage2 { |
| // required string foo = 1; |
| // required string bar = 2; |
| // required string mooo = 6; |
| // } |
| // |
| // // in "map.proto" |
| // message TestMessage3 { |
| // map<int32, int32> map_int32_int32 = 1; |
| // } |
| // |
| // // in "json.proto" |
| // message TestMessage4 { |
| // optional int32 field_name1 = 1; |
| // optional int32 fieldName2 = 2; |
| // optional int32 FieldName3 = 3; |
| // optional int32 _field_name4 = 4; |
| // optional int32 FIELD_NAME5 = 5; |
| // optional int32 field_name6 = 6 [json_name = "@type"]; |
| // } |
| // |
| // We cheat and use TestForeign as the type for moo rather than create |
| // an actual nested type. |
| // |
| // Since all primitive types (including string) use the same building |
| // code, there's no need to test each one individually. |
| // |
| // TestMessage2 is primarily here to test FindFieldByName and friends. |
| // All messages created from the same DescriptorPool share the same lookup |
| // table, so we need to insure that they don't interfere. |
| |
| FileDescriptorProto foo_file; |
| foo_file.set_name("foo.proto"); |
| AddMessage(&foo_file, "TestForeign"); |
| AddEmptyEnum(&foo_file, "TestEnum"); |
| |
| DescriptorProto* message = AddMessage(&foo_file, "TestMessage"); |
| AddField(message, "foo", 1, FieldDescriptorProto::LABEL_REQUIRED, |
| FieldDescriptorProto::TYPE_STRING); |
| AddField(message, "bar", 6, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_ENUM) |
| ->set_type_name("TestEnum"); |
| AddField(message, "baz", 500000000, FieldDescriptorProto::LABEL_REPEATED, |
| FieldDescriptorProto::TYPE_MESSAGE) |
| ->set_type_name("TestForeign"); |
| AddField(message, "moo", 15, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_GROUP) |
| ->set_type_name("TestForeign"); |
| |
| FileDescriptorProto bar_file; |
| bar_file.set_name("bar.proto"); |
| bar_file.set_package("corge.grault"); |
| |
| DescriptorProto* message2 = AddMessage(&bar_file, "TestMessage2"); |
| AddField(message2, "foo", 1, FieldDescriptorProto::LABEL_REQUIRED, |
| FieldDescriptorProto::TYPE_STRING); |
| AddField(message2, "bar", 2, FieldDescriptorProto::LABEL_REQUIRED, |
| FieldDescriptorProto::TYPE_STRING); |
| AddField(message2, "mooo", 6, FieldDescriptorProto::LABEL_REQUIRED, |
| FieldDescriptorProto::TYPE_STRING); |
| |
| FileDescriptorProto map_file; |
| map_file.set_name("map.proto"); |
| DescriptorProto* message3 = AddMessage(&map_file, "TestMessage3"); |
| |
| DescriptorProto* entry = AddNestedMessage(message3, "MapInt32Int32Entry"); |
| AddField(entry, "key", 1, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddField(entry, "value", 2, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| entry->mutable_options()->set_map_entry(true); |
| |
| AddField(message3, "map_int32_int32", 1, |
| FieldDescriptorProto::LABEL_REPEATED, |
| FieldDescriptorProto::TYPE_MESSAGE) |
| ->set_type_name("MapInt32Int32Entry"); |
| |
| FileDescriptorProto json_file; |
| json_file.set_name("json.proto"); |
| json_file.set_syntax("proto3"); |
| DescriptorProto* message4 = AddMessage(&json_file, "TestMessage4"); |
| AddField(message4, "field_name1", 1, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddField(message4, "fieldName2", 2, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddField(message4, "FieldName3", 3, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddField(message4, "_field_name4", 4, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddField(message4, "FIELD_NAME5", 5, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddField(message4, "field_name6", 6, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32) |
| ->set_json_name("@type"); |
| AddField(message4, "fieldname7", 7, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| |
| // Build the descriptors and get the pointers. |
| foo_file_ = pool_.BuildFile(foo_file); |
| ASSERT_TRUE(foo_file_ != nullptr); |
| |
| bar_file_ = pool_.BuildFile(bar_file); |
| ASSERT_TRUE(bar_file_ != nullptr); |
| |
| map_file_ = pool_.BuildFile(map_file); |
| ASSERT_TRUE(map_file_ != nullptr); |
| |
| json_file_ = pool_.BuildFile(json_file); |
| ASSERT_TRUE(json_file_ != nullptr); |
| |
| ASSERT_EQ(1, foo_file_->enum_type_count()); |
| enum_ = foo_file_->enum_type(0); |
| |
| ASSERT_EQ(2, foo_file_->message_type_count()); |
| foreign_ = foo_file_->message_type(0); |
| message_ = foo_file_->message_type(1); |
| |
| ASSERT_EQ(4, message_->field_count()); |
| foo_ = message_->field(0); |
| bar_ = message_->field(1); |
| baz_ = message_->field(2); |
| moo_ = message_->field(3); |
| |
| ASSERT_EQ(1, bar_file_->message_type_count()); |
| message2_ = bar_file_->message_type(0); |
| |
| ASSERT_EQ(3, message2_->field_count()); |
| foo2_ = message2_->field(0); |
| bar2_ = message2_->field(1); |
| mooo2_ = message2_->field(2); |
| |
| ASSERT_EQ(1, map_file_->message_type_count()); |
| message3_ = map_file_->message_type(0); |
| |
| ASSERT_EQ(1, message3_->field_count()); |
| map_ = message3_->field(0); |
| |
| ASSERT_EQ(1, json_file_->message_type_count()); |
| message4_ = json_file_->message_type(0); |
| } |
| |
| void CopyWithJsonName(const Descriptor* message, DescriptorProto* proto) { |
| message->CopyTo(proto); |
| message->CopyJsonNameTo(proto); |
| } |
| |
| const EnumValueDescriptor* FindValueByNumberCreatingIfUnknown( |
| const EnumDescriptor* desc, int number) { |
| return desc->FindValueByNumberCreatingIfUnknown(number); |
| } |
| |
| DescriptorPool pool_; |
| |
| const FileDescriptor* foo_file_; |
| const FileDescriptor* bar_file_; |
| const FileDescriptor* map_file_; |
| const FileDescriptor* json_file_; |
| |
| const Descriptor* message_; |
| const Descriptor* message2_; |
| const Descriptor* message3_; |
| const Descriptor* message4_; |
| const Descriptor* foreign_; |
| const EnumDescriptor* enum_; |
| |
| const FieldDescriptor* foo_; |
| const FieldDescriptor* bar_; |
| const FieldDescriptor* baz_; |
| const FieldDescriptor* moo_; |
| |
| const FieldDescriptor* foo2_; |
| const FieldDescriptor* bar2_; |
| const FieldDescriptor* mooo2_; |
| |
| const FieldDescriptor* map_; |
| }; |
| |
| TEST_F(DescriptorTest, Name) { |
| EXPECT_EQ("TestMessage", message_->name()); |
| EXPECT_EQ("TestMessage", message_->full_name()); |
| EXPECT_EQ(foo_file_, message_->file()); |
| |
| EXPECT_EQ("TestMessage2", message2_->name()); |
| EXPECT_EQ("corge.grault.TestMessage2", message2_->full_name()); |
| EXPECT_EQ(bar_file_, message2_->file()); |
| } |
| |
| TEST_F(DescriptorTest, ContainingType) { |
| EXPECT_TRUE(message_->containing_type() == nullptr); |
| EXPECT_TRUE(message2_->containing_type() == nullptr); |
| } |
| |
| TEST_F(DescriptorTest, FieldNamesDedup) { |
| const auto collect_unique_names = [](const FieldDescriptor* field) { |
| absl::btree_set<std::string> names{field->name(), field->lowercase_name(), |
| field->camelcase_name(), |
| field->json_name()}; |
| // Verify that we have the same number of string objects as we have string |
| // values. That is, duplicate names use the same std::string object. |
| // This is for memory efficiency. |
| EXPECT_EQ(names.size(), (absl::flat_hash_set<const std::string*>{ |
| &field->name(), &field->lowercase_name(), |
| &field->camelcase_name(), &field->json_name()} |
| .size())) |
| << testing::PrintToString(names); |
| return names; |
| }; |
| |
| // field_name1 |
| EXPECT_THAT(collect_unique_names(message4_->field(0)), |
| ElementsAre("fieldName1", "field_name1")); |
| // fieldName2 |
| EXPECT_THAT(collect_unique_names(message4_->field(1)), |
| ElementsAre("fieldName2", "fieldname2")); |
| // FieldName3 |
| EXPECT_THAT(collect_unique_names(message4_->field(2)), |
| ElementsAre("FieldName3", "fieldName3", "fieldname3")); |
| // _field_name4 |
| EXPECT_THAT(collect_unique_names(message4_->field(3)), |
| ElementsAre("FieldName4", "_field_name4", "fieldName4")); |
| // FIELD_NAME5 |
| EXPECT_THAT( |
| collect_unique_names(message4_->field(4)), |
| ElementsAre("FIELDNAME5", "FIELD_NAME5", "fIELDNAME5", "field_name5")); |
| // field_name6, with json name @type |
| EXPECT_THAT(collect_unique_names(message4_->field(5)), |
| ElementsAre("@type", "fieldName6", "field_name6")); |
| // fieldname7 |
| EXPECT_THAT(collect_unique_names(message4_->field(6)), |
| ElementsAre("fieldname7")); |
| } |
| |
| TEST_F(DescriptorTest, FieldNameDedupJsonEqFull) { |
| // Test a regression where json_name == full_name |
| FileDescriptorProto proto; |
| proto.set_name("file"); |
| auto* message = AddMessage(&proto, "Name1"); |
| auto* field = |
| AddField(message, "Name2", 1, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| field->set_json_name("Name1.Name2"); |
| auto* file = pool_.BuildFile(proto); |
| EXPECT_EQ(file->message_type(0)->name(), "Name1"); |
| EXPECT_EQ(file->message_type(0)->field(0)->name(), "Name2"); |
| EXPECT_EQ(file->message_type(0)->field(0)->full_name(), "Name1.Name2"); |
| EXPECT_EQ(file->message_type(0)->field(0)->json_name(), "Name1.Name2"); |
| } |
| |
| TEST_F(DescriptorTest, FieldsByIndex) { |
| ASSERT_EQ(4, message_->field_count()); |
| EXPECT_EQ(foo_, message_->field(0)); |
| EXPECT_EQ(bar_, message_->field(1)); |
| EXPECT_EQ(baz_, message_->field(2)); |
| EXPECT_EQ(moo_, message_->field(3)); |
| } |
| |
| TEST_F(DescriptorTest, FindFieldByName) { |
| // All messages in the same DescriptorPool share a single lookup table for |
| // fields. So, in addition to testing that FindFieldByName finds the fields |
| // of the message, we need to test that it does *not* find the fields of |
| // *other* messages. |
| |
| EXPECT_EQ(foo_, message_->FindFieldByName("foo")); |
| EXPECT_EQ(bar_, message_->FindFieldByName("bar")); |
| EXPECT_EQ(baz_, message_->FindFieldByName("baz")); |
| EXPECT_EQ(moo_, message_->FindFieldByName("moo")); |
| EXPECT_TRUE(message_->FindFieldByName("no_such_field") == nullptr); |
| EXPECT_TRUE(message_->FindFieldByName("mooo") == nullptr); |
| |
| EXPECT_EQ(foo2_, message2_->FindFieldByName("foo")); |
| EXPECT_EQ(bar2_, message2_->FindFieldByName("bar")); |
| EXPECT_EQ(mooo2_, message2_->FindFieldByName("mooo")); |
| EXPECT_TRUE(message2_->FindFieldByName("baz") == nullptr); |
| EXPECT_TRUE(message2_->FindFieldByName("moo") == nullptr); |
| } |
| |
| TEST_F(DescriptorTest, FindFieldByNumber) { |
| EXPECT_EQ(foo_, message_->FindFieldByNumber(1)); |
| EXPECT_EQ(bar_, message_->FindFieldByNumber(6)); |
| EXPECT_EQ(baz_, message_->FindFieldByNumber(500000000)); |
| EXPECT_EQ(moo_, message_->FindFieldByNumber(15)); |
| EXPECT_TRUE(message_->FindFieldByNumber(837592) == nullptr); |
| EXPECT_TRUE(message_->FindFieldByNumber(2) == nullptr); |
| |
| EXPECT_EQ(foo2_, message2_->FindFieldByNumber(1)); |
| EXPECT_EQ(bar2_, message2_->FindFieldByNumber(2)); |
| EXPECT_EQ(mooo2_, message2_->FindFieldByNumber(6)); |
| EXPECT_TRUE(message2_->FindFieldByNumber(15) == nullptr); |
| EXPECT_TRUE(message2_->FindFieldByNumber(500000000) == nullptr); |
| } |
| |
| TEST_F(DescriptorTest, FieldName) { |
| EXPECT_EQ("foo", foo_->name()); |
| EXPECT_EQ("bar", bar_->name()); |
| EXPECT_EQ("baz", baz_->name()); |
| EXPECT_EQ("moo", moo_->name()); |
| } |
| |
| TEST_F(DescriptorTest, FieldFullName) { |
| EXPECT_EQ("TestMessage.foo", foo_->full_name()); |
| EXPECT_EQ("TestMessage.bar", bar_->full_name()); |
| EXPECT_EQ("TestMessage.baz", baz_->full_name()); |
| EXPECT_EQ("TestMessage.moo", moo_->full_name()); |
| |
| EXPECT_EQ("corge.grault.TestMessage2.foo", foo2_->full_name()); |
| EXPECT_EQ("corge.grault.TestMessage2.bar", bar2_->full_name()); |
| EXPECT_EQ("corge.grault.TestMessage2.mooo", mooo2_->full_name()); |
| } |
| |
| TEST_F(DescriptorTest, PrintableNameIsFullNameForNonExtensionFields) { |
| EXPECT_EQ("TestMessage.foo", foo_->PrintableNameForExtension()); |
| EXPECT_EQ("TestMessage.bar", bar_->PrintableNameForExtension()); |
| EXPECT_EQ("TestMessage.baz", baz_->PrintableNameForExtension()); |
| EXPECT_EQ("TestMessage.moo", moo_->PrintableNameForExtension()); |
| |
| EXPECT_EQ("corge.grault.TestMessage2.foo", |
| foo2_->PrintableNameForExtension()); |
| EXPECT_EQ("corge.grault.TestMessage2.bar", |
| bar2_->PrintableNameForExtension()); |
| EXPECT_EQ("corge.grault.TestMessage2.mooo", |
| mooo2_->PrintableNameForExtension()); |
| } |
| |
| TEST_F(DescriptorTest, PrintableNameIsFullNameForNonMessageSetExtension) { |
| EXPECT_EQ("protobuf_unittest.Aggregate.nested", |
| protobuf_unittest::Aggregate::descriptor() |
| ->FindExtensionByName("nested") |
| ->PrintableNameForExtension()); |
| } |
| |
| TEST_F(DescriptorTest, PrintableNameIsExtendingTypeForMessageSetExtension) { |
| EXPECT_EQ("protobuf_unittest.AggregateMessageSetElement", |
| protobuf_unittest::AggregateMessageSetElement::descriptor() |
| ->FindExtensionByName("message_set_extension") |
| ->PrintableNameForExtension()); |
| } |
| |
| TEST_F(DescriptorTest, FieldJsonName) { |
| EXPECT_EQ("fieldName1", message4_->field(0)->json_name()); |
| EXPECT_EQ("fieldName2", message4_->field(1)->json_name()); |
| EXPECT_EQ("FieldName3", message4_->field(2)->json_name()); |
| EXPECT_EQ("FieldName4", message4_->field(3)->json_name()); |
| EXPECT_EQ("FIELDNAME5", message4_->field(4)->json_name()); |
| EXPECT_EQ("@type", message4_->field(5)->json_name()); |
| |
| DescriptorProto proto; |
| message4_->CopyTo(&proto); |
| ASSERT_EQ(7, proto.field_size()); |
| EXPECT_FALSE(proto.field(0).has_json_name()); |
| EXPECT_FALSE(proto.field(1).has_json_name()); |
| EXPECT_FALSE(proto.field(2).has_json_name()); |
| EXPECT_FALSE(proto.field(3).has_json_name()); |
| EXPECT_FALSE(proto.field(4).has_json_name()); |
| EXPECT_EQ("@type", proto.field(5).json_name()); |
| EXPECT_FALSE(proto.field(6).has_json_name()); |
| |
| proto.Clear(); |
| CopyWithJsonName(message4_, &proto); |
| ASSERT_EQ(7, proto.field_size()); |
| EXPECT_EQ("fieldName1", proto.field(0).json_name()); |
| EXPECT_EQ("fieldName2", proto.field(1).json_name()); |
| EXPECT_EQ("FieldName3", proto.field(2).json_name()); |
| EXPECT_EQ("FieldName4", proto.field(3).json_name()); |
| EXPECT_EQ("FIELDNAME5", proto.field(4).json_name()); |
| EXPECT_EQ("@type", proto.field(5).json_name()); |
| EXPECT_EQ("fieldname7", proto.field(6).json_name()); |
| |
| // Test generated descriptor. |
| const Descriptor* generated = protobuf_unittest::TestJsonName::descriptor(); |
| ASSERT_EQ(7, generated->field_count()); |
| EXPECT_EQ("fieldName1", generated->field(0)->json_name()); |
| EXPECT_EQ("fieldName2", generated->field(1)->json_name()); |
| EXPECT_EQ("FieldName3", generated->field(2)->json_name()); |
| EXPECT_EQ("FieldName4", generated->field(3)->json_name()); |
| EXPECT_EQ("FIELDNAME5", generated->field(4)->json_name()); |
| EXPECT_EQ("@type", generated->field(5)->json_name()); |
| EXPECT_EQ("fieldname7", generated->field(6)->json_name()); |
| } |
| |
| TEST_F(DescriptorTest, FieldFile) { |
| EXPECT_EQ(foo_file_, foo_->file()); |
| EXPECT_EQ(foo_file_, bar_->file()); |
| EXPECT_EQ(foo_file_, baz_->file()); |
| EXPECT_EQ(foo_file_, moo_->file()); |
| |
| EXPECT_EQ(bar_file_, foo2_->file()); |
| EXPECT_EQ(bar_file_, bar2_->file()); |
| EXPECT_EQ(bar_file_, mooo2_->file()); |
| } |
| |
| TEST_F(DescriptorTest, FieldIndex) { |
| EXPECT_EQ(0, foo_->index()); |
| EXPECT_EQ(1, bar_->index()); |
| EXPECT_EQ(2, baz_->index()); |
| EXPECT_EQ(3, moo_->index()); |
| } |
| |
| TEST_F(DescriptorTest, FieldNumber) { |
| EXPECT_EQ(1, foo_->number()); |
| EXPECT_EQ(6, bar_->number()); |
| EXPECT_EQ(500000000, baz_->number()); |
| EXPECT_EQ(15, moo_->number()); |
| } |
| |
| TEST_F(DescriptorTest, FieldType) { |
| EXPECT_EQ(FieldDescriptor::TYPE_STRING, foo_->type()); |
| EXPECT_EQ(FieldDescriptor::TYPE_ENUM, bar_->type()); |
| EXPECT_EQ(FieldDescriptor::TYPE_MESSAGE, baz_->type()); |
| EXPECT_EQ(FieldDescriptor::TYPE_GROUP, moo_->type()); |
| } |
| |
| TEST_F(DescriptorTest, FieldLabel) { |
| EXPECT_EQ(FieldDescriptor::LABEL_REQUIRED, foo_->label()); |
| EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, bar_->label()); |
| EXPECT_EQ(FieldDescriptor::LABEL_REPEATED, baz_->label()); |
| EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, moo_->label()); |
| |
| EXPECT_TRUE(foo_->is_required()); |
| EXPECT_FALSE(foo_->is_optional()); |
| EXPECT_FALSE(foo_->is_repeated()); |
| |
| EXPECT_FALSE(bar_->is_required()); |
| EXPECT_TRUE(bar_->is_optional()); |
| EXPECT_FALSE(bar_->is_repeated()); |
| |
| EXPECT_FALSE(baz_->is_required()); |
| EXPECT_FALSE(baz_->is_optional()); |
| EXPECT_TRUE(baz_->is_repeated()); |
| } |
| |
| TEST_F(DescriptorTest, NeedsUtf8Check) { |
| EXPECT_FALSE(foo_->requires_utf8_validation()); |
| EXPECT_FALSE(bar_->requires_utf8_validation()); |
| |
| // Build a copy of the file in proto3. |
| FileDescriptorProto foo_file3; |
| foo_file_->CopyTo(&foo_file3); |
| foo_file3.set_syntax("proto3"); |
| |
| // Make this valid proto3 by removing `required` and the one group field. |
| for (auto& f : *foo_file3.mutable_message_type(1)->mutable_field()) { |
| f.clear_label(); |
| if (f.type() == FieldDescriptorProto::TYPE_GROUP) { |
| f.set_type(FieldDescriptorProto::TYPE_MESSAGE); |
| } |
| } |
| // Make this valid proto3 by making the first enum value be zero. |
| foo_file3.mutable_enum_type(0)->mutable_value(0)->set_number(0); |
| |
| DescriptorPool pool3; |
| const Descriptor* message3 = pool3.BuildFile(foo_file3)->message_type(1); |
| const FieldDescriptor* foo3 = message3->field(0); |
| const FieldDescriptor* bar3 = message3->field(1); |
| |
| EXPECT_TRUE(foo3->requires_utf8_validation()); |
| EXPECT_FALSE(bar3->requires_utf8_validation()); |
| } |
| |
| TEST_F(DescriptorTest, EnumFieldTreatedAsClosed) { |
| // Make an open enum definition. |
| FileDescriptorProto open_enum_file; |
| open_enum_file.set_name("open_enum.proto"); |
| open_enum_file.set_syntax("proto3"); |
| AddEnumValue(AddEnum(&open_enum_file, "TestEnumOpen"), "TestEnumOpen_VALUE0", |
| 0); |
| |
| const EnumDescriptor* open_enum = |
| pool_.BuildFile(open_enum_file)->enum_type(0); |
| EXPECT_FALSE(open_enum->is_closed()); |
| |
| // Create a message that treats enum fields as closed. |
| FileDescriptorProto closed_file; |
| closed_file.set_name("closed_enum_field.proto"); |
| closed_file.add_dependency("open_enum.proto"); |
| closed_file.add_dependency("foo.proto"); |
| |
| DescriptorProto* message = AddMessage(&closed_file, "TestClosedEnumField"); |
| AddField(message, "int_field", 1, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddField(message, "open_enum", 2, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_ENUM) |
| ->set_type_name("TestEnumOpen"); |
| AddField(message, "closed_enum", 3, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_ENUM) |
| ->set_type_name("TestEnum"); |
| const Descriptor* closed_message = |
| pool_.BuildFile(closed_file)->message_type(0); |
| |
| EXPECT_FALSE(closed_message->FindFieldByName("int_field") |
| ->legacy_enum_field_treated_as_closed()); |
| EXPECT_TRUE(closed_message->FindFieldByName("closed_enum") |
| ->legacy_enum_field_treated_as_closed()); |
| EXPECT_TRUE(closed_message->FindFieldByName("open_enum") |
| ->legacy_enum_field_treated_as_closed()); |
| } |
| |
| TEST_F(DescriptorTest, EnumFieldTreatedAsOpen) { |
| FileDescriptorProto open_enum_file; |
| open_enum_file.set_name("open_enum.proto"); |
| open_enum_file.set_syntax("proto3"); |
| AddEnumValue(AddEnum(&open_enum_file, "TestEnumOpen"), "TestEnumOpen_VALUE0", |
| 0); |
| DescriptorProto* message = AddMessage(&open_enum_file, "TestOpenEnumField"); |
| AddField(message, "int_field", 1, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddField(message, "open_enum", 2, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_ENUM) |
| ->set_type_name("TestEnumOpen"); |
| const FileDescriptor* open_enum_file_desc = pool_.BuildFile(open_enum_file); |
| const Descriptor* open_message = open_enum_file_desc->message_type(0); |
| const EnumDescriptor* open_enum = open_enum_file_desc->enum_type(0); |
| EXPECT_FALSE(open_enum->is_closed()); |
| EXPECT_FALSE(open_message->FindFieldByName("int_field") |
| ->legacy_enum_field_treated_as_closed()); |
| EXPECT_FALSE(open_message->FindFieldByName("open_enum") |
| ->legacy_enum_field_treated_as_closed()); |
| } |
| |
| TEST_F(DescriptorTest, IsMap) { |
| EXPECT_TRUE(map_->is_map()); |
| EXPECT_FALSE(baz_->is_map()); |
| EXPECT_TRUE(map_->message_type()->options().map_entry()); |
| } |
| |
| TEST_F(DescriptorTest, GetMap) { |
| const Descriptor* map_desc = map_->message_type(); |
| const FieldDescriptor* map_key = map_desc->map_key(); |
| ASSERT_TRUE(map_key != nullptr); |
| EXPECT_EQ(map_key->name(), "key"); |
| EXPECT_EQ(map_key->number(), 1); |
| |
| const FieldDescriptor* map_value = map_desc->map_value(); |
| ASSERT_TRUE(map_value != nullptr); |
| EXPECT_EQ(map_value->name(), "value"); |
| EXPECT_EQ(map_value->number(), 2); |
| |
| EXPECT_EQ(message_->map_key(), nullptr); |
| EXPECT_EQ(message_->map_value(), nullptr); |
| } |
| |
| TEST_F(DescriptorTest, FieldHasDefault) { |
| EXPECT_FALSE(foo_->has_default_value()); |
| EXPECT_FALSE(bar_->has_default_value()); |
| EXPECT_FALSE(baz_->has_default_value()); |
| EXPECT_FALSE(moo_->has_default_value()); |
| } |
| |
| TEST_F(DescriptorTest, FieldContainingType) { |
| EXPECT_EQ(message_, foo_->containing_type()); |
| EXPECT_EQ(message_, bar_->containing_type()); |
| EXPECT_EQ(message_, baz_->containing_type()); |
| EXPECT_EQ(message_, moo_->containing_type()); |
| |
| EXPECT_EQ(message2_, foo2_->containing_type()); |
| EXPECT_EQ(message2_, bar2_->containing_type()); |
| EXPECT_EQ(message2_, mooo2_->containing_type()); |
| } |
| |
| TEST_F(DescriptorTest, FieldMessageType) { |
| EXPECT_TRUE(foo_->message_type() == nullptr); |
| EXPECT_TRUE(bar_->message_type() == nullptr); |
| |
| EXPECT_EQ(foreign_, baz_->message_type()); |
| EXPECT_EQ(foreign_, moo_->message_type()); |
| } |
| |
| TEST_F(DescriptorTest, FieldEnumType) { |
| EXPECT_TRUE(foo_->enum_type() == nullptr); |
| EXPECT_TRUE(baz_->enum_type() == nullptr); |
| EXPECT_TRUE(moo_->enum_type() == nullptr); |
| |
| EXPECT_EQ(enum_, bar_->enum_type()); |
| } |
| |
| TEST_F(DescriptorTest, AbslStringifyWorks) { |
| EXPECT_THAT(absl::StrFormat("%v", *message_), |
| HasSubstr(message_->full_name())); |
| EXPECT_THAT(absl::StrFormat("%v", *foo_), HasSubstr(foo_->name())); |
| } |
| |
| |
| // =================================================================== |
| |
| // Test simple flat messages and fields. |
| class OneofDescriptorTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| // Build descriptors for the following definitions: |
| // |
| // package garply; |
| // message TestOneof { |
| // optional int32 a = 1; |
| // oneof foo { |
| // string b = 2; |
| // TestOneof c = 3; |
| // } |
| // oneof bar { |
| // float d = 4; |
| // } |
| // } |
| |
| FileDescriptorProto baz_file; |
| baz_file.set_name("baz.proto"); |
| baz_file.set_package("garply"); |
| |
| DescriptorProto* oneof_message = AddMessage(&baz_file, "TestOneof"); |
| oneof_message->add_oneof_decl()->set_name("foo"); |
| oneof_message->add_oneof_decl()->set_name("bar"); |
| |
| AddField(oneof_message, "a", 1, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddField(oneof_message, "b", 2, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_STRING); |
| oneof_message->mutable_field(1)->set_oneof_index(0); |
| AddField(oneof_message, "c", 3, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_MESSAGE); |
| oneof_message->mutable_field(2)->set_oneof_index(0); |
| oneof_message->mutable_field(2)->set_type_name("TestOneof"); |
| |
| AddField(oneof_message, "d", 4, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_FLOAT); |
| oneof_message->mutable_field(3)->set_oneof_index(1); |
| |
| // Build the descriptors and get the pointers. |
| baz_file_ = pool_.BuildFile(baz_file); |
| ASSERT_TRUE(baz_file_ != nullptr); |
| |
| ASSERT_EQ(1, baz_file_->message_type_count()); |
| oneof_message_ = baz_file_->message_type(0); |
| |
| ASSERT_EQ(2, oneof_message_->oneof_decl_count()); |
| oneof_ = oneof_message_->oneof_decl(0); |
| oneof2_ = oneof_message_->oneof_decl(1); |
| |
| ASSERT_EQ(4, oneof_message_->field_count()); |
| a_ = oneof_message_->field(0); |
| b_ = oneof_message_->field(1); |
| c_ = oneof_message_->field(2); |
| d_ = oneof_message_->field(3); |
| } |
| |
| DescriptorPool pool_; |
| |
| const FileDescriptor* baz_file_; |
| |
| const Descriptor* oneof_message_; |
| |
| const OneofDescriptor* oneof_; |
| const OneofDescriptor* oneof2_; |
| const FieldDescriptor* a_; |
| const FieldDescriptor* b_; |
| const FieldDescriptor* c_; |
| const FieldDescriptor* d_; |
| }; |
| |
| TEST_F(OneofDescriptorTest, Normal) { |
| EXPECT_EQ("foo", oneof_->name()); |
| EXPECT_EQ("garply.TestOneof.foo", oneof_->full_name()); |
| EXPECT_EQ(0, oneof_->index()); |
| ASSERT_EQ(2, oneof_->field_count()); |
| EXPECT_EQ(b_, oneof_->field(0)); |
| EXPECT_EQ(c_, oneof_->field(1)); |
| EXPECT_TRUE(a_->containing_oneof() == nullptr); |
| EXPECT_EQ(oneof_, b_->containing_oneof()); |
| EXPECT_EQ(oneof_, c_->containing_oneof()); |
| } |
| |
| TEST_F(OneofDescriptorTest, FindByName) { |
| EXPECT_EQ(oneof_, oneof_message_->FindOneofByName("foo")); |
| EXPECT_EQ(oneof2_, oneof_message_->FindOneofByName("bar")); |
| EXPECT_TRUE(oneof_message_->FindOneofByName("no_such_oneof") == nullptr); |
| } |
| |
| TEST_F(OneofDescriptorTest, AbslStringifyWorks) { |
| EXPECT_THAT(absl::StrFormat("%v", *oneof_), HasSubstr(oneof_->name())); |
| } |
| |
| // =================================================================== |
| |
| class StylizedFieldNamesTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| FileDescriptorProto file; |
| file.set_name("foo.proto"); |
| |
| AddExtensionRange(AddMessage(&file, "ExtendableMessage"), 1, 1000); |
| |
| DescriptorProto* message = AddMessage(&file, "TestMessage"); |
| PROTOBUF_IGNORE_DEPRECATION_START |
| message->mutable_options()->set_deprecated_legacy_json_field_conflicts( |
| true); |
| PROTOBUF_IGNORE_DEPRECATION_STOP |
| AddField(message, "foo_foo", 1, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddField(message, "FooBar", 2, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddField(message, "fooBaz", 3, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddField(message, "fooFoo", 4, // Camel-case conflict with foo_foo. |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddField(message, "foobar", 5, // Lower-case conflict with FooBar. |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| |
| AddNestedExtension(message, "ExtendableMessage", "bar_foo", 1, |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddNestedExtension(message, "ExtendableMessage", "BarBar", 2, |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddNestedExtension(message, "ExtendableMessage", "BarBaz", 3, |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddNestedExtension(message, "ExtendableMessage", "barFoo", 4, // Conflict |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddNestedExtension(message, "ExtendableMessage", "barbar", 5, // Conflict |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| |
| AddExtension(&file, "ExtendableMessage", "baz_foo", 11, |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddExtension(&file, "ExtendableMessage", "BazBar", 12, |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddExtension(&file, "ExtendableMessage", "BazBaz", 13, |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddExtension(&file, "ExtendableMessage", "bazFoo", 14, // Conflict |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddExtension(&file, "ExtendableMessage", "bazbar", 15, // Conflict |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| |
| file_ = pool_.BuildFile(file); |
| ASSERT_TRUE(file_ != nullptr); |
| ASSERT_EQ(2, file_->message_type_count()); |
| message_ = file_->message_type(1); |
| ASSERT_EQ("TestMessage", message_->name()); |
| ASSERT_EQ(5, message_->field_count()); |
| ASSERT_EQ(5, message_->extension_count()); |
| ASSERT_EQ(5, file_->extension_count()); |
| } |
| |
| DescriptorPool pool_; |
| const FileDescriptor* file_; |
| const Descriptor* message_; |
| }; |
| |
| TEST_F(StylizedFieldNamesTest, LowercaseName) { |
| EXPECT_EQ("foo_foo", message_->field(0)->lowercase_name()); |
| EXPECT_EQ("foobar", message_->field(1)->lowercase_name()); |
| EXPECT_EQ("foobaz", message_->field(2)->lowercase_name()); |
| EXPECT_EQ("foofoo", message_->field(3)->lowercase_name()); |
| EXPECT_EQ("foobar", message_->field(4)->lowercase_name()); |
| |
| EXPECT_EQ("bar_foo", message_->extension(0)->lowercase_name()); |
| EXPECT_EQ("barbar", message_->extension(1)->lowercase_name()); |
| EXPECT_EQ("barbaz", message_->extension(2)->lowercase_name()); |
| EXPECT_EQ("barfoo", message_->extension(3)->lowercase_name()); |
| EXPECT_EQ("barbar", message_->extension(4)->lowercase_name()); |
| |
| EXPECT_EQ("baz_foo", file_->extension(0)->lowercase_name()); |
| EXPECT_EQ("bazbar", file_->extension(1)->lowercase_name()); |
| EXPECT_EQ("bazbaz", file_->extension(2)->lowercase_name()); |
| EXPECT_EQ("bazfoo", file_->extension(3)->lowercase_name()); |
| EXPECT_EQ("bazbar", file_->extension(4)->lowercase_name()); |
| } |
| |
| TEST_F(StylizedFieldNamesTest, CamelcaseName) { |
| EXPECT_EQ("fooFoo", message_->field(0)->camelcase_name()); |
| EXPECT_EQ("fooBar", message_->field(1)->camelcase_name()); |
| EXPECT_EQ("fooBaz", message_->field(2)->camelcase_name()); |
| EXPECT_EQ("fooFoo", message_->field(3)->camelcase_name()); |
| EXPECT_EQ("foobar", message_->field(4)->camelcase_name()); |
| |
| EXPECT_EQ("barFoo", message_->extension(0)->camelcase_name()); |
| EXPECT_EQ("barBar", message_->extension(1)->camelcase_name()); |
| EXPECT_EQ("barBaz", message_->extension(2)->camelcase_name()); |
| EXPECT_EQ("barFoo", message_->extension(3)->camelcase_name()); |
| EXPECT_EQ("barbar", message_->extension(4)->camelcase_name()); |
| |
| EXPECT_EQ("bazFoo", file_->extension(0)->camelcase_name()); |
| EXPECT_EQ("bazBar", file_->extension(1)->camelcase_name()); |
| EXPECT_EQ("bazBaz", file_->extension(2)->camelcase_name()); |
| EXPECT_EQ("bazFoo", file_->extension(3)->camelcase_name()); |
| EXPECT_EQ("bazbar", file_->extension(4)->camelcase_name()); |
| } |
| |
| TEST_F(StylizedFieldNamesTest, FindByLowercaseName) { |
| EXPECT_EQ(message_->field(0), message_->FindFieldByLowercaseName("foo_foo")); |
| EXPECT_THAT(message_->FindFieldByLowercaseName("foobar"), |
| AnyOf(message_->field(1), message_->field(4))); |
| EXPECT_EQ(message_->field(2), message_->FindFieldByLowercaseName("foobaz")); |
| EXPECT_TRUE(message_->FindFieldByLowercaseName("FooBar") == nullptr); |
| EXPECT_TRUE(message_->FindFieldByLowercaseName("fooBaz") == nullptr); |
| EXPECT_TRUE(message_->FindFieldByLowercaseName("bar_foo") == nullptr); |
| EXPECT_TRUE(message_->FindFieldByLowercaseName("nosuchfield") == nullptr); |
| |
| EXPECT_EQ(message_->extension(0), |
| message_->FindExtensionByLowercaseName("bar_foo")); |
| EXPECT_THAT(message_->FindExtensionByLowercaseName("barbar"), |
| AnyOf(message_->extension(1), message_->extension(4))); |
| EXPECT_EQ(message_->extension(2), |
| message_->FindExtensionByLowercaseName("barbaz")); |
| EXPECT_TRUE(message_->FindExtensionByLowercaseName("BarBar") == nullptr); |
| EXPECT_TRUE(message_->FindExtensionByLowercaseName("barBaz") == nullptr); |
| EXPECT_TRUE(message_->FindExtensionByLowercaseName("foo_foo") == nullptr); |
| EXPECT_TRUE(message_->FindExtensionByLowercaseName("nosuchfield") == nullptr); |
| |
| EXPECT_EQ(file_->extension(0), |
| file_->FindExtensionByLowercaseName("baz_foo")); |
| EXPECT_THAT(file_->FindExtensionByLowercaseName("bazbar"), |
| AnyOf(file_->extension(1), file_->extension(4))); |
| EXPECT_EQ(file_->extension(2), file_->FindExtensionByLowercaseName("bazbaz")); |
| EXPECT_TRUE(file_->FindExtensionByLowercaseName("BazBar") == nullptr); |
| EXPECT_TRUE(file_->FindExtensionByLowercaseName("bazBaz") == nullptr); |
| EXPECT_TRUE(file_->FindExtensionByLowercaseName("nosuchfield") == nullptr); |
| } |
| |
| TEST_F(StylizedFieldNamesTest, FindByCamelcaseName) { |
| // Conflict (here, foo_foo and fooFoo) always resolves to the field with |
| // the lower field number. |
| EXPECT_EQ(message_->field(0), message_->FindFieldByCamelcaseName("fooFoo")); |
| EXPECT_EQ(message_->field(1), message_->FindFieldByCamelcaseName("fooBar")); |
| EXPECT_EQ(message_->field(2), message_->FindFieldByCamelcaseName("fooBaz")); |
| EXPECT_TRUE(message_->FindFieldByCamelcaseName("foo_foo") == nullptr); |
| EXPECT_TRUE(message_->FindFieldByCamelcaseName("FooBar") == nullptr); |
| EXPECT_TRUE(message_->FindFieldByCamelcaseName("barFoo") == nullptr); |
| EXPECT_TRUE(message_->FindFieldByCamelcaseName("nosuchfield") == nullptr); |
| |
| // Conflict (here, bar_foo and barFoo) always resolves to the field with |
| // the lower field number. |
| EXPECT_EQ(message_->extension(0), |
| message_->FindExtensionByCamelcaseName("barFoo")); |
| EXPECT_EQ(message_->extension(1), |
| message_->FindExtensionByCamelcaseName("barBar")); |
| EXPECT_EQ(message_->extension(2), |
| message_->FindExtensionByCamelcaseName("barBaz")); |
| EXPECT_TRUE(message_->FindExtensionByCamelcaseName("bar_foo") == nullptr); |
| EXPECT_TRUE(message_->FindExtensionByCamelcaseName("BarBar") == nullptr); |
| EXPECT_TRUE(message_->FindExtensionByCamelcaseName("fooFoo") == nullptr); |
| EXPECT_TRUE(message_->FindExtensionByCamelcaseName("nosuchfield") == nullptr); |
| |
| // Conflict (here, baz_foo and bazFoo) always resolves to the field with |
| // the lower field number. |
| EXPECT_EQ(file_->extension(0), file_->FindExtensionByCamelcaseName("bazFoo")); |
| EXPECT_EQ(file_->extension(1), file_->FindExtensionByCamelcaseName("bazBar")); |
| EXPECT_EQ(file_->extension(2), file_->FindExtensionByCamelcaseName("bazBaz")); |
| EXPECT_TRUE(file_->FindExtensionByCamelcaseName("baz_foo") == nullptr); |
| EXPECT_TRUE(file_->FindExtensionByCamelcaseName("BazBar") == nullptr); |
| EXPECT_TRUE(file_->FindExtensionByCamelcaseName("nosuchfield") == nullptr); |
| } |
| |
| // =================================================================== |
| |
| // Test enum descriptors. |
| class EnumDescriptorTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| // Build descriptors for the following definitions: |
| // |
| // // in "foo.proto" |
| // enum TestEnum { |
| // FOO = 1; |
| // BAR = 2; |
| // } |
| // |
| // // in "bar.proto" |
| // package corge.grault; |
| // enum TestEnum2 { |
| // FOO = 1; |
| // BAZ = 3; |
| // } |
| // |
| // TestEnum2 is primarily here to test FindValueByName and friends. |
| // All enums created from the same DescriptorPool share the same lookup |
| // table, so we need to insure that they don't interfere. |
| |
| // TestEnum |
| FileDescriptorProto foo_file; |
| foo_file.set_name("foo.proto"); |
| |
| EnumDescriptorProto* enum_proto = AddEnum(&foo_file, "TestEnum"); |
| AddEnumValue(enum_proto, "FOO", 1); |
| AddEnumValue(enum_proto, "BAR", 2); |
| |
| // TestEnum2 |
| FileDescriptorProto bar_file; |
| bar_file.set_name("bar.proto"); |
| bar_file.set_package("corge.grault"); |
| |
| EnumDescriptorProto* enum2_proto = AddEnum(&bar_file, "TestEnum2"); |
| AddEnumValue(enum2_proto, "FOO", 1); |
| AddEnumValue(enum2_proto, "BAZ", 3); |
| |
| // Build the descriptors and get the pointers. |
| foo_file_ = pool_.BuildFile(foo_file); |
| ASSERT_TRUE(foo_file_ != nullptr); |
| |
| bar_file_ = pool_.BuildFile(bar_file); |
| ASSERT_TRUE(bar_file_ != nullptr); |
| |
| ASSERT_EQ(1, foo_file_->enum_type_count()); |
| enum_ = foo_file_->enum_type(0); |
| |
| ASSERT_EQ(2, enum_->value_count()); |
| foo_ = enum_->value(0); |
| bar_ = enum_->value(1); |
| |
| ASSERT_EQ(1, bar_file_->enum_type_count()); |
| enum2_ = bar_file_->enum_type(0); |
| |
| ASSERT_EQ(2, enum2_->value_count()); |
| foo2_ = enum2_->value(0); |
| baz2_ = enum2_->value(1); |
| } |
| |
| DescriptorPool pool_; |
| |
| const FileDescriptor* foo_file_; |
| const FileDescriptor* bar_file_; |
| |
| const EnumDescriptor* enum_; |
| const EnumDescriptor* enum2_; |
| |
| const EnumValueDescriptor* foo_; |
| const EnumValueDescriptor* bar_; |
| |
| const EnumValueDescriptor* foo2_; |
| const EnumValueDescriptor* baz2_; |
| }; |
| |
| TEST_F(EnumDescriptorTest, Name) { |
| EXPECT_EQ("TestEnum", enum_->name()); |
| EXPECT_EQ("TestEnum", enum_->full_name()); |
| EXPECT_EQ(foo_file_, enum_->file()); |
| |
| EXPECT_EQ("TestEnum2", enum2_->name()); |
| EXPECT_EQ("corge.grault.TestEnum2", enum2_->full_name()); |
| EXPECT_EQ(bar_file_, enum2_->file()); |
| } |
| |
| TEST_F(EnumDescriptorTest, ContainingType) { |
| EXPECT_TRUE(enum_->containing_type() == nullptr); |
| EXPECT_TRUE(enum2_->containing_type() == nullptr); |
| } |
| |
| TEST_F(EnumDescriptorTest, ValuesByIndex) { |
| ASSERT_EQ(2, enum_->value_count()); |
| EXPECT_EQ(foo_, enum_->value(0)); |
| EXPECT_EQ(bar_, enum_->value(1)); |
| } |
| |
| TEST_F(EnumDescriptorTest, FindValueByName) { |
| EXPECT_EQ(foo_, enum_->FindValueByName("FOO")); |
| EXPECT_EQ(bar_, enum_->FindValueByName("BAR")); |
| EXPECT_EQ(foo2_, enum2_->FindValueByName("FOO")); |
| EXPECT_EQ(baz2_, enum2_->FindValueByName("BAZ")); |
| |
| EXPECT_TRUE(enum_->FindValueByName("NO_SUCH_VALUE") == nullptr); |
| EXPECT_TRUE(enum_->FindValueByName("BAZ") == nullptr); |
| EXPECT_TRUE(enum2_->FindValueByName("BAR") == nullptr); |
| } |
| |
| TEST_F(EnumDescriptorTest, FindValueByNumber) { |
| EXPECT_EQ(foo_, enum_->FindValueByNumber(1)); |
| EXPECT_EQ(bar_, enum_->FindValueByNumber(2)); |
| EXPECT_EQ(foo2_, enum2_->FindValueByNumber(1)); |
| EXPECT_EQ(baz2_, enum2_->FindValueByNumber(3)); |
| |
| EXPECT_TRUE(enum_->FindValueByNumber(416) == nullptr); |
| EXPECT_TRUE(enum_->FindValueByNumber(3) == nullptr); |
| EXPECT_TRUE(enum2_->FindValueByNumber(2) == nullptr); |
| } |
| |
| TEST_F(EnumDescriptorTest, ValueName) { |
| EXPECT_EQ("FOO", foo_->name()); |
| EXPECT_EQ("BAR", bar_->name()); |
| } |
| |
| TEST_F(EnumDescriptorTest, ValueFullName) { |
| EXPECT_EQ("FOO", foo_->full_name()); |
| EXPECT_EQ("BAR", bar_->full_name()); |
| EXPECT_EQ("corge.grault.FOO", foo2_->full_name()); |
| EXPECT_EQ("corge.grault.BAZ", baz2_->full_name()); |
| } |
| |
| TEST_F(EnumDescriptorTest, ValueIndex) { |
| EXPECT_EQ(0, foo_->index()); |
| EXPECT_EQ(1, bar_->index()); |
| } |
| |
| TEST_F(EnumDescriptorTest, ValueNumber) { |
| EXPECT_EQ(1, foo_->number()); |
| EXPECT_EQ(2, bar_->number()); |
| } |
| |
| TEST_F(EnumDescriptorTest, ValueType) { |
| EXPECT_EQ(enum_, foo_->type()); |
| EXPECT_EQ(enum_, bar_->type()); |
| EXPECT_EQ(enum2_, foo2_->type()); |
| EXPECT_EQ(enum2_, baz2_->type()); |
| } |
| |
| TEST_F(EnumDescriptorTest, IsClosed) { |
| // enum_ is proto2. |
| EXPECT_TRUE(enum_->is_closed()); |
| |
| // Make a proto3 version of enum_. |
| FileDescriptorProto foo_file3; |
| foo_file_->CopyTo(&foo_file3); |
| foo_file3.set_syntax("proto3"); |
| |
| // Make this valid proto3 by making the first enum value be zero. |
| foo_file3.mutable_enum_type(0)->mutable_value(0)->set_number(0); |
| |
| DescriptorPool pool3; |
| const EnumDescriptor* enum3 = pool3.BuildFile(foo_file3)->enum_type(0); |
| EXPECT_FALSE(enum3->is_closed()); |
| } |
| |
| TEST_F(EnumDescriptorTest, AbslStringifyWorks) { |
| EXPECT_THAT(absl::StrFormat("%v", *enum_), HasSubstr(enum_->full_name())); |
| EXPECT_THAT(absl::StrFormat("%v", *foo_), HasSubstr(foo_->name())); |
| } |
| |
| // =================================================================== |
| |
| // Test service descriptors. |
| class ServiceDescriptorTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| // Build descriptors for the following messages and service: |
| // // in "foo.proto" |
| // message FooRequest {} |
| // message FooResponse {} |
| // message BarRequest {} |
| // message BarResponse {} |
| // message BazRequest {} |
| // message BazResponse {} |
| // |
| // service TestService { |
| // rpc Foo(FooRequest) returns (FooResponse); |
| // rpc Bar(BarRequest) returns (BarResponse); |
| // } |
| // |
| // // in "bar.proto" |
| // package corge.grault |
| // service TestService2 { |
| // rpc Foo(FooRequest) returns (FooResponse); |
| // rpc Baz(BazRequest) returns (BazResponse); |
| // } |
| |
| FileDescriptorProto foo_file; |
| foo_file.set_name("foo.proto"); |
| |
| AddMessage(&foo_file, "FooRequest"); |
| AddMessage(&foo_file, "FooResponse"); |
| AddMessage(&foo_file, "BarRequest"); |
| AddMessage(&foo_file, "BarResponse"); |
| AddMessage(&foo_file, "BazRequest"); |
| AddMessage(&foo_file, "BazResponse"); |
| |
| ServiceDescriptorProto* service = AddService(&foo_file, "TestService"); |
| AddMethod(service, "Foo", "FooRequest", "FooResponse"); |
| AddMethod(service, "Bar", "BarRequest", "BarResponse"); |
| |
| FileDescriptorProto bar_file; |
| bar_file.set_name("bar.proto"); |
| bar_file.set_package("corge.grault"); |
| bar_file.add_dependency("foo.proto"); |
| |
| ServiceDescriptorProto* service2 = AddService(&bar_file, "TestService2"); |
| AddMethod(service2, "Foo", "FooRequest", "FooResponse"); |
| AddMethod(service2, "Baz", "BazRequest", "BazResponse"); |
| |
| // Build the descriptors and get the pointers. |
| foo_file_ = pool_.BuildFile(foo_file); |
| ASSERT_TRUE(foo_file_ != nullptr); |
| |
| bar_file_ = pool_.BuildFile(bar_file); |
| ASSERT_TRUE(bar_file_ != nullptr); |
| |
| ASSERT_EQ(6, foo_file_->message_type_count()); |
| foo_request_ = foo_file_->message_type(0); |
| foo_response_ = foo_file_->message_type(1); |
| bar_request_ = foo_file_->message_type(2); |
| bar_response_ = foo_file_->message_type(3); |
| baz_request_ = foo_file_->message_type(4); |
| baz_response_ = foo_file_->message_type(5); |
| |
| ASSERT_EQ(1, foo_file_->service_count()); |
| service_ = foo_file_->service(0); |
| |
| ASSERT_EQ(2, service_->method_count()); |
| foo_ = service_->method(0); |
| bar_ = service_->method(1); |
| |
| ASSERT_EQ(1, bar_file_->service_count()); |
| service2_ = bar_file_->service(0); |
| |
| ASSERT_EQ(2, service2_->method_count()); |
| foo2_ = service2_->method(0); |
| baz2_ = service2_->method(1); |
| } |
| |
| DescriptorPool pool_; |
| |
| const FileDescriptor* foo_file_; |
| const FileDescriptor* bar_file_; |
| |
| const Descriptor* foo_request_; |
| const Descriptor* foo_response_; |
| const Descriptor* bar_request_; |
| const Descriptor* bar_response_; |
| const Descriptor* baz_request_; |
| const Descriptor* baz_response_; |
| |
| const ServiceDescriptor* service_; |
| const ServiceDescriptor* service2_; |
| |
| const MethodDescriptor* foo_; |
| const MethodDescriptor* bar_; |
| |
| const MethodDescriptor* foo2_; |
| const MethodDescriptor* baz2_; |
| }; |
| |
| TEST_F(ServiceDescriptorTest, Name) { |
| EXPECT_EQ("TestService", service_->name()); |
| EXPECT_EQ("TestService", service_->full_name()); |
| EXPECT_EQ(foo_file_, service_->file()); |
| |
| EXPECT_EQ("TestService2", service2_->name()); |
| EXPECT_EQ("corge.grault.TestService2", service2_->full_name()); |
| EXPECT_EQ(bar_file_, service2_->file()); |
| } |
| |
| TEST_F(ServiceDescriptorTest, MethodsByIndex) { |
| ASSERT_EQ(2, service_->method_count()); |
| EXPECT_EQ(foo_, service_->method(0)); |
| EXPECT_EQ(bar_, service_->method(1)); |
| } |
| |
| TEST_F(ServiceDescriptorTest, FindMethodByName) { |
| EXPECT_EQ(foo_, service_->FindMethodByName("Foo")); |
| EXPECT_EQ(bar_, service_->FindMethodByName("Bar")); |
| EXPECT_EQ(foo2_, service2_->FindMethodByName("Foo")); |
| EXPECT_EQ(baz2_, service2_->FindMethodByName("Baz")); |
| |
| EXPECT_TRUE(service_->FindMethodByName("NoSuchMethod") == nullptr); |
| EXPECT_TRUE(service_->FindMethodByName("Baz") == nullptr); |
| EXPECT_TRUE(service2_->FindMethodByName("Bar") == nullptr); |
| } |
| |
| TEST_F(ServiceDescriptorTest, MethodName) { |
| EXPECT_EQ("Foo", foo_->name()); |
| EXPECT_EQ("Bar", bar_->name()); |
| } |
| TEST_F(ServiceDescriptorTest, MethodFullName) { |
| EXPECT_EQ("TestService.Foo", foo_->full_name()); |
| EXPECT_EQ("TestService.Bar", bar_->full_name()); |
| EXPECT_EQ("corge.grault.TestService2.Foo", foo2_->full_name()); |
| EXPECT_EQ("corge.grault.TestService2.Baz", baz2_->full_name()); |
| } |
| |
| TEST_F(ServiceDescriptorTest, MethodIndex) { |
| EXPECT_EQ(0, foo_->index()); |
| EXPECT_EQ(1, bar_->index()); |
| } |
| |
| TEST_F(ServiceDescriptorTest, MethodParent) { |
| EXPECT_EQ(service_, foo_->service()); |
| EXPECT_EQ(service_, bar_->service()); |
| } |
| |
| TEST_F(ServiceDescriptorTest, MethodInputType) { |
| EXPECT_EQ(foo_request_, foo_->input_type()); |
| EXPECT_EQ(bar_request_, bar_->input_type()); |
| } |
| |
| TEST_F(ServiceDescriptorTest, MethodOutputType) { |
| EXPECT_EQ(foo_response_, foo_->output_type()); |
| EXPECT_EQ(bar_response_, bar_->output_type()); |
| } |
| |
| TEST_F(ServiceDescriptorTest, AbslStringifyWorks) { |
| EXPECT_THAT(absl::StrFormat("%v", *service_), HasSubstr(service_->name())); |
| EXPECT_THAT(absl::StrFormat("%v", *foo_), HasSubstr(foo_->name())); |
| } |
| |
| // =================================================================== |
| |
| // Test nested types. |
| class NestedDescriptorTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| // Build descriptors for the following definitions: |
| // |
| // // in "foo.proto" |
| // message TestMessage { |
| // message Foo {} |
| // message Bar {} |
| // enum Baz { A = 1; } |
| // enum Moo { B = 1; } |
| // } |
| // |
| // // in "bar.proto" |
| // package corge.grault; |
| // message TestMessage2 { |
| // message Foo {} |
| // message Baz {} |
| // enum Moo { A = 1; } |
| // enum Mooo { C = 1; } |
| // } |
| // |
| // TestMessage2 is primarily here to test FindNestedTypeByName and friends. |
| // All messages created from the same DescriptorPool share the same lookup |
| // table, so we need to insure that they don't interfere. |
| // |
| // We add enum values to the enums in order to test searching for enum |
| // values across a message's scope. |
| |
| FileDescriptorProto foo_file; |
| foo_file.set_name("foo.proto"); |
| |
| DescriptorProto* message = AddMessage(&foo_file, "TestMessage"); |
| AddNestedMessage(message, "Foo"); |
| AddNestedMessage(message, "Bar"); |
| EnumDescriptorProto* baz = AddNestedEnum(message, "Baz"); |
| AddEnumValue(baz, "A", 1); |
| EnumDescriptorProto* moo = AddNestedEnum(message, "Moo"); |
| AddEnumValue(moo, "B", 1); |
| |
| FileDescriptorProto bar_file; |
| bar_file.set_name("bar.proto"); |
| bar_file.set_package("corge.grault"); |
| |
| DescriptorProto* message2 = AddMessage(&bar_file, "TestMessage2"); |
| AddNestedMessage(message2, "Foo"); |
| AddNestedMessage(message2, "Baz"); |
| EnumDescriptorProto* moo2 = AddNestedEnum(message2, "Moo"); |
| AddEnumValue(moo2, "A", 1); |
| EnumDescriptorProto* mooo2 = AddNestedEnum(message2, "Mooo"); |
| AddEnumValue(mooo2, "C", 1); |
| |
| // Build the descriptors and get the pointers. |
| foo_file_ = pool_.BuildFile(foo_file); |
| ASSERT_TRUE(foo_file_ != nullptr); |
| |
| bar_file_ = pool_.BuildFile(bar_file); |
| ASSERT_TRUE(bar_file_ != nullptr); |
| |
| ASSERT_EQ(1, foo_file_->message_type_count()); |
| message_ = foo_file_->message_type(0); |
| |
| ASSERT_EQ(2, message_->nested_type_count()); |
| foo_ = message_->nested_type(0); |
| bar_ = message_->nested_type(1); |
| |
| ASSERT_EQ(2, message_->enum_type_count()); |
| baz_ = message_->enum_type(0); |
| moo_ = message_->enum_type(1); |
| |
| ASSERT_EQ(1, baz_->value_count()); |
| a_ = baz_->value(0); |
| ASSERT_EQ(1, moo_->value_count()); |
| b_ = moo_->value(0); |
| |
| ASSERT_EQ(1, bar_file_->message_type_count()); |
| message2_ = bar_file_->message_type(0); |
| |
| ASSERT_EQ(2, message2_->nested_type_count()); |
| foo2_ = message2_->nested_type(0); |
| baz2_ = message2_->nested_type(1); |
| |
| ASSERT_EQ(2, message2_->enum_type_count()); |
| moo2_ = message2_->enum_type(0); |
| mooo2_ = message2_->enum_type(1); |
| |
| ASSERT_EQ(1, moo2_->value_count()); |
| a2_ = moo2_->value(0); |
| ASSERT_EQ(1, mooo2_->value_count()); |
| c2_ = mooo2_->value(0); |
| } |
| |
| DescriptorPool pool_; |
| |
| const FileDescriptor* foo_file_; |
| const FileDescriptor* bar_file_; |
| |
| const Descriptor* message_; |
| const Descriptor* message2_; |
| |
| const Descriptor* foo_; |
| const Descriptor* bar_; |
| const EnumDescriptor* baz_; |
| const EnumDescriptor* moo_; |
| const EnumValueDescriptor* a_; |
| const EnumValueDescriptor* b_; |
| |
| const Descriptor* foo2_; |
| const Descriptor* baz2_; |
| const EnumDescriptor* moo2_; |
| const EnumDescriptor* mooo2_; |
| const EnumValueDescriptor* a2_; |
| const EnumValueDescriptor* c2_; |
| }; |
| |
| TEST_F(NestedDescriptorTest, MessageName) { |
| EXPECT_EQ("Foo", foo_->name()); |
| EXPECT_EQ("Bar", bar_->name()); |
| EXPECT_EQ("Foo", foo2_->name()); |
| EXPECT_EQ("Baz", baz2_->name()); |
| |
| EXPECT_EQ("TestMessage.Foo", foo_->full_name()); |
| EXPECT_EQ("TestMessage.Bar", bar_->full_name()); |
| EXPECT_EQ("corge.grault.TestMessage2.Foo", foo2_->full_name()); |
| EXPECT_EQ("corge.grault.TestMessage2.Baz", baz2_->full_name()); |
| } |
| |
| TEST_F(NestedDescriptorTest, MessageContainingType) { |
| EXPECT_EQ(message_, foo_->containing_type()); |
| EXPECT_EQ(message_, bar_->containing_type()); |
| EXPECT_EQ(message2_, foo2_->containing_type()); |
| EXPECT_EQ(message2_, baz2_->containing_type()); |
| } |
| |
| TEST_F(NestedDescriptorTest, NestedMessagesByIndex) { |
| ASSERT_EQ(2, message_->nested_type_count()); |
| EXPECT_EQ(foo_, message_->nested_type(0)); |
| EXPECT_EQ(bar_, message_->nested_type(1)); |
| } |
| |
| TEST_F(NestedDescriptorTest, FindFieldByNameDoesntFindNestedTypes) { |
| EXPECT_TRUE(message_->FindFieldByName("Foo") == nullptr); |
| EXPECT_TRUE(message_->FindFieldByName("Moo") == nullptr); |
| EXPECT_TRUE(message_->FindExtensionByName("Foo") == nullptr); |
| EXPECT_TRUE(message_->FindExtensionByName("Moo") == nullptr); |
| } |
| |
| TEST_F(NestedDescriptorTest, FindNestedTypeByName) { |
| EXPECT_EQ(foo_, message_->FindNestedTypeByName("Foo")); |
| EXPECT_EQ(bar_, message_->FindNestedTypeByName("Bar")); |
| EXPECT_EQ(foo2_, message2_->FindNestedTypeByName("Foo")); |
| EXPECT_EQ(baz2_, message2_->FindNestedTypeByName("Baz")); |
| |
| EXPECT_TRUE(message_->FindNestedTypeByName("NoSuchType") == nullptr); |
| EXPECT_TRUE(message_->FindNestedTypeByName("Baz") == nullptr); |
| EXPECT_TRUE(message2_->FindNestedTypeByName("Bar") == nullptr); |
| |
| EXPECT_TRUE(message_->FindNestedTypeByName("Moo") == nullptr); |
| } |
| |
| TEST_F(NestedDescriptorTest, EnumName) { |
| EXPECT_EQ("Baz", baz_->name()); |
| EXPECT_EQ("Moo", moo_->name()); |
| EXPECT_EQ("Moo", moo2_->name()); |
| EXPECT_EQ("Mooo", mooo2_->name()); |
| |
| EXPECT_EQ("TestMessage.Baz", baz_->full_name()); |
| EXPECT_EQ("TestMessage.Moo", moo_->full_name()); |
| EXPECT_EQ("corge.grault.TestMessage2.Moo", moo2_->full_name()); |
| EXPECT_EQ("corge.grault.TestMessage2.Mooo", mooo2_->full_name()); |
| } |
| |
| TEST_F(NestedDescriptorTest, EnumContainingType) { |
| EXPECT_EQ(message_, baz_->containing_type()); |
| EXPECT_EQ(message_, moo_->containing_type()); |
| EXPECT_EQ(message2_, moo2_->containing_type()); |
| EXPECT_EQ(message2_, mooo2_->containing_type()); |
| } |
| |
| TEST_F(NestedDescriptorTest, NestedEnumsByIndex) { |
| ASSERT_EQ(2, message_->nested_type_count()); |
| EXPECT_EQ(foo_, message_->nested_type(0)); |
| EXPECT_EQ(bar_, message_->nested_type(1)); |
| } |
| |
| TEST_F(NestedDescriptorTest, FindEnumTypeByName) { |
| EXPECT_EQ(baz_, message_->FindEnumTypeByName("Baz")); |
| EXPECT_EQ(moo_, message_->FindEnumTypeByName("Moo")); |
| EXPECT_EQ(moo2_, message2_->FindEnumTypeByName("Moo")); |
| EXPECT_EQ(mooo2_, message2_->FindEnumTypeByName("Mooo")); |
| |
| EXPECT_TRUE(message_->FindEnumTypeByName("NoSuchType") == nullptr); |
| EXPECT_TRUE(message_->FindEnumTypeByName("Mooo") == nullptr); |
| EXPECT_TRUE(message2_->FindEnumTypeByName("Baz") == nullptr); |
| |
| EXPECT_TRUE(message_->FindEnumTypeByName("Foo") == nullptr); |
| } |
| |
| TEST_F(NestedDescriptorTest, FindEnumValueByName) { |
| EXPECT_EQ(a_, message_->FindEnumValueByName("A")); |
| EXPECT_EQ(b_, message_->FindEnumValueByName("B")); |
| EXPECT_EQ(a2_, message2_->FindEnumValueByName("A")); |
| EXPECT_EQ(c2_, message2_->FindEnumValueByName("C")); |
| |
| EXPECT_TRUE(message_->FindEnumValueByName("NO_SUCH_VALUE") == nullptr); |
| EXPECT_TRUE(message_->FindEnumValueByName("C") == nullptr); |
| EXPECT_TRUE(message2_->FindEnumValueByName("B") == nullptr); |
| |
| EXPECT_TRUE(message_->FindEnumValueByName("Foo") == nullptr); |
| } |
| |
| // =================================================================== |
| |
| // Test extensions. |
| class ExtensionDescriptorTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| // Build descriptors for the following definitions: |
| // |
| // enum Baz {} |
| // message Moo {} |
| // |
| // message Foo { |
| // extensions 10 to 19; |
| // extensions 30 to 39; |
| // } |
| // extend Foo { |
| // optional int32 foo_int32 = 10; |
| // } |
| // extend Foo { |
| // repeated TestEnum foo_enum = 19; |
| // } |
| // message Bar { |
| // optional int32 non_ext_int32 = 1; |
| // extend Foo { |
| // optional Moo foo_message = 30; |
| // repeated Moo foo_group = 39; // (but internally set to TYPE_GROUP) |
| // } |
| // } |
| |
| FileDescriptorProto foo_file; |
| foo_file.set_name("foo.proto"); |
| |
| AddEmptyEnum(&foo_file, "Baz"); |
| AddMessage(&foo_file, "Moo"); |
| |
| DescriptorProto* foo = AddMessage(&foo_file, "Foo"); |
| AddExtensionRange(foo, 10, 20); |
| AddExtensionRange(foo, 30, 40); |
| |
| AddExtension(&foo_file, "Foo", "foo_int32", 10, |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddExtension(&foo_file, "Foo", "foo_enum", 19, |
| FieldDescriptorProto::LABEL_REPEATED, |
| FieldDescriptorProto::TYPE_ENUM) |
| ->set_type_name("Baz"); |
| |
| DescriptorProto* bar = AddMessage(&foo_file, "Bar"); |
| AddField(bar, "non_ext_int32", 1, FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| AddNestedExtension(bar, "Foo", "foo_message", 30, |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_MESSAGE) |
| ->set_type_name("Moo"); |
| AddNestedExtension(bar, "Foo", "foo_group", 39, |
| FieldDescriptorProto::LABEL_REPEATED, |
| FieldDescriptorProto::TYPE_GROUP) |
| ->set_type_name("Moo"); |
| |
| // Build the descriptors and get the pointers. |
| foo_file_ = pool_.BuildFile(foo_file); |
| ASSERT_TRUE(foo_file_ != nullptr); |
| |
| ASSERT_EQ(1, foo_file_->enum_type_count()); |
| baz_ = foo_file_->enum_type(0); |
| |
| ASSERT_EQ(3, foo_file_->message_type_count()); |
| moo_ = foo_file_->message_type(0); |
| foo_ = foo_file_->message_type(1); |
| bar_ = foo_file_->message_type(2); |
| } |
| |
| DescriptorPool pool_; |
| |
| const FileDescriptor* foo_file_; |
| |
| const Descriptor* foo_; |
| const Descriptor* bar_; |
| const EnumDescriptor* baz_; |
| const Descriptor* moo_; |
| }; |
| |
| TEST_F(ExtensionDescriptorTest, ExtensionRanges) { |
| EXPECT_EQ(0, bar_->extension_range_count()); |
| ASSERT_EQ(2, foo_->extension_range_count()); |
| |
| EXPECT_EQ(10, foo_->extension_range(0)->start_number()); |
| EXPECT_EQ(30, foo_->extension_range(1)->start_number()); |
| |
| EXPECT_EQ(20, foo_->extension_range(0)->end_number()); |
| EXPECT_EQ(40, foo_->extension_range(1)->end_number()); |
| } |
| |
| TEST_F(ExtensionDescriptorTest, Extensions) { |
| EXPECT_EQ(0, foo_->extension_count()); |
| ASSERT_EQ(2, foo_file_->extension_count()); |
| ASSERT_EQ(2, bar_->extension_count()); |
| |
| EXPECT_TRUE(foo_file_->extension(0)->is_extension()); |
| EXPECT_TRUE(foo_file_->extension(1)->is_extension()); |
| EXPECT_TRUE(bar_->extension(0)->is_extension()); |
| EXPECT_TRUE(bar_->extension(1)->is_extension()); |
| |
| EXPECT_EQ("foo_int32", foo_file_->extension(0)->name()); |
| EXPECT_EQ("foo_enum", foo_file_->extension(1)->name()); |
| EXPECT_EQ("foo_message", bar_->extension(0)->name()); |
| EXPECT_EQ("foo_group", bar_->extension(1)->name()); |
| |
| EXPECT_EQ(10, foo_file_->extension(0)->number()); |
| EXPECT_EQ(19, foo_file_->extension(1)->number()); |
| EXPECT_EQ(30, bar_->extension(0)->number()); |
| EXPECT_EQ(39, bar_->extension(1)->number()); |
| |
| EXPECT_EQ(FieldDescriptor::TYPE_INT32, foo_file_->extension(0)->type()); |
| EXPECT_EQ(FieldDescriptor::TYPE_ENUM, foo_file_->extension(1)->type()); |
| EXPECT_EQ(FieldDescriptor::TYPE_MESSAGE, bar_->extension(0)->type()); |
| EXPECT_EQ(FieldDescriptor::TYPE_GROUP, bar_->extension(1)->type()); |
| |
| EXPECT_EQ(baz_, foo_file_->extension(1)->enum_type()); |
| EXPECT_EQ(moo_, bar_->extension(0)->message_type()); |
| EXPECT_EQ(moo_, bar_->extension(1)->message_type()); |
| |
| EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, foo_file_->extension(0)->label()); |
| EXPECT_EQ(FieldDescriptor::LABEL_REPEATED, foo_file_->extension(1)->label()); |
| EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, bar_->extension(0)->label()); |
| EXPECT_EQ(FieldDescriptor::LABEL_REPEATED, bar_->extension(1)->label()); |
| |
| EXPECT_EQ(foo_, foo_file_->extension(0)->containing_type()); |
| EXPECT_EQ(foo_, foo_file_->extension(1)->containing_type()); |
| EXPECT_EQ(foo_, bar_->extension(0)->containing_type()); |
| EXPECT_EQ(foo_, bar_->extension(1)->containing_type()); |
| |
| EXPECT_TRUE(foo_file_->extension(0)->extension_scope() == nullptr); |
| EXPECT_TRUE(foo_file_->extension(1)->extension_scope() == nullptr); |
| EXPECT_EQ(bar_, bar_->extension(0)->extension_scope()); |
| EXPECT_EQ(bar_, bar_->extension(1)->extension_scope()); |
| } |
| |
| TEST_F(ExtensionDescriptorTest, IsExtensionNumber) { |
| EXPECT_FALSE(foo_->IsExtensionNumber(9)); |
| EXPECT_TRUE(foo_->IsExtensionNumber(10)); |
| EXPECT_TRUE(foo_->IsExtensionNumber(19)); |
| EXPECT_FALSE(foo_->IsExtensionNumber(20)); |
| EXPECT_FALSE(foo_->IsExtensionNumber(29)); |
| EXPECT_TRUE(foo_->IsExtensionNumber(30)); |
| EXPECT_TRUE(foo_->IsExtensionNumber(39)); |
| EXPECT_FALSE(foo_->IsExtensionNumber(40)); |
| } |
| |
| TEST_F(ExtensionDescriptorTest, FindExtensionByName) { |
| // Note that FileDescriptor::FindExtensionByName() is tested by |
| // FileDescriptorTest. |
| ASSERT_EQ(2, bar_->extension_count()); |
| |
| EXPECT_EQ(bar_->extension(0), bar_->FindExtensionByName("foo_message")); |
| EXPECT_EQ(bar_->extension(1), bar_->FindExtensionByName("foo_group")); |
| |
| EXPECT_TRUE(bar_->FindExtensionByName("no_such_extension") == nullptr); |
| EXPECT_TRUE(foo_->FindExtensionByName("foo_int32") == nullptr); |
| EXPECT_TRUE(foo_->FindExtensionByName("foo_message") == nullptr); |
| } |
| |
| TEST_F(ExtensionDescriptorTest, FieldVsExtension) { |
| EXPECT_EQ(foo_->FindFieldByName("foo_message"), nullptr); |
| EXPECT_EQ(bar_->FindFieldByName("foo_message"), nullptr); |
| EXPECT_NE(bar_->FindFieldByName("non_ext_int32"), nullptr); |
| EXPECT_EQ(foo_->FindExtensionByName("foo_message"), nullptr); |
| EXPECT_NE(bar_->FindExtensionByName("foo_message"), nullptr); |
| EXPECT_EQ(bar_->FindExtensionByName("non_ext_int32"), nullptr); |
| } |
| |
| TEST_F(ExtensionDescriptorTest, FindExtensionByPrintableName) { |
| EXPECT_TRUE(pool_.FindExtensionByPrintableName(foo_, "no_such_extension") == |
| nullptr); |
| EXPECT_TRUE(pool_.FindExtensionByPrintableName(bar_, "no_such_extension") == |
| nullptr); |
| |
| ASSERT_FALSE(pool_.FindExtensionByPrintableName(foo_, "Bar.foo_message") == |
| nullptr); |
| ASSERT_FALSE(pool_.FindExtensionByPrintableName(foo_, "Bar.foo_group") == |
| nullptr); |
| EXPECT_TRUE(pool_.FindExtensionByPrintableName(bar_, "foo_message") == |
| nullptr); |
| EXPECT_TRUE(pool_.FindExtensionByPrintableName(bar_, "foo_group") == nullptr); |
| EXPECT_EQ(bar_->FindExtensionByName("foo_message"), |
| pool_.FindExtensionByPrintableName(foo_, "Bar.foo_message")); |
| EXPECT_EQ(bar_->FindExtensionByName("foo_group"), |
| pool_.FindExtensionByPrintableName(foo_, "Bar.foo_group")); |
| |
| ASSERT_FALSE(pool_.FindExtensionByPrintableName(foo_, "foo_int32") == |
| nullptr); |
| ASSERT_FALSE(pool_.FindExtensionByPrintableName(foo_, "foo_enum") == nullptr); |
| EXPECT_TRUE(pool_.FindExtensionByPrintableName(bar_, "foo_int32") == nullptr); |
| EXPECT_TRUE(pool_.FindExtensionByPrintableName(bar_, "foo_enum") == nullptr); |
| EXPECT_EQ(foo_file_->FindExtensionByName("foo_int32"), |
| pool_.FindExtensionByPrintableName(foo_, "foo_int32")); |
| EXPECT_EQ(foo_file_->FindExtensionByName("foo_enum"), |
| pool_.FindExtensionByPrintableName(foo_, "foo_enum")); |
| } |
| |
| TEST_F(ExtensionDescriptorTest, FindAllExtensions) { |
| std::vector<const FieldDescriptor*> extensions; |
| pool_.FindAllExtensions(foo_, &extensions); |
| ASSERT_EQ(4, extensions.size()); |
| EXPECT_EQ(10, extensions[0]->number()); |
| EXPECT_EQ(19, extensions[1]->number()); |
| EXPECT_EQ(30, extensions[2]->number()); |
| EXPECT_EQ(39, extensions[3]->number()); |
| } |
| |
| |
| TEST_F(ExtensionDescriptorTest, DuplicateFieldNumber) { |
| DescriptorPool pool; |
| FileDescriptorProto file_proto; |
| // Add "google/protobuf/descriptor.proto". |
| FileDescriptorProto::descriptor()->file()->CopyTo(&file_proto); |
| ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr); |
| // Add "foo.proto": |
| // import "google/protobuf/descriptor.proto"; |
| // extend google.protobuf.FieldOptions { |
| // optional int32 option1 = 1000; |
| // } |
| file_proto.Clear(); |
| file_proto.set_name("foo.proto"); |
| file_proto.add_dependency("google/protobuf/descriptor.proto"); |
| AddExtension(&file_proto, "google.protobuf.FieldOptions", "option1", 1000, |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr); |
| // Add "bar.proto": |
| // import "google/protobuf/descriptor.proto"; |
| // extend google.protobuf.FieldOptions { |
| // optional int32 option2 = 1000; |
| // } |
| file_proto.Clear(); |
| file_proto.set_name("bar.proto"); |
| file_proto.add_dependency("google/protobuf/descriptor.proto"); |
| AddExtension(&file_proto, "google.protobuf.FieldOptions", "option2", 1000, |
| FieldDescriptorProto::LABEL_OPTIONAL, |
| FieldDescriptorProto::TYPE_INT32); |
| // Currently we only generate a warning for conflicting extension numbers. |
| // TODO: Change it to an error. |
| ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr); |
| } |
| |
| // =================================================================== |
| |
| // Ensure that overlapping extension ranges are not allowed. |
| TEST(OverlappingExtensionRangeTest, ExtensionRangeInternal) { |
| // Build descriptors for the following definitions: |
| // |
| // message Foo { |
| // extensions 10 to 19; |
| // extensions 15; |
| // } |
| FileDescriptorProto foo_file; |
| foo_file.set_name("foo.proto"); |
| |
| DescriptorProto* foo = AddMessage(&foo_file, "Foo"); |
| AddExtensionRange(foo, 10, 20); |
| AddExtensionRange(foo, 15, 16); |
| |
| DescriptorPool pool; |
| MockErrorCollector error_collector; |
| // The extensions ranges are invalid, so the proto shouldn't build. |
| ASSERT_TRUE(pool.BuildFileCollectingErrors(foo_file, &error_collector) == |
| nullptr); |
| ASSERT_EQ( |
| "foo.proto: Foo: NUMBER: Extension range 15 to 15 overlaps with " |
| "already-defined range 10 to 19.\n", |
| error_collector.text_); |
| } |
| |
| TEST(OverlappingExtensionRangeTest, ExtensionRangeAfter) { |
| // Build descriptors for the following definitions: |
| // |
| // message Foo { |
| // extensions 10 to 19; |
| // extensions 15 to 24; |
| // } |
| FileDescriptorProto foo_file; |
| foo_file.set_name("foo.proto"); |
| |
| DescriptorProto* foo = AddMessage(&foo_file, "Foo"); |
| AddExtensionRange(foo, 10, 20); |
| AddExtensionRange(foo, 15, 25); |
| |
| DescriptorPool pool; |
| MockErrorCollector error_collector; |
| // The extensions ranges are invalid, so the proto shouldn't build. |
| ASSERT_TRUE(pool.BuildFileCollectingErrors(foo_file, &error_collector) == |
| nullptr); |
| ASSERT_EQ( |
| "foo.proto: Foo: NUMBER: Extension range 15 to 24 overlaps with " |
| "already-defined range 10 to 19.\n", |
| error_collector.text_); |
| } |
| |
| TEST(OverlappingExtensionRangeTest, ExtensionRangeBefore) { |
| // Build descriptors for the following definitions: |
| // |
| // message Foo { |
| // extensions 10 to 19; |
| // extensions 5 to 14; |
| // } |
| FileDescriptorProto foo_file; |
| foo_file.set_name("foo.proto"); |
| |
| DescriptorProto* foo = AddMessage(&foo_file, "Foo"); |
| AddExtensionRange(foo, 10, 20); |
| AddExtensionRange(foo, 5, 15); |
| |
| DescriptorPool pool; |
| MockErrorCollector error_collector; |
| // The extensions ranges are invalid, so the proto shouldn't build. |
| ASSERT_TRUE(pool.BuildFileCollectingErrors(foo_file, &error_collector) == |
| nullptr); |
| ASSERT_EQ( |
| "foo.proto: Foo: NUMBER: Extension range 5 to 14 overlaps with " |
| "already-defined range 10 to 19.\n", |
| error_collector.text_); |
| } |
| |
| // =================================================================== |
| |
| // Test reserved fields. |
| class ReservedDescriptorTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| // Build descriptors for the following definitions: |
| // |
| // message Foo { |
| // reserved 2, 9 to 11, 15; |
| // reserved "foo", "bar"; |
| // } |
| |
| FileDescriptorProto foo_file; |
| foo_file.set_name("foo.proto"); |
| |
| DescriptorProto* foo = AddMessage(&foo_file, "Foo"); |
| AddReservedRange(foo, 2, 3); |
| AddReservedRange(foo, 9, 12); |
| AddReservedRange(foo, 15, 16); |
| |
| foo->add_reserved_name("foo"); |
| foo->add_reserved_name("bar"); |
| |
| // Build the descriptors and get the pointers. |
| foo_file_ = pool_.BuildFile(foo_file); |
| ASSERT_TRUE(foo_file_ != nullptr); |
| |
| ASSERT_EQ(1, foo_file_->message_type_count()); |
| foo_ = foo_file_->message_type(0); |
| } |
| |
| DescriptorPool pool_; |
| const FileDescriptor* foo_file_; |
| const Descriptor* foo_; |
| }; |
| |
| TEST_F(ReservedDescriptorTest, ReservedRanges) { |
| ASSERT_EQ(3, foo_->reserved_range_count()); |
| |
| EXPECT_EQ(2, foo_->reserved_range(0)->start); |
| EXPECT_EQ(3, foo_->reserved_range(0)->end); |
| |
| EXPECT_EQ(9, foo_->reserved_range(1)->start); |
| EXPECT_EQ(12, foo_->reserved_range(1)->end); |
| |
| EXPECT_EQ(15, foo_->reserved_range(2)->start); |
| EXPECT_EQ(16, foo_->reserved_range(2)->end); |
| } |
| |
| TEST_F(ReservedDescriptorTest, IsReservedNumber) { |
| EXPECT_FALSE(foo_->IsReservedNumber(1)); |
| EXPECT_TRUE(foo_->IsReservedNumber(2)); |
| EXPECT_FALSE(foo_->IsReservedNumber(3)); |
| EXPECT_FALSE(foo_->IsReservedNumber(8)); |
| EXPECT_TRUE(foo_->IsReservedNumber(9)); |
| EXPECT_TRUE(foo_->IsReservedNumber(10)); |
| EXPECT_TRUE(foo_->IsReservedNumber(11)); |
| EXPECT_FALSE(foo_->IsReservedNumber(12)); |
| EXPECT_FALSE(foo_->IsReservedNumber(13)); |
| EXPECT_FALSE(foo_->IsReservedNumber(14)); |
| EXPECT_TRUE(foo_->IsReservedNumber(15)); |
| EXPECT_FALSE(foo_->IsReservedNumber(16)); |
| } |
| |
| TEST_F(ReservedDescriptorTest, ReservedNames) { |
| ASSERT_EQ(2, foo_->reserved_name_count()); |
| |
| EXPECT_EQ("foo", foo_->reserved_name(0)); |
| EXPECT_EQ("bar", foo_->reserved_name(1)); |
| } |
| |
| TEST_F(ReservedDescriptorTest, IsReservedName) { |
| EXPECT_TRUE(foo_->IsReservedName("foo")); |
| EXPECT_TRUE(foo_->IsReservedName("bar")); |
| EXPECT_FALSE(foo_->IsReservedName("baz")); |
| } |
| |
| // =================================================================== |
| |
| // Test reserved enum fields. |
| class ReservedEnumDescriptorTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| // 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_ != nullptr); |
| |
| 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. |
| const FieldDescriptor* GetFieldDescriptorOfType(FieldDescriptor::Type type) { |
| FileDescriptorProto file_proto; |
| file_proto.set_name("foo.proto"); |
| AddEmptyEnum(&file_proto, "DummyEnum"); |
| |
| DescriptorProto* message = AddMessage(&file_proto, "TestMessage"); |
| FieldDescriptorProto* field = AddField( |
| message, "foo", 1, FieldDescriptorProto::LABEL_OPTIONAL, |
| static_cast<FieldDescriptorProto::Type>(static_cast<int>(type))); |
| |
| if (type == FieldDescriptor::TYPE_MESSAGE || |
| type == FieldDescriptor::TYPE_GROUP) { |
| field->set_type_name("TestMessage"); |
| } else if (type == FieldDescriptor::TYPE_ENUM) { |
| field->set_type_name("DummyEnum"); |
| } |
| |
| // Build the descriptors and get the pointers. |
| pool_ = std::make_unique<DescriptorPool>(); |
| const FileDescriptor* file = pool_->BuildFile(file_proto); |
| |
| if (file != nullptr && file->message_type_count() == 1 && |
| file->message_type(0)->field_count() == 1) { |
| return file->message_type(0)->field(0); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| const char* GetTypeNameForFieldType(FieldDescriptor::Type type) { |
| const FieldDescriptor* field = GetFieldDescriptorOfType(type); |
| return field != nullptr ? field->type_name() : ""; |
| } |
| |
| FieldDescriptor::CppType GetCppTypeForFieldType(FieldDescriptor::Type type) { |
| const FieldDescriptor* field = GetFieldDescriptorOfType(type); |
| return field != nullptr ? field->cpp_type() |
| : static_cast<FieldDescriptor::CppType>(0); |
| } |
| |
| const char* GetCppTypeNameForFieldType(FieldDescriptor::Type type) { |
| const FieldDescriptor* field = GetFieldDescriptorOfType(type); |
| return field != nullptr ? field->cpp_type_name() : ""; |
| } |
| |
| const Descriptor* GetMessageDescriptorForFieldType( |
| FieldDescriptor::Type type) { |
| const FieldDescriptor* field = GetFieldDescriptorOfType(type); |
| return field != nullptr ? field->message_type() : nullptr; |
| } |
| |
| const EnumDescriptor* GetEnumDescriptorForFieldType( |
| FieldDescriptor::Type type) { |
| const FieldDescriptor* field = GetFieldDescriptorOfType(type); |
| return field != nullptr ? field->enum_type() : nullptr; |
| } |
| |
| std::unique_ptr<DescriptorPool> pool_; |
| }; |
| |
| TEST_F(MiscTest, TypeNames) { |
| // Test that correct type names are returned. |
| |
| typedef FieldDescriptor FD; // avoid ugly line wrapping |
| |
| EXPECT_STREQ("double", GetTypeNameForFieldType(FD::TYPE_DOUBLE)); |
| EXPECT_STREQ("float", GetTypeNameForFieldType(FD::TYPE_FLOAT)); |
| EXPECT_STREQ("int64", GetTypeNameForFieldType(FD::TYPE_INT64)); |
| EXPECT_STREQ("uint64", GetTypeNameForFieldType(FD::TYPE_UINT64)); |
| EXPECT_STREQ("int32", GetTypeNameForFieldType(FD::TYPE_INT32)); |
| EXPECT_STREQ("fixed64", GetTypeNameForFieldType(FD::TYPE_FIXED64)); |
| EXPECT_STREQ("fixed32", GetTypeNameForFieldType(FD::TYPE_FIXED32)); |
| EXPECT_STREQ("bool", GetTypeNameForFieldType(FD::TYPE_BOOL)); |
| EXPECT_STREQ("string", GetTypeNameForFieldType(FD::TYPE_STRING)); |
| EXPECT_STREQ("group", GetTypeNameForFieldType(FD::TYPE_GROUP)); |
| EXPECT_STREQ("message", GetTypeNameForFieldType(FD::TYPE_MESSAGE)); |
| EXPECT_STREQ("bytes", GetTypeNameForFieldType(FD::TYPE_BYTES)); |
| EXPECT_STREQ("uint32", GetTypeNameForFieldType(FD::TYPE_UINT32)); |
| EXPECT_STREQ("enum", GetTypeNameForFieldType(FD::TYPE_ENUM)); |
| EXPECT_STREQ("sfixed32", GetTypeNameForFieldType(FD::TYPE_SFIXED32)); |
| EXPECT_STREQ("sfixed64", GetTypeNameForFieldType(FD::TYPE_SFIXED64)); |
| EXPECT_STREQ("sint32", GetTypeNameForFieldType(FD::TYPE_SINT32)); |
| EXPECT_STREQ("sint64", GetTypeNameForFieldType(FD::TYPE_SINT64)); |
| } |
| |
| TEST_F(MiscTest, StaticTypeNames) { |
| // Test that correct type names are returned. |
| |
| typedef FieldDescriptor FD; // avoid ugly line wrapping |
| |
| EXPECT_STREQ("double", FD::TypeName(FD::TYPE_DOUBLE)); |
| EXPECT_STREQ("float", FD::TypeName(FD::TYPE_FLOAT)); |
| EXPECT_STREQ("int64", FD::TypeName(FD::TYPE_INT64)); |
| EXPECT_STREQ("uint64", FD::TypeName(FD::TYPE_UINT64)); |
| EXPECT_STREQ("int32", FD::TypeName(FD::TYPE_INT32)); |
| EXPECT_STREQ("fixed64", FD::TypeName(FD::TYPE_FIXED64)); |
| EXPECT_STREQ("fixed32", FD::TypeName(FD::TYPE_FIXED32)); |
| EXPECT_STREQ("bool", FD::TypeName(FD::TYPE_BOOL)); |
| EXPECT_STREQ("string", FD::TypeName(FD::TYPE_STRING)); |
| EXPECT_STREQ("group", FD::TypeName(FD::TYPE_GROUP)); |
| EXPECT_STREQ("message", FD::TypeName(FD::TYPE_MESSAGE)); |
| EXPECT_STREQ("bytes", FD::TypeName(FD::TYPE_BYTES)); |
| EXPECT_STREQ("uint32", FD::TypeName(FD::TYPE_UINT32)); |
| EXPECT_STREQ("enum", FD::TypeName(FD::TYPE_ENUM)); |
| EXPECT_STREQ("sfixed32", FD::TypeName(FD::TYPE_SFIXED32)); |
| EXPECT_STREQ("sfixed64", FD::TypeName(FD::TYPE_SFIXED64)); |
| EXPECT_STREQ("sint32", FD::TypeName(FD::TYPE_SINT32)); |
| EXPECT_STREQ("sint64", FD::TypeName(FD::TYPE_SINT64)); |
| } |
| |
| TEST_F(MiscTest, CppTypes) { |
| // Test that CPP types are assigned correctly. |
| |
| typedef FieldDescriptor FD; // avoid ugly line wrapping |
| |
| EXPECT_EQ(FD::CPPTYPE_DOUBLE, GetCppTypeForFieldType(FD::TYPE_DOUBLE)); |
| EXPECT_EQ(FD::CPPTYPE_FLOAT, GetCppTypeForFieldType(FD::TYPE_FLOAT)); |
| EXPECT_EQ(FD::CPPTYPE_INT64, GetCppTypeForFieldType(FD::TYPE_INT64)); |
| EXPECT_EQ(FD::CPPTYPE_UINT64, GetCppTypeForFieldType(FD::TYPE_UINT64)); |
| EXPECT_EQ(FD::CPPTYPE_INT32, GetCppTypeForFieldType(FD::TYPE_INT32)); |
| EXPECT_EQ(FD::CPPTYPE_UINT64, GetCppTypeForFieldType(FD::TYPE_FIXED64)); |
| EXPECT_EQ(FD::CPPTYPE_UINT32, GetCppTypeForFieldType(FD::TYPE_FIXED32)); |
| EXPECT_EQ(FD::CPPTYPE_BOOL, GetCppTypeForFieldType(FD::TYPE_BOOL)); |
| EXPECT_EQ(FD::CPPTYPE_STRING, GetCppTypeForFieldType(FD::TYPE_STRING)); |
| EXPECT_EQ(FD::CPPTYPE_MESSAGE, GetCppTypeForFieldType(FD::TYPE_GROUP)); |
| EXPECT_EQ(FD::CPPTYPE_MESSAGE, GetCppTypeForFieldType(FD::TYPE_MESSAGE)); |
| EXPECT_EQ(FD::CPPTYPE_STRING, GetCppTypeForFieldType(FD::TYPE_BYTES |