1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 // * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 // Author: kenton@google.com (Kenton Varda)
32 // Based on original Protocol Buffers design by
33 // Sanjay Ghemawat, Jeff Dean, and others.
34
35 #include <fcntl.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38
39 #ifndef _MSC_VER
40 #include <unistd.h>
41 #endif
42 #include <memory>
43 #include <vector>
44
45 #include <google/protobuf/stubs/stringprintf.h>
46 #include <google/protobuf/testing/file.h>
47 #include <google/protobuf/testing/file.h>
48 #include <google/protobuf/compiler/mock_code_generator.h>
49 #include <google/protobuf/compiler/subprocess.h>
50 #include <google/protobuf/compiler/code_generator.h>
51 #include <google/protobuf/compiler/command_line_interface.h>
52 #include <google/protobuf/test_util2.h>
53 #include <google/protobuf/unittest.pb.h>
54 #include <google/protobuf/io/io_win32.h>
55 #include <google/protobuf/io/printer.h>
56 #include <google/protobuf/io/zero_copy_stream.h>
57 #include <google/protobuf/descriptor.pb.h>
58 #include <google/protobuf/descriptor.h>
59 #include <google/protobuf/stubs/substitute.h>
60
61 #include <google/protobuf/testing/file.h>
62 #include <google/protobuf/testing/googletest.h>
63 #include <gtest/gtest.h>
64
65 #include <google/protobuf/stubs/strutil.h>
66
67 namespace google {
68 namespace protobuf {
69 namespace compiler {
70
71 #if defined(_WIN32)
72 // DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
73 // them like we do below.
74 using google::protobuf::io::win32::access;
75 using google::protobuf::io::win32::close;
76 using google::protobuf::io::win32::dup;
77 using google::protobuf::io::win32::dup2;
78 using google::protobuf::io::win32::open;
79 using google::protobuf::io::win32::write;
80 #endif
81
82 // Disable the whole test when we use tcmalloc for "draconian" heap checks, in
83 // which case tcmalloc will print warnings that fail the plugin tests.
84 #if !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
85
86
87 namespace {
88
FileExists(const std::string & path)89 bool FileExists(const std::string& path) {
90 return File::Exists(path);
91 }
92
93 class CommandLineInterfaceTest : public testing::Test {
94 protected:
95 virtual void SetUp();
96 virtual void TearDown();
97
98 // Runs the CommandLineInterface with the given command line. The
99 // command is automatically split on spaces, and the string "$tmpdir"
100 // is replaced with TestTempDir().
101 void Run(const std::string& command);
102 void RunWithArgs(std::vector<std::string> args);
103
104 // -----------------------------------------------------------------
105 // Methods to set up the test (called before Run()).
106
107 class NullCodeGenerator;
108
109 // Normally plugins are allowed for all tests. Call this to explicitly
110 // disable them.
DisallowPlugins()111 void DisallowPlugins() { disallow_plugins_ = true; }
112
113 // Create a temp file within temp_directory_ with the given name.
114 // The containing directory is also created if necessary.
115 void CreateTempFile(const std::string& name, const std::string& contents);
116
117 // Create a subdirectory within temp_directory_.
118 void CreateTempDir(const std::string& name);
119
120 #ifdef PROTOBUF_OPENSOURCE
121 // Change working directory to temp directory.
SwitchToTempDirectory()122 void SwitchToTempDirectory() {
123 File::ChangeWorkingDirectory(temp_directory_);
124 }
125 #else // !PROTOBUF_OPENSOURCE
126 // TODO(teboring): Figure out how to change and get working directory in
127 // google3.
128 #endif // !PROTOBUF_OPENSOURCE
129
130 // -----------------------------------------------------------------
131 // Methods to check the test results (called after Run()).
132
133 // Checks that no text was written to stderr during Run(), and Run()
134 // returned 0.
135 void ExpectNoErrors();
136
137 // Checks that Run() returned non-zero and the stderr output is exactly
138 // the text given. expected_test may contain references to "$tmpdir",
139 // which will be replaced by the temporary directory path.
140 void ExpectErrorText(const std::string& expected_text);
141
142 // Checks that Run() returned non-zero and the stderr contains the given
143 // substring.
144 void ExpectErrorSubstring(const std::string& expected_substring);
145
146 // Checks that the captured stdout is the same as the expected_text.
147 void ExpectCapturedStdout(const std::string& expected_text);
148
149 // Checks that Run() returned zero and the stdout contains the given
150 // substring.
151 void ExpectCapturedStdoutSubstringWithZeroReturnCode(
152 const std::string& expected_substring);
153
154 #if defined(_WIN32) && !defined(__CYGWIN__)
155 // Returns true if ExpectErrorSubstring(expected_substring) would pass, but
156 // does not fail otherwise.
157 bool HasAlternateErrorSubstring(const std::string& expected_substring);
158 #endif // _WIN32 && !__CYGWIN__
159
160 // Checks that MockCodeGenerator::Generate() was called in the given
161 // context (or the generator in test_plugin.cc, which produces the same
162 // output). That is, this tests if the generator with the given name
163 // was called with the given parameter and proto file and produced the
164 // given output file. This is checked by reading the output file and
165 // checking that it contains the content that MockCodeGenerator would
166 // generate given these inputs. message_name is the name of the first
167 // message that appeared in the proto file; this is just to make extra
168 // sure that the correct file was parsed.
169 void ExpectGenerated(const std::string& generator_name,
170 const std::string& parameter,
171 const std::string& proto_name,
172 const std::string& message_name);
173 void ExpectGenerated(const std::string& generator_name,
174 const std::string& parameter,
175 const std::string& proto_name,
176 const std::string& message_name,
177 const std::string& output_directory);
178 void ExpectGeneratedWithMultipleInputs(const std::string& generator_name,
179 const std::string& all_proto_names,
180 const std::string& proto_name,
181 const std::string& message_name);
182 void ExpectGeneratedWithInsertions(const std::string& generator_name,
183 const std::string& parameter,
184 const std::string& insertions,
185 const std::string& proto_name,
186 const std::string& message_name);
187 void CheckGeneratedAnnotations(const std::string& name,
188 const std::string& file);
189
190 #if defined(_WIN32)
191 void ExpectNullCodeGeneratorCalled(const std::string& parameter);
192 #endif // _WIN32
193
194
195 void ReadDescriptorSet(const std::string& filename,
196 FileDescriptorSet* descriptor_set);
197
198 void WriteDescriptorSet(const std::string& filename,
199 const FileDescriptorSet* descriptor_set);
200
201 void ExpectFileContent(const std::string& filename,
202 const std::string& content);
203
204 private:
205 // The object we are testing.
206 CommandLineInterface cli_;
207
208 // Was DisallowPlugins() called?
209 bool disallow_plugins_;
210
211 // We create a directory within TestTempDir() in order to add extra
212 // protection against accidentally deleting user files (since we recursively
213 // delete this directory during the test). This is the full path of that
214 // directory.
215 std::string temp_directory_;
216
217 // The result of Run().
218 int return_code_;
219
220 // The captured stderr output.
221 std::string error_text_;
222
223 // The captured stdout.
224 std::string captured_stdout_;
225
226 // Pointers which need to be deleted later.
227 std::vector<CodeGenerator*> mock_generators_to_delete_;
228
229 NullCodeGenerator* null_generator_;
230 };
231
232 class CommandLineInterfaceTest::NullCodeGenerator : public CodeGenerator {
233 public:
NullCodeGenerator()234 NullCodeGenerator() : called_(false) {}
~NullCodeGenerator()235 ~NullCodeGenerator() {}
236
237 mutable bool called_;
238 mutable std::string parameter_;
239
240 // implements CodeGenerator ----------------------------------------
Generate(const FileDescriptor * file,const std::string & parameter,GeneratorContext * context,std::string * error) const241 bool Generate(const FileDescriptor* file, const std::string& parameter,
242 GeneratorContext* context, std::string* error) const {
243 called_ = true;
244 parameter_ = parameter;
245 return true;
246 }
247 };
248
249 // ===================================================================
250
SetUp()251 void CommandLineInterfaceTest::SetUp() {
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
278 disallow_plugins_ = false;
279 }
280
TearDown()281 void CommandLineInterfaceTest::TearDown() {
282 // Delete the temp directory.
283 if (FileExists(temp_directory_)) {
284 File::DeleteRecursively(temp_directory_, NULL, NULL);
285 }
286
287 // Delete all the MockCodeGenerators.
288 for (int i = 0; i < mock_generators_to_delete_.size(); i++) {
289 delete mock_generators_to_delete_[i];
290 }
291 mock_generators_to_delete_.clear();
292 }
293
Run(const std::string & command)294 void CommandLineInterfaceTest::Run(const std::string& command) {
295 RunWithArgs(Split(command, " ", true));
296 }
297
RunWithArgs(std::vector<std::string> args)298 void CommandLineInterfaceTest::RunWithArgs(std::vector<std::string> args) {
299 if (!disallow_plugins_) {
300 cli_.AllowPlugins("prefix-");
301 std::string plugin_path;
302 #ifdef GOOGLE_PROTOBUF_TEST_PLUGIN_PATH
303 plugin_path = GOOGLE_PROTOBUF_TEST_PLUGIN_PATH;
304 #else
305 const char* possible_paths[] = {
306 // When building with shared libraries, libtool hides the real
307 // executable
308 // in .libs and puts a fake wrapper in the current directory.
309 // Unfortunately, due to an apparent bug on Cygwin/MinGW, if one program
310 // wrapped in this way (e.g. protobuf-tests.exe) tries to execute
311 // another
312 // program wrapped in this way (e.g. test_plugin.exe), the latter fails
313 // with error code 127 and no explanation message. Presumably the
314 // problem
315 // is that the wrapper for protobuf-tests.exe set some environment
316 // variables that confuse the wrapper for test_plugin.exe. Luckily, it
317 // turns out that if we simply invoke the wrapped test_plugin.exe
318 // directly, it works -- I guess the environment variables set by the
319 // protobuf-tests.exe wrapper happen to be correct for it too. So we do
320 // that.
321 ".libs/test_plugin.exe", // Win32 w/autotool (Cygwin / MinGW)
322 "test_plugin.exe", // Other Win32 (MSVC)
323 "test_plugin", // Unix
324 };
325 for (int i = 0; i < GOOGLE_ARRAYSIZE(possible_paths); i++) {
326 if (access(possible_paths[i], F_OK) == 0) {
327 plugin_path = possible_paths[i];
328 break;
329 }
330 }
331 #endif
332
333 if (plugin_path.empty()) {
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 std::unique_ptr<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
CreateTempFile(const std::string & name,const std::string & contents)366 void CommandLineInterfaceTest::CreateTempFile(const std::string& name,
367 const std::string& contents) {
368 // Create parent directory, if necessary.
369 std::string::size_type slash_pos = name.find_last_of('/');
370 if (slash_pos != std::string::npos) {
371 std::string dir = name.substr(0, slash_pos);
372 if (!FileExists(temp_directory_ + "/" + dir)) {
373 GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + dir,
374 0777));
375 }
376 }
377
378 // Write file.
379 std::string full_name = temp_directory_ + "/" + name;
380 GOOGLE_CHECK_OK(File::SetContents(
381 full_name, StringReplace(contents, "$tmpdir", temp_directory_, true),
382 true));
383 }
384
CreateTempDir(const std::string & name)385 void CommandLineInterfaceTest::CreateTempDir(const std::string& name) {
386 GOOGLE_CHECK_OK(File::RecursivelyCreateDir(temp_directory_ + "/" + name,
387 0777));
388 }
389
390 // -------------------------------------------------------------------
391
ExpectNoErrors()392 void CommandLineInterfaceTest::ExpectNoErrors() {
393 EXPECT_EQ(0, return_code_);
394 EXPECT_EQ("", error_text_);
395 }
396
ExpectErrorText(const std::string & expected_text)397 void CommandLineInterfaceTest::ExpectErrorText(
398 const std::string& expected_text) {
399 EXPECT_NE(0, return_code_);
400 EXPECT_EQ(StringReplace(expected_text, "$tmpdir", temp_directory_, true),
401 error_text_);
402 }
403
ExpectErrorSubstring(const std::string & expected_substring)404 void CommandLineInterfaceTest::ExpectErrorSubstring(
405 const std::string& expected_substring) {
406 EXPECT_NE(0, return_code_);
407 EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_);
408 }
409
410 #if defined(_WIN32) && !defined(__CYGWIN__)
HasAlternateErrorSubstring(const std::string & expected_substring)411 bool CommandLineInterfaceTest::HasAlternateErrorSubstring(
412 const std::string& expected_substring) {
413 EXPECT_NE(0, return_code_);
414 return error_text_.find(expected_substring) != std::string::npos;
415 }
416 #endif // _WIN32 && !__CYGWIN__
417
ExpectGenerated(const std::string & generator_name,const std::string & parameter,const std::string & proto_name,const std::string & message_name)418 void CommandLineInterfaceTest::ExpectGenerated(
419 const std::string& generator_name, const std::string& parameter,
420 const std::string& proto_name, const std::string& message_name) {
421 MockCodeGenerator::ExpectGenerated(generator_name, parameter, "", proto_name,
422 message_name, proto_name, temp_directory_);
423 }
424
ExpectGenerated(const std::string & generator_name,const std::string & parameter,const std::string & proto_name,const std::string & message_name,const std::string & output_directory)425 void CommandLineInterfaceTest::ExpectGenerated(
426 const std::string& generator_name, const std::string& parameter,
427 const std::string& proto_name, const std::string& message_name,
428 const std::string& output_directory) {
429 MockCodeGenerator::ExpectGenerated(generator_name, parameter, "", proto_name,
430 message_name, proto_name,
431 temp_directory_ + "/" + output_directory);
432 }
433
ExpectGeneratedWithMultipleInputs(const std::string & generator_name,const std::string & all_proto_names,const std::string & proto_name,const std::string & message_name)434 void CommandLineInterfaceTest::ExpectGeneratedWithMultipleInputs(
435 const std::string& generator_name, const std::string& all_proto_names,
436 const std::string& proto_name, const std::string& message_name) {
437 MockCodeGenerator::ExpectGenerated(generator_name, "", "", proto_name,
438 message_name, all_proto_names,
439 temp_directory_);
440 }
441
ExpectGeneratedWithInsertions(const std::string & generator_name,const std::string & parameter,const std::string & insertions,const std::string & proto_name,const std::string & message_name)442 void CommandLineInterfaceTest::ExpectGeneratedWithInsertions(
443 const std::string& generator_name, const std::string& parameter,
444 const std::string& insertions, const std::string& proto_name,
445 const std::string& message_name) {
446 MockCodeGenerator::ExpectGenerated(generator_name, parameter, insertions,
447 proto_name, message_name, proto_name,
448 temp_directory_);
449 }
450
CheckGeneratedAnnotations(const std::string & name,const std::string & file)451 void CommandLineInterfaceTest::CheckGeneratedAnnotations(
452 const std::string& name, const std::string& file) {
453 MockCodeGenerator::CheckGeneratedAnnotations(name, file, temp_directory_);
454 }
455
456 #if defined(_WIN32)
ExpectNullCodeGeneratorCalled(const std::string & parameter)457 void CommandLineInterfaceTest::ExpectNullCodeGeneratorCalled(
458 const std::string& parameter) {
459 EXPECT_TRUE(null_generator_->called_);
460 EXPECT_EQ(parameter, null_generator_->parameter_);
461 }
462 #endif // _WIN32
463
464
ReadDescriptorSet(const std::string & filename,FileDescriptorSet * descriptor_set)465 void CommandLineInterfaceTest::ReadDescriptorSet(
466 const std::string& filename, FileDescriptorSet* descriptor_set) {
467 std::string path = temp_directory_ + "/" + filename;
468 std::string file_contents;
469 GOOGLE_CHECK_OK(File::GetContents(path, &file_contents, true));
470
471 if (!descriptor_set->ParseFromString(file_contents)) {
472 FAIL() << "Could not parse file contents: " << path;
473 }
474 }
475
WriteDescriptorSet(const std::string & filename,const FileDescriptorSet * descriptor_set)476 void CommandLineInterfaceTest::WriteDescriptorSet(
477 const std::string& filename, const FileDescriptorSet* descriptor_set) {
478 std::string binary_proto;
479 GOOGLE_CHECK(descriptor_set->SerializeToString(&binary_proto));
480 CreateTempFile(filename, binary_proto);
481 }
482
ExpectCapturedStdout(const std::string & expected_text)483 void CommandLineInterfaceTest::ExpectCapturedStdout(
484 const std::string& expected_text) {
485 EXPECT_EQ(expected_text, captured_stdout_);
486 }
487
ExpectCapturedStdoutSubstringWithZeroReturnCode(const std::string & expected_substring)488 void CommandLineInterfaceTest::ExpectCapturedStdoutSubstringWithZeroReturnCode(
489 const std::string& expected_substring) {
490 EXPECT_EQ(0, return_code_);
491 EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring,
492 captured_stdout_);
493 }
494
ExpectFileContent(const std::string & filename,const std::string & content)495 void CommandLineInterfaceTest::ExpectFileContent(const std::string& filename,
496 const std::string& content) {
497 std::string path = temp_directory_ + "/" + filename;
498 std::string file_contents;
499 GOOGLE_CHECK_OK(File::GetContents(path, &file_contents, true));
500
501 EXPECT_EQ(StringReplace(content, "$tmpdir", temp_directory_, true),
502 file_contents);
503 }
504
505 // ===================================================================
506
TEST_F(CommandLineInterfaceTest,BasicOutput)507 TEST_F(CommandLineInterfaceTest, BasicOutput) {
508 // Test that the common case works.
509
510 CreateTempFile("foo.proto",
511 "syntax = \"proto2\";\n"
512 "message Foo {}\n");
513
514 Run("protocol_compiler --test_out=$tmpdir "
515 "--proto_path=$tmpdir foo.proto");
516
517 ExpectNoErrors();
518 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
519 }
520
TEST_F(CommandLineInterfaceTest,BasicOutput_DescriptorSetIn)521 TEST_F(CommandLineInterfaceTest, BasicOutput_DescriptorSetIn) {
522 // Test that the common case works.
523 FileDescriptorSet file_descriptor_set;
524 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
525 file_descriptor_proto->set_name("foo.proto");
526 file_descriptor_proto->add_message_type()->set_name("Foo");
527
528 WriteDescriptorSet("foo.bin", &file_descriptor_set);
529
530 Run("protocol_compiler --test_out=$tmpdir "
531 "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
532
533 ExpectNoErrors();
534 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
535 }
536
TEST_F(CommandLineInterfaceTest,BasicPlugin)537 TEST_F(CommandLineInterfaceTest, BasicPlugin) {
538 // Test that basic plugins work.
539
540 CreateTempFile("foo.proto",
541 "syntax = \"proto2\";\n"
542 "message Foo {}\n");
543
544 Run("protocol_compiler --plug_out=$tmpdir "
545 "--proto_path=$tmpdir foo.proto");
546
547 ExpectNoErrors();
548 ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
549 }
550
TEST_F(CommandLineInterfaceTest,BasicPlugin_DescriptorSetIn)551 TEST_F(CommandLineInterfaceTest, BasicPlugin_DescriptorSetIn) {
552 // Test that basic plugins work.
553
554 FileDescriptorSet file_descriptor_set;
555 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
556 file_descriptor_proto->set_name("foo.proto");
557 file_descriptor_proto->add_message_type()->set_name("Foo");
558
559 WriteDescriptorSet("foo.bin", &file_descriptor_set);
560
561 Run("protocol_compiler --plug_out=$tmpdir "
562 "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
563
564 ExpectNoErrors();
565 ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
566 }
567
TEST_F(CommandLineInterfaceTest,GeneratorAndPlugin)568 TEST_F(CommandLineInterfaceTest, GeneratorAndPlugin) {
569 // Invoke a generator and a plugin at the same time.
570
571 CreateTempFile("foo.proto",
572 "syntax = \"proto2\";\n"
573 "message Foo {}\n");
574
575 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
576 "--proto_path=$tmpdir foo.proto");
577
578 ExpectNoErrors();
579 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
580 ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
581 }
582
TEST_F(CommandLineInterfaceTest,GeneratorAndPlugin_DescriptorSetIn)583 TEST_F(CommandLineInterfaceTest, GeneratorAndPlugin_DescriptorSetIn) {
584 // Invoke a generator and a plugin at the same time.
585
586 FileDescriptorSet file_descriptor_set;
587 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
588 file_descriptor_proto->set_name("foo.proto");
589 file_descriptor_proto->add_message_type()->set_name("Foo");
590
591 WriteDescriptorSet("foo.bin", &file_descriptor_set);
592
593 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
594 "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
595
596 ExpectNoErrors();
597 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
598 ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
599 }
600
TEST_F(CommandLineInterfaceTest,MultipleInputs)601 TEST_F(CommandLineInterfaceTest, MultipleInputs) {
602 // Test parsing multiple input files.
603
604 CreateTempFile("foo.proto",
605 "syntax = \"proto2\";\n"
606 "message Foo {}\n");
607 CreateTempFile("bar.proto",
608 "syntax = \"proto2\";\n"
609 "message Bar {}\n");
610
611 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
612 "--proto_path=$tmpdir foo.proto bar.proto");
613
614 ExpectNoErrors();
615 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
616 "foo.proto", "Foo");
617 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
618 "bar.proto", "Bar");
619 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
620 "foo.proto", "Foo");
621 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
622 "bar.proto", "Bar");
623 }
624
TEST_F(CommandLineInterfaceTest,MultipleInputs_DescriptorSetIn)625 TEST_F(CommandLineInterfaceTest, MultipleInputs_DescriptorSetIn) {
626 // Test parsing multiple input files.
627 FileDescriptorSet file_descriptor_set;
628
629 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
630 file_descriptor_proto->set_name("foo.proto");
631 file_descriptor_proto->add_message_type()->set_name("Foo");
632
633 file_descriptor_proto = file_descriptor_set.add_file();
634 file_descriptor_proto->set_name("bar.proto");
635 file_descriptor_proto->add_message_type()->set_name("Bar");
636
637 WriteDescriptorSet("foo.bin", &file_descriptor_set);
638
639 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
640 "--descriptor_set_in=$tmpdir/foo.bin foo.proto bar.proto");
641
642 ExpectNoErrors();
643 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
644 "foo.proto", "Foo");
645 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
646 "bar.proto", "Bar");
647 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
648 "foo.proto", "Foo");
649 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
650 "bar.proto", "Bar");
651 }
652
TEST_F(CommandLineInterfaceTest,MultipleInputsWithImport)653 TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport) {
654 // Test parsing multiple input files with an import of a separate file.
655
656 CreateTempFile("foo.proto",
657 "syntax = \"proto2\";\n"
658 "message Foo {}\n");
659 CreateTempFile("bar.proto",
660 "syntax = \"proto2\";\n"
661 "import \"baz.proto\";\n"
662 "message Bar {\n"
663 " optional Baz a = 1;\n"
664 "}\n");
665 CreateTempFile("baz.proto",
666 "syntax = \"proto2\";\n"
667 "message Baz {}\n");
668
669 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
670 "--proto_path=$tmpdir foo.proto bar.proto");
671
672 ExpectNoErrors();
673 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
674 "foo.proto", "Foo");
675 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
676 "bar.proto", "Bar");
677 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
678 "foo.proto", "Foo");
679 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
680 "bar.proto", "Bar");
681 }
682
TEST_F(CommandLineInterfaceTest,MultipleInputsWithImport_DescriptorSetIn)683 TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport_DescriptorSetIn) {
684 // Test parsing multiple input files with an import of a separate file.
685 FileDescriptorSet file_descriptor_set;
686
687 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
688 file_descriptor_proto->set_name("foo.proto");
689 file_descriptor_proto->add_message_type()->set_name("Foo");
690
691 file_descriptor_proto = file_descriptor_set.add_file();
692 file_descriptor_proto->set_name("bar.proto");
693 file_descriptor_proto->add_dependency("baz.proto");
694 DescriptorProto* message = file_descriptor_proto->add_message_type();
695 message->set_name("Bar");
696 FieldDescriptorProto* field = message->add_field();
697 field->set_type_name("Baz");
698 field->set_name("a");
699 field->set_number(1);
700
701 WriteDescriptorSet("foo_and_bar.bin", &file_descriptor_set);
702
703 file_descriptor_set.clear_file();
704 file_descriptor_proto = file_descriptor_set.add_file();
705 file_descriptor_proto->set_name("baz.proto");
706 file_descriptor_proto->add_message_type()->set_name("Baz");
707
708 file_descriptor_proto = file_descriptor_set.add_file();
709 file_descriptor_proto->set_name("bat.proto");
710 file_descriptor_proto->add_dependency("baz.proto");
711 message = file_descriptor_proto->add_message_type();
712 message->set_name("Bat");
713 field = message->add_field();
714 field->set_type_name("Baz");
715 field->set_name("a");
716 field->set_number(1);
717
718 WriteDescriptorSet("baz_and_bat.bin", &file_descriptor_set);
719 Run(strings::Substitute(
720 "protocol_compiler --test_out=$$tmpdir --plug_out=$$tmpdir "
721 "--descriptor_set_in=$0 foo.proto bar.proto",
722 std::string("$tmpdir/foo_and_bar.bin") +
723 CommandLineInterface::kPathSeparator + "$tmpdir/baz_and_bat.bin"));
724
725 ExpectNoErrors();
726 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
727 "foo.proto", "Foo");
728 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
729 "bar.proto", "Bar");
730 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
731 "foo.proto", "Foo");
732 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
733 "bar.proto", "Bar");
734
735 Run(strings::Substitute(
736 "protocol_compiler --test_out=$$tmpdir --plug_out=$$tmpdir "
737 "--descriptor_set_in=$0 baz.proto bat.proto",
738 std::string("$tmpdir/foo_and_bar.bin") +
739 CommandLineInterface::kPathSeparator + "$tmpdir/baz_and_bat.bin"));
740
741 ExpectNoErrors();
742 ExpectGeneratedWithMultipleInputs("test_generator", "baz.proto,bat.proto",
743 "baz.proto", "Baz");
744 ExpectGeneratedWithMultipleInputs("test_generator", "baz.proto,bat.proto",
745 "bat.proto", "Bat");
746 ExpectGeneratedWithMultipleInputs("test_plugin", "baz.proto,bat.proto",
747 "baz.proto", "Baz");
748 ExpectGeneratedWithMultipleInputs("test_plugin", "baz.proto,bat.proto",
749 "bat.proto", "Bat");
750 }
751
TEST_F(CommandLineInterfaceTest,MultipleInputsWithImport_DescriptorSetIn_DuplicateFileDescriptor)752 TEST_F(CommandLineInterfaceTest,
753 MultipleInputsWithImport_DescriptorSetIn_DuplicateFileDescriptor) {
754 // Test parsing multiple input files with an import of a separate file.
755 FileDescriptorSet file_descriptor_set;
756
757 FileDescriptorProto foo_file_descriptor_proto;
758 foo_file_descriptor_proto.set_name("foo.proto");
759 foo_file_descriptor_proto.add_message_type()->set_name("Foo");
760
761 file_descriptor_set.add_file()->CopyFrom(foo_file_descriptor_proto);
762
763 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
764 file_descriptor_proto->set_name("bar.proto");
765 file_descriptor_proto->add_dependency("baz.proto");
766 file_descriptor_proto->add_dependency("foo.proto");
767 DescriptorProto* message = file_descriptor_proto->add_message_type();
768 message->set_name("Bar");
769 FieldDescriptorProto* field = message->add_field();
770 field->set_type_name("Baz");
771 field->set_name("a");
772 field->set_number(1);
773 field = message->add_field();
774 field->set_type_name("Foo");
775 field->set_name("f");
776 field->set_number(2);
777 WriteDescriptorSet("foo_and_bar.bin", &file_descriptor_set);
778
779 file_descriptor_set.clear_file();
780 file_descriptor_set.add_file()->CopyFrom(foo_file_descriptor_proto);
781
782 file_descriptor_proto = file_descriptor_set.add_file();
783 file_descriptor_proto->set_name("baz.proto");
784 file_descriptor_proto->add_dependency("foo.proto");
785 message = file_descriptor_proto->add_message_type();
786 message->set_name("Baz");
787 field = message->add_field();
788 field->set_type_name("Foo");
789 field->set_name("f");
790 field->set_number(1);
791 WriteDescriptorSet("foo_and_baz.bin", &file_descriptor_set);
792
793 Run(strings::Substitute(
794 "protocol_compiler --test_out=$$tmpdir --plug_out=$$tmpdir "
795 "--descriptor_set_in=$0 bar.proto",
796 std::string("$tmpdir/foo_and_bar.bin") +
797 CommandLineInterface::kPathSeparator + "$tmpdir/foo_and_baz.bin"));
798
799 ExpectNoErrors();
800 ExpectGenerated("test_generator", "", "bar.proto", "Bar");
801 ExpectGenerated("test_plugin", "", "bar.proto", "Bar");
802 }
803
TEST_F(CommandLineInterfaceTest,MultipleInputsWithImport_DescriptorSetIn_MissingImport)804 TEST_F(CommandLineInterfaceTest,
805 MultipleInputsWithImport_DescriptorSetIn_MissingImport) {
806 // Test parsing multiple input files with an import of a separate file.
807 FileDescriptorSet file_descriptor_set;
808
809 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
810 file_descriptor_proto->set_name("foo.proto");
811 file_descriptor_proto->add_message_type()->set_name("Foo");
812
813 file_descriptor_proto = file_descriptor_set.add_file();
814 file_descriptor_proto->set_name("bar.proto");
815 file_descriptor_proto->add_dependency("baz.proto");
816 DescriptorProto* message = file_descriptor_proto->add_message_type();
817 message->set_name("Bar");
818 FieldDescriptorProto* field = message->add_field();
819 field->set_type_name("Baz");
820 field->set_name("a");
821 field->set_number(1);
822
823 WriteDescriptorSet("foo_and_bar.bin", &file_descriptor_set);
824
825 file_descriptor_set.clear_file();
826 file_descriptor_proto = file_descriptor_set.add_file();
827 file_descriptor_proto->set_name("baz.proto");
828 file_descriptor_proto->add_message_type()->set_name("Baz");
829
830 WriteDescriptorSet("baz.bin", &file_descriptor_set);
831 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
832 "--descriptor_set_in=$tmpdir/foo_and_bar.bin "
833 "foo.proto bar.proto");
834 ExpectErrorSubstring(
835 "bar.proto: Import \"baz.proto\" was not found or had errors.");
836 ExpectErrorSubstring("bar.proto: \"Baz\" is not defined.");
837 }
838
TEST_F(CommandLineInterfaceTest,CreateDirectory)839 TEST_F(CommandLineInterfaceTest, CreateDirectory) {
840 // Test that when we output to a sub-directory, it is created.
841
842 CreateTempFile("bar/baz/foo.proto",
843 "syntax = \"proto2\";\n"
844 "message Foo {}\n");
845 CreateTempDir("out");
846 CreateTempDir("plugout");
847
848 Run("protocol_compiler --test_out=$tmpdir/out --plug_out=$tmpdir/plugout "
849 "--proto_path=$tmpdir bar/baz/foo.proto");
850
851 ExpectNoErrors();
852 ExpectGenerated("test_generator", "", "bar/baz/foo.proto", "Foo", "out");
853 ExpectGenerated("test_plugin", "", "bar/baz/foo.proto", "Foo", "plugout");
854 }
855
TEST_F(CommandLineInterfaceTest,GeneratorParameters)856 TEST_F(CommandLineInterfaceTest, GeneratorParameters) {
857 // Test that generator parameters are correctly parsed from the command line.
858
859 CreateTempFile("foo.proto",
860 "syntax = \"proto2\";\n"
861 "message Foo {}\n");
862
863 Run("protocol_compiler --test_out=TestParameter:$tmpdir "
864 "--plug_out=TestPluginParameter:$tmpdir "
865 "--proto_path=$tmpdir foo.proto");
866
867 ExpectNoErrors();
868 ExpectGenerated("test_generator", "TestParameter", "foo.proto", "Foo");
869 ExpectGenerated("test_plugin", "TestPluginParameter", "foo.proto", "Foo");
870 }
871
TEST_F(CommandLineInterfaceTest,ExtraGeneratorParameters)872 TEST_F(CommandLineInterfaceTest, ExtraGeneratorParameters) {
873 // Test that generator parameters specified with the option flag are
874 // correctly passed to the code generator.
875
876 CreateTempFile("foo.proto",
877 "syntax = \"proto2\";\n"
878 "message Foo {}\n");
879 // Create the "a" and "b" sub-directories.
880 CreateTempDir("a");
881 CreateTempDir("b");
882
883 Run("protocol_compiler "
884 "--test_opt=foo1 "
885 "--test_out=bar:$tmpdir/a "
886 "--test_opt=foo2 "
887 "--test_out=baz:$tmpdir/b "
888 "--test_opt=foo3 "
889 "--proto_path=$tmpdir foo.proto");
890
891 ExpectNoErrors();
892 ExpectGenerated("test_generator", "bar,foo1,foo2,foo3", "foo.proto", "Foo",
893 "a");
894 ExpectGenerated("test_generator", "baz,foo1,foo2,foo3", "foo.proto", "Foo",
895 "b");
896 }
897
TEST_F(CommandLineInterfaceTest,ExtraPluginParameters)898 TEST_F(CommandLineInterfaceTest, ExtraPluginParameters) {
899 // Test that generator parameters specified with the option flag are
900 // correctly passed to the code generator.
901
902 CreateTempFile("foo.proto",
903 "syntax = \"proto2\";\n"
904 "message Foo {}\n");
905 // Create the "a" and "b" sub-directories.
906 CreateTempDir("a");
907 CreateTempDir("b");
908
909 Run("protocol_compiler "
910 "--plug_opt=foo1 "
911 "--plug_out=bar:$tmpdir/a "
912 "--plug_opt=foo2 "
913 "--plug_out=baz:$tmpdir/b "
914 "--plug_opt=foo3 "
915 "--proto_path=$tmpdir foo.proto");
916
917 ExpectNoErrors();
918 ExpectGenerated("test_plugin", "bar,foo1,foo2,foo3", "foo.proto", "Foo", "a");
919 ExpectGenerated("test_plugin", "baz,foo1,foo2,foo3", "foo.proto", "Foo", "b");
920 }
921
TEST_F(CommandLineInterfaceTest,UnrecognizedExtraParameters)922 TEST_F(CommandLineInterfaceTest, UnrecognizedExtraParameters) {
923 CreateTempFile("foo.proto",
924 "syntax = \"proto2\";\n"
925 "message Foo {}\n");
926
927 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
928 "--unknown_plug_a_opt=Foo "
929 "--unknown_plug_b_opt=Bar "
930 "--proto_path=$tmpdir foo.proto");
931
932 ExpectErrorSubstring("Unknown flag: --unknown_plug_a_opt");
933 ExpectErrorSubstring("Unknown flag: --unknown_plug_b_opt");
934 }
935
TEST_F(CommandLineInterfaceTest,ExtraPluginParametersForOutParameters)936 TEST_F(CommandLineInterfaceTest, ExtraPluginParametersForOutParameters) {
937 // This doesn't rely on the plugin having been registred and instead that
938 // the existence of --[name]_out is enough to make the --[name]_opt valid.
939 // However, running out of process plugins found via the search path (i.e. -
940 // not pre registered with --plugin) isn't support in this test suite, so we
941 // list the options pre/post the _out directive, and then include _opt that
942 // will be unknown, and confirm the failure output is about the expected
943 // unknown directive, which means the other were accepted.
944 // NOTE: UnrecognizedExtraParameters confirms that if two unknown _opt
945 // directives appear, they both are reported.
946
947 CreateTempFile("foo.proto",
948 "syntax = \"proto2\";\n"
949 "message Foo {}\n");
950
951 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
952 "--xyz_opt=foo=bar --xyz_out=$tmpdir "
953 "--abc_out=$tmpdir --abc_opt=foo=bar "
954 "--unknown_plug_opt=Foo "
955 "--proto_path=$tmpdir foo.proto");
956
957 ExpectErrorText("Unknown flag: --unknown_plug_opt\n");
958 }
959
TEST_F(CommandLineInterfaceTest,Insert)960 TEST_F(CommandLineInterfaceTest, Insert) {
961 // Test running a generator that inserts code into another's output.
962
963 CreateTempFile("foo.proto",
964 "syntax = \"proto2\";\n"
965 "message Foo {}\n");
966
967 Run("protocol_compiler "
968 "--test_out=TestParameter:$tmpdir "
969 "--plug_out=TestPluginParameter:$tmpdir "
970 "--test_out=insert=test_generator,test_plugin:$tmpdir "
971 "--plug_out=insert=test_generator,test_plugin:$tmpdir "
972 "--proto_path=$tmpdir foo.proto");
973
974 ExpectNoErrors();
975 ExpectGeneratedWithInsertions("test_generator", "TestParameter",
976 "test_generator,test_plugin", "foo.proto",
977 "Foo");
978 ExpectGeneratedWithInsertions("test_plugin", "TestPluginParameter",
979 "test_generator,test_plugin", "foo.proto",
980 "Foo");
981 }
982
TEST_F(CommandLineInterfaceTest,InsertWithAnnotationFixup)983 TEST_F(CommandLineInterfaceTest, InsertWithAnnotationFixup) {
984 // Check that annotation spans are updated after insertions.
985
986 CreateTempFile("foo.proto",
987 "syntax = \"proto2\";\n"
988 "message MockCodeGenerator_Annotate {}\n");
989
990 Run("protocol_compiler "
991 "--test_out=TestParameter:$tmpdir "
992 "--plug_out=TestPluginParameter:$tmpdir "
993 "--test_out=insert=test_generator,test_plugin:$tmpdir "
994 "--plug_out=insert=test_generator,test_plugin:$tmpdir "
995 "--proto_path=$tmpdir foo.proto");
996
997 ExpectNoErrors();
998 CheckGeneratedAnnotations("test_generator", "foo.proto");
999 CheckGeneratedAnnotations("test_plugin", "foo.proto");
1000 }
1001
1002 #if defined(_WIN32)
1003
TEST_F(CommandLineInterfaceTest,WindowsOutputPath)1004 TEST_F(CommandLineInterfaceTest, WindowsOutputPath) {
1005 // Test that the output path can be a Windows-style path.
1006
1007 CreateTempFile("foo.proto", "syntax = \"proto2\";\n");
1008
1009 Run("protocol_compiler --null_out=C:\\ "
1010 "--proto_path=$tmpdir foo.proto");
1011
1012 ExpectNoErrors();
1013 ExpectNullCodeGeneratorCalled("");
1014 }
1015
TEST_F(CommandLineInterfaceTest,WindowsOutputPathAndParameter)1016 TEST_F(CommandLineInterfaceTest, WindowsOutputPathAndParameter) {
1017 // Test that we can have a windows-style output path and a parameter.
1018
1019 CreateTempFile("foo.proto", "syntax = \"proto2\";\n");
1020
1021 Run("protocol_compiler --null_out=bar:C:\\ "
1022 "--proto_path=$tmpdir foo.proto");
1023
1024 ExpectNoErrors();
1025 ExpectNullCodeGeneratorCalled("bar");
1026 }
1027
TEST_F(CommandLineInterfaceTest,TrailingBackslash)1028 TEST_F(CommandLineInterfaceTest, TrailingBackslash) {
1029 // Test that the directories can end in backslashes. Some users claim this
1030 // doesn't work on their system.
1031
1032 CreateTempFile("foo.proto",
1033 "syntax = \"proto2\";\n"
1034 "message Foo {}\n");
1035
1036 Run("protocol_compiler --test_out=$tmpdir\\ "
1037 "--proto_path=$tmpdir\\ foo.proto");
1038
1039 ExpectNoErrors();
1040 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1041 }
1042
TEST_F(CommandLineInterfaceTest,Win32ErrorMessage)1043 TEST_F(CommandLineInterfaceTest, Win32ErrorMessage) {
1044 EXPECT_EQ("The system cannot find the file specified.\r\n",
1045 Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND));
1046 }
1047
1048 #endif // defined(_WIN32) || defined(__CYGWIN__)
1049
TEST_F(CommandLineInterfaceTest,PathLookup)1050 TEST_F(CommandLineInterfaceTest, PathLookup) {
1051 // Test that specifying multiple directories in the proto search path works.
1052
1053 CreateTempFile("b/bar.proto",
1054 "syntax = \"proto2\";\n"
1055 "message Bar {}\n");
1056 CreateTempFile("a/foo.proto",
1057 "syntax = \"proto2\";\n"
1058 "import \"bar.proto\";\n"
1059 "message Foo {\n"
1060 " optional Bar a = 1;\n"
1061 "}\n");
1062 CreateTempFile("b/foo.proto", "this should not be parsed\n");
1063
1064 Run("protocol_compiler --test_out=$tmpdir "
1065 "--proto_path=$tmpdir/a --proto_path=$tmpdir/b foo.proto");
1066
1067 ExpectNoErrors();
1068 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1069 }
1070
TEST_F(CommandLineInterfaceTest,ColonDelimitedPath)1071 TEST_F(CommandLineInterfaceTest, ColonDelimitedPath) {
1072 // Same as PathLookup, but we provide the proto_path in a single flag.
1073
1074 CreateTempFile("b/bar.proto",
1075 "syntax = \"proto2\";\n"
1076 "message Bar {}\n");
1077 CreateTempFile("a/foo.proto",
1078 "syntax = \"proto2\";\n"
1079 "import \"bar.proto\";\n"
1080 "message Foo {\n"
1081 " optional Bar a = 1;\n"
1082 "}\n");
1083 CreateTempFile("b/foo.proto", "this should not be parsed\n");
1084
1085 Run(strings::Substitute(
1086 "protocol_compiler --test_out=$$tmpdir --proto_path=$0 foo.proto",
1087 std::string("$tmpdir/a") + CommandLineInterface::kPathSeparator +
1088 "$tmpdir/b"));
1089
1090 ExpectNoErrors();
1091 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1092 }
1093
TEST_F(CommandLineInterfaceTest,NonRootMapping)1094 TEST_F(CommandLineInterfaceTest, NonRootMapping) {
1095 // Test setting up a search path mapping a directory to a non-root location.
1096
1097 CreateTempFile("foo.proto",
1098 "syntax = \"proto2\";\n"
1099 "message Foo {}\n");
1100
1101 Run("protocol_compiler --test_out=$tmpdir "
1102 "--proto_path=bar=$tmpdir bar/foo.proto");
1103
1104 ExpectNoErrors();
1105 ExpectGenerated("test_generator", "", "bar/foo.proto", "Foo");
1106 }
1107
TEST_F(CommandLineInterfaceTest,PathWithEqualsSign)1108 TEST_F(CommandLineInterfaceTest, PathWithEqualsSign) {
1109 // Test setting up a search path which happens to have '=' in it.
1110
1111 CreateTempDir("with=sign");
1112 CreateTempFile("with=sign/foo.proto",
1113 "syntax = \"proto2\";\n"
1114 "message Foo {}\n");
1115
1116 Run("protocol_compiler --test_out=$tmpdir "
1117 "--proto_path=$tmpdir/with=sign foo.proto");
1118
1119 ExpectNoErrors();
1120 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1121 }
1122
TEST_F(CommandLineInterfaceTest,MultipleGenerators)1123 TEST_F(CommandLineInterfaceTest, MultipleGenerators) {
1124 // Test that we can have multiple generators and use both in one invocation,
1125 // each with a different output directory.
1126
1127 CreateTempFile("foo.proto",
1128 "syntax = \"proto2\";\n"
1129 "message Foo {}\n");
1130 // Create the "a" and "b" sub-directories.
1131 CreateTempDir("a");
1132 CreateTempDir("b");
1133
1134 Run("protocol_compiler "
1135 "--test_out=$tmpdir/a "
1136 "--alt_out=$tmpdir/b "
1137 "--proto_path=$tmpdir foo.proto");
1138
1139 ExpectNoErrors();
1140 ExpectGenerated("test_generator", "", "foo.proto", "Foo", "a");
1141 ExpectGenerated("alt_generator", "", "foo.proto", "Foo", "b");
1142 }
1143
TEST_F(CommandLineInterfaceTest,DisallowServicesNoServices)1144 TEST_F(CommandLineInterfaceTest, DisallowServicesNoServices) {
1145 // Test that --disallow_services doesn't cause a problem when there are no
1146 // services.
1147
1148 CreateTempFile("foo.proto",
1149 "syntax = \"proto2\";\n"
1150 "message Foo {}\n");
1151
1152 Run("protocol_compiler --disallow_services --test_out=$tmpdir "
1153 "--proto_path=$tmpdir foo.proto");
1154
1155 ExpectNoErrors();
1156 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1157 }
1158
TEST_F(CommandLineInterfaceTest,DisallowServicesHasService)1159 TEST_F(CommandLineInterfaceTest, DisallowServicesHasService) {
1160 // Test that --disallow_services produces an error when there are services.
1161
1162 CreateTempFile("foo.proto",
1163 "syntax = \"proto2\";\n"
1164 "message Foo {}\n"
1165 "service Bar {}\n");
1166
1167 Run("protocol_compiler --disallow_services --test_out=$tmpdir "
1168 "--proto_path=$tmpdir foo.proto");
1169
1170 ExpectErrorSubstring("foo.proto: This file contains services");
1171 }
1172
TEST_F(CommandLineInterfaceTest,AllowServicesHasService)1173 TEST_F(CommandLineInterfaceTest, AllowServicesHasService) {
1174 // Test that services work fine as long as --disallow_services is not used.
1175
1176 CreateTempFile("foo.proto",
1177 "syntax = \"proto2\";\n"
1178 "message Foo {}\n"
1179 "service Bar {}\n");
1180
1181 Run("protocol_compiler --test_out=$tmpdir "
1182 "--proto_path=$tmpdir foo.proto");
1183
1184 ExpectNoErrors();
1185 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1186 }
1187
TEST_F(CommandLineInterfaceTest,DirectDependencies_Missing_EmptyList)1188 TEST_F(CommandLineInterfaceTest, DirectDependencies_Missing_EmptyList) {
1189 CreateTempFile("foo.proto",
1190 "syntax = \"proto2\";\n"
1191 "import \"bar.proto\";\n"
1192 "message Foo { optional Bar bar = 1; }");
1193 CreateTempFile("bar.proto",
1194 "syntax = \"proto2\";\n"
1195 "message Bar { optional string text = 1; }");
1196
1197 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
1198 "--direct_dependencies= foo.proto");
1199
1200 ExpectErrorText(
1201 "foo.proto: File is imported but not declared in --direct_dependencies: "
1202 "bar.proto\n");
1203 }
1204
TEST_F(CommandLineInterfaceTest,DirectDependencies_Missing)1205 TEST_F(CommandLineInterfaceTest, DirectDependencies_Missing) {
1206 CreateTempFile("foo.proto",
1207 "syntax = \"proto2\";\n"
1208 "import \"bar.proto\";\n"
1209 "import \"bla.proto\";\n"
1210 "message Foo { optional Bar bar = 1; optional Bla bla = 2; }");
1211 CreateTempFile("bar.proto",
1212 "syntax = \"proto2\";\n"
1213 "message Bar { optional string text = 1; }");
1214 CreateTempFile("bla.proto",
1215 "syntax = \"proto2\";\n"
1216 "message Bla { optional int64 number = 1; }");
1217
1218 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
1219 "--direct_dependencies=bla.proto foo.proto");
1220
1221 ExpectErrorText(
1222 "foo.proto: File is imported but not declared in --direct_dependencies: "
1223 "bar.proto\n");
1224 }
1225
TEST_F(CommandLineInterfaceTest,DirectDependencies_NoViolation)1226 TEST_F(CommandLineInterfaceTest, DirectDependencies_NoViolation) {
1227 CreateTempFile("foo.proto",
1228 "syntax = \"proto2\";\n"
1229 "import \"bar.proto\";\n"
1230 "message Foo { optional Bar bar = 1; }");
1231 CreateTempFile("bar.proto",
1232 "syntax = \"proto2\";\n"
1233 "message Bar { optional string text = 1; }");
1234
1235 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
1236 "--direct_dependencies=bar.proto foo.proto");
1237
1238 ExpectNoErrors();
1239 }
1240
TEST_F(CommandLineInterfaceTest,DirectDependencies_NoViolation_MultiImports)1241 TEST_F(CommandLineInterfaceTest, DirectDependencies_NoViolation_MultiImports) {
1242 CreateTempFile("foo.proto",
1243 "syntax = \"proto2\";\n"
1244 "import \"bar.proto\";\n"
1245 "import \"bla.proto\";\n"
1246 "message Foo { optional Bar bar = 1; optional Bla bla = 2; }");
1247 CreateTempFile("bar.proto",
1248 "syntax = \"proto2\";\n"
1249 "message Bar { optional string text = 1; }");
1250 CreateTempFile("bla.proto",
1251 "syntax = \"proto2\";\n"
1252 "message Bla { optional int64 number = 1; }");
1253
1254 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
1255 "--direct_dependencies=bar.proto:bla.proto foo.proto");
1256
1257 ExpectNoErrors();
1258 }
1259
TEST_F(CommandLineInterfaceTest,DirectDependencies_ProvidedMultipleTimes)1260 TEST_F(CommandLineInterfaceTest, DirectDependencies_ProvidedMultipleTimes) {
1261 CreateTempFile("foo.proto", "syntax = \"proto2\";\n");
1262
1263 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
1264 "--direct_dependencies=bar.proto --direct_dependencies=bla.proto "
1265 "foo.proto");
1266
1267 ExpectErrorText(
1268 "--direct_dependencies may only be passed once. To specify multiple "
1269 "direct dependencies, pass them all as a single parameter separated by "
1270 "':'.\n");
1271 }
1272
TEST_F(CommandLineInterfaceTest,DirectDependencies_CustomErrorMessage)1273 TEST_F(CommandLineInterfaceTest, DirectDependencies_CustomErrorMessage) {
1274 CreateTempFile("foo.proto",
1275 "syntax = \"proto2\";\n"
1276 "import \"bar.proto\";\n"
1277 "message Foo { optional Bar bar = 1; }");
1278 CreateTempFile("bar.proto",
1279 "syntax = \"proto2\";\n"
1280 "message Bar { optional string text = 1; }");
1281
1282 std::vector<std::string> commands;
1283 commands.push_back("protocol_compiler");
1284 commands.push_back("--test_out=$tmpdir");
1285 commands.push_back("--proto_path=$tmpdir");
1286 commands.push_back("--direct_dependencies=");
1287 commands.push_back("--direct_dependencies_violation_msg=Bla \"%s\" Bla");
1288 commands.push_back("foo.proto");
1289 RunWithArgs(commands);
1290
1291 ExpectErrorText("foo.proto: Bla \"bar.proto\" Bla\n");
1292 }
1293
TEST_F(CommandLineInterfaceTest,CwdRelativeInputs)1294 TEST_F(CommandLineInterfaceTest, CwdRelativeInputs) {
1295 // Test that we can accept working-directory-relative input files.
1296
1297 CreateTempFile("foo.proto",
1298 "syntax = \"proto2\";\n"
1299 "message Foo {}\n");
1300
1301 Run("protocol_compiler --test_out=$tmpdir "
1302 "--proto_path=$tmpdir $tmpdir/foo.proto");
1303
1304 ExpectNoErrors();
1305 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1306 }
1307
TEST_F(CommandLineInterfaceTest,WriteDescriptorSet)1308 TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) {
1309 CreateTempFile("foo.proto",
1310 "syntax = \"proto2\";\n"
1311 "message Foo {}\n");
1312 CreateTempFile("bar.proto",
1313 "syntax = \"proto2\";\n"
1314 "import \"foo.proto\";\n"
1315 "message Bar {\n"
1316 " optional Foo foo = 1;\n"
1317 "}\n");
1318
1319 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
1320 "--proto_path=$tmpdir bar.proto");
1321
1322 ExpectNoErrors();
1323
1324 FileDescriptorSet descriptor_set;
1325 ReadDescriptorSet("descriptor_set", &descriptor_set);
1326 if (HasFatalFailure()) return;
1327 EXPECT_EQ(1, descriptor_set.file_size());
1328 EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
1329 // Descriptor set should not have source code info.
1330 EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
1331 // Descriptor set should have json_name.
1332 EXPECT_EQ("Bar", descriptor_set.file(0).message_type(0).name());
1333 EXPECT_EQ("foo", descriptor_set.file(0).message_type(0).field(0).name());
1334 EXPECT_TRUE(descriptor_set.file(0).message_type(0).field(0).has_json_name());
1335 }
1336
TEST_F(CommandLineInterfaceTest,WriteDescriptorSetWithDuplicates)1337 TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithDuplicates) {
1338 CreateTempFile("foo.proto",
1339 "syntax = \"proto2\";\n"
1340 "message Foo {}\n");
1341 CreateTempFile("bar.proto",
1342 "syntax = \"proto2\";\n"
1343 "import \"foo.proto\";\n"
1344 "message Bar {\n"
1345 " optional Foo foo = 1;\n"
1346 "}\n");
1347 CreateTempFile("baz.proto",
1348 "syntax = \"proto2\";\n"
1349 "import \"foo.proto\";\n"
1350 "message Baz {\n"
1351 " optional Foo foo = 1;\n"
1352 "}\n");
1353
1354 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
1355 "--proto_path=$tmpdir bar.proto foo.proto bar.proto baz.proto");
1356
1357 ExpectNoErrors();
1358
1359 FileDescriptorSet descriptor_set;
1360 ReadDescriptorSet("descriptor_set", &descriptor_set);
1361 if (HasFatalFailure()) return;
1362 EXPECT_EQ(3, descriptor_set.file_size());
1363 // foo should come first since the output is in dependency order.
1364 // since bar and baz are unordered, they should be in command line order.
1365 EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
1366 EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
1367 EXPECT_EQ("baz.proto", descriptor_set.file(2).name());
1368 // Descriptor set should not have source code info.
1369 EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
1370 // Descriptor set should have json_name.
1371 EXPECT_EQ("Bar", descriptor_set.file(1).message_type(0).name());
1372 EXPECT_EQ("foo", descriptor_set.file(1).message_type(0).field(0).name());
1373 EXPECT_TRUE(descriptor_set.file(1).message_type(0).field(0).has_json_name());
1374 }
1375
TEST_F(CommandLineInterfaceTest,WriteDescriptorSetWithSourceInfo)1376 TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) {
1377 CreateTempFile("foo.proto",
1378 "syntax = \"proto2\";\n"
1379 "message Foo {}\n");
1380 CreateTempFile("bar.proto",
1381 "syntax = \"proto2\";\n"
1382 "import \"foo.proto\";\n"
1383 "message Bar {\n"
1384 " optional Foo foo = 1;\n"
1385 "}\n");
1386
1387 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
1388 "--include_source_info --proto_path=$tmpdir bar.proto");
1389
1390 ExpectNoErrors();
1391
1392 FileDescriptorSet descriptor_set;
1393 ReadDescriptorSet("descriptor_set", &descriptor_set);
1394 if (HasFatalFailure()) return;
1395 EXPECT_EQ(1, descriptor_set.file_size());
1396 EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
1397 // Source code info included.
1398 EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
1399 }
1400
TEST_F(CommandLineInterfaceTest,WriteTransitiveDescriptorSet)1401 TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) {
1402 CreateTempFile("foo.proto",
1403 "syntax = \"proto2\";\n"
1404 "message Foo {}\n");
1405 CreateTempFile("bar.proto",
1406 "syntax = \"proto2\";\n"
1407 "import \"foo.proto\";\n"
1408 "message Bar {\n"
1409 " optional Foo foo = 1;\n"
1410 "}\n");
1411
1412 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
1413 "--include_imports --proto_path=$tmpdir bar.proto");
1414
1415 ExpectNoErrors();
1416
1417 FileDescriptorSet descriptor_set;
1418 ReadDescriptorSet("descriptor_set", &descriptor_set);
1419 if (HasFatalFailure()) return;
1420 EXPECT_EQ(2, descriptor_set.file_size());
1421 if (descriptor_set.file(0).name() == "bar.proto") {
1422 std::swap(descriptor_set.mutable_file()->mutable_data()[0],
1423 descriptor_set.mutable_file()->mutable_data()[1]);
1424 }
1425 EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
1426 EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
1427 // Descriptor set should not have source code info.
1428 EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
1429 EXPECT_FALSE(descriptor_set.file(1).has_source_code_info());
1430 }
1431
TEST_F(CommandLineInterfaceTest,WriteTransitiveDescriptorSetWithSourceInfo)1432 TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSetWithSourceInfo) {
1433 CreateTempFile("foo.proto",
1434 "syntax = \"proto2\";\n"
1435 "message Foo {}\n");
1436 CreateTempFile("bar.proto",
1437 "syntax = \"proto2\";\n"
1438 "import \"foo.proto\";\n"
1439 "message Bar {\n"
1440 " optional Foo foo = 1;\n"
1441 "}\n");
1442
1443 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
1444 "--include_imports --include_source_info --proto_path=$tmpdir bar.proto");
1445
1446 ExpectNoErrors();
1447
1448 FileDescriptorSet descriptor_set;
1449 ReadDescriptorSet("descriptor_set", &descriptor_set);
1450 if (HasFatalFailure()) return;
1451 EXPECT_EQ(2, descriptor_set.file_size());
1452 if (descriptor_set.file(0).name() == "bar.proto") {
1453 std::swap(descriptor_set.mutable_file()->mutable_data()[0],
1454 descriptor_set.mutable_file()->mutable_data()[1]);
1455 }
1456 EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
1457 EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
1458 // Source code info included.
1459 EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
1460 EXPECT_TRUE(descriptor_set.file(1).has_source_code_info());
1461 }
1462
1463 #ifdef _WIN32
1464 // TODO(teboring): Figure out how to write test on windows.
1465 #else
TEST_F(CommandLineInterfaceTest,WriteDependencyManifestFileGivenTwoInputs)1466 TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileGivenTwoInputs) {
1467 CreateTempFile("foo.proto",
1468 "syntax = \"proto2\";\n"
1469 "message Foo {}\n");
1470 CreateTempFile("bar.proto",
1471 "syntax = \"proto2\";\n"
1472 "import \"foo.proto\";\n"
1473 "message Bar {\n"
1474 " optional Foo foo = 1;\n"
1475 "}\n");
1476
1477 Run("protocol_compiler --dependency_out=$tmpdir/manifest "
1478 "--test_out=$tmpdir --proto_path=$tmpdir bar.proto foo.proto");
1479
1480 ExpectErrorText(
1481 "Can only process one input file when using --dependency_out=FILE.\n");
1482 }
1483
1484 #ifdef PROTOBUF_OPENSOURCE
TEST_F(CommandLineInterfaceTest,WriteDependencyManifestFile)1485 TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFile) {
1486 CreateTempFile("foo.proto",
1487 "syntax = \"proto2\";\n"
1488 "message Foo {}\n");
1489 CreateTempFile("bar.proto",
1490 "syntax = \"proto2\";\n"
1491 "import \"foo.proto\";\n"
1492 "message Bar {\n"
1493 " optional Foo foo = 1;\n"
1494 "}\n");
1495
1496 std::string current_working_directory = getcwd(NULL, 0);
1497 SwitchToTempDirectory();
1498
1499 Run("protocol_compiler --dependency_out=manifest --test_out=. "
1500 "bar.proto");
1501
1502 ExpectNoErrors();
1503
1504 ExpectFileContent("manifest",
1505 "bar.proto.MockCodeGenerator.test_generator: "
1506 "foo.proto\\\n bar.proto");
1507
1508 File::ChangeWorkingDirectory(current_working_directory);
1509 }
1510 #else // !PROTOBUF_OPENSOURCE
1511 // TODO(teboring): Figure out how to change and get working directory in
1512 // google3.
1513 #endif // !PROTOBUF_OPENSOURCE
1514
TEST_F(CommandLineInterfaceTest,WriteDependencyManifestFileForAbsolutePath)1515 TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileForAbsolutePath) {
1516 CreateTempFile("foo.proto",
1517 "syntax = \"proto2\";\n"
1518 "message Foo {}\n");
1519 CreateTempFile("bar.proto",
1520 "syntax = \"proto2\";\n"
1521 "import \"foo.proto\";\n"
1522 "message Bar {\n"
1523 " optional Foo foo = 1;\n"
1524 "}\n");
1525
1526 Run("protocol_compiler --dependency_out=$tmpdir/manifest "
1527 "--test_out=$tmpdir --proto_path=$tmpdir bar.proto");
1528
1529 ExpectNoErrors();
1530
1531 ExpectFileContent("manifest",
1532 "$tmpdir/bar.proto.MockCodeGenerator.test_generator: "
1533 "$tmpdir/foo.proto\\\n $tmpdir/bar.proto");
1534 }
1535 #endif // !_WIN32
1536
TEST_F(CommandLineInterfaceTest,TestArgumentFile)1537 TEST_F(CommandLineInterfaceTest, TestArgumentFile) {
1538 // Test parsing multiple input files using an argument file.
1539
1540 CreateTempFile("foo.proto",
1541 "syntax = \"proto2\";\n"
1542 "message Foo {}\n");
1543 CreateTempFile("bar.proto",
1544 "syntax = \"proto2\";\n"
1545 "message Bar {}\n");
1546 CreateTempFile("arguments.txt",
1547 "--test_out=$tmpdir\n"
1548 "--plug_out=$tmpdir\n"
1549 "--proto_path=$tmpdir\n"
1550 "--direct_dependencies_violation_msg=%s is not imported\n"
1551 "foo.proto\n"
1552 "bar.proto");
1553
1554 Run("protocol_compiler @$tmpdir/arguments.txt");
1555
1556 ExpectNoErrors();
1557 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
1558 "foo.proto", "Foo");
1559 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
1560 "bar.proto", "Bar");
1561 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
1562 "foo.proto", "Foo");
1563 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
1564 "bar.proto", "Bar");
1565 }
1566
1567
1568 // -------------------------------------------------------------------
1569
TEST_F(CommandLineInterfaceTest,ParseErrors)1570 TEST_F(CommandLineInterfaceTest, ParseErrors) {
1571 // Test that parse errors are reported.
1572
1573 CreateTempFile("foo.proto",
1574 "syntax = \"proto2\";\n"
1575 "badsyntax\n");
1576
1577 Run("protocol_compiler --test_out=$tmpdir "
1578 "--proto_path=$tmpdir foo.proto");
1579
1580 ExpectErrorText(
1581 "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
1582 }
1583
TEST_F(CommandLineInterfaceTest,ParseErrors_DescriptorSetIn)1584 TEST_F(CommandLineInterfaceTest, ParseErrors_DescriptorSetIn) {
1585 // Test that parse errors are reported.
1586 CreateTempFile("foo.bin", "not a FileDescriptorSet");
1587
1588 Run("protocol_compiler --test_out=$tmpdir "
1589 "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
1590
1591 ExpectErrorText("$tmpdir/foo.bin: Unable to parse.\n");
1592 }
1593
TEST_F(CommandLineInterfaceTest,ParseErrorsMultipleFiles)1594 TEST_F(CommandLineInterfaceTest, ParseErrorsMultipleFiles) {
1595 // Test that parse errors are reported from multiple files.
1596
1597 // We set up files such that foo.proto actually depends on bar.proto in
1598 // two ways: Directly and through baz.proto. bar.proto's errors should
1599 // only be reported once.
1600 CreateTempFile("bar.proto",
1601 "syntax = \"proto2\";\n"
1602 "badsyntax\n");
1603 CreateTempFile("baz.proto",
1604 "syntax = \"proto2\";\n"
1605 "import \"bar.proto\";\n");
1606 CreateTempFile("foo.proto",
1607 "syntax = \"proto2\";\n"
1608 "import \"bar.proto\";\n"
1609 "import \"baz.proto\";\n");
1610
1611 Run("protocol_compiler --test_out=$tmpdir "
1612 "--proto_path=$tmpdir foo.proto");
1613
1614 ExpectErrorText(
1615 "bar.proto:2:1: Expected top-level statement (e.g. \"message\").\n"
1616 "baz.proto:2:1: Import \"bar.proto\" was not found or had errors.\n"
1617 "foo.proto:2:1: Import \"bar.proto\" was not found or had errors.\n"
1618 "foo.proto:3:1: Import \"baz.proto\" was not found or had errors.\n");
1619 }
1620
TEST_F(CommandLineInterfaceTest,RecursiveImportFails)1621 TEST_F(CommandLineInterfaceTest, RecursiveImportFails) {
1622 // Create a proto file that imports itself.
1623 CreateTempFile("foo.proto",
1624 "syntax = \"proto2\";\n"
1625 "import \"foo.proto\";\n");
1626
1627 Run("protocol_compiler --test_out=$tmpdir "
1628 "--proto_path=$tmpdir foo.proto");
1629
1630 ExpectErrorSubstring(
1631 "foo.proto:2:1: File recursively imports itself: "
1632 "foo.proto -> foo.proto\n");
1633 }
1634
TEST_F(CommandLineInterfaceTest,InputNotFoundError)1635 TEST_F(CommandLineInterfaceTest, InputNotFoundError) {
1636 // Test what happens if the input file is not found.
1637
1638 Run("protocol_compiler --test_out=$tmpdir "
1639 "--proto_path=$tmpdir foo.proto");
1640
1641 ExpectErrorText("foo.proto: No such file or directory\n");
1642 }
1643
TEST_F(CommandLineInterfaceTest,InputNotFoundError_DescriptorSetIn)1644 TEST_F(CommandLineInterfaceTest, InputNotFoundError_DescriptorSetIn) {
1645 // Test what happens if the input file is not found.
1646
1647 Run("protocol_compiler --test_out=$tmpdir "
1648 "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
1649
1650 ExpectErrorText("$tmpdir/foo.bin: No such file or directory\n");
1651 }
1652
TEST_F(CommandLineInterfaceTest,CwdRelativeInputNotFoundError)1653 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundError) {
1654 // Test what happens when a working-directory-relative input file is not
1655 // found.
1656
1657 Run("protocol_compiler --test_out=$tmpdir "
1658 "--proto_path=$tmpdir $tmpdir/foo.proto");
1659
1660 ExpectErrorText("$tmpdir/foo.proto: No such file or directory\n");
1661 }
1662
TEST_F(CommandLineInterfaceTest,CwdRelativeInputNotMappedError)1663 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotMappedError) {
1664 // Test what happens when a working-directory-relative input file is not
1665 // mapped to a virtual path.
1666
1667 CreateTempFile("foo.proto",
1668 "syntax = \"proto2\";\n"
1669 "message Foo {}\n");
1670
1671 // Create a directory called "bar" so that we can point --proto_path at it.
1672 CreateTempFile("bar/dummy", "");
1673
1674 Run("protocol_compiler --test_out=$tmpdir "
1675 "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
1676
1677 ExpectErrorText(
1678 "$tmpdir/foo.proto: File does not reside within any path "
1679 "specified using --proto_path (or -I). You must specify a "
1680 "--proto_path which encompasses this file. Note that the "
1681 "proto_path must be an exact prefix of the .proto file "
1682 "names -- protoc is too dumb to figure out when two paths "
1683 "(e.g. absolute and relative) are equivalent (it's harder "
1684 "than you think).\n");
1685 }
1686
TEST_F(CommandLineInterfaceTest,CwdRelativeInputNotFoundAndNotMappedError)1687 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundAndNotMappedError) {
1688 // Check what happens if the input file is not found *and* is not mapped
1689 // in the proto_path.
1690
1691 // Create a directory called "bar" so that we can point --proto_path at it.
1692 CreateTempFile("bar/dummy", "");
1693
1694 Run("protocol_compiler --test_out=$tmpdir "
1695 "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
1696
1697 ExpectErrorText("$tmpdir/foo.proto: No such file or directory\n");
1698 }
1699
TEST_F(CommandLineInterfaceTest,CwdRelativeInputShadowedError)1700 TEST_F(CommandLineInterfaceTest, CwdRelativeInputShadowedError) {
1701 // Test what happens when a working-directory-relative input file is shadowed
1702 // by another file in the virtual path.
1703
1704 CreateTempFile("foo/foo.proto",
1705 "syntax = \"proto2\";\n"
1706 "message Foo {}\n");
1707 CreateTempFile("bar/foo.proto",
1708 "syntax = \"proto2\";\n"
1709 "message Bar {}\n");
1710
1711 Run("protocol_compiler --test_out=$tmpdir "
1712 "--proto_path=$tmpdir/foo --proto_path=$tmpdir/bar "
1713 "$tmpdir/bar/foo.proto");
1714
1715 ExpectErrorText(
1716 "$tmpdir/bar/foo.proto: Input is shadowed in the --proto_path "
1717 "by \"$tmpdir/foo/foo.proto\". Either use the latter "
1718 "file as your input or reorder the --proto_path so that the "
1719 "former file's location comes first.\n");
1720 }
1721
TEST_F(CommandLineInterfaceTest,ProtoPathNotFoundError)1722 TEST_F(CommandLineInterfaceTest, ProtoPathNotFoundError) {
1723 // Test what happens if the input file is not found.
1724
1725 Run("protocol_compiler --test_out=$tmpdir "
1726 "--proto_path=$tmpdir/foo foo.proto");
1727
1728 ExpectErrorText(
1729 "$tmpdir/foo: warning: directory does not exist.\n"
1730 "foo.proto: No such file or directory\n");
1731 }
1732
TEST_F(CommandLineInterfaceTest,ProtoPathAndDescriptorSetIn)1733 TEST_F(CommandLineInterfaceTest, ProtoPathAndDescriptorSetIn) {
1734 Run("protocol_compiler --test_out=$tmpdir "
1735 "--proto_path=$tmpdir --descriptor_set_in=$tmpdir/foo.bin foo.proto");
1736 ExpectErrorText("$tmpdir/foo.bin: No such file or directory\n");
1737
1738 Run("protocol_compiler --test_out=$tmpdir "
1739 "--descriptor_set_in=$tmpdir/foo.bin --proto_path=$tmpdir foo.proto");
1740 ExpectErrorText("$tmpdir/foo.bin: No such file or directory\n");
1741 }
1742
TEST_F(CommandLineInterfaceTest,ProtoPathAndDescriptorSetIn_CompileFiles)1743 TEST_F(CommandLineInterfaceTest, ProtoPathAndDescriptorSetIn_CompileFiles) {
1744 // Test what happens if a proto is in a --descriptor_set_in and also exists
1745 // on disk.
1746 FileDescriptorSet file_descriptor_set;
1747
1748 // NOTE: This file desc SHOULD be different from the one created as a temp
1749 // to make it easier to test that the file was output instead of the
1750 // contents of the --descriptor_set_in file.
1751 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
1752 file_descriptor_proto->set_name("foo.proto");
1753 file_descriptor_proto->add_message_type()->set_name("Foo");
1754
1755 WriteDescriptorSet("foo.bin", &file_descriptor_set);
1756
1757 CreateTempFile("foo.proto",
1758 "syntax = \"proto2\";\n"
1759 "message FooBar { required string foo_message = 1; }\n");
1760
1761 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
1762 "--descriptor_set_in=$tmpdir/foo.bin "
1763 "--include_source_info "
1764 "--proto_path=$tmpdir foo.proto");
1765
1766 ExpectNoErrors();
1767
1768 FileDescriptorSet descriptor_set;
1769 ReadDescriptorSet("descriptor_set", &descriptor_set);
1770
1771 EXPECT_EQ(1, descriptor_set.file_size());
1772 EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
1773 // Descriptor set SHOULD have source code info.
1774 EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
1775
1776 EXPECT_EQ("FooBar", descriptor_set.file(0).message_type(0).name());
1777 EXPECT_EQ("foo_message",
1778 descriptor_set.file(0).message_type(0).field(0).name());
1779 }
1780
TEST_F(CommandLineInterfaceTest,ProtoPathAndDependencyOut)1781 TEST_F(CommandLineInterfaceTest, ProtoPathAndDependencyOut) {
1782 Run("protocol_compiler --test_out=$tmpdir "
1783 "--dependency_out=$tmpdir/manifest "
1784 "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
1785 ExpectErrorText(
1786 "--descriptor_set_in cannot be used with --dependency_out.\n");
1787
1788 Run("protocol_compiler --test_out=$tmpdir "
1789 "--descriptor_set_in=$tmpdir/foo.bin "
1790 "--dependency_out=$tmpdir/manifest foo.proto");
1791 ExpectErrorText(
1792 "--dependency_out cannot be used with --descriptor_set_in.\n");
1793 }
1794
TEST_F(CommandLineInterfaceTest,MissingInputError)1795 TEST_F(CommandLineInterfaceTest, MissingInputError) {
1796 // Test that we get an error if no inputs are given.
1797
1798 Run("protocol_compiler --test_out=$tmpdir "
1799 "--proto_path=$tmpdir");
1800
1801 ExpectErrorText("Missing input file.\n");
1802 }
1803
TEST_F(CommandLineInterfaceTest,MissingOutputError)1804 TEST_F(CommandLineInterfaceTest, MissingOutputError) {
1805 CreateTempFile("foo.proto",
1806 "syntax = \"proto2\";\n"
1807 "message Foo {}\n");
1808
1809 Run("protocol_compiler --proto_path=$tmpdir foo.proto");
1810
1811 ExpectErrorText("Missing output directives.\n");
1812 }
1813
TEST_F(CommandLineInterfaceTest,OutputWriteError)1814 TEST_F(CommandLineInterfaceTest, OutputWriteError) {
1815 CreateTempFile("foo.proto",
1816 "syntax = \"proto2\";\n"
1817 "message Foo {}\n");
1818
1819 std::string output_file =
1820 MockCodeGenerator::GetOutputFileName("test_generator", "foo.proto");
1821
1822 // Create a directory blocking our output location.
1823 CreateTempDir(output_file);
1824
1825 Run("protocol_compiler --test_out=$tmpdir "
1826 "--proto_path=$tmpdir foo.proto");
1827
1828 // MockCodeGenerator no longer detects an error because we actually write to
1829 // an in-memory location first, then dump to disk at the end. This is no
1830 // big deal.
1831 // ExpectErrorSubstring("MockCodeGenerator detected write error.");
1832
1833 #if defined(_WIN32) && !defined(__CYGWIN__)
1834 // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
1835 if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
1836 return;
1837 }
1838 #endif
1839
1840 ExpectErrorSubstring(output_file + ": Is a directory");
1841 }
1842
TEST_F(CommandLineInterfaceTest,PluginOutputWriteError)1843 TEST_F(CommandLineInterfaceTest, PluginOutputWriteError) {
1844 CreateTempFile("foo.proto",
1845 "syntax = \"proto2\";\n"
1846 "message Foo {}\n");
1847
1848 std::string output_file =
1849 MockCodeGenerator::GetOutputFileName("test_plugin", "foo.proto");
1850
1851 // Create a directory blocking our output location.
1852 CreateTempDir(output_file);
1853
1854 Run("protocol_compiler --plug_out=$tmpdir "
1855 "--proto_path=$tmpdir foo.proto");
1856
1857 #if defined(_WIN32) && !defined(__CYGWIN__)
1858 // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
1859 if (HasAlternateErrorSubstring(output_file + ": Permission denied")) {
1860 return;
1861 }
1862 #endif
1863
1864 ExpectErrorSubstring(output_file + ": Is a directory");
1865 }
1866
TEST_F(CommandLineInterfaceTest,OutputDirectoryNotFoundError)1867 TEST_F(CommandLineInterfaceTest, OutputDirectoryNotFoundError) {
1868 CreateTempFile("foo.proto",
1869 "syntax = \"proto2\";\n"
1870 "message Foo {}\n");
1871
1872 Run("protocol_compiler --test_out=$tmpdir/nosuchdir "
1873 "--proto_path=$tmpdir foo.proto");
1874
1875 ExpectErrorSubstring("nosuchdir/: No such file or directory");
1876 }
1877
TEST_F(CommandLineInterfaceTest,PluginOutputDirectoryNotFoundError)1878 TEST_F(CommandLineInterfaceTest, PluginOutputDirectoryNotFoundError) {
1879 CreateTempFile("foo.proto",
1880 "syntax = \"proto2\";\n"
1881 "message Foo {}\n");
1882
1883 Run("protocol_compiler --plug_out=$tmpdir/nosuchdir "
1884 "--proto_path=$tmpdir foo.proto");
1885
1886 ExpectErrorSubstring("nosuchdir/: No such file or directory");
1887 }
1888
TEST_F(CommandLineInterfaceTest,OutputDirectoryIsFileError)1889 TEST_F(CommandLineInterfaceTest, OutputDirectoryIsFileError) {
1890 CreateTempFile("foo.proto",
1891 "syntax = \"proto2\";\n"
1892 "message Foo {}\n");
1893
1894 Run("protocol_compiler --test_out=$tmpdir/foo.proto "
1895 "--proto_path=$tmpdir foo.proto");
1896
1897 #if defined(_WIN32) && !defined(__CYGWIN__)
1898 // Windows with MSVCRT.dll produces EINVAL instead of ENOTDIR.
1899 if (HasAlternateErrorSubstring("foo.proto/: Invalid argument")) {
1900 return;
1901 }
1902 #endif
1903
1904 ExpectErrorSubstring("foo.proto/: Not a directory");
1905 }
1906
TEST_F(CommandLineInterfaceTest,GeneratorError)1907 TEST_F(CommandLineInterfaceTest, GeneratorError) {
1908 CreateTempFile("foo.proto",
1909 "syntax = \"proto2\";\n"
1910 "message MockCodeGenerator_Error {}\n");
1911
1912 Run("protocol_compiler --test_out=$tmpdir "
1913 "--proto_path=$tmpdir foo.proto");
1914
1915 ExpectErrorSubstring(
1916 "--test_out: foo.proto: Saw message type MockCodeGenerator_Error.");
1917 }
1918
TEST_F(CommandLineInterfaceTest,GeneratorPluginError)1919 TEST_F(CommandLineInterfaceTest, GeneratorPluginError) {
1920 // Test a generator plugin that returns an error.
1921
1922 CreateTempFile("foo.proto",
1923 "syntax = \"proto2\";\n"
1924 "message MockCodeGenerator_Error {}\n");
1925
1926 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1927 "--proto_path=$tmpdir foo.proto");
1928
1929 ExpectErrorSubstring(
1930 "--plug_out: foo.proto: Saw message type MockCodeGenerator_Error.");
1931 }
1932
TEST_F(CommandLineInterfaceTest,GeneratorPluginFail)1933 TEST_F(CommandLineInterfaceTest, GeneratorPluginFail) {
1934 // Test a generator plugin that exits with an error code.
1935
1936 CreateTempFile("foo.proto",
1937 "syntax = \"proto2\";\n"
1938 "message MockCodeGenerator_Exit {}\n");
1939
1940 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1941 "--proto_path=$tmpdir foo.proto");
1942
1943 ExpectErrorSubstring("Saw message type MockCodeGenerator_Exit.");
1944 ExpectErrorSubstring(
1945 "--plug_out: prefix-gen-plug: Plugin failed with status code 123.");
1946 }
1947
TEST_F(CommandLineInterfaceTest,GeneratorPluginCrash)1948 TEST_F(CommandLineInterfaceTest, GeneratorPluginCrash) {
1949 // Test a generator plugin that crashes.
1950
1951 CreateTempFile("foo.proto",
1952 "syntax = \"proto2\";\n"
1953 "message MockCodeGenerator_Abort {}\n");
1954
1955 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1956 "--proto_path=$tmpdir foo.proto");
1957
1958 ExpectErrorSubstring("Saw message type MockCodeGenerator_Abort.");
1959
1960 #ifdef _WIN32
1961 // Windows doesn't have signals. It looks like abort()ing causes the process
1962 // to exit with status code 3, but let's not depend on the exact number here.
1963 ExpectErrorSubstring(
1964 "--plug_out: prefix-gen-plug: Plugin failed with status code");
1965 #else
1966 // Don't depend on the exact signal number.
1967 ExpectErrorSubstring("--plug_out: prefix-gen-plug: Plugin killed by signal");
1968 #endif
1969 }
1970
TEST_F(CommandLineInterfaceTest,PluginReceivesSourceCodeInfo)1971 TEST_F(CommandLineInterfaceTest, PluginReceivesSourceCodeInfo) {
1972 CreateTempFile("foo.proto",
1973 "syntax = \"proto2\";\n"
1974 "message MockCodeGenerator_HasSourceCodeInfo {}\n");
1975
1976 Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
1977
1978 ExpectErrorSubstring(
1979 "Saw message type MockCodeGenerator_HasSourceCodeInfo: 1.");
1980 }
1981
TEST_F(CommandLineInterfaceTest,PluginReceivesJsonName)1982 TEST_F(CommandLineInterfaceTest, PluginReceivesJsonName) {
1983 CreateTempFile("foo.proto",
1984 "syntax = \"proto2\";\n"
1985 "message MockCodeGenerator_HasJsonName {\n"
1986 " optional int32 value = 1;\n"
1987 "}\n");
1988
1989 Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
1990
1991 ExpectErrorSubstring("Saw json_name: 1");
1992 }
1993
TEST_F(CommandLineInterfaceTest,PluginReceivesCompilerVersion)1994 TEST_F(CommandLineInterfaceTest, PluginReceivesCompilerVersion) {
1995 CreateTempFile("foo.proto",
1996 "syntax = \"proto2\";\n"
1997 "message MockCodeGenerator_ShowVersionNumber {\n"
1998 " optional int32 value = 1;\n"
1999 "}\n");
2000
2001 Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
2002
2003 ExpectErrorSubstring(StringPrintf("Saw compiler_version: %d %s",
2004 GOOGLE_PROTOBUF_VERSION,
2005 GOOGLE_PROTOBUF_VERSION_SUFFIX));
2006 }
2007
TEST_F(CommandLineInterfaceTest,GeneratorPluginNotFound)2008 TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
2009 // Test what happens if the plugin isn't found.
2010
2011 CreateTempFile("error.proto",
2012 "syntax = \"proto2\";\n"
2013 "message Foo {}\n");
2014
2015 Run("protocol_compiler --badplug_out=TestParameter:$tmpdir "
2016 "--plugin=prefix-gen-badplug=no_such_file "
2017 "--proto_path=$tmpdir error.proto");
2018
2019 #ifdef _WIN32
2020 ExpectErrorSubstring("--badplug_out: prefix-gen-badplug: " +
2021 Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND));
2022 #else
2023 // Error written to stdout by child process after exec() fails.
2024 ExpectErrorSubstring("no_such_file: program not found or is not executable");
2025
2026 ExpectErrorSubstring(
2027 "Please specify a program using absolute path or make sure "
2028 "the program is available in your PATH system variable");
2029
2030 // Error written by parent process when child fails.
2031 ExpectErrorSubstring(
2032 "--badplug_out: prefix-gen-badplug: Plugin failed with status code 1.");
2033 #endif
2034 }
2035
TEST_F(CommandLineInterfaceTest,GeneratorPluginNotAllowed)2036 TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) {
2037 // Test what happens if plugins aren't allowed.
2038
2039 CreateTempFile("error.proto",
2040 "syntax = \"proto2\";\n"
2041 "message Foo {}\n");
2042
2043 DisallowPlugins();
2044 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
2045 "--proto_path=$tmpdir error.proto");
2046
2047 ExpectErrorSubstring("Unknown flag: --plug_out");
2048 }
2049
TEST_F(CommandLineInterfaceTest,HelpText)2050 TEST_F(CommandLineInterfaceTest, HelpText) {
2051 Run("test_exec_name --help");
2052
2053 ExpectCapturedStdoutSubstringWithZeroReturnCode("Usage: test_exec_name ");
2054 ExpectCapturedStdoutSubstringWithZeroReturnCode("--test_out=OUT_DIR");
2055 ExpectCapturedStdoutSubstringWithZeroReturnCode("Test output.");
2056 ExpectCapturedStdoutSubstringWithZeroReturnCode("--alt_out=OUT_DIR");
2057 ExpectCapturedStdoutSubstringWithZeroReturnCode("Alt output.");
2058 }
2059
TEST_F(CommandLineInterfaceTest,GccFormatErrors)2060 TEST_F(CommandLineInterfaceTest, GccFormatErrors) {
2061 // Test --error_format=gcc (which is the default, but we want to verify
2062 // that it can be set explicitly).
2063
2064 CreateTempFile("foo.proto",
2065 "syntax = \"proto2\";\n"
2066 "badsyntax\n");
2067
2068 Run("protocol_compiler --test_out=$tmpdir "
2069 "--proto_path=$tmpdir --error_format=gcc foo.proto");
2070
2071 ExpectErrorText(
2072 "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
2073 }
2074
TEST_F(CommandLineInterfaceTest,MsvsFormatErrors)2075 TEST_F(CommandLineInterfaceTest, MsvsFormatErrors) {
2076 // Test --error_format=msvs
2077
2078 CreateTempFile("foo.proto",
2079 "syntax = \"proto2\";\n"
2080 "badsyntax\n");
2081
2082 Run("protocol_compiler --test_out=$tmpdir "
2083 "--proto_path=$tmpdir --error_format=msvs foo.proto");
2084
2085 ExpectErrorText(
2086 "$tmpdir/foo.proto(2) : error in column=1: Expected top-level statement "
2087 "(e.g. \"message\").\n");
2088 }
2089
TEST_F(CommandLineInterfaceTest,InvalidErrorFormat)2090 TEST_F(CommandLineInterfaceTest, InvalidErrorFormat) {
2091 // Test --error_format=msvs
2092
2093 CreateTempFile("foo.proto",
2094 "syntax = \"proto2\";\n"
2095 "badsyntax\n");
2096
2097 Run("protocol_compiler --test_out=$tmpdir "
2098 "--proto_path=$tmpdir --error_format=invalid foo.proto");
2099
2100 ExpectErrorText("Unknown error format: invalid\n");
2101 }
2102
2103 // -------------------------------------------------------------------
2104 // Flag parsing tests
2105
TEST_F(CommandLineInterfaceTest,ParseSingleCharacterFlag)2106 TEST_F(CommandLineInterfaceTest, ParseSingleCharacterFlag) {
2107 // Test that a single-character flag works.
2108
2109 CreateTempFile("foo.proto",
2110 "syntax = \"proto2\";\n"
2111 "message Foo {}\n");
2112
2113 Run("protocol_compiler -t$tmpdir "
2114 "--proto_path=$tmpdir foo.proto");
2115
2116 ExpectNoErrors();
2117 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
2118 }
2119
TEST_F(CommandLineInterfaceTest,ParseSpaceDelimitedValue)2120 TEST_F(CommandLineInterfaceTest, ParseSpaceDelimitedValue) {
2121 // Test that separating the flag value with a space works.
2122
2123 CreateTempFile("foo.proto",
2124 "syntax = \"proto2\";\n"
2125 "message Foo {}\n");
2126
2127 Run("protocol_compiler --test_out $tmpdir "
2128 "--proto_path=$tmpdir foo.proto");
2129
2130 ExpectNoErrors();
2131 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
2132 }
2133
TEST_F(CommandLineInterfaceTest,ParseSingleCharacterSpaceDelimitedValue)2134 TEST_F(CommandLineInterfaceTest, ParseSingleCharacterSpaceDelimitedValue) {
2135 // Test that separating the flag value with a space works for
2136 // single-character flags.
2137
2138 CreateTempFile("foo.proto",
2139 "syntax = \"proto2\";\n"
2140 "message Foo {}\n");
2141
2142 Run("protocol_compiler -t $tmpdir "
2143 "--proto_path=$tmpdir foo.proto");
2144
2145 ExpectNoErrors();
2146 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
2147 }
2148
TEST_F(CommandLineInterfaceTest,MissingValueError)2149 TEST_F(CommandLineInterfaceTest, MissingValueError) {
2150 // Test that we get an error if a flag is missing its value.
2151
2152 Run("protocol_compiler --test_out --proto_path=$tmpdir foo.proto");
2153
2154 ExpectErrorText("Missing value for flag: --test_out\n");
2155 }
2156
TEST_F(CommandLineInterfaceTest,MissingValueAtEndError)2157 TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) {
2158 // Test that we get an error if the last argument is a flag requiring a
2159 // value.
2160
2161 Run("protocol_compiler --test_out");
2162
2163 ExpectErrorText("Missing value for flag: --test_out\n");
2164 }
2165
TEST_F(CommandLineInterfaceTest,PrintFreeFieldNumbers)2166 TEST_F(CommandLineInterfaceTest, PrintFreeFieldNumbers) {
2167 CreateTempFile("foo.proto",
2168 "syntax = \"proto2\";\n"
2169 "package foo;\n"
2170 "message Foo {\n"
2171 " optional int32 a = 2;\n"
2172 " optional string b = 4;\n"
2173 " optional string c = 5;\n"
2174 " optional int64 d = 8;\n"
2175 " optional double e = 10;\n"
2176 "}\n");
2177 CreateTempFile("bar.proto",
2178 "syntax = \"proto2\";\n"
2179 "message Bar {\n"
2180 " optional int32 a = 2;\n"
2181 " extensions 4 to 5;\n"
2182 " optional int64 d = 8;\n"
2183 " extensions 10;\n"
2184 "}\n");
2185 CreateTempFile("baz.proto",
2186 "syntax = \"proto2\";\n"
2187 "message Baz {\n"
2188 " optional int32 a = 2;\n"
2189 " optional int64 d = 8;\n"
2190 " extensions 15 to max;\n" // unordered.
2191 " extensions 13;\n"
2192 " extensions 10 to 12;\n"
2193 " extensions 5;\n"
2194 " extensions 4;\n"
2195 "}\n");
2196 CreateTempFile(
2197 "quz.proto",
2198 "syntax = \"proto2\";\n"
2199 "message Quz {\n"
2200 " message Foo {}\n" // nested message
2201 " optional int32 a = 2;\n"
2202 " optional group C = 4 {\n"
2203 " optional int32 d = 5;\n"
2204 " }\n"
2205 " extensions 8 to 10;\n"
2206 " optional group E = 11 {\n"
2207 " optional int32 f = 9;\n" // explicitly reuse extension range 8-10
2208 " optional group G = 15 {\n" // nested group
2209 " message Foo {}\n" // nested message inside nested group
2210 " }\n"
2211 " }\n"
2212 "}\n");
2213
2214 Run("protocol_compiler --print_free_field_numbers --proto_path=$tmpdir "
2215 "foo.proto bar.proto baz.proto quz.proto");
2216
2217 ExpectNoErrors();
2218
2219 // TODO(jieluo): Cygwin doesn't work well if we try to capture stderr and
2220 // stdout at the same time. Need to figure out why and add this test back
2221 // for Cygwin.
2222 #if !defined(__CYGWIN__)
2223 ExpectCapturedStdout(
2224 "foo.Foo free: 1 3 6-7 9 11-INF\n"
2225 "Bar free: 1 3 6-7 9 11-INF\n"
2226 "Baz free: 1 3 6-7 9 14\n"
2227 "Quz.Foo free: 1-INF\n"
2228 "Quz.E.G.Foo free: 1-INF\n"
2229 "Quz free: 1 3 6-7 12-14 16-INF\n");
2230 #endif
2231 }
2232
2233 // ===================================================================
2234
2235 // Test for --encode and --decode. Note that it would be easier to do this
2236 // test as a shell script, but we'd like to be able to run the test on
2237 // platforms that don't have a Bourne-compatible shell available (especially
2238 // Windows/MSVC).
2239
2240 enum EncodeDecodeTestMode { PROTO_PATH, DESCRIPTOR_SET_IN };
2241
2242 class EncodeDecodeTest : public testing::TestWithParam<EncodeDecodeTestMode> {
2243 protected:
SetUp()2244 virtual void SetUp() {
2245 WriteUnittestProtoDescriptorSet();
2246 duped_stdin_ = dup(STDIN_FILENO);
2247 }
2248
TearDown()2249 virtual void TearDown() {
2250 dup2(duped_stdin_, STDIN_FILENO);
2251 close(duped_stdin_);
2252 }
2253
RedirectStdinFromText(const std::string & input)2254 void RedirectStdinFromText(const std::string& input) {
2255 std::string filename = TestTempDir() + "/test_stdin";
2256 GOOGLE_CHECK_OK(File::SetContents(filename, input, true));
2257 GOOGLE_CHECK(RedirectStdinFromFile(filename));
2258 }
2259
RedirectStdinFromFile(const std::string & filename)2260 bool RedirectStdinFromFile(const std::string& filename) {
2261 int fd = open(filename.c_str(), O_RDONLY);
2262 if (fd < 0) return false;
2263 dup2(fd, STDIN_FILENO);
2264 close(fd);
2265 return true;
2266 }
2267
2268 // Remove '\r' characters from text.
StripCR(const std::string & text)2269 std::string StripCR(const std::string& text) {
2270 std::string result;
2271
2272 for (int i = 0; i < text.size(); i++) {
2273 if (text[i] != '\r') {
2274 result.push_back(text[i]);
2275 }
2276 }
2277
2278 return result;
2279 }
2280
2281 enum Type { TEXT, BINARY };
2282 enum ReturnCode { SUCCESS, ERROR };
2283
Run(const std::string & command)2284 bool Run(const std::string& command) {
2285 std::vector<std::string> args;
2286 args.push_back("protoc");
2287 SplitStringUsing(command, " ", &args);
2288 switch (GetParam()) {
2289 case PROTO_PATH:
2290 args.push_back("--proto_path=" + TestUtil::TestSourceDir());
2291 break;
2292 case DESCRIPTOR_SET_IN:
2293 args.push_back(StrCat("--descriptor_set_in=",
2294 unittest_proto_descriptor_set_filename_));
2295 break;
2296 default:
2297 ADD_FAILURE() << "unexpected EncodeDecodeTestMode: " << GetParam();
2298 }
2299
2300 std::unique_ptr<const char*[]> argv(new const char*[args.size()]);
2301 for (int i = 0; i < args.size(); i++) {
2302 argv[i] = args[i].c_str();
2303 }
2304
2305 CommandLineInterface cli;
2306
2307 CaptureTestStdout();
2308 CaptureTestStderr();
2309
2310 int result = cli.Run(args.size(), argv.get());
2311
2312 captured_stdout_ = GetCapturedTestStdout();
2313 captured_stderr_ = GetCapturedTestStderr();
2314
2315 return result == 0;
2316 }
2317
ExpectStdoutMatchesBinaryFile(const std::string & filename)2318 void ExpectStdoutMatchesBinaryFile(const std::string& filename) {
2319 std::string expected_output;
2320 GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
2321
2322 // Don't use EXPECT_EQ because we don't want to print raw binary data to
2323 // stdout on failure.
2324 EXPECT_TRUE(captured_stdout_ == expected_output);
2325 }
2326
ExpectStdoutMatchesTextFile(const std::string & filename)2327 void ExpectStdoutMatchesTextFile(const std::string& filename) {
2328 std::string expected_output;
2329 GOOGLE_CHECK_OK(File::GetContents(filename, &expected_output, true));
2330
2331 ExpectStdoutMatchesText(expected_output);
2332 }
2333
ExpectStdoutMatchesText(const std::string & expected_text)2334 void ExpectStdoutMatchesText(const std::string& expected_text) {
2335 EXPECT_EQ(StripCR(expected_text), StripCR(captured_stdout_));
2336 }
2337
ExpectStderrMatchesText(const std::string & expected_text)2338 void ExpectStderrMatchesText(const std::string& expected_text) {
2339 EXPECT_EQ(StripCR(expected_text), StripCR(captured_stderr_));
2340 }
2341
2342 private:
WriteUnittestProtoDescriptorSet()2343 void WriteUnittestProtoDescriptorSet() {
2344 unittest_proto_descriptor_set_filename_ =
2345 TestTempDir() + "/unittest_proto_descriptor_set.bin";
2346 FileDescriptorSet file_descriptor_set;
2347 protobuf_unittest::TestAllTypes test_all_types;
2348 test_all_types.descriptor()->file()->CopyTo(file_descriptor_set.add_file());
2349
2350 protobuf_unittest_import::ImportMessage import_message;
2351 import_message.descriptor()->file()->CopyTo(file_descriptor_set.add_file());
2352
2353 protobuf_unittest_import::PublicImportMessage public_import_message;
2354 public_import_message.descriptor()->file()->CopyTo(
2355 file_descriptor_set.add_file());
2356 GOOGLE_DCHECK(file_descriptor_set.IsInitialized());
2357
2358 std::string binary_proto;
2359 GOOGLE_CHECK(file_descriptor_set.SerializeToString(&binary_proto));
2360 GOOGLE_CHECK_OK(File::SetContents(unittest_proto_descriptor_set_filename_,
2361 binary_proto, true));
2362 }
2363
2364 int duped_stdin_;
2365 std::string captured_stdout_;
2366 std::string captured_stderr_;
2367 std::string unittest_proto_descriptor_set_filename_;
2368 };
2369
TEST_P(EncodeDecodeTest,Encode)2370 TEST_P(EncodeDecodeTest, Encode) {
2371 RedirectStdinFromFile(TestUtil::GetTestDataPath(
2372 "net/proto2/internal/"
2373 "testdata/text_format_unittest_data_oneof_implemented.txt"));
2374 EXPECT_TRUE(
2375 Run(TestUtil::MaybeTranslatePath("net/proto2/internal/unittest.proto") +
2376 " --encode=protobuf_unittest.TestAllTypes"));
2377 ExpectStdoutMatchesBinaryFile(TestUtil::GetTestDataPath(
2378 "net/proto2/internal/testdata/golden_message_oneof_implemented"));
2379 ExpectStderrMatchesText("");
2380 }
2381
TEST_P(EncodeDecodeTest,Decode)2382 TEST_P(EncodeDecodeTest, Decode) {
2383 RedirectStdinFromFile(TestUtil::GetTestDataPath(
2384 "net/proto2/internal/testdata/golden_message_oneof_implemented"));
2385 EXPECT_TRUE(
2386 Run(TestUtil::MaybeTranslatePath("net/proto2/internal/unittest.proto") +
2387 " --decode=protobuf_unittest.TestAllTypes"));
2388 ExpectStdoutMatchesTextFile(TestUtil::GetTestDataPath(
2389 "net/proto2/internal/"
2390 "testdata/text_format_unittest_data_oneof_implemented.txt"));
2391 ExpectStderrMatchesText("");
2392 }
2393
TEST_P(EncodeDecodeTest,Partial)2394 TEST_P(EncodeDecodeTest, Partial) {
2395 RedirectStdinFromText("");
2396 EXPECT_TRUE(
2397 Run(TestUtil::MaybeTranslatePath("net/proto2/internal/unittest.proto") +
2398 " --encode=protobuf_unittest.TestRequired"));
2399 ExpectStdoutMatchesText("");
2400 ExpectStderrMatchesText(
2401 "warning: Input message is missing required fields: a, b, c\n");
2402 }
2403
TEST_P(EncodeDecodeTest,DecodeRaw)2404 TEST_P(EncodeDecodeTest, DecodeRaw) {
2405 protobuf_unittest::TestAllTypes message;
2406 message.set_optional_int32(123);
2407 message.set_optional_string("foo");
2408 std::string data;
2409 message.SerializeToString(&data);
2410
2411 RedirectStdinFromText(data);
2412 EXPECT_TRUE(Run("--decode_raw"));
2413 ExpectStdoutMatchesText(
2414 "1: 123\n"
2415 "14: \"foo\"\n");
2416 ExpectStderrMatchesText("");
2417 }
2418
TEST_P(EncodeDecodeTest,UnknownType)2419 TEST_P(EncodeDecodeTest, UnknownType) {
2420 EXPECT_FALSE(
2421 Run(TestUtil::MaybeTranslatePath("net/proto2/internal/unittest.proto") +
2422 " --encode=NoSuchType"));
2423 ExpectStdoutMatchesText("");
2424 ExpectStderrMatchesText("Type not defined: NoSuchType\n");
2425 }
2426
TEST_P(EncodeDecodeTest,ProtoParseError)2427 TEST_P(EncodeDecodeTest, ProtoParseError) {
2428 EXPECT_FALSE(
2429 Run("net/proto2/internal/no_such_file.proto "
2430 "--encode=NoSuchType"));
2431 ExpectStdoutMatchesText("");
2432 ExpectStderrMatchesText(
2433 "net/proto2/internal/no_such_file.proto: No such file or directory\n");
2434 }
2435
2436 INSTANTIATE_TEST_SUITE_P(FileDescriptorSetSource, EncodeDecodeTest,
2437 testing::Values(PROTO_PATH, DESCRIPTOR_SET_IN));
2438 } // anonymous namespace
2439
2440 #endif // !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
2441
2442 } // namespace compiler
2443 } // namespace protobuf
2444 } // namespace google
2445