1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 // Author: kenton@google.com (Kenton Varda)
9 // Based on original Protocol Buffers design by
10 // Sanjay Ghemawat, Jeff Dean, and others.
11
12 #include <fcntl.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15
16 #include <cstddef>
17 #include <cstdint>
18
19 #include <gmock/gmock.h>
20 #include "absl/log/absl_check.h"
21 #include "absl/strings/escaping.h"
22 #include "absl/strings/str_cat.h"
23 #include "absl/types/span.h"
24 #include "google/protobuf/compiler/command_line_interface_tester.h"
25 #include "google/protobuf/unittest_features.pb.h"
26 #include "google/protobuf/unittest_invalid_features.pb.h"
27
28 #ifndef _MSC_VER
29 #include <unistd.h>
30 #endif
31 #include <memory>
32 #include <string>
33 #include <utility>
34 #include <vector>
35
36 #include "google/protobuf/testing/file.h"
37 #include "google/protobuf/testing/file.h"
38 #include "google/protobuf/any.pb.h"
39 #include "google/protobuf/descriptor.pb.h"
40 #include "google/protobuf/testing/googletest.h"
41 #include <gtest/gtest.h>
42 #include "absl/strings/str_split.h"
43 #include "absl/strings/string_view.h"
44 #include "absl/strings/substitute.h"
45 #include "google/protobuf/compiler/code_generator.h"
46 #include "google/protobuf/compiler/command_line_interface.h"
47 #include "google/protobuf/compiler/cpp/names.h"
48 #include "google/protobuf/compiler/mock_code_generator.h"
49 #include "google/protobuf/compiler/plugin.pb.h"
50 #include "google/protobuf/test_textproto.h"
51 #include "google/protobuf/test_util.h"
52 #include "google/protobuf/test_util2.h"
53 #include "google/protobuf/unittest.pb.h"
54 #include "google/protobuf/unittest_custom_options.pb.h"
55 #include "google/protobuf/unittest_import.pb.h"
56
57 #ifdef GOOGLE_PROTOBUF_USE_BAZEL_GENERATED_PLUGIN_PATHS
58 // This is needed because of https://github.com/bazelbuild/bazel/issues/19124.
59 #include "google/protobuf/compiler/test_plugin_paths.h"
60 #endif // GOOGLE_PROTOBUF_USE_BAZEL_GENERATED_PLUGIN_PATHS
61
62 #ifdef _WIN32
63 #include "google/protobuf/compiler/subprocess.h"
64 #include "google/protobuf/io/io_win32.h"
65 #endif
66
67 // Must be included last.
68 #include "google/protobuf/port_def.inc"
69
70 namespace google {
71 namespace protobuf {
72 namespace compiler {
73
74 #if defined(_WIN32)
75 // DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
76 // them like we do below.
77 using google::protobuf::io::win32::access;
78 using google::protobuf::io::win32::close;
79 using google::protobuf::io::win32::dup;
80 using google::protobuf::io::win32::dup2;
81 using google::protobuf::io::win32::open;
82 using google::protobuf::io::win32::write;
83 #endif
84
85 // Disable the whole test when we use tcmalloc for "draconian" heap checks, in
86 // which case tcmalloc will print warnings that fail the plugin tests.
87 #if !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
88
89
90 namespace {
91
CreatePluginArg()92 std::string CreatePluginArg() {
93 std::string plugin_path;
94 #ifdef GOOGLE_PROTOBUF_TEST_PLUGIN_PATH
95 plugin_path = GOOGLE_PROTOBUF_TEST_PLUGIN_PATH;
96 #else
97 const char* possible_paths[] = {
98 // When building with shared libraries, libtool hides the real
99 // executable
100 // in .libs and puts a fake wrapper in the current directory.
101 // Unfortunately, due to an apparent bug on Cygwin/MinGW, if one program
102 // wrapped in this way (e.g. protobuf-tests.exe) tries to execute
103 // another
104 // program wrapped in this way (e.g. test_plugin.exe), the latter fails
105 // with error code 127 and no explanation message. Presumably the
106 // problem
107 // is that the wrapper for protobuf-tests.exe set some environment
108 // variables that confuse the wrapper for test_plugin.exe. Luckily, it
109 // turns out that if we simply invoke the wrapped test_plugin.exe
110 // directly, it works -- I guess the environment variables set by the
111 // protobuf-tests.exe wrapper happen to be correct for it too. So we do
112 // that.
113 ".libs/test_plugin.exe", // Win32 w/autotool (Cygwin / MinGW)
114 "test_plugin.exe", // Other Win32 (MSVC)
115 "test_plugin", // Unix
116 };
117 for (int i = 0; i < ABSL_ARRAYSIZE(possible_paths); ++i) {
118 if (access(possible_paths[i], F_OK) == 0) {
119 plugin_path = possible_paths[i];
120 break;
121 }
122 }
123 #endif
124
125 if (plugin_path.empty() || !File::Exists(plugin_path)) {
126 ABSL_LOG(ERROR)
127 << "Plugin executable not found. Plugin tests are likely to fail. "
128 << plugin_path;
129 return "";
130 }
131 return absl::StrCat("--plugin=prefix-gen-plug=", plugin_path);
132 }
133
134 class CommandLineInterfaceTest : public CommandLineInterfaceTester {
135 protected:
136 CommandLineInterfaceTest() = default;
137
138 void SetUp() override;
139
140 // Runs the CommandLineInterface with the given command line. The
141 // command is automatically split on spaces, and the string "$tmpdir"
142 // is replaced with TestTempDir().
143 void Run(std::string command);
144 void RunWithArgs(std::vector<std::string> args);
145
146 // -----------------------------------------------------------------
147 // Methods to set up the test (called before Run()).
148
149 class NullCodeGenerator;
150
151 // Normally plugins are allowed for all tests. Call this to explicitly
152 // disable them.
DisallowPlugins()153 void DisallowPlugins() { disallow_plugins_ = true; }
154 // Checks that MockCodeGenerator::Generate() was called in the given
155 // context (or the generator in test_plugin.cc, which produces the same
156 // output). That is, this tests if the generator with the given name
157 // was called with the given parameter and proto file and produced the
158 // given output file. This is checked by reading the output file and
159 // checking that it contains the content that MockCodeGenerator would
160 // generate given these inputs. message_name is the name of the first
161 // message that appeared in the proto file; this is just to make extra
162 // sure that the correct file was parsed.
163 void ExpectGenerated(const std::string& generator_name,
164 const std::string& parameter,
165 const std::string& proto_name,
166 const std::string& message_name);
167 void ExpectGenerated(const std::string& generator_name,
168 const std::string& parameter,
169 const std::string& proto_name,
170 const std::string& message_name,
171 const std::string& output_directory);
172 void ExpectGeneratedWithMultipleInputs(const std::string& generator_name,
173 const std::string& all_proto_names,
174 const std::string& proto_name,
175 const std::string& message_name);
176 void ExpectGeneratedWithInsertions(const std::string& generator_name,
177 const std::string& parameter,
178 const std::string& insertions,
179 const std::string& proto_name,
180 const std::string& message_name);
181 void CheckGeneratedAnnotations(const std::string& name,
182 const std::string& file);
183
184 #if defined(_WIN32)
185 void ExpectNullCodeGeneratorCalled(const std::string& parameter);
186 #endif // _WIN32
187
188
189 std::string ReadFile(absl::string_view filename);
190 void ReadDescriptorSet(absl::string_view filename,
191 FileDescriptorSet* descriptor_set);
192
193 void WriteDescriptorSet(absl::string_view filename,
194 const FileDescriptorSet* descriptor_set);
195
196 FeatureSetDefaults ReadEditionDefaults(absl::string_view filename);
197
198 // The default code generators support all features. Use this to create a
199 // code generator that omits the given feature(s).
CreateGeneratorWithMissingFeatures(const std::string & name,const std::string & description,uint64_t features)200 void CreateGeneratorWithMissingFeatures(const std::string& name,
201 const std::string& description,
202 uint64_t features) {
203 auto generator = std::make_unique<MockCodeGenerator>(name);
204 generator->SuppressFeatures(features);
205 RegisterGenerator(name, std::move(generator), description);
206 }
207
SetMockGeneratorTestCase(absl::string_view name)208 void SetMockGeneratorTestCase(absl::string_view name) {
209 #ifdef _WIN32
210 ::_putenv(absl::StrCat("TEST_CASE", "=", name).c_str());
211 #else
212 ::setenv("TEST_CASE", name.data(), 1);
213 #endif
214 }
215
216 MockCodeGenerator* mock_generator_ = nullptr;
217
218 private:
219 // Was DisallowPlugins() called?
220 bool disallow_plugins_ = false;
221
222 NullCodeGenerator* null_generator_ = nullptr;
223 };
224
225 class CommandLineInterfaceTest::NullCodeGenerator : public CodeGenerator {
226 public:
NullCodeGenerator()227 NullCodeGenerator() : called_(false) {}
228 ~NullCodeGenerator() override = default;
229
230 mutable bool called_;
231 mutable std::string parameter_;
232
233 // implements CodeGenerator ----------------------------------------
Generate(const FileDescriptor * file,const std::string & parameter,GeneratorContext * context,std::string * error) const234 bool Generate(const FileDescriptor* file, const std::string& parameter,
235 GeneratorContext* context, std::string* error) const override {
236 called_ = true;
237 parameter_ = parameter;
238 return true;
239 }
240 };
241
242 class ProtocMinimalCLITest : public CommandLineInterfaceTest {
243 protected:
SetUp()244 void SetUp() override {}
245 };
246
247 // ===================================================================
248
SetUp()249 void CommandLineInterfaceTest::SetUp() {
250 // Reset the mock generator's test case environment variable.
251 SetMockGeneratorTestCase("");
252
253 // Register generators.
254 auto mock_generator = std::make_unique<MockCodeGenerator>("test_generator");
255 mock_generator_ = mock_generator.get();
256 RegisterGenerator("--test_out", "--test_opt", std::move(mock_generator),
257 "Test output.");
258 RegisterGenerator("-t", std::make_unique<MockCodeGenerator>("test_generator"),
259 "Test output.");
260
261 RegisterGenerator("--alt_out",
262 std::make_unique<MockCodeGenerator>("alt_generator"),
263 "Alt output.");
264
265 auto null_generator = std::make_unique<NullCodeGenerator>();
266 null_generator_ = null_generator.get();
267 RegisterGenerator("--null_out", std::move(null_generator), "Null output.");
268
269 }
270
Run(std::string command)271 void CommandLineInterfaceTest::Run(std::string command) {
272 if (!disallow_plugins_) {
273 AllowPlugins("prefix-");
274 absl::StrAppend(&command, " ", CreatePluginArg());
275 }
276
277 RunProtoc(command);
278 }
279
RunWithArgs(std::vector<std::string> args)280 void CommandLineInterfaceTest::RunWithArgs(std::vector<std::string> args) {
281 if (!disallow_plugins_) {
282 AllowPlugins("prefix-");
283 args.push_back(CreatePluginArg());
284 }
285
286 RunProtocWithArgs(std::move(args));
287 }
288
289 // -------------------------------------------------------------------
290
ExpectGenerated(const std::string & generator_name,const std::string & parameter,const std::string & proto_name,const std::string & message_name)291 void CommandLineInterfaceTest::ExpectGenerated(
292 const std::string& generator_name, const std::string& parameter,
293 const std::string& proto_name, const std::string& message_name) {
294 MockCodeGenerator::ExpectGenerated(generator_name, parameter, "", proto_name,
295 message_name, proto_name,
296 temp_directory());
297 }
298
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)299 void CommandLineInterfaceTest::ExpectGenerated(
300 const std::string& generator_name, const std::string& parameter,
301 const std::string& proto_name, const std::string& message_name,
302 const std::string& output_directory) {
303 MockCodeGenerator::ExpectGenerated(
304 generator_name, parameter, "", proto_name, message_name, proto_name,
305 absl::StrCat(temp_directory(), "/", output_directory));
306 }
307
ExpectGeneratedWithMultipleInputs(const std::string & generator_name,const std::string & all_proto_names,const std::string & proto_name,const std::string & message_name)308 void CommandLineInterfaceTest::ExpectGeneratedWithMultipleInputs(
309 const std::string& generator_name, const std::string& all_proto_names,
310 const std::string& proto_name, const std::string& message_name) {
311 MockCodeGenerator::ExpectGenerated(generator_name, "", "", proto_name,
312 message_name, all_proto_names,
313 temp_directory());
314 }
315
ExpectGeneratedWithInsertions(const std::string & generator_name,const std::string & parameter,const std::string & insertions,const std::string & proto_name,const std::string & message_name)316 void CommandLineInterfaceTest::ExpectGeneratedWithInsertions(
317 const std::string& generator_name, const std::string& parameter,
318 const std::string& insertions, const std::string& proto_name,
319 const std::string& message_name) {
320 MockCodeGenerator::ExpectGenerated(generator_name, parameter, insertions,
321 proto_name, message_name, proto_name,
322 temp_directory());
323 }
324
CheckGeneratedAnnotations(const std::string & name,const std::string & file)325 void CommandLineInterfaceTest::CheckGeneratedAnnotations(
326 const std::string& name, const std::string& file) {
327 MockCodeGenerator::CheckGeneratedAnnotations(name, file, temp_directory());
328 }
329
330 #if defined(_WIN32)
ExpectNullCodeGeneratorCalled(const std::string & parameter)331 void CommandLineInterfaceTest::ExpectNullCodeGeneratorCalled(
332 const std::string& parameter) {
333 EXPECT_TRUE(null_generator_->called_);
334 EXPECT_EQ(parameter, null_generator_->parameter_);
335 }
336 #endif // _WIN32
337
338
ReadFile(absl::string_view filename)339 std::string CommandLineInterfaceTest::ReadFile(absl::string_view filename) {
340 std::string path = absl::StrCat(temp_directory(), "/", filename);
341 std::string file_contents;
342 ABSL_CHECK_OK(File::GetContents(path, &file_contents, true));
343 return file_contents;
344 }
345
ReadDescriptorSet(absl::string_view filename,FileDescriptorSet * descriptor_set)346 void CommandLineInterfaceTest::ReadDescriptorSet(
347 absl::string_view filename, FileDescriptorSet* descriptor_set) {
348 std::string file_contents = ReadFile(filename);
349 if (!descriptor_set->ParseFromString(file_contents)) {
350 FAIL() << "Could not parse file contents: " << filename;
351 }
352 }
353
ReadEditionDefaults(absl::string_view filename)354 FeatureSetDefaults CommandLineInterfaceTest::ReadEditionDefaults(
355 absl::string_view filename) {
356 FeatureSetDefaults defaults;
357 std::string file_contents = ReadFile(filename);
358 ABSL_CHECK(defaults.ParseFromString(file_contents))
359 << "Could not parse file contents: " << filename;
360 return defaults;
361 }
362
WriteDescriptorSet(absl::string_view filename,const FileDescriptorSet * descriptor_set)363 void CommandLineInterfaceTest::WriteDescriptorSet(
364 absl::string_view filename, const FileDescriptorSet* descriptor_set) {
365 std::string binary_proto;
366 ABSL_CHECK(descriptor_set->SerializeToString(&binary_proto));
367 CreateTempFile(filename, binary_proto);
368 }
369
370 // ===================================================================
371
TEST_F(CommandLineInterfaceTest,BasicOutput)372 TEST_F(CommandLineInterfaceTest, BasicOutput) {
373 // Test that the common case works.
374
375 CreateTempFile("foo.proto",
376 "syntax = \"proto2\";\n"
377 "message Foo {}\n");
378
379 Run("protocol_compiler --test_out=$tmpdir "
380 "--proto_path=$tmpdir foo.proto");
381
382 ExpectNoErrors();
383 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
384 }
385
TEST_F(CommandLineInterfaceTest,BasicOutput_DescriptorSetIn)386 TEST_F(CommandLineInterfaceTest, BasicOutput_DescriptorSetIn) {
387 // Test that the common case works.
388 FileDescriptorSet file_descriptor_set;
389 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
390 file_descriptor_proto->set_name("foo.proto");
391 file_descriptor_proto->add_message_type()->set_name("Foo");
392
393 WriteDescriptorSet("foo.bin", &file_descriptor_set);
394
395 Run("protocol_compiler --test_out=$tmpdir "
396 "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
397
398 ExpectNoErrors();
399 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
400 }
401
TEST_F(CommandLineInterfaceTest,BasicPlugin)402 TEST_F(CommandLineInterfaceTest, BasicPlugin) {
403 // Test that basic plugins work.
404
405 CreateTempFile("foo.proto",
406 "syntax = \"proto2\";\n"
407 "message Foo {}\n");
408
409 Run("protocol_compiler --plug_out=$tmpdir "
410 "--proto_path=$tmpdir foo.proto");
411
412 ExpectNoErrors();
413 ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
414 }
415
TEST_F(CommandLineInterfaceTest,BasicPlugin_DescriptorSetIn)416 TEST_F(CommandLineInterfaceTest, BasicPlugin_DescriptorSetIn) {
417 // Test that basic plugins work.
418
419 FileDescriptorSet file_descriptor_set;
420 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
421 file_descriptor_proto->set_name("foo.proto");
422 file_descriptor_proto->add_message_type()->set_name("Foo");
423
424 WriteDescriptorSet("foo.bin", &file_descriptor_set);
425
426 Run("protocol_compiler --plug_out=$tmpdir "
427 "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
428
429 ExpectNoErrors();
430 ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
431 }
432
TEST_F(CommandLineInterfaceTest,Plugin_OptionRetention)433 TEST_F(CommandLineInterfaceTest, Plugin_OptionRetention) {
434 CreateTempFile("foo.proto",
435 R"pb(syntax = "proto2"
436 ;
437 import "bar.proto";
438 package foo;
439 message Foo {
440 optional bar.Bar b = 1;
441 extensions 1000 to max [
442 declaration = {
443 number: 1000
444 full_name: ".foo.my_ext"
445 type: ".foo.MyType"
446 }
447 ];
448 })pb");
449 CreateTempFile("bar.proto",
450 R"pb(syntax = "proto2"
451 ;
452 package bar;
453 message Bar {
454 extensions 1000 to max [
455 declaration = {
456 number: 1000
457 full_name: ".baz.my_ext"
458 type: ".baz.MyType"
459 }
460 ];
461 })pb");
462
463 #ifdef GOOGLE_PROTOBUF_FAKE_PLUGIN_PATH
464 std::string plugin_path = GOOGLE_PROTOBUF_FAKE_PLUGIN_PATH;
465 #else
466 std::string plugin_path = absl::StrCat(
467 TestUtil::TestSourceDir(), "/google/protobuf/compiler/fake_plugin");
468 #endif
469
470 // Invoke protoc with fake_plugin to get ahold of the CodeGeneratorRequest
471 // sent by protoc.
472 Run(absl::StrCat(
473 "protocol_compiler --fake_plugin_out=$tmpdir --proto_path=$tmpdir "
474 "foo.proto --plugin=prefix-gen-fake_plugin=",
475 plugin_path));
476 ExpectNoErrors();
477 std::string base64_output = ReadFile("foo.proto.request");
478 std::string binary_request;
479 ASSERT_TRUE(absl::Base64Unescape(base64_output, &binary_request));
480 CodeGeneratorRequest request;
481 ASSERT_TRUE(request.ParseFromString(binary_request));
482
483 // request.proto_file() should include source-retention options for bar.proto
484 // but not for foo.proto. Protoc should strip source-retention options from
485 // the immediate proto files being built, but not for all dependencies.
486 ASSERT_EQ(request.proto_file_size(), 2);
487 {
488 EXPECT_EQ(request.proto_file(0).name(), "bar.proto");
489 ASSERT_EQ(request.proto_file(0).message_type_size(), 1);
490 const DescriptorProto& m = request.proto_file(0).message_type(0);
491 ASSERT_EQ(m.extension_range_size(), 1);
492 EXPECT_EQ(m.extension_range(0).options().declaration_size(), 1);
493 }
494
495 {
496 EXPECT_EQ(request.proto_file(1).name(), "foo.proto");
497 ASSERT_EQ(request.proto_file(1).message_type_size(), 1);
498 const DescriptorProto& m = request.proto_file(1).message_type(0);
499 ASSERT_EQ(m.extension_range_size(), 1);
500 EXPECT_TRUE(m.extension_range(0).options().declaration().empty());
501 }
502 }
503
TEST_F(CommandLineInterfaceTest,Plugin_SourceFileDescriptors)504 TEST_F(CommandLineInterfaceTest, Plugin_SourceFileDescriptors) {
505 CreateTempFile("foo.proto",
506 R"pb(syntax = "proto2"
507 ;
508 import "bar.proto";
509 package foo;
510 message Foo {
511 optional bar.Bar b = 1;
512 extensions 1000 to max [
513 declaration = {
514 number: 1000
515 full_name: ".foo.my_ext"
516 type: ".foo.MyType"
517 }
518 ];
519 })pb");
520 CreateTempFile("bar.proto",
521 R"pb(syntax = "proto2"
522 ;
523 package bar;
524 message Bar {
525 extensions 1000 to max [
526 declaration = {
527 number: 1000
528 full_name: ".baz.my_ext"
529 type: ".baz.MyType"
530 }
531 ];
532 })pb");
533
534 #ifdef GOOGLE_PROTOBUF_FAKE_PLUGIN_PATH
535 std::string plugin_path = GOOGLE_PROTOBUF_FAKE_PLUGIN_PATH;
536 #else
537 std::string plugin_path = absl::StrCat(
538 TestUtil::TestSourceDir(), "/google/protobuf/compiler/fake_plugin");
539 #endif
540
541 // Invoke protoc with fake_plugin to get ahold of the CodeGeneratorRequest
542 // sent by protoc.
543 Run(absl::StrCat(
544 "protocol_compiler --fake_plugin_out=$tmpdir --proto_path=$tmpdir "
545 "foo.proto --plugin=prefix-gen-fake_plugin=",
546 plugin_path));
547 ExpectNoErrors();
548 std::string base64_output = ReadFile("foo.proto.request");
549 std::string binary_request;
550 ASSERT_TRUE(absl::Base64Unescape(base64_output, &binary_request));
551 CodeGeneratorRequest request;
552 ASSERT_TRUE(request.ParseFromString(binary_request));
553
554 // request.source_file_descriptors() should consist of a descriptor for
555 // foo.proto that includes source-retention options.
556 ASSERT_EQ(request.source_file_descriptors_size(), 1);
557 EXPECT_EQ(request.source_file_descriptors(0).name(), "foo.proto");
558 ASSERT_EQ(request.source_file_descriptors(0).message_type_size(), 1);
559 const DescriptorProto& m = request.source_file_descriptors(0).message_type(0);
560 ASSERT_EQ(m.extension_range_size(), 1);
561 EXPECT_EQ(m.extension_range(0).options().declaration_size(), 1);
562 }
563
TEST_F(CommandLineInterfaceTest,GeneratorAndPlugin)564 TEST_F(CommandLineInterfaceTest, GeneratorAndPlugin) {
565 // Invoke a generator and a plugin at the same time.
566
567 CreateTempFile("foo.proto",
568 "syntax = \"proto2\";\n"
569 "message Foo {}\n");
570
571 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
572 "--proto_path=$tmpdir foo.proto");
573
574 ExpectNoErrors();
575 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
576 ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
577 }
578
TEST_F(CommandLineInterfaceTest,GeneratorAndPlugin_DescriptorSetIn)579 TEST_F(CommandLineInterfaceTest, GeneratorAndPlugin_DescriptorSetIn) {
580 // Invoke a generator and a plugin at the same time.
581
582 FileDescriptorSet file_descriptor_set;
583 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
584 file_descriptor_proto->set_name("foo.proto");
585 file_descriptor_proto->add_message_type()->set_name("Foo");
586
587 WriteDescriptorSet("foo.bin", &file_descriptor_set);
588
589 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
590 "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
591
592 ExpectNoErrors();
593 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
594 ExpectGenerated("test_plugin", "", "foo.proto", "Foo");
595 }
596
TEST_F(CommandLineInterfaceTest,MultipleInputs)597 TEST_F(CommandLineInterfaceTest, MultipleInputs) {
598 // Test parsing multiple input files.
599
600 CreateTempFile("foo.proto",
601 "syntax = \"proto2\";\n"
602 "message Foo {}\n");
603 CreateTempFile("bar.proto",
604 "syntax = \"proto2\";\n"
605 "message Bar {}\n");
606
607 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
608 "--proto_path=$tmpdir foo.proto bar.proto");
609
610 ExpectNoErrors();
611 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
612 "foo.proto", "Foo");
613 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
614 "bar.proto", "Bar");
615 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
616 "foo.proto", "Foo");
617 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
618 "bar.proto", "Bar");
619 }
620
TEST_F(CommandLineInterfaceTest,MultipleInputs_DescriptorSetIn)621 TEST_F(CommandLineInterfaceTest, MultipleInputs_DescriptorSetIn) {
622 // Test parsing multiple input files.
623 FileDescriptorSet file_descriptor_set;
624
625 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
626 file_descriptor_proto->set_name("foo.proto");
627 file_descriptor_proto->add_message_type()->set_name("Foo");
628
629 file_descriptor_proto = file_descriptor_set.add_file();
630 file_descriptor_proto->set_name("bar.proto");
631 file_descriptor_proto->add_message_type()->set_name("Bar");
632
633 WriteDescriptorSet("foo.bin", &file_descriptor_set);
634
635 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
636 "--descriptor_set_in=$tmpdir/foo.bin foo.proto bar.proto");
637
638 ExpectNoErrors();
639 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
640 "foo.proto", "Foo");
641 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
642 "bar.proto", "Bar");
643 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
644 "foo.proto", "Foo");
645 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
646 "bar.proto", "Bar");
647 }
648
TEST_F(CommandLineInterfaceTest,MultipleInputs_UnusedImport_DescriptorSetIn)649 TEST_F(CommandLineInterfaceTest, MultipleInputs_UnusedImport_DescriptorSetIn) {
650 // Test unused import warning is not raised when descriptor_set_in is called
651 // and custom options are in unknown field instead of uninterpreted_options.
652 FileDescriptorSet file_descriptor_set;
653
654 const FileDescriptor* descriptor_file =
655 FileDescriptorProto::descriptor()->file();
656 descriptor_file->CopyTo(file_descriptor_set.add_file());
657
658 FileDescriptorProto& any_proto = *file_descriptor_set.add_file();
659 google::protobuf::Any::descriptor()->file()->CopyTo(&any_proto);
660
661 const FileDescriptor* custom_file =
662 protobuf_unittest::AggregateMessage::descriptor()->file();
663 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
664 custom_file->CopyTo(file_descriptor_proto);
665 file_descriptor_proto->set_name("custom_options.proto");
666 // Add a custom message option.
667 FieldDescriptorProto* extension_option =
668 file_descriptor_proto->add_extension();
669 extension_option->set_name("unknown_option");
670 extension_option->set_extendee(".google.protobuf.MessageOptions");
671 extension_option->set_number(1111);
672 extension_option->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
673 extension_option->set_type(FieldDescriptorProto::TYPE_INT64);
674
675 file_descriptor_proto = file_descriptor_set.add_file();
676 file_descriptor_proto->set_name("import_custom_unknown_options.proto");
677 file_descriptor_proto->add_dependency("custom_options.proto");
678 // Add custom message option to unknown field. This custom option is
679 // not known in generated pool, thus option will be in unknown fields.
680 file_descriptor_proto->add_message_type()->set_name("Bar");
681 file_descriptor_proto->mutable_message_type(0)
682 ->mutable_options()
683 ->mutable_unknown_fields()
684 ->AddVarint(1111, 2222);
685
686 WriteDescriptorSet("foo.bin", &file_descriptor_set);
687
688 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
689 "--descriptor_set_in=$tmpdir/foo.bin "
690 "import_custom_unknown_options.proto");
691
692 // TODO: Fix this test. This test case only happens when
693 // CommandLineInterface::Run() is used instead of invoke protoc combined
694 // with descriptor_set_in, and same custom options are defined in both
695 // generated pool and descriptor_set_in. There's no such uages for now but
696 // still need to be fixed.
697 /*
698 file_descriptor_proto = file_descriptor_set.add_file();
699 file_descriptor_proto->set_name("import_custom_extension_options.proto");
700 file_descriptor_proto->add_dependency("custom_options.proto");
701 // Add custom message option to unknown field. This custom option is
702 // also defined in generated pool, thus option will be in extensions.
703 file_descriptor_proto->add_message_type()->set_name("Foo");
704 file_descriptor_proto->mutable_message_type(0)
705 ->mutable_options()
706 ->mutable_unknown_fields()
707 ->AddVarint(protobuf_unittest::message_opt1.number(), 2222);
708
709 WriteDescriptorSet("foo.bin", &file_descriptor_set);
710
711 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
712 "--descriptor_set_in=$tmpdir/foo.bin import_custom_unknown_options.proto "
713 "import_custom_extension_options.proto");
714 */
715
716 ExpectNoErrors();
717 }
718
TEST_F(CommandLineInterfaceTest,MultipleInputsWithImport)719 TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport) {
720 // Test parsing multiple input files with an import of a separate file.
721
722 CreateTempFile("foo.proto",
723 "syntax = \"proto2\";\n"
724 "message Foo {}\n");
725 CreateTempFile("bar.proto",
726 "syntax = \"proto2\";\n"
727 "import \"baz.proto\";\n"
728 "message Bar {\n"
729 " optional Baz a = 1;\n"
730 "}\n");
731 CreateTempFile("baz.proto",
732 "syntax = \"proto2\";\n"
733 "message Baz {}\n");
734
735 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
736 "--proto_path=$tmpdir foo.proto bar.proto");
737
738 ExpectNoErrors();
739 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
740 "foo.proto", "Foo");
741 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
742 "bar.proto", "Bar");
743 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
744 "foo.proto", "Foo");
745 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
746 "bar.proto", "Bar");
747 }
748
749
TEST_F(CommandLineInterfaceTest,MultipleInputsWithImport_DescriptorSetIn)750 TEST_F(CommandLineInterfaceTest, MultipleInputsWithImport_DescriptorSetIn) {
751 // Test parsing multiple input files with an import of a separate file.
752 FileDescriptorSet file_descriptor_set;
753
754 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
755 file_descriptor_proto->set_name("foo.proto");
756 file_descriptor_proto->add_message_type()->set_name("Foo");
757
758 file_descriptor_proto = file_descriptor_set.add_file();
759 file_descriptor_proto->set_name("bar.proto");
760 file_descriptor_proto->add_dependency("baz.proto");
761 DescriptorProto* message = file_descriptor_proto->add_message_type();
762 message->set_name("Bar");
763 FieldDescriptorProto* field = message->add_field();
764 field->set_type_name("Baz");
765 field->set_name("a");
766 field->set_number(1);
767
768 WriteDescriptorSet("foo_and_bar.bin", &file_descriptor_set);
769
770 file_descriptor_set.clear_file();
771 file_descriptor_proto = file_descriptor_set.add_file();
772 file_descriptor_proto->set_name("baz.proto");
773 file_descriptor_proto->add_message_type()->set_name("Baz");
774
775 file_descriptor_proto = file_descriptor_set.add_file();
776 file_descriptor_proto->set_name("bat.proto");
777 file_descriptor_proto->add_dependency("baz.proto");
778 message = file_descriptor_proto->add_message_type();
779 message->set_name("Bat");
780 field = message->add_field();
781 field->set_type_name("Baz");
782 field->set_name("a");
783 field->set_number(1);
784
785 WriteDescriptorSet("baz_and_bat.bin", &file_descriptor_set);
786 Run(absl::Substitute(
787 "protocol_compiler --test_out=$$tmpdir --plug_out=$$tmpdir "
788 "--descriptor_set_in=$0 foo.proto bar.proto",
789 absl::StrCat("$tmpdir/foo_and_bar.bin",
790 CommandLineInterface::kPathSeparator,
791 "$tmpdir/baz_and_bat.bin")));
792
793 ExpectNoErrors();
794 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
795 "foo.proto", "Foo");
796 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
797 "bar.proto", "Bar");
798 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
799 "foo.proto", "Foo");
800 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
801 "bar.proto", "Bar");
802
803 Run(absl::Substitute(
804 "protocol_compiler --test_out=$$tmpdir --plug_out=$$tmpdir "
805 "--descriptor_set_in=$0 baz.proto bat.proto",
806 absl::StrCat("$tmpdir/foo_and_bar.bin",
807 CommandLineInterface::kPathSeparator,
808 "$tmpdir/baz_and_bat.bin")));
809
810 ExpectNoErrors();
811 ExpectGeneratedWithMultipleInputs("test_generator", "baz.proto,bat.proto",
812 "baz.proto", "Baz");
813 ExpectGeneratedWithMultipleInputs("test_generator", "baz.proto,bat.proto",
814 "bat.proto", "Bat");
815 ExpectGeneratedWithMultipleInputs("test_plugin", "baz.proto,bat.proto",
816 "baz.proto", "Baz");
817 ExpectGeneratedWithMultipleInputs("test_plugin", "baz.proto,bat.proto",
818 "bat.proto", "Bat");
819 }
820
TEST_F(CommandLineInterfaceTest,MultipleInputsWithImport_DescriptorSetIn_DuplicateFileDescriptor)821 TEST_F(CommandLineInterfaceTest,
822 MultipleInputsWithImport_DescriptorSetIn_DuplicateFileDescriptor) {
823 // Test parsing multiple input files with an import of a separate file.
824 FileDescriptorSet file_descriptor_set;
825
826 FileDescriptorProto foo_file_descriptor_proto;
827 foo_file_descriptor_proto.set_name("foo.proto");
828 foo_file_descriptor_proto.add_message_type()->set_name("Foo");
829
830 *file_descriptor_set.add_file() = foo_file_descriptor_proto;
831
832 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
833 file_descriptor_proto->set_name("bar.proto");
834 file_descriptor_proto->add_dependency("baz.proto");
835 file_descriptor_proto->add_dependency("foo.proto");
836 DescriptorProto* message = file_descriptor_proto->add_message_type();
837 message->set_name("Bar");
838 FieldDescriptorProto* field = message->add_field();
839 field->set_type_name("Baz");
840 field->set_name("a");
841 field->set_number(1);
842 field = message->add_field();
843 field->set_type_name("Foo");
844 field->set_name("f");
845 field->set_number(2);
846 WriteDescriptorSet("foo_and_bar.bin", &file_descriptor_set);
847
848 file_descriptor_set.clear_file();
849 *file_descriptor_set.add_file() = foo_file_descriptor_proto;
850
851 file_descriptor_proto = file_descriptor_set.add_file();
852 file_descriptor_proto->set_name("baz.proto");
853 file_descriptor_proto->add_dependency("foo.proto");
854 message = file_descriptor_proto->add_message_type();
855 message->set_name("Baz");
856 field = message->add_field();
857 field->set_type_name("Foo");
858 field->set_name("f");
859 field->set_number(1);
860 WriteDescriptorSet("foo_and_baz.bin", &file_descriptor_set);
861
862 Run(absl::Substitute(
863 "protocol_compiler --test_out=$$tmpdir --plug_out=$$tmpdir "
864 "--descriptor_set_in=$0 bar.proto",
865 absl::StrCat("$tmpdir/foo_and_bar.bin",
866 CommandLineInterface::kPathSeparator,
867 "$tmpdir/foo_and_baz.bin")));
868
869 ExpectNoErrors();
870 ExpectGenerated("test_generator", "", "bar.proto", "Bar");
871 ExpectGenerated("test_plugin", "", "bar.proto", "Bar");
872 }
873
TEST_F(CommandLineInterfaceTest,MultipleInputsWithImport_DescriptorSetIn_MissingImport)874 TEST_F(CommandLineInterfaceTest,
875 MultipleInputsWithImport_DescriptorSetIn_MissingImport) {
876 // Test parsing multiple input files with an import of a separate file.
877 FileDescriptorSet file_descriptor_set;
878
879 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
880 file_descriptor_proto->set_name("foo.proto");
881 file_descriptor_proto->add_message_type()->set_name("Foo");
882
883 file_descriptor_proto = file_descriptor_set.add_file();
884 file_descriptor_proto->set_name("bar.proto");
885 file_descriptor_proto->add_dependency("baz.proto");
886 DescriptorProto* message = file_descriptor_proto->add_message_type();
887 message->set_name("Bar");
888 FieldDescriptorProto* field = message->add_field();
889 field->set_type_name("Baz");
890 field->set_name("a");
891 field->set_number(1);
892
893 WriteDescriptorSet("foo_and_bar.bin", &file_descriptor_set);
894
895 file_descriptor_set.clear_file();
896 file_descriptor_proto = file_descriptor_set.add_file();
897 file_descriptor_proto->set_name("baz.proto");
898 file_descriptor_proto->add_message_type()->set_name("Baz");
899
900 WriteDescriptorSet("baz.bin", &file_descriptor_set);
901 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
902 "--descriptor_set_in=$tmpdir/foo_and_bar.bin "
903 "foo.proto bar.proto");
904 ExpectErrorSubstring(
905 "bar.proto: Import \"baz.proto\" was not found or had errors.");
906 ExpectErrorSubstring("bar.proto: \"Baz\" is not defined.");
907 }
908
TEST_F(CommandLineInterfaceTest,InputsOnlyFromDescriptorSetIn_UnusedImportIsNotReported)909 TEST_F(CommandLineInterfaceTest,
910 InputsOnlyFromDescriptorSetIn_UnusedImportIsNotReported) {
911 FileDescriptorSet file_descriptor_set;
912
913 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
914 file_descriptor_proto->set_name("unused.proto");
915 file_descriptor_proto->add_message_type()->set_name("Unused");
916
917 file_descriptor_proto = file_descriptor_set.add_file();
918 file_descriptor_proto->set_name("bar.proto");
919 file_descriptor_proto->add_dependency("unused.proto");
920 file_descriptor_proto->add_message_type()->set_name("Bar");
921
922 WriteDescriptorSet("unused_and_bar.bin", &file_descriptor_set);
923
924 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
925 "--descriptor_set_in=$tmpdir/unused_and_bar.bin unused.proto bar.proto");
926 ExpectNoErrors();
927 }
928
TEST_F(CommandLineInterfaceTest,InputsFromDescriptorSetInAndFileSystem_UnusedImportIsReported)929 TEST_F(CommandLineInterfaceTest,
930 InputsFromDescriptorSetInAndFileSystem_UnusedImportIsReported) {
931 FileDescriptorSet file_descriptor_set;
932
933 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
934 file_descriptor_proto->set_name("unused.proto");
935 file_descriptor_proto->add_message_type()->set_name("Unused");
936
937 file_descriptor_proto = file_descriptor_set.add_file();
938 file_descriptor_proto->set_name("bar.proto");
939 file_descriptor_proto->add_dependency("unused.proto");
940 file_descriptor_proto->add_message_type()->set_name("Bar");
941
942 WriteDescriptorSet("unused_and_bar.bin", &file_descriptor_set);
943
944 CreateTempFile("foo.proto",
945 "syntax = \"proto2\";\n"
946 "import \"bar.proto\";\n"
947 "message Foo {\n"
948 " optional Bar bar = 1;\n"
949 "}\n");
950
951 Run("protocol_compiler --test_out=$tmpdir --plug_out=$tmpdir "
952 "--descriptor_set_in=$tmpdir/unused_and_bar.bin "
953 "--proto_path=$tmpdir unused.proto bar.proto foo.proto");
954 // Reporting unused imports here is unfair, since it's unactionable. Notice
955 // the lack of a line number.
956 // TODO: If the file with unused import is from the descriptor
957 // set and not from the file system, suppress the warning.
958 ExpectWarningSubstring("bar.proto: warning: Import unused.proto is unused.");
959 }
960
TEST_F(CommandLineInterfaceTest,OnlyReportsUnusedImportsForFilesBeingGenerated)961 TEST_F(CommandLineInterfaceTest,
962 OnlyReportsUnusedImportsForFilesBeingGenerated) {
963 CreateTempFile("unused.proto",
964 "syntax = \"proto2\";\n"
965 "message Unused {}\n");
966 CreateTempFile("bar.proto",
967 "syntax = \"proto2\";\n"
968 "import \"unused.proto\";\n"
969 "message Bar {}\n");
970 CreateTempFile("foo.proto",
971 "syntax = \"proto2\";\n"
972 "import \"bar.proto\";\n"
973 "message Foo {\n"
974 " optional Bar bar = 1;\n"
975 "}\n");
976
977 Run("protocol_compiler --test_out=$tmpdir "
978 "--proto_path=$tmpdir foo.proto");
979 ExpectNoErrors();
980 }
981
TEST_F(CommandLineInterfaceTest,ReportsTransitiveMissingImports_LeafFirst)982 TEST_F(CommandLineInterfaceTest, ReportsTransitiveMissingImports_LeafFirst) {
983 CreateTempFile("unused.proto",
984 "syntax = \"proto2\";\n"
985 "message Unused {}\n");
986 CreateTempFile("bar.proto",
987 "syntax = \"proto2\";\n"
988 "import \"unused.proto\";\n"
989 "message Bar {}\n");
990 CreateTempFile("foo.proto",
991 "syntax = \"proto2\";\n"
992 "import \"bar.proto\";\n"
993 "message Foo {\n"
994 " optional Bar bar = 1;\n"
995 "}\n");
996
997 Run("protocol_compiler --test_out=$tmpdir "
998 "--proto_path=$tmpdir bar.proto foo.proto");
999 ExpectWarningSubstring(
1000 "bar.proto:2:1: warning: Import unused.proto is unused.");
1001 }
1002
TEST_F(CommandLineInterfaceTest,ReportsTransitiveMissingImports_LeafLast)1003 TEST_F(CommandLineInterfaceTest, ReportsTransitiveMissingImports_LeafLast) {
1004 CreateTempFile("unused.proto",
1005 "syntax = \"proto2\";\n"
1006 "message Unused {}\n");
1007 CreateTempFile("bar.proto",
1008 "syntax = \"proto2\";\n"
1009 "import \"unused.proto\";\n"
1010 "message Bar {}\n");
1011 CreateTempFile("foo.proto",
1012 "syntax = \"proto2\";\n"
1013 "import \"bar.proto\";\n"
1014 "message Foo {\n"
1015 " optional Bar bar = 1;\n"
1016 "}\n");
1017
1018 Run("protocol_compiler --test_out=$tmpdir "
1019 "--proto_path=$tmpdir foo.proto bar.proto");
1020 ExpectWarningSubstring(
1021 "bar.proto:2:1: warning: Import unused.proto is unused.");
1022 }
TEST_F(CommandLineInterfaceTest,CreateDirectory)1023 TEST_F(CommandLineInterfaceTest, CreateDirectory) {
1024 // Test that when we output to a sub-directory, it is created.
1025
1026 CreateTempFile("bar/baz/foo.proto",
1027 "syntax = \"proto2\";\n"
1028 "message Foo {}\n");
1029 CreateTempDir("out");
1030 CreateTempDir("plugout");
1031
1032 Run("protocol_compiler --test_out=$tmpdir/out --plug_out=$tmpdir/plugout "
1033 "--proto_path=$tmpdir bar/baz/foo.proto");
1034
1035 ExpectNoErrors();
1036 ExpectGenerated("test_generator", "", "bar/baz/foo.proto", "Foo", "out");
1037 ExpectGenerated("test_plugin", "", "bar/baz/foo.proto", "Foo", "plugout");
1038 }
1039
TEST_F(CommandLineInterfaceTest,GeneratorParameters)1040 TEST_F(CommandLineInterfaceTest, GeneratorParameters) {
1041 // Test that generator parameters are correctly parsed from the command line.
1042
1043 CreateTempFile("foo.proto",
1044 "syntax = \"proto2\";\n"
1045 "message Foo {}\n");
1046
1047 Run("protocol_compiler --test_out=TestParameter:$tmpdir "
1048 "--plug_out=TestPluginParameter:$tmpdir "
1049 "--proto_path=$tmpdir foo.proto");
1050
1051 ExpectNoErrors();
1052 ExpectGenerated("test_generator", "TestParameter", "foo.proto", "Foo");
1053 ExpectGenerated("test_plugin", "TestPluginParameter", "foo.proto", "Foo");
1054 }
1055
TEST_F(CommandLineInterfaceTest,ExtraGeneratorParameters)1056 TEST_F(CommandLineInterfaceTest, ExtraGeneratorParameters) {
1057 // Test that generator parameters specified with the option flag are
1058 // correctly passed to the code generator.
1059
1060 CreateTempFile("foo.proto",
1061 "syntax = \"proto2\";\n"
1062 "message Foo {}\n");
1063 // Create the "a" and "b" sub-directories.
1064 CreateTempDir("a");
1065 CreateTempDir("b");
1066
1067 Run("protocol_compiler "
1068 "--test_opt=foo1 "
1069 "--test_out=bar:$tmpdir/a "
1070 "--test_opt=foo2 "
1071 "--test_out=baz:$tmpdir/b "
1072 "--test_opt=foo3 "
1073 "--proto_path=$tmpdir foo.proto");
1074
1075 ExpectNoErrors();
1076 ExpectGenerated("test_generator", "bar,foo1,foo2,foo3", "foo.proto", "Foo",
1077 "a");
1078 ExpectGenerated("test_generator", "baz,foo1,foo2,foo3", "foo.proto", "Foo",
1079 "b");
1080 }
1081
TEST_F(CommandLineInterfaceTest,ExtraPluginParameters)1082 TEST_F(CommandLineInterfaceTest, ExtraPluginParameters) {
1083 // Test that generator parameters specified with the option flag are
1084 // correctly passed to the code generator.
1085
1086 CreateTempFile("foo.proto",
1087 "syntax = \"proto2\";\n"
1088 "message Foo {}\n");
1089 // Create the "a" and "b" sub-directories.
1090 CreateTempDir("a");
1091 CreateTempDir("b");
1092
1093 Run("protocol_compiler "
1094 "--plug_opt=foo1 "
1095 "--plug_out=bar:$tmpdir/a "
1096 "--plug_opt=foo2 "
1097 "--plug_out=baz:$tmpdir/b "
1098 "--plug_opt=foo3 "
1099 "--proto_path=$tmpdir foo.proto");
1100
1101 ExpectNoErrors();
1102 ExpectGenerated("test_plugin", "bar,foo1,foo2,foo3", "foo.proto", "Foo", "a");
1103 ExpectGenerated("test_plugin", "baz,foo1,foo2,foo3", "foo.proto", "Foo", "b");
1104 }
1105
TEST_F(CommandLineInterfaceTest,UnrecognizedExtraParameters)1106 TEST_F(CommandLineInterfaceTest, UnrecognizedExtraParameters) {
1107 CreateTempFile("foo.proto",
1108 "syntax = \"proto2\";\n"
1109 "message Foo {}\n");
1110
1111 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1112 "--unknown_plug_a_opt=Foo "
1113 "--unknown_plug_b_opt=Bar "
1114 "--proto_path=$tmpdir foo.proto");
1115
1116 ExpectErrorSubstring("Unknown flag: --unknown_plug_a_opt");
1117 ExpectErrorSubstring("Unknown flag: --unknown_plug_b_opt");
1118 }
1119
TEST_F(CommandLineInterfaceTest,ExtraPluginParametersForOutParameters)1120 TEST_F(CommandLineInterfaceTest, ExtraPluginParametersForOutParameters) {
1121 // This doesn't rely on the plugin having been registered and instead that
1122 // the existence of --[name]_out is enough to make the --[name]_opt valid.
1123 // However, running out of process plugins found via the search path (i.e. -
1124 // not pre registered with --plugin) isn't support in this test suite, so we
1125 // list the options pre/post the _out directive, and then include _opt that
1126 // will be unknown, and confirm the failure output is about the expected
1127 // unknown directive, which means the other were accepted.
1128 // NOTE: UnrecognizedExtraParameters confirms that if two unknown _opt
1129 // directives appear, they both are reported.
1130
1131 CreateTempFile("foo.proto",
1132 "syntax = \"proto2\";\n"
1133 "message Foo {}\n");
1134
1135 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
1136 "--xyz_opt=foo=bar --xyz_out=$tmpdir "
1137 "--abc_out=$tmpdir --abc_opt=foo=bar "
1138 "--unknown_plug_opt=Foo "
1139 "--proto_path=$tmpdir foo.proto");
1140
1141 ExpectErrorText("Unknown flag: --unknown_plug_opt\n");
1142 }
1143
TEST_F(CommandLineInterfaceTest,Insert)1144 TEST_F(CommandLineInterfaceTest, Insert) {
1145 // Test running a generator that inserts code into another's output.
1146
1147 CreateTempFile("foo.proto",
1148 "syntax = \"proto2\";\n"
1149 "message Foo {}\n");
1150
1151 Run("protocol_compiler "
1152 "--test_out=TestParameter:$tmpdir "
1153 "--plug_out=TestPluginParameter:$tmpdir "
1154 "--test_out=insert=test_generator,test_plugin:$tmpdir "
1155 "--plug_out=insert=test_generator,test_plugin:$tmpdir "
1156 "--proto_path=$tmpdir foo.proto");
1157
1158 ExpectNoErrors();
1159 ExpectGeneratedWithInsertions("test_generator", "TestParameter",
1160 "test_generator,test_plugin", "foo.proto",
1161 "Foo");
1162 ExpectGeneratedWithInsertions("test_plugin", "TestPluginParameter",
1163 "test_generator,test_plugin", "foo.proto",
1164 "Foo");
1165 }
1166
TEST_F(CommandLineInterfaceTest,InsertWithAnnotationFixup)1167 TEST_F(CommandLineInterfaceTest, InsertWithAnnotationFixup) {
1168 // Check that annotation spans are updated after insertions.
1169
1170 CreateTempFile("foo.proto",
1171 "syntax = \"proto2\";\n"
1172 "message MockCodeGenerator_Annotate {}\n");
1173
1174 Run("protocol_compiler "
1175 "--test_out=TestParameter:$tmpdir "
1176 "--plug_out=TestPluginParameter:$tmpdir "
1177 "--test_out=insert_endlines=test_generator,test_plugin:$tmpdir "
1178 "--plug_out=insert_endlines=test_generator,test_plugin:$tmpdir "
1179 "--proto_path=$tmpdir foo.proto");
1180
1181 ExpectNoErrors();
1182 CheckGeneratedAnnotations("test_generator", "foo.proto");
1183 CheckGeneratedAnnotations("test_plugin", "foo.proto");
1184 }
1185
1186 #if defined(_WIN32)
1187
TEST_F(CommandLineInterfaceTest,WindowsOutputPath)1188 TEST_F(CommandLineInterfaceTest, WindowsOutputPath) {
1189 // Test that the output path can be a Windows-style path.
1190
1191 CreateTempFile("foo.proto", "syntax = \"proto2\";\n");
1192
1193 Run("protocol_compiler --null_out=C:\\ "
1194 "--proto_path=$tmpdir foo.proto");
1195
1196 ExpectNoErrors();
1197 ExpectNullCodeGeneratorCalled("");
1198 }
1199
TEST_F(CommandLineInterfaceTest,WindowsOutputPathAndParameter)1200 TEST_F(CommandLineInterfaceTest, WindowsOutputPathAndParameter) {
1201 // Test that we can have a windows-style output path and a parameter.
1202
1203 CreateTempFile("foo.proto", "syntax = \"proto2\";\n");
1204
1205 Run("protocol_compiler --null_out=bar:C:\\ "
1206 "--proto_path=$tmpdir foo.proto");
1207
1208 ExpectNoErrors();
1209 ExpectNullCodeGeneratorCalled("bar");
1210 }
1211
TEST_F(CommandLineInterfaceTest,TrailingBackslash)1212 TEST_F(CommandLineInterfaceTest, TrailingBackslash) {
1213 // Test that the directories can end in backslashes. Some users claim this
1214 // doesn't work on their system.
1215
1216 CreateTempFile("foo.proto",
1217 "syntax = \"proto2\";\n"
1218 "message Foo {}\n");
1219
1220 Run("protocol_compiler --test_out=$tmpdir\\ "
1221 "--proto_path=$tmpdir\\ foo.proto");
1222
1223 ExpectNoErrors();
1224 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1225 }
1226
TEST_F(CommandLineInterfaceTest,Win32ErrorMessage)1227 TEST_F(CommandLineInterfaceTest, Win32ErrorMessage) {
1228 EXPECT_EQ("The system cannot find the file specified.\r\n",
1229 Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND));
1230 }
1231
1232 #endif // defined(_WIN32) || defined(__CYGWIN__)
1233
TEST_F(CommandLineInterfaceTest,PathLookup)1234 TEST_F(CommandLineInterfaceTest, PathLookup) {
1235 // Test that specifying multiple directories in the proto search path works.
1236
1237 CreateTempFile("b/bar.proto",
1238 "syntax = \"proto2\";\n"
1239 "message Bar {}\n");
1240 CreateTempFile("a/foo.proto",
1241 "syntax = \"proto2\";\n"
1242 "import \"bar.proto\";\n"
1243 "message Foo {\n"
1244 " optional Bar a = 1;\n"
1245 "}\n");
1246 CreateTempFile("b/foo.proto", "this should not be parsed\n");
1247
1248 Run("protocol_compiler --test_out=$tmpdir "
1249 "--proto_path=$tmpdir/a --proto_path=$tmpdir/b foo.proto");
1250
1251 ExpectNoErrors();
1252 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1253 }
1254
TEST_F(CommandLineInterfaceTest,ColonDelimitedPath)1255 TEST_F(CommandLineInterfaceTest, ColonDelimitedPath) {
1256 // Same as PathLookup, but we provide the proto_path in a single flag.
1257
1258 CreateTempFile("b/bar.proto",
1259 "syntax = \"proto2\";\n"
1260 "message Bar {}\n");
1261 CreateTempFile("a/foo.proto",
1262 "syntax = \"proto2\";\n"
1263 "import \"bar.proto\";\n"
1264 "message Foo {\n"
1265 " optional Bar a = 1;\n"
1266 "}\n");
1267 CreateTempFile("b/foo.proto", "this should not be parsed\n");
1268
1269 Run(absl::Substitute(
1270 "protocol_compiler --test_out=$$tmpdir --proto_path=$0 foo.proto",
1271 std::string("$tmpdir/a") + CommandLineInterface::kPathSeparator +
1272 "$tmpdir/b"));
1273
1274 ExpectNoErrors();
1275 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1276 }
1277
TEST_F(CommandLineInterfaceTest,NonRootMapping)1278 TEST_F(CommandLineInterfaceTest, NonRootMapping) {
1279 // Test setting up a search path mapping a directory to a non-root location.
1280
1281 CreateTempFile("foo.proto",
1282 "syntax = \"proto2\";\n"
1283 "message Foo {}\n");
1284
1285 Run("protocol_compiler --test_out=$tmpdir "
1286 "--proto_path=bar=$tmpdir bar/foo.proto");
1287
1288 ExpectNoErrors();
1289 ExpectGenerated("test_generator", "", "bar/foo.proto", "Foo");
1290 }
1291
TEST_F(CommandLineInterfaceTest,PathWithEqualsSign)1292 TEST_F(CommandLineInterfaceTest, PathWithEqualsSign) {
1293 // Test setting up a search path which happens to have '=' in it.
1294
1295 CreateTempDir("with=sign");
1296 CreateTempFile("with=sign/foo.proto",
1297 "syntax = \"proto2\";\n"
1298 "message Foo {}\n");
1299
1300 Run("protocol_compiler --test_out=$tmpdir "
1301 "--proto_path=$tmpdir/with=sign foo.proto");
1302
1303 ExpectNoErrors();
1304 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1305 }
1306
TEST_F(CommandLineInterfaceTest,MultipleGenerators)1307 TEST_F(CommandLineInterfaceTest, MultipleGenerators) {
1308 // Test that we can have multiple generators and use both in one invocation,
1309 // each with a different output directory.
1310
1311 CreateTempFile("foo.proto",
1312 "syntax = \"proto2\";\n"
1313 "message Foo {}\n");
1314 // Create the "a" and "b" sub-directories.
1315 CreateTempDir("a");
1316 CreateTempDir("b");
1317
1318 Run("protocol_compiler "
1319 "--test_out=$tmpdir/a "
1320 "--alt_out=$tmpdir/b "
1321 "--proto_path=$tmpdir foo.proto");
1322
1323 ExpectNoErrors();
1324 ExpectGenerated("test_generator", "", "foo.proto", "Foo", "a");
1325 ExpectGenerated("alt_generator", "", "foo.proto", "Foo", "b");
1326 }
1327
TEST_F(CommandLineInterfaceTest,DisallowServicesNoServices)1328 TEST_F(CommandLineInterfaceTest, DisallowServicesNoServices) {
1329 // Test that --disallow_services doesn't cause a problem when there are no
1330 // services.
1331
1332 CreateTempFile("foo.proto",
1333 "syntax = \"proto2\";\n"
1334 "message Foo {}\n");
1335
1336 Run("protocol_compiler --disallow_services --test_out=$tmpdir "
1337 "--proto_path=$tmpdir foo.proto");
1338
1339 ExpectNoErrors();
1340 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1341 }
1342
TEST_F(CommandLineInterfaceTest,DisallowServicesHasService)1343 TEST_F(CommandLineInterfaceTest, DisallowServicesHasService) {
1344 // Test that --disallow_services produces an error when there are services.
1345
1346 CreateTempFile("foo.proto",
1347 "syntax = \"proto2\";\n"
1348 "message Foo {}\n"
1349 "service Bar {}\n");
1350
1351 Run("protocol_compiler --disallow_services --test_out=$tmpdir "
1352 "--proto_path=$tmpdir foo.proto");
1353
1354 ExpectErrorSubstring("foo.proto: This file contains services");
1355 }
1356
TEST_F(CommandLineInterfaceTest,AllowServicesHasService)1357 TEST_F(CommandLineInterfaceTest, AllowServicesHasService) {
1358 // Test that services work fine as long as --disallow_services is not used.
1359
1360 CreateTempFile("foo.proto",
1361 "syntax = \"proto2\";\n"
1362 "message Foo {}\n"
1363 "service Bar {}\n");
1364
1365 Run("protocol_compiler --test_out=$tmpdir "
1366 "--proto_path=$tmpdir foo.proto");
1367
1368 ExpectNoErrors();
1369 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
1370 }
1371
TEST_F(CommandLineInterfaceTest,NonExperimentalEditions)1372 TEST_F(CommandLineInterfaceTest, NonExperimentalEditions) {
1373 CreateTempFile("foo.proto",
1374 "edition = \"2023\";\n"
1375 "message FooRequest {}\n");
1376
1377 Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir foo.proto");
1378
1379 ExpectNoErrors();
1380 }
1381
TEST_F(CommandLineInterfaceTest,EditionsFlagExplicitTrue)1382 TEST_F(CommandLineInterfaceTest, EditionsFlagExplicitTrue) {
1383 CreateTempFile("foo.proto",
1384 "edition = \"2023\";\n"
1385 "message FooRequest {}\n");
1386
1387 Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir foo.proto");
1388 ExpectNoErrors();
1389 }
1390
TEST_F(CommandLineInterfaceTest,FeaturesEditionZero)1391 TEST_F(CommandLineInterfaceTest, FeaturesEditionZero) {
1392 CreateTempFile("foo.proto",
1393 R"schema(
1394 edition = "2023";
1395 option features.field_presence = IMPLICIT;
1396 message Foo {
1397 int32 bar = 1 [default = 5, features.field_presence = EXPLICIT];
1398 int32 baz = 2;
1399 })schema");
1400
1401 Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir foo.proto");
1402 ExpectNoErrors();
1403 }
1404
TEST_F(CommandLineInterfaceTest,FeatureExtensions)1405 TEST_F(CommandLineInterfaceTest, FeatureExtensions) {
1406 CreateTempFile("google/protobuf/descriptor.proto",
1407 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
1408 CreateTempFile("features.proto",
1409 R"schema(
1410 syntax = "proto2";
1411 package pb;
1412 import "google/protobuf/descriptor.proto";
1413 extend google.protobuf.FeatureSet {
1414 optional TestFeatures test = 9999;
1415 }
1416 message TestFeatures {
1417 optional int32 int_feature = 1 [
1418 retention = RETENTION_RUNTIME,
1419 targets = TARGET_TYPE_FIELD,
1420 edition_defaults = { edition: EDITION_2023, value: "3" }
1421 ];
1422 })schema");
1423 CreateTempFile("foo.proto",
1424 R"schema(
1425 edition = "2023";
1426 import "features.proto";
1427 message Foo {
1428 int32 bar = 1;
1429 int32 baz = 2 [features.(pb.test).int_feature = 5];
1430 })schema");
1431
1432 Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir foo.proto");
1433 ExpectNoErrors();
1434 }
1435
TEST_F(CommandLineInterfaceTest,FeatureValidationError)1436 TEST_F(CommandLineInterfaceTest, FeatureValidationError) {
1437 CreateTempFile("foo.proto",
1438 R"schema(
1439 edition = "2023";
1440 option features.field_presence = IMPLICIT;
1441 message Foo {
1442 int32 bar = 1 [default = 5, features.field_presence = FIELD_PRESENCE_UNKNOWN];
1443 int32 baz = 2;
1444 })schema");
1445
1446 Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir foo.proto");
1447 ExpectErrorSubstring(
1448 "`field_presence` must resolve to a known value, found "
1449 "FIELD_PRESENCE_UNKNOWN");
1450 }
1451
TEST_F(CommandLineInterfaceTest,FeatureTargetError)1452 TEST_F(CommandLineInterfaceTest, FeatureTargetError) {
1453 CreateTempFile("foo.proto",
1454 R"schema(
1455 edition = "2023";
1456 message Foo {
1457 option features.field_presence = IMPLICIT;
1458 int32 bar = 1 [default = 5, features.field_presence = EXPLICIT];
1459 int32 baz = 2;
1460 })schema");
1461
1462 Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir foo.proto");
1463 ExpectErrorSubstring(
1464 "FeatureSet.field_presence cannot be set on an entity of type `message`");
1465 }
1466
TEST_F(CommandLineInterfaceTest,FeatureExtensionError)1467 TEST_F(CommandLineInterfaceTest, FeatureExtensionError) {
1468 CreateTempFile("google/protobuf/descriptor.proto",
1469 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
1470 CreateTempFile("features.proto",
1471 pb::TestInvalidFeatures::descriptor()->file()->DebugString());
1472 CreateTempFile("foo.proto",
1473 R"schema(
1474 edition = "2023";
1475 import "features.proto";
1476 message Foo {
1477 int32 bar = 1;
1478 int32 baz = 2 [features.(pb.test_invalid).repeated_feature = 5];
1479 })schema");
1480
1481 mock_generator_->set_feature_extensions(
1482 {GetExtensionReflection(pb::test_invalid)});
1483
1484 Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir foo.proto");
1485 ExpectErrorSubstring(
1486 "Feature field pb.TestInvalidFeatures.repeated_feature is an unsupported "
1487 "repeated field");
1488 }
1489
TEST_F(CommandLineInterfaceTest,InvalidMinimumEditionError)1490 TEST_F(CommandLineInterfaceTest, InvalidMinimumEditionError) {
1491 CreateTempFile("foo.proto", R"schema(edition = "2023";)schema");
1492
1493 mock_generator_->set_minimum_edition(EDITION_1_TEST_ONLY);
1494
1495 Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir foo.proto");
1496 ExpectErrorSubstring(
1497 "generator --test_out specifies a minimum edition 1_TEST_ONLY which is "
1498 "not the protoc minimum PROTO2");
1499 }
1500
TEST_F(CommandLineInterfaceTest,InvalidMaximumEditionError)1501 TEST_F(CommandLineInterfaceTest, InvalidMaximumEditionError) {
1502 CreateTempFile("foo.proto", R"schema(edition = "2023";)schema");
1503
1504 mock_generator_->set_maximum_edition(EDITION_99999_TEST_ONLY);
1505
1506 Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir foo.proto");
1507 ExpectErrorSubstring(
1508 "generator --test_out specifies a maximum edition 99999_TEST_ONLY which "
1509 "is not the protoc maximum 2023");
1510 }
1511
TEST_F(CommandLineInterfaceTest,InvalidFeatureExtensionError)1512 TEST_F(CommandLineInterfaceTest, InvalidFeatureExtensionError) {
1513 CreateTempFile("foo.proto", R"schema(edition = "2023";)schema");
1514
1515 mock_generator_->set_feature_extensions({nullptr});
1516
1517 Run("protocol_compiler --proto_path=$tmpdir --test_out=$tmpdir foo.proto");
1518 ExpectErrorSubstring(
1519 "generator --test_out specifies an unknown feature extension");
1520 }
1521
TEST_F(CommandLineInterfaceTest,Plugin_InvalidFeatureExtensionError)1522 TEST_F(CommandLineInterfaceTest, Plugin_InvalidFeatureExtensionError) {
1523 CreateTempFile("foo.proto", R"schema(
1524 edition = "2023";
1525 message Foo {
1526 int32 i = 1;
1527 }
1528 )schema");
1529
1530 SetMockGeneratorTestCase("invalid_features");
1531 Run("protocol_compiler --proto_path=$tmpdir foo.proto --plug_out=$tmpdir");
1532
1533 ExpectErrorSubstring(
1534 "error generating feature defaults: Unknown extension of "
1535 "google.protobuf.FeatureSet");
1536 }
1537
TEST_F(CommandLineInterfaceTest,Plugin_DeprecatedEdition)1538 TEST_F(CommandLineInterfaceTest, Plugin_DeprecatedEdition) {
1539 CreateTempFile("foo.proto", R"schema(
1540 edition = "2023";
1541 message Foo {
1542 int32 i = 1;
1543 }
1544 )schema");
1545
1546 SetMockGeneratorTestCase("high_minimum");
1547 Run("protocol_compiler "
1548 "--proto_path=$tmpdir foo.proto --plug_out=$tmpdir");
1549
1550 ExpectErrorSubstring(
1551 "foo.proto: is a file using edition 2023, which isn't supported by code "
1552 "generator prefix-gen-plug. Please upgrade your file to at least "
1553 "edition 99997_TEST_ONLY.");
1554 }
1555
TEST_F(CommandLineInterfaceTest,Plugin_DeprecatedFeature)1556 TEST_F(CommandLineInterfaceTest, Plugin_DeprecatedFeature) {
1557 CreateTempFile("google/protobuf/descriptor.proto",
1558 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
1559 CreateTempFile("google/protobuf/unittest_features.proto",
1560 pb::TestFeatures::descriptor()->file()->DebugString());
1561 CreateTempFile("foo.proto",
1562 R"schema(
1563 edition = "2023";
1564 import "google/protobuf/unittest_features.proto";
1565 package foo;
1566 option features.(pb.test).removed_feature = VALUE9;
1567 )schema");
1568
1569 Run("protocol_compiler --test_out=$tmpdir "
1570 "--proto_path=$tmpdir foo.proto");
1571 ExpectWarningSubstring(
1572 "foo.proto:4:5: warning: Feature pb.TestFeatures.removed_feature has "
1573 "been deprecated in edition 2023: Custom feature deprecation warning\n");
1574 }
1575
TEST_F(CommandLineInterfaceTest,Plugin_TransitiveDeprecatedFeature)1576 TEST_F(CommandLineInterfaceTest, Plugin_TransitiveDeprecatedFeature) {
1577 CreateTempFile("google/protobuf/descriptor.proto",
1578 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
1579 CreateTempFile("google/protobuf/unittest_features.proto",
1580 pb::TestFeatures::descriptor()->file()->DebugString());
1581 CreateTempFile("unused.proto",
1582 R"schema(
1583 edition = "2023";
1584 import "google/protobuf/unittest_features.proto";
1585 package foo;
1586 option features.(pb.test).removed_feature = VALUE9;
1587 message Foo {}
1588 )schema");
1589 CreateTempFile("foo.proto",
1590 R"schema(
1591 edition = "2023";
1592 import "unused.proto";
1593 package foo;
1594 message Bar {
1595 Foo foo = 1;
1596 }
1597 )schema");
1598
1599 Run("protocol_compiler --test_out=$tmpdir "
1600 "--proto_path=$tmpdir foo.proto");
1601 ExpectNoErrors();
1602 }
1603
TEST_F(CommandLineInterfaceTest,Plugin_FutureEdition)1604 TEST_F(CommandLineInterfaceTest, Plugin_FutureEdition) {
1605 CreateTempFile("foo.proto", R"schema(
1606 edition = "2023";
1607 message Foo {
1608 int32 i = 1;
1609 }
1610 )schema");
1611
1612 SetMockGeneratorTestCase("low_maximum");
1613 Run("protocol_compiler "
1614 "--proto_path=$tmpdir foo.proto --plug_out=$tmpdir");
1615
1616 ExpectErrorSubstring(
1617 "foo.proto: is a file using edition 2023, which isn't supported by code "
1618 "generator prefix-gen-plug. Please ask the owner of this code generator "
1619 "to add support or switch back to a maximum of edition PROTO2.");
1620 }
1621
TEST_F(CommandLineInterfaceTest,Plugin_VersionSkewFuture)1622 TEST_F(CommandLineInterfaceTest, Plugin_VersionSkewFuture) {
1623 CreateTempFile("foo.proto", R"schema(
1624 edition = "99997_TEST_ONLY";
1625 message Foo {
1626 int32 i = 1;
1627 }
1628 )schema");
1629
1630 SetMockGeneratorTestCase("high_maximum");
1631 Run("protocol_compiler "
1632 "--proto_path=$tmpdir foo.proto --plug_out=$tmpdir");
1633
1634 ExpectErrorSubstring(
1635 "foo.proto:2:5: Edition 99997_TEST_ONLY is later than the maximum "
1636 "supported edition 2023");
1637 }
1638
TEST_F(CommandLineInterfaceTest,Plugin_VersionSkewPast)1639 TEST_F(CommandLineInterfaceTest, Plugin_VersionSkewPast) {
1640 CreateTempFile("foo.proto", R"schema(
1641 edition = "1_TEST_ONLY";
1642 message Foo {
1643 int32 i = 1;
1644 }
1645 )schema");
1646
1647 SetMockGeneratorTestCase("low_minimum");
1648 Run("protocol_compiler "
1649 "--proto_path=$tmpdir foo.proto --plug_out=$tmpdir");
1650
1651 ExpectErrorSubstring(
1652 "foo.proto:2:5: Edition 1_TEST_ONLY is earlier than the minimum "
1653 "supported edition PROTO2");
1654 }
1655
TEST_F(CommandLineInterfaceTest,Plugin_MissingFeatureExtensionError)1656 TEST_F(CommandLineInterfaceTest, Plugin_MissingFeatureExtensionError) {
1657 CreateTempFile("foo.proto", R"schema(
1658 edition = "2023";
1659 message Foo {
1660 int32 i = 1;
1661 }
1662 )schema");
1663
1664 SetMockGeneratorTestCase("no_feature_defaults");
1665 Run("protocol_compiler --proto_path=$tmpdir foo.proto --plug_out=$tmpdir");
1666
1667 ExpectErrorSubstring("Test features were not resolved properly");
1668 }
1669
TEST_F(CommandLineInterfaceTest,Plugin_TestFeatures)1670 TEST_F(CommandLineInterfaceTest, Plugin_TestFeatures) {
1671 CreateTempFile("foo.proto", R"schema(
1672 edition = "2023";
1673 message Foo {
1674 int32 i = 1;
1675 }
1676 )schema");
1677
1678 Run("protocol_compiler --proto_path=$tmpdir foo.proto --plug_out=$tmpdir");
1679
1680 ExpectNoErrors();
1681 }
1682
TEST_F(CommandLineInterfaceTest,Plugin_LegacyFeatures)1683 TEST_F(CommandLineInterfaceTest, Plugin_LegacyFeatures) {
1684 CreateTempFile("foo.proto",
1685 R"schema(
1686 syntax = "proto2";
1687 package foo;
1688 message Foo {
1689 optional int32 b = 1;
1690 })schema");
1691
1692 #ifdef GOOGLE_PROTOBUF_FAKE_PLUGIN_PATH
1693 std::string plugin_path = GOOGLE_PROTOBUF_FAKE_PLUGIN_PATH;
1694 #else
1695 std::string plugin_path = absl::StrCat(
1696 TestUtil::TestSourceDir(), "/google/protobuf/compiler/fake_plugin");
1697 #endif
1698
1699 // Invoke protoc with fake_plugin to get ahold of the CodeGeneratorRequest
1700 // sent by protoc.
1701 Run(absl::StrCat(
1702 "protocol_compiler --fake_plugin_out=$tmpdir --proto_path=$tmpdir "
1703 "foo.proto --plugin=prefix-gen-fake_plugin=",
1704 plugin_path));
1705 ExpectNoErrors();
1706 std::string base64_output = ReadFile("foo.proto.request");
1707 std::string binary_request;
1708 ASSERT_TRUE(absl::Base64Unescape(base64_output, &binary_request));
1709 CodeGeneratorRequest request;
1710 ASSERT_TRUE(request.ParseFromString(binary_request));
1711
1712 EXPECT_FALSE(
1713 request.proto_file(0).message_type(0).field(0).options().has_features());
1714 EXPECT_FALSE(request.source_file_descriptors(0)
1715 .message_type(0)
1716 .field(0)
1717 .options()
1718 .has_features());
1719 }
1720
TEST_F(CommandLineInterfaceTest,Plugin_RuntimeFeatures)1721 TEST_F(CommandLineInterfaceTest, Plugin_RuntimeFeatures) {
1722 CreateTempFile("foo.proto",
1723 R"schema(
1724 edition = "2023";
1725 package foo;
1726 message Foo {
1727 int32 b = 1 [features.field_presence = IMPLICIT];
1728 })schema");
1729
1730 #ifdef GOOGLE_PROTOBUF_FAKE_PLUGIN_PATH
1731 std::string plugin_path = GOOGLE_PROTOBUF_FAKE_PLUGIN_PATH;
1732 #else
1733 std::string plugin_path = absl::StrCat(
1734 TestUtil::TestSourceDir(), "/google/protobuf/compiler/fake_plugin");
1735 #endif
1736
1737 // Invoke protoc with fake_plugin to get ahold of the CodeGeneratorRequest
1738 // sent by protoc.
1739 Run(absl::StrCat(
1740 "protocol_compiler --fake_plugin_out=$tmpdir --proto_path=$tmpdir "
1741 "foo.proto --plugin=prefix-gen-fake_plugin=",
1742 plugin_path));
1743 ExpectNoErrors();
1744 std::string base64_output = ReadFile("foo.proto.request");
1745 std::string binary_request;
1746 ASSERT_TRUE(absl::Base64Unescape(base64_output, &binary_request));
1747 CodeGeneratorRequest request;
1748 ASSERT_TRUE(request.ParseFromString(binary_request));
1749
1750 EXPECT_THAT(
1751 request.proto_file(0).message_type(0).field(0).options().features(),
1752 EqualsProto(R"pb(field_presence: IMPLICIT)pb"));
1753 EXPECT_THAT(request.source_file_descriptors(0)
1754 .message_type(0)
1755 .field(0)
1756 .options()
1757 .features(),
1758 EqualsProto(R"pb(field_presence: IMPLICIT)pb"));
1759 }
1760
TEST_F(CommandLineInterfaceTest,Plugin_SourceFeatures)1761 TEST_F(CommandLineInterfaceTest, Plugin_SourceFeatures) {
1762 CreateTempFile("google/protobuf/descriptor.proto",
1763 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
1764 CreateTempFile("google/protobuf/unittest_features.proto",
1765 pb::TestFeatures::descriptor()->file()->DebugString());
1766 CreateTempFile("foo.proto",
1767 R"schema(
1768 edition = "2023";
1769 import "google/protobuf/unittest_features.proto";
1770 package foo;
1771 message Foo {
1772 int32 b = 1 [
1773 features.(pb.test).field_feature = VALUE6,
1774 features.(pb.test).source_feature = VALUE5
1775 ];
1776 }
1777 )schema");
1778
1779 #ifdef GOOGLE_PROTOBUF_FAKE_PLUGIN_PATH
1780 std::string plugin_path = GOOGLE_PROTOBUF_FAKE_PLUGIN_PATH;
1781 #else
1782 std::string plugin_path = absl::StrCat(
1783 TestUtil::TestSourceDir(), "/google/protobuf/compiler/fake_plugin");
1784 #endif
1785
1786 // Invoke protoc with fake_plugin to get ahold of the CodeGeneratorRequest
1787 // sent by protoc.
1788 Run(absl::StrCat(
1789 "protocol_compiler --fake_plugin_out=$tmpdir --proto_path=$tmpdir "
1790 "foo.proto --plugin=prefix-gen-fake_plugin=",
1791 plugin_path));
1792 ExpectNoErrors();
1793 std::string base64_output = ReadFile("foo.proto.request");
1794 std::string binary_request;
1795 ASSERT_TRUE(absl::Base64Unescape(base64_output, &binary_request));
1796 CodeGeneratorRequest request;
1797 ASSERT_TRUE(request.ParseFromString(binary_request));
1798
1799 {
1800 ASSERT_EQ(request.proto_file(2).name(), "foo.proto");
1801 const FeatureSet& features =
1802 request.proto_file(2).message_type(0).field(0).options().features();
1803 EXPECT_THAT(features,
1804 EqualsProto(R"pb([pb.test] { field_feature: VALUE6 })pb"));
1805 }
1806
1807 {
1808 ASSERT_EQ(request.source_file_descriptors(0).name(), "foo.proto");
1809 const FeatureSet& features = request.source_file_descriptors(0)
1810 .message_type(0)
1811 .field(0)
1812 .options()
1813 .features();
1814 EXPECT_THAT(features, EqualsProto(R"pb([pb.test] {
1815 field_feature: VALUE6
1816 source_feature: VALUE5
1817 })pb"));
1818 }
1819 }
1820
TEST_F(CommandLineInterfaceTest,GeneratorFeatureLifetimeError)1821 TEST_F(CommandLineInterfaceTest, GeneratorFeatureLifetimeError) {
1822 CreateTempFile("google/protobuf/descriptor.proto",
1823 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
1824 CreateTempFile("google/protobuf/unittest_features.proto",
1825 pb::TestFeatures::descriptor()->file()->DebugString());
1826 CreateTempFile("foo.proto",
1827 R"schema(
1828 edition = "2024";
1829 import "google/protobuf/unittest_features.proto";
1830 package foo;
1831 message Foo {
1832 int32 b = 1 [
1833 features.(pb.test).removed_feature = VALUE6
1834 ];
1835 }
1836 )schema");
1837
1838 Run("protocol_compiler --experimental_editions --proto_path=$tmpdir "
1839 "--test_out=$tmpdir foo.proto");
1840 ExpectErrorSubstring(
1841 "foo.proto:6:13: Feature pb.TestFeatures.removed_feature has been "
1842 "removed in edition 2024");
1843 }
1844
TEST_F(CommandLineInterfaceTest,PluginFeatureLifetimeError)1845 TEST_F(CommandLineInterfaceTest, PluginFeatureLifetimeError) {
1846 CreateTempFile("google/protobuf/descriptor.proto",
1847 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
1848 CreateTempFile("google/protobuf/unittest_features.proto",
1849 pb::TestFeatures::descriptor()->file()->DebugString());
1850 CreateTempFile("foo.proto",
1851 R"schema(
1852 edition = "2023";
1853 import "google/protobuf/unittest_features.proto";
1854 package foo;
1855 message Foo {
1856 int32 b = 1 [
1857 features.(pb.test).future_feature = VALUE6
1858 ];
1859 }
1860 )schema");
1861
1862 #ifdef GOOGLE_PROTOBUF_FAKE_PLUGIN_PATH
1863 std::string plugin_path = GOOGLE_PROTOBUF_FAKE_PLUGIN_PATH;
1864 #else
1865 std::string plugin_path = absl::StrCat(
1866 TestUtil::TestSourceDir(), "/google/protobuf/compiler/fake_plugin");
1867 #endif
1868
1869 Run(absl::StrCat(
1870 "protocol_compiler --fake_plugin_out=$tmpdir --proto_path=$tmpdir "
1871 "foo.proto --plugin=prefix-gen-fake_plugin=",
1872 plugin_path));
1873 ExpectErrorSubstring(
1874 "foo.proto:6:13: Feature pb.TestFeatures.future_feature wasn't "
1875 "introduced until edition 2024");
1876 }
1877
TEST_F(CommandLineInterfaceTest,GeneratorNoEditionsSupport)1878 TEST_F(CommandLineInterfaceTest, GeneratorNoEditionsSupport) {
1879 CreateTempFile("foo.proto", R"schema(
1880 edition = "2023";
1881 message Foo {
1882 int32 i = 1;
1883 }
1884 )schema");
1885
1886 CreateGeneratorWithMissingFeatures("--no_editions_out",
1887 "Doesn't support editions",
1888 CodeGenerator::FEATURE_SUPPORTS_EDITIONS);
1889
1890 Run("protocol_compiler "
1891 "--proto_path=$tmpdir foo.proto --no_editions_out=$tmpdir");
1892
1893 ExpectErrorSubstring(
1894 "code generator --no_editions_out hasn't been updated to support "
1895 "editions");
1896 }
1897
TEST_F(CommandLineInterfaceTest,PluginNoEditionsSupport)1898 TEST_F(CommandLineInterfaceTest, PluginNoEditionsSupport) {
1899 CreateTempFile("foo.proto", R"schema(
1900 edition = "2023";
1901 message Foo {
1902 int32 i = 1;
1903 }
1904 )schema");
1905
1906 SetMockGeneratorTestCase("no_editions");
1907 Run("protocol_compiler "
1908 "--proto_path=$tmpdir foo.proto --plug_out=$tmpdir");
1909
1910 ExpectErrorSubstring(
1911 "code generator prefix-gen-plug hasn't been updated to support editions");
1912 }
1913
TEST_F(CommandLineInterfaceTest,PluginErrorAndNoEditionsSupport)1914 TEST_F(CommandLineInterfaceTest, PluginErrorAndNoEditionsSupport) {
1915 CreateTempFile("foo.proto", R"schema(
1916 edition = "2023";
1917 message MockCodeGenerator_Error { }
1918 )schema");
1919
1920 SetMockGeneratorTestCase("no_editions");
1921 Run("protocol_compiler "
1922 "--proto_path=$tmpdir foo.proto --plug_out=$tmpdir");
1923
1924 ExpectErrorSubstring(
1925 "code generator prefix-gen-plug hasn't been updated to support editions");
1926 ExpectErrorSubstring(
1927 "--plug_out: foo.proto: Saw message type MockCodeGenerator_Error.");
1928 }
1929
TEST_F(CommandLineInterfaceTest,EditionDefaults)1930 TEST_F(CommandLineInterfaceTest, EditionDefaults) {
1931 CreateTempFile("google/protobuf/descriptor.proto",
1932 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
1933 Run("protocol_compiler --proto_path=$tmpdir "
1934 "--edition_defaults_out=$tmpdir/defaults "
1935 "google/protobuf/descriptor.proto");
1936 ExpectNoErrors();
1937
1938 FeatureSetDefaults defaults = ReadEditionDefaults("defaults");
1939 EXPECT_THAT(defaults, EqualsProto(R"pb(
1940 defaults {
1941 edition: EDITION_LEGACY
1942 overridable_features {}
1943 fixed_features {
1944 field_presence: EXPLICIT
1945 enum_type: CLOSED
1946 repeated_field_encoding: EXPANDED
1947 utf8_validation: NONE
1948 message_encoding: LENGTH_PREFIXED
1949 json_format: LEGACY_BEST_EFFORT
1950 }
1951 }
1952 defaults {
1953 edition: EDITION_PROTO3
1954 overridable_features {}
1955 fixed_features {
1956 field_presence: IMPLICIT
1957 enum_type: OPEN
1958 repeated_field_encoding: PACKED
1959 utf8_validation: VERIFY
1960 message_encoding: LENGTH_PREFIXED
1961 json_format: ALLOW
1962 }
1963 }
1964 defaults {
1965 edition: EDITION_2023
1966 overridable_features {
1967 field_presence: EXPLICIT
1968 enum_type: OPEN
1969 repeated_field_encoding: PACKED
1970 utf8_validation: VERIFY
1971 message_encoding: LENGTH_PREFIXED
1972 json_format: ALLOW
1973 }
1974 fixed_features {}
1975 }
1976 minimum_edition: EDITION_PROTO2
1977 maximum_edition: EDITION_2023
1978 )pb"));
1979 }
1980
TEST_F(CommandLineInterfaceTest,EditionDefaultsWithMaximum)1981 TEST_F(CommandLineInterfaceTest, EditionDefaultsWithMaximum) {
1982 CreateTempFile("google/protobuf/descriptor.proto",
1983 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
1984 Run("protocol_compiler --proto_path=$tmpdir "
1985 "--edition_defaults_out=$tmpdir/defaults "
1986 "--edition_defaults_maximum=99997_TEST_ONLY "
1987 "google/protobuf/descriptor.proto");
1988 ExpectNoErrors();
1989
1990 FeatureSetDefaults defaults = ReadEditionDefaults("defaults");
1991 EXPECT_THAT(defaults, EqualsProto(R"pb(
1992 defaults {
1993 edition: EDITION_LEGACY
1994 overridable_features {}
1995 fixed_features {
1996 field_presence: EXPLICIT
1997 enum_type: CLOSED
1998 repeated_field_encoding: EXPANDED
1999 utf8_validation: NONE
2000 message_encoding: LENGTH_PREFIXED
2001 json_format: LEGACY_BEST_EFFORT
2002 }
2003 }
2004 defaults {
2005 edition: EDITION_PROTO3
2006 overridable_features {}
2007 fixed_features {
2008 field_presence: IMPLICIT
2009 enum_type: OPEN
2010 repeated_field_encoding: PACKED
2011 utf8_validation: VERIFY
2012 message_encoding: LENGTH_PREFIXED
2013 json_format: ALLOW
2014 }
2015 }
2016 defaults {
2017 edition: EDITION_2023
2018 overridable_features {
2019 field_presence: EXPLICIT
2020 enum_type: OPEN
2021 repeated_field_encoding: PACKED
2022 utf8_validation: VERIFY
2023 message_encoding: LENGTH_PREFIXED
2024 json_format: ALLOW
2025 }
2026 fixed_features {}
2027 }
2028 minimum_edition: EDITION_PROTO2
2029 maximum_edition: EDITION_99997_TEST_ONLY
2030 )pb"));
2031 }
2032
TEST_F(CommandLineInterfaceTest,EditionDefaultsWithMinimum)2033 TEST_F(CommandLineInterfaceTest, EditionDefaultsWithMinimum) {
2034 CreateTempFile("google/protobuf/descriptor.proto",
2035 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
2036 Run("protocol_compiler --proto_path=$tmpdir "
2037 "--edition_defaults_out=$tmpdir/defaults "
2038 "--edition_defaults_minimum=99997_TEST_ONLY "
2039 "--edition_defaults_maximum=99999_TEST_ONLY "
2040 "google/protobuf/descriptor.proto");
2041 ExpectNoErrors();
2042
2043 FeatureSetDefaults defaults = ReadEditionDefaults("defaults");
2044 EXPECT_THAT(defaults, EqualsProto(R"pb(
2045 defaults {
2046 edition: EDITION_LEGACY
2047 overridable_features {}
2048 fixed_features {
2049 field_presence: EXPLICIT
2050 enum_type: CLOSED
2051 repeated_field_encoding: EXPANDED
2052 utf8_validation: NONE
2053 message_encoding: LENGTH_PREFIXED
2054 json_format: LEGACY_BEST_EFFORT
2055 }
2056 }
2057 defaults {
2058 edition: EDITION_PROTO3
2059 overridable_features {}
2060 fixed_features {
2061 field_presence: IMPLICIT
2062 enum_type: OPEN
2063 repeated_field_encoding: PACKED
2064 utf8_validation: VERIFY
2065 message_encoding: LENGTH_PREFIXED
2066 json_format: ALLOW
2067 }
2068 }
2069 defaults {
2070 edition: EDITION_2023
2071 overridable_features {
2072 field_presence: EXPLICIT
2073 enum_type: OPEN
2074 repeated_field_encoding: PACKED
2075 utf8_validation: VERIFY
2076 message_encoding: LENGTH_PREFIXED
2077 json_format: ALLOW
2078 }
2079 fixed_features {}
2080 }
2081 minimum_edition: EDITION_99997_TEST_ONLY
2082 maximum_edition: EDITION_99999_TEST_ONLY
2083 )pb"));
2084 }
2085
TEST_F(CommandLineInterfaceTest,EditionDefaultsWithExtension)2086 TEST_F(CommandLineInterfaceTest, EditionDefaultsWithExtension) {
2087 CreateTempFile("google/protobuf/descriptor.proto",
2088 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
2089 CreateTempFile("features.proto",
2090 pb::TestFeatures::descriptor()->file()->DebugString());
2091 Run("protocol_compiler --proto_path=$tmpdir "
2092 "--edition_defaults_out=$tmpdir/defaults "
2093 "--edition_defaults_maximum=99999_TEST_ONLY "
2094 "features.proto google/protobuf/descriptor.proto");
2095 ExpectNoErrors();
2096
2097 FeatureSetDefaults defaults = ReadEditionDefaults("defaults");
2098 EXPECT_EQ(defaults.minimum_edition(), EDITION_PROTO2);
2099 EXPECT_EQ(defaults.maximum_edition(), EDITION_99999_TEST_ONLY);
2100 ASSERT_EQ(defaults.defaults_size(), 7);
2101 EXPECT_EQ(defaults.defaults(0).edition(), EDITION_LEGACY);
2102 EXPECT_EQ(defaults.defaults(2).edition(), EDITION_2023);
2103 EXPECT_EQ(defaults.defaults(3).edition(), EDITION_2024);
2104 EXPECT_EQ(defaults.defaults(4).edition(), EDITION_99997_TEST_ONLY);
2105 EXPECT_EQ(defaults.defaults(5).edition(), EDITION_99998_TEST_ONLY);
2106 EXPECT_EQ(defaults.defaults(0)
2107 .fixed_features()
2108 .GetExtension(pb::test)
2109 .file_feature(),
2110 pb::EnumFeature::VALUE1);
2111 EXPECT_EQ(defaults.defaults(2)
2112 .overridable_features()
2113 .GetExtension(pb::test)
2114 .file_feature(),
2115 pb::EnumFeature::VALUE3);
2116 EXPECT_EQ(defaults.defaults(3)
2117 .overridable_features()
2118 .GetExtension(pb::test)
2119 .file_feature(),
2120 pb::EnumFeature::VALUE3);
2121 EXPECT_EQ(defaults.defaults(4)
2122 .overridable_features()
2123 .GetExtension(pb::test)
2124 .file_feature(),
2125 pb::EnumFeature::VALUE4);
2126 EXPECT_EQ(defaults.defaults(5)
2127 .overridable_features()
2128 .GetExtension(pb::test)
2129 .file_feature(),
2130 pb::EnumFeature::VALUE5);
2131 }
2132
2133 #ifndef _WIN32
TEST_F(CommandLineInterfaceTest,EditionDefaultsDependencyManifest)2134 TEST_F(CommandLineInterfaceTest, EditionDefaultsDependencyManifest) {
2135 CreateTempFile("google/protobuf/descriptor.proto",
2136 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
2137 CreateTempFile("features.proto",
2138 pb::TestFeatures::descriptor()->file()->DebugString());
2139
2140 Run("protocol_compiler --dependency_out=$tmpdir/manifest "
2141 "--edition_defaults_out=$tmpdir/defaults "
2142 "--proto_path=$tmpdir features.proto");
2143
2144 ExpectNoErrors();
2145
2146 ExpectFileContent(
2147 "manifest",
2148 "$tmpdir/defaults: "
2149 "$tmpdir/google/protobuf/descriptor.proto\\\n $tmpdir/features.proto");
2150 }
2151 #endif // _WIN32
2152
TEST_F(CommandLineInterfaceTest,EditionDefaultsInvalidMissingDescriptor)2153 TEST_F(CommandLineInterfaceTest, EditionDefaultsInvalidMissingDescriptor) {
2154 CreateTempFile("features.proto", R"schema(
2155 syntax = "proto2";
2156 message Foo {}
2157 )schema");
2158 Run("protocol_compiler --proto_path=$tmpdir "
2159 "--edition_defaults_out=$tmpdir/defaults "
2160 "features.proto");
2161 ExpectErrorSubstring("Could not find FeatureSet in descriptor pool");
2162 }
2163
TEST_F(CommandLineInterfaceTest,EditionDefaultsInvalidTwice)2164 TEST_F(CommandLineInterfaceTest, EditionDefaultsInvalidTwice) {
2165 CreateTempFile("google/protobuf/descriptor.proto",
2166 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
2167 Run("protocol_compiler --proto_path=$tmpdir "
2168 "--edition_defaults_out=$tmpdir/defaults "
2169 "--edition_defaults_out=$tmpdir/defaults "
2170 "google/protobuf/descriptor.proto");
2171 ExpectErrorSubstring("edition_defaults_out may only be passed once");
2172 }
2173
TEST_F(CommandLineInterfaceTest,EditionDefaultsInvalidEmpty)2174 TEST_F(CommandLineInterfaceTest, EditionDefaultsInvalidEmpty) {
2175 CreateTempFile("google/protobuf/descriptor.proto",
2176 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
2177 Run("protocol_compiler --proto_path=$tmpdir "
2178 "--edition_defaults_out= "
2179 "google/protobuf/descriptor.proto");
2180 ExpectErrorSubstring("edition_defaults_out requires a non-empty value");
2181 }
2182
TEST_F(CommandLineInterfaceTest,EditionDefaultsInvalidCompile)2183 TEST_F(CommandLineInterfaceTest, EditionDefaultsInvalidCompile) {
2184 CreateTempFile("google/protobuf/descriptor.proto",
2185 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
2186 Run("protocol_compiler --proto_path=$tmpdir "
2187 "--encode=pb.CppFeatures "
2188 "--edition_defaults_out=$tmpdir/defaults "
2189 "google/protobuf/descriptor.proto");
2190 ExpectErrorSubstring("Cannot use --encode or --decode and generate defaults");
2191 }
2192
TEST_F(CommandLineInterfaceTest,EditionDefaultsInvalidMinimumTwice)2193 TEST_F(CommandLineInterfaceTest, EditionDefaultsInvalidMinimumTwice) {
2194 CreateTempFile("google/protobuf/descriptor.proto",
2195 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
2196 Run("protocol_compiler --proto_path=$tmpdir "
2197 "--edition_defaults_minimum=2023 "
2198 "--edition_defaults_minimum=2023 "
2199 "google/protobuf/descriptor.proto");
2200 ExpectErrorSubstring("edition_defaults_minimum may only be passed once");
2201 }
2202
TEST_F(CommandLineInterfaceTest,EditionDefaultsInvalidMinimumEmpty)2203 TEST_F(CommandLineInterfaceTest, EditionDefaultsInvalidMinimumEmpty) {
2204 CreateTempFile("google/protobuf/descriptor.proto",
2205 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
2206 Run("protocol_compiler --proto_path=$tmpdir "
2207 "--edition_defaults_minimum= "
2208 "google/protobuf/descriptor.proto");
2209 ExpectErrorSubstring("unknown edition \"\"");
2210 }
2211
TEST_F(CommandLineInterfaceTest,EditionDefaultsInvalidMinimumUnknown)2212 TEST_F(CommandLineInterfaceTest, EditionDefaultsInvalidMinimumUnknown) {
2213 CreateTempFile("google/protobuf/descriptor.proto",
2214 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
2215 Run("protocol_compiler --proto_path=$tmpdir "
2216 "--edition_defaults_minimum=2022 "
2217 "google/protobuf/descriptor.proto");
2218 ExpectErrorSubstring("unknown edition \"2022\"");
2219 }
2220
TEST_F(CommandLineInterfaceTest,EditionDefaultsInvalidMaximumTwice)2221 TEST_F(CommandLineInterfaceTest, EditionDefaultsInvalidMaximumTwice) {
2222 CreateTempFile("google/protobuf/descriptor.proto",
2223 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
2224 Run("protocol_compiler --proto_path=$tmpdir "
2225 "--edition_defaults_maximum=2023 "
2226 "--edition_defaults_maximum=2023 "
2227 "google/protobuf/descriptor.proto");
2228 ExpectErrorSubstring("edition_defaults_maximum may only be passed once");
2229 }
2230
TEST_F(CommandLineInterfaceTest,EditionDefaultsInvalidMaximumEmpty)2231 TEST_F(CommandLineInterfaceTest, EditionDefaultsInvalidMaximumEmpty) {
2232 CreateTempFile("google/protobuf/descriptor.proto",
2233 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
2234 Run("protocol_compiler --proto_path=$tmpdir "
2235 "--edition_defaults_maximum= "
2236 "google/protobuf/descriptor.proto");
2237 ExpectErrorSubstring("unknown edition \"\"");
2238 }
2239
TEST_F(CommandLineInterfaceTest,EditionDefaultsInvalidMaximumUnknown)2240 TEST_F(CommandLineInterfaceTest, EditionDefaultsInvalidMaximumUnknown) {
2241 CreateTempFile("google/protobuf/descriptor.proto",
2242 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
2243 Run("protocol_compiler --proto_path=$tmpdir "
2244 "--edition_defaults_maximum=2022 "
2245 "google/protobuf/descriptor.proto");
2246 ExpectErrorSubstring("unknown edition \"2022\"");
2247 }
2248
2249
TEST_F(CommandLineInterfaceTest,DirectDependencies_Missing_EmptyList)2250 TEST_F(CommandLineInterfaceTest, DirectDependencies_Missing_EmptyList) {
2251 CreateTempFile("foo.proto",
2252 "syntax = \"proto2\";\n"
2253 "import \"bar.proto\";\n"
2254 "message Foo { optional Bar bar = 1; }");
2255 CreateTempFile("bar.proto",
2256 "syntax = \"proto2\";\n"
2257 "message Bar { optional string text = 1; }");
2258
2259 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
2260 "--direct_dependencies= foo.proto");
2261
2262 ExpectErrorText(
2263 "foo.proto: File is imported but not declared in --direct_dependencies: "
2264 "bar.proto\n");
2265 }
2266
TEST_F(CommandLineInterfaceTest,DirectDependencies_Missing)2267 TEST_F(CommandLineInterfaceTest, DirectDependencies_Missing) {
2268 CreateTempFile("foo.proto",
2269 "syntax = \"proto2\";\n"
2270 "import \"bar.proto\";\n"
2271 "import \"bla.proto\";\n"
2272 "message Foo { optional Bar bar = 1; optional Bla bla = 2; }");
2273 CreateTempFile("bar.proto",
2274 "syntax = \"proto2\";\n"
2275 "message Bar { optional string text = 1; }");
2276 CreateTempFile("bla.proto",
2277 "syntax = \"proto2\";\n"
2278 "message Bla { optional int64 number = 1; }");
2279
2280 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
2281 "--direct_dependencies=bla.proto foo.proto");
2282
2283 ExpectErrorText(
2284 "foo.proto: File is imported but not declared in --direct_dependencies: "
2285 "bar.proto\n");
2286 }
2287
TEST_F(CommandLineInterfaceTest,DirectDependencies_NoViolation)2288 TEST_F(CommandLineInterfaceTest, DirectDependencies_NoViolation) {
2289 CreateTempFile("foo.proto",
2290 "syntax = \"proto2\";\n"
2291 "import \"bar.proto\";\n"
2292 "message Foo { optional Bar bar = 1; }");
2293 CreateTempFile("bar.proto",
2294 "syntax = \"proto2\";\n"
2295 "message Bar { optional string text = 1; }");
2296
2297 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
2298 "--direct_dependencies=bar.proto foo.proto");
2299
2300 ExpectNoErrors();
2301 }
2302
TEST_F(CommandLineInterfaceTest,DirectDependencies_NoViolation_MultiImports)2303 TEST_F(CommandLineInterfaceTest, DirectDependencies_NoViolation_MultiImports) {
2304 CreateTempFile("foo.proto",
2305 "syntax = \"proto2\";\n"
2306 "import \"bar.proto\";\n"
2307 "import \"bla.proto\";\n"
2308 "message Foo { optional Bar bar = 1; optional Bla bla = 2; }");
2309 CreateTempFile("bar.proto",
2310 "syntax = \"proto2\";\n"
2311 "message Bar { optional string text = 1; }");
2312 CreateTempFile("bla.proto",
2313 "syntax = \"proto2\";\n"
2314 "message Bla { optional int64 number = 1; }");
2315
2316 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
2317 "--direct_dependencies=bar.proto:bla.proto foo.proto");
2318
2319 ExpectNoErrors();
2320 }
2321
TEST_F(CommandLineInterfaceTest,DirectDependencies_ProvidedMultipleTimes)2322 TEST_F(CommandLineInterfaceTest, DirectDependencies_ProvidedMultipleTimes) {
2323 CreateTempFile("foo.proto", "syntax = \"proto2\";\n");
2324
2325 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir "
2326 "--direct_dependencies=bar.proto --direct_dependencies=bla.proto "
2327 "foo.proto");
2328
2329 ExpectErrorText(
2330 "--direct_dependencies may only be passed once. To specify multiple "
2331 "direct dependencies, pass them all as a single parameter separated by "
2332 "':'.\n");
2333 }
2334
TEST_F(CommandLineInterfaceTest,DirectDependencies_CustomErrorMessage)2335 TEST_F(CommandLineInterfaceTest, DirectDependencies_CustomErrorMessage) {
2336 CreateTempFile("foo.proto",
2337 "syntax = \"proto2\";\n"
2338 "import \"bar.proto\";\n"
2339 "message Foo { optional Bar bar = 1; }");
2340 CreateTempFile("bar.proto",
2341 "syntax = \"proto2\";\n"
2342 "message Bar { optional string text = 1; }");
2343
2344 std::vector<std::string> commands;
2345 commands.push_back("protocol_compiler");
2346 commands.push_back("--test_out=$tmpdir");
2347 commands.push_back("--proto_path=$tmpdir");
2348 commands.push_back("--direct_dependencies=");
2349 commands.push_back("--direct_dependencies_violation_msg=Bla \"%s\" Bla");
2350 commands.push_back("foo.proto");
2351 RunWithArgs(commands);
2352
2353 ExpectErrorText("foo.proto: Bla \"bar.proto\" Bla\n");
2354 }
2355
TEST_F(CommandLineInterfaceTest,CwdRelativeInputs)2356 TEST_F(CommandLineInterfaceTest, CwdRelativeInputs) {
2357 // Test that we can accept working-directory-relative input files.
2358
2359 CreateTempFile("foo.proto",
2360 "syntax = \"proto2\";\n"
2361 "message Foo {}\n");
2362
2363 Run("protocol_compiler --test_out=$tmpdir "
2364 "--proto_path=$tmpdir $tmpdir/foo.proto");
2365
2366 ExpectNoErrors();
2367 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
2368 }
2369
TEST_F(CommandLineInterfaceTest,WriteDescriptorSet)2370 TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) {
2371 CreateTempFile("foo.proto",
2372 "syntax = \"proto2\";\n"
2373 "message Foo {}\n");
2374 CreateTempFile("bar.proto",
2375 "syntax = \"proto2\";\n"
2376 "import \"foo.proto\";\n"
2377 "message Bar {\n"
2378 " optional Foo foo = 1;\n"
2379 "}\n");
2380
2381 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
2382 "--proto_path=$tmpdir bar.proto");
2383
2384 ExpectNoErrors();
2385
2386 FileDescriptorSet descriptor_set;
2387 ReadDescriptorSet("descriptor_set", &descriptor_set);
2388 if (HasFatalFailure()) return;
2389 EXPECT_EQ(1, descriptor_set.file_size());
2390 EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
2391 // Descriptor set should not have source code info.
2392 EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
2393 // Descriptor set should have json_name.
2394 EXPECT_EQ("Bar", descriptor_set.file(0).message_type(0).name());
2395 EXPECT_EQ("foo", descriptor_set.file(0).message_type(0).field(0).name());
2396 EXPECT_TRUE(descriptor_set.file(0).message_type(0).field(0).has_json_name());
2397 }
2398
TEST_F(CommandLineInterfaceTest,WriteDescriptorSetWithDuplicates)2399 TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithDuplicates) {
2400 CreateTempFile("foo.proto",
2401 "syntax = \"proto2\";\n"
2402 "message Foo {}\n");
2403 CreateTempFile("bar.proto",
2404 "syntax = \"proto2\";\n"
2405 "import \"foo.proto\";\n"
2406 "message Bar {\n"
2407 " optional Foo foo = 1;\n"
2408 "}\n");
2409 CreateTempFile("baz.proto",
2410 "syntax = \"proto2\";\n"
2411 "import \"foo.proto\";\n"
2412 "message Baz {\n"
2413 " optional Foo foo = 1;\n"
2414 "}\n");
2415
2416 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
2417 "--proto_path=$tmpdir bar.proto foo.proto bar.proto baz.proto");
2418
2419 ExpectNoErrors();
2420
2421 FileDescriptorSet descriptor_set;
2422 ReadDescriptorSet("descriptor_set", &descriptor_set);
2423 if (HasFatalFailure()) return;
2424 EXPECT_EQ(3, descriptor_set.file_size());
2425 // foo should come first since the output is in dependency order.
2426 // since bar and baz are unordered, they should be in command line order.
2427 EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
2428 EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
2429 EXPECT_EQ("baz.proto", descriptor_set.file(2).name());
2430 // Descriptor set should not have source code info.
2431 EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
2432 // Descriptor set should have json_name.
2433 EXPECT_EQ("Bar", descriptor_set.file(1).message_type(0).name());
2434 EXPECT_EQ("foo", descriptor_set.file(1).message_type(0).field(0).name());
2435 EXPECT_TRUE(descriptor_set.file(1).message_type(0).field(0).has_json_name());
2436 }
2437
TEST_F(CommandLineInterfaceTest,WriteDescriptorSetWithSourceInfo)2438 TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) {
2439 CreateTempFile("foo.proto",
2440 "syntax = \"proto2\";\n"
2441 "message Foo {}\n");
2442 CreateTempFile("bar.proto",
2443 "syntax = \"proto2\";\n"
2444 "import \"foo.proto\";\n"
2445 "message Bar {\n"
2446 " optional Foo foo = 1;\n"
2447 "}\n");
2448
2449 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
2450 "--include_source_info --proto_path=$tmpdir bar.proto");
2451
2452 ExpectNoErrors();
2453
2454 FileDescriptorSet descriptor_set;
2455 ReadDescriptorSet("descriptor_set", &descriptor_set);
2456 if (HasFatalFailure()) return;
2457 EXPECT_EQ(1, descriptor_set.file_size());
2458 EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
2459 // Source code info included.
2460 EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
2461 }
2462
TEST_F(CommandLineInterfaceTest,WriteTransitiveDescriptorSet)2463 TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) {
2464 CreateTempFile("foo.proto",
2465 "syntax = \"proto2\";\n"
2466 "message Foo {}\n");
2467 CreateTempFile("bar.proto",
2468 "syntax = \"proto2\";\n"
2469 "import \"foo.proto\";\n"
2470 "message Bar {\n"
2471 " optional Foo foo = 1;\n"
2472 "}\n");
2473
2474 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
2475 "--include_imports --proto_path=$tmpdir bar.proto");
2476
2477 ExpectNoErrors();
2478
2479 FileDescriptorSet descriptor_set;
2480 ReadDescriptorSet("descriptor_set", &descriptor_set);
2481 if (HasFatalFailure()) return;
2482 EXPECT_EQ(2, descriptor_set.file_size());
2483 if (descriptor_set.file(0).name() == "bar.proto") {
2484 std::swap(descriptor_set.mutable_file()->mutable_data()[0],
2485 descriptor_set.mutable_file()->mutable_data()[1]);
2486 }
2487 EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
2488 EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
2489 // Descriptor set should not have source code info.
2490 EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
2491 EXPECT_FALSE(descriptor_set.file(1).has_source_code_info());
2492 }
2493
TEST_F(CommandLineInterfaceTest,WriteTransitiveDescriptorSetWithSourceInfo)2494 TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSetWithSourceInfo) {
2495 CreateTempFile("foo.proto",
2496 "syntax = \"proto2\";\n"
2497 "message Foo {}\n");
2498 CreateTempFile("bar.proto",
2499 "syntax = \"proto2\";\n"
2500 "import \"foo.proto\";\n"
2501 "message Bar {\n"
2502 " optional Foo foo = 1;\n"
2503 "}\n");
2504
2505 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
2506 "--include_imports --include_source_info --proto_path=$tmpdir bar.proto");
2507
2508 ExpectNoErrors();
2509
2510 FileDescriptorSet descriptor_set;
2511 ReadDescriptorSet("descriptor_set", &descriptor_set);
2512 if (HasFatalFailure()) return;
2513 EXPECT_EQ(2, descriptor_set.file_size());
2514 if (descriptor_set.file(0).name() == "bar.proto") {
2515 std::swap(descriptor_set.mutable_file()->mutable_data()[0],
2516 descriptor_set.mutable_file()->mutable_data()[1]);
2517 }
2518 EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
2519 EXPECT_EQ("bar.proto", descriptor_set.file(1).name());
2520 // Source code info included.
2521 EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
2522 EXPECT_TRUE(descriptor_set.file(1).has_source_code_info());
2523 }
2524
TEST_F(CommandLineInterfaceTest,DescriptorSetOptionRetention)2525 TEST_F(CommandLineInterfaceTest, DescriptorSetOptionRetention) {
2526 // clang-format off
2527 CreateTempFile(
2528 "foo.proto",
2529 absl::Substitute(R"pb(
2530 syntax = "proto2";
2531 import "$0";
2532 extend google.protobuf.FileOptions {
2533 optional int32 runtime_retention_option = 50001
2534 [retention = RETENTION_RUNTIME];
2535 optional int32 source_retention_option = 50002
2536 [retention = RETENTION_SOURCE];
2537 }
2538 option (runtime_retention_option) = 2;
2539 option (source_retention_option) = 3;)pb",
2540 DescriptorProto::descriptor()->file()->name()));
2541 // clang-format on
2542 std::string descriptor_proto_base_dir = "src";
2543 Run(absl::Substitute(
2544 "protocol_compiler --descriptor_set_out=$$tmpdir/descriptor_set "
2545 "--proto_path=$$tmpdir --proto_path=$0 foo.proto",
2546 descriptor_proto_base_dir));
2547 ExpectNoErrors();
2548
2549 FileDescriptorSet descriptor_set;
2550 ReadDescriptorSet("descriptor_set", &descriptor_set);
2551 ASSERT_EQ(descriptor_set.file_size(), 1);
2552 const UnknownFieldSet& unknown_fields =
2553 descriptor_set.file(0).options().unknown_fields();
2554
2555 // We expect runtime_retention_option to be present while
2556 // source_retention_option should have been stripped.
2557 ASSERT_EQ(unknown_fields.field_count(), 1);
2558 EXPECT_EQ(unknown_fields.field(0).number(), 50001);
2559 EXPECT_EQ(unknown_fields.field(0).varint(), 2);
2560 }
2561
TEST_F(CommandLineInterfaceTest,DescriptorSetOptionRetentionOverride)2562 TEST_F(CommandLineInterfaceTest, DescriptorSetOptionRetentionOverride) {
2563 // clang-format off
2564 CreateTempFile(
2565 "foo.proto",
2566 absl::Substitute(R"pb(
2567 syntax = "proto2";
2568 import "$0";
2569 extend google.protobuf.FileOptions {
2570 optional int32 runtime_retention_option = 50001
2571 [retention = RETENTION_RUNTIME];
2572 optional int32 source_retention_option = 50002
2573 [retention = RETENTION_SOURCE];
2574 }
2575 option (runtime_retention_option) = 2;
2576 option (source_retention_option) = 3;)pb",
2577 DescriptorProto::descriptor()->file()->name()));
2578 // clang-format on
2579 std::string descriptor_proto_base_dir = "src";
2580 Run(absl::Substitute(
2581 "protocol_compiler --descriptor_set_out=$$tmpdir/descriptor_set "
2582 "--proto_path=$$tmpdir --retain_options --proto_path=$0 foo.proto",
2583 descriptor_proto_base_dir));
2584 ExpectNoErrors();
2585
2586 FileDescriptorSet descriptor_set;
2587 ReadDescriptorSet("descriptor_set", &descriptor_set);
2588 ASSERT_EQ(descriptor_set.file_size(), 1);
2589 const UnknownFieldSet& unknown_fields =
2590 descriptor_set.file(0).options().unknown_fields();
2591
2592 // We expect all options to be present.
2593 ASSERT_EQ(unknown_fields.field_count(), 2);
2594 EXPECT_EQ(unknown_fields.field(0).number(), 50001);
2595 EXPECT_EQ(unknown_fields.field(1).number(), 50002);
2596 EXPECT_EQ(unknown_fields.field(0).varint(), 2);
2597 EXPECT_EQ(unknown_fields.field(1).varint(), 3);
2598 }
2599
2600 #ifdef _WIN32
2601 // TODO: Figure out how to write test on windows.
2602 #else
TEST_F(CommandLineInterfaceTest,WriteDependencyManifestFileGivenTwoInputs)2603 TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileGivenTwoInputs) {
2604 CreateTempFile("foo.proto",
2605 "syntax = \"proto2\";\n"
2606 "message Foo {}\n");
2607 CreateTempFile("bar.proto",
2608 "syntax = \"proto2\";\n"
2609 "import \"foo.proto\";\n"
2610 "message Bar {\n"
2611 " optional Foo foo = 1;\n"
2612 "}\n");
2613
2614 Run("protocol_compiler --dependency_out=$tmpdir/manifest "
2615 "--test_out=$tmpdir --proto_path=$tmpdir bar.proto foo.proto");
2616
2617 ExpectErrorText(
2618 "Can only process one input file when using --dependency_out=FILE.\n");
2619 }
2620
2621 #ifdef PROTOBUF_OPENSOURCE
TEST_F(CommandLineInterfaceTest,WriteDependencyManifestFile)2622 TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFile) {
2623 CreateTempFile("foo.proto",
2624 "syntax = \"proto2\";\n"
2625 "message Foo {}\n");
2626 CreateTempFile("bar.proto",
2627 "syntax = \"proto2\";\n"
2628 "import \"foo.proto\";\n"
2629 "message Bar {\n"
2630 " optional Foo foo = 1;\n"
2631 "}\n");
2632
2633 std::string current_working_directory = getcwd(nullptr, 0);
2634 SwitchToTempDirectory();
2635
2636 Run("protocol_compiler --dependency_out=manifest --test_out=. "
2637 "bar.proto");
2638
2639 ExpectNoErrors();
2640
2641 ExpectFileContent("manifest",
2642 "bar.proto.MockCodeGenerator.test_generator: "
2643 "foo.proto\\\n bar.proto");
2644
2645 File::ChangeWorkingDirectory(current_working_directory);
2646 }
2647 #else // !PROTOBUF_OPENSOURCE
2648 // TODO: Figure out how to change and get working directory in
2649 // google3.
2650 #endif // !PROTOBUF_OPENSOURCE
2651
TEST_F(CommandLineInterfaceTest,WriteDependencyManifestFileForAbsolutePath)2652 TEST_F(CommandLineInterfaceTest, WriteDependencyManifestFileForAbsolutePath) {
2653 CreateTempFile("foo.proto",
2654 "syntax = \"proto2\";\n"
2655 "message Foo {}\n");
2656 CreateTempFile("bar.proto",
2657 "syntax = \"proto2\";\n"
2658 "import \"foo.proto\";\n"
2659 "message Bar {\n"
2660 " optional Foo foo = 1;\n"
2661 "}\n");
2662
2663 Run("protocol_compiler --dependency_out=$tmpdir/manifest "
2664 "--test_out=$tmpdir --proto_path=$tmpdir bar.proto");
2665
2666 ExpectNoErrors();
2667
2668 ExpectFileContent("manifest",
2669 "$tmpdir/bar.proto.MockCodeGenerator.test_generator: "
2670 "$tmpdir/foo.proto\\\n $tmpdir/bar.proto");
2671 }
2672
TEST_F(CommandLineInterfaceTest,WriteDependencyManifestFileWithDescriptorSetOut)2673 TEST_F(CommandLineInterfaceTest,
2674 WriteDependencyManifestFileWithDescriptorSetOut) {
2675 CreateTempFile("foo.proto",
2676 "syntax = \"proto2\";\n"
2677 "message Foo {}\n");
2678 CreateTempFile("bar.proto",
2679 "syntax = \"proto2\";\n"
2680 "import \"foo.proto\";\n"
2681 "message Bar {\n"
2682 " optional Foo foo = 1;\n"
2683 "}\n");
2684
2685 Run("protocol_compiler --dependency_out=$tmpdir/manifest "
2686 "--descriptor_set_out=$tmpdir/bar.pb --proto_path=$tmpdir bar.proto");
2687
2688 ExpectNoErrors();
2689
2690 ExpectFileContent("manifest",
2691 "$tmpdir/bar.pb: "
2692 "$tmpdir/foo.proto\\\n $tmpdir/bar.proto");
2693 }
2694 #endif // !_WIN32
2695
TEST_F(CommandLineInterfaceTest,TestArgumentFile)2696 TEST_F(CommandLineInterfaceTest, TestArgumentFile) {
2697 // Test parsing multiple input files using an argument file.
2698
2699 CreateTempFile("foo.proto",
2700 "syntax = \"proto2\";\n"
2701 "message Foo {}\n");
2702 CreateTempFile("bar.proto",
2703 "syntax = \"proto2\";\n"
2704 "message Bar {}\n");
2705 CreateTempFile("arguments.txt",
2706 "--test_out=$tmpdir\n"
2707 "--plug_out=$tmpdir\n"
2708 "--proto_path=$tmpdir\n"
2709 "--direct_dependencies_violation_msg=%s is not imported\n"
2710 "foo.proto\n"
2711 "bar.proto");
2712
2713 Run("protocol_compiler @$tmpdir/arguments.txt");
2714
2715 ExpectNoErrors();
2716 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
2717 "foo.proto", "Foo");
2718 ExpectGeneratedWithMultipleInputs("test_generator", "foo.proto,bar.proto",
2719 "bar.proto", "Bar");
2720 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
2721 "foo.proto", "Foo");
2722 ExpectGeneratedWithMultipleInputs("test_plugin", "foo.proto,bar.proto",
2723 "bar.proto", "Bar");
2724 }
2725
2726
2727 // -------------------------------------------------------------------
2728
TEST_F(CommandLineInterfaceTest,ParseErrors)2729 TEST_F(CommandLineInterfaceTest, ParseErrors) {
2730 // Test that parse errors are reported.
2731
2732 CreateTempFile("foo.proto",
2733 "syntax = \"proto2\";\n"
2734 "badsyntax\n");
2735
2736 Run("protocol_compiler --test_out=$tmpdir "
2737 "--proto_path=$tmpdir foo.proto");
2738
2739 ExpectErrorText(
2740 "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
2741 }
2742
TEST_F(CommandLineInterfaceTest,ParseErrors_DescriptorSetIn)2743 TEST_F(CommandLineInterfaceTest, ParseErrors_DescriptorSetIn) {
2744 // Test that parse errors are reported.
2745 CreateTempFile("foo.bin", "not a FileDescriptorSet");
2746
2747 Run("protocol_compiler --test_out=$tmpdir "
2748 "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
2749
2750 ExpectErrorText("$tmpdir/foo.bin: Unable to parse.\n");
2751 }
2752
TEST_F(CommandLineInterfaceTest,ParseErrorsMultipleFiles)2753 TEST_F(CommandLineInterfaceTest, ParseErrorsMultipleFiles) {
2754 // Test that parse errors are reported from multiple files.
2755
2756 // We set up files such that foo.proto actually depends on bar.proto in
2757 // two ways: Directly and through baz.proto. bar.proto's errors should
2758 // only be reported once.
2759 CreateTempFile("bar.proto",
2760 "syntax = \"proto2\";\n"
2761 "badsyntax\n");
2762 CreateTempFile("baz.proto",
2763 "syntax = \"proto2\";\n"
2764 "import \"bar.proto\";\n");
2765 CreateTempFile("foo.proto",
2766 "syntax = \"proto2\";\n"
2767 "import \"bar.proto\";\n"
2768 "import \"baz.proto\";\n");
2769
2770 Run("protocol_compiler --test_out=$tmpdir "
2771 "--proto_path=$tmpdir foo.proto");
2772
2773 ExpectErrorText(
2774 "bar.proto:2:1: Expected top-level statement (e.g. \"message\").\n"
2775 "baz.proto:2:1: Import \"bar.proto\" was not found or had errors.\n"
2776 "foo.proto:2:1: Import \"bar.proto\" was not found or had errors.\n"
2777 "foo.proto:3:1: Import \"baz.proto\" was not found or had errors.\n");
2778 }
2779
TEST_F(CommandLineInterfaceTest,RecursiveImportFails)2780 TEST_F(CommandLineInterfaceTest, RecursiveImportFails) {
2781 // Create a proto file that imports itself.
2782 CreateTempFile("foo.proto",
2783 "syntax = \"proto2\";\n"
2784 "import \"foo.proto\";\n");
2785
2786 Run("protocol_compiler --test_out=$tmpdir "
2787 "--proto_path=$tmpdir foo.proto");
2788
2789 ExpectErrorSubstring(
2790 "foo.proto:2:1: File recursively imports itself: "
2791 "foo.proto -> foo.proto\n");
2792 }
2793
TEST_F(CommandLineInterfaceTest,InputNotFoundError)2794 TEST_F(CommandLineInterfaceTest, InputNotFoundError) {
2795 // Test what happens if the input file is not found.
2796
2797 Run("protocol_compiler --test_out=$tmpdir "
2798 "--proto_path=$tmpdir foo.proto");
2799
2800 ExpectErrorText(
2801 "Could not make proto path relative: foo.proto: No such file or "
2802 "directory\n");
2803 }
2804
TEST_F(CommandLineInterfaceTest,InputNotFoundError_DescriptorSetIn)2805 TEST_F(CommandLineInterfaceTest, InputNotFoundError_DescriptorSetIn) {
2806 // Test what happens if the input file is not found.
2807
2808 Run("protocol_compiler --test_out=$tmpdir "
2809 "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
2810
2811 ExpectErrorText("$tmpdir/foo.bin: No such file or directory\n");
2812 }
2813
TEST_F(CommandLineInterfaceTest,CwdRelativeInputNotFoundError)2814 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundError) {
2815 // Test what happens when a working-directory-relative input file is not
2816 // found.
2817
2818 Run("protocol_compiler --test_out=$tmpdir "
2819 "--proto_path=$tmpdir $tmpdir/foo.proto");
2820
2821 ExpectErrorText(
2822 "Could not make proto path relative: $tmpdir/foo.proto: No such file or "
2823 "directory\n");
2824 }
2825
TEST_F(CommandLineInterfaceTest,CwdRelativeInputNotMappedError)2826 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotMappedError) {
2827 // Test what happens when a working-directory-relative input file is not
2828 // mapped to a virtual path.
2829
2830 CreateTempFile("foo.proto",
2831 "syntax = \"proto2\";\n"
2832 "message Foo {}\n");
2833
2834 // Create a directory called "bar" so that we can point --proto_path at it.
2835 CreateTempFile("bar/dummy", "");
2836
2837 Run("protocol_compiler --test_out=$tmpdir "
2838 "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
2839
2840 ExpectErrorText(
2841 "$tmpdir/foo.proto: File does not reside within any path "
2842 "specified using --proto_path (or -I). You must specify a "
2843 "--proto_path which encompasses this file. Note that the "
2844 "proto_path must be an exact prefix of the .proto file "
2845 "names -- protoc is too dumb to figure out when two paths "
2846 "(e.g. absolute and relative) are equivalent (it's harder "
2847 "than you think).\n");
2848 }
2849
TEST_F(CommandLineInterfaceTest,CwdRelativeInputNotFoundAndNotMappedError)2850 TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundAndNotMappedError) {
2851 // Check what happens if the input file is not found *and* is not mapped
2852 // in the proto_path.
2853
2854 // Create a directory called "bar" so that we can point --proto_path at it.
2855 CreateTempFile("bar/dummy", "");
2856
2857 Run("protocol_compiler --test_out=$tmpdir "
2858 "--proto_path=$tmpdir/bar $tmpdir/foo.proto");
2859
2860 ExpectErrorText(
2861 "Could not make proto path relative: $tmpdir/foo.proto: No such file or "
2862 "directory\n");
2863 }
2864
TEST_F(CommandLineInterfaceTest,CwdRelativeInputShadowedError)2865 TEST_F(CommandLineInterfaceTest, CwdRelativeInputShadowedError) {
2866 // Test what happens when a working-directory-relative input file is shadowed
2867 // by another file in the virtual path.
2868
2869 CreateTempFile("foo/foo.proto",
2870 "syntax = \"proto2\";\n"
2871 "message Foo {}\n");
2872 CreateTempFile("bar/foo.proto",
2873 "syntax = \"proto2\";\n"
2874 "message Bar {}\n");
2875
2876 Run("protocol_compiler --test_out=$tmpdir "
2877 "--proto_path=$tmpdir/foo --proto_path=$tmpdir/bar "
2878 "$tmpdir/bar/foo.proto");
2879
2880 ExpectErrorText(
2881 "$tmpdir/bar/foo.proto: Input is shadowed in the --proto_path "
2882 "by \"$tmpdir/foo/foo.proto\". Either use the latter "
2883 "file as your input or reorder the --proto_path so that the "
2884 "former file's location comes first.\n");
2885 }
2886
TEST_F(CommandLineInterfaceTest,ProtoPathNotFoundError)2887 TEST_F(CommandLineInterfaceTest, ProtoPathNotFoundError) {
2888 // Test what happens if the input file is not found.
2889
2890 Run("protocol_compiler --test_out=$tmpdir "
2891 "--proto_path=$tmpdir/foo foo.proto");
2892
2893 ExpectErrorText(
2894 "$tmpdir/foo: warning: directory does not exist.\n"
2895 "Could not make proto path relative: foo.proto: No such file or "
2896 "directory\n");
2897 }
2898
TEST_F(CommandLineInterfaceTest,ProtoPathAndDescriptorSetIn)2899 TEST_F(CommandLineInterfaceTest, ProtoPathAndDescriptorSetIn) {
2900 Run("protocol_compiler --test_out=$tmpdir "
2901 "--proto_path=$tmpdir --descriptor_set_in=$tmpdir/foo.bin foo.proto");
2902 ExpectErrorText("$tmpdir/foo.bin: No such file or directory\n");
2903
2904 Run("protocol_compiler --test_out=$tmpdir "
2905 "--descriptor_set_in=$tmpdir/foo.bin --proto_path=$tmpdir foo.proto");
2906 ExpectErrorText("$tmpdir/foo.bin: No such file or directory\n");
2907 }
2908
TEST_F(CommandLineInterfaceTest,ProtoPathAndDescriptorSetIn_CompileFiles)2909 TEST_F(CommandLineInterfaceTest, ProtoPathAndDescriptorSetIn_CompileFiles) {
2910 // Test what happens if a proto is in a --descriptor_set_in and also exists
2911 // on disk.
2912 FileDescriptorSet file_descriptor_set;
2913
2914 // NOTE: This file desc SHOULD be different from the one created as a temp
2915 // to make it easier to test that the file was output instead of the
2916 // contents of the --descriptor_set_in file.
2917 FileDescriptorProto* file_descriptor_proto = file_descriptor_set.add_file();
2918 file_descriptor_proto->set_name("foo.proto");
2919 file_descriptor_proto->add_message_type()->set_name("Foo");
2920
2921 WriteDescriptorSet("foo.bin", &file_descriptor_set);
2922
2923 CreateTempFile("foo.proto",
2924 "syntax = \"proto2\";\n"
2925 "message FooBar { required string foo_message = 1; }\n");
2926
2927 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
2928 "--descriptor_set_in=$tmpdir/foo.bin "
2929 "--include_source_info "
2930 "--proto_path=$tmpdir foo.proto");
2931
2932 ExpectNoErrors();
2933
2934 FileDescriptorSet descriptor_set;
2935 ReadDescriptorSet("descriptor_set", &descriptor_set);
2936
2937 EXPECT_EQ(1, descriptor_set.file_size());
2938 EXPECT_EQ("foo.proto", descriptor_set.file(0).name());
2939 // Descriptor set SHOULD have source code info.
2940 EXPECT_TRUE(descriptor_set.file(0).has_source_code_info());
2941
2942 EXPECT_EQ("FooBar", descriptor_set.file(0).message_type(0).name());
2943 EXPECT_EQ("foo_message",
2944 descriptor_set.file(0).message_type(0).field(0).name());
2945 }
2946
TEST_F(CommandLineInterfaceTest,ProtoPathAndDependencyOut)2947 TEST_F(CommandLineInterfaceTest, ProtoPathAndDependencyOut) {
2948 Run("protocol_compiler --test_out=$tmpdir "
2949 "--dependency_out=$tmpdir/manifest "
2950 "--descriptor_set_in=$tmpdir/foo.bin foo.proto");
2951 ExpectErrorText(
2952 "--descriptor_set_in cannot be used with --dependency_out.\n");
2953
2954 Run("protocol_compiler --test_out=$tmpdir "
2955 "--descriptor_set_in=$tmpdir/foo.bin "
2956 "--dependency_out=$tmpdir/manifest foo.proto");
2957 ExpectErrorText(
2958 "--dependency_out cannot be used with --descriptor_set_in.\n");
2959 }
2960
TEST_F(CommandLineInterfaceTest,MissingInputError)2961 TEST_F(CommandLineInterfaceTest, MissingInputError) {
2962 // Test that we get an error if no inputs are given.
2963
2964 Run("protocol_compiler --test_out=$tmpdir "
2965 "--proto_path=$tmpdir");
2966
2967 ExpectErrorText("Missing input file.\n");
2968 }
2969
TEST_F(CommandLineInterfaceTest,MissingOutputError)2970 TEST_F(CommandLineInterfaceTest, MissingOutputError) {
2971 CreateTempFile("foo.proto",
2972 "syntax = \"proto2\";\n"
2973 "message Foo {}\n");
2974
2975 Run("protocol_compiler --proto_path=$tmpdir foo.proto");
2976
2977 ExpectErrorText("Missing output directives.\n");
2978 }
2979
TEST_F(CommandLineInterfaceTest,OutputWriteError)2980 TEST_F(CommandLineInterfaceTest, OutputWriteError) {
2981 CreateTempFile("foo.proto",
2982 "syntax = \"proto2\";\n"
2983 "message Foo {}\n");
2984
2985 std::string output_file =
2986 MockCodeGenerator::GetOutputFileName("test_generator", "foo.proto");
2987
2988 // Create a directory blocking our output location.
2989 CreateTempDir(output_file);
2990
2991 Run("protocol_compiler --test_out=$tmpdir "
2992 "--proto_path=$tmpdir foo.proto");
2993
2994 // MockCodeGenerator no longer detects an error because we actually write to
2995 // an in-memory location first, then dump to disk at the end. This is no
2996 // big deal.
2997 // ExpectErrorSubstring("MockCodeGenerator detected write error.");
2998
2999 #if defined(_WIN32) && !defined(__CYGWIN__)
3000 // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
3001 if (HasAlternateErrorSubstring(
3002 absl::StrCat(output_file, ": Permission denied"))) {
3003 return;
3004 }
3005 #endif
3006
3007 ExpectErrorSubstring(absl::StrCat(output_file, ": Is a directory"));
3008 }
3009
TEST_F(CommandLineInterfaceTest,PluginOutputWriteError)3010 TEST_F(CommandLineInterfaceTest, PluginOutputWriteError) {
3011 CreateTempFile("foo.proto",
3012 "syntax = \"proto2\";\n"
3013 "message Foo {}\n");
3014
3015 std::string output_file =
3016 MockCodeGenerator::GetOutputFileName("test_plugin", "foo.proto");
3017
3018 // Create a directory blocking our output location.
3019 CreateTempDir(output_file);
3020
3021 Run("protocol_compiler --plug_out=$tmpdir "
3022 "--proto_path=$tmpdir foo.proto");
3023
3024 #if defined(_WIN32) && !defined(__CYGWIN__)
3025 // Windows with MSVCRT.dll produces EPERM instead of EISDIR.
3026 if (HasAlternateErrorSubstring(
3027 absl::StrCat(output_file, ": Permission denied"))) {
3028 return;
3029 }
3030 #endif
3031
3032 ExpectErrorSubstring(absl::StrCat(output_file, ": Is a directory"));
3033 }
3034
TEST_F(CommandLineInterfaceTest,OutputDirectoryNotFoundError)3035 TEST_F(CommandLineInterfaceTest, OutputDirectoryNotFoundError) {
3036 CreateTempFile("foo.proto",
3037 "syntax = \"proto2\";\n"
3038 "message Foo {}\n");
3039
3040 Run("protocol_compiler --test_out=$tmpdir/nosuchdir "
3041 "--proto_path=$tmpdir foo.proto");
3042
3043 ExpectErrorSubstring("nosuchdir/: No such file or directory");
3044 }
3045
TEST_F(CommandLineInterfaceTest,PluginOutputDirectoryNotFoundError)3046 TEST_F(CommandLineInterfaceTest, PluginOutputDirectoryNotFoundError) {
3047 CreateTempFile("foo.proto",
3048 "syntax = \"proto2\";\n"
3049 "message Foo {}\n");
3050
3051 Run("protocol_compiler --plug_out=$tmpdir/nosuchdir "
3052 "--proto_path=$tmpdir foo.proto");
3053
3054 ExpectErrorSubstring("nosuchdir/: No such file or directory");
3055 }
3056
TEST_F(CommandLineInterfaceTest,OutputDirectoryIsFileError)3057 TEST_F(CommandLineInterfaceTest, OutputDirectoryIsFileError) {
3058 CreateTempFile("foo.proto",
3059 "syntax = \"proto2\";\n"
3060 "message Foo {}\n");
3061
3062 Run("protocol_compiler --test_out=$tmpdir/foo.proto "
3063 "--proto_path=$tmpdir foo.proto");
3064
3065 #if defined(_WIN32) && !defined(__CYGWIN__)
3066 // Windows with MSVCRT.dll produces EINVAL instead of ENOTDIR.
3067 if (HasAlternateErrorSubstring("foo.proto/: Invalid argument")) {
3068 return;
3069 }
3070 #endif
3071
3072 ExpectErrorSubstring("foo.proto/: Not a directory");
3073 }
3074
TEST_F(CommandLineInterfaceTest,GeneratorError)3075 TEST_F(CommandLineInterfaceTest, GeneratorError) {
3076 CreateTempFile("foo.proto",
3077 "syntax = \"proto2\";\n"
3078 "message MockCodeGenerator_Error {}\n");
3079
3080 Run("protocol_compiler --test_out=$tmpdir "
3081 "--proto_path=$tmpdir foo.proto");
3082
3083 ExpectErrorSubstring(
3084 "--test_out: foo.proto: Saw message type MockCodeGenerator_Error.");
3085 }
3086
TEST_F(CommandLineInterfaceTest,GeneratorPluginError)3087 TEST_F(CommandLineInterfaceTest, GeneratorPluginError) {
3088 // Test a generator plugin that returns an error.
3089
3090 CreateTempFile("foo.proto",
3091 "syntax = \"proto2\";\n"
3092 "message MockCodeGenerator_Error {}\n");
3093
3094 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
3095 "--proto_path=$tmpdir foo.proto");
3096
3097 ExpectErrorSubstring(
3098 "--plug_out: foo.proto: Saw message type MockCodeGenerator_Error.");
3099 }
3100
TEST_F(CommandLineInterfaceTest,GeneratorPluginFail)3101 TEST_F(CommandLineInterfaceTest, GeneratorPluginFail) {
3102 // Test a generator plugin that exits with an error code.
3103
3104 CreateTempFile("foo.proto",
3105 "syntax = \"proto2\";\n"
3106 "message MockCodeGenerator_Exit {}\n");
3107
3108 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
3109 "--proto_path=$tmpdir foo.proto");
3110
3111 ExpectErrorSubstring("Saw message type MockCodeGenerator_Exit.");
3112 ExpectErrorSubstring(
3113 "--plug_out: prefix-gen-plug: Plugin failed with status code 123.");
3114 }
3115
TEST_F(CommandLineInterfaceTest,GeneratorPluginCrash)3116 TEST_F(CommandLineInterfaceTest, GeneratorPluginCrash) {
3117 // Test a generator plugin that crashes.
3118
3119 CreateTempFile("foo.proto",
3120 "syntax = \"proto2\";\n"
3121 "message MockCodeGenerator_Abort {}\n");
3122
3123 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
3124 "--proto_path=$tmpdir foo.proto");
3125
3126 ExpectErrorSubstring("Saw message type MockCodeGenerator_Abort.");
3127
3128 #ifdef _WIN32
3129 // Windows doesn't have signals. It looks like abort()ing causes the process
3130 // to exit with status code 3, but let's not depend on the exact number here.
3131 ExpectErrorSubstring(
3132 "--plug_out: prefix-gen-plug: Plugin failed with status code");
3133 #else
3134 // Don't depend on the exact signal number.
3135 ExpectErrorSubstring("--plug_out: prefix-gen-plug: Plugin killed by signal");
3136 #endif
3137 }
3138
TEST_F(CommandLineInterfaceTest,PluginReceivesSourceCodeInfo)3139 TEST_F(CommandLineInterfaceTest, PluginReceivesSourceCodeInfo) {
3140 CreateTempFile("foo.proto",
3141 "syntax = \"proto2\";\n"
3142 "message MockCodeGenerator_HasSourceCodeInfo {}\n");
3143
3144 Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
3145
3146 ExpectErrorSubstring(
3147 "Saw message type MockCodeGenerator_HasSourceCodeInfo: true.");
3148 }
3149
TEST_F(CommandLineInterfaceTest,PluginReceivesJsonName)3150 TEST_F(CommandLineInterfaceTest, PluginReceivesJsonName) {
3151 CreateTempFile("foo.proto",
3152 "syntax = \"proto2\";\n"
3153 "message MockCodeGenerator_HasJsonName {\n"
3154 " optional int32 value = 1;\n"
3155 "}\n");
3156
3157 Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
3158
3159 ExpectErrorSubstring("Saw json_name: true");
3160 }
3161
TEST_F(CommandLineInterfaceTest,PluginReceivesCompilerVersion)3162 TEST_F(CommandLineInterfaceTest, PluginReceivesCompilerVersion) {
3163 CreateTempFile("foo.proto",
3164 "syntax = \"proto2\";\n"
3165 "message MockCodeGenerator_ShowVersionNumber {\n"
3166 " optional int32 value = 1;\n"
3167 "}\n");
3168
3169 Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
3170
3171 ExpectErrorSubstring(absl::StrFormat("Saw compiler_version: %d %s",
3172 GOOGLE_PROTOBUF_VERSION,
3173 GOOGLE_PROTOBUF_VERSION_SUFFIX));
3174 }
3175
TEST_F(CommandLineInterfaceTest,GeneratorPluginNotFound)3176 TEST_F(CommandLineInterfaceTest, GeneratorPluginNotFound) {
3177 // Test what happens if the plugin isn't found.
3178
3179 CreateTempFile("error.proto",
3180 "syntax = \"proto2\";\n"
3181 "message Foo {}\n");
3182
3183 Run("protocol_compiler --badplug_out=TestParameter:$tmpdir "
3184 "--plugin=prefix-gen-badplug=no_such_file "
3185 "--proto_path=$tmpdir error.proto");
3186
3187 #ifdef _WIN32
3188 ExpectErrorSubstring(
3189 absl::StrCat("--badplug_out: prefix-gen-badplug: ",
3190 Subprocess::Win32ErrorMessage(ERROR_FILE_NOT_FOUND)));
3191 #else
3192 // Error written to stdout by child process after exec() fails.
3193 ExpectErrorSubstring("no_such_file: program not found or is not executable");
3194
3195 ExpectErrorSubstring(
3196 "Please specify a program using absolute path or make sure "
3197 "the program is available in your PATH system variable");
3198
3199 // Error written by parent process when child fails.
3200 ExpectErrorSubstring(
3201 "--badplug_out: prefix-gen-badplug: Plugin failed with status code 1.");
3202 #endif
3203 }
3204
TEST_F(CommandLineInterfaceTest,GeneratorPluginNotAllowed)3205 TEST_F(CommandLineInterfaceTest, GeneratorPluginNotAllowed) {
3206 // Test what happens if plugins aren't allowed.
3207
3208 CreateTempFile("error.proto",
3209 "syntax = \"proto2\";\n"
3210 "message Foo {}\n");
3211
3212 DisallowPlugins();
3213 Run("protocol_compiler --plug_out=TestParameter:$tmpdir "
3214 "--proto_path=$tmpdir error.proto");
3215
3216 ExpectErrorSubstring("Unknown flag: --plug_out");
3217 }
3218
TEST_F(CommandLineInterfaceTest,HelpText)3219 TEST_F(CommandLineInterfaceTest, HelpText) {
3220 Run("test_exec_name --help");
3221
3222 ExpectCapturedStdoutSubstringWithZeroReturnCode("Usage: test_exec_name ");
3223 ExpectCapturedStdoutSubstringWithZeroReturnCode("--test_out=OUT_DIR");
3224 ExpectCapturedStdoutSubstringWithZeroReturnCode("Test output.");
3225 ExpectCapturedStdoutSubstringWithZeroReturnCode("--alt_out=OUT_DIR");
3226 ExpectCapturedStdoutSubstringWithZeroReturnCode("Alt output.");
3227 }
3228
TEST_F(CommandLineInterfaceTest,GccFormatErrors)3229 TEST_F(CommandLineInterfaceTest, GccFormatErrors) {
3230 // Test --error_format=gcc (which is the default, but we want to verify
3231 // that it can be set explicitly).
3232
3233 CreateTempFile("foo.proto",
3234 "syntax = \"proto2\";\n"
3235 "badsyntax\n");
3236
3237 Run("protocol_compiler --test_out=$tmpdir "
3238 "--proto_path=$tmpdir --error_format=gcc foo.proto");
3239
3240 ExpectErrorText(
3241 "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n");
3242 }
3243
TEST_F(CommandLineInterfaceTest,MsvsFormatErrors)3244 TEST_F(CommandLineInterfaceTest, MsvsFormatErrors) {
3245 // Test --error_format=msvs
3246
3247 CreateTempFile("foo.proto",
3248 "syntax = \"proto2\";\n"
3249 "badsyntax\n");
3250
3251 Run("protocol_compiler --test_out=$tmpdir "
3252 "--proto_path=$tmpdir --error_format=msvs foo.proto");
3253
3254 ExpectErrorText(
3255 "$tmpdir/foo.proto(2) : error in column=1: Expected top-level statement "
3256 "(e.g. \"message\").\n");
3257 }
3258
TEST_F(CommandLineInterfaceTest,InvalidErrorFormat)3259 TEST_F(CommandLineInterfaceTest, InvalidErrorFormat) {
3260 // Test invalid --error_format
3261
3262 CreateTempFile("foo.proto",
3263 "syntax = \"proto2\";\n"
3264 "badsyntax\n");
3265
3266 Run("protocol_compiler --test_out=$tmpdir "
3267 "--proto_path=$tmpdir --error_format=invalid foo.proto");
3268
3269 ExpectErrorText("Unknown error format: invalid\n");
3270 }
3271
TEST_F(CommandLineInterfaceTest,Warnings)3272 TEST_F(CommandLineInterfaceTest, Warnings) {
3273 // Test --fatal_warnings.
3274
3275 CreateTempFile("foo.proto",
3276 "syntax = \"proto2\";\n"
3277 "import \"bar.proto\";\n");
3278 CreateTempFile("bar.proto", "syntax = \"proto2\";\n");
3279
3280 Run("protocol_compiler --test_out=$tmpdir "
3281 "--proto_path=$tmpdir foo.proto");
3282 ExpectCapturedStderrSubstringWithZeroReturnCode(
3283 "foo.proto:2:1: warning: Import bar.proto is unused.");
3284
3285 Run("protocol_compiler --test_out=$tmpdir --fatal_warnings "
3286 "--proto_path=$tmpdir foo.proto");
3287 ExpectErrorSubstring("foo.proto:2:1: warning: Import bar.proto is unused.");
3288 }
3289
3290 // -------------------------------------------------------------------
3291 // Flag parsing tests
3292
TEST_F(CommandLineInterfaceTest,ParseSingleCharacterFlag)3293 TEST_F(CommandLineInterfaceTest, ParseSingleCharacterFlag) {
3294 // Test that a single-character flag works.
3295
3296 CreateTempFile("foo.proto",
3297 "syntax = \"proto2\";\n"
3298 "message Foo {}\n");
3299
3300 Run("protocol_compiler -t$tmpdir "
3301 "--proto_path=$tmpdir foo.proto");
3302
3303 ExpectNoErrors();
3304 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
3305 }
3306
TEST_F(CommandLineInterfaceTest,ParseSpaceDelimitedValue)3307 TEST_F(CommandLineInterfaceTest, ParseSpaceDelimitedValue) {
3308 // Test that separating the flag value with a space works.
3309
3310 CreateTempFile("foo.proto",
3311 "syntax = \"proto2\";\n"
3312 "message Foo {}\n");
3313
3314 Run("protocol_compiler --test_out $tmpdir "
3315 "--proto_path=$tmpdir foo.proto");
3316
3317 ExpectNoErrors();
3318 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
3319 }
3320
TEST_F(CommandLineInterfaceTest,ParseSingleCharacterSpaceDelimitedValue)3321 TEST_F(CommandLineInterfaceTest, ParseSingleCharacterSpaceDelimitedValue) {
3322 // Test that separating the flag value with a space works for
3323 // single-character flags.
3324
3325 CreateTempFile("foo.proto",
3326 "syntax = \"proto2\";\n"
3327 "message Foo {}\n");
3328
3329 Run("protocol_compiler -t $tmpdir "
3330 "--proto_path=$tmpdir foo.proto");
3331
3332 ExpectNoErrors();
3333 ExpectGenerated("test_generator", "", "foo.proto", "Foo");
3334 }
3335
TEST_F(CommandLineInterfaceTest,MissingValueError)3336 TEST_F(CommandLineInterfaceTest, MissingValueError) {
3337 // Test that we get an error if a flag is missing its value.
3338
3339 Run("protocol_compiler --test_out --proto_path=$tmpdir foo.proto");
3340
3341 ExpectErrorText("Missing value for flag: --test_out\n");
3342 }
3343
TEST_F(CommandLineInterfaceTest,MissingValueAtEndError)3344 TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) {
3345 // Test that we get an error if the last argument is a flag requiring a
3346 // value.
3347
3348 Run("protocol_compiler --test_out");
3349
3350 ExpectErrorText("Missing value for flag: --test_out\n");
3351 }
3352
TEST_F(CommandLineInterfaceTest,Proto3OptionalDisallowedNoCodegenSupport)3353 TEST_F(CommandLineInterfaceTest, Proto3OptionalDisallowedNoCodegenSupport) {
3354 CreateTempFile("google/foo.proto",
3355 "syntax = \"proto3\";\n"
3356 "message Foo {\n"
3357 " optional int32 i = 1;\n"
3358 "}\n");
3359
3360 CreateGeneratorWithMissingFeatures("--no_proto3_optional_out",
3361 "Doesn't support proto3 optional",
3362 CodeGenerator::FEATURE_PROTO3_OPTIONAL);
3363
3364 Run("protocol_compiler --experimental_allow_proto3_optional "
3365 "--proto_path=$tmpdir google/foo.proto --no_proto3_optional_out=$tmpdir");
3366
3367 ExpectErrorSubstring(
3368 "code generator --no_proto3_optional_out hasn't been updated to support "
3369 "optional fields in proto3");
3370 }
3371
TEST_F(CommandLineInterfaceTest,ReservedFieldNumbersFail)3372 TEST_F(CommandLineInterfaceTest, ReservedFieldNumbersFail) {
3373 CreateTempFile("foo.proto",
3374 R"(
3375 syntax = "proto2";
3376 message Foo {
3377 optional int32 i = 19123;
3378 }
3379 )");
3380
3381 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
3382
3383 ExpectErrorSubstring(
3384 "foo.proto: Field numbers 19000 through 19999 are reserved for the "
3385 "protocol buffer library implementation.");
3386 }
3387
TEST_F(CommandLineInterfaceTest,ReservedFieldNumbersFailAsOneof)3388 TEST_F(CommandLineInterfaceTest, ReservedFieldNumbersFailAsOneof) {
3389 CreateTempFile("foo.proto",
3390 R"(
3391 syntax = "proto2";
3392 message Foo {
3393 oneof one {
3394 int32 i = 19123;
3395 }
3396 }
3397 )");
3398
3399 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
3400
3401 ExpectErrorSubstring(
3402 "foo.proto: Field numbers 19000 through 19999 are reserved for the "
3403 "protocol buffer library implementation.");
3404 }
3405
TEST_F(CommandLineInterfaceTest,ReservedFieldNumbersFailAsExtension)3406 TEST_F(CommandLineInterfaceTest, ReservedFieldNumbersFailAsExtension) {
3407 CreateTempFile("foo.proto",
3408 R"(
3409 syntax = "proto2";
3410 message Foo {
3411 extensions 4 to max;
3412 }
3413 extend Foo {
3414 optional int32 i = 19123;
3415 }
3416 )");
3417
3418 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
3419
3420 ExpectErrorSubstring(
3421 "foo.proto: Field numbers 19000 through 19999 are reserved for the "
3422 "protocol buffer library implementation.");
3423
3424 CreateTempFile("foo.proto",
3425 R"(
3426 syntax = "proto2";
3427 message Foo {
3428 extensions 4 to max;
3429 }
3430 message Bar {
3431 extend Foo {
3432 optional int32 i = 19123;
3433 }
3434 }
3435 )");
3436
3437 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
3438
3439 ExpectErrorSubstring(
3440 "foo.proto: Field numbers 19000 through 19999 are reserved for the "
3441 "protocol buffer library implementation.");
3442 }
3443
3444
TEST_F(CommandLineInterfaceTest,Proto3OptionalAllowWithFlag)3445 TEST_F(CommandLineInterfaceTest, Proto3OptionalAllowWithFlag) {
3446 CreateTempFile("google/foo.proto",
3447 "syntax = \"proto3\";\n"
3448 "message Foo {\n"
3449 " optional int32 i = 1;\n"
3450 "}\n");
3451
3452 Run("protocol_compiler --experimental_allow_proto3_optional "
3453 "--proto_path=$tmpdir google/foo.proto --test_out=$tmpdir");
3454 ExpectNoErrors();
3455 }
3456
TEST_F(CommandLineInterfaceTest,PrintFreeFieldNumbers)3457 TEST_F(CommandLineInterfaceTest, PrintFreeFieldNumbers) {
3458 CreateTempFile("foo.proto",
3459 "syntax = \"proto2\";\n"
3460 "package foo;\n"
3461 "message Foo {\n"
3462 " optional int32 a = 2;\n"
3463 " optional string b = 4;\n"
3464 " optional string c = 5;\n"
3465 " optional int64 d = 8;\n"
3466 " optional double e = 10;\n"
3467 "}\n");
3468 CreateTempFile("bar.proto",
3469 "syntax = \"proto2\";\n"
3470 "message Bar {\n"
3471 " optional int32 a = 2;\n"
3472 " extensions 4 to 5;\n"
3473 " optional int64 d = 8;\n"
3474 " extensions 10;\n"
3475 "}\n");
3476 CreateTempFile("baz.proto",
3477 "syntax = \"proto2\";\n"
3478 "message Baz {\n"
3479 " optional int32 a = 2;\n"
3480 " optional int64 d = 8;\n"
3481 " extensions 15 to max;\n" // unordered.
3482 " extensions 13;\n"
3483 " extensions 10 to 12;\n"
3484 " extensions 5;\n"
3485 " extensions 4;\n"
3486 "}\n");
3487 CreateTempFile(
3488 "quz.proto",
3489 "syntax = \"proto2\";\n"
3490 "message Quz {\n"
3491 " message Foo {}\n" // nested message
3492 " optional int32 a = 2;\n"
3493 " optional group C = 4 {\n"
3494 " optional int32 d = 5;\n"
3495 " }\n"
3496 " extensions 8 to 10;\n"
3497 " optional group E = 11 {\n"
3498 " optional int32 f = 9;\n" // explicitly reuse extension range 8-10
3499 " optional group G = 15 {\n" // nested group
3500 " message Foo {}\n" // nested message inside nested group
3501 " }\n"
3502 " }\n"
3503 "}\n");
3504
3505 Run("protocol_compiler --print_free_field_numbers --proto_path=$tmpdir "
3506 "foo.proto bar.proto baz.proto quz.proto");
3507
3508 ExpectNoErrors();
3509
3510 // TODO: Cygwin doesn't work well if we try to capture stderr and
3511 // stdout at the same time. Need to figure out why and add this test back
3512 // for Cygwin.
3513 #if !defined(__CYGWIN__)
3514 ExpectCapturedStdout(
3515 "foo.Foo free: 1 3 6-7 9 11-INF\n"
3516 "Bar free: 1 3 6-7 9 11-INF\n"
3517 "Baz free: 1 3 6-7 9 14\n"
3518 "Quz.Foo free: 1-INF\n"
3519 "Quz.C free: 1-4 6-INF\n"
3520 "Quz.E.G.Foo free: 1-INF\n"
3521 "Quz.E.G free: 1-INF\n"
3522 "Quz.E free: 1-8 10-14 16-INF\n"
3523 "Quz free: 1 3 5-7 12-INF\n");
3524 #endif
3525 }
3526
TEST_F(CommandLineInterfaceTest,TargetTypeEnforcement)3527 TEST_F(CommandLineInterfaceTest, TargetTypeEnforcement) {
3528 // The target option on a field indicates what kind of entity it may apply to
3529 // when it is used as an option. This test verifies that the enforcement
3530 // works correctly on all entity types.
3531 CreateTempFile("google/protobuf/descriptor.proto",
3532 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
3533 CreateTempFile("foo.proto",
3534 R"schema(
3535 syntax = "proto2";
3536 package protobuf_unittest;
3537 import "google/protobuf/descriptor.proto";
3538 message MyOptions {
3539 optional string file_option = 1 [targets = TARGET_TYPE_FILE];
3540 optional string extension_range_option = 2 [targets =
3541 TARGET_TYPE_EXTENSION_RANGE];
3542 optional string message_option = 3 [targets = TARGET_TYPE_MESSAGE];
3543 optional string field_option = 4 [targets = TARGET_TYPE_FIELD];
3544 optional string oneof_option = 5 [targets = TARGET_TYPE_ONEOF];
3545 optional string enum_option = 6 [targets = TARGET_TYPE_ENUM];
3546 optional string enum_value_option = 7 [targets =
3547 TARGET_TYPE_ENUM_ENTRY];
3548 optional string service_option = 8 [targets = TARGET_TYPE_SERVICE];
3549 optional string method_option = 9 [targets = TARGET_TYPE_METHOD];
3550 }
3551 extend google.protobuf.FileOptions {
3552 optional MyOptions file_options = 5000;
3553 }
3554 extend google.protobuf.ExtensionRangeOptions {
3555 optional MyOptions extension_range_options = 5000;
3556 }
3557 extend google.protobuf.MessageOptions {
3558 optional MyOptions message_options = 5000;
3559 }
3560 extend google.protobuf.FieldOptions {
3561 optional MyOptions field_options = 5000;
3562 }
3563 extend google.protobuf.OneofOptions {
3564 optional MyOptions oneof_options = 5000;
3565 }
3566 extend google.protobuf.EnumOptions {
3567 optional MyOptions enum_options = 5000;
3568 }
3569 extend google.protobuf.EnumValueOptions {
3570 optional MyOptions enum_value_options = 5000;
3571 }
3572 extend google.protobuf.ServiceOptions {
3573 optional MyOptions service_options = 5000;
3574 }
3575 extend google.protobuf.MethodOptions {
3576 optional MyOptions method_options = 5000;
3577 }
3578 option (file_options).enum_option = "x";
3579 message MyMessage {
3580 option (message_options).enum_option = "x";
3581 optional int32 i = 1 [(field_options).enum_option = "x"];
3582 extensions 2 [(extension_range_options).enum_option = "x"];
3583 oneof o {
3584 option (oneof_options).enum_option = "x";
3585 bool oneof_field = 3;
3586 }
3587 }
3588 enum MyEnum {
3589 option (enum_options).file_option = "x";
3590 UNKNOWN_MY_ENUM = 0 [(enum_value_options).enum_option = "x"];
3591 }
3592 service MyService {
3593 option (service_options).enum_option = "x";
3594 rpc MyMethod(MyMessage) returns (MyMessage) {
3595 option (method_options).enum_option = "x";
3596 }
3597 }
3598 )schema");
3599
3600 Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
3601 ExpectErrorSubstring(
3602 "Option protobuf_unittest.MyOptions.enum_option "
3603 "cannot be set on an entity of type `file`.");
3604 ExpectErrorSubstring(
3605 "Option protobuf_unittest.MyOptions.enum_option "
3606 "cannot be set on an entity of type `extension range`.");
3607 ExpectErrorSubstring(
3608 "Option protobuf_unittest.MyOptions.enum_option "
3609 "cannot be set on an entity of type `message`.");
3610 ExpectErrorSubstring(
3611 "Option protobuf_unittest.MyOptions.enum_option "
3612 "cannot be set on an entity of type `field`.");
3613 ExpectErrorSubstring(
3614 "Option protobuf_unittest.MyOptions.enum_option "
3615 "cannot be set on an entity of type `oneof`.");
3616 ExpectErrorSubstring(
3617 "Option protobuf_unittest.MyOptions.file_option "
3618 "cannot be set on an entity of type `enum`.");
3619 ExpectErrorSubstring(
3620 "Option protobuf_unittest.MyOptions.enum_option "
3621 "cannot be set on an entity of type `enum entry`.");
3622 ExpectErrorSubstring(
3623 "Option protobuf_unittest.MyOptions.enum_option "
3624 "cannot be set on an entity of type `service`.");
3625 ExpectErrorSubstring(
3626 "Option protobuf_unittest.MyOptions.enum_option "
3627 "cannot be set on an entity of type `method`.");
3628 }
3629
TEST_F(CommandLineInterfaceTest,TargetTypeEnforcementMultipleTargetsValid)3630 TEST_F(CommandLineInterfaceTest, TargetTypeEnforcementMultipleTargetsValid) {
3631 CreateTempFile("google/protobuf/descriptor.proto",
3632 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
3633 CreateTempFile("foo.proto",
3634 R"schema(
3635 syntax = "proto2";
3636 package protobuf_unittest;
3637 import "google/protobuf/descriptor.proto";
3638 message MyOptions {
3639 optional string message_or_file_option = 1 [
3640 targets = TARGET_TYPE_MESSAGE, targets = TARGET_TYPE_FILE];
3641 }
3642 extend google.protobuf.FileOptions {
3643 optional MyOptions file_options = 5000;
3644 }
3645 extend google.protobuf.MessageOptions {
3646 optional MyOptions message_options = 5000;
3647 }
3648 option (file_options).message_or_file_option = "x";
3649 message MyMessage {
3650 option (message_options).message_or_file_option = "y";
3651 }
3652 )schema");
3653
3654 Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
3655 ExpectNoErrors();
3656 }
3657
TEST_F(CommandLineInterfaceTest,TargetTypeEnforcementMultipleTargetsInvalid)3658 TEST_F(CommandLineInterfaceTest, TargetTypeEnforcementMultipleTargetsInvalid) {
3659 CreateTempFile("google/protobuf/descriptor.proto",
3660 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
3661 CreateTempFile("foo.proto",
3662 R"schema(
3663 syntax = "proto2";
3664 package protobuf_unittest;
3665 import "google/protobuf/descriptor.proto";
3666 message MyOptions {
3667 optional string message_or_file_option = 1 [
3668 targets = TARGET_TYPE_MESSAGE, targets = TARGET_TYPE_FILE];
3669 }
3670 extend google.protobuf.EnumOptions {
3671 optional MyOptions enum_options = 5000;
3672 }
3673 enum MyEnum {
3674 MY_ENUM_UNSPECIFIED = 0;
3675 option (enum_options).message_or_file_option = "y";
3676 }
3677 )schema");
3678
3679 Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
3680 ExpectErrorSubstring(
3681 "Option protobuf_unittest.MyOptions.message_or_file_option cannot be set "
3682 "on an entity of type `enum`.");
3683 }
3684
TEST_F(CommandLineInterfaceTest,TargetTypeEnforcementMultipleEdgesWithConstraintsValid)3685 TEST_F(CommandLineInterfaceTest,
3686 TargetTypeEnforcementMultipleEdgesWithConstraintsValid) {
3687 CreateTempFile("google/protobuf/descriptor.proto",
3688 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
3689 CreateTempFile("foo.proto",
3690 R"schema(
3691 syntax = "proto2";
3692 package protobuf_unittest;
3693 import "google/protobuf/descriptor.proto";
3694 message A {
3695 optional B b = 1 [targets = TARGET_TYPE_FILE,
3696 targets = TARGET_TYPE_ENUM];
3697 }
3698 message B {
3699 optional int32 i = 1 [targets = TARGET_TYPE_ONEOF,
3700 targets = TARGET_TYPE_FILE];
3701 }
3702 extend google.protobuf.FileOptions {
3703 optional A file_options = 5000;
3704 }
3705 option (file_options).b.i = 42;
3706 )schema");
3707
3708 Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
3709 ExpectNoErrors();
3710 }
3711
TEST_F(CommandLineInterfaceTest,TargetTypeEnforcementMultipleEdgesWithConstraintsInvalid)3712 TEST_F(CommandLineInterfaceTest,
3713 TargetTypeEnforcementMultipleEdgesWithConstraintsInvalid) {
3714 CreateTempFile("google/protobuf/descriptor.proto",
3715 google::protobuf::DescriptorProto::descriptor()->file()->DebugString());
3716 CreateTempFile("foo.proto",
3717 R"schema(
3718 syntax = "proto2";
3719 package protobuf_unittest;
3720 import "google/protobuf/descriptor.proto";
3721 message A {
3722 optional B b = 1 [targets = TARGET_TYPE_ENUM];
3723 }
3724 message B {
3725 optional int32 i = 1 [targets = TARGET_TYPE_ONEOF];
3726 }
3727 extend google.protobuf.FileOptions {
3728 optional A file_options = 5000;
3729 }
3730 option (file_options).b.i = 42;
3731 )schema");
3732
3733 Run("protocol_compiler --plug_out=$tmpdir --proto_path=$tmpdir foo.proto");
3734 // We have target constraint violations at two different edges in the file
3735 // options, so let's make sure both are caught.
3736 ExpectErrorSubstring(
3737 "Option protobuf_unittest.A.b cannot be set on an entity of type `file`.");
3738 ExpectErrorSubstring(
3739 "Option protobuf_unittest.B.i cannot be set on an entity of type `file`.");
3740 }
3741
3742
TEST_F(CommandLineInterfaceTest,ExtensionDeclarationEnforcement)3743 TEST_F(CommandLineInterfaceTest, ExtensionDeclarationEnforcement) {
3744 CreateTempFile("foo.proto", R"schema(
3745 syntax = "proto2";
3746 package foo;
3747 message Foo {
3748 extensions 4000 to max [
3749 declaration = {
3750 number: 5000,
3751 full_name: ".foo.o"
3752 type: "int32"
3753 },
3754 declaration = {
3755 number: 9000,
3756 full_name: ".baz.z"
3757 type: ".foo.Bar"
3758 }];
3759 }
3760
3761 extend Foo {
3762 optional int32 o = 5000;
3763 repeated int32 i = 9000;
3764 })schema");
3765
3766 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
3767 ExpectErrorSubstring(
3768 "extension field 9000 is expected to be type \".foo.Bar\", not "
3769 "\"int32\"");
3770 ExpectErrorSubstring(
3771 "extension field 9000 is expected to have field name \".baz.z\", not "
3772 "\".foo.i\"");
3773 ExpectErrorSubstring("extension field 9000 is expected to be optional");
3774 }
3775
TEST_F(CommandLineInterfaceTest,ExtensionDeclarationDuplicateNames)3776 TEST_F(CommandLineInterfaceTest, ExtensionDeclarationDuplicateNames) {
3777 CreateTempFile("foo.proto", R"schema(
3778 syntax = "proto2";
3779 package foo;
3780 message Foo {
3781 extensions 4000 to max [
3782 declaration = {
3783 number: 5000,
3784 full_name: ".foo.o"
3785 type: "int32"
3786 },
3787 declaration = {
3788 number: 9000,
3789 full_name: ".foo.o"
3790 type: ".foo.Bar"
3791 }];
3792 })schema");
3793
3794 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
3795 ExpectErrorSubstring(
3796 "Extension field name \".foo.o\" is declared multiple times");
3797 }
3798
TEST_F(CommandLineInterfaceTest,ExtensionDeclarationDuplicateNumbers)3799 TEST_F(CommandLineInterfaceTest, ExtensionDeclarationDuplicateNumbers) {
3800 CreateTempFile("foo.proto", R"schema(
3801 syntax = "proto2";
3802 package foo;
3803 message Foo {
3804 extensions 4000 to max [
3805 declaration = {
3806 number: 5000,
3807 full_name: ".foo.o"
3808 type: "int32"
3809 },
3810 declaration = {
3811 number: 5000,
3812 full_name: ".foo.o"
3813 type: ".foo.Bar"
3814 }];
3815 })schema");
3816
3817 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
3818 ExpectErrorSubstring(
3819 "Extension declaration number 5000 is declared multiple times");
3820 }
3821
TEST_F(CommandLineInterfaceTest,ExtensionDeclarationCannotUseReservedNumber)3822 TEST_F(CommandLineInterfaceTest, ExtensionDeclarationCannotUseReservedNumber) {
3823 CreateTempFile("foo.proto", R"schema(
3824 syntax = "proto2";
3825 package foo;
3826 message Foo {
3827 extensions 4000 to max [
3828 declaration = {
3829 number: 5000,
3830 reserved: true
3831 full_name: ".foo.o"
3832 type: "int32"
3833 }];
3834 }
3835
3836 extend Foo {
3837 optional int32 o = 5000;
3838 })schema");
3839
3840 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
3841 ExpectErrorSubstring(
3842 "Cannot use number 5000 for extension field foo.o, as it is reserved in "
3843 "the extension declarations for message foo.Foo.");
3844 }
3845
TEST_F(CommandLineInterfaceTest,ExtensionDeclarationReservedMissingOneOfNameAndType)3846 TEST_F(CommandLineInterfaceTest,
3847 ExtensionDeclarationReservedMissingOneOfNameAndType) {
3848 CreateTempFile("foo.proto", R"schema(
3849 syntax = "proto2";
3850 package foo;
3851 message Foo {
3852 extensions 4000 to max [
3853 declaration = {
3854 number: 5000,
3855 reserved: true
3856 type: "int32"
3857 }];
3858 })schema");
3859
3860 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
3861 ExpectErrorSubstring(
3862 "Extension declaration #5000 should have both \"full_name\" and \"type\" "
3863 "set");
3864 }
3865
TEST_F(CommandLineInterfaceTest,ExtensionDeclarationMissingBothNameAndType)3866 TEST_F(CommandLineInterfaceTest, ExtensionDeclarationMissingBothNameAndType) {
3867 CreateTempFile("foo.proto", R"schema(
3868 syntax = "proto2";
3869 package foo;
3870 message Foo {
3871 extensions 4000 to max [
3872 declaration = {
3873 number: 6000
3874 }];
3875 })schema");
3876
3877 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
3878 ExpectErrorSubstring(
3879 "Extension declaration #6000 should have both \"full_name\" and \"type\" "
3880 "set");
3881 }
3882
TEST_F(CommandLineInterfaceTest,ExtensionDeclarationReservedOptionalNameAndType)3883 TEST_F(CommandLineInterfaceTest,
3884 ExtensionDeclarationReservedOptionalNameAndType) {
3885 CreateTempFile("foo.proto", R"schema(
3886 syntax = "proto2";
3887 package foo;
3888 message Foo {
3889 extensions 4000 to max [
3890 declaration = {
3891 number: 5000,
3892 reserved: true
3893 full_name: ".foo.o"
3894 type: "int32"
3895 },
3896 declaration = {
3897 number: 9000,
3898 reserved: true
3899 }];
3900 })schema");
3901
3902 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
3903 ExpectNoErrors();
3904 }
3905
TEST_F(CommandLineInterfaceTest,ExtensionDeclarationRequireDeclarationsForAll)3906 TEST_F(CommandLineInterfaceTest,
3907 ExtensionDeclarationRequireDeclarationsForAll) {
3908 CreateTempFile("foo.proto", R"schema(
3909 syntax = "proto2";
3910 package foo;
3911 message Foo {
3912 extensions 4000 to max [ declaration = {
3913 number: 5000,
3914 full_name: ".foo.o"
3915 type: "int32"
3916 }];
3917 }
3918
3919 extend Foo {
3920 optional int32 o = 5000;
3921 repeated int32 i = 9000;
3922 })schema");
3923
3924 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
3925 ExpectErrorSubstring(
3926 "Missing extension declaration for field foo.i with number 9000 in "
3927 "extendee message foo.Foo");
3928 }
3929
TEST_F(CommandLineInterfaceTest,ExtensionDeclarationVerificationDeclarationUndeclaredError)3930 TEST_F(CommandLineInterfaceTest,
3931 ExtensionDeclarationVerificationDeclarationUndeclaredError) {
3932 CreateTempFile("foo.proto", R"schema(
3933 syntax = "proto2";
3934 package foo;
3935 message Foo {
3936 extensions 4000 to max [verification = DECLARATION];
3937 }
3938 extend Foo {
3939 optional string my_field = 5000;
3940 })schema");
3941
3942 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
3943 ExpectErrorSubstring(
3944 "Missing extension declaration for field foo.my_field with number 5000 "
3945 "in extendee message foo.Foo");
3946 }
3947
TEST_F(CommandLineInterfaceTest,ExtensionDeclarationVerificationDeclarationDeclaredCompile)3948 TEST_F(CommandLineInterfaceTest,
3949 ExtensionDeclarationVerificationDeclarationDeclaredCompile) {
3950 CreateTempFile("foo.proto", R"schema(
3951 syntax = "proto2";
3952 package foo;
3953 message Foo {
3954 extensions 4000 to max [
3955 verification = DECLARATION,
3956 declaration = {
3957 number: 5000,
3958 full_name: ".foo.my_field",
3959 type: "string"
3960 }];
3961 }
3962 extend Foo {
3963 optional string my_field = 5000;
3964 })schema");
3965
3966 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
3967 ExpectNoErrors();
3968 }
3969
TEST_F(CommandLineInterfaceTest,ExtensionDeclarationUnverifiedWithDeclarationsError)3970 TEST_F(CommandLineInterfaceTest,
3971 ExtensionDeclarationUnverifiedWithDeclarationsError) {
3972 CreateTempFile("foo.proto", R"schema(
3973 syntax = "proto2";
3974 package foo;
3975 message Foo {
3976 extensions 4000 to max [
3977 verification = UNVERIFIED,
3978 declaration = {
3979 number: 5000,
3980 full_name: "foo.my_field",
3981 type: "string"
3982 }];
3983 }
3984 extend Foo {
3985 optional string my_field = 5000;
3986 })schema");
3987
3988 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
3989 ExpectErrorSubstring(
3990 "Cannot mark the extension range as UNVERIFIED when it has extension(s) "
3991 "declared.");
3992 }
3993
TEST_F(CommandLineInterfaceTest,ExtensionDeclarationDefaultUnverifiedEmptyRange)3994 TEST_F(CommandLineInterfaceTest,
3995 ExtensionDeclarationDefaultUnverifiedEmptyRange) {
3996 CreateTempFile("foo.proto", R"schema(
3997 syntax = "proto2";
3998 package foo;
3999 message Foo {
4000 extensions 4000 to max;
4001 }
4002 extend Foo {
4003 optional string my_field = 5000;
4004 })schema");
4005
4006 Run("protocol_compiler --test_out=$tmpdir --proto_path=$tmpdir foo.proto");
4007 ExpectNoErrors();
4008 }
4009
4010 // Returns true if x is a prefix of y.
IsPrefix(absl::Span<const int> x,absl::Span<const int> y)4011 bool IsPrefix(absl::Span<const int> x, absl::Span<const int> y) {
4012 return x.size() <= y.size() && x == y.subspan(0, x.size());
4013 }
4014
TEST_F(CommandLineInterfaceTest,SourceInfoOptionRetention)4015 TEST_F(CommandLineInterfaceTest, SourceInfoOptionRetention) {
4016 CreateTempFile("foo.proto",
4017 "syntax = \"proto2\";\n"
4018 "message Foo {\n"
4019 " extensions 1000 to max [\n"
4020 " declaration = {\n"
4021 " number: 1000\n"
4022 " full_name: \".video.cat_video\"\n"
4023 " type: \".video.CatVideo\"\n"
4024 " }];\n"
4025 "}\n");
4026
4027 Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
4028 "--include_source_info --proto_path=$tmpdir foo.proto");
4029
4030 ExpectNoErrors();
4031
4032 FileDescriptorSet descriptor_set;
4033 ReadDescriptorSet("descriptor_set", &descriptor_set);
4034 if (HasFatalFailure()) return;
4035 ASSERT_EQ(descriptor_set.file_size(), 1);
4036 EXPECT_EQ(descriptor_set.file(0).name(), "foo.proto");
4037
4038 // Everything starting with this path should be have been stripped from the
4039 // source code info.
4040 const int declaration_option_path[] = {
4041 FileDescriptorProto::kMessageTypeFieldNumber,
4042 0,
4043 DescriptorProto::kExtensionRangeFieldNumber,
4044 0,
4045 DescriptorProto::ExtensionRange::kOptionsFieldNumber,
4046 ExtensionRangeOptions::kDeclarationFieldNumber};
4047
4048 const SourceCodeInfo& source_code_info =
4049 descriptor_set.file(0).source_code_info();
4050 EXPECT_GT(source_code_info.location_size(), 0);
4051 for (const SourceCodeInfo::Location& location : source_code_info.location()) {
4052 EXPECT_FALSE(IsPrefix(declaration_option_path, location.path()));
4053 }
4054 }
4055
4056 // ===================================================================
4057
4058 // Test for --encode and --decode. Note that it would be easier to do this
4059 // test as a shell script, but we'd like to be able to run the test on
4060 // platforms that don't have a Bourne-compatible shell available (especially
4061 // Windows/MSVC).
4062
4063 enum EncodeDecodeTestMode { PROTO_PATH, DESCRIPTOR_SET_IN };
4064
4065 class EncodeDecodeTest : public testing::TestWithParam<EncodeDecodeTestMode> {
4066 protected:
SetUp()4067 void SetUp() override {
4068 WriteUnittestProtoDescriptorSet();
4069 duped_stdin_ = dup(STDIN_FILENO);
4070 }
4071
TearDown()4072 void TearDown() override {
4073 dup2(duped_stdin_, STDIN_FILENO);
4074 close(duped_stdin_);
4075 }
4076
RedirectStdinFromText(const std::string & input)4077 void RedirectStdinFromText(const std::string& input) {
4078 std::string filename = absl::StrCat(TestTempDir(), "/test_stdin");
4079 ABSL_CHECK_OK(File::SetContents(filename, input, true));
4080 ABSL_CHECK(RedirectStdinFromFile(filename));
4081 }
4082
RedirectStdinFromFile(const std::string & filename)4083 bool RedirectStdinFromFile(const std::string& filename) {
4084 int fd = open(filename.c_str(), O_RDONLY);
4085 if (fd < 0) return false;
4086 dup2(fd, STDIN_FILENO);
4087 close(fd);
4088 return true;
4089 }
4090
4091 // Remove '\r' characters from text.
StripCR(const std::string & text)4092 std::string StripCR(const std::string& text) {
4093 std::string result;
4094
4095 for (size_t i = 0; i < text.size(); ++i) {
4096 if (text[i] != '\r') {
4097 result.push_back(text[i]);
4098 }
4099 }
4100
4101 return result;
4102 }
4103
4104 enum Type { TEXT, BINARY };
4105 enum ReturnCode { SUCCESS, ERROR };
4106
Run(const std::string & command,bool specify_proto_files=true)4107 bool Run(const std::string& command, bool specify_proto_files = true) {
4108 std::vector<std::string> args;
4109 args.push_back("protoc");
4110 for (absl::string_view split_piece :
4111 absl::StrSplit(command, ' ', absl::SkipEmpty())) {
4112 args.push_back(std::string(split_piece));
4113 }
4114 if (specify_proto_files) {
4115 switch (GetParam()) {
4116 case PROTO_PATH:
4117 args.push_back(
4118 absl::StrCat("--proto_path=", TestUtil::TestSourceDir()));
4119 break;
4120 case DESCRIPTOR_SET_IN:
4121 args.push_back(absl::StrCat("--descriptor_set_in=",
4122 unittest_proto_descriptor_set_filename_));
4123 break;
4124 default:
4125 ADD_FAILURE() << "unexpected EncodeDecodeTestMode: " << GetParam();
4126 }
4127 }
4128
4129 std::unique_ptr<const char*[]> argv(new const char*[args.size()]);
4130 for (size_t i = 0; i < args.size(); ++i) {
4131 argv[i] = args[i].c_str();
4132 }
4133
4134 CommandLineInterface cli;
4135
4136 CaptureTestStdout();
4137 CaptureTestStderr();
4138
4139 int result = cli.Run(args.size(), argv.get());
4140
4141 captured_stdout_ = GetCapturedTestStdout();
4142 captured_stderr_ = GetCapturedTestStderr();
4143
4144 return result == 0;
4145 }
4146
ExpectStdoutMatchesBinaryFile(const std::string & filename)4147 void ExpectStdoutMatchesBinaryFile(const std::string& filename) {
4148 std::string expected_output;
4149 ABSL_CHECK_OK(
4150 File::GetContents(filename, &expected_output, true));
4151
4152 // Don't use EXPECT_EQ because we don't want to print raw binary data to
4153 // stdout on failure.
4154 EXPECT_TRUE(captured_stdout_ == expected_output);
4155 }
4156
ExpectStdoutMatchesTextFile(const std::string & filename)4157 void ExpectStdoutMatchesTextFile(const std::string& filename) {
4158 std::string expected_output;
4159 ABSL_CHECK_OK(
4160 File::GetContents(filename, &expected_output, true));
4161
4162 ExpectStdoutMatchesText(expected_output);
4163 }
4164
ExpectStdoutMatchesText(const std::string & expected_text)4165 void ExpectStdoutMatchesText(const std::string& expected_text) {
4166 EXPECT_EQ(StripCR(expected_text), StripCR(captured_stdout_));
4167 }
4168
ExpectStderrMatchesText(const std::string & expected_text)4169 void ExpectStderrMatchesText(const std::string& expected_text) {
4170 EXPECT_EQ(StripCR(expected_text), StripCR(captured_stderr_));
4171 }
4172
ExpectStderrContainsText(const std::string & expected_text)4173 void ExpectStderrContainsText(const std::string& expected_text) {
4174 EXPECT_NE(StripCR(captured_stderr_).find(StripCR(expected_text)),
4175 std::string::npos);
4176 }
4177
4178 private:
WriteUnittestProtoDescriptorSet()4179 void WriteUnittestProtoDescriptorSet() {
4180 unittest_proto_descriptor_set_filename_ =
4181 absl::StrCat(TestTempDir(), "/unittest_proto_descriptor_set.bin");
4182 FileDescriptorSet file_descriptor_set;
4183 protobuf_unittest::TestAllTypes test_all_types;
4184 test_all_types.descriptor()->file()->CopyTo(file_descriptor_set.add_file());
4185
4186 protobuf_unittest_import::ImportMessage import_message;
4187 import_message.descriptor()->file()->CopyTo(file_descriptor_set.add_file());
4188
4189 protobuf_unittest_import::PublicImportMessage public_import_message;
4190 public_import_message.descriptor()->file()->CopyTo(
4191 file_descriptor_set.add_file());
4192 ABSL_DCHECK(file_descriptor_set.IsInitialized());
4193
4194 std::string binary_proto;
4195 ABSL_CHECK(file_descriptor_set.SerializeToString(&binary_proto));
4196 ABSL_CHECK_OK(File::SetContents(unittest_proto_descriptor_set_filename_,
4197 binary_proto, true));
4198 }
4199
4200 int duped_stdin_;
4201 std::string captured_stdout_;
4202 std::string captured_stderr_;
4203 std::string unittest_proto_descriptor_set_filename_;
4204 };
4205
WriteGoldenMessage(const std::string & filename)4206 static void WriteGoldenMessage(const std::string& filename) {
4207 protobuf_unittest::TestAllTypes message;
4208 TestUtil::SetAllFields(&message);
4209 std::string golden = message.SerializeAsString();
4210 ABSL_CHECK_OK(File::SetContents(filename, golden, true));
4211 }
4212
TEST_P(EncodeDecodeTest,Encode)4213 TEST_P(EncodeDecodeTest, Encode) {
4214 std::string golden_path = absl::StrCat(TestTempDir(), "/golden_message");
4215 WriteGoldenMessage(golden_path);
4216 RedirectStdinFromFile(TestUtil::GetTestDataPath(
4217 "google/protobuf/"
4218 "testdata/text_format_unittest_data_oneof_implemented.txt"));
4219 std::string args;
4220 if (GetParam() != DESCRIPTOR_SET_IN) {
4221 args.append("google/protobuf/unittest.proto");
4222 }
4223 EXPECT_TRUE(
4224 Run(absl::StrCat(args, " --encode=protobuf_unittest.TestAllTypes")));
4225 ExpectStdoutMatchesBinaryFile(golden_path);
4226 ExpectStderrMatchesText("");
4227 }
4228
TEST_P(EncodeDecodeTest,Decode)4229 TEST_P(EncodeDecodeTest, Decode) {
4230 std::string golden_path = absl::StrCat(TestTempDir(), "/golden_message");
4231 WriteGoldenMessage(golden_path);
4232 RedirectStdinFromFile(golden_path);
4233 EXPECT_TRUE(
4234 Run("google/protobuf/unittest.proto"
4235 " --decode=protobuf_unittest.TestAllTypes"));
4236 ExpectStdoutMatchesTextFile(TestUtil::GetTestDataPath(
4237 "google/protobuf/"
4238 "testdata/text_format_unittest_data_oneof_implemented.txt"));
4239 ExpectStderrMatchesText("");
4240 }
4241
TEST_P(EncodeDecodeTest,Partial)4242 TEST_P(EncodeDecodeTest, Partial) {
4243 RedirectStdinFromText("");
4244 EXPECT_TRUE(
4245 Run("google/protobuf/unittest.proto"
4246 " --encode=protobuf_unittest.TestRequired"));
4247 ExpectStdoutMatchesText("");
4248 ExpectStderrMatchesText(
4249 "warning: Input message is missing required fields: a, b, c\n");
4250 }
4251
TEST_P(EncodeDecodeTest,DecodeRaw)4252 TEST_P(EncodeDecodeTest, DecodeRaw) {
4253 protobuf_unittest::TestAllTypes message;
4254 message.set_optional_int32(123);
4255 message.set_optional_string("foo");
4256 std::string data;
4257 message.SerializeToString(&data);
4258
4259 RedirectStdinFromText(data);
4260 EXPECT_TRUE(Run("--decode_raw", /*specify_proto_files=*/false));
4261 ExpectStdoutMatchesText(
4262 "1: 123\n"
4263 "14: \"foo\"\n");
4264 ExpectStderrMatchesText("");
4265 }
4266
TEST_P(EncodeDecodeTest,UnknownType)4267 TEST_P(EncodeDecodeTest, UnknownType) {
4268 EXPECT_FALSE(
4269 Run("google/protobuf/unittest.proto"
4270 " --encode=NoSuchType"));
4271 ExpectStdoutMatchesText("");
4272 ExpectStderrMatchesText("Type not defined: NoSuchType\n");
4273 }
4274
TEST_P(EncodeDecodeTest,ProtoParseError)4275 TEST_P(EncodeDecodeTest, ProtoParseError) {
4276 EXPECT_FALSE(
4277 Run("net/proto2/internal/no_such_file.proto "
4278 "--encode=NoSuchType"));
4279 ExpectStdoutMatchesText("");
4280 ExpectStderrContainsText(
4281 "net/proto2/internal/no_such_file.proto: No such file or directory\n");
4282 }
4283
TEST_P(EncodeDecodeTest,EncodeDeterministicOutput)4284 TEST_P(EncodeDecodeTest, EncodeDeterministicOutput) {
4285 std::string golden_path = absl::StrCat(TestTempDir(), "/golden_message");
4286 WriteGoldenMessage(golden_path);
4287 RedirectStdinFromFile(TestUtil::GetTestDataPath(
4288 "google/protobuf/"
4289 "testdata/text_format_unittest_data_oneof_implemented.txt"));
4290 std::string args;
4291 if (GetParam() != DESCRIPTOR_SET_IN) {
4292 args.append("google/protobuf/unittest.proto");
4293 }
4294 EXPECT_TRUE(Run(absl::StrCat(
4295 args, " --encode=protobuf_unittest.TestAllTypes --deterministic_output")));
4296 ExpectStdoutMatchesBinaryFile(golden_path);
4297 ExpectStderrMatchesText("");
4298 }
4299
TEST_P(EncodeDecodeTest,DecodeDeterministicOutput)4300 TEST_P(EncodeDecodeTest, DecodeDeterministicOutput) {
4301 std::string golden_path = absl::StrCat(TestTempDir(), "/golden_message");
4302 WriteGoldenMessage(golden_path);
4303 RedirectStdinFromFile(golden_path);
4304 EXPECT_FALSE(
4305 Run("google/protobuf/unittest.proto"
4306 " --decode=protobuf_unittest.TestAllTypes --deterministic_output"));
4307 ExpectStderrMatchesText(
4308 "Can only use --deterministic_output with --encode.\n");
4309 }
4310
4311 INSTANTIATE_TEST_SUITE_P(FileDescriptorSetSource, EncodeDecodeTest,
4312 testing::Values(PROTO_PATH, DESCRIPTOR_SET_IN));
4313 } // anonymous namespace
4314
4315 #endif // !GOOGLE_PROTOBUF_HEAP_CHECK_DRACONIAN
4316
4317 #include "google/protobuf/port_undef.inc"
4318
4319 } // namespace compiler
4320 } // namespace protobuf
4321 } // namespace google
4322