This source file includes following definitions.
- DisallowPlugins
- SetInputsAreProtoPathRelative
- NullCodeGenerator
- NullCodeGenerator
- Generate
- SetUp
- TearDown
- Run
- CreateTempFile
- CreateTempDir
- ExpectNoErrors
- ExpectErrorText
- ExpectErrorSubstring
- ExpectErrorSubstringWithZeroReturnCode
- HasAlternateErrorSubstring
- ExpectGenerated
- ExpectGenerated
- ExpectGeneratedWithMultipleInputs
- ExpectGeneratedWithInsertions
- ExpectNullCodeGeneratorCalled
- ReadDescriptorSet
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- SetUp
- TearDown
- RedirectStdinFromText
- RedirectStdinFromFile
- StripCR
- Run
- ExpectStdoutMatchesBinaryFile
- ExpectStdoutMatchesTextFile
- ExpectStdoutMatchesText
- ExpectStderrMatchesText
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef _MSC_VER
#include <io.h>
#else
#include <unistd.h>
#endif
#include <vector>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/compiler/command_line_interface.h>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/mock_code_generator.h>
#include <google/protobuf/compiler/subprocess.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/unittest.pb.h>
#include <google/protobuf/testing/file.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
namespace compiler {
#if defined(_WIN32)
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif
#ifndef STDOUT_FILENO
#define STDOUT_FILENO 1
#endif
#ifndef F_OK
#define F_OK 00
#endif
#endif
namespace {
class CommandLineInterfaceTest : public testing::Test {
protected:
virtual void SetUp();
virtual void TearDown();
void Run(const string& command);
class NullCodeGenerator;
void DisallowPlugins() { disallow_plugins_ = true; }
void CreateTempFile(const string& name, const string& contents);
void CreateTempDir(const string& name);
void SetInputsAreProtoPathRelative(bool enable) {
cli_.SetInputsAreProtoPathRelative(enable);
}
void ExpectNoErrors();
void ExpectErrorText(const string& expected_text);
void ExpectErrorSubstring(const string& expected_substring);
void ExpectErrorSubstringWithZeroReturnCode(
const string& expected_substring);
bool HasAlternateErrorSubstring(const string& expected_substring);
void ExpectGenerated(const string& generator_name,
const string& parameter,
const string& proto_name,
const string& message_name);
void ExpectGenerated(const string& generator_name,
const string& parameter,
const string& proto_name,
const string& message_name,
const string& output_directory);
void ExpectGeneratedWithMultipleInputs(const string& generator_name,
const string& all_proto_names,
const string& proto_name,
const string& message_name);
void ExpectGeneratedWithInsertions(const string& generator_name,
const string& parameter,
const string& insertions,
const string& proto_name,
const string& message_name);
void ExpectNullCodeGeneratorCalled(const string& parameter);
void ReadDescriptorSet(const string& filename,
FileDescriptorSet* descriptor_set);
private:
CommandLineInterface cli_;
bool disallow_plugins_;
string temp_directory_;
int return_code_;
string error_text_;
vector<CodeGenerator*> mock_generators_to_delete_;
NullCodeGenerator* null_generator_;
};
class CommandLineInterfaceTest::NullCodeGenerator : public CodeGenerator {
public:
NullCodeGenerator() : called_(false) {}
~NullCodeGenerator() {}
mutable bool called_;
mutable string parameter_;
bool Generate(const FileDescriptor* file,
const string& parameter,
GeneratorContext* context,
string* error) const {
called_ = true;
parameter_ = parameter;
return true;
}
};
void CommandLineInterfaceTest::SetUp() {
cli_.SetInputsAreProtoPathRelative(true);
temp_directory_ = TestTempDir() + "/proto2_cli_test_temp";
if (File::Exists(temp_directory_)) {
File::DeleteRecursively(temp_directory_, NULL, NULL);
}
GOOGLE_CHECK(File::CreateDir(temp_directory_.c_str(), DEFAULT_FILE_MODE));
CodeGenerator* generator = new MockCodeGenerator("test_generator");
mock_generators_to_delete_.push_back(generator);
cli_.RegisterGenerator("--test_out", "--test_opt", generator, "Test output.");
cli_.RegisterGenerator("-t", generator, "Test output.");
generator = new MockCodeGenerator("alt_generator");
mock_generators_to_delete_.push_back(generator);
cli_.RegisterGenerator("--alt_out", generator, "Alt output.");
generator = null_generator_ = new NullCodeGenerator();
mock_generators_to_delete_.push_back(generator);
cli_.RegisterGenerator("--null_out", generator, "Null output.");
disallow_plugins_ = false;
}
void CommandLineInterfaceTest::TearDown() {
File::DeleteRecursively(temp_directory_, NULL, NULL);
for (int i = 0; i < mock_generators_to_delete_.size(); i++) {
delete mock_generators_to_delete_[i];
}
mock_generators_to_delete_.clear();
}
void CommandLineInterfaceTest::Run(const string& command) {
vector<string> args;
SplitStringUsing(command, " ", &args);
if (!disallow_plugins_) {
cli_.AllowPlugins("prefix-");
const char* possible_paths[] = {
".libs/test_plugin.exe",
"test_plugin.exe",
"test_plugin",
};
string plugin_path;
for (int i = 0; i < GOOGLE_ARRAYSIZE(possible_paths); i++) {
if (access(possible_paths[i], F_OK) == 0) {
plugin_path = possible_paths[i];
break;
}
}
if (plugin_path.empty()) {
GOOGLE_LOG(ERROR)
<< "Plugin executable not found. Plugin tests are likely to fail.";
} else {
args.push_back("--plugin=prefix-gen-plug=" + plugin_path);
}
}
scoped_array<const char*> argv(new const char*[args.size()]);
for (int i = 0; i < args.size(); i++) {
args[i] = StringReplace(args[i], "$tmpdir", temp_directory_, true);
argv[i] = args[i].c_str();
}
CaptureTestStderr();
return_code_ = cli_.Run(args.size(), argv.get());
error_text_ = GetCapturedTestStderr();
}
void CommandLineInterfaceTest::CreateTempFile(
const string& name,
const string& contents) {
string::size_type slash_pos = name.find_last_of('/');
if (slash_pos != string::npos) {
string dir = name.substr(0, slash_pos);
File::RecursivelyCreateDir(temp_directory_ + "/" + dir, 0777);
}
string full_name = temp_directory_ + "/" + name;
File::WriteStringToFileOrDie(contents, full_name);
}
void CommandLineInterfaceTest::CreateTempDir(const string& name) {
File::RecursivelyCreateDir(temp_directory_ + "/" + name, 0777);
}
void CommandLineInterfaceTest::ExpectNoErrors() {
EXPECT_EQ(0, return_code_);
EXPECT_EQ("", error_text_);
}
void CommandLineInterfaceTest::ExpectErrorText(const string& expected_text) {
EXPECT_NE(0, return_code_);
EXPECT_EQ(StringReplace(expected_text, "$tmpdir", temp_directory_, true),
error_text_);
}
void CommandLineInterfaceTest::ExpectErrorSubstring(
const string& expected_substring) {
EXPECT_NE(0, return_code_);
EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_);
}
void CommandLineInterfaceTest::ExpectErrorSubstringWithZeroReturnCode(
const string& expected_substring) {
EXPECT_EQ(0, return_code_);
EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_);
}
bool CommandLineInterfaceTest::HasAlternateErrorSubstring(
const string& expected_substring) {
EXPECT_NE(0, return_code_);
return error_text_.find(expected_substring) != string::npos;
}
void CommandLineInterfaceTest::ExpectGenerated(
const string& generator_name,
const string& parameter,
const string& proto_name,
const string& message_name) {
MockCodeGenerator::ExpectGenerated(
generator_name, parameter, "", proto_name, message_name, proto_name,
temp_directory_);
}
void CommandLineInterfaceTest::ExpectGenerated(
const string& generator_name,
const string& parameter,
const string& proto_name,
const string& message_name,
const string& output_directory) {
MockCodeGenerator::ExpectGenerated(
generator_name, parameter, "", proto_name, message_name, proto_name,
temp_directory_ + "/" + output_directory);
}
void CommandLineInterfaceTest::ExpectGeneratedWithMultipleInputs(
const string& generator_name,
const string& all_proto_names,
const string& proto_name,
const string& message_name) {
MockCodeGenerator::ExpectGenerated(
generator_name, "", "", proto_name, message_name,
all_proto_names,
temp_directory_);
}
void CommandLineInterfaceTest::ExpectGeneratedWithInsertions(
const string& generator_name,
const string& parameter,
const string& insertions,
const string& proto_name,
const string& message_name) {
MockCodeGenerator::ExpectGenerated(
generator_name, parameter, insertions, proto_name, message_name,
proto_name, temp_directory_);
}
void CommandLineInterfaceTest::ExpectNullCodeGeneratorCalled(
const string& parameter) {
EXPECT_TRUE(null_generator_->called_);
EXPECT_EQ(parameter, null_generator_->parameter_);
}
void CommandLineInterfaceTest::ReadDescriptorSet(
const string& filename, FileDescriptorSet* descriptor_set) {
string path = temp_directory_ + "/" + filename;
string file_contents;
if (!File::ReadFileToString(path, &file_contents)) {
FAIL() << "File not found: " << path;
}
if (!descriptor_set->ParseFromString(file_contents)) {
FAIL() << "Could not parse file contents: " << path;
}
}
TEST_F(CommandLineInterfaceTest, BasicOutput) {
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, BasicPlugin) {
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, GeneratorAndPlugin) {
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, MultipleInputs) {
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, MultipleInputsWithImport) {
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, CreateDirectory) {
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) {
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) {
CreateTempFile("foo.proto",
"syntax = \"proto2\";\n"
"message Foo {}\n");
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, Insert) {
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");
}
#if defined(_WIN32)
TEST_F(CommandLineInterfaceTest, WindowsOutputPath) {
CreateTempFile("foo.proto",
"syntax = \"proto2\";\n");
Run("protocol_compiler --null_out=C:\\ "
"--proto_path=$tmpdir foo.proto");
ExpectNoErrors();
ExpectNullCodeGeneratorCalled("");
}
TEST_F(CommandLineInterfaceTest, WindowsOutputPathAndParameter) {
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) {
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");
}
#endif
TEST_F(CommandLineInterfaceTest, PathLookup) {
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) {
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");
#undef PATH_SEPARATOR
#if defined(_WIN32)
#define PATH_SEPARATOR ";"
#else
#define PATH_SEPARATOR ":"
#endif
Run("protocol_compiler --test_out=$tmpdir "
"--proto_path=$tmpdir/a"PATH_SEPARATOR"$tmpdir/b foo.proto");
#undef PATH_SEPARATOR
ExpectNoErrors();
ExpectGenerated("test_generator", "", "foo.proto", "Foo");
}
TEST_F(CommandLineInterfaceTest, NonRootMapping) {
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, MultipleGenerators) {
CreateTempFile("foo.proto",
"syntax = \"proto2\";\n"
"message Foo {}\n");
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) {
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) {
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) {
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");
}
TEST_F(CommandLineInterfaceTest, CwdRelativeInputs) {
SetInputsAreProtoPathRelative(false);
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;
ASSERT_EQ(1, descriptor_set.file_size());
EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
}
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;
ASSERT_EQ(1, descriptor_set.file_size());
EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
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;
ASSERT_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());
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;
ASSERT_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());
EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
EXPECT_TRUE(descriptor_set.file(1).has_source_code_info());
}
TEST_F(CommandLineInterfaceTest, ParseErrors) {
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, ParseErrorsMultipleFiles) {
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: Import \"bar.proto\" was not found or had errors.\n"
"foo.proto: Import \"bar.proto\" was not found or had errors.\n"
"foo.proto: Import \"baz.proto\" was not found or had errors.\n");
}
TEST_F(CommandLineInterfaceTest, InputNotFoundError) {
Run("protocol_compiler --test_out=$tmpdir "
"--proto_path=$tmpdir foo.proto");
ExpectErrorText(
"foo.proto: File not found.\n");
}
TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundError) {
SetInputsAreProtoPathRelative(false);
Run("protocol_compiler --test_out=$tmpdir "
"--proto_path=$tmpdir $tmpdir/foo.proto");
ExpectErrorText(
"$tmpdir/foo.proto: No such file or directory\n");
}
TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotMappedError) {
SetInputsAreProtoPathRelative(false);
CreateTempFile("foo.proto",
"syntax = \"proto2\";\n"
"message Foo {}\n");
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) {
SetInputsAreProtoPathRelative(false);
CreateTempFile("bar/dummy", "");
Run("protocol_compiler --test_out=$tmpdir "
"--proto_path=$tmpdir/bar $tmpdir/foo.proto");
ExpectErrorText(
"$tmpdir/foo.proto: No such file or directory\n");
}
TEST_F(CommandLineInterfaceTest, CwdRelativeInputShadowedError) {
SetInputsAreProtoPathRelative(false);
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) {
Run("protocol_compiler --test_out=$tmpdir "
"--proto_path=$tmpdir/foo foo.proto");
ExpectErrorText(
"$tmpdir/foo: warning: directory does not exist.\n"
"foo.proto: File not found.\n");
}
TEST_F(CommandLineInterfaceTest, MissingInputError) {
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");
string output_file =
MockCodeGenerator::GetOutputFileName("test_generator", "foo.proto");
CreateTempDir(output_file);
Run("protocol_compiler --test_out=$tmpdir "
"--proto_path=$tmpdir foo.proto");
#if defined(_WIN32) && !defined(__CYGWIN__)
if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
return;
}
#endif
ExpectErrorSubstring(output_file + ": Is a directory");
}
TEST_F(CommandLineInterfaceTest, PluginOutputWriteError) {
CreateTempFile("foo.proto",
"syntax = \"proto2\";\n"
"message Foo {}\n");
string output_file =
MockCodeGenerator::GetOutputFileName("test_plugin", "foo.proto");
CreateTempDir(output_file);
Run("protocol_compiler --plug_out=$tmpdir "
"--proto_path=$tmpdir foo.proto");
#if defined(_WIN32) && !defined(__CYGWIN__)
if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
return;
}
#endif
ExpectErrorSubstring(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__)
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) {
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) {
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) {
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
ExpectErrorSubstring(
"--plug_out: prefix-gen-plug: Plugin failed with status code");
#else
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: 1.");
}
TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
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("--badplug_out: prefix-gen-badplug: " +
Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND));
#else
ExpectErrorSubstring(
"no_such_file: program not found or is not executable");
ExpectErrorSubstring(
"--badplug_out: prefix-gen-badplug: Plugin failed with status code 1.");
#endif
}
TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) {
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");
ExpectErrorSubstringWithZeroReturnCode("Usage: test_exec_name ");
ExpectErrorSubstringWithZeroReturnCode("--test_out=OUT_DIR");
ExpectErrorSubstringWithZeroReturnCode("Test output.");
ExpectErrorSubstringWithZeroReturnCode("--alt_out=OUT_DIR");
ExpectErrorSubstringWithZeroReturnCode("Alt output.");
}
TEST_F(CommandLineInterfaceTest, GccFormatErrors) {
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) {
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) {
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, ParseSingleCharacterFlag) {
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) {
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) {
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) {
Run("protocol_compiler --test_out --proto_path=$tmpdir foo.proto");
ExpectErrorText("Missing value for flag: --test_out\n");
}
TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) {
Run("protocol_compiler --test_out");
ExpectErrorText("Missing value for flag: --test_out\n");
}
class EncodeDecodeTest : public testing::Test {
protected:
virtual void SetUp() {
duped_stdin_ = dup(STDIN_FILENO);
}
virtual void TearDown() {
dup2(duped_stdin_, STDIN_FILENO);
close(duped_stdin_);
}
void RedirectStdinFromText(const string& input) {
string filename = TestTempDir() + "/test_stdin";
File::WriteStringToFileOrDie(input, filename);
GOOGLE_CHECK(RedirectStdinFromFile(filename));
}
bool RedirectStdinFromFile(const string& filename) {
int fd = open(filename.c_str(), O_RDONLY);
if (fd < 0) return false;
dup2(fd, STDIN_FILENO);
close(fd);
return true;
}
string StripCR(const string& text) {
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 string& command) {
vector<string> args;
args.push_back("protoc");
SplitStringUsing(command, " ", &args);
args.push_back("--proto_path=" + TestSourceDir());
scoped_array<const char*> argv(new const char*[args.size()]);
for (int i = 0; i < args.size(); i++) {
argv[i] = args[i].c_str();
}
CommandLineInterface cli;
cli.SetInputsAreProtoPathRelative(true);
CaptureTestStdout();
CaptureTestStderr();
int result = cli.Run(args.size(), argv.get());
captured_stdout_ = GetCapturedTestStdout();
captured_stderr_ = GetCapturedTestStderr();
return result == 0;
}
void ExpectStdoutMatchesBinaryFile(const string& filename) {
string expected_output;
ASSERT_TRUE(File::ReadFileToString(filename, &expected_output));
EXPECT_TRUE(captured_stdout_ == expected_output);
}
void ExpectStdoutMatchesTextFile(const string& filename) {
string expected_output;
ASSERT_TRUE(File::ReadFileToString(filename, &expected_output));
ExpectStdoutMatchesText(expected_output);
}
void ExpectStdoutMatchesText(const string& expected_text) {
EXPECT_EQ(StripCR(expected_text), StripCR(captured_stdout_));
}
void ExpectStderrMatchesText(const string& expected_text) {
EXPECT_EQ(StripCR(expected_text), StripCR(captured_stderr_));
}
private:
int duped_stdin_;
string captured_stdout_;
string captured_stderr_;
};
TEST_F(EncodeDecodeTest, Encode) {
RedirectStdinFromFile(TestSourceDir() +
"/google/protobuf/testdata/text_format_unittest_data.txt");
EXPECT_TRUE(Run("google/protobuf/unittest.proto "
"--encode=protobuf_unittest.TestAllTypes"));
ExpectStdoutMatchesBinaryFile(TestSourceDir() +
"/google/protobuf/testdata/golden_message");
ExpectStderrMatchesText("");
}
TEST_F(EncodeDecodeTest, Decode) {
RedirectStdinFromFile(TestSourceDir() +
"/google/protobuf/testdata/golden_message");
EXPECT_TRUE(Run("google/protobuf/unittest.proto "
"--decode=protobuf_unittest.TestAllTypes"));
ExpectStdoutMatchesTextFile(TestSourceDir() +
"/google/protobuf/testdata/text_format_unittest_data.txt");
ExpectStderrMatchesText("");
}
TEST_F(EncodeDecodeTest, Partial) {
RedirectStdinFromText("");
EXPECT_TRUE(Run("google/protobuf/unittest.proto "
"--encode=protobuf_unittest.TestRequired"));
ExpectStdoutMatchesText("");
ExpectStderrMatchesText(
"warning: Input message is missing required fields: a, b, c\n");
}
TEST_F(EncodeDecodeTest, DecodeRaw) {
protobuf_unittest::TestAllTypes message;
message.set_optional_int32(123);
message.set_optional_string("foo");
string data;
message.SerializeToString(&data);
RedirectStdinFromText(data);
EXPECT_TRUE(Run("--decode_raw"));
ExpectStdoutMatchesText("1: 123\n"
"14: \"foo\"\n");
ExpectStderrMatchesText("");
}
TEST_F(EncodeDecodeTest, UnknownType) {
EXPECT_FALSE(Run("google/protobuf/unittest.proto "
"--encode=NoSuchType"));
ExpectStdoutMatchesText("");
ExpectStderrMatchesText("Type not defined: NoSuchType\n");
}
TEST_F(EncodeDecodeTest, ProtoParseError) {
EXPECT_FALSE(Run("google/protobuf/no_such_file.proto "
"--encode=NoSuchType"));
ExpectStdoutMatchesText("");
ExpectStderrMatchesText(
"google/protobuf/no_such_file.proto: File not found.\n");
}
}
}
}
}