1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // http://code.google.com/p/protobuf/
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
15 // * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 // Author: kenton@google.com (Kenton Varda)
32 // Based on original Protocol Buffers design by
33 // Sanjay Ghemawat, Jeff Dean, and others.
35 #include <sys/types.h>
45 #include <google/protobuf/descriptor.pb.h>
46 #include <google/protobuf/descriptor.h>
47 #include <google/protobuf/io/zero_copy_stream.h>
48 #include <google/protobuf/compiler/command_line_interface.h>
49 #include <google/protobuf/compiler/code_generator.h>
50 #include <google/protobuf/compiler/mock_code_generator.h>
51 #include <google/protobuf/compiler/subprocess.h>
52 #include <google/protobuf/io/printer.h>
53 #include <google/protobuf/unittest.pb.h>
54 #include <google/protobuf/testing/file.h>
55 #include <google/protobuf/stubs/strutil.h>
56 #include <google/protobuf/stubs/substitute.h>
58 #include <google/protobuf/testing/googletest.h>
59 #include <gtest/gtest.h>
67 #define STDIN_FILENO 0
70 #define STDOUT_FILENO 1
73 #define F_OK 00 // not defined by MSVC for whatever reason
79 class CommandLineInterfaceTest : public testing::Test {
82 virtual void TearDown();
84 // Runs the CommandLineInterface with the given command line. The
85 // command is automatically split on spaces, and the string "$tmpdir"
86 // is replaced with TestTempDir().
87 void Run(const string& command);
89 // -----------------------------------------------------------------
90 // Methods to set up the test (called before Run()).
92 class NullCodeGenerator;
94 // Normally plugins are allowed for all tests. Call this to explicitly
96 void DisallowPlugins() { disallow_plugins_ = true; }
98 // Create a temp file within temp_directory_ with the given name.
99 // The containing directory is also created if necessary.
100 void CreateTempFile(const string& name, const string& contents);
102 // Create a subdirectory within temp_directory_.
103 void CreateTempDir(const string& name);
105 void SetInputsAreProtoPathRelative(bool enable) {
106 cli_.SetInputsAreProtoPathRelative(enable);
109 // -----------------------------------------------------------------
110 // Methods to check the test results (called after Run()).
112 // Checks that no text was written to stderr during Run(), and Run()
114 void ExpectNoErrors();
116 // Checks that Run() returned non-zero and the stderr output is exactly
117 // the text given. expected_test may contain references to "$tmpdir",
118 // which will be replaced by the temporary directory path.
119 void ExpectErrorText(const string& expected_text);
121 // Checks that Run() returned non-zero and the stderr contains the given
123 void ExpectErrorSubstring(const string& expected_substring);
125 // Like ExpectErrorSubstring, but checks that Run() returned zero.
126 void ExpectErrorSubstringWithZeroReturnCode(
127 const string& expected_substring);
129 // Returns true if ExpectErrorSubstring(expected_substring) would pass, but
130 // does not fail otherwise.
131 bool HasAlternateErrorSubstring(const string& expected_substring);
133 // Checks that MockCodeGenerator::Generate() was called in the given
134 // context (or the generator in test_plugin.cc, which produces the same
135 // output). That is, this tests if the generator with the given name
136 // was called with the given parameter and proto file and produced the
137 // given output file. This is checked by reading the output file and
138 // checking that it contains the content that MockCodeGenerator would
139 // generate given these inputs. message_name is the name of the first
140 // message that appeared in the proto file; this is just to make extra
141 // sure that the correct file was parsed.
142 void ExpectGenerated(const string& generator_name,
143 const string& parameter,
144 const string& proto_name,
145 const string& message_name);
146 void ExpectGenerated(const string& generator_name,
147 const string& parameter,
148 const string& proto_name,
149 const string& message_name,
150 const string& output_directory);
151 void ExpectGeneratedWithMultipleInputs(const string& generator_name,
152 const string& all_proto_names,
153 const string& proto_name,
154 const string& message_name);
155 void ExpectGeneratedWithInsertions(const string& generator_name,
156 const string& parameter,
157 const string& insertions,
158 const string& proto_name,
159 const string& message_name);
161 void ExpectNullCodeGeneratorCalled(const string& parameter);
163 void ReadDescriptorSet(const string& filename,
164 FileDescriptorSet* descriptor_set);
167 // The object we are testing.
168 CommandLineInterface cli_;
170 // Was DisallowPlugins() called?
171 bool disallow_plugins_;
173 // We create a directory within TestTempDir() in order to add extra
174 // protection against accidentally deleting user files (since we recursively
175 // delete this directory during the test). This is the full path of that
177 string temp_directory_;
179 // The result of Run().
182 // The captured stderr output.
185 // Pointers which need to be deleted later.
186 vector<CodeGenerator*> mock_generators_to_delete_;
188 NullCodeGenerator* null_generator_;
191 class CommandLineInterfaceTest::NullCodeGenerator : public CodeGenerator {
193 NullCodeGenerator() : called_(false) {}
194 ~NullCodeGenerator() {}
196 mutable bool called_;
197 mutable string parameter_;
199 // implements CodeGenerator ----------------------------------------
200 bool Generate(const FileDescriptor* file,
201 const string& parameter,
202 GeneratorContext* context,
203 string* error) const {
205 parameter_ = parameter;
210 // ===================================================================
212 void CommandLineInterfaceTest::SetUp() {
213 // Most of these tests were written before this option was added, so we
214 // run with the option on (which used to be the only way) except in certain
215 // tests where we turn it off.
216 cli_.SetInputsAreProtoPathRelative(true);
218 temp_directory_ = TestTempDir() + "/proto2_cli_test_temp";
220 // If the temp directory already exists, it must be left over from a
221 // previous run. Delete it.
222 if (File::Exists(temp_directory_)) {
223 File::DeleteRecursively(temp_directory_, NULL, NULL);
226 // Create the temp directory.
227 GOOGLE_CHECK(File::CreateDir(temp_directory_.c_str(), DEFAULT_FILE_MODE));
229 // Register generators.
230 CodeGenerator* generator = new MockCodeGenerator("test_generator");
231 mock_generators_to_delete_.push_back(generator);
232 cli_.RegisterGenerator("--test_out", "--test_opt", generator, "Test output.");
233 cli_.RegisterGenerator("-t", generator, "Test output.");
235 generator = new MockCodeGenerator("alt_generator");
236 mock_generators_to_delete_.push_back(generator);
237 cli_.RegisterGenerator("--alt_out", generator, "Alt output.");
239 generator = null_generator_ = new NullCodeGenerator();
240 mock_generators_to_delete_.push_back(generator);
241 cli_.RegisterGenerator("--null_out", generator, "Null output.");
243 disallow_plugins_ = false;
246 void CommandLineInterfaceTest::TearDown() {
247 // Delete the temp directory.
248 File::DeleteRecursively(temp_directory_, NULL, NULL);
250 // Delete all the MockCodeGenerators.
251 for (int i = 0; i < mock_generators_to_delete_.size(); i++) {
252 delete mock_generators_to_delete_[i];
254 mock_generators_to_delete_.clear();
257 void CommandLineInterfaceTest::Run(const string& command) {
259 SplitStringUsing(command, " ", &args);
261 if (!disallow_plugins_) {
262 cli_.AllowPlugins("prefix-");
263 const char* possible_paths[] = {
264 // When building with shared libraries, libtool hides the real executable
265 // in .libs and puts a fake wrapper in the current directory.
266 // Unfortunately, due to an apparent bug on Cygwin/MinGW, if one program
267 // wrapped in this way (e.g. protobuf-tests.exe) tries to execute another
268 // program wrapped in this way (e.g. test_plugin.exe), the latter fails
269 // with error code 127 and no explanation message. Presumably the problem
270 // is that the wrapper for protobuf-tests.exe set some environment
271 // variables that confuse the wrapper for test_plugin.exe. Luckily, it
272 // turns out that if we simply invoke the wrapped test_plugin.exe
273 // directly, it works -- I guess the environment variables set by the
274 // protobuf-tests.exe wrapper happen to be correct for it too. So we do
276 ".libs/test_plugin.exe", // Win32 w/autotool (Cygwin / MinGW)
277 "test_plugin.exe", // Other Win32 (MSVC)
278 "test_plugin", // Unix
283 for (int i = 0; i < GOOGLE_ARRAYSIZE(possible_paths); i++) {
284 if (access(possible_paths[i], F_OK) == 0) {
285 plugin_path = possible_paths[i];
290 if (plugin_path.empty()) {
292 << "Plugin executable not found. Plugin tests are likely to fail.";
294 args.push_back("--plugin=prefix-gen-plug=" + plugin_path);
298 scoped_array<const char*> argv(new const char*[args.size()]);
300 for (int i = 0; i < args.size(); i++) {
301 args[i] = StringReplace(args[i], "$tmpdir", temp_directory_, true);
302 argv[i] = args[i].c_str();
307 return_code_ = cli_.Run(args.size(), argv.get());
309 error_text_ = GetCapturedTestStderr();
312 // -------------------------------------------------------------------
314 void CommandLineInterfaceTest::CreateTempFile(
316 const string& contents) {
317 // Create parent directory, if necessary.
318 string::size_type slash_pos = name.find_last_of('/');
319 if (slash_pos != string::npos) {
320 string dir = name.substr(0, slash_pos);
321 File::RecursivelyCreateDir(temp_directory_ + "/" + dir, 0777);
325 string full_name = temp_directory_ + "/" + name;
326 File::WriteStringToFileOrDie(contents, full_name);
329 void CommandLineInterfaceTest::CreateTempDir(const string& name) {
330 File::RecursivelyCreateDir(temp_directory_ + "/" + name, 0777);
333 // -------------------------------------------------------------------
335 void CommandLineInterfaceTest::ExpectNoErrors() {
336 EXPECT_EQ(0, return_code_);
337 EXPECT_EQ("", error_text_);
340 void CommandLineInterfaceTest::ExpectErrorText(const string& expected_text) {
341 EXPECT_NE(0, return_code_);
342 EXPECT_EQ(StringReplace(expected_text, "$tmpdir", temp_directory_, true),
346 void CommandLineInterfaceTest::ExpectErrorSubstring(
347 const string& expected_substring) {
348 EXPECT_NE(0, return_code_);
349 EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_);
352 void CommandLineInterfaceTest::ExpectErrorSubstringWithZeroReturnCode(
353 const string& expected_substring) {
354 EXPECT_EQ(0, return_code_);
355 EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_);
358 bool CommandLineInterfaceTest::HasAlternateErrorSubstring(
359 const string& expected_substring) {
360 EXPECT_NE(0, return_code_);
361 return error_text_.find(expected_substring) != string::npos;
364 void CommandLineInterfaceTest::ExpectGenerated(
365 const string& generator_name,
366 const string& parameter,
367 const string& proto_name,
368 const string& message_name) {
369 MockCodeGenerator::ExpectGenerated(
370 generator_name, parameter, "", proto_name, message_name, proto_name,
374 void CommandLineInterfaceTest::ExpectGenerated(
375 const string& generator_name,
376 const string& parameter,
377 const string& proto_name,
378 const string& message_name,
379 const string& output_directory) {
380 MockCodeGenerator::ExpectGenerated(
381 generator_name, parameter, "", proto_name, message_name, proto_name,
382 temp_directory_ + "/" + output_directory);
385 void CommandLineInterfaceTest::ExpectGeneratedWithMultipleInputs(
386 const string& generator_name,
387 const string& all_proto_names,
388 const string& proto_name,
389 const string& message_name) {
390 MockCodeGenerator::ExpectGenerated(
391 generator_name, "", "", proto_name, message_name,
396 void CommandLineInterfaceTest::ExpectGeneratedWithInsertions(
397 const string& generator_name,
398 const string& parameter,
399 const string& insertions,
400 const string& proto_name,
401 const string& message_name) {
402 MockCodeGenerator::ExpectGenerated(
403 generator_name, parameter, insertions, proto_name, message_name,
404 proto_name, temp_directory_);
407 void CommandLineInterfaceTest::ExpectNullCodeGeneratorCalled(
408 const string& parameter) {
409 EXPECT_TRUE(null_generator_->called_);
410 EXPECT_EQ(parameter, null_generator_->parameter_);
413 void CommandLineInterfaceTest::ReadDescriptorSet(
414 const string& filename, FileDescriptorSet* descriptor_set) {
415 string path = temp_directory_ + "/" + filename;
416 string file_contents;
417 if (!File::ReadFileToString(path, &file_contents)) {
418 FAIL() << "File not found: " << path;
420 if (!descriptor_set->ParseFromString(file_contents)) {
421 FAIL() << "Could not parse file contents: " << path;
425 // ===================================================================
427 TEST_F(CommandLineInterfaceTest, BasicOutput) {
428 // Test that the common case works.
430 CreateTempFile("foo.proto",
431 "syntax = \"proto2\";\n"
434 Run("protocol_compiler --test_out=$tmpdir "
435 "--proto_path=$tmpdir foo.proto");
438 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
441 TEST_F(CommandLineInterfaceTest, BasicPlugin) {
442 // Test that basic plugins work.
444 CreateTempFile("foo.proto",
445 "syntax = \"proto2\";\n"
448 Run("protocol_compiler --plug_out=$tmpdir "
449 "--proto_path=$tmpdir foo.proto");
452 ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
455 TEST_F(CommandLineInterfaceTest, GeneratorAndPlugin) {
456 // Invoke a generator and a plugin at the same time.
458 CreateTempFile("foo.proto",
459 "syntax = \"proto2\";\n"
462 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
463 "--proto_path=$tmpdir foo.proto");
466 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
467 ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
470 TEST_F(CommandLineInterfaceTest, MultipleInputs) {
471 // Test parsing multiple input files.
473 CreateTempFile("foo.proto",
474 "syntax = \"proto2\";\n"
476 CreateTempFile("bar.proto",
477 "syntax = \"proto2\";\n"
480 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
481 "--proto_path=$tmpdir foo.proto bar.proto");
484 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
486 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
488 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
490 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
494 TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport) {
495 // Test parsing multiple input files with an import of a separate file.
497 CreateTempFile("foo.proto",
498 "syntax = \"proto2\";\n"
500 CreateTempFile("bar.proto",
501 "syntax = \"proto2\";\n"
502 "import \"baz.proto\";\n"
504 " optional Baz a = 1;\n"
506 CreateTempFile("baz.proto",
507 "syntax = \"proto2\";\n"
510 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
511 "--proto_path=$tmpdir foo.proto bar.proto");
514 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
516 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
518 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
520 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
524 TEST_F(CommandLineInterfaceTest, CreateDirectory) {
525 // Test that when we output to a sub-directory, it is created.
527 CreateTempFile("bar/baz/foo.proto",
528 "syntax = \"proto2\";\n"
530 CreateTempDir("out");
531 CreateTempDir("plugout");
533 Run("protocol_compiler --test_out=$tmpdir/out --plug_out=$tmpdir/plugout "
534 "--proto_path=$tmpdir bar/baz/foo.proto");
537 ExpectGenerated("test_generator", "", "bar/baz/foo.proto", "Foo", "out");
538 ExpectGenerated("test_plugin", "", "bar/baz/foo.proto", "Foo", "plugout");
541 TEST_F(CommandLineInterfaceTest, GeneratorParameters) {
542 // Test that generator parameters are correctly parsed from the command line.
544 CreateTempFile("foo.proto",
545 "syntax = \"proto2\";\n"
548 Run("protocol_compiler --test_out=TestParameter:$tmpdir "
549 "--plug_out=TestPluginParameter:$tmpdir "
550 "--proto_path=$tmpdir foo.proto");
553 ExpectGenerated("test_generator", "TestParameter", "foo.proto", "Foo");
554 ExpectGenerated("test_plugin", "TestPluginParameter", "foo.proto", "Foo");
557 TEST_F(CommandLineInterfaceTest, ExtraGeneratorParameters) {
558 // Test that generator parameters specified with the option flag are
559 // correctly passed to the code generator.
561 CreateTempFile("foo.proto",
562 "syntax = \"proto2\";\n"
564 // Create the "a" and "b" sub-directories.
568 Run("protocol_compiler "
570 "--test_out=bar:$tmpdir/a "
572 "--test_out=baz:$tmpdir/b "
574 "--proto_path=$tmpdir foo.proto");
578 "test_generator", "bar,foo1,foo2,foo3", "foo.proto", "Foo", "a");
580 "test_generator", "baz,foo1,foo2,foo3", "foo.proto", "Foo", "b");
583 TEST_F(CommandLineInterfaceTest, Insert) {
584 // Test running a generator that inserts code into another's output.
586 CreateTempFile("foo.proto",
587 "syntax = \"proto2\";\n"
590 Run("protocol_compiler "
591 "--test_out=TestParameter:$tmpdir "
592 "--plug_out=TestPluginParameter:$tmpdir "
593 "--test_out=insert=test_generator,test_plugin:$tmpdir "
594 "--plug_out=insert=test_generator,test_plugin:$tmpdir "
595 "--proto_path=$tmpdir foo.proto");
598 ExpectGeneratedWithInsertions(
599 "test_generator", "TestParameter", "test_generator,test_plugin",
601 ExpectGeneratedWithInsertions(
602 "test_plugin", "TestPluginParameter", "test_generator,test_plugin",
608 TEST_F(CommandLineInterfaceTest, WindowsOutputPath) {
609 // Test that the output path can be a Windows-style path.
611 CreateTempFile("foo.proto",
612 "syntax = \"proto2\";\n");
614 Run("protocol_compiler --null_out=C:\\ "
615 "--proto_path=$tmpdir foo.proto");
618 ExpectNullCodeGeneratorCalled("");
621 TEST_F(CommandLineInterfaceTest, WindowsOutputPathAndParameter) {
622 // Test that we can have a windows-style output path and a parameter.
624 CreateTempFile("foo.proto",
625 "syntax = \"proto2\";\n");
627 Run("protocol_compiler --null_out=bar:C:\\ "
628 "--proto_path=$tmpdir foo.proto");
631 ExpectNullCodeGeneratorCalled("bar");
634 TEST_F(CommandLineInterfaceTest, TrailingBackslash) {
635 // Test that the directories can end in backslashes. Some users claim this
636 // doesn't work on their system.
638 CreateTempFile("foo.proto",
639 "syntax = \"proto2\";\n"
642 Run("protocol_compiler --test_out=$tmpdir\\ "
643 "--proto_path=$tmpdir\\ foo.proto");
646 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
649 #endif // defined(_WIN32) || defined(__CYGWIN__)
651 TEST_F(CommandLineInterfaceTest, PathLookup) {
652 // Test that specifying multiple directories in the proto search path works.
654 CreateTempFile("b/bar.proto",
655 "syntax = \"proto2\";\n"
657 CreateTempFile("a/foo.proto",
658 "syntax = \"proto2\";\n"
659 "import \"bar.proto\";\n"
661 " optional Bar a = 1;\n"
663 CreateTempFile("b/foo.proto", "this should not be parsed\n");
665 Run("protocol_compiler --test_out=$tmpdir "
666 "--proto_path=$tmpdir/a --proto_path=$tmpdir/b foo.proto");
669 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
672 TEST_F(CommandLineInterfaceTest, ColonDelimitedPath) {
673 // Same as PathLookup, but we provide the proto_path in a single flag.
675 CreateTempFile("b/bar.proto",
676 "syntax = \"proto2\";\n"
678 CreateTempFile("a/foo.proto",
679 "syntax = \"proto2\";\n"
680 "import \"bar.proto\";\n"
682 " optional Bar a = 1;\n"
684 CreateTempFile("b/foo.proto", "this should not be parsed\n");
686 #undef PATH_SEPARATOR
688 #define PATH_SEPARATOR ";"
690 #define PATH_SEPARATOR ":"
693 Run("protocol_compiler --test_out=$tmpdir "
694 "--proto_path=$tmpdir/a"PATH_SEPARATOR"$tmpdir/b foo.proto");
696 #undef PATH_SEPARATOR
699 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
702 TEST_F(CommandLineInterfaceTest, NonRootMapping) {
703 // Test setting up a search path mapping a directory to a non-root location.
705 CreateTempFile("foo.proto",
706 "syntax = \"proto2\";\n"
709 Run("protocol_compiler --test_out=$tmpdir "
710 "--proto_path=bar=$tmpdir bar/foo.proto");
713 ExpectGenerated("test_generator", "", "bar/foo.proto", "Foo");
716 TEST_F(CommandLineInterfaceTest, MultipleGenerators) {
717 // Test that we can have multiple generators and use both in one invocation,
718 // each with a different output directory.
720 CreateTempFile("foo.proto",
721 "syntax = \"proto2\";\n"
723 // Create the "a" and "b" sub-directories.
727 Run("protocol_compiler "
728 "--test_out=$tmpdir/a "
729 "--alt_out=$tmpdir/b "
730 "--proto_path=$tmpdir foo.proto");
733 ExpectGenerated("test_generator", "", "foo.proto", "Foo", "a");
734 ExpectGenerated("alt_generator", "", "foo.proto", "Foo", "b");
737 TEST_F(CommandLineInterfaceTest, DisallowServicesNoServices) {
738 // Test that --disallow_services doesn't cause a problem when there are no
741 CreateTempFile("foo.proto",
742 "syntax = \"proto2\";\n"
745 Run("protocol_compiler --disallow_services --test_out=$tmpdir "
746 "--proto_path=$tmpdir foo.proto");
749 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
752 TEST_F(CommandLineInterfaceTest, DisallowServicesHasService) {
753 // Test that --disallow_services produces an error when there are services.
755 CreateTempFile("foo.proto",
756 "syntax = \"proto2\";\n"
760 Run("protocol_compiler --disallow_services --test_out=$tmpdir "
761 "--proto_path=$tmpdir foo.proto");
763 ExpectErrorSubstring("foo.proto: This file contains services");
766 TEST_F(CommandLineInterfaceTest, AllowServicesHasService) {
767 // Test that services work fine as long as --disallow_services is not used.
769 CreateTempFile("foo.proto",
770 "syntax = \"proto2\";\n"
774 Run("protocol_compiler --test_out=$tmpdir "
775 "--proto_path=$tmpdir foo.proto");
778 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
781 TEST_F(CommandLineInterfaceTest, CwdRelativeInputs) {
782 // Test that we can accept working-directory-relative input files.
784 SetInputsAreProtoPathRelative(false);
786 CreateTempFile("foo.proto",
787 "syntax = \"proto2\";\n"
790 Run("protocol_compiler --test_out=$tmpdir "
791 "--proto_path=$tmpdir $tmpdir/foo.proto");
794 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
797 TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) {
798 CreateTempFile("foo.proto",
799 "syntax = \"proto2\";\n"
801 CreateTempFile("bar.proto",
802 "syntax = \"proto2\";\n"
803 "import \"foo.proto\";\n"
805 " optional Foo foo = 1;\n"
808 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
809 "--proto_path=$tmpdir bar.proto");
813 FileDescriptorSet descriptor_set;
814 ReadDescriptorSet("descriptor_set", &descriptor_set);
815 if (HasFatalFailure()) return;
816 ASSERT_EQ(1, descriptor_set.file_size());
817 EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
818 // Descriptor set should not have source code info.
819 EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
822 TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) {
823 CreateTempFile("foo.proto",
824 "syntax = \"proto2\";\n"
826 CreateTempFile("bar.proto",
827 "syntax = \"proto2\";\n"
828 "import \"foo.proto\";\n"
830 " optional Foo foo = 1;\n"
833 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
834 "--include_source_info --proto_path=$tmpdir bar.proto");
838 FileDescriptorSet descriptor_set;
839 ReadDescriptorSet("descriptor_set", &descriptor_set);
840 if (HasFatalFailure()) return;
841 ASSERT_EQ(1, descriptor_set.file_size());
842 EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
843 // Source code info included.
844 EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
847 TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) {
848 CreateTempFile("foo.proto",
849 "syntax = \"proto2\";\n"
851 CreateTempFile("bar.proto",
852 "syntax = \"proto2\";\n"
853 "import \"foo.proto\";\n"
855 " optional Foo foo = 1;\n"
858 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
859 "--include_imports --proto_path=$tmpdir bar.proto");
863 FileDescriptorSet descriptor_set;
864 ReadDescriptorSet("descriptor_set", &descriptor_set);
865 if (HasFatalFailure()) return;
866 ASSERT_EQ(2, descriptor_set.file_size());
867 if (descriptor_set.file(0).name() == "bar.proto") {
868 std::swap(descriptor_set.mutable_file()->mutable_data()[0],
869 descriptor_set.mutable_file()->mutable_data()[1]);
871 EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
872 EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
873 // Descriptor set should not have source code info.
874 EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
875 EXPECT_FALSE(descriptor_set.file(1).has_source_code_info());
878 TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSetWithSourceInfo) {
879 CreateTempFile("foo.proto",
880 "syntax = \"proto2\";\n"
882 CreateTempFile("bar.proto",
883 "syntax = \"proto2\";\n"
884 "import \"foo.proto\";\n"
886 " optional Foo foo = 1;\n"
889 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
890 "--include_imports --include_source_info --proto_path=$tmpdir bar.proto");
894 FileDescriptorSet descriptor_set;
895 ReadDescriptorSet("descriptor_set", &descriptor_set);
896 if (HasFatalFailure()) return;
897 ASSERT_EQ(2, descriptor_set.file_size());
898 if (descriptor_set.file(0).name() == "bar.proto") {
899 std::swap(descriptor_set.mutable_file()->mutable_data()[0],
900 descriptor_set.mutable_file()->mutable_data()[1]);
902 EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
903 EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
904 // Source code info included.
905 EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
906 EXPECT_TRUE(descriptor_set.file(1).has_source_code_info());
909 // -------------------------------------------------------------------
911 TEST_F(CommandLineInterfaceTest, ParseErrors) {
912 // Test that parse errors are reported.
914 CreateTempFile("foo.proto",
915 "syntax = \"proto2\";\n"
918 Run("protocol_compiler --test_out=$tmpdir "
919 "--proto_path=$tmpdir foo.proto");
922 "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
925 TEST_F(CommandLineInterfaceTest, ParseErrorsMultipleFiles) {
926 // Test that parse errors are reported from multiple files.
928 // We set up files such that foo.proto actually depends on bar.proto in
929 // two ways: Directly and through baz.proto. bar.proto's errors should
930 // only be reported once.
931 CreateTempFile("bar.proto",
932 "syntax = \"proto2\";\n"
934 CreateTempFile("baz.proto",
935 "syntax = \"proto2\";\n"
936 "import \"bar.proto\";\n");
937 CreateTempFile("foo.proto",
938 "syntax = \"proto2\";\n"
939 "import \"bar.proto\";\n"
940 "import \"baz.proto\";\n");
942 Run("protocol_compiler --test_out=$tmpdir "
943 "--proto_path=$tmpdir foo.proto");
946 "bar.proto:2:1: Expected top-level statement (e.g. \"message\").\n"
947 "baz.proto: Import \"bar.proto\" was not found or had errors.\n"
948 "foo.proto: Import \"bar.proto\" was not found or had errors.\n"
949 "foo.proto: Import \"baz.proto\" was not found or had errors.\n");
952 TEST_F(CommandLineInterfaceTest, InputNotFoundError) {
953 // Test what happens if the input file is not found.
955 Run("protocol_compiler --test_out=$tmpdir "
956 "--proto_path=$tmpdir foo.proto");
959 "foo.proto: File not found.\n");
962 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundError) {
963 // Test what happens when a working-directory-relative input file is not
966 SetInputsAreProtoPathRelative(false);
968 Run("protocol_compiler --test_out=$tmpdir "
969 "--proto_path=$tmpdir $tmpdir/foo.proto");
972 "$tmpdir/foo.proto: No such file or directory\n");
975 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotMappedError) {
976 // Test what happens when a working-directory-relative input file is not
977 // mapped to a virtual path.
979 SetInputsAreProtoPathRelative(false);
981 CreateTempFile("foo.proto",
982 "syntax = \"proto2\";\n"
985 // Create a directory called "bar" so that we can point --proto_path at it.
986 CreateTempFile("bar/dummy", "");
988 Run("protocol_compiler --test_out=$tmpdir "
989 "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
992 "$tmpdir/foo.proto: File does not reside within any path "
993 "specified using --proto_path (or -I). You must specify a "
994 "--proto_path which encompasses this file. Note that the "
995 "proto_path must be an exact prefix of the .proto file "
996 "names -- protoc is too dumb to figure out when two paths "
997 "(e.g. absolute and relative) are equivalent (it's harder "
998 "than you think).\n");
1001 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundAndNotMappedError) {
1002 // Check what happens if the input file is not found *and* is not mapped
1003 // in the proto_path.
1005 SetInputsAreProtoPathRelative(false);
1007 // Create a directory called "bar" so that we can point --proto_path at it.
1008 CreateTempFile("bar/dummy", "");
1010 Run("protocol_compiler --test_out=$tmpdir "
1011 "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
1014 "$tmpdir/foo.proto: No such file or directory\n");
1017 TEST_F(CommandLineInterfaceTest, CwdRelativeInputShadowedError) {
1018 // Test what happens when a working-directory-relative input file is shadowed
1019 // by another file in the virtual path.
1021 SetInputsAreProtoPathRelative(false);
1023 CreateTempFile("foo/foo.proto",
1024 "syntax = \"proto2\";\n"
1025 "message Foo {}\n");
1026 CreateTempFile("bar/foo.proto",
1027 "syntax = \"proto2\";\n"
1028 "message Bar {}\n");
1030 Run("protocol_compiler --test_out=$tmpdir "
1031 "--proto_path=$tmpdir/foo --proto_path=$tmpdir/bar "
1032 "$tmpdir/bar/foo.proto");
1035 "$tmpdir/bar/foo.proto: Input is shadowed in the --proto_path "
1036 "by \"$tmpdir/foo/foo.proto\". Either use the latter "
1037 "file as your input or reorder the --proto_path so that the "
1038 "former file's location comes first.\n");
1041 TEST_F(CommandLineInterfaceTest, ProtoPathNotFoundError) {
1042 // Test what happens if the input file is not found.
1044 Run("protocol_compiler --test_out=$tmpdir "
1045 "--proto_path=$tmpdir/foo foo.proto");
1048 "$tmpdir/foo: warning: directory does not exist.\n"
1049 "foo.proto: File not found.\n");
1052 TEST_F(CommandLineInterfaceTest, MissingInputError) {
1053 // Test that we get an error if no inputs are given.
1055 Run("protocol_compiler --test_out=$tmpdir "
1056 "--proto_path=$tmpdir");
1058 ExpectErrorText("Missing input file.\n");
1061 TEST_F(CommandLineInterfaceTest, MissingOutputError) {
1062 CreateTempFile("foo.proto",
1063 "syntax = \"proto2\";\n"
1064 "message Foo {}\n");
1066 Run("protocol_compiler --proto_path=$tmpdir foo.proto");
1068 ExpectErrorText("Missing output directives.\n");
1071 TEST_F(CommandLineInterfaceTest, OutputWriteError) {
1072 CreateTempFile("foo.proto",
1073 "syntax = \"proto2\";\n"
1074 "message Foo {}\n");
1076 string output_file =
1077 MockCodeGenerator::GetOutputFileName("test_generator", "foo.proto");
1079 // Create a directory blocking our output location.
1080 CreateTempDir(output_file);
1082 Run("protocol_compiler --test_out=$tmpdir "
1083 "--proto_path=$tmpdir foo.proto");
1085 // MockCodeGenerator no longer detects an error because we actually write to
1086 // an in-memory location first, then dump to disk at the end. This is no
1088 // ExpectErrorSubstring("MockCodeGenerator detected write error.");
1090 #if defined(_WIN32) && !defined(__CYGWIN__)
1091 // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
1092 if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
1097 ExpectErrorSubstring(output_file + ": Is a directory");
1100 TEST_F(CommandLineInterfaceTest, PluginOutputWriteError) {
1101 CreateTempFile("foo.proto",
1102 "syntax = \"proto2\";\n"
1103 "message Foo {}\n");
1105 string output_file =
1106 MockCodeGenerator::GetOutputFileName("test_plugin", "foo.proto");
1108 // Create a directory blocking our output location.
1109 CreateTempDir(output_file);
1111 Run("protocol_compiler --plug_out=$tmpdir "
1112 "--proto_path=$tmpdir foo.proto");
1114 #if defined(_WIN32) && !defined(__CYGWIN__)
1115 // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
1116 if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
1121 ExpectErrorSubstring(output_file + ": Is a directory");
1124 TEST_F(CommandLineInterfaceTest, OutputDirectoryNotFoundError) {
1125 CreateTempFile("foo.proto",
1126 "syntax = \"proto2\";\n"
1127 "message Foo {}\n");
1129 Run("protocol_compiler --test_out=$tmpdir/nosuchdir "
1130 "--proto_path=$tmpdir foo.proto");
1132 ExpectErrorSubstring("nosuchdir/: No such file or directory");
1135 TEST_F(CommandLineInterfaceTest, PluginOutputDirectoryNotFoundError) {
1136 CreateTempFile("foo.proto",
1137 "syntax = \"proto2\";\n"
1138 "message Foo {}\n");
1140 Run("protocol_compiler --plug_out=$tmpdir/nosuchdir "
1141 "--proto_path=$tmpdir foo.proto");
1143 ExpectErrorSubstring("nosuchdir/: No such file or directory");
1146 TEST_F(CommandLineInterfaceTest, OutputDirectoryIsFileError) {
1147 CreateTempFile("foo.proto",
1148 "syntax = \"proto2\";\n"
1149 "message Foo {}\n");
1151 Run("protocol_compiler --test_out=$tmpdir/foo.proto "
1152 "--proto_path=$tmpdir foo.proto");
1154 #if defined(_WIN32) && !defined(__CYGWIN__)
1155 // Windows with MSVCRT.dll produces EINVAL instead of ENOTDIR.
1156 if (HasAlternateErrorSubstring("foo.proto/: Invalid argument")) {
1161 ExpectErrorSubstring("foo.proto/: Not a directory");
1164 TEST_F(CommandLineInterfaceTest, GeneratorError) {
1165 CreateTempFile("foo.proto",
1166 "syntax = \"proto2\";\n"
1167 "message MockCodeGenerator_Error {}\n");
1169 Run("protocol_compiler --test_out=$tmpdir "
1170 "--proto_path=$tmpdir foo.proto");
1172 ExpectErrorSubstring(
1173 "--test_out: foo.proto: Saw message type MockCodeGenerator_Error.");
1176 TEST_F(CommandLineInterfaceTest, GeneratorPluginError) {
1177 // Test a generator plugin that returns an error.
1179 CreateTempFile("foo.proto",
1180 "syntax = \"proto2\";\n"
1181 "message MockCodeGenerator_Error {}\n");
1183 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1184 "--proto_path=$tmpdir foo.proto");
1186 ExpectErrorSubstring(
1187 "--plug_out: foo.proto: Saw message type MockCodeGenerator_Error.");
1190 TEST_F(CommandLineInterfaceTest, GeneratorPluginFail) {
1191 // Test a generator plugin that exits with an error code.
1193 CreateTempFile("foo.proto",
1194 "syntax = \"proto2\";\n"
1195 "message MockCodeGenerator_Exit {}\n");
1197 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1198 "--proto_path=$tmpdir foo.proto");
1200 ExpectErrorSubstring("Saw message type MockCodeGenerator_Exit.");
1201 ExpectErrorSubstring(
1202 "--plug_out: prefix-gen-plug: Plugin failed with status code 123.");
1205 TEST_F(CommandLineInterfaceTest, GeneratorPluginCrash) {
1206 // Test a generator plugin that crashes.
1208 CreateTempFile("foo.proto",
1209 "syntax = \"proto2\";\n"
1210 "message MockCodeGenerator_Abort {}\n");
1212 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1213 "--proto_path=$tmpdir foo.proto");
1215 ExpectErrorSubstring("Saw message type MockCodeGenerator_Abort.");
1218 // Windows doesn't have signals. It looks like abort()ing causes the process
1219 // to exit with status code 3, but let's not depend on the exact number here.
1220 ExpectErrorSubstring(
1221 "--plug_out: prefix-gen-plug: Plugin failed with status code");
1223 // Don't depend on the exact signal number.
1224 ExpectErrorSubstring(
1225 "--plug_out: prefix-gen-plug: Plugin killed by signal");
1229 TEST_F(CommandLineInterfaceTest, PluginReceivesSourceCodeInfo) {
1230 CreateTempFile("foo.proto",
1231 "syntax = \"proto2\";\n"
1232 "message MockCodeGenerator_HasSourceCodeInfo {}\n");
1234 Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
1236 ExpectErrorSubstring(
1237 "Saw message type MockCodeGenerator_HasSourceCodeInfo: 1.");
1240 TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
1241 // Test what happens if the plugin isn't found.
1243 CreateTempFile("error.proto",
1244 "syntax = \"proto2\";\n"
1245 "message Foo {}\n");
1247 Run("protocol_compiler --badplug_out=TestParameter:$tmpdir "
1248 "--plugin=prefix-gen-badplug=no_such_file "
1249 "--proto_path=$tmpdir error.proto");
1252 ExpectErrorSubstring("--badplug_out: prefix-gen-badplug: " +
1253 Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND));
1255 // Error written to stdout by child process after exec() fails.
1256 ExpectErrorSubstring(
1257 "no_such_file: program not found or is not executable");
1259 // Error written by parent process when child fails.
1260 ExpectErrorSubstring(
1261 "--badplug_out: prefix-gen-badplug: Plugin failed with status code 1.");
1265 TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) {
1266 // Test what happens if plugins aren't allowed.
1268 CreateTempFile("error.proto",
1269 "syntax = \"proto2\";\n"
1270 "message Foo {}\n");
1273 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1274 "--proto_path=$tmpdir error.proto");
1276 ExpectErrorSubstring("Unknown flag: --plug_out");
1279 TEST_F(CommandLineInterfaceTest, HelpText) {
1280 Run("test_exec_name --help");
1282 ExpectErrorSubstringWithZeroReturnCode("Usage: test_exec_name ");
1283 ExpectErrorSubstringWithZeroReturnCode("--test_out=OUT_DIR");
1284 ExpectErrorSubstringWithZeroReturnCode("Test output.");
1285 ExpectErrorSubstringWithZeroReturnCode("--alt_out=OUT_DIR");
1286 ExpectErrorSubstringWithZeroReturnCode("Alt output.");
1289 TEST_F(CommandLineInterfaceTest, GccFormatErrors) {
1290 // Test --error_format=gcc (which is the default, but we want to verify
1291 // that it can be set explicitly).
1293 CreateTempFile("foo.proto",
1294 "syntax = \"proto2\";\n"
1297 Run("protocol_compiler --test_out=$tmpdir "
1298 "--proto_path=$tmpdir --error_format=gcc foo.proto");
1301 "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
1304 TEST_F(CommandLineInterfaceTest, MsvsFormatErrors) {
1305 // Test --error_format=msvs
1307 CreateTempFile("foo.proto",
1308 "syntax = \"proto2\";\n"
1311 Run("protocol_compiler --test_out=$tmpdir "
1312 "--proto_path=$tmpdir --error_format=msvs foo.proto");
1315 "$tmpdir/foo.proto(2) : error in column=1: Expected top-level statement "
1316 "(e.g. \"message\").\n");
1319 TEST_F(CommandLineInterfaceTest, InvalidErrorFormat) {
1320 // Test --error_format=msvs
1322 CreateTempFile("foo.proto",
1323 "syntax = \"proto2\";\n"
1326 Run("protocol_compiler --test_out=$tmpdir "
1327 "--proto_path=$tmpdir --error_format=invalid foo.proto");
1330 "Unknown error format: invalid\n");
1333 // -------------------------------------------------------------------
1334 // Flag parsing tests
1336 TEST_F(CommandLineInterfaceTest, ParseSingleCharacterFlag) {
1337 // Test that a single-character flag works.
1339 CreateTempFile("foo.proto",
1340 "syntax = \"proto2\";\n"
1341 "message Foo {}\n");
1343 Run("protocol_compiler -t$tmpdir "
1344 "--proto_path=$tmpdir foo.proto");
1347 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1350 TEST_F(CommandLineInterfaceTest, ParseSpaceDelimitedValue) {
1351 // Test that separating the flag value with a space works.
1353 CreateTempFile("foo.proto",
1354 "syntax = \"proto2\";\n"
1355 "message Foo {}\n");
1357 Run("protocol_compiler --test_out $tmpdir "
1358 "--proto_path=$tmpdir foo.proto");
1361 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1364 TEST_F(CommandLineInterfaceTest, ParseSingleCharacterSpaceDelimitedValue) {
1365 // Test that separating the flag value with a space works for
1366 // single-character flags.
1368 CreateTempFile("foo.proto",
1369 "syntax = \"proto2\";\n"
1370 "message Foo {}\n");
1372 Run("protocol_compiler -t $tmpdir "
1373 "--proto_path=$tmpdir foo.proto");
1376 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1379 TEST_F(CommandLineInterfaceTest, MissingValueError) {
1380 // Test that we get an error if a flag is missing its value.
1382 Run("protocol_compiler --test_out --proto_path=$tmpdir foo.proto");
1384 ExpectErrorText("Missing value for flag: --test_out\n");
1387 TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) {
1388 // Test that we get an error if the last argument is a flag requiring a
1391 Run("protocol_compiler --test_out");
1393 ExpectErrorText("Missing value for flag: --test_out\n");
1396 // ===================================================================
1398 // Test for --encode and --decode. Note that it would be easier to do this
1399 // test as a shell script, but we'd like to be able to run the test on
1400 // platforms that don't have a Bourne-compatible shell available (especially
1402 class EncodeDecodeTest : public testing::Test {
1404 virtual void SetUp() {
1405 duped_stdin_ = dup(STDIN_FILENO);
1408 virtual void TearDown() {
1409 dup2(duped_stdin_, STDIN_FILENO);
1410 close(duped_stdin_);
1413 void RedirectStdinFromText(const string& input) {
1414 string filename = TestTempDir() + "/test_stdin";
1415 File::WriteStringToFileOrDie(input, filename);
1416 GOOGLE_CHECK(RedirectStdinFromFile(filename));
1419 bool RedirectStdinFromFile(const string& filename) {
1420 int fd = open(filename.c_str(), O_RDONLY);
1421 if (fd < 0) return false;
1422 dup2(fd, STDIN_FILENO);
1427 // Remove '\r' characters from text.
1428 string StripCR(const string& text) {
1431 for (int i = 0; i < text.size(); i++) {
1432 if (text[i] != '\r') {
1433 result.push_back(text[i]);
1440 enum Type { TEXT, BINARY };
1441 enum ReturnCode { SUCCESS, ERROR };
1443 bool Run(const string& command) {
1444 vector<string> args;
1445 args.push_back("protoc");
1446 SplitStringUsing(command, " ", &args);
1447 args.push_back("--proto_path=" + TestSourceDir());
1449 scoped_array<const char*> argv(new const char*[args.size()]);
1450 for (int i = 0; i < args.size(); i++) {
1451 argv[i] = args[i].c_str();
1454 CommandLineInterface cli;
1455 cli.SetInputsAreProtoPathRelative(true);
1457 CaptureTestStdout();
1458 CaptureTestStderr();
1460 int result = cli.Run(args.size(), argv.get());
1462 captured_stdout_ = GetCapturedTestStdout();
1463 captured_stderr_ = GetCapturedTestStderr();
1468 void ExpectStdoutMatchesBinaryFile(const string& filename) {
1469 string expected_output;
1470 ASSERT_TRUE(File::ReadFileToString(filename, &expected_output));
1472 // Don't use EXPECT_EQ because we don't want to print raw binary data to
1473 // stdout on failure.
1474 EXPECT_TRUE(captured_stdout_ == expected_output);
1477 void ExpectStdoutMatchesTextFile(const string& filename) {
1478 string expected_output;
1479 ASSERT_TRUE(File::ReadFileToString(filename, &expected_output));
1481 ExpectStdoutMatchesText(expected_output);
1484 void ExpectStdoutMatchesText(const string& expected_text) {
1485 EXPECT_EQ(StripCR(expected_text), StripCR(captured_stdout_));
1488 void ExpectStderrMatchesText(const string& expected_text) {
1489 EXPECT_EQ(StripCR(expected_text), StripCR(captured_stderr_));
1494 string captured_stdout_;
1495 string captured_stderr_;
1498 TEST_F(EncodeDecodeTest, Encode) {
1499 RedirectStdinFromFile(TestSourceDir() +
1500 "/google/protobuf/testdata/text_format_unittest_data.txt");
1501 EXPECT_TRUE(Run("google/protobuf/unittest.proto "
1502 "--encode=protobuf_unittest.TestAllTypes"));
1503 ExpectStdoutMatchesBinaryFile(TestSourceDir() +
1504 "/google/protobuf/testdata/golden_message");
1505 ExpectStderrMatchesText("");
1508 TEST_F(EncodeDecodeTest, Decode) {
1509 RedirectStdinFromFile(TestSourceDir() +
1510 "/google/protobuf/testdata/golden_message");
1511 EXPECT_TRUE(Run("google/protobuf/unittest.proto "
1512 "--decode=protobuf_unittest.TestAllTypes"));
1513 ExpectStdoutMatchesTextFile(TestSourceDir() +
1514 "/google/protobuf/testdata/text_format_unittest_data.txt");
1515 ExpectStderrMatchesText("");
1518 TEST_F(EncodeDecodeTest, Partial) {
1519 RedirectStdinFromText("");
1520 EXPECT_TRUE(Run("google/protobuf/unittest.proto "
1521 "--encode=protobuf_unittest.TestRequired"));
1522 ExpectStdoutMatchesText("");
1523 ExpectStderrMatchesText(
1524 "warning: Input message is missing required fields: a, b, c\n");
1527 TEST_F(EncodeDecodeTest, DecodeRaw) {
1528 protobuf_unittest::TestAllTypes message;
1529 message.set_optional_int32(123);
1530 message.set_optional_string("foo");
1532 message.SerializeToString(&data);
1534 RedirectStdinFromText(data);
1535 EXPECT_TRUE(Run("--decode_raw"));
1536 ExpectStdoutMatchesText("1: 123\n"
1538 ExpectStderrMatchesText("");
1541 TEST_F(EncodeDecodeTest, UnknownType) {
1542 EXPECT_FALSE(Run("google/protobuf/unittest.proto "
1543 "--encode=NoSuchType"));
1544 ExpectStdoutMatchesText("");
1545 ExpectStderrMatchesText("Type not defined: NoSuchType\n");
1548 TEST_F(EncodeDecodeTest, ProtoParseError) {
1549 EXPECT_FALSE(Run("google/protobuf/no_such_file.proto "
1550 "--encode=NoSuchType"));
1551 ExpectStdoutMatchesText("");
1552 ExpectStderrMatchesText(
1553 "google/protobuf/no_such_file.proto: File not found.\n");
1556 } // anonymous namespace
1558 } // namespace compiler
1559 } // namespace protobuf
1560 } // namespace google