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