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