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