blob: ad37a443bc69d3e3c8436c8c44b33db723257aa3 [file] [log] [blame]
// 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