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 // This file makes extensive use of RFC 3092. :)
13
14 #include "google/protobuf/descriptor.h"
15
16 #include <limits.h>
17
18 #include <atomic>
19 #include <cstdint>
20 #include <cstdlib>
21 #include <deque>
22 #include <functional>
23 #include <limits>
24 #include <memory>
25 #include <string>
26 #include <tuple>
27 #include <utility>
28 #include <vector>
29
30 #include "google/protobuf/any.pb.h"
31 #include "google/protobuf/descriptor.pb.h"
32 #include <gmock/gmock.h>
33 #include <gtest/gtest.h>
34 #include "absl/base/log_severity.h"
35 #include "absl/container/btree_set.h"
36 #include "absl/container/flat_hash_set.h"
37 #include "absl/flags/flag.h"
38 #include "absl/functional/any_invocable.h"
39 #include "absl/log/absl_check.h"
40 #include "absl/log/absl_log.h"
41 #include "absl/log/die_if_null.h"
42 #include "absl/log/scoped_mock_log.h"
43 #include "absl/status/status.h"
44 #include "absl/status/statusor.h"
45 #include "absl/strings/numbers.h"
46 #include "absl/strings/str_cat.h"
47 #include "absl/strings/str_format.h"
48 #include "absl/strings/str_split.h"
49 #include "absl/strings/string_view.h"
50 #include "absl/strings/strip.h"
51 #include "absl/strings/substitute.h"
52 #include "absl/synchronization/notification.h"
53 #include "google/protobuf/compiler/importer.h"
54 #include "google/protobuf/compiler/parser.h"
55 #include "google/protobuf/cpp_features.pb.h"
56 #include "google/protobuf/descriptor_database.h"
57 #include "google/protobuf/descriptor_legacy.h"
58 #include "google/protobuf/dynamic_message.h"
59 #include "google/protobuf/feature_resolver.h"
60 #include "google/protobuf/io/coded_stream.h"
61 #include "google/protobuf/io/tokenizer.h"
62 #include "google/protobuf/io/zero_copy_stream_impl_lite.h"
63 #include "google/protobuf/test_textproto.h"
64 #include "google/protobuf/text_format.h"
65 #include "google/protobuf/unittest.pb.h"
66 #include "google/protobuf/unittest_custom_options.pb.h"
67 #include "google/protobuf/unittest_delimited.pb.h"
68 #include "google/protobuf/unittest_delimited_import.pb.h"
69 #include "google/protobuf/unittest_features.pb.h"
70 #include "google/protobuf/unittest_lazy_dependencies.pb.h"
71 #include "google/protobuf/unittest_lazy_dependencies_custom_option.pb.h"
72 #include "google/protobuf/unittest_lazy_dependencies_enum.pb.h"
73 #include "google/protobuf/unittest_proto3_arena.pb.h"
74 #include "google/protobuf/unittest_string_type.pb.h"
75
76
77 // Must be included last.
78 #include "google/protobuf/port_def.inc"
79
80 using ::google::protobuf::internal::cpp::GetUtf8CheckMode;
81 using ::google::protobuf::internal::cpp::HasPreservingUnknownEnumSemantics;
82 using ::google::protobuf::internal::cpp::Utf8CheckMode;
83 using ::testing::AnyOf;
84 using ::testing::AtLeast;
85 using ::testing::ElementsAre;
86 using ::testing::HasSubstr;
87 using ::testing::NotNull;
88 using ::testing::Return;
89
GetStatus(const absl::Status & s)90 absl::Status GetStatus(const absl::Status& s) { return s; }
91 template <typename T>
GetStatus(const absl::StatusOr<T> & s)92 absl::Status GetStatus(const absl::StatusOr<T>& s) {
93 return s.status();
94 }
95 MATCHER_P2(StatusIs, status, message,
96 absl::StrCat(".status() is ", testing::PrintToString(status))) {
97 return GetStatus(arg).code() == status &&
98 testing::ExplainMatchResult(message, GetStatus(arg).message(),
99 result_listener);
100 }
101 #define EXPECT_OK(x) EXPECT_THAT(x, StatusIs(absl::StatusCode::kOk, testing::_))
102 #define ASSERT_OK(x) ASSERT_THAT(x, StatusIs(absl::StatusCode::kOk, testing::_))
103
104 namespace google {
105 namespace protobuf {
106
107 // Can't use an anonymous namespace here due to brokenness of Tru64 compiler.
108 namespace descriptor_unittest {
109
110 // Some helpers to make assembling descriptors faster.
AddMessage(FileDescriptorProto * file,const std::string & name)111 DescriptorProto* AddMessage(FileDescriptorProto* file,
112 const std::string& name) {
113 DescriptorProto* result = file->add_message_type();
114 result->set_name(name);
115 return result;
116 }
117
AddNestedMessage(DescriptorProto * parent,const std::string & name)118 DescriptorProto* AddNestedMessage(DescriptorProto* parent,
119 const std::string& name) {
120 DescriptorProto* result = parent->add_nested_type();
121 result->set_name(name);
122 return result;
123 }
124
AddEnum(FileDescriptorProto * file,absl::string_view name)125 EnumDescriptorProto* AddEnum(FileDescriptorProto* file,
126 absl::string_view name) {
127 EnumDescriptorProto* result = file->add_enum_type();
128 result->set_name(name);
129 return result;
130 }
131
AddNestedEnum(DescriptorProto * parent,const std::string & name)132 EnumDescriptorProto* AddNestedEnum(DescriptorProto* parent,
133 const std::string& name) {
134 EnumDescriptorProto* result = parent->add_enum_type();
135 result->set_name(name);
136 return result;
137 }
138
AddService(FileDescriptorProto * file,const std::string & name)139 ServiceDescriptorProto* AddService(FileDescriptorProto* file,
140 const std::string& name) {
141 ServiceDescriptorProto* result = file->add_service();
142 result->set_name(name);
143 return result;
144 }
145
AddField(DescriptorProto * parent,const std::string & name,int number,FieldDescriptorProto::Label label,FieldDescriptorProto::Type type)146 FieldDescriptorProto* AddField(DescriptorProto* parent, const std::string& name,
147 int number, FieldDescriptorProto::Label label,
148 FieldDescriptorProto::Type type) {
149 FieldDescriptorProto* result = parent->add_field();
150 result->set_name(name);
151 result->set_number(number);
152 result->set_label(label);
153 result->set_type(type);
154 return result;
155 }
156
AddExtension(FileDescriptorProto * file,const std::string & extendee,const std::string & name,int number,FieldDescriptorProto::Label label,FieldDescriptorProto::Type type)157 FieldDescriptorProto* AddExtension(FileDescriptorProto* file,
158 const std::string& extendee,
159 const std::string& name, int number,
160 FieldDescriptorProto::Label label,
161 FieldDescriptorProto::Type type) {
162 FieldDescriptorProto* result = file->add_extension();
163 result->set_name(name);
164 result->set_number(number);
165 result->set_label(label);
166 result->set_type(type);
167 result->set_extendee(extendee);
168 return result;
169 }
170
AddNestedExtension(DescriptorProto * parent,const std::string & extendee,const std::string & name,int number,FieldDescriptorProto::Label label,FieldDescriptorProto::Type type)171 FieldDescriptorProto* AddNestedExtension(DescriptorProto* parent,
172 const std::string& extendee,
173 const std::string& name, int number,
174 FieldDescriptorProto::Label label,
175 FieldDescriptorProto::Type type) {
176 FieldDescriptorProto* result = parent->add_extension();
177 result->set_name(name);
178 result->set_number(number);
179 result->set_label(label);
180 result->set_type(type);
181 result->set_extendee(extendee);
182 return result;
183 }
184
AddExtensionRange(DescriptorProto * parent,int start,int end)185 DescriptorProto::ExtensionRange* AddExtensionRange(DescriptorProto* parent,
186 int start, int end) {
187 DescriptorProto::ExtensionRange* result = parent->add_extension_range();
188 result->set_start(start);
189 result->set_end(end);
190 return result;
191 }
192
AddReservedRange(DescriptorProto * parent,int start,int end)193 DescriptorProto::ReservedRange* AddReservedRange(DescriptorProto* parent,
194 int start, int end) {
195 DescriptorProto::ReservedRange* result = parent->add_reserved_range();
196 result->set_start(start);
197 result->set_end(end);
198 return result;
199 }
200
AddReservedRange(EnumDescriptorProto * parent,int start,int end)201 EnumDescriptorProto::EnumReservedRange* AddReservedRange(
202 EnumDescriptorProto* parent, int start, int end) {
203 EnumDescriptorProto::EnumReservedRange* result = parent->add_reserved_range();
204 result->set_start(start);
205 result->set_end(end);
206 return result;
207 }
208
AddEnumValue(EnumDescriptorProto * enum_proto,const std::string & name,int number)209 EnumValueDescriptorProto* AddEnumValue(EnumDescriptorProto* enum_proto,
210 const std::string& name, int number) {
211 EnumValueDescriptorProto* result = enum_proto->add_value();
212 result->set_name(name);
213 result->set_number(number);
214 return result;
215 }
216
AddMethod(ServiceDescriptorProto * service,const std::string & name,const std::string & input_type,const std::string & output_type)217 MethodDescriptorProto* AddMethod(ServiceDescriptorProto* service,
218 const std::string& name,
219 const std::string& input_type,
220 const std::string& output_type) {
221 MethodDescriptorProto* result = service->add_method();
222 result->set_name(name);
223 result->set_input_type(input_type);
224 result->set_output_type(output_type);
225 return result;
226 }
227
228 // Empty enums technically aren't allowed. We need to insert a dummy value
229 // into them.
AddEmptyEnum(FileDescriptorProto * file,absl::string_view name)230 void AddEmptyEnum(FileDescriptorProto* file, absl::string_view name) {
231 AddEnumValue(AddEnum(file, name), absl::StrCat(name, "_DUMMY"), 1);
232 }
233
234 class MockErrorCollector : public DescriptorPool::ErrorCollector {
235 public:
236 MockErrorCollector() = default;
237 ~MockErrorCollector() override = default;
238
239 std::string text_;
240 std::string warning_text_;
241
242 // implements ErrorCollector ---------------------------------------
RecordError(absl::string_view filename,absl::string_view element_name,const Message * descriptor,ErrorLocation location,absl::string_view message)243 void RecordError(absl::string_view filename, absl::string_view element_name,
244 const Message* descriptor, ErrorLocation location,
245 absl::string_view message) override {
246 absl::SubstituteAndAppend(&text_, "$0: $1: $2: $3\n", filename,
247 element_name, ErrorLocationName(location),
248 message);
249 }
250
251 // implements ErrorCollector ---------------------------------------
RecordWarning(absl::string_view filename,absl::string_view element_name,const Message * descriptor,ErrorLocation location,absl::string_view message)252 void RecordWarning(absl::string_view filename, absl::string_view element_name,
253 const Message* descriptor, ErrorLocation location,
254 absl::string_view message) override {
255 absl::SubstituteAndAppend(&warning_text_, "$0: $1: $2: $3\n", filename,
256 element_name, ErrorLocationName(location),
257 message);
258 }
259 };
260
261 // ===================================================================
262
263 // Test simple files.
264 class FileDescriptorTest : public testing::Test {
265 protected:
SetUp()266 void SetUp() override {
267 // Build descriptors for the following definitions:
268 //
269 // // in "foo.proto"
270 // message FooMessage { extensions 1; }
271 // enum FooEnum {FOO_ENUM_VALUE = 1;}
272 // service FooService {}
273 // extend FooMessage { optional int32 foo_extension = 1; }
274 //
275 // // in "bar.proto"
276 // package bar_package;
277 // message BarMessage { extensions 1; }
278 // enum BarEnum {BAR_ENUM_VALUE = 1;}
279 // service BarService {}
280 // extend BarMessage { optional int32 bar_extension = 1; }
281 //
282 // Also, we have an empty file "baz.proto". This file's purpose is to
283 // make sure that even though it has the same package as foo.proto,
284 // searching it for members of foo.proto won't work.
285
286 FileDescriptorProto foo_file;
287 foo_file.set_name("foo.proto");
288 AddExtensionRange(AddMessage(&foo_file, "FooMessage"), 1, 2);
289 AddEnumValue(AddEnum(&foo_file, "FooEnum"), "FOO_ENUM_VALUE", 1);
290 AddService(&foo_file, "FooService");
291 AddExtension(&foo_file, "FooMessage", "foo_extension", 1,
292 FieldDescriptorProto::LABEL_OPTIONAL,
293 FieldDescriptorProto::TYPE_INT32);
294
295 FileDescriptorProto bar_file;
296 bar_file.set_name("bar.proto");
297 bar_file.set_package("bar_package");
298 bar_file.add_dependency("foo.proto");
299 AddExtensionRange(AddMessage(&bar_file, "BarMessage"), 1, 2);
300 AddEnumValue(AddEnum(&bar_file, "BarEnum"), "BAR_ENUM_VALUE", 1);
301 AddService(&bar_file, "BarService");
302 AddExtension(&bar_file, "bar_package.BarMessage", "bar_extension", 1,
303 FieldDescriptorProto::LABEL_OPTIONAL,
304 FieldDescriptorProto::TYPE_INT32);
305
306 FileDescriptorProto baz_file;
307 baz_file.set_name("baz.proto");
308
309 // Build the descriptors and get the pointers.
310 foo_file_ = pool_.BuildFile(foo_file);
311 ASSERT_TRUE(foo_file_ != nullptr);
312
313 bar_file_ = pool_.BuildFile(bar_file);
314 ASSERT_TRUE(bar_file_ != nullptr);
315
316 baz_file_ = pool_.BuildFile(baz_file);
317 ASSERT_TRUE(baz_file_ != nullptr);
318
319 ASSERT_EQ(1, foo_file_->message_type_count());
320 foo_message_ = foo_file_->message_type(0);
321 ASSERT_EQ(1, foo_file_->enum_type_count());
322 foo_enum_ = foo_file_->enum_type(0);
323 ASSERT_EQ(1, foo_enum_->value_count());
324 foo_enum_value_ = foo_enum_->value(0);
325 ASSERT_EQ(1, foo_file_->service_count());
326 foo_service_ = foo_file_->service(0);
327 ASSERT_EQ(1, foo_file_->extension_count());
328 foo_extension_ = foo_file_->extension(0);
329
330 ASSERT_EQ(1, bar_file_->message_type_count());
331 bar_message_ = bar_file_->message_type(0);
332 ASSERT_EQ(1, bar_file_->enum_type_count());
333 bar_enum_ = bar_file_->enum_type(0);
334 ASSERT_EQ(1, bar_enum_->value_count());
335 bar_enum_value_ = bar_enum_->value(0);
336 ASSERT_EQ(1, bar_file_->service_count());
337 bar_service_ = bar_file_->service(0);
338 ASSERT_EQ(1, bar_file_->extension_count());
339 bar_extension_ = bar_file_->extension(0);
340 }
341
342 DescriptorPool pool_;
343
344 const FileDescriptor* foo_file_;
345 const FileDescriptor* bar_file_;
346 const FileDescriptor* baz_file_;
347
348 const Descriptor* foo_message_;
349 const EnumDescriptor* foo_enum_;
350 const EnumValueDescriptor* foo_enum_value_;
351 const ServiceDescriptor* foo_service_;
352 const FieldDescriptor* foo_extension_;
353
354 const Descriptor* bar_message_;
355 const EnumDescriptor* bar_enum_;
356 const EnumValueDescriptor* bar_enum_value_;
357 const ServiceDescriptor* bar_service_;
358 const FieldDescriptor* bar_extension_;
359 };
360
TEST_F(FileDescriptorTest,Name)361 TEST_F(FileDescriptorTest, Name) {
362 EXPECT_EQ("foo.proto", foo_file_->name());
363 EXPECT_EQ("bar.proto", bar_file_->name());
364 EXPECT_EQ("baz.proto", baz_file_->name());
365 }
366
TEST_F(FileDescriptorTest,Package)367 TEST_F(FileDescriptorTest, Package) {
368 EXPECT_EQ("", foo_file_->package());
369 EXPECT_EQ("bar_package", bar_file_->package());
370 }
371
TEST_F(FileDescriptorTest,Dependencies)372 TEST_F(FileDescriptorTest, Dependencies) {
373 EXPECT_EQ(0, foo_file_->dependency_count());
374 EXPECT_EQ(1, bar_file_->dependency_count());
375 EXPECT_EQ(foo_file_, bar_file_->dependency(0));
376 }
377
TEST_F(FileDescriptorTest,FindMessageTypeByName)378 TEST_F(FileDescriptorTest, FindMessageTypeByName) {
379 EXPECT_EQ(foo_message_, foo_file_->FindMessageTypeByName("FooMessage"));
380 EXPECT_EQ(bar_message_, bar_file_->FindMessageTypeByName("BarMessage"));
381
382 EXPECT_TRUE(foo_file_->FindMessageTypeByName("BarMessage") == nullptr);
383 EXPECT_TRUE(bar_file_->FindMessageTypeByName("FooMessage") == nullptr);
384 EXPECT_TRUE(baz_file_->FindMessageTypeByName("FooMessage") == nullptr);
385
386 EXPECT_TRUE(foo_file_->FindMessageTypeByName("NoSuchMessage") == nullptr);
387 EXPECT_TRUE(foo_file_->FindMessageTypeByName("FooEnum") == nullptr);
388 }
389
TEST_F(FileDescriptorTest,FindEnumTypeByName)390 TEST_F(FileDescriptorTest, FindEnumTypeByName) {
391 EXPECT_EQ(foo_enum_, foo_file_->FindEnumTypeByName("FooEnum"));
392 EXPECT_EQ(bar_enum_, bar_file_->FindEnumTypeByName("BarEnum"));
393
394 EXPECT_TRUE(foo_file_->FindEnumTypeByName("BarEnum") == nullptr);
395 EXPECT_TRUE(bar_file_->FindEnumTypeByName("FooEnum") == nullptr);
396 EXPECT_TRUE(baz_file_->FindEnumTypeByName("FooEnum") == nullptr);
397
398 EXPECT_TRUE(foo_file_->FindEnumTypeByName("NoSuchEnum") == nullptr);
399 EXPECT_TRUE(foo_file_->FindEnumTypeByName("FooMessage") == nullptr);
400 }
401
TEST_F(FileDescriptorTest,FindEnumValueByName)402 TEST_F(FileDescriptorTest, FindEnumValueByName) {
403 EXPECT_EQ(foo_enum_value_, foo_file_->FindEnumValueByName("FOO_ENUM_VALUE"));
404 EXPECT_EQ(bar_enum_value_, bar_file_->FindEnumValueByName("BAR_ENUM_VALUE"));
405
406 EXPECT_TRUE(foo_file_->FindEnumValueByName("BAR_ENUM_VALUE") == nullptr);
407 EXPECT_TRUE(bar_file_->FindEnumValueByName("FOO_ENUM_VALUE") == nullptr);
408 EXPECT_TRUE(baz_file_->FindEnumValueByName("FOO_ENUM_VALUE") == nullptr);
409
410 EXPECT_TRUE(foo_file_->FindEnumValueByName("NO_SUCH_VALUE") == nullptr);
411 EXPECT_TRUE(foo_file_->FindEnumValueByName("FooMessage") == nullptr);
412 }
413
TEST_F(FileDescriptorTest,FindServiceByName)414 TEST_F(FileDescriptorTest, FindServiceByName) {
415 EXPECT_EQ(foo_service_, foo_file_->FindServiceByName("FooService"));
416 EXPECT_EQ(bar_service_, bar_file_->FindServiceByName("BarService"));
417
418 EXPECT_TRUE(foo_file_->FindServiceByName("BarService") == nullptr);
419 EXPECT_TRUE(bar_file_->FindServiceByName("FooService") == nullptr);
420 EXPECT_TRUE(baz_file_->FindServiceByName("FooService") == nullptr);
421
422 EXPECT_TRUE(foo_file_->FindServiceByName("NoSuchService") == nullptr);
423 EXPECT_TRUE(foo_file_->FindServiceByName("FooMessage") == nullptr);
424 }
425
TEST_F(FileDescriptorTest,FindExtensionByName)426 TEST_F(FileDescriptorTest, FindExtensionByName) {
427 EXPECT_EQ(foo_extension_, foo_file_->FindExtensionByName("foo_extension"));
428 EXPECT_EQ(bar_extension_, bar_file_->FindExtensionByName("bar_extension"));
429
430 EXPECT_TRUE(foo_file_->FindExtensionByName("bar_extension") == nullptr);
431 EXPECT_TRUE(bar_file_->FindExtensionByName("foo_extension") == nullptr);
432 EXPECT_TRUE(baz_file_->FindExtensionByName("foo_extension") == nullptr);
433
434 EXPECT_TRUE(foo_file_->FindExtensionByName("no_such_extension") == nullptr);
435 EXPECT_TRUE(foo_file_->FindExtensionByName("FooMessage") == nullptr);
436 }
437
TEST_F(FileDescriptorTest,FindExtensionByNumber)438 TEST_F(FileDescriptorTest, FindExtensionByNumber) {
439 EXPECT_EQ(foo_extension_, pool_.FindExtensionByNumber(foo_message_, 1));
440 EXPECT_EQ(bar_extension_, pool_.FindExtensionByNumber(bar_message_, 1));
441
442 EXPECT_TRUE(pool_.FindExtensionByNumber(foo_message_, 2) == nullptr);
443 }
444
445
TEST_F(FileDescriptorTest,BuildAgain)446 TEST_F(FileDescriptorTest, BuildAgain) {
447 // Test that if we call BuildFile again on the same input we get the same
448 // FileDescriptor back.
449 FileDescriptorProto file;
450 foo_file_->CopyTo(&file);
451 EXPECT_EQ(foo_file_, pool_.BuildFile(file));
452
453 // But if we change the file then it won't work.
454 file.set_package("some.other.package");
455 EXPECT_TRUE(pool_.BuildFile(file) == nullptr);
456 }
457
TEST_F(FileDescriptorTest,BuildAgainWithSyntax)458 TEST_F(FileDescriptorTest, BuildAgainWithSyntax) {
459 // Test that if we call BuildFile again on the same input we get the same
460 // FileDescriptor back even if syntax param is specified.
461 FileDescriptorProto proto_syntax2;
462 proto_syntax2.set_name("foo_syntax2");
463 proto_syntax2.set_syntax("proto2");
464
465 const FileDescriptor* proto2_descriptor = pool_.BuildFile(proto_syntax2);
466 EXPECT_TRUE(proto2_descriptor != nullptr);
467 EXPECT_EQ(proto2_descriptor, pool_.BuildFile(proto_syntax2));
468
469 FileDescriptorProto implicit_proto2;
470 implicit_proto2.set_name("foo_implicit_syntax2");
471
472 const FileDescriptor* implicit_proto2_descriptor =
473 pool_.BuildFile(implicit_proto2);
474 EXPECT_TRUE(implicit_proto2_descriptor != nullptr);
475 // We get the same FileDescriptor back if syntax param is explicitly
476 // specified.
477 implicit_proto2.set_syntax("proto2");
478 EXPECT_EQ(implicit_proto2_descriptor, pool_.BuildFile(implicit_proto2));
479
480 FileDescriptorProto proto_syntax3;
481 proto_syntax3.set_name("foo_syntax3");
482 proto_syntax3.set_syntax("proto3");
483
484 const FileDescriptor* proto3_descriptor = pool_.BuildFile(proto_syntax3);
485 EXPECT_TRUE(proto3_descriptor != nullptr);
486 EXPECT_EQ(proto3_descriptor, pool_.BuildFile(proto_syntax3));
487 }
488
TEST_F(FileDescriptorTest,Edition)489 TEST_F(FileDescriptorTest, Edition) {
490 FileDescriptorProto proto;
491 proto.set_name("foo");
492 {
493 proto.set_syntax("proto2");
494 DescriptorPool pool;
495 const FileDescriptor* file = pool.BuildFile(proto);
496 ASSERT_TRUE(file != nullptr);
497 EXPECT_EQ(FileDescriptorLegacy(file).edition(), Edition::EDITION_PROTO2);
498 FileDescriptorProto other;
499 file->CopyTo(&other);
500 EXPECT_EQ("", other.syntax());
501 EXPECT_FALSE(other.has_edition());
502 }
503 {
504 proto.set_syntax("proto3");
505 DescriptorPool pool;
506 const FileDescriptor* file = pool.BuildFile(proto);
507 ASSERT_TRUE(file != nullptr);
508 EXPECT_EQ(FileDescriptorLegacy(file).edition(), Edition::EDITION_PROTO3);
509 FileDescriptorProto other;
510 file->CopyTo(&other);
511 EXPECT_EQ("proto3", other.syntax());
512 EXPECT_FALSE(other.has_edition());
513 }
514 {
515 proto.set_syntax("editions");
516 proto.set_edition(EDITION_2023);
517 DescriptorPool pool;
518 const FileDescriptor* file = pool.BuildFile(proto);
519 ASSERT_TRUE(file != nullptr);
520 EXPECT_EQ(FileDescriptorLegacy(file).edition(), Edition::EDITION_2023);
521 FileDescriptorProto other;
522 file->CopyTo(&other);
523 EXPECT_EQ("editions", other.syntax());
524 EXPECT_EQ(other.edition(), EDITION_2023);
525 }
526 }
527
TEST_F(FileDescriptorTest,CopyHeadingTo)528 TEST_F(FileDescriptorTest, CopyHeadingTo) {
529 FileDescriptorProto proto;
530 proto.set_name("foo.proto");
531 proto.set_package("foo.bar.baz");
532 proto.set_syntax("proto3");
533 proto.mutable_options()->set_java_package("foo.bar.baz");
534
535 // Won't be copied.
536 proto.add_message_type()->set_name("Foo");
537
538 DescriptorPool pool;
539 const FileDescriptor* file = pool.BuildFile(proto);
540 ASSERT_NE(file, nullptr);
541
542 FileDescriptorProto other;
543 file->CopyHeadingTo(&other);
544 EXPECT_EQ(other.name(), "foo.proto");
545 EXPECT_EQ(other.package(), "foo.bar.baz");
546 EXPECT_EQ(other.syntax(), "proto3");
547 EXPECT_EQ(other.options().java_package(), "foo.bar.baz");
548 EXPECT_TRUE(other.message_type().empty());
549 EXPECT_EQ(&other.options().features(), &FeatureSet::default_instance());
550 {
551 proto.set_syntax("editions");
552 proto.set_edition(EDITION_2023);
553
554 DescriptorPool pool;
555 const FileDescriptor* file = pool.BuildFile(proto);
556 ASSERT_NE(file, nullptr);
557
558 FileDescriptorProto other;
559 file->CopyHeadingTo(&other);
560 EXPECT_EQ(other.name(), "foo.proto");
561 EXPECT_EQ(other.package(), "foo.bar.baz");
562 EXPECT_EQ(other.syntax(), "editions");
563 EXPECT_EQ(other.edition(), EDITION_2023);
564 EXPECT_EQ(other.options().java_package(), "foo.bar.baz");
565 EXPECT_TRUE(other.message_type().empty());
566 EXPECT_EQ(&other.options().features(), &FeatureSet::default_instance());
567 }
568 }
569
ExtractDebugString(const FileDescriptor * file,absl::flat_hash_set<absl::string_view> * visited,std::vector<std::pair<absl::string_view,std::string>> * debug_strings)570 void ExtractDebugString(
571 const FileDescriptor* file, absl::flat_hash_set<absl::string_view>* visited,
572 std::vector<std::pair<absl::string_view, std::string>>* debug_strings) {
573 if (!visited->insert(file->name()).second) {
574 return;
575 }
576 for (int i = 0; i < file->dependency_count(); ++i) {
577 ExtractDebugString(file->dependency(i), visited, debug_strings);
578 }
579 debug_strings->push_back({file->name(), file->DebugString()});
580 }
581
582 class SimpleErrorCollector : public io::ErrorCollector {
583 public:
584 // implements ErrorCollector ---------------------------------------
RecordError(int line,int column,absl::string_view message)585 void RecordError(int line, int column, absl::string_view message) override {
586 last_error_ = absl::StrFormat("%d:%d:%s", line, column, message);
587 }
588
last_error()589 const std::string& last_error() { return last_error_; }
590
591 private:
592 std::string last_error_;
593 };
594 // Test that the result of FileDescriptor::DebugString() can be used to create
595 // the original descriptors.
TEST_F(FileDescriptorTest,DebugStringRoundTrip)596 TEST_F(FileDescriptorTest, DebugStringRoundTrip) {
597 absl::flat_hash_set<absl::string_view> visited;
598 std::vector<std::pair<absl::string_view, std::string>> debug_strings;
599 ExtractDebugString(protobuf_unittest::TestAllTypes::descriptor()->file(),
600 &visited, &debug_strings);
601 ExtractDebugString(
602 protobuf_unittest::TestMessageWithCustomOptions::descriptor()->file(),
603 &visited, &debug_strings);
604 ExtractDebugString(proto3_arena_unittest::TestAllTypes::descriptor()->file(),
605 &visited, &debug_strings);
606 ASSERT_GE(debug_strings.size(), 3);
607
608 DescriptorPool pool;
609 for (size_t i = 0; i < debug_strings.size(); ++i) {
610 const absl::string_view name = debug_strings[i].first;
611 const std::string& content = debug_strings[i].second;
612 io::ArrayInputStream input_stream(content.data(), content.size());
613 SimpleErrorCollector error_collector;
614 io::Tokenizer tokenizer(&input_stream, &error_collector);
615 compiler::Parser parser;
616 parser.RecordErrorsTo(&error_collector);
617 FileDescriptorProto proto;
618 ASSERT_TRUE(parser.Parse(&tokenizer, &proto))
619 << error_collector.last_error() << "\n"
620 << content;
621 ASSERT_EQ("", error_collector.last_error());
622 proto.set_name(name);
623 const FileDescriptor* descriptor = pool.BuildFile(proto);
624 ASSERT_TRUE(descriptor != nullptr) << error_collector.last_error();
625 EXPECT_EQ(content, descriptor->DebugString());
626 }
627 }
628
TEST_F(FileDescriptorTest,AbslStringifyWorks)629 TEST_F(FileDescriptorTest, AbslStringifyWorks) {
630 std::string s = absl::StrFormat(
631 "%v",
632 *protobuf_unittest::TestMessageWithCustomOptions::descriptor()->file());
633 EXPECT_THAT(s, HasSubstr("TestMessageWithCustomOptions"));
634 }
635
636 // ===================================================================
637
638 // Test simple flat messages and fields.
639 class DescriptorTest : public testing::Test {
640 protected:
SetUp()641 void SetUp() override {
642 // Build descriptors for the following definitions:
643 //
644 // // in "foo.proto"
645 // message TestForeign {}
646 // enum TestEnum {}
647 //
648 // message TestMessage {
649 // required string foo = 1;
650 // optional TestEnum bar = 6;
651 // repeated TestForeign baz = 500000000;
652 // optional group moo = 15 {}
653 // }
654 //
655 // // in "bar.proto"
656 // package corge.grault;
657 // message TestMessage2 {
658 // required string foo = 1;
659 // required string bar = 2;
660 // required string mooo = 6;
661 // }
662 //
663 // // in "map.proto"
664 // message TestMessage3 {
665 // map<int32, int32> map_int32_int32 = 1;
666 // }
667 //
668 // // in "json.proto"
669 // message TestMessage4 {
670 // optional int32 field_name1 = 1;
671 // optional int32 fieldName2 = 2;
672 // optional int32 FieldName3 = 3;
673 // optional int32 _field_name4 = 4;
674 // optional int32 FIELD_NAME5 = 5;
675 // optional int32 field_name6 = 6 [json_name = "@type"];
676 // }
677 //
678 // We cheat and use TestForeign as the type for moo rather than create
679 // an actual nested type.
680 //
681 // Since all primitive types (including string) use the same building
682 // code, there's no need to test each one individually.
683 //
684 // TestMessage2 is primarily here to test FindFieldByName and friends.
685 // All messages created from the same DescriptorPool share the same lookup
686 // table, so we need to insure that they don't interfere.
687
688 FileDescriptorProto foo_file;
689 foo_file.set_name("foo.proto");
690 AddMessage(&foo_file, "TestForeign");
691 AddEmptyEnum(&foo_file, "TestEnum");
692
693 DescriptorProto* message = AddMessage(&foo_file, "TestMessage");
694 AddField(message, "foo", 1, FieldDescriptorProto::LABEL_REQUIRED,
695 FieldDescriptorProto::TYPE_STRING);
696 AddField(message, "bar", 6, FieldDescriptorProto::LABEL_OPTIONAL,
697 FieldDescriptorProto::TYPE_ENUM)
698 ->set_type_name("TestEnum");
699 AddField(message, "baz", 500000000, FieldDescriptorProto::LABEL_REPEATED,
700 FieldDescriptorProto::TYPE_MESSAGE)
701 ->set_type_name("TestForeign");
702 AddField(message, "moo", 15, FieldDescriptorProto::LABEL_OPTIONAL,
703 FieldDescriptorProto::TYPE_GROUP)
704 ->set_type_name("TestForeign");
705
706 FileDescriptorProto bar_file;
707 bar_file.set_name("bar.proto");
708 bar_file.set_package("corge.grault");
709
710 DescriptorProto* message2 = AddMessage(&bar_file, "TestMessage2");
711 AddField(message2, "foo", 1, FieldDescriptorProto::LABEL_REQUIRED,
712 FieldDescriptorProto::TYPE_STRING);
713 AddField(message2, "bar", 2, FieldDescriptorProto::LABEL_REQUIRED,
714 FieldDescriptorProto::TYPE_STRING);
715 AddField(message2, "mooo", 6, FieldDescriptorProto::LABEL_REQUIRED,
716 FieldDescriptorProto::TYPE_STRING);
717
718 FileDescriptorProto map_file;
719 map_file.set_name("map.proto");
720 DescriptorProto* message3 = AddMessage(&map_file, "TestMessage3");
721
722 DescriptorProto* entry = AddNestedMessage(message3, "MapInt32Int32Entry");
723 AddField(entry, "key", 1, FieldDescriptorProto::LABEL_OPTIONAL,
724 FieldDescriptorProto::TYPE_INT32);
725 AddField(entry, "value", 2, FieldDescriptorProto::LABEL_OPTIONAL,
726 FieldDescriptorProto::TYPE_INT32);
727 entry->mutable_options()->set_map_entry(true);
728
729 AddField(message3, "map_int32_int32", 1,
730 FieldDescriptorProto::LABEL_REPEATED,
731 FieldDescriptorProto::TYPE_MESSAGE)
732 ->set_type_name("MapInt32Int32Entry");
733
734 FileDescriptorProto json_file;
735 json_file.set_name("json.proto");
736 json_file.set_syntax("proto3");
737 DescriptorProto* message4 = AddMessage(&json_file, "TestMessage4");
738 AddField(message4, "field_name1", 1, FieldDescriptorProto::LABEL_OPTIONAL,
739 FieldDescriptorProto::TYPE_INT32);
740 AddField(message4, "fieldName2", 2, FieldDescriptorProto::LABEL_OPTIONAL,
741 FieldDescriptorProto::TYPE_INT32);
742 AddField(message4, "FieldName3", 3, FieldDescriptorProto::LABEL_OPTIONAL,
743 FieldDescriptorProto::TYPE_INT32);
744 AddField(message4, "_field_name4", 4, FieldDescriptorProto::LABEL_OPTIONAL,
745 FieldDescriptorProto::TYPE_INT32);
746 AddField(message4, "FIELD_NAME5", 5, FieldDescriptorProto::LABEL_OPTIONAL,
747 FieldDescriptorProto::TYPE_INT32);
748 AddField(message4, "field_name6", 6, FieldDescriptorProto::LABEL_OPTIONAL,
749 FieldDescriptorProto::TYPE_INT32)
750 ->set_json_name("@type");
751 AddField(message4, "fieldname7", 7, FieldDescriptorProto::LABEL_OPTIONAL,
752 FieldDescriptorProto::TYPE_INT32);
753
754 // Build the descriptors and get the pointers.
755 foo_file_ = pool_.BuildFile(foo_file);
756 ASSERT_TRUE(foo_file_ != nullptr);
757
758 bar_file_ = pool_.BuildFile(bar_file);
759 ASSERT_TRUE(bar_file_ != nullptr);
760
761 map_file_ = pool_.BuildFile(map_file);
762 ASSERT_TRUE(map_file_ != nullptr);
763
764 json_file_ = pool_.BuildFile(json_file);
765 ASSERT_TRUE(json_file_ != nullptr);
766
767 ASSERT_EQ(1, foo_file_->enum_type_count());
768 enum_ = foo_file_->enum_type(0);
769
770 ASSERT_EQ(2, foo_file_->message_type_count());
771 foreign_ = foo_file_->message_type(0);
772 message_ = foo_file_->message_type(1);
773
774 ASSERT_EQ(4, message_->field_count());
775 foo_ = message_->field(0);
776 bar_ = message_->field(1);
777 baz_ = message_->field(2);
778 moo_ = message_->field(3);
779
780 ASSERT_EQ(1, bar_file_->message_type_count());
781 message2_ = bar_file_->message_type(0);
782
783 ASSERT_EQ(3, message2_->field_count());
784 foo2_ = message2_->field(0);
785 bar2_ = message2_->field(1);
786 mooo2_ = message2_->field(2);
787
788 ASSERT_EQ(1, map_file_->message_type_count());
789 message3_ = map_file_->message_type(0);
790
791 ASSERT_EQ(1, message3_->field_count());
792 map_ = message3_->field(0);
793
794 ASSERT_EQ(1, json_file_->message_type_count());
795 message4_ = json_file_->message_type(0);
796 }
797
CopyWithJsonName(const Descriptor * message,DescriptorProto * proto)798 void CopyWithJsonName(const Descriptor* message, DescriptorProto* proto) {
799 message->CopyTo(proto);
800 message->CopyJsonNameTo(proto);
801 }
802
FindValueByNumberCreatingIfUnknown(const EnumDescriptor * desc,int number)803 const EnumValueDescriptor* FindValueByNumberCreatingIfUnknown(
804 const EnumDescriptor* desc, int number) {
805 return desc->FindValueByNumberCreatingIfUnknown(number);
806 }
807
808 DescriptorPool pool_;
809
810 const FileDescriptor* foo_file_;
811 const FileDescriptor* bar_file_;
812 const FileDescriptor* map_file_;
813 const FileDescriptor* json_file_;
814
815 const Descriptor* message_;
816 const Descriptor* message2_;
817 const Descriptor* message3_;
818 const Descriptor* message4_;
819 const Descriptor* foreign_;
820 const EnumDescriptor* enum_;
821
822 const FieldDescriptor* foo_;
823 const FieldDescriptor* bar_;
824 const FieldDescriptor* baz_;
825 const FieldDescriptor* moo_;
826
827 const FieldDescriptor* foo2_;
828 const FieldDescriptor* bar2_;
829 const FieldDescriptor* mooo2_;
830
831 const FieldDescriptor* map_;
832 };
833
TEST_F(DescriptorTest,Name)834 TEST_F(DescriptorTest, Name) {
835 EXPECT_EQ("TestMessage", message_->name());
836 EXPECT_EQ("TestMessage", message_->full_name());
837 EXPECT_EQ(foo_file_, message_->file());
838
839 EXPECT_EQ("TestMessage2", message2_->name());
840 EXPECT_EQ("corge.grault.TestMessage2", message2_->full_name());
841 EXPECT_EQ(bar_file_, message2_->file());
842 }
843
TEST_F(DescriptorTest,ContainingType)844 TEST_F(DescriptorTest, ContainingType) {
845 EXPECT_TRUE(message_->containing_type() == nullptr);
846 EXPECT_TRUE(message2_->containing_type() == nullptr);
847 }
848
TEST_F(DescriptorTest,FieldNamesDedup)849 TEST_F(DescriptorTest, FieldNamesDedup) {
850 const auto collect_unique_names = [](const FieldDescriptor* field) {
851 absl::btree_set<absl::string_view> names{
852 field->name(), field->lowercase_name(), field->camelcase_name(),
853 field->json_name()};
854 // Verify that we have the same number of string objects as we have string
855 // values. That is, duplicate names use the same std::string object.
856 // This is for memory efficiency.
857 EXPECT_EQ(names.size(),
858 (absl::flat_hash_set<const void*>{
859 field->name().data(), field->lowercase_name().data(),
860 field->camelcase_name().data(), field->json_name().data()}
861 .size()))
862 << testing::PrintToString(names);
863 return names;
864 };
865
866 // field_name1
867 EXPECT_THAT(collect_unique_names(message4_->field(0)),
868 ElementsAre("fieldName1", "field_name1"));
869 // fieldName2
870 EXPECT_THAT(collect_unique_names(message4_->field(1)),
871 ElementsAre("fieldName2", "fieldname2"));
872 // FieldName3
873 EXPECT_THAT(collect_unique_names(message4_->field(2)),
874 ElementsAre("FieldName3", "fieldName3", "fieldname3"));
875 // _field_name4
876 EXPECT_THAT(collect_unique_names(message4_->field(3)),
877 ElementsAre("FieldName4", "_field_name4", "fieldName4"));
878 // FIELD_NAME5
879 EXPECT_THAT(
880 collect_unique_names(message4_->field(4)),
881 ElementsAre("FIELDNAME5", "FIELD_NAME5", "fIELDNAME5", "field_name5"));
882 // field_name6, with json name @type
883 EXPECT_THAT(collect_unique_names(message4_->field(5)),
884 ElementsAre("@type", "fieldName6", "field_name6"));
885 // fieldname7
886 EXPECT_THAT(collect_unique_names(message4_->field(6)),
887 ElementsAre("fieldname7"));
888 }
889
TEST_F(DescriptorTest,FieldNameDedupJsonEqFull)890 TEST_F(DescriptorTest, FieldNameDedupJsonEqFull) {
891 // Test a regression where json_name == full_name
892 FileDescriptorProto proto;
893 proto.set_name("file");
894 auto* message = AddMessage(&proto, "Name1");
895 auto* field =
896 AddField(message, "Name2", 1, FieldDescriptorProto::LABEL_OPTIONAL,
897 FieldDescriptorProto::TYPE_INT32);
898 field->set_json_name("Name1.Name2");
899 auto* file = pool_.BuildFile(proto);
900 EXPECT_EQ(file->message_type(0)->name(), "Name1");
901 EXPECT_EQ(file->message_type(0)->field(0)->name(), "Name2");
902 EXPECT_EQ(file->message_type(0)->field(0)->full_name(), "Name1.Name2");
903 EXPECT_EQ(file->message_type(0)->field(0)->json_name(), "Name1.Name2");
904 }
905
TEST_F(DescriptorTest,FieldsByIndex)906 TEST_F(DescriptorTest, FieldsByIndex) {
907 ASSERT_EQ(4, message_->field_count());
908 EXPECT_EQ(foo_, message_->field(0));
909 EXPECT_EQ(bar_, message_->field(1));
910 EXPECT_EQ(baz_, message_->field(2));
911 EXPECT_EQ(moo_, message_->field(3));
912 }
913
TEST_F(DescriptorTest,FindFieldByName)914 TEST_F(DescriptorTest, FindFieldByName) {
915 // All messages in the same DescriptorPool share a single lookup table for
916 // fields. So, in addition to testing that FindFieldByName finds the fields
917 // of the message, we need to test that it does *not* find the fields of
918 // *other* messages.
919
920 EXPECT_EQ(foo_, message_->FindFieldByName("foo"));
921 EXPECT_EQ(bar_, message_->FindFieldByName("bar"));
922 EXPECT_EQ(baz_, message_->FindFieldByName("baz"));
923 EXPECT_EQ(moo_, message_->FindFieldByName("moo"));
924 EXPECT_TRUE(message_->FindFieldByName("no_such_field") == nullptr);
925 EXPECT_TRUE(message_->FindFieldByName("mooo") == nullptr);
926
927 EXPECT_EQ(foo2_, message2_->FindFieldByName("foo"));
928 EXPECT_EQ(bar2_, message2_->FindFieldByName("bar"));
929 EXPECT_EQ(mooo2_, message2_->FindFieldByName("mooo"));
930 EXPECT_TRUE(message2_->FindFieldByName("baz") == nullptr);
931 EXPECT_TRUE(message2_->FindFieldByName("moo") == nullptr);
932 }
933
TEST_F(DescriptorTest,FindFieldByNumber)934 TEST_F(DescriptorTest, FindFieldByNumber) {
935 EXPECT_EQ(foo_, message_->FindFieldByNumber(1));
936 EXPECT_EQ(bar_, message_->FindFieldByNumber(6));
937 EXPECT_EQ(baz_, message_->FindFieldByNumber(500000000));
938 EXPECT_EQ(moo_, message_->FindFieldByNumber(15));
939 EXPECT_TRUE(message_->FindFieldByNumber(837592) == nullptr);
940 EXPECT_TRUE(message_->FindFieldByNumber(2) == nullptr);
941
942 EXPECT_EQ(foo2_, message2_->FindFieldByNumber(1));
943 EXPECT_EQ(bar2_, message2_->FindFieldByNumber(2));
944 EXPECT_EQ(mooo2_, message2_->FindFieldByNumber(6));
945 EXPECT_TRUE(message2_->FindFieldByNumber(15) == nullptr);
946 EXPECT_TRUE(message2_->FindFieldByNumber(500000000) == nullptr);
947 }
948
TEST_F(DescriptorTest,FieldName)949 TEST_F(DescriptorTest, FieldName) {
950 EXPECT_EQ("foo", foo_->name());
951 EXPECT_EQ("bar", bar_->name());
952 EXPECT_EQ("baz", baz_->name());
953 EXPECT_EQ("moo", moo_->name());
954 }
955
TEST_F(DescriptorTest,FieldFullName)956 TEST_F(DescriptorTest, FieldFullName) {
957 EXPECT_EQ("TestMessage.foo", foo_->full_name());
958 EXPECT_EQ("TestMessage.bar", bar_->full_name());
959 EXPECT_EQ("TestMessage.baz", baz_->full_name());
960 EXPECT_EQ("TestMessage.moo", moo_->full_name());
961
962 EXPECT_EQ("corge.grault.TestMessage2.foo", foo2_->full_name());
963 EXPECT_EQ("corge.grault.TestMessage2.bar", bar2_->full_name());
964 EXPECT_EQ("corge.grault.TestMessage2.mooo", mooo2_->full_name());
965 }
966
TEST_F(DescriptorTest,PrintableNameIsFullNameForNonExtensionFields)967 TEST_F(DescriptorTest, PrintableNameIsFullNameForNonExtensionFields) {
968 EXPECT_EQ("TestMessage.foo", foo_->PrintableNameForExtension());
969 EXPECT_EQ("TestMessage.bar", bar_->PrintableNameForExtension());
970 EXPECT_EQ("TestMessage.baz", baz_->PrintableNameForExtension());
971 EXPECT_EQ("TestMessage.moo", moo_->PrintableNameForExtension());
972
973 EXPECT_EQ("corge.grault.TestMessage2.foo",
974 foo2_->PrintableNameForExtension());
975 EXPECT_EQ("corge.grault.TestMessage2.bar",
976 bar2_->PrintableNameForExtension());
977 EXPECT_EQ("corge.grault.TestMessage2.mooo",
978 mooo2_->PrintableNameForExtension());
979 }
980
TEST_F(DescriptorTest,PrintableNameIsFullNameForNonMessageSetExtension)981 TEST_F(DescriptorTest, PrintableNameIsFullNameForNonMessageSetExtension) {
982 EXPECT_EQ("protobuf_unittest.Aggregate.nested",
983 protobuf_unittest::Aggregate::descriptor()
984 ->FindExtensionByName("nested")
985 ->PrintableNameForExtension());
986 }
987
TEST_F(DescriptorTest,PrintableNameIsExtendingTypeForMessageSetExtension)988 TEST_F(DescriptorTest, PrintableNameIsExtendingTypeForMessageSetExtension) {
989 EXPECT_EQ("protobuf_unittest.AggregateMessageSetElement",
990 protobuf_unittest::AggregateMessageSetElement::descriptor()
991 ->FindExtensionByName("message_set_extension")
992 ->PrintableNameForExtension());
993 }
994
TEST_F(DescriptorTest,FieldJsonName)995 TEST_F(DescriptorTest, FieldJsonName) {
996 EXPECT_EQ("fieldName1", message4_->field(0)->json_name());
997 EXPECT_EQ("fieldName2", message4_->field(1)->json_name());
998 EXPECT_EQ("FieldName3", message4_->field(2)->json_name());
999 EXPECT_EQ("FieldName4", message4_->field(3)->json_name());
1000 EXPECT_EQ("FIELDNAME5", message4_->field(4)->json_name());
1001 EXPECT_EQ("@type", message4_->field(5)->json_name());
1002
1003 DescriptorProto proto;
1004 message4_->CopyTo(&proto);
1005 ASSERT_EQ(7, proto.field_size());
1006 EXPECT_FALSE(proto.field(0).has_json_name());
1007 EXPECT_FALSE(proto.field(1).has_json_name());
1008 EXPECT_FALSE(proto.field(2).has_json_name());
1009 EXPECT_FALSE(proto.field(3).has_json_name());
1010 EXPECT_FALSE(proto.field(4).has_json_name());
1011 EXPECT_EQ("@type", proto.field(5).json_name());
1012 EXPECT_FALSE(proto.field(6).has_json_name());
1013
1014 proto.Clear();
1015 CopyWithJsonName(message4_, &proto);
1016 ASSERT_EQ(7, proto.field_size());
1017 EXPECT_EQ("fieldName1", proto.field(0).json_name());
1018 EXPECT_EQ("fieldName2", proto.field(1).json_name());
1019 EXPECT_EQ("FieldName3", proto.field(2).json_name());
1020 EXPECT_EQ("FieldName4", proto.field(3).json_name());
1021 EXPECT_EQ("FIELDNAME5", proto.field(4).json_name());
1022 EXPECT_EQ("@type", proto.field(5).json_name());
1023 EXPECT_EQ("fieldname7", proto.field(6).json_name());
1024
1025 // Test generated descriptor.
1026 const Descriptor* generated = protobuf_unittest::TestJsonName::descriptor();
1027 ASSERT_EQ(7, generated->field_count());
1028 EXPECT_EQ("fieldName1", generated->field(0)->json_name());
1029 EXPECT_EQ("fieldName2", generated->field(1)->json_name());
1030 EXPECT_EQ("FieldName3", generated->field(2)->json_name());
1031 EXPECT_EQ("FieldName4", generated->field(3)->json_name());
1032 EXPECT_EQ("FIELDNAME5", generated->field(4)->json_name());
1033 EXPECT_EQ("@type", generated->field(5)->json_name());
1034 EXPECT_EQ("fieldname7", generated->field(6)->json_name());
1035 }
1036
TEST_F(DescriptorTest,FieldFile)1037 TEST_F(DescriptorTest, FieldFile) {
1038 EXPECT_EQ(foo_file_, foo_->file());
1039 EXPECT_EQ(foo_file_, bar_->file());
1040 EXPECT_EQ(foo_file_, baz_->file());
1041 EXPECT_EQ(foo_file_, moo_->file());
1042
1043 EXPECT_EQ(bar_file_, foo2_->file());
1044 EXPECT_EQ(bar_file_, bar2_->file());
1045 EXPECT_EQ(bar_file_, mooo2_->file());
1046 }
1047
TEST_F(DescriptorTest,FieldIndex)1048 TEST_F(DescriptorTest, FieldIndex) {
1049 EXPECT_EQ(0, foo_->index());
1050 EXPECT_EQ(1, bar_->index());
1051 EXPECT_EQ(2, baz_->index());
1052 EXPECT_EQ(3, moo_->index());
1053 }
1054
TEST_F(DescriptorTest,FieldNumber)1055 TEST_F(DescriptorTest, FieldNumber) {
1056 EXPECT_EQ(1, foo_->number());
1057 EXPECT_EQ(6, bar_->number());
1058 EXPECT_EQ(500000000, baz_->number());
1059 EXPECT_EQ(15, moo_->number());
1060 }
1061
TEST_F(DescriptorTest,FieldType)1062 TEST_F(DescriptorTest, FieldType) {
1063 EXPECT_EQ(FieldDescriptor::TYPE_STRING, foo_->type());
1064 EXPECT_EQ(FieldDescriptor::TYPE_ENUM, bar_->type());
1065 EXPECT_EQ(FieldDescriptor::TYPE_MESSAGE, baz_->type());
1066 EXPECT_EQ(FieldDescriptor::TYPE_GROUP, moo_->type());
1067 }
1068
TEST_F(DescriptorTest,FieldLabel)1069 TEST_F(DescriptorTest, FieldLabel) {
1070 EXPECT_EQ(FieldDescriptor::LABEL_REQUIRED, foo_->label());
1071 EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, bar_->label());
1072 EXPECT_EQ(FieldDescriptor::LABEL_REPEATED, baz_->label());
1073 EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, moo_->label());
1074
1075 EXPECT_TRUE(foo_->is_required());
1076 EXPECT_FALSE(foo_->is_optional());
1077 EXPECT_FALSE(foo_->is_repeated());
1078
1079 EXPECT_FALSE(bar_->is_required());
1080 EXPECT_TRUE(bar_->is_optional());
1081 EXPECT_FALSE(bar_->is_repeated());
1082
1083 EXPECT_FALSE(baz_->is_required());
1084 EXPECT_FALSE(baz_->is_optional());
1085 EXPECT_TRUE(baz_->is_repeated());
1086 }
1087
TEST_F(DescriptorTest,NeedsUtf8Check)1088 TEST_F(DescriptorTest, NeedsUtf8Check) {
1089 EXPECT_FALSE(foo_->requires_utf8_validation());
1090 EXPECT_FALSE(bar_->requires_utf8_validation());
1091
1092 // Build a copy of the file in proto3.
1093 FileDescriptorProto foo_file3;
1094 foo_file_->CopyTo(&foo_file3);
1095 foo_file3.set_syntax("proto3");
1096
1097 // Make this valid proto3 by removing `required` and the one group field.
1098 for (auto& f : *foo_file3.mutable_message_type(1)->mutable_field()) {
1099 f.clear_label();
1100 if (f.type() == FieldDescriptorProto::TYPE_GROUP) {
1101 f.set_type(FieldDescriptorProto::TYPE_MESSAGE);
1102 }
1103 }
1104 // Make this valid proto3 by making the first enum value be zero.
1105 foo_file3.mutable_enum_type(0)->mutable_value(0)->set_number(0);
1106
1107 DescriptorPool pool3;
1108 const Descriptor* message3 = pool3.BuildFile(foo_file3)->message_type(1);
1109 const FieldDescriptor* foo3 = message3->field(0);
1110 const FieldDescriptor* bar3 = message3->field(1);
1111
1112 EXPECT_TRUE(foo3->requires_utf8_validation());
1113 EXPECT_FALSE(bar3->requires_utf8_validation());
1114 }
1115
TEST_F(DescriptorTest,EnumFieldTreatedAsClosed)1116 TEST_F(DescriptorTest, EnumFieldTreatedAsClosed) {
1117 // Make an open enum definition.
1118 FileDescriptorProto open_enum_file;
1119 open_enum_file.set_name("open_enum.proto");
1120 open_enum_file.set_syntax("proto3");
1121 AddEnumValue(AddEnum(&open_enum_file, "TestEnumOpen"), "TestEnumOpen_VALUE0",
1122 0);
1123
1124 const EnumDescriptor* open_enum =
1125 pool_.BuildFile(open_enum_file)->enum_type(0);
1126 EXPECT_FALSE(open_enum->is_closed());
1127
1128 // Create a message that treats enum fields as closed.
1129 FileDescriptorProto closed_file;
1130 closed_file.set_name("closed_enum_field.proto");
1131 closed_file.add_dependency("open_enum.proto");
1132 closed_file.add_dependency("foo.proto");
1133
1134 DescriptorProto* message = AddMessage(&closed_file, "TestClosedEnumField");
1135 AddField(message, "int_field", 1, FieldDescriptorProto::LABEL_OPTIONAL,
1136 FieldDescriptorProto::TYPE_INT32);
1137 AddField(message, "open_enum", 2, FieldDescriptorProto::LABEL_OPTIONAL,
1138 FieldDescriptorProto::TYPE_ENUM)
1139 ->set_type_name("TestEnumOpen");
1140 AddField(message, "closed_enum", 3, FieldDescriptorProto::LABEL_OPTIONAL,
1141 FieldDescriptorProto::TYPE_ENUM)
1142 ->set_type_name("TestEnum");
1143 const Descriptor* closed_message =
1144 pool_.BuildFile(closed_file)->message_type(0);
1145
1146 EXPECT_FALSE(closed_message->FindFieldByName("int_field")
1147 ->legacy_enum_field_treated_as_closed());
1148 EXPECT_TRUE(closed_message->FindFieldByName("closed_enum")
1149 ->legacy_enum_field_treated_as_closed());
1150 EXPECT_TRUE(closed_message->FindFieldByName("open_enum")
1151 ->legacy_enum_field_treated_as_closed());
1152 }
1153
TEST_F(DescriptorTest,EnumFieldTreatedAsOpen)1154 TEST_F(DescriptorTest, EnumFieldTreatedAsOpen) {
1155 FileDescriptorProto open_enum_file;
1156 open_enum_file.set_name("open_enum.proto");
1157 open_enum_file.set_syntax("proto3");
1158 AddEnumValue(AddEnum(&open_enum_file, "TestEnumOpen"), "TestEnumOpen_VALUE0",
1159 0);
1160 DescriptorProto* message = AddMessage(&open_enum_file, "TestOpenEnumField");
1161 AddField(message, "int_field", 1, FieldDescriptorProto::LABEL_OPTIONAL,
1162 FieldDescriptorProto::TYPE_INT32);
1163 AddField(message, "open_enum", 2, FieldDescriptorProto::LABEL_OPTIONAL,
1164 FieldDescriptorProto::TYPE_ENUM)
1165 ->set_type_name("TestEnumOpen");
1166 const FileDescriptor* open_enum_file_desc = pool_.BuildFile(open_enum_file);
1167 const Descriptor* open_message = open_enum_file_desc->message_type(0);
1168 const EnumDescriptor* open_enum = open_enum_file_desc->enum_type(0);
1169 EXPECT_FALSE(open_enum->is_closed());
1170 EXPECT_FALSE(open_message->FindFieldByName("int_field")
1171 ->legacy_enum_field_treated_as_closed());
1172 EXPECT_FALSE(open_message->FindFieldByName("open_enum")
1173 ->legacy_enum_field_treated_as_closed());
1174 }
1175
TEST_F(DescriptorTest,IsMap)1176 TEST_F(DescriptorTest, IsMap) {
1177 EXPECT_TRUE(map_->is_map());
1178 EXPECT_FALSE(baz_->is_map());
1179 EXPECT_TRUE(map_->message_type()->options().map_entry());
1180 }
1181
TEST_F(DescriptorTest,GetMap)1182 TEST_F(DescriptorTest, GetMap) {
1183 const Descriptor* map_desc = map_->message_type();
1184 const FieldDescriptor* map_key = map_desc->map_key();
1185 ASSERT_TRUE(map_key != nullptr);
1186 EXPECT_EQ(map_key->name(), "key");
1187 EXPECT_EQ(map_key->number(), 1);
1188
1189 const FieldDescriptor* map_value = map_desc->map_value();
1190 ASSERT_TRUE(map_value != nullptr);
1191 EXPECT_EQ(map_value->name(), "value");
1192 EXPECT_EQ(map_value->number(), 2);
1193
1194 EXPECT_EQ(message_->map_key(), nullptr);
1195 EXPECT_EQ(message_->map_value(), nullptr);
1196 }
1197
TEST_F(DescriptorTest,FieldHasDefault)1198 TEST_F(DescriptorTest, FieldHasDefault) {
1199 EXPECT_FALSE(foo_->has_default_value());
1200 EXPECT_FALSE(bar_->has_default_value());
1201 EXPECT_FALSE(baz_->has_default_value());
1202 EXPECT_FALSE(moo_->has_default_value());
1203 }
1204
TEST_F(DescriptorTest,FieldContainingType)1205 TEST_F(DescriptorTest, FieldContainingType) {
1206 EXPECT_EQ(message_, foo_->containing_type());
1207 EXPECT_EQ(message_, bar_->containing_type());
1208 EXPECT_EQ(message_, baz_->containing_type());
1209 EXPECT_EQ(message_, moo_->containing_type());
1210
1211 EXPECT_EQ(message2_, foo2_->containing_type());
1212 EXPECT_EQ(message2_, bar2_->containing_type());
1213 EXPECT_EQ(message2_, mooo2_->containing_type());
1214 }
1215
TEST_F(DescriptorTest,FieldMessageType)1216 TEST_F(DescriptorTest, FieldMessageType) {
1217 EXPECT_TRUE(foo_->message_type() == nullptr);
1218 EXPECT_TRUE(bar_->message_type() == nullptr);
1219
1220 EXPECT_EQ(foreign_, baz_->message_type());
1221 EXPECT_EQ(foreign_, moo_->message_type());
1222 }
1223
TEST_F(DescriptorTest,FieldEnumType)1224 TEST_F(DescriptorTest, FieldEnumType) {
1225 EXPECT_TRUE(foo_->enum_type() == nullptr);
1226 EXPECT_TRUE(baz_->enum_type() == nullptr);
1227 EXPECT_TRUE(moo_->enum_type() == nullptr);
1228
1229 EXPECT_EQ(enum_, bar_->enum_type());
1230 }
1231
TEST_F(DescriptorTest,AbslStringifyWorks)1232 TEST_F(DescriptorTest, AbslStringifyWorks) {
1233 EXPECT_THAT(absl::StrFormat("%v", *message_),
1234 HasSubstr(message_->full_name()));
1235 EXPECT_THAT(absl::StrFormat("%v", *foo_), HasSubstr(foo_->name()));
1236 }
1237
1238
1239 // ===================================================================
1240
1241 // Test simple flat messages and fields.
1242 class OneofDescriptorTest : public testing::Test {
1243 protected:
SetUp()1244 void SetUp() override {
1245 // Build descriptors for the following definitions:
1246 //
1247 // package garply;
1248 // message TestOneof {
1249 // optional int32 a = 1;
1250 // oneof foo {
1251 // string b = 2;
1252 // TestOneof c = 3;
1253 // }
1254 // oneof bar {
1255 // float d = 4;
1256 // }
1257 // }
1258
1259 FileDescriptorProto baz_file;
1260 baz_file.set_name("baz.proto");
1261 baz_file.set_package("garply");
1262
1263 DescriptorProto* oneof_message = AddMessage(&baz_file, "TestOneof");
1264 oneof_message->add_oneof_decl()->set_name("foo");
1265 oneof_message->add_oneof_decl()->set_name("bar");
1266
1267 AddField(oneof_message, "a", 1, FieldDescriptorProto::LABEL_OPTIONAL,
1268 FieldDescriptorProto::TYPE_INT32);
1269 AddField(oneof_message, "b", 2, FieldDescriptorProto::LABEL_OPTIONAL,
1270 FieldDescriptorProto::TYPE_STRING);
1271 oneof_message->mutable_field(1)->set_oneof_index(0);
1272 AddField(oneof_message, "c", 3, FieldDescriptorProto::LABEL_OPTIONAL,
1273 FieldDescriptorProto::TYPE_MESSAGE);
1274 oneof_message->mutable_field(2)->set_oneof_index(0);
1275 oneof_message->mutable_field(2)->set_type_name("TestOneof");
1276
1277 AddField(oneof_message, "d", 4, FieldDescriptorProto::LABEL_OPTIONAL,
1278 FieldDescriptorProto::TYPE_FLOAT);
1279 oneof_message->mutable_field(3)->set_oneof_index(1);
1280
1281 // Build the descriptors and get the pointers.
1282 baz_file_ = pool_.BuildFile(baz_file);
1283 ASSERT_TRUE(baz_file_ != nullptr);
1284
1285 ASSERT_EQ(1, baz_file_->message_type_count());
1286 oneof_message_ = baz_file_->message_type(0);
1287
1288 ASSERT_EQ(2, oneof_message_->oneof_decl_count());
1289 oneof_ = oneof_message_->oneof_decl(0);
1290 oneof2_ = oneof_message_->oneof_decl(1);
1291
1292 ASSERT_EQ(4, oneof_message_->field_count());
1293 a_ = oneof_message_->field(0);
1294 b_ = oneof_message_->field(1);
1295 c_ = oneof_message_->field(2);
1296 d_ = oneof_message_->field(3);
1297 }
1298
1299 DescriptorPool pool_;
1300
1301 const FileDescriptor* baz_file_;
1302
1303 const Descriptor* oneof_message_;
1304
1305 const OneofDescriptor* oneof_;
1306 const OneofDescriptor* oneof2_;
1307 const FieldDescriptor* a_;
1308 const FieldDescriptor* b_;
1309 const FieldDescriptor* c_;
1310 const FieldDescriptor* d_;
1311 };
1312
TEST_F(OneofDescriptorTest,Normal)1313 TEST_F(OneofDescriptorTest, Normal) {
1314 EXPECT_EQ("foo", oneof_->name());
1315 EXPECT_EQ("garply.TestOneof.foo", oneof_->full_name());
1316 EXPECT_EQ(0, oneof_->index());
1317 ASSERT_EQ(2, oneof_->field_count());
1318 EXPECT_EQ(b_, oneof_->field(0));
1319 EXPECT_EQ(c_, oneof_->field(1));
1320 EXPECT_TRUE(a_->containing_oneof() == nullptr);
1321 EXPECT_EQ(oneof_, b_->containing_oneof());
1322 EXPECT_EQ(oneof_, c_->containing_oneof());
1323 }
1324
TEST_F(OneofDescriptorTest,FindByName)1325 TEST_F(OneofDescriptorTest, FindByName) {
1326 EXPECT_EQ(oneof_, oneof_message_->FindOneofByName("foo"));
1327 EXPECT_EQ(oneof2_, oneof_message_->FindOneofByName("bar"));
1328 EXPECT_TRUE(oneof_message_->FindOneofByName("no_such_oneof") == nullptr);
1329 }
1330
TEST_F(OneofDescriptorTest,AbslStringifyWorks)1331 TEST_F(OneofDescriptorTest, AbslStringifyWorks) {
1332 EXPECT_THAT(absl::StrFormat("%v", *oneof_), HasSubstr(oneof_->name()));
1333 }
1334
1335 // ===================================================================
1336
1337 class StylizedFieldNamesTest : public testing::Test {
1338 protected:
SetUp()1339 void SetUp() override {
1340 FileDescriptorProto file;
1341 file.set_name("foo.proto");
1342
1343 AddExtensionRange(AddMessage(&file, "ExtendableMessage"), 1, 1000);
1344
1345 DescriptorProto* message = AddMessage(&file, "TestMessage");
1346 PROTOBUF_IGNORE_DEPRECATION_START
1347 message->mutable_options()->set_deprecated_legacy_json_field_conflicts(
1348 true);
1349 PROTOBUF_IGNORE_DEPRECATION_STOP
1350 AddField(message, "foo_foo", 1, FieldDescriptorProto::LABEL_OPTIONAL,
1351 FieldDescriptorProto::TYPE_INT32);
1352 AddField(message, "FooBar", 2, FieldDescriptorProto::LABEL_OPTIONAL,
1353 FieldDescriptorProto::TYPE_INT32);
1354 AddField(message, "fooBaz", 3, FieldDescriptorProto::LABEL_OPTIONAL,
1355 FieldDescriptorProto::TYPE_INT32);
1356 AddField(message, "fooFoo", 4, // Camel-case conflict with foo_foo.
1357 FieldDescriptorProto::LABEL_OPTIONAL,
1358 FieldDescriptorProto::TYPE_INT32);
1359 AddField(message, "foobar", 5, // Lower-case conflict with FooBar.
1360 FieldDescriptorProto::LABEL_OPTIONAL,
1361 FieldDescriptorProto::TYPE_INT32);
1362
1363 AddNestedExtension(message, "ExtendableMessage", "bar_foo", 1,
1364 FieldDescriptorProto::LABEL_OPTIONAL,
1365 FieldDescriptorProto::TYPE_INT32);
1366 AddNestedExtension(message, "ExtendableMessage", "BarBar", 2,
1367 FieldDescriptorProto::LABEL_OPTIONAL,
1368 FieldDescriptorProto::TYPE_INT32);
1369 AddNestedExtension(message, "ExtendableMessage", "BarBaz", 3,
1370 FieldDescriptorProto::LABEL_OPTIONAL,
1371 FieldDescriptorProto::TYPE_INT32);
1372 AddNestedExtension(message, "ExtendableMessage", "barFoo", 4, // Conflict
1373 FieldDescriptorProto::LABEL_OPTIONAL,
1374 FieldDescriptorProto::TYPE_INT32);
1375 AddNestedExtension(message, "ExtendableMessage", "barbar", 5, // Conflict
1376 FieldDescriptorProto::LABEL_OPTIONAL,
1377 FieldDescriptorProto::TYPE_INT32);
1378
1379 AddExtension(&file, "ExtendableMessage", "baz_foo", 11,
1380 FieldDescriptorProto::LABEL_OPTIONAL,
1381 FieldDescriptorProto::TYPE_INT32);
1382 AddExtension(&file, "ExtendableMessage", "BazBar", 12,
1383 FieldDescriptorProto::LABEL_OPTIONAL,
1384 FieldDescriptorProto::TYPE_INT32);
1385 AddExtension(&file, "ExtendableMessage", "BazBaz", 13,
1386 FieldDescriptorProto::LABEL_OPTIONAL,
1387 FieldDescriptorProto::TYPE_INT32);
1388 AddExtension(&file, "ExtendableMessage", "bazFoo", 14, // Conflict
1389 FieldDescriptorProto::LABEL_OPTIONAL,
1390 FieldDescriptorProto::TYPE_INT32);
1391 AddExtension(&file, "ExtendableMessage", "bazbar", 15, // Conflict
1392 FieldDescriptorProto::LABEL_OPTIONAL,
1393 FieldDescriptorProto::TYPE_INT32);
1394
1395 file_ = pool_.BuildFile(file);
1396 ASSERT_TRUE(file_ != nullptr);
1397 ASSERT_EQ(2, file_->message_type_count());
1398 message_ = file_->message_type(1);
1399 ASSERT_EQ("TestMessage", message_->name());
1400 ASSERT_EQ(5, message_->field_count());
1401 ASSERT_EQ(5, message_->extension_count());
1402 ASSERT_EQ(5, file_->extension_count());
1403 }
1404
1405 DescriptorPool pool_;
1406 const FileDescriptor* file_;
1407 const Descriptor* message_;
1408 };
1409
TEST_F(StylizedFieldNamesTest,LowercaseName)1410 TEST_F(StylizedFieldNamesTest, LowercaseName) {
1411 EXPECT_EQ("foo_foo", message_->field(0)->lowercase_name());
1412 EXPECT_EQ("foobar", message_->field(1)->lowercase_name());
1413 EXPECT_EQ("foobaz", message_->field(2)->lowercase_name());
1414 EXPECT_EQ("foofoo", message_->field(3)->lowercase_name());
1415 EXPECT_EQ("foobar", message_->field(4)->lowercase_name());
1416
1417 EXPECT_EQ("bar_foo", message_->extension(0)->lowercase_name());
1418 EXPECT_EQ("barbar", message_->extension(1)->lowercase_name());
1419 EXPECT_EQ("barbaz", message_->extension(2)->lowercase_name());
1420 EXPECT_EQ("barfoo", message_->extension(3)->lowercase_name());
1421 EXPECT_EQ("barbar", message_->extension(4)->lowercase_name());
1422
1423 EXPECT_EQ("baz_foo", file_->extension(0)->lowercase_name());
1424 EXPECT_EQ("bazbar", file_->extension(1)->lowercase_name());
1425 EXPECT_EQ("bazbaz", file_->extension(2)->lowercase_name());
1426 EXPECT_EQ("bazfoo", file_->extension(3)->lowercase_name());
1427 EXPECT_EQ("bazbar", file_->extension(4)->lowercase_name());
1428 }
1429
TEST_F(StylizedFieldNamesTest,CamelcaseName)1430 TEST_F(StylizedFieldNamesTest, CamelcaseName) {
1431 EXPECT_EQ("fooFoo", message_->field(0)->camelcase_name());
1432 EXPECT_EQ("fooBar", message_->field(1)->camelcase_name());
1433 EXPECT_EQ("fooBaz", message_->field(2)->camelcase_name());
1434 EXPECT_EQ("fooFoo", message_->field(3)->camelcase_name());
1435 EXPECT_EQ("foobar", message_->field(4)->camelcase_name());
1436
1437 EXPECT_EQ("barFoo", message_->extension(0)->camelcase_name());
1438 EXPECT_EQ("barBar", message_->extension(1)->camelcase_name());
1439 EXPECT_EQ("barBaz", message_->extension(2)->camelcase_name());
1440 EXPECT_EQ("barFoo", message_->extension(3)->camelcase_name());
1441 EXPECT_EQ("barbar", message_->extension(4)->camelcase_name());
1442
1443 EXPECT_EQ("bazFoo", file_->extension(0)->camelcase_name());
1444 EXPECT_EQ("bazBar", file_->extension(1)->camelcase_name());
1445 EXPECT_EQ("bazBaz", file_->extension(2)->camelcase_name());
1446 EXPECT_EQ("bazFoo", file_->extension(3)->camelcase_name());
1447 EXPECT_EQ("bazbar", file_->extension(4)->camelcase_name());
1448 }
1449
TEST_F(StylizedFieldNamesTest,FindByLowercaseName)1450 TEST_F(StylizedFieldNamesTest, FindByLowercaseName) {
1451 EXPECT_EQ(message_->field(0), message_->FindFieldByLowercaseName("foo_foo"));
1452 EXPECT_THAT(message_->FindFieldByLowercaseName("foobar"),
1453 AnyOf(message_->field(1), message_->field(4)));
1454 EXPECT_EQ(message_->field(2), message_->FindFieldByLowercaseName("foobaz"));
1455 EXPECT_TRUE(message_->FindFieldByLowercaseName("FooBar") == nullptr);
1456 EXPECT_TRUE(message_->FindFieldByLowercaseName("fooBaz") == nullptr);
1457 EXPECT_TRUE(message_->FindFieldByLowercaseName("bar_foo") == nullptr);
1458 EXPECT_TRUE(message_->FindFieldByLowercaseName("nosuchfield") == nullptr);
1459
1460 EXPECT_EQ(message_->extension(0),
1461 message_->FindExtensionByLowercaseName("bar_foo"));
1462 EXPECT_THAT(message_->FindExtensionByLowercaseName("barbar"),
1463 AnyOf(message_->extension(1), message_->extension(4)));
1464 EXPECT_EQ(message_->extension(2),
1465 message_->FindExtensionByLowercaseName("barbaz"));
1466 EXPECT_TRUE(message_->FindExtensionByLowercaseName("BarBar") == nullptr);
1467 EXPECT_TRUE(message_->FindExtensionByLowercaseName("barBaz") == nullptr);
1468 EXPECT_TRUE(message_->FindExtensionByLowercaseName("foo_foo") == nullptr);
1469 EXPECT_TRUE(message_->FindExtensionByLowercaseName("nosuchfield") == nullptr);
1470
1471 EXPECT_EQ(file_->extension(0),
1472 file_->FindExtensionByLowercaseName("baz_foo"));
1473 EXPECT_THAT(file_->FindExtensionByLowercaseName("bazbar"),
1474 AnyOf(file_->extension(1), file_->extension(4)));
1475 EXPECT_EQ(file_->extension(2), file_->FindExtensionByLowercaseName("bazbaz"));
1476 EXPECT_TRUE(file_->FindExtensionByLowercaseName("BazBar") == nullptr);
1477 EXPECT_TRUE(file_->FindExtensionByLowercaseName("bazBaz") == nullptr);
1478 EXPECT_TRUE(file_->FindExtensionByLowercaseName("nosuchfield") == nullptr);
1479 }
1480
TEST_F(StylizedFieldNamesTest,FindByCamelcaseName)1481 TEST_F(StylizedFieldNamesTest, FindByCamelcaseName) {
1482 // Conflict (here, foo_foo and fooFoo) always resolves to the field with
1483 // the lower field number.
1484 EXPECT_EQ(message_->field(0), message_->FindFieldByCamelcaseName("fooFoo"));
1485 EXPECT_EQ(message_->field(1), message_->FindFieldByCamelcaseName("fooBar"));
1486 EXPECT_EQ(message_->field(2), message_->FindFieldByCamelcaseName("fooBaz"));
1487 EXPECT_TRUE(message_->FindFieldByCamelcaseName("foo_foo") == nullptr);
1488 EXPECT_TRUE(message_->FindFieldByCamelcaseName("FooBar") == nullptr);
1489 EXPECT_TRUE(message_->FindFieldByCamelcaseName("barFoo") == nullptr);
1490 EXPECT_TRUE(message_->FindFieldByCamelcaseName("nosuchfield") == nullptr);
1491
1492 // Conflict (here, bar_foo and barFoo) always resolves to the field with
1493 // the lower field number.
1494 EXPECT_EQ(message_->extension(0),
1495 message_->FindExtensionByCamelcaseName("barFoo"));
1496 EXPECT_EQ(message_->extension(1),
1497 message_->FindExtensionByCamelcaseName("barBar"));
1498 EXPECT_EQ(message_->extension(2),
1499 message_->FindExtensionByCamelcaseName("barBaz"));
1500 EXPECT_TRUE(message_->FindExtensionByCamelcaseName("bar_foo") == nullptr);
1501 EXPECT_TRUE(message_->FindExtensionByCamelcaseName("BarBar") == nullptr);
1502 EXPECT_TRUE(message_->FindExtensionByCamelcaseName("fooFoo") == nullptr);
1503 EXPECT_TRUE(message_->FindExtensionByCamelcaseName("nosuchfield") == nullptr);
1504
1505 // Conflict (here, baz_foo and bazFoo) always resolves to the field with
1506 // the lower field number.
1507 EXPECT_EQ(file_->extension(0), file_->FindExtensionByCamelcaseName("bazFoo"));
1508 EXPECT_EQ(file_->extension(1), file_->FindExtensionByCamelcaseName("bazBar"));
1509 EXPECT_EQ(file_->extension(2), file_->FindExtensionByCamelcaseName("bazBaz"));
1510 EXPECT_TRUE(file_->FindExtensionByCamelcaseName("baz_foo") == nullptr);
1511 EXPECT_TRUE(file_->FindExtensionByCamelcaseName("BazBar") == nullptr);
1512 EXPECT_TRUE(file_->FindExtensionByCamelcaseName("nosuchfield") == nullptr);
1513 }
1514
1515 // ===================================================================
1516
1517 // Test enum descriptors.
1518 class EnumDescriptorTest : public testing::Test {
1519 protected:
SetUp()1520 void SetUp() override {
1521 // Build descriptors for the following definitions:
1522 //
1523 // // in "foo.proto"
1524 // enum TestEnum {
1525 // FOO = 1;
1526 // BAR = 2;
1527 // }
1528 //
1529 // // in "bar.proto"
1530 // package corge.grault;
1531 // enum TestEnum2 {
1532 // FOO = 1;
1533 // BAZ = 3;
1534 // }
1535 //
1536 // TestEnum2 is primarily here to test FindValueByName and friends.
1537 // All enums created from the same DescriptorPool share the same lookup
1538 // table, so we need to insure that they don't interfere.
1539
1540 // TestEnum
1541 FileDescriptorProto foo_file;
1542 foo_file.set_name("foo.proto");
1543
1544 EnumDescriptorProto* enum_proto = AddEnum(&foo_file, "TestEnum");
1545 AddEnumValue(enum_proto, "FOO", 1);
1546 AddEnumValue(enum_proto, "BAR", 2);
1547
1548 // TestEnum2
1549 FileDescriptorProto bar_file;
1550 bar_file.set_name("bar.proto");
1551 bar_file.set_package("corge.grault");
1552
1553 EnumDescriptorProto* enum2_proto = AddEnum(&bar_file, "TestEnum2");
1554 AddEnumValue(enum2_proto, "FOO", 1);
1555 AddEnumValue(enum2_proto, "BAZ", 3);
1556
1557 // Build the descriptors and get the pointers.
1558 foo_file_ = pool_.BuildFile(foo_file);
1559 ASSERT_TRUE(foo_file_ != nullptr);
1560
1561 bar_file_ = pool_.BuildFile(bar_file);
1562 ASSERT_TRUE(bar_file_ != nullptr);
1563
1564 ASSERT_EQ(1, foo_file_->enum_type_count());
1565 enum_ = foo_file_->enum_type(0);
1566
1567 ASSERT_EQ(2, enum_->value_count());
1568 foo_ = enum_->value(0);
1569 bar_ = enum_->value(1);
1570
1571 ASSERT_EQ(1, bar_file_->enum_type_count());
1572 enum2_ = bar_file_->enum_type(0);
1573
1574 ASSERT_EQ(2, enum2_->value_count());
1575 foo2_ = enum2_->value(0);
1576 baz2_ = enum2_->value(1);
1577 }
1578
1579 DescriptorPool pool_;
1580
1581 const FileDescriptor* foo_file_;
1582 const FileDescriptor* bar_file_;
1583
1584 const EnumDescriptor* enum_;
1585 const EnumDescriptor* enum2_;
1586
1587 const EnumValueDescriptor* foo_;
1588 const EnumValueDescriptor* bar_;
1589
1590 const EnumValueDescriptor* foo2_;
1591 const EnumValueDescriptor* baz2_;
1592 };
1593
TEST_F(EnumDescriptorTest,Name)1594 TEST_F(EnumDescriptorTest, Name) {
1595 EXPECT_EQ("TestEnum", enum_->name());
1596 EXPECT_EQ("TestEnum", enum_->full_name());
1597 EXPECT_EQ(foo_file_, enum_->file());
1598
1599 EXPECT_EQ("TestEnum2", enum2_->name());
1600 EXPECT_EQ("corge.grault.TestEnum2", enum2_->full_name());
1601 EXPECT_EQ(bar_file_, enum2_->file());
1602 }
1603
TEST_F(EnumDescriptorTest,ContainingType)1604 TEST_F(EnumDescriptorTest, ContainingType) {
1605 EXPECT_TRUE(enum_->containing_type() == nullptr);
1606 EXPECT_TRUE(enum2_->containing_type() == nullptr);
1607 }
1608
TEST_F(EnumDescriptorTest,ValuesByIndex)1609 TEST_F(EnumDescriptorTest, ValuesByIndex) {
1610 ASSERT_EQ(2, enum_->value_count());
1611 EXPECT_EQ(foo_, enum_->value(0));
1612 EXPECT_EQ(bar_, enum_->value(1));
1613 }
1614
TEST_F(EnumDescriptorTest,FindValueByName)1615 TEST_F(EnumDescriptorTest, FindValueByName) {
1616 EXPECT_EQ(foo_, enum_->FindValueByName("FOO"));
1617 EXPECT_EQ(bar_, enum_->FindValueByName("BAR"));
1618 EXPECT_EQ(foo2_, enum2_->FindValueByName("FOO"));
1619 EXPECT_EQ(baz2_, enum2_->FindValueByName("BAZ"));
1620
1621 EXPECT_TRUE(enum_->FindValueByName("NO_SUCH_VALUE") == nullptr);
1622 EXPECT_TRUE(enum_->FindValueByName("BAZ") == nullptr);
1623 EXPECT_TRUE(enum2_->FindValueByName("BAR") == nullptr);
1624 }
1625
TEST_F(EnumDescriptorTest,FindValueByNumber)1626 TEST_F(EnumDescriptorTest, FindValueByNumber) {
1627 EXPECT_EQ(foo_, enum_->FindValueByNumber(1));
1628 EXPECT_EQ(bar_, enum_->FindValueByNumber(2));
1629 EXPECT_EQ(foo2_, enum2_->FindValueByNumber(1));
1630 EXPECT_EQ(baz2_, enum2_->FindValueByNumber(3));
1631
1632 EXPECT_TRUE(enum_->FindValueByNumber(416) == nullptr);
1633 EXPECT_TRUE(enum_->FindValueByNumber(3) == nullptr);
1634 EXPECT_TRUE(enum2_->FindValueByNumber(2) == nullptr);
1635 }
1636
TEST_F(EnumDescriptorTest,ValueName)1637 TEST_F(EnumDescriptorTest, ValueName) {
1638 EXPECT_EQ("FOO", foo_->name());
1639 EXPECT_EQ("BAR", bar_->name());
1640 }
1641
TEST_F(EnumDescriptorTest,ValueFullName)1642 TEST_F(EnumDescriptorTest, ValueFullName) {
1643 EXPECT_EQ("FOO", foo_->full_name());
1644 EXPECT_EQ("BAR", bar_->full_name());
1645 EXPECT_EQ("corge.grault.FOO", foo2_->full_name());
1646 EXPECT_EQ("corge.grault.BAZ", baz2_->full_name());
1647 }
1648
TEST_F(EnumDescriptorTest,ValueIndex)1649 TEST_F(EnumDescriptorTest, ValueIndex) {
1650 EXPECT_EQ(0, foo_->index());
1651 EXPECT_EQ(1, bar_->index());
1652 }
1653
TEST_F(EnumDescriptorTest,ValueNumber)1654 TEST_F(EnumDescriptorTest, ValueNumber) {
1655 EXPECT_EQ(1, foo_->number());
1656 EXPECT_EQ(2, bar_->number());
1657 }
1658
TEST_F(EnumDescriptorTest,ValueType)1659 TEST_F(EnumDescriptorTest, ValueType) {
1660 EXPECT_EQ(enum_, foo_->type());
1661 EXPECT_EQ(enum_, bar_->type());
1662 EXPECT_EQ(enum2_, foo2_->type());
1663 EXPECT_EQ(enum2_, baz2_->type());
1664 }
1665
TEST_F(EnumDescriptorTest,IsClosed)1666 TEST_F(EnumDescriptorTest, IsClosed) {
1667 // enum_ is proto2.
1668 EXPECT_TRUE(enum_->is_closed());
1669
1670 // Make a proto3 version of enum_.
1671 FileDescriptorProto foo_file3;
1672 foo_file_->CopyTo(&foo_file3);
1673 foo_file3.set_syntax("proto3");
1674
1675 // Make this valid proto3 by making the first enum value be zero.
1676 foo_file3.mutable_enum_type(0)->mutable_value(0)->set_number(0);
1677
1678 DescriptorPool pool3;
1679 const EnumDescriptor* enum3 = pool3.BuildFile(foo_file3)->enum_type(0);
1680 EXPECT_FALSE(enum3->is_closed());
1681 }
1682
TEST_F(EnumDescriptorTest,AbslStringifyWorks)1683 TEST_F(EnumDescriptorTest, AbslStringifyWorks) {
1684 EXPECT_THAT(absl::StrFormat("%v", *enum_), HasSubstr(enum_->full_name()));
1685 EXPECT_THAT(absl::StrFormat("%v", *foo_), HasSubstr(foo_->name()));
1686 }
1687
1688 // ===================================================================
1689
1690 // Test service descriptors.
1691 class ServiceDescriptorTest : public testing::Test {
1692 protected:
SetUp()1693 void SetUp() override {
1694 // Build descriptors for the following messages and service:
1695 // // in "foo.proto"
1696 // message FooRequest {}
1697 // message FooResponse {}
1698 // message BarRequest {}
1699 // message BarResponse {}
1700 // message BazRequest {}
1701 // message BazResponse {}
1702 //
1703 // service TestService {
1704 // rpc Foo(FooRequest) returns (FooResponse);
1705 // rpc Bar(BarRequest) returns (BarResponse);
1706 // }
1707 //
1708 // // in "bar.proto"
1709 // package corge.grault
1710 // service TestService2 {
1711 // rpc Foo(FooRequest) returns (FooResponse);
1712 // rpc Baz(BazRequest) returns (BazResponse);
1713 // }
1714
1715 FileDescriptorProto foo_file;
1716 foo_file.set_name("foo.proto");
1717
1718 AddMessage(&foo_file, "FooRequest");
1719 AddMessage(&foo_file, "FooResponse");
1720 AddMessage(&foo_file, "BarRequest");
1721 AddMessage(&foo_file, "BarResponse");
1722 AddMessage(&foo_file, "BazRequest");
1723 AddMessage(&foo_file, "BazResponse");
1724
1725 ServiceDescriptorProto* service = AddService(&foo_file, "TestService");
1726 AddMethod(service, "Foo", "FooRequest", "FooResponse");
1727 AddMethod(service, "Bar", "BarRequest", "BarResponse");
1728
1729 FileDescriptorProto bar_file;
1730 bar_file.set_name("bar.proto");
1731 bar_file.set_package("corge.grault");
1732 bar_file.add_dependency("foo.proto");
1733
1734 ServiceDescriptorProto* service2 = AddService(&bar_file, "TestService2");
1735 AddMethod(service2, "Foo", "FooRequest", "FooResponse");
1736 AddMethod(service2, "Baz", "BazRequest", "BazResponse");
1737
1738 // Build the descriptors and get the pointers.
1739 foo_file_ = pool_.BuildFile(foo_file);
1740 ASSERT_TRUE(foo_file_ != nullptr);
1741
1742 bar_file_ = pool_.BuildFile(bar_file);
1743 ASSERT_TRUE(bar_file_ != nullptr);
1744
1745 ASSERT_EQ(6, foo_file_->message_type_count());
1746 foo_request_ = foo_file_->message_type(0);
1747 foo_response_ = foo_file_->message_type(1);
1748 bar_request_ = foo_file_->message_type(2);
1749 bar_response_ = foo_file_->message_type(3);
1750 baz_request_ = foo_file_->message_type(4);
1751 baz_response_ = foo_file_->message_type(5);
1752
1753 ASSERT_EQ(1, foo_file_->service_count());
1754 service_ = foo_file_->service(0);
1755
1756 ASSERT_EQ(2, service_->method_count());
1757 foo_ = service_->method(0);
1758 bar_ = service_->method(1);
1759
1760 ASSERT_EQ(1, bar_file_->service_count());
1761 service2_ = bar_file_->service(0);
1762
1763 ASSERT_EQ(2, service2_->method_count());
1764 foo2_ = service2_->method(0);
1765 baz2_ = service2_->method(1);
1766 }
1767
1768 DescriptorPool pool_;
1769
1770 const FileDescriptor* foo_file_;
1771 const FileDescriptor* bar_file_;
1772
1773 const Descriptor* foo_request_;
1774 const Descriptor* foo_response_;
1775 const Descriptor* bar_request_;
1776 const Descriptor* bar_response_;
1777 const Descriptor* baz_request_;
1778 const Descriptor* baz_response_;
1779
1780 const ServiceDescriptor* service_;
1781 const ServiceDescriptor* service2_;
1782
1783 const MethodDescriptor* foo_;
1784 const MethodDescriptor* bar_;
1785
1786 const MethodDescriptor* foo2_;
1787 const MethodDescriptor* baz2_;
1788 };
1789
TEST_F(ServiceDescriptorTest,Name)1790 TEST_F(ServiceDescriptorTest, Name) {
1791 EXPECT_EQ("TestService", service_->name());
1792 EXPECT_EQ("TestService", service_->full_name());
1793 EXPECT_EQ(foo_file_, service_->file());
1794
1795 EXPECT_EQ("TestService2", service2_->name());
1796 EXPECT_EQ("corge.grault.TestService2", service2_->full_name());
1797 EXPECT_EQ(bar_file_, service2_->file());
1798 }
1799
TEST_F(ServiceDescriptorTest,MethodsByIndex)1800 TEST_F(ServiceDescriptorTest, MethodsByIndex) {
1801 ASSERT_EQ(2, service_->method_count());
1802 EXPECT_EQ(foo_, service_->method(0));
1803 EXPECT_EQ(bar_, service_->method(1));
1804 }
1805
TEST_F(ServiceDescriptorTest,FindMethodByName)1806 TEST_F(ServiceDescriptorTest, FindMethodByName) {
1807 EXPECT_EQ(foo_, service_->FindMethodByName("Foo"));
1808 EXPECT_EQ(bar_, service_->FindMethodByName("Bar"));
1809 EXPECT_EQ(foo2_, service2_->FindMethodByName("Foo"));
1810 EXPECT_EQ(baz2_, service2_->FindMethodByName("Baz"));
1811
1812 EXPECT_TRUE(service_->FindMethodByName("NoSuchMethod") == nullptr);
1813 EXPECT_TRUE(service_->FindMethodByName("Baz") == nullptr);
1814 EXPECT_TRUE(service2_->FindMethodByName("Bar") == nullptr);
1815 }
1816
TEST_F(ServiceDescriptorTest,MethodName)1817 TEST_F(ServiceDescriptorTest, MethodName) {
1818 EXPECT_EQ("Foo", foo_->name());
1819 EXPECT_EQ("Bar", bar_->name());
1820 }
TEST_F(ServiceDescriptorTest,MethodFullName)1821 TEST_F(ServiceDescriptorTest, MethodFullName) {
1822 EXPECT_EQ("TestService.Foo", foo_->full_name());
1823 EXPECT_EQ("TestService.Bar", bar_->full_name());
1824 EXPECT_EQ("corge.grault.TestService2.Foo", foo2_->full_name());
1825 EXPECT_EQ("corge.grault.TestService2.Baz", baz2_->full_name());
1826 }
1827
TEST_F(ServiceDescriptorTest,MethodIndex)1828 TEST_F(ServiceDescriptorTest, MethodIndex) {
1829 EXPECT_EQ(0, foo_->index());
1830 EXPECT_EQ(1, bar_->index());
1831 }
1832
TEST_F(ServiceDescriptorTest,MethodParent)1833 TEST_F(ServiceDescriptorTest, MethodParent) {
1834 EXPECT_EQ(service_, foo_->service());
1835 EXPECT_EQ(service_, bar_->service());
1836 }
1837
TEST_F(ServiceDescriptorTest,MethodInputType)1838 TEST_F(ServiceDescriptorTest, MethodInputType) {
1839 EXPECT_EQ(foo_request_, foo_->input_type());
1840 EXPECT_EQ(bar_request_, bar_->input_type());
1841 }
1842
TEST_F(ServiceDescriptorTest,MethodOutputType)1843 TEST_F(ServiceDescriptorTest, MethodOutputType) {
1844 EXPECT_EQ(foo_response_, foo_->output_type());
1845 EXPECT_EQ(bar_response_, bar_->output_type());
1846 }
1847
TEST_F(ServiceDescriptorTest,AbslStringifyWorks)1848 TEST_F(ServiceDescriptorTest, AbslStringifyWorks) {
1849 EXPECT_THAT(absl::StrFormat("%v", *service_), HasSubstr(service_->name()));
1850 EXPECT_THAT(absl::StrFormat("%v", *foo_), HasSubstr(foo_->name()));
1851 }
1852
1853 // ===================================================================
1854
1855 // Test nested types.
1856 class NestedDescriptorTest : public testing::Test {
1857 protected:
SetUp()1858 void SetUp() override {
1859 // Build descriptors for the following definitions:
1860 //
1861 // // in "foo.proto"
1862 // message TestMessage {
1863 // message Foo {}
1864 // message Bar {}
1865 // enum Baz { A = 1; }
1866 // enum Moo { B = 1; }
1867 // }
1868 //
1869 // // in "bar.proto"
1870 // package corge.grault;
1871 // message TestMessage2 {
1872 // message Foo {}
1873 // message Baz {}
1874 // enum Moo { A = 1; }
1875 // enum Mooo { C = 1; }
1876 // }
1877 //
1878 // TestMessage2 is primarily here to test FindNestedTypeByName and friends.
1879 // All messages created from the same DescriptorPool share the same lookup
1880 // table, so we need to insure that they don't interfere.
1881 //
1882 // We add enum values to the enums in order to test searching for enum
1883 // values across a message's scope.
1884
1885 FileDescriptorProto foo_file;
1886 foo_file.set_name("foo.proto");
1887
1888 DescriptorProto* message = AddMessage(&foo_file, "TestMessage");
1889 AddNestedMessage(message, "Foo");
1890 AddNestedMessage(message, "Bar");
1891 EnumDescriptorProto* baz = AddNestedEnum(message, "Baz");
1892 AddEnumValue(baz, "A", 1);
1893 EnumDescriptorProto* moo = AddNestedEnum(message, "Moo");
1894 AddEnumValue(moo, "B", 1);
1895
1896 FileDescriptorProto bar_file;
1897 bar_file.set_name("bar.proto");
1898 bar_file.set_package("corge.grault");
1899
1900 DescriptorProto* message2 = AddMessage(&bar_file, "TestMessage2");
1901 AddNestedMessage(message2, "Foo");
1902 AddNestedMessage(message2, "Baz");
1903 EnumDescriptorProto* moo2 = AddNestedEnum(message2, "Moo");
1904 AddEnumValue(moo2, "A", 1);
1905 EnumDescriptorProto* mooo2 = AddNestedEnum(message2, "Mooo");
1906 AddEnumValue(mooo2, "C", 1);
1907
1908 // Build the descriptors and get the pointers.
1909 foo_file_ = pool_.BuildFile(foo_file);
1910 ASSERT_TRUE(foo_file_ != nullptr);
1911
1912 bar_file_ = pool_.BuildFile(bar_file);
1913 ASSERT_TRUE(bar_file_ != nullptr);
1914
1915 ASSERT_EQ(1, foo_file_->message_type_count());
1916 message_ = foo_file_->message_type(0);
1917
1918 ASSERT_EQ(2, message_->nested_type_count());
1919 foo_ = message_->nested_type(0);
1920 bar_ = message_->nested_type(1);
1921
1922 ASSERT_EQ(2, message_->enum_type_count());
1923 baz_ = message_->enum_type(0);
1924 moo_ = message_->enum_type(1);
1925
1926 ASSERT_EQ(1, baz_->value_count());
1927 a_ = baz_->value(0);
1928 ASSERT_EQ(1, moo_->value_count());
1929 b_ = moo_->value(0);
1930
1931 ASSERT_EQ(1, bar_file_->message_type_count());
1932 message2_ = bar_file_->message_type(0);
1933
1934 ASSERT_EQ(2, message2_->nested_type_count());
1935 foo2_ = message2_->nested_type(0);
1936 baz2_ = message2_->nested_type(1);
1937
1938 ASSERT_EQ(2, message2_->enum_type_count());
1939 moo2_ = message2_->enum_type(0);
1940 mooo2_ = message2_->enum_type(1);
1941
1942 ASSERT_EQ(1, moo2_->value_count());
1943 a2_ = moo2_->value(0);
1944 ASSERT_EQ(1, mooo2_->value_count());
1945 c2_ = mooo2_->value(0);
1946 }
1947
1948 DescriptorPool pool_;
1949
1950 const FileDescriptor* foo_file_;
1951 const FileDescriptor* bar_file_;
1952
1953 const Descriptor* message_;
1954 const Descriptor* message2_;
1955
1956 const Descriptor* foo_;
1957 const Descriptor* bar_;
1958 const EnumDescriptor* baz_;
1959 const EnumDescriptor* moo_;
1960 const EnumValueDescriptor* a_;
1961 const EnumValueDescriptor* b_;
1962
1963 const Descriptor* foo2_;
1964 const Descriptor* baz2_;
1965 const EnumDescriptor* moo2_;
1966 const EnumDescriptor* mooo2_;
1967 const EnumValueDescriptor* a2_;
1968 const EnumValueDescriptor* c2_;
1969 };
1970
TEST_F(NestedDescriptorTest,MessageName)1971 TEST_F(NestedDescriptorTest, MessageName) {
1972 EXPECT_EQ("Foo", foo_->name());
1973 EXPECT_EQ("Bar", bar_->name());
1974 EXPECT_EQ("Foo", foo2_->name());
1975 EXPECT_EQ("Baz", baz2_->name());
1976
1977 EXPECT_EQ("TestMessage.Foo", foo_->full_name());
1978 EXPECT_EQ("TestMessage.Bar", bar_->full_name());
1979 EXPECT_EQ("corge.grault.TestMessage2.Foo", foo2_->full_name());
1980 EXPECT_EQ("corge.grault.TestMessage2.Baz", baz2_->full_name());
1981 }
1982
TEST_F(NestedDescriptorTest,MessageContainingType)1983 TEST_F(NestedDescriptorTest, MessageContainingType) {
1984 EXPECT_EQ(message_, foo_->containing_type());
1985 EXPECT_EQ(message_, bar_->containing_type());
1986 EXPECT_EQ(message2_, foo2_->containing_type());
1987 EXPECT_EQ(message2_, baz2_->containing_type());
1988 }
1989
TEST_F(NestedDescriptorTest,NestedMessagesByIndex)1990 TEST_F(NestedDescriptorTest, NestedMessagesByIndex) {
1991 ASSERT_EQ(2, message_->nested_type_count());
1992 EXPECT_EQ(foo_, message_->nested_type(0));
1993 EXPECT_EQ(bar_, message_->nested_type(1));
1994 }
1995
TEST_F(NestedDescriptorTest,FindFieldByNameDoesntFindNestedTypes)1996 TEST_F(NestedDescriptorTest, FindFieldByNameDoesntFindNestedTypes) {
1997 EXPECT_TRUE(message_->FindFieldByName("Foo") == nullptr);
1998 EXPECT_TRUE(message_->FindFieldByName("Moo") == nullptr);
1999 EXPECT_TRUE(message_->FindExtensionByName("Foo") == nullptr);
2000 EXPECT_TRUE(message_->FindExtensionByName("Moo") == nullptr);
2001 }
2002
TEST_F(NestedDescriptorTest,FindNestedTypeByName)2003 TEST_F(NestedDescriptorTest, FindNestedTypeByName) {
2004 EXPECT_EQ(foo_, message_->FindNestedTypeByName("Foo"));
2005 EXPECT_EQ(bar_, message_->FindNestedTypeByName("Bar"));
2006 EXPECT_EQ(foo2_, message2_->FindNestedTypeByName("Foo"));
2007 EXPECT_EQ(baz2_, message2_->FindNestedTypeByName("Baz"));
2008
2009 EXPECT_TRUE(message_->FindNestedTypeByName("NoSuchType") == nullptr);
2010 EXPECT_TRUE(message_->FindNestedTypeByName("Baz") == nullptr);
2011 EXPECT_TRUE(message2_->FindNestedTypeByName("Bar") == nullptr);
2012
2013 EXPECT_TRUE(message_->FindNestedTypeByName("Moo") == nullptr);
2014 }
2015
TEST_F(NestedDescriptorTest,EnumName)2016 TEST_F(NestedDescriptorTest, EnumName) {
2017 EXPECT_EQ("Baz", baz_->name());
2018 EXPECT_EQ("Moo", moo_->name());
2019 EXPECT_EQ("Moo", moo2_->name());
2020 EXPECT_EQ("Mooo", mooo2_->name());
2021
2022 EXPECT_EQ("TestMessage.Baz", baz_->full_name());
2023 EXPECT_EQ("TestMessage.Moo", moo_->full_name());
2024 EXPECT_EQ("corge.grault.TestMessage2.Moo", moo2_->full_name());
2025 EXPECT_EQ("corge.grault.TestMessage2.Mooo", mooo2_->full_name());
2026 }
2027
TEST_F(NestedDescriptorTest,EnumContainingType)2028 TEST_F(NestedDescriptorTest, EnumContainingType) {
2029 EXPECT_EQ(message_, baz_->containing_type());
2030 EXPECT_EQ(message_, moo_->containing_type());
2031 EXPECT_EQ(message2_, moo2_->containing_type());
2032 EXPECT_EQ(message2_, mooo2_->containing_type());
2033 }
2034
TEST_F(NestedDescriptorTest,NestedEnumsByIndex)2035 TEST_F(NestedDescriptorTest, NestedEnumsByIndex) {
2036 ASSERT_EQ(2, message_->nested_type_count());
2037 EXPECT_EQ(foo_, message_->nested_type(0));
2038 EXPECT_EQ(bar_, message_->nested_type(1));
2039 }
2040
TEST_F(NestedDescriptorTest,FindEnumTypeByName)2041 TEST_F(NestedDescriptorTest, FindEnumTypeByName) {
2042 EXPECT_EQ(baz_, message_->FindEnumTypeByName("Baz"));
2043 EXPECT_EQ(moo_, message_->FindEnumTypeByName("Moo"));
2044 EXPECT_EQ(moo2_, message2_->FindEnumTypeByName("Moo"));
2045 EXPECT_EQ(mooo2_, message2_->FindEnumTypeByName("Mooo"));
2046
2047 EXPECT_TRUE(message_->FindEnumTypeByName("NoSuchType") == nullptr);
2048 EXPECT_TRUE(message_->FindEnumTypeByName("Mooo") == nullptr);
2049 EXPECT_TRUE(message2_->FindEnumTypeByName("Baz") == nullptr);
2050
2051 EXPECT_TRUE(message_->FindEnumTypeByName("Foo") == nullptr);
2052 }
2053
TEST_F(NestedDescriptorTest,FindEnumValueByName)2054 TEST_F(NestedDescriptorTest, FindEnumValueByName) {
2055 EXPECT_EQ(a_, message_->FindEnumValueByName("A"));
2056 EXPECT_EQ(b_, message_->FindEnumValueByName("B"));
2057 EXPECT_EQ(a2_, message2_->FindEnumValueByName("A"));
2058 EXPECT_EQ(c2_, message2_->FindEnumValueByName("C"));
2059
2060 EXPECT_TRUE(message_->FindEnumValueByName("NO_SUCH_VALUE") == nullptr);
2061 EXPECT_TRUE(message_->FindEnumValueByName("C") == nullptr);
2062 EXPECT_TRUE(message2_->FindEnumValueByName("B") == nullptr);
2063
2064 EXPECT_TRUE(message_->FindEnumValueByName("Foo") == nullptr);
2065 }
2066
2067 // ===================================================================
2068
2069 // Test extensions.
2070 class ExtensionDescriptorTest : public testing::Test {
2071 protected:
SetUp()2072 void SetUp() override {
2073 // Build descriptors for the following definitions:
2074 //
2075 // enum Baz {}
2076 // message Moo {}
2077 //
2078 // message Foo {
2079 // extensions 10 to 19;
2080 // extensions 30 to 39;
2081 // }
2082 // extend Foo {
2083 // optional int32 foo_int32 = 10;
2084 // }
2085 // extend Foo {
2086 // repeated TestEnum foo_enum = 19;
2087 // }
2088 // message Bar {
2089 // optional int32 non_ext_int32 = 1;
2090 // extend Foo {
2091 // optional Moo foo_message = 30;
2092 // repeated Moo foo_group = 39; // (but internally set to TYPE_GROUP)
2093 // }
2094 // }
2095
2096 FileDescriptorProto foo_file;
2097 foo_file.set_name("foo.proto");
2098
2099 AddEmptyEnum(&foo_file, "Baz");
2100 AddMessage(&foo_file, "Moo");
2101
2102 DescriptorProto* foo = AddMessage(&foo_file, "Foo");
2103 AddExtensionRange(foo, 10, 20);
2104 AddExtensionRange(foo, 30, 40);
2105
2106 AddExtension(&foo_file, "Foo", "foo_int32", 10,
2107 FieldDescriptorProto::LABEL_OPTIONAL,
2108 FieldDescriptorProto::TYPE_INT32);
2109 AddExtension(&foo_file, "Foo", "foo_enum", 19,
2110 FieldDescriptorProto::LABEL_REPEATED,
2111 FieldDescriptorProto::TYPE_ENUM)
2112 ->set_type_name("Baz");
2113
2114 DescriptorProto* bar = AddMessage(&foo_file, "Bar");
2115 AddField(bar, "non_ext_int32", 1, FieldDescriptorProto::LABEL_OPTIONAL,
2116 FieldDescriptorProto::TYPE_INT32);
2117 AddNestedExtension(bar, "Foo", "foo_message", 30,
2118 FieldDescriptorProto::LABEL_OPTIONAL,
2119 FieldDescriptorProto::TYPE_MESSAGE)
2120 ->set_type_name("Moo");
2121 AddNestedExtension(bar, "Foo", "foo_group", 39,
2122 FieldDescriptorProto::LABEL_REPEATED,
2123 FieldDescriptorProto::TYPE_GROUP)
2124 ->set_type_name("Moo");
2125
2126 // Build the descriptors and get the pointers.
2127 foo_file_ = pool_.BuildFile(foo_file);
2128 ASSERT_TRUE(foo_file_ != nullptr);
2129
2130 ASSERT_EQ(1, foo_file_->enum_type_count());
2131 baz_ = foo_file_->enum_type(0);
2132
2133 ASSERT_EQ(3, foo_file_->message_type_count());
2134 moo_ = foo_file_->message_type(0);
2135 foo_ = foo_file_->message_type(1);
2136 bar_ = foo_file_->message_type(2);
2137 }
2138
2139 DescriptorPool pool_;
2140
2141 const FileDescriptor* foo_file_;
2142
2143 const Descriptor* foo_;
2144 const Descriptor* bar_;
2145 const EnumDescriptor* baz_;
2146 const Descriptor* moo_;
2147 };
2148
TEST_F(ExtensionDescriptorTest,ExtensionRanges)2149 TEST_F(ExtensionDescriptorTest, ExtensionRanges) {
2150 EXPECT_EQ(0, bar_->extension_range_count());
2151 ASSERT_EQ(2, foo_->extension_range_count());
2152
2153 EXPECT_EQ(10, foo_->extension_range(0)->start_number());
2154 EXPECT_EQ(30, foo_->extension_range(1)->start_number());
2155
2156 EXPECT_EQ(20, foo_->extension_range(0)->end_number());
2157 EXPECT_EQ(40, foo_->extension_range(1)->end_number());
2158 }
2159
TEST_F(ExtensionDescriptorTest,Extensions)2160 TEST_F(ExtensionDescriptorTest, Extensions) {
2161 EXPECT_EQ(0, foo_->extension_count());
2162 ASSERT_EQ(2, foo_file_->extension_count());
2163 ASSERT_EQ(2, bar_->extension_count());
2164
2165 EXPECT_TRUE(foo_file_->extension(0)->is_extension());
2166 EXPECT_TRUE(foo_file_->extension(1)->is_extension());
2167 EXPECT_TRUE(bar_->extension(0)->is_extension());
2168 EXPECT_TRUE(bar_->extension(1)->is_extension());
2169
2170 EXPECT_EQ("foo_int32", foo_file_->extension(0)->name());
2171 EXPECT_EQ("foo_enum", foo_file_->extension(1)->name());
2172 EXPECT_EQ("foo_message", bar_->extension(0)->name());
2173 EXPECT_EQ("foo_group", bar_->extension(1)->name());
2174
2175 EXPECT_EQ(10, foo_file_->extension(0)->number());
2176 EXPECT_EQ(19, foo_file_->extension(1)->number());
2177 EXPECT_EQ(30, bar_->extension(0)->number());
2178 EXPECT_EQ(39, bar_->extension(1)->number());
2179
2180 EXPECT_EQ(FieldDescriptor::TYPE_INT32, foo_file_->extension(0)->type());
2181 EXPECT_EQ(FieldDescriptor::TYPE_ENUM, foo_file_->extension(1)->type());
2182 EXPECT_EQ(FieldDescriptor::TYPE_MESSAGE, bar_->extension(0)->type());
2183 EXPECT_EQ(FieldDescriptor::TYPE_GROUP, bar_->extension(1)->type());
2184
2185 EXPECT_EQ(baz_, foo_file_->extension(1)->enum_type());
2186 EXPECT_EQ(moo_, bar_->extension(0)->message_type());
2187 EXPECT_EQ(moo_, bar_->extension(1)->message_type());
2188
2189 EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, foo_file_->extension(0)->label());
2190 EXPECT_EQ(FieldDescriptor::LABEL_REPEATED, foo_file_->extension(1)->label());
2191 EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, bar_->extension(0)->label());
2192 EXPECT_EQ(FieldDescriptor::LABEL_REPEATED, bar_->extension(1)->label());
2193
2194 EXPECT_EQ(foo_, foo_file_->extension(0)->containing_type());
2195 EXPECT_EQ(foo_, foo_file_->extension(1)->containing_type());
2196 EXPECT_EQ(foo_, bar_->extension(0)->containing_type());
2197 EXPECT_EQ(foo_, bar_->extension(1)->containing_type());
2198
2199 EXPECT_TRUE(foo_file_->extension(0)->extension_scope() == nullptr);
2200 EXPECT_TRUE(foo_file_->extension(1)->extension_scope() == nullptr);
2201 EXPECT_EQ(bar_, bar_->extension(0)->extension_scope());
2202 EXPECT_EQ(bar_, bar_->extension(1)->extension_scope());
2203 }
2204
TEST_F(ExtensionDescriptorTest,IsExtensionNumber)2205 TEST_F(ExtensionDescriptorTest, IsExtensionNumber) {
2206 EXPECT_FALSE(foo_->IsExtensionNumber(9));
2207 EXPECT_TRUE(foo_->IsExtensionNumber(10));
2208 EXPECT_TRUE(foo_->IsExtensionNumber(19));
2209 EXPECT_FALSE(foo_->IsExtensionNumber(20));
2210 EXPECT_FALSE(foo_->IsExtensionNumber(29));
2211 EXPECT_TRUE(foo_->IsExtensionNumber(30));
2212 EXPECT_TRUE(foo_->IsExtensionNumber(39));
2213 EXPECT_FALSE(foo_->IsExtensionNumber(40));
2214 }
2215
TEST_F(ExtensionDescriptorTest,FindExtensionByName)2216 TEST_F(ExtensionDescriptorTest, FindExtensionByName) {
2217 // Note that FileDescriptor::FindExtensionByName() is tested by
2218 // FileDescriptorTest.
2219 ASSERT_EQ(2, bar_->extension_count());
2220
2221 EXPECT_EQ(bar_->extension(0), bar_->FindExtensionByName("foo_message"));
2222 EXPECT_EQ(bar_->extension(1), bar_->FindExtensionByName("foo_group"));
2223
2224 EXPECT_TRUE(bar_->FindExtensionByName("no_such_extension") == nullptr);
2225 EXPECT_TRUE(foo_->FindExtensionByName("foo_int32") == nullptr);
2226 EXPECT_TRUE(foo_->FindExtensionByName("foo_message") == nullptr);
2227 }
2228
TEST_F(ExtensionDescriptorTest,FieldVsExtension)2229 TEST_F(ExtensionDescriptorTest, FieldVsExtension) {
2230 EXPECT_EQ(foo_->FindFieldByName("foo_message"), nullptr);
2231 EXPECT_EQ(bar_->FindFieldByName("foo_message"), nullptr);
2232 EXPECT_NE(bar_->FindFieldByName("non_ext_int32"), nullptr);
2233 EXPECT_EQ(foo_->FindExtensionByName("foo_message"), nullptr);
2234 EXPECT_NE(bar_->FindExtensionByName("foo_message"), nullptr);
2235 EXPECT_EQ(bar_->FindExtensionByName("non_ext_int32"), nullptr);
2236 }
2237
TEST_F(ExtensionDescriptorTest,FindExtensionByPrintableName)2238 TEST_F(ExtensionDescriptorTest, FindExtensionByPrintableName) {
2239 EXPECT_TRUE(pool_.FindExtensionByPrintableName(foo_, "no_such_extension") ==
2240 nullptr);
2241 EXPECT_TRUE(pool_.FindExtensionByPrintableName(bar_, "no_such_extension") ==
2242 nullptr);
2243
2244 ASSERT_FALSE(pool_.FindExtensionByPrintableName(foo_, "Bar.foo_message") ==
2245 nullptr);
2246 ASSERT_FALSE(pool_.FindExtensionByPrintableName(foo_, "Bar.foo_group") ==
2247 nullptr);
2248 EXPECT_TRUE(pool_.FindExtensionByPrintableName(bar_, "foo_message") ==
2249 nullptr);
2250 EXPECT_TRUE(pool_.FindExtensionByPrintableName(bar_, "foo_group") == nullptr);
2251 EXPECT_EQ(bar_->FindExtensionByName("foo_message"),
2252 pool_.FindExtensionByPrintableName(foo_, "Bar.foo_message"));
2253 EXPECT_EQ(bar_->FindExtensionByName("foo_group"),
2254 pool_.FindExtensionByPrintableName(foo_, "Bar.foo_group"));
2255
2256 ASSERT_FALSE(pool_.FindExtensionByPrintableName(foo_, "foo_int32") ==
2257 nullptr);
2258 ASSERT_FALSE(pool_.FindExtensionByPrintableName(foo_, "foo_enum") == nullptr);
2259 EXPECT_TRUE(pool_.FindExtensionByPrintableName(bar_, "foo_int32") == nullptr);
2260 EXPECT_TRUE(pool_.FindExtensionByPrintableName(bar_, "foo_enum") == nullptr);
2261 EXPECT_EQ(foo_file_->FindExtensionByName("foo_int32"),
2262 pool_.FindExtensionByPrintableName(foo_, "foo_int32"));
2263 EXPECT_EQ(foo_file_->FindExtensionByName("foo_enum"),
2264 pool_.FindExtensionByPrintableName(foo_, "foo_enum"));
2265 }
2266
TEST_F(ExtensionDescriptorTest,FindAllExtensions)2267 TEST_F(ExtensionDescriptorTest, FindAllExtensions) {
2268 std::vector<const FieldDescriptor*> extensions;
2269 pool_.FindAllExtensions(foo_, &extensions);
2270 ASSERT_EQ(4, extensions.size());
2271 EXPECT_EQ(10, extensions[0]->number());
2272 EXPECT_EQ(19, extensions[1]->number());
2273 EXPECT_EQ(30, extensions[2]->number());
2274 EXPECT_EQ(39, extensions[3]->number());
2275 }
2276
2277
TEST_F(ExtensionDescriptorTest,DuplicateFieldNumber)2278 TEST_F(ExtensionDescriptorTest, DuplicateFieldNumber) {
2279 DescriptorPool pool;
2280 FileDescriptorProto file_proto;
2281 // Add "google/protobuf/descriptor.proto".
2282 FileDescriptorProto::descriptor()->file()->CopyTo(&file_proto);
2283 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
2284 // Add "foo.proto":
2285 // import "google/protobuf/descriptor.proto";
2286 // extend google.protobuf.FieldOptions {
2287 // optional int32 option1 = 1000;
2288 // }
2289 file_proto.Clear();
2290 file_proto.set_name("foo.proto");
2291 file_proto.add_dependency("google/protobuf/descriptor.proto");
2292 AddExtension(&file_proto, "google.protobuf.FieldOptions", "option1", 1000,
2293 FieldDescriptorProto::LABEL_OPTIONAL,
2294 FieldDescriptorProto::TYPE_INT32);
2295 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
2296 // Add "bar.proto":
2297 // import "google/protobuf/descriptor.proto";
2298 // extend google.protobuf.FieldOptions {
2299 // optional int32 option2 = 1000;
2300 // }
2301 file_proto.Clear();
2302 file_proto.set_name("bar.proto");
2303 file_proto.add_dependency("google/protobuf/descriptor.proto");
2304 AddExtension(&file_proto, "google.protobuf.FieldOptions", "option2", 1000,
2305 FieldDescriptorProto::LABEL_OPTIONAL,
2306 FieldDescriptorProto::TYPE_INT32);
2307 // Currently we only generate a warning for conflicting extension numbers.
2308 // TODO: Change it to an error.
2309 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
2310 }
2311
2312 // ===================================================================
2313
2314 // Ensure that overlapping extension ranges are not allowed.
TEST(OverlappingExtensionRangeTest,ExtensionRangeInternal)2315 TEST(OverlappingExtensionRangeTest, ExtensionRangeInternal) {
2316 // Build descriptors for the following definitions:
2317 //
2318 // message Foo {
2319 // extensions 10 to 19;
2320 // extensions 15;
2321 // }
2322 FileDescriptorProto foo_file;
2323 foo_file.set_name("foo.proto");
2324
2325 DescriptorProto* foo = AddMessage(&foo_file, "Foo");
2326 AddExtensionRange(foo, 10, 20);
2327 AddExtensionRange(foo, 15, 16);
2328
2329 DescriptorPool pool;
2330 MockErrorCollector error_collector;
2331 // The extensions ranges are invalid, so the proto shouldn't build.
2332 ASSERT_TRUE(pool.BuildFileCollectingErrors(foo_file, &error_collector) ==
2333 nullptr);
2334 ASSERT_EQ(
2335 "foo.proto: Foo: NUMBER: Extension range 15 to 15 overlaps with "
2336 "already-defined range 10 to 19.\n",
2337 error_collector.text_);
2338 }
2339
TEST(OverlappingExtensionRangeTest,ExtensionRangeAfter)2340 TEST(OverlappingExtensionRangeTest, ExtensionRangeAfter) {
2341 // Build descriptors for the following definitions:
2342 //
2343 // message Foo {
2344 // extensions 10 to 19;
2345 // extensions 15 to 24;
2346 // }
2347 FileDescriptorProto foo_file;
2348 foo_file.set_name("foo.proto");
2349
2350 DescriptorProto* foo = AddMessage(&foo_file, "Foo");
2351 AddExtensionRange(foo, 10, 20);
2352 AddExtensionRange(foo, 15, 25);
2353
2354 DescriptorPool pool;
2355 MockErrorCollector error_collector;
2356 // The extensions ranges are invalid, so the proto shouldn't build.
2357 ASSERT_TRUE(pool.BuildFileCollectingErrors(foo_file, &error_collector) ==
2358 nullptr);
2359 ASSERT_EQ(
2360 "foo.proto: Foo: NUMBER: Extension range 15 to 24 overlaps with "
2361 "already-defined range 10 to 19.\n",
2362 error_collector.text_);
2363 }
2364
TEST(OverlappingExtensionRangeTest,ExtensionRangeBefore)2365 TEST(OverlappingExtensionRangeTest, ExtensionRangeBefore) {
2366 // Build descriptors for the following definitions:
2367 //
2368 // message Foo {
2369 // extensions 10 to 19;
2370 // extensions 5 to 14;
2371 // }
2372 FileDescriptorProto foo_file;
2373 foo_file.set_name("foo.proto");
2374
2375 DescriptorProto* foo = AddMessage(&foo_file, "Foo");
2376 AddExtensionRange(foo, 10, 20);
2377 AddExtensionRange(foo, 5, 15);
2378
2379 DescriptorPool pool;
2380 MockErrorCollector error_collector;
2381 // The extensions ranges are invalid, so the proto shouldn't build.
2382 ASSERT_TRUE(pool.BuildFileCollectingErrors(foo_file, &error_collector) ==
2383 nullptr);
2384 ASSERT_EQ(
2385 "foo.proto: Foo: NUMBER: Extension range 5 to 14 overlaps with "
2386 "already-defined range 10 to 19.\n",
2387 error_collector.text_);
2388 }
2389
2390 // ===================================================================
2391
2392 // Test reserved fields.
2393 class ReservedDescriptorTest : public testing::Test {
2394 protected:
SetUp()2395 void SetUp() override {
2396 // Build descriptors for the following definitions:
2397 //
2398 // message Foo {
2399 // reserved 2, 9 to 11, 15;
2400 // reserved "foo", "bar";
2401 // }
2402
2403 FileDescriptorProto foo_file;
2404 foo_file.set_name("foo.proto");
2405
2406 DescriptorProto* foo = AddMessage(&foo_file, "Foo");
2407 AddReservedRange(foo, 2, 3);
2408 AddReservedRange(foo, 9, 12);
2409 AddReservedRange(foo, 15, 16);
2410
2411 foo->add_reserved_name("foo");
2412 foo->add_reserved_name("bar");
2413
2414 // Build the descriptors and get the pointers.
2415 foo_file_ = pool_.BuildFile(foo_file);
2416 ASSERT_TRUE(foo_file_ != nullptr);
2417
2418 ASSERT_EQ(1, foo_file_->message_type_count());
2419 foo_ = foo_file_->message_type(0);
2420 }
2421
2422 DescriptorPool pool_;
2423 const FileDescriptor* foo_file_;
2424 const Descriptor* foo_;
2425 };
2426
TEST_F(ReservedDescriptorTest,ReservedRanges)2427 TEST_F(ReservedDescriptorTest, ReservedRanges) {
2428 ASSERT_EQ(3, foo_->reserved_range_count());
2429
2430 EXPECT_EQ(2, foo_->reserved_range(0)->start);
2431 EXPECT_EQ(3, foo_->reserved_range(0)->end);
2432
2433 EXPECT_EQ(9, foo_->reserved_range(1)->start);
2434 EXPECT_EQ(12, foo_->reserved_range(1)->end);
2435
2436 EXPECT_EQ(15, foo_->reserved_range(2)->start);
2437 EXPECT_EQ(16, foo_->reserved_range(2)->end);
2438 }
2439
TEST_F(ReservedDescriptorTest,IsReservedNumber)2440 TEST_F(ReservedDescriptorTest, IsReservedNumber) {
2441 EXPECT_FALSE(foo_->IsReservedNumber(1));
2442 EXPECT_TRUE(foo_->IsReservedNumber(2));
2443 EXPECT_FALSE(foo_->IsReservedNumber(3));
2444 EXPECT_FALSE(foo_->IsReservedNumber(8));
2445 EXPECT_TRUE(foo_->IsReservedNumber(9));
2446 EXPECT_TRUE(foo_->IsReservedNumber(10));
2447 EXPECT_TRUE(foo_->IsReservedNumber(11));
2448 EXPECT_FALSE(foo_->IsReservedNumber(12));
2449 EXPECT_FALSE(foo_->IsReservedNumber(13));
2450 EXPECT_FALSE(foo_->IsReservedNumber(14));
2451 EXPECT_TRUE(foo_->IsReservedNumber(15));
2452 EXPECT_FALSE(foo_->IsReservedNumber(16));
2453 }
2454
TEST_F(ReservedDescriptorTest,ReservedNames)2455 TEST_F(ReservedDescriptorTest, ReservedNames) {
2456 ASSERT_EQ(2, foo_->reserved_name_count());
2457
2458 EXPECT_EQ("foo", foo_->reserved_name(0));
2459 EXPECT_EQ("bar", foo_->reserved_name(1));
2460 }
2461
TEST_F(ReservedDescriptorTest,IsReservedName)2462 TEST_F(ReservedDescriptorTest, IsReservedName) {
2463 EXPECT_TRUE(foo_->IsReservedName("foo"));
2464 EXPECT_TRUE(foo_->IsReservedName("bar"));
2465 EXPECT_FALSE(foo_->IsReservedName("baz"));
2466 }
2467
2468 // ===================================================================
2469
2470 // Test reserved enum fields.
2471 class ReservedEnumDescriptorTest : public testing::Test {
2472 protected:
SetUp()2473 void SetUp() override {
2474 // Build descriptors for the following definitions:
2475 //
2476 // enum Foo {
2477 // BAR = 1;
2478 // reserved 2, 9 to 11, 15;
2479 // reserved "foo", "bar";
2480 // }
2481
2482 FileDescriptorProto foo_file;
2483 foo_file.set_name("foo.proto");
2484
2485 EnumDescriptorProto* foo = AddEnum(&foo_file, "Foo");
2486 EnumDescriptorProto* edge1 = AddEnum(&foo_file, "Edge1");
2487 EnumDescriptorProto* edge2 = AddEnum(&foo_file, "Edge2");
2488
2489 AddEnumValue(foo, "BAR", 4);
2490 AddReservedRange(foo, -5, -3);
2491 AddReservedRange(foo, -2, 1);
2492 AddReservedRange(foo, 2, 3);
2493 AddReservedRange(foo, 9, 12);
2494 AddReservedRange(foo, 15, 16);
2495
2496 foo->add_reserved_name("foo");
2497 foo->add_reserved_name("bar");
2498
2499 // Some additional edge cases that cover most or all of the range of enum
2500 // values
2501
2502 // Note: We use INT_MAX as the maximum reserved range upper bound,
2503 // inclusive.
2504 AddEnumValue(edge1, "EDGE1", 1);
2505 AddReservedRange(edge1, 10, INT_MAX);
2506 AddEnumValue(edge2, "EDGE2", 15);
2507 AddReservedRange(edge2, INT_MIN, 10);
2508
2509 // Build the descriptors and get the pointers.
2510 foo_file_ = pool_.BuildFile(foo_file);
2511 ASSERT_TRUE(foo_file_ != nullptr);
2512
2513 ASSERT_EQ(3, foo_file_->enum_type_count());
2514 foo_ = foo_file_->enum_type(0);
2515 edge1_ = foo_file_->enum_type(1);
2516 edge2_ = foo_file_->enum_type(2);
2517 }
2518
2519 DescriptorPool pool_;
2520 const FileDescriptor* foo_file_;
2521 const EnumDescriptor* foo_;
2522 const EnumDescriptor* edge1_;
2523 const EnumDescriptor* edge2_;
2524 };
2525
TEST_F(ReservedEnumDescriptorTest,ReservedRanges)2526 TEST_F(ReservedEnumDescriptorTest, ReservedRanges) {
2527 ASSERT_EQ(5, foo_->reserved_range_count());
2528
2529 EXPECT_EQ(-5, foo_->reserved_range(0)->start);
2530 EXPECT_EQ(-3, foo_->reserved_range(0)->end);
2531
2532 EXPECT_EQ(-2, foo_->reserved_range(1)->start);
2533 EXPECT_EQ(1, foo_->reserved_range(1)->end);
2534
2535 EXPECT_EQ(2, foo_->reserved_range(2)->start);
2536 EXPECT_EQ(3, foo_->reserved_range(2)->end);
2537
2538 EXPECT_EQ(9, foo_->reserved_range(3)->start);
2539 EXPECT_EQ(12, foo_->reserved_range(3)->end);
2540
2541 EXPECT_EQ(15, foo_->reserved_range(4)->start);
2542 EXPECT_EQ(16, foo_->reserved_range(4)->end);
2543
2544 ASSERT_EQ(1, edge1_->reserved_range_count());
2545 EXPECT_EQ(10, edge1_->reserved_range(0)->start);
2546 EXPECT_EQ(INT_MAX, edge1_->reserved_range(0)->end);
2547
2548 ASSERT_EQ(1, edge2_->reserved_range_count());
2549 EXPECT_EQ(INT_MIN, edge2_->reserved_range(0)->start);
2550 EXPECT_EQ(10, edge2_->reserved_range(0)->end);
2551 }
2552
TEST_F(ReservedEnumDescriptorTest,IsReservedNumber)2553 TEST_F(ReservedEnumDescriptorTest, IsReservedNumber) {
2554 EXPECT_TRUE(foo_->IsReservedNumber(-5));
2555 EXPECT_TRUE(foo_->IsReservedNumber(-4));
2556 EXPECT_TRUE(foo_->IsReservedNumber(-3));
2557 EXPECT_TRUE(foo_->IsReservedNumber(-2));
2558 EXPECT_TRUE(foo_->IsReservedNumber(-1));
2559 EXPECT_TRUE(foo_->IsReservedNumber(0));
2560 EXPECT_TRUE(foo_->IsReservedNumber(1));
2561 EXPECT_TRUE(foo_->IsReservedNumber(2));
2562 EXPECT_TRUE(foo_->IsReservedNumber(3));
2563 EXPECT_FALSE(foo_->IsReservedNumber(8));
2564 EXPECT_TRUE(foo_->IsReservedNumber(9));
2565 EXPECT_TRUE(foo_->IsReservedNumber(10));
2566 EXPECT_TRUE(foo_->IsReservedNumber(11));
2567 EXPECT_TRUE(foo_->IsReservedNumber(12));
2568 EXPECT_FALSE(foo_->IsReservedNumber(13));
2569 EXPECT_FALSE(foo_->IsReservedNumber(13));
2570 EXPECT_FALSE(foo_->IsReservedNumber(14));
2571 EXPECT_TRUE(foo_->IsReservedNumber(15));
2572 EXPECT_TRUE(foo_->IsReservedNumber(16));
2573 EXPECT_FALSE(foo_->IsReservedNumber(17));
2574
2575 EXPECT_FALSE(edge1_->IsReservedNumber(9));
2576 EXPECT_TRUE(edge1_->IsReservedNumber(10));
2577 EXPECT_TRUE(edge1_->IsReservedNumber(INT_MAX - 1));
2578 EXPECT_TRUE(edge1_->IsReservedNumber(INT_MAX));
2579
2580 EXPECT_TRUE(edge2_->IsReservedNumber(INT_MIN));
2581 EXPECT_TRUE(edge2_->IsReservedNumber(9));
2582 EXPECT_TRUE(edge2_->IsReservedNumber(10));
2583 EXPECT_FALSE(edge2_->IsReservedNumber(11));
2584 }
2585
TEST_F(ReservedEnumDescriptorTest,ReservedNames)2586 TEST_F(ReservedEnumDescriptorTest, ReservedNames) {
2587 ASSERT_EQ(2, foo_->reserved_name_count());
2588
2589 EXPECT_EQ("foo", foo_->reserved_name(0));
2590 EXPECT_EQ("bar", foo_->reserved_name(1));
2591 }
2592
TEST_F(ReservedEnumDescriptorTest,IsReservedName)2593 TEST_F(ReservedEnumDescriptorTest, IsReservedName) {
2594 EXPECT_TRUE(foo_->IsReservedName("foo"));
2595 EXPECT_TRUE(foo_->IsReservedName("bar"));
2596 EXPECT_FALSE(foo_->IsReservedName("baz"));
2597 }
2598
2599 // ===================================================================
2600
2601 class MiscTest : public testing::Test {
2602 protected:
2603 // Function which makes a field descriptor of the given type.
GetFieldDescriptorOfType(FieldDescriptor::Type type)2604 const FieldDescriptor* GetFieldDescriptorOfType(FieldDescriptor::Type type) {
2605 FileDescriptorProto file_proto;
2606 file_proto.set_name("foo.proto");
2607 AddEmptyEnum(&file_proto, "DummyEnum");
2608
2609 DescriptorProto* message = AddMessage(&file_proto, "TestMessage");
2610 FieldDescriptorProto* field = AddField(
2611 message, "foo", 1, FieldDescriptorProto::LABEL_OPTIONAL,
2612 static_cast<FieldDescriptorProto::Type>(static_cast<int>(type)));
2613
2614 if (type == FieldDescriptor::TYPE_MESSAGE ||
2615 type == FieldDescriptor::TYPE_GROUP) {
2616 field->set_type_name("TestMessage");
2617 } else if (type == FieldDescriptor::TYPE_ENUM) {
2618 field->set_type_name("DummyEnum");
2619 }
2620
2621 // Build the descriptors and get the pointers.
2622 pool_ = std::make_unique<DescriptorPool>();
2623 const FileDescriptor* file = pool_->BuildFile(file_proto);
2624
2625 if (file != nullptr && file->message_type_count() == 1 &&
2626 file->message_type(0)->field_count() == 1) {
2627 return file->message_type(0)->field(0);
2628 } else {
2629 return nullptr;
2630 }
2631 }
2632
GetTypeNameForFieldType(FieldDescriptor::Type type)2633 const char* GetTypeNameForFieldType(FieldDescriptor::Type type) {
2634 const FieldDescriptor* field = GetFieldDescriptorOfType(type);
2635 return field != nullptr ? field->type_name() : "";
2636 }
2637
GetCppTypeForFieldType(FieldDescriptor::Type type)2638 FieldDescriptor::CppType GetCppTypeForFieldType(FieldDescriptor::Type type) {
2639 const FieldDescriptor* field = GetFieldDescriptorOfType(type);
2640 return field != nullptr ? field->cpp_type()
2641 : static_cast<FieldDescriptor::CppType>(0);
2642 }
2643
GetCppTypeNameForFieldType(FieldDescriptor::Type type)2644 const char* GetCppTypeNameForFieldType(FieldDescriptor::Type type) {
2645 const FieldDescriptor* field = GetFieldDescriptorOfType(type);
2646 return field != nullptr ? field->cpp_type_name() : "";
2647 }
2648
GetMessageDescriptorForFieldType(FieldDescriptor::Type type)2649 const Descriptor* GetMessageDescriptorForFieldType(
2650 FieldDescriptor::Type type) {
2651 const FieldDescriptor* field = GetFieldDescriptorOfType(type);
2652 return field != nullptr ? field->message_type() : nullptr;
2653 }
2654
GetEnumDescriptorForFieldType(FieldDescriptor::Type type)2655 const EnumDescriptor* GetEnumDescriptorForFieldType(
2656 FieldDescriptor::Type type) {
2657 const FieldDescriptor* field = GetFieldDescriptorOfType(type);
2658 return field != nullptr ? field->enum_type() : nullptr;
2659 }
2660
2661 std::unique_ptr<DescriptorPool> pool_;
2662 };
2663
TEST_F(MiscTest,TypeNames)2664 TEST_F(MiscTest, TypeNames) {
2665 // Test that correct type names are returned.
2666
2667 typedef FieldDescriptor FD; // avoid ugly line wrapping
2668
2669 EXPECT_STREQ("double", GetTypeNameForFieldType(FD::TYPE_DOUBLE));
2670 EXPECT_STREQ("float", GetTypeNameForFieldType(FD::TYPE_FLOAT));
2671 EXPECT_STREQ("int64", GetTypeNameForFieldType(FD::TYPE_INT64));
2672 EXPECT_STREQ("uint64", GetTypeNameForFieldType(FD::TYPE_UINT64));
2673 EXPECT_STREQ("int32", GetTypeNameForFieldType(FD::TYPE_INT32));
2674 EXPECT_STREQ("fixed64", GetTypeNameForFieldType(FD::TYPE_FIXED64));
2675 EXPECT_STREQ("fixed32", GetTypeNameForFieldType(FD::TYPE_FIXED32));
2676 EXPECT_STREQ("bool", GetTypeNameForFieldType(FD::TYPE_BOOL));
2677 EXPECT_STREQ("string", GetTypeNameForFieldType(FD::TYPE_STRING));
2678 EXPECT_STREQ("group", GetTypeNameForFieldType(FD::TYPE_GROUP));
2679 EXPECT_STREQ("message", GetTypeNameForFieldType(FD::TYPE_MESSAGE));
2680 EXPECT_STREQ("bytes", GetTypeNameForFieldType(FD::TYPE_BYTES));
2681 EXPECT_STREQ("uint32", GetTypeNameForFieldType(FD::TYPE_UINT32));
2682 EXPECT_STREQ("enum", GetTypeNameForFieldType(FD::TYPE_ENUM));
2683 EXPECT_STREQ("sfixed32", GetTypeNameForFieldType(FD::TYPE_SFIXED32));
2684 EXPECT_STREQ("sfixed64", GetTypeNameForFieldType(FD::TYPE_SFIXED64));
2685 EXPECT_STREQ("sint32", GetTypeNameForFieldType(FD::TYPE_SINT32));
2686 EXPECT_STREQ("sint64", GetTypeNameForFieldType(FD::TYPE_SINT64));
2687 }
2688
TEST_F(MiscTest,StaticTypeNames)2689 TEST_F(MiscTest, StaticTypeNames) {
2690 // Test that correct type names are returned.
2691
2692 typedef FieldDescriptor FD; // avoid ugly line wrapping
2693
2694 EXPECT_STREQ("double", FD::TypeName(FD::TYPE_DOUBLE));
2695 EXPECT_STREQ("float", FD::TypeName(FD::TYPE_FLOAT));
2696 EXPECT_STREQ("int64", FD::TypeName(FD::TYPE_INT64));
2697 EXPECT_STREQ("uint64", FD::TypeName(FD::TYPE_UINT64));
2698 EXPECT_STREQ("int32", FD::TypeName(FD::TYPE_INT32));
2699 EXPECT_STREQ("fixed64", FD::TypeName(FD::TYPE_FIXED64));
2700 EXPECT_STREQ("fixed32", FD::TypeName(FD::TYPE_FIXED32));
2701 EXPECT_STREQ("bool", FD::TypeName(FD::TYPE_BOOL));
2702 EXPECT_STREQ("string", FD::TypeName(FD::TYPE_STRING));
2703 EXPECT_STREQ("group", FD::TypeName(FD::TYPE_GROUP));
2704 EXPECT_STREQ("message", FD::TypeName(FD::TYPE_MESSAGE));
2705 EXPECT_STREQ("bytes", FD::TypeName(FD::TYPE_BYTES));
2706 EXPECT_STREQ("uint32", FD::TypeName(FD::TYPE_UINT32));
2707 EXPECT_STREQ("enum", FD::TypeName(FD::TYPE_ENUM));
2708 EXPECT_STREQ("sfixed32", FD::TypeName(FD::TYPE_SFIXED32));
2709 EXPECT_STREQ("sfixed64", FD::TypeName(FD::TYPE_SFIXED64));
2710 EXPECT_STREQ("sint32", FD::TypeName(FD::TYPE_SINT32));
2711 EXPECT_STREQ("sint64", FD::TypeName(FD::TYPE_SINT64));
2712 }
2713
TEST_F(MiscTest,CppTypes)2714 TEST_F(MiscTest, CppTypes) {
2715 // Test that CPP types are assigned correctly.
2716
2717 typedef FieldDescriptor FD; // avoid ugly line wrapping
2718
2719 EXPECT_EQ(FD::CPPTYPE_DOUBLE, GetCppTypeForFieldType(FD::TYPE_DOUBLE));
2720 EXPECT_EQ(FD::CPPTYPE_FLOAT, GetCppTypeForFieldType(FD::TYPE_FLOAT));
2721 EXPECT_EQ(FD::CPPTYPE_INT64, GetCppTypeForFieldType(FD::TYPE_INT64));
2722 EXPECT_EQ(FD::CPPTYPE_UINT64, GetCppTypeForFieldType(FD::TYPE_UINT64));
2723 EXPECT_EQ(FD::CPPTYPE_INT32, GetCppTypeForFieldType(FD::TYPE_INT32));
2724 EXPECT_EQ(FD::CPPTYPE_UINT64, GetCppTypeForFieldType(FD::TYPE_FIXED64));
2725 EXPECT_EQ(FD::CPPTYPE_UINT32, GetCppTypeForFieldType(FD::TYPE_FIXED32));
2726 EXPECT_EQ(FD::CPPTYPE_BOOL, GetCppTypeForFieldType(FD::TYPE_BOOL));
2727 EXPECT_EQ(FD::CPPTYPE_STRING, GetCppTypeForFieldType(FD::TYPE_STRING));
2728 EXPECT_EQ(FD::CPPTYPE_MESSAGE, GetCppTypeForFieldType(FD::TYPE_GROUP));
2729 EXPECT_EQ(FD::CPPTYPE_MESSAGE, GetCppTypeForFieldType(FD::TYPE_MESSAGE));
2730 EXPECT_EQ(FD::CPPTYPE_STRING, GetCppTypeForFieldType(FD::TYPE_BYTES));
2731 EXPECT_EQ(FD::CPPTYPE_UINT32, GetCppTypeForFieldType(FD::TYPE_UINT32));
2732 EXPECT_EQ(FD::CPPTYPE_ENUM, GetCppTypeForFieldType(FD::TYPE_ENUM));
2733 EXPECT_EQ(FD::CPPTYPE_INT32, GetCppTypeForFieldType(FD::TYPE_SFIXED32));
2734 EXPECT_EQ(FD::CPPTYPE_INT64, GetCppTypeForFieldType(FD::TYPE_SFIXED64));
2735 EXPECT_EQ(FD::CPPTYPE_INT32, GetCppTypeForFieldType(FD::TYPE_SINT32));
2736 EXPECT_EQ(FD::CPPTYPE_INT64, GetCppTypeForFieldType(FD::TYPE_SINT64));
2737 }
2738
TEST_F(MiscTest,CppTypeNames)2739 TEST_F(MiscTest, CppTypeNames) {
2740 // Test that correct CPP type names are returned.
2741
2742 typedef FieldDescriptor FD; // avoid ugly line wrapping
2743
2744 EXPECT_STREQ("double", GetCppTypeNameForFieldType(FD::TYPE_DOUBLE));
2745 EXPECT_STREQ("float", GetCppTypeNameForFieldType(FD::TYPE_FLOAT));
2746 EXPECT_STREQ("int64", GetCppTypeNameForFieldType(FD::TYPE_INT64));
2747 EXPECT_STREQ("uint64", GetCppTypeNameForFieldType(FD::TYPE_UINT64));
2748 EXPECT_STREQ("int32", GetCppTypeNameForFieldType(FD::TYPE_INT32));
2749 EXPECT_STREQ("uint64", GetCppTypeNameForFieldType(FD::TYPE_FIXED64));
2750 EXPECT_STREQ("uint32", GetCppTypeNameForFieldType(FD::TYPE_FIXED32));
2751 EXPECT_STREQ("bool", GetCppTypeNameForFieldType(FD::TYPE_BOOL));
2752 EXPECT_STREQ("string", GetCppTypeNameForFieldType(FD::TYPE_STRING));
2753 EXPECT_STREQ("message", GetCppTypeNameForFieldType(FD::TYPE_GROUP));
2754 EXPECT_STREQ("message", GetCppTypeNameForFieldType(FD::TYPE_MESSAGE));
2755 EXPECT_STREQ("string", GetCppTypeNameForFieldType(FD::TYPE_BYTES));
2756 EXPECT_STREQ("uint32", GetCppTypeNameForFieldType(FD::TYPE_UINT32));
2757 EXPECT_STREQ("enum", GetCppTypeNameForFieldType(FD::TYPE_ENUM));
2758 EXPECT_STREQ("int32", GetCppTypeNameForFieldType(FD::TYPE_SFIXED32));
2759 EXPECT_STREQ("int64", GetCppTypeNameForFieldType(FD::TYPE_SFIXED64));
2760 EXPECT_STREQ("int32", GetCppTypeNameForFieldType(FD::TYPE_SINT32));
2761 EXPECT_STREQ("int64", GetCppTypeNameForFieldType(FD::TYPE_SINT64));
2762 }
2763
TEST_F(MiscTest,StaticCppTypeNames)2764 TEST_F(MiscTest, StaticCppTypeNames) {
2765 // Test that correct CPP type names are returned.
2766
2767 typedef FieldDescriptor FD; // avoid ugly line wrapping
2768
2769 EXPECT_STREQ("int32", FD::CppTypeName(FD::CPPTYPE_INT32));
2770 EXPECT_STREQ("int64", FD::CppTypeName(FD::CPPTYPE_INT64));
2771 EXPECT_STREQ("uint32", FD::CppTypeName(FD::CPPTYPE_UINT32));
2772 EXPECT_STREQ("uint64", FD::CppTypeName(FD::CPPTYPE_UINT64));
2773 EXPECT_STREQ("double", FD::CppTypeName(FD::CPPTYPE_DOUBLE));
2774 EXPECT_STREQ("float", FD::CppTypeName(FD::CPPTYPE_FLOAT));
2775 EXPECT_STREQ("bool", FD::CppTypeName(FD::CPPTYPE_BOOL));
2776 EXPECT_STREQ("enum", FD::CppTypeName(FD::CPPTYPE_ENUM));
2777 EXPECT_STREQ("string", FD::CppTypeName(FD::CPPTYPE_STRING));
2778 EXPECT_STREQ("message", FD::CppTypeName(FD::CPPTYPE_MESSAGE));
2779 }
2780
TEST_F(MiscTest,MessageType)2781 TEST_F(MiscTest, MessageType) {
2782 // Test that message_type() is nullptr for non-aggregate fields
2783
2784 typedef FieldDescriptor FD; // avoid ugly line wrapping
2785
2786 EXPECT_TRUE(nullptr == GetMessageDescriptorForFieldType(FD::TYPE_DOUBLE));
2787 EXPECT_TRUE(nullptr == GetMessageDescriptorForFieldType(FD::TYPE_FLOAT));
2788 EXPECT_TRUE(nullptr == GetMessageDescriptorForFieldType(FD::TYPE_INT64));
2789 EXPECT_TRUE(nullptr == GetMessageDescriptorForFieldType(FD::TYPE_UINT64));
2790 EXPECT_TRUE(nullptr == GetMessageDescriptorForFieldType(FD::TYPE_INT32));
2791 EXPECT_TRUE(nullptr == GetMessageDescriptorForFieldType(FD::TYPE_FIXED64));
2792 EXPECT_TRUE(nullptr == GetMessageDescriptorForFieldType(FD::TYPE_FIXED32));
2793 EXPECT_TRUE(nullptr == GetMessageDescriptorForFieldType(FD::TYPE_BOOL));
2794 EXPECT_TRUE(nullptr == GetMessageDescriptorForFieldType(FD::TYPE_STRING));
2795 EXPECT_TRUE(nullptr != GetMessageDescriptorForFieldType(FD::TYPE_GROUP));
2796 EXPECT_TRUE(nullptr != GetMessageDescriptorForFieldType(FD::TYPE_MESSAGE));
2797 EXPECT_TRUE(nullptr == GetMessageDescriptorForFieldType(FD::TYPE_BYTES));
2798 EXPECT_TRUE(nullptr == GetMessageDescriptorForFieldType(FD::TYPE_UINT32));
2799 EXPECT_TRUE(nullptr == GetMessageDescriptorForFieldType(FD::TYPE_ENUM));
2800 EXPECT_TRUE(nullptr == GetMessageDescriptorForFieldType(FD::TYPE_SFIXED32));
2801 EXPECT_TRUE(nullptr == GetMessageDescriptorForFieldType(FD::TYPE_SFIXED64));
2802 EXPECT_TRUE(nullptr == GetMessageDescriptorForFieldType(FD::TYPE_SINT32));
2803 EXPECT_TRUE(nullptr == GetMessageDescriptorForFieldType(FD::TYPE_SINT64));
2804 }
2805
TEST_F(MiscTest,EnumType)2806 TEST_F(MiscTest, EnumType) {
2807 // Test that enum_type() is nullptr for non-enum fields
2808
2809 typedef FieldDescriptor FD; // avoid ugly line wrapping
2810
2811 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_DOUBLE));
2812 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_FLOAT));
2813 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_INT64));
2814 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_UINT64));
2815 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_INT32));
2816 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_FIXED64));
2817 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_FIXED32));
2818 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_BOOL));
2819 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_STRING));
2820 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_GROUP));
2821 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_MESSAGE));
2822 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_BYTES));
2823 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_UINT32));
2824 EXPECT_TRUE(nullptr != GetEnumDescriptorForFieldType(FD::TYPE_ENUM));
2825 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_SFIXED32));
2826 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_SFIXED64));
2827 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_SINT32));
2828 EXPECT_TRUE(nullptr == GetEnumDescriptorForFieldType(FD::TYPE_SINT64));
2829 }
2830
TEST_F(MiscTest,DefaultValues)2831 TEST_F(MiscTest, DefaultValues) {
2832 // Test that setting default values works.
2833 FileDescriptorProto file_proto;
2834 file_proto.set_name("foo.proto");
2835
2836 EnumDescriptorProto* enum_type_proto = AddEnum(&file_proto, "DummyEnum");
2837 AddEnumValue(enum_type_proto, "A", 1);
2838 AddEnumValue(enum_type_proto, "B", 2);
2839
2840 DescriptorProto* message_proto = AddMessage(&file_proto, "TestMessage");
2841
2842 typedef FieldDescriptorProto FD; // avoid ugly line wrapping
2843 const FD::Label label = FD::LABEL_OPTIONAL;
2844
2845 // Create fields of every CPP type with default values.
2846 AddField(message_proto, "int32", 1, label, FD::TYPE_INT32)
2847 ->set_default_value("-1");
2848 AddField(message_proto, "int64", 2, label, FD::TYPE_INT64)
2849 ->set_default_value("-1000000000000");
2850 AddField(message_proto, "uint32", 3, label, FD::TYPE_UINT32)
2851 ->set_default_value("42");
2852 AddField(message_proto, "uint64", 4, label, FD::TYPE_UINT64)
2853 ->set_default_value("2000000000000");
2854 AddField(message_proto, "float", 5, label, FD::TYPE_FLOAT)
2855 ->set_default_value("4.5");
2856 AddField(message_proto, "double", 6, label, FD::TYPE_DOUBLE)
2857 ->set_default_value("10e100");
2858 AddField(message_proto, "bool", 7, label, FD::TYPE_BOOL)
2859 ->set_default_value("true");
2860 AddField(message_proto, "string", 8, label, FD::TYPE_STRING)
2861 ->set_default_value("hello");
2862 AddField(message_proto, "data", 9, label, FD::TYPE_BYTES)
2863 ->set_default_value("\\001\\002\\003");
2864 AddField(message_proto, "data2", 10, label, FD::TYPE_BYTES)
2865 ->set_default_value("\\X01\\X2\\X3");
2866 AddField(message_proto, "data3", 11, label, FD::TYPE_BYTES)
2867 ->set_default_value("\\x01\\x2\\x3");
2868
2869 FieldDescriptorProto* enum_field =
2870 AddField(message_proto, "enum", 12, label, FD::TYPE_ENUM);
2871 enum_field->set_type_name("DummyEnum");
2872 enum_field->set_default_value("B");
2873
2874 // Strings are allowed to have empty defaults. (At one point, due to
2875 // a bug, empty defaults for strings were rejected. Oops.)
2876 AddField(message_proto, "empty_string", 13, label, FD::TYPE_STRING)
2877 ->set_default_value("");
2878
2879 // Add a second set of fields with implicit default values.
2880 AddField(message_proto, "implicit_int32", 21, label, FD::TYPE_INT32);
2881 AddField(message_proto, "implicit_int64", 22, label, FD::TYPE_INT64);
2882 AddField(message_proto, "implicit_uint32", 23, label, FD::TYPE_UINT32);
2883 AddField(message_proto, "implicit_uint64", 24, label, FD::TYPE_UINT64);
2884 AddField(message_proto, "implicit_float", 25, label, FD::TYPE_FLOAT);
2885 AddField(message_proto, "implicit_double", 26, label, FD::TYPE_DOUBLE);
2886 AddField(message_proto, "implicit_bool", 27, label, FD::TYPE_BOOL);
2887 AddField(message_proto, "implicit_string", 28, label, FD::TYPE_STRING);
2888 AddField(message_proto, "implicit_data", 29, label, FD::TYPE_BYTES);
2889 AddField(message_proto, "implicit_enum", 30, label, FD::TYPE_ENUM)
2890 ->set_type_name("DummyEnum");
2891
2892 // Build it.
2893 DescriptorPool pool;
2894 const FileDescriptor* file = pool.BuildFile(file_proto);
2895 ASSERT_TRUE(file != nullptr);
2896
2897 ASSERT_EQ(1, file->enum_type_count());
2898 const EnumDescriptor* enum_type = file->enum_type(0);
2899 ASSERT_EQ(2, enum_type->value_count());
2900 const EnumValueDescriptor* enum_value_a = enum_type->value(0);
2901 const EnumValueDescriptor* enum_value_b = enum_type->value(1);
2902
2903 ASSERT_EQ(1, file->message_type_count());
2904 const Descriptor* message = file->message_type(0);
2905
2906 ASSERT_EQ(23, message->field_count());
2907
2908 // Check the default values.
2909 ASSERT_TRUE(message->field(0)->has_default_value());
2910 ASSERT_TRUE(message->field(1)->has_default_value());
2911 ASSERT_TRUE(message->field(2)->has_default_value());
2912 ASSERT_TRUE(message->field(3)->has_default_value());
2913 ASSERT_TRUE(message->field(4)->has_default_value());
2914 ASSERT_TRUE(message->field(5)->has_default_value());
2915 ASSERT_TRUE(message->field(6)->has_default_value());
2916 ASSERT_TRUE(message->field(7)->has_default_value());
2917 ASSERT_TRUE(message->field(8)->has_default_value());
2918 ASSERT_TRUE(message->field(9)->has_default_value());
2919 ASSERT_TRUE(message->field(10)->has_default_value());
2920 ASSERT_TRUE(message->field(11)->has_default_value());
2921 ASSERT_TRUE(message->field(12)->has_default_value());
2922
2923 EXPECT_EQ(-1, message->field(0)->default_value_int32());
2924 EXPECT_EQ(int64_t{-1000000000000}, message->field(1)->default_value_int64());
2925 EXPECT_EQ(42, message->field(2)->default_value_uint32());
2926 EXPECT_EQ(uint64_t{2000000000000}, message->field(3)->default_value_uint64());
2927 EXPECT_EQ(4.5, message->field(4)->default_value_float());
2928 EXPECT_EQ(10e100, message->field(5)->default_value_double());
2929 EXPECT_TRUE(message->field(6)->default_value_bool());
2930 EXPECT_EQ("hello", message->field(7)->default_value_string());
2931 EXPECT_EQ("\001\002\003", message->field(8)->default_value_string());
2932 EXPECT_EQ("\001\002\003", message->field(9)->default_value_string());
2933 EXPECT_EQ("\001\002\003", message->field(10)->default_value_string());
2934 EXPECT_EQ(enum_value_b, message->field(11)->default_value_enum());
2935 EXPECT_EQ("", message->field(12)->default_value_string());
2936
2937 ASSERT_FALSE(message->field(13)->has_default_value());
2938 ASSERT_FALSE(message->field(14)->has_default_value());
2939 ASSERT_FALSE(message->field(15)->has_default_value());
2940 ASSERT_FALSE(message->field(16)->has_default_value());
2941 ASSERT_FALSE(message->field(17)->has_default_value());
2942 ASSERT_FALSE(message->field(18)->has_default_value());
2943 ASSERT_FALSE(message->field(19)->has_default_value());
2944 ASSERT_FALSE(message->field(20)->has_default_value());
2945 ASSERT_FALSE(message->field(21)->has_default_value());
2946 ASSERT_FALSE(message->field(22)->has_default_value());
2947
2948 EXPECT_EQ(0, message->field(13)->default_value_int32());
2949 EXPECT_EQ(0, message->field(14)->default_value_int64());
2950 EXPECT_EQ(0, message->field(15)->default_value_uint32());
2951 EXPECT_EQ(0, message->field(16)->default_value_uint64());
2952 EXPECT_EQ(0.0f, message->field(17)->default_value_float());
2953 EXPECT_EQ(0.0, message->field(18)->default_value_double());
2954 EXPECT_FALSE(message->field(19)->default_value_bool());
2955 EXPECT_EQ("", message->field(20)->default_value_string());
2956 EXPECT_EQ("", message->field(21)->default_value_string());
2957 EXPECT_EQ(enum_value_a, message->field(22)->default_value_enum());
2958 }
2959
TEST_F(MiscTest,FieldOptions)2960 TEST_F(MiscTest, FieldOptions) {
2961 // Try setting field options.
2962
2963 FileDescriptorProto file_proto;
2964 file_proto.set_name("foo.proto");
2965
2966 DescriptorProto* message_proto = AddMessage(&file_proto, "TestMessage");
2967 AddField(message_proto, "foo", 1, FieldDescriptorProto::LABEL_OPTIONAL,
2968 FieldDescriptorProto::TYPE_INT32);
2969 FieldDescriptorProto* bar_proto =
2970 AddField(message_proto, "bar", 2, FieldDescriptorProto::LABEL_OPTIONAL,
2971 FieldDescriptorProto::TYPE_BYTES);
2972
2973 FieldOptions* options = bar_proto->mutable_options();
2974 options->set_ctype(FieldOptions::CORD);
2975
2976 // Build the descriptors and get the pointers.
2977 DescriptorPool pool;
2978 const FileDescriptor* file = pool.BuildFile(file_proto);
2979 ASSERT_TRUE(file != nullptr);
2980
2981 ASSERT_EQ(1, file->message_type_count());
2982 const Descriptor* message = file->message_type(0);
2983
2984 ASSERT_EQ(2, message->field_count());
2985 const FieldDescriptor* foo = message->field(0);
2986 const FieldDescriptor* bar = message->field(1);
2987
2988 // "foo" had no options set, so it should return the default options.
2989 EXPECT_EQ(&FieldOptions::default_instance(), &foo->options());
2990
2991 // "bar" had options set.
2992 EXPECT_NE(&FieldOptions::default_instance(), options);
2993 EXPECT_TRUE(bar->options().has_ctype());
2994 EXPECT_EQ(FieldOptions::CORD, bar->options().ctype());
2995 }
2996
2997 // ===================================================================
2998 enum DescriptorPoolMode { NO_DATABASE, FALLBACK_DATABASE };
2999
3000 class AllowUnknownDependenciesTest
3001 : public testing::TestWithParam<
3002 std::tuple<DescriptorPoolMode, const char*>> {
3003 protected:
mode()3004 DescriptorPoolMode mode() { return std::get<0>(GetParam()); }
syntax()3005 const char* syntax() { return std::get<1>(GetParam()); }
3006
SetUp()3007 void SetUp() override {
3008 FileDescriptorProto foo_proto, bar_proto;
3009
3010 switch (mode()) {
3011 case NO_DATABASE:
3012 pool_ = std::make_unique<DescriptorPool>();
3013 break;
3014 case FALLBACK_DATABASE:
3015 pool_ = std::make_unique<DescriptorPool>(&db_);
3016 break;
3017 }
3018
3019 pool_->AllowUnknownDependencies();
3020
3021 ASSERT_TRUE(TextFormat::ParseFromString(
3022 "name: 'foo.proto'"
3023 "dependency: 'bar.proto'"
3024 "dependency: 'baz.proto'"
3025 "message_type {"
3026 " name: 'Foo'"
3027 " field { name:'bar' number:1 label:LABEL_OPTIONAL type_name:'Bar' }"
3028 " field { name:'baz' number:2 label:LABEL_OPTIONAL type_name:'Baz' }"
3029 " field { name:'moo' number:3 label:LABEL_OPTIONAL"
3030 " type_name: '.corge.Moo'"
3031 " type: TYPE_ENUM"
3032 " options {"
3033 " uninterpreted_option {"
3034 " name {"
3035 " name_part: 'grault'"
3036 " is_extension: true"
3037 " }"
3038 " positive_int_value: 1234"
3039 " }"
3040 " }"
3041 " }"
3042 "}",
3043 &foo_proto));
3044 foo_proto.set_syntax(syntax());
3045
3046 ASSERT_TRUE(
3047 TextFormat::ParseFromString("name: 'bar.proto'"
3048 "message_type { name: 'Bar' }",
3049 &bar_proto));
3050 bar_proto.set_syntax(syntax());
3051
3052 // Collect pointers to stuff.
3053 bar_file_ = BuildFile(bar_proto);
3054 ASSERT_TRUE(bar_file_ != nullptr);
3055
3056 ASSERT_EQ(1, bar_file_->message_type_count());
3057 bar_type_ = bar_file_->message_type(0);
3058
3059 foo_file_ = BuildFile(foo_proto);
3060 ASSERT_TRUE(foo_file_ != nullptr);
3061
3062 ASSERT_EQ(1, foo_file_->message_type_count());
3063 foo_type_ = foo_file_->message_type(0);
3064
3065 ASSERT_EQ(3, foo_type_->field_count());
3066 bar_field_ = foo_type_->field(0);
3067 baz_field_ = foo_type_->field(1);
3068 moo_field_ = foo_type_->field(2);
3069 }
3070
BuildFile(const FileDescriptorProto & proto)3071 const FileDescriptor* BuildFile(const FileDescriptorProto& proto) {
3072 switch (mode()) {
3073 case NO_DATABASE:
3074 return pool_->BuildFile(proto);
3075 break;
3076 case FALLBACK_DATABASE: {
3077 EXPECT_TRUE(db_.Add(proto));
3078 return pool_->FindFileByName(proto.name());
3079 }
3080 }
3081 ABSL_LOG(FATAL) << "Can't get here.";
3082 return nullptr;
3083 }
3084
3085 const FileDescriptor* bar_file_;
3086 const Descriptor* bar_type_;
3087 const FileDescriptor* foo_file_;
3088 const Descriptor* foo_type_;
3089 const FieldDescriptor* bar_field_;
3090 const FieldDescriptor* baz_field_;
3091 const FieldDescriptor* moo_field_;
3092
3093 SimpleDescriptorDatabase db_; // used if in FALLBACK_DATABASE mode.
3094 std::unique_ptr<DescriptorPool> pool_;
3095 };
3096
TEST_P(AllowUnknownDependenciesTest,PlaceholderFile)3097 TEST_P(AllowUnknownDependenciesTest, PlaceholderFile) {
3098 ASSERT_EQ(2, foo_file_->dependency_count());
3099 EXPECT_EQ(bar_file_, foo_file_->dependency(0));
3100 EXPECT_FALSE(bar_file_->is_placeholder());
3101
3102 const FileDescriptor* baz_file = foo_file_->dependency(1);
3103 EXPECT_EQ("baz.proto", baz_file->name());
3104 EXPECT_EQ(0, baz_file->message_type_count());
3105 EXPECT_TRUE(baz_file->is_placeholder());
3106
3107 // Placeholder files should not be findable.
3108 EXPECT_EQ(bar_file_, pool_->FindFileByName(bar_file_->name()));
3109 EXPECT_TRUE(pool_->FindFileByName(baz_file->name()) == nullptr);
3110
3111 // Copy*To should not crash for placeholder files.
3112 FileDescriptorProto baz_file_proto;
3113 baz_file->CopyTo(&baz_file_proto);
3114 baz_file->CopySourceCodeInfoTo(&baz_file_proto);
3115 EXPECT_FALSE(baz_file_proto.has_source_code_info());
3116 }
3117
TEST_P(AllowUnknownDependenciesTest,PlaceholderTypes)3118 TEST_P(AllowUnknownDependenciesTest, PlaceholderTypes) {
3119 ASSERT_EQ(FieldDescriptor::TYPE_MESSAGE, bar_field_->type());
3120 EXPECT_EQ(bar_type_, bar_field_->message_type());
3121 EXPECT_FALSE(bar_type_->is_placeholder());
3122
3123 ASSERT_EQ(FieldDescriptor::TYPE_MESSAGE, baz_field_->type());
3124 const Descriptor* baz_type = baz_field_->message_type();
3125 EXPECT_EQ("Baz", baz_type->name());
3126 EXPECT_EQ("Baz", baz_type->full_name());
3127 EXPECT_EQ(0, baz_type->extension_range_count());
3128 EXPECT_TRUE(baz_type->is_placeholder());
3129
3130 ASSERT_EQ(FieldDescriptor::TYPE_ENUM, moo_field_->type());
3131 const EnumDescriptor* moo_type = moo_field_->enum_type();
3132 EXPECT_EQ("Moo", moo_type->name());
3133 EXPECT_EQ("corge.Moo", moo_type->full_name());
3134 EXPECT_TRUE(moo_type->is_placeholder());
3135 // Placeholder enum values should not be findable.
3136 EXPECT_EQ(moo_type->FindValueByNumber(0), nullptr);
3137
3138 // Placeholder types should not be findable.
3139 EXPECT_EQ(bar_type_, pool_->FindMessageTypeByName(bar_type_->full_name()));
3140 EXPECT_TRUE(pool_->FindMessageTypeByName(baz_type->full_name()) == nullptr);
3141 EXPECT_TRUE(pool_->FindEnumTypeByName(moo_type->full_name()) == nullptr);
3142 }
3143
TEST_P(AllowUnknownDependenciesTest,CopyTo)3144 TEST_P(AllowUnknownDependenciesTest, CopyTo) {
3145 // FieldDescriptor::CopyTo() should write non-fully-qualified type names
3146 // for placeholder types which were not originally fully-qualified.
3147 FieldDescriptorProto proto;
3148
3149 // Bar is not a placeholder, so it is fully-qualified.
3150 bar_field_->CopyTo(&proto);
3151 EXPECT_EQ(".Bar", proto.type_name());
3152 EXPECT_EQ(FieldDescriptorProto::TYPE_MESSAGE, proto.type());
3153
3154 // Baz is an unqualified placeholder.
3155 proto.Clear();
3156 baz_field_->CopyTo(&proto);
3157 EXPECT_EQ("Baz", proto.type_name());
3158 EXPECT_FALSE(proto.has_type());
3159
3160 // Moo is a fully-qualified placeholder.
3161 proto.Clear();
3162 moo_field_->CopyTo(&proto);
3163 EXPECT_EQ(".corge.Moo", proto.type_name());
3164 EXPECT_EQ(FieldDescriptorProto::TYPE_ENUM, proto.type());
3165 }
3166
TEST_P(AllowUnknownDependenciesTest,CustomOptions)3167 TEST_P(AllowUnknownDependenciesTest, CustomOptions) {
3168 // Moo should still have the uninterpreted option attached.
3169 ASSERT_EQ(1, moo_field_->options().uninterpreted_option_size());
3170 const UninterpretedOption& option =
3171 moo_field_->options().uninterpreted_option(0);
3172 ASSERT_EQ(1, option.name_size());
3173 EXPECT_EQ("grault", option.name(0).name_part());
3174 }
3175
TEST_P(AllowUnknownDependenciesTest,UnknownExtendee)3176 TEST_P(AllowUnknownDependenciesTest, UnknownExtendee) {
3177 // Test that we can extend an unknown type. This is slightly tricky because
3178 // it means that the placeholder type must have an extension range.
3179
3180 FileDescriptorProto extension_proto;
3181
3182 ASSERT_TRUE(TextFormat::ParseFromString(
3183 "name: 'extension.proto'"
3184 "extension { extendee: 'UnknownType' name:'some_extension' number:123"
3185 " label:LABEL_OPTIONAL type:TYPE_INT32 }",
3186 &extension_proto));
3187 const FileDescriptor* file = BuildFile(extension_proto);
3188
3189 ASSERT_TRUE(file != nullptr);
3190
3191 ASSERT_EQ(1, file->extension_count());
3192 const Descriptor* extendee = file->extension(0)->containing_type();
3193 EXPECT_EQ("UnknownType", extendee->name());
3194 EXPECT_TRUE(extendee->is_placeholder());
3195 ASSERT_EQ(1, extendee->extension_range_count());
3196 EXPECT_EQ(1, extendee->extension_range(0)->start_number());
3197 EXPECT_EQ(FieldDescriptor::kMaxNumber + 1,
3198 extendee->extension_range(0)->end_number());
3199 }
3200
3201
TEST_P(AllowUnknownDependenciesTest,CustomOption)3202 TEST_P(AllowUnknownDependenciesTest, CustomOption) {
3203 // Test that we can use a custom option without having parsed
3204 // descriptor.proto.
3205
3206 FileDescriptorProto option_proto;
3207
3208 ASSERT_TRUE(TextFormat::ParseFromString(
3209 "name: \"unknown_custom_options.proto\" "
3210 "dependency: \"google/protobuf/descriptor.proto\" "
3211 "extension { "
3212 " extendee: \"google.protobuf.FileOptions\" "
3213 " name: \"some_option\" "
3214 " number: 123456 "
3215 " label: LABEL_OPTIONAL "
3216 " type: TYPE_INT32 "
3217 "} "
3218 "options { "
3219 " uninterpreted_option { "
3220 " name { "
3221 " name_part: \"some_option\" "
3222 " is_extension: true "
3223 " } "
3224 " positive_int_value: 1234 "
3225 " } "
3226 " uninterpreted_option { "
3227 " name { "
3228 " name_part: \"unknown_option\" "
3229 " is_extension: true "
3230 " } "
3231 " positive_int_value: 1234 "
3232 " } "
3233 " uninterpreted_option { "
3234 " name { "
3235 " name_part: \"optimize_for\" "
3236 " is_extension: false "
3237 " } "
3238 " identifier_value: \"SPEED\" "
3239 " } "
3240 "}",
3241 &option_proto));
3242
3243 const FileDescriptor* file = BuildFile(option_proto);
3244 ASSERT_TRUE(file != nullptr);
3245
3246 // Verify that no extension options were set, but they were left as
3247 // uninterpreted_options.
3248 std::vector<const FieldDescriptor*> fields;
3249 file->options().GetReflection()->ListFields(file->options(), &fields);
3250 ASSERT_EQ(2, fields.size());
3251 EXPECT_TRUE(file->options().has_optimize_for());
3252 EXPECT_EQ(2, file->options().uninterpreted_option_size());
3253 }
3254
TEST_P(AllowUnknownDependenciesTest,UndeclaredDependencyTriggersBuildOfDependency)3255 TEST_P(AllowUnknownDependenciesTest,
3256 UndeclaredDependencyTriggersBuildOfDependency) {
3257 // Crazy case: suppose foo.proto refers to a symbol without declaring the
3258 // dependency that finds it. In the event that the pool is backed by a
3259 // DescriptorDatabase, the pool will attempt to find the symbol in the
3260 // database. If successful, it will build the undeclared dependency to verify
3261 // that the file does indeed contain the symbol. If that file fails to build,
3262 // then its descriptors must be rolled back. However, we still want foo.proto
3263 // to build successfully, since we are allowing unknown dependencies.
3264
3265 FileDescriptorProto undeclared_dep_proto;
3266 // We make this file fail to build by giving it two fields with tag 1.
3267 ASSERT_TRUE(TextFormat::ParseFromString(
3268 "name: \"invalid_file_as_undeclared_dep.proto\" "
3269 "package: \"undeclared\" "
3270 "message_type: { "
3271 " name: \"Mooo\" "
3272 " field { "
3273 " name:'moo' number:1 label:LABEL_OPTIONAL type: TYPE_INT32 "
3274 " }"
3275 " field { "
3276 " name:'mooo' number:1 label:LABEL_OPTIONAL type: TYPE_INT64 "
3277 " }"
3278 "}",
3279 &undeclared_dep_proto));
3280 // We can't use the BuildFile() helper because we don't actually want to build
3281 // it into the descriptor pool in the fallback database case: it just needs to
3282 // be sitting in the database so that it gets built during the building of
3283 // test.proto below.
3284 switch (mode()) {
3285 case NO_DATABASE: {
3286 ASSERT_TRUE(pool_->BuildFile(undeclared_dep_proto) == nullptr);
3287 break;
3288 }
3289 case FALLBACK_DATABASE: {
3290 ASSERT_TRUE(db_.Add(undeclared_dep_proto));
3291 }
3292 }
3293
3294 FileDescriptorProto test_proto;
3295 ASSERT_TRUE(TextFormat::ParseFromString(
3296 "name: \"test.proto\" "
3297 "message_type: { "
3298 " name: \"Corge\" "
3299 " field { "
3300 " name:'mooo' number:1 label: LABEL_OPTIONAL "
3301 " type_name:'undeclared.Mooo' type: TYPE_MESSAGE "
3302 " }"
3303 "}",
3304 &test_proto));
3305
3306 const FileDescriptor* file = BuildFile(test_proto);
3307 ASSERT_TRUE(file != nullptr);
3308 ABSL_LOG(INFO) << file->DebugString();
3309
3310 EXPECT_EQ(0, file->dependency_count());
3311 ASSERT_EQ(1, file->message_type_count());
3312 const Descriptor* corge_desc = file->message_type(0);
3313 ASSERT_EQ("Corge", corge_desc->name());
3314 ASSERT_EQ(1, corge_desc->field_count());
3315 EXPECT_FALSE(corge_desc->is_placeholder());
3316
3317 const FieldDescriptor* mooo_field = corge_desc->field(0);
3318 ASSERT_EQ(FieldDescriptor::TYPE_MESSAGE, mooo_field->type());
3319 ASSERT_EQ("Mooo", mooo_field->message_type()->name());
3320 ASSERT_EQ("undeclared.Mooo", mooo_field->message_type()->full_name());
3321 EXPECT_TRUE(mooo_field->message_type()->is_placeholder());
3322 // The place holder type should not be findable.
3323 ASSERT_TRUE(pool_->FindMessageTypeByName("undeclared.Mooo") == nullptr);
3324 }
3325
3326 INSTANTIATE_TEST_SUITE_P(DatabaseSource, AllowUnknownDependenciesTest,
3327 testing::Combine(testing::Values(NO_DATABASE,
3328 FALLBACK_DATABASE),
3329 testing::Values("proto2", "proto3")));
3330
3331 // ===================================================================
3332
TEST(CustomOptions,OptionLocations)3333 TEST(CustomOptions, OptionLocations) {
3334 const Descriptor* message =
3335 protobuf_unittest::TestMessageWithCustomOptions::descriptor();
3336 const FileDescriptor* file = message->file();
3337 const FieldDescriptor* field = message->FindFieldByName("field1");
3338 const OneofDescriptor* oneof = message->FindOneofByName("AnOneof");
3339 const FieldDescriptor* map_field = message->FindFieldByName("map_field");
3340 const EnumDescriptor* enm = message->FindEnumTypeByName("AnEnum");
3341 // TODO: Support EnumValue options, once the compiler does.
3342 const ServiceDescriptor* service =
3343 file->FindServiceByName("TestServiceWithCustomOptions");
3344 const MethodDescriptor* method = service->FindMethodByName("Foo");
3345
3346 EXPECT_EQ(int64_t{9876543210},
3347 file->options().GetExtension(protobuf_unittest::file_opt1));
3348 EXPECT_EQ(-56,
3349 message->options().GetExtension(protobuf_unittest::message_opt1));
3350 EXPECT_EQ(int64_t{8765432109},
3351 field->options().GetExtension(protobuf_unittest::field_opt1));
3352 EXPECT_EQ(42, // Check that we get the default for an option we don't set.
3353 field->options().GetExtension(protobuf_unittest::field_opt2));
3354 EXPECT_EQ(-99, oneof->options().GetExtension(protobuf_unittest::oneof_opt1));
3355 EXPECT_EQ(int64_t{12345},
3356 map_field->options().GetExtension(protobuf_unittest::field_opt1));
3357 EXPECT_EQ(-789, enm->options().GetExtension(protobuf_unittest::enum_opt1));
3358 EXPECT_EQ(123, enm->value(1)->options().GetExtension(
3359 protobuf_unittest::enum_value_opt1));
3360 EXPECT_EQ(int64_t{-9876543210},
3361 service->options().GetExtension(protobuf_unittest::service_opt1));
3362 EXPECT_EQ(protobuf_unittest::METHODOPT1_VAL2,
3363 method->options().GetExtension(protobuf_unittest::method_opt1));
3364
3365 // See that the regular options went through unscathed.
3366 EXPECT_TRUE(message->options().has_message_set_wire_format());
3367 EXPECT_EQ(FieldOptions::CORD, field->options().ctype());
3368 }
3369
TEST(CustomOptions,OptionTypes)3370 TEST(CustomOptions, OptionTypes) {
3371 const MessageOptions* options = nullptr;
3372
3373 constexpr int32_t kint32min = std::numeric_limits<int32_t>::min();
3374 constexpr int32_t kint32max = std::numeric_limits<int32_t>::max();
3375 constexpr uint32_t kuint32max = std::numeric_limits<uint32_t>::max();
3376 constexpr int64_t kint64min = std::numeric_limits<int64_t>::min();
3377 constexpr int64_t kint64max = std::numeric_limits<int64_t>::max();
3378 constexpr uint64_t kuint64max = std::numeric_limits<uint64_t>::max();
3379
3380 options =
3381 &protobuf_unittest::CustomOptionMinIntegerValues::descriptor()->options();
3382 EXPECT_EQ(false, options->GetExtension(protobuf_unittest::bool_opt));
3383 EXPECT_EQ(kint32min, options->GetExtension(protobuf_unittest::int32_opt));
3384 EXPECT_EQ(kint64min, options->GetExtension(protobuf_unittest::int64_opt));
3385 EXPECT_EQ(0, options->GetExtension(protobuf_unittest::uint32_opt));
3386 EXPECT_EQ(0, options->GetExtension(protobuf_unittest::uint64_opt));
3387 EXPECT_EQ(kint32min, options->GetExtension(protobuf_unittest::sint32_opt));
3388 EXPECT_EQ(kint64min, options->GetExtension(protobuf_unittest::sint64_opt));
3389 EXPECT_EQ(0, options->GetExtension(protobuf_unittest::fixed32_opt));
3390 EXPECT_EQ(0, options->GetExtension(protobuf_unittest::fixed64_opt));
3391 EXPECT_EQ(kint32min, options->GetExtension(protobuf_unittest::sfixed32_opt));
3392 EXPECT_EQ(kint64min, options->GetExtension(protobuf_unittest::sfixed64_opt));
3393
3394 options =
3395 &protobuf_unittest::CustomOptionMaxIntegerValues::descriptor()->options();
3396 EXPECT_EQ(true, options->GetExtension(protobuf_unittest::bool_opt));
3397 EXPECT_EQ(kint32max, options->GetExtension(protobuf_unittest::int32_opt));
3398 EXPECT_EQ(kint64max, options->GetExtension(protobuf_unittest::int64_opt));
3399 EXPECT_EQ(kuint32max, options->GetExtension(protobuf_unittest::uint32_opt));
3400 EXPECT_EQ(kuint64max, options->GetExtension(protobuf_unittest::uint64_opt));
3401 EXPECT_EQ(kint32max, options->GetExtension(protobuf_unittest::sint32_opt));
3402 EXPECT_EQ(kint64max, options->GetExtension(protobuf_unittest::sint64_opt));
3403 EXPECT_EQ(kuint32max, options->GetExtension(protobuf_unittest::fixed32_opt));
3404 EXPECT_EQ(kuint64max, options->GetExtension(protobuf_unittest::fixed64_opt));
3405 EXPECT_EQ(kint32max, options->GetExtension(protobuf_unittest::sfixed32_opt));
3406 EXPECT_EQ(kint64max, options->GetExtension(protobuf_unittest::sfixed64_opt));
3407
3408 options = &protobuf_unittest::CustomOptionOtherValues::descriptor()->options();
3409 EXPECT_EQ(-100, options->GetExtension(protobuf_unittest::int32_opt));
3410 EXPECT_FLOAT_EQ(12.3456789,
3411 options->GetExtension(protobuf_unittest::float_opt));
3412 EXPECT_DOUBLE_EQ(1.234567890123456789,
3413 options->GetExtension(protobuf_unittest::double_opt));
3414 EXPECT_EQ("Hello, \"World\"",
3415 options->GetExtension(protobuf_unittest::string_opt));
3416
3417 EXPECT_EQ(std::string("Hello\0World", 11),
3418 options->GetExtension(protobuf_unittest::bytes_opt));
3419
3420 EXPECT_EQ(protobuf_unittest::DummyMessageContainingEnum::TEST_OPTION_ENUM_TYPE2,
3421 options->GetExtension(protobuf_unittest::enum_opt));
3422
3423 options =
3424 &protobuf_unittest::SettingRealsFromPositiveInts::descriptor()->options();
3425 EXPECT_FLOAT_EQ(12, options->GetExtension(protobuf_unittest::float_opt));
3426 EXPECT_DOUBLE_EQ(154, options->GetExtension(protobuf_unittest::double_opt));
3427
3428 options =
3429 &protobuf_unittest::SettingRealsFromNegativeInts::descriptor()->options();
3430 EXPECT_FLOAT_EQ(-12, options->GetExtension(protobuf_unittest::float_opt));
3431 EXPECT_DOUBLE_EQ(-154, options->GetExtension(protobuf_unittest::double_opt));
3432 }
3433
TEST(CustomOptions,ComplexExtensionOptions)3434 TEST(CustomOptions, ComplexExtensionOptions) {
3435 const MessageOptions* options =
3436 &protobuf_unittest::VariousComplexOptions::descriptor()->options();
3437 EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt1).foo(), 42);
3438 EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt1)
3439 .GetExtension(protobuf_unittest::mooo),
3440 324);
3441 EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt1)
3442 .GetExtension(protobuf_unittest::corge)
3443 .moo(),
3444 876);
3445 EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2).baz(), 987);
3446 EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2)
3447 .GetExtension(protobuf_unittest::grault),
3448 654);
3449 EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2).bar().foo(),
3450 743);
3451 EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2)
3452 .bar()
3453 .GetExtension(protobuf_unittest::mooo),
3454 1999);
3455 EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2)
3456 .bar()
3457 .GetExtension(protobuf_unittest::corge)
3458 .moo(),
3459 2008);
3460 EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2)
3461 .GetExtension(protobuf_unittest::garply)
3462 .foo(),
3463 741);
3464 EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2)
3465 .GetExtension(protobuf_unittest::garply)
3466 .GetExtension(protobuf_unittest::mooo),
3467 1998);
3468 EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2)
3469 .GetExtension(protobuf_unittest::garply)
3470 .GetExtension(protobuf_unittest::corge)
3471 .moo(),
3472 2121);
3473 EXPECT_EQ(options
3474 ->GetExtension(protobuf_unittest::ComplexOptionType2::
3475 ComplexOptionType4::complex_opt4)
3476 .waldo(),
3477 1971);
3478 EXPECT_EQ(options->GetExtension(protobuf_unittest::complex_opt2).fred().waldo(),
3479 321);
3480 EXPECT_EQ(9, options->GetExtension(protobuf_unittest::complex_opt3).moo());
3481 EXPECT_EQ(22, options->GetExtension(protobuf_unittest::complex_opt3)
3482 .complexoptiontype5()
3483 .plugh());
3484 EXPECT_EQ(24, options->GetExtension(protobuf_unittest::complexopt6).xyzzy());
3485 }
3486
TEST(CustomOptions,OptionsFromOtherFile)3487 TEST(CustomOptions, OptionsFromOtherFile) {
3488 // Test that to use a custom option, we only need to import the file
3489 // defining the option; we do not also have to import descriptor.proto.
3490 DescriptorPool pool;
3491 {
3492 FileDescriptorProto file_proto;
3493 FileDescriptorProto::descriptor()->file()->CopyTo(&file_proto);
3494 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
3495 }
3496 {
3497 // We have to import the Any dependency.
3498 FileDescriptorProto any_proto;
3499 google::protobuf::Any::descriptor()->file()->CopyTo(&any_proto);
3500 ASSERT_TRUE(pool.BuildFile(any_proto) != nullptr);
3501 }
3502 FileDescriptorProto file_proto;
3503 protobuf_unittest::TestMessageWithCustomOptions::descriptor()->file()->CopyTo(
3504 &file_proto);
3505 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
3506
3507 ASSERT_TRUE(TextFormat::ParseFromString(
3508 "name: \"custom_options_import.proto\" "
3509 "package: \"protobuf_unittest\" "
3510 "dependency: \"google/protobuf/unittest_custom_options.proto\" "
3511 "options { "
3512 " uninterpreted_option { "
3513 " name { "
3514 " name_part: \"file_opt1\" "
3515 " is_extension: true "
3516 " } "
3517 " positive_int_value: 1234 "
3518 " } "
3519 // Test a non-extension option too. (At one point this failed due to a
3520 // bug.)
3521 " uninterpreted_option { "
3522 " name { "
3523 " name_part: \"java_package\" "
3524 " is_extension: false "
3525 " } "
3526 " string_value: \"foo\" "
3527 " } "
3528 // Test that enum-typed options still work too. (At one point this also
3529 // failed due to a bug.)
3530 " uninterpreted_option { "
3531 " name { "
3532 " name_part: \"optimize_for\" "
3533 " is_extension: false "
3534 " } "
3535 " identifier_value: \"SPEED\" "
3536 " } "
3537 "}",
3538 &file_proto));
3539
3540 const FileDescriptor* file = pool.BuildFile(file_proto);
3541 ASSERT_TRUE(file != nullptr);
3542 EXPECT_EQ(1234, file->options().GetExtension(protobuf_unittest::file_opt1));
3543 EXPECT_TRUE(file->options().has_java_package());
3544 EXPECT_EQ("foo", file->options().java_package());
3545 EXPECT_TRUE(file->options().has_optimize_for());
3546 EXPECT_EQ(FileOptions::SPEED, file->options().optimize_for());
3547 }
3548
TEST(CustomOptions,MessageOptionThreeFieldsSet)3549 TEST(CustomOptions, MessageOptionThreeFieldsSet) {
3550 // This tests a bug which previously existed in custom options parsing. The
3551 // bug occurred when you defined a custom option with message type and then
3552 // set three fields of that option on a single definition (see the example
3553 // below). The bug is a bit hard to explain, so check the change history if
3554 // you want to know more.
3555 DescriptorPool pool;
3556
3557 {
3558 FileDescriptorProto file_proto;
3559 FileDescriptorProto::descriptor()->file()->CopyTo(&file_proto);
3560 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
3561 }
3562 {
3563 FileDescriptorProto any_proto;
3564 google::protobuf::Any::descriptor()->file()->CopyTo(&any_proto);
3565 ASSERT_TRUE(pool.BuildFile(any_proto) != nullptr);
3566 }
3567 FileDescriptorProto file_proto;
3568 protobuf_unittest::TestMessageWithCustomOptions::descriptor()->file()->CopyTo(
3569 &file_proto);
3570 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
3571
3572 // The following represents the definition:
3573 //
3574 // import "google/protobuf/unittest_custom_options.proto"
3575 // package protobuf_unittest;
3576 // message Foo {
3577 // option (complex_opt1).foo = 1234;
3578 // option (complex_opt1).foo2 = 1234;
3579 // option (complex_opt1).foo3 = 1234;
3580 // }
3581 ASSERT_TRUE(TextFormat::ParseFromString(
3582 "name: \"custom_options_import.proto\" "
3583 "package: \"protobuf_unittest\" "
3584 "dependency: \"google/protobuf/unittest_custom_options.proto\" "
3585 "message_type { "
3586 " name: \"Foo\" "
3587 " options { "
3588 " uninterpreted_option { "
3589 " name { "
3590 " name_part: \"complex_opt1\" "
3591 " is_extension: true "
3592 " } "
3593 " name { "
3594 " name_part: \"foo\" "
3595 " is_extension: false "
3596 " } "
3597 " positive_int_value: 1234 "
3598 " } "
3599 " uninterpreted_option { "
3600 " name { "
3601 " name_part: \"complex_opt1\" "
3602 " is_extension: true "
3603 " } "
3604 " name { "
3605 " name_part: \"foo2\" "
3606 " is_extension: false "
3607 " } "
3608 " positive_int_value: 1234 "
3609 " } "
3610 " uninterpreted_option { "
3611 " name { "
3612 " name_part: \"complex_opt1\" "
3613 " is_extension: true "
3614 " } "
3615 " name { "
3616 " name_part: \"foo3\" "
3617 " is_extension: false "
3618 " } "
3619 " positive_int_value: 1234 "
3620 " } "
3621 " } "
3622 "}",
3623 &file_proto));
3624
3625 const FileDescriptor* file = pool.BuildFile(file_proto);
3626 ASSERT_TRUE(file != nullptr);
3627 ASSERT_EQ(1, file->message_type_count());
3628
3629 const MessageOptions& options = file->message_type(0)->options();
3630 EXPECT_EQ(1234, options.GetExtension(protobuf_unittest::complex_opt1).foo());
3631 }
3632
TEST(CustomOptions,MessageOptionRepeatedLeafFieldSet)3633 TEST(CustomOptions, MessageOptionRepeatedLeafFieldSet) {
3634 // This test verifies that repeated fields in custom options can be
3635 // given multiple values by repeating the option with a different value.
3636 // This test checks repeated leaf values. Each repeated custom value
3637 // appears in a different uninterpreted_option, which will be concatenated
3638 // when they are merged into the final option value.
3639 DescriptorPool pool;
3640
3641 {
3642 FileDescriptorProto file_proto;
3643 FileDescriptorProto::descriptor()->file()->CopyTo(&file_proto);
3644 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
3645 }
3646 {
3647 FileDescriptorProto any_proto;
3648 google::protobuf::Any::descriptor()->file()->CopyTo(&any_proto);
3649 ASSERT_TRUE(pool.BuildFile(any_proto) != nullptr);
3650 }
3651 FileDescriptorProto file_proto;
3652 protobuf_unittest::TestMessageWithCustomOptions::descriptor()->file()->CopyTo(
3653 &file_proto);
3654 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
3655
3656 // The following represents the definition:
3657 //
3658 // import "google/protobuf/unittest_custom_options.proto"
3659 // package protobuf_unittest;
3660 // message Foo {
3661 // option (complex_opt1).foo4 = 12;
3662 // option (complex_opt1).foo4 = 34;
3663 // option (complex_opt1).foo4 = 56;
3664 // }
3665 ASSERT_TRUE(TextFormat::ParseFromString(
3666 "name: \"custom_options_import.proto\" "
3667 "package: \"protobuf_unittest\" "
3668 "dependency: \"google/protobuf/unittest_custom_options.proto\" "
3669 "message_type { "
3670 " name: \"Foo\" "
3671 " options { "
3672 " uninterpreted_option { "
3673 " name { "
3674 " name_part: \"complex_opt1\" "
3675 " is_extension: true "
3676 " } "
3677 " name { "
3678 " name_part: \"foo4\" "
3679 " is_extension: false "
3680 " } "
3681 " positive_int_value: 12 "
3682 " } "
3683 " uninterpreted_option { "
3684 " name { "
3685 " name_part: \"complex_opt1\" "
3686 " is_extension: true "
3687 " } "
3688 " name { "
3689 " name_part: \"foo4\" "
3690 " is_extension: false "
3691 " } "
3692 " positive_int_value: 34 "
3693 " } "
3694 " uninterpreted_option { "
3695 " name { "
3696 " name_part: \"complex_opt1\" "
3697 " is_extension: true "
3698 " } "
3699 " name { "
3700 " name_part: \"foo4\" "
3701 " is_extension: false "
3702 " } "
3703 " positive_int_value: 56 "
3704 " } "
3705 " } "
3706 "}",
3707 &file_proto));
3708
3709 const FileDescriptor* file = pool.BuildFile(file_proto);
3710 ASSERT_TRUE(file != nullptr);
3711 ASSERT_EQ(1, file->message_type_count());
3712
3713 const MessageOptions& options = file->message_type(0)->options();
3714 EXPECT_EQ(3, options.GetExtension(protobuf_unittest::complex_opt1).foo4_size());
3715 EXPECT_EQ(12, options.GetExtension(protobuf_unittest::complex_opt1).foo4(0));
3716 EXPECT_EQ(34, options.GetExtension(protobuf_unittest::complex_opt1).foo4(1));
3717 EXPECT_EQ(56, options.GetExtension(protobuf_unittest::complex_opt1).foo4(2));
3718 }
3719
TEST(CustomOptions,MessageOptionRepeatedMsgFieldSet)3720 TEST(CustomOptions, MessageOptionRepeatedMsgFieldSet) {
3721 // This test verifies that repeated fields in custom options can be
3722 // given multiple values by repeating the option with a different value.
3723 // This test checks repeated message values. Each repeated custom value
3724 // appears in a different uninterpreted_option, which will be concatenated
3725 // when they are merged into the final option value.
3726 DescriptorPool pool;
3727
3728 {
3729 FileDescriptorProto file_proto;
3730 FileDescriptorProto::descriptor()->file()->CopyTo(&file_proto);
3731 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
3732 }
3733 {
3734 FileDescriptorProto any_proto;
3735 google::protobuf::Any::descriptor()->file()->CopyTo(&any_proto);
3736 ASSERT_TRUE(pool.BuildFile(any_proto) != nullptr);
3737 }
3738 FileDescriptorProto file_proto;
3739 protobuf_unittest::TestMessageWithCustomOptions::descriptor()->file()->CopyTo(
3740 &file_proto);
3741 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
3742
3743 // The following represents the definition:
3744 //
3745 // import "google/protobuf/unittest_custom_options.proto"
3746 // package protobuf_unittest;
3747 // message Foo {
3748 // option (complex_opt2).barney = {waldo: 1};
3749 // option (complex_opt2).barney = {waldo: 10};
3750 // option (complex_opt2).barney = {waldo: 100};
3751 // }
3752 ASSERT_TRUE(TextFormat::ParseFromString(
3753 "name: \"custom_options_import.proto\" "
3754 "package: \"protobuf_unittest\" "
3755 "dependency: \"google/protobuf/unittest_custom_options.proto\" "
3756 "message_type { "
3757 " name: \"Foo\" "
3758 " options { "
3759 " uninterpreted_option { "
3760 " name { "
3761 " name_part: \"complex_opt2\" "
3762 " is_extension: true "
3763 " } "
3764 " name { "
3765 " name_part: \"barney\" "
3766 " is_extension: false "
3767 " } "
3768 " aggregate_value: \"waldo: 1\" "
3769 " } "
3770 " uninterpreted_option { "
3771 " name { "
3772 " name_part: \"complex_opt2\" "
3773 " is_extension: true "
3774 " } "
3775 " name { "
3776 " name_part: \"barney\" "
3777 " is_extension: false "
3778 " } "
3779 " aggregate_value: \"waldo: 10\" "
3780 " } "
3781 " uninterpreted_option { "
3782 " name { "
3783 " name_part: \"complex_opt2\" "
3784 " is_extension: true "
3785 " } "
3786 " name { "
3787 " name_part: \"barney\" "
3788 " is_extension: false "
3789 " } "
3790 " aggregate_value: \"waldo: 100\" "
3791 " } "
3792 " } "
3793 "}",
3794 &file_proto));
3795
3796 const FileDescriptor* file = pool.BuildFile(file_proto);
3797 ASSERT_TRUE(file != nullptr);
3798 ASSERT_EQ(1, file->message_type_count());
3799
3800 const MessageOptions& options = file->message_type(0)->options();
3801 EXPECT_EQ(3,
3802 options.GetExtension(protobuf_unittest::complex_opt2).barney_size());
3803 EXPECT_EQ(
3804 1, options.GetExtension(protobuf_unittest::complex_opt2).barney(0).waldo());
3805 EXPECT_EQ(
3806 10,
3807 options.GetExtension(protobuf_unittest::complex_opt2).barney(1).waldo());
3808 EXPECT_EQ(
3809 100,
3810 options.GetExtension(protobuf_unittest::complex_opt2).barney(2).waldo());
3811 }
3812
3813 // Check that aggregate options were parsed and saved correctly in
3814 // the appropriate descriptors.
TEST(CustomOptions,AggregateOptions)3815 TEST(CustomOptions, AggregateOptions) {
3816 const Descriptor* msg = protobuf_unittest::AggregateMessage::descriptor();
3817 const FileDescriptor* file = msg->file();
3818 const FieldDescriptor* field = msg->FindFieldByName("fieldname");
3819 const EnumDescriptor* enumd = file->FindEnumTypeByName("AggregateEnum");
3820 const EnumValueDescriptor* enumv = enumd->FindValueByName("VALUE");
3821 const ServiceDescriptor* service =
3822 file->FindServiceByName("AggregateService");
3823 const MethodDescriptor* method = service->FindMethodByName("Method");
3824
3825 // Tests for the different types of data embedded in fileopt
3826 const protobuf_unittest::Aggregate& file_options =
3827 file->options().GetExtension(protobuf_unittest::fileopt);
3828 EXPECT_EQ(100, file_options.i());
3829 EXPECT_EQ("FileAnnotation", file_options.s());
3830 EXPECT_EQ("NestedFileAnnotation", file_options.sub().s());
3831 EXPECT_EQ("FileExtensionAnnotation",
3832 file_options.file().GetExtension(protobuf_unittest::fileopt).s());
3833 EXPECT_EQ("EmbeddedMessageSetElement",
3834 file_options.mset()
3835 .GetExtension(protobuf_unittest::AggregateMessageSetElement ::
3836 message_set_extension)
3837 .s());
3838
3839 protobuf_unittest::AggregateMessageSetElement any_payload;
3840 ASSERT_TRUE(file_options.any().UnpackTo(&any_payload));
3841 EXPECT_EQ("EmbeddedMessageSetElement", any_payload.s());
3842
3843 // Simple tests for all the other types of annotations
3844 EXPECT_EQ("MessageAnnotation",
3845 msg->options().GetExtension(protobuf_unittest::msgopt).s());
3846 EXPECT_EQ("FieldAnnotation",
3847 field->options().GetExtension(protobuf_unittest::fieldopt).s());
3848 EXPECT_EQ("EnumAnnotation",
3849 enumd->options().GetExtension(protobuf_unittest::enumopt).s());
3850 EXPECT_EQ("EnumValueAnnotation",
3851 enumv->options().GetExtension(protobuf_unittest::enumvalopt).s());
3852 EXPECT_EQ("ServiceAnnotation",
3853 service->options().GetExtension(protobuf_unittest::serviceopt).s());
3854 EXPECT_EQ("MethodAnnotation",
3855 method->options().GetExtension(protobuf_unittest::methodopt).s());
3856 }
3857
TEST(CustomOptions,UnusedImportError)3858 TEST(CustomOptions, UnusedImportError) {
3859 DescriptorPool pool;
3860
3861 {
3862 FileDescriptorProto file_proto;
3863 FileDescriptorProto::descriptor()->file()->CopyTo(&file_proto);
3864 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
3865 }
3866 {
3867 FileDescriptorProto any_proto;
3868 google::protobuf::Any::descriptor()->file()->CopyTo(&any_proto);
3869 ASSERT_TRUE(pool.BuildFile(any_proto) != nullptr);
3870 }
3871 FileDescriptorProto file_proto;
3872 protobuf_unittest::TestMessageWithCustomOptions::descriptor()->file()->CopyTo(
3873 &file_proto);
3874 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
3875
3876 pool.AddDirectInputFile("custom_options_import.proto", true);
3877 ASSERT_TRUE(TextFormat::ParseFromString(
3878 "name: \"custom_options_import.proto\" "
3879 "package: \"protobuf_unittest\" "
3880 "dependency: \"google/protobuf/unittest_custom_options.proto\" ",
3881 &file_proto));
3882
3883 MockErrorCollector error_collector;
3884 EXPECT_FALSE(pool.BuildFileCollectingErrors(file_proto, &error_collector));
3885 EXPECT_EQ(
3886 "custom_options_import.proto: "
3887 "google/protobuf/unittest_custom_options.proto: IMPORT: Import "
3888 "google/protobuf/unittest_custom_options.proto is unused.\n",
3889 error_collector.text_);
3890 }
3891
3892 // Verifies that proto files can correctly be parsed, even if the
3893 // custom options defined in the file are incompatible with those
3894 // compiled in the binary. See http://b/19276250.
TEST(CustomOptions,OptionsWithIncompatibleDescriptors)3895 TEST(CustomOptions, OptionsWithIncompatibleDescriptors) {
3896 DescriptorPool pool;
3897
3898 FileDescriptorProto file_proto;
3899 MessageOptions::descriptor()->file()->CopyTo(&file_proto);
3900 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
3901
3902 // Create a new file descriptor proto containing a subset of the
3903 // messages defined in google/protobuf/unittest_custom_options.proto.
3904 file_proto.Clear();
3905 file_proto.set_name("unittest_custom_options.proto");
3906 file_proto.set_package("protobuf_unittest");
3907 file_proto.add_dependency("google/protobuf/descriptor.proto");
3908
3909 // Add the "required_enum_opt" extension.
3910 FieldDescriptorProto* extension = file_proto.add_extension();
3911 protobuf_unittest::OldOptionType::descriptor()
3912 ->file()
3913 ->FindExtensionByName("required_enum_opt")
3914 ->CopyTo(extension);
3915
3916 // Add a test message that uses the "required_enum_opt" option.
3917 DescriptorProto* test_message_type = file_proto.add_message_type();
3918 protobuf_unittest::TestMessageWithRequiredEnumOption::descriptor()->CopyTo(
3919 test_message_type);
3920
3921 // Instruct the extension to use NewOptionType instead of
3922 // OldOptionType, and add the descriptor of NewOptionType.
3923 extension->set_type_name(".protobuf_unittest.NewOptionType");
3924 DescriptorProto* new_option_type = file_proto.add_message_type();
3925 protobuf_unittest::NewOptionType::descriptor()->CopyTo(new_option_type);
3926
3927 // Replace the value of the "required_enum_opt" option used in the
3928 // test message with an enum value that only exists in NewOptionType.
3929 ASSERT_TRUE(
3930 TextFormat::ParseFromString("uninterpreted_option { "
3931 " name { "
3932 " name_part: 'required_enum_opt' "
3933 " is_extension: true "
3934 " } "
3935 " aggregate_value: 'value: NEW_VALUE'"
3936 "}",
3937 test_message_type->mutable_options()));
3938
3939 // Adding the file descriptor to the pool should fail.
3940 EXPECT_TRUE(pool.BuildFile(file_proto) == nullptr);
3941 }
3942
3943 // Test that FileDescriptor::DebugString() formats custom options correctly.
TEST(CustomOptions,DebugString)3944 TEST(CustomOptions, DebugString) {
3945 DescriptorPool pool;
3946
3947 FileDescriptorProto file_proto;
3948 MessageOptions::descriptor()->file()->CopyTo(&file_proto);
3949 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
3950
3951 // Add "foo.proto":
3952 // import "google/protobuf/descriptor.proto";
3953 // package "protobuf_unittest";
3954 // option (protobuf_unittest.cc_option1) = 1;
3955 // option (protobuf_unittest.cc_option2) = 2;
3956 // extend google.protobuf.FieldOptions {
3957 // optional int32 cc_option1 = 7736974;
3958 // optional int32 cc_option2 = 7736975;
3959 // }
3960 ASSERT_TRUE(TextFormat::ParseFromString(
3961 "name: \"foo.proto\" "
3962 "package: \"protobuf_unittest\" "
3963 "dependency: \"google/protobuf/descriptor.proto\" "
3964 "options { "
3965 " uninterpreted_option { "
3966 " name { "
3967 " name_part: \"protobuf_unittest.cc_option1\" "
3968 " is_extension: true "
3969 " } "
3970 " positive_int_value: 1 "
3971 " } "
3972 " uninterpreted_option { "
3973 " name { "
3974 " name_part: \"protobuf_unittest.cc_option2\" "
3975 " is_extension: true "
3976 " } "
3977 " positive_int_value: 2 "
3978 " } "
3979 "} "
3980 "extension { "
3981 " name: \"cc_option1\" "
3982 " extendee: \".google.protobuf.FileOptions\" "
3983 // This field number is intentionally chosen to be the same as
3984 // (.fileopt1) defined in unittest_custom_options.proto (linked
3985 // in this test binary). This is to test whether we are messing
3986 // generated pool with custom descriptor pools when dealing with
3987 // custom options.
3988 " number: 7736974 "
3989 " label: LABEL_OPTIONAL "
3990 " type: TYPE_INT32 "
3991 "}"
3992 "extension { "
3993 " name: \"cc_option2\" "
3994 " extendee: \".google.protobuf.FileOptions\" "
3995 " number: 7736975 "
3996 " label: LABEL_OPTIONAL "
3997 " type: TYPE_INT32 "
3998 "}",
3999 &file_proto));
4000 const FileDescriptor* descriptor = pool.BuildFile(file_proto);
4001 ASSERT_TRUE(descriptor != nullptr);
4002
4003 EXPECT_EQ(2, descriptor->extension_count());
4004
4005 ASSERT_EQ(
4006 "syntax = \"proto2\";\n"
4007 "\n"
4008 "import \"google/protobuf/descriptor.proto\";\n"
4009 "package protobuf_unittest;\n"
4010 "\n"
4011 "option (.protobuf_unittest.cc_option1) = 1;\n"
4012 "option (.protobuf_unittest.cc_option2) = 2;\n"
4013 "\n"
4014 "extend .google.protobuf.FileOptions {\n"
4015 " optional int32 cc_option1 = 7736974;\n"
4016 " optional int32 cc_option2 = 7736975;\n"
4017 "}\n"
4018 "\n",
4019 descriptor->DebugString());
4020 }
4021
4022 // ===================================================================
4023
4024
4025 class ValidationErrorTest : public testing::Test {
4026 protected:
SetUp()4027 void SetUp() override {
4028 // Enable extension declaration enforcement since most test cases want to
4029 // exercise the full validation.
4030 pool_.EnforceExtensionDeclarations(true);
4031 }
4032 // Parse file_text as a FileDescriptorProto in text format and add it
4033 // to the DescriptorPool. Expect no errors.
BuildFile(absl::string_view file_text)4034 const FileDescriptor* BuildFile(absl::string_view file_text) {
4035 FileDescriptorProto file_proto;
4036 EXPECT_TRUE(TextFormat::ParseFromString(file_text, &file_proto));
4037 return ABSL_DIE_IF_NULL(pool_.BuildFile(file_proto));
4038 }
4039
ParseFile(absl::string_view file_name,absl::string_view file_text)4040 FileDescriptorProto ParseFile(absl::string_view file_name,
4041 absl::string_view file_text) {
4042 io::ArrayInputStream input_stream(file_text.data(), file_text.size());
4043 SimpleErrorCollector error_collector;
4044 io::Tokenizer tokenizer(&input_stream, &error_collector);
4045 compiler::Parser parser;
4046 parser.RecordErrorsTo(&error_collector);
4047 FileDescriptorProto proto;
4048 ABSL_CHECK(parser.Parse(&tokenizer, &proto))
4049 << error_collector.last_error() << "\n"
4050 << file_text;
4051 ABSL_CHECK_EQ("", error_collector.last_error());
4052 proto.set_name(file_name);
4053 return proto;
4054 }
4055
ParseAndBuildFile(absl::string_view file_name,absl::string_view file_text)4056 const FileDescriptor* ParseAndBuildFile(absl::string_view file_name,
4057 absl::string_view file_text) {
4058 return pool_.BuildFile(ParseFile(file_name, file_text));
4059 }
4060
4061
4062 // Add file_proto to the DescriptorPool. Expect errors to be produced which
4063 // match the given error text.
BuildFileWithErrors(const FileDescriptorProto & file_proto,const std::string & expected_errors)4064 void BuildFileWithErrors(const FileDescriptorProto& file_proto,
4065 const std::string& expected_errors) {
4066 MockErrorCollector error_collector;
4067 EXPECT_TRUE(pool_.BuildFileCollectingErrors(file_proto, &error_collector) ==
4068 nullptr);
4069 EXPECT_EQ(expected_errors, error_collector.text_);
4070 }
4071
4072 // Parse file_text as a FileDescriptorProto in text format and add it
4073 // to the DescriptorPool. Expect errors to be produced which match the
4074 // given error text.
BuildFileWithErrors(const std::string & file_text,const std::string & expected_errors)4075 void BuildFileWithErrors(const std::string& file_text,
4076 const std::string& expected_errors) {
4077 FileDescriptorProto file_proto;
4078 ASSERT_TRUE(TextFormat::ParseFromString(file_text, &file_proto));
4079 BuildFileWithErrors(file_proto, expected_errors);
4080 }
4081
4082 // Parse a proto file and build it. Expect errors to be produced which match
4083 // the given error text.
ParseAndBuildFileWithErrors(absl::string_view file_name,absl::string_view file_text,absl::string_view expected_errors)4084 void ParseAndBuildFileWithErrors(absl::string_view file_name,
4085 absl::string_view file_text,
4086 absl::string_view expected_errors) {
4087 MockErrorCollector error_collector;
4088 EXPECT_TRUE(pool_.BuildFileCollectingErrors(ParseFile(file_name, file_text),
4089 &error_collector) == nullptr);
4090 EXPECT_EQ(expected_errors, error_collector.text_);
4091 }
4092
4093 // Parse file_text as a FileDescriptorProto in text format and add it
4094 // to the DescriptorPool. Expect errors to be produced which match the
4095 // given warning text.
BuildFileWithWarnings(const std::string & file_text,const std::string & expected_warnings)4096 void BuildFileWithWarnings(const std::string& file_text,
4097 const std::string& expected_warnings) {
4098 FileDescriptorProto file_proto;
4099 ASSERT_TRUE(TextFormat::ParseFromString(file_text, &file_proto));
4100
4101 MockErrorCollector error_collector;
4102 EXPECT_TRUE(pool_.BuildFileCollectingErrors(file_proto, &error_collector));
4103 EXPECT_EQ(expected_warnings, error_collector.warning_text_);
4104 }
4105
4106 // Builds some already-parsed file in our test pool.
BuildFileInTestPool(const FileDescriptor * file)4107 void BuildFileInTestPool(const FileDescriptor* file) {
4108 FileDescriptorProto file_proto;
4109 file->CopyTo(&file_proto);
4110 ASSERT_TRUE(pool_.BuildFile(file_proto) != nullptr);
4111 }
4112
4113 // Build descriptor.proto in our test pool. This allows us to extend it in
4114 // the test pool, so we can test custom options.
BuildDescriptorMessagesInTestPool()4115 void BuildDescriptorMessagesInTestPool() {
4116 BuildFileInTestPool(DescriptorProto::descriptor()->file());
4117 }
4118
BuildDescriptorMessagesInTestPoolWithErrors(absl::string_view expected_errors)4119 void BuildDescriptorMessagesInTestPoolWithErrors(
4120 absl::string_view expected_errors) {
4121 FileDescriptorProto file_proto;
4122 DescriptorProto::descriptor()->file()->CopyTo(&file_proto);
4123 MockErrorCollector error_collector;
4124 EXPECT_TRUE(pool_.BuildFileCollectingErrors(file_proto, &error_collector) ==
4125 nullptr);
4126 EXPECT_EQ(error_collector.text_, expected_errors);
4127 }
4128
4129 DescriptorPool pool_;
4130 };
4131
TEST_F(ValidationErrorTest,AlreadyDefined)4132 TEST_F(ValidationErrorTest, AlreadyDefined) {
4133 BuildFileWithErrors(
4134 "name: \"foo.proto\" "
4135 "message_type { name: \"Foo\" }"
4136 "message_type { name: \"Foo\" }",
4137
4138 "foo.proto: Foo: NAME: \"Foo\" is already defined.\n");
4139 }
4140
TEST_F(ValidationErrorTest,AlreadyDefinedInPackage)4141 TEST_F(ValidationErrorTest, AlreadyDefinedInPackage) {
4142 BuildFileWithErrors(
4143 "name: \"foo.proto\" "
4144 "package: \"foo.bar\" "
4145 "message_type { name: \"Foo\" }"
4146 "message_type { name: \"Foo\" }",
4147
4148 "foo.proto: foo.bar.Foo: NAME: \"Foo\" is already defined in "
4149 "\"foo.bar\".\n");
4150 }
4151
TEST_F(ValidationErrorTest,AlreadyDefinedInOtherFile)4152 TEST_F(ValidationErrorTest, AlreadyDefinedInOtherFile) {
4153 BuildFile(
4154 "name: \"foo.proto\" "
4155 "message_type { name: \"Foo\" }");
4156
4157 BuildFileWithErrors(
4158 "name: \"bar.proto\" "
4159 "message_type { name: \"Foo\" }",
4160
4161 "bar.proto: Foo: NAME: \"Foo\" is already defined in file "
4162 "\"foo.proto\".\n");
4163 }
4164
TEST_F(ValidationErrorTest,PackageAlreadyDefined)4165 TEST_F(ValidationErrorTest, PackageAlreadyDefined) {
4166 BuildFile(
4167 "name: \"foo.proto\" "
4168 "message_type { name: \"foo\" }");
4169 BuildFileWithErrors(
4170 "name: \"bar.proto\" "
4171 "package: \"foo.bar\"",
4172
4173 "bar.proto: foo: NAME: \"foo\" is already defined (as something other "
4174 "than a package) in file \"foo.proto\".\n");
4175 }
4176
TEST_F(ValidationErrorTest,EnumValueAlreadyDefinedInParent)4177 TEST_F(ValidationErrorTest, EnumValueAlreadyDefinedInParent) {
4178 BuildFileWithErrors(
4179 "name: \"foo.proto\" "
4180 "enum_type { name: \"Foo\" value { name: \"FOO\" number: 1 } } "
4181 "enum_type { name: \"Bar\" value { name: \"FOO\" number: 1 } } ",
4182
4183 "foo.proto: FOO: NAME: \"FOO\" is already defined.\n"
4184 "foo.proto: FOO: NAME: Note that enum values use C++ scoping rules, "
4185 "meaning that enum values are siblings of their type, not children of "
4186 "it. Therefore, \"FOO\" must be unique within the global scope, not "
4187 "just within \"Bar\".\n");
4188 }
4189
TEST_F(ValidationErrorTest,EnumValueAlreadyDefinedInParentNonGlobal)4190 TEST_F(ValidationErrorTest, EnumValueAlreadyDefinedInParentNonGlobal) {
4191 BuildFileWithErrors(
4192 "name: \"foo.proto\" "
4193 "package: \"pkg\" "
4194 "enum_type { name: \"Foo\" value { name: \"FOO\" number: 1 } } "
4195 "enum_type { name: \"Bar\" value { name: \"FOO\" number: 1 } } ",
4196
4197 "foo.proto: pkg.FOO: NAME: \"FOO\" is already defined in \"pkg\".\n"
4198 "foo.proto: pkg.FOO: NAME: Note that enum values use C++ scoping rules, "
4199 "meaning that enum values are siblings of their type, not children of "
4200 "it. Therefore, \"FOO\" must be unique within \"pkg\", not just within "
4201 "\"Bar\".\n");
4202 }
4203
TEST_F(ValidationErrorTest,MissingName)4204 TEST_F(ValidationErrorTest, MissingName) {
4205 BuildFileWithErrors(
4206 "name: \"foo.proto\" "
4207 "message_type { }",
4208
4209 "foo.proto: : NAME: Missing name.\n");
4210 }
4211
TEST_F(ValidationErrorTest,InvalidName)4212 TEST_F(ValidationErrorTest, InvalidName) {
4213 BuildFileWithErrors(
4214 "name: \"foo.proto\" "
4215 "message_type { name: \"$\" }",
4216
4217 "foo.proto: $: NAME: \"$\" is not a valid identifier.\n");
4218 }
4219
TEST_F(ValidationErrorTest,InvalidPackageName)4220 TEST_F(ValidationErrorTest, InvalidPackageName) {
4221 BuildFileWithErrors(
4222 "name: \"foo.proto\" "
4223 "package: \"foo.$\"",
4224
4225 "foo.proto: foo.$: NAME: \"$\" is not a valid identifier.\n");
4226 }
4227
4228 // 'str' is a static C-style string that may contain '\0'
4229 #define STATIC_STR(str) std::string((str), sizeof(str) - 1)
4230
TEST_F(ValidationErrorTest,NullCharSymbolName)4231 TEST_F(ValidationErrorTest, NullCharSymbolName) {
4232 BuildFileWithErrors(
4233 "name: \"bar.proto\" "
4234 "package: \"foo\""
4235 "message_type { "
4236 " name: '\\000\\001\\013.Bar' "
4237 " field { name: \"foo\" number: 9 label:LABEL_OPTIONAL type:TYPE_INT32 "
4238 "} "
4239 "}",
4240 STATIC_STR("bar.proto: foo.\0\x1\v.Bar: NAME: \"\0\x1\v.Bar\" is not a "
4241 "valid identifier.\nbar.proto: foo.\0\x1\v.Bar.foo: NAME: "
4242 "\"foo.\0\x1\v.Bar.foo\" contains null character.\nbar.proto: "
4243 "foo.\0\x1\v.Bar: NAME: \"foo.\0\x1\v.Bar\" contains null "
4244 "character.\n"));
4245 }
4246
TEST_F(ValidationErrorTest,NullCharFileName)4247 TEST_F(ValidationErrorTest, NullCharFileName) {
4248 BuildFileWithErrors(
4249 "name: \"bar\\000\\001\\013.proto\" "
4250 "package: \"outer.foo\"",
4251 STATIC_STR("bar\0\x1\v.proto: bar\0\x1\v.proto: NAME: "
4252 "\"bar\0\x1\v.proto\" contains null character.\n"));
4253 }
4254
TEST_F(ValidationErrorTest,NullCharPackageName)4255 TEST_F(ValidationErrorTest, NullCharPackageName) {
4256 BuildFileWithErrors(
4257 "name: \"bar.proto\" "
4258 "package: \"\\000\\001\\013.\"",
4259 STATIC_STR("bar.proto: \0\x1\v.: NAME: \"\0\x1\v.\" contains null "
4260 "character.\n"));
4261 }
4262
TEST_F(ValidationErrorTest,MissingFileName)4263 TEST_F(ValidationErrorTest, MissingFileName) {
4264 BuildFileWithErrors("",
4265
4266 ": : OTHER: Missing field: FileDescriptorProto.name.\n");
4267 }
4268
TEST_F(ValidationErrorTest,DupeDependency)4269 TEST_F(ValidationErrorTest, DupeDependency) {
4270 BuildFile("name: \"foo.proto\"");
4271 BuildFileWithErrors(
4272 "name: \"bar.proto\" "
4273 "dependency: \"foo.proto\" "
4274 "dependency: \"foo.proto\" ",
4275
4276 "bar.proto: foo.proto: IMPORT: Import \"foo.proto\" was listed twice.\n");
4277 }
4278
TEST_F(ValidationErrorTest,UnknownDependency)4279 TEST_F(ValidationErrorTest, UnknownDependency) {
4280 BuildFileWithErrors(
4281 "name: \"bar.proto\" "
4282 "dependency: \"foo.proto\" ",
4283
4284 "bar.proto: foo.proto: IMPORT: Import \"foo.proto\" has not been "
4285 "loaded.\n");
4286 }
4287
TEST_F(ValidationErrorTest,InvalidPublicDependencyIndex)4288 TEST_F(ValidationErrorTest, InvalidPublicDependencyIndex) {
4289 BuildFile("name: \"foo.proto\"");
4290 BuildFileWithErrors(
4291 "name: \"bar.proto\" "
4292 "dependency: \"foo.proto\" "
4293 "public_dependency: 1",
4294 "bar.proto: bar.proto: OTHER: Invalid public dependency index.\n");
4295 }
4296
TEST_F(ValidationErrorTest,ForeignUnimportedPackageNoCrash)4297 TEST_F(ValidationErrorTest, ForeignUnimportedPackageNoCrash) {
4298 // Used to crash: If we depend on a non-existent file and then refer to a
4299 // package defined in a file that we didn't import, and that package is
4300 // nested within a parent package which this file is also in, and we don't
4301 // include that parent package in the name (i.e. we do a relative lookup)...
4302 // Yes, really.
4303 BuildFile(
4304 "name: 'foo.proto' "
4305 "package: 'outer.foo' ");
4306 BuildFileWithErrors(
4307 "name: 'bar.proto' "
4308 "dependency: 'baz.proto' "
4309 "package: 'outer.bar' "
4310 "message_type { "
4311 " name: 'Bar' "
4312 " field { name:'bar' number:1 label:LABEL_OPTIONAL type_name:'foo.Foo' }"
4313 "}",
4314
4315 "bar.proto: baz.proto: IMPORT: Import \"baz.proto\" has not been "
4316 "loaded.\n"
4317 "bar.proto: outer.bar.Bar.bar: TYPE: \"outer.foo\" seems to be defined "
4318 "in "
4319 "\"foo.proto\", which is not imported by \"bar.proto\". To use it here, "
4320 "please add the necessary import.\n");
4321 }
4322
TEST_F(ValidationErrorTest,DupeFile)4323 TEST_F(ValidationErrorTest, DupeFile) {
4324 BuildFile(
4325 "name: \"foo.proto\" "
4326 "message_type { name: \"Foo\" }");
4327 // Note: We should *not* get redundant errors about "Foo" already being
4328 // defined.
4329 BuildFileWithErrors(
4330 "name: \"foo.proto\" "
4331 "message_type { name: \"Foo\" } "
4332 // Add another type so that the files aren't identical (in which case
4333 // there would be no error).
4334 "enum_type { name: \"Bar\" }",
4335
4336 "foo.proto: foo.proto: OTHER: A file with this name is already in the "
4337 "pool.\n");
4338 }
4339
TEST_F(ValidationErrorTest,FieldInExtensionRange)4340 TEST_F(ValidationErrorTest, FieldInExtensionRange) {
4341 BuildFileWithErrors(
4342 "name: \"foo.proto\" "
4343 "message_type {"
4344 " name: \"Foo\""
4345 " field { name: \"foo\" number: 9 label:LABEL_OPTIONAL type:TYPE_INT32 "
4346 "}"
4347 " field { name: \"bar\" number: 10 label:LABEL_OPTIONAL type:TYPE_INT32 "
4348 "}"
4349 " field { name: \"baz\" number: 19 label:LABEL_OPTIONAL type:TYPE_INT32 "
4350 "}"
4351 " field { name: \"moo\" number: 20 label:LABEL_OPTIONAL type:TYPE_INT32 "
4352 "}"
4353 " extension_range { start: 10 end: 20 }"
4354 "}",
4355
4356 "foo.proto: Foo.bar: NUMBER: Extension range 10 to 19 includes field "
4357 "\"bar\" (10).\n"
4358 "foo.proto: Foo.baz: NUMBER: Extension range 10 to 19 includes field "
4359 "\"baz\" (19).\n"
4360 "foo.proto: Foo: NUMBER: Suggested field numbers for Foo: 1, 2\n");
4361 }
4362
TEST_F(ValidationErrorTest,OverlappingExtensionRanges)4363 TEST_F(ValidationErrorTest, OverlappingExtensionRanges) {
4364 BuildFileWithErrors(
4365 "name: \"foo.proto\" "
4366 "message_type {"
4367 " name: \"Foo\""
4368 " extension_range { start: 10 end: 20 }"
4369 " extension_range { start: 20 end: 30 }"
4370 " extension_range { start: 19 end: 21 }"
4371 "}",
4372
4373 "foo.proto: Foo: NUMBER: Extension range 19 to 20 overlaps with "
4374 "already-defined range 10 to 19.\n"
4375 "foo.proto: Foo: NUMBER: Extension range 19 to 20 overlaps with "
4376 "already-defined range 20 to 29.\n");
4377 }
4378
TEST_F(ValidationErrorTest,ReservedFieldError)4379 TEST_F(ValidationErrorTest, ReservedFieldError) {
4380 BuildFileWithErrors(
4381 "name: \"foo.proto\" "
4382 "message_type {"
4383 " name: \"Foo\""
4384 " field { name: \"foo\" number: 15 label:LABEL_OPTIONAL type:TYPE_INT32 "
4385 "}"
4386 " reserved_range { start: 10 end: 20 }"
4387 "}",
4388
4389 "foo.proto: Foo.foo: NUMBER: Field \"foo\" uses reserved number 15.\n"
4390 "foo.proto: Foo: NUMBER: Suggested field numbers for Foo: 1\n");
4391 }
4392
TEST_F(ValidationErrorTest,ReservedExtensionRangeError)4393 TEST_F(ValidationErrorTest, ReservedExtensionRangeError) {
4394 BuildFileWithErrors(
4395 "name: \"foo.proto\" "
4396 "message_type {"
4397 " name: \"Foo\""
4398 " extension_range { start: 10 end: 20 }"
4399 " reserved_range { start: 5 end: 15 }"
4400 "}",
4401
4402 "foo.proto: Foo: NUMBER: Extension range 10 to 19"
4403 " overlaps with reserved range 5 to 14.\n");
4404 }
4405
TEST_F(ValidationErrorTest,ReservedExtensionRangeAdjacent)4406 TEST_F(ValidationErrorTest, ReservedExtensionRangeAdjacent) {
4407 BuildFile(
4408 "name: \"foo.proto\" "
4409 "message_type {"
4410 " name: \"Foo\""
4411 " extension_range { start: 10 end: 20 }"
4412 " reserved_range { start: 5 end: 10 }"
4413 "}");
4414 }
4415
TEST_F(ValidationErrorTest,ReservedRangeOverlap)4416 TEST_F(ValidationErrorTest, ReservedRangeOverlap) {
4417 BuildFileWithErrors(
4418 "name: \"foo.proto\" "
4419 "message_type {"
4420 " name: \"Foo\""
4421 " reserved_range { start: 10 end: 20 }"
4422 " reserved_range { start: 5 end: 15 }"
4423 "}",
4424
4425 "foo.proto: Foo: NUMBER: Reserved range 5 to 14"
4426 " overlaps with already-defined range 10 to 19.\n");
4427 }
4428
TEST_F(ValidationErrorTest,ReservedNameError)4429 TEST_F(ValidationErrorTest, ReservedNameError) {
4430 BuildFileWithErrors(
4431 "name: \"foo.proto\" "
4432 "message_type {"
4433 " name: \"Foo\""
4434 " field { name: \"foo\" number: 15 label:LABEL_OPTIONAL type:TYPE_INT32 "
4435 "}"
4436 " field { name: \"bar\" number: 16 label:LABEL_OPTIONAL type:TYPE_INT32 "
4437 "}"
4438 " field { name: \"baz\" number: 17 label:LABEL_OPTIONAL type:TYPE_INT32 "
4439 "}"
4440 " reserved_name: \"foo\""
4441 " reserved_name: \"bar\""
4442 "}",
4443
4444 "foo.proto: Foo.foo: NAME: Field name \"foo\" is reserved.\n"
4445 "foo.proto: Foo.bar: NAME: Field name \"bar\" is reserved.\n");
4446 }
4447
TEST_F(ValidationErrorTest,ReservedNameRedundant)4448 TEST_F(ValidationErrorTest, ReservedNameRedundant) {
4449 BuildFileWithErrors(
4450 "name: \"foo.proto\" "
4451 "message_type {"
4452 " name: \"Foo\""
4453 " reserved_name: \"foo\""
4454 " reserved_name: \"foo\""
4455 "}",
4456
4457 "foo.proto: foo: NAME: Field name \"foo\" is reserved multiple times.\n");
4458 }
4459
TEST_F(ValidationErrorTest,ReservedFieldsDebugString)4460 TEST_F(ValidationErrorTest, ReservedFieldsDebugString) {
4461 const FileDescriptor* file = BuildFile(
4462 "name: \"foo.proto\" "
4463 "message_type {"
4464 " name: \"Foo\""
4465 " reserved_name: \"foo\""
4466 " reserved_name: \"bar\""
4467 " reserved_range { start: 5 end: 6 }"
4468 " reserved_range { start: 10 end: 20 }"
4469 "}");
4470
4471 ASSERT_EQ(
4472 "syntax = \"proto2\";\n\n"
4473 "message Foo {\n"
4474 " reserved 5, 10 to 19;\n"
4475 " reserved \"foo\", \"bar\";\n"
4476 "}\n\n",
4477 file->DebugString());
4478 }
4479
TEST_F(ValidationErrorTest,ReservedFieldsDebugString2023)4480 TEST_F(ValidationErrorTest, ReservedFieldsDebugString2023) {
4481 const FileDescriptor* file = BuildFile(R"pb(
4482 syntax: "editions"
4483 edition: EDITION_2023
4484 name: "foo.proto"
4485 message_type {
4486 name: "Foo"
4487 reserved_name: "foo"
4488 reserved_name: "bar"
4489 reserved_range { start: 5 end: 6 }
4490 reserved_range { start: 10 end: 20 }
4491 })pb");
4492
4493 ASSERT_EQ(
4494 "edition = \"2023\";\n\n"
4495 "message Foo {\n"
4496 " reserved 5, 10 to 19;\n"
4497 " reserved foo, bar;\n"
4498 "}\n\n",
4499 file->DebugString());
4500 }
4501
TEST_F(ValidationErrorTest,DebugStringReservedRangeMax)4502 TEST_F(ValidationErrorTest, DebugStringReservedRangeMax) {
4503 const FileDescriptor* file = BuildFile(absl::Substitute(
4504 "name: \"foo.proto\" "
4505 "enum_type { "
4506 " name: \"Bar\""
4507 " value { name:\"BAR\" number:1 }"
4508 " reserved_range { start: 5 end: $0 }"
4509 "}"
4510 "message_type {"
4511 " name: \"Foo\""
4512 " reserved_range { start: 5 end: $1 }"
4513 "}",
4514 std::numeric_limits<int>::max(), FieldDescriptor::kMaxNumber + 1));
4515
4516 ASSERT_EQ(
4517 "syntax = \"proto2\";\n\n"
4518 "enum Bar {\n"
4519 " BAR = 1;\n"
4520 " reserved 5 to max;\n"
4521 "}\n\n"
4522 "message Foo {\n"
4523 " reserved 5 to max;\n"
4524 "}\n\n",
4525 file->DebugString());
4526 }
4527
TEST_F(ValidationErrorTest,EnumReservedFieldError)4528 TEST_F(ValidationErrorTest, EnumReservedFieldError) {
4529 BuildFileWithErrors(
4530 "name: \"foo.proto\" "
4531 "enum_type {"
4532 " name: \"Foo\""
4533 " value { name:\"BAR\" number:15 }"
4534 " reserved_range { start: 10 end: 20 }"
4535 "}",
4536
4537 "foo.proto: BAR: NUMBER: Enum value \"BAR\" uses reserved number 15.\n");
4538 }
4539
TEST_F(ValidationErrorTest,EnumNegativeReservedFieldError)4540 TEST_F(ValidationErrorTest, EnumNegativeReservedFieldError) {
4541 BuildFileWithErrors(
4542 "name: \"foo.proto\" "
4543 "enum_type {"
4544 " name: \"Foo\""
4545 " value { name:\"BAR\" number:-15 }"
4546 " reserved_range { start: -20 end: -10 }"
4547 "}",
4548
4549 "foo.proto: BAR: NUMBER: Enum value \"BAR\" uses reserved number -15.\n");
4550 }
4551
TEST_F(ValidationErrorTest,EnumReservedRangeOverlap)4552 TEST_F(ValidationErrorTest, EnumReservedRangeOverlap) {
4553 BuildFileWithErrors(
4554 "name: \"foo.proto\" "
4555 "enum_type {"
4556 " name: \"Foo\""
4557 " value { name:\"BAR\" number:0 }"
4558 " reserved_range { start: 10 end: 20 }"
4559 " reserved_range { start: 5 end: 15 }"
4560 "}",
4561
4562 "foo.proto: Foo: NUMBER: Reserved range 5 to 15"
4563 " overlaps with already-defined range 10 to 20.\n");
4564 }
4565
TEST_F(ValidationErrorTest,EnumReservedRangeOverlapByOne)4566 TEST_F(ValidationErrorTest, EnumReservedRangeOverlapByOne) {
4567 BuildFileWithErrors(
4568 "name: \"foo.proto\" "
4569 "enum_type {"
4570 " name: \"Foo\""
4571 " value { name:\"BAR\" number:0 }"
4572 " reserved_range { start: 10 end: 20 }"
4573 " reserved_range { start: 5 end: 10 }"
4574 "}",
4575
4576 "foo.proto: Foo: NUMBER: Reserved range 5 to 10"
4577 " overlaps with already-defined range 10 to 20.\n");
4578 }
4579
TEST_F(ValidationErrorTest,EnumNegativeReservedRangeOverlap)4580 TEST_F(ValidationErrorTest, EnumNegativeReservedRangeOverlap) {
4581 BuildFileWithErrors(
4582 "name: \"foo.proto\" "
4583 "enum_type {"
4584 " name: \"Foo\""
4585 " value { name:\"BAR\" number:0 }"
4586 " reserved_range { start: -20 end: -10 }"
4587 " reserved_range { start: -15 end: -5 }"
4588 "}",
4589
4590 "foo.proto: Foo: NUMBER: Reserved range -15 to -5"
4591 " overlaps with already-defined range -20 to -10.\n");
4592 }
4593
TEST_F(ValidationErrorTest,EnumMixedReservedRangeOverlap)4594 TEST_F(ValidationErrorTest, EnumMixedReservedRangeOverlap) {
4595 BuildFileWithErrors(
4596 "name: \"foo.proto\" "
4597 "enum_type {"
4598 " name: \"Foo\""
4599 " value { name:\"BAR\" number:20 }"
4600 " reserved_range { start: -20 end: 10 }"
4601 " reserved_range { start: -15 end: 5 }"
4602 "}",
4603
4604 "foo.proto: Foo: NUMBER: Reserved range -15 to 5"
4605 " overlaps with already-defined range -20 to 10.\n");
4606 }
4607
TEST_F(ValidationErrorTest,EnumMixedReservedRangeOverlap2)4608 TEST_F(ValidationErrorTest, EnumMixedReservedRangeOverlap2) {
4609 BuildFileWithErrors(
4610 "name: \"foo.proto\" "
4611 "enum_type {"
4612 " name: \"Foo\""
4613 " value { name:\"BAR\" number:20 }"
4614 " reserved_range { start: -20 end: 10 }"
4615 " reserved_range { start: 10 end: 10 }"
4616 "}",
4617
4618 "foo.proto: Foo: NUMBER: Reserved range 10 to 10"
4619 " overlaps with already-defined range -20 to 10.\n");
4620 }
4621
TEST_F(ValidationErrorTest,EnumReservedRangeStartGreaterThanEnd)4622 TEST_F(ValidationErrorTest, EnumReservedRangeStartGreaterThanEnd) {
4623 BuildFileWithErrors(
4624 "name: \"foo.proto\" "
4625 "enum_type {"
4626 " name: \"Foo\""
4627 " value { name:\"BAR\" number:20 }"
4628 " reserved_range { start: 11 end: 10 }"
4629 "}",
4630
4631 "foo.proto: Foo: NUMBER: Reserved range end number must be greater"
4632 " than start number.\n");
4633 }
4634
TEST_F(ValidationErrorTest,EnumReservedNameError)4635 TEST_F(ValidationErrorTest, EnumReservedNameError) {
4636 BuildFileWithErrors(
4637 "name: \"foo.proto\" "
4638 "enum_type {"
4639 " name: \"Foo\""
4640 " value { name:\"FOO\" number:15 }"
4641 " value { name:\"BAR\" number:15 }"
4642 " reserved_name: \"FOO\""
4643 " reserved_name: \"BAR\""
4644 "}",
4645
4646 "foo.proto: FOO: NAME: Enum value \"FOO\" is reserved.\n"
4647 "foo.proto: BAR: NAME: Enum value \"BAR\" is reserved.\n");
4648 }
4649
TEST_F(ValidationErrorTest,EnumReservedNameRedundant)4650 TEST_F(ValidationErrorTest, EnumReservedNameRedundant) {
4651 BuildFileWithErrors(
4652 "name: \"foo.proto\" "
4653 "enum_type {"
4654 " name: \"Foo\""
4655 " value { name:\"FOO\" number:15 }"
4656 " reserved_name: \"foo\""
4657 " reserved_name: \"foo\""
4658 "}",
4659
4660 "foo.proto: foo: NAME: Enum value \"foo\" is reserved multiple times.\n");
4661 }
4662
TEST_F(ValidationErrorTest,EnumReservedFieldsDebugString)4663 TEST_F(ValidationErrorTest, EnumReservedFieldsDebugString) {
4664 const FileDescriptor* file = BuildFile(
4665 "name: \"foo.proto\" "
4666 "enum_type {"
4667 " name: \"Foo\""
4668 " value { name:\"FOO\" number:3 }"
4669 " reserved_name: \"foo\""
4670 " reserved_name: \"bar\""
4671 " reserved_range { start: -6 end: -6 }"
4672 " reserved_range { start: -5 end: -4 }"
4673 " reserved_range { start: -1 end: 1 }"
4674 " reserved_range { start: 5 end: 5 }"
4675 " reserved_range { start: 10 end: 19 }"
4676 "}");
4677
4678 ASSERT_EQ(
4679 "syntax = \"proto2\";\n\n"
4680 "enum Foo {\n"
4681 " FOO = 3;\n"
4682 " reserved -6, -5 to -4, -1 to 1, 5, 10 to 19;\n"
4683 " reserved \"foo\", \"bar\";\n"
4684 "}\n\n",
4685 file->DebugString());
4686 }
4687
TEST_F(ValidationErrorTest,EnumReservedFieldsDebugString2023)4688 TEST_F(ValidationErrorTest, EnumReservedFieldsDebugString2023) {
4689 const FileDescriptor* file = BuildFile(R"pb(
4690 syntax: "editions"
4691 edition: EDITION_2023
4692 name: "foo.proto"
4693 enum_type {
4694 name: "Foo"
4695 value { name: "FOO" number: 3 }
4696 options { features { enum_type: CLOSED } }
4697 reserved_name: "foo"
4698 reserved_name: "bar"
4699 reserved_range { start: -6 end: -6 }
4700 reserved_range { start: -5 end: -4 }
4701 reserved_range { start: -1 end: 1 }
4702 reserved_range { start: 5 end: 5 }
4703 reserved_range { start: 10 end: 19 }
4704 })pb");
4705
4706 ASSERT_EQ(
4707 "edition = \"2023\";\n\n"
4708 "enum Foo {\n"
4709 " option features = {\n"
4710 " enum_type: CLOSED\n"
4711 " };\n"
4712 " FOO = 3;\n"
4713 " reserved -6, -5 to -4, -1 to 1, 5, 10 to 19;\n"
4714 " reserved foo, bar;\n"
4715 "}\n\n",
4716 file->DebugString());
4717 }
4718
TEST_F(ValidationErrorTest,InvalidDefaults)4719 TEST_F(ValidationErrorTest, InvalidDefaults) {
4720 BuildFileWithErrors(
4721 "name: \"foo.proto\" "
4722 "message_type {"
4723 " name: \"Foo\""
4724
4725 // Invalid number.
4726 " field { name: \"foo\" number: 1 label: LABEL_OPTIONAL type: TYPE_INT32"
4727 " default_value: \"abc\" }"
4728
4729 // Empty default value.
4730 " field { name: \"bar\" number: 2 label: LABEL_OPTIONAL type: TYPE_INT32"
4731 " default_value: \"\" }"
4732
4733 // Invalid boolean.
4734 " field { name: \"baz\" number: 3 label: LABEL_OPTIONAL type: TYPE_BOOL"
4735 " default_value: \"abc\" }"
4736
4737 // Messages can't have defaults.
4738 " field { name: \"moo\" number: 4 label: LABEL_OPTIONAL type: "
4739 "TYPE_MESSAGE"
4740 " default_value: \"abc\" type_name: \"Foo\" }"
4741
4742 // Same thing, but we don't know that this field has message type until
4743 // we look up the type name.
4744 " field { name: \"mooo\" number: 5 label: LABEL_OPTIONAL"
4745 " default_value: \"abc\" type_name: \"Foo\" }"
4746
4747 // Repeateds can't have defaults.
4748 " field { name: \"corge\" number: 6 label: LABEL_REPEATED type: "
4749 "TYPE_INT32"
4750 " default_value: \"1\" }"
4751
4752 // Invalid CEscaped bytes default.
4753 " field { name: \"bytes_default\" number: 7 label: LABEL_OPTIONAL "
4754 " type: TYPE_BYTES"
4755 " default_value: \"\\\\\" }"
4756
4757 "}",
4758
4759 "foo.proto: Foo.foo: DEFAULT_VALUE: Couldn't parse default value "
4760 "\"abc\".\n"
4761 "foo.proto: Foo.bar: DEFAULT_VALUE: Couldn't parse default value \"\".\n"
4762 "foo.proto: Foo.baz: DEFAULT_VALUE: Boolean default must be true or "
4763 "false.\n"
4764 "foo.proto: Foo.moo: DEFAULT_VALUE: Messages can't have default values.\n"
4765 "foo.proto: Foo.corge: DEFAULT_VALUE: Repeated fields can't have default "
4766 "values.\n"
4767 "foo.proto: Foo.bytes_default: DEFAULT_VALUE: Invalid escaping in "
4768 "default value.\n"
4769 // This ends up being reported later because the error is detected at
4770 // cross-linking time.
4771 "foo.proto: Foo.mooo: DEFAULT_VALUE: Messages can't have default "
4772 "values.\n");
4773 }
4774
TEST_F(ValidationErrorTest,NegativeFieldNumber)4775 TEST_F(ValidationErrorTest, NegativeFieldNumber) {
4776 BuildFileWithErrors(
4777 "name: \"foo.proto\" "
4778 "message_type {"
4779 " name: \"Foo\""
4780 " field { name: \"foo\" number: -1 label:LABEL_OPTIONAL type:TYPE_INT32 "
4781 "}"
4782 "}",
4783
4784 "foo.proto: Foo.foo: NUMBER: Field numbers must be positive integers.\n"
4785 "foo.proto: Foo: NUMBER: Suggested field numbers for Foo: 1\n");
4786 }
4787
TEST_F(ValidationErrorTest,HugeFieldNumber)4788 TEST_F(ValidationErrorTest, HugeFieldNumber) {
4789 BuildFileWithErrors(
4790 "name: \"foo.proto\" "
4791 "message_type {"
4792 " name: \"Foo\""
4793 " field { name: \"foo\" number: 0x70000000 "
4794 " label:LABEL_OPTIONAL type:TYPE_INT32 }"
4795 "}",
4796
4797 "foo.proto: Foo.foo: NUMBER: Field numbers cannot be greater than "
4798 "536870911.\n"
4799 "foo.proto: Foo: NUMBER: Suggested field numbers for Foo: 1\n");
4800 }
4801
TEST_F(ValidationErrorTest,ExtensionMissingExtendee)4802 TEST_F(ValidationErrorTest, ExtensionMissingExtendee) {
4803 BuildFileWithErrors(
4804 "name: \"foo.proto\" "
4805 "message_type {"
4806 " name: \"Foo\""
4807 " extension { name: \"foo\" number: 1 label: LABEL_OPTIONAL"
4808 " type_name: \"Foo\" }"
4809 "}",
4810
4811 "foo.proto: Foo.foo: EXTENDEE: FieldDescriptorProto.extendee not set for "
4812 "extension field.\n");
4813 }
4814
TEST_F(ValidationErrorTest,NonExtensionWithExtendee)4815 TEST_F(ValidationErrorTest, NonExtensionWithExtendee) {
4816 BuildFileWithErrors(
4817 "name: \"foo.proto\" "
4818 "message_type {"
4819 " name: \"Bar\""
4820 " extension_range { start: 1 end: 2 }"
4821 "}"
4822 "message_type {"
4823 " name: \"Foo\""
4824 " field { name: \"foo\" number: 1 label: LABEL_OPTIONAL"
4825 " type_name: \"Foo\" extendee: \"Bar\" }"
4826 "}",
4827
4828 "foo.proto: Foo.foo: EXTENDEE: FieldDescriptorProto.extendee set for "
4829 "non-extension field.\n");
4830 }
4831
TEST_F(ValidationErrorTest,FieldOneofIndexTooLarge)4832 TEST_F(ValidationErrorTest, FieldOneofIndexTooLarge) {
4833 BuildFileWithErrors(
4834 "name: \"foo.proto\" "
4835 "message_type {"
4836 " name: \"Foo\""
4837 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_INT32 "
4838 " oneof_index: 1 }"
4839 " field { name:\"dummy\" number:2 label:LABEL_OPTIONAL type:TYPE_INT32 "
4840 " oneof_index: 0 }"
4841 " oneof_decl { name:\"bar\" }"
4842 "}",
4843
4844 "foo.proto: Foo.foo: TYPE: FieldDescriptorProto.oneof_index 1 is out of "
4845 "range for type \"Foo\".\n");
4846 }
4847
TEST_F(ValidationErrorTest,FieldOneofIndexNegative)4848 TEST_F(ValidationErrorTest, FieldOneofIndexNegative) {
4849 BuildFileWithErrors(
4850 "name: \"foo.proto\" "
4851 "message_type {"
4852 " name: \"Foo\""
4853 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_INT32 "
4854 " oneof_index: -1 }"
4855 " field { name:\"dummy\" number:2 label:LABEL_OPTIONAL type:TYPE_INT32 "
4856 " oneof_index: 0 }"
4857 " oneof_decl { name:\"bar\" }"
4858 "}",
4859
4860 "foo.proto: Foo.foo: TYPE: FieldDescriptorProto.oneof_index -1 is out "
4861 "of "
4862 "range for type \"Foo\".\n");
4863 }
4864
TEST_F(ValidationErrorTest,OneofFieldsConsecutiveDefinition)4865 TEST_F(ValidationErrorTest, OneofFieldsConsecutiveDefinition) {
4866 // Fields belonging to the same oneof must be defined consecutively.
4867 BuildFileWithErrors(
4868 "name: \"foo.proto\" "
4869 "message_type {"
4870 " name: \"Foo\""
4871 " field { name:\"foo1\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 "
4872 " oneof_index: 0 }"
4873 " field { name:\"bar\" number: 2 label:LABEL_OPTIONAL type:TYPE_INT32 }"
4874 " field { name:\"foo2\" number: 3 label:LABEL_OPTIONAL type:TYPE_INT32 "
4875 " oneof_index: 0 }"
4876 " oneof_decl { name:\"foos\" }"
4877 "}",
4878
4879 "foo.proto: Foo.bar: TYPE: Fields in the same oneof must be defined "
4880 "consecutively. \"bar\" cannot be defined before the completion of the "
4881 "\"foos\" oneof definition.\n");
4882
4883 // Prevent interleaved fields, which belong to different oneofs.
4884 BuildFileWithErrors(
4885 "name: \"foo2.proto\" "
4886 "message_type {"
4887 " name: \"Foo2\""
4888 " field { name:\"foo1\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 "
4889 " oneof_index: 0 }"
4890 " field { name:\"bar1\" number: 2 label:LABEL_OPTIONAL type:TYPE_INT32 "
4891 " oneof_index: 1 }"
4892 " field { name:\"foo2\" number: 3 label:LABEL_OPTIONAL type:TYPE_INT32 "
4893 " oneof_index: 0 }"
4894 " field { name:\"bar2\" number: 4 label:LABEL_OPTIONAL type:TYPE_INT32 "
4895 " oneof_index: 1 }"
4896 " oneof_decl { name:\"foos\" }"
4897 " oneof_decl { name:\"bars\" }"
4898 "}",
4899 "foo2.proto: Foo2.bar1: TYPE: Fields in the same oneof must be defined "
4900 "consecutively. \"bar1\" cannot be defined before the completion of the "
4901 "\"foos\" oneof definition.\n"
4902 "foo2.proto: Foo2.foo2: TYPE: Fields in the same oneof must be defined "
4903 "consecutively. \"foo2\" cannot be defined before the completion of the "
4904 "\"bars\" oneof definition.\n");
4905
4906 // Another case for normal fields and different oneof fields interleave.
4907 BuildFileWithErrors(
4908 "name: \"foo3.proto\" "
4909 "message_type {"
4910 " name: \"Foo3\""
4911 " field { name:\"foo1\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 "
4912 " oneof_index: 0 }"
4913 " field { name:\"bar1\" number: 2 label:LABEL_OPTIONAL type:TYPE_INT32 "
4914 " oneof_index: 1 }"
4915 " field { name:\"baz\" number: 3 label:LABEL_OPTIONAL type:TYPE_INT32 }"
4916 " field { name:\"foo2\" number: 4 label:LABEL_OPTIONAL type:TYPE_INT32 "
4917 " oneof_index: 0 }"
4918 " oneof_decl { name:\"foos\" }"
4919 " oneof_decl { name:\"bars\" }"
4920 "}",
4921 "foo3.proto: Foo3.baz: TYPE: Fields in the same oneof must be defined "
4922 "consecutively. \"baz\" cannot be defined before the completion of the "
4923 "\"foos\" oneof definition.\n");
4924 }
4925
TEST_F(ValidationErrorTest,FieldNumberConflict)4926 TEST_F(ValidationErrorTest, FieldNumberConflict) {
4927 BuildFileWithErrors(
4928 "name: \"foo.proto\" "
4929 "message_type {"
4930 " name: \"Foo\""
4931 " field { name: \"foo\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 }"
4932 " field { name: \"bar\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 }"
4933 "}",
4934
4935 "foo.proto: Foo.bar: NUMBER: Field number 1 has already been used in "
4936 "\"Foo\" by field \"foo\".\n");
4937 }
4938
TEST_F(ValidationErrorTest,BadMessageSetExtensionType)4939 TEST_F(ValidationErrorTest, BadMessageSetExtensionType) {
4940 BuildFileWithErrors(
4941 "name: \"foo.proto\" "
4942 "message_type {"
4943 " name: \"MessageSet\""
4944 " options { message_set_wire_format: true }"
4945 " extension_range { start: 4 end: 5 }"
4946 "}"
4947 "message_type {"
4948 " name: \"Foo\""
4949 " extension { name:\"foo\" number:4 label:LABEL_OPTIONAL type:TYPE_INT32"
4950 " extendee: \"MessageSet\" }"
4951 "}",
4952
4953 "foo.proto: Foo.foo: TYPE: Extensions of MessageSets must be optional "
4954 "messages.\n");
4955 }
4956
TEST_F(ValidationErrorTest,BadMessageSetExtensionLabel)4957 TEST_F(ValidationErrorTest, BadMessageSetExtensionLabel) {
4958 BuildFileWithErrors(
4959 "name: \"foo.proto\" "
4960 "message_type {"
4961 " name: \"MessageSet\""
4962 " options { message_set_wire_format: true }"
4963 " extension_range { start: 4 end: 5 }"
4964 "}"
4965 "message_type {"
4966 " name: \"Foo\""
4967 " extension { name:\"foo\" number:4 label:LABEL_REPEATED "
4968 "type:TYPE_MESSAGE"
4969 " type_name: \"Foo\" extendee: \"MessageSet\" }"
4970 "}",
4971
4972 "foo.proto: Foo.foo: TYPE: Extensions of MessageSets must be optional "
4973 "messages.\n");
4974 }
4975
TEST_F(ValidationErrorTest,FieldInMessageSet)4976 TEST_F(ValidationErrorTest, FieldInMessageSet) {
4977 BuildFileWithErrors(
4978 "name: \"foo.proto\" "
4979 "message_type {"
4980 " name: \"Foo\""
4981 " options { message_set_wire_format: true }"
4982 " field { name: \"foo\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 }"
4983 "}",
4984
4985 "foo.proto: Foo.foo: NAME: MessageSets cannot have fields, only "
4986 "extensions.\n");
4987 }
4988
TEST_F(ValidationErrorTest,NegativeExtensionRangeNumber)4989 TEST_F(ValidationErrorTest, NegativeExtensionRangeNumber) {
4990 BuildFileWithErrors(
4991 "name: \"foo.proto\" "
4992 "message_type {"
4993 " name: \"Foo\""
4994 " extension_range { start: -10 end: -1 }"
4995 "}",
4996
4997 "foo.proto: Foo: NUMBER: Extension numbers must be positive integers.\n");
4998 }
4999
TEST_F(ValidationErrorTest,HugeExtensionRangeNumber)5000 TEST_F(ValidationErrorTest, HugeExtensionRangeNumber) {
5001 BuildFileWithErrors(
5002 "name: \"foo.proto\" "
5003 "message_type {"
5004 " name: \"Foo\""
5005 " extension_range { start: 1 end: 0x70000000 }"
5006 "}",
5007
5008 "foo.proto: Foo: NUMBER: Extension numbers cannot be greater than "
5009 "536870911.\n");
5010 }
5011
TEST_F(ValidationErrorTest,ExtensionRangeEndBeforeStart)5012 TEST_F(ValidationErrorTest, ExtensionRangeEndBeforeStart) {
5013 BuildFileWithErrors(
5014 "name: \"foo.proto\" "
5015 "message_type {"
5016 " name: \"Foo\""
5017 " extension_range { start: 10 end: 10 }"
5018 " extension_range { start: 10 end: 5 }"
5019 "}",
5020
5021 "foo.proto: Foo: NUMBER: Extension range end number must be greater than "
5022 "start number.\n"
5023 "foo.proto: Foo: NUMBER: Extension range end number must be greater than "
5024 "start number.\n");
5025 }
5026
TEST_F(ValidationErrorTest,EmptyEnum)5027 TEST_F(ValidationErrorTest, EmptyEnum) {
5028 BuildFileWithErrors(
5029 "name: \"foo.proto\" "
5030 "enum_type { name: \"Foo\" }"
5031 // Also use the empty enum in a message to make sure there are no crashes
5032 // during validation (possible if the code attempts to derive a default
5033 // value for the field).
5034 "message_type {"
5035 " name: \"Bar\""
5036 " field { name: \"foo\" number: 1 label:LABEL_OPTIONAL "
5037 "type_name:\"Foo\" }"
5038 " field { name: \"bar\" number: 2 label:LABEL_OPTIONAL "
5039 "type_name:\"Foo\" "
5040 " default_value: \"NO_SUCH_VALUE\" }"
5041 "}",
5042
5043 "foo.proto: Foo: NAME: Enums must contain at least one value.\n"
5044 "foo.proto: Bar.bar: DEFAULT_VALUE: Enum type \"Foo\" has no value named "
5045 "\"NO_SUCH_VALUE\".\n");
5046 }
5047
TEST_F(ValidationErrorTest,UndefinedExtendee)5048 TEST_F(ValidationErrorTest, UndefinedExtendee) {
5049 BuildFileWithErrors(
5050 "name: \"foo.proto\" "
5051 "message_type {"
5052 " name: \"Foo\""
5053 " extension { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_INT32"
5054 " extendee: \"Bar\" }"
5055 "}",
5056
5057 "foo.proto: Foo.foo: EXTENDEE: \"Bar\" is not defined.\n");
5058 }
5059
TEST_F(ValidationErrorTest,NonMessageExtendee)5060 TEST_F(ValidationErrorTest, NonMessageExtendee) {
5061 BuildFileWithErrors(
5062 "name: \"foo.proto\" "
5063 "enum_type { name: \"Bar\" value { name:\"DUMMY\" number:0 } }"
5064 "message_type {"
5065 " name: \"Foo\""
5066 " extension { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_INT32"
5067 " extendee: \"Bar\" }"
5068 "}",
5069
5070 "foo.proto: Foo.foo: EXTENDEE: \"Bar\" is not a message type.\n");
5071 }
5072
TEST_F(ValidationErrorTest,NotAnExtensionNumber)5073 TEST_F(ValidationErrorTest, NotAnExtensionNumber) {
5074 BuildFileWithErrors(
5075 "name: \"foo.proto\" "
5076 "message_type {"
5077 " name: \"Bar\""
5078 "}"
5079 "message_type {"
5080 " name: \"Foo\""
5081 " extension { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_INT32"
5082 " extendee: \"Bar\" }"
5083 "}",
5084
5085 "foo.proto: Foo.foo: NUMBER: \"Bar\" does not declare 1 as an extension "
5086 "number.\n");
5087 }
5088
TEST_F(ValidationErrorTest,RequiredExtension)5089 TEST_F(ValidationErrorTest, RequiredExtension) {
5090 BuildFileWithErrors(
5091 "name: \"foo.proto\" "
5092 "message_type {"
5093 " name: \"Bar\""
5094 " extension_range { start: 1000 end: 10000 }"
5095 "}"
5096 "message_type {"
5097 " name: \"Foo\""
5098 " extension {"
5099 " name:\"foo\""
5100 " number:1000"
5101 " label:LABEL_REQUIRED"
5102 " type:TYPE_INT32"
5103 " extendee: \"Bar\""
5104 " }"
5105 "}",
5106
5107 "foo.proto: Foo.foo: TYPE: The extension Foo.foo cannot be required.\n");
5108 }
5109
TEST_F(ValidationErrorTest,UndefinedFieldType)5110 TEST_F(ValidationErrorTest, UndefinedFieldType) {
5111 BuildFileWithErrors(
5112 "name: \"foo.proto\" "
5113 "message_type {"
5114 " name: \"Foo\""
5115 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"Bar\" }"
5116 "}",
5117
5118 "foo.proto: Foo.foo: TYPE: \"Bar\" is not defined.\n");
5119 }
5120
TEST_F(ValidationErrorTest,UndefinedFieldTypeWithDefault)5121 TEST_F(ValidationErrorTest, UndefinedFieldTypeWithDefault) {
5122 // See b/12533582. Previously this failed because the default value was not
5123 // accepted by the parser, which assumed an enum type, leading to an unclear
5124 // error message. We want this input to yield a validation error instead,
5125 // since the unknown type is the primary problem.
5126 BuildFileWithErrors(
5127 "name: \"foo.proto\" "
5128 "message_type {"
5129 " name: \"Foo\""
5130 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"int\" "
5131 " default_value:\"1\" }"
5132 "}",
5133
5134 "foo.proto: Foo.foo: TYPE: \"int\" is not defined.\n");
5135 }
5136
TEST_F(ValidationErrorTest,UndefinedNestedFieldType)5137 TEST_F(ValidationErrorTest, UndefinedNestedFieldType) {
5138 BuildFileWithErrors(
5139 "name: \"foo.proto\" "
5140 "message_type {"
5141 " name: \"Foo\""
5142 " nested_type { name:\"Baz\" }"
5143 " field { name:\"foo\" number:1"
5144 " label:LABEL_OPTIONAL"
5145 " type_name:\"Foo.Baz.Bar\" }"
5146 "}",
5147
5148 "foo.proto: Foo.foo: TYPE: \"Foo.Baz.Bar\" is not defined.\n");
5149 }
5150
TEST_F(ValidationErrorTest,FieldTypeDefinedInUndeclaredDependency)5151 TEST_F(ValidationErrorTest, FieldTypeDefinedInUndeclaredDependency) {
5152 BuildFile(
5153 "name: \"bar.proto\" "
5154 "message_type { name: \"Bar\" } ");
5155
5156 BuildFileWithErrors(
5157 "name: \"foo.proto\" "
5158 "message_type {"
5159 " name: \"Foo\""
5160 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"Bar\" }"
5161 "}",
5162 "foo.proto: Foo.foo: TYPE: \"Bar\" seems to be defined in \"bar.proto\", "
5163 "which is not imported by \"foo.proto\". To use it here, please add the "
5164 "necessary import.\n");
5165 }
5166
TEST_F(ValidationErrorTest,FieldTypeDefinedInIndirectDependency)5167 TEST_F(ValidationErrorTest, FieldTypeDefinedInIndirectDependency) {
5168 // Test for hidden dependencies.
5169 //
5170 // // bar.proto
5171 // message Bar{}
5172 //
5173 // // forward.proto
5174 // import "bar.proto"
5175 //
5176 // // foo.proto
5177 // import "forward.proto"
5178 // message Foo {
5179 // optional Bar foo = 1; // Error, needs to import bar.proto explicitly.
5180 // }
5181 //
5182 BuildFile(
5183 "name: \"bar.proto\" "
5184 "message_type { name: \"Bar\" }");
5185
5186 BuildFile(
5187 "name: \"forward.proto\""
5188 "dependency: \"bar.proto\"");
5189
5190 BuildFileWithErrors(
5191 "name: \"foo.proto\" "
5192 "dependency: \"forward.proto\" "
5193 "message_type {"
5194 " name: \"Foo\""
5195 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"Bar\" }"
5196 "}",
5197 "foo.proto: Foo.foo: TYPE: \"Bar\" seems to be defined in \"bar.proto\", "
5198 "which is not imported by \"foo.proto\". To use it here, please add the "
5199 "necessary import.\n");
5200 }
5201
TEST_F(ValidationErrorTest,FieldTypeDefinedInPublicDependency)5202 TEST_F(ValidationErrorTest, FieldTypeDefinedInPublicDependency) {
5203 // Test for public dependencies.
5204 //
5205 // // bar.proto
5206 // message Bar{}
5207 //
5208 // // forward.proto
5209 // import public "bar.proto"
5210 //
5211 // // foo.proto
5212 // import "forward.proto"
5213 // message Foo {
5214 // optional Bar foo = 1; // Correct. "bar.proto" is public imported into
5215 // // forward.proto, so when "foo.proto" imports
5216 // // "forward.proto", it imports "bar.proto" too.
5217 // }
5218 //
5219 BuildFile(
5220 "name: \"bar.proto\" "
5221 "message_type { name: \"Bar\" }");
5222
5223 BuildFile(
5224 "name: \"forward.proto\""
5225 "dependency: \"bar.proto\" "
5226 "public_dependency: 0");
5227
5228 BuildFile(
5229 "name: \"foo.proto\" "
5230 "dependency: \"forward.proto\" "
5231 "message_type {"
5232 " name: \"Foo\""
5233 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"Bar\" }"
5234 "}");
5235 }
5236
TEST_F(ValidationErrorTest,FieldTypeDefinedInTransitivePublicDependency)5237 TEST_F(ValidationErrorTest, FieldTypeDefinedInTransitivePublicDependency) {
5238 // Test for public dependencies.
5239 //
5240 // // bar.proto
5241 // message Bar{}
5242 //
5243 // // forward.proto
5244 // import public "bar.proto"
5245 //
5246 // // forward2.proto
5247 // import public "forward.proto"
5248 //
5249 // // foo.proto
5250 // import "forward2.proto"
5251 // message Foo {
5252 // optional Bar foo = 1; // Correct, public imports are transitive.
5253 // }
5254 //
5255 BuildFile(
5256 "name: \"bar.proto\" "
5257 "message_type { name: \"Bar\" }");
5258
5259 BuildFile(
5260 "name: \"forward.proto\""
5261 "dependency: \"bar.proto\" "
5262 "public_dependency: 0");
5263
5264 BuildFile(
5265 "name: \"forward2.proto\""
5266 "dependency: \"forward.proto\" "
5267 "public_dependency: 0");
5268
5269 BuildFile(
5270 "name: \"foo.proto\" "
5271 "dependency: \"forward2.proto\" "
5272 "message_type {"
5273 " name: \"Foo\""
5274 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"Bar\" }"
5275 "}");
5276 }
5277
TEST_F(ValidationErrorTest,FieldTypeDefinedInPrivateDependencyOfPublicDependency)5278 TEST_F(ValidationErrorTest,
5279 FieldTypeDefinedInPrivateDependencyOfPublicDependency) {
5280 // Test for public dependencies.
5281 //
5282 // // bar.proto
5283 // message Bar{}
5284 //
5285 // // forward.proto
5286 // import "bar.proto"
5287 //
5288 // // forward2.proto
5289 // import public "forward.proto"
5290 //
5291 // // foo.proto
5292 // import "forward2.proto"
5293 // message Foo {
5294 // optional Bar foo = 1; // Error, the "bar.proto" is not public imported
5295 // // into "forward.proto", so will not be imported
5296 // // into either "forward2.proto" or "foo.proto".
5297 // }
5298 //
5299 BuildFile(
5300 "name: \"bar.proto\" "
5301 "message_type { name: \"Bar\" }");
5302
5303 BuildFile(
5304 "name: \"forward.proto\""
5305 "dependency: \"bar.proto\"");
5306
5307 BuildFile(
5308 "name: \"forward2.proto\""
5309 "dependency: \"forward.proto\" "
5310 "public_dependency: 0");
5311
5312 BuildFileWithErrors(
5313 "name: \"foo.proto\" "
5314 "dependency: \"forward2.proto\" "
5315 "message_type {"
5316 " name: \"Foo\""
5317 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"Bar\" }"
5318 "}",
5319 "foo.proto: Foo.foo: TYPE: \"Bar\" seems to be defined in \"bar.proto\", "
5320 "which is not imported by \"foo.proto\". To use it here, please add the "
5321 "necessary import.\n");
5322 }
5323
5324
TEST_F(ValidationErrorTest,SearchMostLocalFirst)5325 TEST_F(ValidationErrorTest, SearchMostLocalFirst) {
5326 // The following should produce an error that Bar.Baz is resolved but
5327 // not defined:
5328 // message Bar { message Baz {} }
5329 // message Foo {
5330 // message Bar {
5331 // // Placing "message Baz{}" here, or removing Foo.Bar altogether,
5332 // // would fix the error.
5333 // }
5334 // optional Bar.Baz baz = 1;
5335 // }
5336 // An one point the lookup code incorrectly did not produce an error in this
5337 // case, because when looking for Bar.Baz, it would try "Foo.Bar.Baz" first,
5338 // fail, and ten try "Bar.Baz" and succeed, even though "Bar" should actually
5339 // refer to the inner Bar, not the outer one.
5340 BuildFileWithErrors(
5341 "name: \"foo.proto\" "
5342 "message_type {"
5343 " name: \"Bar\""
5344 " nested_type { name: \"Baz\" }"
5345 "}"
5346 "message_type {"
5347 " name: \"Foo\""
5348 " nested_type { name: \"Bar\" }"
5349 " field { name:\"baz\" number:1 label:LABEL_OPTIONAL"
5350 " type_name:\"Bar.Baz\" }"
5351 "}",
5352
5353 "foo.proto: Foo.baz: TYPE: \"Bar.Baz\" is resolved to \"Foo.Bar.Baz\","
5354 " which is not defined. The innermost scope is searched first in name "
5355 "resolution. Consider using a leading '.'(i.e., \".Bar.Baz\") to start "
5356 "from the outermost scope.\n");
5357 }
5358
TEST_F(ValidationErrorTest,SearchMostLocalFirst2)5359 TEST_F(ValidationErrorTest, SearchMostLocalFirst2) {
5360 // This test would find the most local "Bar" first, and does, but
5361 // proceeds to find the outer one because the inner one's not an
5362 // aggregate.
5363 BuildFile(
5364 "name: \"foo.proto\" "
5365 "message_type {"
5366 " name: \"Bar\""
5367 " nested_type { name: \"Baz\" }"
5368 "}"
5369 "message_type {"
5370 " name: \"Foo\""
5371 " field { name: \"Bar\" number:1 type:TYPE_BYTES } "
5372 " field { name:\"baz\" number:2 label:LABEL_OPTIONAL"
5373 " type_name:\"Bar.Baz\" }"
5374 "}");
5375 }
5376
TEST_F(ValidationErrorTest,PackageOriginallyDeclaredInTransitiveDependent)5377 TEST_F(ValidationErrorTest, PackageOriginallyDeclaredInTransitiveDependent) {
5378 // Imagine we have the following:
5379 //
5380 // foo.proto:
5381 // package foo.bar;
5382 // bar.proto:
5383 // package foo.bar;
5384 // import "foo.proto";
5385 // message Bar {}
5386 // baz.proto:
5387 // package foo;
5388 // import "bar.proto"
5389 // message Baz { optional bar.Bar moo = 1; }
5390 //
5391 // When validating baz.proto, we will look up "bar.Bar". As part of this
5392 // lookup, we first lookup "bar" then try to find "Bar" within it. "bar"
5393 // should resolve to "foo.bar". Note, though, that "foo.bar" was originally
5394 // defined in foo.proto, which is not a direct dependency of baz.proto. The
5395 // implementation of FindSymbol() normally only returns symbols in direct
5396 // dependencies, not indirect ones. This test insures that this does not
5397 // prevent it from finding "foo.bar".
5398
5399 BuildFile(
5400 "name: \"foo.proto\" "
5401 "package: \"foo.bar\" ");
5402 BuildFile(
5403 "name: \"bar.proto\" "
5404 "package: \"foo.bar\" "
5405 "dependency: \"foo.proto\" "
5406 "message_type { name: \"Bar\" }");
5407 BuildFile(
5408 "name: \"baz.proto\" "
5409 "package: \"foo\" "
5410 "dependency: \"bar.proto\" "
5411 "message_type { "
5412 " name: \"Baz\" "
5413 " field { name:\"moo\" number:1 label:LABEL_OPTIONAL "
5414 " type_name:\"bar.Bar\" }"
5415 "}");
5416 }
5417
TEST_F(ValidationErrorTest,FieldTypeNotAType)5418 TEST_F(ValidationErrorTest, FieldTypeNotAType) {
5419 BuildFileWithErrors(
5420 "name: \"foo.proto\" "
5421 "message_type {"
5422 " name: \"Foo\""
5423 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL "
5424 " type_name:\".Foo.bar\" }"
5425 " field { name:\"bar\" number:2 label:LABEL_OPTIONAL type:TYPE_INT32 }"
5426 "}",
5427
5428 "foo.proto: Foo.foo: TYPE: \".Foo.bar\" is not a type.\n");
5429 }
5430
TEST_F(ValidationErrorTest,RelativeFieldTypeNotAType)5431 TEST_F(ValidationErrorTest, RelativeFieldTypeNotAType) {
5432 BuildFileWithErrors(
5433 "name: \"foo.proto\" "
5434 "message_type {"
5435 " nested_type {"
5436 " name: \"Bar\""
5437 " field { name:\"Baz\" number:2 label:LABEL_OPTIONAL type:TYPE_INT32 }"
5438 " }"
5439 " name: \"Foo\""
5440 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL "
5441 " type_name:\"Bar.Baz\" }"
5442 "}",
5443 "foo.proto: Foo.foo: TYPE: \"Bar.Baz\" is not a type.\n");
5444 }
5445
TEST_F(ValidationErrorTest,FieldTypeMayBeItsName)5446 TEST_F(ValidationErrorTest, FieldTypeMayBeItsName) {
5447 BuildFile(
5448 "name: \"foo.proto\" "
5449 "message_type {"
5450 " name: \"Bar\""
5451 "}"
5452 "message_type {"
5453 " name: \"Foo\""
5454 " field { name:\"Bar\" number:1 label:LABEL_OPTIONAL type_name:\"Bar\" }"
5455 "}");
5456 }
5457
TEST_F(ValidationErrorTest,EnumFieldTypeIsMessage)5458 TEST_F(ValidationErrorTest, EnumFieldTypeIsMessage) {
5459 BuildFileWithErrors(
5460 "name: \"foo.proto\" "
5461 "message_type { name: \"Bar\" } "
5462 "message_type {"
5463 " name: \"Foo\""
5464 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_ENUM"
5465 " type_name:\"Bar\" }"
5466 "}",
5467
5468 "foo.proto: Foo.foo: TYPE: \"Bar\" is not an enum type.\n");
5469 }
5470
TEST_F(ValidationErrorTest,MessageFieldTypeIsEnum)5471 TEST_F(ValidationErrorTest, MessageFieldTypeIsEnum) {
5472 BuildFileWithErrors(
5473 "name: \"foo.proto\" "
5474 "enum_type { name: \"Bar\" value { name:\"DUMMY\" number:0 } } "
5475 "message_type {"
5476 " name: \"Foo\""
5477 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_MESSAGE"
5478 " type_name:\"Bar\" }"
5479 "}",
5480
5481 "foo.proto: Foo.foo: TYPE: \"Bar\" is not a message type.\n");
5482 }
5483
TEST_F(ValidationErrorTest,BadEnumDefaultValue)5484 TEST_F(ValidationErrorTest, BadEnumDefaultValue) {
5485 BuildFileWithErrors(
5486 "name: \"foo.proto\" "
5487 "enum_type { name: \"Bar\" value { name:\"DUMMY\" number:0 } } "
5488 "message_type {"
5489 " name: \"Foo\""
5490 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"Bar\""
5491 " default_value:\"NO_SUCH_VALUE\" }"
5492 "}",
5493
5494 "foo.proto: Foo.foo: DEFAULT_VALUE: Enum type \"Bar\" has no value named "
5495 "\"NO_SUCH_VALUE\".\n");
5496 }
5497
TEST_F(ValidationErrorTest,EnumDefaultValueIsInteger)5498 TEST_F(ValidationErrorTest, EnumDefaultValueIsInteger) {
5499 BuildFileWithErrors(
5500 "name: \"foo.proto\" "
5501 "enum_type { name: \"Bar\" value { name:\"DUMMY\" number:0 } } "
5502 "message_type {"
5503 " name: \"Foo\""
5504 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"Bar\""
5505 " default_value:\"0\" }"
5506 "}",
5507
5508 "foo.proto: Foo.foo: DEFAULT_VALUE: Default value for an enum field must "
5509 "be an identifier.\n");
5510 }
5511
TEST_F(ValidationErrorTest,PrimitiveWithTypeName)5512 TEST_F(ValidationErrorTest, PrimitiveWithTypeName) {
5513 BuildFileWithErrors(
5514 "name: \"foo.proto\" "
5515 "message_type {"
5516 " name: \"Foo\""
5517 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_INT32"
5518 " type_name:\"Foo\" }"
5519 "}",
5520
5521 "foo.proto: Foo.foo: TYPE: Field with primitive type has type_name.\n");
5522 }
5523
TEST_F(ValidationErrorTest,NonPrimitiveWithoutTypeName)5524 TEST_F(ValidationErrorTest, NonPrimitiveWithoutTypeName) {
5525 BuildFileWithErrors(
5526 "name: \"foo.proto\" "
5527 "message_type {"
5528 " name: \"Foo\""
5529 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_MESSAGE }"
5530 "}",
5531
5532 "foo.proto: Foo.foo: TYPE: Field with message or enum type missing "
5533 "type_name.\n");
5534 }
5535
TEST_F(ValidationErrorTest,OneofWithNoFields)5536 TEST_F(ValidationErrorTest, OneofWithNoFields) {
5537 BuildFileWithErrors(
5538 "name: \"foo.proto\" "
5539 "message_type {"
5540 " name: \"Foo\""
5541 " oneof_decl { name:\"bar\" }"
5542 "}",
5543
5544 "foo.proto: Foo.bar: NAME: Oneof must have at least one field.\n");
5545 }
5546
TEST_F(ValidationErrorTest,OneofLabelMismatch)5547 TEST_F(ValidationErrorTest, OneofLabelMismatch) {
5548 BuildFileWithErrors(
5549 "name: \"foo.proto\" "
5550 "message_type {"
5551 " name: \"Foo\""
5552 " field { name:\"foo\" number:1 label:LABEL_REPEATED type:TYPE_INT32 "
5553 " oneof_index:0 }"
5554 " oneof_decl { name:\"bar\" }"
5555 "}",
5556
5557 "foo.proto: Foo.foo: NAME: Fields of oneofs must themselves have label "
5558 "LABEL_OPTIONAL.\n");
5559 }
5560
TEST_F(ValidationErrorTest,InputTypeNotDefined)5561 TEST_F(ValidationErrorTest, InputTypeNotDefined) {
5562 BuildFileWithErrors(
5563 "name: \"foo.proto\" "
5564 "message_type { name: \"Foo\" } "
5565 "service {"
5566 " name: \"TestService\""
5567 " method { name: \"A\" input_type: \"Bar\" output_type: \"Foo\" }"
5568 "}",
5569
5570 "foo.proto: TestService.A: INPUT_TYPE: \"Bar\" is not defined.\n");
5571 }
5572
TEST_F(ValidationErrorTest,InputTypeNotAMessage)5573 TEST_F(ValidationErrorTest, InputTypeNotAMessage) {
5574 BuildFileWithErrors(
5575 "name: \"foo.proto\" "
5576 "message_type { name: \"Foo\" } "
5577 "enum_type { name: \"Bar\" value { name:\"DUMMY\" number:0 } } "
5578 "service {"
5579 " name: \"TestService\""
5580 " method { name: \"A\" input_type: \"Bar\" output_type: \"Foo\" }"
5581 "}",
5582
5583 "foo.proto: TestService.A: INPUT_TYPE: \"Bar\" is not a message type.\n");
5584 }
5585
TEST_F(ValidationErrorTest,OutputTypeNotDefined)5586 TEST_F(ValidationErrorTest, OutputTypeNotDefined) {
5587 BuildFileWithErrors(
5588 "name: \"foo.proto\" "
5589 "message_type { name: \"Foo\" } "
5590 "service {"
5591 " name: \"TestService\""
5592 " method { name: \"A\" input_type: \"Foo\" output_type: \"Bar\" }"
5593 "}",
5594
5595 "foo.proto: TestService.A: OUTPUT_TYPE: \"Bar\" is not defined.\n");
5596 }
5597
TEST_F(ValidationErrorTest,OutputTypeNotAMessage)5598 TEST_F(ValidationErrorTest, OutputTypeNotAMessage) {
5599 BuildFileWithErrors(
5600 "name: \"foo.proto\" "
5601 "message_type { name: \"Foo\" } "
5602 "enum_type { name: \"Bar\" value { name:\"DUMMY\" number:0 } } "
5603 "service {"
5604 " name: \"TestService\""
5605 " method { name: \"A\" input_type: \"Foo\" output_type: \"Bar\" }"
5606 "}",
5607
5608 "foo.proto: TestService.A: OUTPUT_TYPE: \"Bar\" is not a message "
5609 "type.\n");
5610 }
5611
5612
TEST_F(ValidationErrorTest,IllegalPackedField)5613 TEST_F(ValidationErrorTest, IllegalPackedField) {
5614 BuildFileWithErrors(
5615 "name: \"foo.proto\" "
5616 "message_type {\n"
5617 " name: \"Foo\""
5618 " field { name:\"packed_string\" number:1 label:LABEL_REPEATED "
5619 " type:TYPE_STRING "
5620 " options { uninterpreted_option {"
5621 " name { name_part: \"packed\" is_extension: false }"
5622 " identifier_value: \"true\" }}}\n"
5623 " field { name:\"packed_message\" number:3 label:LABEL_REPEATED "
5624 " type_name: \"Foo\""
5625 " options { uninterpreted_option {"
5626 " name { name_part: \"packed\" is_extension: false }"
5627 " identifier_value: \"true\" }}}\n"
5628 " field { name:\"optional_int32\" number: 4 label: LABEL_OPTIONAL "
5629 " type:TYPE_INT32 "
5630 " options { uninterpreted_option {"
5631 " name { name_part: \"packed\" is_extension: false }"
5632 " identifier_value: \"true\" }}}\n"
5633 "}",
5634
5635 "foo.proto: Foo.packed_string: TYPE: [packed = true] can only be "
5636 "specified for repeated primitive fields.\n"
5637 "foo.proto: Foo.packed_message: TYPE: [packed = true] can only be "
5638 "specified for repeated primitive fields.\n"
5639 "foo.proto: Foo.optional_int32: TYPE: [packed = true] can only be "
5640 "specified for repeated primitive fields.\n");
5641 }
5642
TEST_F(ValidationErrorTest,OptionWrongType)5643 TEST_F(ValidationErrorTest, OptionWrongType) {
5644 BuildFileWithErrors(
5645 "name: \"foo.proto\" "
5646 "message_type { "
5647 " name: \"TestMessage\" "
5648 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_STRING "
5649 " options { uninterpreted_option { name { name_part: \"ctype\" "
5650 " is_extension: false }"
5651 " positive_int_value: 1 }"
5652 " }"
5653 " }"
5654 "}\n",
5655
5656 "foo.proto: TestMessage.foo: OPTION_VALUE: Value must be identifier for "
5657 "enum-valued option \"google.protobuf.FieldOptions.ctype\".\n");
5658 }
5659
TEST_F(ValidationErrorTest,OptionExtendsAtomicType)5660 TEST_F(ValidationErrorTest, OptionExtendsAtomicType) {
5661 BuildFileWithErrors(
5662 "name: \"foo.proto\" "
5663 "message_type { "
5664 " name: \"TestMessage\" "
5665 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_STRING "
5666 " options { uninterpreted_option { name { name_part: \"ctype\" "
5667 " is_extension: false }"
5668 " name { name_part: \"foo\" "
5669 " is_extension: true }"
5670 " positive_int_value: 1 }"
5671 " }"
5672 " }"
5673 "}\n",
5674
5675 "foo.proto: TestMessage.foo: OPTION_NAME: Option \"ctype\" is an "
5676 "atomic type, not a message.\n");
5677 }
5678
TEST_F(ValidationErrorTest,DupOption)5679 TEST_F(ValidationErrorTest, DupOption) {
5680 BuildFileWithErrors(
5681 "name: \"foo.proto\" "
5682 "message_type { "
5683 " name: \"TestMessage\" "
5684 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_UINT32 "
5685 " options { uninterpreted_option { name { name_part: \"ctype\" "
5686 " is_extension: false }"
5687 " identifier_value: \"CORD\" }"
5688 " uninterpreted_option { name { name_part: \"ctype\" "
5689 " is_extension: false }"
5690 " identifier_value: \"CORD\" }"
5691 " }"
5692 " }"
5693 "}\n",
5694
5695 "foo.proto: TestMessage.foo: OPTION_NAME: Option \"ctype\" was "
5696 "already set.\n");
5697 }
5698
TEST_F(ValidationErrorTest,InvalidOptionName)5699 TEST_F(ValidationErrorTest, InvalidOptionName) {
5700 BuildFileWithErrors(
5701 "name: \"foo.proto\" "
5702 "message_type { "
5703 " name: \"TestMessage\" "
5704 " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_BOOL "
5705 " options { uninterpreted_option { "
5706 " name { name_part: \"uninterpreted_option\" "
5707 " is_extension: false }"
5708 " positive_int_value: 1 "
5709 " }"
5710 " }"
5711 " }"
5712 "}\n",
5713
5714 "foo.proto: TestMessage.foo: OPTION_NAME: Option must not use "
5715 "reserved name \"uninterpreted_option\".\n");
5716 }
5717
TEST_F(ValidationErrorTest,RepeatedMessageOption)5718 TEST_F(ValidationErrorTest, RepeatedMessageOption) {
5719 BuildDescriptorMessagesInTestPool();
5720
5721 BuildFileWithErrors(
5722 "name: \"foo.proto\" "
5723 "dependency: \"google/protobuf/descriptor.proto\" "
5724 "message_type: { name: \"Bar\" field: { "
5725 " name: \"foo\" number: 1 label: LABEL_OPTIONAL type: TYPE_INT32 } "
5726 "} "
5727 "extension { name: \"bar\" number: 7672757 label: LABEL_REPEATED "
5728 " type: TYPE_MESSAGE type_name: \"Bar\" "
5729 " extendee: \"google.protobuf.FileOptions\" }"
5730 "options { uninterpreted_option { name { name_part: \"bar\" "
5731 " is_extension: true } "
5732 " name { name_part: \"foo\" "
5733 " is_extension: false } "
5734 " positive_int_value: 1 } }",
5735
5736 "foo.proto: foo.proto: OPTION_NAME: Option field \"(bar)\" is a "
5737 "repeated message. Repeated message options must be initialized "
5738 "using an aggregate value.\n");
5739 }
5740
TEST_F(ValidationErrorTest,ResolveUndefinedOption)5741 TEST_F(ValidationErrorTest, ResolveUndefinedOption) {
5742 // The following should produce an error that baz.bar is resolved but not
5743 // defined.
5744 // foo.proto:
5745 // package baz
5746 // import google/protobuf/descriptor.proto
5747 // message Bar { optional int32 foo = 1; }
5748 // extend FileOptions { optional Bar bar = 7672757; }
5749 //
5750 // moo.proto:
5751 // package moo.baz
5752 // option (baz.bar).foo = 1;
5753 //
5754 // Although "baz.bar" is already defined, the lookup code will try
5755 // "moo.baz.bar", since it's the match from the innermost scope, which will
5756 // cause a symbol not defined error.
5757 BuildDescriptorMessagesInTestPool();
5758
5759 BuildFile(
5760 "name: \"foo.proto\" "
5761 "package: \"baz\" "
5762 "dependency: \"google/protobuf/descriptor.proto\" "
5763 "message_type: { name: \"Bar\" field: { "
5764 " name: \"foo\" number: 1 label: LABEL_OPTIONAL type: TYPE_INT32 } "
5765 "} "
5766 "extension { name: \"bar\" number: 7672757 label: LABEL_OPTIONAL "
5767 " type: TYPE_MESSAGE type_name: \"Bar\" "
5768 " extendee: \"google.protobuf.FileOptions\" }");
5769
5770 BuildFileWithErrors(
5771 "name: \"moo.proto\" "
5772 "package: \"moo.baz\" "
5773 "options { uninterpreted_option { name { name_part: \"baz.bar\" "
5774 " is_extension: true } "
5775 " name { name_part: \"foo\" "
5776 " is_extension: false } "
5777 " positive_int_value: 1 } }",
5778
5779 "moo.proto: moo.proto: OPTION_NAME: Option \"(baz.bar)\" is resolved to "
5780 "\"(moo.baz.bar)\","
5781 " which is not defined. The innermost scope is searched first in name "
5782 "resolution. Consider using a leading '.'(i.e., \"(.baz.bar)\") to start "
5783 "from the outermost scope.\n");
5784 }
5785
TEST_F(ValidationErrorTest,UnknownOption)5786 TEST_F(ValidationErrorTest, UnknownOption) {
5787 BuildFileWithErrors(
5788 "name: \"moo.proto\" "
5789 "package: \"moo.baz\" "
5790 "options { uninterpreted_option { name { name_part: \"baaz.bar\" "
5791 " is_extension: true } "
5792 " name { name_part: \"foo\" "
5793 " is_extension: false } "
5794 " positive_int_value: 1 } }",
5795
5796 "moo.proto: moo.proto: OPTION_NAME: Option \"(baaz.bar)\" unknown. "
5797 "Ensure "
5798 "that your proto definition file imports the proto which defines the "
5799 "option.\n");
5800 }
5801
TEST_F(ValidationErrorTest,CustomOptionConflictingFieldNumber)5802 TEST_F(ValidationErrorTest, CustomOptionConflictingFieldNumber) {
5803 BuildDescriptorMessagesInTestPool();
5804
5805 BuildFileWithErrors(
5806 "name: \"foo.proto\" "
5807 "dependency: \"google/protobuf/descriptor.proto\" "
5808 "extension { name: \"foo1\" number: 7672757 label: LABEL_OPTIONAL "
5809 " type: TYPE_INT32 extendee: \"google.protobuf.FieldOptions\" }"
5810 "extension { name: \"foo2\" number: 7672757 label: LABEL_OPTIONAL "
5811 " type: TYPE_INT32 extendee: \"google.protobuf.FieldOptions\" }",
5812
5813 "foo.proto: foo2: NUMBER: Extension number 7672757 has already been used "
5814 "in \"google.protobuf.FieldOptions\" by extension \"foo1\".\n");
5815 }
5816
TEST_F(ValidationErrorTest,Int32OptionValueOutOfPositiveRange)5817 TEST_F(ValidationErrorTest, Int32OptionValueOutOfPositiveRange) {
5818 BuildDescriptorMessagesInTestPool();
5819
5820 BuildFileWithErrors(
5821 "name: \"foo.proto\" "
5822 "dependency: \"google/protobuf/descriptor.proto\" "
5823 "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
5824 " type: TYPE_INT32 extendee: \"google.protobuf.FileOptions\" }"
5825 "options { uninterpreted_option { name { name_part: \"foo\" "
5826 " is_extension: true } "
5827 " positive_int_value: 0x80000000 } "
5828 "}",
5829
5830 "foo.proto: foo.proto: OPTION_VALUE: Value out of range, -2147483648 to "
5831 "2147483647, for int32 option \"foo\".\n");
5832 }
5833
TEST_F(ValidationErrorTest,Int32OptionValueOutOfNegativeRange)5834 TEST_F(ValidationErrorTest, Int32OptionValueOutOfNegativeRange) {
5835 BuildDescriptorMessagesInTestPool();
5836
5837 BuildFileWithErrors(
5838 "name: \"foo.proto\" "
5839 "dependency: \"google/protobuf/descriptor.proto\" "
5840 "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
5841 " type: TYPE_INT32 extendee: \"google.protobuf.FileOptions\" }"
5842 "options { uninterpreted_option { name { name_part: \"foo\" "
5843 " is_extension: true } "
5844 " negative_int_value: -0x80000001 } "
5845 "}",
5846
5847 "foo.proto: foo.proto: OPTION_VALUE: Value out of range, -2147483648 to "
5848 "2147483647, for int32 option \"foo\".\n");
5849 }
5850
TEST_F(ValidationErrorTest,Int32OptionValueIsNotPositiveInt)5851 TEST_F(ValidationErrorTest, Int32OptionValueIsNotPositiveInt) {
5852 BuildDescriptorMessagesInTestPool();
5853
5854 BuildFileWithErrors(
5855 "name: \"foo.proto\" "
5856 "dependency: \"google/protobuf/descriptor.proto\" "
5857 "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
5858 " type: TYPE_INT32 extendee: \"google.protobuf.FileOptions\" }"
5859 "options { uninterpreted_option { name { name_part: \"foo\" "
5860 " is_extension: true } "
5861 " string_value: \"5\" } }",
5862
5863 "foo.proto: foo.proto: OPTION_VALUE: Value must be integer, from "
5864 "-2147483648 to 2147483647, for int32 option \"foo\".\n");
5865 }
5866
TEST_F(ValidationErrorTest,Int64OptionValueOutOfRange)5867 TEST_F(ValidationErrorTest, Int64OptionValueOutOfRange) {
5868 BuildDescriptorMessagesInTestPool();
5869
5870 BuildFileWithErrors(
5871 "name: \"foo.proto\" "
5872 "dependency: \"google/protobuf/descriptor.proto\" "
5873 "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
5874 " type: TYPE_INT64 extendee: \"google.protobuf.FileOptions\" }"
5875 "options { uninterpreted_option { name { name_part: \"foo\" "
5876 " is_extension: true } "
5877 " positive_int_value: 0x8000000000000000 "
5878 "} "
5879 "}",
5880
5881 "foo.proto: foo.proto: OPTION_VALUE: Value out of range, "
5882 "-9223372036854775808 to 9223372036854775807, for int64 option "
5883 "\"foo\".\n");
5884 }
5885
TEST_F(ValidationErrorTest,Int64OptionValueIsNotPositiveInt)5886 TEST_F(ValidationErrorTest, Int64OptionValueIsNotPositiveInt) {
5887 BuildDescriptorMessagesInTestPool();
5888
5889 BuildFileWithErrors(
5890 "name: \"foo.proto\" "
5891 "dependency: \"google/protobuf/descriptor.proto\" "
5892 "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
5893 " type: TYPE_INT64 extendee: \"google.protobuf.FileOptions\" }"
5894 "options { uninterpreted_option { name { name_part: \"foo\" "
5895 " is_extension: true } "
5896 " identifier_value: \"5\" } }",
5897
5898 "foo.proto: foo.proto: OPTION_VALUE: Value must be integer, from "
5899 "-9223372036854775808 to 9223372036854775807, for int64 option "
5900 "\"foo\".\n");
5901 }
5902
TEST_F(ValidationErrorTest,UInt32OptionValueOutOfRange)5903 TEST_F(ValidationErrorTest, UInt32OptionValueOutOfRange) {
5904 BuildDescriptorMessagesInTestPool();
5905
5906 BuildFileWithErrors(
5907 "name: \"foo.proto\" "
5908 "dependency: \"google/protobuf/descriptor.proto\" "
5909 "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
5910 " type: TYPE_UINT32 extendee: \"google.protobuf.FileOptions\" }"
5911 "options { uninterpreted_option { name { name_part: \"foo\" "
5912 " is_extension: true } "
5913 " positive_int_value: 0x100000000 } }",
5914
5915 "foo.proto: foo.proto: OPTION_VALUE: Value out of range, 0 to "
5916 "4294967295, for uint32 option \"foo\".\n");
5917 }
5918
TEST_F(ValidationErrorTest,UInt32OptionValueIsNotPositiveInt)5919 TEST_F(ValidationErrorTest, UInt32OptionValueIsNotPositiveInt) {
5920 BuildDescriptorMessagesInTestPool();
5921
5922 BuildFileWithErrors(
5923 "name: \"foo.proto\" "
5924 "dependency: \"google/protobuf/descriptor.proto\" "
5925 "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
5926 " type: TYPE_UINT32 extendee: \"google.protobuf.FileOptions\" }"
5927 "options { uninterpreted_option { name { name_part: \"foo\" "
5928 " is_extension: true } "
5929 " double_value: -5.6 } }",
5930
5931 "foo.proto: foo.proto: OPTION_VALUE: Value must be integer, from 0 to "
5932 "4294967295, for uint32 option \"foo\".\n");
5933 }
5934
TEST_F(ValidationErrorTest,UInt64OptionValueIsNotPositiveInt)5935 TEST_F(ValidationErrorTest, UInt64OptionValueIsNotPositiveInt) {
5936 BuildDescriptorMessagesInTestPool();
5937
5938 BuildFileWithErrors(
5939 "name: \"foo.proto\" "
5940 "dependency: \"google/protobuf/descriptor.proto\" "
5941 "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
5942 " type: TYPE_UINT64 extendee: \"google.protobuf.FileOptions\" }"
5943 "options { uninterpreted_option { name { name_part: \"foo\" "
5944 " is_extension: true } "
5945 " negative_int_value: -5 } }",
5946
5947 "foo.proto: foo.proto: OPTION_VALUE: Value must be integer, from 0 to "
5948 "18446744073709551615, for uint64 option \"foo\".\n");
5949 }
5950
TEST_F(ValidationErrorTest,FloatOptionValueIsNotNumber)5951 TEST_F(ValidationErrorTest, FloatOptionValueIsNotNumber) {
5952 BuildDescriptorMessagesInTestPool();
5953
5954 BuildFileWithErrors(
5955 "name: \"foo.proto\" "
5956 "dependency: \"google/protobuf/descriptor.proto\" "
5957 "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
5958 " type: TYPE_FLOAT extendee: \"google.protobuf.FileOptions\" }"
5959 "options { uninterpreted_option { name { name_part: \"foo\" "
5960 " is_extension: true } "
5961 " string_value: \"bar\" } }",
5962
5963 "foo.proto: foo.proto: OPTION_VALUE: Value must be number "
5964 "for float option \"foo\".\n");
5965 }
5966
TEST_F(ValidationErrorTest,DoubleOptionValueIsNotNumber)5967 TEST_F(ValidationErrorTest, DoubleOptionValueIsNotNumber) {
5968 BuildDescriptorMessagesInTestPool();
5969
5970 BuildFileWithErrors(
5971 "name: \"foo.proto\" "
5972 "dependency: \"google/protobuf/descriptor.proto\" "
5973 "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
5974 " type: TYPE_DOUBLE extendee: \"google.protobuf.FileOptions\" }"
5975 "options { uninterpreted_option { name { name_part: \"foo\" "
5976 " is_extension: true } "
5977 " string_value: \"bar\" } }",
5978
5979 "foo.proto: foo.proto: OPTION_VALUE: Value must be number "
5980 "for double option \"foo\".\n");
5981 }
5982
TEST_F(ValidationErrorTest,BoolOptionValueIsNotTrueOrFalse)5983 TEST_F(ValidationErrorTest, BoolOptionValueIsNotTrueOrFalse) {
5984 BuildDescriptorMessagesInTestPool();
5985
5986 BuildFileWithErrors(
5987 "name: \"foo.proto\" "
5988 "dependency: \"google/protobuf/descriptor.proto\" "
5989 "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
5990 " type: TYPE_BOOL extendee: \"google.protobuf.FileOptions\" }"
5991 "options { uninterpreted_option { name { name_part: \"foo\" "
5992 " is_extension: true } "
5993 " identifier_value: \"bar\" } }",
5994
5995 "foo.proto: foo.proto: OPTION_VALUE: Value must be \"true\" or \"false\" "
5996 "for boolean option \"foo\".\n");
5997 }
5998
TEST_F(ValidationErrorTest,EnumOptionValueIsNotIdentifier)5999 TEST_F(ValidationErrorTest, EnumOptionValueIsNotIdentifier) {
6000 BuildDescriptorMessagesInTestPool();
6001
6002 BuildFileWithErrors(
6003 "name: \"foo.proto\" "
6004 "dependency: \"google/protobuf/descriptor.proto\" "
6005 "enum_type { name: \"FooEnum\" value { name: \"BAR\" number: 1 } "
6006 " value { name: \"BAZ\" number: 2 } }"
6007 "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
6008 " type: TYPE_ENUM type_name: \"FooEnum\" "
6009 " extendee: \"google.protobuf.FileOptions\" }"
6010 "options { uninterpreted_option { name { name_part: \"foo\" "
6011 " is_extension: true } "
6012 " string_value: \"MOOO\" } }",
6013
6014 "foo.proto: foo.proto: OPTION_VALUE: Value must be identifier for "
6015 "enum-valued option \"foo\".\n");
6016 }
6017
TEST_F(ValidationErrorTest,EnumOptionValueIsNotEnumValueName)6018 TEST_F(ValidationErrorTest, EnumOptionValueIsNotEnumValueName) {
6019 BuildDescriptorMessagesInTestPool();
6020
6021 BuildFileWithErrors(
6022 "name: \"foo.proto\" "
6023 "dependency: \"google/protobuf/descriptor.proto\" "
6024 "enum_type { name: \"FooEnum\" value { name: \"BAR\" number: 1 } "
6025 " value { name: \"BAZ\" number: 2 } }"
6026 "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
6027 " type: TYPE_ENUM type_name: \"FooEnum\" "
6028 " extendee: \"google.protobuf.FileOptions\" }"
6029 "options { uninterpreted_option { name { name_part: \"foo\" "
6030 " is_extension: true } "
6031 " identifier_value: \"MOOO\" } }",
6032
6033 "foo.proto: foo.proto: OPTION_VALUE: Enum type \"FooEnum\" has no value "
6034 "named \"MOOO\" for option \"foo\".\n");
6035 }
6036
TEST_F(ValidationErrorTest,EnumOptionValueIsSiblingEnumValueName)6037 TEST_F(ValidationErrorTest, EnumOptionValueIsSiblingEnumValueName) {
6038 BuildDescriptorMessagesInTestPool();
6039
6040 BuildFileWithErrors(
6041 "name: \"foo.proto\" "
6042 "dependency: \"google/protobuf/descriptor.proto\" "
6043 "enum_type { name: \"FooEnum1\" value { name: \"BAR\" number: 1 } "
6044 " value { name: \"BAZ\" number: 2 } }"
6045 "enum_type { name: \"FooEnum2\" value { name: \"MOO\" number: 1 } "
6046 " value { name: \"MOOO\" number: 2 } }"
6047 "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
6048 " type: TYPE_ENUM type_name: \"FooEnum1\" "
6049 " extendee: \"google.protobuf.FileOptions\" }"
6050 "options { uninterpreted_option { name { name_part: \"foo\" "
6051 " is_extension: true } "
6052 " identifier_value: \"MOOO\" } }",
6053
6054 "foo.proto: foo.proto: OPTION_VALUE: Enum type \"FooEnum1\" has no value "
6055 "named \"MOOO\" for option \"foo\". This appears to be a value from a "
6056 "sibling type.\n");
6057 }
6058
TEST_F(ValidationErrorTest,StringOptionValueIsNotString)6059 TEST_F(ValidationErrorTest, StringOptionValueIsNotString) {
6060 BuildDescriptorMessagesInTestPool();
6061
6062 BuildFileWithErrors(
6063 "name: \"foo.proto\" "
6064 "dependency: \"google/protobuf/descriptor.proto\" "
6065 "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
6066 " type: TYPE_STRING extendee: \"google.protobuf.FileOptions\" }"
6067 "options { uninterpreted_option { name { name_part: \"foo\" "
6068 " is_extension: true } "
6069 " identifier_value: \"MOOO\" } }",
6070
6071 "foo.proto: foo.proto: OPTION_VALUE: Value must be quoted string "
6072 "for string option \"foo\".\n");
6073 }
6074
TEST_F(ValidationErrorTest,JsonNameOptionOnExtensions)6075 TEST_F(ValidationErrorTest, JsonNameOptionOnExtensions) {
6076 BuildFileWithErrors(
6077 "name: \"foo.proto\" "
6078 "package: \"foo\" "
6079 "message_type {"
6080 " name: \"Foo\""
6081 " extension_range { start: 10 end: 20 }"
6082 "}"
6083 "extension {"
6084 " name: \"value\""
6085 " number: 10"
6086 " label: LABEL_OPTIONAL"
6087 " type: TYPE_INT32"
6088 " extendee: \"foo.Foo\""
6089 " json_name: \"myName\""
6090 "}",
6091 "foo.proto: foo.value: OPTION_NAME: option json_name is not allowed on "
6092 "extension fields.\n");
6093 }
6094
TEST_F(ValidationErrorTest,JsonNameEmbeddedNull)6095 TEST_F(ValidationErrorTest, JsonNameEmbeddedNull) {
6096 BuildFileWithErrors(
6097 "name: \"foo.proto\" "
6098 "package: \"foo\" "
6099 "message_type {"
6100 " name: \"Foo\""
6101 " field {"
6102 " name: \"value\""
6103 " number: 10"
6104 " label: LABEL_OPTIONAL"
6105 " type: TYPE_INT32"
6106 " json_name: \"embedded\\000null\""
6107 " }"
6108 "}",
6109 "foo.proto: foo.Foo.value: OPTION_NAME: json_name cannot have embedded "
6110 "null characters.\n");
6111 }
6112
TEST_F(ValidationErrorTest,DuplicateExtensionFieldNumber)6113 TEST_F(ValidationErrorTest, DuplicateExtensionFieldNumber) {
6114 BuildDescriptorMessagesInTestPool();
6115
6116 BuildFile(
6117 "name: \"foo.proto\" "
6118 "dependency: \"google/protobuf/descriptor.proto\" "
6119 "extension { name: \"option1\" number: 1000 label: LABEL_OPTIONAL "
6120 " type: TYPE_INT32 extendee: \"google.protobuf.FileOptions\" }");
6121
6122 BuildFileWithWarnings(
6123 "name: \"bar.proto\" "
6124 "dependency: \"google/protobuf/descriptor.proto\" "
6125 "extension { name: \"option2\" number: 1000 label: LABEL_OPTIONAL "
6126 " type: TYPE_INT32 extendee: \"google.protobuf.FileOptions\" }",
6127 "bar.proto: option2: NUMBER: Extension number 1000 has already been used "
6128 "in \"google.protobuf.FileOptions\" by extension \"option1\" defined in "
6129 "foo.proto.\n");
6130 }
6131
6132 // Helper function for tests that check for aggregate value parsing
6133 // errors. The "value" argument is embedded inside the
6134 // "uninterpreted_option" portion of the result.
EmbedAggregateValue(const char * value)6135 static std::string EmbedAggregateValue(const char* value) {
6136 return absl::Substitute(
6137 "name: \"foo.proto\" "
6138 "dependency: \"google/protobuf/descriptor.proto\" "
6139 "message_type { name: \"Foo\" } "
6140 "extension { name: \"foo\" number: 7672757 label: LABEL_OPTIONAL "
6141 " type: TYPE_MESSAGE type_name: \"Foo\" "
6142 " extendee: \"google.protobuf.FileOptions\" }"
6143 "options { uninterpreted_option { name { name_part: \"foo\" "
6144 " is_extension: true } "
6145 " $0 } }",
6146 value);
6147 }
6148
TEST_F(ValidationErrorTest,AggregateValueNotFound)6149 TEST_F(ValidationErrorTest, AggregateValueNotFound) {
6150 BuildDescriptorMessagesInTestPool();
6151
6152 BuildFileWithErrors(
6153 EmbedAggregateValue("string_value: \"\""),
6154 "foo.proto: foo.proto: OPTION_VALUE: Option \"foo\" is a message. "
6155 "To set the entire message, use syntax like "
6156 "\"foo = { <proto text format> }\". To set fields within it, use "
6157 "syntax like \"foo.foo = value\".\n");
6158 }
6159
TEST_F(ValidationErrorTest,AggregateValueParseError)6160 TEST_F(ValidationErrorTest, AggregateValueParseError) {
6161 BuildDescriptorMessagesInTestPool();
6162
6163 BuildFileWithErrors(
6164 EmbedAggregateValue("aggregate_value: \"1+2\""),
6165 "foo.proto: foo.proto: OPTION_VALUE: Error while parsing option "
6166 "value for \"foo\": Expected identifier, got: 1\n");
6167 }
6168
TEST_F(ValidationErrorTest,AggregateValueUnknownFields)6169 TEST_F(ValidationErrorTest, AggregateValueUnknownFields) {
6170 BuildDescriptorMessagesInTestPool();
6171
6172 BuildFileWithErrors(
6173 EmbedAggregateValue("aggregate_value: \"x:100\""),
6174 "foo.proto: foo.proto: OPTION_VALUE: Error while parsing option "
6175 "value for \"foo\": Message type \"Foo\" has no field named \"x\".\n");
6176 }
6177
TEST_F(ValidationErrorTest,NotLiteImportsLite)6178 TEST_F(ValidationErrorTest, NotLiteImportsLite) {
6179 BuildFile(
6180 "name: \"bar.proto\" "
6181 "options { optimize_for: LITE_RUNTIME } ");
6182
6183 BuildFileWithErrors(
6184 "name: \"foo.proto\" "
6185 "dependency: \"bar.proto\" ",
6186
6187 "foo.proto: bar.proto: IMPORT: Files that do not use optimize_for = "
6188 "LITE_RUNTIME cannot import files which do use this option. This file "
6189 "is not lite, but it imports \"bar.proto\" which is.\n");
6190 }
6191
TEST_F(ValidationErrorTest,LiteExtendsNotLite)6192 TEST_F(ValidationErrorTest, LiteExtendsNotLite) {
6193 BuildFile(
6194 "name: \"bar.proto\" "
6195 "message_type: {"
6196 " name: \"Bar\""
6197 " extension_range { start: 1 end: 1000 }"
6198 "}");
6199
6200 BuildFileWithErrors(
6201 "name: \"foo.proto\" "
6202 "dependency: \"bar.proto\" "
6203 "options { optimize_for: LITE_RUNTIME } "
6204 "extension { name: \"ext\" number: 123 label: LABEL_OPTIONAL "
6205 " type: TYPE_INT32 extendee: \"Bar\" }",
6206
6207 "foo.proto: ext: EXTENDEE: Extensions to non-lite types can only be "
6208 "declared in non-lite files. Note that you cannot extend a non-lite "
6209 "type to contain a lite type, but the reverse is allowed.\n");
6210 }
6211
TEST_F(ValidationErrorTest,NoLiteServices)6212 TEST_F(ValidationErrorTest, NoLiteServices) {
6213 BuildFileWithErrors(
6214 "name: \"foo.proto\" "
6215 "options {"
6216 " optimize_for: LITE_RUNTIME"
6217 " cc_generic_services: true"
6218 " java_generic_services: true"
6219 "} "
6220 "service { name: \"Foo\" }",
6221
6222 "foo.proto: Foo: NAME: Files with optimize_for = LITE_RUNTIME cannot "
6223 "define services unless you set both options cc_generic_services and "
6224 "java_generic_services to false.\n");
6225
6226 BuildFile(
6227 "name: \"bar.proto\" "
6228 "options {"
6229 " optimize_for: LITE_RUNTIME"
6230 " cc_generic_services: false"
6231 " java_generic_services: false"
6232 "} "
6233 "service { name: \"Bar\" }");
6234 }
6235
TEST_F(ValidationErrorTest,RollbackAfterError)6236 TEST_F(ValidationErrorTest, RollbackAfterError) {
6237 // Build a file which contains every kind of construct but references an
6238 // undefined type. All these constructs will be added to the symbol table
6239 // before the undefined type error is noticed. The DescriptorPool will then
6240 // have to roll everything back.
6241 BuildFileWithErrors(
6242 "name: \"foo.proto\" "
6243 "message_type {"
6244 " name: \"TestMessage\""
6245 " field { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 }"
6246 "} "
6247 "enum_type {"
6248 " name: \"TestEnum\""
6249 " value { name:\"BAR\" number:1 }"
6250 "} "
6251 "service {"
6252 " name: \"TestService\""
6253 " method {"
6254 " name: \"Baz\""
6255 " input_type: \"NoSuchType\"" // error
6256 " output_type: \"TestMessage\""
6257 " }"
6258 "}",
6259
6260 "foo.proto: TestService.Baz: INPUT_TYPE: \"NoSuchType\" is not "
6261 "defined.\n");
6262
6263 // Make sure that if we build the same file again with the error fixed,
6264 // it works. If the above rollback was incomplete, then some symbols will
6265 // be left defined, and this second attempt will fail since it tries to
6266 // re-define the same symbols.
6267 BuildFile(
6268 "name: \"foo.proto\" "
6269 "message_type {"
6270 " name: \"TestMessage\""
6271 " field { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 }"
6272 "} "
6273 "enum_type {"
6274 " name: \"TestEnum\""
6275 " value { name:\"BAR\" number:1 }"
6276 "} "
6277 "service {"
6278 " name: \"TestService\""
6279 " method { name:\"Baz\""
6280 " input_type:\"TestMessage\""
6281 " output_type:\"TestMessage\" }"
6282 "}");
6283 }
6284
TEST_F(ValidationErrorTest,ErrorsReportedToLogError)6285 TEST_F(ValidationErrorTest, ErrorsReportedToLogError) {
6286 // Test that errors are reported to ABSL_LOG(ERROR) if no error collector is
6287 // provided.
6288
6289 FileDescriptorProto file_proto;
6290 ASSERT_TRUE(
6291 TextFormat::ParseFromString("name: \"foo.proto\" "
6292 "message_type { name: \"Foo\" } "
6293 "message_type { name: \"Foo\" } ",
6294 &file_proto));
6295 {
6296 absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
6297 EXPECT_CALL(log, Log(absl::LogSeverity::kError, testing::_,
6298 "Invalid proto descriptor for file \"foo.proto\":"));
6299 EXPECT_CALL(log, Log(absl::LogSeverity::kError, testing::_,
6300 " Foo: \"Foo\" is already defined."));
6301 log.StartCapturingLogs();
6302 EXPECT_TRUE(pool_.BuildFile(file_proto) == nullptr);
6303 }
6304 }
6305
TEST_F(ValidationErrorTest,DisallowEnumAlias)6306 TEST_F(ValidationErrorTest, DisallowEnumAlias) {
6307 BuildFileWithErrors(
6308 "name: \"foo.proto\" "
6309 "enum_type {"
6310 " name: \"Bar\""
6311 " value { name:\"ENUM_A\" number:0 }"
6312 " value { name:\"ENUM_B\" number:0 }"
6313 "}",
6314 "foo.proto: Bar: NUMBER: "
6315 "\"ENUM_B\" uses the same enum value as \"ENUM_A\". "
6316 "If this is intended, set 'option allow_alias = true;' to the enum "
6317 "definition. The next available enum value is 1.\n");
6318
6319 BuildFileWithErrors(
6320 R"pb(
6321 name: "foo.proto"
6322 enum_type {
6323 name: "Bar"
6324 value { name: "ENUM_A" number: 10 }
6325 value { name: "ENUM_B" number: 10 }
6326 value { name: "ENUM_C" number: 11 }
6327 value { name: "ENUM_D" number: 20 }
6328 })pb",
6329 "foo.proto: Bar: NUMBER: "
6330 "\"ENUM_B\" uses the same enum value as \"ENUM_A\". "
6331 "If this is intended, set 'option allow_alias = true;' to the enum "
6332 "definition. The next available enum value is 12.\n");
6333
6334 BuildFileWithErrors(
6335 absl::Substitute(R"pb(
6336 name: "foo.proto"
6337 enum_type {
6338 name: "Bar"
6339 value { name: "ENUM_A" number: $0 }
6340 value { name: "ENUM_B" number: $0 }
6341 })pb",
6342 std::numeric_limits<int32_t>::max()),
6343 "foo.proto: Bar: NUMBER: "
6344 "\"ENUM_B\" uses the same enum value as \"ENUM_A\". "
6345 "If this is intended, set 'option allow_alias = true;' to the enum "
6346 "definition.\n");
6347 }
6348
TEST_F(ValidationErrorTest,AllowEnumAlias)6349 TEST_F(ValidationErrorTest, AllowEnumAlias) {
6350 BuildFile(
6351 "name: \"foo.proto\" "
6352 "enum_type {"
6353 " name: \"Bar\""
6354 " value { name:\"ENUM_A\" number:0 }"
6355 " value { name:\"ENUM_B\" number:0 }"
6356 " options { allow_alias: true }"
6357 "}");
6358 }
6359
TEST_F(ValidationErrorTest,UnusedImportWarning)6360 TEST_F(ValidationErrorTest, UnusedImportWarning) {
6361 pool_.AddDirectInputFile("bar.proto");
6362 BuildFile(
6363 "name: \"bar.proto\" "
6364 "message_type { name: \"Bar\" }");
6365
6366 pool_.AddDirectInputFile("base.proto");
6367 BuildFile(
6368 "name: \"base.proto\" "
6369 "message_type { name: \"Base\" }");
6370
6371 pool_.AddDirectInputFile("baz.proto");
6372 BuildFile(
6373 "name: \"baz.proto\" "
6374 "message_type { name: \"Baz\" }");
6375
6376 pool_.AddDirectInputFile("public.proto");
6377 BuildFile(
6378 "name: \"public.proto\" "
6379 "dependency: \"bar.proto\""
6380 "public_dependency: 0");
6381
6382 // // forward.proto
6383 // import "base.proto" // No warning: Base message is used.
6384 // import "bar.proto" // Will log a warning.
6385 // import public "baz.proto" // No warning: Do not track import public.
6386 // import "public.proto" // No warning: public.proto has import public.
6387 // message Forward {
6388 // optional Base base = 1;
6389 // }
6390 //
6391 pool_.AddDirectInputFile("forward.proto");
6392 BuildFileWithWarnings(
6393 "name: \"forward.proto\""
6394 "dependency: \"base.proto\""
6395 "dependency: \"bar.proto\""
6396 "dependency: \"baz.proto\""
6397 "dependency: \"public.proto\""
6398 "public_dependency: 2 "
6399 "message_type {"
6400 " name: \"Forward\""
6401 " field { name:\"base\" number:1 label:LABEL_OPTIONAL "
6402 "type_name:\"Base\" }"
6403 "}",
6404 "forward.proto: bar.proto: IMPORT: Import bar.proto is unused.\n");
6405 }
6406
6407 // Verifies that the dependency checker isn't fooled by package symbols,
6408 // which can be defined in multiple files.
TEST_F(ValidationErrorTest,SamePackageUnusedImportError)6409 TEST_F(ValidationErrorTest, SamePackageUnusedImportError) {
6410 BuildFile(R"pb(
6411 name: "unused_dependency.proto"
6412 package: "protobuf_unittest.subpackage"
6413 message_type { name: "Foo" }
6414 )pb");
6415
6416 BuildFile(R"pb(
6417 name: "used_dependency.proto"
6418 package: "protobuf_unittest.subpackage"
6419 message_type { name: "Bar" }
6420 )pb");
6421
6422 pool_.AddDirectInputFile("import.proto", true);
6423 BuildFileWithErrors(R"pb(
6424 name: "import.proto"
6425 package: "protobuf_unittest"
6426 dependency: "unused_dependency.proto"
6427 dependency: "used_dependency.proto"
6428 message_type {
6429 name: "Baz"
6430 field {
6431 name: "bar"
6432 number: 1
6433 label: LABEL_OPTIONAL
6434 type: TYPE_MESSAGE
6435 type_name: "subpackage.Bar"
6436 }
6437 }
6438 )pb",
6439 "import.proto: unused_dependency.proto: "
6440 "IMPORT: Import unused_dependency.proto is unused.\n");
6441 }
6442
6443 namespace {
FillValidMapEntry(FileDescriptorProto * file_proto)6444 void FillValidMapEntry(FileDescriptorProto* file_proto) {
6445 ASSERT_TRUE(TextFormat::ParseFromString(
6446 "name: 'foo.proto' "
6447 "message_type { "
6448 " name: 'Foo' "
6449 " field { "
6450 " name: 'foo_map' number: 1 label:LABEL_REPEATED "
6451 " type_name: 'FooMapEntry' "
6452 " } "
6453 " nested_type { "
6454 " name: 'FooMapEntry' "
6455 " options { map_entry: true } "
6456 " field { "
6457 " name: 'key' number: 1 type:TYPE_INT32 label:LABEL_OPTIONAL "
6458 " } "
6459 " field { "
6460 " name: 'value' number: 2 type:TYPE_INT32 label:LABEL_OPTIONAL "
6461 " } "
6462 " } "
6463 "} "
6464 "message_type { "
6465 " name: 'Bar' "
6466 " extension_range { start: 1 end: 10 }"
6467 "} ",
6468 file_proto));
6469 }
6470 static const char* kMapEntryErrorMessage =
6471 "foo.proto: Foo.foo_map: TYPE: map_entry should not be set explicitly. "
6472 "Use map<KeyType, ValueType> instead.\n";
6473 static const char* kMapEntryKeyTypeErrorMessage =
6474 "foo.proto: Foo.foo_map: TYPE: Key in map fields cannot be float/double, "
6475 "bytes or message types.\n";
6476
6477 } // namespace
6478
TEST_F(ValidationErrorTest,MapEntryBase)6479 TEST_F(ValidationErrorTest, MapEntryBase) {
6480 FileDescriptorProto file_proto;
6481 FillValidMapEntry(&file_proto);
6482 std::string text_proto;
6483 TextFormat::PrintToString(file_proto, &text_proto);
6484 BuildFile(text_proto);
6485 }
6486
TEST_F(ValidationErrorTest,MapEntryExtensionRange)6487 TEST_F(ValidationErrorTest, MapEntryExtensionRange) {
6488 FileDescriptorProto file_proto;
6489 FillValidMapEntry(&file_proto);
6490 TextFormat::MergeFromString(
6491 "extension_range { "
6492 " start: 10 end: 20 "
6493 "} ",
6494 file_proto.mutable_message_type(0)->mutable_nested_type(0));
6495 BuildFileWithErrors(file_proto, kMapEntryErrorMessage);
6496 }
6497
TEST_F(ValidationErrorTest,MapEntryExtension)6498 TEST_F(ValidationErrorTest, MapEntryExtension) {
6499 FileDescriptorProto file_proto;
6500 FillValidMapEntry(&file_proto);
6501 TextFormat::MergeFromString(
6502 "extension { "
6503 " name: 'foo_ext' extendee: '.Bar' number: 5"
6504 "} ",
6505 file_proto.mutable_message_type(0)->mutable_nested_type(0));
6506 BuildFileWithErrors(file_proto, kMapEntryErrorMessage);
6507 }
6508
TEST_F(ValidationErrorTest,MapEntryNestedType)6509 TEST_F(ValidationErrorTest, MapEntryNestedType) {
6510 FileDescriptorProto file_proto;
6511 FillValidMapEntry(&file_proto);
6512 TextFormat::MergeFromString(
6513 "nested_type { "
6514 " name: 'Bar' "
6515 "} ",
6516 file_proto.mutable_message_type(0)->mutable_nested_type(0));
6517 BuildFileWithErrors(file_proto, kMapEntryErrorMessage);
6518 }
6519
TEST_F(ValidationErrorTest,MapEntryEnumTypes)6520 TEST_F(ValidationErrorTest, MapEntryEnumTypes) {
6521 FileDescriptorProto file_proto;
6522 FillValidMapEntry(&file_proto);
6523 TextFormat::MergeFromString(
6524 "enum_type { "
6525 " name: 'BarEnum' "
6526 " value { name: 'BAR_BAR' number:0 } "
6527 "} ",
6528 file_proto.mutable_message_type(0)->mutable_nested_type(0));
6529 BuildFileWithErrors(file_proto, kMapEntryErrorMessage);
6530 }
6531
TEST_F(ValidationErrorTest,MapEntryExtraField)6532 TEST_F(ValidationErrorTest, MapEntryExtraField) {
6533 FileDescriptorProto file_proto;
6534 FillValidMapEntry(&file_proto);
6535 TextFormat::MergeFromString(
6536 "field { "
6537 " name: 'other_field' "
6538 " label: LABEL_OPTIONAL "
6539 " type: TYPE_INT32 "
6540 " number: 3 "
6541 "} ",
6542 file_proto.mutable_message_type(0)->mutable_nested_type(0));
6543 BuildFileWithErrors(file_proto, kMapEntryErrorMessage);
6544 }
6545
TEST_F(ValidationErrorTest,MapEntryMessageName)6546 TEST_F(ValidationErrorTest, MapEntryMessageName) {
6547 FileDescriptorProto file_proto;
6548 FillValidMapEntry(&file_proto);
6549 file_proto.mutable_message_type(0)->mutable_nested_type(0)->set_name(
6550 "OtherMapEntry");
6551 file_proto.mutable_message_type(0)->mutable_field(0)->set_type_name(
6552 "OtherMapEntry");
6553 BuildFileWithErrors(file_proto, kMapEntryErrorMessage);
6554 }
6555
TEST_F(ValidationErrorTest,MapEntryNoneRepeatedMapEntry)6556 TEST_F(ValidationErrorTest, MapEntryNoneRepeatedMapEntry) {
6557 FileDescriptorProto file_proto;
6558 FillValidMapEntry(&file_proto);
6559 file_proto.mutable_message_type(0)->mutable_field(0)->set_label(
6560 FieldDescriptorProto::LABEL_OPTIONAL);
6561 BuildFileWithErrors(file_proto, kMapEntryErrorMessage);
6562 }
6563
TEST_F(ValidationErrorTest,MapEntryDifferentContainingType)6564 TEST_F(ValidationErrorTest, MapEntryDifferentContainingType) {
6565 FileDescriptorProto file_proto;
6566 FillValidMapEntry(&file_proto);
6567 // Move the nested MapEntry message into the top level, which should not pass
6568 // the validation.
6569 file_proto.mutable_message_type()->AddAllocated(
6570 file_proto.mutable_message_type(0)->mutable_nested_type()->ReleaseLast());
6571 BuildFileWithErrors(file_proto, kMapEntryErrorMessage);
6572 }
6573
TEST_F(ValidationErrorTest,MapEntryKeyName)6574 TEST_F(ValidationErrorTest, MapEntryKeyName) {
6575 FileDescriptorProto file_proto;
6576 FillValidMapEntry(&file_proto);
6577 FieldDescriptorProto* key =
6578 file_proto.mutable_message_type(0)->mutable_nested_type(0)->mutable_field(
6579 0);
6580 key->set_name("Key");
6581 BuildFileWithErrors(file_proto, kMapEntryErrorMessage);
6582 }
6583
TEST_F(ValidationErrorTest,MapEntryKeyLabel)6584 TEST_F(ValidationErrorTest, MapEntryKeyLabel) {
6585 FileDescriptorProto file_proto;
6586 FillValidMapEntry(&file_proto);
6587 FieldDescriptorProto* key =
6588 file_proto.mutable_message_type(0)->mutable_nested_type(0)->mutable_field(
6589 0);
6590 key->set_label(FieldDescriptorProto::LABEL_REQUIRED);
6591 BuildFileWithErrors(file_proto, kMapEntryErrorMessage);
6592 }
6593
TEST_F(ValidationErrorTest,MapEntryKeyNumber)6594 TEST_F(ValidationErrorTest, MapEntryKeyNumber) {
6595 FileDescriptorProto file_proto;
6596 FillValidMapEntry(&file_proto);
6597 FieldDescriptorProto* key =
6598 file_proto.mutable_message_type(0)->mutable_nested_type(0)->mutable_field(
6599 0);
6600 key->set_number(3);
6601 BuildFileWithErrors(file_proto, kMapEntryErrorMessage);
6602 }
6603
TEST_F(ValidationErrorTest,MapEntryValueName)6604 TEST_F(ValidationErrorTest, MapEntryValueName) {
6605 FileDescriptorProto file_proto;
6606 FillValidMapEntry(&file_proto);
6607 FieldDescriptorProto* value =
6608 file_proto.mutable_message_type(0)->mutable_nested_type(0)->mutable_field(
6609 1);
6610 value->set_name("Value");
6611 BuildFileWithErrors(file_proto, kMapEntryErrorMessage);
6612 }
6613
TEST_F(ValidationErrorTest,MapEntryValueLabel)6614 TEST_F(ValidationErrorTest, MapEntryValueLabel) {
6615 FileDescriptorProto file_proto;
6616 FillValidMapEntry(&file_proto);
6617 FieldDescriptorProto* value =
6618 file_proto.mutable_message_type(0)->mutable_nested_type(0)->mutable_field(
6619 1);
6620 value->set_label(FieldDescriptorProto::LABEL_REQUIRED);
6621 BuildFileWithErrors(file_proto, kMapEntryErrorMessage);
6622 }
6623
TEST_F(ValidationErrorTest,MapEntryValueNumber)6624 TEST_F(ValidationErrorTest, MapEntryValueNumber) {
6625 FileDescriptorProto file_proto;
6626 FillValidMapEntry(&file_proto);
6627 FieldDescriptorProto* value =
6628 file_proto.mutable_message_type(0)->mutable_nested_type(0)->mutable_field(
6629 1);
6630 value->set_number(3);
6631 BuildFileWithErrors(file_proto, kMapEntryErrorMessage);
6632 }
6633
TEST_F(ValidationErrorTest,MapEntryKeyTypeFloat)6634 TEST_F(ValidationErrorTest, MapEntryKeyTypeFloat) {
6635 FileDescriptorProto file_proto;
6636 FillValidMapEntry(&file_proto);
6637 FieldDescriptorProto* key =
6638 file_proto.mutable_message_type(0)->mutable_nested_type(0)->mutable_field(
6639 0);
6640 key->set_type(FieldDescriptorProto::TYPE_FLOAT);
6641 BuildFileWithErrors(file_proto, kMapEntryKeyTypeErrorMessage);
6642 }
6643
TEST_F(ValidationErrorTest,MapEntryKeyTypeDouble)6644 TEST_F(ValidationErrorTest, MapEntryKeyTypeDouble) {
6645 FileDescriptorProto file_proto;
6646 FillValidMapEntry(&file_proto);
6647 FieldDescriptorProto* key =
6648 file_proto.mutable_message_type(0)->mutable_nested_type(0)->mutable_field(
6649 0);
6650 key->set_type(FieldDescriptorProto::TYPE_DOUBLE);
6651 BuildFileWithErrors(file_proto, kMapEntryKeyTypeErrorMessage);
6652 }
6653
TEST_F(ValidationErrorTest,MapEntryKeyTypeBytes)6654 TEST_F(ValidationErrorTest, MapEntryKeyTypeBytes) {
6655 FileDescriptorProto file_proto;
6656 FillValidMapEntry(&file_proto);
6657 FieldDescriptorProto* key =
6658 file_proto.mutable_message_type(0)->mutable_nested_type(0)->mutable_field(
6659 0);
6660 key->set_type(FieldDescriptorProto::TYPE_BYTES);
6661 BuildFileWithErrors(file_proto, kMapEntryKeyTypeErrorMessage);
6662 }
6663
TEST_F(ValidationErrorTest,MapEntryKeyTypeEnum)6664 TEST_F(ValidationErrorTest, MapEntryKeyTypeEnum) {
6665 FileDescriptorProto file_proto;
6666 FillValidMapEntry(&file_proto);
6667 FieldDescriptorProto* key =
6668 file_proto.mutable_message_type(0)->mutable_nested_type(0)->mutable_field(
6669 0);
6670 key->clear_type();
6671 key->set_type_name("BarEnum");
6672 EnumDescriptorProto* enum_proto = file_proto.add_enum_type();
6673 enum_proto->set_name("BarEnum");
6674 EnumValueDescriptorProto* enum_value_proto = enum_proto->add_value();
6675 enum_value_proto->set_name("BAR_VALUE0");
6676 enum_value_proto->set_number(0);
6677 BuildFileWithErrors(file_proto,
6678 "foo.proto: Foo.foo_map: TYPE: Key in map fields cannot "
6679 "be enum types.\n");
6680 // Enum keys are not allowed in proto3 as well.
6681 // Get rid of extensions for proto3 to make it proto3 compatible.
6682 file_proto.mutable_message_type()->RemoveLast();
6683 file_proto.set_syntax("proto3");
6684 BuildFileWithErrors(file_proto,
6685 "foo.proto: Foo.foo_map: TYPE: Key in map fields cannot "
6686 "be enum types.\n");
6687 }
6688
TEST_F(ValidationErrorTest,MapEntryKeyTypeMessage)6689 TEST_F(ValidationErrorTest, MapEntryKeyTypeMessage) {
6690 FileDescriptorProto file_proto;
6691 FillValidMapEntry(&file_proto);
6692 FieldDescriptorProto* key =
6693 file_proto.mutable_message_type(0)->mutable_nested_type(0)->mutable_field(
6694 0);
6695 key->clear_type();
6696 key->set_type_name(".Bar");
6697 BuildFileWithErrors(file_proto, kMapEntryKeyTypeErrorMessage);
6698 }
6699
TEST_F(ValidationErrorTest,MapEntryConflictsWithField)6700 TEST_F(ValidationErrorTest, MapEntryConflictsWithField) {
6701 FileDescriptorProto file_proto;
6702 FillValidMapEntry(&file_proto);
6703 TextFormat::MergeFromString(
6704 "field { "
6705 " name: 'FooMapEntry' "
6706 " type: TYPE_INT32 "
6707 " label: LABEL_OPTIONAL "
6708 " number: 100 "
6709 "}",
6710 file_proto.mutable_message_type(0));
6711 BuildFileWithErrors(
6712 file_proto,
6713 "foo.proto: Foo.FooMapEntry: NAME: \"FooMapEntry\" is already defined in "
6714 "\"Foo\".\n"
6715 "foo.proto: Foo.foo_map: TYPE: \"FooMapEntry\" is not defined.\n"
6716 "foo.proto: Foo: NAME: Expanded map entry type FooMapEntry conflicts "
6717 "with an existing field.\n");
6718 }
6719
TEST_F(ValidationErrorTest,MapEntryConflictsWithMessage)6720 TEST_F(ValidationErrorTest, MapEntryConflictsWithMessage) {
6721 FileDescriptorProto file_proto;
6722 FillValidMapEntry(&file_proto);
6723 TextFormat::MergeFromString(
6724 "nested_type { "
6725 " name: 'FooMapEntry' "
6726 "}",
6727 file_proto.mutable_message_type(0));
6728 BuildFileWithErrors(
6729 file_proto,
6730 "foo.proto: Foo.FooMapEntry: NAME: \"FooMapEntry\" is already defined in "
6731 "\"Foo\".\n"
6732 "foo.proto: Foo: NAME: Expanded map entry type FooMapEntry conflicts "
6733 "with an existing nested message type.\n");
6734 }
6735
TEST_F(ValidationErrorTest,MapEntryConflictsWithEnum)6736 TEST_F(ValidationErrorTest, MapEntryConflictsWithEnum) {
6737 FileDescriptorProto file_proto;
6738 FillValidMapEntry(&file_proto);
6739 TextFormat::MergeFromString(
6740 "enum_type { "
6741 " name: 'FooMapEntry' "
6742 " value { name: 'ENTRY_FOO' number: 0 }"
6743 "}",
6744 file_proto.mutable_message_type(0));
6745 BuildFileWithErrors(
6746 file_proto,
6747 "foo.proto: Foo.FooMapEntry: NAME: \"FooMapEntry\" is already defined in "
6748 "\"Foo\".\n"
6749 "foo.proto: Foo: NAME: Expanded map entry type FooMapEntry conflicts "
6750 "with an existing enum type.\n");
6751 }
6752
TEST_F(ValidationErrorTest,Proto3EnumValuesConflictWithDifferentCasing)6753 TEST_F(ValidationErrorTest, Proto3EnumValuesConflictWithDifferentCasing) {
6754 BuildFileWithErrors(
6755 "syntax: 'proto3'"
6756 "name: 'foo.proto' "
6757 "enum_type {"
6758 " name: 'FooEnum' "
6759 " value { name: 'BAR' number: 0 }"
6760 " value { name: 'bar' number: 1 }"
6761 "}",
6762 "foo.proto: bar: NAME: Enum name bar has the same name as BAR "
6763 "if you ignore case and strip out the enum name prefix (if any). "
6764 "(If you are using allow_alias, please assign the same number "
6765 "to each enum value name.)\n");
6766
6767 BuildFileWithErrors(
6768 "syntax: 'proto2'"
6769 "name: 'foo.proto' "
6770 "enum_type {"
6771 " name: 'FooEnum' "
6772 " value { name: 'BAR' number: 0 }"
6773 " value { name: 'bar' number: 1 }"
6774 "}",
6775 "foo.proto: bar: NAME: Enum name bar has the same name as BAR "
6776 "if you ignore case and strip out the enum name prefix (if any). "
6777 "(If you are using allow_alias, please assign the same number "
6778 "to each enum value name.)\n");
6779
6780 // Not an error because both enums are mapped to the same value.
6781 BuildFile(
6782 "syntax: 'proto3'"
6783 "name: 'foo.proto' "
6784 "enum_type {"
6785 " name: 'FooEnum' "
6786 " options { allow_alias: true }"
6787 " value { name: 'UNKNOWN' number: 0 }"
6788 " value { name: 'BAR' number: 1 }"
6789 " value { name: 'bar' number: 1 }"
6790 "}");
6791 }
6792
TEST_F(ValidationErrorTest,EnumValuesConflictWhenPrefixesStripped)6793 TEST_F(ValidationErrorTest, EnumValuesConflictWhenPrefixesStripped) {
6794 BuildFileWithErrors(
6795 "syntax: 'proto3'"
6796 "name: 'foo.proto' "
6797 "enum_type {"
6798 " name: 'FooEnum' "
6799 " value { name: 'FOO_ENUM_BAZ' number: 0 }"
6800 " value { name: 'BAZ' number: 1 }"
6801 "}",
6802 "foo.proto: BAZ: NAME: Enum name BAZ has the same name as FOO_ENUM_BAZ "
6803 "if you ignore case and strip out the enum name prefix (if any). "
6804 "(If you are using allow_alias, please assign the same number "
6805 "to each enum value name.)\n");
6806
6807 BuildFileWithErrors(
6808 "syntax: 'proto3'"
6809 "name: 'foo.proto' "
6810 "enum_type {"
6811 " name: 'FooEnum' "
6812 " value { name: 'FOOENUM_BAZ' number: 0 }"
6813 " value { name: 'BAZ' number: 1 }"
6814 "}",
6815 "foo.proto: BAZ: NAME: Enum name BAZ has the same name as FOOENUM_BAZ "
6816 "if you ignore case and strip out the enum name prefix (if any). "
6817 "(If you are using allow_alias, please assign the same number "
6818 "to each enum value name.)\n");
6819
6820 BuildFileWithErrors(
6821 "syntax: 'proto3'"
6822 "name: 'foo.proto' "
6823 "enum_type {"
6824 " name: 'FooEnum' "
6825 " value { name: 'FOO_ENUM_BAR_BAZ' number: 0 }"
6826 " value { name: 'BAR__BAZ' number: 1 }"
6827 "}",
6828 "foo.proto: BAR__BAZ: NAME: Enum name BAR__BAZ has the same name as "
6829 "FOO_ENUM_BAR_BAZ if you ignore case and strip out the enum name prefix "
6830 "(if any). (If you are using allow_alias, please assign the same number "
6831 "to each enum value name.)\n");
6832
6833 BuildFileWithErrors(
6834 "syntax: 'proto3'"
6835 "name: 'foo.proto' "
6836 "enum_type {"
6837 " name: 'FooEnum' "
6838 " value { name: 'FOO_ENUM__BAR_BAZ' number: 0 }"
6839 " value { name: 'BAR_BAZ' number: 1 }"
6840 "}",
6841 "foo.proto: BAR_BAZ: NAME: Enum name BAR_BAZ has the same name as "
6842 "FOO_ENUM__BAR_BAZ if you ignore case and strip out the enum name prefix "
6843 "(if any). (If you are using allow_alias, please assign the same number "
6844 "to each enum value name.)\n");
6845
6846 BuildFileWithErrors(
6847 "syntax: 'proto2'"
6848 "name: 'foo.proto' "
6849 "enum_type {"
6850 " name: 'FooEnum' "
6851 " value { name: 'FOO_ENUM__BAR_BAZ' number: 0 }"
6852 " value { name: 'BAR_BAZ' number: 1 }"
6853 "}",
6854 "foo.proto: BAR_BAZ: NAME: Enum name BAR_BAZ has the same name as "
6855 "FOO_ENUM__BAR_BAZ if you ignore case and strip out the enum name prefix "
6856 "(if any). (If you are using allow_alias, please assign the same number "
6857 "to each enum value name.)\n");
6858
6859 // This isn't an error because the underscore will cause the PascalCase to
6860 // differ by case (BarBaz vs. Barbaz).
6861 BuildFile(
6862 "syntax: 'proto3'"
6863 "name: 'foo.proto' "
6864 "enum_type {"
6865 " name: 'FooEnum' "
6866 " value { name: 'BAR_BAZ' number: 0 }"
6867 " value { name: 'BARBAZ' number: 1 }"
6868 "}");
6869 }
6870
TEST_F(ValidationErrorTest,EnumValuesConflictLegacyBehavior)6871 TEST_F(ValidationErrorTest, EnumValuesConflictLegacyBehavior) {
6872 BuildFileWithErrors(
6873 "syntax: 'proto3'"
6874 "name: 'foo.proto' "
6875 "enum_type {"
6876 " name: 'FooEnum' "
6877 " options { deprecated_legacy_json_field_conflicts: true }"
6878 " value { name: 'BAR' number: 0 }"
6879 " value { name: 'bar' number: 1 }"
6880 "}",
6881 "foo.proto: bar: NAME: Enum name bar has the same name as BAR "
6882 "if you ignore case and strip out the enum name prefix (if any). "
6883 "(If you are using allow_alias, please assign the same number "
6884 "to each enum value name.)\n");
6885
6886 BuildFileWithErrors(
6887 "syntax: 'proto3'"
6888 "name: 'foo.proto' "
6889 "enum_type {"
6890 " name: 'FooEnum' "
6891 " options { deprecated_legacy_json_field_conflicts: true }"
6892 " value { name: 'FOO_ENUM__BAR_BAZ' number: 0 }"
6893 " value { name: 'BAR_BAZ' number: 1 }"
6894 "}",
6895 "foo.proto: BAR_BAZ: NAME: Enum name BAR_BAZ has the same name as "
6896 "FOO_ENUM__BAR_BAZ if you ignore case and strip out the enum name "
6897 "prefix "
6898 "(if any). (If you are using allow_alias, please assign the same "
6899 "number to each enum value name.)\n");
6900
6901 BuildFileWithWarnings(
6902 "syntax: 'proto2'"
6903 "name: 'foo.proto' "
6904 "enum_type {"
6905 " name: 'FooEnum' "
6906 " options { deprecated_legacy_json_field_conflicts: true }"
6907 " value { name: 'BAR' number: 0 }"
6908 " value { name: 'bar' number: 1 }"
6909 "}",
6910 "foo.proto: bar: NAME: Enum name bar has the same name as BAR "
6911 "if you ignore case and strip out the enum name prefix (if any). "
6912 "(If you are using allow_alias, please assign the same number "
6913 "to each enum value name.)\n");
6914 }
6915
TEST_F(ValidationErrorTest,MapEntryConflictsWithOneof)6916 TEST_F(ValidationErrorTest, MapEntryConflictsWithOneof) {
6917 FileDescriptorProto file_proto;
6918 FillValidMapEntry(&file_proto);
6919 TextFormat::MergeFromString(
6920 "oneof_decl { "
6921 " name: 'FooMapEntry' "
6922 "}"
6923 "field { "
6924 " name: 'int_field' "
6925 " type: TYPE_INT32 "
6926 " label: LABEL_OPTIONAL "
6927 " oneof_index: 0 "
6928 " number: 100 "
6929 "} ",
6930 file_proto.mutable_message_type(0));
6931 BuildFileWithErrors(
6932 file_proto,
6933 "foo.proto: Foo.FooMapEntry: NAME: \"FooMapEntry\" is already defined in "
6934 "\"Foo\".\n"
6935 "foo.proto: Foo.foo_map: TYPE: \"FooMapEntry\" is not defined.\n"
6936 "foo.proto: Foo: NAME: Expanded map entry type FooMapEntry conflicts "
6937 "with an existing oneof type.\n");
6938 }
6939
TEST_F(ValidationErrorTest,MapEntryUsesNoneZeroEnumDefaultValue)6940 TEST_F(ValidationErrorTest, MapEntryUsesNoneZeroEnumDefaultValue) {
6941 BuildFileWithErrors(
6942 "name: \"foo.proto\" "
6943 "enum_type {"
6944 " name: \"Bar\""
6945 " value { name:\"ENUM_A\" number:1 }"
6946 " value { name:\"ENUM_B\" number:2 }"
6947 "}"
6948 "message_type {"
6949 " name: 'Foo' "
6950 " field { "
6951 " name: 'foo_map' number: 1 label:LABEL_REPEATED "
6952 " type_name: 'FooMapEntry' "
6953 " } "
6954 " nested_type { "
6955 " name: 'FooMapEntry' "
6956 " options { map_entry: true } "
6957 " field { "
6958 " name: 'key' number: 1 type:TYPE_INT32 label:LABEL_OPTIONAL "
6959 " } "
6960 " field { "
6961 " name: 'value' number: 2 type_name:\"Bar\" label:LABEL_OPTIONAL "
6962 " } "
6963 " } "
6964 "}",
6965 "foo.proto: Foo.foo_map: "
6966 "TYPE: Enum value in map must define 0 as the first value.\n");
6967 }
6968
TEST_F(ValidationErrorTest,Proto3RequiredFields)6969 TEST_F(ValidationErrorTest, Proto3RequiredFields) {
6970 BuildFileWithErrors(
6971 "name: 'foo.proto' "
6972 "syntax: 'proto3' "
6973 "message_type { "
6974 " name: 'Foo' "
6975 " field { name:'foo' number:1 label:LABEL_REQUIRED type:TYPE_INT32 } "
6976 "}",
6977 "foo.proto: Foo.foo: TYPE: Required fields are not allowed in "
6978 "proto3.\n");
6979
6980 // applied to nested types as well.
6981 BuildFileWithErrors(
6982 "name: 'foo.proto' "
6983 "syntax: 'proto3' "
6984 "message_type { "
6985 " name: 'Foo' "
6986 " nested_type { "
6987 " name : 'Bar' "
6988 " field { name:'bar' number:1 label:LABEL_REQUIRED type:TYPE_INT32 } "
6989 " } "
6990 "}",
6991 "foo.proto: Foo.Bar.bar: TYPE: Required fields are not allowed in "
6992 "proto3.\n");
6993
6994 // optional and repeated fields are OK.
6995 BuildFile(
6996 "name: 'foo.proto' "
6997 "syntax: 'proto3' "
6998 "message_type { "
6999 " name: 'Foo' "
7000 " field { name:'foo' number:1 label:LABEL_OPTIONAL type:TYPE_INT32 } "
7001 " field { name:'bar' number:2 label:LABEL_REPEATED type:TYPE_INT32 } "
7002 "}");
7003 }
7004
TEST_F(ValidationErrorTest,ValidateProto3DefaultValue)7005 TEST_F(ValidationErrorTest, ValidateProto3DefaultValue) {
7006 BuildFileWithErrors(
7007 "name: 'foo.proto' "
7008 "syntax: 'proto3' "
7009 "message_type { "
7010 " name: 'Foo' "
7011 " field { name:'foo' number:1 label:LABEL_OPTIONAL type:TYPE_INT32 "
7012 " default_value: '1' }"
7013 "}",
7014 "foo.proto: Foo.foo: DEFAULT_VALUE: Explicit default values are not "
7015 "allowed in proto3.\n");
7016
7017 BuildFileWithErrors(
7018 "name: 'foo.proto' "
7019 "syntax: 'proto3' "
7020 "message_type { "
7021 " name: 'Foo' "
7022 " nested_type { "
7023 " name : 'Bar' "
7024 " field { name:'bar' number:1 label:LABEL_OPTIONAL type:TYPE_INT32 "
7025 " default_value: '1' }"
7026 " } "
7027 "}",
7028 "foo.proto: Foo.Bar.bar: DEFAULT_VALUE: Explicit default values are not "
7029 "allowed in proto3.\n");
7030 }
7031
TEST_F(ValidationErrorTest,ValidateProto3ExtensionRange)7032 TEST_F(ValidationErrorTest, ValidateProto3ExtensionRange) {
7033 BuildFileWithErrors(
7034 "name: 'foo.proto' "
7035 "syntax: 'proto3' "
7036 "message_type { "
7037 " name: 'Foo' "
7038 " field { name:'foo' number:1 label:LABEL_OPTIONAL type:TYPE_INT32 } "
7039 " extension_range { start:10 end:100 } "
7040 "}",
7041 "foo.proto: Foo: NUMBER: Extension ranges are not allowed in "
7042 "proto3.\n");
7043
7044 BuildFileWithErrors(
7045 "name: 'foo.proto' "
7046 "syntax: 'proto3' "
7047 "message_type { "
7048 " name: 'Foo' "
7049 " nested_type { "
7050 " name : 'Bar' "
7051 " field { name:'bar' number:1 label:LABEL_OPTIONAL type:TYPE_INT32 } "
7052 " extension_range { start:10 end:100 } "
7053 " } "
7054 "}",
7055 "foo.proto: Foo.Bar: NUMBER: Extension ranges are not allowed in "
7056 "proto3.\n");
7057 }
7058
TEST_F(ValidationErrorTest,ValidateProto3MessageSetWireFormat)7059 TEST_F(ValidationErrorTest, ValidateProto3MessageSetWireFormat) {
7060 BuildFileWithErrors(
7061 "name: 'foo.proto' "
7062 "syntax: 'proto3' "
7063 "message_type { "
7064 " name: 'Foo' "
7065 " options { message_set_wire_format: true } "
7066 "}",
7067 "foo.proto: Foo: NAME: MessageSet is not supported "
7068 "in proto3.\n");
7069 }
7070
TEST_F(ValidationErrorTest,ValidateProto3Enum)7071 TEST_F(ValidationErrorTest, ValidateProto3Enum) {
7072 BuildFileWithErrors(
7073 "name: 'foo.proto' "
7074 "syntax: 'proto3' "
7075 "enum_type { "
7076 " name: 'FooEnum' "
7077 " value { name: 'FOO_FOO' number:1 } "
7078 "}",
7079 "foo.proto: FooEnum: NUMBER: The first enum value must be "
7080 "zero for open enums.\n");
7081
7082 BuildFileWithErrors(
7083 "name: 'foo.proto' "
7084 "syntax: 'proto3' "
7085 "message_type { "
7086 " name: 'Foo' "
7087 " enum_type { "
7088 " name: 'FooEnum' "
7089 " value { name: 'FOO_FOO' number:1 } "
7090 " } "
7091 "}",
7092 "foo.proto: Foo.FooEnum: NUMBER: The first enum value must be "
7093 "zero for open enums.\n");
7094
7095 // valid case.
7096 BuildFile(
7097 "name: 'foo.proto' "
7098 "syntax: 'proto3' "
7099 "enum_type { "
7100 " name: 'FooEnum' "
7101 " value { name: 'FOO_FOO' number:0 } "
7102 "}");
7103 }
7104
TEST_F(ValidationErrorTest,ValidateProto3Group)7105 TEST_F(ValidationErrorTest, ValidateProto3Group) {
7106 BuildFileWithErrors(
7107 "name: 'foo.proto' "
7108 "syntax: 'proto3' "
7109 "message_type { "
7110 " name: 'Foo' "
7111 " nested_type { "
7112 " name: 'FooGroup' "
7113 " } "
7114 " field { name:'foo_group' number: 1 label:LABEL_OPTIONAL "
7115 " type: TYPE_GROUP type_name:'FooGroup' } "
7116 "}",
7117 "foo.proto: Foo.foo_group: TYPE: Groups are not supported in proto3 "
7118 "syntax.\n");
7119 }
7120
7121
TEST_F(ValidationErrorTest,ValidateProto3EnumFromProto2)7122 TEST_F(ValidationErrorTest, ValidateProto3EnumFromProto2) {
7123 // Define an enum in a proto2 file.
7124 BuildFile(
7125 "name: 'foo.proto' "
7126 "package: 'foo' "
7127 "syntax: 'proto2' "
7128 "enum_type { "
7129 " name: 'FooEnum' "
7130 " value { name: 'DEFAULT_OPTION' number:0 } "
7131 "}");
7132
7133 // Now try to refer to it. (All tests in the fixture use the same pool, so we
7134 // can refer to the enum above in this definition.)
7135 BuildFileWithErrors(
7136 "name: 'bar.proto' "
7137 "dependency: 'foo.proto' "
7138 "syntax: 'proto3' "
7139 "message_type { "
7140 " name: 'Foo' "
7141 " field { name:'bar' number:1 label:LABEL_OPTIONAL type:TYPE_ENUM "
7142 " type_name: 'foo.FooEnum' }"
7143 "}",
7144 "bar.proto: Foo.bar: TYPE: Enum type \"foo.FooEnum\" is not an open "
7145 "enum, but is used in \"Foo\" which is a proto3 message type.\n");
7146 }
7147
TEST_F(ValidationErrorTest,ValidateProto3ClosedEnum)7148 TEST_F(ValidationErrorTest, ValidateProto3ClosedEnum) {
7149 // Define a closed enum in an editions file.
7150 BuildFile(R"pb(name: 'foo.proto'
7151 package: 'foo'
7152 syntax: 'editions'
7153 edition: EDITION_2023
7154 enum_type {
7155 name: 'FooEnum'
7156 value { name: 'DEFAULT_OPTION' number: 0 }
7157 options { features { enum_type: CLOSED } }
7158 })pb");
7159
7160 BuildFileWithErrors(
7161 R"pb(name: 'bar.proto'
7162 dependency: 'foo.proto'
7163 syntax: 'proto3'
7164 message_type {
7165 name: 'Foo'
7166 field {
7167 name: 'bar'
7168 number: 1
7169 label: LABEL_OPTIONAL
7170 type: TYPE_ENUM
7171 type_name: 'foo.FooEnum'
7172 }
7173 })pb",
7174 "bar.proto: Foo.bar: TYPE: Enum type \"foo.FooEnum\" is not an open "
7175 "enum, but is used in \"Foo\" which is a proto3 message type.\n");
7176 }
7177
TEST_F(ValidationErrorTest,ValidateProto3OpenEnum)7178 TEST_F(ValidationErrorTest, ValidateProto3OpenEnum) {
7179 // Define an open enum in an editions file.
7180 const FileDescriptor* foo =
7181 BuildFile(R"pb(name: 'foo.proto'
7182 package: 'foo'
7183 syntax: 'editions'
7184 edition: EDITION_2023
7185 enum_type {
7186 name: 'FooEnum'
7187 value { name: 'DEFAULT_OPTION' number: 0 }
7188 })pb");
7189 const EnumDescriptor* enm = foo->enum_type(0);
7190 ASSERT_NE(enm, nullptr);
7191
7192 const FileDescriptor* bar = BuildFile(
7193 R"pb(name: 'bar.proto'
7194 dependency: 'foo.proto'
7195 syntax: 'proto3'
7196 message_type {
7197 name: 'Foo'
7198 field {
7199 name: 'bar'
7200 number: 1
7201 label: LABEL_OPTIONAL
7202 type: TYPE_ENUM
7203 type_name: 'foo.FooEnum'
7204 }
7205 })pb");
7206 ASSERT_NE(bar, nullptr);
7207
7208 EXPECT_EQ(bar->message_type(0)->field(0)->enum_type(), enm);
7209 }
7210
TEST_F(ValidationErrorTest,ValidateProto3Extension)7211 TEST_F(ValidationErrorTest, ValidateProto3Extension) {
7212 // Valid for options.
7213 DescriptorPool pool;
7214 FileDescriptorProto file_proto;
7215 // Add "google/protobuf/descriptor.proto".
7216 FileDescriptorProto::descriptor()->file()->CopyTo(&file_proto);
7217 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
7218 // Add "foo.proto":
7219 // import "google/protobuf/descriptor.proto";
7220 // extend google.protobuf.FileOptions {
7221 // optional string test_file_opt = 1001;
7222 // }
7223 // extend google.protobuf.MessageOptions {
7224 // optional string test_msg_opt = 1002;
7225 // }
7226 // extend google.protobuf.FieldOptions {
7227 // optional string test_field_opt = 1003;
7228 // }
7229 // extend google.protobuf.EnumOptions {
7230 // repeated int32 test_enum_opt = 1004;
7231 // }
7232 // extend google.protobuf.EnumValueOptions {
7233 // optional int32 test_enumval_opt = 1005;
7234 // }
7235 // extend google.protobuf.ServiceOptions {
7236 // repeated int32 test_svc_opt = 1006;
7237 // }
7238 // extend google.protobuf.MethodOptions {
7239 // optional string test_method_opt = 1007;
7240 // }
7241 // extend google.protobuf.OneofOptions {
7242 // optional string test_oneof_opt = 1008;
7243 // }
7244 // extend google.protobuf.ExtensionRangeOptions {
7245 // optional string test_ext_opt = 1009;
7246 // }
7247 file_proto.Clear();
7248 file_proto.set_name("foo.proto");
7249 file_proto.set_syntax("proto3");
7250 file_proto.add_dependency("google/protobuf/descriptor.proto");
7251 AddExtension(&file_proto, "google.protobuf.FileOptions", "test_file_opt", 1001,
7252 FieldDescriptorProto::LABEL_OPTIONAL,
7253 FieldDescriptorProto::TYPE_STRING);
7254 AddExtension(&file_proto, "google.protobuf.MessageOptions", "test_msg_opt", 1001,
7255 FieldDescriptorProto::LABEL_OPTIONAL,
7256 FieldDescriptorProto::TYPE_STRING);
7257 AddExtension(&file_proto, "google.protobuf.FieldOptions", "test_field_opt", 1003,
7258 FieldDescriptorProto::LABEL_OPTIONAL,
7259 FieldDescriptorProto::TYPE_STRING);
7260 AddExtension(&file_proto, "google.protobuf.EnumOptions", "test_enum_opt", 1004,
7261 FieldDescriptorProto::LABEL_REPEATED,
7262 FieldDescriptorProto::TYPE_INT32);
7263 AddExtension(&file_proto, "google.protobuf.EnumValueOptions", "test_enumval_opt", 1005,
7264 FieldDescriptorProto::LABEL_OPTIONAL,
7265 FieldDescriptorProto::TYPE_INT32);
7266 AddExtension(&file_proto, "google.protobuf.ServiceOptions", "test_svc_opt", 1006,
7267 FieldDescriptorProto::LABEL_REPEATED,
7268 FieldDescriptorProto::TYPE_INT32);
7269 AddExtension(&file_proto, "google.protobuf.MethodOptions", "test_method_opt", 1007,
7270 FieldDescriptorProto::LABEL_OPTIONAL,
7271 FieldDescriptorProto::TYPE_STRING);
7272 AddExtension(&file_proto, "google.protobuf.OneofOptions", "test_oneof_opt", 1008,
7273 FieldDescriptorProto::LABEL_OPTIONAL,
7274 FieldDescriptorProto::TYPE_STRING);
7275 AddExtension(&file_proto, "google.protobuf.ExtensionRangeOptions", "test_ext_opt",
7276 1009, FieldDescriptorProto::LABEL_OPTIONAL,
7277 FieldDescriptorProto::TYPE_STRING);
7278 ASSERT_TRUE(pool.BuildFile(file_proto) != nullptr);
7279
7280 // Copy and change the package of the descriptor.proto
7281 BuildFile(
7282 "name: 'google.protobuf.proto' "
7283 "syntax: 'proto2' "
7284 "message_type { "
7285 " name: 'Container' extension_range { start: 1 end: 1000 } "
7286 "}");
7287 BuildFileWithErrors(
7288 "name: 'bar.proto' "
7289 "syntax: 'proto3' "
7290 "dependency: 'google.protobuf.proto' "
7291 "extension { "
7292 " name: 'bar' number: 1 label: LABEL_OPTIONAL type: TYPE_INT32 "
7293 " extendee: 'Container' "
7294 "}",
7295 "bar.proto: bar: EXTENDEE: Extensions in proto3 are only allowed for "
7296 "defining options.\n");
7297 }
7298
7299 // Test that field names that may conflict in JSON is not allowed by protoc.
TEST_F(ValidationErrorTest,ValidateJsonNameConflictProto3)7300 TEST_F(ValidationErrorTest, ValidateJsonNameConflictProto3) {
7301 // The comparison is case-insensitive.
7302 BuildFileWithErrors(
7303 "name: 'foo.proto' "
7304 "syntax: 'proto3' "
7305 "message_type {"
7306 " name: 'Foo'"
7307 " field { name:'_name' number:1 label:LABEL_OPTIONAL type:TYPE_INT32 }"
7308 " field { name:'Name' number:2 label:LABEL_OPTIONAL type:TYPE_INT32 }"
7309 "}",
7310 "foo.proto: Foo: NAME: The default JSON name of field \"Name\" "
7311 "(\"Name\") "
7312 "conflicts with the default JSON name of field \"_name\".\n");
7313
7314 // Underscores are ignored.
7315 BuildFileWithErrors(
7316 "name: 'foo.proto' "
7317 "syntax: 'proto3' "
7318 "message_type {"
7319 " name: 'Foo'"
7320 " field { name:'AB' number:1 label:LABEL_OPTIONAL type:TYPE_INT32 }"
7321 " field { name:'_a__b_' number:2 label:LABEL_OPTIONAL type:TYPE_INT32 }"
7322 "}",
7323 "foo.proto: Foo: NAME: The default JSON name of field \"_a__b_\" "
7324 "(\"AB\") "
7325 "conflicts with the default JSON name of field \"AB\".\n");
7326 }
7327
TEST_F(ValidationErrorTest,ValidateJsonNameConflictProto2)7328 TEST_F(ValidationErrorTest, ValidateJsonNameConflictProto2) {
7329 BuildFileWithWarnings(
7330 "name: 'foo.proto' "
7331 "syntax: 'proto2' "
7332 "message_type {"
7333 " name: 'Foo'"
7334 " field { name:'AB' number:1 label:LABEL_OPTIONAL type:TYPE_INT32 }"
7335 " field { name:'_a__b_' number:2 label:LABEL_OPTIONAL type:TYPE_INT32 }"
7336 "}",
7337 "foo.proto: Foo: NAME: The default JSON name of field \"_a__b_\" "
7338 "(\"AB\") "
7339 "conflicts with the default JSON name of field \"AB\".\n");
7340 }
7341
7342 // Test that field names that may conflict in JSON is not allowed by protoc.
TEST_F(ValidationErrorTest,ValidateJsonNameConflictProto3Legacy)7343 TEST_F(ValidationErrorTest, ValidateJsonNameConflictProto3Legacy) {
7344 BuildFile(
7345 "name: 'foo.proto' "
7346 "syntax: 'proto3' "
7347 "message_type {"
7348 " name: 'Foo'"
7349 " options { deprecated_legacy_json_field_conflicts: true }"
7350 " field { name:'AB' number:1 label:LABEL_OPTIONAL type:TYPE_INT32 }"
7351 " field { name:'_a__b_' number:2 label:LABEL_OPTIONAL type:TYPE_INT32 }"
7352 "}");
7353 }
7354
TEST_F(ValidationErrorTest,ValidateJsonNameConflictProto2Legacy)7355 TEST_F(ValidationErrorTest, ValidateJsonNameConflictProto2Legacy) {
7356 BuildFile(
7357 "name: 'foo.proto' "
7358 "syntax: 'proto2' "
7359 "message_type {"
7360 " name: 'Foo'"
7361 " options { deprecated_legacy_json_field_conflicts: true }"
7362 " field { name:'AB' number:1 label:LABEL_OPTIONAL type:TYPE_INT32 }"
7363 " field { name:'_a__b_' number:2 label:LABEL_OPTIONAL type:TYPE_INT32 }"
7364 "}");
7365 }
7366
7367
TEST_F(ValidationErrorTest,UnusedImportWithOtherError)7368 TEST_F(ValidationErrorTest, UnusedImportWithOtherError) {
7369 BuildFile(
7370 "name: 'bar.proto' "
7371 "message_type {"
7372 " name: 'Bar'"
7373 "}");
7374
7375 pool_.AddDirectInputFile("foo.proto", true);
7376 BuildFileWithErrors(
7377 "name: 'foo.proto' "
7378 "dependency: 'bar.proto' "
7379 "message_type {"
7380 " name: 'Foo'"
7381 " extension { name:'foo' number:1 label:LABEL_OPTIONAL type:TYPE_INT32"
7382 " extendee: 'Baz' }"
7383 "}",
7384
7385 // Should not also contain unused import error.
7386 "foo.proto: Foo.foo: EXTENDEE: \"Baz\" is not defined.\n");
7387 }
7388
TEST(IsGroupLike,GroupLikeDelimited)7389 TEST(IsGroupLike, GroupLikeDelimited) {
7390 using internal::cpp::IsGroupLike;
7391 const Descriptor& msg = *editions_unittest::TestDelimited::descriptor();
7392 const FileDescriptor& file =
7393 *editions_unittest::TestDelimited::descriptor()->file();
7394
7395 EXPECT_EQ(msg.FindFieldByName("grouplike")->type(),
7396 FieldDescriptor::TYPE_GROUP);
7397 EXPECT_TRUE(IsGroupLike(*msg.FindFieldByName("grouplike")));
7398 EXPECT_EQ(file.FindExtensionByName("grouplikefilescope")->type(),
7399 FieldDescriptor::TYPE_GROUP);
7400 EXPECT_TRUE(IsGroupLike(*file.FindExtensionByName("grouplikefilescope")));
7401 }
7402
TEST(IsGroupLike,GroupLikeNotDelimited)7403 TEST(IsGroupLike, GroupLikeNotDelimited) {
7404 using internal::cpp::IsGroupLike;
7405 const Descriptor& msg = *editions_unittest::TestDelimited::descriptor();
7406 const FileDescriptor& file =
7407 *editions_unittest::TestDelimited::descriptor()->file();
7408
7409 EXPECT_EQ(msg.FindFieldByName("lengthprefixed")->type(),
7410 FieldDescriptor::TYPE_MESSAGE);
7411 EXPECT_FALSE(IsGroupLike(*msg.FindFieldByName("lengthprefixed")));
7412 EXPECT_EQ(file.FindExtensionByName("lengthprefixed")->type(),
7413 FieldDescriptor::TYPE_MESSAGE);
7414 EXPECT_FALSE(IsGroupLike(*file.FindExtensionByName("lengthprefixed")));
7415 }
7416
TEST(IsGroupLike,GroupLikeMismatchedName)7417 TEST(IsGroupLike, GroupLikeMismatchedName) {
7418 using internal::cpp::IsGroupLike;
7419 const Descriptor& msg = *editions_unittest::TestDelimited::descriptor();
7420 const FileDescriptor& file =
7421 *editions_unittest::TestDelimited::descriptor()->file();
7422
7423 EXPECT_EQ(msg.FindFieldByName("notgrouplike")->type(),
7424 FieldDescriptor::TYPE_GROUP);
7425 EXPECT_FALSE(IsGroupLike(*msg.FindFieldByName("notgrouplike")));
7426 EXPECT_EQ(file.FindExtensionByName("not_group_like_scope")->type(),
7427 FieldDescriptor::TYPE_GROUP);
7428 EXPECT_FALSE(IsGroupLike(*file.FindExtensionByName("not_group_like_scope")));
7429 }
7430
TEST(IsGroupLike,GroupLikeMismatchedScope)7431 TEST(IsGroupLike, GroupLikeMismatchedScope) {
7432 using internal::cpp::IsGroupLike;
7433 const Descriptor& msg = *editions_unittest::TestDelimited::descriptor();
7434 const FileDescriptor& file =
7435 *editions_unittest::TestDelimited::descriptor()->file();
7436
7437 EXPECT_EQ(msg.FindFieldByName("notgrouplikescope")->type(),
7438 FieldDescriptor::TYPE_GROUP);
7439 EXPECT_FALSE(IsGroupLike(*msg.FindFieldByName("notgrouplikescope")));
7440 EXPECT_EQ(file.FindExtensionByName("grouplike")->type(),
7441 FieldDescriptor::TYPE_GROUP);
7442 EXPECT_FALSE(IsGroupLike(*file.FindExtensionByName("grouplike")));
7443 }
7444
TEST(IsGroupLike,GroupLikeMismatchedFile)7445 TEST(IsGroupLike, GroupLikeMismatchedFile) {
7446 using internal::cpp::IsGroupLike;
7447 const Descriptor& msg = *editions_unittest::TestDelimited::descriptor();
7448 const FileDescriptor& file =
7449 *editions_unittest::TestDelimited::descriptor()->file();
7450
7451 EXPECT_EQ(msg.FindFieldByName("messageimport")->type(),
7452 FieldDescriptor::TYPE_GROUP);
7453 EXPECT_FALSE(IsGroupLike(*msg.FindFieldByName("messageimport")));
7454 EXPECT_EQ(file.FindExtensionByName("messageimport")->type(),
7455 FieldDescriptor::TYPE_GROUP);
7456 EXPECT_FALSE(IsGroupLike(*file.FindExtensionByName("messageimport")));
7457 }
7458
7459 using FeaturesBaseTest = ValidationErrorTest;
7460
7461 class FeaturesTest : public FeaturesBaseTest {
7462 protected:
SetUp()7463 void SetUp() override {
7464 ValidationErrorTest::SetUp();
7465
7466 auto default_spec = FeatureResolver::CompileDefaults(
7467 FeatureSet::descriptor(),
7468 {GetExtensionReflection(pb::cpp), GetExtensionReflection(pb::test),
7469 GetExtensionReflection(pb::TestMessage::test_message),
7470 GetExtensionReflection(pb::TestMessage::Nested::test_nested)},
7471 EDITION_PROTO2, EDITION_99999_TEST_ONLY);
7472 ASSERT_OK(default_spec);
7473 ASSERT_OK(pool_.SetFeatureSetDefaults(std::move(default_spec).value()));
7474 }
7475 };
7476
7477 template <typename T>
GetFeatures(const T * descriptor)7478 const FeatureSet& GetFeatures(const T* descriptor) {
7479 return internal::InternalFeatureHelper::GetFeatures<T>(*descriptor);
7480 }
7481
7482 template <typename T>
GetCoreFeatures(const T * descriptor)7483 FeatureSet GetCoreFeatures(const T* descriptor) {
7484 FeatureSet features = GetFeatures(descriptor);
7485 // Strip test features to avoid excessive brittleness.
7486 features.ClearExtension(pb::test);
7487 features.ClearExtension(pb::TestMessage::test_message);
7488 features.ClearExtension(pb::TestMessage::Nested::test_nested);
7489 return features;
7490 }
7491
TEST_F(FeaturesTest,InvalidProto2Features)7492 TEST_F(FeaturesTest, InvalidProto2Features) {
7493 BuildDescriptorMessagesInTestPool();
7494 BuildFileWithErrors(
7495 R"pb(
7496 name: "foo.proto"
7497 syntax: "proto2"
7498 options { features { field_presence: IMPLICIT } }
7499 )pb",
7500 "foo.proto: foo.proto: EDITIONS: Features are only valid under "
7501 "editions.\n");
7502 }
7503
TEST_F(FeaturesTest,InvalidProto3Features)7504 TEST_F(FeaturesTest, InvalidProto3Features) {
7505 BuildDescriptorMessagesInTestPool();
7506 BuildFileWithErrors(
7507 R"pb(
7508 name: "foo.proto"
7509 syntax: "proto3"
7510 options { features { field_presence: IMPLICIT } }
7511 )pb",
7512 "foo.proto: foo.proto: EDITIONS: Features are only valid "
7513 "under editions.\n");
7514 }
7515
TEST_F(FeaturesTest,Proto2Features)7516 TEST_F(FeaturesTest, Proto2Features) {
7517 FileDescriptorProto file_proto = ParseTextOrDie(R"pb(
7518 name: "foo.proto"
7519 message_type {
7520 name: "Foo"
7521 field { name: "bar" number: 1 label: LABEL_OPTIONAL type: TYPE_INT64 }
7522 field {
7523 name: "group"
7524 number: 2
7525 label: LABEL_OPTIONAL
7526 type: TYPE_GROUP
7527 type_name: ".Foo"
7528 }
7529 field { name: "str" number: 3 label: LABEL_OPTIONAL type: TYPE_STRING }
7530 field { name: "rep" number: 4 label: LABEL_REPEATED type: TYPE_INT32 }
7531 field {
7532 name: "packed"
7533 number: 5
7534 label: LABEL_REPEATED
7535 type: TYPE_INT64
7536 options { packed: true }
7537 }
7538 field { name: "utf8" number: 6 label: LABEL_REPEATED type: TYPE_STRING }
7539 field { name: "req" number: 7 label: LABEL_REQUIRED type: TYPE_INT32 }
7540 field {
7541 name: "cord"
7542 number: 8
7543 label: LABEL_OPTIONAL
7544 type: TYPE_BYTES
7545 options { ctype: CORD }
7546 }
7547 field {
7548 name: "piece"
7549 number: 9
7550 label: LABEL_OPTIONAL
7551 type: TYPE_STRING
7552 options { ctype: STRING_PIECE }
7553 }
7554 }
7555 enum_type {
7556 name: "Foo2"
7557 value { name: "BAR" number: 1 }
7558 }
7559 )pb");
7560
7561 BuildDescriptorMessagesInTestPool();
7562 BuildFileInTestPool(pb::CppFeatures::GetDescriptor()->file());
7563 const FileDescriptor* file = ABSL_DIE_IF_NULL(pool_.BuildFile(file_proto));
7564 const Descriptor* message = file->message_type(0);
7565 const FieldDescriptor* field = message->field(0);
7566 const FieldDescriptor* group = message->field(1);
7567 EXPECT_THAT(file->options(), EqualsProto(""));
7568 EXPECT_EQ(GetFeatures(file).GetExtension(pb::test).file_feature(),
7569 pb::VALUE1);
7570 EXPECT_THAT(GetCoreFeatures(file), EqualsProto(R"pb(
7571 field_presence: EXPLICIT
7572 enum_type: CLOSED
7573 repeated_field_encoding: EXPANDED
7574 utf8_validation: NONE
7575 message_encoding: LENGTH_PREFIXED
7576 json_format: LEGACY_BEST_EFFORT
7577 [pb.cpp] {
7578 legacy_closed_enum: true
7579 string_type: STRING
7580 enum_name_uses_string_view: false
7581 })pb"));
7582 EXPECT_THAT(GetCoreFeatures(field), EqualsProto(R"pb(
7583 field_presence: EXPLICIT
7584 enum_type: CLOSED
7585 repeated_field_encoding: EXPANDED
7586 utf8_validation: NONE
7587 message_encoding: LENGTH_PREFIXED
7588 json_format: LEGACY_BEST_EFFORT
7589 [pb.cpp] {
7590 legacy_closed_enum: true
7591 string_type: STRING
7592 enum_name_uses_string_view: false
7593 })pb"));
7594 EXPECT_THAT(GetCoreFeatures(group), EqualsProto(R"pb(
7595 field_presence: EXPLICIT
7596 enum_type: CLOSED
7597 repeated_field_encoding: EXPANDED
7598 utf8_validation: NONE
7599 message_encoding: DELIMITED
7600 json_format: LEGACY_BEST_EFFORT
7601 [pb.cpp] {
7602 legacy_closed_enum: true
7603 string_type: STRING
7604 enum_name_uses_string_view: false
7605 })pb"));
7606 EXPECT_TRUE(field->has_presence());
7607 EXPECT_FALSE(field->requires_utf8_validation());
7608 EXPECT_EQ(
7609 GetUtf8CheckMode(message->FindFieldByName("str"), /*is_lite=*/false),
7610 Utf8CheckMode::kVerify);
7611 EXPECT_EQ(GetUtf8CheckMode(message->FindFieldByName("str"), /*is_lite=*/true),
7612 Utf8CheckMode::kNone);
7613 EXPECT_EQ(GetCoreFeatures(message->FindFieldByName("cord"))
7614 .GetExtension(pb::cpp)
7615 .string_type(),
7616 pb::CppFeatures::CORD);
7617 EXPECT_FALSE(field->is_packed());
7618 EXPECT_FALSE(field->legacy_enum_field_treated_as_closed());
7619 EXPECT_FALSE(HasPreservingUnknownEnumSemantics(field));
7620 EXPECT_FALSE(message->FindFieldByName("str")->requires_utf8_validation());
7621 EXPECT_FALSE(message->FindFieldByName("rep")->is_packed());
7622 EXPECT_FALSE(message->FindFieldByName("utf8")->requires_utf8_validation());
7623 EXPECT_TRUE(message->FindFieldByName("packed")->is_packed());
7624 EXPECT_TRUE(message->FindFieldByName("req")->is_required());
7625 EXPECT_TRUE(file->enum_type(0)->is_closed());
7626
7627 EXPECT_EQ(message->FindFieldByName("str")->cpp_string_type(),
7628 FieldDescriptor::CppStringType::kString);
7629 EXPECT_EQ(message->FindFieldByName("cord")->cpp_string_type(),
7630 FieldDescriptor::CppStringType::kCord);
7631
7632 // Check round-trip consistency.
7633 FileDescriptorProto proto;
7634 file->CopyTo(&proto);
7635 std::string file_textproto;
7636 google::protobuf::TextFormat::PrintToString(file_proto, &file_textproto);
7637 EXPECT_THAT(proto, EqualsProto(file_textproto));
7638 }
7639
TEST_F(FeaturesTest,Proto3Features)7640 TEST_F(FeaturesTest, Proto3Features) {
7641 FileDescriptorProto file_proto = ParseTextOrDie(R"pb(
7642 name: "foo.proto"
7643 syntax: "proto3"
7644 message_type {
7645 name: "Foo"
7646 field { name: "bar" number: 1 label: LABEL_OPTIONAL type: TYPE_INT64 }
7647 field { name: "rep" number: 2 label: LABEL_REPEATED type: TYPE_INT64 }
7648 field { name: "str" number: 3 label: LABEL_OPTIONAL type: TYPE_STRING }
7649 field {
7650 name: "expanded"
7651 number: 4
7652 label: LABEL_REPEATED
7653 type: TYPE_INT64
7654 options { packed: false }
7655 }
7656 field { name: "utf8" number: 5 label: LABEL_OPTIONAL type: TYPE_STRING }
7657 }
7658 enum_type {
7659 name: "Foo2"
7660 value { name: "DEFAULT" number: 0 }
7661 value { name: "BAR" number: 1 }
7662 })pb");
7663
7664 BuildDescriptorMessagesInTestPool();
7665 const FileDescriptor* file = ABSL_DIE_IF_NULL(pool_.BuildFile(file_proto));
7666 const Descriptor* message = file->message_type(0);
7667 const FieldDescriptor* field = message->field(0);
7668 EXPECT_THAT(file->options(), EqualsProto(""));
7669 EXPECT_EQ(GetFeatures(file).GetExtension(pb::test).file_feature(),
7670 pb::VALUE2);
7671 EXPECT_THAT(GetCoreFeatures(file), EqualsProto(R"pb(
7672 field_presence: IMPLICIT
7673 enum_type: OPEN
7674 repeated_field_encoding: PACKED
7675 utf8_validation: VERIFY
7676 message_encoding: LENGTH_PREFIXED
7677 json_format: ALLOW
7678 [pb.cpp] {
7679 legacy_closed_enum: false
7680 string_type: STRING
7681 enum_name_uses_string_view: false
7682 })pb"));
7683 EXPECT_THAT(GetCoreFeatures(field), EqualsProto(R"pb(
7684 field_presence: IMPLICIT
7685 enum_type: OPEN
7686 repeated_field_encoding: PACKED
7687 utf8_validation: VERIFY
7688 message_encoding: LENGTH_PREFIXED
7689 json_format: ALLOW
7690 [pb.cpp] {
7691 legacy_closed_enum: false
7692 string_type: STRING
7693 enum_name_uses_string_view: false
7694 })pb"));
7695 EXPECT_FALSE(field->has_presence());
7696 EXPECT_FALSE(field->requires_utf8_validation());
7697 EXPECT_EQ(
7698 GetUtf8CheckMode(message->FindFieldByName("str"), /*is_lite=*/false),
7699 Utf8CheckMode::kStrict);
7700 EXPECT_EQ(GetUtf8CheckMode(message->FindFieldByName("str"), /*is_lite=*/true),
7701 Utf8CheckMode::kStrict);
7702 EXPECT_FALSE(field->is_packed());
7703 EXPECT_FALSE(field->legacy_enum_field_treated_as_closed());
7704 EXPECT_FALSE(HasPreservingUnknownEnumSemantics(field));
7705 EXPECT_TRUE(message->FindFieldByName("rep")->is_packed());
7706 EXPECT_TRUE(message->FindFieldByName("str")->requires_utf8_validation());
7707 EXPECT_FALSE(message->FindFieldByName("expanded")->is_packed());
7708 EXPECT_FALSE(file->enum_type(0)->is_closed());
7709
7710 // Check round-trip consistency.
7711 FileDescriptorProto proto;
7712 file->CopyTo(&proto);
7713 std::string file_textproto;
7714 google::protobuf::TextFormat::PrintToString(file_proto, &file_textproto);
7715 EXPECT_THAT(proto, EqualsProto(file_textproto));
7716 }
7717
TEST_F(FeaturesTest,Proto2Proto3EnumFeatures)7718 TEST_F(FeaturesTest, Proto2Proto3EnumFeatures) {
7719 BuildDescriptorMessagesInTestPool();
7720 BuildFileInTestPool(pb::CppFeatures::GetDescriptor()->file());
7721 const FileDescriptor* file_proto3 = BuildFile(R"pb(
7722 name: "foo3.proto"
7723 syntax: "proto3"
7724 enum_type {
7725 name: "Enum3"
7726 value { name: "DEFAULT_ENUM3" number: 0 }
7727 value { name: "BAR_ENUM3" number: 1 }
7728 }
7729 message_type {
7730 name: "Message3"
7731 field {
7732 name: "enum_field"
7733 number: 1
7734 label: LABEL_OPTIONAL
7735 type: TYPE_ENUM
7736 type_name: ".Enum3"
7737 }
7738 }
7739 )pb");
7740 const FileDescriptor* file_proto2 = BuildFile(R"pb(
7741 name: "foo2.proto"
7742 dependency: "foo3.proto"
7743 enum_type {
7744 name: "Enum2"
7745 value { name: "DEFAULT_ENUM2" number: 0 }
7746 value { name: "BAR_ENUM2" number: 1 }
7747 }
7748 message_type {
7749 name: "Message2"
7750 field {
7751 name: "enum_field2"
7752 number: 1
7753 label: LABEL_OPTIONAL
7754 type: TYPE_ENUM
7755 type_name: ".Enum2"
7756 }
7757 field {
7758 name: "enum_field3"
7759 number: 2
7760 label: LABEL_OPTIONAL
7761 type: TYPE_ENUM
7762 type_name: ".Enum3"
7763 }
7764 }
7765 )pb");
7766 const Descriptor* message_proto2 = file_proto2->message_type(0);
7767 const Descriptor* message_proto3 = file_proto3->message_type(0);
7768 const FieldDescriptor* field_proto3 = message_proto3->field(0);
7769 const FieldDescriptor* field_proto2_closed = message_proto2->field(0);
7770 const FieldDescriptor* field_proto2_open = message_proto2->field(1);
7771
7772 EXPECT_FALSE(field_proto3->legacy_enum_field_treated_as_closed());
7773 EXPECT_TRUE(field_proto2_closed->legacy_enum_field_treated_as_closed());
7774 EXPECT_TRUE(field_proto2_open->legacy_enum_field_treated_as_closed());
7775 }
7776
7777 // Reproduces the reported issue in b/286244726 where custom options in proto3
7778 // ended up losing implicit presence. This only occurs when options are defined
7779 // and used in the same file.
TEST_F(FeaturesTest,Proto3Extensions)7780 TEST_F(FeaturesTest, Proto3Extensions) {
7781 BuildDescriptorMessagesInTestPool();
7782 const FileDescriptor* file = BuildFile(R"pb(
7783 name: "foo.proto"
7784 syntax: "proto3"
7785 dependency: "google/protobuf/descriptor.proto"
7786 message_type {
7787 name: "Ext"
7788 field { name: "bar" number: 1 label: LABEL_OPTIONAL type: TYPE_STRING }
7789 field { name: "baz" number: 2 label: LABEL_OPTIONAL type: TYPE_INT64 }
7790 }
7791 extension {
7792 name: "bar_ext"
7793 number: 99999
7794 label: LABEL_OPTIONAL
7795 type: TYPE_MESSAGE
7796 type_name: ".Ext"
7797 extendee: ".google.protobuf.EnumValueOptions"
7798 }
7799 enum_type {
7800 name: "Foo"
7801 value {
7802 name: "BAR"
7803 number: 0
7804 options {
7805 uninterpreted_option {
7806 name { name_part: "bar_ext" is_extension: true }
7807 aggregate_value: "bar: \"\" baz: 1"
7808 }
7809 }
7810 }
7811 }
7812 )pb");
7813 EXPECT_THAT(file->enum_type(0)->value(0)->options(),
7814 EqualsProtoSerialized(&pool_, "google.protobuf.EnumValueOptions",
7815 R"pb([bar_ext] { baz: 1 })pb"));
7816 }
7817
TEST_F(FeaturesTest,Proto3ExtensionPresence)7818 TEST_F(FeaturesTest, Proto3ExtensionPresence) {
7819 BuildDescriptorMessagesInTestPool();
7820 const FileDescriptor* file = BuildFile(R"pb(
7821 name: "foo.proto"
7822 syntax: "proto3"
7823 dependency: "google/protobuf/descriptor.proto"
7824 extension {
7825 name: "singular_ext"
7826 number: 1001
7827 label: LABEL_OPTIONAL
7828 type: TYPE_STRING
7829 extendee: ".google.protobuf.FileOptions"
7830 }
7831 extension {
7832 name: "singular_proto3_optional_ext"
7833 number: 1002
7834 label: LABEL_OPTIONAL
7835 type: TYPE_STRING
7836 extendee: ".google.protobuf.FileOptions"
7837 proto3_optional: true
7838 }
7839 extension {
7840 name: "repeated_ext"
7841 number: 1003
7842 label: LABEL_REPEATED
7843 type: TYPE_STRING
7844 extendee: ".google.protobuf.FileOptions"
7845 }
7846 )pb");
7847
7848 EXPECT_TRUE(file->extension(0)->has_presence());
7849 EXPECT_TRUE(file->extension(1)->has_presence());
7850 EXPECT_FALSE(file->extension(2)->has_presence());
7851 }
7852
TEST_F(FeaturesTest,Edition2023Defaults)7853 TEST_F(FeaturesTest, Edition2023Defaults) {
7854 FileDescriptorProto file_proto = ParseTextOrDie(R"pb(
7855 name: "foo.proto"
7856 syntax: "editions"
7857 edition: EDITION_2023
7858 )pb");
7859
7860 BuildDescriptorMessagesInTestPool();
7861 const FileDescriptor* file = ABSL_DIE_IF_NULL(pool_.BuildFile(file_proto));
7862 EXPECT_THAT(file->options(), EqualsProto(""));
7863 EXPECT_THAT(GetCoreFeatures(file), EqualsProto(R"pb(
7864 field_presence: EXPLICIT
7865 enum_type: OPEN
7866 repeated_field_encoding: PACKED
7867 utf8_validation: VERIFY
7868 message_encoding: LENGTH_PREFIXED
7869 json_format: ALLOW
7870 [pb.cpp] {
7871 legacy_closed_enum: false
7872 string_type: STRING
7873 enum_name_uses_string_view: false
7874 }
7875 )pb"));
7876
7877 // Since pb::test is registered in the pool, it should end up with defaults in
7878 // our FeatureSet.
7879 EXPECT_TRUE(GetFeatures(file).HasExtension(pb::test));
7880 EXPECT_EQ(GetFeatures(file).GetExtension(pb::test).file_feature(),
7881 pb::VALUE3);
7882 }
7883
TEST_F(FeaturesTest,Edition2023InferredFeatures)7884 TEST_F(FeaturesTest, Edition2023InferredFeatures) {
7885 FileDescriptorProto file_proto = ParseTextOrDie(R"pb(
7886 name: "foo.proto"
7887 syntax: "editions"
7888 edition: EDITION_2023
7889 message_type {
7890 name: "Foo"
7891 field { name: "str" number: 1 label: LABEL_OPTIONAL type: TYPE_STRING }
7892 field {
7893 name: "cord"
7894 number: 2
7895 label: LABEL_OPTIONAL
7896 type: TYPE_STRING
7897 options { ctype: CORD }
7898 }
7899 field {
7900 name: "piece"
7901 number: 3
7902 label: LABEL_OPTIONAL
7903 type: TYPE_STRING
7904 options { ctype: STRING_PIECE }
7905 }
7906 field {
7907 name: "view"
7908 number: 4
7909 label: LABEL_OPTIONAL
7910 type: TYPE_STRING
7911 options {
7912 features {
7913 [pb.cpp] { string_type: VIEW }
7914 }
7915 }
7916 }
7917 }
7918 )pb");
7919
7920 BuildDescriptorMessagesInTestPool();
7921 BuildFileInTestPool(pb::CppFeatures::GetDescriptor()->file());
7922 const FileDescriptor* file = ABSL_DIE_IF_NULL(pool_.BuildFile(file_proto));
7923 const Descriptor* message = file->message_type(0);
7924
7925 EXPECT_EQ(
7926 GetCoreFeatures(message->field(0)).GetExtension(pb::cpp).string_type(),
7927 pb::CppFeatures::STRING);
7928 EXPECT_EQ(
7929 GetCoreFeatures(message->field(1)).GetExtension(pb::cpp).string_type(),
7930 pb::CppFeatures::CORD);
7931 EXPECT_EQ(
7932 GetCoreFeatures(message->field(3)).GetExtension(pb::cpp).string_type(),
7933 pb::CppFeatures::VIEW);
7934 }
7935
TEST_F(FeaturesTest,Edition2024Defaults)7936 TEST_F(FeaturesTest, Edition2024Defaults) {
7937 FileDescriptorProto file_proto = ParseTextOrDie(R"pb(
7938 name: "foo.proto"
7939 syntax: "editions"
7940 edition: EDITION_2024
7941 )pb");
7942
7943 BuildDescriptorMessagesInTestPool();
7944 const FileDescriptor* file = ABSL_DIE_IF_NULL(pool_.BuildFile(file_proto));
7945 EXPECT_THAT(file->options(), EqualsProto(""));
7946 EXPECT_THAT(GetCoreFeatures(file), EqualsProto(R"pb(
7947 field_presence: EXPLICIT
7948 enum_type: OPEN
7949 repeated_field_encoding: PACKED
7950 utf8_validation: VERIFY
7951 message_encoding: LENGTH_PREFIXED
7952 json_format: ALLOW
7953 [pb.cpp] {
7954 legacy_closed_enum: false
7955 string_type: VIEW
7956 enum_name_uses_string_view: true
7957 }
7958 )pb"));
7959
7960 // Since pb::test is registered in the pool, it should end up with defaults in
7961 // our FeatureSet.
7962 EXPECT_TRUE(GetFeatures(file).HasExtension(pb::test));
7963 EXPECT_EQ(GetFeatures(file).GetExtension(pb::test).file_feature(),
7964 pb::VALUE3);
7965 }
7966
TEST_F(FeaturesBaseTest,DefaultEdition2023Defaults)7967 TEST_F(FeaturesBaseTest, DefaultEdition2023Defaults) {
7968 BuildDescriptorMessagesInTestPool();
7969 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
7970 const FileDescriptor* file = BuildFile(R"pb(
7971 name: "foo.proto"
7972 syntax: "editions"
7973 edition: EDITION_2023
7974 dependency: "google/protobuf/unittest_features.proto"
7975 )pb");
7976 ASSERT_NE(file, nullptr);
7977
7978 EXPECT_THAT(file->options(), EqualsProto(""));
7979 EXPECT_THAT(GetFeatures(file), EqualsProto(R"pb(
7980 field_presence: EXPLICIT
7981 enum_type: OPEN
7982 repeated_field_encoding: PACKED
7983 utf8_validation: VERIFY
7984 message_encoding: LENGTH_PREFIXED
7985 json_format: ALLOW
7986 [pb.cpp] {
7987 legacy_closed_enum: false
7988 string_type: STRING
7989 enum_name_uses_string_view: false
7990 }
7991 )pb"));
7992 EXPECT_FALSE(GetFeatures(file).HasExtension(pb::test));
7993 }
7994
TEST_F(FeaturesTest,ClearsOptions)7995 TEST_F(FeaturesTest, ClearsOptions) {
7996 BuildDescriptorMessagesInTestPool();
7997 const FileDescriptor* file = BuildFile(R"pb(
7998 name: "foo.proto"
7999 syntax: "editions"
8000 edition: EDITION_2023
8001 options {
8002 java_package: "bar"
8003 features { field_presence: IMPLICIT }
8004 }
8005 )pb");
8006 EXPECT_THAT(file->options(), EqualsProto("java_package: 'bar'"));
8007 EXPECT_THAT(GetCoreFeatures(file), EqualsProto(R"pb(
8008 field_presence: IMPLICIT
8009 enum_type: OPEN
8010 repeated_field_encoding: PACKED
8011 utf8_validation: VERIFY
8012 message_encoding: LENGTH_PREFIXED
8013 json_format: ALLOW
8014 [pb.cpp] {
8015 legacy_closed_enum: false
8016 string_type: STRING
8017 enum_name_uses_string_view: false
8018 })pb"));
8019 }
8020
TEST_F(FeaturesTest,RestoresOptionsRoundTrip)8021 TEST_F(FeaturesTest, RestoresOptionsRoundTrip) {
8022 BuildDescriptorMessagesInTestPool();
8023 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
8024 const FileDescriptor* file = BuildFile(R"pb(
8025 name: "foo.proto"
8026 syntax: "editions"
8027 edition: EDITION_2023
8028 dependency: "google/protobuf/unittest_features.proto"
8029 options {
8030 java_package: "bar"
8031 features {
8032 [pb.test] { file_feature: VALUE3 }
8033 }
8034 }
8035 message_type {
8036 name: "Foo"
8037 options {
8038 deprecated: true
8039 features {
8040 [pb.test] { message_feature: VALUE3 }
8041 }
8042 }
8043 field {
8044 name: "bar"
8045 number: 1
8046 label: LABEL_REPEATED
8047 type: TYPE_INT64
8048 options {
8049 deprecated: true
8050 features {
8051 [pb.test] { field_feature: VALUE9 }
8052 }
8053 }
8054 }
8055 field {
8056 name: "oneof_field"
8057 number: 2
8058 label: LABEL_OPTIONAL
8059 type: TYPE_INT64
8060 oneof_index: 0
8061 }
8062 oneof_decl {
8063 name: "foo_oneof"
8064 options {
8065 features {
8066 [pb.test] { oneof_feature: VALUE7 }
8067 }
8068 }
8069 }
8070 extension_range {
8071 start: 10
8072 end: 100
8073 options {
8074 verification: UNVERIFIED
8075 features {
8076 [pb.test] { extension_range_feature: VALUE15 }
8077 }
8078 }
8079 }
8080 }
8081 enum_type {
8082 name: "FooEnum"
8083 options {
8084 deprecated: true
8085 features {
8086 [pb.test] { enum_feature: VALUE4 }
8087 }
8088 }
8089 value {
8090 name: "BAR"
8091 number: 0
8092 options {
8093 deprecated: true
8094 features {
8095 [pb.test] { enum_entry_feature: VALUE8 }
8096 }
8097 }
8098 }
8099 }
8100 service {
8101 name: "FooService"
8102 options {
8103 deprecated: true
8104 features {
8105 [pb.test] { service_feature: VALUE11 }
8106 }
8107 }
8108 method {
8109 name: "BarMethod"
8110 input_type: "Foo"
8111 output_type: "Foo"
8112 options {
8113 deprecated: true
8114 features {
8115 [pb.test] { method_feature: VALUE12 }
8116 }
8117 }
8118 }
8119 }
8120 )pb");
8121 FileDescriptorProto proto;
8122 file->CopyTo(&proto);
8123 EXPECT_THAT(proto.options(),
8124 EqualsProto(R"pb(java_package: 'bar'
8125 features {
8126 [pb.test] { file_feature: VALUE3 }
8127 })pb"));
8128 EXPECT_THAT(proto.message_type(0).options(),
8129 EqualsProto(R"pb(deprecated: true
8130 features {
8131 [pb.test] { message_feature: VALUE3 }
8132 })pb"));
8133 EXPECT_THAT(proto.message_type(0).field(0).options(),
8134 EqualsProto(R"pb(deprecated: true
8135 features {
8136 [pb.test] { field_feature: VALUE9 }
8137 })pb"));
8138 EXPECT_THAT(proto.message_type(0).oneof_decl(0).options(),
8139 EqualsProto(R"pb(features {
8140 [pb.test] { oneof_feature: VALUE7 }
8141 })pb"));
8142 EXPECT_THAT(proto.message_type(0).extension_range(0).options(),
8143 EqualsProto(R"pb(verification: UNVERIFIED
8144 features {
8145 [pb.test] { extension_range_feature: VALUE15 }
8146 })pb"));
8147 EXPECT_THAT(proto.enum_type(0).options(),
8148 EqualsProto(R"pb(deprecated: true
8149 features {
8150 [pb.test] { enum_feature: VALUE4 }
8151 })pb"));
8152 EXPECT_THAT(proto.enum_type(0).value(0).options(),
8153 EqualsProto(R"pb(deprecated: true
8154 features {
8155 [pb.test] { enum_entry_feature: VALUE8 }
8156 })pb"));
8157 EXPECT_THAT(proto.service(0).options(),
8158 EqualsProto(R"pb(deprecated: true
8159 features {
8160 [pb.test] { service_feature: VALUE11 }
8161 })pb"));
8162 EXPECT_THAT(proto.service(0).method(0).options(),
8163 EqualsProto(R"pb(deprecated: true
8164 features {
8165 [pb.test] { method_feature: VALUE12 }
8166 })pb"));
8167 }
8168
TEST_F(FeaturesTest,ReusesFeaturesFromParent)8169 TEST_F(FeaturesTest, ReusesFeaturesFromParent) {
8170 BuildDescriptorMessagesInTestPool();
8171 const FileDescriptor* file = BuildFile(R"pb(
8172 name: "foo.proto"
8173 syntax: "editions"
8174 edition: EDITION_2023
8175 options { features { field_presence: IMPLICIT } }
8176 message_type {
8177 name: "Foo"
8178 options { deprecated: true }
8179 field {
8180 name: "bar"
8181 number: 1
8182 label: LABEL_REPEATED
8183 type: TYPE_INT64
8184 options { deprecated: true }
8185 }
8186 }
8187 )pb");
8188 EXPECT_EQ(&GetFeatures(file), &GetFeatures(file->message_type(0)));
8189 EXPECT_EQ(&GetFeatures(file), &GetFeatures(file->message_type(0)->field(0)));
8190 }
8191
TEST_F(FeaturesTest,ReusesFeaturesFromSibling)8192 TEST_F(FeaturesTest, ReusesFeaturesFromSibling) {
8193 BuildDescriptorMessagesInTestPool();
8194 const FileDescriptor* file = BuildFile(R"pb(
8195 name: "foo.proto"
8196 syntax: "editions"
8197 edition: EDITION_2023
8198 options { features { field_presence: IMPLICIT } }
8199 message_type {
8200 name: "Foo"
8201 options { deprecated: true }
8202 field {
8203 name: "bar1"
8204 number: 1
8205 label: LABEL_OPTIONAL
8206 type: TYPE_INT64
8207 options {
8208 deprecated: true
8209 features { field_presence: EXPLICIT }
8210 }
8211 }
8212 field {
8213 name: "baz"
8214 number: 2
8215 label: LABEL_OPTIONAL
8216 type: TYPE_STRING
8217 options { features { field_presence: EXPLICIT } }
8218 }
8219 }
8220 )pb");
8221 const Descriptor* message = file->message_type(0);
8222 EXPECT_NE(&GetFeatures(file), &GetFeatures(message->field(0)));
8223 EXPECT_EQ(&GetFeatures(message->field(0)), &GetFeatures(message->field(1)));
8224 }
8225
TEST_F(FeaturesTest,ReusesFeaturesFromDifferentFile)8226 TEST_F(FeaturesTest, ReusesFeaturesFromDifferentFile) {
8227 BuildDescriptorMessagesInTestPool();
8228 const FileDescriptor* file1 = BuildFile(R"pb(
8229 name: "foo.proto"
8230 syntax: "editions"
8231 edition: EDITION_2023
8232 options { features { field_presence: IMPLICIT } }
8233 )pb");
8234 const FileDescriptor* file2 = BuildFile(R"pb(
8235 name: "bar.proto"
8236 syntax: "editions"
8237 edition: EDITION_2023
8238 options { features { field_presence: IMPLICIT } }
8239 )pb");
8240 EXPECT_EQ(&GetFeatures(file1), &GetFeatures(file2));
8241 }
8242
TEST_F(FeaturesTest,ReusesFeaturesExtension)8243 TEST_F(FeaturesTest, ReusesFeaturesExtension) {
8244 BuildDescriptorMessagesInTestPool();
8245 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
8246 const FileDescriptor* file1 = BuildFile(R"pb(
8247 name: "foo.proto"
8248 syntax: "editions"
8249 edition: EDITION_2023
8250 options {
8251 features {
8252 [pb.TestMessage.test_message] { file_feature: VALUE6 }
8253 [pb.TestMessage.Nested.test_nested] { file_feature: VALUE5 }
8254 [pb.test] { file_feature: VALUE7 }
8255 }
8256 }
8257 )pb");
8258 const FileDescriptor* file2 = BuildFile(R"pb(
8259 name: "bar.proto"
8260 syntax: "editions"
8261 edition: EDITION_2023
8262 options {
8263 features {
8264 [pb.test] { file_feature: VALUE7 }
8265 [pb.TestMessage.test_message] { file_feature: VALUE6 }
8266 [pb.TestMessage.Nested.test_nested] { file_feature: VALUE5 }
8267 }
8268 }
8269 )pb");
8270 EXPECT_EQ(&GetFeatures(file1), &GetFeatures(file2));
8271 }
8272
TEST_F(FeaturesTest,RestoresLabelRoundTrip)8273 TEST_F(FeaturesTest, RestoresLabelRoundTrip) {
8274 BuildDescriptorMessagesInTestPool();
8275 const FileDescriptor* file = BuildFile(R"pb(
8276 name: "foo.proto"
8277 syntax: "editions"
8278 edition: EDITION_2023
8279 message_type {
8280 name: "Foo"
8281 field {
8282 name: "bar"
8283 number: 1
8284 label: LABEL_OPTIONAL
8285 type: TYPE_STRING
8286 options { features { field_presence: LEGACY_REQUIRED } }
8287 }
8288 }
8289 )pb");
8290 const FieldDescriptor* field = file->message_type(0)->field(0);
8291 ASSERT_EQ(field->label(), FieldDescriptor::LABEL_REQUIRED);
8292 ASSERT_TRUE(field->is_required());
8293
8294 FileDescriptorProto proto;
8295 file->CopyTo(&proto);
8296 const FieldDescriptorProto& field_proto = proto.message_type(0).field(0);
8297 EXPECT_EQ(field_proto.label(), FieldDescriptorProto::LABEL_OPTIONAL);
8298 EXPECT_THAT(
8299 field_proto.options(),
8300 EqualsProto(R"pb(features { field_presence: LEGACY_REQUIRED })pb"));
8301 }
8302
TEST_F(FeaturesTest,RestoresGroupRoundTrip)8303 TEST_F(FeaturesTest, RestoresGroupRoundTrip) {
8304 BuildDescriptorMessagesInTestPool();
8305 const FileDescriptor* file = BuildFile(R"pb(
8306 name: "foo.proto"
8307 syntax: "editions"
8308 edition: EDITION_2023
8309 message_type {
8310 name: "Foo"
8311 nested_type {
8312 name: "FooGroup"
8313 field { name: "bar" number: 1 label: LABEL_OPTIONAL type: TYPE_STRING }
8314 }
8315 field {
8316 name: "baz"
8317 number: 1
8318 label: LABEL_OPTIONAL
8319 type: TYPE_MESSAGE
8320 type_name: ".Foo.FooGroup"
8321 options { features { message_encoding: DELIMITED } }
8322 }
8323 }
8324 )pb");
8325 const FieldDescriptor* field = file->message_type(0)->field(0);
8326 ASSERT_EQ(field->type(), FieldDescriptor::TYPE_GROUP);
8327 ASSERT_NE(field->message_type(), nullptr);
8328
8329 FileDescriptorProto proto;
8330 file->CopyTo(&proto);
8331 const FieldDescriptorProto& field_proto = proto.message_type(0).field(0);
8332 EXPECT_EQ(field_proto.type(), FieldDescriptorProto::TYPE_MESSAGE);
8333 EXPECT_THAT(field_proto.options(),
8334 EqualsProto(R"pb(features { message_encoding: DELIMITED })pb"));
8335 }
8336
TEST_F(FeaturesTest,OnlyMessagesInheritGroupEncoding)8337 TEST_F(FeaturesTest, OnlyMessagesInheritGroupEncoding) {
8338 BuildDescriptorMessagesInTestPool();
8339 const FileDescriptor* file = BuildFile(R"pb(
8340 name: "foo.proto"
8341 syntax: "editions"
8342 edition: EDITION_2023
8343 options { features { message_encoding: DELIMITED } }
8344 message_type {
8345 name: "Foo"
8346 nested_type {
8347 name: "FooGroup"
8348 field { name: "bar" number: 1 label: LABEL_OPTIONAL type: TYPE_STRING }
8349 }
8350 field {
8351 name: "baz"
8352 number: 1
8353 label: LABEL_OPTIONAL
8354 type: TYPE_MESSAGE
8355 type_name: ".Foo.FooGroup"
8356 }
8357 field { name: "str" number: 2 label: LABEL_OPTIONAL type: TYPE_STRING }
8358 }
8359 )pb");
8360 const FieldDescriptor* group_field = file->message_type(0)->field(0);
8361 const FieldDescriptor* string_field = file->message_type(0)->field(1);
8362 EXPECT_EQ(group_field->type(), FieldDescriptor::TYPE_GROUP);
8363 EXPECT_EQ(string_field->type(), FieldDescriptor::TYPE_STRING);
8364 EXPECT_NE(group_field->message_type(), nullptr);
8365 EXPECT_EQ(string_field->message_type(), nullptr);
8366 }
8367
TEST_F(FeaturesTest,NoOptions)8368 TEST_F(FeaturesTest, NoOptions) {
8369 BuildDescriptorMessagesInTestPool();
8370 const FileDescriptor* file =
8371 BuildFile(R"pb(
8372 name: "foo.proto" syntax: "editions" edition: EDITION_2023
8373 )pb");
8374 EXPECT_EQ(&file->options(), &FileOptions::default_instance());
8375 EXPECT_THAT(GetCoreFeatures(file), EqualsProto(R"pb(
8376 field_presence: EXPLICIT
8377 enum_type: OPEN
8378 repeated_field_encoding: PACKED
8379 utf8_validation: VERIFY
8380 message_encoding: LENGTH_PREFIXED
8381 json_format: ALLOW
8382 [pb.cpp] {
8383 legacy_closed_enum: false
8384 string_type: STRING
8385 enum_name_uses_string_view: false
8386 })pb"));
8387 }
8388
TEST_F(FeaturesTest,InvalidEdition)8389 TEST_F(FeaturesTest, InvalidEdition) {
8390 BuildDescriptorMessagesInTestPool();
8391 BuildFileWithErrors(
8392 R"pb(
8393 name: "foo.proto" syntax: "editions" edition: EDITION_1_TEST_ONLY
8394 )pb",
8395 "foo.proto: foo.proto: EDITIONS: Edition 1_TEST_ONLY is earlier than the "
8396 "minimum supported edition PROTO2\n");
8397 }
8398
8399 TEST_F(FeaturesTest, FileFeatures) {
8400 BuildDescriptorMessagesInTestPool();
8401 const FileDescriptor* file = BuildFile(R"pb(
8402 name: "foo.proto"
8403 syntax: "editions"
8404 edition: EDITION_2023
8405 options { features { field_presence: IMPLICIT } }
8406 )pb");
8407 EXPECT_THAT(file->options(), EqualsProto(""));
8408 EXPECT_THAT(GetCoreFeatures(file), EqualsProto(R"pb(
8409 field_presence: IMPLICIT
8410 enum_type: OPEN
8411 repeated_field_encoding: PACKED
8412 utf8_validation: VERIFY
8413 message_encoding: LENGTH_PREFIXED
8414 json_format: ALLOW
8415 [pb.cpp] {
8416 legacy_closed_enum: false
8417 string_type: STRING
8418 enum_name_uses_string_view: false
8419 })pb"));
8420 }
8421
TEST_F(FeaturesTest,FileFeaturesExtension)8422 TEST_F(FeaturesTest, FileFeaturesExtension) {
8423 BuildDescriptorMessagesInTestPool();
8424 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
8425 const FileDescriptor* file = BuildFile(R"pb(
8426 name: "foo.proto"
8427 syntax: "editions"
8428 edition: EDITION_99998_TEST_ONLY
8429 dependency: "google/protobuf/unittest_features.proto"
8430 options { features { field_presence: IMPLICIT } }
8431 )pb");
8432 EXPECT_THAT(file->options(), EqualsProto(""));
8433 EXPECT_EQ(GetFeatures(file).field_presence(), FeatureSet::IMPLICIT);
8434 EXPECT_EQ(GetFeatures(file).enum_type(), FeatureSet::OPEN);
8435 EXPECT_EQ(GetFeatures(file).GetExtension(pb::test).file_feature(),
8436 pb::VALUE5);
8437 EXPECT_EQ(GetFeatures(file)
8438 .GetExtension(pb::TestMessage::test_message)
8439 .file_feature(),
8440 pb::VALUE5);
8441 EXPECT_EQ(GetFeatures(file)
8442 .GetExtension(pb::TestMessage::Nested::test_nested)
8443 .file_feature(),
8444 pb::VALUE5);
8445 }
8446
TEST_F(FeaturesTest,FileFeaturesExtensionOverride)8447 TEST_F(FeaturesTest, FileFeaturesExtensionOverride) {
8448 BuildDescriptorMessagesInTestPool();
8449 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
8450 const FileDescriptor* file = BuildFile(R"pb(
8451 name: "foo.proto"
8452 syntax: "editions"
8453 edition: EDITION_99998_TEST_ONLY
8454 dependency: "google/protobuf/unittest_features.proto"
8455 options {
8456 features {
8457 field_presence: IMPLICIT
8458 [pb.test] { file_feature: VALUE7 }
8459 [pb.TestMessage.test_message] { file_feature: VALUE6 }
8460 [pb.TestMessage.Nested.test_nested] { file_feature: VALUE5 }
8461 }
8462 }
8463 )pb");
8464 EXPECT_THAT(file->options(), EqualsProto(""));
8465 EXPECT_EQ(GetFeatures(file).field_presence(), FeatureSet::IMPLICIT);
8466 EXPECT_EQ(GetFeatures(file).enum_type(), FeatureSet::OPEN);
8467 EXPECT_EQ(GetFeatures(file).GetExtension(pb::test).file_feature(),
8468 pb::VALUE7);
8469 EXPECT_EQ(GetFeatures(file)
8470 .GetExtension(pb::TestMessage::test_message)
8471 .file_feature(),
8472 pb::VALUE6);
8473 EXPECT_EQ(GetFeatures(file)
8474 .GetExtension(pb::TestMessage::Nested::test_nested)
8475 .file_feature(),
8476 pb::VALUE5);
8477 }
8478
TEST_F(FeaturesTest,MessageFeaturesDefault)8479 TEST_F(FeaturesTest, MessageFeaturesDefault) {
8480 BuildDescriptorMessagesInTestPool();
8481 const FileDescriptor* file = BuildFile(R"pb(
8482 name: "foo.proto"
8483 syntax: "editions"
8484 edition: EDITION_2023
8485 message_type { name: "Foo" }
8486 )pb");
8487 const Descriptor* message = file->message_type(0);
8488 EXPECT_THAT(message->options(), EqualsProto(""));
8489 EXPECT_THAT(GetCoreFeatures(message), EqualsProto(R"pb(
8490 field_presence: EXPLICIT
8491 enum_type: OPEN
8492 repeated_field_encoding: PACKED
8493 utf8_validation: VERIFY
8494 message_encoding: LENGTH_PREFIXED
8495 json_format: ALLOW
8496 [pb.cpp] {
8497 legacy_closed_enum: false
8498 string_type: STRING
8499 enum_name_uses_string_view: false
8500 })pb"));
8501 }
8502
TEST_F(FeaturesTest,MessageFeaturesInherit)8503 TEST_F(FeaturesTest, MessageFeaturesInherit) {
8504 BuildDescriptorMessagesInTestPool();
8505 const FileDescriptor* file = BuildFile(R"pb(
8506 name: "foo.proto"
8507 syntax: "editions"
8508 edition: EDITION_2023
8509 options { features { field_presence: IMPLICIT } }
8510 message_type { name: "Foo" }
8511 )pb");
8512 const Descriptor* message = file->message_type(0);
8513 EXPECT_THAT(message->options(), EqualsProto(""));
8514 EXPECT_EQ(GetFeatures(message).field_presence(), FeatureSet::IMPLICIT);
8515 }
8516
TEST_F(FeaturesTest,MessageFeaturesOverride)8517 TEST_F(FeaturesTest, MessageFeaturesOverride) {
8518 BuildDescriptorMessagesInTestPool();
8519 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
8520 const FileDescriptor* file = BuildFile(R"pb(
8521 name: "foo.proto"
8522 syntax: "editions"
8523 edition: EDITION_2023
8524 dependency: "google/protobuf/unittest_features.proto"
8525 options {
8526 features {
8527 [pb.test] { multiple_feature: VALUE2 }
8528 }
8529 }
8530 message_type {
8531 name: "Foo"
8532 options {
8533 features {
8534 [pb.test] { multiple_feature: VALUE9 }
8535 }
8536 }
8537 }
8538 )pb");
8539 const Descriptor* message = file->message_type(0);
8540 EXPECT_THAT(message->options(), EqualsProto(""));
8541 EXPECT_EQ(GetFeatures(message).GetExtension(pb::test).multiple_feature(),
8542 pb::VALUE9);
8543 }
8544
TEST_F(FeaturesTest,NestedMessageFeaturesOverride)8545 TEST_F(FeaturesTest, NestedMessageFeaturesOverride) {
8546 BuildDescriptorMessagesInTestPool();
8547 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
8548 const FileDescriptor* file = BuildFile(R"pb(
8549 name: "foo.proto"
8550 syntax: "editions"
8551 edition: EDITION_2023
8552 dependency: "google/protobuf/unittest_features.proto"
8553 options {
8554 features {
8555 [pb.test] { multiple_feature: VALUE2 file_feature: VALUE7 }
8556 }
8557 }
8558 message_type {
8559 name: "Foo"
8560 options {
8561 features {
8562 [pb.test] { multiple_feature: VALUE10 message_feature: VALUE3 }
8563 }
8564 }
8565 nested_type {
8566 name: "Bar"
8567 options {
8568 features {
8569 [pb.test] { multiple_feature: VALUE5 }
8570 }
8571 }
8572 }
8573 }
8574 )pb");
8575 const Descriptor* message = file->message_type(0)->nested_type(0);
8576 EXPECT_THAT(message->options(), EqualsProto(""));
8577 EXPECT_EQ(GetFeatures(message).GetExtension(pb::test).field_feature(),
8578 pb::VALUE1);
8579 EXPECT_EQ(GetFeatures(message).GetExtension(pb::test).multiple_feature(),
8580 pb::VALUE5);
8581 EXPECT_EQ(GetFeatures(message).GetExtension(pb::test).file_feature(),
8582 pb::VALUE7);
8583 EXPECT_EQ(GetFeatures(message).GetExtension(pb::test).message_feature(),
8584 pb::VALUE3);
8585 }
8586
TEST_F(FeaturesTest,FieldFeaturesDefault)8587 TEST_F(FeaturesTest, FieldFeaturesDefault) {
8588 BuildDescriptorMessagesInTestPool();
8589 const FileDescriptor* file = BuildFile(R"pb(
8590 name: "foo.proto"
8591 syntax: "editions"
8592 edition: EDITION_2023
8593 message_type {
8594 name: "Foo"
8595 field { name: "bar" number: 1 label: LABEL_REPEATED type: TYPE_INT64 }
8596 }
8597 )pb");
8598 const FieldDescriptor* field = file->message_type(0)->field(0);
8599 EXPECT_THAT(field->options(), EqualsProto(""));
8600 EXPECT_THAT(GetCoreFeatures(field), EqualsProto(R"pb(
8601 field_presence: EXPLICIT
8602 enum_type: OPEN
8603 repeated_field_encoding: PACKED
8604 utf8_validation: VERIFY
8605 message_encoding: LENGTH_PREFIXED
8606 json_format: ALLOW
8607 [pb.cpp] {
8608 legacy_closed_enum: false
8609 string_type: STRING
8610 enum_name_uses_string_view: false
8611 })pb"));
8612 }
8613
TEST_F(FeaturesTest,FieldFeaturesInherit)8614 TEST_F(FeaturesTest, FieldFeaturesInherit) {
8615 BuildDescriptorMessagesInTestPool();
8616 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
8617 const FileDescriptor* file = BuildFile(R"pb(
8618 name: "foo.proto"
8619 syntax: "editions"
8620 edition: EDITION_2023
8621 dependency: "google/protobuf/unittest_features.proto"
8622 options {
8623 features {
8624 field_presence: IMPLICIT
8625 [pb.test] { multiple_feature: VALUE1 }
8626 }
8627 }
8628 message_type {
8629 name: "Foo"
8630 options {
8631 features {
8632 [pb.test] { multiple_feature: VALUE9 }
8633 }
8634 }
8635 field { name: "bar" number: 1 label: LABEL_REPEATED type: TYPE_INT64 }
8636 }
8637 )pb");
8638 const FieldDescriptor* field = file->message_type(0)->field(0);
8639 EXPECT_THAT(field->options(), EqualsProto(""));
8640 EXPECT_EQ(GetFeatures(field).field_presence(), FeatureSet::IMPLICIT);
8641 EXPECT_EQ(GetFeatures(field).GetExtension(pb::test).multiple_feature(),
8642 pb::VALUE9);
8643 }
8644
TEST_F(FeaturesTest,FieldFeaturesOverride)8645 TEST_F(FeaturesTest, FieldFeaturesOverride) {
8646 BuildDescriptorMessagesInTestPool();
8647 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
8648 const FileDescriptor* file = BuildFile(R"pb(
8649 name: "foo.proto"
8650 syntax: "editions"
8651 edition: EDITION_2023
8652 dependency: "google/protobuf/unittest_features.proto"
8653 options {
8654 features {
8655 enum_type: CLOSED
8656 field_presence: IMPLICIT
8657 [pb.test] { multiple_feature: VALUE2 }
8658 }
8659 }
8660 message_type {
8661 name: "Foo"
8662 options {
8663 features {
8664 [pb.test] { multiple_feature: VALUE3 }
8665 }
8666 }
8667 field {
8668 name: "bar"
8669 number: 1
8670 label: LABEL_OPTIONAL
8671 type: TYPE_STRING
8672 options {
8673 features {
8674 field_presence: EXPLICIT
8675 [pb.test] { multiple_feature: VALUE9 }
8676 }
8677 }
8678 }
8679 }
8680 )pb");
8681 const FieldDescriptor* field = file->message_type(0)->field(0);
8682 EXPECT_THAT(field->options(), EqualsProto(""));
8683 EXPECT_EQ(GetFeatures(field).field_presence(), FeatureSet::EXPLICIT);
8684 EXPECT_EQ(GetFeatures(field).enum_type(), FeatureSet::CLOSED);
8685 EXPECT_EQ(GetFeatures(field).GetExtension(pb::test).multiple_feature(),
8686 pb::VALUE9);
8687 }
8688
TEST_F(FeaturesTest,OneofFieldFeaturesInherit)8689 TEST_F(FeaturesTest, OneofFieldFeaturesInherit) {
8690 BuildDescriptorMessagesInTestPool();
8691 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
8692 const FileDescriptor* file = BuildFile(R"pb(
8693 name: "foo.proto"
8694 syntax: "editions"
8695 edition: EDITION_2023
8696 dependency: "google/protobuf/unittest_features.proto"
8697 options {
8698 features {
8699 field_presence: IMPLICIT
8700 [pb.test] { multiple_feature: VALUE1 }
8701 }
8702 }
8703 message_type {
8704 name: "Foo"
8705 options {
8706 features {
8707 [pb.test] { multiple_feature: VALUE6 }
8708 }
8709 }
8710 field {
8711 name: "bar"
8712 number: 1
8713 label: LABEL_OPTIONAL
8714 type: TYPE_INT64
8715 oneof_index: 0
8716 }
8717 oneof_decl {
8718 name: "foo_oneof"
8719 options {
8720 features {
8721 [pb.test] { multiple_feature: VALUE9 }
8722 }
8723 }
8724 }
8725 }
8726 )pb");
8727 const FieldDescriptor* field = file->message_type(0)->field(0);
8728 EXPECT_THAT(field->options(), EqualsProto(""));
8729 EXPECT_EQ(GetFeatures(field).field_presence(), FeatureSet::IMPLICIT);
8730 EXPECT_EQ(GetFeatures(field).GetExtension(pb::test).multiple_feature(),
8731 pb::VALUE9);
8732 }
8733
TEST_F(FeaturesTest,OneofFieldFeaturesOverride)8734 TEST_F(FeaturesTest, OneofFieldFeaturesOverride) {
8735 BuildDescriptorMessagesInTestPool();
8736 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
8737 const FileDescriptor* file = BuildFile(R"pb(
8738 name: "foo.proto"
8739 syntax: "editions"
8740 edition: EDITION_2023
8741 dependency: "google/protobuf/unittest_features.proto"
8742 options {
8743 features {
8744 [pb.test] { multiple_feature: VALUE2 file_feature: VALUE4 }
8745 }
8746 }
8747 message_type {
8748 name: "Foo"
8749 options {
8750 features {
8751 [pb.test] { multiple_feature: VALUE3 message_feature: VALUE3 }
8752 }
8753 }
8754 field {
8755 name: "bar"
8756 number: 1
8757 label: LABEL_OPTIONAL
8758 type: TYPE_STRING
8759 options {
8760 features {
8761 [pb.test] { multiple_feature: VALUE9 }
8762 }
8763 }
8764 oneof_index: 0
8765 }
8766 oneof_decl {
8767 name: "foo_oneof"
8768 options {
8769 features {
8770 [pb.test] { multiple_feature: VALUE6 oneof_feature: VALUE6 }
8771 }
8772 }
8773 }
8774 }
8775 )pb");
8776 const FieldDescriptor* field = file->message_type(0)->field(0);
8777 EXPECT_THAT(field->options(), EqualsProto(""));
8778 EXPECT_EQ(GetFeatures(field).GetExtension(pb::test).multiple_feature(),
8779 pb::VALUE9);
8780 EXPECT_EQ(GetFeatures(field).GetExtension(pb::test).oneof_feature(),
8781 pb::VALUE6);
8782 EXPECT_EQ(GetFeatures(field).GetExtension(pb::test).message_feature(),
8783 pb::VALUE3);
8784 EXPECT_EQ(GetFeatures(field).GetExtension(pb::test).file_feature(),
8785 pb::VALUE4);
8786 }
8787
TEST_F(FeaturesTest,MapFieldFeaturesOverride)8788 TEST_F(FeaturesTest, MapFieldFeaturesOverride) {
8789 BuildDescriptorMessagesInTestPool();
8790 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
8791 const FileDescriptor* file = ParseAndBuildFile("foo.proto", R"schema(
8792 edition = "2023";
8793
8794 import "google/protobuf/unittest_features.proto";
8795
8796 option features.(pb.test).file_feature = VALUE7;
8797 option features.(pb.test).multiple_feature = VALUE1;
8798
8799 message Foo {
8800 option features.(pb.test).message_feature = VALUE8;
8801 option features.(pb.test).multiple_feature = VALUE2;
8802
8803 map<string, string> map_field = 1 [
8804 features.(pb.test).field_feature = VALUE10,
8805 features.(pb.test).multiple_feature = VALUE3
8806 ];
8807 }
8808 )schema");
8809 ASSERT_THAT(file, NotNull());
8810
8811 const FieldDescriptor* map_field = file->message_type(0)->field(0);
8812 const FieldDescriptor* key = map_field->message_type()->field(0);
8813 const FieldDescriptor* value = map_field->message_type()->field(1);
8814
__anon064ad7990302(const FieldDescriptor* desc) 8815 auto validate = [](const FieldDescriptor* desc) {
8816 EXPECT_EQ(GetFeatures(desc).GetExtension(pb::test).file_feature(),
8817 pb::VALUE7)
8818 << desc->DebugString();
8819 EXPECT_EQ(GetFeatures(desc).GetExtension(pb::test).message_feature(),
8820 pb::VALUE8)
8821 << desc->DebugString();
8822 EXPECT_EQ(GetFeatures(desc).GetExtension(pb::test).field_feature(),
8823 pb::VALUE10)
8824 << desc->DebugString();
8825 EXPECT_EQ(GetFeatures(desc).GetExtension(pb::test).multiple_feature(),
8826 pb::VALUE3)
8827 << desc->DebugString();
8828 };
8829
8830 validate(map_field);
8831 validate(key);
8832 validate(value);
8833 }
8834
8835 TEST_F(FeaturesTest, MapFieldFeaturesStringValidation) {
8836 BuildDescriptorMessagesInTestPool();
8837 const FileDescriptor* file = ParseAndBuildFile("foo.proto", R"schema(
8838 edition = "2023";
8839
8840 message Foo {
8841 map<string, string> map_field = 1 [
8842 features.utf8_validation = NONE
8843 ];
8844 map<int32, string> map_field_value = 2 [
8845 features.utf8_validation = NONE
8846 ];
8847 map<string, int32> map_field_key = 3 [
8848 features.utf8_validation = NONE
8849 ];
8850 }
8851 )schema");
8852 ASSERT_THAT(file, NotNull());
8853
__anon064ad7990402(const FieldDescriptor* field) 8854 auto validate_map_field = [](const FieldDescriptor* field) {
8855 const FieldDescriptor* key = field->message_type()->field(0);
8856 const FieldDescriptor* value = field->message_type()->field(1);
8857
8858 EXPECT_FALSE(field->requires_utf8_validation()) << field->DebugString();
8859 EXPECT_FALSE(key->requires_utf8_validation()) << field->DebugString();
8860 EXPECT_FALSE(value->requires_utf8_validation()) << field->DebugString();
8861 };
8862
8863 validate_map_field(file->message_type(0)->field(0));
8864 validate_map_field(file->message_type(0)->field(1));
8865 validate_map_field(file->message_type(0)->field(2));
8866 }
8867
8868 TEST_F(FeaturesTest, MapFieldFeaturesImplicitPresence) {
8869 BuildDescriptorMessagesInTestPool();
8870 const FileDescriptor* editions = ParseAndBuildFile("editions.proto", R"schema(
8871 edition = "2023";
8872
8873 option features.field_presence = IMPLICIT;
8874
8875 message Foo {
8876 map<string, Foo> message_map = 1;
8877 map<string, string> string_map = 2;
8878 }
8879 )schema");
8880 ASSERT_THAT(editions, NotNull());
8881 const FileDescriptor* proto3 = ParseAndBuildFile("proto3.proto", R"schema(
8882 syntax = "proto3";
8883
8884 message Bar {
8885 map<string, Bar> message_map = 1;
8886 map<string, string> string_map = 2;
8887 }
8888 )schema");
8889 ASSERT_THAT(proto3, NotNull());
8890
__anon064ad7990502(const FileDescriptor* file) 8891 auto validate_maps = [](const FileDescriptor* file) {
8892 const FieldDescriptor* message_map = file->message_type(0)->field(0);
8893 EXPECT_FALSE(message_map->has_presence());
8894 EXPECT_FALSE(message_map->message_type()->field(0)->has_presence());
8895 EXPECT_TRUE(message_map->message_type()->field(1)->has_presence());
8896
8897 const FieldDescriptor* string_map = file->message_type(0)->field(1);
8898 EXPECT_FALSE(string_map->has_presence());
8899 EXPECT_FALSE(string_map->message_type()->field(0)->has_presence());
8900 EXPECT_FALSE(string_map->message_type()->field(1)->has_presence());
8901 };
8902 validate_maps(editions);
8903 validate_maps(proto3);
8904 }
8905
8906 TEST_F(FeaturesTest, MapFieldFeaturesExplicitPresence) {
8907 BuildDescriptorMessagesInTestPool();
8908 const FileDescriptor* editions = ParseAndBuildFile("editions.proto", R"schema(
8909 edition = "2023";
8910
8911 message Foo {
8912 map<string, Foo> message_map = 1;
8913 map<string, string> string_map = 2;
8914 }
8915 )schema");
8916 ASSERT_THAT(editions, NotNull());
8917 const FileDescriptor* proto2 = ParseAndBuildFile("google.protobuf.proto", R"schema(
8918 syntax = "proto2";
8919
8920 message Bar {
8921 map<string, Bar> message_map = 1;
8922 map<string, string> string_map = 2;
8923 }
8924 )schema");
8925 ASSERT_THAT(proto2, NotNull());
8926
__anon064ad7990602(const FileDescriptor* file) 8927 auto validate_maps = [](const FileDescriptor* file) {
8928 const FieldDescriptor* message_map = file->message_type(0)->field(0);
8929 EXPECT_FALSE(message_map->has_presence());
8930 EXPECT_TRUE(message_map->message_type()->field(0)->has_presence());
8931 EXPECT_TRUE(message_map->message_type()->field(1)->has_presence());
8932
8933 const FieldDescriptor* string_map = file->message_type(0)->field(1);
8934 EXPECT_FALSE(string_map->has_presence());
8935 EXPECT_TRUE(string_map->message_type()->field(0)->has_presence());
8936 EXPECT_TRUE(string_map->message_type()->field(1)->has_presence());
8937 };
8938 validate_maps(editions);
8939 validate_maps(proto2);
8940 }
8941
8942 TEST_F(FeaturesTest, MapFieldFeaturesInheritedMessageEncoding) {
8943 BuildDescriptorMessagesInTestPool();
8944 const FileDescriptor* file = ParseAndBuildFile("foo.proto", R"schema(
8945 edition = "2023";
8946
8947 option features.message_encoding = DELIMITED;
8948
8949 message Foo {
8950 map<int32, Foo> message_map = 1;
8951 map<string, string> string_map = 2;
8952 }
8953 )schema");
8954 ASSERT_THAT(file, NotNull());
8955
8956 const FieldDescriptor* message_map = file->message_type(0)->field(0);
8957 EXPECT_EQ(message_map->type(), FieldDescriptor::TYPE_MESSAGE);
8958 EXPECT_EQ(message_map->message_type()->field(0)->type(),
8959 FieldDescriptor::TYPE_INT32);
8960 EXPECT_EQ(message_map->message_type()->field(1)->type(),
8961 FieldDescriptor::TYPE_MESSAGE);
8962
8963 const FieldDescriptor* string_map = file->message_type(0)->field(1);
8964 EXPECT_EQ(string_map->type(), FieldDescriptor::TYPE_MESSAGE);
8965 EXPECT_EQ(string_map->message_type()->field(0)->type(),
8966 FieldDescriptor::TYPE_STRING);
8967 EXPECT_EQ(string_map->message_type()->field(1)->type(),
8968 FieldDescriptor::TYPE_STRING);
8969 }
8970
TEST_F(FeaturesTest,RootExtensionFeaturesOverride)8971 TEST_F(FeaturesTest, RootExtensionFeaturesOverride) {
8972 BuildDescriptorMessagesInTestPool();
8973 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
8974 const FileDescriptor* file = BuildFile(R"pb(
8975 name: "foo.proto"
8976 syntax: "editions"
8977 edition: EDITION_2023
8978 dependency: "google/protobuf/unittest_features.proto"
8979 options {
8980 features {
8981 enum_type: CLOSED
8982 field_presence: IMPLICIT
8983 [pb.test] { multiple_feature: VALUE2 }
8984 }
8985 }
8986 extension {
8987 name: "bar"
8988 number: 1
8989 label: LABEL_OPTIONAL
8990 type: TYPE_STRING
8991 options {
8992 features {
8993 enum_type: OPEN
8994 [pb.test] { multiple_feature: VALUE9 }
8995 }
8996 }
8997 extendee: "Foo"
8998 }
8999 message_type {
9000 name: "Foo"
9001 extension_range { start: 1 end: 2 }
9002 }
9003 )pb");
9004 const FieldDescriptor* field = file->extension(0);
9005 EXPECT_THAT(field->options(), EqualsProto(""));
9006 EXPECT_EQ(GetFeatures(field).field_presence(), FeatureSet::IMPLICIT);
9007 EXPECT_EQ(GetFeatures(field).enum_type(), FeatureSet::OPEN);
9008 EXPECT_EQ(GetFeatures(field).GetExtension(pb::test).multiple_feature(),
9009 pb::VALUE9);
9010 }
9011
TEST_F(FeaturesTest,MessageExtensionFeaturesOverride)9012 TEST_F(FeaturesTest, MessageExtensionFeaturesOverride) {
9013 BuildDescriptorMessagesInTestPool();
9014 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
9015 const FileDescriptor* file = BuildFile(R"pb(
9016 name: "foo.proto"
9017 syntax: "editions"
9018 edition: EDITION_2023
9019 dependency: "google/protobuf/unittest_features.proto"
9020 options {
9021 features {
9022 enum_type: CLOSED
9023 field_presence: IMPLICIT
9024 [pb.test] { multiple_feature: VALUE2 }
9025 }
9026 }
9027 message_type {
9028 name: "Foo"
9029 options {
9030 features {
9031 [pb.test] { multiple_feature: VALUE3 }
9032 }
9033 }
9034 extension {
9035 name: "bar"
9036 number: 1
9037 label: LABEL_OPTIONAL
9038 type: TYPE_STRING
9039 options { features { enum_type: OPEN } }
9040 extendee: "Foo2"
9041 }
9042 }
9043 message_type {
9044 name: "Foo2"
9045 extension_range { start: 1 end: 2 }
9046 options {
9047 features {
9048 [pb.test] { multiple_feature: VALUE7 }
9049 }
9050 }
9051 }
9052 )pb");
9053 const FieldDescriptor* field = file->message_type(0)->extension(0);
9054 EXPECT_THAT(field->options(), EqualsProto(""));
9055 EXPECT_EQ(GetFeatures(field).field_presence(), FeatureSet::IMPLICIT);
9056 EXPECT_EQ(GetFeatures(field).enum_type(), FeatureSet::OPEN);
9057 EXPECT_EQ(GetFeatures(field).GetExtension(pb::test).multiple_feature(),
9058 pb::VALUE3);
9059 }
9060
TEST_F(FeaturesTest,EnumFeaturesDefault)9061 TEST_F(FeaturesTest, EnumFeaturesDefault) {
9062 BuildDescriptorMessagesInTestPool();
9063 const FileDescriptor* file = BuildFile(R"pb(
9064 name: "foo.proto"
9065 syntax: "editions"
9066 edition: EDITION_2023
9067 enum_type {
9068 name: "Foo"
9069 value { name: "BAR" number: 0 }
9070 }
9071 )pb");
9072 const EnumDescriptor* enm = file->enum_type(0);
9073 EXPECT_THAT(enm->options(), EqualsProto(""));
9074 EXPECT_THAT(GetCoreFeatures(enm), EqualsProto(R"pb(
9075 field_presence: EXPLICIT
9076 enum_type: OPEN
9077 repeated_field_encoding: PACKED
9078 utf8_validation: VERIFY
9079 message_encoding: LENGTH_PREFIXED
9080 json_format: ALLOW
9081 [pb.cpp] {
9082 legacy_closed_enum: false
9083 string_type: STRING
9084 enum_name_uses_string_view: false
9085 })pb"));
9086 }
9087
TEST_F(FeaturesTest,EnumFeaturesInherit)9088 TEST_F(FeaturesTest, EnumFeaturesInherit) {
9089 BuildDescriptorMessagesInTestPool();
9090 const FileDescriptor* file = BuildFile(R"pb(
9091 name: "foo.proto"
9092 syntax: "editions"
9093 edition: EDITION_2023
9094 options { features { enum_type: CLOSED } }
9095 enum_type {
9096 name: "Foo"
9097 value { name: "BAR" number: 0 }
9098 }
9099 )pb");
9100 const EnumDescriptor* enm = file->enum_type(0);
9101 EXPECT_THAT(enm->options(), EqualsProto(""));
9102 EXPECT_EQ(GetFeatures(enm).enum_type(), FeatureSet::CLOSED);
9103 }
9104
TEST_F(FeaturesTest,EnumFeaturesOverride)9105 TEST_F(FeaturesTest, EnumFeaturesOverride) {
9106 BuildDescriptorMessagesInTestPool();
9107 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
9108 const FileDescriptor* file = BuildFile(R"pb(
9109 name: "foo.proto"
9110 syntax: "editions"
9111 edition: EDITION_2023
9112 dependency: "google/protobuf/unittest_features.proto"
9113 options {
9114 features {
9115 [pb.test] { multiple_feature: VALUE2 }
9116 }
9117 }
9118 enum_type {
9119 name: "Foo"
9120 options {
9121 features {
9122 [pb.test] { multiple_feature: VALUE9 }
9123 }
9124 }
9125 value { name: "BAR" number: 0 }
9126 }
9127 )pb");
9128 const EnumDescriptor* enm = file->enum_type(0);
9129 EXPECT_THAT(enm->options(), EqualsProto(""));
9130 EXPECT_EQ(GetFeatures(enm).GetExtension(pb::test).multiple_feature(),
9131 pb::VALUE9);
9132 }
9133
TEST_F(FeaturesTest,NestedEnumFeatures)9134 TEST_F(FeaturesTest, NestedEnumFeatures) {
9135 BuildDescriptorMessagesInTestPool();
9136 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
9137 const FileDescriptor* file = BuildFile(R"pb(
9138 name: "foo.proto"
9139 syntax: "editions"
9140 edition: EDITION_2023
9141 dependency: "google/protobuf/unittest_features.proto"
9142 options {
9143 features {
9144 [pb.test] { multiple_feature: VALUE2 file_feature: VALUE7 }
9145 }
9146 }
9147 message_type {
9148 name: "Foo"
9149 options {
9150 features {
9151 [pb.test] { multiple_feature: VALUE10 message_feature: VALUE3 }
9152 }
9153 }
9154 enum_type {
9155 name: "Bar"
9156 options {
9157 features {
9158 [pb.test] { multiple_feature: VALUE5 }
9159 }
9160 }
9161 value { name: "BAR" number: 0 }
9162 }
9163 }
9164 )pb");
9165 const EnumDescriptor* enm = file->message_type(0)->enum_type(0);
9166 EXPECT_THAT(enm->options(), EqualsProto(""));
9167 EXPECT_EQ(GetFeatures(enm).GetExtension(pb::test).field_feature(),
9168 pb::VALUE1);
9169 EXPECT_EQ(GetFeatures(enm).GetExtension(pb::test).multiple_feature(),
9170 pb::VALUE5);
9171 EXPECT_EQ(GetFeatures(enm).GetExtension(pb::test).file_feature(), pb::VALUE7);
9172 EXPECT_EQ(GetFeatures(enm).GetExtension(pb::test).message_feature(),
9173 pb::VALUE3);
9174 }
9175
TEST_F(FeaturesTest,EnumValueFeaturesDefault)9176 TEST_F(FeaturesTest, EnumValueFeaturesDefault) {
9177 BuildDescriptorMessagesInTestPool();
9178 const FileDescriptor* file = BuildFile(R"pb(
9179 name: "foo.proto"
9180 syntax: "editions"
9181 edition: EDITION_2023
9182 enum_type {
9183 name: "Foo"
9184 value { name: "BAR" number: 0 }
9185 }
9186 )pb");
9187 const EnumValueDescriptor* value = file->enum_type(0)->value(0);
9188 EXPECT_THAT(value->options(), EqualsProto(""));
9189 EXPECT_THAT(GetCoreFeatures(value), EqualsProto(R"pb(
9190 field_presence: EXPLICIT
9191 enum_type: OPEN
9192 repeated_field_encoding: PACKED
9193 utf8_validation: VERIFY
9194 message_encoding: LENGTH_PREFIXED
9195 json_format: ALLOW
9196 [pb.cpp] {
9197 legacy_closed_enum: false
9198 string_type: STRING
9199 enum_name_uses_string_view: false
9200 })pb"));
9201 }
9202
TEST_F(FeaturesTest,EnumValueFeaturesInherit)9203 TEST_F(FeaturesTest, EnumValueFeaturesInherit) {
9204 BuildDescriptorMessagesInTestPool();
9205 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
9206 const FileDescriptor* file = BuildFile(R"pb(
9207 name: "foo.proto"
9208 syntax: "editions"
9209 edition: EDITION_2023
9210 dependency: "google/protobuf/unittest_features.proto"
9211 options { features { enum_type: CLOSED } }
9212 enum_type {
9213 name: "Foo"
9214 options {
9215 features {
9216 [pb.test] { enum_feature: VALUE9 }
9217 }
9218 }
9219 value { name: "BAR" number: 0 }
9220 }
9221 )pb");
9222 const EnumValueDescriptor* value = file->enum_type(0)->value(0);
9223 EXPECT_THAT(value->options(), EqualsProto(""));
9224 EXPECT_EQ(GetFeatures(value).enum_type(), FeatureSet::CLOSED);
9225 EXPECT_EQ(GetFeatures(value).GetExtension(pb::test).enum_feature(),
9226 pb::VALUE9);
9227 }
9228
TEST_F(FeaturesTest,EnumValueFeaturesOverride)9229 TEST_F(FeaturesTest, EnumValueFeaturesOverride) {
9230 BuildDescriptorMessagesInTestPool();
9231 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
9232 const FileDescriptor* file = BuildFile(R"pb(
9233 name: "foo.proto"
9234 syntax: "editions"
9235 edition: EDITION_2023
9236 dependency: "google/protobuf/unittest_features.proto"
9237 options {
9238 features {
9239 [pb.test] { multiple_feature: VALUE7 }
9240 }
9241 }
9242 enum_type {
9243 name: "Foo"
9244 options {
9245 features {
9246 [pb.test] { multiple_feature: VALUE8 }
9247 }
9248 }
9249 value {
9250 name: "BAR"
9251 number: 0
9252 options {
9253 features {
9254 [pb.test] { multiple_feature: VALUE9 enum_entry_feature: VALUE8 }
9255 }
9256 }
9257 }
9258 }
9259 )pb");
9260 const EnumValueDescriptor* value = file->enum_type(0)->value(0);
9261 EXPECT_THAT(value->options(), EqualsProto(""));
9262 EXPECT_EQ(GetFeatures(value).GetExtension(pb::test).multiple_feature(),
9263 pb::VALUE9);
9264 EXPECT_EQ(GetFeatures(value).GetExtension(pb::test).enum_entry_feature(),
9265 pb::VALUE8);
9266 }
9267
TEST_F(FeaturesTest,OneofFeaturesDefault)9268 TEST_F(FeaturesTest, OneofFeaturesDefault) {
9269 BuildDescriptorMessagesInTestPool();
9270 const FileDescriptor* file = BuildFile(R"pb(
9271 name: "foo.proto"
9272 syntax: "editions"
9273 edition: EDITION_2023
9274 message_type {
9275 name: "Foo"
9276 field {
9277 name: "oneof_field"
9278 number: 1
9279 label: LABEL_OPTIONAL
9280 type: TYPE_INT64
9281 oneof_index: 0
9282 }
9283 oneof_decl { name: "foo_oneof" }
9284 }
9285 )pb");
9286 const OneofDescriptor* oneof = file->message_type(0)->oneof_decl(0);
9287 EXPECT_THAT(oneof->options(), EqualsProto(""));
9288 EXPECT_THAT(GetCoreFeatures(oneof), EqualsProto(R"pb(
9289 field_presence: EXPLICIT
9290 enum_type: OPEN
9291 repeated_field_encoding: PACKED
9292 utf8_validation: VERIFY
9293 message_encoding: LENGTH_PREFIXED
9294 json_format: ALLOW
9295 [pb.cpp] {
9296 legacy_closed_enum: false
9297 string_type: STRING
9298 enum_name_uses_string_view: false
9299 })pb"));
9300 }
9301
TEST_F(FeaturesTest,OneofFeaturesInherit)9302 TEST_F(FeaturesTest, OneofFeaturesInherit) {
9303 BuildDescriptorMessagesInTestPool();
9304 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
9305 const FileDescriptor* file = BuildFile(R"pb(
9306 name: "foo.proto"
9307 syntax: "editions"
9308 edition: EDITION_2023
9309 dependency: "google/protobuf/unittest_features.proto"
9310 options { features { enum_type: CLOSED } }
9311 message_type {
9312 name: "Foo"
9313 field {
9314 name: "oneof_field"
9315 number: 1
9316 label: LABEL_OPTIONAL
9317 type: TYPE_INT64
9318 oneof_index: 0
9319 }
9320 oneof_decl { name: "foo_oneof" }
9321 options {
9322 features {
9323 [pb.test] { message_feature: VALUE9 }
9324 }
9325 }
9326 }
9327 )pb");
9328 const OneofDescriptor* oneof = file->message_type(0)->oneof_decl(0);
9329 EXPECT_THAT(oneof->options(), EqualsProto(""));
9330 EXPECT_EQ(GetFeatures(oneof).enum_type(), FeatureSet::CLOSED);
9331 EXPECT_EQ(GetFeatures(oneof).GetExtension(pb::test).message_feature(),
9332 pb::VALUE9);
9333 }
9334
TEST_F(FeaturesTest,OneofFeaturesOverride)9335 TEST_F(FeaturesTest, OneofFeaturesOverride) {
9336 BuildDescriptorMessagesInTestPool();
9337 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
9338 const FileDescriptor* file = BuildFile(R"pb(
9339 name: "foo.proto"
9340 syntax: "editions"
9341 edition: EDITION_2023
9342 dependency: "google/protobuf/unittest_features.proto"
9343 options {
9344 features {
9345 [pb.test] { multiple_feature: VALUE2 file_feature: VALUE4 }
9346 }
9347 }
9348 message_type {
9349 name: "Foo"
9350 field {
9351 name: "oneof_field"
9352 number: 1
9353 label: LABEL_OPTIONAL
9354 type: TYPE_INT64
9355 oneof_index: 0
9356 }
9357 oneof_decl {
9358 name: "foo_oneof"
9359 options {
9360 features {
9361 [pb.test] { multiple_feature: VALUE9 }
9362 }
9363 }
9364 }
9365 options {
9366 features {
9367 [pb.test] { multiple_feature: VALUE5 message_feature: VALUE5 }
9368 }
9369 }
9370 }
9371 )pb");
9372 const OneofDescriptor* oneof = file->message_type(0)->oneof_decl(0);
9373 EXPECT_THAT(oneof->options(), EqualsProto(""));
9374 EXPECT_EQ(GetFeatures(oneof).GetExtension(pb::test).multiple_feature(),
9375 pb::VALUE9);
9376 EXPECT_EQ(GetFeatures(oneof).GetExtension(pb::test).message_feature(),
9377 pb::VALUE5);
9378 EXPECT_EQ(GetFeatures(oneof).GetExtension(pb::test).file_feature(),
9379 pb::VALUE4);
9380 }
9381
TEST_F(FeaturesTest,ExtensionRangeFeaturesDefault)9382 TEST_F(FeaturesTest, ExtensionRangeFeaturesDefault) {
9383 BuildDescriptorMessagesInTestPool();
9384 const FileDescriptor* file = BuildFile(R"pb(
9385 name: "foo.proto"
9386 syntax: "editions"
9387 edition: EDITION_2023
9388 message_type {
9389 name: "Foo"
9390 extension_range { start: 1 end: 100 }
9391 }
9392 )pb");
9393 const Descriptor::ExtensionRange* range =
9394 file->message_type(0)->extension_range(0);
9395 EXPECT_THAT(range->options(), EqualsProto(""));
9396 EXPECT_THAT(GetCoreFeatures(range), EqualsProto(R"pb(
9397 field_presence: EXPLICIT
9398 enum_type: OPEN
9399 repeated_field_encoding: PACKED
9400 utf8_validation: VERIFY
9401 message_encoding: LENGTH_PREFIXED
9402 json_format: ALLOW
9403 [pb.cpp] {
9404 legacy_closed_enum: false
9405 string_type: STRING
9406 enum_name_uses_string_view: false
9407 })pb"));
9408 }
9409
TEST_F(FeaturesTest,ExtensionRangeFeaturesInherit)9410 TEST_F(FeaturesTest, ExtensionRangeFeaturesInherit) {
9411 BuildDescriptorMessagesInTestPool();
9412 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
9413 const FileDescriptor* file = BuildFile(R"pb(
9414 name: "foo.proto"
9415 syntax: "editions"
9416 edition: EDITION_2023
9417 dependency: "google/protobuf/unittest_features.proto"
9418 options { features { enum_type: CLOSED } }
9419 message_type {
9420 name: "Foo"
9421 options {
9422 features {
9423 [pb.test] { message_feature: VALUE9 }
9424 }
9425 }
9426 extension_range { start: 1 end: 100 }
9427 }
9428 )pb");
9429 const Descriptor::ExtensionRange* range =
9430 file->message_type(0)->extension_range(0);
9431 EXPECT_THAT(range->options(), EqualsProto(""));
9432 EXPECT_EQ(GetFeatures(range).enum_type(), FeatureSet::CLOSED);
9433 EXPECT_EQ(GetFeatures(range).GetExtension(pb::test).message_feature(),
9434 pb::VALUE9);
9435 }
9436
TEST_F(FeaturesTest,ExtensionRangeFeaturesOverride)9437 TEST_F(FeaturesTest, ExtensionRangeFeaturesOverride) {
9438 BuildDescriptorMessagesInTestPool();
9439 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
9440 const FileDescriptor* file = BuildFile(R"pb(
9441 name: "foo.proto"
9442 syntax: "editions"
9443 edition: EDITION_2023
9444 dependency: "google/protobuf/unittest_features.proto"
9445 options {
9446 features {
9447 [pb.test] { multiple_feature: VALUE2 file_feature: VALUE4 }
9448 }
9449 }
9450 message_type {
9451 name: "Foo"
9452 options {
9453 features {
9454 [pb.test] { multiple_feature: VALUE5 message_feature: VALUE5 }
9455 }
9456 }
9457 extension_range {
9458 start: 1
9459 end: 100
9460 options {
9461 features {
9462 [pb.test] { multiple_feature: VALUE9 }
9463 }
9464 }
9465 }
9466 }
9467 )pb");
9468 const Descriptor::ExtensionRange* range =
9469 file->message_type(0)->extension_range(0);
9470 EXPECT_THAT(range->options(), EqualsProto(""));
9471 EXPECT_EQ(GetFeatures(range).GetExtension(pb::test).multiple_feature(),
9472 pb::VALUE9);
9473 EXPECT_EQ(GetFeatures(range).GetExtension(pb::test).message_feature(),
9474 pb::VALUE5);
9475 EXPECT_EQ(GetFeatures(range).GetExtension(pb::test).file_feature(),
9476 pb::VALUE4);
9477 }
9478
TEST_F(FeaturesTest,ServiceFeaturesDefault)9479 TEST_F(FeaturesTest, ServiceFeaturesDefault) {
9480 BuildDescriptorMessagesInTestPool();
9481 const FileDescriptor* file = BuildFile(R"pb(
9482 name: "foo.proto"
9483 syntax: "editions"
9484 edition: EDITION_2023
9485 service { name: "Foo" }
9486 )pb");
9487 const ServiceDescriptor* service = file->service(0);
9488 EXPECT_THAT(service->options(), EqualsProto(""));
9489 EXPECT_THAT(GetCoreFeatures(service), EqualsProto(R"pb(
9490 field_presence: EXPLICIT
9491 enum_type: OPEN
9492 repeated_field_encoding: PACKED
9493 utf8_validation: VERIFY
9494 message_encoding: LENGTH_PREFIXED
9495 json_format: ALLOW
9496 [pb.cpp] {
9497 legacy_closed_enum: false
9498 string_type: STRING
9499 enum_name_uses_string_view: false
9500 })pb"));
9501 }
9502
TEST_F(FeaturesTest,ServiceFeaturesInherit)9503 TEST_F(FeaturesTest, ServiceFeaturesInherit) {
9504 BuildDescriptorMessagesInTestPool();
9505 const FileDescriptor* file = BuildFile(R"pb(
9506 name: "foo.proto"
9507 syntax: "editions"
9508 edition: EDITION_2023
9509 options { features { enum_type: CLOSED } }
9510 service { name: "Foo" }
9511 )pb");
9512 const ServiceDescriptor* service = file->service(0);
9513 EXPECT_THAT(service->options(), EqualsProto(""));
9514 EXPECT_EQ(GetFeatures(service).enum_type(), FeatureSet::CLOSED);
9515 }
9516
TEST_F(FeaturesTest,ServiceFeaturesOverride)9517 TEST_F(FeaturesTest, ServiceFeaturesOverride) {
9518 BuildDescriptorMessagesInTestPool();
9519 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
9520 const FileDescriptor* file = BuildFile(R"pb(
9521 name: "foo.proto"
9522 syntax: "editions"
9523 edition: EDITION_2023
9524 dependency: "google/protobuf/unittest_features.proto"
9525 options {
9526 features {
9527 [pb.test] { multiple_feature: VALUE2 }
9528 }
9529 }
9530 service {
9531 name: "Foo"
9532 options {
9533 features {
9534 [pb.test] { multiple_feature: VALUE9 }
9535 }
9536 }
9537 }
9538 )pb");
9539 const ServiceDescriptor* service = file->service(0);
9540 EXPECT_THAT(service->options(), EqualsProto(""));
9541 EXPECT_EQ(GetFeatures(service).GetExtension(pb::test).multiple_feature(),
9542 pb::VALUE9);
9543 }
9544
TEST_F(FeaturesTest,MethodFeaturesDefault)9545 TEST_F(FeaturesTest, MethodFeaturesDefault) {
9546 BuildDescriptorMessagesInTestPool();
9547 const FileDescriptor* file = BuildFile(R"pb(
9548 name: "foo.proto"
9549 syntax: "editions"
9550 edition: EDITION_2023
9551 message_type { name: "EmptyMsg" }
9552 service {
9553 name: "Foo"
9554 method { name: "Bar" input_type: "EmptyMsg" output_type: "EmptyMsg" }
9555 }
9556 )pb");
9557 const MethodDescriptor* method = file->service(0)->method(0);
9558 EXPECT_THAT(method->options(), EqualsProto(""));
9559 EXPECT_THAT(GetCoreFeatures(method), EqualsProto(R"pb(
9560 field_presence: EXPLICIT
9561 enum_type: OPEN
9562 repeated_field_encoding: PACKED
9563 utf8_validation: VERIFY
9564 message_encoding: LENGTH_PREFIXED
9565 json_format: ALLOW
9566 [pb.cpp] {
9567 legacy_closed_enum: false
9568 string_type: STRING
9569 enum_name_uses_string_view: false
9570 })pb"));
9571 }
9572
TEST_F(FeaturesTest,MethodFeaturesInherit)9573 TEST_F(FeaturesTest, MethodFeaturesInherit) {
9574 BuildDescriptorMessagesInTestPool();
9575 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
9576 const FileDescriptor* file = BuildFile(R"pb(
9577 name: "foo.proto"
9578 syntax: "editions"
9579 edition: EDITION_2023
9580 dependency: "google/protobuf/unittest_features.proto"
9581 message_type { name: "EmptyMsg" }
9582 options { features { enum_type: CLOSED } }
9583 service {
9584 name: "Foo"
9585 options {
9586 features {
9587 [pb.test] { service_feature: VALUE9 }
9588 }
9589 }
9590 method { name: "Bar" input_type: "EmptyMsg" output_type: "EmptyMsg" }
9591 }
9592 )pb");
9593 const MethodDescriptor* method = file->service(0)->method(0);
9594 EXPECT_THAT(method->options(), EqualsProto(""));
9595 EXPECT_EQ(GetFeatures(method).enum_type(), FeatureSet::CLOSED);
9596 EXPECT_EQ(GetFeatures(method).GetExtension(pb::test).service_feature(),
9597 pb::VALUE9);
9598 }
9599
TEST_F(FeaturesTest,MethodFeaturesOverride)9600 TEST_F(FeaturesTest, MethodFeaturesOverride) {
9601 BuildDescriptorMessagesInTestPool();
9602 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
9603 const FileDescriptor* file = BuildFile(R"pb(
9604 name: "foo.proto"
9605 syntax: "editions"
9606 edition: EDITION_2023
9607 dependency: "google/protobuf/unittest_features.proto"
9608 message_type { name: "EmptyMsg" }
9609 options {
9610 features {
9611 enum_type: CLOSED
9612 [pb.test] { multiple_feature: VALUE2 }
9613 }
9614 }
9615 service {
9616 name: "Foo"
9617 options {
9618 features {
9619 [pb.test] { service_feature: VALUE4 multiple_feature: VALUE4 }
9620 }
9621 }
9622 method {
9623 name: "Bar"
9624 input_type: "EmptyMsg"
9625 output_type: "EmptyMsg"
9626 options {
9627 features {
9628 [pb.test] { multiple_feature: VALUE9 }
9629 }
9630 }
9631 }
9632 }
9633 )pb");
9634 const MethodDescriptor* method = file->service(0)->method(0);
9635 EXPECT_THAT(method->options(), EqualsProto(""));
9636 EXPECT_EQ(GetFeatures(method).enum_type(), FeatureSet::CLOSED);
9637 EXPECT_EQ(GetFeatures(method).GetExtension(pb::test).service_feature(),
9638 pb::VALUE4);
9639 EXPECT_EQ(GetFeatures(method).GetExtension(pb::test).multiple_feature(),
9640 pb::VALUE9);
9641 }
9642
TEST_F(FeaturesTest,FieldFeatureHelpers)9643 TEST_F(FeaturesTest, FieldFeatureHelpers) {
9644 BuildDescriptorMessagesInTestPool();
9645 const FileDescriptor* file = BuildFile(R"pb(
9646 name: "foo.proto"
9647 syntax: "editions"
9648 edition: EDITION_2023
9649 message_type {
9650 name: "Foo"
9651 field { name: "def" number: 1 label: LABEL_OPTIONAL type: TYPE_STRING }
9652 field { name: "rep" number: 2 label: LABEL_REPEATED type: TYPE_INT32 }
9653 field {
9654 name: "implicit_field"
9655 number: 3
9656 label: LABEL_OPTIONAL
9657 type: TYPE_STRING
9658 options { features { field_presence: IMPLICIT } }
9659 }
9660 field {
9661 name: "required_field"
9662 number: 4
9663 label: LABEL_OPTIONAL
9664 type: TYPE_STRING
9665 options { features { field_presence: LEGACY_REQUIRED } }
9666 }
9667 field {
9668 name: "required_message_field"
9669 number: 5
9670 label: LABEL_OPTIONAL
9671 type: TYPE_MESSAGE
9672 type_name: "Foo"
9673 options { features { field_presence: LEGACY_REQUIRED } }
9674 }
9675 field {
9676 name: "expanded_field"
9677 number: 6
9678 label: LABEL_REPEATED
9679 type: TYPE_STRING
9680 options { features { repeated_field_encoding: EXPANDED } }
9681 }
9682 field {
9683 name: "utf8_verify_field"
9684 number: 7
9685 label: LABEL_REPEATED
9686 type: TYPE_STRING
9687 options { features { utf8_validation: NONE } }
9688 }
9689 }
9690 )pb");
9691 const Descriptor* message = file->message_type(0);
9692 const FieldDescriptor* default_field = message->field(0);
9693 const FieldDescriptor* default_repeated_field = message->field(1);
9694 const FieldDescriptor* implicit_field = message->field(2);
9695 const FieldDescriptor* required_field = message->field(3);
9696 const FieldDescriptor* required_message_field = message->field(4);
9697 const FieldDescriptor* expanded_field = message->field(5);
9698 const FieldDescriptor* utf8_verify_field = message->field(6);
9699
9700 EXPECT_FALSE(default_field->is_packed());
9701 EXPECT_FALSE(default_field->is_required());
9702 EXPECT_TRUE(default_field->has_presence());
9703 EXPECT_TRUE(default_field->requires_utf8_validation());
9704 EXPECT_EQ(GetUtf8CheckMode(default_field, /*is_lite=*/false),
9705 Utf8CheckMode::kStrict);
9706 EXPECT_EQ(GetUtf8CheckMode(default_field, /*is_lite=*/true),
9707 Utf8CheckMode::kStrict);
9708
9709 EXPECT_TRUE(default_repeated_field->is_packed());
9710 EXPECT_FALSE(default_repeated_field->has_presence());
9711 EXPECT_FALSE(default_repeated_field->requires_utf8_validation());
9712 EXPECT_EQ(GetUtf8CheckMode(default_repeated_field, /*is_lite=*/false),
9713 Utf8CheckMode::kNone);
9714 EXPECT_EQ(GetUtf8CheckMode(default_repeated_field, /*is_lite=*/true),
9715 Utf8CheckMode::kNone);
9716
9717 EXPECT_TRUE(required_field->has_presence());
9718 EXPECT_TRUE(required_field->is_required());
9719 EXPECT_TRUE(required_message_field->has_presence());
9720 EXPECT_TRUE(required_message_field->is_required());
9721
9722 EXPECT_FALSE(implicit_field->has_presence());
9723 EXPECT_FALSE(expanded_field->is_packed());
9724 EXPECT_FALSE(utf8_verify_field->requires_utf8_validation());
9725 EXPECT_EQ(GetUtf8CheckMode(utf8_verify_field, /*is_lite=*/false),
9726 Utf8CheckMode::kVerify);
9727 EXPECT_EQ(GetUtf8CheckMode(utf8_verify_field, /*is_lite=*/true),
9728 Utf8CheckMode::kNone);
9729 EXPECT_EQ(GetUtf8CheckMode(utf8_verify_field, /*is_lite=*/false),
9730 Utf8CheckMode::kVerify);
9731 EXPECT_EQ(GetUtf8CheckMode(utf8_verify_field, /*is_lite=*/true),
9732 Utf8CheckMode::kNone);
9733 }
9734
TEST_F(FeaturesTest,EnumFeatureHelpers)9735 TEST_F(FeaturesTest, EnumFeatureHelpers) {
9736 BuildDescriptorMessagesInTestPool();
9737 BuildFileInTestPool(pb::CppFeatures::GetDescriptor()->file());
9738 const FileDescriptor* file = BuildFile(R"pb(
9739 name: "foo.proto"
9740 syntax: "editions"
9741 dependency: "google/protobuf/cpp_features.proto"
9742 edition: EDITION_2023
9743 enum_type {
9744 name: "FooOpen"
9745 value { name: "BAR" number: 0 }
9746 }
9747 enum_type {
9748 name: "FooClosed"
9749 value { name: "BAZ" number: 0 }
9750 options { features { enum_type: CLOSED } }
9751 }
9752 message_type {
9753 name: "FooMessage"
9754 field {
9755 name: "open"
9756 number: 1
9757 label: LABEL_OPTIONAL
9758 type: TYPE_ENUM
9759 type_name: "FooOpen"
9760 }
9761 field {
9762 name: "closed"
9763 number: 2
9764 label: LABEL_OPTIONAL
9765 type: TYPE_ENUM
9766 type_name: "FooClosed"
9767 }
9768 field {
9769 name: "legacy_closed"
9770 number: 3
9771 label: LABEL_OPTIONAL
9772 type: TYPE_ENUM
9773 type_name: "FooOpen"
9774 options {
9775 features {
9776 [pb.cpp] { legacy_closed_enum: true }
9777 }
9778 }
9779 }
9780 }
9781 )pb");
9782 const EnumDescriptor* open = file->enum_type(0);
9783 const EnumDescriptor* closed = file->enum_type(1);
9784 const FieldDescriptor* field_open = file->message_type(0)->field(0);
9785 const FieldDescriptor* field_closed = file->message_type(0)->field(1);
9786 const FieldDescriptor* field_legacy_closed = file->message_type(0)->field(2);
9787 ASSERT_EQ(field_legacy_closed->enum_type(), field_open->enum_type());
9788
9789 EXPECT_FALSE(open->is_closed());
9790 EXPECT_TRUE(closed->is_closed());
9791 EXPECT_FALSE(field_open->legacy_enum_field_treated_as_closed());
9792 EXPECT_TRUE(field_closed->legacy_enum_field_treated_as_closed());
9793 EXPECT_TRUE(field_legacy_closed->legacy_enum_field_treated_as_closed());
9794 EXPECT_TRUE(HasPreservingUnknownEnumSemantics(field_open));
9795 EXPECT_FALSE(HasPreservingUnknownEnumSemantics(field_closed));
9796 EXPECT_FALSE(HasPreservingUnknownEnumSemantics(field_legacy_closed));
9797 }
9798
TEST_F(FeaturesTest,FieldCppStringType)9799 TEST_F(FeaturesTest, FieldCppStringType) {
9800 BuildDescriptorMessagesInTestPool();
9801 const std::string file_contents = absl::Substitute(
9802 R"pb(
9803 name: "foo.proto"
9804 syntax: "editions"
9805 edition: EDITION_2024
9806 message_type {
9807 name: "Foo"
9808 field {
9809 name: "view"
9810 number: 1
9811 label: LABEL_OPTIONAL
9812 type: TYPE_STRING
9813 }
9814 field {
9815 name: "str"
9816 number: 2
9817 label: LABEL_OPTIONAL
9818 type: TYPE_STRING
9819 options {
9820 features {
9821 [pb.cpp] { string_type: STRING }
9822 }
9823 }
9824 }
9825 field {
9826 name: "cord"
9827 number: 3
9828 label: LABEL_OPTIONAL
9829 type: TYPE_STRING
9830 options {
9831 features {
9832 [pb.cpp] { string_type: CORD }
9833 }
9834 }
9835 }
9836 field {
9837 name: "cord_bytes"
9838 number: 4
9839 label: LABEL_OPTIONAL
9840 type: TYPE_BYTES
9841 options {
9842 features {
9843 [pb.cpp] { string_type: CORD }
9844 }
9845 }
9846 } $0
9847 }
9848 )pb",
9849 ""
9850 );
9851 const FileDescriptor* file = BuildFile(file_contents);
9852 const Descriptor* message = file->message_type(0);
9853 const FieldDescriptor* view = message->field(0);
9854 const FieldDescriptor* str = message->field(1);
9855 const FieldDescriptor* cord = message->field(2);
9856 const FieldDescriptor* cord_bytes = message->field(3);
9857
9858 EXPECT_EQ(view->cpp_string_type(), FieldDescriptor::CppStringType::kView);
9859 EXPECT_EQ(str->cpp_string_type(), FieldDescriptor::CppStringType::kString);
9860 EXPECT_EQ(cord_bytes->cpp_string_type(),
9861 FieldDescriptor::CppStringType::kCord);
9862 EXPECT_EQ(cord->cpp_string_type(), FieldDescriptor::CppStringType::kString);
9863
9864 }
9865
TEST_F(FeaturesTest,MergeFeatureValidationFailed)9866 TEST_F(FeaturesTest, MergeFeatureValidationFailed) {
9867 BuildDescriptorMessagesInTestPool();
9868 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
9869 BuildFileWithErrors(
9870 R"pb(
9871 name: "foo.proto"
9872 syntax: "editions"
9873 edition: EDITION_2023
9874 dependency: "google/protobuf/unittest_features.proto"
9875 options { features { field_presence: FIELD_PRESENCE_UNKNOWN } }
9876 )pb",
9877 "foo.proto: foo.proto: EDITIONS: Feature field "
9878 "`field_presence` must resolve to a known value, found "
9879 "FIELD_PRESENCE_UNKNOWN\n");
9880 }
9881
TEST_F(FeaturesTest,FeaturesOutsideEditions)9882 TEST_F(FeaturesTest, FeaturesOutsideEditions) {
9883 BuildDescriptorMessagesInTestPool();
9884 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
9885 BuildFileWithErrors(
9886 R"pb(
9887 name: "foo.proto"
9888 syntax: "proto2"
9889 dependency: "google/protobuf/unittest_features.proto"
9890 options { features { field_presence: IMPLICIT } }
9891 )pb",
9892 "foo.proto: foo.proto: EDITIONS: Features are only valid under "
9893 "editions.\n");
9894 }
9895
TEST_F(FeaturesTest,InvalidFileRequiredPresence)9896 TEST_F(FeaturesTest, InvalidFileRequiredPresence) {
9897 BuildDescriptorMessagesInTestPool();
9898 BuildFileWithErrors(
9899 R"pb(
9900 name: "foo.proto"
9901 syntax: "editions"
9902 edition: EDITION_2023
9903 options { features { field_presence: LEGACY_REQUIRED } }
9904 )pb",
9905 "foo.proto: foo.proto: EDITIONS: Required presence can't be specified "
9906 "by default.\n");
9907 }
9908
TEST_F(FeaturesTest,InvalidFileJavaStringCheckUtf8)9909 TEST_F(FeaturesTest, InvalidFileJavaStringCheckUtf8) {
9910 BuildDescriptorMessagesInTestPool();
9911 BuildFileWithErrors(
9912 R"pb(
9913 name: "foo.proto"
9914 syntax: "editions"
9915 edition: EDITION_2023
9916 options { java_string_check_utf8: true }
9917 )pb",
9918 "foo.proto: foo.proto: EDITIONS: File option java_string_check_utf8 is "
9919 "not allowed under editions. Use the (pb.java).utf8_validation feature "
9920 "to control this behavior.\n");
9921 }
9922
9923 TEST_F(FeaturesTest, Proto2FileJavaStringCheckUtf8) {
9924 BuildDescriptorMessagesInTestPool();
9925 const FileDescriptor* file = BuildFile(
9926 R"pb(
9927 name: "foo.proto"
9928 syntax: "proto2"
9929 options { java_string_check_utf8: true }
9930 )pb");
9931 EXPECT_EQ(file->options().java_string_check_utf8(), true);
9932 }
9933 TEST_F(FeaturesTest, InvalidFieldPacked) {
9934 BuildDescriptorMessagesInTestPool();
9935 BuildFileWithErrors(
9936 R"pb(
9937 name: "foo.proto"
9938 syntax: "editions"
9939 edition: EDITION_2023
9940 message_type {
9941 name: "Foo"
9942 field {
9943 name: "bar"
9944 number: 1
9945 label: LABEL_REPEATED
9946 type: TYPE_INT64
9947 options { packed: true }
9948 }
9949 }
9950 )pb",
9951 "foo.proto: Foo.bar: NAME: Field option packed is not allowed under "
9952 "editions. Use the repeated_field_encoding feature to control this "
9953 "behavior.\n");
9954 }
9955
9956 TEST_F(FeaturesTest, NoCtypeFromEdition2024) {
9957 BuildDescriptorMessagesInTestPool();
9958 BuildFileWithErrors(
9959 R"pb(
9960 name: "foo.proto"
9961 syntax: "editions"
9962 edition: EDITION_2024
9963 message_type {
9964 name: "Foo"
9965 field { name: "foo" number: 1 label: LABEL_OPTIONAL type: TYPE_INT32 }
9966 field {
9967 name: "bar"
9968 number: 2
9969 label: LABEL_OPTIONAL
9970 type: TYPE_STRING
9971 options { ctype: CORD }
9972 }
9973 }
9974 )pb",
9975 "foo.proto: Foo.bar: NAME: ctype option is not allowed under edition "
9976 "2024 and beyond. Use the feature string_type = VIEW|CORD|STRING|... "
9977 "instead.\n");
9978 }
9979
9980
9981 TEST_F(FeaturesTest, InvalidFieldImplicitDefault) {
9982 BuildDescriptorMessagesInTestPool();
9983 BuildFileWithErrors(
9984 R"pb(
9985 name: "foo.proto"
9986 syntax: "editions"
9987 edition: EDITION_2023
9988 message_type {
9989 name: "Foo"
9990 field {
9991 name: "bar"
9992 number: 1
9993 label: LABEL_OPTIONAL
9994 type: TYPE_STRING
9995 default_value: "Hello world"
9996 options { features { field_presence: IMPLICIT } }
9997 }
9998 }
9999 )pb",
10000 "foo.proto: Foo.bar: NAME: Implicit presence fields can't specify "
10001 "defaults.\n");
10002 }
10003
10004 TEST_F(FeaturesTest, ValidExtensionFieldImplicitDefault) {
10005 BuildDescriptorMessagesInTestPool();
10006 const FileDescriptor* file = BuildFile(
10007 R"pb(
10008 name: "foo.proto"
10009 syntax: "editions"
10010 edition: EDITION_2023
10011 options { features { field_presence: IMPLICIT } }
10012 message_type {
10013 name: "Foo"
10014 extension_range { start: 1 end: 100 }
10015 }
10016 extension {
10017 name: "bar"
10018 number: 1
10019 label: LABEL_OPTIONAL
10020 type: TYPE_STRING
10021 default_value: "Hello world"
10022 extendee: "Foo"
10023 }
10024 )pb");
10025 ASSERT_THAT(file, NotNull());
10026
10027 EXPECT_TRUE(file->extension(0)->has_presence());
10028 EXPECT_EQ(file->extension(0)->default_value_string(), "Hello world");
10029 }
10030
TEST_F(FeaturesTest,ValidOneofFieldImplicitDefault)10031 TEST_F(FeaturesTest, ValidOneofFieldImplicitDefault) {
10032 BuildDescriptorMessagesInTestPool();
10033 const FileDescriptor* file = BuildFile(
10034 R"pb(
10035 name: "foo.proto"
10036 syntax: "editions"
10037 edition: EDITION_2023
10038 options { features { field_presence: IMPLICIT } }
10039 message_type {
10040 name: "Foo"
10041 field {
10042 name: "bar"
10043 number: 1
10044 label: LABEL_OPTIONAL
10045 type: TYPE_STRING
10046 default_value: "Hello world"
10047 oneof_index: 0
10048 }
10049 oneof_decl { name: "_foo" }
10050 }
10051 )pb");
10052 ASSERT_THAT(file, NotNull());
10053
10054 EXPECT_TRUE(file->message_type(0)->field(0)->has_presence());
10055 EXPECT_EQ(file->message_type(0)->field(0)->default_value_string(),
10056 "Hello world");
10057 }
10058
TEST_F(FeaturesTest,InvalidFieldImplicitClosed)10059 TEST_F(FeaturesTest, InvalidFieldImplicitClosed) {
10060 BuildDescriptorMessagesInTestPool();
10061 BuildFileWithErrors(
10062 R"pb(
10063 name: "foo.proto"
10064 syntax: "editions"
10065 edition: EDITION_2023
10066 message_type {
10067 name: "Foo"
10068 field {
10069 name: "bar"
10070 number: 1
10071 label: LABEL_OPTIONAL
10072 type: TYPE_ENUM
10073 type_name: "Enum"
10074 options { features { field_presence: IMPLICIT } }
10075 }
10076 }
10077 enum_type {
10078 name: "Enum"
10079 value { name: "BAR" number: 0 }
10080 options { features { enum_type: CLOSED } }
10081 }
10082 )pb",
10083 "foo.proto: Foo.bar: NAME: Implicit presence enum fields must always "
10084 "be open.\n");
10085 }
10086
TEST_F(FeaturesTest,ValidRepeatedFieldImplicitClosed)10087 TEST_F(FeaturesTest, ValidRepeatedFieldImplicitClosed) {
10088 BuildDescriptorMessagesInTestPool();
10089 const FileDescriptor* file = BuildFile(
10090 R"pb(
10091 name: "foo.proto"
10092 syntax: "editions"
10093 edition: EDITION_2023
10094 options { features { field_presence: IMPLICIT } }
10095 message_type {
10096 name: "Foo"
10097 field {
10098 name: "bar"
10099 number: 1
10100 label: LABEL_REPEATED
10101 type: TYPE_ENUM
10102 type_name: "Enum"
10103 }
10104 }
10105 enum_type {
10106 name: "Enum"
10107 value { name: "BAR" number: 0 }
10108 options { features { enum_type: CLOSED } }
10109 }
10110 )pb");
10111 ASSERT_THAT(file, NotNull());
10112
10113 EXPECT_FALSE(file->message_type(0)->field(0)->has_presence());
10114 EXPECT_TRUE(file->enum_type(0)->is_closed());
10115 }
10116
TEST_F(FeaturesTest,ValidOneofFieldImplicitClosed)10117 TEST_F(FeaturesTest, ValidOneofFieldImplicitClosed) {
10118 BuildDescriptorMessagesInTestPool();
10119 const FileDescriptor* file = BuildFile(
10120 R"pb(
10121 name: "foo.proto"
10122 syntax: "editions"
10123 edition: EDITION_2023
10124 options { features { field_presence: IMPLICIT } }
10125 message_type {
10126 name: "Foo"
10127 field {
10128 name: "bar"
10129 number: 1
10130 label: LABEL_OPTIONAL
10131 type: TYPE_ENUM
10132 type_name: "Enum"
10133 oneof_index: 0
10134 }
10135 oneof_decl { name: "_foo" }
10136 }
10137 enum_type {
10138 name: "Enum"
10139 value { name: "BAR" number: 0 }
10140 options { features { enum_type: CLOSED } }
10141 }
10142 )pb");
10143 ASSERT_THAT(file, NotNull());
10144
10145 EXPECT_TRUE(file->message_type(0)->field(0)->has_presence());
10146 EXPECT_TRUE(file->enum_type(0)->is_closed());
10147 }
10148
TEST_F(FeaturesTest,InvalidFieldRequiredExtension)10149 TEST_F(FeaturesTest, InvalidFieldRequiredExtension) {
10150 BuildDescriptorMessagesInTestPool();
10151 BuildFileWithErrors(
10152 R"pb(
10153 name: "foo.proto"
10154 syntax: "editions"
10155 edition: EDITION_2023
10156 message_type {
10157 name: "Foo"
10158 extension_range { start: 1 end: 100 }
10159 }
10160 extension {
10161 name: "bar"
10162 number: 1
10163 label: LABEL_OPTIONAL
10164 type: TYPE_STRING
10165 options { features { field_presence: LEGACY_REQUIRED } }
10166 extendee: "Foo"
10167 }
10168 )pb",
10169 "foo.proto: bar: NAME: Extensions can't be required.\n");
10170 }
10171
TEST_F(FeaturesTest,InvalidFieldImplicitExtension)10172 TEST_F(FeaturesTest, InvalidFieldImplicitExtension) {
10173 BuildDescriptorMessagesInTestPool();
10174 BuildFileWithErrors(
10175 R"pb(
10176 name: "foo.proto"
10177 syntax: "editions"
10178 edition: EDITION_2023
10179 message_type {
10180 name: "Foo"
10181 extension_range { start: 1 end: 100 }
10182 }
10183 extension {
10184 name: "bar"
10185 number: 1
10186 label: LABEL_OPTIONAL
10187 type: TYPE_STRING
10188 options { features { field_presence: IMPLICIT } }
10189 extendee: "Foo"
10190 }
10191 )pb",
10192 "foo.proto: bar: NAME: Extensions can't specify field presence.\n");
10193 }
10194
10195 TEST_F(FeaturesTest, InvalidFieldMessageImplicit) {
10196 BuildDescriptorMessagesInTestPool();
10197 BuildFileWithErrors(
10198 R"pb(
10199 name: "foo.proto"
10200 syntax: "editions"
10201 edition: EDITION_2023
10202 message_type {
10203 name: "Foo"
10204 field {
10205 name: "bar"
10206 number: 1
10207 label: LABEL_OPTIONAL
10208 type: TYPE_MESSAGE
10209 type_name: "Foo"
10210 options { features { field_presence: IMPLICIT } }
10211 }
10212 }
10213 )pb",
10214 "foo.proto: Foo.bar: NAME: Message fields can't specify implicit "
10215 "presence.\n");
10216 }
10217
TEST_F(FeaturesTest,InvalidFieldRepeatedImplicit)10218 TEST_F(FeaturesTest, InvalidFieldRepeatedImplicit) {
10219 BuildDescriptorMessagesInTestPool();
10220 BuildFileWithErrors(
10221 R"pb(
10222 name: "foo.proto"
10223 syntax: "editions"
10224 edition: EDITION_2023
10225 message_type {
10226 name: "Foo"
10227 field {
10228 name: "bar"
10229 number: 1
10230 label: LABEL_REPEATED
10231 type: TYPE_STRING
10232 options { features { field_presence: IMPLICIT } }
10233 }
10234 }
10235 )pb",
10236 "foo.proto: Foo.bar: NAME: Repeated fields can't specify field "
10237 "presence.\n");
10238 }
10239
10240 TEST_F(FeaturesTest, InvalidFieldMapImplicit) {
10241 constexpr absl::string_view kProtoFile = R"schema(
10242 edition = "2023";
10243
10244 message Foo {
10245 map<string, Foo> bar = 1 [
10246 features.field_presence = IMPLICIT
10247 ];
10248 }
10249 )schema";
10250 io::ArrayInputStream input_stream(kProtoFile.data(), kProtoFile.size());
10251 SimpleErrorCollector error_collector;
10252 io::Tokenizer tokenizer(&input_stream, &error_collector);
10253 compiler::Parser parser;
10254 parser.RecordErrorsTo(&error_collector);
10255 FileDescriptorProto proto;
10256 ASSERT_TRUE(parser.Parse(&tokenizer, &proto))
10257 << error_collector.last_error() << "\n"
10258 << kProtoFile;
10259 ASSERT_EQ("", error_collector.last_error());
10260 proto.set_name("foo.proto");
10261
10262 BuildDescriptorMessagesInTestPool();
10263 BuildFileWithErrors(proto,
10264 "foo.proto: Foo.bar: NAME: Repeated fields can't specify "
10265 "field presence.\n");
10266 }
10267
TEST_F(FeaturesTest,InvalidFieldOneofImplicit)10268 TEST_F(FeaturesTest, InvalidFieldOneofImplicit) {
10269 BuildDescriptorMessagesInTestPool();
10270 BuildFileWithErrors(
10271 R"pb(
10272 name: "foo.proto"
10273 syntax: "editions"
10274 edition: EDITION_2023
10275 message_type {
10276 name: "Foo"
10277 field {
10278 name: "bar"
10279 number: 1
10280 oneof_index: 0
10281 label: LABEL_OPTIONAL
10282 type: TYPE_INT64
10283 options { features { field_presence: IMPLICIT } }
10284 }
10285 oneof_decl { name: "_foo" }
10286 }
10287 )pb",
10288 "foo.proto: Foo.bar: NAME: Oneof fields can't specify field presence.\n");
10289 }
10290
10291 TEST_F(FeaturesTest, InvalidFieldRepeatedRequired) {
10292 BuildDescriptorMessagesInTestPool();
10293 BuildFileWithErrors(
10294 R"pb(
10295 name: "foo.proto"
10296 syntax: "editions"
10297 edition: EDITION_2023
10298 message_type {
10299 name: "Foo"
10300 field {
10301 name: "bar"
10302 number: 1
10303 label: LABEL_REPEATED
10304 type: TYPE_STRING
10305 options { features { field_presence: LEGACY_REQUIRED } }
10306 }
10307 }
10308 )pb",
10309 "foo.proto: Foo.bar: NAME: Repeated fields can't specify field "
10310 "presence.\n");
10311 }
10312
TEST_F(FeaturesTest,InvalidFieldOneofRequired)10313 TEST_F(FeaturesTest, InvalidFieldOneofRequired) {
10314 BuildDescriptorMessagesInTestPool();
10315 BuildFileWithErrors(
10316 R"pb(
10317 name: "foo.proto"
10318 syntax: "editions"
10319 edition: EDITION_2023
10320 message_type {
10321 name: "Foo"
10322 field {
10323 name: "bar"
10324 number: 1
10325 oneof_index: 0
10326 label: LABEL_OPTIONAL
10327 type: TYPE_INT64
10328 options { features { field_presence: LEGACY_REQUIRED } }
10329 }
10330 oneof_decl { name: "_foo" }
10331 }
10332 )pb",
10333 "foo.proto: Foo.bar: NAME: Oneof fields can't specify field presence.\n");
10334 }
10335
10336 TEST_F(FeaturesTest, InvalidFieldNonStringWithStringValidation) {
10337 BuildDescriptorMessagesInTestPool();
10338 BuildFileWithErrors(
10339 R"pb(
10340 name: "foo.proto"
10341 syntax: "editions"
10342 edition: EDITION_2023
10343 message_type {
10344 name: "Foo"
10345 field {
10346 name: "bar"
10347 number: 1
10348 label: LABEL_OPTIONAL
10349 type: TYPE_INT64
10350 options { features { utf8_validation: NONE } }
10351 }
10352 }
10353 )pb",
10354 "foo.proto: Foo.bar: NAME: Only string fields can specify "
10355 "utf8 validation.\n");
10356 }
10357
TEST_F(FeaturesTest,InvalidFieldNonStringMapWithStringValidation)10358 TEST_F(FeaturesTest, InvalidFieldNonStringMapWithStringValidation) {
10359 BuildDescriptorMessagesInTestPool();
10360 BuildFileWithErrors(
10361 R"pb(
10362 name: "foo.proto"
10363 syntax: "editions"
10364 edition: EDITION_2023
10365 message_type {
10366 name: "Foo"
10367 nested_type {
10368 name: "MapFieldEntry"
10369 field {
10370 name: "key"
10371 number: 1
10372 label: LABEL_OPTIONAL
10373 type: TYPE_INT32
10374 options {
10375 uninterpreted_option {
10376 name { name_part: "features" is_extension: false }
10377 name { name_part: "utf8_validation" is_extension: false }
10378 identifier_value: "NONE"
10379 }
10380 }
10381 }
10382 field {
10383 name: "value"
10384 number: 2
10385 label: LABEL_OPTIONAL
10386 type: TYPE_INT32
10387 options {
10388 uninterpreted_option {
10389 name { name_part: "features" is_extension: false }
10390 name { name_part: "utf8_validation" is_extension: false }
10391 identifier_value: "NONE"
10392 }
10393 }
10394 }
10395 options { map_entry: true }
10396 }
10397 field {
10398 name: "map_field"
10399 number: 1
10400 label: LABEL_REPEATED
10401 type_name: "MapFieldEntry"
10402 options {
10403 uninterpreted_option {
10404 name { name_part: "features" is_extension: false }
10405 name { name_part: "utf8_validation" is_extension: false }
10406 identifier_value: "NONE"
10407 }
10408 }
10409 }
10410 }
10411 )pb",
10412 "foo.proto: Foo.map_field: NAME: Only string fields can specify "
10413 "utf8 validation.\n");
10414 }
10415
TEST_F(FeaturesTest,InvalidFieldNonRepeatedWithRepeatedEncoding)10416 TEST_F(FeaturesTest, InvalidFieldNonRepeatedWithRepeatedEncoding) {
10417 BuildDescriptorMessagesInTestPool();
10418 BuildFileWithErrors(
10419 R"pb(
10420 name: "foo.proto"
10421 syntax: "editions"
10422 edition: EDITION_2023
10423 message_type {
10424 name: "Foo"
10425 field {
10426 name: "bar"
10427 number: 1
10428 label: LABEL_OPTIONAL
10429 type: TYPE_INT64
10430 options { features { repeated_field_encoding: EXPANDED } }
10431 }
10432 }
10433 )pb",
10434 "foo.proto: Foo.bar: NAME: Only repeated fields can specify repeated "
10435 "field encoding.\n");
10436 }
10437
TEST_F(FeaturesTest,InvalidFieldNonPackableWithPackedRepeatedEncoding)10438 TEST_F(FeaturesTest, InvalidFieldNonPackableWithPackedRepeatedEncoding) {
10439 BuildDescriptorMessagesInTestPool();
10440 BuildFileWithErrors(
10441 R"pb(
10442 name: "foo.proto"
10443 syntax: "editions"
10444 edition: EDITION_2023
10445 message_type {
10446 name: "Foo"
10447 field {
10448 name: "bar"
10449 number: 1
10450 label: LABEL_REPEATED
10451 type: TYPE_STRING
10452 options { features { repeated_field_encoding: PACKED } }
10453 }
10454 }
10455 )pb",
10456 "foo.proto: Foo.bar: NAME: Only repeated primitive fields can specify "
10457 "PACKED repeated field encoding.\n");
10458 }
10459
TEST_F(FeaturesTest,InvalidFieldNonMessageWithMessageEncoding)10460 TEST_F(FeaturesTest, InvalidFieldNonMessageWithMessageEncoding) {
10461 BuildDescriptorMessagesInTestPool();
10462 BuildFileWithErrors(
10463 R"pb(
10464 name: "foo.proto"
10465 syntax: "editions"
10466 edition: EDITION_2023
10467 message_type {
10468 name: "Foo"
10469 field {
10470 name: "bar"
10471 number: 1
10472 label: LABEL_OPTIONAL
10473 type: TYPE_INT64
10474 options { features { message_encoding: DELIMITED } }
10475 }
10476 }
10477 )pb",
10478 "foo.proto: Foo.bar: NAME: Only message fields can specify message "
10479 "encoding.\n");
10480 }
10481
TEST_F(FeaturesTest,InvalidFieldMapWithMessageEncoding)10482 TEST_F(FeaturesTest, InvalidFieldMapWithMessageEncoding) {
10483 constexpr absl::string_view kProtoFile = R"schema(
10484 edition = "2023";
10485
10486 message Foo {
10487 map<string, Foo> bar = 1 [
10488 features.message_encoding = DELIMITED
10489 ];
10490 }
10491 )schema";
10492 io::ArrayInputStream input_stream(kProtoFile.data(), kProtoFile.size());
10493 SimpleErrorCollector error_collector;
10494 io::Tokenizer tokenizer(&input_stream, &error_collector);
10495 compiler::Parser parser;
10496 parser.RecordErrorsTo(&error_collector);
10497 FileDescriptorProto proto;
10498 ASSERT_TRUE(parser.Parse(&tokenizer, &proto))
10499 << error_collector.last_error() << "\n"
10500 << kProtoFile;
10501 ASSERT_EQ("", error_collector.last_error());
10502 proto.set_name("foo.proto");
10503
10504 BuildDescriptorMessagesInTestPool();
10505 BuildFileWithErrors(
10506 proto,
10507 "foo.proto: Foo.bar: NAME: Only message fields can specify message "
10508 "encoding.\n");
10509 }
10510
TEST_F(FeaturesTest,InvalidOpenEnumNonZeroFirstValue)10511 TEST_F(FeaturesTest, InvalidOpenEnumNonZeroFirstValue) {
10512 BuildDescriptorMessagesInTestPool();
10513 BuildFileWithErrors(
10514 R"pb(
10515 name: "foo.proto"
10516 syntax: "editions"
10517 edition: EDITION_2023
10518 enum_type {
10519 name: "Enum"
10520 value { name: "BAR" number: 1 }
10521 options { features { enum_type: OPEN } }
10522 }
10523 )pb",
10524 "foo.proto: Enum: NUMBER: The first enum value must be zero for open "
10525 "enums.\n");
10526 }
10527
TEST_F(FeaturesTest,InvalidUseFeaturesInSameFile)10528 TEST_F(FeaturesTest, InvalidUseFeaturesInSameFile) {
10529 BuildDescriptorMessagesInTestPool();
10530 ParseAndBuildFileWithErrors("foo.proto", R"schema(
10531 edition = "2023";
10532
10533 package test;
10534 import "google/protobuf/descriptor.proto";
10535
10536 message Foo {
10537 string bar = 1 [
10538 features.(test.custom).foo = "xyz",
10539 features.(test.another) = {foo: -321}
10540 ];
10541 }
10542
10543 message Custom {
10544 string foo = 1 [features = { [test.custom]: {foo: "abc"} }];
10545 }
10546 message Another {
10547 Enum foo = 1;
10548 }
10549
10550 enum Enum {
10551 option features.enum_type = CLOSED;
10552 ZERO = 0;
10553 ONE = 1;
10554 }
10555
10556 extend google.protobuf.FeatureSet {
10557 Custom custom = 1002 [features.message_encoding=DELIMITED];
10558 Another another = 1001;
10559 }
10560 )schema",
10561 "foo.proto: test.Foo.bar: OPTION_NAME: Feature "
10562 "\"features.(test.custom)\" can't be used in the "
10563 "same file it's defined in.\n");
10564 }
10565
TEST_F(FeaturesTest,ClosedEnumNonZeroFirstValue)10566 TEST_F(FeaturesTest, ClosedEnumNonZeroFirstValue) {
10567 BuildDescriptorMessagesInTestPool();
10568 const FileDescriptor* file = BuildFile(
10569 R"pb(
10570 name: "foo.proto"
10571 syntax: "editions"
10572 edition: EDITION_2023
10573 enum_type {
10574 name: "Enum"
10575 value { name: "BAR" number: 9 }
10576 options { features { enum_type: CLOSED } }
10577 }
10578 )pb");
10579
10580 EXPECT_EQ(file->enum_type(0)->value(0)->number(), 9);
10581 }
10582
TEST_F(FeaturesTest,CopyToIncludesFeatures)10583 TEST_F(FeaturesTest, CopyToIncludesFeatures) {
10584 BuildDescriptorMessagesInTestPool();
10585 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
10586 const FileDescriptor* file = BuildFile(R"pb(
10587 name: "foo.proto"
10588 syntax: "editions"
10589 edition: EDITION_2023
10590 dependency: "google/protobuf/unittest_features.proto"
10591 options {
10592 java_package: "pkg"
10593 features { field_presence: IMPLICIT }
10594 }
10595 message_type {
10596 name: "Foo"
10597 options {
10598 features {
10599 [pb.test] { multiple_feature: VALUE9 }
10600 }
10601 }
10602 field {
10603 name: "bar"
10604 number: 1
10605 label: LABEL_REPEATED
10606 type: TYPE_INT64
10607 options { features { repeated_field_encoding: EXPANDED } }
10608 }
10609 }
10610 )pb");
10611 FileDescriptorProto proto;
10612 file->CopyTo(&proto);
10613 EXPECT_THAT(proto.options(),
10614 EqualsProto(R"pb(java_package: "pkg"
10615 features { field_presence: IMPLICIT })pb"));
10616 EXPECT_THAT(proto.message_type(0).options(),
10617 EqualsProto(R"pb(features {
10618 [pb.test] { multiple_feature: VALUE9 }
10619 })pb"));
10620 EXPECT_THAT(
10621 proto.message_type(0).field(0).options(),
10622 EqualsProto(R"pb(features { repeated_field_encoding: EXPANDED })pb"));
10623 }
10624
TEST_F(FeaturesTest,UninterpretedOptions)10625 TEST_F(FeaturesTest, UninterpretedOptions) {
10626 BuildDescriptorMessagesInTestPool();
10627 const FileDescriptor* file = BuildFile(R"pb(
10628 name: "foo.proto"
10629 syntax: "editions"
10630 edition: EDITION_2023
10631 options {
10632 uninterpreted_option {
10633 name { name_part: "features" is_extension: false }
10634 name { name_part: "field_presence" is_extension: false }
10635 identifier_value: "IMPLICIT"
10636 }
10637 }
10638 )pb");
10639 EXPECT_THAT(file->options(), EqualsProto(""));
10640 EXPECT_THAT(GetCoreFeatures(file), EqualsProto(R"pb(
10641 field_presence: IMPLICIT
10642 enum_type: OPEN
10643 repeated_field_encoding: PACKED
10644 utf8_validation: VERIFY
10645 message_encoding: LENGTH_PREFIXED
10646 json_format: ALLOW
10647 [pb.cpp] {
10648 legacy_closed_enum: false
10649 string_type: STRING
10650 enum_name_uses_string_view: false
10651 })pb"));
10652 }
10653
TEST_F(FeaturesTest,UninterpretedOptionsMerge)10654 TEST_F(FeaturesTest, UninterpretedOptionsMerge) {
10655 BuildDescriptorMessagesInTestPool();
10656 const FileDescriptor* file = BuildFile(R"pb(
10657 name: "foo.proto"
10658 syntax: "editions"
10659 edition: EDITION_2023
10660 options {
10661 uninterpreted_option {
10662 name { name_part: "features" is_extension: false }
10663 name { name_part: "enum_type" is_extension: false }
10664 identifier_value: "CLOSED"
10665 }
10666 }
10667 message_type {
10668 name: "Foo"
10669 field {
10670 name: "bar"
10671 number: 1
10672 label: LABEL_OPTIONAL
10673 type: TYPE_STRING
10674 options {
10675 uninterpreted_option {
10676 name { name_part: "features" is_extension: false }
10677 name { name_part: "enum_type" is_extension: false }
10678 identifier_value: "OPEN"
10679 }
10680 }
10681 }
10682 }
10683 )pb");
10684 const FieldDescriptor* field = file->message_type(0)->field(0);
10685 EXPECT_THAT(file->options(), EqualsProto(""));
10686 EXPECT_THAT(field->options(), EqualsProto(""));
10687 EXPECT_EQ(GetFeatures(file).enum_type(), FeatureSet::CLOSED);
10688 EXPECT_EQ(GetFeatures(field).enum_type(), FeatureSet::OPEN);
10689 }
10690
TEST_F(FeaturesTest,UninterpretedOptionsMergeExtension)10691 TEST_F(FeaturesTest, UninterpretedOptionsMergeExtension) {
10692 BuildDescriptorMessagesInTestPool();
10693 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
10694 const FileDescriptor* file = BuildFile(R"pb(
10695 name: "foo.proto"
10696 syntax: "editions"
10697 edition: EDITION_2023
10698 dependency: "google/protobuf/unittest_features.proto"
10699 options {
10700 uninterpreted_option {
10701 name { name_part: "features" is_extension: false }
10702 name { name_part: "pb.test" is_extension: true }
10703 name { name_part: "multiple_feature" is_extension: false }
10704 identifier_value: "VALUE5"
10705 }
10706 uninterpreted_option {
10707 name { name_part: "features" is_extension: false }
10708 name { name_part: "pb.test" is_extension: true }
10709 name { name_part: "file_feature" is_extension: false }
10710 identifier_value: "VALUE5"
10711 }
10712 }
10713 message_type {
10714 name: "Foo"
10715 options {
10716 uninterpreted_option {
10717 name { name_part: "features" is_extension: false }
10718 name { name_part: "pb.test" is_extension: true }
10719 name { name_part: "multiple_feature" is_extension: false }
10720 identifier_value: "VALUE6"
10721 }
10722 uninterpreted_option {
10723 name { name_part: "features" is_extension: false }
10724 name { name_part: "pb.test" is_extension: true }
10725 name { name_part: "message_feature" is_extension: false }
10726 identifier_value: "VALUE6"
10727 }
10728 }
10729 field {
10730 name: "bar"
10731 number: 1
10732 label: LABEL_OPTIONAL
10733 type: TYPE_STRING
10734 options {
10735 uninterpreted_option {
10736 name { name_part: "features" is_extension: false }
10737 name { name_part: "pb.test" is_extension: true }
10738 name { name_part: "multiple_feature" is_extension: false }
10739 identifier_value: "VALUE7"
10740 }
10741 uninterpreted_option {
10742 name { name_part: "features" is_extension: false }
10743 name { name_part: "pb.test" is_extension: true }
10744 name { name_part: "field_feature" is_extension: false }
10745 identifier_value: "VALUE7"
10746 }
10747 }
10748 }
10749 }
10750 )pb");
10751 const FieldDescriptor* field = file->message_type(0)->field(0);
10752 EXPECT_THAT(field->options(), EqualsProto(""));
10753 EXPECT_EQ(GetFeatures(field).GetExtension(pb::test).file_feature(),
10754 pb::VALUE5);
10755 EXPECT_EQ(GetFeatures(field).GetExtension(pb::test).message_feature(),
10756 pb::VALUE6);
10757 EXPECT_EQ(GetFeatures(field).GetExtension(pb::test).field_feature(),
10758 pb::VALUE7);
10759 EXPECT_EQ(GetFeatures(field).GetExtension(pb::test).multiple_feature(),
10760 pb::VALUE7);
10761 }
10762
TEST_F(FeaturesTest,InvalidJsonUniquenessDefaultWarning)10763 TEST_F(FeaturesTest, InvalidJsonUniquenessDefaultWarning) {
10764 BuildFileWithWarnings(
10765 R"pb(
10766 name: "foo.proto"
10767 syntax: "editions"
10768 edition: EDITION_2023
10769 message_type {
10770 name: "Foo"
10771 field {
10772 name: "bar"
10773 number: 1
10774 label: LABEL_OPTIONAL
10775 type: TYPE_STRING
10776 }
10777 field {
10778 name: "bar_"
10779 number: 2
10780 label: LABEL_OPTIONAL
10781 type: TYPE_STRING
10782 }
10783 options { features { json_format: LEGACY_BEST_EFFORT } }
10784 }
10785 )pb",
10786 "foo.proto: Foo: NAME: The default JSON name of field \"bar_\" (\"bar\") "
10787 "conflicts with the default JSON name of field \"bar\".\n");
10788 }
10789
TEST_F(FeaturesTest,InvalidJsonUniquenessDefaultError)10790 TEST_F(FeaturesTest, InvalidJsonUniquenessDefaultError) {
10791 BuildFileWithErrors(
10792 R"pb(
10793 name: "foo.proto"
10794 syntax: "editions"
10795 edition: EDITION_2023
10796 message_type {
10797 name: "Foo"
10798 field {
10799 name: "bar"
10800 number: 1
10801 label: LABEL_OPTIONAL
10802 type: TYPE_STRING
10803 }
10804 field {
10805 name: "bar_"
10806 number: 2
10807 label: LABEL_OPTIONAL
10808 type: TYPE_STRING
10809 }
10810 options { features { json_format: ALLOW } }
10811 }
10812 )pb",
10813 "foo.proto: Foo: NAME: The default JSON name of field \"bar_\" (\"bar\") "
10814 "conflicts with the default JSON name of field \"bar\".\n");
10815 }
10816
TEST_F(FeaturesTest,InvalidJsonUniquenessCustomError)10817 TEST_F(FeaturesTest, InvalidJsonUniquenessCustomError) {
10818 BuildFileWithErrors(
10819 R"pb(
10820 name: "foo.proto"
10821 syntax: "editions"
10822 edition: EDITION_2023
10823 message_type {
10824 name: "Foo"
10825 field {
10826 name: "bar"
10827 json_name: "baz"
10828 number: 1
10829 label: LABEL_OPTIONAL
10830 type: TYPE_STRING
10831 }
10832 field {
10833 name: "bar2"
10834 json_name: "baz"
10835 number: 2
10836 label: LABEL_OPTIONAL
10837 type: TYPE_STRING
10838 }
10839 options { features { json_format: LEGACY_BEST_EFFORT } }
10840 }
10841 )pb",
10842 "foo.proto: Foo: NAME: The custom JSON name of field \"bar2\" (\"baz\") "
10843 "conflicts with the custom JSON name of field \"bar\".\n");
10844 }
10845
TEST_F(FeaturesTest,InvalidRequiredLabel)10846 TEST_F(FeaturesTest, InvalidRequiredLabel) {
10847 BuildDescriptorMessagesInTestPool();
10848 BuildFileWithErrors(
10849 R"pb(
10850 name: "foo.proto"
10851 syntax: "editions"
10852 edition: EDITION_2023
10853 message_type {
10854 name: "Foo"
10855 field {
10856 name: "bar"
10857 number: 1
10858 label: LABEL_REQUIRED
10859 type: TYPE_STRING
10860 }
10861 }
10862 )pb",
10863 "foo.proto: Foo.bar: NAME: Required label is not allowed under editions. "
10864 " Use the feature field_presence = LEGACY_REQUIRED to control this "
10865 "behavior.\n");
10866 }
10867
TEST_F(FeaturesTest,InvalidGroupLabel)10868 TEST_F(FeaturesTest, InvalidGroupLabel) {
10869 BuildDescriptorMessagesInTestPool();
10870 BuildFileWithErrors(
10871 R"pb(
10872 name: "foo.proto"
10873 syntax: "editions"
10874 edition: EDITION_2023
10875 message_type {
10876 name: "Foo"
10877 field {
10878 name: "bar"
10879 number: 1
10880 type_name: ".Foo"
10881 label: LABEL_OPTIONAL
10882 type: TYPE_GROUP
10883 }
10884 }
10885 )pb",
10886 "foo.proto: Foo.bar: NAME: Group types are not allowed under editions. "
10887 "Use the feature message_encoding = DELIMITED to control this "
10888 "behavior.\n");
10889 }
10890
TEST_F(FeaturesTest,DeprecatedFeature)10891 TEST_F(FeaturesTest, DeprecatedFeature) {
10892 pool_.AddDirectInputFile("foo.proto");
10893 BuildDescriptorMessagesInTestPool();
10894 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
10895 BuildFileWithWarnings(
10896 R"pb(
10897 name: "foo.proto"
10898 syntax: "editions"
10899 edition: EDITION_2023
10900 dependency: "google/protobuf/unittest_features.proto"
10901 options {
10902 uninterpreted_option {
10903 name { name_part: "features" is_extension: false }
10904 name { name_part: "pb.test" is_extension: true }
10905 name { name_part: "removed_feature" is_extension: false }
10906 identifier_value: "VALUE9"
10907 }
10908 }
10909 )pb",
10910 "foo.proto: foo.proto: NAME: Feature "
10911 "pb.TestFeatures.removed_feature has been deprecated in edition 2023: "
10912 "Custom feature deprecation warning\n");
10913 const FileDescriptor* file = pool_.FindFileByName("foo.proto");
10914 ASSERT_THAT(file, NotNull());
10915
10916 EXPECT_EQ(GetFeatures(file).GetExtension(pb::test).removed_feature(),
10917 pb::VALUE9);
10918 }
10919
TEST_F(FeaturesTest,IgnoreDeprecatedFeature)10920 TEST_F(FeaturesTest, IgnoreDeprecatedFeature) {
10921 BuildDescriptorMessagesInTestPool();
10922 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
10923 BuildFileWithWarnings(
10924 R"pb(
10925 name: "foo.proto"
10926 syntax: "editions"
10927 edition: EDITION_2023
10928 dependency: "google/protobuf/unittest_features.proto"
10929 options {
10930 uninterpreted_option {
10931 name { name_part: "features" is_extension: false }
10932 name { name_part: "pb.test" is_extension: true }
10933 name { name_part: "removed_feature" is_extension: false }
10934 identifier_value: "VALUE9"
10935 }
10936 }
10937 )pb",
10938 "");
10939 }
10940
TEST_F(FeaturesTest,IgnoreTransitiveFeature)10941 TEST_F(FeaturesTest, IgnoreTransitiveFeature) {
10942 pool_.AddDirectInputFile("bar.proto");
10943 BuildDescriptorMessagesInTestPool();
10944 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
10945 BuildFileWithWarnings(
10946 R"pb(
10947 name: "foo.proto"
10948 syntax: "editions"
10949 edition: EDITION_2023
10950 dependency: "google/protobuf/unittest_features.proto"
10951 options {
10952 uninterpreted_option {
10953 name { name_part: "features" is_extension: false }
10954 name { name_part: "pb.test" is_extension: true }
10955 name { name_part: "removed_feature" is_extension: false }
10956 identifier_value: "VALUE9"
10957 }
10958 }
10959 message_type { name: "Foo" }
10960 )pb",
10961 "");
10962 BuildFileWithWarnings(
10963 R"pb(
10964 name: "bar.proto"
10965 syntax: "editions"
10966 edition: EDITION_2023
10967 dependency: "foo.proto"
10968 message_type {
10969 name: "Bar"
10970 field {
10971 name: "bar"
10972 number: 1
10973 label: LABEL_OPTIONAL
10974 type: TYPE_MESSAGE
10975 type_name: ".Foo"
10976 }
10977 }
10978 )pb",
10979 "");
10980 }
10981
TEST_F(FeaturesTest,RemovedFeature)10982 TEST_F(FeaturesTest, RemovedFeature) {
10983 BuildDescriptorMessagesInTestPool();
10984 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
10985 BuildFileWithErrors(
10986 R"pb(
10987 name: "foo.proto"
10988 syntax: "editions"
10989 edition: EDITION_2024
10990 dependency: "google/protobuf/unittest_features.proto"
10991 options {
10992 features {
10993 [pb.test] { removed_feature: VALUE9 }
10994 }
10995 }
10996 )pb",
10997 "foo.proto: foo.proto: NAME: Feature "
10998 "pb.TestFeatures.removed_feature has been removed in edition 2024 and "
10999 "can't be used in edition 2024\n");
11000 }
11001
TEST_F(FeaturesTest,RemovedFeatureDefault)11002 TEST_F(FeaturesTest, RemovedFeatureDefault) {
11003 BuildDescriptorMessagesInTestPool();
11004 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
11005 const FileDescriptor* file =
11006 BuildFile(R"pb(
11007 name: "foo.proto" syntax: "editions" edition: EDITION_2024
11008 )pb");
11009 ASSERT_THAT(file, NotNull());
11010 EXPECT_EQ(GetFeatures(file).GetExtension(pb::test).removed_feature(),
11011 pb::VALUE3);
11012 }
11013
TEST_F(FeaturesTest,FutureFeature)11014 TEST_F(FeaturesTest, FutureFeature) {
11015 BuildDescriptorMessagesInTestPool();
11016 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
11017 BuildFileWithErrors(
11018 R"pb(
11019 name: "foo.proto"
11020 syntax: "editions"
11021 edition: EDITION_2023
11022 dependency: "google/protobuf/unittest_features.proto"
11023 options {
11024 features {
11025 [pb.test] { future_feature: VALUE9 }
11026 }
11027 }
11028 )pb",
11029 "foo.proto: foo.proto: NAME: Feature "
11030 "pb.TestFeatures.future_feature wasn't introduced until edition 2024 and "
11031 "can't be used in edition 2023\n");
11032 }
11033
TEST_F(FeaturesTest,FutureFeatureDefault)11034 TEST_F(FeaturesTest, FutureFeatureDefault) {
11035 BuildDescriptorMessagesInTestPool();
11036 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
11037 const FileDescriptor* file =
11038 BuildFile(R"pb(
11039 name: "foo.proto" syntax: "editions" edition: EDITION_2023
11040 )pb");
11041 ASSERT_THAT(file, NotNull());
11042 EXPECT_EQ(GetFeatures(file).GetExtension(pb::test).future_feature(),
11043 pb::VALUE1);
11044 }
11045
11046 // Test that the result of FileDescriptor::DebugString() can be used to create
11047 // the original descriptors.
11048 class FeaturesDebugStringTest
11049 : public FeaturesTest,
11050 public testing::WithParamInterface<
11051 std::tuple<absl::string_view, absl::string_view>> {
11052 protected:
LoadFile(absl::string_view name,absl::string_view content)11053 const FileDescriptor* LoadFile(absl::string_view name,
11054 absl::string_view content) {
11055 io::ArrayInputStream input_stream(content.data(), content.size());
11056 SimpleErrorCollector error_collector;
11057 io::Tokenizer tokenizer(&input_stream, &error_collector);
11058 compiler::Parser parser;
11059 parser.RecordErrorsTo(&error_collector);
11060 FileDescriptorProto proto;
11061 ABSL_CHECK(parser.Parse(&tokenizer, &proto))
11062 << error_collector.last_error() << "\n"
11063 << content;
11064 ABSL_CHECK_EQ("", error_collector.last_error());
11065 proto.set_name(name);
11066 return ABSL_DIE_IF_NULL(roundtrip_pool_.BuildFile(proto));
11067 }
11068
GetFileProto()11069 std::string GetFileProto() { return std::string(std::get<1>(GetParam())); }
11070
EqualsRoundTrip()11071 auto EqualsRoundTrip() { return EqualsProto(std::get<1>(GetParam())); }
11072
11073 private:
11074 DescriptorPool roundtrip_pool_;
11075 };
11076
TEST_P(FeaturesDebugStringTest,RoundTrip)11077 TEST_P(FeaturesDebugStringTest, RoundTrip) {
11078 BuildDescriptorMessagesInTestPool();
11079 BuildFileInTestPool(pb::TestFeatures::descriptor()->file());
11080 const FileDescriptor* file = BuildFile(GetFileProto());
11081 ASSERT_THAT(file, NotNull());
11082
11083 LoadFile("google/protobuf/descriptor.proto",
11084 DescriptorProto::GetDescriptor()->file()->DebugString());
11085 LoadFile("google/protobuf/unittest_features.proto",
11086 pb::TestFeatures::GetDescriptor()->file()->DebugString());
11087 const FileDescriptor* roundtripped =
11088 LoadFile(file->name(), file->DebugString());
11089
11090 FileDescriptorProto roundtripped_proto;
11091 roundtripped->CopyTo(&roundtripped_proto);
11092 EXPECT_THAT(roundtripped_proto, EqualsRoundTrip())
11093 << "With generated proto file: \n"
11094 << file->DebugString();
11095 }
11096
11097 INSTANTIATE_TEST_SUITE_P(
11098 FeaturesDebugStringTestInst, FeaturesDebugStringTest,
11099 testing::Values(
11100 std::make_tuple("Empty", R"pb(name: "foo.proto"
11101 syntax: "editions"
11102 edition: EDITION_2023
11103 )pb"),
11104 std::make_tuple(
11105 "FileFeature",
11106 R"pb(name: "foo.proto"
11107 syntax: "editions"
11108 edition: EDITION_2023
11109 dependency: "google/protobuf/unittest_features.proto"
11110 options {
11111 features {
11112 [pb.test] { file_feature: VALUE3 }
11113 }
11114 }
11115 )pb"),
11116 std::make_tuple("FieldFeature",
11117 R"pb(name: "foo.proto"
11118 syntax: "editions"
11119 edition: EDITION_2023
11120 message_type {
11121 name: "Foo"
11122 field {
11123 name: "bar"
11124 number: 1
11125 label: LABEL_OPTIONAL
11126 type: TYPE_INT64
11127 options {
11128 features { field_presence: IMPLICIT }
11129 }
11130 }
11131 }
11132 )pb"),
11133 std::make_tuple("Required",
11134 R"pb(name: "foo.proto"
11135 syntax: "editions"
11136 edition: EDITION_2023
11137 message_type {
11138 name: "Foo"
11139 field {
11140 name: "bar"
11141 number: 1
11142 label: LABEL_OPTIONAL
11143 type: TYPE_INT64
11144 options {
11145 features { field_presence: LEGACY_REQUIRED }
11146 }
11147 }
11148 }
11149 )pb"),
11150 std::make_tuple("Group",
11151 R"pb(name: "foo.proto"
11152 syntax: "editions"
11153 edition: EDITION_2023
11154 message_type {
11155 name: "Foo"
11156 nested_type {
11157 name: "Bar"
11158 field {
11159 name: "baz"
11160 number: 1
11161 label: LABEL_OPTIONAL
11162 type: TYPE_INT32
11163 }
11164 }
11165 field {
11166 name: "bar"
11167 number: 1
11168 label: LABEL_OPTIONAL
11169 type: TYPE_MESSAGE
11170 type_name: ".Foo.Bar"
11171 options {
11172 features { message_encoding: DELIMITED }
11173 }
11174 }
11175 }
11176 )pb"),
11177 std::make_tuple("MessageFeature",
11178 R"pb(name: "foo.proto"
11179 syntax: "editions"
11180 edition: EDITION_2023
11181 message_type {
11182 name: "Foo"
11183 options {
11184 features { json_format: LEGACY_BEST_EFFORT }
11185 }
11186 }
11187 )pb"),
11188 std::make_tuple(
11189 "OneofFeature",
11190 R"pb(name: "foo.proto"
11191 syntax: "editions"
11192 edition: EDITION_2023
11193 dependency: "google/protobuf/unittest_features.proto"
11194 message_type {
11195 name: "Foo"
11196 field {
11197 name: "bar"
11198 number: 2
11199 label: LABEL_OPTIONAL
11200 type: TYPE_INT64
11201 oneof_index: 0
11202 }
11203 oneof_decl {
11204 name: "foo_oneof"
11205 options {
11206 features {
11207 [pb.test] { oneof_feature: VALUE7 }
11208 }
11209 }
11210 }
11211 })pb"),
11212 std::make_tuple(
11213 "ExtensionRangeFeature",
11214 R"pb(name: "foo.proto"
11215 syntax: "editions"
11216 edition: EDITION_2023
11217 dependency: "google/protobuf/unittest_features.proto"
11218 message_type {
11219 name: "Foo"
11220 extension_range {
11221 start: 10
11222 end: 100
11223 options {
11224 features {
11225 [pb.test] { extension_range_feature: VALUE15 }
11226 }
11227 }
11228 }
11229 }
11230 )pb"),
11231 std::make_tuple("EnumFeature",
11232 R"pb(name: "foo.proto"
11233 syntax: "editions"
11234 edition: EDITION_2023
11235 enum_type {
11236 name: "Foo"
11237 value { name: "BAR" number: 1 }
11238 options { features { enum_type: CLOSED } }
11239 }
11240 )pb"),
11241 std::make_tuple(
11242 "EnumValueFeature",
11243 R"pb(name: "foo.proto"
11244 syntax: "editions"
11245 edition: EDITION_2023
11246 dependency: "google/protobuf/unittest_features.proto"
11247 enum_type {
11248 name: "Foo"
11249 value {
11250 name: "BAR"
11251 number: 0
11252 options {
11253 features {
11254 [pb.test] { enum_entry_feature: VALUE1 }
11255 }
11256 }
11257 }
11258
11259 }
11260 )pb"),
11261 std::make_tuple(
11262 "ServiceFeature",
11263 R"pb(name: "foo.proto"
11264 syntax: "editions"
11265 edition: EDITION_2023
11266 dependency: "google/protobuf/unittest_features.proto"
11267 service {
11268 name: "FooService"
11269 options {
11270 features {
11271 [pb.test] { service_feature: VALUE11 }
11272 }
11273 }
11274 }
11275 )pb"),
11276 std::make_tuple(
11277 "MethodFeature",
11278 R"pb(name: "foo.proto"
11279 syntax: "editions"
11280 edition: EDITION_2023
11281 dependency: "google/protobuf/unittest_features.proto"
11282 message_type { name: "EmptyMessage" }
11283 service {
11284 name: "FooService"
11285 method {
11286 name: "BarMethod"
11287 input_type: ".EmptyMessage"
11288 output_type: ".EmptyMessage"
11289 options {
11290 features {
11291 [pb.test] { method_feature: VALUE12 }
11292 }
11293 }
11294 }
11295 })pb")),
11296 [](const ::testing::TestParamInfo<FeaturesDebugStringTest::ParamType>&
__anon064ad7990702(const ::testing::TestParamInfo<FeaturesDebugStringTest::ParamType>& info) 11297 info) { return std::string(std::get<0>(info.param)); });
11298
11299 using DescriptorPoolFeaturesTest = FeaturesBaseTest;
11300
TEST_F(DescriptorPoolFeaturesTest,BuildStarted)11301 TEST_F(DescriptorPoolFeaturesTest, BuildStarted) {
11302 BuildDescriptorMessagesInTestPool();
11303 FeatureSetDefaults defaults = ParseTextOrDie(R"pb()pb");
11304 EXPECT_THAT(pool_.SetFeatureSetDefaults(std::move(defaults)),
11305 StatusIs(absl::StatusCode::kFailedPrecondition,
11306 HasSubstr("defaults can't be changed")));
11307 }
11308
TEST_F(DescriptorPoolFeaturesTest,InvalidRange)11309 TEST_F(DescriptorPoolFeaturesTest, InvalidRange) {
11310 FeatureSetDefaults defaults = ParseTextOrDie(R"pb(
11311 minimum_edition: EDITION_2023
11312 maximum_edition: EDITION_PROTO2
11313 )pb");
11314 EXPECT_THAT(pool_.SetFeatureSetDefaults(std::move(defaults)),
11315 StatusIs(absl::StatusCode::kInvalidArgument,
11316 AllOf(HasSubstr("Invalid edition range"),
11317 HasSubstr("PROTO2"), HasSubstr("2023"))));
11318 }
11319
TEST_F(DescriptorPoolFeaturesTest,UnknownDefaults)11320 TEST_F(DescriptorPoolFeaturesTest, UnknownDefaults) {
11321 FeatureSetDefaults defaults = ParseTextOrDie(R"pb(
11322 defaults {
11323 edition: EDITION_UNKNOWN
11324 overridable_features {}
11325 }
11326 minimum_edition: EDITION_PROTO2
11327 maximum_edition: EDITION_2023
11328 )pb");
11329 EXPECT_THAT(pool_.SetFeatureSetDefaults(std::move(defaults)),
11330 StatusIs(absl::StatusCode::kInvalidArgument,
11331 AllOf(HasSubstr("Invalid edition UNKNOWN"))));
11332 }
11333
TEST_F(DescriptorPoolFeaturesTest,NotStrictlyIncreasing)11334 TEST_F(DescriptorPoolFeaturesTest, NotStrictlyIncreasing) {
11335 FeatureSetDefaults defaults = ParseTextOrDie(R"pb(
11336 defaults {
11337 edition: EDITION_PROTO3
11338 overridable_features {}
11339 }
11340 defaults {
11341 edition: EDITION_PROTO2
11342 overridable_features {}
11343 }
11344 minimum_edition: EDITION_PROTO2
11345 maximum_edition: EDITION_2023
11346 )pb");
11347 EXPECT_THAT(
11348 pool_.SetFeatureSetDefaults(std::move(defaults)),
11349 StatusIs(
11350 absl::StatusCode::kInvalidArgument,
11351 AllOf(
11352 HasSubstr("not strictly increasing"),
11353 HasSubstr("PROTO3 is greater than or equal to edition PROTO2"))));
11354 }
11355
TEST_F(DescriptorPoolFeaturesTest,OverrideDefaults)11356 TEST_F(DescriptorPoolFeaturesTest, OverrideDefaults) {
11357 FeatureSetDefaults defaults = ParseTextOrDie(R"pb(
11358 defaults {
11359 edition: EDITION_PROTO2
11360 overridable_features {
11361 field_presence: EXPLICIT
11362 enum_type: CLOSED
11363 repeated_field_encoding: EXPANDED
11364 utf8_validation: VERIFY
11365 message_encoding: LENGTH_PREFIXED
11366 json_format: ALLOW
11367 }
11368 }
11369 minimum_edition: EDITION_PROTO2
11370 maximum_edition: EDITION_2023
11371 )pb");
11372 EXPECT_OK(pool_.SetFeatureSetDefaults(std::move(defaults)));
11373
11374 FileDescriptorProto file_proto = ParseTextOrDie(R"pb(
11375 name: "foo.proto"
11376 syntax: "editions"
11377 edition: EDITION_PROTO3
11378 )pb");
11379
11380 BuildDescriptorMessagesInTestPool();
11381 const FileDescriptor* file = ABSL_DIE_IF_NULL(pool_.BuildFile(file_proto));
11382 EXPECT_THAT(GetFeatures(file), EqualsProto(R"pb(
11383 field_presence: EXPLICIT
11384 enum_type: CLOSED
11385 repeated_field_encoding: EXPANDED
11386 utf8_validation: VERIFY
11387 message_encoding: LENGTH_PREFIXED
11388 json_format: ALLOW
11389 )pb"));
11390 }
11391
11392
11393
11394
TEST_F(ValidationErrorTest,ExtensionDeclarationsMatchFullNameCompile)11395 TEST_F(ValidationErrorTest, ExtensionDeclarationsMatchFullNameCompile) {
11396 BuildFile(R"pb(
11397 name: "foo.proto"
11398 package: "ext.test"
11399 message_type {
11400 name: "Foo"
11401 extension_range {
11402 start: 11
11403 end: 999
11404 options: {
11405 declaration: {
11406 number: 100
11407 full_name: ".ext.test.foo"
11408 type: ".ext.test.Bar"
11409 }
11410 }
11411 }
11412 }
11413 message_type { name: "Bar" }
11414 extension { extendee: "Foo" name: "foo" number: 100 type_name: "Bar" }
11415 )pb");
11416 }
11417
TEST_F(ValidationErrorTest,ExtensionDeclarationsMismatchFullName)11418 TEST_F(ValidationErrorTest, ExtensionDeclarationsMismatchFullName) {
11419 BuildFileWithErrors(
11420 R"pb(
11421 name: "foo.proto"
11422 package: "ext.test"
11423 message_type {
11424 name: "Foo"
11425 extension_range {
11426 start: 11
11427 end: 999
11428 options: {
11429 declaration: {
11430 number: 100
11431 full_name: ".ext.test.buz"
11432 type: ".ext.test.Bar"
11433 }
11434 }
11435 }
11436 }
11437 message_type { name: "Bar" }
11438 extension { extendee: "Foo" name: "foo" number: 100 type_name: "Bar" }
11439 )pb",
11440 "foo.proto: ext.test.foo: EXTENDEE: \"ext.test.Foo\" extension field 100"
11441 " is expected to have field name \".ext.test.buz\", not "
11442 "\".ext.test.foo\".\n");
11443 }
11444
TEST_F(ValidationErrorTest,ExtensionDeclarationsMismatchFullNameAllowed)11445 TEST_F(ValidationErrorTest, ExtensionDeclarationsMismatchFullNameAllowed) {
11446 // Make sure that extension declaration names and types are not validated
11447 // outside of protoc. This is important for allowing extensions to be renamed
11448 // safely.
11449 pool_.EnforceExtensionDeclarations(false);
11450 BuildFile(
11451 R"pb(
11452 name: "foo.proto"
11453 package: "ext.test"
11454 message_type {
11455 name: "Foo"
11456 extension_range {
11457 start: 11
11458 end: 999
11459 options: {
11460 declaration: {
11461 number: 100
11462 full_name: ".ext.test.buz"
11463 type: ".ext.test.Bar"
11464 }
11465 }
11466 }
11467 }
11468 message_type { name: "Bar" }
11469 extension { extendee: "Foo" name: "foo" number: 100 type_name: "Bar" }
11470 )pb");
11471 }
11472
TEST_F(ValidationErrorTest,ExtensionDeclarationsFullNameDoesNotLookLikeIdentifier)11473 TEST_F(ValidationErrorTest,
11474 ExtensionDeclarationsFullNameDoesNotLookLikeIdentifier) {
11475 BuildFileWithErrors(
11476 R"pb(
11477 name: "foo.proto"
11478 message_type {
11479 name: "Foo"
11480 extension_range {
11481 start: 10
11482 end: 11
11483 options: {
11484 declaration: {
11485 number: 10
11486 full_name: ".ext..test.bar"
11487 type: ".baz"
11488 }
11489 }
11490 }
11491 }
11492 )pb",
11493 "foo.proto: Foo: NAME: \".ext..test.bar\" contains invalid "
11494 "identifiers.\n");
11495 }
11496
TEST_F(ValidationErrorTest,ExtensionDeclarationsDuplicateNames)11497 TEST_F(ValidationErrorTest, ExtensionDeclarationsDuplicateNames) {
11498 BuildFileWithErrors(
11499 R"pb(
11500 name: "foo.proto"
11501 message_type {
11502 name: "Foo"
11503 extension_range {
11504 start: 11
11505 end: 1000
11506 options: {
11507 declaration: {
11508 number: 123
11509 full_name: ".foo.Bar.baz",
11510 type: ".Bar"
11511 }
11512 declaration: {
11513 number: 999
11514 full_name: ".foo.Bar.baz",
11515 type: "int32"
11516 }
11517 }
11518 }
11519 }
11520 )pb",
11521 "foo.proto: .foo.Bar.baz: NAME: Extension field name \".foo.Bar.baz\" is "
11522 "declared multiple times.\n");
11523 }
11524
TEST_F(ValidationErrorTest,ExtensionDeclarationMissingFullNameOrType)11525 TEST_F(ValidationErrorTest, ExtensionDeclarationMissingFullNameOrType) {
11526 BuildFileWithErrors(
11527 R"pb(
11528 name: "foo.proto"
11529 message_type {
11530 name: "Foo"
11531 extension_range {
11532 start: 10
11533 end: 11
11534 options: { declaration: { number: 10 full_name: ".foo.Bar.foo" } }
11535 }
11536 extension_range {
11537 start: 11
11538 end: 12
11539 options: { declaration: { number: 11 type: ".Baz" } }
11540 }
11541 }
11542 )pb",
11543 "foo.proto: Foo: EXTENDEE: Extension declaration #10 should have both"
11544 " \"full_name\" and \"type\" set.\n"
11545 "foo.proto: Foo: EXTENDEE: Extension declaration #11 should have both"
11546 " \"full_name\" and \"type\" set.\n");
11547 }
11548
TEST_F(ValidationErrorTest,ExtensionDeclarationsNumberNotInRange)11549 TEST_F(ValidationErrorTest, ExtensionDeclarationsNumberNotInRange) {
11550 BuildFileWithErrors(
11551 R"pb(
11552 name: "foo.proto"
11553 message_type {
11554 name: "Foo"
11555 extension_range {
11556 start: 4
11557 end: 9999
11558 options: {
11559 declaration: { number: 9999 full_name: ".abc" type: ".Bar" }
11560 }
11561 }
11562 }
11563 )pb",
11564 "foo.proto: Foo: NUMBER: Extension declaration number 9999 is not in the "
11565 "extension range.\n");
11566 }
11567
TEST_F(ValidationErrorTest,ExtensionDeclarationsFullNameMissingLeadingDot)11568 TEST_F(ValidationErrorTest, ExtensionDeclarationsFullNameMissingLeadingDot) {
11569 BuildFileWithErrors(
11570 R"pb(
11571 name: "foo.proto"
11572 message_type {
11573 name: "Foo"
11574 extension_range {
11575 start: 4
11576 end: 9999
11577 options: {
11578 declaration: { number: 10 full_name: "bar" type: "fixed64" }
11579 }
11580 }
11581 }
11582 )pb",
11583 "foo.proto: Foo: NAME: \"bar\" must have a leading dot to indicate the "
11584 "fully-qualified scope.\n");
11585 }
11586
11587 struct ExtensionDeclarationsTestParams {
11588 std::string test_name;
11589 };
11590
11591 // For OSS, we only have declaration to test with.
11592 using ExtensionDeclarationsTest =
11593 testing::TestWithParam<ExtensionDeclarationsTestParams>;
11594
11595 // For OSS, this is a function that directly returns the parsed
11596 // FileDescriptorProto.
ParameterizeFileProto(absl::string_view file_text,const ExtensionDeclarationsTestParams & param)11597 absl::StatusOr<FileDescriptorProto> ParameterizeFileProto(
11598 absl::string_view file_text, const ExtensionDeclarationsTestParams& param) {
11599 (void)file_text; // Parameter is used by Google-internal code.
11600 (void)param; // Parameter is used by Google-internal code.
11601 FileDescriptorProto file_proto;
11602 if (!TextFormat::ParseFromString(file_text, &file_proto)) {
11603 return absl::InvalidArgumentError("Failed to parse the input file text.");
11604 }
11605
11606 return file_proto;
11607 }
11608
TEST_P(ExtensionDeclarationsTest,DotPrefixTypeCompile)11609 TEST_P(ExtensionDeclarationsTest, DotPrefixTypeCompile) {
11610 absl::StatusOr<FileDescriptorProto> file_proto = ParameterizeFileProto(
11611 R"pb(
11612 name: "foo.proto"
11613 package: "ext.test"
11614 message_type {
11615 name: "Foo"
11616 extension_range {
11617 start: 4
11618 end: 99999
11619 options: {
11620 declaration: {
11621 number: 10
11622 full_name: ".ext.test.bar"
11623 type: ".ext.test.Bar"
11624 }
11625 }
11626 }
11627 }
11628 message_type { name: "Bar" }
11629 extension { extendee: "Foo" name: "bar" number: 10 type_name: "Bar" }
11630 )pb",
11631 GetParam());
11632 ASSERT_OK(file_proto);
11633
11634 DescriptorPool pool;
11635 pool.EnforceExtensionDeclarations(true);
11636 EXPECT_NE(pool.BuildFile(*file_proto), nullptr);
11637 }
11638
TEST_P(ExtensionDeclarationsTest,EnumTypeCompile)11639 TEST_P(ExtensionDeclarationsTest, EnumTypeCompile) {
11640 absl::StatusOr<FileDescriptorProto> file_proto = ParameterizeFileProto(
11641 R"pb(
11642 name: "foo.proto"
11643 package: "ext.test"
11644 message_type {
11645 name: "Foo"
11646 extension_range {
11647 start: 4
11648 end: 99999
11649 options: {
11650 declaration: {
11651 number: 10
11652 full_name: ".ext.test.bar"
11653 type: ".ext.test.Bar"
11654 }
11655 }
11656 }
11657 }
11658 enum_type {
11659 name: "Bar"
11660 value: { name: "BUZ" number: 123 }
11661 }
11662 extension { extendee: "Foo" name: "bar" number: 10 type_name: "Bar" }
11663 )pb",
11664 GetParam());
11665 ASSERT_OK(file_proto);
11666
11667 DescriptorPool pool;
11668 pool.EnforceExtensionDeclarations(true);
11669 EXPECT_NE(pool.BuildFile(*file_proto), nullptr);
11670 }
11671
TEST_P(ExtensionDeclarationsTest,MismatchEnumType)11672 TEST_P(ExtensionDeclarationsTest, MismatchEnumType) {
11673 absl::StatusOr<FileDescriptorProto> file_proto = ParameterizeFileProto(
11674 R"pb(
11675 name: "foo.proto"
11676 package: "ext.test"
11677 message_type {
11678 name: "Foo"
11679 extension_range {
11680 start: 4
11681 end: 99999
11682 options: {
11683 declaration: {
11684 number: 10
11685 full_name: ".ext.test.bar"
11686 type: ".ext.test.Bar"
11687 }
11688 }
11689 }
11690 }
11691 enum_type {
11692 name: "Bar"
11693 value: { name: "BUZ1" number: 123 }
11694 }
11695 enum_type {
11696 name: "Abc"
11697 value: { name: "BUZ2" number: 456 }
11698 }
11699 extension { extendee: "Foo" name: "bar" number: 10 type_name: "Abc" }
11700 )pb",
11701 GetParam());
11702 ASSERT_OK(file_proto);
11703
11704 DescriptorPool pool;
11705 pool.EnforceExtensionDeclarations(true);
11706 MockErrorCollector error_collector;
11707 EXPECT_EQ(pool.BuildFileCollectingErrors(*file_proto, &error_collector),
11708 nullptr);
11709 EXPECT_EQ(
11710 error_collector.text_,
11711 "foo.proto: ext.test.bar: EXTENDEE: \"ext.test.Foo\" extension field 10 "
11712 "is expected to be type \".ext.test.Bar\", not \".ext.test.Abc\".\n");
11713 }
11714
TEST_P(ExtensionDeclarationsTest,DotPrefixFullNameCompile)11715 TEST_P(ExtensionDeclarationsTest, DotPrefixFullNameCompile) {
11716 absl::StatusOr<FileDescriptorProto> file_proto = ParameterizeFileProto(
11717 R"pb(
11718 name: "foo.proto"
11719 package: "ext.test"
11720 message_type {
11721 name: "Foo"
11722 extension_range {
11723 start: 4
11724 end: 99999
11725 options: {
11726 declaration: {
11727 number: 10
11728 full_name: ".ext.test.bar"
11729 type: ".ext.test.Bar"
11730 }
11731 }
11732 }
11733 }
11734 message_type { name: "Bar" }
11735 extension { extendee: "Foo" name: "bar" number: 10 type_name: "Bar" }
11736 )pb",
11737 GetParam());
11738 ASSERT_OK(file_proto);
11739
11740 DescriptorPool pool;
11741 pool.EnforceExtensionDeclarations(true);
11742 EXPECT_NE(pool.BuildFile(*file_proto), nullptr);
11743 }
11744
TEST_P(ExtensionDeclarationsTest,MismatchDotPrefixTypeExpectingMessage)11745 TEST_P(ExtensionDeclarationsTest, MismatchDotPrefixTypeExpectingMessage) {
11746 absl::StatusOr<FileDescriptorProto> file_proto = ParameterizeFileProto(
11747 R"pb(
11748 name: "foo.proto"
11749 package: "ext.test"
11750 message_type {
11751 name: "Foo"
11752 extension_range {
11753 start: 4
11754 end: 99999
11755 options: {
11756 declaration: {
11757 number: 10
11758 full_name: ".ext.test.bar"
11759 type: ".int32"
11760 }
11761 }
11762 }
11763 }
11764 extension { name: "bar" number: 10 type: TYPE_INT32 extendee: "Foo" }
11765 )pb",
11766 GetParam());
11767 ASSERT_OK(file_proto);
11768
11769 DescriptorPool pool;
11770 pool.EnforceExtensionDeclarations(true);
11771 MockErrorCollector error_collector;
11772 EXPECT_EQ(pool.BuildFileCollectingErrors(*file_proto, &error_collector),
11773 nullptr);
11774 EXPECT_EQ(error_collector.text_,
11775 "foo.proto: ext.test.bar: EXTENDEE: \"ext.test.Foo\" extension "
11776 "field 10 is expected to be type \".int32\", not \"int32\".\n");
11777 }
11778
TEST_P(ExtensionDeclarationsTest,MismatchDotPrefixTypeExpectingNonMessage)11779 TEST_P(ExtensionDeclarationsTest, MismatchDotPrefixTypeExpectingNonMessage) {
11780 absl::StatusOr<FileDescriptorProto> file_proto = ParameterizeFileProto(
11781 R"pb(
11782 name: "foo.proto"
11783 message_type {
11784 name: "Foo"
11785 extension_range {
11786 start: 4
11787 end: 99999
11788 options: {
11789 declaration: { number: 10 full_name: ".bar" type: "int32" }
11790 }
11791 }
11792 }
11793 message_type { name: "int32" }
11794 extension { name: "bar" number: 10 type_name: "int32" extendee: "Foo" }
11795 )pb",
11796 GetParam());
11797 ASSERT_OK(file_proto);
11798
11799 DescriptorPool pool;
11800 pool.EnforceExtensionDeclarations(true);
11801 MockErrorCollector error_collector;
11802 EXPECT_EQ(pool.BuildFileCollectingErrors(*file_proto, &error_collector),
11803 nullptr);
11804 EXPECT_EQ(error_collector.text_,
11805 "foo.proto: bar: EXTENDEE: \"Foo\" extension field 10 is expected "
11806 "to be type \"int32\", not \".int32\".\n");
11807 }
11808
TEST_P(ExtensionDeclarationsTest,MismatchMessageType)11809 TEST_P(ExtensionDeclarationsTest, MismatchMessageType) {
11810 absl::StatusOr<FileDescriptorProto> file_proto = ParameterizeFileProto(
11811 R"pb(
11812 name: "foo.proto"
11813 package: "ext.test"
11814 message_type {
11815 name: "Foo"
11816 extension_range {
11817 start: 4
11818 end: 99999
11819 options: {
11820 declaration: {
11821 number: 10
11822 full_name: ".ext.test.foo"
11823 type: ".ext.test.Foo"
11824 }
11825 }
11826 }
11827 }
11828 message_type { name: "Bar" }
11829 extension { extendee: "Foo" name: "foo" number: 10 type_name: "Bar" }
11830 )pb",
11831 GetParam());
11832 ASSERT_OK(file_proto);
11833
11834 DescriptorPool pool;
11835 pool.EnforceExtensionDeclarations(true);
11836 MockErrorCollector error_collector;
11837 EXPECT_EQ(pool.BuildFileCollectingErrors(*file_proto, &error_collector),
11838 nullptr);
11839 EXPECT_EQ(
11840 error_collector.text_,
11841 "foo.proto: ext.test.foo: EXTENDEE: \"ext.test.Foo\" extension field 10 "
11842 "is expected to be type \".ext.test.Foo\", not \".ext.test.Bar\".\n");
11843 }
11844
TEST_P(ExtensionDeclarationsTest,NonMessageTypeCompile)11845 TEST_P(ExtensionDeclarationsTest, NonMessageTypeCompile) {
11846 absl::StatusOr<FileDescriptorProto> file_proto = ParameterizeFileProto(
11847 R"pb(
11848 name: "foo.proto"
11849 message_type {
11850 name: "Foo"
11851 extension_range {
11852 start: 10
11853 end: 11
11854 options: {
11855 declaration: { number: 10 full_name: ".bar" type: "fixed64" }
11856 }
11857 }
11858 }
11859 extension { name: "bar" number: 10 type: TYPE_FIXED64 extendee: "Foo" }
11860 )pb",
11861 GetParam());
11862 ASSERT_OK(file_proto);
11863
11864 DescriptorPool pool;
11865 pool.EnforceExtensionDeclarations(true);
11866 EXPECT_NE(pool.BuildFile(*file_proto), nullptr);
11867 }
11868
TEST_P(ExtensionDeclarationsTest,MismatchNonMessageType)11869 TEST_P(ExtensionDeclarationsTest, MismatchNonMessageType) {
11870 absl::StatusOr<FileDescriptorProto> file_proto = ParameterizeFileProto(
11871 R"pb(
11872 name: "foo.proto"
11873 package: "ext.test"
11874 message_type {
11875 name: "Foo"
11876 extension_range {
11877 start: 10
11878 end: 11
11879 options: {
11880 declaration: {
11881 number: 10
11882 full_name: ".ext.test.bar"
11883 type: "int32"
11884 }
11885 }
11886 }
11887 }
11888 extension { name: "bar" number: 10 type: TYPE_FIXED64 extendee: "Foo" }
11889 )pb",
11890 GetParam());
11891 ASSERT_OK(file_proto);
11892
11893 DescriptorPool pool;
11894 pool.EnforceExtensionDeclarations(true);
11895 MockErrorCollector error_collector;
11896 EXPECT_EQ(pool.BuildFileCollectingErrors(*file_proto, &error_collector),
11897 nullptr);
11898 EXPECT_EQ(error_collector.text_,
11899 "foo.proto: ext.test.bar: EXTENDEE: \"ext.test.Foo\" extension "
11900 "field 10 is expected to be type \"int32\", not \"fixed64\".\n");
11901 }
11902
TEST_P(ExtensionDeclarationsTest,MismatchCardinalityExpectingRepeated)11903 TEST_P(ExtensionDeclarationsTest, MismatchCardinalityExpectingRepeated) {
11904 absl::StatusOr<FileDescriptorProto> file_proto = ParameterizeFileProto(
11905 R"pb(
11906 name: "foo.proto"
11907 package: "ext.test"
11908 message_type {
11909 name: "Foo"
11910 extension_range {
11911 start: 10
11912 end: 11
11913 options: {
11914 declaration: {
11915 number: 10
11916 full_name: ".ext.test.bar"
11917 type: "fixed64"
11918 repeated: true
11919 }
11920 }
11921 }
11922 }
11923 extension { name: "bar" number: 10 type: TYPE_FIXED64 extendee: "Foo" }
11924 )pb",
11925 GetParam());
11926 ASSERT_OK(file_proto);
11927
11928 DescriptorPool pool;
11929 pool.EnforceExtensionDeclarations(true);
11930 MockErrorCollector error_collector;
11931 EXPECT_EQ(pool.BuildFileCollectingErrors(*file_proto, &error_collector),
11932 nullptr);
11933 EXPECT_EQ(error_collector.text_,
11934 "foo.proto: ext.test.bar: EXTENDEE: \"ext.test.Foo\" extension "
11935 "field 10 is expected to be repeated.\n");
11936 }
11937
TEST_P(ExtensionDeclarationsTest,MismatchCardinalityExpectingOptional)11938 TEST_P(ExtensionDeclarationsTest, MismatchCardinalityExpectingOptional) {
11939 absl::StatusOr<FileDescriptorProto> file_proto = ParameterizeFileProto(
11940 R"pb(
11941 name: "foo.proto"
11942 package: "ext.test"
11943 message_type {
11944 name: "Foo"
11945 extension_range {
11946 start: 10
11947 end: 11
11948 options: {
11949 declaration: {
11950 number: 10
11951 full_name: ".ext.test.bar"
11952 type: "fixed64"
11953 }
11954 }
11955 }
11956 }
11957 extension {
11958 name: "bar"
11959 number: 10
11960 type: TYPE_FIXED64
11961 extendee: "Foo"
11962 label: LABEL_REPEATED
11963 }
11964 )pb",
11965 GetParam());
11966 ASSERT_OK(file_proto);
11967
11968 DescriptorPool pool;
11969 pool.EnforceExtensionDeclarations(true);
11970 MockErrorCollector error_collector;
11971 EXPECT_EQ(pool.BuildFileCollectingErrors(*file_proto, &error_collector),
11972 nullptr);
11973 EXPECT_EQ(error_collector.text_,
11974 "foo.proto: ext.test.bar: EXTENDEE: \"ext.test.Foo\" extension "
11975 "field 10 is expected to be optional.\n");
11976 }
11977
TEST_P(ExtensionDeclarationsTest,TypeDoesNotLookLikeIdentifier)11978 TEST_P(ExtensionDeclarationsTest, TypeDoesNotLookLikeIdentifier) {
11979 absl::StatusOr<FileDescriptorProto> file_proto = ParameterizeFileProto(
11980 R"pb(
11981 name: "foo.proto"
11982 message_type {
11983 name: "Foo"
11984 extension_range {
11985 start: 10
11986 end: 11
11987 options: {
11988 declaration: {
11989 number: 10
11990 full_name: ".ext.test.bar"
11991 type: ".b#az"
11992 }
11993 }
11994 }
11995 }
11996 )pb",
11997 GetParam());
11998 ASSERT_OK(file_proto);
11999
12000 DescriptorPool pool;
12001 pool.EnforceExtensionDeclarations(true);
12002 MockErrorCollector error_collector;
12003 EXPECT_EQ(pool.BuildFileCollectingErrors(*file_proto, &error_collector),
12004 nullptr);
12005 EXPECT_EQ(error_collector.text_,
12006 "foo.proto: Foo: NAME: \".b#az\" contains invalid identifiers.\n");
12007 }
12008
TEST_P(ExtensionDeclarationsTest,MultipleDeclarationsInARangeCompile)12009 TEST_P(ExtensionDeclarationsTest, MultipleDeclarationsInARangeCompile) {
12010 absl::StatusOr<FileDescriptorProto> file_proto = ParameterizeFileProto(
12011 R"pb(
12012 name: "foo.proto"
12013 package: "ext.test"
12014 message_type {
12015 name: "Foo"
12016 extension_range {
12017 start: 4
12018 end: 99999
12019 options: {
12020 declaration: {
12021 number: 10
12022 full_name: ".ext.test.foo"
12023 type: ".ext.test.Bar"
12024 }
12025 declaration: {
12026 number: 99998
12027 full_name: ".ext.test.bar"
12028 type: ".ext.test.Bar"
12029 }
12030 declaration: {
12031 number: 12345
12032 full_name: ".ext.test.baz"
12033 type: ".ext.test.Bar"
12034 }
12035 }
12036 }
12037 }
12038 message_type { name: "Bar" }
12039 extension { extendee: "Foo" name: "foo" number: 10 type_name: "Bar" }
12040 extension { extendee: "Foo" name: "bar" number: 99998 type_name: "Bar" }
12041 extension { extendee: "Foo" name: "baz" number: 12345 type_name: "Bar" }
12042 )pb",
12043 GetParam());
12044 ASSERT_OK(file_proto);
12045
12046 DescriptorPool pool;
12047 pool.EnforceExtensionDeclarations(true);
12048 EXPECT_NE(pool.BuildFile(*file_proto), nullptr);
12049 }
12050
12051 INSTANTIATE_TEST_SUITE_P(
12052 ExtensionDeclarationTests, ExtensionDeclarationsTest,
12053 testing::ValuesIn<ExtensionDeclarationsTestParams>({
12054 {"Declaration"},
12055 }),
12056 [](const testing::TestParamInfo<ExtensionDeclarationsTest::ParamType>&
__anon064ad7990802(const testing::TestParamInfo<ExtensionDeclarationsTest::ParamType>& info) 12057 info) { return info.param.test_name; });
12058
12059
TEST_F(ValidationErrorTest,PackageTooLong)12060 TEST_F(ValidationErrorTest, PackageTooLong) {
12061 BuildFileWithErrors(
12062 "name: \"foo.proto\" "
12063 "syntax: \"proto3\" "
12064 "package: "
12065 "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
12066 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
12067 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
12068 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
12069 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
12070 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
12071 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
12072 "aaaaaaaaaa\"",
12073 "foo.proto: "
12074 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
12075 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
12076 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
12077 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
12078 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
12079 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
12080 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
12081 "aaaaaaaa: NAME: Package name is too long\n");
12082 }
12083
12084
12085 // ===================================================================
12086 // DescriptorDatabase
12087
AddToDatabase(SimpleDescriptorDatabase * database,absl::string_view file_text)12088 static void AddToDatabase(SimpleDescriptorDatabase* database,
12089 absl::string_view file_text) {
12090 FileDescriptorProto file_proto;
12091 EXPECT_TRUE(TextFormat::ParseFromString(file_text, &file_proto));
12092 database->Add(file_proto);
12093 }
12094
12095 class DatabaseBackedPoolTest : public testing::Test {
12096 protected:
12097 DatabaseBackedPoolTest() = default;
12098
12099 SimpleDescriptorDatabase database_;
12100
SetUp()12101 void SetUp() override {
12102 AddToDatabase(
12103 &database_,
12104 "name: 'foo.proto' "
12105 "message_type { name:'Foo' extension_range { start: 1 end: 100 } } "
12106 "enum_type { name:'TestEnum' value { name:'DUMMY' number:0 } } "
12107 "service { name:'TestService' } ");
12108 AddToDatabase(&database_,
12109 "name: 'bar.proto' "
12110 "dependency: 'foo.proto' "
12111 "message_type { name:'Bar' } "
12112 "extension { name:'foo_ext' extendee: '.Foo' number:5 "
12113 " label:LABEL_OPTIONAL type:TYPE_INT32 } ");
12114 // Baz has an undeclared dependency on Foo.
12115 AddToDatabase(
12116 &database_,
12117 "name: 'baz.proto' "
12118 "message_type { "
12119 " name:'Baz' "
12120 " field { name:'foo' number:1 label:LABEL_OPTIONAL type_name:'Foo' } "
12121 "}");
12122 }
12123
12124 // We can't inject a file containing errors into a DescriptorPool, so we
12125 // need an actual mock DescriptorDatabase to test errors.
12126 class ErrorDescriptorDatabase : public DescriptorDatabase {
12127 public:
12128 ErrorDescriptorDatabase() = default;
12129 ~ErrorDescriptorDatabase() override = default;
12130
12131 // implements DescriptorDatabase ---------------------------------
FindFileByName(const std::string & filename,FileDescriptorProto * output)12132 bool FindFileByName(const std::string& filename,
12133 FileDescriptorProto* output) override {
12134 // error.proto and error2.proto cyclically import each other.
12135 if (filename == "error.proto") {
12136 output->Clear();
12137 output->set_name("error.proto");
12138 output->add_dependency("error2.proto");
12139 return true;
12140 } else if (filename == "error2.proto") {
12141 output->Clear();
12142 output->set_name("error2.proto");
12143 output->add_dependency("error.proto");
12144 return true;
12145 } else {
12146 return false;
12147 }
12148 }
FindFileContainingSymbol(const std::string & symbol_name,FileDescriptorProto * output)12149 bool FindFileContainingSymbol(const std::string& symbol_name,
12150 FileDescriptorProto* output) override {
12151 return false;
12152 }
FindFileContainingExtension(const std::string & containing_type,int field_number,FileDescriptorProto * output)12153 bool FindFileContainingExtension(const std::string& containing_type,
12154 int field_number,
12155 FileDescriptorProto* output) override {
12156 return false;
12157 }
12158 };
12159
12160 // A DescriptorDatabase that counts how many times each method has been
12161 // called and forwards to some other DescriptorDatabase.
12162 class CallCountingDatabase : public DescriptorDatabase {
12163 public:
CallCountingDatabase(DescriptorDatabase * wrapped_db)12164 explicit CallCountingDatabase(DescriptorDatabase* wrapped_db)
12165 : wrapped_db_(wrapped_db) {
12166 Clear();
12167 }
12168 ~CallCountingDatabase() override = default;
12169
12170 DescriptorDatabase* wrapped_db_;
12171
12172 int call_count_;
12173
Clear()12174 void Clear() { call_count_ = 0; }
12175
12176 // implements DescriptorDatabase ---------------------------------
FindFileByName(const std::string & filename,FileDescriptorProto * output)12177 bool FindFileByName(const std::string& filename,
12178 FileDescriptorProto* output) override {
12179 ++call_count_;
12180 return wrapped_db_->FindFileByName(filename, output);
12181 }
FindFileContainingSymbol(const std::string & symbol_name,FileDescriptorProto * output)12182 bool FindFileContainingSymbol(const std::string& symbol_name,
12183 FileDescriptorProto* output) override {
12184 ++call_count_;
12185 return wrapped_db_->FindFileContainingSymbol(symbol_name, output);
12186 }
FindFileContainingExtension(const std::string & containing_type,int field_number,FileDescriptorProto * output)12187 bool FindFileContainingExtension(const std::string& containing_type,
12188 int field_number,
12189 FileDescriptorProto* output) override {
12190 ++call_count_;
12191 return wrapped_db_->FindFileContainingExtension(containing_type,
12192 field_number, output);
12193 }
12194 };
12195
12196 // A DescriptorDatabase which falsely always returns foo.proto when searching
12197 // for any symbol or extension number. This shouldn't cause the
12198 // DescriptorPool to reload foo.proto if it is already loaded.
12199 class FalsePositiveDatabase : public DescriptorDatabase {
12200 public:
FalsePositiveDatabase(DescriptorDatabase * wrapped_db)12201 explicit FalsePositiveDatabase(DescriptorDatabase* wrapped_db)
12202 : wrapped_db_(wrapped_db) {}
12203 ~FalsePositiveDatabase() override = default;
12204
12205 DescriptorDatabase* wrapped_db_;
12206
12207 // implements DescriptorDatabase ---------------------------------
FindFileByName(const std::string & filename,FileDescriptorProto * output)12208 bool FindFileByName(const std::string& filename,
12209 FileDescriptorProto* output) override {
12210 return wrapped_db_->FindFileByName(filename, output);
12211 }
FindFileContainingSymbol(const std::string & symbol_name,FileDescriptorProto * output)12212 bool FindFileContainingSymbol(const std::string& symbol_name,
12213 FileDescriptorProto* output) override {
12214 return FindFileByName("foo.proto", output);
12215 }
FindFileContainingExtension(const std::string & containing_type,int field_number,FileDescriptorProto * output)12216 bool FindFileContainingExtension(const std::string& containing_type,
12217 int field_number,
12218 FileDescriptorProto* output) override {
12219 return FindFileByName("foo.proto", output);
12220 }
12221 };
12222 };
12223
TEST_F(DatabaseBackedPoolTest,FindFileByName)12224 TEST_F(DatabaseBackedPoolTest, FindFileByName) {
12225 DescriptorPool pool(&database_);
12226
12227 const FileDescriptor* foo = pool.FindFileByName("foo.proto");
12228 ASSERT_TRUE(foo != nullptr);
12229 EXPECT_EQ("foo.proto", foo->name());
12230 ASSERT_EQ(1, foo->message_type_count());
12231 EXPECT_EQ("Foo", foo->message_type(0)->name());
12232
12233 EXPECT_EQ(foo, pool.FindFileByName("foo.proto"));
12234
12235 EXPECT_TRUE(pool.FindFileByName("no_such_file.proto") == nullptr);
12236 }
12237
TEST_F(DatabaseBackedPoolTest,FindDependencyBeforeDependent)12238 TEST_F(DatabaseBackedPoolTest, FindDependencyBeforeDependent) {
12239 DescriptorPool pool(&database_);
12240
12241 const FileDescriptor* foo = pool.FindFileByName("foo.proto");
12242 ASSERT_TRUE(foo != nullptr);
12243 EXPECT_EQ("foo.proto", foo->name());
12244 ASSERT_EQ(1, foo->message_type_count());
12245 EXPECT_EQ("Foo", foo->message_type(0)->name());
12246
12247 const FileDescriptor* bar = pool.FindFileByName("bar.proto");
12248 ASSERT_TRUE(bar != nullptr);
12249 EXPECT_EQ("bar.proto", bar->name());
12250 ASSERT_EQ(1, bar->message_type_count());
12251 EXPECT_EQ("Bar", bar->message_type(0)->name());
12252
12253 ASSERT_EQ(1, bar->dependency_count());
12254 EXPECT_EQ(foo, bar->dependency(0));
12255 }
12256
TEST_F(DatabaseBackedPoolTest,FindDependentBeforeDependency)12257 TEST_F(DatabaseBackedPoolTest, FindDependentBeforeDependency) {
12258 DescriptorPool pool(&database_);
12259
12260 const FileDescriptor* bar = pool.FindFileByName("bar.proto");
12261 ASSERT_TRUE(bar != nullptr);
12262 EXPECT_EQ("bar.proto", bar->name());
12263 ASSERT_EQ(1, bar->message_type_count());
12264 ASSERT_EQ("Bar", bar->message_type(0)->name());
12265
12266 const FileDescriptor* foo = pool.FindFileByName("foo.proto");
12267 ASSERT_TRUE(foo != nullptr);
12268 EXPECT_EQ("foo.proto", foo->name());
12269 ASSERT_EQ(1, foo->message_type_count());
12270 ASSERT_EQ("Foo", foo->message_type(0)->name());
12271
12272 ASSERT_EQ(1, bar->dependency_count());
12273 EXPECT_EQ(foo, bar->dependency(0));
12274 }
12275
TEST_F(DatabaseBackedPoolTest,FindFileContainingSymbol)12276 TEST_F(DatabaseBackedPoolTest, FindFileContainingSymbol) {
12277 DescriptorPool pool(&database_);
12278
12279 const FileDescriptor* file = pool.FindFileContainingSymbol("Foo");
12280 ASSERT_TRUE(file != nullptr);
12281 EXPECT_EQ("foo.proto", file->name());
12282 EXPECT_EQ(file, pool.FindFileByName("foo.proto"));
12283
12284 EXPECT_TRUE(pool.FindFileContainingSymbol("NoSuchSymbol") == nullptr);
12285 }
12286
TEST_F(DatabaseBackedPoolTest,FindMessageTypeByName)12287 TEST_F(DatabaseBackedPoolTest, FindMessageTypeByName) {
12288 DescriptorPool pool(&database_);
12289
12290 const Descriptor* type = pool.FindMessageTypeByName("Foo");
12291 ASSERT_TRUE(type != nullptr);
12292 EXPECT_EQ("Foo", type->name());
12293 EXPECT_EQ(type->file(), pool.FindFileByName("foo.proto"));
12294
12295 EXPECT_TRUE(pool.FindMessageTypeByName("NoSuchType") == nullptr);
12296 }
12297
TEST_F(DatabaseBackedPoolTest,FindExtensionByNumber)12298 TEST_F(DatabaseBackedPoolTest, FindExtensionByNumber) {
12299 DescriptorPool pool(&database_);
12300
12301 const Descriptor* foo = pool.FindMessageTypeByName("Foo");
12302 ASSERT_TRUE(foo != nullptr);
12303
12304 const FieldDescriptor* extension = pool.FindExtensionByNumber(foo, 5);
12305 ASSERT_TRUE(extension != nullptr);
12306 EXPECT_EQ("foo_ext", extension->name());
12307 EXPECT_EQ(extension->file(), pool.FindFileByName("bar.proto"));
12308
12309 EXPECT_TRUE(pool.FindExtensionByNumber(foo, 12) == nullptr);
12310 }
12311
TEST_F(DatabaseBackedPoolTest,FindAllExtensions)12312 TEST_F(DatabaseBackedPoolTest, FindAllExtensions) {
12313 DescriptorPool pool(&database_);
12314
12315 const Descriptor* foo = pool.FindMessageTypeByName("Foo");
12316
12317 for (int i = 0; i < 2; ++i) {
12318 // Repeat the lookup twice, to check that we get consistent
12319 // results despite the fallback database lookup mutating the pool.
12320 std::vector<const FieldDescriptor*> extensions;
12321 pool.FindAllExtensions(foo, &extensions);
12322 ASSERT_EQ(1, extensions.size());
12323 EXPECT_EQ(5, extensions[0]->number());
12324 }
12325 }
12326
TEST_F(DatabaseBackedPoolTest,ErrorWithoutErrorCollector)12327 TEST_F(DatabaseBackedPoolTest, ErrorWithoutErrorCollector) {
12328 ErrorDescriptorDatabase error_database;
12329 DescriptorPool pool(&error_database);
12330
12331 {
12332 absl::ScopedMockLog log;
12333 EXPECT_CALL(log, Log(absl::LogSeverity::kError, testing::_, testing::_))
12334 .Times(testing::AtLeast(1));
12335 log.StartCapturingLogs();
12336 EXPECT_TRUE(pool.FindFileByName("error.proto") == nullptr);
12337 }
12338 }
12339
TEST_F(DatabaseBackedPoolTest,ErrorWithErrorCollector)12340 TEST_F(DatabaseBackedPoolTest, ErrorWithErrorCollector) {
12341 ErrorDescriptorDatabase error_database;
12342 MockErrorCollector error_collector;
12343 DescriptorPool pool(&error_database, &error_collector);
12344
12345 EXPECT_TRUE(pool.FindFileByName("error.proto") == nullptr);
12346 EXPECT_EQ(
12347 "error.proto: error2.proto: IMPORT: File recursively imports itself: "
12348 "error.proto -> error2.proto -> error.proto\n"
12349 "error2.proto: error.proto: IMPORT: Import \"error.proto\" was not "
12350 "found or had errors.\n"
12351 "error.proto: error2.proto: IMPORT: Import \"error2.proto\" was not "
12352 "found or had errors.\n",
12353 error_collector.text_);
12354 }
12355
TEST_F(DatabaseBackedPoolTest,UndeclaredDependencyOnUnbuiltType)12356 TEST_F(DatabaseBackedPoolTest, UndeclaredDependencyOnUnbuiltType) {
12357 // Check that we find and report undeclared dependencies on types that exist
12358 // in the descriptor database but that have not been built yet.
12359 MockErrorCollector error_collector;
12360 DescriptorPool pool(&database_, &error_collector);
12361 EXPECT_TRUE(pool.FindMessageTypeByName("Baz") == nullptr);
12362 EXPECT_EQ(
12363 "baz.proto: Baz.foo: TYPE: \"Foo\" seems to be defined in \"foo.proto\", "
12364 "which is not imported by \"baz.proto\". To use it here, please add "
12365 "the necessary import.\n",
12366 error_collector.text_);
12367 }
12368
TEST_F(DatabaseBackedPoolTest,RollbackAfterError)12369 TEST_F(DatabaseBackedPoolTest, RollbackAfterError) {
12370 // Make sure that all traces of bad types are removed from the pool. This used
12371 // to be b/4529436, due to the fact that a symbol resolution failure could
12372 // potentially cause another file to be recursively built, which would trigger
12373 // a checkpoint _past_ possibly invalid symbols.
12374 // Baz is defined in the database, but the file is invalid because it is
12375 // missing a necessary import.
12376 DescriptorPool pool(&database_);
12377 EXPECT_TRUE(pool.FindMessageTypeByName("Baz") == nullptr);
12378 // Make sure that searching again for the file or the type fails.
12379 EXPECT_TRUE(pool.FindFileByName("baz.proto") == nullptr);
12380 EXPECT_TRUE(pool.FindMessageTypeByName("Baz") == nullptr);
12381 }
12382
TEST_F(DatabaseBackedPoolTest,UnittestProto)12383 TEST_F(DatabaseBackedPoolTest, UnittestProto) {
12384 // Try to load all of unittest.proto from a DescriptorDatabase. This should
12385 // thoroughly test all paths through DescriptorBuilder to insure that there
12386 // are no deadlocking problems when pool_->mutex_ is non-null.
12387 const FileDescriptor* original_file =
12388 protobuf_unittest::TestAllTypes::descriptor()->file();
12389
12390 DescriptorPoolDatabase database(*DescriptorPool::generated_pool());
12391 DescriptorPool pool(&database);
12392 const FileDescriptor* file_from_database =
12393 pool.FindFileByName(original_file->name());
12394
12395 ASSERT_TRUE(file_from_database != nullptr);
12396
12397 FileDescriptorProto original_file_proto;
12398 original_file->CopyTo(&original_file_proto);
12399
12400 FileDescriptorProto file_from_database_proto;
12401 file_from_database->CopyTo(&file_from_database_proto);
12402
12403 EXPECT_EQ(original_file_proto.DebugString(),
12404 file_from_database_proto.DebugString());
12405
12406 // Also verify that CopyTo() did not omit any information.
12407 EXPECT_EQ(original_file->DebugString(), file_from_database->DebugString());
12408 }
12409
TEST_F(DatabaseBackedPoolTest,FeatureResolution)12410 TEST_F(DatabaseBackedPoolTest, FeatureResolution) {
12411 {
12412 FileDescriptorProto proto;
12413 FileDescriptorProto::descriptor()->file()->CopyTo(&proto);
12414 std::string text_proto;
12415 google::protobuf::TextFormat::PrintToString(proto, &text_proto);
12416 AddToDatabase(&database_, text_proto);
12417 }
12418 {
12419 FileDescriptorProto proto;
12420 pb::TestFeatures::descriptor()->file()->CopyTo(&proto);
12421 std::string text_proto;
12422 google::protobuf::TextFormat::PrintToString(proto, &text_proto);
12423 AddToDatabase(&database_, text_proto);
12424 }
12425 AddToDatabase(&database_, R"pb(
12426 name: "features.proto"
12427 syntax: "editions"
12428 edition: EDITION_2023
12429 dependency: "google/protobuf/unittest_features.proto"
12430 options {
12431 features {
12432 enum_type: CLOSED
12433 [pb.test] { file_feature: VALUE9 multiple_feature: VALUE9 }
12434 }
12435 }
12436 message_type {
12437 name: "FooFeatures"
12438 options {
12439 features {
12440 [pb.test] { message_feature: VALUE8 multiple_feature: VALUE8 }
12441 }
12442 }
12443 }
12444 )pb");
12445 MockErrorCollector error_collector;
12446 DescriptorPool pool(&database_, &error_collector);
12447
12448 auto default_spec = FeatureResolver::CompileDefaults(
12449 FeatureSet::descriptor(),
12450 {GetExtensionReflection(pb::cpp), GetExtensionReflection(pb::test)},
12451 EDITION_PROTO2, EDITION_99999_TEST_ONLY);
12452 ASSERT_OK(default_spec);
12453 ASSERT_OK(pool.SetFeatureSetDefaults(std::move(default_spec).value()));
12454
12455 const Descriptor* foo = pool.FindMessageTypeByName("FooFeatures");
12456 ASSERT_TRUE(foo != nullptr);
12457 EXPECT_EQ(GetFeatures(foo).enum_type(), FeatureSet::CLOSED);
12458 EXPECT_EQ(GetFeatures(foo).repeated_field_encoding(), FeatureSet::PACKED);
12459 EXPECT_EQ(GetFeatures(foo).GetExtension(pb::test).enum_feature(), pb::VALUE1);
12460 EXPECT_EQ(GetFeatures(foo).GetExtension(pb::test).file_feature(), pb::VALUE9);
12461 EXPECT_EQ(GetFeatures(foo).GetExtension(pb::test).message_feature(),
12462 pb::VALUE8);
12463 EXPECT_EQ(GetFeatures(foo).GetExtension(pb::test).multiple_feature(),
12464 pb::VALUE8);
12465 }
12466
TEST_F(DatabaseBackedPoolTest,FeatureLifetimeError)12467 TEST_F(DatabaseBackedPoolTest, FeatureLifetimeError) {
12468 {
12469 FileDescriptorProto proto;
12470 FileDescriptorProto::descriptor()->file()->CopyTo(&proto);
12471 std::string text_proto;
12472 google::protobuf::TextFormat::PrintToString(proto, &text_proto);
12473 AddToDatabase(&database_, text_proto);
12474 }
12475 {
12476 FileDescriptorProto proto;
12477 pb::TestFeatures::descriptor()->file()->CopyTo(&proto);
12478 std::string text_proto;
12479 google::protobuf::TextFormat::PrintToString(proto, &text_proto);
12480 AddToDatabase(&database_, text_proto);
12481 }
12482 AddToDatabase(&database_, R"pb(
12483 name: "features.proto"
12484 syntax: "editions"
12485 edition: EDITION_2023
12486 dependency: "google/protobuf/unittest_features.proto"
12487 message_type {
12488 name: "FooFeatures"
12489 options {
12490 features {
12491 [pb.test] { future_feature: VALUE9 }
12492 }
12493 }
12494 }
12495 )pb");
12496 MockErrorCollector error_collector;
12497 DescriptorPool pool(&database_, &error_collector);
12498
12499 EXPECT_TRUE(pool.FindMessageTypeByName("FooFeatures") == nullptr);
12500 EXPECT_EQ(error_collector.text_,
12501 "features.proto: FooFeatures: NAME: Feature "
12502 "pb.TestFeatures.future_feature wasn't introduced until edition "
12503 "2024 and can't be used in edition 2023\n");
12504 }
12505
TEST_F(DatabaseBackedPoolTest,FeatureLifetimeErrorUnknownDependencies)12506 TEST_F(DatabaseBackedPoolTest, FeatureLifetimeErrorUnknownDependencies) {
12507 {
12508 FileDescriptorProto proto;
12509 FileDescriptorProto::descriptor()->file()->CopyTo(&proto);
12510 std::string text_proto;
12511 google::protobuf::TextFormat::PrintToString(proto, &text_proto);
12512 AddToDatabase(&database_, text_proto);
12513 }
12514 {
12515 FileDescriptorProto proto;
12516 pb::TestFeatures::descriptor()->file()->CopyTo(&proto);
12517 std::string text_proto;
12518 google::protobuf::TextFormat::PrintToString(proto, &text_proto);
12519 AddToDatabase(&database_, text_proto);
12520 }
12521 AddToDatabase(&database_, R"pb(
12522 name: "option.proto"
12523 syntax: "editions"
12524 edition: EDITION_2023
12525 dependency: "google/protobuf/descriptor.proto"
12526 dependency: "google/protobuf/unittest_features.proto"
12527 extension {
12528 name: "foo_extension"
12529 number: 1000
12530 type: TYPE_STRING
12531 extendee: ".google.protobuf.MessageOptions"
12532 options {
12533 features {
12534 [pb.test] { legacy_feature: VALUE9 }
12535 }
12536 }
12537 }
12538 )pb");
12539
12540 // Note, we very carefully don't put a dependency here, otherwise option.proto
12541 // will be built eagerly beforehand. This triggers a rare condition where
12542 // DeferredValidation is filled with descriptors that are then rolled back.
12543 AddToDatabase(&database_, R"pb(
12544 name: "use_option.proto"
12545 syntax: "editions"
12546 edition: EDITION_2023
12547 message_type {
12548 name: "FooMessage"
12549 options {
12550 uninterpreted_option {
12551 name { name_part: "foo_extension" is_extension: true }
12552 string_value: "test"
12553 }
12554 }
12555 field { name: "bar" number: 1 type: TYPE_INT64 }
12556 }
12557 )pb");
12558 MockErrorCollector error_collector;
12559 DescriptorPool pool(&database_, &error_collector);
12560
12561 ASSERT_EQ(pool.FindMessageTypeByName("FooMessage"), nullptr);
12562 EXPECT_EQ(error_collector.text_,
12563 "use_option.proto: FooMessage: OPTION_NAME: Option "
12564 "\"(foo_extension)\" unknown. Ensure that your proto definition "
12565 "file imports the proto which defines the option.\n");
12566
12567 // Verify that the extension does trigger a lifetime error.
12568 error_collector.text_.clear();
12569 ASSERT_EQ(pool.FindExtensionByName("foo_extension"), nullptr);
12570 EXPECT_EQ(error_collector.text_,
12571 "option.proto: foo_extension: NAME: Feature "
12572 "pb.TestFeatures.legacy_feature has been removed in edition 2023 "
12573 "and can't be used in edition 2023\n");
12574 }
12575
TEST_F(DatabaseBackedPoolTest,DoesntRetryDbUnnecessarily)12576 TEST_F(DatabaseBackedPoolTest, DoesntRetryDbUnnecessarily) {
12577 // Searching for a child of an existing descriptor should never fall back
12578 // to the DescriptorDatabase even if it isn't found, because we know all
12579 // children are already loaded.
12580 CallCountingDatabase call_counter(&database_);
12581 DescriptorPool pool(&call_counter);
12582
12583 const FileDescriptor* file = pool.FindFileByName("foo.proto");
12584 ASSERT_TRUE(file != nullptr);
12585 const Descriptor* foo = pool.FindMessageTypeByName("Foo");
12586 ASSERT_TRUE(foo != nullptr);
12587 const EnumDescriptor* test_enum = pool.FindEnumTypeByName("TestEnum");
12588 ASSERT_TRUE(test_enum != nullptr);
12589 const ServiceDescriptor* test_service = pool.FindServiceByName("TestService");
12590 ASSERT_TRUE(test_service != nullptr);
12591
12592 EXPECT_NE(0, call_counter.call_count_);
12593 call_counter.Clear();
12594
12595 EXPECT_TRUE(foo->FindFieldByName("no_such_field") == nullptr);
12596 EXPECT_TRUE(foo->FindExtensionByName("no_such_extension") == nullptr);
12597 EXPECT_TRUE(foo->FindNestedTypeByName("NoSuchMessageType") == nullptr);
12598 EXPECT_TRUE(foo->FindEnumTypeByName("NoSuchEnumType") == nullptr);
12599 EXPECT_TRUE(foo->FindEnumValueByName("NO_SUCH_VALUE") == nullptr);
12600 EXPECT_TRUE(test_enum->FindValueByName("NO_SUCH_VALUE") == nullptr);
12601 EXPECT_TRUE(test_service->FindMethodByName("NoSuchMethod") == nullptr);
12602
12603 EXPECT_TRUE(file->FindMessageTypeByName("NoSuchMessageType") == nullptr);
12604 EXPECT_TRUE(file->FindEnumTypeByName("NoSuchEnumType") == nullptr);
12605 EXPECT_TRUE(file->FindEnumValueByName("NO_SUCH_VALUE") == nullptr);
12606 EXPECT_TRUE(file->FindServiceByName("NO_SUCH_VALUE") == nullptr);
12607 EXPECT_TRUE(file->FindExtensionByName("no_such_extension") == nullptr);
12608
12609 EXPECT_TRUE(pool.FindFileContainingSymbol("Foo.no.such.field") == nullptr);
12610 EXPECT_TRUE(pool.FindFileContainingSymbol("Foo.no_such_field") == nullptr);
12611 EXPECT_TRUE(pool.FindMessageTypeByName("Foo.NoSuchMessageType") == nullptr);
12612 EXPECT_TRUE(pool.FindFieldByName("Foo.no_such_field") == nullptr);
12613 EXPECT_TRUE(pool.FindExtensionByName("Foo.no_such_extension") == nullptr);
12614 EXPECT_TRUE(pool.FindEnumTypeByName("Foo.NoSuchEnumType") == nullptr);
12615 EXPECT_TRUE(pool.FindEnumValueByName("Foo.NO_SUCH_VALUE") == nullptr);
12616 EXPECT_TRUE(pool.FindMethodByName("TestService.NoSuchMethod") == nullptr);
12617
12618 EXPECT_EQ(0, call_counter.call_count_);
12619 }
12620
TEST_F(DatabaseBackedPoolTest,DoesntReloadFilesUncesessarily)12621 TEST_F(DatabaseBackedPoolTest, DoesntReloadFilesUncesessarily) {
12622 // If FindFileContainingSymbol() or FindFileContainingExtension() return a
12623 // file that is already in the DescriptorPool, it should not attempt to
12624 // reload the file.
12625 FalsePositiveDatabase false_positive_database(&database_);
12626 MockErrorCollector error_collector;
12627 DescriptorPool pool(&false_positive_database, &error_collector);
12628
12629 // First make sure foo.proto is loaded.
12630 const Descriptor* foo = pool.FindMessageTypeByName("Foo");
12631 ASSERT_TRUE(foo != nullptr);
12632
12633 // Try inducing false positives.
12634 EXPECT_TRUE(pool.FindMessageTypeByName("NoSuchSymbol") == nullptr);
12635 EXPECT_TRUE(pool.FindExtensionByNumber(foo, 22) == nullptr);
12636
12637 // No errors should have been reported. (If foo.proto was incorrectly
12638 // loaded multiple times, errors would have been reported.)
12639 EXPECT_EQ("", error_collector.text_);
12640 }
12641
12642 // DescriptorDatabase that attempts to induce exponentially-bad performance
12643 // in DescriptorPool. For every positive N, the database contains a file
12644 // fileN.proto, which defines a message MessageN, which contains fields of
12645 // type MessageK for all K in [0,N). Message0 is not defined anywhere
12646 // (file0.proto exists, but is empty), so every other file and message type
12647 // will fail to build.
12648 //
12649 // If the DescriptorPool is not careful to memoize errors, an attempt to
12650 // build a descriptor for MessageN can require O(2^N) time.
12651 class ExponentialErrorDatabase : public DescriptorDatabase {
12652 public:
12653 ExponentialErrorDatabase() = default;
12654 ~ExponentialErrorDatabase() override = default;
12655
12656 // implements DescriptorDatabase ---------------------------------
FindFileByName(const std::string & filename,FileDescriptorProto * output)12657 bool FindFileByName(const std::string& filename,
12658 FileDescriptorProto* output) override {
12659 int file_num = -1;
12660 FullMatch(filename, "file", ".proto", &file_num);
12661 if (file_num > -1) {
12662 return PopulateFile(file_num, output);
12663 } else {
12664 return false;
12665 }
12666 }
FindFileContainingSymbol(const std::string & symbol_name,FileDescriptorProto * output)12667 bool FindFileContainingSymbol(const std::string& symbol_name,
12668 FileDescriptorProto* output) override {
12669 int file_num = -1;
12670 FullMatch(symbol_name, "Message", "", &file_num);
12671 if (file_num > 0) {
12672 return PopulateFile(file_num, output);
12673 } else {
12674 return false;
12675 }
12676 }
FindFileContainingExtension(const std::string & containing_type,int field_number,FileDescriptorProto * output)12677 bool FindFileContainingExtension(const std::string& containing_type,
12678 int field_number,
12679 FileDescriptorProto* output) override {
12680 return false;
12681 }
12682
12683 private:
FullMatch(absl::string_view name,absl::string_view begin_with,absl::string_view end_with,int32_t * file_num)12684 void FullMatch(absl::string_view name, absl::string_view begin_with,
12685 absl::string_view end_with, int32_t* file_num) {
12686 if (!absl::ConsumePrefix(&name, begin_with)) return;
12687 if (!absl::ConsumeSuffix(&name, end_with)) return;
12688 ABSL_CHECK(absl::SimpleAtoi(name, file_num));
12689 }
12690
PopulateFile(int file_num,FileDescriptorProto * output)12691 bool PopulateFile(int file_num, FileDescriptorProto* output) {
12692 ABSL_CHECK_GE(file_num, 0);
12693 output->Clear();
12694 output->set_name(absl::Substitute("file$0.proto", file_num));
12695 // file0.proto doesn't define Message0
12696 if (file_num > 0) {
12697 DescriptorProto* message = output->add_message_type();
12698 message->set_name(absl::Substitute("Message$0", file_num));
12699 for (int i = 0; i < file_num; ++i) {
12700 output->add_dependency(absl::Substitute("file$0.proto", i));
12701 FieldDescriptorProto* field = message->add_field();
12702 field->set_name(absl::Substitute("field$0", i));
12703 field->set_number(i);
12704 field->set_label(FieldDescriptorProto::LABEL_OPTIONAL);
12705 field->set_type(FieldDescriptorProto::TYPE_MESSAGE);
12706 field->set_type_name(absl::Substitute("Message$0", i));
12707 }
12708 }
12709 return true;
12710 }
12711 };
12712
TEST_F(DatabaseBackedPoolTest,DoesntReloadKnownBadFiles)12713 TEST_F(DatabaseBackedPoolTest, DoesntReloadKnownBadFiles) {
12714 ExponentialErrorDatabase error_database;
12715 DescriptorPool pool(&error_database);
12716
12717 ABSL_LOG(INFO) << "A timeout in this test probably indicates a real bug.";
12718
12719 EXPECT_TRUE(pool.FindFileByName("file40.proto") == nullptr);
12720 EXPECT_TRUE(pool.FindMessageTypeByName("Message40") == nullptr);
12721 }
12722
TEST_F(DatabaseBackedPoolTest,DoesntFallbackOnWrongType)12723 TEST_F(DatabaseBackedPoolTest, DoesntFallbackOnWrongType) {
12724 // If a lookup finds a symbol of the wrong type (e.g. we pass a type name
12725 // to FindFieldByName()), we should fail fast, without checking the fallback
12726 // database.
12727 CallCountingDatabase call_counter(&database_);
12728 DescriptorPool pool(&call_counter);
12729
12730 const FileDescriptor* file = pool.FindFileByName("foo.proto");
12731 ASSERT_TRUE(file != nullptr);
12732 const Descriptor* foo = pool.FindMessageTypeByName("Foo");
12733 ASSERT_TRUE(foo != nullptr);
12734 const EnumDescriptor* test_enum = pool.FindEnumTypeByName("TestEnum");
12735 ASSERT_TRUE(test_enum != nullptr);
12736
12737 EXPECT_NE(0, call_counter.call_count_);
12738 call_counter.Clear();
12739
12740 EXPECT_TRUE(pool.FindMessageTypeByName("TestEnum") == nullptr);
12741 EXPECT_TRUE(pool.FindFieldByName("Foo") == nullptr);
12742 EXPECT_TRUE(pool.FindExtensionByName("Foo") == nullptr);
12743 EXPECT_TRUE(pool.FindEnumTypeByName("Foo") == nullptr);
12744 EXPECT_TRUE(pool.FindEnumValueByName("Foo") == nullptr);
12745 EXPECT_TRUE(pool.FindServiceByName("Foo") == nullptr);
12746 EXPECT_TRUE(pool.FindMethodByName("Foo") == nullptr);
12747
12748 EXPECT_EQ(0, call_counter.call_count_);
12749 }
12750
12751 // ===================================================================
12752
12753 class AbortingErrorCollector : public DescriptorPool::ErrorCollector {
12754 public:
12755 AbortingErrorCollector() = default;
12756 AbortingErrorCollector(const AbortingErrorCollector&) = delete;
12757 AbortingErrorCollector& operator=(const AbortingErrorCollector&) = delete;
12758
RecordError(absl::string_view filename,absl::string_view element_name,const Message * message,ErrorLocation location,absl::string_view error_message)12759 void RecordError(absl::string_view filename, absl::string_view element_name,
12760 const Message* message, ErrorLocation location,
12761 absl::string_view error_message) override {
12762 ABSL_LOG(FATAL) << "AddError() called unexpectedly: " << filename << " ["
12763 << element_name << "]: " << error_message;
12764 }
12765 };
12766
12767 // A source tree containing only one file.
12768 class SingletonSourceTree : public compiler::SourceTree {
12769 public:
SingletonSourceTree(const std::string & filename,const std::string & contents)12770 SingletonSourceTree(const std::string& filename, const std::string& contents)
12771 : filename_(filename), contents_(contents) {}
12772 SingletonSourceTree(const SingletonSourceTree&) = delete;
12773 SingletonSourceTree& operator=(const SingletonSourceTree&) = delete;
12774
Open(absl::string_view filename)12775 io::ZeroCopyInputStream* Open(absl::string_view filename) override {
12776 return filename == filename_
12777 ? new io::ArrayInputStream(contents_.data(), contents_.size())
12778 : nullptr;
12779 }
12780
12781 private:
12782 const std::string filename_;
12783 const std::string contents_;
12784 };
12785
12786 const char* const kSourceLocationTestInput =
12787 "syntax = \"proto2\";\n"
12788 "option java_package = \"com.foo.bar\";\n"
12789 "option (test_file_opt) = \"foobar\";\n"
12790 "message A {\n"
12791 " option (test_msg_opt) = \"foobar\";\n"
12792 " optional int32 a = 1 [deprecated = true];\n"
12793 " message B {\n"
12794 " required double b = 1 [(test_field_opt) = \"foobar\"];\n"
12795 " }\n"
12796 " oneof c {\n"
12797 " option (test_oneof_opt) = \"foobar\";\n"
12798 " string d = 2;\n"
12799 " string e = 3;\n"
12800 " string f = 4;\n"
12801 " }\n"
12802 "}\n"
12803 "enum Indecision {\n"
12804 " option (test_enum_opt) = 21;\n"
12805 " option (test_enum_opt) = 42;\n"
12806 " option (test_enum_opt) = 63;\n"
12807 " YES = 1 [(test_enumval_opt).a = 100];\n"
12808 " NO = 2 [(test_enumval_opt) = {a:200}];\n"
12809 " MAYBE = 3;\n"
12810 "}\n"
12811 "service S {\n"
12812 " option (test_svc_opt) = {a:100};\n"
12813 " option (test_svc_opt) = {a:200};\n"
12814 " option (test_svc_opt) = {a:300};\n"
12815 " rpc Method(A) returns (A.B);\n"
12816 // Put an empty line here to make the source location range match.
12817 "\n"
12818 " rpc OtherMethod(A) returns (A) {\n"
12819 " option deprecated = true;\n"
12820 " option (test_method_opt) = \"foobar\";\n"
12821 " }\n"
12822 "}\n"
12823 "message MessageWithExtensions {\n"
12824 " extensions 1000 to 2000, 2001 to max [(test_ext_opt) = \"foobar\"];\n"
12825 "}\n"
12826 "extend MessageWithExtensions {\n"
12827 " repeated int32 int32_extension = 1001 [packed=true];\n"
12828 "}\n"
12829 "message C {\n"
12830 " extend MessageWithExtensions {\n"
12831 " optional C message_extension = 1002;\n"
12832 " }\n"
12833 "}\n"
12834 "import \"google/protobuf/descriptor.proto\";\n"
12835 "extend google.protobuf.FileOptions {\n"
12836 " optional string test_file_opt = 10101;\n"
12837 "}\n"
12838 "extend google.protobuf.MessageOptions {\n"
12839 " optional string test_msg_opt = 10101;\n"
12840 "}\n"
12841 "extend google.protobuf.FieldOptions {\n"
12842 " optional string test_field_opt = 10101;\n"
12843 "}\n"
12844 "extend google.protobuf.EnumOptions {\n"
12845 " repeated int32 test_enum_opt = 10101;\n"
12846 "}\n"
12847 "extend google.protobuf.EnumValueOptions {\n"
12848 " optional A test_enumval_opt = 10101;\n"
12849 "}\n"
12850 "extend google.protobuf.ServiceOptions {\n"
12851 " repeated A test_svc_opt = 10101;\n"
12852 "}\n"
12853 "extend google.protobuf.MethodOptions {\n"
12854 " optional string test_method_opt = 10101;\n"
12855 "}\n"
12856 "extend google.protobuf.OneofOptions {\n"
12857 " optional string test_oneof_opt = 10101;\n"
12858 "}\n"
12859 "extend google.protobuf.ExtensionRangeOptions {\n"
12860 " optional string test_ext_opt = 10101;\n"
12861 "}\n";
12862
12863 class SourceLocationTest : public testing::Test {
12864 public:
SourceLocationTest()12865 SourceLocationTest()
12866 : source_tree_("/test/test.proto", kSourceLocationTestInput),
12867 simple_db_(),
12868 source_tree_db_(&source_tree_),
12869 merged_db_(&simple_db_, &source_tree_db_),
12870 pool_(&merged_db_, &collector_) {
12871 // we need descriptor.proto to be accessible by the pool
12872 // since our test file imports it
12873 FileDescriptorProto::descriptor()->file()->CopyTo(&file_proto_);
12874 simple_db_.Add(file_proto_);
12875 }
12876
PrintSourceLocation(const SourceLocation & loc)12877 static std::string PrintSourceLocation(const SourceLocation& loc) {
12878 return absl::Substitute("$0:$1-$2:$3", 1 + loc.start_line,
12879 1 + loc.start_column, 1 + loc.end_line,
12880 1 + loc.end_column);
12881 }
12882
12883 private:
12884 FileDescriptorProto file_proto_;
12885 AbortingErrorCollector collector_;
12886 SingletonSourceTree source_tree_;
12887 SimpleDescriptorDatabase simple_db_; // contains descriptor.proto
12888 compiler::SourceTreeDescriptorDatabase source_tree_db_; // loads test.proto
12889 MergedDescriptorDatabase merged_db_; // combines above two dbs
12890
12891 protected:
12892 DescriptorPool pool_;
12893
12894 // tag number of all custom options in above test file
12895 static constexpr int kCustomOptionFieldNumber = 10101;
12896 // tag number of field "a" in message type "A" in above test file
12897 static constexpr int kAFieldNumber = 1;
12898 };
12899
12900 // TODO: implement support for option fields and for
12901 // subparts of declarations.
12902
TEST_F(SourceLocationTest,GetSourceLocation)12903 TEST_F(SourceLocationTest, GetSourceLocation) {
12904 SourceLocation loc;
12905
12906 const FileDescriptor* file_desc =
12907 ABSL_DIE_IF_NULL(pool_.FindFileByName("/test/test.proto"));
12908
12909 const Descriptor* a_desc = file_desc->FindMessageTypeByName("A");
12910 EXPECT_TRUE(a_desc->GetSourceLocation(&loc));
12911 EXPECT_EQ("4:1-16:2", PrintSourceLocation(loc));
12912
12913 const Descriptor* a_b_desc = a_desc->FindNestedTypeByName("B");
12914 EXPECT_TRUE(a_b_desc->GetSourceLocation(&loc));
12915 EXPECT_EQ("7:3-9:4", PrintSourceLocation(loc));
12916
12917 const EnumDescriptor* e_desc = file_desc->FindEnumTypeByName("Indecision");
12918 EXPECT_TRUE(e_desc->GetSourceLocation(&loc));
12919 EXPECT_EQ("17:1-24:2", PrintSourceLocation(loc));
12920
12921 const EnumValueDescriptor* yes_desc = e_desc->FindValueByName("YES");
12922 EXPECT_TRUE(yes_desc->GetSourceLocation(&loc));
12923 EXPECT_EQ("21:3-21:42", PrintSourceLocation(loc));
12924
12925 const ServiceDescriptor* s_desc = file_desc->FindServiceByName("S");
12926 EXPECT_TRUE(s_desc->GetSourceLocation(&loc));
12927 EXPECT_EQ("25:1-35:2", PrintSourceLocation(loc));
12928
12929 const MethodDescriptor* m_desc = s_desc->FindMethodByName("Method");
12930 EXPECT_TRUE(m_desc->GetSourceLocation(&loc));
12931 EXPECT_EQ("29:3-29:31", PrintSourceLocation(loc));
12932 }
12933
TEST_F(SourceLocationTest,ExtensionSourceLocation)12934 TEST_F(SourceLocationTest, ExtensionSourceLocation) {
12935 SourceLocation loc;
12936
12937 const FileDescriptor* file_desc =
12938 ABSL_DIE_IF_NULL(pool_.FindFileByName("/test/test.proto"));
12939
12940 const FieldDescriptor* int32_extension_desc =
12941 file_desc->FindExtensionByName("int32_extension");
12942 EXPECT_TRUE(int32_extension_desc->GetSourceLocation(&loc));
12943 EXPECT_EQ("40:3-40:55", PrintSourceLocation(loc));
12944
12945 const Descriptor* c_desc = file_desc->FindMessageTypeByName("C");
12946 EXPECT_TRUE(c_desc->GetSourceLocation(&loc));
12947 EXPECT_EQ("42:1-46:2", PrintSourceLocation(loc));
12948
12949 const FieldDescriptor* message_extension_desc =
12950 c_desc->FindExtensionByName("message_extension");
12951 EXPECT_TRUE(message_extension_desc->GetSourceLocation(&loc));
12952 EXPECT_EQ("44:5-44:41", PrintSourceLocation(loc));
12953 }
12954
TEST_F(SourceLocationTest,InterpretedOptionSourceLocation)12955 TEST_F(SourceLocationTest, InterpretedOptionSourceLocation) {
12956 // This one's a doozy. It checks every kind of option, including
12957 // extension range options.
12958
12959 // We are verifying that the file's source info contains correct
12960 // info for interpreted options and that it does *not* contain
12961 // any info for corresponding uninterpreted option path.
12962
12963 SourceLocation loc;
12964
12965 const FileDescriptor* file_desc =
12966 ABSL_DIE_IF_NULL(pool_.FindFileByName("/test/test.proto"));
12967
12968 // File options
12969 {
12970 int path[] = {FileDescriptorProto::kOptionsFieldNumber,
12971 FileOptions::kJavaPackageFieldNumber};
12972 int unint[] = {FileDescriptorProto::kOptionsFieldNumber,
12973 FileOptions::kUninterpretedOptionFieldNumber, 0};
12974
12975 std::vector<int> vpath(path, path + 2);
12976 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
12977 EXPECT_EQ("2:1-2:37", PrintSourceLocation(loc));
12978
12979 std::vector<int> vunint(unint, unint + 3);
12980 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
12981 }
12982 {
12983 int path[] = {FileDescriptorProto::kOptionsFieldNumber,
12984 kCustomOptionFieldNumber};
12985 int unint[] = {FileDescriptorProto::kOptionsFieldNumber,
12986 FileOptions::kUninterpretedOptionFieldNumber, 1};
12987 std::vector<int> vpath(path, path + 2);
12988 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
12989 EXPECT_EQ("3:1-3:35", PrintSourceLocation(loc));
12990
12991 std::vector<int> vunint(unint, unint + 3);
12992 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
12993 }
12994
12995 // Message option
12996 {
12997 int path[] = {FileDescriptorProto::kMessageTypeFieldNumber, 0,
12998 DescriptorProto::kOptionsFieldNumber,
12999 kCustomOptionFieldNumber};
13000 int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber, 0,
13001 DescriptorProto::kOptionsFieldNumber,
13002 MessageOptions::kUninterpretedOptionFieldNumber, 0};
13003 std::vector<int> vpath(path, path + 4);
13004 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13005 EXPECT_EQ("5:3-5:36", PrintSourceLocation(loc));
13006
13007 std::vector<int> vunint(unint, unint + 5);
13008 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13009 }
13010
13011 // Field option
13012 {
13013 int path[] = {FileDescriptorProto::kMessageTypeFieldNumber,
13014 0,
13015 DescriptorProto::kFieldFieldNumber,
13016 0,
13017 FieldDescriptorProto::kOptionsFieldNumber,
13018 FieldOptions::kDeprecatedFieldNumber};
13019 int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber,
13020 0,
13021 DescriptorProto::kFieldFieldNumber,
13022 0,
13023 FieldDescriptorProto::kOptionsFieldNumber,
13024 FieldOptions::kUninterpretedOptionFieldNumber,
13025 0};
13026 std::vector<int> vpath(path, path + 6);
13027 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13028 EXPECT_EQ("6:25-6:42", PrintSourceLocation(loc));
13029
13030 std::vector<int> vunint(unint, unint + 7);
13031 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13032 }
13033
13034 // Nested message option
13035 {
13036 int path[] = {
13037 FileDescriptorProto::kMessageTypeFieldNumber, 0,
13038 DescriptorProto::kNestedTypeFieldNumber, 0,
13039 DescriptorProto::kFieldFieldNumber, 0,
13040 FieldDescriptorProto::kOptionsFieldNumber, kCustomOptionFieldNumber};
13041 int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber,
13042 0,
13043 DescriptorProto::kNestedTypeFieldNumber,
13044 0,
13045 DescriptorProto::kFieldFieldNumber,
13046 0,
13047 FieldDescriptorProto::kOptionsFieldNumber,
13048 FieldOptions::kUninterpretedOptionFieldNumber,
13049 0};
13050 std::vector<int> vpath(path, path + 8);
13051 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13052 EXPECT_EQ("8:28-8:55", PrintSourceLocation(loc));
13053
13054 std::vector<int> vunint(unint, unint + 9);
13055 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13056 }
13057
13058 // One-of option
13059 {
13060 int path[] = {
13061 FileDescriptorProto::kMessageTypeFieldNumber, 0,
13062 DescriptorProto::kOneofDeclFieldNumber, 0,
13063 OneofDescriptorProto::kOptionsFieldNumber, kCustomOptionFieldNumber};
13064 int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber,
13065 0,
13066 DescriptorProto::kOneofDeclFieldNumber,
13067 0,
13068 OneofDescriptorProto::kOptionsFieldNumber,
13069 OneofOptions::kUninterpretedOptionFieldNumber,
13070 0};
13071 std::vector<int> vpath(path, path + 6);
13072 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13073 EXPECT_EQ("11:5-11:40", PrintSourceLocation(loc));
13074
13075 std::vector<int> vunint(unint, unint + 7);
13076 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13077 }
13078
13079 // Enum option, repeated options
13080 {
13081 int path[] = {FileDescriptorProto::kEnumTypeFieldNumber, 0,
13082 EnumDescriptorProto::kOptionsFieldNumber,
13083 kCustomOptionFieldNumber, 0};
13084 int unint[] = {FileDescriptorProto::kEnumTypeFieldNumber, 0,
13085 EnumDescriptorProto::kOptionsFieldNumber,
13086 EnumOptions::kUninterpretedOptionFieldNumber, 0};
13087 std::vector<int> vpath(path, path + 5);
13088 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13089 EXPECT_EQ("18:3-18:31", PrintSourceLocation(loc));
13090
13091 std::vector<int> vunint(unint, unint + 5);
13092 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13093 }
13094 {
13095 int path[] = {FileDescriptorProto::kEnumTypeFieldNumber, 0,
13096 EnumDescriptorProto::kOptionsFieldNumber,
13097 kCustomOptionFieldNumber, 1};
13098 int unint[] = {FileDescriptorProto::kEnumTypeFieldNumber, 0,
13099 EnumDescriptorProto::kOptionsFieldNumber,
13100 EnumOptions::kUninterpretedOptionFieldNumber, 1};
13101 std::vector<int> vpath(path, path + 5);
13102 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13103 EXPECT_EQ("19:3-19:31", PrintSourceLocation(loc));
13104
13105 std::vector<int> vunint(unint, unint + 5);
13106 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13107 }
13108 {
13109 int path[] = {FileDescriptorProto::kEnumTypeFieldNumber, 0,
13110 EnumDescriptorProto::kOptionsFieldNumber,
13111 kCustomOptionFieldNumber, 2};
13112 int unint[] = {FileDescriptorProto::kEnumTypeFieldNumber, 0,
13113 EnumDescriptorProto::kOptionsFieldNumber,
13114 OneofOptions::kUninterpretedOptionFieldNumber, 2};
13115 std::vector<int> vpath(path, path + 5);
13116 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13117 EXPECT_EQ("20:3-20:31", PrintSourceLocation(loc));
13118
13119 std::vector<int> vunint(unint, unint + 5);
13120 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13121 }
13122
13123 // Enum value options
13124 {
13125 // option w/ message type that directly sets field
13126 int path[] = {FileDescriptorProto::kEnumTypeFieldNumber,
13127 0,
13128 EnumDescriptorProto::kValueFieldNumber,
13129 0,
13130 EnumValueDescriptorProto::kOptionsFieldNumber,
13131 kCustomOptionFieldNumber,
13132 kAFieldNumber};
13133 int unint[] = {FileDescriptorProto::kEnumTypeFieldNumber,
13134 0,
13135 EnumDescriptorProto::kValueFieldNumber,
13136 0,
13137 EnumValueDescriptorProto::kOptionsFieldNumber,
13138 EnumValueOptions::kUninterpretedOptionFieldNumber,
13139 0};
13140 std::vector<int> vpath(path, path + 7);
13141 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13142 EXPECT_EQ("21:14-21:40", PrintSourceLocation(loc));
13143
13144 std::vector<int> vunint(unint, unint + 7);
13145 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13146 }
13147 {
13148 int path[] = {FileDescriptorProto::kEnumTypeFieldNumber,
13149 0,
13150 EnumDescriptorProto::kValueFieldNumber,
13151 1,
13152 EnumValueDescriptorProto::kOptionsFieldNumber,
13153 kCustomOptionFieldNumber};
13154 int unint[] = {FileDescriptorProto::kEnumTypeFieldNumber,
13155 0,
13156 EnumDescriptorProto::kValueFieldNumber,
13157 1,
13158 EnumValueDescriptorProto::kOptionsFieldNumber,
13159 EnumValueOptions::kUninterpretedOptionFieldNumber,
13160 0};
13161 std::vector<int> vpath(path, path + 6);
13162 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13163 EXPECT_EQ("22:14-22:42", PrintSourceLocation(loc));
13164
13165 std::vector<int> vunint(unint, unint + 7);
13166 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13167 }
13168
13169 // Service option, repeated options
13170 {
13171 int path[] = {FileDescriptorProto::kServiceFieldNumber, 0,
13172 ServiceDescriptorProto::kOptionsFieldNumber,
13173 kCustomOptionFieldNumber, 0};
13174 int unint[] = {FileDescriptorProto::kServiceFieldNumber, 0,
13175 ServiceDescriptorProto::kOptionsFieldNumber,
13176 ServiceOptions::kUninterpretedOptionFieldNumber, 0};
13177 std::vector<int> vpath(path, path + 5);
13178 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13179 EXPECT_EQ("26:3-26:35", PrintSourceLocation(loc));
13180
13181 std::vector<int> vunint(unint, unint + 5);
13182 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13183 }
13184 {
13185 int path[] = {FileDescriptorProto::kServiceFieldNumber, 0,
13186 ServiceDescriptorProto::kOptionsFieldNumber,
13187 kCustomOptionFieldNumber, 1};
13188 int unint[] = {FileDescriptorProto::kServiceFieldNumber, 0,
13189 ServiceDescriptorProto::kOptionsFieldNumber,
13190 ServiceOptions::kUninterpretedOptionFieldNumber, 1};
13191 std::vector<int> vpath(path, path + 5);
13192 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13193 EXPECT_EQ("27:3-27:35", PrintSourceLocation(loc));
13194
13195 std::vector<int> vunint(unint, unint + 5);
13196 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13197 }
13198 {
13199 int path[] = {FileDescriptorProto::kServiceFieldNumber, 0,
13200 ServiceDescriptorProto::kOptionsFieldNumber,
13201 kCustomOptionFieldNumber, 2};
13202 int unint[] = {FileDescriptorProto::kServiceFieldNumber, 0,
13203 ServiceDescriptorProto::kOptionsFieldNumber,
13204 ServiceOptions::kUninterpretedOptionFieldNumber, 2};
13205 std::vector<int> vpath(path, path + 5);
13206 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13207 EXPECT_EQ("28:3-28:35", PrintSourceLocation(loc));
13208
13209 std::vector<int> vunint(unint, unint + 5);
13210 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13211 }
13212
13213 // Method options
13214 {
13215 int path[] = {FileDescriptorProto::kServiceFieldNumber,
13216 0,
13217 ServiceDescriptorProto::kMethodFieldNumber,
13218 1,
13219 MethodDescriptorProto::kOptionsFieldNumber,
13220 MethodOptions::kDeprecatedFieldNumber};
13221 int unint[] = {FileDescriptorProto::kServiceFieldNumber,
13222 0,
13223 ServiceDescriptorProto::kMethodFieldNumber,
13224 1,
13225 MethodDescriptorProto::kOptionsFieldNumber,
13226 MethodOptions::kUninterpretedOptionFieldNumber,
13227 0};
13228 std::vector<int> vpath(path, path + 6);
13229 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13230 EXPECT_EQ("32:5-32:30", PrintSourceLocation(loc));
13231
13232 std::vector<int> vunint(unint, unint + 7);
13233 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13234 }
13235 {
13236 int path[] = {
13237 FileDescriptorProto::kServiceFieldNumber, 0,
13238 ServiceDescriptorProto::kMethodFieldNumber, 1,
13239 MethodDescriptorProto::kOptionsFieldNumber, kCustomOptionFieldNumber};
13240 int unint[] = {FileDescriptorProto::kServiceFieldNumber,
13241 0,
13242 ServiceDescriptorProto::kMethodFieldNumber,
13243 1,
13244 MethodDescriptorProto::kOptionsFieldNumber,
13245 MethodOptions::kUninterpretedOptionFieldNumber,
13246 1};
13247 std::vector<int> vpath(path, path + 6);
13248 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13249 EXPECT_EQ("33:5-33:41", PrintSourceLocation(loc));
13250
13251 std::vector<int> vunint(unint, unint + 7);
13252 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13253 }
13254
13255 // Extension range options
13256 {
13257 int path[] = {FileDescriptorProto::kMessageTypeFieldNumber, 1,
13258 DescriptorProto::kExtensionRangeFieldNumber, 0,
13259 DescriptorProto_ExtensionRange::kOptionsFieldNumber};
13260 std::vector<int> vpath(path, path + 5);
13261 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13262 EXPECT_EQ("37:40-37:67", PrintSourceLocation(loc));
13263 }
13264 {
13265 int path[] = {FileDescriptorProto::kMessageTypeFieldNumber,
13266 1,
13267 DescriptorProto::kExtensionRangeFieldNumber,
13268 0,
13269 DescriptorProto_ExtensionRange::kOptionsFieldNumber,
13270 kCustomOptionFieldNumber};
13271 int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber,
13272 1,
13273 DescriptorProto::kExtensionRangeFieldNumber,
13274 0,
13275 DescriptorProto_ExtensionRange::kOptionsFieldNumber,
13276 ExtensionRangeOptions::kUninterpretedOptionFieldNumber,
13277 0};
13278 std::vector<int> vpath(path, path + 6);
13279 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13280 EXPECT_EQ("37:41-37:66", PrintSourceLocation(loc));
13281
13282 std::vector<int> vunint(unint, unint + 7);
13283 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13284 }
13285 {
13286 int path[] = {FileDescriptorProto::kMessageTypeFieldNumber,
13287 1,
13288 DescriptorProto::kExtensionRangeFieldNumber,
13289 1,
13290 DescriptorProto_ExtensionRange::kOptionsFieldNumber,
13291 kCustomOptionFieldNumber};
13292 int unint[] = {FileDescriptorProto::kMessageTypeFieldNumber,
13293 1,
13294 DescriptorProto::kExtensionRangeFieldNumber,
13295 1,
13296 DescriptorProto_ExtensionRange::kOptionsFieldNumber,
13297 ExtensionRangeOptions::kUninterpretedOptionFieldNumber,
13298 0};
13299 std::vector<int> vpath(path, path + 6);
13300 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13301 EXPECT_EQ("37:41-37:66", PrintSourceLocation(loc));
13302
13303 std::vector<int> vunint(unint, unint + 7);
13304 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13305 }
13306
13307 // Field option on extension
13308 {
13309 int path[] = {FileDescriptorProto::kExtensionFieldNumber, 0,
13310 FieldDescriptorProto::kOptionsFieldNumber,
13311 FieldOptions::kPackedFieldNumber};
13312 int unint[] = {FileDescriptorProto::kExtensionFieldNumber, 0,
13313 FieldDescriptorProto::kOptionsFieldNumber,
13314 FieldOptions::kUninterpretedOptionFieldNumber, 0};
13315 std::vector<int> vpath(path, path + 4);
13316 EXPECT_TRUE(file_desc->GetSourceLocation(vpath, &loc));
13317 EXPECT_EQ("40:42-40:53", PrintSourceLocation(loc));
13318
13319 std::vector<int> vunint(unint, unint + 5);
13320 EXPECT_FALSE(file_desc->GetSourceLocation(vunint, &loc));
13321 }
13322 }
13323
13324 // Missing SourceCodeInfo doesn't cause crash:
TEST_F(SourceLocationTest,GetSourceLocation_MissingSourceCodeInfo)13325 TEST_F(SourceLocationTest, GetSourceLocation_MissingSourceCodeInfo) {
13326 SourceLocation loc;
13327
13328 const FileDescriptor* file_desc =
13329 ABSL_DIE_IF_NULL(pool_.FindFileByName("/test/test.proto"));
13330
13331 FileDescriptorProto proto;
13332 file_desc->CopyTo(&proto); // Note, this discards the SourceCodeInfo.
13333 EXPECT_FALSE(proto.has_source_code_info());
13334
13335 DescriptorPool bad1_pool(&pool_);
13336 const FileDescriptor* bad1_file_desc =
13337 ABSL_DIE_IF_NULL(bad1_pool.BuildFile(proto));
13338 const Descriptor* bad1_a_desc = bad1_file_desc->FindMessageTypeByName("A");
13339 EXPECT_FALSE(bad1_a_desc->GetSourceLocation(&loc));
13340 }
13341
13342 // Corrupt SourceCodeInfo doesn't cause crash:
TEST_F(SourceLocationTest,GetSourceLocation_BogusSourceCodeInfo)13343 TEST_F(SourceLocationTest, GetSourceLocation_BogusSourceCodeInfo) {
13344 SourceLocation loc;
13345
13346 const FileDescriptor* file_desc =
13347 ABSL_DIE_IF_NULL(pool_.FindFileByName("/test/test.proto"));
13348
13349 FileDescriptorProto proto;
13350 file_desc->CopyTo(&proto); // Note, this discards the SourceCodeInfo.
13351 EXPECT_FALSE(proto.has_source_code_info());
13352 SourceCodeInfo_Location* loc_msg =
13353 proto.mutable_source_code_info()->add_location();
13354 loc_msg->add_path(1);
13355 loc_msg->add_path(2);
13356 loc_msg->add_path(3);
13357 loc_msg->add_span(4);
13358 loc_msg->add_span(5);
13359 loc_msg->add_span(6);
13360
13361 DescriptorPool bad2_pool(&pool_);
13362 const FileDescriptor* bad2_file_desc =
13363 ABSL_DIE_IF_NULL(bad2_pool.BuildFile(proto));
13364 const Descriptor* bad2_a_desc = bad2_file_desc->FindMessageTypeByName("A");
13365 EXPECT_FALSE(bad2_a_desc->GetSourceLocation(&loc));
13366 }
13367
13368 // ===================================================================
13369
13370 const char* const kCopySourceCodeInfoToTestInput =
13371 "syntax = \"proto2\";\n"
13372 "message Foo {}\n";
13373
13374 // Required since source code information is not preserved by
13375 // FileDescriptorTest.
13376 class CopySourceCodeInfoToTest : public testing::Test {
13377 public:
CopySourceCodeInfoToTest()13378 CopySourceCodeInfoToTest()
13379 : source_tree_("/test/test.proto", kCopySourceCodeInfoToTestInput),
13380 db_(&source_tree_),
13381 pool_(&db_, &collector_) {}
13382
13383 private:
13384 AbortingErrorCollector collector_;
13385 SingletonSourceTree source_tree_;
13386 compiler::SourceTreeDescriptorDatabase db_;
13387
13388 protected:
13389 DescriptorPool pool_;
13390 };
13391
TEST_F(CopySourceCodeInfoToTest,CopyTo_DoesNotCopySourceCodeInfo)13392 TEST_F(CopySourceCodeInfoToTest, CopyTo_DoesNotCopySourceCodeInfo) {
13393 const FileDescriptor* file_desc =
13394 ABSL_DIE_IF_NULL(pool_.FindFileByName("/test/test.proto"));
13395 FileDescriptorProto file_desc_proto;
13396 ASSERT_FALSE(file_desc_proto.has_source_code_info());
13397
13398 file_desc->CopyTo(&file_desc_proto);
13399 EXPECT_FALSE(file_desc_proto.has_source_code_info());
13400 }
13401
TEST_F(CopySourceCodeInfoToTest,CopySourceCodeInfoTo)13402 TEST_F(CopySourceCodeInfoToTest, CopySourceCodeInfoTo) {
13403 const FileDescriptor* file_desc =
13404 ABSL_DIE_IF_NULL(pool_.FindFileByName("/test/test.proto"));
13405 FileDescriptorProto file_desc_proto;
13406 ASSERT_FALSE(file_desc_proto.has_source_code_info());
13407
13408 file_desc->CopySourceCodeInfoTo(&file_desc_proto);
13409 const SourceCodeInfo& info = file_desc_proto.source_code_info();
13410 ASSERT_EQ(4, info.location_size());
13411 // Get the Foo message location
13412 const SourceCodeInfo_Location& foo_location = info.location(2);
13413 ASSERT_EQ(2, foo_location.path_size());
13414 EXPECT_EQ(FileDescriptorProto::kMessageTypeFieldNumber, foo_location.path(0));
13415 EXPECT_EQ(0, foo_location.path(1)); // Foo is the first message defined
13416 ASSERT_EQ(3, foo_location.span_size()); // Foo spans one line
13417 EXPECT_EQ(1, foo_location.span(0)); // Foo is declared on line 1
13418 EXPECT_EQ(0, foo_location.span(1)); // Foo starts at column 0
13419 EXPECT_EQ(14, foo_location.span(2)); // Foo ends on column 14
13420 }
13421
13422 // ===================================================================
13423
13424 class LazilyBuildDependenciesTest : public testing::Test {
13425 public:
LazilyBuildDependenciesTest()13426 LazilyBuildDependenciesTest() : pool_(&db_, nullptr) {
13427 pool_.InternalSetLazilyBuildDependencies();
13428 }
13429
ParseProtoAndAddToDb(absl::string_view proto)13430 void ParseProtoAndAddToDb(absl::string_view proto) {
13431 FileDescriptorProto tmp;
13432 ASSERT_TRUE(TextFormat::ParseFromString(proto, &tmp));
13433 db_.Add(tmp);
13434 }
13435
AddSimpleMessageProtoFileToDb(absl::string_view file_name,absl::string_view message_name)13436 void AddSimpleMessageProtoFileToDb(absl::string_view file_name,
13437 absl::string_view message_name) {
13438 ParseProtoAndAddToDb(absl::StrFormat(
13439 R"pb(
13440 name: '%s.proto'
13441 package: "protobuf_unittest"
13442 message_type {
13443 name: '%s'
13444 field { name: 'a' number: 1 label: LABEL_OPTIONAL type: TYPE_INT32 }
13445 })pb",
13446 file_name, message_name));
13447 }
13448
AddSimpleEnumProtoFileToDb(absl::string_view file_name,absl::string_view enum_name,absl::string_view enum_value_name)13449 void AddSimpleEnumProtoFileToDb(absl::string_view file_name,
13450 absl::string_view enum_name,
13451 absl::string_view enum_value_name) {
13452 ParseProtoAndAddToDb(absl::StrCat("name: '", file_name,
13453 ".proto' "
13454 "package: 'protobuf_unittest' "
13455 "enum_type { "
13456 " name:'",
13457 enum_name,
13458 "' "
13459 " value { name:'",
13460 enum_value_name,
13461 "' number:1 } "
13462 "}"));
13463 }
13464
13465 protected:
13466 SimpleDescriptorDatabase db_;
13467 DescriptorPool pool_;
13468 };
13469
TEST_F(LazilyBuildDependenciesTest,Message)13470 TEST_F(LazilyBuildDependenciesTest, Message) {
13471 ParseProtoAndAddToDb(R"pb(
13472 name: 'foo.proto'
13473 package: 'protobuf_unittest'
13474 dependency: 'bar.proto'
13475 message_type {
13476 name: 'Foo'
13477 field {
13478 name: 'bar'
13479 number: 1
13480 label: LABEL_OPTIONAL
13481 type: TYPE_MESSAGE
13482 type_name: '.protobuf_unittest.Bar'
13483 }
13484 })pb");
13485 AddSimpleMessageProtoFileToDb("bar", "Bar");
13486
13487 // Verify neither has been built yet.
13488 EXPECT_FALSE(pool_.InternalIsFileLoaded("foo.proto"));
13489 EXPECT_FALSE(pool_.InternalIsFileLoaded("bar.proto"));
13490
13491 const FileDescriptor* file = pool_.FindFileByName("foo.proto");
13492
13493 // Verify only foo gets built when asking for foo.proto
13494 EXPECT_TRUE(file != nullptr);
13495 EXPECT_TRUE(pool_.InternalIsFileLoaded("foo.proto"));
13496 EXPECT_FALSE(pool_.InternalIsFileLoaded("bar.proto"));
13497
13498 // Verify calling FindFieldBy* works when the type of the field was
13499 // not built at cross link time. Verify this doesn't build the file
13500 // the field's type is defined in, as well.
13501 const Descriptor* desc = file->FindMessageTypeByName("Foo");
13502 const FieldDescriptor* field = desc->FindFieldByName("bar");
13503 EXPECT_TRUE(field != nullptr);
13504 EXPECT_EQ(field, desc->FindFieldByNumber(1));
13505 EXPECT_EQ(field, desc->FindFieldByLowercaseName("bar"));
13506 EXPECT_EQ(field, desc->FindFieldByCamelcaseName("bar"));
13507 EXPECT_FALSE(pool_.InternalIsFileLoaded("bar.proto"));
13508
13509 // Finally, verify that if we call message_type() on the field, we will
13510 // build the file where the message is defined, and get a valid descriptor
13511 EXPECT_TRUE(field->message_type() != nullptr);
13512 EXPECT_TRUE(pool_.InternalIsFileLoaded("bar.proto"));
13513 }
13514
TEST_F(LazilyBuildDependenciesTest,Enum)13515 TEST_F(LazilyBuildDependenciesTest, Enum) {
13516 ParseProtoAndAddToDb(
13517 R"pb(
13518 name: 'foo.proto'
13519 package: 'protobuf_unittest'
13520 dependency: 'enum1.proto'
13521 dependency: 'enum2.proto'
13522 message_type {
13523 name: 'Lazy'
13524 field {
13525 name: 'enum1'
13526 number: 1
13527 label: LABEL_OPTIONAL
13528 type: TYPE_ENUM
13529 type_name: '.protobuf_unittest.Enum1'
13530 }
13531 field {
13532 name: 'enum2'
13533 number: 1
13534 label: LABEL_OPTIONAL
13535 type: TYPE_ENUM
13536 type_name: '.protobuf_unittest.Enum2'
13537 }
13538 })pb");
13539 AddSimpleEnumProtoFileToDb("enum1", "Enum1", "ENUM1");
13540 AddSimpleEnumProtoFileToDb("enum2", "Enum2", "ENUM2");
13541
13542 const FileDescriptor* file = pool_.FindFileByName("foo.proto");
13543
13544 // Verify calling enum_type() on a field whose definition is not
13545 // yet built will build the file and return a descriptor.
13546 EXPECT_FALSE(pool_.InternalIsFileLoaded("enum1.proto"));
13547 const Descriptor* desc = file->FindMessageTypeByName("Lazy");
13548 EXPECT_TRUE(desc != nullptr);
13549 const FieldDescriptor* field = desc->FindFieldByName("enum1");
13550 EXPECT_TRUE(field != nullptr);
13551 EXPECT_TRUE(field->enum_type() != nullptr);
13552 EXPECT_TRUE(pool_.InternalIsFileLoaded("enum1.proto"));
13553
13554 // Verify calling default_value_enum() on a field whose definition is not
13555 // yet built will build the file and return a descriptor to the value.
13556 EXPECT_FALSE(pool_.InternalIsFileLoaded("enum2.proto"));
13557 field = desc->FindFieldByName("enum2");
13558 EXPECT_TRUE(field != nullptr);
13559 EXPECT_TRUE(field->default_value_enum() != nullptr);
13560 EXPECT_TRUE(pool_.InternalIsFileLoaded("enum2.proto"));
13561 }
13562
TEST_F(LazilyBuildDependenciesTest,Type)13563 TEST_F(LazilyBuildDependenciesTest, Type) {
13564 ParseProtoAndAddToDb(
13565 R"pb(
13566 name: 'foo.proto'
13567 package: 'protobuf_unittest'
13568 dependency: 'message1.proto'
13569 dependency: 'message2.proto'
13570 dependency: 'enum1.proto'
13571 dependency: 'enum2.proto'
13572 message_type {
13573 name: 'Lazy'
13574 field {
13575 name: 'message1'
13576 number: 1
13577 label: LABEL_OPTIONAL
13578 type: TYPE_MESSAGE
13579 type_name: '.protobuf_unittest.Message1'
13580 }
13581 field {
13582 name: 'message2'
13583 number: 1
13584 label: LABEL_OPTIONAL
13585 type: TYPE_MESSAGE
13586 type_name: '.protobuf_unittest.Message2'
13587 }
13588 field {
13589 name: 'enum1'
13590 number: 1
13591 label: LABEL_OPTIONAL
13592 type: TYPE_ENUM
13593 type_name: '.protobuf_unittest.Enum1'
13594 }
13595 field {
13596 name: 'enum2'
13597 number: 1
13598 label: LABEL_OPTIONAL
13599 type: TYPE_ENUM
13600 type_name: '.protobuf_unittest.Enum2'
13601 }
13602 })pb");
13603 AddSimpleMessageProtoFileToDb("message1", "Message1");
13604 AddSimpleMessageProtoFileToDb("message2", "Message2");
13605 AddSimpleEnumProtoFileToDb("enum1", "Enum1", "ENUM1");
13606 AddSimpleEnumProtoFileToDb("enum2", "Enum2", "ENUM2");
13607
13608 const FileDescriptor* file = pool_.FindFileByName("foo.proto");
13609
13610 // Verify calling type() on a field that is a message type will _not_
13611 // build the type defined in another file.
13612 EXPECT_FALSE(pool_.InternalIsFileLoaded("message1.proto"));
13613 const Descriptor* desc = file->FindMessageTypeByName("Lazy");
13614 EXPECT_TRUE(desc != nullptr);
13615 const FieldDescriptor* field = desc->FindFieldByName("message1");
13616 EXPECT_TRUE(field != nullptr);
13617 EXPECT_EQ(field->type(), FieldDescriptor::TYPE_MESSAGE);
13618 EXPECT_FALSE(pool_.InternalIsFileLoaded("message1.proto"));
13619
13620 // Verify calling cpp_type() on a field that is a message type will _not_
13621 // build the type defined in another file.
13622 EXPECT_FALSE(pool_.InternalIsFileLoaded("message2.proto"));
13623 field = desc->FindFieldByName("message2");
13624 EXPECT_TRUE(field != nullptr);
13625 EXPECT_EQ(field->cpp_type(), FieldDescriptor::CPPTYPE_MESSAGE);
13626 EXPECT_FALSE(pool_.InternalIsFileLoaded("message2.proto"));
13627
13628 // Verify calling type() on a field that is an enum type will _not_
13629 // build the type defined in another file.
13630 EXPECT_FALSE(pool_.InternalIsFileLoaded("enum1.proto"));
13631 field = desc->FindFieldByName("enum1");
13632 EXPECT_TRUE(field != nullptr);
13633 EXPECT_EQ(field->type(), FieldDescriptor::TYPE_ENUM);
13634 EXPECT_FALSE(pool_.InternalIsFileLoaded("enum1.proto"));
13635
13636 // Verify calling cpp_type() on a field that is an enum type will _not_
13637 // build the type defined in another file.
13638 EXPECT_FALSE(pool_.InternalIsFileLoaded("enum2.proto"));
13639 field = desc->FindFieldByName("enum2");
13640 EXPECT_TRUE(field != nullptr);
13641 EXPECT_EQ(field->cpp_type(), FieldDescriptor::CPPTYPE_ENUM);
13642 EXPECT_FALSE(pool_.InternalIsFileLoaded("enum2.proto"));
13643 }
13644
TEST_F(LazilyBuildDependenciesTest,Extension)13645 TEST_F(LazilyBuildDependenciesTest, Extension) {
13646 ParseProtoAndAddToDb(
13647 R"pb(
13648 name: 'foo.proto'
13649 package: 'protobuf_unittest'
13650 dependency: 'bar.proto'
13651 dependency: 'baz.proto'
13652 extension {
13653 extendee: '.protobuf_unittest.Bar'
13654 name: 'bar'
13655 number: 11
13656 label: LABEL_OPTIONAL
13657 type: TYPE_MESSAGE
13658 type_name: '.protobuf_unittest.Baz'
13659 }
13660 )pb");
13661 ParseProtoAndAddToDb(
13662 R"pb(
13663 name: 'bar.proto'
13664 package: 'protobuf_unittest'
13665 message_type {
13666 name: 'Bar'
13667 extension_range { start: 10 end: 20 }
13668 }
13669 )pb");
13670 AddSimpleMessageProtoFileToDb("baz", "Baz");
13671
13672 // Verify none have been built yet.
13673 EXPECT_FALSE(pool_.InternalIsFileLoaded("foo.proto"));
13674 EXPECT_FALSE(pool_.InternalIsFileLoaded("bar.proto"));
13675 EXPECT_FALSE(pool_.InternalIsFileLoaded("baz.proto"));
13676
13677 const FileDescriptor* file = pool_.FindFileByName("foo.proto");
13678
13679 // Verify foo.bar gets loaded, and bar.proto gets loaded
13680 // to register the extension. baz.proto should not get loaded.
13681 EXPECT_TRUE(file != nullptr);
13682 EXPECT_TRUE(pool_.InternalIsFileLoaded("foo.proto"));
13683 EXPECT_TRUE(pool_.InternalIsFileLoaded("bar.proto"));
13684 EXPECT_FALSE(pool_.InternalIsFileLoaded("baz.proto"));
13685 }
13686
TEST_F(LazilyBuildDependenciesTest,Service)13687 TEST_F(LazilyBuildDependenciesTest, Service) {
13688 ParseProtoAndAddToDb(R"pb(
13689 name: 'foo.proto'
13690 package: 'protobuf_unittest'
13691 dependency: 'message1.proto'
13692 dependency: 'message2.proto'
13693 dependency: 'message3.proto'
13694 dependency: 'message4.proto'
13695 service {
13696 name: 'LazyService'
13697 method {
13698 name: 'A'
13699 input_type: '.protobuf_unittest.Message1'
13700 output_type: '.protobuf_unittest.Message2'
13701 }
13702 })pb");
13703 AddSimpleMessageProtoFileToDb("message1", "Message1");
13704 AddSimpleMessageProtoFileToDb("message2", "Message2");
13705 AddSimpleMessageProtoFileToDb("message3", "Message3");
13706 AddSimpleMessageProtoFileToDb("message4", "Message4");
13707
13708 const FileDescriptor* file = pool_.FindFileByName("foo.proto");
13709
13710 // Verify calling FindServiceByName or FindMethodByName doesn't build the
13711 // files defining the input and output type, and input_type() and
13712 // output_type() does indeed build the appropriate files.
13713 const ServiceDescriptor* service = file->FindServiceByName("LazyService");
13714 EXPECT_TRUE(service != nullptr);
13715 const MethodDescriptor* method = service->FindMethodByName("A");
13716 EXPECT_FALSE(pool_.InternalIsFileLoaded("message1.proto"));
13717 EXPECT_FALSE(pool_.InternalIsFileLoaded("message2.proto"));
13718 EXPECT_TRUE(method != nullptr);
13719 EXPECT_TRUE(method->input_type() != nullptr);
13720 EXPECT_TRUE(pool_.InternalIsFileLoaded("message1.proto"));
13721 EXPECT_FALSE(pool_.InternalIsFileLoaded("message2.proto"));
13722 EXPECT_TRUE(method->output_type() != nullptr);
13723 EXPECT_TRUE(pool_.InternalIsFileLoaded("message2.proto"));
13724 }
13725
13726
TEST_F(LazilyBuildDependenciesTest,GeneratedFile)13727 TEST_F(LazilyBuildDependenciesTest, GeneratedFile) {
13728 // Most testing is done with custom pools with lazy dependencies forced on,
13729 // do some sanity checking that lazy imports is on by default for the
13730 // generated pool, and do custom options testing with generated to
13731 // be able to use the GetExtension ids for the custom options.
13732
13733 // Verify none of the files are loaded yet.
13734 EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded(
13735 "google/protobuf/unittest_lazy_dependencies.proto"));
13736 EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded(
13737 "google/protobuf/unittest_lazy_dependencies_custom_option.proto"));
13738 EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded(
13739 "google/protobuf/unittest_lazy_dependencies_enum.proto"));
13740
13741 // Verify calling autogenerated function to get a descriptor in the base
13742 // file will build that file but none of its imports. This verifies that
13743 // lazily_build_dependencies_ is set on the generated pool, and also that
13744 // the generated function "descriptor()" doesn't somehow subvert the laziness
13745 // by manually loading the dependencies or something.
13746 EXPECT_TRUE(protobuf_unittest::lazy_imports::ImportedMessage::descriptor() !=
13747 nullptr);
13748 EXPECT_TRUE(DescriptorPool::generated_pool()->InternalIsFileLoaded(
13749 "google/protobuf/unittest_lazy_dependencies.proto"));
13750 EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded(
13751 "google/protobuf/unittest_lazy_dependencies_custom_option.proto"));
13752 EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded(
13753 "google/protobuf/unittest_lazy_dependencies_enum.proto"));
13754
13755 // Verify custom options work when defined in an import that isn't loaded,
13756 // and that a non-default value of a custom option doesn't load the file
13757 // where that enum is defined.
13758 const MessageOptions& options =
13759 protobuf_unittest::lazy_imports::MessageCustomOption::descriptor()
13760 ->options();
13761 protobuf_unittest::lazy_imports::LazyEnum custom_option_value =
13762 options.GetExtension(protobuf_unittest::lazy_imports::lazy_enum_option);
13763
13764 EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded(
13765 "google/protobuf/unittest_lazy_dependencies_custom_option.proto"));
13766 EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded(
13767 "google/protobuf/unittest_lazy_dependencies_enum.proto"));
13768 EXPECT_EQ(custom_option_value, protobuf_unittest::lazy_imports::LAZY_ENUM_1);
13769
13770 const MessageOptions& options2 =
13771 protobuf_unittest::lazy_imports::MessageCustomOption2::descriptor()
13772 ->options();
13773 custom_option_value =
13774 options2.GetExtension(protobuf_unittest::lazy_imports::lazy_enum_option);
13775
13776 EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded(
13777 "google/protobuf/unittest_lazy_dependencies_custom_option.proto"));
13778 EXPECT_FALSE(DescriptorPool::generated_pool()->InternalIsFileLoaded(
13779 "google/protobuf/unittest_lazy_dependencies_enum.proto"));
13780 EXPECT_EQ(custom_option_value, protobuf_unittest::lazy_imports::LAZY_ENUM_0);
13781 }
13782
TEST_F(LazilyBuildDependenciesTest,Dependency)13783 TEST_F(LazilyBuildDependenciesTest, Dependency) {
13784 ParseProtoAndAddToDb(
13785 R"pb(
13786 name: 'foo.proto'
13787 package: 'protobuf_unittest'
13788 dependency: 'bar.proto'
13789 message_type {
13790 name: 'Foo'
13791 field {
13792 name: 'bar'
13793 number: 1
13794 label: LABEL_OPTIONAL
13795 type: TYPE_MESSAGE
13796 type_name: '.protobuf_unittest.Bar'
13797 }
13798 }
13799 )pb");
13800 ParseProtoAndAddToDb(
13801 R"pb(
13802 name: 'bar.proto'
13803 package: 'protobuf_unittest'
13804 dependency: 'baz.proto'
13805 message_type {
13806 name: 'Bar'
13807 field {
13808 name: 'baz'
13809 number: 1
13810 label: LABEL_OPTIONAL
13811 type: TYPE_MESSAGE
13812 type_name: '.protobuf_unittest.Baz'
13813 }
13814 }
13815 )pb");
13816 AddSimpleMessageProtoFileToDb("baz", "Baz");
13817
13818 const FileDescriptor* foo_file = pool_.FindFileByName("foo.proto");
13819 EXPECT_TRUE(foo_file != nullptr);
13820 // As expected, requesting foo.proto shouldn't build its dependencies
13821 EXPECT_TRUE(pool_.InternalIsFileLoaded("foo.proto"));
13822 EXPECT_FALSE(pool_.InternalIsFileLoaded("bar.proto"));
13823 EXPECT_FALSE(pool_.InternalIsFileLoaded("baz.proto"));
13824
13825 // Verify calling dependency(N) will build the dependency, but
13826 // not that file's dependencies.
13827 const FileDescriptor* bar_file = foo_file->dependency(0);
13828 EXPECT_TRUE(bar_file != nullptr);
13829 EXPECT_TRUE(pool_.InternalIsFileLoaded("bar.proto"));
13830 EXPECT_FALSE(pool_.InternalIsFileLoaded("baz.proto"));
13831 }
13832
13833 // ===================================================================
13834
13835
13836 } // namespace descriptor_unittest
13837 } // namespace protobuf
13838 } // namespace google
13839
13840 #include "google/protobuf/port_undef.inc"
13841