3237 lines
115 KiB
C++
3237 lines
115 KiB
C++
// Protocol Buffers - Google's data interchange format
|
|
// Copyright 2008 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.
|
|
|
|
// Author: kenton@google.com (Kenton Varda)
|
|
// Based on original Protocol Buffers design by
|
|
// Sanjay Ghemawat, Jeff Dean, and others.
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <cstdint>
|
|
|
|
#include <gmock/gmock.h>
|
|
#include "absl/log/absl_check.h"
|
|
#include "absl/strings/str_cat.h"
|
|
#include "absl/strings/str_format.h"
|
|
#include "google/protobuf/compiler/command_line_interface_tester.h"
|
|
|
|
#ifndef _MSC_VER
|
|
#include <unistd.h>
|
|
#endif
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "google/protobuf/testing/file.h"
|
|
#include "google/protobuf/any.pb.h"
|
|
#include "google/protobuf/descriptor.pb.h"
|
|
#include "google/protobuf/testing/googletest.h"
|
|
#include <gtest/gtest.h>
|
|
#include "absl/strings/str_split.h"
|
|
#include "absl/strings/string_view.h"
|
|
#include "absl/strings/substitute.h"
|
|
#include "google/protobuf/compiler/code_generator.h"
|
|
#include "google/protobuf/compiler/command_line_interface.h"
|
|
#include "google/protobuf/compiler/cpp/names.h"
|
|
#include "google/protobuf/compiler/mock_code_generator.h"
|
|
#include "google/protobuf/compiler/plugin.pb.h"
|
|
#include "google/protobuf/compiler/subprocess.h"
|
|
#include "google/protobuf/io/io_win32.h"
|
|
#include "google/protobuf/test_util2.h"
|
|
#include "google/protobuf/unittest.pb.h"
|
|
#include "google/protobuf/unittest_custom_options.pb.h"
|
|
|
|
// Must be included last.
|
|
#include "google/protobuf/port_def.inc"
|
|
|
|
namespace google {
|
|
namespace protobuf {
|
|
namespace compiler {
|
|
|
|
#if defined(_WIN32)
|
|
// DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
|
|
// them like we do below.
|
|
using google::protobuf::io::win32::access;
|
|
using google::protobuf::io::win32::close;
|
|
using google::protobuf::io::win32::dup;
|
|
using google::protobuf::io::win32::dup2;
|
|
using google::protobuf::io::win32::open;
|
|
using google::protobuf::io::win32::write;
|
|
#endif
|
|
|
|
// Disable the whole test when we use tcmalloc for "draconian" heap checks, in
|
|
// which case tcmalloc will print warnings that fail the plugin tests.
|
|
#if !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
|
|
|
|
|
|
namespace {
|
|
|
|
std::string CreatePluginArg() {
|
|
std::string plugin_path;
|
|
#ifdef GOOGLE_PROTOBUF_TEST_PLUGIN_PATH
|
|
plugin_path = GOOGLE_PROTOBUF_TEST_PLUGIN_PATH;
|
|
#else
|
|
const char* possible_paths[] = {
|
|
// When building with shared libraries, libtool hides the real
|
|
// executable
|
|
// in .libs and puts a fake wrapper in the current directory.
|
|
// Unfortunately, due to an apparent bug on Cygwin/MinGW, if one program
|
|
// wrapped in this way (e.g. protobuf-tests.exe) tries to execute
|
|
// another
|
|
// program wrapped in this way (e.g. test_plugin.exe), the latter fails
|
|
// with error code 127 and no explanation message. Presumably the
|
|
// problem
|
|
// is that the wrapper for protobuf-tests.exe set some environment
|
|
// variables that confuse the wrapper for test_plugin.exe. Luckily, it
|
|
// turns out that if we simply invoke the wrapped test_plugin.exe
|
|
// directly, it works -- I guess the environment variables set by the
|
|
// protobuf-tests.exe wrapper happen to be correct for it too. So we do
|
|
// that.
|
|
".libs/test_plugin.exe", // Win32 w/autotool (Cygwin / MinGW)
|
|
"test_plugin.exe", // Other Win32 (MSVC)
|
|
"test_plugin", // Unix
|
|
};
|
|
for (int i = 0; i < ABSL_ARRAYSIZE(possible_paths); i++) {
|
|
if (access(possible_paths[i], F_OK) == 0) {
|
|
plugin_path = possible_paths[i];
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (plugin_path.empty() || !File::Exists(plugin_path)) {
|
|
ABSL_LOG(ERROR)
|
|
<< "Plugin executable not found. Plugin tests are likely to fail. "
|
|
<< plugin_path;
|
|
return "";
|
|
}
|
|
return absl::StrCat("--plugin=prefix-gen-plug=", plugin_path);
|
|
}
|
|
|
|
class CommandLineInterfaceTest : public CommandLineInterfaceTester {
|
|
protected:
|
|
CommandLineInterfaceTest();
|
|
|
|
// Runs the CommandLineInterface with the given command line. The
|
|
// command is automatically split on spaces, and the string "$tmpdir"
|
|
// is replaced with TestTempDir().
|
|
void Run(std::string command);
|
|
void RunWithArgs(std::vector<std::string> args);
|
|
|
|
// -----------------------------------------------------------------
|
|
// Methods to set up the test (called before Run()).
|
|
|
|
class NullCodeGenerator;
|
|
|
|
// Normally plugins are allowed for all tests. Call this to explicitly
|
|
// disable them.
|
|
void DisallowPlugins() { disallow_plugins_ = true; }
|
|
// Checks that MockCodeGenerator::Generate() was called in the given
|
|
// context (or the generator in test_plugin.cc, which produces the same
|
|
// output). That is, this tests if the generator with the given name
|
|
// was called with the given parameter and proto file and produced the
|
|
// given output file. This is checked by reading the output file and
|
|
// checking that it contains the content that MockCodeGenerator would
|
|
// generate given these inputs. message_name is the name of the first
|
|
// message that appeared in the proto file; this is just to make extra
|
|
// sure that the correct file was parsed.
|
|
void ExpectGenerated(const std::string& generator_name,
|
|
const std::string& parameter,
|
|
const std::string& proto_name,
|
|
const std::string& message_name);
|
|
void ExpectGenerated(const std::string& generator_name,
|
|
const std::string& parameter,
|
|
const std::string& proto_name,
|
|
const std::string& message_name,
|
|
const std::string& output_directory);
|
|
void ExpectGeneratedWithMultipleInputs(const std::string& generator_name,
|
|
const std::string& all_proto_names,
|
|
const std::string& proto_name,
|
|
const std::string& message_name);
|
|
void ExpectGeneratedWithInsertions(const std::string& generator_name,
|
|
const std::string& parameter,
|
|
const std::string& insertions,
|
|
const std::string& proto_name,
|
|
const std::string& message_name);
|
|
void CheckGeneratedAnnotations(const std::string& name,
|
|
const std::string& file);
|
|
|
|
#if defined(_WIN32)
|
|
void ExpectNullCodeGeneratorCalled(const std::string& parameter);
|
|
#endif // _WIN32
|
|
|
|
|
|
std::string ReadFile(absl::string_view filename);
|
|
void ReadDescriptorSet(absl::string_view filename,
|
|
FileDescriptorSet* descriptor_set);
|
|
|
|
void WriteDescriptorSet(absl::string_view filename,
|
|
const FileDescriptorSet* descriptor_set);
|
|
|
|
// The default code generators support all features. Use this to create a
|
|
// code generator that omits the given feature(s).
|
|
void CreateGeneratorWithMissingFeatures(const std::string& name,
|
|
const std::string& description,
|
|
uint64_t features) {
|
|
auto generator = std::make_unique<MockCodeGenerator>(name);
|
|
generator->SuppressFeatures(features);
|
|
RegisterGenerator(name, std::move(generator), description);
|
|
}
|
|
|
|
private:
|
|
// Was DisallowPlugins() called?
|
|
bool disallow_plugins_ = false;
|
|
|
|
NullCodeGenerator* null_generator_ = nullptr;
|
|
};
|
|
|
|
class CommandLineInterfaceTest::NullCodeGenerator : public CodeGenerator {
|
|
public:
|
|
NullCodeGenerator() : called_(false) {}
|
|
~NullCodeGenerator() override {}
|
|
|
|
mutable bool called_;
|
|
mutable std::string parameter_;
|
|
|
|
// implements CodeGenerator ----------------------------------------
|
|
bool Generate(const FileDescriptor* file, const std::string& parameter,
|
|
GeneratorContext* context, std::string* error) const override {
|
|
called_ = true;
|
|
parameter_ = parameter;
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// ===================================================================
|
|
|
|
CommandLineInterfaceTest::CommandLineInterfaceTest() {
|
|
// Register generators.
|
|
RegisterGenerator("--test_out", "--test_opt",
|
|
std::make_unique<MockCodeGenerator>("test_generator"),
|
|
"Test output.");
|
|
RegisterGenerator("-t", std::make_unique<MockCodeGenerator>("test_generator"),
|
|
"Test output.");
|
|
|
|
RegisterGenerator("--alt_out",
|
|
std::make_unique<MockCodeGenerator>("alt_generator"),
|
|
"Alt output.");
|
|
|
|
auto null_generator = std::make_unique<NullCodeGenerator>();
|
|
null_generator_ = null_generator.get();
|
|
RegisterGenerator("--null_out", std::move(null_generator), "Null output.");
|
|
|
|
}
|
|
|
|
void CommandLineInterfaceTest::Run(std::string command) {
|
|
if (!disallow_plugins_) {
|
|
AllowPlugins("prefix-");
|
|
absl::StrAppend(&command, " ", CreatePluginArg());
|
|
}
|
|
|
|
RunProtoc(command);
|
|
}
|
|
|
|
void CommandLineInterfaceTest::RunWithArgs(std::vector<std::string> args) {
|
|
if (!disallow_plugins_) {
|
|
AllowPlugins("prefix-");
|
|
args.push_back(CreatePluginArg());
|
|
}
|
|
|
|
RunProtocWithArgs(std::move(args));
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
void CommandLineInterfaceTest::ExpectGenerated(
|
|
const std::string& generator_name, const std::string& parameter,
|
|
const std::string& proto_name, const std::string& message_name) {
|
|
MockCodeGenerator::ExpectGenerated(generator_name, parameter, "", proto_name,
|
|
message_name, proto_name,
|
|
temp_directory());
|
|
}
|
|
|
|
void CommandLineInterfaceTest::ExpectGenerated(
|
|
const std::string& generator_name, const std::string& parameter,
|
|
const std::string& proto_name, const std::string& message_name,
|
|
const std::string& output_directory) {
|
|
MockCodeGenerator::ExpectGenerated(
|
|
generator_name, parameter, "", proto_name, message_name, proto_name,
|
|
absl::StrCat(temp_directory(), "/", output_directory));
|
|
}
|
|
|
|
void CommandLineInterfaceTest::ExpectGeneratedWithMultipleInputs(
|
|
const std::string& generator_name, const std::string& all_proto_names,
|
|
const std::string& proto_name, const std::string& message_name) {
|
|
MockCodeGenerator::ExpectGenerated(generator_name, "", "", proto_name,
|
|
message_name, all_proto_names,
|
|
temp_directory());
|
|
}
|
|
|
|
void CommandLineInterfaceTest::ExpectGeneratedWithInsertions(
|
|
const std::string& generator_name, const std::string& parameter,
|
|
const std::string& insertions, const std::string& proto_name,
|
|
const std::string& message_name) {
|
|
MockCodeGenerator::ExpectGenerated(generator_name, parameter, insertions,
|
|
proto_name, message_name, proto_name,
|
|
temp_directory());
|
|
}
|
|
|
|
void CommandLineInterfaceTest::CheckGeneratedAnnotations(
|
|
const std::string& name, const std::string& file) {
|
|
MockCodeGenerator::CheckGeneratedAnnotations(name, file, temp_directory());
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
void CommandLineInterfaceTest::ExpectNullCodeGeneratorCalled(
|
|
const std::string& parameter) {
|
|
EXPECT_TRUE(null_generator_->called_);
|
|
EXPECT_EQ(parameter, null_generator_->parameter_);
|
|
}
|
|
#endif // _WIN32
|
|
|
|
|
|
std::string CommandLineInterfaceTest::ReadFile(absl::string_view filename) {
|
|
std::string path = absl::StrCat(temp_directory(), "/", filename);
|
|
std::string file_contents;
|
|
ABSL_CHECK_OK(File::GetContents(path, &file_contents, true));
|
|
return file_contents;
|
|
}
|
|
|
|
void CommandLineInterfaceTest::ReadDescriptorSet(
|
|
absl::string_view filename, FileDescriptorSet* descriptor_set) {
|
|
std::string file_contents = ReadFile(filename);
|
|
if (!descriptor_set->ParseFromString(file_contents)) {
|
|
FAIL() << "Could not parse file contents: " << filename;
|
|
}
|
|
}
|
|
|
|
void CommandLineInterfaceTest::WriteDescriptorSet(
|
|
absl::string_view filename, const FileDescriptorSet* descriptor_set) {
|
|
std::string binary_proto;
|
|
ABSL_CHECK(descriptor_set->SerializeToString(&binary_proto));
|
|
CreateTempFile(filename, binary_proto);
|
|
}
|
|
|
|
// ===================================================================
|
|
|
|
TEST_F(CommandLineInterfaceTest, BasicOutput) {
|
|
// Test that the common case works.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, BasicOutput_DescriptorSetIn) {
|
|
// Test that the common case works.
|
|
FileDescriptorSet file_descriptor_set;
|
|
FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("foo.proto");
|
|
file_descriptor_proto->add_message_type()->set_name("Foo");
|
|
|
|
WriteDescriptorSet("foo.bin", &file_descriptor_set);
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--descriptor_set_in=$tmpdir/foo.bin foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, BasicPlugin) {
|
|
// Test that basic plugins work.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --plug_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, BasicPlugin_DescriptorSetIn) {
|
|
// Test that basic plugins work.
|
|
|
|
FileDescriptorSet file_descriptor_set;
|
|
FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("foo.proto");
|
|
file_descriptor_proto->add_message_type()->set_name("Foo");
|
|
|
|
WriteDescriptorSet("foo.bin", &file_descriptor_set);
|
|
|
|
Run("protocol_compiler --plug_out=$tmpdir "
|
|
"--descriptor_set_in=$tmpdir/foo.bin foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, Plugin_OptionRetention) {
|
|
CreateTempFile("foo.proto",
|
|
R"pb(syntax = "proto2"
|
|
;
|
|
import "bar.proto";
|
|
package foo;
|
|
message Foo {
|
|
optional bar.Bar b = 1;
|
|
extensions 1000 to max [
|
|
declaration = {
|
|
number: 1000
|
|
full_name: ".foo.my_ext"
|
|
type: ".foo.MyType"
|
|
}
|
|
];
|
|
})pb");
|
|
CreateTempFile("bar.proto",
|
|
R"pb(syntax = "proto2"
|
|
;
|
|
package bar;
|
|
message Bar {
|
|
extensions 1000 to max [
|
|
declaration = {
|
|
number: 1000
|
|
full_name: ".baz.my_ext"
|
|
type: ".baz.MyType"
|
|
}
|
|
];
|
|
})pb");
|
|
|
|
#ifdef GOOGLE_PROTOBUF_FAKE_PLUGIN_PATH
|
|
std::string plugin_path = GOOGLE_PROTOBUF_FAKE_PLUGIN_PATH;
|
|
#else
|
|
std::string plugin_path = absl::StrCat(
|
|
TestUtil::TestSourceDir(), "/third_party/protobuf/compiler/fake_plugin");
|
|
#endif
|
|
|
|
// Invoke protoc with fake_plugin to get ahold of the CodeGeneratorRequest
|
|
// sent by protoc.
|
|
Run(absl::StrCat(
|
|
"protocol_compiler --fake_plugin_out=$tmpdir --proto_path=$tmpdir "
|
|
"foo.proto --plugin=prefix-gen-fake_plugin=",
|
|
plugin_path));
|
|
ExpectNoErrors();
|
|
std::string base64_output = ReadFile("foo.proto.request");
|
|
std::string binary_request;
|
|
ASSERT_TRUE(absl::Base64Unescape(base64_output, &binary_request));
|
|
CodeGeneratorRequest request;
|
|
ASSERT_TRUE(request.ParseFromString(binary_request));
|
|
|
|
// request.proto_file() should include source-retention options for bar.proto
|
|
// but not for foo.proto. Protoc should strip source-retention options from
|
|
// the immediate proto files being built, but not for all dependencies.
|
|
ASSERT_EQ(request.proto_file_size(), 2);
|
|
{
|
|
EXPECT_EQ(request.proto_file(0).name(), "bar.proto");
|
|
ASSERT_EQ(request.proto_file(0).message_type_size(), 1);
|
|
const DescriptorProto& m = request.proto_file(0).message_type(0);
|
|
ASSERT_EQ(m.extension_range_size(), 1);
|
|
EXPECT_EQ(m.extension_range(0).options().declaration_size(), 1);
|
|
}
|
|
|
|
{
|
|
EXPECT_EQ(request.proto_file(1).name(), "foo.proto");
|
|
ASSERT_EQ(request.proto_file(1).message_type_size(), 1);
|
|
const DescriptorProto& m = request.proto_file(1).message_type(0);
|
|
ASSERT_EQ(m.extension_range_size(), 1);
|
|
EXPECT_TRUE(m.extension_range(0).options().declaration().empty());
|
|
}
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, Plugin_SourceFileDescriptors) {
|
|
CreateTempFile("foo.proto",
|
|
R"pb(syntax = "proto2"
|
|
;
|
|
import "bar.proto";
|
|
package foo;
|
|
message Foo {
|
|
optional bar.Bar b = 1;
|
|
extensions 1000 to max [
|
|
declaration = {
|
|
number: 1000
|
|
full_name: ".foo.my_ext"
|
|
type: ".foo.MyType"
|
|
}
|
|
];
|
|
})pb");
|
|
CreateTempFile("bar.proto",
|
|
R"pb(syntax = "proto2"
|
|
;
|
|
package bar;
|
|
message Bar {
|
|
extensions 1000 to max [
|
|
declaration = {
|
|
number: 1000
|
|
full_name: ".baz.my_ext"
|
|
type: ".baz.MyType"
|
|
}
|
|
];
|
|
})pb");
|
|
|
|
#ifdef GOOGLE_PROTOBUF_FAKE_PLUGIN_PATH
|
|
std::string plugin_path = GOOGLE_PROTOBUF_FAKE_PLUGIN_PATH;
|
|
#else
|
|
std::string plugin_path = absl::StrCat(
|
|
TestUtil::TestSourceDir(), "/third_party/protobuf/compiler/fake_plugin");
|
|
#endif
|
|
|
|
// Invoke protoc with fake_plugin to get ahold of the CodeGeneratorRequest
|
|
// sent by protoc.
|
|
Run(absl::StrCat(
|
|
"protocol_compiler --fake_plugin_out=$tmpdir --proto_path=$tmpdir "
|
|
"foo.proto --plugin=prefix-gen-fake_plugin=",
|
|
plugin_path));
|
|
ExpectNoErrors();
|
|
std::string base64_output = ReadFile("foo.proto.request");
|
|
std::string binary_request;
|
|
ASSERT_TRUE(absl::Base64Unescape(base64_output, &binary_request));
|
|
CodeGeneratorRequest request;
|
|
ASSERT_TRUE(request.ParseFromString(binary_request));
|
|
|
|
// request.source_file_descriptors() should consist of a descriptor for
|
|
// foo.proto that includes source-retention options.
|
|
ASSERT_EQ(request.source_file_descriptors_size(), 1);
|
|
EXPECT_EQ(request.source_file_descriptors(0).name(), "foo.proto");
|
|
ASSERT_EQ(request.source_file_descriptors(0).message_type_size(), 1);
|
|
const DescriptorProto& m = request.source_file_descriptors(0).message_type(0);
|
|
ASSERT_EQ(m.extension_range_size(), 1);
|
|
EXPECT_EQ(m.extension_range(0).options().declaration_size(), 1);
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, GeneratorAndPlugin) {
|
|
// Invoke a generator and a plugin at the same time.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
|
|
ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, GeneratorAndPlugin_DescriptorSetIn) {
|
|
// Invoke a generator and a plugin at the same time.
|
|
|
|
FileDescriptorSet file_descriptor_set;
|
|
FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("foo.proto");
|
|
file_descriptor_proto->add_message_type()->set_name("Foo");
|
|
|
|
WriteDescriptorSet("foo.bin", &file_descriptor_set);
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
|
|
"--descriptor_set_in=$tmpdir/foo.bin foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
|
|
ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, MultipleInputs) {
|
|
// Test parsing multiple input files.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Bar {}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto bar.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
|
|
"foo.proto", "Foo");
|
|
ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
|
|
"bar.proto", "Bar");
|
|
ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
|
|
"foo.proto", "Foo");
|
|
ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
|
|
"bar.proto", "Bar");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, MultipleInputs_DescriptorSetIn) {
|
|
// Test parsing multiple input files.
|
|
FileDescriptorSet file_descriptor_set;
|
|
|
|
FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("foo.proto");
|
|
file_descriptor_proto->add_message_type()->set_name("Foo");
|
|
|
|
file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("bar.proto");
|
|
file_descriptor_proto->add_message_type()->set_name("Bar");
|
|
|
|
WriteDescriptorSet("foo.bin", &file_descriptor_set);
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
|
|
"--descriptor_set_in=$tmpdir/foo.bin foo.proto bar.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
|
|
"foo.proto", "Foo");
|
|
ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
|
|
"bar.proto", "Bar");
|
|
ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
|
|
"foo.proto", "Foo");
|
|
ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
|
|
"bar.proto", "Bar");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, MultipleInputs_UnusedImport_DescriptorSetIn) {
|
|
// Test unused import warning is not raised when descriptor_set_in is called
|
|
// and custom options are in unknown field instead of uninterpreted_options.
|
|
FileDescriptorSet file_descriptor_set;
|
|
|
|
const FileDescriptor* descriptor_file =
|
|
FileDescriptorProto::descriptor()->file();
|
|
descriptor_file->CopyTo(file_descriptor_set.add_file());
|
|
|
|
FileDescriptorProto& any_proto = *file_descriptor_set.add_file();
|
|
google::protobuf::Any::descriptor()->file()->CopyTo(&any_proto);
|
|
|
|
const FileDescriptor* custom_file =
|
|
protobuf_unittest::AggregateMessage::descriptor()->file();
|
|
FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
|
|
custom_file->CopyTo(file_descriptor_proto);
|
|
file_descriptor_proto->set_name("custom_options.proto");
|
|
// Add a custom message option.
|
|
FieldDescriptorProto* extension_option =
|
|
file_descriptor_proto->add_extension();
|
|
extension_option->set_name("unknown_option");
|
|
extension_option->set_extendee(".google.protobuf.MessageOptions");
|
|
extension_option->set_number(1111);
|
|
extension_option->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
|
|
extension_option->set_type(FieldDescriptorProto::TYPE_INT64);
|
|
|
|
file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("import_custom_unknown_options.proto");
|
|
file_descriptor_proto->add_dependency("custom_options.proto");
|
|
// Add custom message option to unknown field. This custom option is
|
|
// not known in generated pool, thus option will be in unknown fields.
|
|
file_descriptor_proto->add_message_type()->set_name("Bar");
|
|
file_descriptor_proto->mutable_message_type(0)
|
|
->mutable_options()
|
|
->mutable_unknown_fields()
|
|
->AddVarint(1111, 2222);
|
|
|
|
WriteDescriptorSet("foo.bin", &file_descriptor_set);
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
|
|
"--descriptor_set_in=$tmpdir/foo.bin "
|
|
"import_custom_unknown_options.proto");
|
|
|
|
// TODO(jieluo): Fix this test. This test case only happens when
|
|
// CommandLineInterface::Run() is used instead of invoke protoc combined
|
|
// with descriptor_set_in, and same custom options are defined in both
|
|
// generated pool and descriptor_set_in. There's no such uages for now but
|
|
// still need to be fixed.
|
|
/*
|
|
file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("import_custom_extension_options.proto");
|
|
file_descriptor_proto->add_dependency("custom_options.proto");
|
|
// Add custom message option to unknown field. This custom option is
|
|
// also defined in generated pool, thus option will be in extensions.
|
|
file_descriptor_proto->add_message_type()->set_name("Foo");
|
|
file_descriptor_proto->mutable_message_type(0)
|
|
->mutable_options()
|
|
->mutable_unknown_fields()
|
|
->AddVarint(protobuf_unittest::message_opt1.number(), 2222);
|
|
|
|
WriteDescriptorSet("foo.bin", &file_descriptor_set);
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
|
|
"--descriptor_set_in=$tmpdir/foo.bin import_custom_unknown_options.proto "
|
|
"import_custom_extension_options.proto");
|
|
*/
|
|
|
|
ExpectNoErrors();
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport) {
|
|
// Test parsing multiple input files with an import of a separate file.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"baz.proto\";\n"
|
|
"message Bar {\n"
|
|
" optional Baz a = 1;\n"
|
|
"}\n");
|
|
CreateTempFile("baz.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Baz {}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto bar.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
|
|
"foo.proto", "Foo");
|
|
ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
|
|
"bar.proto", "Bar");
|
|
ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
|
|
"foo.proto", "Foo");
|
|
ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
|
|
"bar.proto", "Bar");
|
|
}
|
|
|
|
|
|
TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport_DescriptorSetIn) {
|
|
// Test parsing multiple input files with an import of a separate file.
|
|
FileDescriptorSet file_descriptor_set;
|
|
|
|
FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("foo.proto");
|
|
file_descriptor_proto->add_message_type()->set_name("Foo");
|
|
|
|
file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("bar.proto");
|
|
file_descriptor_proto->add_dependency("baz.proto");
|
|
DescriptorProto* message = file_descriptor_proto->add_message_type();
|
|
message->set_name("Bar");
|
|
FieldDescriptorProto* field = message->add_field();
|
|
field->set_type_name("Baz");
|
|
field->set_name("a");
|
|
field->set_number(1);
|
|
|
|
WriteDescriptorSet("foo_and_bar.bin", &file_descriptor_set);
|
|
|
|
file_descriptor_set.clear_file();
|
|
file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("baz.proto");
|
|
file_descriptor_proto->add_message_type()->set_name("Baz");
|
|
|
|
file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("bat.proto");
|
|
file_descriptor_proto->add_dependency("baz.proto");
|
|
message = file_descriptor_proto->add_message_type();
|
|
message->set_name("Bat");
|
|
field = message->add_field();
|
|
field->set_type_name("Baz");
|
|
field->set_name("a");
|
|
field->set_number(1);
|
|
|
|
WriteDescriptorSet("baz_and_bat.bin", &file_descriptor_set);
|
|
Run(absl::Substitute(
|
|
"protocol_compiler --test_out=$$tmpdir --plug_out=$$tmpdir "
|
|
"--descriptor_set_in=$0 foo.proto bar.proto",
|
|
absl::StrCat("$tmpdir/foo_and_bar.bin",
|
|
CommandLineInterface::kPathSeparator,
|
|
"$tmpdir/baz_and_bat.bin")));
|
|
|
|
ExpectNoErrors();
|
|
ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
|
|
"foo.proto", "Foo");
|
|
ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
|
|
"bar.proto", "Bar");
|
|
ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
|
|
"foo.proto", "Foo");
|
|
ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
|
|
"bar.proto", "Bar");
|
|
|
|
Run(absl::Substitute(
|
|
"protocol_compiler --test_out=$$tmpdir --plug_out=$$tmpdir "
|
|
"--descriptor_set_in=$0 baz.proto bat.proto",
|
|
absl::StrCat("$tmpdir/foo_and_bar.bin",
|
|
CommandLineInterface::kPathSeparator,
|
|
"$tmpdir/baz_and_bat.bin")));
|
|
|
|
ExpectNoErrors();
|
|
ExpectGeneratedWithMultipleInputs("test_generator", "baz.proto,bat.proto",
|
|
"baz.proto", "Baz");
|
|
ExpectGeneratedWithMultipleInputs("test_generator", "baz.proto,bat.proto",
|
|
"bat.proto", "Bat");
|
|
ExpectGeneratedWithMultipleInputs("test_plugin", "baz.proto,bat.proto",
|
|
"baz.proto", "Baz");
|
|
ExpectGeneratedWithMultipleInputs("test_plugin", "baz.proto,bat.proto",
|
|
"bat.proto", "Bat");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest,
|
|
MultipleInputsWithImport_DescriptorSetIn_DuplicateFileDescriptor) {
|
|
// Test parsing multiple input files with an import of a separate file.
|
|
FileDescriptorSet file_descriptor_set;
|
|
|
|
FileDescriptorProto foo_file_descriptor_proto;
|
|
foo_file_descriptor_proto.set_name("foo.proto");
|
|
foo_file_descriptor_proto.add_message_type()->set_name("Foo");
|
|
|
|
file_descriptor_set.add_file()->CopyFrom(foo_file_descriptor_proto);
|
|
|
|
FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("bar.proto");
|
|
file_descriptor_proto->add_dependency("baz.proto");
|
|
file_descriptor_proto->add_dependency("foo.proto");
|
|
DescriptorProto* message = file_descriptor_proto->add_message_type();
|
|
message->set_name("Bar");
|
|
FieldDescriptorProto* field = message->add_field();
|
|
field->set_type_name("Baz");
|
|
field->set_name("a");
|
|
field->set_number(1);
|
|
field = message->add_field();
|
|
field->set_type_name("Foo");
|
|
field->set_name("f");
|
|
field->set_number(2);
|
|
WriteDescriptorSet("foo_and_bar.bin", &file_descriptor_set);
|
|
|
|
file_descriptor_set.clear_file();
|
|
file_descriptor_set.add_file()->CopyFrom(foo_file_descriptor_proto);
|
|
|
|
file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("baz.proto");
|
|
file_descriptor_proto->add_dependency("foo.proto");
|
|
message = file_descriptor_proto->add_message_type();
|
|
message->set_name("Baz");
|
|
field = message->add_field();
|
|
field->set_type_name("Foo");
|
|
field->set_name("f");
|
|
field->set_number(1);
|
|
WriteDescriptorSet("foo_and_baz.bin", &file_descriptor_set);
|
|
|
|
Run(absl::Substitute(
|
|
"protocol_compiler --test_out=$$tmpdir --plug_out=$$tmpdir "
|
|
"--descriptor_set_in=$0 bar.proto",
|
|
absl::StrCat("$tmpdir/foo_and_bar.bin",
|
|
CommandLineInterface::kPathSeparator,
|
|
"$tmpdir/foo_and_baz.bin")));
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "bar.proto", "Bar");
|
|
ExpectGenerated("test_plugin", "", "bar.proto", "Bar");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest,
|
|
MultipleInputsWithImport_DescriptorSetIn_MissingImport) {
|
|
// Test parsing multiple input files with an import of a separate file.
|
|
FileDescriptorSet file_descriptor_set;
|
|
|
|
FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("foo.proto");
|
|
file_descriptor_proto->add_message_type()->set_name("Foo");
|
|
|
|
file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("bar.proto");
|
|
file_descriptor_proto->add_dependency("baz.proto");
|
|
DescriptorProto* message = file_descriptor_proto->add_message_type();
|
|
message->set_name("Bar");
|
|
FieldDescriptorProto* field = message->add_field();
|
|
field->set_type_name("Baz");
|
|
field->set_name("a");
|
|
field->set_number(1);
|
|
|
|
WriteDescriptorSet("foo_and_bar.bin", &file_descriptor_set);
|
|
|
|
file_descriptor_set.clear_file();
|
|
file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("baz.proto");
|
|
file_descriptor_proto->add_message_type()->set_name("Baz");
|
|
|
|
WriteDescriptorSet("baz.bin", &file_descriptor_set);
|
|
Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
|
|
"--descriptor_set_in=$tmpdir/foo_and_bar.bin "
|
|
"foo.proto bar.proto");
|
|
ExpectErrorSubstring(
|
|
"bar.proto: Import \"baz.proto\" was not found or had errors.");
|
|
ExpectErrorSubstring("bar.proto: \"Baz\" is not defined.");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest,
|
|
InputsOnlyFromDescriptorSetIn_UnusedImportIsNotReported) {
|
|
FileDescriptorSet file_descriptor_set;
|
|
|
|
FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("unused.proto");
|
|
file_descriptor_proto->add_message_type()->set_name("Unused");
|
|
|
|
file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("bar.proto");
|
|
file_descriptor_proto->add_dependency("unused.proto");
|
|
file_descriptor_proto->add_message_type()->set_name("Bar");
|
|
|
|
WriteDescriptorSet("unused_and_bar.bin", &file_descriptor_set);
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
|
|
"--descriptor_set_in=$tmpdir/unused_and_bar.bin unused.proto bar.proto");
|
|
ExpectNoErrors();
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest,
|
|
InputsFromDescriptorSetInAndFileSystem_UnusedImportIsReported) {
|
|
FileDescriptorSet file_descriptor_set;
|
|
|
|
FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("unused.proto");
|
|
file_descriptor_proto->add_message_type()->set_name("Unused");
|
|
|
|
file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("bar.proto");
|
|
file_descriptor_proto->add_dependency("unused.proto");
|
|
file_descriptor_proto->add_message_type()->set_name("Bar");
|
|
|
|
WriteDescriptorSet("unused_and_bar.bin", &file_descriptor_set);
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"bar.proto\";\n"
|
|
"message Foo {\n"
|
|
" optional Bar bar = 1;\n"
|
|
"}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
|
|
"--descriptor_set_in=$tmpdir/unused_and_bar.bin "
|
|
"--proto_path=$tmpdir unused.proto bar.proto foo.proto");
|
|
// Reporting unused imports here is unfair, since it's unactionable. Notice
|
|
// the lack of a line number.
|
|
// TODO(b/144853061): If the file with unused import is from the descriptor
|
|
// set and not from the file system, suppress the warning.
|
|
ExpectWarningSubstring("bar.proto: warning: Import unused.proto is unused.");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest,
|
|
OnlyReportsUnusedImportsForFilesBeingGenerated) {
|
|
CreateTempFile("unused.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Unused {}\n");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"unused.proto\";\n"
|
|
"message Bar {}\n");
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"bar.proto\";\n"
|
|
"message Foo {\n"
|
|
" optional Bar bar = 1;\n"
|
|
"}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
ExpectNoErrors();
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ReportsTransitiveMisingImports_LeafFirst) {
|
|
CreateTempFile("unused.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Unused {}\n");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"unused.proto\";\n"
|
|
"message Bar {}\n");
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"bar.proto\";\n"
|
|
"message Foo {\n"
|
|
" optional Bar bar = 1;\n"
|
|
"}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir bar.proto foo.proto");
|
|
ExpectWarningSubstring(
|
|
"bar.proto:2:1: warning: Import unused.proto is unused.");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ReportsTransitiveMisingImports_LeafLast) {
|
|
CreateTempFile("unused.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Unused {}\n");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"unused.proto\";\n"
|
|
"message Bar {}\n");
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"bar.proto\";\n"
|
|
"message Foo {\n"
|
|
" optional Bar bar = 1;\n"
|
|
"}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto bar.proto");
|
|
ExpectWarningSubstring(
|
|
"bar.proto:2:1: warning: Import unused.proto is unused.");
|
|
}
|
|
TEST_F(CommandLineInterfaceTest, CreateDirectory) {
|
|
// Test that when we output to a sub-directory, it is created.
|
|
|
|
CreateTempFile("bar/baz/foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
CreateTempDir("out");
|
|
CreateTempDir("plugout");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir/out --plug_out=$tmpdir/plugout "
|
|
"--proto_path=$tmpdir bar/baz/foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "bar/baz/foo.proto", "Foo", "out");
|
|
ExpectGenerated("test_plugin", "", "bar/baz/foo.proto", "Foo", "plugout");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, GeneratorParameters) {
|
|
// Test that generator parameters are correctly parsed from the command line.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --test_out=TestParameter:$tmpdir "
|
|
"--plug_out=TestPluginParameter:$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "TestParameter", "foo.proto", "Foo");
|
|
ExpectGenerated("test_plugin", "TestPluginParameter", "foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ExtraGeneratorParameters) {
|
|
// Test that generator parameters specified with the option flag are
|
|
// correctly passed to the code generator.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
// Create the "a" and "b" sub-directories.
|
|
CreateTempDir("a");
|
|
CreateTempDir("b");
|
|
|
|
Run("protocol_compiler "
|
|
"--test_opt=foo1 "
|
|
"--test_out=bar:$tmpdir/a "
|
|
"--test_opt=foo2 "
|
|
"--test_out=baz:$tmpdir/b "
|
|
"--test_opt=foo3 "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "bar,foo1,foo2,foo3", "foo.proto", "Foo",
|
|
"a");
|
|
ExpectGenerated("test_generator", "baz,foo1,foo2,foo3", "foo.proto", "Foo",
|
|
"b");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ExtraPluginParameters) {
|
|
// Test that generator parameters specified with the option flag are
|
|
// correctly passed to the code generator.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
// Create the "a" and "b" sub-directories.
|
|
CreateTempDir("a");
|
|
CreateTempDir("b");
|
|
|
|
Run("protocol_compiler "
|
|
"--plug_opt=foo1 "
|
|
"--plug_out=bar:$tmpdir/a "
|
|
"--plug_opt=foo2 "
|
|
"--plug_out=baz:$tmpdir/b "
|
|
"--plug_opt=foo3 "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_plugin", "bar,foo1,foo2,foo3", "foo.proto", "Foo", "a");
|
|
ExpectGenerated("test_plugin", "baz,foo1,foo2,foo3", "foo.proto", "Foo", "b");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, UnrecognizedExtraParameters) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
|
|
"--unknown_plug_a_opt=Foo "
|
|
"--unknown_plug_b_opt=Bar "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring("Unknown flag: --unknown_plug_a_opt");
|
|
ExpectErrorSubstring("Unknown flag: --unknown_plug_b_opt");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ExtraPluginParametersForOutParameters) {
|
|
// This doesn't rely on the plugin having been registered and instead that
|
|
// the existence of --[name]_out is enough to make the --[name]_opt valid.
|
|
// However, running out of process plugins found via the search path (i.e. -
|
|
// not pre registered with --plugin) isn't support in this test suite, so we
|
|
// list the options pre/post the _out directive, and then include _opt that
|
|
// will be unknown, and confirm the failure output is about the expected
|
|
// unknown directive, which means the other were accepted.
|
|
// NOTE: UnrecognizedExtraParameters confirms that if two unknown _opt
|
|
// directives appear, they both are reported.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
|
|
"--xyz_opt=foo=bar --xyz_out=$tmpdir "
|
|
"--abc_out=$tmpdir --abc_opt=foo=bar "
|
|
"--unknown_plug_opt=Foo "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorText("Unknown flag: --unknown_plug_opt\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, Insert) {
|
|
// Test running a generator that inserts code into another's output.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler "
|
|
"--test_out=TestParameter:$tmpdir "
|
|
"--plug_out=TestPluginParameter:$tmpdir "
|
|
"--test_out=insert=test_generator,test_plugin:$tmpdir "
|
|
"--plug_out=insert=test_generator,test_plugin:$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGeneratedWithInsertions("test_generator", "TestParameter",
|
|
"test_generator,test_plugin", "foo.proto",
|
|
"Foo");
|
|
ExpectGeneratedWithInsertions("test_plugin", "TestPluginParameter",
|
|
"test_generator,test_plugin", "foo.proto",
|
|
"Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, InsertWithAnnotationFixup) {
|
|
// Check that annotation spans are updated after insertions.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message MockCodeGenerator_Annotate {}\n");
|
|
|
|
Run("protocol_compiler "
|
|
"--test_out=TestParameter:$tmpdir "
|
|
"--plug_out=TestPluginParameter:$tmpdir "
|
|
"--test_out=insert_endlines=test_generator,test_plugin:$tmpdir "
|
|
"--plug_out=insert_endlines=test_generator,test_plugin:$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
CheckGeneratedAnnotations("test_generator", "foo.proto");
|
|
CheckGeneratedAnnotations("test_plugin", "foo.proto");
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
|
|
TEST_F(CommandLineInterfaceTest, WindowsOutputPath) {
|
|
// Test that the output path can be a Windows-style path.
|
|
|
|
CreateTempFile("foo.proto", "syntax = \"proto2\";\n");
|
|
|
|
Run("protocol_compiler --null_out=C:\\ "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectNullCodeGeneratorCalled("");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, WindowsOutputPathAndParameter) {
|
|
// Test that we can have a windows-style output path and a parameter.
|
|
|
|
CreateTempFile("foo.proto", "syntax = \"proto2\";\n");
|
|
|
|
Run("protocol_compiler --null_out=bar:C:\\ "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectNullCodeGeneratorCalled("bar");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, TrailingBackslash) {
|
|
// Test that the directories can end in backslashes. Some users claim this
|
|
// doesn't work on their system.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir\\ "
|
|
"--proto_path=$tmpdir\\ foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, Win32ErrorMessage) {
|
|
EXPECT_EQ("The system cannot find the file specified.\r\n",
|
|
Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND));
|
|
}
|
|
|
|
#endif // defined(_WIN32) || defined(__CYGWIN__)
|
|
|
|
TEST_F(CommandLineInterfaceTest, PathLookup) {
|
|
// Test that specifying multiple directories in the proto search path works.
|
|
|
|
CreateTempFile("b/bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Bar {}\n");
|
|
CreateTempFile("a/foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"bar.proto\";\n"
|
|
"message Foo {\n"
|
|
" optional Bar a = 1;\n"
|
|
"}\n");
|
|
CreateTempFile("b/foo.proto", "this should not be parsed\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir/a --proto_path=$tmpdir/b foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ColonDelimitedPath) {
|
|
// Same as PathLookup, but we provide the proto_path in a single flag.
|
|
|
|
CreateTempFile("b/bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Bar {}\n");
|
|
CreateTempFile("a/foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"bar.proto\";\n"
|
|
"message Foo {\n"
|
|
" optional Bar a = 1;\n"
|
|
"}\n");
|
|
CreateTempFile("b/foo.proto", "this should not be parsed\n");
|
|
|
|
Run(absl::Substitute(
|
|
"protocol_compiler --test_out=$$tmpdir --proto_path=$0 foo.proto",
|
|
std::string("$tmpdir/a") + CommandLineInterface::kPathSeparator +
|
|
"$tmpdir/b"));
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, NonRootMapping) {
|
|
// Test setting up a search path mapping a directory to a non-root location.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=bar=$tmpdir bar/foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "bar/foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, PathWithEqualsSign) {
|
|
// Test setting up a search path which happens to have '=' in it.
|
|
|
|
CreateTempDir("with=sign");
|
|
CreateTempFile("with=sign/foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir/with=sign foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, MultipleGenerators) {
|
|
// Test that we can have multiple generators and use both in one invocation,
|
|
// each with a different output directory.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
// Create the "a" and "b" sub-directories.
|
|
CreateTempDir("a");
|
|
CreateTempDir("b");
|
|
|
|
Run("protocol_compiler "
|
|
"--test_out=$tmpdir/a "
|
|
"--alt_out=$tmpdir/b "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "foo.proto", "Foo", "a");
|
|
ExpectGenerated("alt_generator", "", "foo.proto", "Foo", "b");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, DisallowServicesNoServices) {
|
|
// Test that --disallow_services doesn't cause a problem when there are no
|
|
// services.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --disallow_services --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, DisallowServicesHasService) {
|
|
// Test that --disallow_services produces an error when there are services.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n"
|
|
"service Bar {}\n");
|
|
|
|
Run("protocol_compiler --disallow_services --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring("foo.proto: This file contains services");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, AllowServicesHasService) {
|
|
// Test that services work fine as long as --disallow_services is not used.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n"
|
|
"service Bar {}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
|
|
}
|
|
|
|
#ifdef PROTOBUF_FUTURE_EDITIONS
|
|
|
|
TEST_F(CommandLineInterfaceTest, EditionsAreNotAllowed) {
|
|
CreateTempFile("foo.proto",
|
|
"edition = \"very-cool\";\n"
|
|
"message FooRequest {}\n");
|
|
|
|
Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring(
|
|
"This file uses editions, but --experimental_editions has not been "
|
|
"enabled.");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, EditionsFlagExplicitTrue) {
|
|
CreateTempFile("foo.proto",
|
|
"edition = \"very-cool\";\n"
|
|
"message FooRequest {}\n");
|
|
|
|
Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir "
|
|
"--experimental_editions foo.proto");
|
|
ExpectNoErrors();
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, FeaturesEditionZero) {
|
|
CreateTempFile("foo.proto",
|
|
R"schema(
|
|
edition = "2023";
|
|
option features.field_presence = IMPLICIT;
|
|
message Foo {
|
|
int32 bar = 1 [default = 5, features.field_presence = EXPLICIT];
|
|
int32 baz = 2;
|
|
})schema");
|
|
|
|
Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir "
|
|
"--experimental_editions foo.proto");
|
|
ExpectNoErrors();
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, FeatureExtensions) {
|
|
CreateTempFile("net/proto2/proto/descriptor.proto",
|
|
google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
|
|
CreateTempFile("features.proto",
|
|
R"schema(
|
|
syntax = "proto2";
|
|
package pb;
|
|
import "net/proto2/proto/descriptor.proto";
|
|
extend google.protobuf.FeatureSet {
|
|
optional TestFeatures test = 9999;
|
|
}
|
|
message TestFeatures {
|
|
optional int32 int_feature = 1 [
|
|
retention = RETENTION_RUNTIME,
|
|
targets = TARGET_TYPE_FIELD,
|
|
edition_defaults = { edition: "2023", value: "3" }
|
|
];
|
|
})schema");
|
|
CreateTempFile("foo.proto",
|
|
R"schema(
|
|
edition = "2023";
|
|
import "features.proto";
|
|
message Foo {
|
|
int32 bar = 1;
|
|
int32 baz = 2 [features.(pb.test).int_feature = 5];
|
|
})schema");
|
|
|
|
Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir "
|
|
"--experimental_editions foo.proto");
|
|
ExpectNoErrors();
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, FeatureValidationError) {
|
|
CreateTempFile("foo.proto",
|
|
R"schema(
|
|
edition = "2023";
|
|
option features.field_presence = IMPLICIT;
|
|
message Foo {
|
|
int32 bar = 1 [default = 5, features.field_presence = FIELD_PRESENCE_UNKNOWN];
|
|
int32 baz = 2;
|
|
})schema");
|
|
|
|
Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir "
|
|
"--experimental_editions foo.proto");
|
|
ExpectErrorSubstring(
|
|
"FeatureSet.field_presence must resolve to a known value, found "
|
|
"FIELD_PRESENCE_UNKNOWN");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, FeatureTargetError) {
|
|
CreateTempFile("foo.proto",
|
|
R"schema(
|
|
edition = "2023";
|
|
message Foo {
|
|
option features.field_presence = IMPLICIT;
|
|
int32 bar = 1 [default = 5, features.field_presence = EXPLICIT];
|
|
int32 baz = 2;
|
|
})schema");
|
|
|
|
Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir "
|
|
"--experimental_editions foo.proto");
|
|
ExpectErrorSubstring(
|
|
"FeatureSet.field_presence cannot be set on an entity of type `message`");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, FeatureExtensionError) {
|
|
CreateTempFile("net/proto2/proto/descriptor.proto",
|
|
google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
|
|
CreateTempFile("features.proto",
|
|
R"schema(
|
|
syntax = "proto2";
|
|
package pb;
|
|
import "net/proto2/proto/descriptor.proto";
|
|
extend google.protobuf.FeatureSet {
|
|
optional TestFeatures test = 9999;
|
|
}
|
|
message TestFeatures {
|
|
repeated int32 repeated_feature = 1 [
|
|
retention = RETENTION_RUNTIME,
|
|
targets = TARGET_TYPE_FIELD,
|
|
edition_defaults = { edition: "2023", value: "3" }
|
|
];
|
|
})schema");
|
|
CreateTempFile("foo.proto",
|
|
R"schema(
|
|
edition = "2023";
|
|
import "features.proto";
|
|
message Foo {
|
|
int32 bar = 1;
|
|
int32 baz = 2 [features.(pb.test).int_feature = 5];
|
|
})schema");
|
|
|
|
Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir "
|
|
"--experimental_editions foo.proto");
|
|
ExpectErrorSubstring(
|
|
"Feature field pb.TestFeatures.repeated_feature is an unsupported "
|
|
"repeated field");
|
|
}
|
|
|
|
#endif // PROTOBUF_FUTURE_EDITIONS
|
|
|
|
|
|
TEST_F(CommandLineInterfaceTest, DirectDependencies_Missing_EmptyList) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"bar.proto\";\n"
|
|
"message Foo { optional Bar bar = 1; }");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Bar { optional string text = 1; }");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
|
|
"--direct_dependencies= foo.proto");
|
|
|
|
ExpectErrorText(
|
|
"foo.proto: File is imported but not declared in --direct_dependencies: "
|
|
"bar.proto\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, DirectDependencies_Missing) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"bar.proto\";\n"
|
|
"import \"bla.proto\";\n"
|
|
"message Foo { optional Bar bar = 1; optional Bla bla = 2; }");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Bar { optional string text = 1; }");
|
|
CreateTempFile("bla.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Bla { optional int64 number = 1; }");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
|
|
"--direct_dependencies=bla.proto foo.proto");
|
|
|
|
ExpectErrorText(
|
|
"foo.proto: File is imported but not declared in --direct_dependencies: "
|
|
"bar.proto\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, DirectDependencies_NoViolation) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"bar.proto\";\n"
|
|
"message Foo { optional Bar bar = 1; }");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Bar { optional string text = 1; }");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
|
|
"--direct_dependencies=bar.proto foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, DirectDependencies_NoViolation_MultiImports) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"bar.proto\";\n"
|
|
"import \"bla.proto\";\n"
|
|
"message Foo { optional Bar bar = 1; optional Bla bla = 2; }");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Bar { optional string text = 1; }");
|
|
CreateTempFile("bla.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Bla { optional int64 number = 1; }");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
|
|
"--direct_dependencies=bar.proto:bla.proto foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, DirectDependencies_ProvidedMultipleTimes) {
|
|
CreateTempFile("foo.proto", "syntax = \"proto2\";\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
|
|
"--direct_dependencies=bar.proto --direct_dependencies=bla.proto "
|
|
"foo.proto");
|
|
|
|
ExpectErrorText(
|
|
"--direct_dependencies may only be passed once. To specify multiple "
|
|
"direct dependencies, pass them all as a single parameter separated by "
|
|
"':'.\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, DirectDependencies_CustomErrorMessage) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"bar.proto\";\n"
|
|
"message Foo { optional Bar bar = 1; }");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Bar { optional string text = 1; }");
|
|
|
|
std::vector<std::string> commands;
|
|
commands.push_back("protocol_compiler");
|
|
commands.push_back("--test_out=$tmpdir");
|
|
commands.push_back("--proto_path=$tmpdir");
|
|
commands.push_back("--direct_dependencies=");
|
|
commands.push_back("--direct_dependencies_violation_msg=Bla \"%s\" Bla");
|
|
commands.push_back("foo.proto");
|
|
RunWithArgs(commands);
|
|
|
|
ExpectErrorText("foo.proto: Bla \"bar.proto\" Bla\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, CwdRelativeInputs) {
|
|
// Test that we can accept working-directory-relative input files.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir $tmpdir/foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"foo.proto\";\n"
|
|
"message Bar {\n"
|
|
" optional Foo foo = 1;\n"
|
|
"}\n");
|
|
|
|
Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
|
|
"--proto_path=$tmpdir bar.proto");
|
|
|
|
ExpectNoErrors();
|
|
|
|
FileDescriptorSet descriptor_set;
|
|
ReadDescriptorSet("descriptor_set", &descriptor_set);
|
|
if (HasFatalFailure()) return;
|
|
EXPECT_EQ(1, descriptor_set.file_size());
|
|
EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
|
|
// Descriptor set should not have source code info.
|
|
EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
|
|
// Descriptor set should have json_name.
|
|
EXPECT_EQ("Bar", descriptor_set.file(0).message_type(0).name());
|
|
EXPECT_EQ("foo", descriptor_set.file(0).message_type(0).field(0).name());
|
|
EXPECT_TRUE(descriptor_set.file(0).message_type(0).field(0).has_json_name());
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithDuplicates) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"foo.proto\";\n"
|
|
"message Bar {\n"
|
|
" optional Foo foo = 1;\n"
|
|
"}\n");
|
|
CreateTempFile("baz.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"foo.proto\";\n"
|
|
"message Baz {\n"
|
|
" optional Foo foo = 1;\n"
|
|
"}\n");
|
|
|
|
Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
|
|
"--proto_path=$tmpdir bar.proto foo.proto bar.proto baz.proto");
|
|
|
|
ExpectNoErrors();
|
|
|
|
FileDescriptorSet descriptor_set;
|
|
ReadDescriptorSet("descriptor_set", &descriptor_set);
|
|
if (HasFatalFailure()) return;
|
|
EXPECT_EQ(3, descriptor_set.file_size());
|
|
// foo should come first since the output is in dependency order.
|
|
// since bar and baz are unordered, they should be in command line order.
|
|
EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
|
|
EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
|
|
EXPECT_EQ("baz.proto", descriptor_set.file(2).name());
|
|
// Descriptor set should not have source code info.
|
|
EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
|
|
// Descriptor set should have json_name.
|
|
EXPECT_EQ("Bar", descriptor_set.file(1).message_type(0).name());
|
|
EXPECT_EQ("foo", descriptor_set.file(1).message_type(0).field(0).name());
|
|
EXPECT_TRUE(descriptor_set.file(1).message_type(0).field(0).has_json_name());
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"foo.proto\";\n"
|
|
"message Bar {\n"
|
|
" optional Foo foo = 1;\n"
|
|
"}\n");
|
|
|
|
Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
|
|
"--include_source_info --proto_path=$tmpdir bar.proto");
|
|
|
|
ExpectNoErrors();
|
|
|
|
FileDescriptorSet descriptor_set;
|
|
ReadDescriptorSet("descriptor_set", &descriptor_set);
|
|
if (HasFatalFailure()) return;
|
|
EXPECT_EQ(1, descriptor_set.file_size());
|
|
EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
|
|
// Source code info included.
|
|
EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"foo.proto\";\n"
|
|
"message Bar {\n"
|
|
" optional Foo foo = 1;\n"
|
|
"}\n");
|
|
|
|
Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
|
|
"--include_imports --proto_path=$tmpdir bar.proto");
|
|
|
|
ExpectNoErrors();
|
|
|
|
FileDescriptorSet descriptor_set;
|
|
ReadDescriptorSet("descriptor_set", &descriptor_set);
|
|
if (HasFatalFailure()) return;
|
|
EXPECT_EQ(2, descriptor_set.file_size());
|
|
if (descriptor_set.file(0).name() == "bar.proto") {
|
|
std::swap(descriptor_set.mutable_file()->mutable_data()[0],
|
|
descriptor_set.mutable_file()->mutable_data()[1]);
|
|
}
|
|
EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
|
|
EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
|
|
// Descriptor set should not have source code info.
|
|
EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
|
|
EXPECT_FALSE(descriptor_set.file(1).has_source_code_info());
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSetWithSourceInfo) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"foo.proto\";\n"
|
|
"message Bar {\n"
|
|
" optional Foo foo = 1;\n"
|
|
"}\n");
|
|
|
|
Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
|
|
"--include_imports --include_source_info --proto_path=$tmpdir bar.proto");
|
|
|
|
ExpectNoErrors();
|
|
|
|
FileDescriptorSet descriptor_set;
|
|
ReadDescriptorSet("descriptor_set", &descriptor_set);
|
|
if (HasFatalFailure()) return;
|
|
EXPECT_EQ(2, descriptor_set.file_size());
|
|
if (descriptor_set.file(0).name() == "bar.proto") {
|
|
std::swap(descriptor_set.mutable_file()->mutable_data()[0],
|
|
descriptor_set.mutable_file()->mutable_data()[1]);
|
|
}
|
|
EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
|
|
EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
|
|
// Source code info included.
|
|
EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
|
|
EXPECT_TRUE(descriptor_set.file(1).has_source_code_info());
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, DescriptorSetOptionRetention) {
|
|
// clang-format off
|
|
CreateTempFile(
|
|
"foo.proto",
|
|
absl::Substitute(R"pb(
|
|
syntax = "proto2";
|
|
import "$0";
|
|
extend google.protobuf.FileOptions {
|
|
optional int32 runtime_retention_option = 50001
|
|
[retention = RETENTION_RUNTIME];
|
|
optional int32 source_retention_option = 50002
|
|
[retention = RETENTION_SOURCE];
|
|
}
|
|
option (runtime_retention_option) = 2;
|
|
option (source_retention_option) = 3;)pb",
|
|
DescriptorProto::descriptor()->file()->name()));
|
|
// clang-format on
|
|
std::string descriptor_proto_base_dir = "src";
|
|
Run(absl::Substitute(
|
|
"protocol_compiler --descriptor_set_out=$$tmpdir/descriptor_set "
|
|
"--proto_path=$$tmpdir --proto_path=$0 foo.proto",
|
|
descriptor_proto_base_dir));
|
|
ExpectNoErrors();
|
|
|
|
FileDescriptorSet descriptor_set;
|
|
ReadDescriptorSet("descriptor_set", &descriptor_set);
|
|
ASSERT_EQ(descriptor_set.file_size(), 1);
|
|
const UnknownFieldSet& unknown_fields =
|
|
descriptor_set.file(0).options().unknown_fields();
|
|
|
|
// We expect runtime_retention_option to be present while
|
|
// source_retention_option should have been stripped.
|
|
ASSERT_EQ(unknown_fields.field_count(), 1);
|
|
EXPECT_EQ(unknown_fields.field(0).number(), 50001);
|
|
EXPECT_EQ(unknown_fields.field(0).varint(), 2);
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, DescriptorSetOptionRetentionOverride) {
|
|
// clang-format off
|
|
CreateTempFile(
|
|
"foo.proto",
|
|
absl::Substitute(R"pb(
|
|
syntax = "proto2";
|
|
import "$0";
|
|
extend google.protobuf.FileOptions {
|
|
optional int32 runtime_retention_option = 50001
|
|
[retention = RETENTION_RUNTIME];
|
|
optional int32 source_retention_option = 50002
|
|
[retention = RETENTION_SOURCE];
|
|
}
|
|
option (runtime_retention_option) = 2;
|
|
option (source_retention_option) = 3;)pb",
|
|
DescriptorProto::descriptor()->file()->name()));
|
|
// clang-format on
|
|
std::string descriptor_proto_base_dir = "src";
|
|
Run(absl::Substitute(
|
|
"protocol_compiler --descriptor_set_out=$$tmpdir/descriptor_set "
|
|
"--proto_path=$$tmpdir --retain_options --proto_path=$0 foo.proto",
|
|
descriptor_proto_base_dir));
|
|
ExpectNoErrors();
|
|
|
|
FileDescriptorSet descriptor_set;
|
|
ReadDescriptorSet("descriptor_set", &descriptor_set);
|
|
ASSERT_EQ(descriptor_set.file_size(), 1);
|
|
const UnknownFieldSet& unknown_fields =
|
|
descriptor_set.file(0).options().unknown_fields();
|
|
|
|
// We expect all options to be present.
|
|
ASSERT_EQ(unknown_fields.field_count(), 2);
|
|
EXPECT_EQ(unknown_fields.field(0).number(), 50001);
|
|
EXPECT_EQ(unknown_fields.field(1).number(), 50002);
|
|
EXPECT_EQ(unknown_fields.field(0).varint(), 2);
|
|
EXPECT_EQ(unknown_fields.field(1).varint(), 3);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
// TODO(teboring): Figure out how to write test on windows.
|
|
#else
|
|
TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileGivenTwoInputs) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"foo.proto\";\n"
|
|
"message Bar {\n"
|
|
" optional Foo foo = 1;\n"
|
|
"}\n");
|
|
|
|
Run("protocol_compiler --dependency_out=$tmpdir/manifest "
|
|
"--test_out=$tmpdir --proto_path=$tmpdir bar.proto foo.proto");
|
|
|
|
ExpectErrorText(
|
|
"Can only process one input file when using --dependency_out=FILE.\n");
|
|
}
|
|
|
|
#ifdef PROTOBUF_OPENSOURCE
|
|
TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFile) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"foo.proto\";\n"
|
|
"message Bar {\n"
|
|
" optional Foo foo = 1;\n"
|
|
"}\n");
|
|
|
|
std::string current_working_directory = getcwd(nullptr, 0);
|
|
SwitchToTempDirectory();
|
|
|
|
Run("protocol_compiler --dependency_out=manifest --test_out=. "
|
|
"bar.proto");
|
|
|
|
ExpectNoErrors();
|
|
|
|
ExpectFileContent("manifest",
|
|
"bar.proto.MockCodeGenerator.test_generator: "
|
|
"foo.proto\\\n bar.proto");
|
|
|
|
File::ChangeWorkingDirectory(current_working_directory);
|
|
}
|
|
#else // !PROTOBUF_OPENSOURCE
|
|
// TODO(teboring): Figure out how to change and get working directory in
|
|
// google3.
|
|
#endif // !PROTOBUF_OPENSOURCE
|
|
|
|
TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileForAbsolutePath) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"foo.proto\";\n"
|
|
"message Bar {\n"
|
|
" optional Foo foo = 1;\n"
|
|
"}\n");
|
|
|
|
Run("protocol_compiler --dependency_out=$tmpdir/manifest "
|
|
"--test_out=$tmpdir --proto_path=$tmpdir bar.proto");
|
|
|
|
ExpectNoErrors();
|
|
|
|
ExpectFileContent("manifest",
|
|
"$tmpdir/bar.proto.MockCodeGenerator.test_generator: "
|
|
"$tmpdir/foo.proto\\\n $tmpdir/bar.proto");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest,
|
|
WriteDependencyManifestFileWithDescriptorSetOut) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"foo.proto\";\n"
|
|
"message Bar {\n"
|
|
" optional Foo foo = 1;\n"
|
|
"}\n");
|
|
|
|
Run("protocol_compiler --dependency_out=$tmpdir/manifest "
|
|
"--descriptor_set_out=$tmpdir/bar.pb --proto_path=$tmpdir bar.proto");
|
|
|
|
ExpectNoErrors();
|
|
|
|
ExpectFileContent("manifest",
|
|
"$tmpdir/bar.pb: "
|
|
"$tmpdir/foo.proto\\\n $tmpdir/bar.proto");
|
|
}
|
|
#endif // !_WIN32
|
|
|
|
TEST_F(CommandLineInterfaceTest, TestArgumentFile) {
|
|
// Test parsing multiple input files using an argument file.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Bar {}\n");
|
|
CreateTempFile("arguments.txt",
|
|
"--test_out=$tmpdir\n"
|
|
"--plug_out=$tmpdir\n"
|
|
"--proto_path=$tmpdir\n"
|
|
"--direct_dependencies_violation_msg=%s is not imported\n"
|
|
"foo.proto\n"
|
|
"bar.proto");
|
|
|
|
Run("protocol_compiler @$tmpdir/arguments.txt");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
|
|
"foo.proto", "Foo");
|
|
ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
|
|
"bar.proto", "Bar");
|
|
ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
|
|
"foo.proto", "Foo");
|
|
ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
|
|
"bar.proto", "Bar");
|
|
}
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
TEST_F(CommandLineInterfaceTest, ParseErrors) {
|
|
// Test that parse errors are reported.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"badsyntax\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorText(
|
|
"foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ParseErrors_DescriptorSetIn) {
|
|
// Test that parse errors are reported.
|
|
CreateTempFile("foo.bin", "not a FileDescriptorSet");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--descriptor_set_in=$tmpdir/foo.bin foo.proto");
|
|
|
|
ExpectErrorText("$tmpdir/foo.bin: Unable to parse.\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ParseErrorsMultipleFiles) {
|
|
// Test that parse errors are reported from multiple files.
|
|
|
|
// We set up files such that foo.proto actually depends on bar.proto in
|
|
// two ways: Directly and through baz.proto. bar.proto's errors should
|
|
// only be reported once.
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"badsyntax\n");
|
|
CreateTempFile("baz.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"bar.proto\";\n");
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"bar.proto\";\n"
|
|
"import \"baz.proto\";\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorText(
|
|
"bar.proto:2:1: Expected top-level statement (e.g. \"message\").\n"
|
|
"baz.proto:2:1: Import \"bar.proto\" was not found or had errors.\n"
|
|
"foo.proto:2:1: Import \"bar.proto\" was not found or had errors.\n"
|
|
"foo.proto:3:1: Import \"baz.proto\" was not found or had errors.\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, RecursiveImportFails) {
|
|
// Create a proto file that imports itself.
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"foo.proto\";\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring(
|
|
"foo.proto:2:1: File recursively imports itself: "
|
|
"foo.proto -> foo.proto\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, InputNotFoundError) {
|
|
// Test what happens if the input file is not found.
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorText(
|
|
"Could not make proto path relative: foo.proto: No such file or "
|
|
"directory\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, InputNotFoundError_DescriptorSetIn) {
|
|
// Test what happens if the input file is not found.
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--descriptor_set_in=$tmpdir/foo.bin foo.proto");
|
|
|
|
ExpectErrorText("$tmpdir/foo.bin: No such file or directory\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundError) {
|
|
// Test what happens when a working-directory-relative input file is not
|
|
// found.
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir $tmpdir/foo.proto");
|
|
|
|
ExpectErrorText(
|
|
"Could not make proto path relative: $tmpdir/foo.proto: No such file or "
|
|
"directory\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotMappedError) {
|
|
// Test what happens when a working-directory-relative input file is not
|
|
// mapped to a virtual path.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
// Create a directory called "bar" so that we can point --proto_path at it.
|
|
CreateTempFile("bar/dummy", "");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir/bar $tmpdir/foo.proto");
|
|
|
|
ExpectErrorText(
|
|
"$tmpdir/foo.proto: File does not reside within any path "
|
|
"specified using --proto_path (or -I). You must specify a "
|
|
"--proto_path which encompasses this file. Note that the "
|
|
"proto_path must be an exact prefix of the .proto file "
|
|
"names -- protoc is too dumb to figure out when two paths "
|
|
"(e.g. absolute and relative) are equivalent (it's harder "
|
|
"than you think).\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundAndNotMappedError) {
|
|
// Check what happens if the input file is not found *and* is not mapped
|
|
// in the proto_path.
|
|
|
|
// Create a directory called "bar" so that we can point --proto_path at it.
|
|
CreateTempFile("bar/dummy", "");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir/bar $tmpdir/foo.proto");
|
|
|
|
ExpectErrorText(
|
|
"Could not make proto path relative: $tmpdir/foo.proto: No such file or "
|
|
"directory\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, CwdRelativeInputShadowedError) {
|
|
// Test what happens when a working-directory-relative input file is shadowed
|
|
// by another file in the virtual path.
|
|
|
|
CreateTempFile("foo/foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
CreateTempFile("bar/foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Bar {}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir/foo --proto_path=$tmpdir/bar "
|
|
"$tmpdir/bar/foo.proto");
|
|
|
|
ExpectErrorText(
|
|
"$tmpdir/bar/foo.proto: Input is shadowed in the --proto_path "
|
|
"by \"$tmpdir/foo/foo.proto\". Either use the latter "
|
|
"file as your input or reorder the --proto_path so that the "
|
|
"former file's location comes first.\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ProtoPathNotFoundError) {
|
|
// Test what happens if the input file is not found.
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir/foo foo.proto");
|
|
|
|
ExpectErrorText(
|
|
"$tmpdir/foo: warning: directory does not exist.\n"
|
|
"Could not make proto path relative: foo.proto: No such file or "
|
|
"directory\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ProtoPathAndDescriptorSetIn) {
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir --descriptor_set_in=$tmpdir/foo.bin foo.proto");
|
|
ExpectErrorText("$tmpdir/foo.bin: No such file or directory\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--descriptor_set_in=$tmpdir/foo.bin --proto_path=$tmpdir foo.proto");
|
|
ExpectErrorText("$tmpdir/foo.bin: No such file or directory\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ProtoPathAndDescriptorSetIn_CompileFiles) {
|
|
// Test what happens if a proto is in a --descriptor_set_in and also exists
|
|
// on disk.
|
|
FileDescriptorSet file_descriptor_set;
|
|
|
|
// NOTE: This file desc SHOULD be different from the one created as a temp
|
|
// to make it easier to test that the file was output instead of the
|
|
// contents of the --descriptor_set_in file.
|
|
FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
|
|
file_descriptor_proto->set_name("foo.proto");
|
|
file_descriptor_proto->add_message_type()->set_name("Foo");
|
|
|
|
WriteDescriptorSet("foo.bin", &file_descriptor_set);
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message FooBar { required string foo_message = 1; }\n");
|
|
|
|
Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
|
|
"--descriptor_set_in=$tmpdir/foo.bin "
|
|
"--include_source_info "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
|
|
FileDescriptorSet descriptor_set;
|
|
ReadDescriptorSet("descriptor_set", &descriptor_set);
|
|
|
|
EXPECT_EQ(1, descriptor_set.file_size());
|
|
EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
|
|
// Descriptor set SHOULD have source code info.
|
|
EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
|
|
|
|
EXPECT_EQ("FooBar", descriptor_set.file(0).message_type(0).name());
|
|
EXPECT_EQ("foo_message",
|
|
descriptor_set.file(0).message_type(0).field(0).name());
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ProtoPathAndDependencyOut) {
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--dependency_out=$tmpdir/manifest "
|
|
"--descriptor_set_in=$tmpdir/foo.bin foo.proto");
|
|
ExpectErrorText(
|
|
"--descriptor_set_in cannot be used with --dependency_out.\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--descriptor_set_in=$tmpdir/foo.bin "
|
|
"--dependency_out=$tmpdir/manifest foo.proto");
|
|
ExpectErrorText(
|
|
"--dependency_out cannot be used with --descriptor_set_in.\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, MissingInputError) {
|
|
// Test that we get an error if no inputs are given.
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir");
|
|
|
|
ExpectErrorText("Missing input file.\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, MissingOutputError) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorText("Missing output directives.\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, OutputWriteError) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
std::string output_file =
|
|
MockCodeGenerator::GetOutputFileName("test_generator", "foo.proto");
|
|
|
|
// Create a directory blocking our output location.
|
|
CreateTempDir(output_file);
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
// MockCodeGenerator no longer detects an error because we actually write to
|
|
// an in-memory location first, then dump to disk at the end. This is no
|
|
// big deal.
|
|
// ExpectErrorSubstring("MockCodeGenerator detected write error.");
|
|
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
// Windows with MSVCRT.dll produces EPERM instead of EISDIR.
|
|
if (HasAlternateErrorSubstring(
|
|
absl::StrCat(output_file, ": Permission denied"))) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
ExpectErrorSubstring(absl::StrCat(output_file, ": Is a directory"));
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, PluginOutputWriteError) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
std::string output_file =
|
|
MockCodeGenerator::GetOutputFileName("test_plugin", "foo.proto");
|
|
|
|
// Create a directory blocking our output location.
|
|
CreateTempDir(output_file);
|
|
|
|
Run("protocol_compiler --plug_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
// Windows with MSVCRT.dll produces EPERM instead of EISDIR.
|
|
if (HasAlternateErrorSubstring(
|
|
absl::StrCat(output_file, ": Permission denied"))) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
ExpectErrorSubstring(absl::StrCat(output_file, ": Is a directory"));
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, OutputDirectoryNotFoundError) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir/nosuchdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring("nosuchdir/: No such file or directory");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, PluginOutputDirectoryNotFoundError) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --plug_out=$tmpdir/nosuchdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring("nosuchdir/: No such file or directory");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, OutputDirectoryIsFileError) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir/foo.proto "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
// Windows with MSVCRT.dll produces EINVAL instead of ENOTDIR.
|
|
if (HasAlternateErrorSubstring("foo.proto/: Invalid argument")) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
ExpectErrorSubstring("foo.proto/: Not a directory");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, GeneratorError) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message MockCodeGenerator_Error {}\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring(
|
|
"--test_out: foo.proto: Saw message type MockCodeGenerator_Error.");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, GeneratorPluginError) {
|
|
// Test a generator plugin that returns an error.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message MockCodeGenerator_Error {}\n");
|
|
|
|
Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring(
|
|
"--plug_out: foo.proto: Saw message type MockCodeGenerator_Error.");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, GeneratorPluginFail) {
|
|
// Test a generator plugin that exits with an error code.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message MockCodeGenerator_Exit {}\n");
|
|
|
|
Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring("Saw message type MockCodeGenerator_Exit.");
|
|
ExpectErrorSubstring(
|
|
"--plug_out: prefix-gen-plug: Plugin failed with status code 123.");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, GeneratorPluginCrash) {
|
|
// Test a generator plugin that crashes.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message MockCodeGenerator_Abort {}\n");
|
|
|
|
Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring("Saw message type MockCodeGenerator_Abort.");
|
|
|
|
#ifdef _WIN32
|
|
// Windows doesn't have signals. It looks like abort()ing causes the process
|
|
// to exit with status code 3, but let's not depend on the exact number here.
|
|
ExpectErrorSubstring(
|
|
"--plug_out: prefix-gen-plug: Plugin failed with status code");
|
|
#else
|
|
// Don't depend on the exact signal number.
|
|
ExpectErrorSubstring("--plug_out: prefix-gen-plug: Plugin killed by signal");
|
|
#endif
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, PluginReceivesSourceCodeInfo) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message MockCodeGenerator_HasSourceCodeInfo {}\n");
|
|
|
|
Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring(
|
|
"Saw message type MockCodeGenerator_HasSourceCodeInfo: true.");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, PluginReceivesJsonName) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message MockCodeGenerator_HasJsonName {\n"
|
|
" optional int32 value = 1;\n"
|
|
"}\n");
|
|
|
|
Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring("Saw json_name: true");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, PluginReceivesCompilerVersion) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message MockCodeGenerator_ShowVersionNumber {\n"
|
|
" optional int32 value = 1;\n"
|
|
"}\n");
|
|
|
|
Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring(absl::StrFormat("Saw compiler_version: %d %s",
|
|
GOOGLE_PROTOBUF_VERSION,
|
|
GOOGLE_PROTOBUF_VERSION_SUFFIX));
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
|
|
// Test what happens if the plugin isn't found.
|
|
|
|
CreateTempFile("error.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --badplug_out=TestParameter:$tmpdir "
|
|
"--plugin=prefix-gen-badplug=no_such_file "
|
|
"--proto_path=$tmpdir error.proto");
|
|
|
|
#ifdef _WIN32
|
|
ExpectErrorSubstring(
|
|
absl::StrCat("--badplug_out: prefix-gen-badplug: ",
|
|
Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND)));
|
|
#else
|
|
// Error written to stdout by child process after exec() fails.
|
|
ExpectErrorSubstring("no_such_file: program not found or is not executable");
|
|
|
|
ExpectErrorSubstring(
|
|
"Please specify a program using absolute path or make sure "
|
|
"the program is available in your PATH system variable");
|
|
|
|
// Error written by parent process when child fails.
|
|
ExpectErrorSubstring(
|
|
"--badplug_out: prefix-gen-badplug: Plugin failed with status code 1.");
|
|
#endif
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) {
|
|
// Test what happens if plugins aren't allowed.
|
|
|
|
CreateTempFile("error.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
DisallowPlugins();
|
|
Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
|
|
"--proto_path=$tmpdir error.proto");
|
|
|
|
ExpectErrorSubstring("Unknown flag: --plug_out");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, HelpText) {
|
|
Run("test_exec_name --help");
|
|
|
|
ExpectCapturedStdoutSubstringWithZeroReturnCode("Usage: test_exec_name ");
|
|
ExpectCapturedStdoutSubstringWithZeroReturnCode("--test_out=OUT_DIR");
|
|
ExpectCapturedStdoutSubstringWithZeroReturnCode("Test output.");
|
|
ExpectCapturedStdoutSubstringWithZeroReturnCode("--alt_out=OUT_DIR");
|
|
ExpectCapturedStdoutSubstringWithZeroReturnCode("Alt output.");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, GccFormatErrors) {
|
|
// Test --error_format=gcc (which is the default, but we want to verify
|
|
// that it can be set explicitly).
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"badsyntax\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir --error_format=gcc foo.proto");
|
|
|
|
ExpectErrorText(
|
|
"foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, MsvsFormatErrors) {
|
|
// Test --error_format=msvs
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"badsyntax\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir --error_format=msvs foo.proto");
|
|
|
|
ExpectErrorText(
|
|
"$tmpdir/foo.proto(2) : error in column=1: Expected top-level statement "
|
|
"(e.g. \"message\").\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, InvalidErrorFormat) {
|
|
// Test invalid --error_format
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"badsyntax\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir --error_format=invalid foo.proto");
|
|
|
|
ExpectErrorText("Unknown error format: invalid\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, Warnings) {
|
|
// Test --fatal_warnings.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"import \"bar.proto\";\n");
|
|
CreateTempFile("bar.proto", "syntax = \"proto2\";\n");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
ExpectCapturedStderrSubstringWithZeroReturnCode(
|
|
"foo.proto:2:1: warning: Import bar.proto is unused.");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --fatal_warnings "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
ExpectErrorSubstring("foo.proto:2:1: warning: Import bar.proto is unused.");
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
// Flag parsing tests
|
|
|
|
TEST_F(CommandLineInterfaceTest, ParseSingleCharacterFlag) {
|
|
// Test that a single-character flag works.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler -t$tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ParseSpaceDelimitedValue) {
|
|
// Test that separating the flag value with a space works.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler --test_out $tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ParseSingleCharacterSpaceDelimitedValue) {
|
|
// Test that separating the flag value with a space works for
|
|
// single-character flags.
|
|
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Foo {}\n");
|
|
|
|
Run("protocol_compiler -t $tmpdir "
|
|
"--proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectNoErrors();
|
|
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, MissingValueError) {
|
|
// Test that we get an error if a flag is missing its value.
|
|
|
|
Run("protocol_compiler --test_out --proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorText("Missing value for flag: --test_out\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) {
|
|
// Test that we get an error if the last argument is a flag requiring a
|
|
// value.
|
|
|
|
Run("protocol_compiler --test_out");
|
|
|
|
ExpectErrorText("Missing value for flag: --test_out\n");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, Proto3OptionalDisallowedNoCodegenSupport) {
|
|
CreateTempFile("google/foo.proto",
|
|
"syntax = \"proto3\";\n"
|
|
"message Foo {\n"
|
|
" optional int32 i = 1;\n"
|
|
"}\n");
|
|
|
|
CreateGeneratorWithMissingFeatures("--no_proto3_optional_out",
|
|
"Doesn't support proto3 optional",
|
|
CodeGenerator::FEATURE_PROTO3_OPTIONAL);
|
|
|
|
Run("protocol_compiler --experimental_allow_proto3_optional "
|
|
"--proto_path=$tmpdir google/foo.proto --no_proto3_optional_out=$tmpdir");
|
|
|
|
ExpectErrorSubstring(
|
|
"code generator --no_proto3_optional_out hasn't been updated to support "
|
|
"optional fields in proto3");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ReservedFieldNumbersFail) {
|
|
CreateTempFile("foo.proto",
|
|
R"(
|
|
syntax = "proto2";
|
|
message Foo {
|
|
optional int32 i = 19123;
|
|
}
|
|
)");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring(
|
|
"foo.proto: Field numbers 19000 through 19999 are reserved for the "
|
|
"protocol buffer library implementation.");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ReservedFieldNumbersFailAsOneof) {
|
|
CreateTempFile("foo.proto",
|
|
R"(
|
|
syntax = "proto2";
|
|
message Foo {
|
|
oneof one {
|
|
int32 i = 19123;
|
|
}
|
|
}
|
|
)");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring(
|
|
"foo.proto: Field numbers 19000 through 19999 are reserved for the "
|
|
"protocol buffer library implementation.");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, ReservedFieldNumbersFailAsExtension) {
|
|
CreateTempFile("foo.proto",
|
|
R"(
|
|
syntax = "proto2";
|
|
message Foo {
|
|
extensions 4 to max;
|
|
}
|
|
extend Foo {
|
|
optional int32 i = 19123;
|
|
}
|
|
)");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring(
|
|
"foo.proto: Field numbers 19000 through 19999 are reserved for the "
|
|
"protocol buffer library implementation.");
|
|
|
|
CreateTempFile("foo.proto",
|
|
R"(
|
|
syntax = "proto2";
|
|
message Foo {
|
|
extensions 4 to max;
|
|
}
|
|
message Bar {
|
|
extend Foo {
|
|
optional int32 i = 19123;
|
|
}
|
|
}
|
|
)");
|
|
|
|
Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
|
|
|
|
ExpectErrorSubstring(
|
|
"foo.proto: Field numbers 19000 through 19999 are reserved for the "
|
|
"protocol buffer library implementation.");
|
|
}
|
|
|
|
|
|
TEST_F(CommandLineInterfaceTest, Proto3OptionalAllowWithFlag) {
|
|
CreateTempFile("google/foo.proto",
|
|
"syntax = \"proto3\";\n"
|
|
"message Foo {\n"
|
|
" optional int32 i = 1;\n"
|
|
"}\n");
|
|
|
|
Run("protocol_compiler --experimental_allow_proto3_optional "
|
|
"--proto_path=$tmpdir google/foo.proto --test_out=$tmpdir");
|
|
ExpectNoErrors();
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, PrintFreeFieldNumbers) {
|
|
CreateTempFile("foo.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"package foo;\n"
|
|
"message Foo {\n"
|
|
" optional int32 a = 2;\n"
|
|
" optional string b = 4;\n"
|
|
" optional string c = 5;\n"
|
|
" optional int64 d = 8;\n"
|
|
" optional double e = 10;\n"
|
|
"}\n");
|
|
CreateTempFile("bar.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Bar {\n"
|
|
" optional int32 a = 2;\n"
|
|
" extensions 4 to 5;\n"
|
|
" optional int64 d = 8;\n"
|
|
" extensions 10;\n"
|
|
"}\n");
|
|
CreateTempFile("baz.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Baz {\n"
|
|
" optional int32 a = 2;\n"
|
|
" optional int64 d = 8;\n"
|
|
" extensions 15 to max;\n" // unordered.
|
|
" extensions 13;\n"
|
|
" extensions 10 to 12;\n"
|
|
" extensions 5;\n"
|
|
" extensions 4;\n"
|
|
"}\n");
|
|
CreateTempFile(
|
|
"quz.proto",
|
|
"syntax = \"proto2\";\n"
|
|
"message Quz {\n"
|
|
" message Foo {}\n" // nested message
|
|
" optional int32 a = 2;\n"
|
|
" optional group C = 4 {\n"
|
|
" optional int32 d = 5;\n"
|
|
" }\n"
|
|
" extensions 8 to 10;\n"
|
|
" optional group E = 11 {\n"
|
|
" optional int32 f = 9;\n" // explicitly reuse extension range 8-10
|
|
" optional group G = 15 {\n" // nested group
|
|
" message Foo {}\n" // nested message inside nested group
|
|
" }\n"
|
|
" }\n"
|
|
"}\n");
|
|
|
|
Run("protocol_compiler --print_free_field_numbers --proto_path=$tmpdir "
|
|
"foo.proto bar.proto baz.proto quz.proto");
|
|
|
|
ExpectNoErrors();
|
|
|
|
// TODO(jieluo): Cygwin doesn't work well if we try to capture stderr and
|
|
// stdout at the same time. Need to figure out why and add this test back
|
|
// for Cygwin.
|
|
#if !defined(__CYGWIN__)
|
|
ExpectCapturedStdout(
|
|
"foo.Foo free: 1 3 6-7 9 11-INF\n"
|
|
"Bar free: 1 3 6-7 9 11-INF\n"
|
|
"Baz free: 1 3 6-7 9 14\n"
|
|
"Quz.Foo free: 1-INF\n"
|
|
"Quz.C free: 1-4 6-INF\n"
|
|
"Quz.E.G.Foo free: 1-INF\n"
|
|
"Quz.E.G free: 1-INF\n"
|
|
"Quz.E free: 1-8 10-14 16-INF\n"
|
|
"Quz free: 1 3 5-7 12-INF\n");
|
|
#endif
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, TargetTypeEnforcement) {
|
|
// The target option on a field indicates what kind of entity it may apply to
|
|
// when it is used as an option. This test verifies that the enforcement
|
|
// works correctly on all entity types.
|
|
CreateTempFile("net/proto2/proto/descriptor.proto",
|
|
google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
|
|
CreateTempFile("foo.proto",
|
|
R"schema(
|
|
syntax = "proto2";
|
|
package protobuf_unittest;
|
|
import "net/proto2/proto/descriptor.proto";
|
|
message MyOptions {
|
|
optional string file_option = 1 [targets = TARGET_TYPE_FILE];
|
|
optional string extension_range_option = 2 [targets =
|
|
TARGET_TYPE_EXTENSION_RANGE];
|
|
optional string message_option = 3 [targets = TARGET_TYPE_MESSAGE];
|
|
optional string field_option = 4 [targets = TARGET_TYPE_FIELD];
|
|
optional string oneof_option = 5 [targets = TARGET_TYPE_ONEOF];
|
|
optional string enum_option = 6 [targets = TARGET_TYPE_ENUM];
|
|
optional string enum_value_option = 7 [targets =
|
|
TARGET_TYPE_ENUM_ENTRY];
|
|
optional string service_option = 8 [targets = TARGET_TYPE_SERVICE];
|
|
optional string method_option = 9 [targets = TARGET_TYPE_METHOD];
|
|
}
|
|
extend google.protobuf.FileOptions {
|
|
optional MyOptions file_options = 5000;
|
|
}
|
|
extend google.protobuf.ExtensionRangeOptions {
|
|
optional MyOptions extension_range_options = 5000;
|
|
}
|
|
extend google.protobuf.MessageOptions {
|
|
optional MyOptions message_options = 5000;
|
|
}
|
|
extend google.protobuf.FieldOptions {
|
|
optional MyOptions field_options = 5000;
|
|
}
|
|
extend google.protobuf.OneofOptions {
|
|
optional MyOptions oneof_options = 5000;
|
|
}
|
|
extend google.protobuf.EnumOptions {
|
|
optional MyOptions enum_options = 5000;
|
|
}
|
|
extend google.protobuf.EnumValueOptions {
|
|
optional MyOptions enum_value_options = 5000;
|
|
}
|
|
extend google.protobuf.ServiceOptions {
|
|
optional MyOptions service_options = 5000;
|
|
}
|
|
extend google.protobuf.MethodOptions {
|
|
optional MyOptions method_options = 5000;
|
|
}
|
|
option (file_options).enum_option = "x";
|
|
message MyMessage {
|
|
option (message_options).enum_option = "x";
|
|
optional int32 i = 1 [(field_options).enum_option = "x"];
|
|
extensions 2 [(extension_range_options).enum_option = "x"];
|
|
oneof o {
|
|
option (oneof_options).enum_option = "x";
|
|
bool oneof_field = 3;
|
|
}
|
|
}
|
|
enum MyEnum {
|
|
option (enum_options).file_option = "x";
|
|
UNKNOWN_MY_ENUM = 0 [(enum_value_options).enum_option = "x"];
|
|
}
|
|
service MyService {
|
|
option (service_options).enum_option = "x";
|
|
rpc MyMethod(MyMessage) returns (MyMessage) {
|
|
option (method_options).enum_option = "x";
|
|
}
|
|
}
|
|
)schema");
|
|
|
|
Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
|
|
ExpectErrorSubstring(
|
|
"Option protobuf_unittest.MyOptions.enum_option "
|
|
"cannot be set on an entity of type `file`.");
|
|
ExpectErrorSubstring(
|
|
"Option protobuf_unittest.MyOptions.enum_option "
|
|
"cannot be set on an entity of type `extension range`.");
|
|
ExpectErrorSubstring(
|
|
"Option protobuf_unittest.MyOptions.enum_option "
|
|
"cannot be set on an entity of type `message`.");
|
|
ExpectErrorSubstring(
|
|
"Option protobuf_unittest.MyOptions.enum_option "
|
|
"cannot be set on an entity of type `field`.");
|
|
ExpectErrorSubstring(
|
|
"Option protobuf_unittest.MyOptions.enum_option "
|
|
"cannot be set on an entity of type `oneof`.");
|
|
ExpectErrorSubstring(
|
|
"Option protobuf_unittest.MyOptions.file_option "
|
|
"cannot be set on an entity of type `enum`.");
|
|
ExpectErrorSubstring(
|
|
"Option protobuf_unittest.MyOptions.enum_option "
|
|
"cannot be set on an entity of type `enum entry`.");
|
|
ExpectErrorSubstring(
|
|
"Option protobuf_unittest.MyOptions.enum_option "
|
|
"cannot be set on an entity of type `service`.");
|
|
ExpectErrorSubstring(
|
|
"Option protobuf_unittest.MyOptions.enum_option "
|
|
"cannot be set on an entity of type `method`.");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, TargetTypeEnforcementMultipleTargetsValid) {
|
|
CreateTempFile("net/proto2/proto/descriptor.proto",
|
|
google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
|
|
CreateTempFile("foo.proto",
|
|
R"schema(
|
|
syntax = "proto2";
|
|
package protobuf_unittest;
|
|
import "net/proto2/proto/descriptor.proto";
|
|
message MyOptions {
|
|
optional string message_or_file_option = 1 [
|
|
targets = TARGET_TYPE_MESSAGE, targets = TARGET_TYPE_FILE];
|
|
}
|
|
extend google.protobuf.FileOptions {
|
|
optional MyOptions file_options = 5000;
|
|
}
|
|
extend google.protobuf.MessageOptions {
|
|
optional MyOptions message_options = 5000;
|
|
}
|
|
option (file_options).message_or_file_option = "x";
|
|
message MyMessage {
|
|
option (message_options).message_or_file_option = "y";
|
|
}
|
|
)schema");
|
|
|
|
Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
|
|
ExpectNoErrors();
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest, TargetTypeEnforcementMultipleTargetsInvalid) {
|
|
CreateTempFile("net/proto2/proto/descriptor.proto",
|
|
google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
|
|
CreateTempFile("foo.proto",
|
|
R"schema(
|
|
syntax = "proto2";
|
|
package protobuf_unittest;
|
|
import "net/proto2/proto/descriptor.proto";
|
|
message MyOptions {
|
|
optional string message_or_file_option = 1 [
|
|
targets = TARGET_TYPE_MESSAGE, targets = TARGET_TYPE_FILE];
|
|
}
|
|
extend google.protobuf.EnumOptions {
|
|
optional MyOptions enum_options = 5000;
|
|
}
|
|
enum MyEnum {
|
|
MY_ENUM_UNSPECIFIED = 0;
|
|
option (enum_options).message_or_file_option = "y";
|
|
}
|
|
)schema");
|
|
|
|
Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
|
|
ExpectErrorSubstring(
|
|
"Option protobuf_unittest.MyOptions.message_or_file_option cannot be set "
|
|
"on an entity of type `enum`.");
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest,
|
|
TargetTypeEnforcementMultipleEdgesWithConstraintsValid) {
|
|
CreateTempFile("net/proto2/proto/descriptor.proto",
|
|
google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
|
|
CreateTempFile("foo.proto",
|
|
R"schema(
|
|
syntax = "proto2";
|
|
package protobuf_unittest;
|
|
import "net/proto2/proto/descriptor.proto";
|
|
message A {
|
|
optional B b = 1 [targets = TARGET_TYPE_FILE,
|
|
targets = TARGET_TYPE_ENUM];
|
|
}
|
|
message B {
|
|
optional int32 i = 1 [targets = TARGET_TYPE_ONEOF,
|
|
targets = TARGET_TYPE_FILE];
|
|
}
|
|
extend google.protobuf.FileOptions {
|
|
optional A file_options = 5000;
|
|
}
|
|
option (file_options).b.i = 42;
|
|
)schema");
|
|
|
|
Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
|
|
ExpectNoErrors();
|
|
}
|
|
|
|
TEST_F(CommandLineInterfaceTest,
|
|
TargetTypeEnforcementMultipleEdgesWithConstraintsInvalid) {
|
|
CreateTempFile("net/proto2/proto/descriptor.proto",
|
|
google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
|
|
CreateTempFile("foo.proto",
|
|
R"schema(
|
|
syntax = "proto2";
|
|
package protobuf_unittest;
|
|
import "net/proto2/proto/descriptor.proto";
|
|
message A {
|
|
optional B b = 1 [targets = TARGET_TYPE_ENUM];
|
|
}
|
|
message B {
|
|
optional int32 i = 1 [targets = TARGET_TYPE_ONEOF];
|
|
}
|
|
extend google.protobuf.FileOptions {
|
|
optional A file_options = 5000;
|
|
}
|
|
option (file_options).b.i = 42;
|
|
)schema");
|
|
|
|
Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
|
|
// We have target constraint violations at two different edges in the file
|
|
// options, so let's make sure both are caught.
|
|
ExpectErrorSubstring(
|
|
"Option protobuf_unittest.A.b cannot be set on an entity of type `file`.");
|
|
ExpectErrorSubstring(
|
|
"Option protobuf_unittest.B.i cannot be set on an entity of type `file`.");
|
|
}
|
|
|
|
// ===================================================================
|
|
|
|
// Test for --encode and --decode. Note that it would be easier to do this
|
|
// test as a shell script, but we'd like to be able to run the test on
|
|
// platforms that don't have a Bourne-compatible shell available (especially
|
|
// Windows/MSVC).
|
|
|
|
enum EncodeDecodeTestMode { PROTO_PATH, DESCRIPTOR_SET_IN };
|
|
|
|
class EncodeDecodeTest : public testing::TestWithParam<EncodeDecodeTestMode> {
|
|
protected:
|
|
void SetUp() override {
|
|
WriteUnittestProtoDescriptorSet();
|
|
duped_stdin_ = dup(STDIN_FILENO);
|
|
}
|
|
|
|
void TearDown() override {
|
|
dup2(duped_stdin_, STDIN_FILENO);
|
|
close(duped_stdin_);
|
|
}
|
|
|
|
void RedirectStdinFromText(const std::string& input) {
|
|
std::string filename = absl::StrCat(TestTempDir(), "/test_stdin");
|
|
ABSL_CHECK_OK(File::SetContents(filename, input, true));
|
|
ABSL_CHECK(RedirectStdinFromFile(filename));
|
|
}
|
|
|
|
bool RedirectStdinFromFile(const std::string& filename) {
|
|
int fd = open(filename.c_str(), O_RDONLY);
|
|
if (fd < 0) return false;
|
|
dup2(fd, STDIN_FILENO);
|
|
close(fd);
|
|
return true;
|
|
}
|
|
|
|
// Remove '\r' characters from text.
|
|
std::string StripCR(const std::string& text) {
|
|
std::string result;
|
|
|
|
for (int i = 0; i < text.size(); i++) {
|
|
if (text[i] != '\r') {
|
|
result.push_back(text[i]);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
enum Type { TEXT, BINARY };
|
|
enum ReturnCode { SUCCESS, ERROR };
|
|
|
|
bool Run(const std::string& command, bool specify_proto_files = true) {
|
|
std::vector<std::string> args;
|
|
args.push_back("protoc");
|
|
for (absl::string_view split_piece :
|
|
absl::StrSplit(command, " ", absl::SkipEmpty())) {
|
|
args.push_back(std::string(split_piece));
|
|
}
|
|
if (specify_proto_files) {
|
|
switch (GetParam()) {
|
|
case PROTO_PATH:
|
|
args.push_back(
|
|
absl::StrCat("--proto_path=", TestUtil::TestSourceDir()));
|
|
break;
|
|
case DESCRIPTOR_SET_IN:
|
|
args.push_back(absl::StrCat("--descriptor_set_in=",
|
|
unittest_proto_descriptor_set_filename_));
|
|
break;
|
|
default:
|
|
ADD_FAILURE() << "unexpected EncodeDecodeTestMode: " << GetParam();
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<const char*[]> argv(new const char*[args.size()]);
|
|
for (int i = 0; i < args.size(); i++) {
|
|
argv[i] = args[i].c_str();
|
|
}
|
|
|
|
CommandLineInterface cli;
|
|
|
|
CaptureTestStdout();
|
|
CaptureTestStderr();
|
|
|
|
int result = cli.Run(args.size(), argv.get());
|
|
|
|
captured_stdout_ = GetCapturedTestStdout();
|
|
captured_stderr_ = GetCapturedTestStderr();
|
|
|
|
return result == 0;
|
|
}
|
|
|
|
void ExpectStdoutMatchesBinaryFile(const std::string& filename) {
|
|
std::string expected_output;
|
|
ABSL_CHECK_OK(
|
|
File::GetContents(filename, &expected_output, true));
|
|
|
|
// Don't use EXPECT_EQ because we don't want to print raw binary data to
|
|
// stdout on failure.
|
|
EXPECT_TRUE(captured_stdout_ == expected_output);
|
|
}
|
|
|
|
void ExpectStdoutMatchesTextFile(const std::string& filename) {
|
|
std::string expected_output;
|
|
ABSL_CHECK_OK(
|
|
File::GetContents(filename, &expected_output, true));
|
|
|
|
ExpectStdoutMatchesText(expected_output);
|
|
}
|
|
|
|
void ExpectStdoutMatchesText(const std::string& expected_text) {
|
|
EXPECT_EQ(StripCR(expected_text), StripCR(captured_stdout_));
|
|
}
|
|
|
|
void ExpectStderrMatchesText(const std::string& expected_text) {
|
|
EXPECT_EQ(StripCR(expected_text), StripCR(captured_stderr_));
|
|
}
|
|
|
|
void ExpectStderrContainsText(const std::string& expected_text) {
|
|
EXPECT_NE(StripCR(captured_stderr_).find(StripCR(expected_text)),
|
|
std::string::npos);
|
|
}
|
|
|
|
private:
|
|
void WriteUnittestProtoDescriptorSet() {
|
|
unittest_proto_descriptor_set_filename_ =
|
|
absl::StrCat(TestTempDir(), "/unittest_proto_descriptor_set.bin");
|
|
FileDescriptorSet file_descriptor_set;
|
|
protobuf_unittest::TestAllTypes test_all_types;
|
|
test_all_types.descriptor()->file()->CopyTo(file_descriptor_set.add_file());
|
|
|
|
protobuf_unittest_import::ImportMessage import_message;
|
|
import_message.descriptor()->file()->CopyTo(file_descriptor_set.add_file());
|
|
|
|
protobuf_unittest_import::PublicImportMessage public_import_message;
|
|
public_import_message.descriptor()->file()->CopyTo(
|
|
file_descriptor_set.add_file());
|
|
ABSL_DCHECK(file_descriptor_set.IsInitialized());
|
|
|
|
std::string binary_proto;
|
|
ABSL_CHECK(file_descriptor_set.SerializeToString(&binary_proto));
|
|
ABSL_CHECK_OK(File::SetContents(unittest_proto_descriptor_set_filename_,
|
|
binary_proto, true));
|
|
}
|
|
|
|
int duped_stdin_;
|
|
std::string captured_stdout_;
|
|
std::string captured_stderr_;
|
|
std::string unittest_proto_descriptor_set_filename_;
|
|
};
|
|
|
|
TEST_P(EncodeDecodeTest, Encode) {
|
|
RedirectStdinFromFile(TestUtil::GetTestDataPath(
|
|
"third_party/protobuf/"
|
|
"testdata/text_format_unittest_data_oneof_implemented.txt"));
|
|
std::string args;
|
|
if (GetParam() != DESCRIPTOR_SET_IN) {
|
|
args.append(
|
|
TestUtil::MaybeTranslatePath("third_party/protobuf/unittest.proto"));
|
|
}
|
|
EXPECT_TRUE(
|
|
Run(absl::StrCat(args, " --encode=protobuf_unittest.TestAllTypes")));
|
|
ExpectStdoutMatchesBinaryFile(TestUtil::GetTestDataPath(
|
|
"third_party/protobuf/testdata/golden_message_oneof_implemented"));
|
|
ExpectStderrMatchesText("");
|
|
}
|
|
|
|
TEST_P(EncodeDecodeTest, Decode) {
|
|
RedirectStdinFromFile(TestUtil::GetTestDataPath(
|
|
"third_party/protobuf/testdata/golden_message_oneof_implemented"));
|
|
EXPECT_TRUE(
|
|
Run(TestUtil::MaybeTranslatePath("third_party/protobuf/unittest.proto") +
|
|
" --decode=protobuf_unittest.TestAllTypes"));
|
|
ExpectStdoutMatchesTextFile(TestUtil::GetTestDataPath(
|
|
"third_party/protobuf/"
|
|
"testdata/text_format_unittest_data_oneof_implemented.txt"));
|
|
ExpectStderrMatchesText("");
|
|
}
|
|
|
|
TEST_P(EncodeDecodeTest, Partial) {
|
|
RedirectStdinFromText("");
|
|
EXPECT_TRUE(
|
|
Run(TestUtil::MaybeTranslatePath("third_party/protobuf/unittest.proto") +
|
|
" --encode=protobuf_unittest.TestRequired"));
|
|
ExpectStdoutMatchesText("");
|
|
ExpectStderrMatchesText(
|
|
"warning: Input message is missing required fields: a, b, c\n");
|
|
}
|
|
|
|
TEST_P(EncodeDecodeTest, DecodeRaw) {
|
|
protobuf_unittest::TestAllTypes message;
|
|
message.set_optional_int32(123);
|
|
message.set_optional_string("foo");
|
|
std::string data;
|
|
message.SerializeToString(&data);
|
|
|
|
RedirectStdinFromText(data);
|
|
EXPECT_TRUE(Run("--decode_raw", /*specify_proto_files=*/false));
|
|
ExpectStdoutMatchesText(
|
|
"1: 123\n"
|
|
"14: \"foo\"\n");
|
|
ExpectStderrMatchesText("");
|
|
}
|
|
|
|
TEST_P(EncodeDecodeTest, UnknownType) {
|
|
EXPECT_FALSE(
|
|
Run(TestUtil::MaybeTranslatePath("third_party/protobuf/unittest.proto") +
|
|
" --encode=NoSuchType"));
|
|
ExpectStdoutMatchesText("");
|
|
ExpectStderrMatchesText("Type not defined: NoSuchType\n");
|
|
}
|
|
|
|
TEST_P(EncodeDecodeTest, ProtoParseError) {
|
|
EXPECT_FALSE(
|
|
Run("net/proto2/internal/no_such_file.proto "
|
|
"--encode=NoSuchType"));
|
|
ExpectStdoutMatchesText("");
|
|
ExpectStderrContainsText(
|
|
"net/proto2/internal/no_such_file.proto: No such file or directory\n");
|
|
}
|
|
|
|
TEST_P(EncodeDecodeTest, EncodeDeterministicOutput) {
|
|
RedirectStdinFromFile(TestUtil::GetTestDataPath(
|
|
"third_party/protobuf/"
|
|
"testdata/text_format_unittest_data_oneof_implemented.txt"));
|
|
std::string args;
|
|
if (GetParam() != DESCRIPTOR_SET_IN) {
|
|
args.append(
|
|
TestUtil::MaybeTranslatePath("third_party/protobuf/unittest.proto"));
|
|
}
|
|
EXPECT_TRUE(Run(absl::StrCat(
|
|
args, " --encode=protobuf_unittest.TestAllTypes --deterministic_output")));
|
|
ExpectStdoutMatchesBinaryFile(TestUtil::GetTestDataPath(
|
|
"third_party/protobuf/testdata/golden_message_oneof_implemented"));
|
|
ExpectStderrMatchesText("");
|
|
}
|
|
|
|
TEST_P(EncodeDecodeTest, DecodeDeterministicOutput) {
|
|
RedirectStdinFromFile(TestUtil::GetTestDataPath(
|
|
"third_party/protobuf/testdata/golden_message_oneof_implemented"));
|
|
EXPECT_FALSE(
|
|
Run(TestUtil::MaybeTranslatePath("third_party/protobuf/unittest.proto") +
|
|
" --decode=protobuf_unittest.TestAllTypes --deterministic_output"));
|
|
ExpectStderrMatchesText(
|
|
"Can only use --deterministic_output with --encode.\n");
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(FileDescriptorSetSource, EncodeDecodeTest,
|
|
testing::Values(PROTO_PATH, DESCRIPTOR_SET_IN));
|
|
} // anonymous namespace
|
|
|
|
#endif // !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
|
|
|
|
#include "google/protobuf/port_undef.inc"
|
|
|
|
} // namespace compiler
|
|
} // namespace protobuf
|
|
} // namespace google
|