• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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