• 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 <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #ifdef _MSC_VER
39 #include <io.h>
40 #else
41 #include <unistd.h>
42 #endif
43 #include <memory>
44 #ifndef _SHARED_PTR_H
45 #include <google/protobuf/stubs/shared_ptr.h>
46 #endif
47 #include <vector>
48 
49 #include <google/protobuf/descriptor.pb.h>
50 #include <google/protobuf/descriptor.h>
51 #include <google/protobuf/io/zero_copy_stream.h>
52 #include <google/protobuf/compiler/command_line_interface.h>
53 #include <google/protobuf/compiler/code_generator.h>
54 #include <google/protobuf/testing/file.h>
55 #include <google/protobuf/compiler/mock_code_generator.h>
56 #include <google/protobuf/compiler/subprocess.h>
57 #include <google/protobuf/io/printer.h>
58 #include <google/protobuf/unittest.pb.h>
59 #include <google/protobuf/testing/file.h>
60 #include <google/protobuf/stubs/strutil.h>
61 #include <google/protobuf/stubs/substitute.h>
62 
63 #include <google/protobuf/testing/file.h>
64 #include <google/protobuf/testing/googletest.h>
65 #include <gtest/gtest.h>
66 
67 
68 namespace google {
69 namespace protobuf {
70 namespace compiler {
71 
72 // Disable the whole test when we use tcmalloc for "draconian" heap checks, in
73 // which case tcmalloc will print warnings that fail the plugin tests.
74 #if !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
75 
76 #if defined(_WIN32)
77 #ifndef STDIN_FILENO
78 #define STDIN_FILENO 0
79 #endif
80 #ifndef STDOUT_FILENO
81 #define STDOUT_FILENO 1
82 #endif
83 #ifndef F_OK
84 #define F_OK 00  // not defined by MSVC for whatever reason
85 #endif
86 #endif
87 
88 namespace {
89 
FileExists(const string & path)90 bool FileExists(const string& path) {
91   return File::Exists(path);
92 }
93 
94 class CommandLineInterfaceTest : public testing::Test {
95  protected:
96   virtual void SetUp();
97   virtual void TearDown();
98 
99   // Runs the CommandLineInterface with the given command line.  The
100   // command is automatically split on spaces, and the string "$tmpdir"
101   // is replaced with TestTempDir().
102   void Run(const string& command);
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 string& name, const string& contents);
116 
117   // Create a subdirectory within temp_directory_.
118   void CreateTempDir(const 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 
SetInputsAreProtoPathRelative(bool enable)130   void SetInputsAreProtoPathRelative(bool enable) {
131     cli_.SetInputsAreProtoPathRelative(enable);
132   }
133 
134   // -----------------------------------------------------------------
135   // Methods to check the test results (called after Run()).
136 
137   // Checks that no text was written to stderr during Run(), and Run()
138   // returned 0.
139   void ExpectNoErrors();
140 
141   // Checks that Run() returned non-zero and the stderr output is exactly
142   // the text given.  expected_test may contain references to "$tmpdir",
143   // which will be replaced by the temporary directory path.
144   void ExpectErrorText(const string& expected_text);
145 
146   // Checks that Run() returned non-zero and the stderr contains the given
147   // substring.
148   void ExpectErrorSubstring(const string& expected_substring);
149 
150   // Like ExpectErrorSubstring, but checks that Run() returned zero.
151   void ExpectErrorSubstringWithZeroReturnCode(
152       const string& expected_substring);
153 
154   // Checks that the captured stdout is the same as the expected_text.
155   void ExpectCapturedStdout(const string& expected_text);
156 
157   // Returns true if ExpectErrorSubstring(expected_substring) would pass, but
158   // does not fail otherwise.
159   bool HasAlternateErrorSubstring(const string& expected_substring);
160 
161   // Checks that MockCodeGenerator::Generate() was called in the given
162   // context (or the generator in test_plugin.cc, which produces the same
163   // output).  That is, this tests if the generator with the given name
164   // was called with the given parameter and proto file and produced the
165   // given output file.  This is checked by reading the output file and
166   // checking that it contains the content that MockCodeGenerator would
167   // generate given these inputs.  message_name is the name of the first
168   // message that appeared in the proto file; this is just to make extra
169   // sure that the correct file was parsed.
170   void ExpectGenerated(const string& generator_name,
171                        const string& parameter,
172                        const string& proto_name,
173                        const string& message_name);
174   void ExpectGenerated(const string& generator_name,
175                        const string& parameter,
176                        const string& proto_name,
177                        const string& message_name,
178                        const string& output_directory);
179   void ExpectGeneratedWithMultipleInputs(const string& generator_name,
180                                          const string& all_proto_names,
181                                          const string& proto_name,
182                                          const string& message_name);
183   void ExpectGeneratedWithInsertions(const string& generator_name,
184                                      const string& parameter,
185                                      const string& insertions,
186                                      const string& proto_name,
187                                      const string& message_name);
188 
189   void ExpectNullCodeGeneratorCalled(const string& parameter);
190 
191   void ReadDescriptorSet(const string& filename,
192                          FileDescriptorSet* descriptor_set);
193 
194   void ExpectFileContent(const string& filename,
195                          const string& content);
196 
197  private:
198   // The object we are testing.
199   CommandLineInterface cli_;
200 
201   // Was DisallowPlugins() called?
202   bool disallow_plugins_;
203 
204   // We create a directory within TestTempDir() in order to add extra
205   // protection against accidentally deleting user files (since we recursively
206   // delete this directory during the test).  This is the full path of that
207   // directory.
208   string temp_directory_;
209 
210   // The result of Run().
211   int return_code_;
212 
213   // The captured stderr output.
214   string error_text_;
215 
216   // The captured stdout.
217   string captured_stdout_;
218 
219   // Pointers which need to be deleted later.
220   vector<CodeGenerator*> mock_generators_to_delete_;
221 
222   NullCodeGenerator* null_generator_;
223 };
224 
225 class CommandLineInterfaceTest::NullCodeGenerator : public CodeGenerator {
226  public:
NullCodeGenerator()227   NullCodeGenerator() : called_(false) {}
~NullCodeGenerator()228   ~NullCodeGenerator() {}
229 
230   mutable bool called_;
231   mutable string parameter_;
232 
233   // implements CodeGenerator ----------------------------------------
Generate(const FileDescriptor * file,const string & parameter,GeneratorContext * context,string * error) const234   bool Generate(const FileDescriptor* file,
235                 const string& parameter,
236                 GeneratorContext* context,
237                 string* error) const {
238     called_ = true;
239     parameter_ = parameter;
240     return true;
241   }
242 };
243 
244 // ===================================================================
245 
SetUp()246 void CommandLineInterfaceTest::SetUp() {
247   // Most of these tests were written before this option was added, so we
248   // run with the option on (which used to be the only way) except in certain
249   // tests where we turn it off.
250   cli_.SetInputsAreProtoPathRelative(true);
251 
252   temp_directory_ = TestTempDir() + "/proto2_cli_test_temp";
253 
254   // If the temp directory already exists, it must be left over from a
255   // previous run.  Delete it.
256   if (FileExists(temp_directory_)) {
257     File::DeleteRecursively(temp_directory_, NULL, NULL);
258   }
259 
260   // Create the temp directory.
261   GOOGLE_CHECK_OK(File::CreateDir(temp_directory_, 0777));
262 
263   // Register generators.
264   CodeGenerator* generator = new MockCodeGenerator("test_generator");
265   mock_generators_to_delete_.push_back(generator);
266   cli_.RegisterGenerator("--test_out", "--test_opt", generator, "Test output.");
267   cli_.RegisterGenerator("-t", generator, "Test output.");
268 
269   generator = new MockCodeGenerator("alt_generator");
270   mock_generators_to_delete_.push_back(generator);
271   cli_.RegisterGenerator("--alt_out", generator, "Alt output.");
272 
273   generator = null_generator_ = new NullCodeGenerator();
274   mock_generators_to_delete_.push_back(generator);
275   cli_.RegisterGenerator("--null_out", generator, "Null output.");
276 
277   disallow_plugins_ = false;
278 }
279 
TearDown()280 void CommandLineInterfaceTest::TearDown() {
281   // Delete the temp directory.
282   if (FileExists(temp_directory_)) {
283     File::DeleteRecursively(temp_directory_, NULL, NULL);
284   }
285 
286   // Delete all the MockCodeGenerators.
287   for (int i = 0; i < mock_generators_to_delete_.size(); i++) {
288     delete mock_generators_to_delete_[i];
289   }
290   mock_generators_to_delete_.clear();
291 }
292 
Run(const string & command)293 void CommandLineInterfaceTest::Run(const string& command) {
294   vector<string> args = Split(command, " ", true);
295 
296   if (!disallow_plugins_) {
297     cli_.AllowPlugins("prefix-");
298 #ifndef GOOGLE_THIRD_PARTY_PROTOBUF
299     string plugin_path;
300 #ifdef GOOGLE_PROTOBUF_TEST_PLUGIN_PATH
301     plugin_path = GOOGLE_PROTOBUF_TEST_PLUGIN_PATH;
302 #else
303     const char* possible_paths[] = {
304       // When building with shared libraries, libtool hides the real executable
305       // in .libs and puts a fake wrapper in the current directory.
306       // Unfortunately, due to an apparent bug on Cygwin/MinGW, if one program
307       // wrapped in this way (e.g. protobuf-tests.exe) tries to execute another
308       // program wrapped in this way (e.g. test_plugin.exe), the latter fails
309       // with error code 127 and no explanation message.  Presumably the problem
310       // is that the wrapper for protobuf-tests.exe set some environment
311       // variables that confuse the wrapper for test_plugin.exe.  Luckily, it
312       // turns out that if we simply invoke the wrapped test_plugin.exe
313       // directly, it works -- I guess the environment variables set by the
314       // protobuf-tests.exe wrapper happen to be correct for it too.  So we do
315       // that.
316       ".libs/test_plugin.exe",  // Win32 w/autotool (Cygwin / MinGW)
317       "test_plugin.exe",        // Other Win32 (MSVC)
318       "test_plugin",            // Unix
319     };
320     for (int i = 0; i < GOOGLE_ARRAYSIZE(possible_paths); i++) {
321       if (access(possible_paths[i], F_OK) == 0) {
322         plugin_path = possible_paths[i];
323         break;
324       }
325     }
326 #endif
327 
328     if (plugin_path.empty()) {
329 #else
330     string plugin_path = "third_party/protobuf/test_plugin";
331 
332     if (access(plugin_path.c_str(), F_OK) != 0) {
333 #endif  // GOOGLE_THIRD_PARTY_PROTOBUF
334       GOOGLE_LOG(ERROR)
335           << "Plugin executable not found.  Plugin tests are likely to fail.";
336     } else {
337       args.push_back("--plugin=prefix-gen-plug=" + plugin_path);
338     }
339   }
340 
341   google::protobuf::scoped_array<const char * > argv(new const char* [args.size()]);
342 
343   for (int i = 0; i < args.size(); i++) {
344     args[i] = StringReplace(args[i], "$tmpdir", temp_directory_, true);
345     argv[i] = args[i].c_str();
346   }
347 
348   // TODO(jieluo): Cygwin doesn't work well if we try to capture stderr and
349   // stdout at the same time. Need to figure out why and add this capture back
350   // for Cygwin.
351 #if !defined(__CYGWIN__)
352   CaptureTestStdout();
353 #endif
354   CaptureTestStderr();
355 
356   return_code_ = cli_.Run(args.size(), argv.get());
357 
358   error_text_ = GetCapturedTestStderr();
359 #if !defined(__CYGWIN__)
360   captured_stdout_ = GetCapturedTestStdout();
361 #endif
362 }
363 
364 // -------------------------------------------------------------------
365 
366 void CommandLineInterfaceTest::CreateTempFile(
367     const string& name,
368     const string& contents) {
369   // Create parent directory, if necessary.
370   string::size_type slash_pos = name.find_last_of('/');
371   if (slash_pos != string::npos) {
372     string dir = name.substr(0, slash_pos);
373     if (!FileExists(temp_directory_ + "/" + dir)) {
374       GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + dir,
375                                           0777));
376     }
377   }
378 
379   // Write file.
380   string full_name = temp_directory_ + "/" + name;
381   GOOGLE_CHECK_OK(File::SetContents(
382       full_name, StringReplace(contents, "$tmpdir", temp_directory_, true),
383       true));
384 }
385 
386 void CommandLineInterfaceTest::CreateTempDir(const string& name) {
387   GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + name,
388                                       0777));
389 }
390 
391 // -------------------------------------------------------------------
392 
393 void CommandLineInterfaceTest::ExpectNoErrors() {
394   EXPECT_EQ(0, return_code_);
395   EXPECT_EQ("", error_text_);
396 }
397 
398 void CommandLineInterfaceTest::ExpectErrorText(const string& expected_text) {
399   EXPECT_NE(0, return_code_);
400   EXPECT_EQ(StringReplace(expected_text, "$tmpdir", temp_directory_, true),
401             error_text_);
402 }
403 
404 void CommandLineInterfaceTest::ExpectErrorSubstring(
405     const string& expected_substring) {
406   EXPECT_NE(0, return_code_);
407   EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_);
408 }
409 
410 void CommandLineInterfaceTest::ExpectErrorSubstringWithZeroReturnCode(
411     const string& expected_substring) {
412   EXPECT_EQ(0, return_code_);
413   EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_);
414 }
415 
416 bool CommandLineInterfaceTest::HasAlternateErrorSubstring(
417     const string& expected_substring) {
418   EXPECT_NE(0, return_code_);
419   return error_text_.find(expected_substring) != string::npos;
420 }
421 
422 void CommandLineInterfaceTest::ExpectGenerated(
423     const string& generator_name,
424     const string& parameter,
425     const string& proto_name,
426     const string& message_name) {
427   MockCodeGenerator::ExpectGenerated(
428       generator_name, parameter, "", proto_name, message_name, proto_name,
429       temp_directory_);
430 }
431 
432 void CommandLineInterfaceTest::ExpectGenerated(
433     const string& generator_name,
434     const string& parameter,
435     const string& proto_name,
436     const string& message_name,
437     const string& output_directory) {
438   MockCodeGenerator::ExpectGenerated(
439       generator_name, parameter, "", proto_name, message_name, proto_name,
440       temp_directory_ + "/" + output_directory);
441 }
442 
443 void CommandLineInterfaceTest::ExpectGeneratedWithMultipleInputs(
444     const string& generator_name,
445     const string& all_proto_names,
446     const string& proto_name,
447     const string& message_name) {
448   MockCodeGenerator::ExpectGenerated(
449       generator_name, "", "", proto_name, message_name,
450       all_proto_names,
451       temp_directory_);
452 }
453 
454 void CommandLineInterfaceTest::ExpectGeneratedWithInsertions(
455     const string& generator_name,
456     const string& parameter,
457     const string& insertions,
458     const string& proto_name,
459     const string& message_name) {
460   MockCodeGenerator::ExpectGenerated(
461       generator_name, parameter, insertions, proto_name, message_name,
462       proto_name, temp_directory_);
463 }
464 
465 void CommandLineInterfaceTest::ExpectNullCodeGeneratorCalled(
466     const string& parameter) {
467   EXPECT_TRUE(null_generator_->called_);
468   EXPECT_EQ(parameter, null_generator_->parameter_);
469 }
470 
471 void CommandLineInterfaceTest::ReadDescriptorSet(
472     const string& filename, FileDescriptorSet* descriptor_set) {
473   string path = temp_directory_ + "/" + filename;
474   string file_contents;
475   GOOGLE_CHECK_OK(File::GetContents(path, &file_contents, true));
476 
477   if (!descriptor_set->ParseFromString(file_contents)) {
478     FAIL() << "Could not parse file contents: " << path;
479   }
480 }
481 
482 void CommandLineInterfaceTest::ExpectCapturedStdout(
483     const string& expected_text) {
484   EXPECT_EQ(expected_text, captured_stdout_);
485 }
486 
487 
488 void CommandLineInterfaceTest::ExpectFileContent(
489     const string& filename, const string& content) {
490   string path = temp_directory_ + "/" + filename;
491   string file_contents;
492   GOOGLE_CHECK_OK(File::GetContents(path, &file_contents, true));
493 
494   EXPECT_EQ(StringReplace(content, "$tmpdir", temp_directory_, true),
495             file_contents);
496 }
497 
498 // ===================================================================
499 
500 TEST_F(CommandLineInterfaceTest, BasicOutput) {
501   // Test that the common case works.
502 
503   CreateTempFile("foo.proto",
504     "syntax = \"proto2\";\n"
505     "message Foo {}\n");
506 
507   Run("protocol_compiler --test_out=$tmpdir "
508       "--proto_path=$tmpdir foo.proto");
509 
510   ExpectNoErrors();
511   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
512 }
513 
514 TEST_F(CommandLineInterfaceTest, BasicPlugin) {
515   // Test that basic plugins work.
516 
517   CreateTempFile("foo.proto",
518     "syntax = \"proto2\";\n"
519     "message Foo {}\n");
520 
521   Run("protocol_compiler --plug_out=$tmpdir "
522       "--proto_path=$tmpdir foo.proto");
523 
524   ExpectNoErrors();
525   ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
526 }
527 
528 TEST_F(CommandLineInterfaceTest, GeneratorAndPlugin) {
529   // Invoke a generator and a plugin at the same time.
530 
531   CreateTempFile("foo.proto",
532     "syntax = \"proto2\";\n"
533     "message Foo {}\n");
534 
535   Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
536       "--proto_path=$tmpdir foo.proto");
537 
538   ExpectNoErrors();
539   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
540   ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
541 }
542 
543 TEST_F(CommandLineInterfaceTest, MultipleInputs) {
544   // Test parsing multiple input files.
545 
546   CreateTempFile("foo.proto",
547     "syntax = \"proto2\";\n"
548     "message Foo {}\n");
549   CreateTempFile("bar.proto",
550     "syntax = \"proto2\";\n"
551     "message Bar {}\n");
552 
553   Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
554       "--proto_path=$tmpdir foo.proto bar.proto");
555 
556   ExpectNoErrors();
557   ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
558                                     "foo.proto", "Foo");
559   ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
560                                     "bar.proto", "Bar");
561   ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
562                                     "foo.proto", "Foo");
563   ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
564                                     "bar.proto", "Bar");
565 }
566 
567 TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport) {
568   // Test parsing multiple input files with an import of a separate file.
569 
570   CreateTempFile("foo.proto",
571     "syntax = \"proto2\";\n"
572     "message Foo {}\n");
573   CreateTempFile("bar.proto",
574     "syntax = \"proto2\";\n"
575     "import \"baz.proto\";\n"
576     "message Bar {\n"
577     "  optional Baz a = 1;\n"
578     "}\n");
579   CreateTempFile("baz.proto",
580     "syntax = \"proto2\";\n"
581     "message Baz {}\n");
582 
583   Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
584       "--proto_path=$tmpdir foo.proto bar.proto");
585 
586   ExpectNoErrors();
587   ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
588                                     "foo.proto", "Foo");
589   ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
590                                     "bar.proto", "Bar");
591   ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
592                                     "foo.proto", "Foo");
593   ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
594                                     "bar.proto", "Bar");
595 }
596 
597 TEST_F(CommandLineInterfaceTest, CreateDirectory) {
598   // Test that when we output to a sub-directory, it is created.
599 
600   CreateTempFile("bar/baz/foo.proto",
601     "syntax = \"proto2\";\n"
602     "message Foo {}\n");
603   CreateTempDir("out");
604   CreateTempDir("plugout");
605 
606   Run("protocol_compiler --test_out=$tmpdir/out --plug_out=$tmpdir/plugout "
607       "--proto_path=$tmpdir bar/baz/foo.proto");
608 
609   ExpectNoErrors();
610   ExpectGenerated("test_generator", "", "bar/baz/foo.proto", "Foo", "out");
611   ExpectGenerated("test_plugin", "", "bar/baz/foo.proto", "Foo", "plugout");
612 }
613 
614 TEST_F(CommandLineInterfaceTest, GeneratorParameters) {
615   // Test that generator parameters are correctly parsed from the command line.
616 
617   CreateTempFile("foo.proto",
618     "syntax = \"proto2\";\n"
619     "message Foo {}\n");
620 
621   Run("protocol_compiler --test_out=TestParameter:$tmpdir "
622       "--plug_out=TestPluginParameter:$tmpdir "
623       "--proto_path=$tmpdir foo.proto");
624 
625   ExpectNoErrors();
626   ExpectGenerated("test_generator", "TestParameter", "foo.proto", "Foo");
627   ExpectGenerated("test_plugin", "TestPluginParameter", "foo.proto", "Foo");
628 }
629 
630 TEST_F(CommandLineInterfaceTest, ExtraGeneratorParameters) {
631   // Test that generator parameters specified with the option flag are
632   // correctly passed to the code generator.
633 
634   CreateTempFile("foo.proto",
635     "syntax = \"proto2\";\n"
636     "message Foo {}\n");
637   // Create the "a" and "b" sub-directories.
638   CreateTempDir("a");
639   CreateTempDir("b");
640 
641   Run("protocol_compiler "
642       "--test_opt=foo1 "
643       "--test_out=bar:$tmpdir/a "
644       "--test_opt=foo2 "
645       "--test_out=baz:$tmpdir/b "
646       "--test_opt=foo3 "
647       "--proto_path=$tmpdir foo.proto");
648 
649   ExpectNoErrors();
650   ExpectGenerated(
651       "test_generator", "bar,foo1,foo2,foo3", "foo.proto", "Foo", "a");
652   ExpectGenerated(
653       "test_generator", "baz,foo1,foo2,foo3", "foo.proto", "Foo", "b");
654 }
655 
656 TEST_F(CommandLineInterfaceTest, Insert) {
657   // Test running a generator that inserts code into another's output.
658 
659   CreateTempFile("foo.proto",
660     "syntax = \"proto2\";\n"
661     "message Foo {}\n");
662 
663   Run("protocol_compiler "
664       "--test_out=TestParameter:$tmpdir "
665       "--plug_out=TestPluginParameter:$tmpdir "
666       "--test_out=insert=test_generator,test_plugin:$tmpdir "
667       "--plug_out=insert=test_generator,test_plugin:$tmpdir "
668       "--proto_path=$tmpdir foo.proto");
669 
670   ExpectNoErrors();
671   ExpectGeneratedWithInsertions(
672       "test_generator", "TestParameter", "test_generator,test_plugin",
673       "foo.proto", "Foo");
674   ExpectGeneratedWithInsertions(
675       "test_plugin", "TestPluginParameter", "test_generator,test_plugin",
676       "foo.proto", "Foo");
677 }
678 
679 #if defined(_WIN32)
680 
681 TEST_F(CommandLineInterfaceTest, WindowsOutputPath) {
682   // Test that the output path can be a Windows-style path.
683 
684   CreateTempFile("foo.proto",
685     "syntax = \"proto2\";\n");
686 
687   Run("protocol_compiler --null_out=C:\\ "
688       "--proto_path=$tmpdir foo.proto");
689 
690   ExpectNoErrors();
691   ExpectNullCodeGeneratorCalled("");
692 }
693 
694 TEST_F(CommandLineInterfaceTest, WindowsOutputPathAndParameter) {
695   // Test that we can have a windows-style output path and a parameter.
696 
697   CreateTempFile("foo.proto",
698     "syntax = \"proto2\";\n");
699 
700   Run("protocol_compiler --null_out=bar:C:\\ "
701       "--proto_path=$tmpdir foo.proto");
702 
703   ExpectNoErrors();
704   ExpectNullCodeGeneratorCalled("bar");
705 }
706 
707 TEST_F(CommandLineInterfaceTest, TrailingBackslash) {
708   // Test that the directories can end in backslashes.  Some users claim this
709   // doesn't work on their system.
710 
711   CreateTempFile("foo.proto",
712     "syntax = \"proto2\";\n"
713     "message Foo {}\n");
714 
715   Run("protocol_compiler --test_out=$tmpdir\\ "
716       "--proto_path=$tmpdir\\ foo.proto");
717 
718   ExpectNoErrors();
719   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
720 }
721 
722 #endif  // defined(_WIN32) || defined(__CYGWIN__)
723 
724 TEST_F(CommandLineInterfaceTest, PathLookup) {
725   // Test that specifying multiple directories in the proto search path works.
726 
727   CreateTempFile("b/bar.proto",
728     "syntax = \"proto2\";\n"
729     "message Bar {}\n");
730   CreateTempFile("a/foo.proto",
731     "syntax = \"proto2\";\n"
732     "import \"bar.proto\";\n"
733     "message Foo {\n"
734     "  optional Bar a = 1;\n"
735     "}\n");
736   CreateTempFile("b/foo.proto", "this should not be parsed\n");
737 
738   Run("protocol_compiler --test_out=$tmpdir "
739       "--proto_path=$tmpdir/a --proto_path=$tmpdir/b foo.proto");
740 
741   ExpectNoErrors();
742   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
743 }
744 
745 TEST_F(CommandLineInterfaceTest, ColonDelimitedPath) {
746   // Same as PathLookup, but we provide the proto_path in a single flag.
747 
748   CreateTempFile("b/bar.proto",
749     "syntax = \"proto2\";\n"
750     "message Bar {}\n");
751   CreateTempFile("a/foo.proto",
752     "syntax = \"proto2\";\n"
753     "import \"bar.proto\";\n"
754     "message Foo {\n"
755     "  optional Bar a = 1;\n"
756     "}\n");
757   CreateTempFile("b/foo.proto", "this should not be parsed\n");
758 
759 #undef PATH_SEPARATOR
760 #if defined(_WIN32)
761 #define PATH_SEPARATOR ";"
762 #else
763 #define PATH_SEPARATOR ":"
764 #endif
765 
766   Run("protocol_compiler --test_out=$tmpdir "
767       "--proto_path=$tmpdir/a" PATH_SEPARATOR "$tmpdir/b foo.proto");
768 
769 #undef PATH_SEPARATOR
770 
771   ExpectNoErrors();
772   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
773 }
774 
775 TEST_F(CommandLineInterfaceTest, NonRootMapping) {
776   // Test setting up a search path mapping a directory to a non-root location.
777 
778   CreateTempFile("foo.proto",
779     "syntax = \"proto2\";\n"
780     "message Foo {}\n");
781 
782   Run("protocol_compiler --test_out=$tmpdir "
783       "--proto_path=bar=$tmpdir bar/foo.proto");
784 
785   ExpectNoErrors();
786   ExpectGenerated("test_generator", "", "bar/foo.proto", "Foo");
787 }
788 
789 TEST_F(CommandLineInterfaceTest, MultipleGenerators) {
790   // Test that we can have multiple generators and use both in one invocation,
791   // each with a different output directory.
792 
793   CreateTempFile("foo.proto",
794     "syntax = \"proto2\";\n"
795     "message Foo {}\n");
796   // Create the "a" and "b" sub-directories.
797   CreateTempDir("a");
798   CreateTempDir("b");
799 
800   Run("protocol_compiler "
801       "--test_out=$tmpdir/a "
802       "--alt_out=$tmpdir/b "
803       "--proto_path=$tmpdir foo.proto");
804 
805   ExpectNoErrors();
806   ExpectGenerated("test_generator", "", "foo.proto", "Foo", "a");
807   ExpectGenerated("alt_generator", "", "foo.proto", "Foo", "b");
808 }
809 
810 TEST_F(CommandLineInterfaceTest, DisallowServicesNoServices) {
811   // Test that --disallow_services doesn't cause a problem when there are no
812   // services.
813 
814   CreateTempFile("foo.proto",
815     "syntax = \"proto2\";\n"
816     "message Foo {}\n");
817 
818   Run("protocol_compiler --disallow_services --test_out=$tmpdir "
819       "--proto_path=$tmpdir foo.proto");
820 
821   ExpectNoErrors();
822   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
823 }
824 
825 TEST_F(CommandLineInterfaceTest, DisallowServicesHasService) {
826   // Test that --disallow_services produces an error when there are services.
827 
828   CreateTempFile("foo.proto",
829     "syntax = \"proto2\";\n"
830     "message Foo {}\n"
831     "service Bar {}\n");
832 
833   Run("protocol_compiler --disallow_services --test_out=$tmpdir "
834       "--proto_path=$tmpdir foo.proto");
835 
836   ExpectErrorSubstring("foo.proto: This file contains services");
837 }
838 
839 TEST_F(CommandLineInterfaceTest, AllowServicesHasService) {
840   // Test that services work fine as long as --disallow_services is not used.
841 
842   CreateTempFile("foo.proto",
843     "syntax = \"proto2\";\n"
844     "message Foo {}\n"
845     "service Bar {}\n");
846 
847   Run("protocol_compiler --test_out=$tmpdir "
848       "--proto_path=$tmpdir foo.proto");
849 
850   ExpectNoErrors();
851   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
852 }
853 
854 TEST_F(CommandLineInterfaceTest, CwdRelativeInputs) {
855   // Test that we can accept working-directory-relative input files.
856 
857   SetInputsAreProtoPathRelative(false);
858 
859   CreateTempFile("foo.proto",
860     "syntax = \"proto2\";\n"
861     "message Foo {}\n");
862 
863   Run("protocol_compiler --test_out=$tmpdir "
864       "--proto_path=$tmpdir $tmpdir/foo.proto");
865 
866   ExpectNoErrors();
867   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
868 }
869 
870 TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) {
871   CreateTempFile("foo.proto",
872     "syntax = \"proto2\";\n"
873     "message Foo {}\n");
874   CreateTempFile("bar.proto",
875     "syntax = \"proto2\";\n"
876     "import \"foo.proto\";\n"
877     "message Bar {\n"
878     "  optional Foo foo = 1;\n"
879     "}\n");
880 
881   Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
882       "--proto_path=$tmpdir bar.proto");
883 
884   ExpectNoErrors();
885 
886   FileDescriptorSet descriptor_set;
887   ReadDescriptorSet("descriptor_set", &descriptor_set);
888   if (HasFatalFailure()) return;
889   EXPECT_EQ(1, descriptor_set.file_size());
890   EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
891   // Descriptor set should not have source code info.
892   EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
893   // Descriptor set should have json_name.
894   EXPECT_EQ("Bar", descriptor_set.file(0).message_type(0).name());
895   EXPECT_EQ("foo", descriptor_set.file(0).message_type(0).field(0).name());
896   EXPECT_TRUE(descriptor_set.file(0).message_type(0).field(0).has_json_name());
897 }
898 
899 TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithDuplicates) {
900   CreateTempFile("foo.proto",
901     "syntax = \"proto2\";\n"
902     "message Foo {}\n");
903   CreateTempFile("bar.proto",
904     "syntax = \"proto2\";\n"
905     "import \"foo.proto\";\n"
906     "message Bar {\n"
907     "  optional Foo foo = 1;\n"
908     "}\n");
909   CreateTempFile("baz.proto",
910     "syntax = \"proto2\";\n"
911     "import \"foo.proto\";\n"
912     "message Baz {\n"
913     "  optional Foo foo = 1;\n"
914     "}\n");
915 
916   Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
917       "--proto_path=$tmpdir bar.proto foo.proto bar.proto baz.proto");
918 
919   ExpectNoErrors();
920 
921   FileDescriptorSet descriptor_set;
922   ReadDescriptorSet("descriptor_set", &descriptor_set);
923   if (HasFatalFailure()) return;
924   EXPECT_EQ(3, descriptor_set.file_size());
925   EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
926   EXPECT_EQ("foo.proto", descriptor_set.file(1).name());
927   EXPECT_EQ("baz.proto", descriptor_set.file(2).name());
928   // Descriptor set should not have source code info.
929   EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
930   // Descriptor set should have json_name.
931   EXPECT_EQ("Bar", descriptor_set.file(0).message_type(0).name());
932   EXPECT_EQ("foo", descriptor_set.file(0).message_type(0).field(0).name());
933   EXPECT_TRUE(descriptor_set.file(0).message_type(0).field(0).has_json_name());
934 }
935 
936 TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) {
937   CreateTempFile("foo.proto",
938     "syntax = \"proto2\";\n"
939     "message Foo {}\n");
940   CreateTempFile("bar.proto",
941     "syntax = \"proto2\";\n"
942     "import \"foo.proto\";\n"
943     "message Bar {\n"
944     "  optional Foo foo = 1;\n"
945     "}\n");
946 
947   Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
948       "--include_source_info --proto_path=$tmpdir bar.proto");
949 
950   ExpectNoErrors();
951 
952   FileDescriptorSet descriptor_set;
953   ReadDescriptorSet("descriptor_set", &descriptor_set);
954   if (HasFatalFailure()) return;
955   EXPECT_EQ(1, descriptor_set.file_size());
956   EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
957   // Source code info included.
958   EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
959 }
960 
961 TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) {
962   CreateTempFile("foo.proto",
963     "syntax = \"proto2\";\n"
964     "message Foo {}\n");
965   CreateTempFile("bar.proto",
966     "syntax = \"proto2\";\n"
967     "import \"foo.proto\";\n"
968     "message Bar {\n"
969     "  optional Foo foo = 1;\n"
970     "}\n");
971 
972   Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
973       "--include_imports --proto_path=$tmpdir bar.proto");
974 
975   ExpectNoErrors();
976 
977   FileDescriptorSet descriptor_set;
978   ReadDescriptorSet("descriptor_set", &descriptor_set);
979   if (HasFatalFailure()) return;
980   EXPECT_EQ(2, descriptor_set.file_size());
981   if (descriptor_set.file(0).name() == "bar.proto") {
982     std::swap(descriptor_set.mutable_file()->mutable_data()[0],
983               descriptor_set.mutable_file()->mutable_data()[1]);
984   }
985   EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
986   EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
987   // Descriptor set should not have source code info.
988   EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
989   EXPECT_FALSE(descriptor_set.file(1).has_source_code_info());
990 }
991 
992 TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSetWithSourceInfo) {
993   CreateTempFile("foo.proto",
994     "syntax = \"proto2\";\n"
995     "message Foo {}\n");
996   CreateTempFile("bar.proto",
997     "syntax = \"proto2\";\n"
998     "import \"foo.proto\";\n"
999     "message Bar {\n"
1000     "  optional Foo foo = 1;\n"
1001     "}\n");
1002 
1003   Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
1004       "--include_imports --include_source_info --proto_path=$tmpdir bar.proto");
1005 
1006   ExpectNoErrors();
1007 
1008   FileDescriptorSet descriptor_set;
1009   ReadDescriptorSet("descriptor_set", &descriptor_set);
1010   if (HasFatalFailure()) return;
1011   EXPECT_EQ(2, descriptor_set.file_size());
1012   if (descriptor_set.file(0).name() == "bar.proto") {
1013     std::swap(descriptor_set.mutable_file()->mutable_data()[0],
1014               descriptor_set.mutable_file()->mutable_data()[1]);
1015   }
1016   EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
1017   EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
1018   // Source code info included.
1019   EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
1020   EXPECT_TRUE(descriptor_set.file(1).has_source_code_info());
1021 }
1022 
1023 #ifdef _WIN32
1024 // TODO(teboring): Figure out how to write test on windows.
1025 #else
1026 TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileGivenTwoInputs) {
1027   CreateTempFile("foo.proto",
1028     "syntax = \"proto2\";\n"
1029     "message Foo {}\n");
1030   CreateTempFile("bar.proto",
1031     "syntax = \"proto2\";\n"
1032     "import \"foo.proto\";\n"
1033     "message Bar {\n"
1034     "  optional Foo foo = 1;\n"
1035     "}\n");
1036 
1037   Run("protocol_compiler --dependency_out=$tmpdir/manifest "
1038       "--test_out=$tmpdir --proto_path=$tmpdir bar.proto foo.proto");
1039 
1040   ExpectErrorText(
1041       "Can only process one input file when using --dependency_out=FILE.\n");
1042 }
1043 
1044 #ifdef PROTOBUF_OPENSOURCE
1045 TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFile) {
1046   CreateTempFile("foo.proto",
1047     "syntax = \"proto2\";\n"
1048     "message Foo {}\n");
1049   CreateTempFile("bar.proto",
1050     "syntax = \"proto2\";\n"
1051     "import \"foo.proto\";\n"
1052     "message Bar {\n"
1053     "  optional Foo foo = 1;\n"
1054     "}\n");
1055 
1056   string current_working_directory = getcwd(NULL, 0);
1057   SwitchToTempDirectory();
1058 
1059   Run("protocol_compiler --dependency_out=manifest --test_out=. "
1060       "bar.proto");
1061 
1062   ExpectNoErrors();
1063 
1064   ExpectFileContent("manifest",
1065                     "bar.proto.MockCodeGenerator.test_generator: "
1066                     "foo.proto\\\n bar.proto");
1067 
1068   File::ChangeWorkingDirectory(current_working_directory);
1069 }
1070 #else  // !PROTOBUF_OPENSOURCE
1071 // TODO(teboring): Figure out how to change and get working directory in
1072 // google3.
1073 #endif  // !PROTOBUF_OPENSOURCE
1074 
1075 TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileForAbsolutePath) {
1076   CreateTempFile("foo.proto",
1077     "syntax = \"proto2\";\n"
1078     "message Foo {}\n");
1079   CreateTempFile("bar.proto",
1080     "syntax = \"proto2\";\n"
1081     "import \"foo.proto\";\n"
1082     "message Bar {\n"
1083     "  optional Foo foo = 1;\n"
1084     "}\n");
1085 
1086   Run("protocol_compiler --dependency_out=$tmpdir/manifest "
1087       "--test_out=$tmpdir --proto_path=$tmpdir bar.proto");
1088 
1089   ExpectNoErrors();
1090 
1091   ExpectFileContent("manifest",
1092                     "$tmpdir/bar.proto.MockCodeGenerator.test_generator: "
1093                     "$tmpdir/foo.proto\\\n $tmpdir/bar.proto");
1094 }
1095 #endif  // !_WIN32
1096 
1097 
1098 // -------------------------------------------------------------------
1099 
1100 TEST_F(CommandLineInterfaceTest, ParseErrors) {
1101   // Test that parse errors are reported.
1102 
1103   CreateTempFile("foo.proto",
1104     "syntax = \"proto2\";\n"
1105     "badsyntax\n");
1106 
1107   Run("protocol_compiler --test_out=$tmpdir "
1108       "--proto_path=$tmpdir foo.proto");
1109 
1110   ExpectErrorText(
1111     "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
1112 }
1113 
1114 TEST_F(CommandLineInterfaceTest, ParseErrorsMultipleFiles) {
1115   // Test that parse errors are reported from multiple files.
1116 
1117   // We set up files such that foo.proto actually depends on bar.proto in
1118   // two ways:  Directly and through baz.proto.  bar.proto's errors should
1119   // only be reported once.
1120   CreateTempFile("bar.proto",
1121     "syntax = \"proto2\";\n"
1122     "badsyntax\n");
1123   CreateTempFile("baz.proto",
1124     "syntax = \"proto2\";\n"
1125     "import \"bar.proto\";\n");
1126   CreateTempFile("foo.proto",
1127     "syntax = \"proto2\";\n"
1128     "import \"bar.proto\";\n"
1129     "import \"baz.proto\";\n");
1130 
1131   Run("protocol_compiler --test_out=$tmpdir "
1132       "--proto_path=$tmpdir foo.proto");
1133 
1134   ExpectErrorText(
1135     "bar.proto:2:1: Expected top-level statement (e.g. \"message\").\n"
1136     "baz.proto: Import \"bar.proto\" was not found or had errors.\n"
1137     "foo.proto: Import \"bar.proto\" was not found or had errors.\n"
1138     "foo.proto: Import \"baz.proto\" was not found or had errors.\n");
1139 }
1140 
1141 TEST_F(CommandLineInterfaceTest, InputNotFoundError) {
1142   // Test what happens if the input file is not found.
1143 
1144   Run("protocol_compiler --test_out=$tmpdir "
1145       "--proto_path=$tmpdir foo.proto");
1146 
1147   ExpectErrorText(
1148     "foo.proto: File not found.\n");
1149 }
1150 
1151 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundError) {
1152   // Test what happens when a working-directory-relative input file is not
1153   // found.
1154 
1155   SetInputsAreProtoPathRelative(false);
1156 
1157   Run("protocol_compiler --test_out=$tmpdir "
1158       "--proto_path=$tmpdir $tmpdir/foo.proto");
1159 
1160   ExpectErrorText(
1161     "$tmpdir/foo.proto: No such file or directory\n");
1162 }
1163 
1164 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotMappedError) {
1165   // Test what happens when a working-directory-relative input file is not
1166   // mapped to a virtual path.
1167 
1168   SetInputsAreProtoPathRelative(false);
1169 
1170   CreateTempFile("foo.proto",
1171     "syntax = \"proto2\";\n"
1172     "message Foo {}\n");
1173 
1174   // Create a directory called "bar" so that we can point --proto_path at it.
1175   CreateTempFile("bar/dummy", "");
1176 
1177   Run("protocol_compiler --test_out=$tmpdir "
1178       "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
1179 
1180   ExpectErrorText(
1181     "$tmpdir/foo.proto: File does not reside within any path "
1182       "specified using --proto_path (or -I).  You must specify a "
1183       "--proto_path which encompasses this file.  Note that the "
1184       "proto_path must be an exact prefix of the .proto file "
1185       "names -- protoc is too dumb to figure out when two paths "
1186       "(e.g. absolute and relative) are equivalent (it's harder "
1187       "than you think).\n");
1188 }
1189 
1190 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundAndNotMappedError) {
1191   // Check what happens if the input file is not found *and* is not mapped
1192   // in the proto_path.
1193 
1194   SetInputsAreProtoPathRelative(false);
1195 
1196   // Create a directory called "bar" so that we can point --proto_path at it.
1197   CreateTempFile("bar/dummy", "");
1198 
1199   Run("protocol_compiler --test_out=$tmpdir "
1200       "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
1201 
1202   ExpectErrorText(
1203     "$tmpdir/foo.proto: No such file or directory\n");
1204 }
1205 
1206 TEST_F(CommandLineInterfaceTest, CwdRelativeInputShadowedError) {
1207   // Test what happens when a working-directory-relative input file is shadowed
1208   // by another file in the virtual path.
1209 
1210   SetInputsAreProtoPathRelative(false);
1211 
1212   CreateTempFile("foo/foo.proto",
1213     "syntax = \"proto2\";\n"
1214     "message Foo {}\n");
1215   CreateTempFile("bar/foo.proto",
1216     "syntax = \"proto2\";\n"
1217     "message Bar {}\n");
1218 
1219   Run("protocol_compiler --test_out=$tmpdir "
1220       "--proto_path=$tmpdir/foo --proto_path=$tmpdir/bar "
1221       "$tmpdir/bar/foo.proto");
1222 
1223   ExpectErrorText(
1224     "$tmpdir/bar/foo.proto: Input is shadowed in the --proto_path "
1225     "by \"$tmpdir/foo/foo.proto\".  Either use the latter "
1226     "file as your input or reorder the --proto_path so that the "
1227     "former file's location comes first.\n");
1228 }
1229 
1230 TEST_F(CommandLineInterfaceTest, ProtoPathNotFoundError) {
1231   // Test what happens if the input file is not found.
1232 
1233   Run("protocol_compiler --test_out=$tmpdir "
1234       "--proto_path=$tmpdir/foo foo.proto");
1235 
1236   ExpectErrorText(
1237     "$tmpdir/foo: warning: directory does not exist.\n"
1238     "foo.proto: File not found.\n");
1239 }
1240 
1241 TEST_F(CommandLineInterfaceTest, MissingInputError) {
1242   // Test that we get an error if no inputs are given.
1243 
1244   Run("protocol_compiler --test_out=$tmpdir "
1245       "--proto_path=$tmpdir");
1246 
1247   ExpectErrorText("Missing input file.\n");
1248 }
1249 
1250 TEST_F(CommandLineInterfaceTest, MissingOutputError) {
1251   CreateTempFile("foo.proto",
1252     "syntax = \"proto2\";\n"
1253     "message Foo {}\n");
1254 
1255   Run("protocol_compiler --proto_path=$tmpdir foo.proto");
1256 
1257   ExpectErrorText("Missing output directives.\n");
1258 }
1259 
1260 TEST_F(CommandLineInterfaceTest, OutputWriteError) {
1261   CreateTempFile("foo.proto",
1262     "syntax = \"proto2\";\n"
1263     "message Foo {}\n");
1264 
1265   string output_file =
1266       MockCodeGenerator::GetOutputFileName("test_generator", "foo.proto");
1267 
1268   // Create a directory blocking our output location.
1269   CreateTempDir(output_file);
1270 
1271   Run("protocol_compiler --test_out=$tmpdir "
1272       "--proto_path=$tmpdir foo.proto");
1273 
1274   // MockCodeGenerator no longer detects an error because we actually write to
1275   // an in-memory location first, then dump to disk at the end.  This is no
1276   // big deal.
1277   //   ExpectErrorSubstring("MockCodeGenerator detected write error.");
1278 
1279 #if defined(_WIN32) && !defined(__CYGWIN__)
1280   // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
1281   if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
1282     return;
1283   }
1284 #endif
1285 
1286   ExpectErrorSubstring(output_file + ": Is a directory");
1287 }
1288 
1289 TEST_F(CommandLineInterfaceTest, PluginOutputWriteError) {
1290   CreateTempFile("foo.proto",
1291     "syntax = \"proto2\";\n"
1292     "message Foo {}\n");
1293 
1294   string output_file =
1295       MockCodeGenerator::GetOutputFileName("test_plugin", "foo.proto");
1296 
1297   // Create a directory blocking our output location.
1298   CreateTempDir(output_file);
1299 
1300   Run("protocol_compiler --plug_out=$tmpdir "
1301       "--proto_path=$tmpdir foo.proto");
1302 
1303 #if defined(_WIN32) && !defined(__CYGWIN__)
1304   // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
1305   if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
1306     return;
1307   }
1308 #endif
1309 
1310   ExpectErrorSubstring(output_file + ": Is a directory");
1311 }
1312 
1313 TEST_F(CommandLineInterfaceTest, OutputDirectoryNotFoundError) {
1314   CreateTempFile("foo.proto",
1315     "syntax = \"proto2\";\n"
1316     "message Foo {}\n");
1317 
1318   Run("protocol_compiler --test_out=$tmpdir/nosuchdir "
1319       "--proto_path=$tmpdir foo.proto");
1320 
1321   ExpectErrorSubstring("nosuchdir/: No such file or directory");
1322 }
1323 
1324 TEST_F(CommandLineInterfaceTest, PluginOutputDirectoryNotFoundError) {
1325   CreateTempFile("foo.proto",
1326     "syntax = \"proto2\";\n"
1327     "message Foo {}\n");
1328 
1329   Run("protocol_compiler --plug_out=$tmpdir/nosuchdir "
1330       "--proto_path=$tmpdir foo.proto");
1331 
1332   ExpectErrorSubstring("nosuchdir/: No such file or directory");
1333 }
1334 
1335 TEST_F(CommandLineInterfaceTest, OutputDirectoryIsFileError) {
1336   CreateTempFile("foo.proto",
1337     "syntax = \"proto2\";\n"
1338     "message Foo {}\n");
1339 
1340   Run("protocol_compiler --test_out=$tmpdir/foo.proto "
1341       "--proto_path=$tmpdir foo.proto");
1342 
1343 #if defined(_WIN32) && !defined(__CYGWIN__)
1344   // Windows with MSVCRT.dll produces EINVAL instead of ENOTDIR.
1345   if (HasAlternateErrorSubstring("foo.proto/: Invalid argument")) {
1346     return;
1347   }
1348 #endif
1349 
1350   ExpectErrorSubstring("foo.proto/: Not a directory");
1351 }
1352 
1353 TEST_F(CommandLineInterfaceTest, GeneratorError) {
1354   CreateTempFile("foo.proto",
1355     "syntax = \"proto2\";\n"
1356     "message MockCodeGenerator_Error {}\n");
1357 
1358   Run("protocol_compiler --test_out=$tmpdir "
1359       "--proto_path=$tmpdir foo.proto");
1360 
1361   ExpectErrorSubstring(
1362       "--test_out: foo.proto: Saw message type MockCodeGenerator_Error.");
1363 }
1364 
1365 TEST_F(CommandLineInterfaceTest, GeneratorPluginError) {
1366   // Test a generator plugin that returns an error.
1367 
1368   CreateTempFile("foo.proto",
1369     "syntax = \"proto2\";\n"
1370     "message MockCodeGenerator_Error {}\n");
1371 
1372   Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1373       "--proto_path=$tmpdir foo.proto");
1374 
1375   ExpectErrorSubstring(
1376       "--plug_out: foo.proto: Saw message type MockCodeGenerator_Error.");
1377 }
1378 
1379 TEST_F(CommandLineInterfaceTest, GeneratorPluginFail) {
1380   // Test a generator plugin that exits with an error code.
1381 
1382   CreateTempFile("foo.proto",
1383     "syntax = \"proto2\";\n"
1384     "message MockCodeGenerator_Exit {}\n");
1385 
1386   Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1387       "--proto_path=$tmpdir foo.proto");
1388 
1389   ExpectErrorSubstring("Saw message type MockCodeGenerator_Exit.");
1390   ExpectErrorSubstring(
1391       "--plug_out: prefix-gen-plug: Plugin failed with status code 123.");
1392 }
1393 
1394 TEST_F(CommandLineInterfaceTest, GeneratorPluginCrash) {
1395   // Test a generator plugin that crashes.
1396 
1397   CreateTempFile("foo.proto",
1398     "syntax = \"proto2\";\n"
1399     "message MockCodeGenerator_Abort {}\n");
1400 
1401   Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1402       "--proto_path=$tmpdir foo.proto");
1403 
1404   ExpectErrorSubstring("Saw message type MockCodeGenerator_Abort.");
1405 
1406 #ifdef _WIN32
1407   // Windows doesn't have signals.  It looks like abort()ing causes the process
1408   // to exit with status code 3, but let's not depend on the exact number here.
1409   ExpectErrorSubstring(
1410       "--plug_out: prefix-gen-plug: Plugin failed with status code");
1411 #else
1412   // Don't depend on the exact signal number.
1413   ExpectErrorSubstring(
1414       "--plug_out: prefix-gen-plug: Plugin killed by signal");
1415 #endif
1416 }
1417 
1418 TEST_F(CommandLineInterfaceTest, PluginReceivesSourceCodeInfo) {
1419   CreateTempFile("foo.proto",
1420     "syntax = \"proto2\";\n"
1421     "message MockCodeGenerator_HasSourceCodeInfo {}\n");
1422 
1423   Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
1424 
1425   ExpectErrorSubstring(
1426       "Saw message type MockCodeGenerator_HasSourceCodeInfo: 1.");
1427 }
1428 
1429 TEST_F(CommandLineInterfaceTest, PluginReceivesJsonName) {
1430   CreateTempFile("foo.proto",
1431     "syntax = \"proto2\";\n"
1432     "message MockCodeGenerator_HasJsonName {\n"
1433     "  optional int32 value = 1;\n"
1434     "}\n");
1435 
1436   Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
1437 
1438   ExpectErrorSubstring("Saw json_name: 1");
1439 }
1440 
1441 TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
1442   // Test what happens if the plugin isn't found.
1443 
1444   CreateTempFile("error.proto",
1445     "syntax = \"proto2\";\n"
1446     "message Foo {}\n");
1447 
1448   Run("protocol_compiler --badplug_out=TestParameter:$tmpdir "
1449       "--plugin=prefix-gen-badplug=no_such_file "
1450       "--proto_path=$tmpdir error.proto");
1451 
1452 #ifdef _WIN32
1453   ExpectErrorSubstring("--badplug_out: prefix-gen-badplug: " +
1454       Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND));
1455 #else
1456   // Error written to stdout by child process after exec() fails.
1457   ExpectErrorSubstring(
1458       "no_such_file: program not found or is not executable");
1459 
1460   // Error written by parent process when child fails.
1461   ExpectErrorSubstring(
1462       "--badplug_out: prefix-gen-badplug: Plugin failed with status code 1.");
1463 #endif
1464 }
1465 
1466 TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) {
1467   // Test what happens if plugins aren't allowed.
1468 
1469   CreateTempFile("error.proto",
1470     "syntax = \"proto2\";\n"
1471     "message Foo {}\n");
1472 
1473   DisallowPlugins();
1474   Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1475       "--proto_path=$tmpdir error.proto");
1476 
1477   ExpectErrorSubstring("Unknown flag: --plug_out");
1478 }
1479 
1480 TEST_F(CommandLineInterfaceTest, HelpText) {
1481   Run("test_exec_name --help");
1482 
1483   ExpectErrorSubstringWithZeroReturnCode("Usage: test_exec_name ");
1484   ExpectErrorSubstringWithZeroReturnCode("--test_out=OUT_DIR");
1485   ExpectErrorSubstringWithZeroReturnCode("Test output.");
1486   ExpectErrorSubstringWithZeroReturnCode("--alt_out=OUT_DIR");
1487   ExpectErrorSubstringWithZeroReturnCode("Alt output.");
1488 }
1489 
1490 TEST_F(CommandLineInterfaceTest, GccFormatErrors) {
1491   // Test --error_format=gcc (which is the default, but we want to verify
1492   // that it can be set explicitly).
1493 
1494   CreateTempFile("foo.proto",
1495     "syntax = \"proto2\";\n"
1496     "badsyntax\n");
1497 
1498   Run("protocol_compiler --test_out=$tmpdir "
1499       "--proto_path=$tmpdir --error_format=gcc foo.proto");
1500 
1501   ExpectErrorText(
1502     "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
1503 }
1504 
1505 TEST_F(CommandLineInterfaceTest, MsvsFormatErrors) {
1506   // Test --error_format=msvs
1507 
1508   CreateTempFile("foo.proto",
1509     "syntax = \"proto2\";\n"
1510     "badsyntax\n");
1511 
1512   Run("protocol_compiler --test_out=$tmpdir "
1513       "--proto_path=$tmpdir --error_format=msvs foo.proto");
1514 
1515   ExpectErrorText(
1516     "$tmpdir/foo.proto(2) : error in column=1: Expected top-level statement "
1517       "(e.g. \"message\").\n");
1518 }
1519 
1520 TEST_F(CommandLineInterfaceTest, InvalidErrorFormat) {
1521   // Test --error_format=msvs
1522 
1523   CreateTempFile("foo.proto",
1524     "syntax = \"proto2\";\n"
1525     "badsyntax\n");
1526 
1527   Run("protocol_compiler --test_out=$tmpdir "
1528       "--proto_path=$tmpdir --error_format=invalid foo.proto");
1529 
1530   ExpectErrorText(
1531     "Unknown error format: invalid\n");
1532 }
1533 
1534 // -------------------------------------------------------------------
1535 // Flag parsing tests
1536 
1537 TEST_F(CommandLineInterfaceTest, ParseSingleCharacterFlag) {
1538   // Test that a single-character flag works.
1539 
1540   CreateTempFile("foo.proto",
1541     "syntax = \"proto2\";\n"
1542     "message Foo {}\n");
1543 
1544   Run("protocol_compiler -t$tmpdir "
1545       "--proto_path=$tmpdir foo.proto");
1546 
1547   ExpectNoErrors();
1548   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1549 }
1550 
1551 TEST_F(CommandLineInterfaceTest, ParseSpaceDelimitedValue) {
1552   // Test that separating the flag value with a space works.
1553 
1554   CreateTempFile("foo.proto",
1555     "syntax = \"proto2\";\n"
1556     "message Foo {}\n");
1557 
1558   Run("protocol_compiler --test_out $tmpdir "
1559       "--proto_path=$tmpdir foo.proto");
1560 
1561   ExpectNoErrors();
1562   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1563 }
1564 
1565 TEST_F(CommandLineInterfaceTest, ParseSingleCharacterSpaceDelimitedValue) {
1566   // Test that separating the flag value with a space works for
1567   // single-character flags.
1568 
1569   CreateTempFile("foo.proto",
1570     "syntax = \"proto2\";\n"
1571     "message Foo {}\n");
1572 
1573   Run("protocol_compiler -t $tmpdir "
1574       "--proto_path=$tmpdir foo.proto");
1575 
1576   ExpectNoErrors();
1577   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1578 }
1579 
1580 TEST_F(CommandLineInterfaceTest, MissingValueError) {
1581   // Test that we get an error if a flag is missing its value.
1582 
1583   Run("protocol_compiler --test_out --proto_path=$tmpdir foo.proto");
1584 
1585   ExpectErrorText("Missing value for flag: --test_out\n");
1586 }
1587 
1588 TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) {
1589   // Test that we get an error if the last argument is a flag requiring a
1590   // value.
1591 
1592   Run("protocol_compiler --test_out");
1593 
1594   ExpectErrorText("Missing value for flag: --test_out\n");
1595 }
1596 
1597 TEST_F(CommandLineInterfaceTest, PrintFreeFieldNumbers) {
1598   CreateTempFile(
1599       "foo.proto",
1600       "syntax = \"proto2\";\n"
1601       "package foo;\n"
1602       "message Foo {\n"
1603       "  optional int32 a = 2;\n"
1604       "  optional string b = 4;\n"
1605       "  optional string c = 5;\n"
1606       "  optional int64 d = 8;\n"
1607       "  optional double e = 10;\n"
1608       "}\n");
1609   CreateTempFile(
1610       "bar.proto",
1611       "syntax = \"proto2\";\n"
1612       "message Bar {\n"
1613       "  optional int32 a = 2;\n"
1614       "  extensions 4 to 5;\n"
1615       "  optional int64 d = 8;\n"
1616       "  extensions 10;\n"
1617       "}\n");
1618   CreateTempFile(
1619       "baz.proto",
1620       "syntax = \"proto2\";\n"
1621       "message Baz {\n"
1622       "  optional int32 a = 2;\n"
1623       "  optional int64 d = 8;\n"
1624       "  extensions 15 to max;\n"  // unordered.
1625       "  extensions 13;\n"
1626       "  extensions 10 to 12;\n"
1627       "  extensions 5;\n"
1628       "  extensions 4;\n"
1629       "}\n");
1630   CreateTempFile(
1631       "quz.proto",
1632       "syntax = \"proto2\";\n"
1633       "message Quz {\n"
1634       "  message Foo {}\n"  // nested message
1635       "  optional int32 a = 2;\n"
1636       "  optional group C = 4 {\n"
1637       "    optional int32 d = 5;\n"
1638       "  }\n"
1639       "  extensions 8 to 10;\n"
1640       "  optional group E = 11 {\n"
1641       "    optional int32 f = 9;\n"    // explicitly reuse extension range 8-10
1642       "    optional group G = 15 {\n"  // nested group
1643       "      message Foo {}\n"         // nested message inside nested group
1644       "    }\n"
1645       "  }\n"
1646       "}\n");
1647 
1648   Run("protocol_compiler --print_free_field_numbers --proto_path=$tmpdir "
1649       "foo.proto bar.proto baz.proto quz.proto");
1650 
1651   ExpectNoErrors();
1652 
1653   // TODO(jieluo): Cygwin doesn't work well if we try to capture stderr and
1654   // stdout at the same time. Need to figure out why and add this test back
1655   // for Cygwin.
1656 #if !defined(__CYGWIN__)
1657   ExpectCapturedStdout(
1658       "foo.Foo                             free: 1 3 6-7 9 11-INF\n"
1659       "Bar                                 free: 1 3 6-7 9 11-INF\n"
1660       "Baz                                 free: 1 3 6-7 9 14\n"
1661       "Quz.Foo                             free: 1-INF\n"
1662       "Quz.E.G.Foo                         free: 1-INF\n"
1663       "Quz                                 free: 1 3 6-7 12-14 16-INF\n");
1664 #endif
1665 }
1666 
1667 // ===================================================================
1668 
1669 // Test for --encode and --decode.  Note that it would be easier to do this
1670 // test as a shell script, but we'd like to be able to run the test on
1671 // platforms that don't have a Bourne-compatible shell available (especially
1672 // Windows/MSVC).
1673 class EncodeDecodeTest : public testing::Test {
1674  protected:
1675   virtual void SetUp() {
1676     duped_stdin_ = dup(STDIN_FILENO);
1677   }
1678 
1679   virtual void TearDown() {
1680     dup2(duped_stdin_, STDIN_FILENO);
1681     close(duped_stdin_);
1682   }
1683 
1684   void RedirectStdinFromText(const string& input) {
1685     string filename = TestTempDir() + "/test_stdin";
1686     GOOGLE_CHECK_OK(File::SetContents(filename, input, true));
1687     GOOGLE_CHECK(RedirectStdinFromFile(filename));
1688   }
1689 
1690   bool RedirectStdinFromFile(const string& filename) {
1691     int fd = open(filename.c_str(), O_RDONLY);
1692     if (fd < 0) return false;
1693     dup2(fd, STDIN_FILENO);
1694     close(fd);
1695     return true;
1696   }
1697 
1698   // Remove '\r' characters from text.
1699   string StripCR(const string& text) {
1700     string result;
1701 
1702     for (int i = 0; i < text.size(); i++) {
1703       if (text[i] != '\r') {
1704         result.push_back(text[i]);
1705       }
1706     }
1707 
1708     return result;
1709   }
1710 
1711   enum Type { TEXT, BINARY };
1712   enum ReturnCode { SUCCESS, ERROR };
1713 
1714   bool Run(const string& command) {
1715     vector<string> args;
1716     args.push_back("protoc");
1717     SplitStringUsing(command, " ", &args);
1718     args.push_back("--proto_path=" + TestSourceDir());
1719 
1720     google::protobuf::scoped_array<const char * > argv(new const char* [args.size()]);
1721     for (int i = 0; i < args.size(); i++) {
1722       argv[i] = args[i].c_str();
1723     }
1724 
1725     CommandLineInterface cli;
1726     cli.SetInputsAreProtoPathRelative(true);
1727 
1728     CaptureTestStdout();
1729     CaptureTestStderr();
1730 
1731     int result = cli.Run(args.size(), argv.get());
1732 
1733     captured_stdout_ = GetCapturedTestStdout();
1734     captured_stderr_ = GetCapturedTestStderr();
1735 
1736     return result == 0;
1737   }
1738 
1739   void ExpectStdoutMatchesBinaryFile(const string& filename) {
1740     string expected_output;
1741     GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
1742 
1743     // Don't use EXPECT_EQ because we don't want to print raw binary data to
1744     // stdout on failure.
1745     EXPECT_TRUE(captured_stdout_ == expected_output);
1746   }
1747 
1748   void ExpectStdoutMatchesTextFile(const string& filename) {
1749     string expected_output;
1750     GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
1751 
1752     ExpectStdoutMatchesText(expected_output);
1753   }
1754 
1755   void ExpectStdoutMatchesText(const string& expected_text) {
1756     EXPECT_EQ(StripCR(expected_text), StripCR(captured_stdout_));
1757   }
1758 
1759   void ExpectStderrMatchesText(const string& expected_text) {
1760     EXPECT_EQ(StripCR(expected_text), StripCR(captured_stderr_));
1761   }
1762 
1763  private:
1764   int duped_stdin_;
1765   string captured_stdout_;
1766   string captured_stderr_;
1767 };
1768 
1769 TEST_F(EncodeDecodeTest, Encode) {
1770   RedirectStdinFromFile(TestSourceDir() + "/google/protobuf/"
1771     "testdata/text_format_unittest_data_oneof_implemented.txt");
1772   EXPECT_TRUE(Run("google/protobuf/unittest.proto "
1773                   "--encode=protobuf_unittest.TestAllTypes"));
1774   ExpectStdoutMatchesBinaryFile(TestSourceDir() +
1775     "/google/protobuf/testdata/golden_message_oneof_implemented");
1776   ExpectStderrMatchesText("");
1777 }
1778 
1779 TEST_F(EncodeDecodeTest, Decode) {
1780   RedirectStdinFromFile(TestSourceDir() +
1781     "/google/protobuf/testdata/golden_message_oneof_implemented");
1782   EXPECT_TRUE(Run("google/protobuf/unittest.proto "
1783                   "--decode=protobuf_unittest.TestAllTypes"));
1784   ExpectStdoutMatchesTextFile(TestSourceDir() +
1785     "/google/protobuf/"
1786     "testdata/text_format_unittest_data_oneof_implemented.txt");
1787   ExpectStderrMatchesText("");
1788 }
1789 
1790 TEST_F(EncodeDecodeTest, Partial) {
1791   RedirectStdinFromText("");
1792   EXPECT_TRUE(Run("google/protobuf/unittest.proto "
1793                   "--encode=protobuf_unittest.TestRequired"));
1794   ExpectStdoutMatchesText("");
1795   ExpectStderrMatchesText(
1796     "warning:  Input message is missing required fields:  a, b, c\n");
1797 }
1798 
1799 TEST_F(EncodeDecodeTest, DecodeRaw) {
1800   protobuf_unittest::TestAllTypes message;
1801   message.set_optional_int32(123);
1802   message.set_optional_string("foo");
1803   string data;
1804   message.SerializeToString(&data);
1805 
1806   RedirectStdinFromText(data);
1807   EXPECT_TRUE(Run("--decode_raw"));
1808   ExpectStdoutMatchesText("1: 123\n"
1809                           "14: \"foo\"\n");
1810   ExpectStderrMatchesText("");
1811 }
1812 
1813 TEST_F(EncodeDecodeTest, UnknownType) {
1814   EXPECT_FALSE(Run("google/protobuf/unittest.proto "
1815                    "--encode=NoSuchType"));
1816   ExpectStdoutMatchesText("");
1817   ExpectStderrMatchesText("Type not defined: NoSuchType\n");
1818 }
1819 
1820 TEST_F(EncodeDecodeTest, ProtoParseError) {
1821   EXPECT_FALSE(Run("google/protobuf/no_such_file.proto "
1822                    "--encode=NoSuchType"));
1823   ExpectStdoutMatchesText("");
1824   ExpectStderrMatchesText(
1825     "google/protobuf/no_such_file.proto: File not found.\n");
1826 }
1827 
1828 }  // anonymous namespace
1829 
1830 #endif  // !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
1831 
1832 }  // namespace compiler
1833 }  // namespace protobuf
1834 }  // namespace google
1835