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