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