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