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, PathWithEqualsSign) {
790   // Test setting up a search path which happens to have '=' in it.
791 
792   CreateTempDir("with=sign");
793   CreateTempFile("with=sign/foo.proto",
794     "syntax = \"proto2\";\n"
795     "message Foo {}\n");
796 
797   Run("protocol_compiler --test_out=$tmpdir "
798       "--proto_path=$tmpdir/with=sign foo.proto");
799 
800   ExpectNoErrors();
801   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
802 }
803 
804 TEST_F(CommandLineInterfaceTest, MultipleGenerators) {
805   // Test that we can have multiple generators and use both in one invocation,
806   // each with a different output directory.
807 
808   CreateTempFile("foo.proto",
809     "syntax = \"proto2\";\n"
810     "message Foo {}\n");
811   // Create the "a" and "b" sub-directories.
812   CreateTempDir("a");
813   CreateTempDir("b");
814 
815   Run("protocol_compiler "
816       "--test_out=$tmpdir/a "
817       "--alt_out=$tmpdir/b "
818       "--proto_path=$tmpdir foo.proto");
819 
820   ExpectNoErrors();
821   ExpectGenerated("test_generator", "", "foo.proto", "Foo", "a");
822   ExpectGenerated("alt_generator", "", "foo.proto", "Foo", "b");
823 }
824 
825 TEST_F(CommandLineInterfaceTest, DisallowServicesNoServices) {
826   // Test that --disallow_services doesn't cause a problem when there are no
827   // services.
828 
829   CreateTempFile("foo.proto",
830     "syntax = \"proto2\";\n"
831     "message Foo {}\n");
832 
833   Run("protocol_compiler --disallow_services --test_out=$tmpdir "
834       "--proto_path=$tmpdir foo.proto");
835 
836   ExpectNoErrors();
837   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
838 }
839 
840 TEST_F(CommandLineInterfaceTest, DisallowServicesHasService) {
841   // Test that --disallow_services produces an error when there are services.
842 
843   CreateTempFile("foo.proto",
844     "syntax = \"proto2\";\n"
845     "message Foo {}\n"
846     "service Bar {}\n");
847 
848   Run("protocol_compiler --disallow_services --test_out=$tmpdir "
849       "--proto_path=$tmpdir foo.proto");
850 
851   ExpectErrorSubstring("foo.proto: This file contains services");
852 }
853 
854 TEST_F(CommandLineInterfaceTest, AllowServicesHasService) {
855   // Test that services work fine as long as --disallow_services is not used.
856 
857   CreateTempFile("foo.proto",
858     "syntax = \"proto2\";\n"
859     "message Foo {}\n"
860     "service Bar {}\n");
861 
862   Run("protocol_compiler --test_out=$tmpdir "
863       "--proto_path=$tmpdir foo.proto");
864 
865   ExpectNoErrors();
866   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
867 }
868 
869 TEST_F(CommandLineInterfaceTest, CwdRelativeInputs) {
870   // Test that we can accept working-directory-relative input files.
871 
872   SetInputsAreProtoPathRelative(false);
873 
874   CreateTempFile("foo.proto",
875     "syntax = \"proto2\";\n"
876     "message Foo {}\n");
877 
878   Run("protocol_compiler --test_out=$tmpdir "
879       "--proto_path=$tmpdir $tmpdir/foo.proto");
880 
881   ExpectNoErrors();
882   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
883 }
884 
885 TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) {
886   CreateTempFile("foo.proto",
887     "syntax = \"proto2\";\n"
888     "message Foo {}\n");
889   CreateTempFile("bar.proto",
890     "syntax = \"proto2\";\n"
891     "import \"foo.proto\";\n"
892     "message Bar {\n"
893     "  optional Foo foo = 1;\n"
894     "}\n");
895 
896   Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
897       "--proto_path=$tmpdir bar.proto");
898 
899   ExpectNoErrors();
900 
901   FileDescriptorSet descriptor_set;
902   ReadDescriptorSet("descriptor_set", &descriptor_set);
903   if (HasFatalFailure()) return;
904   EXPECT_EQ(1, descriptor_set.file_size());
905   EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
906   // Descriptor set should not have source code info.
907   EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
908   // Descriptor set should have json_name.
909   EXPECT_EQ("Bar", descriptor_set.file(0).message_type(0).name());
910   EXPECT_EQ("foo", descriptor_set.file(0).message_type(0).field(0).name());
911   EXPECT_TRUE(descriptor_set.file(0).message_type(0).field(0).has_json_name());
912 }
913 
914 TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithDuplicates) {
915   CreateTempFile("foo.proto",
916     "syntax = \"proto2\";\n"
917     "message Foo {}\n");
918   CreateTempFile("bar.proto",
919     "syntax = \"proto2\";\n"
920     "import \"foo.proto\";\n"
921     "message Bar {\n"
922     "  optional Foo foo = 1;\n"
923     "}\n");
924   CreateTempFile("baz.proto",
925     "syntax = \"proto2\";\n"
926     "import \"foo.proto\";\n"
927     "message Baz {\n"
928     "  optional Foo foo = 1;\n"
929     "}\n");
930 
931   Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
932       "--proto_path=$tmpdir bar.proto foo.proto bar.proto baz.proto");
933 
934   ExpectNoErrors();
935 
936   FileDescriptorSet descriptor_set;
937   ReadDescriptorSet("descriptor_set", &descriptor_set);
938   if (HasFatalFailure()) return;
939   EXPECT_EQ(3, descriptor_set.file_size());
940   EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
941   EXPECT_EQ("foo.proto", descriptor_set.file(1).name());
942   EXPECT_EQ("baz.proto", descriptor_set.file(2).name());
943   // Descriptor set should not have source code info.
944   EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
945   // Descriptor set should have json_name.
946   EXPECT_EQ("Bar", descriptor_set.file(0).message_type(0).name());
947   EXPECT_EQ("foo", descriptor_set.file(0).message_type(0).field(0).name());
948   EXPECT_TRUE(descriptor_set.file(0).message_type(0).field(0).has_json_name());
949 }
950 
951 TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) {
952   CreateTempFile("foo.proto",
953     "syntax = \"proto2\";\n"
954     "message Foo {}\n");
955   CreateTempFile("bar.proto",
956     "syntax = \"proto2\";\n"
957     "import \"foo.proto\";\n"
958     "message Bar {\n"
959     "  optional Foo foo = 1;\n"
960     "}\n");
961 
962   Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
963       "--include_source_info --proto_path=$tmpdir bar.proto");
964 
965   ExpectNoErrors();
966 
967   FileDescriptorSet descriptor_set;
968   ReadDescriptorSet("descriptor_set", &descriptor_set);
969   if (HasFatalFailure()) return;
970   EXPECT_EQ(1, descriptor_set.file_size());
971   EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
972   // Source code info included.
973   EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
974 }
975 
976 TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) {
977   CreateTempFile("foo.proto",
978     "syntax = \"proto2\";\n"
979     "message Foo {}\n");
980   CreateTempFile("bar.proto",
981     "syntax = \"proto2\";\n"
982     "import \"foo.proto\";\n"
983     "message Bar {\n"
984     "  optional Foo foo = 1;\n"
985     "}\n");
986 
987   Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
988       "--include_imports --proto_path=$tmpdir bar.proto");
989 
990   ExpectNoErrors();
991 
992   FileDescriptorSet descriptor_set;
993   ReadDescriptorSet("descriptor_set", &descriptor_set);
994   if (HasFatalFailure()) return;
995   EXPECT_EQ(2, descriptor_set.file_size());
996   if (descriptor_set.file(0).name() == "bar.proto") {
997     std::swap(descriptor_set.mutable_file()->mutable_data()[0],
998               descriptor_set.mutable_file()->mutable_data()[1]);
999   }
1000   EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
1001   EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
1002   // Descriptor set should not have source code info.
1003   EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
1004   EXPECT_FALSE(descriptor_set.file(1).has_source_code_info());
1005 }
1006 
1007 TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSetWithSourceInfo) {
1008   CreateTempFile("foo.proto",
1009     "syntax = \"proto2\";\n"
1010     "message Foo {}\n");
1011   CreateTempFile("bar.proto",
1012     "syntax = \"proto2\";\n"
1013     "import \"foo.proto\";\n"
1014     "message Bar {\n"
1015     "  optional Foo foo = 1;\n"
1016     "}\n");
1017 
1018   Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
1019       "--include_imports --include_source_info --proto_path=$tmpdir bar.proto");
1020 
1021   ExpectNoErrors();
1022 
1023   FileDescriptorSet descriptor_set;
1024   ReadDescriptorSet("descriptor_set", &descriptor_set);
1025   if (HasFatalFailure()) return;
1026   EXPECT_EQ(2, descriptor_set.file_size());
1027   if (descriptor_set.file(0).name() == "bar.proto") {
1028     std::swap(descriptor_set.mutable_file()->mutable_data()[0],
1029               descriptor_set.mutable_file()->mutable_data()[1]);
1030   }
1031   EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
1032   EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
1033   // Source code info included.
1034   EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
1035   EXPECT_TRUE(descriptor_set.file(1).has_source_code_info());
1036 }
1037 
1038 #ifdef _WIN32
1039 // TODO(teboring): Figure out how to write test on windows.
1040 #else
1041 TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileGivenTwoInputs) {
1042   CreateTempFile("foo.proto",
1043     "syntax = \"proto2\";\n"
1044     "message Foo {}\n");
1045   CreateTempFile("bar.proto",
1046     "syntax = \"proto2\";\n"
1047     "import \"foo.proto\";\n"
1048     "message Bar {\n"
1049     "  optional Foo foo = 1;\n"
1050     "}\n");
1051 
1052   Run("protocol_compiler --dependency_out=$tmpdir/manifest "
1053       "--test_out=$tmpdir --proto_path=$tmpdir bar.proto foo.proto");
1054 
1055   ExpectErrorText(
1056       "Can only process one input file when using --dependency_out=FILE.\n");
1057 }
1058 
1059 #ifdef PROTOBUF_OPENSOURCE
1060 TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFile) {
1061   CreateTempFile("foo.proto",
1062     "syntax = \"proto2\";\n"
1063     "message Foo {}\n");
1064   CreateTempFile("bar.proto",
1065     "syntax = \"proto2\";\n"
1066     "import \"foo.proto\";\n"
1067     "message Bar {\n"
1068     "  optional Foo foo = 1;\n"
1069     "}\n");
1070 
1071   string current_working_directory = getcwd(NULL, 0);
1072   SwitchToTempDirectory();
1073 
1074   Run("protocol_compiler --dependency_out=manifest --test_out=. "
1075       "bar.proto");
1076 
1077   ExpectNoErrors();
1078 
1079   ExpectFileContent("manifest",
1080                     "bar.proto.MockCodeGenerator.test_generator: "
1081                     "foo.proto\\\n bar.proto");
1082 
1083   File::ChangeWorkingDirectory(current_working_directory);
1084 }
1085 #else  // !PROTOBUF_OPENSOURCE
1086 // TODO(teboring): Figure out how to change and get working directory in
1087 // google3.
1088 #endif  // !PROTOBUF_OPENSOURCE
1089 
1090 TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileForAbsolutePath) {
1091   CreateTempFile("foo.proto",
1092     "syntax = \"proto2\";\n"
1093     "message Foo {}\n");
1094   CreateTempFile("bar.proto",
1095     "syntax = \"proto2\";\n"
1096     "import \"foo.proto\";\n"
1097     "message Bar {\n"
1098     "  optional Foo foo = 1;\n"
1099     "}\n");
1100 
1101   Run("protocol_compiler --dependency_out=$tmpdir/manifest "
1102       "--test_out=$tmpdir --proto_path=$tmpdir bar.proto");
1103 
1104   ExpectNoErrors();
1105 
1106   ExpectFileContent("manifest",
1107                     "$tmpdir/bar.proto.MockCodeGenerator.test_generator: "
1108                     "$tmpdir/foo.proto\\\n $tmpdir/bar.proto");
1109 }
1110 #endif  // !_WIN32
1111 
1112 
1113 // -------------------------------------------------------------------
1114 
1115 TEST_F(CommandLineInterfaceTest, ParseErrors) {
1116   // Test that parse errors are reported.
1117 
1118   CreateTempFile("foo.proto",
1119     "syntax = \"proto2\";\n"
1120     "badsyntax\n");
1121 
1122   Run("protocol_compiler --test_out=$tmpdir "
1123       "--proto_path=$tmpdir foo.proto");
1124 
1125   ExpectErrorText(
1126     "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
1127 }
1128 
1129 TEST_F(CommandLineInterfaceTest, ParseErrorsMultipleFiles) {
1130   // Test that parse errors are reported from multiple files.
1131 
1132   // We set up files such that foo.proto actually depends on bar.proto in
1133   // two ways:  Directly and through baz.proto.  bar.proto's errors should
1134   // only be reported once.
1135   CreateTempFile("bar.proto",
1136     "syntax = \"proto2\";\n"
1137     "badsyntax\n");
1138   CreateTempFile("baz.proto",
1139     "syntax = \"proto2\";\n"
1140     "import \"bar.proto\";\n");
1141   CreateTempFile("foo.proto",
1142     "syntax = \"proto2\";\n"
1143     "import \"bar.proto\";\n"
1144     "import \"baz.proto\";\n");
1145 
1146   Run("protocol_compiler --test_out=$tmpdir "
1147       "--proto_path=$tmpdir foo.proto");
1148 
1149   ExpectErrorText(
1150     "bar.proto:2:1: Expected top-level statement (e.g. \"message\").\n"
1151     "baz.proto: Import \"bar.proto\" was not found or had errors.\n"
1152     "foo.proto: Import \"bar.proto\" was not found or had errors.\n"
1153     "foo.proto: Import \"baz.proto\" was not found or had errors.\n");
1154 }
1155 
1156 TEST_F(CommandLineInterfaceTest, InputNotFoundError) {
1157   // Test what happens if the input file is not found.
1158 
1159   Run("protocol_compiler --test_out=$tmpdir "
1160       "--proto_path=$tmpdir foo.proto");
1161 
1162   ExpectErrorText(
1163     "foo.proto: File not found.\n");
1164 }
1165 
1166 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundError) {
1167   // Test what happens when a working-directory-relative input file is not
1168   // found.
1169 
1170   SetInputsAreProtoPathRelative(false);
1171 
1172   Run("protocol_compiler --test_out=$tmpdir "
1173       "--proto_path=$tmpdir $tmpdir/foo.proto");
1174 
1175   ExpectErrorText(
1176     "$tmpdir/foo.proto: No such file or directory\n");
1177 }
1178 
1179 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotMappedError) {
1180   // Test what happens when a working-directory-relative input file is not
1181   // mapped to a virtual path.
1182 
1183   SetInputsAreProtoPathRelative(false);
1184 
1185   CreateTempFile("foo.proto",
1186     "syntax = \"proto2\";\n"
1187     "message Foo {}\n");
1188 
1189   // Create a directory called "bar" so that we can point --proto_path at it.
1190   CreateTempFile("bar/dummy", "");
1191 
1192   Run("protocol_compiler --test_out=$tmpdir "
1193       "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
1194 
1195   ExpectErrorText(
1196     "$tmpdir/foo.proto: File does not reside within any path "
1197       "specified using --proto_path (or -I).  You must specify a "
1198       "--proto_path which encompasses this file.  Note that the "
1199       "proto_path must be an exact prefix of the .proto file "
1200       "names -- protoc is too dumb to figure out when two paths "
1201       "(e.g. absolute and relative) are equivalent (it's harder "
1202       "than you think).\n");
1203 }
1204 
1205 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundAndNotMappedError) {
1206   // Check what happens if the input file is not found *and* is not mapped
1207   // in the proto_path.
1208 
1209   SetInputsAreProtoPathRelative(false);
1210 
1211   // Create a directory called "bar" so that we can point --proto_path at it.
1212   CreateTempFile("bar/dummy", "");
1213 
1214   Run("protocol_compiler --test_out=$tmpdir "
1215       "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
1216 
1217   ExpectErrorText(
1218     "$tmpdir/foo.proto: No such file or directory\n");
1219 }
1220 
1221 TEST_F(CommandLineInterfaceTest, CwdRelativeInputShadowedError) {
1222   // Test what happens when a working-directory-relative input file is shadowed
1223   // by another file in the virtual path.
1224 
1225   SetInputsAreProtoPathRelative(false);
1226 
1227   CreateTempFile("foo/foo.proto",
1228     "syntax = \"proto2\";\n"
1229     "message Foo {}\n");
1230   CreateTempFile("bar/foo.proto",
1231     "syntax = \"proto2\";\n"
1232     "message Bar {}\n");
1233 
1234   Run("protocol_compiler --test_out=$tmpdir "
1235       "--proto_path=$tmpdir/foo --proto_path=$tmpdir/bar "
1236       "$tmpdir/bar/foo.proto");
1237 
1238   ExpectErrorText(
1239     "$tmpdir/bar/foo.proto: Input is shadowed in the --proto_path "
1240     "by \"$tmpdir/foo/foo.proto\".  Either use the latter "
1241     "file as your input or reorder the --proto_path so that the "
1242     "former file's location comes first.\n");
1243 }
1244 
1245 TEST_F(CommandLineInterfaceTest, ProtoPathNotFoundError) {
1246   // Test what happens if the input file is not found.
1247 
1248   Run("protocol_compiler --test_out=$tmpdir "
1249       "--proto_path=$tmpdir/foo foo.proto");
1250 
1251   ExpectErrorText(
1252     "$tmpdir/foo: warning: directory does not exist.\n"
1253     "foo.proto: File not found.\n");
1254 }
1255 
1256 TEST_F(CommandLineInterfaceTest, MissingInputError) {
1257   // Test that we get an error if no inputs are given.
1258 
1259   Run("protocol_compiler --test_out=$tmpdir "
1260       "--proto_path=$tmpdir");
1261 
1262   ExpectErrorText("Missing input file.\n");
1263 }
1264 
1265 TEST_F(CommandLineInterfaceTest, MissingOutputError) {
1266   CreateTempFile("foo.proto",
1267     "syntax = \"proto2\";\n"
1268     "message Foo {}\n");
1269 
1270   Run("protocol_compiler --proto_path=$tmpdir foo.proto");
1271 
1272   ExpectErrorText("Missing output directives.\n");
1273 }
1274 
1275 TEST_F(CommandLineInterfaceTest, OutputWriteError) {
1276   CreateTempFile("foo.proto",
1277     "syntax = \"proto2\";\n"
1278     "message Foo {}\n");
1279 
1280   string output_file =
1281       MockCodeGenerator::GetOutputFileName("test_generator", "foo.proto");
1282 
1283   // Create a directory blocking our output location.
1284   CreateTempDir(output_file);
1285 
1286   Run("protocol_compiler --test_out=$tmpdir "
1287       "--proto_path=$tmpdir foo.proto");
1288 
1289   // MockCodeGenerator no longer detects an error because we actually write to
1290   // an in-memory location first, then dump to disk at the end.  This is no
1291   // big deal.
1292   //   ExpectErrorSubstring("MockCodeGenerator detected write error.");
1293 
1294 #if defined(_WIN32) && !defined(__CYGWIN__)
1295   // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
1296   if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
1297     return;
1298   }
1299 #endif
1300 
1301   ExpectErrorSubstring(output_file + ": Is a directory");
1302 }
1303 
1304 TEST_F(CommandLineInterfaceTest, PluginOutputWriteError) {
1305   CreateTempFile("foo.proto",
1306     "syntax = \"proto2\";\n"
1307     "message Foo {}\n");
1308 
1309   string output_file =
1310       MockCodeGenerator::GetOutputFileName("test_plugin", "foo.proto");
1311 
1312   // Create a directory blocking our output location.
1313   CreateTempDir(output_file);
1314 
1315   Run("protocol_compiler --plug_out=$tmpdir "
1316       "--proto_path=$tmpdir foo.proto");
1317 
1318 #if defined(_WIN32) && !defined(__CYGWIN__)
1319   // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
1320   if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
1321     return;
1322   }
1323 #endif
1324 
1325   ExpectErrorSubstring(output_file + ": Is a directory");
1326 }
1327 
1328 TEST_F(CommandLineInterfaceTest, OutputDirectoryNotFoundError) {
1329   CreateTempFile("foo.proto",
1330     "syntax = \"proto2\";\n"
1331     "message Foo {}\n");
1332 
1333   Run("protocol_compiler --test_out=$tmpdir/nosuchdir "
1334       "--proto_path=$tmpdir foo.proto");
1335 
1336   ExpectErrorSubstring("nosuchdir/: No such file or directory");
1337 }
1338 
1339 TEST_F(CommandLineInterfaceTest, PluginOutputDirectoryNotFoundError) {
1340   CreateTempFile("foo.proto",
1341     "syntax = \"proto2\";\n"
1342     "message Foo {}\n");
1343 
1344   Run("protocol_compiler --plug_out=$tmpdir/nosuchdir "
1345       "--proto_path=$tmpdir foo.proto");
1346 
1347   ExpectErrorSubstring("nosuchdir/: No such file or directory");
1348 }
1349 
1350 TEST_F(CommandLineInterfaceTest, OutputDirectoryIsFileError) {
1351   CreateTempFile("foo.proto",
1352     "syntax = \"proto2\";\n"
1353     "message Foo {}\n");
1354 
1355   Run("protocol_compiler --test_out=$tmpdir/foo.proto "
1356       "--proto_path=$tmpdir foo.proto");
1357 
1358 #if defined(_WIN32) && !defined(__CYGWIN__)
1359   // Windows with MSVCRT.dll produces EINVAL instead of ENOTDIR.
1360   if (HasAlternateErrorSubstring("foo.proto/: Invalid argument")) {
1361     return;
1362   }
1363 #endif
1364 
1365   ExpectErrorSubstring("foo.proto/: Not a directory");
1366 }
1367 
1368 TEST_F(CommandLineInterfaceTest, GeneratorError) {
1369   CreateTempFile("foo.proto",
1370     "syntax = \"proto2\";\n"
1371     "message MockCodeGenerator_Error {}\n");
1372 
1373   Run("protocol_compiler --test_out=$tmpdir "
1374       "--proto_path=$tmpdir foo.proto");
1375 
1376   ExpectErrorSubstring(
1377       "--test_out: foo.proto: Saw message type MockCodeGenerator_Error.");
1378 }
1379 
1380 TEST_F(CommandLineInterfaceTest, GeneratorPluginError) {
1381   // Test a generator plugin that returns an error.
1382 
1383   CreateTempFile("foo.proto",
1384     "syntax = \"proto2\";\n"
1385     "message MockCodeGenerator_Error {}\n");
1386 
1387   Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1388       "--proto_path=$tmpdir foo.proto");
1389 
1390   ExpectErrorSubstring(
1391       "--plug_out: foo.proto: Saw message type MockCodeGenerator_Error.");
1392 }
1393 
1394 TEST_F(CommandLineInterfaceTest, GeneratorPluginFail) {
1395   // Test a generator plugin that exits with an error code.
1396 
1397   CreateTempFile("foo.proto",
1398     "syntax = \"proto2\";\n"
1399     "message MockCodeGenerator_Exit {}\n");
1400 
1401   Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1402       "--proto_path=$tmpdir foo.proto");
1403 
1404   ExpectErrorSubstring("Saw message type MockCodeGenerator_Exit.");
1405   ExpectErrorSubstring(
1406       "--plug_out: prefix-gen-plug: Plugin failed with status code 123.");
1407 }
1408 
1409 TEST_F(CommandLineInterfaceTest, GeneratorPluginCrash) {
1410   // Test a generator plugin that crashes.
1411 
1412   CreateTempFile("foo.proto",
1413     "syntax = \"proto2\";\n"
1414     "message MockCodeGenerator_Abort {}\n");
1415 
1416   Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1417       "--proto_path=$tmpdir foo.proto");
1418 
1419   ExpectErrorSubstring("Saw message type MockCodeGenerator_Abort.");
1420 
1421 #ifdef _WIN32
1422   // Windows doesn't have signals.  It looks like abort()ing causes the process
1423   // to exit with status code 3, but let's not depend on the exact number here.
1424   ExpectErrorSubstring(
1425       "--plug_out: prefix-gen-plug: Plugin failed with status code");
1426 #else
1427   // Don't depend on the exact signal number.
1428   ExpectErrorSubstring(
1429       "--plug_out: prefix-gen-plug: Plugin killed by signal");
1430 #endif
1431 }
1432 
1433 TEST_F(CommandLineInterfaceTest, PluginReceivesSourceCodeInfo) {
1434   CreateTempFile("foo.proto",
1435     "syntax = \"proto2\";\n"
1436     "message MockCodeGenerator_HasSourceCodeInfo {}\n");
1437 
1438   Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
1439 
1440   ExpectErrorSubstring(
1441       "Saw message type MockCodeGenerator_HasSourceCodeInfo: 1.");
1442 }
1443 
1444 TEST_F(CommandLineInterfaceTest, PluginReceivesJsonName) {
1445   CreateTempFile("foo.proto",
1446     "syntax = \"proto2\";\n"
1447     "message MockCodeGenerator_HasJsonName {\n"
1448     "  optional int32 value = 1;\n"
1449     "}\n");
1450 
1451   Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
1452 
1453   ExpectErrorSubstring("Saw json_name: 1");
1454 }
1455 
1456 TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
1457   // Test what happens if the plugin isn't found.
1458 
1459   CreateTempFile("error.proto",
1460     "syntax = \"proto2\";\n"
1461     "message Foo {}\n");
1462 
1463   Run("protocol_compiler --badplug_out=TestParameter:$tmpdir "
1464       "--plugin=prefix-gen-badplug=no_such_file "
1465       "--proto_path=$tmpdir error.proto");
1466 
1467 #ifdef _WIN32
1468   ExpectErrorSubstring("--badplug_out: prefix-gen-badplug: " +
1469       Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND));
1470 #else
1471   // Error written to stdout by child process after exec() fails.
1472   ExpectErrorSubstring(
1473       "no_such_file: program not found or is not executable");
1474 
1475   // Error written by parent process when child fails.
1476   ExpectErrorSubstring(
1477       "--badplug_out: prefix-gen-badplug: Plugin failed with status code 1.");
1478 #endif
1479 }
1480 
1481 TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) {
1482   // Test what happens if plugins aren't allowed.
1483 
1484   CreateTempFile("error.proto",
1485     "syntax = \"proto2\";\n"
1486     "message Foo {}\n");
1487 
1488   DisallowPlugins();
1489   Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1490       "--proto_path=$tmpdir error.proto");
1491 
1492   ExpectErrorSubstring("Unknown flag: --plug_out");
1493 }
1494 
1495 TEST_F(CommandLineInterfaceTest, HelpText) {
1496   Run("test_exec_name --help");
1497 
1498   ExpectErrorSubstringWithZeroReturnCode("Usage: test_exec_name ");
1499   ExpectErrorSubstringWithZeroReturnCode("--test_out=OUT_DIR");
1500   ExpectErrorSubstringWithZeroReturnCode("Test output.");
1501   ExpectErrorSubstringWithZeroReturnCode("--alt_out=OUT_DIR");
1502   ExpectErrorSubstringWithZeroReturnCode("Alt output.");
1503 }
1504 
1505 TEST_F(CommandLineInterfaceTest, GccFormatErrors) {
1506   // Test --error_format=gcc (which is the default, but we want to verify
1507   // that it can be set explicitly).
1508 
1509   CreateTempFile("foo.proto",
1510     "syntax = \"proto2\";\n"
1511     "badsyntax\n");
1512 
1513   Run("protocol_compiler --test_out=$tmpdir "
1514       "--proto_path=$tmpdir --error_format=gcc foo.proto");
1515 
1516   ExpectErrorText(
1517     "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
1518 }
1519 
1520 TEST_F(CommandLineInterfaceTest, MsvsFormatErrors) {
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=msvs foo.proto");
1529 
1530   ExpectErrorText(
1531     "$tmpdir/foo.proto(2) : error in column=1: Expected top-level statement "
1532       "(e.g. \"message\").\n");
1533 }
1534 
1535 TEST_F(CommandLineInterfaceTest, InvalidErrorFormat) {
1536   // Test --error_format=msvs
1537 
1538   CreateTempFile("foo.proto",
1539     "syntax = \"proto2\";\n"
1540     "badsyntax\n");
1541 
1542   Run("protocol_compiler --test_out=$tmpdir "
1543       "--proto_path=$tmpdir --error_format=invalid foo.proto");
1544 
1545   ExpectErrorText(
1546     "Unknown error format: invalid\n");
1547 }
1548 
1549 // -------------------------------------------------------------------
1550 // Flag parsing tests
1551 
1552 TEST_F(CommandLineInterfaceTest, ParseSingleCharacterFlag) {
1553   // Test that a single-character flag works.
1554 
1555   CreateTempFile("foo.proto",
1556     "syntax = \"proto2\";\n"
1557     "message Foo {}\n");
1558 
1559   Run("protocol_compiler -t$tmpdir "
1560       "--proto_path=$tmpdir foo.proto");
1561 
1562   ExpectNoErrors();
1563   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1564 }
1565 
1566 TEST_F(CommandLineInterfaceTest, ParseSpaceDelimitedValue) {
1567   // Test that separating the flag value with a space works.
1568 
1569   CreateTempFile("foo.proto",
1570     "syntax = \"proto2\";\n"
1571     "message Foo {}\n");
1572 
1573   Run("protocol_compiler --test_out $tmpdir "
1574       "--proto_path=$tmpdir foo.proto");
1575 
1576   ExpectNoErrors();
1577   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1578 }
1579 
1580 TEST_F(CommandLineInterfaceTest, ParseSingleCharacterSpaceDelimitedValue) {
1581   // Test that separating the flag value with a space works for
1582   // single-character flags.
1583 
1584   CreateTempFile("foo.proto",
1585     "syntax = \"proto2\";\n"
1586     "message Foo {}\n");
1587 
1588   Run("protocol_compiler -t $tmpdir "
1589       "--proto_path=$tmpdir foo.proto");
1590 
1591   ExpectNoErrors();
1592   ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1593 }
1594 
1595 TEST_F(CommandLineInterfaceTest, MissingValueError) {
1596   // Test that we get an error if a flag is missing its value.
1597 
1598   Run("protocol_compiler --test_out --proto_path=$tmpdir foo.proto");
1599 
1600   ExpectErrorText("Missing value for flag: --test_out\n");
1601 }
1602 
1603 TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) {
1604   // Test that we get an error if the last argument is a flag requiring a
1605   // value.
1606 
1607   Run("protocol_compiler --test_out");
1608 
1609   ExpectErrorText("Missing value for flag: --test_out\n");
1610 }
1611 
1612 TEST_F(CommandLineInterfaceTest, PrintFreeFieldNumbers) {
1613   CreateTempFile(
1614       "foo.proto",
1615       "syntax = \"proto2\";\n"
1616       "package foo;\n"
1617       "message Foo {\n"
1618       "  optional int32 a = 2;\n"
1619       "  optional string b = 4;\n"
1620       "  optional string c = 5;\n"
1621       "  optional int64 d = 8;\n"
1622       "  optional double e = 10;\n"
1623       "}\n");
1624   CreateTempFile(
1625       "bar.proto",
1626       "syntax = \"proto2\";\n"
1627       "message Bar {\n"
1628       "  optional int32 a = 2;\n"
1629       "  extensions 4 to 5;\n"
1630       "  optional int64 d = 8;\n"
1631       "  extensions 10;\n"
1632       "}\n");
1633   CreateTempFile(
1634       "baz.proto",
1635       "syntax = \"proto2\";\n"
1636       "message Baz {\n"
1637       "  optional int32 a = 2;\n"
1638       "  optional int64 d = 8;\n"
1639       "  extensions 15 to max;\n"  // unordered.
1640       "  extensions 13;\n"
1641       "  extensions 10 to 12;\n"
1642       "  extensions 5;\n"
1643       "  extensions 4;\n"
1644       "}\n");
1645   CreateTempFile(
1646       "quz.proto",
1647       "syntax = \"proto2\";\n"
1648       "message Quz {\n"
1649       "  message Foo {}\n"  // nested message
1650       "  optional int32 a = 2;\n"
1651       "  optional group C = 4 {\n"
1652       "    optional int32 d = 5;\n"
1653       "  }\n"
1654       "  extensions 8 to 10;\n"
1655       "  optional group E = 11 {\n"
1656       "    optional int32 f = 9;\n"    // explicitly reuse extension range 8-10
1657       "    optional group G = 15 {\n"  // nested group
1658       "      message Foo {}\n"         // nested message inside nested group
1659       "    }\n"
1660       "  }\n"
1661       "}\n");
1662 
1663   Run("protocol_compiler --print_free_field_numbers --proto_path=$tmpdir "
1664       "foo.proto bar.proto baz.proto quz.proto");
1665 
1666   ExpectNoErrors();
1667 
1668   // TODO(jieluo): Cygwin doesn't work well if we try to capture stderr and
1669   // stdout at the same time. Need to figure out why and add this test back
1670   // for Cygwin.
1671 #if !defined(__CYGWIN__)
1672   ExpectCapturedStdout(
1673       "foo.Foo                             free: 1 3 6-7 9 11-INF\n"
1674       "Bar                                 free: 1 3 6-7 9 11-INF\n"
1675       "Baz                                 free: 1 3 6-7 9 14\n"
1676       "Quz.Foo                             free: 1-INF\n"
1677       "Quz.E.G.Foo                         free: 1-INF\n"
1678       "Quz                                 free: 1 3 6-7 12-14 16-INF\n");
1679 #endif
1680 }
1681 
1682 // ===================================================================
1683 
1684 // Test for --encode and --decode.  Note that it would be easier to do this
1685 // test as a shell script, but we'd like to be able to run the test on
1686 // platforms that don't have a Bourne-compatible shell available (especially
1687 // Windows/MSVC).
1688 class EncodeDecodeTest : public testing::Test {
1689  protected:
1690   virtual void SetUp() {
1691     duped_stdin_ = dup(STDIN_FILENO);
1692   }
1693 
1694   virtual void TearDown() {
1695     dup2(duped_stdin_, STDIN_FILENO);
1696     close(duped_stdin_);
1697   }
1698 
1699   void RedirectStdinFromText(const string& input) {
1700     string filename = TestTempDir() + "/test_stdin";
1701     GOOGLE_CHECK_OK(File::SetContents(filename, input, true));
1702     GOOGLE_CHECK(RedirectStdinFromFile(filename));
1703   }
1704 
1705   bool RedirectStdinFromFile(const string& filename) {
1706     int fd = open(filename.c_str(), O_RDONLY);
1707     if (fd < 0) return false;
1708     dup2(fd, STDIN_FILENO);
1709     close(fd);
1710     return true;
1711   }
1712 
1713   // Remove '\r' characters from text.
1714   string StripCR(const string& text) {
1715     string result;
1716 
1717     for (int i = 0; i < text.size(); i++) {
1718       if (text[i] != '\r') {
1719         result.push_back(text[i]);
1720       }
1721     }
1722 
1723     return result;
1724   }
1725 
1726   enum Type { TEXT, BINARY };
1727   enum ReturnCode { SUCCESS, ERROR };
1728 
1729   bool Run(const string& command) {
1730     vector<string> args;
1731     args.push_back("protoc");
1732     SplitStringUsing(command, " ", &args);
1733     args.push_back("--proto_path=" + TestSourceDir());
1734 
1735     google::protobuf::scoped_array<const char * > argv(new const char* [args.size()]);
1736     for (int i = 0; i < args.size(); i++) {
1737       argv[i] = args[i].c_str();
1738     }
1739 
1740     CommandLineInterface cli;
1741     cli.SetInputsAreProtoPathRelative(true);
1742 
1743     CaptureTestStdout();
1744     CaptureTestStderr();
1745 
1746     int result = cli.Run(args.size(), argv.get());
1747 
1748     captured_stdout_ = GetCapturedTestStdout();
1749     captured_stderr_ = GetCapturedTestStderr();
1750 
1751     return result == 0;
1752   }
1753 
1754   void ExpectStdoutMatchesBinaryFile(const string& filename) {
1755     string expected_output;
1756     GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
1757 
1758     // Don't use EXPECT_EQ because we don't want to print raw binary data to
1759     // stdout on failure.
1760     EXPECT_TRUE(captured_stdout_ == expected_output);
1761   }
1762 
1763   void ExpectStdoutMatchesTextFile(const string& filename) {
1764     string expected_output;
1765     GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
1766 
1767     ExpectStdoutMatchesText(expected_output);
1768   }
1769 
1770   void ExpectStdoutMatchesText(const string& expected_text) {
1771     EXPECT_EQ(StripCR(expected_text), StripCR(captured_stdout_));
1772   }
1773 
1774   void ExpectStderrMatchesText(const string& expected_text) {
1775     EXPECT_EQ(StripCR(expected_text), StripCR(captured_stderr_));
1776   }
1777 
1778  private:
1779   int duped_stdin_;
1780   string captured_stdout_;
1781   string captured_stderr_;
1782 };
1783 
1784 TEST_F(EncodeDecodeTest, Encode) {
1785   RedirectStdinFromFile(TestSourceDir() + "/google/protobuf/"
1786     "testdata/text_format_unittest_data_oneof_implemented.txt");
1787   EXPECT_TRUE(Run("google/protobuf/unittest.proto "
1788                   "--encode=protobuf_unittest.TestAllTypes"));
1789   ExpectStdoutMatchesBinaryFile(TestSourceDir() +
1790     "/google/protobuf/testdata/golden_message_oneof_implemented");
1791   ExpectStderrMatchesText("");
1792 }
1793 
1794 TEST_F(EncodeDecodeTest, Decode) {
1795   RedirectStdinFromFile(TestSourceDir() +
1796     "/google/protobuf/testdata/golden_message_oneof_implemented");
1797   EXPECT_TRUE(Run("google/protobuf/unittest.proto "
1798                   "--decode=protobuf_unittest.TestAllTypes"));
1799   ExpectStdoutMatchesTextFile(TestSourceDir() +
1800     "/google/protobuf/"
1801     "testdata/text_format_unittest_data_oneof_implemented.txt");
1802   ExpectStderrMatchesText("");
1803 }
1804 
1805 TEST_F(EncodeDecodeTest, Partial) {
1806   RedirectStdinFromText("");
1807   EXPECT_TRUE(Run("google/protobuf/unittest.proto "
1808                   "--encode=protobuf_unittest.TestRequired"));
1809   ExpectStdoutMatchesText("");
1810   ExpectStderrMatchesText(
1811     "warning:  Input message is missing required fields:  a, b, c\n");
1812 }
1813 
1814 TEST_F(EncodeDecodeTest, DecodeRaw) {
1815   protobuf_unittest::TestAllTypes message;
1816   message.set_optional_int32(123);
1817   message.set_optional_string("foo");
1818   string data;
1819   message.SerializeToString(&data);
1820 
1821   RedirectStdinFromText(data);
1822   EXPECT_TRUE(Run("--decode_raw"));
1823   ExpectStdoutMatchesText("1: 123\n"
1824                           "14: \"foo\"\n");
1825   ExpectStderrMatchesText("");
1826 }
1827 
1828 TEST_F(EncodeDecodeTest, UnknownType) {
1829   EXPECT_FALSE(Run("google/protobuf/unittest.proto "
1830                    "--encode=NoSuchType"));
1831   ExpectStdoutMatchesText("");
1832   ExpectStderrMatchesText("Type not defined: NoSuchType\n");
1833 }
1834 
1835 TEST_F(EncodeDecodeTest, ProtoParseError) {
1836   EXPECT_FALSE(Run("google/protobuf/no_such_file.proto "
1837                    "--encode=NoSuchType"));
1838   ExpectStdoutMatchesText("");
1839   ExpectStderrMatchesText(
1840     "google/protobuf/no_such_file.proto: File not found.\n");
1841 }
1842 
1843 }  // anonymous namespace
1844 
1845 #endif  // !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
1846 
1847 }  // namespace compiler
1848 }  // namespace protobuf
1849 }  // namespace google
1850