PageRenderTime 101ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 1ms

/src/google/protobuf/compiler/command_line_interface_unittest.cc

http://protobuf.googlecode.com/
C++ | 1560 lines | 1041 code | 330 blank | 189 comment | 35 complexity | 86188c16f4b5c100cc0aae2aae91f31c MD5 | raw file
Possible License(s): BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file