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