GrpcPrint/PrintS/include/google/protobuf/compiler/code_generator_unittest.cc
2024-03-15 12:31:34 +08:00

314 lines
11 KiB
C++

// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "google/protobuf/compiler/code_generator.h"
#include <string>
#include "google/protobuf/descriptor.pb.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/log/absl_log.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/compiler/parser.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/unittest_features.pb.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace {
using ::testing::NotNull;
class TestGenerator : public CodeGenerator {
public:
bool Generate(const FileDescriptor* file, const std::string& parameter,
GeneratorContext* generator_context,
std::string* error) const override {
return true;
}
// Expose the protected methods for testing.
using CodeGenerator::GetRuntimeProto;
using CodeGenerator::GetSourceFeatures;
using CodeGenerator::GetSourceRawFeatures;
};
class SimpleErrorCollector : public io::ErrorCollector {
public:
void RecordError(int line, int column, absl::string_view message) override {
ABSL_LOG(ERROR) << absl::StrFormat("%d:%d:%s", line, column, message);
}
};
class CodeGeneratorTest : public ::testing::Test {
protected:
void SetUp() override {
ASSERT_THAT(BuildFile(DescriptorProto::descriptor()->file()), NotNull());
ASSERT_THAT(BuildFile(pb::TestMessage::descriptor()->file()), NotNull());
}
const FileDescriptor* BuildFile(absl::string_view schema) {
io::ArrayInputStream input_stream(schema.data(),
static_cast<int>(schema.size()));
SimpleErrorCollector error_collector;
io::Tokenizer tokenizer(&input_stream, &error_collector);
Parser parser;
parser.RecordErrorsTo(&error_collector);
FileDescriptorProto proto;
ABSL_CHECK(parser.Parse(&tokenizer, &proto)) << schema;
proto.set_name("test.proto");
return pool_.BuildFile(proto);
}
const FileDescriptor* BuildFile(const FileDescriptor* file) {
FileDescriptorProto proto;
file->CopyTo(&proto);
return pool_.BuildFile(proto);
}
DescriptorPool pool_;
};
TEST_F(CodeGeneratorTest, GetSourceRawFeaturesRoot) {
auto file = BuildFile(R"schema(
edition = "2023";
package protobuf_unittest;
import "google/protobuf/unittest_features.proto";
option features.field_presence = EXPLICIT; // 2023 default
option features.enum_type = CLOSED; // override
option features.(pb.test).int_file_feature = 8;
option features.(pb.test).string_source_feature = "file";
)schema");
ASSERT_THAT(file, NotNull());
EXPECT_THAT(TestGenerator::GetSourceRawFeatures(*file),
google::protobuf::EqualsProto(R"pb(
field_presence: EXPLICIT
enum_type: CLOSED
[pb.test] { int_file_feature: 8 string_source_feature: "file" }
)pb"));
}
TEST_F(CodeGeneratorTest, GetSourceRawFeaturesInherited) {
auto file = BuildFile(R"schema(
edition = "2023";
package protobuf_unittest;
import "google/protobuf/unittest_features.proto";
option features.enum_type = OPEN;
option features.(pb.test).int_file_feature = 6;
message EditionsMessage {
option features.(pb.test).int_message_feature = 7;
option features.(pb.test).int_multiple_feature = 8;
string field = 1 [
features.field_presence = EXPLICIT,
features.(pb.test).int_multiple_feature = 9,
features.(pb.test).string_source_feature = "field"
];
}
)schema");
ASSERT_THAT(file, NotNull());
const FieldDescriptor* field =
file->FindMessageTypeByName("EditionsMessage")->FindFieldByName("field");
ASSERT_THAT(field, NotNull());
EXPECT_THAT(
TestGenerator::GetSourceRawFeatures(*field), google::protobuf::EqualsProto(R"pb(
field_presence: EXPLICIT
[pb.test] { int_multiple_feature: 9 string_source_feature: "field" }
)pb"));
}
TEST_F(CodeGeneratorTest, GetSourceFeaturesRoot) {
auto file = BuildFile(R"schema(
edition = "2023";
package protobuf_unittest;
import "google/protobuf/unittest_features.proto";
option features.field_presence = EXPLICIT; // 2023 default
option features.enum_type = CLOSED; // override
option features.(pb.test).int_file_feature = 8;
option features.(pb.test).string_source_feature = "file";
)schema");
ASSERT_THAT(file, NotNull());
const FeatureSet& features = TestGenerator::GetSourceFeatures(*file);
const pb::TestFeatures& ext = features.GetExtension(pb::test);
EXPECT_TRUE(features.has_repeated_field_encoding());
EXPECT_TRUE(features.field_presence());
EXPECT_EQ(features.field_presence(), FeatureSet::EXPLICIT);
EXPECT_EQ(features.enum_type(), FeatureSet::CLOSED);
EXPECT_TRUE(ext.has_int_message_feature());
EXPECT_EQ(ext.int_file_feature(), 8);
EXPECT_EQ(ext.string_source_feature(), "file");
}
TEST_F(CodeGeneratorTest, GetSourceFeaturesInherited) {
auto file = BuildFile(R"schema(
edition = "2023";
package protobuf_unittest;
import "google/protobuf/unittest_features.proto";
option features.enum_type = CLOSED;
option features.(pb.test).int_source_feature = 5;
option features.(pb.test).int_file_feature = 6;
message EditionsMessage {
option features.(pb.test).int_message_feature = 7;
option features.(pb.test).int_multiple_feature = 8;
option features.(pb.test).string_source_feature = "message";
string field = 1 [
features.field_presence = IMPLICIT,
features.(pb.test).int_multiple_feature = 9,
features.(pb.test).string_source_feature = "field"
];
}
)schema");
ASSERT_THAT(file, NotNull());
const FieldDescriptor* field =
file->FindMessageTypeByName("EditionsMessage")->FindFieldByName("field");
ASSERT_THAT(field, NotNull());
const FeatureSet& features = TestGenerator::GetSourceFeatures(*field);
const pb::TestFeatures& ext = features.GetExtension(pb::test);
EXPECT_EQ(features.enum_type(), FeatureSet::CLOSED);
EXPECT_EQ(features.field_presence(), FeatureSet::IMPLICIT);
EXPECT_EQ(ext.int_message_feature(), 7);
EXPECT_EQ(ext.int_file_feature(), 6);
EXPECT_EQ(ext.int_multiple_feature(), 9);
EXPECT_EQ(ext.int_source_feature(), 5);
EXPECT_EQ(ext.string_source_feature(), "field");
}
TEST_F(CodeGeneratorTest, GetRuntimeProtoTrivial) {
auto file = BuildFile(R"schema(
edition = "2023";
package protobuf_unittest;
)schema");
ASSERT_THAT(file, NotNull());
FileDescriptorProto proto = TestGenerator::GetRuntimeProto(*file);
const FeatureSet& features = proto.options().features();
EXPECT_TRUE(features.has_raw_features());
EXPECT_THAT(features.raw_features(), EqualsProto(R"pb()pb"));
}
TEST_F(CodeGeneratorTest, GetRuntimeProtoRoot) {
auto file = BuildFile(R"schema(
edition = "2023";
package protobuf_unittest;
import "google/protobuf/unittest_features.proto";
option features.enum_type = CLOSED;
option features.(pb.test).int_source_feature = 5;
option features.(pb.test).int_file_feature = 6;
)schema");
ASSERT_THAT(file, NotNull());
FileDescriptorProto proto = TestGenerator::GetRuntimeProto(*file);
const FeatureSet& features = proto.options().features();
const pb::TestFeatures& ext = features.GetExtension(pb::test);
EXPECT_THAT(features.raw_features(),
EqualsProto(R"pb(enum_type: CLOSED
[pb.test] { int_file_feature: 6 })pb"));
EXPECT_EQ(features.enum_type(), FeatureSet::CLOSED);
EXPECT_TRUE(features.has_field_presence());
EXPECT_EQ(features.field_presence(), FeatureSet::EXPLICIT);
EXPECT_FALSE(ext.has_int_source_feature());
EXPECT_EQ(ext.int_file_feature(), 6);
}
TEST_F(CodeGeneratorTest, GetRuntimeProtoInherited) {
auto file = BuildFile(R"schema(
edition = "2023";
package protobuf_unittest;
import "google/protobuf/unittest_features.proto";
option features.enum_type = CLOSED;
option features.(pb.test).int_source_feature = 5;
option features.(pb.test).int_file_feature = 6;
message EditionsMessage {
option features.(pb.test).int_message_feature = 7;
option features.(pb.test).int_multiple_feature = 8;
string field = 1 [
features.field_presence = IMPLICIT,
features.(pb.test).int_multiple_feature = 9,
features.(pb.test).string_source_feature = "field"
];
}
)schema");
ASSERT_THAT(file, NotNull());
FileDescriptorProto proto = TestGenerator::GetRuntimeProto(*file);
const FieldDescriptorProto& field = proto.message_type(0).field(0);
const FeatureSet& features = field.options().features();
const pb::TestFeatures& ext = features.GetExtension(pb::test);
EXPECT_THAT(features.raw_features(), google::protobuf::EqualsProto(R"pb(
field_presence: IMPLICIT
[pb.test] { int_multiple_feature: 9 }
)pb"));
EXPECT_EQ(features.enum_type(), FeatureSet::CLOSED);
EXPECT_EQ(features.field_presence(), FeatureSet::IMPLICIT);
EXPECT_EQ(ext.int_multiple_feature(), 9);
EXPECT_EQ(ext.int_message_feature(), 7);
EXPECT_EQ(ext.int_file_feature(), 6);
EXPECT_FALSE(ext.has_int_source_feature());
EXPECT_FALSE(ext.has_string_source_feature());
}
} // namespace
} // namespace compiler
} // namespace protobuf
} // namespace google