• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
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
14 // distribution.
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.
18 //
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.
30 
31 // Author: kenton@google.com (Kenton Varda)
32 //  Based on original Protocol Buffers design by
33 //  Sanjay Ghemawat, Jeff Dean, and others.
34 
35 #include <fcntl.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38 
39 #ifndef _MSC_VER
40 #include <unistd.h>
41 #endif
42 #include <memory>
43 #include <vector>
44 
45 #include <google/protobuf/stubs/stringprintf.h>
46 #include <google/protobuf/testing/file.h>
47 #include <google/protobuf/testing/file.h>
48 #include <google/protobuf/compiler/mock_code_generator.h>
49 #include <google/protobuf/compiler/subprocess.h>
50 #include <google/protobuf/compiler/code_generator.h>
51 #include <google/protobuf/compiler/command_line_interface.h>
52 #include <google/protobuf/test_util2.h>
53 #include <google/protobuf/unittest.pb.h>
54 #include <google/protobuf/io/io_win32.h>
55 #include <google/protobuf/io/printer.h>
56 #include <google/protobuf/io/zero_copy_stream.h>
57 #include <google/protobuf/descriptor.pb.h>
58 #include <google/protobuf/descriptor.h>
59 #include <google/protobuf/stubs/substitute.h>
60 
61 #include <google/protobuf/testing/file.h>
62 #include <google/protobuf/testing/googletest.h>
63 #include <gtest/gtest.h>
64 
65 #include <google/protobuf/stubs/strutil.h>
66 
67 namespace google {
68 namespace protobuf {
69 namespace compiler {
70 
71 #if defined(_WIN32)
72 // DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
73 // them like we do below.
74 using google::protobuf::io::win32::access;
75 using google::protobuf::io::win32::close;
76 using google::protobuf::io::win32::dup;
77 using google::protobuf::io::win32::dup2;
78 using google::protobuf::io::win32::open;
79 using google::protobuf::io::win32::write;
80 #endif
81 
82 // Disable the whole test when we use tcmalloc for "draconian" heap checks, in
83 // which case tcmalloc will print warnings that fail the plugin tests.
84 #if !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
85 
86 
87 namespace {
88 
FileExists(const std::string & path)89 bool FileExists(const std::string& path) {
90   return File::Exists(path);
91 }
92 
93 class CommandLineInterfaceTest : public testing::Test {
94  protected:
95   virtual void SetUp();
96   virtual void TearDown();
97 
98   // Runs the CommandLineInterface with the given command line.  The
99   // command is automatically split on spaces, and the string "$tmpdir"
100   // is replaced with TestTempDir().
101   void Run(const std::string& command);
102   void RunWithArgs(std::vector<std::string> args);
103 
104   // -----------------------------------------------------------------
105   // Methods to set up the test (called before Run()).
106 
107   class NullCodeGenerator;
108 
109   // Normally plugins are allowed for all tests.  Call this to explicitly
110   // disable them.
DisallowPlugins()111   void DisallowPlugins() { disallow_plugins_ = true; }
112 
113   // Create a temp file within temp_directory_ with the given name.
114   // The containing directory is also created if necessary.
115   void CreateTempFile(const std::string& name, const std::string& contents);
116 
117   // Create a subdirectory within temp_directory_.
118   void CreateTempDir(const std::string& name);
119 
120 #ifdef PROTOBUF_OPENSOURCE
121   // Change working directory to temp directory.
SwitchToTempDirectory()122   void SwitchToTempDirectory() {
123     File::ChangeWorkingDirectory(temp_directory_);
124   }
125 #else   // !PROTOBUF_OPENSOURCE
126   // TODO(teboring): Figure out how to change and get working directory in
127   // google3.
128 #endif  // !PROTOBUF_OPENSOURCE
129 
130   // -----------------------------------------------------------------
131   // Methods to check the test results (called after Run()).
132 
133   // Checks that no text was written to stderr during Run(), and Run()
134   // returned 0.
135   void ExpectNoErrors();
136 
137   // Checks that Run() returned non-zero and the stderr output is exactly
138   // the text given.  expected_test may contain references to "$tmpdir",
139   // which will be replaced by the temporary directory path.
140   void ExpectErrorText(const std::string& expected_text);
141 
142   // Checks that Run() returned non-zero and the stderr contains the given
143   // substring.
144   void ExpectErrorSubstring(const std::string& expected_substring);
145 
146   // Checks that the captured stdout is the same as the expected_text.
147   void ExpectCapturedStdout(const std::string& expected_text);
148 
149   // Checks that Run() returned zero and the stdout contains the given
150   // substring.
151   void ExpectCapturedStdoutSubstringWithZeroReturnCode(
152       const std::string& expected_substring);
153 
154 #if defined(_WIN32) && !defined(__CYGWIN__)
155   // Returns true if ExpectErrorSubstring(expected_substring) would pass, but
156   // does not fail otherwise.
157   bool HasAlternateErrorSubstring(const std::string& expected_substring);
158 #endif  // _WIN32 && !__CYGWIN__
159 
160   // Checks that MockCodeGenerator::Generate() was called in the given
161   // context (or the generator in test_plugin.cc, which produces the same
162   // output).  That is, this tests if the generator with the given name
163   // was called with the given parameter and proto file and produced the
164   // given output file.  This is checked by reading the output file and
165   // checking that it contains the content that MockCodeGenerator would
166   // generate given these inputs.  message_name is the name of the first
167   // message that appeared in the proto file; this is just to make extra
168   // sure that the correct file was parsed.
169   void ExpectGenerated(const std::string& generator_name,
170                        const std::string& parameter,
171                        const std::string& proto_name,
172                        const std::string& message_name);
173   void ExpectGenerated(const std::string& generator_name,
174                        const std::string& parameter,
175                        const std::string& proto_name,
176                        const std::string& message_name,
177                        const std::string& output_directory);
178   void ExpectGeneratedWithMultipleInputs(const std::string& generator_name,
179                                          const std::string& all_proto_names,
180                                          const std::string& proto_name,
181                                          const std::string& message_name);
182   void ExpectGeneratedWithInsertions(const std::string& generator_name,
183                                      const std::string& parameter,
184                                      const std::string& insertions,
185                                      const std::string& proto_name,
186                                      const std::string& message_name);
187   void CheckGeneratedAnnotations(const std::string& name,
188                                  const std::string& file);
189 
190 #if defined(_WIN32)
191   void ExpectNullCodeGeneratorCalled(const std::string& parameter);
192 #endif  // _WIN32
193 
194 
195   void ReadDescriptorSet(const std::string& filename,
196                          FileDescriptorSet* descriptor_set);
197 
198   void WriteDescriptorSet(const std::string& filename,
199                           const FileDescriptorSet* descriptor_set);
200 
201   void ExpectFileContent(const std::string& filename,
202                          const std::string& content);
203 
204  private:
205   // The object we are testing.
206   CommandLineInterface cli_;
207 
208   // Was DisallowPlugins() called?
209   bool disallow_plugins_;
210 
211   // We create a directory within TestTempDir() in order to add extra
212   // protection against accidentally deleting user files (since we recursively
213   // delete this directory during the test).  This is the full path of that
214   // directory.
215   std::string temp_directory_;
216 
217   // The result of Run().
218   int return_code_;
219 
220   // The captured stderr output.
221   std::string error_text_;
222 
223   // The captured stdout.
224   std::string captured_stdout_;
225 
226   // Pointers which need to be deleted later.
227   std::vector<CodeGenerator*> mock_generators_to_delete_;
228 
229   NullCodeGenerator* null_generator_;
230 };
231 
232 class CommandLineInterfaceTest::NullCodeGenerator : public CodeGenerator {
233  public:
NullCodeGenerator()234   NullCodeGenerator() : called_(false) {}
~NullCodeGenerator()235   ~NullCodeGenerator() {}
236 
237   mutable bool called_;
238   mutable std::string parameter_;
239 
240   // implements CodeGenerator ----------------------------------------
Generate(const FileDescriptor * file,const std::string & parameter,GeneratorContext * context,std::string * error) const241   bool Generate(const FileDescriptor* file, const std::string& parameter,
242                 GeneratorContext* context, std::string* error) const {
243     called_ = true;
244     parameter_ = parameter;
245     return true;
246   }
247 };
248 
249 // ===================================================================
250 
SetUp()251 void CommandLineInterfaceTest::SetUp() {
252   temp_directory_ = TestTempDir() + "/proto2_cli_test_temp";
253 
254   // If the temp directory already exists, it must be left over from a
255   // previous run.  Delete it.
256   if (FileExists(temp_directory_)) {
257     File::DeleteRecursively(temp_directory_, NULL, NULL);
258   }
259 
260   // Create the temp directory.
261   GOOGLE_CHECK_OK(File::CreateDir(temp_directory_, 0777));
262 
263   // Register generators.
264   CodeGenerator* generator = new MockCodeGenerator("test_generator");
265   mock_generators_to_delete_.push_back(generator);
266   cli_.RegisterGenerator("--test_out", "--test_opt", generator, "Test output.");
267   cli_.RegisterGenerator("-t", generator, "Test output.");
268 
269   generator = new MockCodeGenerator("alt_generator");
270   mock_generators_to_delete_.push_back(generator);
271   cli_.RegisterGenerator("--alt_out", generator, "Alt output.");
272 
273   generator = null_generator_ = new NullCodeGenerator();
274   mock_generators_to_delete_.push_back(generator);
275   cli_.RegisterGenerator("--null_out", generator, "Null output.");
276 
277 
278   disallow_plugins_ = false;
279 }
280 
TearDown()281 void CommandLineInterfaceTest::TearDown() {
282   // Delete the temp directory.
283   if (FileExists(temp_directory_)) {
284     File::DeleteRecursively(temp_directory_, NULL, NULL);
285   }
286 
287   // Delete all the MockCodeGenerators.
288   for (int i = 0; i < mock_generators_to_delete_.size(); i++) {
289     delete mock_generators_to_delete_[i];
290   }
291   mock_generators_to_delete_.clear();
292 }
293 
Run(const std::string & command)294 void CommandLineInterfaceTest::Run(const std::string& command) {
295   RunWithArgs(Split(command, " ", true));
296 }
297 
RunWithArgs(std::vector<std::string> args)298 void CommandLineInterfaceTest::RunWithArgs(std::vector<std::string> args) {
299   if (!disallow_plugins_) {
300     cli_.AllowPlugins("prefix-");
301     std::string plugin_path;
302 #ifdef GOOGLE_PROTOBUF_TEST_PLUGIN_PATH
303     plugin_path = GOOGLE_PROTOBUF_TEST_PLUGIN_PATH;
304 #else
305     const char* possible_paths[] = {
306         // When building with shared libraries, libtool hides the real
307         // executable
308         // in .libs and puts a fake wrapper in the current directory.
309         // Unfortunately, due to an apparent bug on Cygwin/MinGW, if one program
310         // wrapped in this way (e.g. protobuf-tests.exe) tries to execute
311         // another
312         // program wrapped in this way (e.g. test_plugin.exe), the latter fails
313         // with error code 127 and no explanation message.  Presumably the
314         // problem
315         // is that the wrapper for protobuf-tests.exe set some environment
316         // variables that confuse the wrapper for test_plugin.exe.  Luckily, it
317         // turns out that if we simply invoke the wrapped test_plugin.exe
318         // directly, it works -- I guess the environment variables set by the
319         // protobuf-tests.exe wrapper happen to be correct for it too.  So we do
320         // that.
321         ".libs/test_plugin.exe",  // Win32 w/autotool (Cygwin / MinGW)
322         "test_plugin.exe",        // Other Win32 (MSVC)
323         "test_plugin",            // Unix
324     };
325     for (int i = 0; i < GOOGLE_ARRAYSIZE(possible_paths); i++) {
326       if (access(possible_paths[i], F_OK) == 0) {
327         plugin_path = possible_paths[i];
328         break;
329       }
330     }
331 #endif
332 
333     if (plugin_path.empty()) {
334       GOOGLE_LOG(ERROR)
335           << "Plugin executable not found.  Plugin tests are likely to fail.";
336     } else {
337       args.push_back("--plugin=prefix-gen-plug=" + plugin_path);
338     }
339   }
340 
341   std::unique_ptr<const char*[]> argv(new const char*[args.size()]);
342 
343   for (int i = 0; i < args.size(); i++) {
344     args[i] = StringReplace(args[i], "$tmpdir", temp_directory_, true);
345     argv[i] = args[i].c_str();
346   }
347 
348   // TODO(jieluo): Cygwin doesn't work well if we try to capture stderr and
349   // stdout at the same time. Need to figure out why and add this capture back
350   // for Cygwin.
351 #if !defined(__CYGWIN__)
352   CaptureTestStdout();
353 #endif
354   CaptureTestStderr();
355 
356   return_code_ = cli_.Run(args.size(), argv.get());
357 
358   error_text_ = GetCapturedTestStderr();
359 #if !defined(__CYGWIN__)
360   captured_stdout_ = GetCapturedTestStdout();
361 #endif
362 }
363 
364 // -------------------------------------------------------------------
365 
CreateTempFile(const std::string & name,const std::string & contents)366 void CommandLineInterfaceTest::CreateTempFile(const std::string& name,
367                                               const std::string& contents) {
368   // Create parent directory, if necessary.
369   std::string::size_type slash_pos = name.find_last_of('/');
370   if (slash_pos != std::string::npos) {
371     std::string dir = name.substr(0, slash_pos);
372     if (!FileExists(temp_directory_ + "/" + dir)) {
373       GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + dir,
374                                           0777));
375     }
376   }
377 
378   // Write file.
379   std::string full_name = temp_directory_ + "/" + name;
380   GOOGLE_CHECK_OK(File::SetContents(
381       full_name, StringReplace(contents, "$tmpdir", temp_directory_, true),
382       true));
383 }
384 
CreateTempDir(const std::string & name)385 void CommandLineInterfaceTest::CreateTempDir(const std::string& name) {
386   GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + name,
387                                       0777));
388 }
389 
390 // -------------------------------------------------------------------
391 
ExpectNoErrors()392 void CommandLineInterfaceTest::ExpectNoErrors() {
393   EXPECT_EQ(0, return_code_);
394   EXPECT_EQ("", error_text_);
395 }
396 
ExpectErrorText(const std::string & expected_text)397 void CommandLineInterfaceTest::ExpectErrorText(
398     const std::string& expected_text) {
399   EXPECT_NE(0, return_code_);
400   EXPECT_EQ(StringReplace(expected_text, "$tmpdir", temp_directory_, true),
401             error_text_);
402 }
403 
ExpectErrorSubstring(const std::string & expected_substring)404 void CommandLineInterfaceTest::ExpectErrorSubstring(
405     const std::string& expected_substring) {
406   EXPECT_NE(0, return_code_);
407   EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_);
408 }
409 
410 #if defined(_WIN32) && !defined(__CYGWIN__)
HasAlternateErrorSubstring(const std::string & expected_substring)411 bool CommandLineInterfaceTest::HasAlternateErrorSubstring(
412     const std::string& expected_substring) {
413   EXPECT_NE(0, return_code_);
414   return error_text_.find(expected_substring) != std::string::npos;
415 }
416 #endif  // _WIN32 && !__CYGWIN__
417 
ExpectGenerated(const std::string & generator_name,const std::string & parameter,const std::string & proto_name,const std::string & message_name)418 void CommandLineInterfaceTest::ExpectGenerated(
419     const std::string& generator_name, const std::string& parameter,
420     const std::string& proto_name, const std::string& message_name) {
421   MockCodeGenerator::ExpectGenerated(generator_name, parameter, "", proto_name,
422                                      message_name, proto_name, temp_directory_);
423 }
424 
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)425 void CommandLineInterfaceTest::ExpectGenerated(
426     const std::string& generator_name, const std::string& parameter,
427     const std::string& proto_name, const std::string& message_name,
428     const std::string& output_directory) {
429   MockCodeGenerator::ExpectGenerated(generator_name, parameter, "", proto_name,
430                                      message_name, proto_name,
431                                      temp_directory_ + "/" + output_directory);
432 }
433 
ExpectGeneratedWithMultipleInputs(const std::string & generator_name,const std::string & all_proto_names,const std::string & proto_name,const std::string & message_name)434 void CommandLineInterfaceTest::ExpectGeneratedWithMultipleInputs(
435     const std::string& generator_name, const std::string& all_proto_names,
436     const std::string& proto_name, const std::string& message_name) {
437   MockCodeGenerator::ExpectGenerated(generator_name, "", "", proto_name,
438                                      message_name, all_proto_names,
439                                      temp_directory_);
440 }
441 
ExpectGeneratedWithInsertions(const std::string & generator_name,const std::string & parameter,const std::string & insertions,const std::string & proto_name,const std::string & message_name)442 void CommandLineInterfaceTest::ExpectGeneratedWithInsertions(
443     const std::string& generator_name, const std::string& parameter,
444     const std::string& insertions, const std::string& proto_name,
445     const std::string& message_name) {
446   MockCodeGenerator::ExpectGenerated(generator_name, parameter, insertions,
447                                      proto_name, message_name, proto_name,
448                                      temp_directory_);
449 }
450 
CheckGeneratedAnnotations(const std::string & name,const std::string & file)451 void CommandLineInterfaceTest::CheckGeneratedAnnotations(
452     const std::string& name, const std::string& file) {
453   MockCodeGenerator::CheckGeneratedAnnotations(name, file, temp_directory_);
454 }
455 
456 #if defined(_WIN32)
ExpectNullCodeGeneratorCalled(const std::string & parameter)457 void CommandLineInterfaceTest::ExpectNullCodeGeneratorCalled(
458     const std::string& parameter) {
459   EXPECT_TRUE(null_generator_->called_);
460   EXPECT_EQ(parameter, null_generator_->parameter_);
461 }
462 #endif  // _WIN32
463 
464 
ReadDescriptorSet(const std::string & filename,FileDescriptorSet * descriptor_set)465 void CommandLineInterfaceTest::ReadDescriptorSet(
466     const std::string& filename, FileDescriptorSet* descriptor_set) {
467   std::string path = temp_directory_ + "/" + filename;
468   std::string file_contents;
469   GOOGLE_CHECK_OK(File::GetContents(path, &file_contents, true));
470 
471   if (!descriptor_set->ParseFromString(file_contents)) {
472     FAIL() << "Could not parse file contents: " << path;
473   }
474 }
475 
WriteDescriptorSet(const std::string & filename,const FileDescriptorSet * descriptor_set)476 void CommandLineInterfaceTest::WriteDescriptorSet(
477     const std::string& filename, const FileDescriptorSet* descriptor_set) {
478   std::string binary_proto;
479   GOOGLE_CHECK(descriptor_set->SerializeToString(&binary_proto));
480   CreateTempFile(filename, binary_proto);
481 }
482 
ExpectCapturedStdout(const std::string & expected_text)483 void CommandLineInterfaceTest::ExpectCapturedStdout(
484     const std::string& expected_text) {
485   EXPECT_EQ(expected_text, captured_stdout_);
486 }
487 
ExpectCapturedStdoutSubstringWithZeroReturnCode(const std::string & expected_substring)488 void CommandLineInterfaceTest::ExpectCapturedStdoutSubstringWithZeroReturnCode(
489     const std::string& expected_substring) {
490   EXPECT_EQ(0, return_code_);
491   EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring,
492                       captured_stdout_);
493 }
494 
ExpectFileContent(const std::string & filename,const std::string & content)495 void CommandLineInterfaceTest::ExpectFileContent(const std::string& filename,
496                                                  const std::string& content) {
497   std::string path = temp_directory_ + "/" + filename;
498   std::string file_contents;
499   GOOGLE_CHECK_OK(File::GetContents(path, &file_contents, true));
500 
501   EXPECT_EQ(StringReplace(content, "$tmpdir", temp_directory_, true),
502             file_contents);
503 }
504 
505 // ===================================================================
506 
TEST_F(CommandLineInterfaceTest,BasicOutput)507 TEST_F(CommandLineInterfaceTest, BasicOutput) {
508   // Test that the common case works.
509 
510   CreateTempFile("foo.proto",
511                  "syntax = \"proto2\";\n"
512                  "message Foo {}\n");
513 
514   Run("protocol_compiler --test_out=$tmpdir "
515       "--proto_path=$tmpdir foo.proto");
516 
517   ExpectNoErrors();
518   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
519 }
520 
TEST_F(CommandLineInterfaceTest,BasicOutput_DescriptorSetIn)521 TEST_F(CommandLineInterfaceTest, BasicOutput_DescriptorSetIn) {
522   // Test that the common case works.
523   FileDescriptorSet file_descriptor_set;
524   FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
525   file_descriptor_proto->set_name("foo.proto");
526   file_descriptor_proto->add_message_type()->set_name("Foo");
527 
528   WriteDescriptorSet("foo.bin", &file_descriptor_set);
529 
530   Run("protocol_compiler --test_out=$tmpdir "
531       "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
532 
533   ExpectNoErrors();
534   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
535 }
536 
TEST_F(CommandLineInterfaceTest,BasicPlugin)537 TEST_F(CommandLineInterfaceTest, BasicPlugin) {
538   // Test that basic plugins work.
539 
540   CreateTempFile("foo.proto",
541                  "syntax = \"proto2\";\n"
542                  "message Foo {}\n");
543 
544   Run("protocol_compiler --plug_out=$tmpdir "
545       "--proto_path=$tmpdir foo.proto");
546 
547   ExpectNoErrors();
548   ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
549 }
550 
TEST_F(CommandLineInterfaceTest,BasicPlugin_DescriptorSetIn)551 TEST_F(CommandLineInterfaceTest, BasicPlugin_DescriptorSetIn) {
552   // Test that basic plugins work.
553 
554   FileDescriptorSet file_descriptor_set;
555   FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
556   file_descriptor_proto->set_name("foo.proto");
557   file_descriptor_proto->add_message_type()->set_name("Foo");
558 
559   WriteDescriptorSet("foo.bin", &file_descriptor_set);
560 
561   Run("protocol_compiler --plug_out=$tmpdir "
562       "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
563 
564   ExpectNoErrors();
565   ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
566 }
567 
TEST_F(CommandLineInterfaceTest,GeneratorAndPlugin)568 TEST_F(CommandLineInterfaceTest, GeneratorAndPlugin) {
569   // Invoke a generator and a plugin at the same time.
570 
571   CreateTempFile("foo.proto",
572                  "syntax = \"proto2\";\n"
573                  "message Foo {}\n");
574 
575   Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
576       "--proto_path=$tmpdir foo.proto");
577 
578   ExpectNoErrors();
579   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
580   ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
581 }
582 
TEST_F(CommandLineInterfaceTest,GeneratorAndPlugin_DescriptorSetIn)583 TEST_F(CommandLineInterfaceTest, GeneratorAndPlugin_DescriptorSetIn) {
584   // Invoke a generator and a plugin at the same time.
585 
586   FileDescriptorSet file_descriptor_set;
587   FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
588   file_descriptor_proto->set_name("foo.proto");
589   file_descriptor_proto->add_message_type()->set_name("Foo");
590 
591   WriteDescriptorSet("foo.bin", &file_descriptor_set);
592 
593   Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
594       "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
595 
596   ExpectNoErrors();
597   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
598   ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
599 }
600 
TEST_F(CommandLineInterfaceTest,MultipleInputs)601 TEST_F(CommandLineInterfaceTest, MultipleInputs) {
602   // Test parsing multiple input files.
603 
604   CreateTempFile("foo.proto",
605                  "syntax = \"proto2\";\n"
606                  "message Foo {}\n");
607   CreateTempFile("bar.proto",
608                  "syntax = \"proto2\";\n"
609                  "message Bar {}\n");
610 
611   Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
612       "--proto_path=$tmpdir foo.proto bar.proto");
613 
614   ExpectNoErrors();
615   ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
616                                     "foo.proto", "Foo");
617   ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
618                                     "bar.proto", "Bar");
619   ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
620                                     "foo.proto", "Foo");
621   ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
622                                     "bar.proto", "Bar");
623 }
624 
TEST_F(CommandLineInterfaceTest,MultipleInputs_DescriptorSetIn)625 TEST_F(CommandLineInterfaceTest, MultipleInputs_DescriptorSetIn) {
626   // Test parsing multiple input files.
627   FileDescriptorSet file_descriptor_set;
628 
629   FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
630   file_descriptor_proto->set_name("foo.proto");
631   file_descriptor_proto->add_message_type()->set_name("Foo");
632 
633   file_descriptor_proto = file_descriptor_set.add_file();
634   file_descriptor_proto->set_name("bar.proto");
635   file_descriptor_proto->add_message_type()->set_name("Bar");
636 
637   WriteDescriptorSet("foo.bin", &file_descriptor_set);
638 
639   Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
640       "--descriptor_set_in=$tmpdir/foo.bin foo.proto bar.proto");
641 
642   ExpectNoErrors();
643   ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
644                                     "foo.proto", "Foo");
645   ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
646                                     "bar.proto", "Bar");
647   ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
648                                     "foo.proto", "Foo");
649   ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
650                                     "bar.proto", "Bar");
651 }
652 
TEST_F(CommandLineInterfaceTest,MultipleInputsWithImport)653 TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport) {
654   // Test parsing multiple input files with an import of a separate file.
655 
656   CreateTempFile("foo.proto",
657                  "syntax = \"proto2\";\n"
658                  "message Foo {}\n");
659   CreateTempFile("bar.proto",
660                  "syntax = \"proto2\";\n"
661                  "import \"baz.proto\";\n"
662                  "message Bar {\n"
663                  "  optional Baz a = 1;\n"
664                  "}\n");
665   CreateTempFile("baz.proto",
666                  "syntax = \"proto2\";\n"
667                  "message Baz {}\n");
668 
669   Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
670       "--proto_path=$tmpdir foo.proto bar.proto");
671 
672   ExpectNoErrors();
673   ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
674                                     "foo.proto", "Foo");
675   ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
676                                     "bar.proto", "Bar");
677   ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
678                                     "foo.proto", "Foo");
679   ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
680                                     "bar.proto", "Bar");
681 }
682 
TEST_F(CommandLineInterfaceTest,MultipleInputsWithImport_DescriptorSetIn)683 TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport_DescriptorSetIn) {
684   // Test parsing multiple input files with an import of a separate file.
685   FileDescriptorSet file_descriptor_set;
686 
687   FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
688   file_descriptor_proto->set_name("foo.proto");
689   file_descriptor_proto->add_message_type()->set_name("Foo");
690 
691   file_descriptor_proto = file_descriptor_set.add_file();
692   file_descriptor_proto->set_name("bar.proto");
693   file_descriptor_proto->add_dependency("baz.proto");
694   DescriptorProto* message = file_descriptor_proto->add_message_type();
695   message->set_name("Bar");
696   FieldDescriptorProto* field = message->add_field();
697   field->set_type_name("Baz");
698   field->set_name("a");
699   field->set_number(1);
700 
701   WriteDescriptorSet("foo_and_bar.bin", &file_descriptor_set);
702 
703   file_descriptor_set.clear_file();
704   file_descriptor_proto = file_descriptor_set.add_file();
705   file_descriptor_proto->set_name("baz.proto");
706   file_descriptor_proto->add_message_type()->set_name("Baz");
707 
708   file_descriptor_proto = file_descriptor_set.add_file();
709   file_descriptor_proto->set_name("bat.proto");
710   file_descriptor_proto->add_dependency("baz.proto");
711   message = file_descriptor_proto->add_message_type();
712   message->set_name("Bat");
713   field = message->add_field();
714   field->set_type_name("Baz");
715   field->set_name("a");
716   field->set_number(1);
717 
718   WriteDescriptorSet("baz_and_bat.bin", &file_descriptor_set);
719   Run(strings::Substitute(
720       "protocol_compiler --test_out=$$tmpdir --plug_out=$$tmpdir "
721       "--descriptor_set_in=$0 foo.proto bar.proto",
722       std::string("$tmpdir/foo_and_bar.bin") +
723           CommandLineInterface::kPathSeparator + "$tmpdir/baz_and_bat.bin"));
724 
725   ExpectNoErrors();
726   ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
727                                     "foo.proto", "Foo");
728   ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
729                                     "bar.proto", "Bar");
730   ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
731                                     "foo.proto", "Foo");
732   ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
733                                     "bar.proto", "Bar");
734 
735   Run(strings::Substitute(
736       "protocol_compiler --test_out=$$tmpdir --plug_out=$$tmpdir "
737       "--descriptor_set_in=$0 baz.proto bat.proto",
738       std::string("$tmpdir/foo_and_bar.bin") +
739           CommandLineInterface::kPathSeparator + "$tmpdir/baz_and_bat.bin"));
740 
741   ExpectNoErrors();
742   ExpectGeneratedWithMultipleInputs("test_generator", "baz.proto,bat.proto",
743                                     "baz.proto", "Baz");
744   ExpectGeneratedWithMultipleInputs("test_generator", "baz.proto,bat.proto",
745                                     "bat.proto", "Bat");
746   ExpectGeneratedWithMultipleInputs("test_plugin", "baz.proto,bat.proto",
747                                     "baz.proto", "Baz");
748   ExpectGeneratedWithMultipleInputs("test_plugin", "baz.proto,bat.proto",
749                                     "bat.proto", "Bat");
750 }
751 
TEST_F(CommandLineInterfaceTest,MultipleInputsWithImport_DescriptorSetIn_DuplicateFileDescriptor)752 TEST_F(CommandLineInterfaceTest,
753        MultipleInputsWithImport_DescriptorSetIn_DuplicateFileDescriptor) {
754   // Test parsing multiple input files with an import of a separate file.
755   FileDescriptorSet file_descriptor_set;
756 
757   FileDescriptorProto foo_file_descriptor_proto;
758   foo_file_descriptor_proto.set_name("foo.proto");
759   foo_file_descriptor_proto.add_message_type()->set_name("Foo");
760 
761   file_descriptor_set.add_file()->CopyFrom(foo_file_descriptor_proto);
762 
763   FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
764   file_descriptor_proto->set_name("bar.proto");
765   file_descriptor_proto->add_dependency("baz.proto");
766   file_descriptor_proto->add_dependency("foo.proto");
767   DescriptorProto* message = file_descriptor_proto->add_message_type();
768   message->set_name("Bar");
769   FieldDescriptorProto* field = message->add_field();
770   field->set_type_name("Baz");
771   field->set_name("a");
772   field->set_number(1);
773   field = message->add_field();
774   field->set_type_name("Foo");
775   field->set_name("f");
776   field->set_number(2);
777   WriteDescriptorSet("foo_and_bar.bin", &file_descriptor_set);
778 
779   file_descriptor_set.clear_file();
780   file_descriptor_set.add_file()->CopyFrom(foo_file_descriptor_proto);
781 
782   file_descriptor_proto = file_descriptor_set.add_file();
783   file_descriptor_proto->set_name("baz.proto");
784   file_descriptor_proto->add_dependency("foo.proto");
785   message = file_descriptor_proto->add_message_type();
786   message->set_name("Baz");
787   field = message->add_field();
788   field->set_type_name("Foo");
789   field->set_name("f");
790   field->set_number(1);
791   WriteDescriptorSet("foo_and_baz.bin", &file_descriptor_set);
792 
793   Run(strings::Substitute(
794       "protocol_compiler --test_out=$$tmpdir --plug_out=$$tmpdir "
795       "--descriptor_set_in=$0 bar.proto",
796       std::string("$tmpdir/foo_and_bar.bin") +
797           CommandLineInterface::kPathSeparator + "$tmpdir/foo_and_baz.bin"));
798 
799   ExpectNoErrors();
800   ExpectGenerated("test_generator", "", "bar.proto", "Bar");
801   ExpectGenerated("test_plugin", "", "bar.proto", "Bar");
802 }
803 
TEST_F(CommandLineInterfaceTest,MultipleInputsWithImport_DescriptorSetIn_MissingImport)804 TEST_F(CommandLineInterfaceTest,
805        MultipleInputsWithImport_DescriptorSetIn_MissingImport) {
806   // Test parsing multiple input files with an import of a separate file.
807   FileDescriptorSet file_descriptor_set;
808 
809   FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
810   file_descriptor_proto->set_name("foo.proto");
811   file_descriptor_proto->add_message_type()->set_name("Foo");
812 
813   file_descriptor_proto = file_descriptor_set.add_file();
814   file_descriptor_proto->set_name("bar.proto");
815   file_descriptor_proto->add_dependency("baz.proto");
816   DescriptorProto* message = file_descriptor_proto->add_message_type();
817   message->set_name("Bar");
818   FieldDescriptorProto* field = message->add_field();
819   field->set_type_name("Baz");
820   field->set_name("a");
821   field->set_number(1);
822 
823   WriteDescriptorSet("foo_and_bar.bin", &file_descriptor_set);
824 
825   file_descriptor_set.clear_file();
826   file_descriptor_proto = file_descriptor_set.add_file();
827   file_descriptor_proto->set_name("baz.proto");
828   file_descriptor_proto->add_message_type()->set_name("Baz");
829 
830   WriteDescriptorSet("baz.bin", &file_descriptor_set);
831   Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
832       "--descriptor_set_in=$tmpdir/foo_and_bar.bin "
833       "foo.proto bar.proto");
834   ExpectErrorSubstring(
835       "bar.proto: Import \"baz.proto\" was not found or had errors.");
836   ExpectErrorSubstring("bar.proto: \"Baz\" is not defined.");
837 }
838 
TEST_F(CommandLineInterfaceTest,CreateDirectory)839 TEST_F(CommandLineInterfaceTest, CreateDirectory) {
840   // Test that when we output to a sub-directory, it is created.
841 
842   CreateTempFile("bar/baz/foo.proto",
843                  "syntax = \"proto2\";\n"
844                  "message Foo {}\n");
845   CreateTempDir("out");
846   CreateTempDir("plugout");
847 
848   Run("protocol_compiler --test_out=$tmpdir/out --plug_out=$tmpdir/plugout "
849       "--proto_path=$tmpdir bar/baz/foo.proto");
850 
851   ExpectNoErrors();
852   ExpectGenerated("test_generator", "", "bar/baz/foo.proto", "Foo", "out");
853   ExpectGenerated("test_plugin", "", "bar/baz/foo.proto", "Foo", "plugout");
854 }
855 
TEST_F(CommandLineInterfaceTest,GeneratorParameters)856 TEST_F(CommandLineInterfaceTest, GeneratorParameters) {
857   // Test that generator parameters are correctly parsed from the command line.
858 
859   CreateTempFile("foo.proto",
860                  "syntax = \"proto2\";\n"
861                  "message Foo {}\n");
862 
863   Run("protocol_compiler --test_out=TestParameter:$tmpdir "
864       "--plug_out=TestPluginParameter:$tmpdir "
865       "--proto_path=$tmpdir foo.proto");
866 
867   ExpectNoErrors();
868   ExpectGenerated("test_generator", "TestParameter", "foo.proto", "Foo");
869   ExpectGenerated("test_plugin", "TestPluginParameter", "foo.proto", "Foo");
870 }
871 
TEST_F(CommandLineInterfaceTest,ExtraGeneratorParameters)872 TEST_F(CommandLineInterfaceTest, ExtraGeneratorParameters) {
873   // Test that generator parameters specified with the option flag are
874   // correctly passed to the code generator.
875 
876   CreateTempFile("foo.proto",
877                  "syntax = \"proto2\";\n"
878                  "message Foo {}\n");
879   // Create the "a" and "b" sub-directories.
880   CreateTempDir("a");
881   CreateTempDir("b");
882 
883   Run("protocol_compiler "
884       "--test_opt=foo1 "
885       "--test_out=bar:$tmpdir/a "
886       "--test_opt=foo2 "
887       "--test_out=baz:$tmpdir/b "
888       "--test_opt=foo3 "
889       "--proto_path=$tmpdir foo.proto");
890 
891   ExpectNoErrors();
892   ExpectGenerated("test_generator", "bar,foo1,foo2,foo3", "foo.proto", "Foo",
893                   "a");
894   ExpectGenerated("test_generator", "baz,foo1,foo2,foo3", "foo.proto", "Foo",
895                   "b");
896 }
897 
TEST_F(CommandLineInterfaceTest,ExtraPluginParameters)898 TEST_F(CommandLineInterfaceTest, ExtraPluginParameters) {
899   // Test that generator parameters specified with the option flag are
900   // correctly passed to the code generator.
901 
902   CreateTempFile("foo.proto",
903                  "syntax = \"proto2\";\n"
904                  "message Foo {}\n");
905   // Create the "a" and "b" sub-directories.
906   CreateTempDir("a");
907   CreateTempDir("b");
908 
909   Run("protocol_compiler "
910       "--plug_opt=foo1 "
911       "--plug_out=bar:$tmpdir/a "
912       "--plug_opt=foo2 "
913       "--plug_out=baz:$tmpdir/b "
914       "--plug_opt=foo3 "
915       "--proto_path=$tmpdir foo.proto");
916 
917   ExpectNoErrors();
918   ExpectGenerated("test_plugin", "bar,foo1,foo2,foo3", "foo.proto", "Foo", "a");
919   ExpectGenerated("test_plugin", "baz,foo1,foo2,foo3", "foo.proto", "Foo", "b");
920 }
921 
TEST_F(CommandLineInterfaceTest,UnrecognizedExtraParameters)922 TEST_F(CommandLineInterfaceTest, UnrecognizedExtraParameters) {
923   CreateTempFile("foo.proto",
924                  "syntax = \"proto2\";\n"
925                  "message Foo {}\n");
926 
927   Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
928       "--unknown_plug_a_opt=Foo "
929       "--unknown_plug_b_opt=Bar "
930       "--proto_path=$tmpdir foo.proto");
931 
932   ExpectErrorSubstring("Unknown flag: --unknown_plug_a_opt");
933   ExpectErrorSubstring("Unknown flag: --unknown_plug_b_opt");
934 }
935 
TEST_F(CommandLineInterfaceTest,ExtraPluginParametersForOutParameters)936 TEST_F(CommandLineInterfaceTest, ExtraPluginParametersForOutParameters) {
937   // This doesn't rely on the plugin having been registred and instead that
938   // the existence of --[name]_out is enough to make the --[name]_opt valid.
939   // However, running out of process plugins found via the search path (i.e. -
940   // not pre registered with --plugin) isn't support in this test suite, so we
941   // list the options pre/post the _out directive, and then include _opt that
942   // will be unknown, and confirm the failure output is about the expected
943   // unknown directive, which means the other were accepted.
944   // NOTE: UnrecognizedExtraParameters confirms that if two unknown _opt
945   // directives appear, they both are reported.
946 
947   CreateTempFile("foo.proto",
948                  "syntax = \"proto2\";\n"
949                  "message Foo {}\n");
950 
951   Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
952       "--xyz_opt=foo=bar --xyz_out=$tmpdir "
953       "--abc_out=$tmpdir --abc_opt=foo=bar "
954       "--unknown_plug_opt=Foo "
955       "--proto_path=$tmpdir foo.proto");
956 
957   ExpectErrorText("Unknown flag: --unknown_plug_opt\n");
958 }
959 
TEST_F(CommandLineInterfaceTest,Insert)960 TEST_F(CommandLineInterfaceTest, Insert) {
961   // Test running a generator that inserts code into another's output.
962 
963   CreateTempFile("foo.proto",
964                  "syntax = \"proto2\";\n"
965                  "message Foo {}\n");
966 
967   Run("protocol_compiler "
968       "--test_out=TestParameter:$tmpdir "
969       "--plug_out=TestPluginParameter:$tmpdir "
970       "--test_out=insert=test_generator,test_plugin:$tmpdir "
971       "--plug_out=insert=test_generator,test_plugin:$tmpdir "
972       "--proto_path=$tmpdir foo.proto");
973 
974   ExpectNoErrors();
975   ExpectGeneratedWithInsertions("test_generator", "TestParameter",
976                                 "test_generator,test_plugin", "foo.proto",
977                                 "Foo");
978   ExpectGeneratedWithInsertions("test_plugin", "TestPluginParameter",
979                                 "test_generator,test_plugin", "foo.proto",
980                                 "Foo");
981 }
982 
TEST_F(CommandLineInterfaceTest,InsertWithAnnotationFixup)983 TEST_F(CommandLineInterfaceTest, InsertWithAnnotationFixup) {
984   // Check that annotation spans are updated after insertions.
985 
986   CreateTempFile("foo.proto",
987                  "syntax = \"proto2\";\n"
988                  "message MockCodeGenerator_Annotate {}\n");
989 
990   Run("protocol_compiler "
991       "--test_out=TestParameter:$tmpdir "
992       "--plug_out=TestPluginParameter:$tmpdir "
993       "--test_out=insert=test_generator,test_plugin:$tmpdir "
994       "--plug_out=insert=test_generator,test_plugin:$tmpdir "
995       "--proto_path=$tmpdir foo.proto");
996 
997   ExpectNoErrors();
998   CheckGeneratedAnnotations("test_generator", "foo.proto");
999   CheckGeneratedAnnotations("test_plugin", "foo.proto");
1000 }
1001 
1002 #if defined(_WIN32)
1003 
TEST_F(CommandLineInterfaceTest,WindowsOutputPath)1004 TEST_F(CommandLineInterfaceTest, WindowsOutputPath) {
1005   // Test that the output path can be a Windows-style path.
1006 
1007   CreateTempFile("foo.proto", "syntax = \"proto2\";\n");
1008 
1009   Run("protocol_compiler --null_out=C:\\ "
1010       "--proto_path=$tmpdir foo.proto");
1011 
1012   ExpectNoErrors();
1013   ExpectNullCodeGeneratorCalled("");
1014 }
1015 
TEST_F(CommandLineInterfaceTest,WindowsOutputPathAndParameter)1016 TEST_F(CommandLineInterfaceTest, WindowsOutputPathAndParameter) {
1017   // Test that we can have a windows-style output path and a parameter.
1018 
1019   CreateTempFile("foo.proto", "syntax = \"proto2\";\n");
1020 
1021   Run("protocol_compiler --null_out=bar:C:\\ "
1022       "--proto_path=$tmpdir foo.proto");
1023 
1024   ExpectNoErrors();
1025   ExpectNullCodeGeneratorCalled("bar");
1026 }
1027 
TEST_F(CommandLineInterfaceTest,TrailingBackslash)1028 TEST_F(CommandLineInterfaceTest, TrailingBackslash) {
1029   // Test that the directories can end in backslashes.  Some users claim this
1030   // doesn't work on their system.
1031 
1032   CreateTempFile("foo.proto",
1033                  "syntax = \"proto2\";\n"
1034                  "message Foo {}\n");
1035 
1036   Run("protocol_compiler --test_out=$tmpdir\\ "
1037       "--proto_path=$tmpdir\\ foo.proto");
1038 
1039   ExpectNoErrors();
1040   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1041 }
1042 
TEST_F(CommandLineInterfaceTest,Win32ErrorMessage)1043 TEST_F(CommandLineInterfaceTest, Win32ErrorMessage) {
1044   EXPECT_EQ("The system cannot find the file specified.\r\n",
1045             Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND));
1046 }
1047 
1048 #endif  // defined(_WIN32) || defined(__CYGWIN__)
1049 
TEST_F(CommandLineInterfaceTest,PathLookup)1050 TEST_F(CommandLineInterfaceTest, PathLookup) {
1051   // Test that specifying multiple directories in the proto search path works.
1052 
1053   CreateTempFile("b/bar.proto",
1054                  "syntax = \"proto2\";\n"
1055                  "message Bar {}\n");
1056   CreateTempFile("a/foo.proto",
1057                  "syntax = \"proto2\";\n"
1058                  "import \"bar.proto\";\n"
1059                  "message Foo {\n"
1060                  "  optional Bar a = 1;\n"
1061                  "}\n");
1062   CreateTempFile("b/foo.proto", "this should not be parsed\n");
1063 
1064   Run("protocol_compiler --test_out=$tmpdir "
1065       "--proto_path=$tmpdir/a --proto_path=$tmpdir/b foo.proto");
1066 
1067   ExpectNoErrors();
1068   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1069 }
1070 
TEST_F(CommandLineInterfaceTest,ColonDelimitedPath)1071 TEST_F(CommandLineInterfaceTest, ColonDelimitedPath) {
1072   // Same as PathLookup, but we provide the proto_path in a single flag.
1073 
1074   CreateTempFile("b/bar.proto",
1075                  "syntax = \"proto2\";\n"
1076                  "message Bar {}\n");
1077   CreateTempFile("a/foo.proto",
1078                  "syntax = \"proto2\";\n"
1079                  "import \"bar.proto\";\n"
1080                  "message Foo {\n"
1081                  "  optional Bar a = 1;\n"
1082                  "}\n");
1083   CreateTempFile("b/foo.proto", "this should not be parsed\n");
1084 
1085   Run(strings::Substitute(
1086       "protocol_compiler --test_out=$$tmpdir --proto_path=$0 foo.proto",
1087       std::string("$tmpdir/a") + CommandLineInterface::kPathSeparator +
1088           "$tmpdir/b"));
1089 
1090   ExpectNoErrors();
1091   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1092 }
1093 
TEST_F(CommandLineInterfaceTest,NonRootMapping)1094 TEST_F(CommandLineInterfaceTest, NonRootMapping) {
1095   // Test setting up a search path mapping a directory to a non-root location.
1096 
1097   CreateTempFile("foo.proto",
1098                  "syntax = \"proto2\";\n"
1099                  "message Foo {}\n");
1100 
1101   Run("protocol_compiler --test_out=$tmpdir "
1102       "--proto_path=bar=$tmpdir bar/foo.proto");
1103 
1104   ExpectNoErrors();
1105   ExpectGenerated("test_generator", "", "bar/foo.proto", "Foo");
1106 }
1107 
TEST_F(CommandLineInterfaceTest,PathWithEqualsSign)1108 TEST_F(CommandLineInterfaceTest, PathWithEqualsSign) {
1109   // Test setting up a search path which happens to have '=' in it.
1110 
1111   CreateTempDir("with=sign");
1112   CreateTempFile("with=sign/foo.proto",
1113                  "syntax = \"proto2\";\n"
1114                  "message Foo {}\n");
1115 
1116   Run("protocol_compiler --test_out=$tmpdir "
1117       "--proto_path=$tmpdir/with=sign foo.proto");
1118 
1119   ExpectNoErrors();
1120   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1121 }
1122 
TEST_F(CommandLineInterfaceTest,MultipleGenerators)1123 TEST_F(CommandLineInterfaceTest, MultipleGenerators) {
1124   // Test that we can have multiple generators and use both in one invocation,
1125   // each with a different output directory.
1126 
1127   CreateTempFile("foo.proto",
1128                  "syntax = \"proto2\";\n"
1129                  "message Foo {}\n");
1130   // Create the "a" and "b" sub-directories.
1131   CreateTempDir("a");
1132   CreateTempDir("b");
1133 
1134   Run("protocol_compiler "
1135       "--test_out=$tmpdir/a "
1136       "--alt_out=$tmpdir/b "
1137       "--proto_path=$tmpdir foo.proto");
1138 
1139   ExpectNoErrors();
1140   ExpectGenerated("test_generator", "", "foo.proto", "Foo", "a");
1141   ExpectGenerated("alt_generator", "", "foo.proto", "Foo", "b");
1142 }
1143 
TEST_F(CommandLineInterfaceTest,DisallowServicesNoServices)1144 TEST_F(CommandLineInterfaceTest, DisallowServicesNoServices) {
1145   // Test that --disallow_services doesn't cause a problem when there are no
1146   // services.
1147 
1148   CreateTempFile("foo.proto",
1149                  "syntax = \"proto2\";\n"
1150                  "message Foo {}\n");
1151 
1152   Run("protocol_compiler --disallow_services --test_out=$tmpdir "
1153       "--proto_path=$tmpdir foo.proto");
1154 
1155   ExpectNoErrors();
1156   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1157 }
1158 
TEST_F(CommandLineInterfaceTest,DisallowServicesHasService)1159 TEST_F(CommandLineInterfaceTest, DisallowServicesHasService) {
1160   // Test that --disallow_services produces an error when there are services.
1161 
1162   CreateTempFile("foo.proto",
1163                  "syntax = \"proto2\";\n"
1164                  "message Foo {}\n"
1165                  "service Bar {}\n");
1166 
1167   Run("protocol_compiler --disallow_services --test_out=$tmpdir "
1168       "--proto_path=$tmpdir foo.proto");
1169 
1170   ExpectErrorSubstring("foo.proto: This file contains services");
1171 }
1172 
TEST_F(CommandLineInterfaceTest,AllowServicesHasService)1173 TEST_F(CommandLineInterfaceTest, AllowServicesHasService) {
1174   // Test that services work fine as long as --disallow_services is not used.
1175 
1176   CreateTempFile("foo.proto",
1177                  "syntax = \"proto2\";\n"
1178                  "message Foo {}\n"
1179                  "service Bar {}\n");
1180 
1181   Run("protocol_compiler --test_out=$tmpdir "
1182       "--proto_path=$tmpdir foo.proto");
1183 
1184   ExpectNoErrors();
1185   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1186 }
1187 
TEST_F(CommandLineInterfaceTest,DirectDependencies_Missing_EmptyList)1188 TEST_F(CommandLineInterfaceTest, DirectDependencies_Missing_EmptyList) {
1189   CreateTempFile("foo.proto",
1190                  "syntax = \"proto2\";\n"
1191                  "import \"bar.proto\";\n"
1192                  "message Foo { optional Bar bar = 1; }");
1193   CreateTempFile("bar.proto",
1194                  "syntax = \"proto2\";\n"
1195                  "message Bar { optional string text = 1; }");
1196 
1197   Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
1198       "--direct_dependencies= foo.proto");
1199 
1200   ExpectErrorText(
1201       "foo.proto: File is imported but not declared in --direct_dependencies: "
1202       "bar.proto\n");
1203 }
1204 
TEST_F(CommandLineInterfaceTest,DirectDependencies_Missing)1205 TEST_F(CommandLineInterfaceTest, DirectDependencies_Missing) {
1206   CreateTempFile("foo.proto",
1207                  "syntax = \"proto2\";\n"
1208                  "import \"bar.proto\";\n"
1209                  "import \"bla.proto\";\n"
1210                  "message Foo { optional Bar bar = 1; optional Bla bla = 2; }");
1211   CreateTempFile("bar.proto",
1212                  "syntax = \"proto2\";\n"
1213                  "message Bar { optional string text = 1; }");
1214   CreateTempFile("bla.proto",
1215                  "syntax = \"proto2\";\n"
1216                  "message Bla { optional int64 number = 1; }");
1217 
1218   Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
1219       "--direct_dependencies=bla.proto foo.proto");
1220 
1221   ExpectErrorText(
1222       "foo.proto: File is imported but not declared in --direct_dependencies: "
1223       "bar.proto\n");
1224 }
1225 
TEST_F(CommandLineInterfaceTest,DirectDependencies_NoViolation)1226 TEST_F(CommandLineInterfaceTest, DirectDependencies_NoViolation) {
1227   CreateTempFile("foo.proto",
1228                  "syntax = \"proto2\";\n"
1229                  "import \"bar.proto\";\n"
1230                  "message Foo { optional Bar bar = 1; }");
1231   CreateTempFile("bar.proto",
1232                  "syntax = \"proto2\";\n"
1233                  "message Bar { optional string text = 1; }");
1234 
1235   Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
1236       "--direct_dependencies=bar.proto foo.proto");
1237 
1238   ExpectNoErrors();
1239 }
1240 
TEST_F(CommandLineInterfaceTest,DirectDependencies_NoViolation_MultiImports)1241 TEST_F(CommandLineInterfaceTest, DirectDependencies_NoViolation_MultiImports) {
1242   CreateTempFile("foo.proto",
1243                  "syntax = \"proto2\";\n"
1244                  "import \"bar.proto\";\n"
1245                  "import \"bla.proto\";\n"
1246                  "message Foo { optional Bar bar = 1; optional Bla bla = 2; }");
1247   CreateTempFile("bar.proto",
1248                  "syntax = \"proto2\";\n"
1249                  "message Bar { optional string text = 1; }");
1250   CreateTempFile("bla.proto",
1251                  "syntax = \"proto2\";\n"
1252                  "message Bla { optional int64 number = 1; }");
1253 
1254   Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
1255       "--direct_dependencies=bar.proto:bla.proto foo.proto");
1256 
1257   ExpectNoErrors();
1258 }
1259 
TEST_F(CommandLineInterfaceTest,DirectDependencies_ProvidedMultipleTimes)1260 TEST_F(CommandLineInterfaceTest, DirectDependencies_ProvidedMultipleTimes) {
1261   CreateTempFile("foo.proto", "syntax = \"proto2\";\n");
1262 
1263   Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
1264       "--direct_dependencies=bar.proto --direct_dependencies=bla.proto "
1265       "foo.proto");
1266 
1267   ExpectErrorText(
1268       "--direct_dependencies may only be passed once. To specify multiple "
1269       "direct dependencies, pass them all as a single parameter separated by "
1270       "':'.\n");
1271 }
1272 
TEST_F(CommandLineInterfaceTest,DirectDependencies_CustomErrorMessage)1273 TEST_F(CommandLineInterfaceTest, DirectDependencies_CustomErrorMessage) {
1274   CreateTempFile("foo.proto",
1275                  "syntax = \"proto2\";\n"
1276                  "import \"bar.proto\";\n"
1277                  "message Foo { optional Bar bar = 1; }");
1278   CreateTempFile("bar.proto",
1279                  "syntax = \"proto2\";\n"
1280                  "message Bar { optional string text = 1; }");
1281 
1282   std::vector<std::string> commands;
1283   commands.push_back("protocol_compiler");
1284   commands.push_back("--test_out=$tmpdir");
1285   commands.push_back("--proto_path=$tmpdir");
1286   commands.push_back("--direct_dependencies=");
1287   commands.push_back("--direct_dependencies_violation_msg=Bla \"%s\" Bla");
1288   commands.push_back("foo.proto");
1289   RunWithArgs(commands);
1290 
1291   ExpectErrorText("foo.proto: Bla \"bar.proto\" Bla\n");
1292 }
1293 
TEST_F(CommandLineInterfaceTest,CwdRelativeInputs)1294 TEST_F(CommandLineInterfaceTest, CwdRelativeInputs) {
1295   // Test that we can accept working-directory-relative input files.
1296 
1297   CreateTempFile("foo.proto",
1298                  "syntax = \"proto2\";\n"
1299                  "message Foo {}\n");
1300 
1301   Run("protocol_compiler --test_out=$tmpdir "
1302       "--proto_path=$tmpdir $tmpdir/foo.proto");
1303 
1304   ExpectNoErrors();
1305   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1306 }
1307 
TEST_F(CommandLineInterfaceTest,WriteDescriptorSet)1308 TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) {
1309   CreateTempFile("foo.proto",
1310                  "syntax = \"proto2\";\n"
1311                  "message Foo {}\n");
1312   CreateTempFile("bar.proto",
1313                  "syntax = \"proto2\";\n"
1314                  "import \"foo.proto\";\n"
1315                  "message Bar {\n"
1316                  "  optional Foo foo = 1;\n"
1317                  "}\n");
1318 
1319   Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
1320       "--proto_path=$tmpdir bar.proto");
1321 
1322   ExpectNoErrors();
1323 
1324   FileDescriptorSet descriptor_set;
1325   ReadDescriptorSet("descriptor_set", &descriptor_set);
1326   if (HasFatalFailure()) return;
1327   EXPECT_EQ(1, descriptor_set.file_size());
1328   EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
1329   // Descriptor set should not have source code info.
1330   EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
1331   // Descriptor set should have json_name.
1332   EXPECT_EQ("Bar", descriptor_set.file(0).message_type(0).name());
1333   EXPECT_EQ("foo", descriptor_set.file(0).message_type(0).field(0).name());
1334   EXPECT_TRUE(descriptor_set.file(0).message_type(0).field(0).has_json_name());
1335 }
1336 
TEST_F(CommandLineInterfaceTest,WriteDescriptorSetWithDuplicates)1337 TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithDuplicates) {
1338   CreateTempFile("foo.proto",
1339                  "syntax = \"proto2\";\n"
1340                  "message Foo {}\n");
1341   CreateTempFile("bar.proto",
1342                  "syntax = \"proto2\";\n"
1343                  "import \"foo.proto\";\n"
1344                  "message Bar {\n"
1345                  "  optional Foo foo = 1;\n"
1346                  "}\n");
1347   CreateTempFile("baz.proto",
1348                  "syntax = \"proto2\";\n"
1349                  "import \"foo.proto\";\n"
1350                  "message Baz {\n"
1351                  "  optional Foo foo = 1;\n"
1352                  "}\n");
1353 
1354   Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
1355       "--proto_path=$tmpdir bar.proto foo.proto bar.proto baz.proto");
1356 
1357   ExpectNoErrors();
1358 
1359   FileDescriptorSet descriptor_set;
1360   ReadDescriptorSet("descriptor_set", &descriptor_set);
1361   if (HasFatalFailure()) return;
1362   EXPECT_EQ(3, descriptor_set.file_size());
1363   // foo should come first since the output is in dependency order.
1364   // since bar and baz are unordered, they should be in command line order.
1365   EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
1366   EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
1367   EXPECT_EQ("baz.proto", descriptor_set.file(2).name());
1368   // Descriptor set should not have source code info.
1369   EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
1370   // Descriptor set should have json_name.
1371   EXPECT_EQ("Bar", descriptor_set.file(1).message_type(0).name());
1372   EXPECT_EQ("foo", descriptor_set.file(1).message_type(0).field(0).name());
1373   EXPECT_TRUE(descriptor_set.file(1).message_type(0).field(0).has_json_name());
1374 }
1375 
TEST_F(CommandLineInterfaceTest,WriteDescriptorSetWithSourceInfo)1376 TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) {
1377   CreateTempFile("foo.proto",
1378                  "syntax = \"proto2\";\n"
1379                  "message Foo {}\n");
1380   CreateTempFile("bar.proto",
1381                  "syntax = \"proto2\";\n"
1382                  "import \"foo.proto\";\n"
1383                  "message Bar {\n"
1384                  "  optional Foo foo = 1;\n"
1385                  "}\n");
1386 
1387   Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
1388       "--include_source_info --proto_path=$tmpdir bar.proto");
1389 
1390   ExpectNoErrors();
1391 
1392   FileDescriptorSet descriptor_set;
1393   ReadDescriptorSet("descriptor_set", &descriptor_set);
1394   if (HasFatalFailure()) return;
1395   EXPECT_EQ(1, descriptor_set.file_size());
1396   EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
1397   // Source code info included.
1398   EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
1399 }
1400 
TEST_F(CommandLineInterfaceTest,WriteTransitiveDescriptorSet)1401 TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) {
1402   CreateTempFile("foo.proto",
1403                  "syntax = \"proto2\";\n"
1404                  "message Foo {}\n");
1405   CreateTempFile("bar.proto",
1406                  "syntax = \"proto2\";\n"
1407                  "import \"foo.proto\";\n"
1408                  "message Bar {\n"
1409                  "  optional Foo foo = 1;\n"
1410                  "}\n");
1411 
1412   Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
1413       "--include_imports --proto_path=$tmpdir bar.proto");
1414 
1415   ExpectNoErrors();
1416 
1417   FileDescriptorSet descriptor_set;
1418   ReadDescriptorSet("descriptor_set", &descriptor_set);
1419   if (HasFatalFailure()) return;
1420   EXPECT_EQ(2, descriptor_set.file_size());
1421   if (descriptor_set.file(0).name() == "bar.proto") {
1422     std::swap(descriptor_set.mutable_file()->mutable_data()[0],
1423               descriptor_set.mutable_file()->mutable_data()[1]);
1424   }
1425   EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
1426   EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
1427   // Descriptor set should not have source code info.
1428   EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
1429   EXPECT_FALSE(descriptor_set.file(1).has_source_code_info());
1430 }
1431 
TEST_F(CommandLineInterfaceTest,WriteTransitiveDescriptorSetWithSourceInfo)1432 TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSetWithSourceInfo) {
1433   CreateTempFile("foo.proto",
1434                  "syntax = \"proto2\";\n"
1435                  "message Foo {}\n");
1436   CreateTempFile("bar.proto",
1437                  "syntax = \"proto2\";\n"
1438                  "import \"foo.proto\";\n"
1439                  "message Bar {\n"
1440                  "  optional Foo foo = 1;\n"
1441                  "}\n");
1442 
1443   Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
1444       "--include_imports --include_source_info --proto_path=$tmpdir bar.proto");
1445 
1446   ExpectNoErrors();
1447 
1448   FileDescriptorSet descriptor_set;
1449   ReadDescriptorSet("descriptor_set", &descriptor_set);
1450   if (HasFatalFailure()) return;
1451   EXPECT_EQ(2, descriptor_set.file_size());
1452   if (descriptor_set.file(0).name() == "bar.proto") {
1453     std::swap(descriptor_set.mutable_file()->mutable_data()[0],
1454               descriptor_set.mutable_file()->mutable_data()[1]);
1455   }
1456   EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
1457   EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
1458   // Source code info included.
1459   EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
1460   EXPECT_TRUE(descriptor_set.file(1).has_source_code_info());
1461 }
1462 
1463 #ifdef _WIN32
1464 // TODO(teboring): Figure out how to write test on windows.
1465 #else
TEST_F(CommandLineInterfaceTest,WriteDependencyManifestFileGivenTwoInputs)1466 TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileGivenTwoInputs) {
1467   CreateTempFile("foo.proto",
1468                  "syntax = \"proto2\";\n"
1469                  "message Foo {}\n");
1470   CreateTempFile("bar.proto",
1471                  "syntax = \"proto2\";\n"
1472                  "import \"foo.proto\";\n"
1473                  "message Bar {\n"
1474                  "  optional Foo foo = 1;\n"
1475                  "}\n");
1476 
1477   Run("protocol_compiler --dependency_out=$tmpdir/manifest "
1478       "--test_out=$tmpdir --proto_path=$tmpdir bar.proto foo.proto");
1479 
1480   ExpectErrorText(
1481       "Can only process one input file when using --dependency_out=FILE.\n");
1482 }
1483 
1484 #ifdef PROTOBUF_OPENSOURCE
TEST_F(CommandLineInterfaceTest,WriteDependencyManifestFile)1485 TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFile) {
1486   CreateTempFile("foo.proto",
1487                  "syntax = \"proto2\";\n"
1488                  "message Foo {}\n");
1489   CreateTempFile("bar.proto",
1490                  "syntax = \"proto2\";\n"
1491                  "import \"foo.proto\";\n"
1492                  "message Bar {\n"
1493                  "  optional Foo foo = 1;\n"
1494                  "}\n");
1495 
1496   std::string current_working_directory = getcwd(NULL, 0);
1497   SwitchToTempDirectory();
1498 
1499   Run("protocol_compiler --dependency_out=manifest --test_out=. "
1500       "bar.proto");
1501 
1502   ExpectNoErrors();
1503 
1504   ExpectFileContent("manifest",
1505                     "bar.proto.MockCodeGenerator.test_generator: "
1506                     "foo.proto\\\n bar.proto");
1507 
1508   File::ChangeWorkingDirectory(current_working_directory);
1509 }
1510 #else  // !PROTOBUF_OPENSOURCE
1511 // TODO(teboring): Figure out how to change and get working directory in
1512 // google3.
1513 #endif  // !PROTOBUF_OPENSOURCE
1514 
TEST_F(CommandLineInterfaceTest,WriteDependencyManifestFileForAbsolutePath)1515 TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileForAbsolutePath) {
1516   CreateTempFile("foo.proto",
1517                  "syntax = \"proto2\";\n"
1518                  "message Foo {}\n");
1519   CreateTempFile("bar.proto",
1520                  "syntax = \"proto2\";\n"
1521                  "import \"foo.proto\";\n"
1522                  "message Bar {\n"
1523                  "  optional Foo foo = 1;\n"
1524                  "}\n");
1525 
1526   Run("protocol_compiler --dependency_out=$tmpdir/manifest "
1527       "--test_out=$tmpdir --proto_path=$tmpdir bar.proto");
1528 
1529   ExpectNoErrors();
1530 
1531   ExpectFileContent("manifest",
1532                     "$tmpdir/bar.proto.MockCodeGenerator.test_generator: "
1533                     "$tmpdir/foo.proto\\\n $tmpdir/bar.proto");
1534 }
1535 #endif  // !_WIN32
1536 
TEST_F(CommandLineInterfaceTest,TestArgumentFile)1537 TEST_F(CommandLineInterfaceTest, TestArgumentFile) {
1538   // Test parsing multiple input files using an argument file.
1539 
1540   CreateTempFile("foo.proto",
1541                  "syntax = \"proto2\";\n"
1542                  "message Foo {}\n");
1543   CreateTempFile("bar.proto",
1544                  "syntax = \"proto2\";\n"
1545                  "message Bar {}\n");
1546   CreateTempFile("arguments.txt",
1547                  "--test_out=$tmpdir\n"
1548                  "--plug_out=$tmpdir\n"
1549                  "--proto_path=$tmpdir\n"
1550                  "--direct_dependencies_violation_msg=%s is not imported\n"
1551                  "foo.proto\n"
1552                  "bar.proto");
1553 
1554   Run("protocol_compiler @$tmpdir/arguments.txt");
1555 
1556   ExpectNoErrors();
1557   ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
1558                                     "foo.proto", "Foo");
1559   ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
1560                                     "bar.proto", "Bar");
1561   ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
1562                                     "foo.proto", "Foo");
1563   ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
1564                                     "bar.proto", "Bar");
1565 }
1566 
1567 
1568 // -------------------------------------------------------------------
1569 
TEST_F(CommandLineInterfaceTest,ParseErrors)1570 TEST_F(CommandLineInterfaceTest, ParseErrors) {
1571   // Test that parse errors are reported.
1572 
1573   CreateTempFile("foo.proto",
1574                  "syntax = \"proto2\";\n"
1575                  "badsyntax\n");
1576 
1577   Run("protocol_compiler --test_out=$tmpdir "
1578       "--proto_path=$tmpdir foo.proto");
1579 
1580   ExpectErrorText(
1581       "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
1582 }
1583 
TEST_F(CommandLineInterfaceTest,ParseErrors_DescriptorSetIn)1584 TEST_F(CommandLineInterfaceTest, ParseErrors_DescriptorSetIn) {
1585   // Test that parse errors are reported.
1586   CreateTempFile("foo.bin", "not a FileDescriptorSet");
1587 
1588   Run("protocol_compiler --test_out=$tmpdir "
1589       "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
1590 
1591   ExpectErrorText("$tmpdir/foo.bin: Unable to parse.\n");
1592 }
1593 
TEST_F(CommandLineInterfaceTest,ParseErrorsMultipleFiles)1594 TEST_F(CommandLineInterfaceTest, ParseErrorsMultipleFiles) {
1595   // Test that parse errors are reported from multiple files.
1596 
1597   // We set up files such that foo.proto actually depends on bar.proto in
1598   // two ways:  Directly and through baz.proto.  bar.proto's errors should
1599   // only be reported once.
1600   CreateTempFile("bar.proto",
1601                  "syntax = \"proto2\";\n"
1602                  "badsyntax\n");
1603   CreateTempFile("baz.proto",
1604                  "syntax = \"proto2\";\n"
1605                  "import \"bar.proto\";\n");
1606   CreateTempFile("foo.proto",
1607                  "syntax = \"proto2\";\n"
1608                  "import \"bar.proto\";\n"
1609                  "import \"baz.proto\";\n");
1610 
1611   Run("protocol_compiler --test_out=$tmpdir "
1612       "--proto_path=$tmpdir foo.proto");
1613 
1614   ExpectErrorText(
1615       "bar.proto:2:1: Expected top-level statement (e.g. \"message\").\n"
1616       "baz.proto:2:1: Import \"bar.proto\" was not found or had errors.\n"
1617       "foo.proto:2:1: Import \"bar.proto\" was not found or had errors.\n"
1618       "foo.proto:3:1: Import \"baz.proto\" was not found or had errors.\n");
1619 }
1620 
TEST_F(CommandLineInterfaceTest,RecursiveImportFails)1621 TEST_F(CommandLineInterfaceTest, RecursiveImportFails) {
1622   // Create a proto file that imports itself.
1623   CreateTempFile("foo.proto",
1624                  "syntax = \"proto2\";\n"
1625                  "import \"foo.proto\";\n");
1626 
1627   Run("protocol_compiler --test_out=$tmpdir "
1628       "--proto_path=$tmpdir foo.proto");
1629 
1630   ExpectErrorSubstring(
1631       "foo.proto:2:1: File recursively imports itself: "
1632       "foo.proto -> foo.proto\n");
1633 }
1634 
TEST_F(CommandLineInterfaceTest,InputNotFoundError)1635 TEST_F(CommandLineInterfaceTest, InputNotFoundError) {
1636   // Test what happens if the input file is not found.
1637 
1638   Run("protocol_compiler --test_out=$tmpdir "
1639       "--proto_path=$tmpdir foo.proto");
1640 
1641   ExpectErrorText("foo.proto: No such file or directory\n");
1642 }
1643 
TEST_F(CommandLineInterfaceTest,InputNotFoundError_DescriptorSetIn)1644 TEST_F(CommandLineInterfaceTest, InputNotFoundError_DescriptorSetIn) {
1645   // Test what happens if the input file is not found.
1646 
1647   Run("protocol_compiler --test_out=$tmpdir "
1648       "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
1649 
1650   ExpectErrorText("$tmpdir/foo.bin: No such file or directory\n");
1651 }
1652 
TEST_F(CommandLineInterfaceTest,CwdRelativeInputNotFoundError)1653 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundError) {
1654   // Test what happens when a working-directory-relative input file is not
1655   // found.
1656 
1657   Run("protocol_compiler --test_out=$tmpdir "
1658       "--proto_path=$tmpdir $tmpdir/foo.proto");
1659 
1660   ExpectErrorText("$tmpdir/foo.proto: No such file or directory\n");
1661 }
1662 
TEST_F(CommandLineInterfaceTest,CwdRelativeInputNotMappedError)1663 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotMappedError) {
1664   // Test what happens when a working-directory-relative input file is not
1665   // mapped to a virtual path.
1666 
1667   CreateTempFile("foo.proto",
1668                  "syntax = \"proto2\";\n"
1669                  "message Foo {}\n");
1670 
1671   // Create a directory called "bar" so that we can point --proto_path at it.
1672   CreateTempFile("bar/dummy", "");
1673 
1674   Run("protocol_compiler --test_out=$tmpdir "
1675       "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
1676 
1677   ExpectErrorText(
1678       "$tmpdir/foo.proto: File does not reside within any path "
1679       "specified using --proto_path (or -I).  You must specify a "
1680       "--proto_path which encompasses this file.  Note that the "
1681       "proto_path must be an exact prefix of the .proto file "
1682       "names -- protoc is too dumb to figure out when two paths "
1683       "(e.g. absolute and relative) are equivalent (it's harder "
1684       "than you think).\n");
1685 }
1686 
TEST_F(CommandLineInterfaceTest,CwdRelativeInputNotFoundAndNotMappedError)1687 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundAndNotMappedError) {
1688   // Check what happens if the input file is not found *and* is not mapped
1689   // in the proto_path.
1690 
1691   // Create a directory called "bar" so that we can point --proto_path at it.
1692   CreateTempFile("bar/dummy", "");
1693 
1694   Run("protocol_compiler --test_out=$tmpdir "
1695       "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
1696 
1697   ExpectErrorText("$tmpdir/foo.proto: No such file or directory\n");
1698 }
1699 
TEST_F(CommandLineInterfaceTest,CwdRelativeInputShadowedError)1700 TEST_F(CommandLineInterfaceTest, CwdRelativeInputShadowedError) {
1701   // Test what happens when a working-directory-relative input file is shadowed
1702   // by another file in the virtual path.
1703 
1704   CreateTempFile("foo/foo.proto",
1705                  "syntax = \"proto2\";\n"
1706                  "message Foo {}\n");
1707   CreateTempFile("bar/foo.proto",
1708                  "syntax = \"proto2\";\n"
1709                  "message Bar {}\n");
1710 
1711   Run("protocol_compiler --test_out=$tmpdir "
1712       "--proto_path=$tmpdir/foo --proto_path=$tmpdir/bar "
1713       "$tmpdir/bar/foo.proto");
1714 
1715   ExpectErrorText(
1716       "$tmpdir/bar/foo.proto: Input is shadowed in the --proto_path "
1717       "by \"$tmpdir/foo/foo.proto\".  Either use the latter "
1718       "file as your input or reorder the --proto_path so that the "
1719       "former file's location comes first.\n");
1720 }
1721 
TEST_F(CommandLineInterfaceTest,ProtoPathNotFoundError)1722 TEST_F(CommandLineInterfaceTest, ProtoPathNotFoundError) {
1723   // Test what happens if the input file is not found.
1724 
1725   Run("protocol_compiler --test_out=$tmpdir "
1726       "--proto_path=$tmpdir/foo foo.proto");
1727 
1728   ExpectErrorText(
1729       "$tmpdir/foo: warning: directory does not exist.\n"
1730       "foo.proto: No such file or directory\n");
1731 }
1732 
TEST_F(CommandLineInterfaceTest,ProtoPathAndDescriptorSetIn)1733 TEST_F(CommandLineInterfaceTest, ProtoPathAndDescriptorSetIn) {
1734   Run("protocol_compiler --test_out=$tmpdir "
1735       "--proto_path=$tmpdir --descriptor_set_in=$tmpdir/foo.bin foo.proto");
1736   ExpectErrorText("$tmpdir/foo.bin: No such file or directory\n");
1737 
1738   Run("protocol_compiler --test_out=$tmpdir "
1739       "--descriptor_set_in=$tmpdir/foo.bin --proto_path=$tmpdir foo.proto");
1740   ExpectErrorText("$tmpdir/foo.bin: No such file or directory\n");
1741 }
1742 
TEST_F(CommandLineInterfaceTest,ProtoPathAndDescriptorSetIn_CompileFiles)1743 TEST_F(CommandLineInterfaceTest, ProtoPathAndDescriptorSetIn_CompileFiles) {
1744   // Test what happens if a proto is in a --descriptor_set_in and also exists
1745   // on disk.
1746   FileDescriptorSet file_descriptor_set;
1747 
1748   // NOTE: This file desc SHOULD be different from the one created as a temp
1749   //       to make it easier to test that the file was output instead of the
1750   //       contents of the --descriptor_set_in file.
1751   FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
1752   file_descriptor_proto->set_name("foo.proto");
1753   file_descriptor_proto->add_message_type()->set_name("Foo");
1754 
1755   WriteDescriptorSet("foo.bin", &file_descriptor_set);
1756 
1757   CreateTempFile("foo.proto",
1758                  "syntax = \"proto2\";\n"
1759                  "message FooBar { required string foo_message = 1; }\n");
1760 
1761   Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
1762       "--descriptor_set_in=$tmpdir/foo.bin "
1763       "--include_source_info "
1764       "--proto_path=$tmpdir foo.proto");
1765 
1766   ExpectNoErrors();
1767 
1768   FileDescriptorSet descriptor_set;
1769   ReadDescriptorSet("descriptor_set", &descriptor_set);
1770 
1771   EXPECT_EQ(1, descriptor_set.file_size());
1772   EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
1773   // Descriptor set SHOULD have source code info.
1774   EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
1775 
1776   EXPECT_EQ("FooBar", descriptor_set.file(0).message_type(0).name());
1777   EXPECT_EQ("foo_message",
1778             descriptor_set.file(0).message_type(0).field(0).name());
1779 }
1780 
TEST_F(CommandLineInterfaceTest,ProtoPathAndDependencyOut)1781 TEST_F(CommandLineInterfaceTest, ProtoPathAndDependencyOut) {
1782   Run("protocol_compiler --test_out=$tmpdir "
1783       "--dependency_out=$tmpdir/manifest "
1784       "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
1785   ExpectErrorText(
1786       "--descriptor_set_in cannot be used with --dependency_out.\n");
1787 
1788   Run("protocol_compiler --test_out=$tmpdir "
1789       "--descriptor_set_in=$tmpdir/foo.bin "
1790       "--dependency_out=$tmpdir/manifest foo.proto");
1791   ExpectErrorText(
1792       "--dependency_out cannot be used with --descriptor_set_in.\n");
1793 }
1794 
TEST_F(CommandLineInterfaceTest,MissingInputError)1795 TEST_F(CommandLineInterfaceTest, MissingInputError) {
1796   // Test that we get an error if no inputs are given.
1797 
1798   Run("protocol_compiler --test_out=$tmpdir "
1799       "--proto_path=$tmpdir");
1800 
1801   ExpectErrorText("Missing input file.\n");
1802 }
1803 
TEST_F(CommandLineInterfaceTest,MissingOutputError)1804 TEST_F(CommandLineInterfaceTest, MissingOutputError) {
1805   CreateTempFile("foo.proto",
1806                  "syntax = \"proto2\";\n"
1807                  "message Foo {}\n");
1808 
1809   Run("protocol_compiler --proto_path=$tmpdir foo.proto");
1810 
1811   ExpectErrorText("Missing output directives.\n");
1812 }
1813 
TEST_F(CommandLineInterfaceTest,OutputWriteError)1814 TEST_F(CommandLineInterfaceTest, OutputWriteError) {
1815   CreateTempFile("foo.proto",
1816                  "syntax = \"proto2\";\n"
1817                  "message Foo {}\n");
1818 
1819   std::string output_file =
1820       MockCodeGenerator::GetOutputFileName("test_generator", "foo.proto");
1821 
1822   // Create a directory blocking our output location.
1823   CreateTempDir(output_file);
1824 
1825   Run("protocol_compiler --test_out=$tmpdir "
1826       "--proto_path=$tmpdir foo.proto");
1827 
1828   // MockCodeGenerator no longer detects an error because we actually write to
1829   // an in-memory location first, then dump to disk at the end.  This is no
1830   // big deal.
1831   //   ExpectErrorSubstring("MockCodeGenerator detected write error.");
1832 
1833 #if defined(_WIN32) && !defined(__CYGWIN__)
1834   // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
1835   if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
1836     return;
1837   }
1838 #endif
1839 
1840   ExpectErrorSubstring(output_file + ": Is a directory");
1841 }
1842 
TEST_F(CommandLineInterfaceTest,PluginOutputWriteError)1843 TEST_F(CommandLineInterfaceTest, PluginOutputWriteError) {
1844   CreateTempFile("foo.proto",
1845                  "syntax = \"proto2\";\n"
1846                  "message Foo {}\n");
1847 
1848   std::string output_file =
1849       MockCodeGenerator::GetOutputFileName("test_plugin", "foo.proto");
1850 
1851   // Create a directory blocking our output location.
1852   CreateTempDir(output_file);
1853 
1854   Run("protocol_compiler --plug_out=$tmpdir "
1855       "--proto_path=$tmpdir foo.proto");
1856 
1857 #if defined(_WIN32) && !defined(__CYGWIN__)
1858   // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
1859   if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
1860     return;
1861   }
1862 #endif
1863 
1864   ExpectErrorSubstring(output_file + ": Is a directory");
1865 }
1866 
TEST_F(CommandLineInterfaceTest,OutputDirectoryNotFoundError)1867 TEST_F(CommandLineInterfaceTest, OutputDirectoryNotFoundError) {
1868   CreateTempFile("foo.proto",
1869                  "syntax = \"proto2\";\n"
1870                  "message Foo {}\n");
1871 
1872   Run("protocol_compiler --test_out=$tmpdir/nosuchdir "
1873       "--proto_path=$tmpdir foo.proto");
1874 
1875   ExpectErrorSubstring("nosuchdir/: No such file or directory");
1876 }
1877 
TEST_F(CommandLineInterfaceTest,PluginOutputDirectoryNotFoundError)1878 TEST_F(CommandLineInterfaceTest, PluginOutputDirectoryNotFoundError) {
1879   CreateTempFile("foo.proto",
1880                  "syntax = \"proto2\";\n"
1881                  "message Foo {}\n");
1882 
1883   Run("protocol_compiler --plug_out=$tmpdir/nosuchdir "
1884       "--proto_path=$tmpdir foo.proto");
1885 
1886   ExpectErrorSubstring("nosuchdir/: No such file or directory");
1887 }
1888 
TEST_F(CommandLineInterfaceTest,OutputDirectoryIsFileError)1889 TEST_F(CommandLineInterfaceTest, OutputDirectoryIsFileError) {
1890   CreateTempFile("foo.proto",
1891                  "syntax = \"proto2\";\n"
1892                  "message Foo {}\n");
1893 
1894   Run("protocol_compiler --test_out=$tmpdir/foo.proto "
1895       "--proto_path=$tmpdir foo.proto");
1896 
1897 #if defined(_WIN32) && !defined(__CYGWIN__)
1898   // Windows with MSVCRT.dll produces EINVAL instead of ENOTDIR.
1899   if (HasAlternateErrorSubstring("foo.proto/: Invalid argument")) {
1900     return;
1901   }
1902 #endif
1903 
1904   ExpectErrorSubstring("foo.proto/: Not a directory");
1905 }
1906 
TEST_F(CommandLineInterfaceTest,GeneratorError)1907 TEST_F(CommandLineInterfaceTest, GeneratorError) {
1908   CreateTempFile("foo.proto",
1909                  "syntax = \"proto2\";\n"
1910                  "message MockCodeGenerator_Error {}\n");
1911 
1912   Run("protocol_compiler --test_out=$tmpdir "
1913       "--proto_path=$tmpdir foo.proto");
1914 
1915   ExpectErrorSubstring(
1916       "--test_out: foo.proto: Saw message type MockCodeGenerator_Error.");
1917 }
1918 
TEST_F(CommandLineInterfaceTest,GeneratorPluginError)1919 TEST_F(CommandLineInterfaceTest, GeneratorPluginError) {
1920   // Test a generator plugin that returns an error.
1921 
1922   CreateTempFile("foo.proto",
1923                  "syntax = \"proto2\";\n"
1924                  "message MockCodeGenerator_Error {}\n");
1925 
1926   Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1927       "--proto_path=$tmpdir foo.proto");
1928 
1929   ExpectErrorSubstring(
1930       "--plug_out: foo.proto: Saw message type MockCodeGenerator_Error.");
1931 }
1932 
TEST_F(CommandLineInterfaceTest,GeneratorPluginFail)1933 TEST_F(CommandLineInterfaceTest, GeneratorPluginFail) {
1934   // Test a generator plugin that exits with an error code.
1935 
1936   CreateTempFile("foo.proto",
1937                  "syntax = \"proto2\";\n"
1938                  "message MockCodeGenerator_Exit {}\n");
1939 
1940   Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1941       "--proto_path=$tmpdir foo.proto");
1942 
1943   ExpectErrorSubstring("Saw message type MockCodeGenerator_Exit.");
1944   ExpectErrorSubstring(
1945       "--plug_out: prefix-gen-plug: Plugin failed with status code 123.");
1946 }
1947 
TEST_F(CommandLineInterfaceTest,GeneratorPluginCrash)1948 TEST_F(CommandLineInterfaceTest, GeneratorPluginCrash) {
1949   // Test a generator plugin that crashes.
1950 
1951   CreateTempFile("foo.proto",
1952                  "syntax = \"proto2\";\n"
1953                  "message MockCodeGenerator_Abort {}\n");
1954 
1955   Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1956       "--proto_path=$tmpdir foo.proto");
1957 
1958   ExpectErrorSubstring("Saw message type MockCodeGenerator_Abort.");
1959 
1960 #ifdef _WIN32
1961   // Windows doesn't have signals.  It looks like abort()ing causes the process
1962   // to exit with status code 3, but let's not depend on the exact number here.
1963   ExpectErrorSubstring(
1964       "--plug_out: prefix-gen-plug: Plugin failed with status code");
1965 #else
1966   // Don't depend on the exact signal number.
1967   ExpectErrorSubstring("--plug_out: prefix-gen-plug: Plugin killed by signal");
1968 #endif
1969 }
1970 
TEST_F(CommandLineInterfaceTest,PluginReceivesSourceCodeInfo)1971 TEST_F(CommandLineInterfaceTest, PluginReceivesSourceCodeInfo) {
1972   CreateTempFile("foo.proto",
1973                  "syntax = \"proto2\";\n"
1974                  "message MockCodeGenerator_HasSourceCodeInfo {}\n");
1975 
1976   Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
1977 
1978   ExpectErrorSubstring(
1979       "Saw message type MockCodeGenerator_HasSourceCodeInfo: 1.");
1980 }
1981 
TEST_F(CommandLineInterfaceTest,PluginReceivesJsonName)1982 TEST_F(CommandLineInterfaceTest, PluginReceivesJsonName) {
1983   CreateTempFile("foo.proto",
1984                  "syntax = \"proto2\";\n"
1985                  "message MockCodeGenerator_HasJsonName {\n"
1986                  "  optional int32 value = 1;\n"
1987                  "}\n");
1988 
1989   Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
1990 
1991   ExpectErrorSubstring("Saw json_name: 1");
1992 }
1993 
TEST_F(CommandLineInterfaceTest,PluginReceivesCompilerVersion)1994 TEST_F(CommandLineInterfaceTest, PluginReceivesCompilerVersion) {
1995   CreateTempFile("foo.proto",
1996                  "syntax = \"proto2\";\n"
1997                  "message MockCodeGenerator_ShowVersionNumber {\n"
1998                  "  optional int32 value = 1;\n"
1999                  "}\n");
2000 
2001   Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
2002 
2003   ExpectErrorSubstring(StringPrintf("Saw compiler_version: %d %s",
2004                                     GOOGLE_PROTOBUF_VERSION,
2005                                     GOOGLE_PROTOBUF_VERSION_SUFFIX));
2006 }
2007 
TEST_F(CommandLineInterfaceTest,GeneratorPluginNotFound)2008 TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
2009   // Test what happens if the plugin isn't found.
2010 
2011   CreateTempFile("error.proto",
2012                  "syntax = \"proto2\";\n"
2013                  "message Foo {}\n");
2014 
2015   Run("protocol_compiler --badplug_out=TestParameter:$tmpdir "
2016       "--plugin=prefix-gen-badplug=no_such_file "
2017       "--proto_path=$tmpdir error.proto");
2018 
2019 #ifdef _WIN32
2020   ExpectErrorSubstring("--badplug_out: prefix-gen-badplug: " +
2021                        Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND));
2022 #else
2023   // Error written to stdout by child process after exec() fails.
2024   ExpectErrorSubstring("no_such_file: program not found or is not executable");
2025 
2026   ExpectErrorSubstring(
2027       "Please specify a program using absolute path or make sure "
2028       "the program is available in your PATH system variable");
2029 
2030   // Error written by parent process when child fails.
2031   ExpectErrorSubstring(
2032       "--badplug_out: prefix-gen-badplug: Plugin failed with status code 1.");
2033 #endif
2034 }
2035 
TEST_F(CommandLineInterfaceTest,GeneratorPluginNotAllowed)2036 TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) {
2037   // Test what happens if plugins aren't allowed.
2038 
2039   CreateTempFile("error.proto",
2040                  "syntax = \"proto2\";\n"
2041                  "message Foo {}\n");
2042 
2043   DisallowPlugins();
2044   Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
2045       "--proto_path=$tmpdir error.proto");
2046 
2047   ExpectErrorSubstring("Unknown flag: --plug_out");
2048 }
2049 
TEST_F(CommandLineInterfaceTest,HelpText)2050 TEST_F(CommandLineInterfaceTest, HelpText) {
2051   Run("test_exec_name --help");
2052 
2053   ExpectCapturedStdoutSubstringWithZeroReturnCode("Usage: test_exec_name ");
2054   ExpectCapturedStdoutSubstringWithZeroReturnCode("--test_out=OUT_DIR");
2055   ExpectCapturedStdoutSubstringWithZeroReturnCode("Test output.");
2056   ExpectCapturedStdoutSubstringWithZeroReturnCode("--alt_out=OUT_DIR");
2057   ExpectCapturedStdoutSubstringWithZeroReturnCode("Alt output.");
2058 }
2059 
TEST_F(CommandLineInterfaceTest,GccFormatErrors)2060 TEST_F(CommandLineInterfaceTest, GccFormatErrors) {
2061   // Test --error_format=gcc (which is the default, but we want to verify
2062   // that it can be set explicitly).
2063 
2064   CreateTempFile("foo.proto",
2065                  "syntax = \"proto2\";\n"
2066                  "badsyntax\n");
2067 
2068   Run("protocol_compiler --test_out=$tmpdir "
2069       "--proto_path=$tmpdir --error_format=gcc foo.proto");
2070 
2071   ExpectErrorText(
2072       "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
2073 }
2074 
TEST_F(CommandLineInterfaceTest,MsvsFormatErrors)2075 TEST_F(CommandLineInterfaceTest, MsvsFormatErrors) {
2076   // Test --error_format=msvs
2077 
2078   CreateTempFile("foo.proto",
2079                  "syntax = \"proto2\";\n"
2080                  "badsyntax\n");
2081 
2082   Run("protocol_compiler --test_out=$tmpdir "
2083       "--proto_path=$tmpdir --error_format=msvs foo.proto");
2084 
2085   ExpectErrorText(
2086       "$tmpdir/foo.proto(2) : error in column=1: Expected top-level statement "
2087       "(e.g. \"message\").\n");
2088 }
2089 
TEST_F(CommandLineInterfaceTest,InvalidErrorFormat)2090 TEST_F(CommandLineInterfaceTest, InvalidErrorFormat) {
2091   // Test --error_format=msvs
2092 
2093   CreateTempFile("foo.proto",
2094                  "syntax = \"proto2\";\n"
2095                  "badsyntax\n");
2096 
2097   Run("protocol_compiler --test_out=$tmpdir "
2098       "--proto_path=$tmpdir --error_format=invalid foo.proto");
2099 
2100   ExpectErrorText("Unknown error format: invalid\n");
2101 }
2102 
2103 // -------------------------------------------------------------------
2104 // Flag parsing tests
2105 
TEST_F(CommandLineInterfaceTest,ParseSingleCharacterFlag)2106 TEST_F(CommandLineInterfaceTest, ParseSingleCharacterFlag) {
2107   // Test that a single-character flag works.
2108 
2109   CreateTempFile("foo.proto",
2110                  "syntax = \"proto2\";\n"
2111                  "message Foo {}\n");
2112 
2113   Run("protocol_compiler -t$tmpdir "
2114       "--proto_path=$tmpdir foo.proto");
2115 
2116   ExpectNoErrors();
2117   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
2118 }
2119 
TEST_F(CommandLineInterfaceTest,ParseSpaceDelimitedValue)2120 TEST_F(CommandLineInterfaceTest, ParseSpaceDelimitedValue) {
2121   // Test that separating the flag value with a space works.
2122 
2123   CreateTempFile("foo.proto",
2124                  "syntax = \"proto2\";\n"
2125                  "message Foo {}\n");
2126 
2127   Run("protocol_compiler --test_out $tmpdir "
2128       "--proto_path=$tmpdir foo.proto");
2129 
2130   ExpectNoErrors();
2131   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
2132 }
2133 
TEST_F(CommandLineInterfaceTest,ParseSingleCharacterSpaceDelimitedValue)2134 TEST_F(CommandLineInterfaceTest, ParseSingleCharacterSpaceDelimitedValue) {
2135   // Test that separating the flag value with a space works for
2136   // single-character flags.
2137 
2138   CreateTempFile("foo.proto",
2139                  "syntax = \"proto2\";\n"
2140                  "message Foo {}\n");
2141 
2142   Run("protocol_compiler -t $tmpdir "
2143       "--proto_path=$tmpdir foo.proto");
2144 
2145   ExpectNoErrors();
2146   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
2147 }
2148 
TEST_F(CommandLineInterfaceTest,MissingValueError)2149 TEST_F(CommandLineInterfaceTest, MissingValueError) {
2150   // Test that we get an error if a flag is missing its value.
2151 
2152   Run("protocol_compiler --test_out --proto_path=$tmpdir foo.proto");
2153 
2154   ExpectErrorText("Missing value for flag: --test_out\n");
2155 }
2156 
TEST_F(CommandLineInterfaceTest,MissingValueAtEndError)2157 TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) {
2158   // Test that we get an error if the last argument is a flag requiring a
2159   // value.
2160 
2161   Run("protocol_compiler --test_out");
2162 
2163   ExpectErrorText("Missing value for flag: --test_out\n");
2164 }
2165 
TEST_F(CommandLineInterfaceTest,PrintFreeFieldNumbers)2166 TEST_F(CommandLineInterfaceTest, PrintFreeFieldNumbers) {
2167   CreateTempFile("foo.proto",
2168                  "syntax = \"proto2\";\n"
2169                  "package foo;\n"
2170                  "message Foo {\n"
2171                  "  optional int32 a = 2;\n"
2172                  "  optional string b = 4;\n"
2173                  "  optional string c = 5;\n"
2174                  "  optional int64 d = 8;\n"
2175                  "  optional double e = 10;\n"
2176                  "}\n");
2177   CreateTempFile("bar.proto",
2178                  "syntax = \"proto2\";\n"
2179                  "message Bar {\n"
2180                  "  optional int32 a = 2;\n"
2181                  "  extensions 4 to 5;\n"
2182                  "  optional int64 d = 8;\n"
2183                  "  extensions 10;\n"
2184                  "}\n");
2185   CreateTempFile("baz.proto",
2186                  "syntax = \"proto2\";\n"
2187                  "message Baz {\n"
2188                  "  optional int32 a = 2;\n"
2189                  "  optional int64 d = 8;\n"
2190                  "  extensions 15 to max;\n"  // unordered.
2191                  "  extensions 13;\n"
2192                  "  extensions 10 to 12;\n"
2193                  "  extensions 5;\n"
2194                  "  extensions 4;\n"
2195                  "}\n");
2196   CreateTempFile(
2197       "quz.proto",
2198       "syntax = \"proto2\";\n"
2199       "message Quz {\n"
2200       "  message Foo {}\n"  // nested message
2201       "  optional int32 a = 2;\n"
2202       "  optional group C = 4 {\n"
2203       "    optional int32 d = 5;\n"
2204       "  }\n"
2205       "  extensions 8 to 10;\n"
2206       "  optional group E = 11 {\n"
2207       "    optional int32 f = 9;\n"    // explicitly reuse extension range 8-10
2208       "    optional group G = 15 {\n"  // nested group
2209       "      message Foo {}\n"         // nested message inside nested group
2210       "    }\n"
2211       "  }\n"
2212       "}\n");
2213 
2214   Run("protocol_compiler --print_free_field_numbers --proto_path=$tmpdir "
2215       "foo.proto bar.proto baz.proto quz.proto");
2216 
2217   ExpectNoErrors();
2218 
2219   // TODO(jieluo): Cygwin doesn't work well if we try to capture stderr and
2220   // stdout at the same time. Need to figure out why and add this test back
2221   // for Cygwin.
2222 #if !defined(__CYGWIN__)
2223   ExpectCapturedStdout(
2224       "foo.Foo                             free: 1 3 6-7 9 11-INF\n"
2225       "Bar                                 free: 1 3 6-7 9 11-INF\n"
2226       "Baz                                 free: 1 3 6-7 9 14\n"
2227       "Quz.Foo                             free: 1-INF\n"
2228       "Quz.E.G.Foo                         free: 1-INF\n"
2229       "Quz                                 free: 1 3 6-7 12-14 16-INF\n");
2230 #endif
2231 }
2232 
2233 // ===================================================================
2234 
2235 // Test for --encode and --decode.  Note that it would be easier to do this
2236 // test as a shell script, but we'd like to be able to run the test on
2237 // platforms that don't have a Bourne-compatible shell available (especially
2238 // Windows/MSVC).
2239 
2240 enum EncodeDecodeTestMode { PROTO_PATH, DESCRIPTOR_SET_IN };
2241 
2242 class EncodeDecodeTest : public testing::TestWithParam<EncodeDecodeTestMode> {
2243  protected:
SetUp()2244   virtual void SetUp() {
2245     WriteUnittestProtoDescriptorSet();
2246     duped_stdin_ = dup(STDIN_FILENO);
2247   }
2248 
TearDown()2249   virtual void TearDown() {
2250     dup2(duped_stdin_, STDIN_FILENO);
2251     close(duped_stdin_);
2252   }
2253 
RedirectStdinFromText(const std::string & input)2254   void RedirectStdinFromText(const std::string& input) {
2255     std::string filename = TestTempDir() + "/test_stdin";
2256     GOOGLE_CHECK_OK(File::SetContents(filename, input, true));
2257     GOOGLE_CHECK(RedirectStdinFromFile(filename));
2258   }
2259 
RedirectStdinFromFile(const std::string & filename)2260   bool RedirectStdinFromFile(const std::string& filename) {
2261     int fd = open(filename.c_str(), O_RDONLY);
2262     if (fd < 0) return false;
2263     dup2(fd, STDIN_FILENO);
2264     close(fd);
2265     return true;
2266   }
2267 
2268   // Remove '\r' characters from text.
StripCR(const std::string & text)2269   std::string StripCR(const std::string& text) {
2270     std::string result;
2271 
2272     for (int i = 0; i < text.size(); i++) {
2273       if (text[i] != '\r') {
2274         result.push_back(text[i]);
2275       }
2276     }
2277 
2278     return result;
2279   }
2280 
2281   enum Type { TEXT, BINARY };
2282   enum ReturnCode { SUCCESS, ERROR };
2283 
Run(const std::string & command)2284   bool Run(const std::string& command) {
2285     std::vector<std::string> args;
2286     args.push_back("protoc");
2287     SplitStringUsing(command, " ", &args);
2288     switch (GetParam()) {
2289       case PROTO_PATH:
2290         args.push_back("--proto_path=" + TestUtil::TestSourceDir());
2291         break;
2292       case DESCRIPTOR_SET_IN:
2293         args.push_back(StrCat("--descriptor_set_in=",
2294                                     unittest_proto_descriptor_set_filename_));
2295         break;
2296       default:
2297         ADD_FAILURE() << "unexpected EncodeDecodeTestMode: " << GetParam();
2298     }
2299 
2300     std::unique_ptr<const char*[]> argv(new const char*[args.size()]);
2301     for (int i = 0; i < args.size(); i++) {
2302       argv[i] = args[i].c_str();
2303     }
2304 
2305     CommandLineInterface cli;
2306 
2307     CaptureTestStdout();
2308     CaptureTestStderr();
2309 
2310     int result = cli.Run(args.size(), argv.get());
2311 
2312     captured_stdout_ = GetCapturedTestStdout();
2313     captured_stderr_ = GetCapturedTestStderr();
2314 
2315     return result == 0;
2316   }
2317 
ExpectStdoutMatchesBinaryFile(const std::string & filename)2318   void ExpectStdoutMatchesBinaryFile(const std::string& filename) {
2319     std::string expected_output;
2320     GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
2321 
2322     // Don't use EXPECT_EQ because we don't want to print raw binary data to
2323     // stdout on failure.
2324     EXPECT_TRUE(captured_stdout_ == expected_output);
2325   }
2326 
ExpectStdoutMatchesTextFile(const std::string & filename)2327   void ExpectStdoutMatchesTextFile(const std::string& filename) {
2328     std::string expected_output;
2329     GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
2330 
2331     ExpectStdoutMatchesText(expected_output);
2332   }
2333 
ExpectStdoutMatchesText(const std::string & expected_text)2334   void ExpectStdoutMatchesText(const std::string& expected_text) {
2335     EXPECT_EQ(StripCR(expected_text), StripCR(captured_stdout_));
2336   }
2337 
ExpectStderrMatchesText(const std::string & expected_text)2338   void ExpectStderrMatchesText(const std::string& expected_text) {
2339     EXPECT_EQ(StripCR(expected_text), StripCR(captured_stderr_));
2340   }
2341 
2342  private:
WriteUnittestProtoDescriptorSet()2343   void WriteUnittestProtoDescriptorSet() {
2344     unittest_proto_descriptor_set_filename_ =
2345         TestTempDir() + "/unittest_proto_descriptor_set.bin";
2346     FileDescriptorSet file_descriptor_set;
2347     protobuf_unittest::TestAllTypes test_all_types;
2348     test_all_types.descriptor()->file()->CopyTo(file_descriptor_set.add_file());
2349 
2350     protobuf_unittest_import::ImportMessage import_message;
2351     import_message.descriptor()->file()->CopyTo(file_descriptor_set.add_file());
2352 
2353     protobuf_unittest_import::PublicImportMessage public_import_message;
2354     public_import_message.descriptor()->file()->CopyTo(
2355         file_descriptor_set.add_file());
2356     GOOGLE_DCHECK(file_descriptor_set.IsInitialized());
2357 
2358     std::string binary_proto;
2359     GOOGLE_CHECK(file_descriptor_set.SerializeToString(&binary_proto));
2360     GOOGLE_CHECK_OK(File::SetContents(unittest_proto_descriptor_set_filename_,
2361                                binary_proto, true));
2362   }
2363 
2364   int duped_stdin_;
2365   std::string captured_stdout_;
2366   std::string captured_stderr_;
2367   std::string unittest_proto_descriptor_set_filename_;
2368 };
2369 
TEST_P(EncodeDecodeTest,Encode)2370 TEST_P(EncodeDecodeTest, Encode) {
2371   RedirectStdinFromFile(TestUtil::GetTestDataPath(
2372       "net/proto2/internal/"
2373       "testdata/text_format_unittest_data_oneof_implemented.txt"));
2374   EXPECT_TRUE(
2375       Run(TestUtil::MaybeTranslatePath("net/proto2/internal/unittest.proto") +
2376           " --encode=protobuf_unittest.TestAllTypes"));
2377   ExpectStdoutMatchesBinaryFile(TestUtil::GetTestDataPath(
2378       "net/proto2/internal/testdata/golden_message_oneof_implemented"));
2379   ExpectStderrMatchesText("");
2380 }
2381 
TEST_P(EncodeDecodeTest,Decode)2382 TEST_P(EncodeDecodeTest, Decode) {
2383   RedirectStdinFromFile(TestUtil::GetTestDataPath(
2384       "net/proto2/internal/testdata/golden_message_oneof_implemented"));
2385   EXPECT_TRUE(
2386       Run(TestUtil::MaybeTranslatePath("net/proto2/internal/unittest.proto") +
2387           " --decode=protobuf_unittest.TestAllTypes"));
2388   ExpectStdoutMatchesTextFile(TestUtil::GetTestDataPath(
2389       "net/proto2/internal/"
2390       "testdata/text_format_unittest_data_oneof_implemented.txt"));
2391   ExpectStderrMatchesText("");
2392 }
2393 
TEST_P(EncodeDecodeTest,Partial)2394 TEST_P(EncodeDecodeTest, Partial) {
2395   RedirectStdinFromText("");
2396   EXPECT_TRUE(
2397       Run(TestUtil::MaybeTranslatePath("net/proto2/internal/unittest.proto") +
2398           " --encode=protobuf_unittest.TestRequired"));
2399   ExpectStdoutMatchesText("");
2400   ExpectStderrMatchesText(
2401       "warning:  Input message is missing required fields:  a, b, c\n");
2402 }
2403 
TEST_P(EncodeDecodeTest,DecodeRaw)2404 TEST_P(EncodeDecodeTest, DecodeRaw) {
2405   protobuf_unittest::TestAllTypes message;
2406   message.set_optional_int32(123);
2407   message.set_optional_string("foo");
2408   std::string data;
2409   message.SerializeToString(&data);
2410 
2411   RedirectStdinFromText(data);
2412   EXPECT_TRUE(Run("--decode_raw"));
2413   ExpectStdoutMatchesText(
2414       "1: 123\n"
2415       "14: \"foo\"\n");
2416   ExpectStderrMatchesText("");
2417 }
2418 
TEST_P(EncodeDecodeTest,UnknownType)2419 TEST_P(EncodeDecodeTest, UnknownType) {
2420   EXPECT_FALSE(
2421       Run(TestUtil::MaybeTranslatePath("net/proto2/internal/unittest.proto") +
2422           " --encode=NoSuchType"));
2423   ExpectStdoutMatchesText("");
2424   ExpectStderrMatchesText("Type not defined: NoSuchType\n");
2425 }
2426 
TEST_P(EncodeDecodeTest,ProtoParseError)2427 TEST_P(EncodeDecodeTest, ProtoParseError) {
2428   EXPECT_FALSE(
2429       Run("net/proto2/internal/no_such_file.proto "
2430           "--encode=NoSuchType"));
2431   ExpectStdoutMatchesText("");
2432   ExpectStderrMatchesText(
2433       "net/proto2/internal/no_such_file.proto: No such file or directory\n");
2434 }
2435 
2436 INSTANTIATE_TEST_SUITE_P(FileDescriptorSetSource, EncodeDecodeTest,
2437                          testing::Values(PROTO_PATH, DESCRIPTOR_SET_IN));
2438 }  // anonymous namespace
2439 
2440 #endif  // !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
2441 
2442 }  // namespace compiler
2443 }  // namespace protobuf
2444 }  // namespace google
2445