• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 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 #include "google/protobuf/feature_resolver.h"
9 
10 #include <utility>
11 #include <vector>
12 
13 #include <gmock/gmock.h>
14 #include <gtest/gtest.h>
15 #include "absl/log/absl_check.h"
16 #include "absl/log/absl_log.h"
17 #include "absl/log/die_if_null.h"
18 #include "absl/memory/memory.h"
19 #include "absl/status/status.h"
20 #include "absl/status/statusor.h"
21 #include "absl/strings/string_view.h"
22 #include "absl/strings/substitute.h"
23 #include "google/protobuf/compiler/parser.h"
24 #include "google/protobuf/cpp_features.pb.h"
25 #include "google/protobuf/descriptor.h"
26 #include "google/protobuf/descriptor.pb.h"
27 #include "google/protobuf/io/tokenizer.h"
28 #include "google/protobuf/io/zero_copy_stream_impl_lite.h"
29 #include "google/protobuf/test_textproto.h"
30 #include "google/protobuf/text_format.h"
31 #include "google/protobuf/unittest_custom_options.pb.h"
32 #include "google/protobuf/unittest_features.pb.h"
33 #include "google/protobuf/stubs/status_macros.h"
34 
35 // Must be included last.
36 #include "google/protobuf/port_def.inc"
37 
38 namespace google {
39 namespace protobuf {
40 namespace {
41 
42 using ::testing::AllOf;
43 using ::testing::ElementsAre;
44 using ::testing::ExplainMatchResult;
45 using ::testing::HasSubstr;
46 using ::testing::IsEmpty;
47 using ::testing::UnorderedElementsAre;
48 
49 // TODO: Use the gtest versions once that's available in OSS.
50 template <typename T>
GetStatus(const absl::StatusOr<T> & s)51 absl::Status GetStatus(const absl::StatusOr<T>& s) {
52   return s.status();
53 }
54 
55 MATCHER_P(HasError, msg_matcher, "") {
56   return GetStatus(arg).code() == absl::StatusCode::kFailedPrecondition &&
57          ExplainMatchResult(msg_matcher, GetStatus(arg).message(),
58                             result_listener);
59 }
60 
61 MATCHER_P(StatusIs, status,
62           absl::StrCat(".status() is ", testing::PrintToString(status))) {
63   return GetStatus(arg).code() == status;
64 }
65 #define EXPECT_OK(x) EXPECT_THAT(x, StatusIs(absl::StatusCode::kOk))
66 #define ASSERT_OK(x) ASSERT_THAT(x, StatusIs(absl::StatusCode::kOk))
67 
68 template <typename ExtensionT>
GetExtension(const ExtensionT & ext,const Descriptor * descriptor=FeatureSet::descriptor ())69 const FieldDescriptor* GetExtension(
70     const ExtensionT& ext,
71     const Descriptor* descriptor = FeatureSet::descriptor()) {
72   return ABSL_DIE_IF_NULL(descriptor->file()->pool()->FindExtensionByNumber(
73       descriptor, ext.number()));
74 }
75 
76 template <typename... Extensions>
SetupFeatureResolver(Edition edition,Extensions...extensions)77 absl::StatusOr<FeatureResolver> SetupFeatureResolver(Edition edition,
78                                                      Extensions... extensions) {
79   absl::StatusOr<FeatureSetDefaults> defaults =
80       FeatureResolver::CompileDefaults(FeatureSet::descriptor(),
81                                        {GetExtension(extensions)...},
82                                        EDITION_2023, EDITION_99997_TEST_ONLY);
83   RETURN_IF_ERROR(defaults.status());
84   return FeatureResolver::Create(edition, *defaults);
85 }
86 
GetDefaults(Edition edition,const FeatureSetDefaults & defaults)87 absl::StatusOr<FeatureSet> GetDefaults(Edition edition,
88                                        const FeatureSetDefaults& defaults) {
89   absl::StatusOr<FeatureResolver> resolver =
90       FeatureResolver::Create(edition, defaults);
91   RETURN_IF_ERROR(resolver.status());
92   FeatureSet parent, child;
93   return resolver->MergeFeatures(parent, child);
94 }
95 
96 template <typename... Extensions>
GetDefaults(Edition edition,Extensions...extensions)97 absl::StatusOr<FeatureSet> GetDefaults(Edition edition,
98                                        Extensions... extensions) {
99   absl::StatusOr<FeatureSetDefaults> defaults =
100       FeatureResolver::CompileDefaults(FeatureSet::descriptor(),
101                                        {GetExtension(extensions)...},
102                                        EDITION_2023, EDITION_99999_TEST_ONLY);
103   RETURN_IF_ERROR(defaults.status());
104   return GetDefaults(edition, *defaults);
105 }
106 
GetProto(const FileDescriptor * file)107 FileDescriptorProto GetProto(const FileDescriptor* file) {
108   FileDescriptorProto proto;
109   file->CopyTo(&proto);
110   return proto;
111 }
112 
TEST(FeatureResolverTest,DefaultsCore2023)113 TEST(FeatureResolverTest, DefaultsCore2023) {
114   absl::StatusOr<FeatureSet> merged = GetDefaults(EDITION_2023);
115   ASSERT_OK(merged);
116 
117   EXPECT_EQ(merged->field_presence(), FeatureSet::EXPLICIT);
118   EXPECT_EQ(merged->enum_type(), FeatureSet::OPEN);
119   EXPECT_EQ(merged->repeated_field_encoding(), FeatureSet::PACKED);
120   EXPECT_EQ(merged->message_encoding(), FeatureSet::LENGTH_PREFIXED);
121   EXPECT_FALSE(merged->HasExtension(pb::test));
122 }
123 
TEST(FeatureResolverTest,DefaultsTest2023)124 TEST(FeatureResolverTest, DefaultsTest2023) {
125   absl::StatusOr<FeatureSet> merged = GetDefaults(EDITION_2023, pb::test);
126   ASSERT_OK(merged);
127 
128   EXPECT_EQ(merged->field_presence(), FeatureSet::EXPLICIT);
129   EXPECT_EQ(merged->enum_type(), FeatureSet::OPEN);
130   EXPECT_EQ(merged->repeated_field_encoding(), FeatureSet::PACKED);
131   EXPECT_EQ(merged->message_encoding(), FeatureSet::LENGTH_PREFIXED);
132 
133   const pb::TestFeatures& ext = merged->GetExtension(pb::test);
134   EXPECT_EQ(ext.file_feature(), pb::VALUE3);
135   EXPECT_EQ(ext.extension_range_feature(), pb::VALUE1);
136   EXPECT_EQ(ext.message_feature(), pb::VALUE1);
137   EXPECT_EQ(ext.field_feature(), pb::VALUE1);
138   EXPECT_EQ(ext.oneof_feature(), pb::VALUE1);
139   EXPECT_EQ(ext.enum_feature(), pb::VALUE1);
140   EXPECT_EQ(ext.enum_entry_feature(), pb::VALUE1);
141   EXPECT_EQ(ext.service_feature(), pb::VALUE1);
142   EXPECT_EQ(ext.method_feature(), pb::VALUE1);
143   EXPECT_FALSE(ext.bool_field_feature());
144 }
145 
TEST(FeatureResolverTest,DefaultsTestMessageExtension)146 TEST(FeatureResolverTest, DefaultsTestMessageExtension) {
147   absl::StatusOr<FeatureSet> merged =
148       GetDefaults(EDITION_2023, pb::TestMessage::test_message);
149   ASSERT_OK(merged);
150 
151   EXPECT_EQ(merged->field_presence(), FeatureSet::EXPLICIT);
152   EXPECT_EQ(merged->enum_type(), FeatureSet::OPEN);
153   EXPECT_EQ(merged->repeated_field_encoding(), FeatureSet::PACKED);
154   EXPECT_EQ(merged->message_encoding(), FeatureSet::LENGTH_PREFIXED);
155   EXPECT_FALSE(merged->HasExtension(pb::test));
156 
157   const pb::TestFeatures& ext =
158       merged->GetExtension(pb::TestMessage::test_message);
159   EXPECT_EQ(ext.file_feature(), pb::VALUE3);
160   EXPECT_EQ(ext.extension_range_feature(), pb::VALUE1);
161   EXPECT_EQ(ext.message_feature(), pb::VALUE1);
162   EXPECT_EQ(ext.field_feature(), pb::VALUE1);
163   EXPECT_EQ(ext.oneof_feature(), pb::VALUE1);
164   EXPECT_EQ(ext.enum_feature(), pb::VALUE1);
165   EXPECT_EQ(ext.enum_entry_feature(), pb::VALUE1);
166   EXPECT_EQ(ext.service_feature(), pb::VALUE1);
167   EXPECT_EQ(ext.method_feature(), pb::VALUE1);
168   EXPECT_FALSE(ext.bool_field_feature());
169 }
170 
TEST(FeatureResolverTest,DefaultsTestNestedExtension)171 TEST(FeatureResolverTest, DefaultsTestNestedExtension) {
172   absl::StatusOr<FeatureSet> merged =
173       GetDefaults(EDITION_2023, pb::TestMessage::Nested::test_nested);
174   ASSERT_OK(merged);
175 
176   EXPECT_EQ(merged->field_presence(), FeatureSet::EXPLICIT);
177   EXPECT_EQ(merged->enum_type(), FeatureSet::OPEN);
178   EXPECT_EQ(merged->repeated_field_encoding(), FeatureSet::PACKED);
179   EXPECT_EQ(merged->message_encoding(), FeatureSet::LENGTH_PREFIXED);
180   EXPECT_FALSE(merged->HasExtension(pb::test));
181 
182   const pb::TestFeatures& ext =
183       merged->GetExtension(pb::TestMessage::Nested::test_nested);
184   EXPECT_EQ(ext.file_feature(), pb::VALUE3);
185   EXPECT_EQ(ext.extension_range_feature(), pb::VALUE1);
186   EXPECT_EQ(ext.message_feature(), pb::VALUE1);
187   EXPECT_EQ(ext.field_feature(), pb::VALUE1);
188   EXPECT_EQ(ext.oneof_feature(), pb::VALUE1);
189   EXPECT_EQ(ext.enum_feature(), pb::VALUE1);
190   EXPECT_EQ(ext.enum_entry_feature(), pb::VALUE1);
191   EXPECT_EQ(ext.service_feature(), pb::VALUE1);
192   EXPECT_EQ(ext.method_feature(), pb::VALUE1);
193   EXPECT_FALSE(ext.bool_field_feature());
194 }
195 
TEST(FeatureResolverTest,DefaultsGeneratedPoolCustom)196 TEST(FeatureResolverTest, DefaultsGeneratedPoolCustom) {
197   DescriptorPool pool;
198   ASSERT_NE(
199       pool.BuildFile(GetProto(google::protobuf::DescriptorProto::descriptor()->file())),
200       nullptr);
201   ASSERT_NE(pool.BuildFile(GetProto(pb::TestFeatures::descriptor()->file())),
202             nullptr);
203   absl::StatusOr<FeatureSetDefaults> defaults =
204       FeatureResolver::CompileDefaults(
205           pool.FindMessageTypeByName("google.protobuf.FeatureSet"),
206           {pool.FindExtensionByName("pb.test")}, EDITION_2023, EDITION_2023);
207   ASSERT_OK(defaults);
208   ASSERT_EQ(defaults->defaults().size(), 3);
209   ASSERT_EQ(defaults->defaults().at(2).edition(), EDITION_2023);
210   FeatureSet merged = defaults->defaults().at(2).overridable_features();
211 
212   EXPECT_EQ(merged.field_presence(), FeatureSet::EXPLICIT);
213   EXPECT_TRUE(merged.HasExtension(pb::test));
214   EXPECT_EQ(merged.GetExtension(pb::test).file_feature(), pb::VALUE3);
215   EXPECT_FALSE(merged.HasExtension(pb::cpp));
216 }
217 
TEST(FeatureResolverTest,DefaultsMergedFeatures)218 TEST(FeatureResolverTest, DefaultsMergedFeatures) {
219   absl::StatusOr<FeatureSetDefaults> defaults =
220       FeatureResolver::CompileDefaults(FeatureSet::descriptor(),
221                                        {GetExtension(pb::test)}, EDITION_2023,
222                                        EDITION_2023);
223   ASSERT_OK(defaults);
224   ASSERT_EQ(defaults->defaults_size(), 3);
225 
226   defaults->mutable_defaults(2)
227       ->mutable_fixed_features()
228       ->MutableExtension(pb::test)
229       ->set_file_feature(pb::VALUE7);
230   defaults->mutable_defaults(2)
231       ->mutable_fixed_features()
232       ->MutableExtension(pb::test)
233       ->set_multiple_feature(pb::VALUE6);
234   defaults->mutable_defaults(2)
235       ->mutable_overridable_features()
236       ->MutableExtension(pb::test)
237       ->clear_file_feature();
238   defaults->mutable_defaults(2)
239       ->mutable_overridable_features()
240       ->MutableExtension(pb::test)
241       ->set_multiple_feature(pb::VALUE8);
242 
243   absl::StatusOr<FeatureSet> features = GetDefaults(EDITION_2023, *defaults);
244   ASSERT_OK(features);
245 
246   const pb::TestFeatures& ext = features->GetExtension(pb::test);
247   EXPECT_EQ(ext.file_feature(), pb::VALUE7);
248   EXPECT_EQ(ext.multiple_feature(), pb::VALUE8);
249 }
250 
TEST(FeatureResolverTest,DefaultsTooEarly)251 TEST(FeatureResolverTest, DefaultsTooEarly) {
252   absl::StatusOr<FeatureSetDefaults> defaults =
253       FeatureResolver::CompileDefaults(FeatureSet::descriptor(),
254                                        {GetExtension(pb::test)}, EDITION_2023,
255                                        EDITION_2023);
256   ASSERT_OK(defaults);
257   defaults->set_minimum_edition(EDITION_1_TEST_ONLY);
258   absl::StatusOr<FeatureSet> merged =
259       GetDefaults(EDITION_1_TEST_ONLY, *defaults);
260   EXPECT_THAT(merged, HasError(AllOf(HasSubstr("No valid default found"),
261                                      HasSubstr("1_TEST_ONLY"))));
262 }
263 
TEST(FeatureResolverTest,DefaultsFarFuture)264 TEST(FeatureResolverTest, DefaultsFarFuture) {
265   absl::StatusOr<FeatureSet> merged =
266       GetDefaults(EDITION_99999_TEST_ONLY, pb::test);
267   ASSERT_OK(merged);
268 
269   pb::TestFeatures ext = merged->GetExtension(pb::test);
270   EXPECT_EQ(ext.file_feature(), pb::VALUE5);
271   EXPECT_TRUE(ext.bool_field_feature());
272 }
273 
TEST(FeatureResolverTest,DefaultsMiddleEdition)274 TEST(FeatureResolverTest, DefaultsMiddleEdition) {
275   absl::StatusOr<FeatureSet> merged =
276       GetDefaults(EDITION_99997_TEST_ONLY, pb::test);
277   ASSERT_OK(merged);
278 
279   pb::TestFeatures ext = merged->GetExtension(pb::test);
280   EXPECT_EQ(ext.file_feature(), pb::VALUE4);
281   EXPECT_TRUE(ext.bool_field_feature());
282 }
283 
TEST(FeatureResolverTest,CompileDefaultsFixedFutureFeature)284 TEST(FeatureResolverTest, CompileDefaultsFixedFutureFeature) {
285   absl::StatusOr<FeatureSetDefaults> defaults =
286       FeatureResolver::CompileDefaults(FeatureSet::descriptor(),
287                                        {GetExtension(pb::test)}, EDITION_PROTO2,
288                                        EDITION_2023);
289   ASSERT_OK(defaults);
290   ASSERT_EQ(defaults->defaults_size(), 3);
291 
292   const auto& edition_defaults = defaults->defaults(2);
293   ASSERT_EQ(edition_defaults.edition(), EDITION_2023);
294 
295   EXPECT_TRUE(edition_defaults.fixed_features()
296                   .GetExtension(pb::test)
297                   .has_future_feature());
298   EXPECT_EQ(
299       edition_defaults.fixed_features().GetExtension(pb::test).future_feature(),
300       pb::VALUE1);
301   EXPECT_FALSE(edition_defaults.overridable_features()
302                    .GetExtension(pb::test)
303                    .has_future_feature());
304 }
305 
TEST(FeatureResolverTest,CompileDefaultsFixedRemovedFeature)306 TEST(FeatureResolverTest, CompileDefaultsFixedRemovedFeature) {
307   absl::StatusOr<FeatureSetDefaults> defaults =
308       FeatureResolver::CompileDefaults(FeatureSet::descriptor(),
309                                        {GetExtension(pb::test)}, EDITION_PROTO2,
310                                        EDITION_2024);
311   ASSERT_OK(defaults);
312   ASSERT_EQ(defaults->defaults_size(), 4);
313 
314   const auto& edition_defaults = defaults->defaults(3);
315   ASSERT_EQ(edition_defaults.edition(), EDITION_2024);
316 
317   EXPECT_TRUE(edition_defaults.fixed_features()
318                   .GetExtension(pb::test)
319                   .has_removed_feature());
320   EXPECT_EQ(edition_defaults.fixed_features()
321                 .GetExtension(pb::test)
322                 .removed_feature(),
323             pb::VALUE3);
324   EXPECT_FALSE(edition_defaults.overridable_features()
325                    .GetExtension(pb::test)
326                    .has_removed_feature());
327 }
328 
TEST(FeatureResolverTest,CompileDefaultsOverridable)329 TEST(FeatureResolverTest, CompileDefaultsOverridable) {
330   absl::StatusOr<FeatureSetDefaults> defaults =
331       FeatureResolver::CompileDefaults(FeatureSet::descriptor(),
332                                        {GetExtension(pb::test)}, EDITION_PROTO2,
333                                        EDITION_2023);
334   ASSERT_OK(defaults);
335   ASSERT_EQ(defaults->defaults_size(), 3);
336 
337   const auto& edition_defaults = defaults->defaults(2);
338   ASSERT_EQ(edition_defaults.edition(), EDITION_2023);
339 
340   EXPECT_FALSE(edition_defaults.fixed_features()
341                    .GetExtension(pb::test)
342                    .has_removed_feature());
343   EXPECT_TRUE(edition_defaults.overridable_features()
344                   .GetExtension(pb::test)
345                   .has_removed_feature());
346   EXPECT_EQ(edition_defaults.overridable_features()
347                 .GetExtension(pb::test)
348                 .removed_feature(),
349             pb::VALUE2);
350 }
351 
TEST(FeatureResolverTest,CreateFromUnsortedDefaults)352 TEST(FeatureResolverTest, CreateFromUnsortedDefaults) {
353   auto valid_defaults = FeatureResolver::CompileDefaults(
354       FeatureSet::descriptor(), {}, EDITION_LEGACY, EDITION_2023);
355   ASSERT_OK(valid_defaults);
356   FeatureSetDefaults defaults = *valid_defaults;
357 
358   defaults.mutable_defaults()->SwapElements(0, 1);
359 
360   EXPECT_THAT(FeatureResolver::Create(EDITION_2023, defaults),
361               HasError(AllOf(HasSubstr("not strictly increasing."),
362                              HasSubstr("Edition PROTO3 is greater "
363                                        "than or equal to edition LEGACY"))));
364 }
365 
TEST(FeatureResolverTest,CreateUnknownEdition)366 TEST(FeatureResolverTest, CreateUnknownEdition) {
367   FeatureSetDefaults defaults = ParseTextOrDie(R"pb(
368     minimum_edition: EDITION_UNKNOWN
369     maximum_edition: EDITION_99999_TEST_ONLY
370     defaults { edition: EDITION_UNKNOWN }
371   )pb");
372   EXPECT_THAT(FeatureResolver::Create(EDITION_2023, defaults),
373               HasError(HasSubstr("Invalid edition UNKNOWN")));
374 }
375 
TEST(FeatureResolverTest,CreateMissingEdition)376 TEST(FeatureResolverTest, CreateMissingEdition) {
377   FeatureSetDefaults defaults = ParseTextOrDie(R"pb(
378     minimum_edition: EDITION_UNKNOWN
379     maximum_edition: EDITION_99999_TEST_ONLY
380     defaults {}
381   )pb");
382   EXPECT_THAT(FeatureResolver::Create(EDITION_2023, defaults),
383               HasError(HasSubstr("Invalid edition UNKNOWN")));
384 }
385 
TEST(FeatureResolverTest,CreateUnknownEnumFeature)386 TEST(FeatureResolverTest, CreateUnknownEnumFeature) {
387   auto valid_defaults = FeatureResolver::CompileDefaults(
388       FeatureSet::descriptor(), {}, EDITION_2023, EDITION_2023);
389   ASSERT_OK(valid_defaults);
390 
391   // Use reflection to make sure we validate every enum feature in FeatureSet.
392   const Descriptor& descriptor = *FeatureSet::descriptor();
393   for (int i = 0; i < descriptor.field_count(); ++i) {
394     const FieldDescriptor& field = *descriptor.field(i);
395 
396     // Clear the feature, which should be invalid.
397     FeatureSetDefaults defaults = *valid_defaults;
398     FeatureSet* features =
399         defaults.mutable_defaults()->Mutable(0)->mutable_overridable_features();
400     features->GetReflection()->ClearField(features, &field);
401     features =
402         defaults.mutable_defaults()->Mutable(0)->mutable_fixed_features();
403     features->GetReflection()->ClearField(features, &field);
404 
405     EXPECT_THAT(FeatureResolver::Create(EDITION_2023, defaults),
406                 HasError(AllOf(HasSubstr(field.name()),
407                                HasSubstr("must resolve to a known value"))));
408   }
409 }
410 
TEST(FeatureResolverTest,CompileDefaultsMissingDescriptor)411 TEST(FeatureResolverTest, CompileDefaultsMissingDescriptor) {
412   EXPECT_THAT(
413       FeatureResolver::CompileDefaults(nullptr, {}, EDITION_2023, EDITION_2023),
414       HasError(HasSubstr("find definition of google.protobuf.FeatureSet")));
415 }
416 
TEST(FeatureResolverTest,CompileDefaultsMissingExtension)417 TEST(FeatureResolverTest, CompileDefaultsMissingExtension) {
418   EXPECT_THAT(
419       FeatureResolver::CompileDefaults(FeatureSet::descriptor(), {nullptr},
420                                        EDITION_2023, EDITION_2023),
421       HasError(HasSubstr("Unknown extension")));
422 }
423 
TEST(FeatureResolverTest,CompileDefaultsInvalidExtension)424 TEST(FeatureResolverTest, CompileDefaultsInvalidExtension) {
425   EXPECT_THAT(
426       FeatureResolver::CompileDefaults(
427           FeatureSet::descriptor(),
428           {GetExtension(protobuf_unittest::file_opt1, FileOptions::descriptor())},
429           EDITION_2023, EDITION_2023),
430       HasError(HasSubstr("is not an extension of")));
431 }
432 
TEST(FeatureResolverTest,CompileDefaultsMinimumLaterThanMaximum)433 TEST(FeatureResolverTest, CompileDefaultsMinimumLaterThanMaximum) {
434   EXPECT_THAT(
435       FeatureResolver::CompileDefaults(FeatureSet::descriptor(), {},
436                                        EDITION_99999_TEST_ONLY, EDITION_2023),
437       HasError(AllOf(HasSubstr("Invalid edition range"),
438                      HasSubstr("99999_TEST_ONLY is newer"),
439                      HasSubstr("2023"))));
440 }
441 
TEST(FeatureResolverTest,MergeFeaturesChildOverrideCore)442 TEST(FeatureResolverTest, MergeFeaturesChildOverrideCore) {
443   absl::StatusOr<FeatureResolver> resolver = SetupFeatureResolver(EDITION_2023);
444   ASSERT_OK(resolver);
445   FeatureSet child = ParseTextOrDie(R"pb(
446     field_presence: IMPLICIT
447     repeated_field_encoding: EXPANDED
448   )pb");
449   absl::StatusOr<FeatureSet> merged =
450       resolver->MergeFeatures(FeatureSet(), child);
451   ASSERT_OK(merged);
452 
453   EXPECT_EQ(merged->field_presence(), FeatureSet::IMPLICIT);
454   EXPECT_EQ(merged->enum_type(), FeatureSet::OPEN);
455   EXPECT_EQ(merged->repeated_field_encoding(), FeatureSet::EXPANDED);
456   EXPECT_EQ(merged->message_encoding(), FeatureSet::LENGTH_PREFIXED);
457 }
458 
TEST(FeatureResolverTest,MergeFeaturesChildOverrideComplex)459 TEST(FeatureResolverTest, MergeFeaturesChildOverrideComplex) {
460   absl::StatusOr<FeatureResolver> resolver =
461       SetupFeatureResolver(EDITION_2023, pb::test);
462   ASSERT_OK(resolver);
463   FeatureSet child = ParseTextOrDie(R"pb(
464     field_presence: IMPLICIT
465     repeated_field_encoding: EXPANDED
466     [pb.test] { field_feature: VALUE5 }
467   )pb");
468   absl::StatusOr<FeatureSet> merged =
469       resolver->MergeFeatures(FeatureSet(), child);
470   ASSERT_OK(merged);
471 
472   EXPECT_EQ(merged->field_presence(), FeatureSet::IMPLICIT);
473   EXPECT_EQ(merged->enum_type(), FeatureSet::OPEN);
474   EXPECT_EQ(merged->repeated_field_encoding(), FeatureSet::EXPANDED);
475   EXPECT_EQ(merged->message_encoding(), FeatureSet::LENGTH_PREFIXED);
476 
477   pb::TestFeatures ext = merged->GetExtension(pb::test);
478   EXPECT_EQ(ext.file_feature(), pb::VALUE3);
479   EXPECT_EQ(ext.field_feature(), pb::VALUE5);
480 }
481 
TEST(FeatureResolverTest,MergeFeaturesParentOverrides)482 TEST(FeatureResolverTest, MergeFeaturesParentOverrides) {
483   absl::StatusOr<FeatureResolver> resolver =
484       SetupFeatureResolver(EDITION_2023, pb::test);
485   ASSERT_OK(resolver);
486   FeatureSet parent = ParseTextOrDie(R"pb(
487     field_presence: IMPLICIT
488     repeated_field_encoding: EXPANDED
489     [pb.test] { message_feature: VALUE2 field_feature: VALUE5 }
490   )pb");
491   FeatureSet child = ParseTextOrDie(R"pb(
492     repeated_field_encoding: PACKED
493     [pb.test] { field_feature: VALUE7 }
494   )pb");
495   absl::StatusOr<FeatureSet> merged = resolver->MergeFeatures(parent, child);
496   ASSERT_OK(merged);
497 
498   EXPECT_EQ(merged->field_presence(), FeatureSet::IMPLICIT);
499   EXPECT_EQ(merged->enum_type(), FeatureSet::OPEN);
500   EXPECT_EQ(merged->repeated_field_encoding(), FeatureSet::PACKED);
501   EXPECT_EQ(merged->message_encoding(), FeatureSet::LENGTH_PREFIXED);
502 
503   pb::TestFeatures ext = merged->GetExtension(pb::test);
504   EXPECT_EQ(ext.file_feature(), pb::VALUE3);
505   EXPECT_EQ(ext.extension_range_feature(), pb::VALUE1);
506   EXPECT_EQ(ext.message_feature(), pb::VALUE2);
507   EXPECT_EQ(ext.field_feature(), pb::VALUE7);
508   EXPECT_EQ(ext.oneof_feature(), pb::VALUE1);
509   EXPECT_EQ(ext.enum_feature(), pb::VALUE1);
510   EXPECT_EQ(ext.enum_entry_feature(), pb::VALUE1);
511   EXPECT_EQ(ext.service_feature(), pb::VALUE1);
512   EXPECT_EQ(ext.method_feature(), pb::VALUE1);
513   EXPECT_FALSE(ext.bool_field_feature());
514 }
515 
TEST(FeatureResolverTest,MergeFeaturesUnknownEnumFeature)516 TEST(FeatureResolverTest, MergeFeaturesUnknownEnumFeature) {
517   absl::StatusOr<FeatureResolver> resolver = SetupFeatureResolver(EDITION_2023);
518   ASSERT_OK(resolver);
519 
520   // Use reflection to make sure we validate every enum feature in FeatureSet.
521   const Descriptor& descriptor = *FeatureSet::descriptor();
522   for (int i = 0; i < descriptor.field_count(); ++i) {
523     const FieldDescriptor& field = *descriptor.field(i);
524 
525     FeatureSet features;
526     const Reflection& reflection = *features.GetReflection();
527 
528     // Set the feature to a value of 0, which is unknown by convention.
529     reflection.SetEnumValue(&features, &field, 0);
530     EXPECT_THAT(
531         resolver->MergeFeatures(FeatureSet(), features),
532         HasError(AllOf(
533             HasSubstr(field.name()), HasSubstr("must resolve to a known value"),
534             HasSubstr(field.enum_type()->FindValueByNumber(0)->name()))));
535   }
536 }
537 
TEST(FeatureResolverTest,MergeFeaturesExtensionEnumUnknown)538 TEST(FeatureResolverTest, MergeFeaturesExtensionEnumUnknown) {
539   absl::StatusOr<FeatureResolver> resolver =
540       SetupFeatureResolver(EDITION_2023, pb::test);
541   ASSERT_OK(resolver);
542   FeatureSet child = ParseTextOrDie(R"pb(
543     [pb.test] { field_feature: TEST_ENUM_FEATURE_UNKNOWN }
544   )pb");
545   absl::StatusOr<FeatureSet> merged =
546       resolver->MergeFeatures(FeatureSet(), child);
547   ASSERT_OK(merged);
548   EXPECT_EQ(merged->GetExtension(pb::test).field_feature(),
549             pb::TEST_ENUM_FEATURE_UNKNOWN);
550 }
551 
TEST(FeatureResolverTest,MergeFeaturesDistantPast)552 TEST(FeatureResolverTest, MergeFeaturesDistantPast) {
553   EXPECT_THAT(SetupFeatureResolver(EDITION_1_TEST_ONLY),
554               HasError(AllOf(HasSubstr("Edition 1_TEST_ONLY"),
555                              HasSubstr("minimum supported edition 2023"))));
556 }
557 
TEST(FeatureResolverTest,MergeFeaturesDistantFuture)558 TEST(FeatureResolverTest, MergeFeaturesDistantFuture) {
559   EXPECT_THAT(
560       SetupFeatureResolver(EDITION_99998_TEST_ONLY),
561       HasError(AllOf(HasSubstr("Edition 99998_TEST_ONLY"),
562                      HasSubstr("maximum supported edition 99997_TEST_ONLY"))));
563 }
564 
TEST(FeatureResolverLifetimesTest,Valid)565 TEST(FeatureResolverLifetimesTest, Valid) {
566   FeatureSet features = ParseTextOrDie(R"pb(
567     [pb.test] { file_feature: VALUE1 }
568   )pb");
569   auto results = FeatureResolver::ValidateFeatureLifetimes(EDITION_2023,
570                                                            features, nullptr);
571   EXPECT_THAT(results.errors, IsEmpty());
572   EXPECT_THAT(results.warnings, IsEmpty());
573 }
574 
TEST(FeatureResolverLifetimesTest,DeprecatedFeature)575 TEST(FeatureResolverLifetimesTest, DeprecatedFeature) {
576   FeatureSet features = ParseTextOrDie(R"pb(
577     [pb.test] { removed_feature: VALUE1 }
578   )pb");
579   auto results = FeatureResolver::ValidateFeatureLifetimes(EDITION_2023,
580                                                            features, nullptr);
581   EXPECT_THAT(results.errors, IsEmpty());
582   EXPECT_THAT(
583       results.warnings,
584       ElementsAre(AllOf(HasSubstr("pb.TestFeatures.removed_feature"),
585                         HasSubstr("deprecated in edition 2023"),
586                         HasSubstr("Custom feature deprecation warning"))));
587 }
588 
TEST(FeatureResolverLifetimesTest,RemovedFeature)589 TEST(FeatureResolverLifetimesTest, RemovedFeature) {
590   FeatureSet features = ParseTextOrDie(R"pb(
591     [pb.test] { removed_feature: VALUE1 }
592   )pb");
593   auto results = FeatureResolver::ValidateFeatureLifetimes(EDITION_2024,
594                                                            features, nullptr);
595   EXPECT_THAT(results.errors,
596               ElementsAre(AllOf(HasSubstr("pb.TestFeatures.removed_feature"),
597                                 HasSubstr("removed in edition 2024"))));
598   EXPECT_THAT(results.warnings, IsEmpty());
599 }
600 
TEST(FeatureResolverLifetimesTest,NotIntroduced)601 TEST(FeatureResolverLifetimesTest, NotIntroduced) {
602   FeatureSet features = ParseTextOrDie(R"pb(
603     [pb.test] { future_feature: VALUE1 }
604   )pb");
605   auto results = FeatureResolver::ValidateFeatureLifetimes(EDITION_2023,
606                                                            features, nullptr);
607   EXPECT_THAT(results.errors,
608               ElementsAre(AllOf(HasSubstr("pb.TestFeatures.future_feature"),
609                                 HasSubstr("introduced until edition 2024"))));
610   EXPECT_THAT(results.warnings, IsEmpty());
611 }
612 
TEST(FeatureResolverLifetimesTest,WarningsAndErrors)613 TEST(FeatureResolverLifetimesTest, WarningsAndErrors) {
614   FeatureSet features = ParseTextOrDie(R"pb(
615     [pb.test] { future_feature: VALUE1 removed_feature: VALUE1 }
616   )pb");
617   auto results = FeatureResolver::ValidateFeatureLifetimes(EDITION_2023,
618                                                            features, nullptr);
619   EXPECT_THAT(results.errors,
620               ElementsAre(HasSubstr("pb.TestFeatures.future_feature")));
621   EXPECT_THAT(results.warnings,
622               ElementsAre(HasSubstr("pb.TestFeatures.removed_feature")));
623 }
624 
TEST(FeatureResolverLifetimesTest,MultipleErrors)625 TEST(FeatureResolverLifetimesTest, MultipleErrors) {
626   FeatureSet features = ParseTextOrDie(R"pb(
627     [pb.test] { future_feature: VALUE1 legacy_feature: VALUE1 }
628   )pb");
629   auto results = FeatureResolver::ValidateFeatureLifetimes(EDITION_2023,
630                                                            features, nullptr);
631   EXPECT_THAT(results.errors, UnorderedElementsAre(
632                                   HasSubstr("pb.TestFeatures.future_feature"),
633                                   HasSubstr("pb.TestFeatures.legacy_feature")));
634   EXPECT_THAT(results.warnings, IsEmpty());
635 }
636 
TEST(FeatureResolverLifetimesTest,DynamicPool)637 TEST(FeatureResolverLifetimesTest, DynamicPool) {
638   DescriptorPool pool;
639   {
640     FileDescriptorProto file;
641     FileDescriptorProto::GetDescriptor()->file()->CopyTo(&file);
642     ASSERT_NE(pool.BuildFile(file), nullptr);
643   }
644   {
645     FileDescriptorProto file;
646     pb::TestFeatures::GetDescriptor()->file()->CopyTo(&file);
647     ASSERT_NE(pool.BuildFile(file), nullptr);
648   }
649   const Descriptor* feature_set =
650       pool.FindMessageTypeByName("google.protobuf.FeatureSet");
651   ASSERT_NE(feature_set, nullptr);
652 
653   FeatureSet features = ParseTextOrDie(R"pb(
654     [pb.test] { future_feature: VALUE1 removed_feature: VALUE1 }
655   )pb");
656   auto results = FeatureResolver::ValidateFeatureLifetimes(
657       EDITION_2023, features, feature_set);
658   EXPECT_THAT(results.errors,
659               ElementsAre(HasSubstr("pb.TestFeatures.future_feature")));
660   EXPECT_THAT(results.warnings,
661               ElementsAre(HasSubstr("pb.TestFeatures.removed_feature")));
662 }
663 
TEST(FeatureResolverLifetimesTest,EmptyValueSupportValid)664 TEST(FeatureResolverLifetimesTest, EmptyValueSupportValid) {
665   FeatureSet features = ParseTextOrDie(R"pb(
666     [pb.test] { value_lifetime_feature: VALUE_LIFETIME_EMPTY_SUPPORT }
667   )pb");
668   auto results = FeatureResolver::ValidateFeatureLifetimes(EDITION_2023,
669                                                            features, nullptr);
670   EXPECT_THAT(results.errors, IsEmpty());
671   EXPECT_THAT(results.warnings, IsEmpty());
672 }
673 
TEST(FeatureResolverLifetimesTest,ValueSupportValid)674 TEST(FeatureResolverLifetimesTest, ValueSupportValid) {
675   FeatureSet features = ParseTextOrDie(R"pb(
676     [pb.test] { value_lifetime_feature: VALUE_LIFETIME_SUPPORT }
677   )pb");
678   auto results = FeatureResolver::ValidateFeatureLifetimes(
679       EDITION_99997_TEST_ONLY, features, nullptr);
680   EXPECT_THAT(results.errors, IsEmpty());
681   EXPECT_THAT(results.warnings, IsEmpty());
682 }
683 
TEST(FeatureResolverLifetimesTest,ValueSupportBeforeIntroduced)684 TEST(FeatureResolverLifetimesTest, ValueSupportBeforeIntroduced) {
685   FeatureSet features = ParseTextOrDie(R"pb(
686     [pb.test] { value_lifetime_feature: VALUE_LIFETIME_FUTURE }
687   )pb");
688   auto results = FeatureResolver::ValidateFeatureLifetimes(EDITION_2023,
689                                                            features, nullptr);
690   EXPECT_THAT(results.errors,
691               ElementsAre(AllOf(
692                   HasSubstr("pb.VALUE_LIFETIME_FUTURE"),
693                   HasSubstr("introduced until edition 99997_TEST_ONLY"))));
694   EXPECT_THAT(results.warnings, IsEmpty());
695 }
696 
TEST(FeatureResolverLifetimesTest,ValueSupportAfterRemoved)697 TEST(FeatureResolverLifetimesTest, ValueSupportAfterRemoved) {
698   FeatureSet features = ParseTextOrDie(R"pb(
699     [pb.test] { value_lifetime_feature: VALUE_LIFETIME_REMOVED }
700   )pb");
701   auto results = FeatureResolver::ValidateFeatureLifetimes(
702       EDITION_99997_TEST_ONLY, features, nullptr);
703   EXPECT_THAT(
704       results.errors,
705       ElementsAre(AllOf(HasSubstr("pb.VALUE_LIFETIME_REMOVED"),
706                         HasSubstr("removed in edition 99997_TEST_ONLY"))));
707   EXPECT_THAT(results.warnings, IsEmpty());
708 }
709 
TEST(FeatureResolverLifetimesTest,ValueSupportDeprecated)710 TEST(FeatureResolverLifetimesTest, ValueSupportDeprecated) {
711   FeatureSet features = ParseTextOrDie(R"pb(
712     [pb.test] { value_lifetime_feature: VALUE_LIFETIME_DEPRECATED }
713   )pb");
714   auto results = FeatureResolver::ValidateFeatureLifetimes(
715       EDITION_99997_TEST_ONLY, features, nullptr);
716   EXPECT_THAT(results.errors, IsEmpty());
717   EXPECT_THAT(
718       results.warnings,
719       ElementsAre(AllOf(HasSubstr("pb.VALUE_LIFETIME_DEPRECATED"),
720                         HasSubstr("deprecated in edition 99997_TEST_ONLY"),
721                         HasSubstr("Custom feature deprecation warning"))));
722 }
723 
TEST(FeatureResolverLifetimesTest,ValueAndFeatureSupportDeprecated)724 TEST(FeatureResolverLifetimesTest, ValueAndFeatureSupportDeprecated) {
725   FeatureSet features = ParseTextOrDie(R"pb(
726     [pb.test] { value_lifetime_feature: VALUE_LIFETIME_DEPRECATED }
727   )pb");
728   auto results = FeatureResolver::ValidateFeatureLifetimes(
729       EDITION_99998_TEST_ONLY, features, nullptr);
730   EXPECT_THAT(results.errors, IsEmpty());
731   EXPECT_THAT(results.warnings,
732               UnorderedElementsAre(
733                   AllOf(HasSubstr("pb.VALUE_LIFETIME_DEPRECATED"),
734                         HasSubstr("deprecated in edition 99997_TEST_ONLY"),
735                         HasSubstr("Custom feature deprecation warning")),
736                   AllOf(HasSubstr("pb.TestFeatures.value_lifetime_feature"),
737                         HasSubstr("deprecated in edition 99998_TEST_ONLY"),
738                         HasSubstr("Custom feature deprecation warning"))));
739 }
740 
TEST(FeatureResolverLifetimesTest,ValueSupportInvalidNumber)741 TEST(FeatureResolverLifetimesTest, ValueSupportInvalidNumber) {
742   FeatureSet features;
743   features.MutableExtension(pb::test)->set_value_lifetime_feature(
744       static_cast<pb::ValueLifetimeFeature>(1234));
745   auto results = FeatureResolver::ValidateFeatureLifetimes(EDITION_2023,
746                                                            features, nullptr);
747   EXPECT_THAT(
748       results.errors,
749       ElementsAre(AllOf(HasSubstr("pb.TestFeatures.value_lifetime_feature"),
750                         HasSubstr("1234"))));
751   EXPECT_THAT(results.warnings, IsEmpty());
752 }
753 
754 class FakeErrorCollector : public io::ErrorCollector {
755  public:
756   FakeErrorCollector() = default;
757   ~FakeErrorCollector() override = default;
RecordWarning(int line,int column,absl::string_view message)758   void RecordWarning(int line, int column, absl::string_view message) override {
759     ABSL_LOG(WARNING) << line << ":" << column << ": " << message;
760   }
RecordError(int line,int column,absl::string_view message)761   void RecordError(int line, int column, absl::string_view message) override {
762     ABSL_LOG(ERROR) << line << ":" << column << ": " << message;
763   }
764 };
765 
766 class FeatureResolverPoolTest : public testing::Test {
767  protected:
SetUp()768   void SetUp() override {
769     FileDescriptorProto file;
770     FileDescriptorProto::GetDescriptor()->file()->CopyTo(&file);
771     ASSERT_NE(pool_.BuildFile(file), nullptr);
772     feature_set_ = pool_.FindMessageTypeByName("google.protobuf.FeatureSet");
773     ASSERT_NE(feature_set_, nullptr);
774     auto defaults = FeatureResolver::CompileDefaults(
775         feature_set_, {}, EDITION_2023, EDITION_2023);
776     ASSERT_OK(defaults);
777     defaults_ = std::move(defaults).value();
778   }
779 
ParseSchema(absl::string_view schema)780   const FileDescriptor* ParseSchema(absl::string_view schema) {
781     FakeErrorCollector error_collector;
782     io::ArrayInputStream raw_input(schema.data(), schema.size());
783     io::Tokenizer input(&raw_input, &error_collector);
784     compiler::Parser parser;
785     parser.RecordErrorsTo(&error_collector);
786 
787     FileDescriptorProto file;
788 
789     ABSL_CHECK(parser.Parse(&input, &file));
790     file.set_name("foo.proto");
791     return pool_.BuildFile(file);
792   }
793 
794   DescriptorPool pool_;
795   FileDescriptorProto file_proto_;
796   const Descriptor* feature_set_;
797   FeatureSetDefaults defaults_;
798 };
799 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidNonMessage)800 TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidNonMessage) {
801   const FileDescriptor* file = ParseSchema(R"schema(
802     syntax = "proto2";
803     package test;
804     import "google/protobuf/descriptor.proto";
805 
806     message Foo {}
807     extend google.protobuf.FeatureSet {
808       optional string bar = 9999;
809     }
810   )schema");
811   ASSERT_NE(file, nullptr);
812 
813   const FieldDescriptor* ext = file->extension(0);
814   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
815                                                EDITION_2023, EDITION_2023),
816               HasError(AllOf(HasSubstr("test.bar"),
817                              HasSubstr("is not of message type"))));
818 }
819 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidRepeated)820 TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidRepeated) {
821   const FileDescriptor* file = ParseSchema(R"schema(
822     syntax = "proto2";
823     package test;
824     import "google/protobuf/descriptor.proto";
825 
826     message Foo {}
827     extend google.protobuf.FeatureSet {
828       repeated Foo bar = 9999;
829     }
830   )schema");
831   ASSERT_NE(file, nullptr);
832 
833   const FieldDescriptor* ext = file->extension(0);
834   EXPECT_THAT(
835       FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023,
836                                        EDITION_2023),
837       HasError(AllOf(HasSubstr("test.bar"), HasSubstr("repeated extension"))));
838 }
839 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidWithExtensions)840 TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithExtensions) {
841   const FileDescriptor* file = ParseSchema(R"schema(
842     syntax = "proto2";
843     package test;
844     import "google/protobuf/descriptor.proto";
845 
846     message Foo {
847       extensions 1;
848     }
849     extend google.protobuf.FeatureSet {
850       optional Foo bar = 9999;
851     }
852     extend Foo {
853       optional Foo bar2 = 1 [
854         targets = TARGET_TYPE_FIELD,
855         feature_support.edition_introduced = EDITION_2023,
856         edition_defaults = { edition: EDITION_LEGACY, value: "" }
857       ];
858     }
859   )schema");
860   ASSERT_NE(file, nullptr);
861 
862   const FieldDescriptor* ext = file->extension(0);
863   EXPECT_THAT(
864       FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023,
865                                        EDITION_2023),
866       HasError(AllOf(HasSubstr("test.bar"), HasSubstr("Nested extensions"))));
867 }
868 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidWithOneof)869 TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithOneof) {
870   const FileDescriptor* file = ParseSchema(R"schema(
871     syntax = "proto2";
872     package test;
873     import "google/protobuf/descriptor.proto";
874 
875     extend google.protobuf.FeatureSet {
876       optional Foo bar = 9999;
877     }
878     message Foo {
879       oneof x {
880         int32 int_field = 1 [
881           targets = TARGET_TYPE_FIELD,
882           feature_support.edition_introduced = EDITION_2023,
883           edition_defaults = { edition: EDITION_LEGACY, value: "1" }
884         ];
885         string string_field = 2 [
886           targets = TARGET_TYPE_FIELD,
887           feature_support.edition_introduced = EDITION_2023,
888           edition_defaults = { edition: EDITION_LEGACY, value: "'hello'" }
889         ];
890       }
891     }
892   )schema");
893   ASSERT_NE(file, nullptr);
894 
895   const FieldDescriptor* ext = file->extension(0);
896   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
897                                                EDITION_2023, EDITION_2023),
898               HasError(AllOf(HasSubstr("test.Foo"),
899                              HasSubstr("oneof feature fields"))));
900 }
901 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidWithRequired)902 TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithRequired) {
903   const FileDescriptor* file = ParseSchema(R"schema(
904     syntax = "proto2";
905     package test;
906     import "google/protobuf/descriptor.proto";
907 
908     extend google.protobuf.FeatureSet {
909       optional Foo bar = 9999;
910     }
911     message Foo {
912       required int32 required_field = 1 [
913         targets = TARGET_TYPE_FIELD,
914         feature_support.edition_introduced = EDITION_2023,
915         edition_defaults = { edition: EDITION_LEGACY, value: "" }
916       ];
917     }
918   )schema");
919   ASSERT_NE(file, nullptr);
920 
921   const FieldDescriptor* ext = file->extension(0);
922   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
923                                                EDITION_2023, EDITION_2023),
924               HasError(AllOf(HasSubstr("test.Foo.required_field"),
925                              HasSubstr("required field"))));
926 }
927 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidWithRepeated)928 TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithRepeated) {
929   const FileDescriptor* file = ParseSchema(R"schema(
930     syntax = "proto2";
931     package test;
932     import "google/protobuf/descriptor.proto";
933 
934     extend google.protobuf.FeatureSet {
935       optional Foo bar = 9999;
936     }
937     message Foo {
938       repeated int32 repeated_field = 1 [
939         targets = TARGET_TYPE_FIELD,
940         feature_support.edition_introduced = EDITION_2023,
941         edition_defaults = { edition: EDITION_LEGACY, value: "1" }
942       ];
943     }
944   )schema");
945   ASSERT_NE(file, nullptr);
946 
947   const FieldDescriptor* ext = file->extension(0);
948   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
949                                                EDITION_2023, EDITION_2023),
950               HasError(AllOf(HasSubstr("test.Foo.repeated_field"),
951                              HasSubstr("repeated field"))));
952 }
953 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidWithMissingTarget)954 TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithMissingTarget) {
955   const FileDescriptor* file = ParseSchema(R"schema(
956     syntax = "proto2";
957     package test;
958     import "google/protobuf/descriptor.proto";
959 
960     extend google.protobuf.FeatureSet {
961       optional Foo bar = 9999;
962     }
963     message Foo {
964       optional bool bool_field = 1 [
965         feature_support.edition_introduced = EDITION_2023,
966         edition_defaults = { edition: EDITION_LEGACY, value: "true" }
967       ];
968     }
969   )schema");
970   ASSERT_NE(file, nullptr);
971 
972   const FieldDescriptor* ext = file->extension(0);
973   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
974                                                EDITION_2023, EDITION_2023),
975               HasError(AllOf(HasSubstr("test.Foo.bool_field"),
976                              HasSubstr("no target specified"))));
977 }
978 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidWithMissingSupport)979 TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithMissingSupport) {
980   const FileDescriptor* file = ParseSchema(R"schema(
981     syntax = "proto2";
982     package test;
983     import "google/protobuf/descriptor.proto";
984 
985     extend google.protobuf.FeatureSet {
986       optional Foo bar = 9999;
987     }
988     message Foo {
989       optional bool bool_field = 1 [
990         targets = TARGET_TYPE_FIELD,
991         edition_defaults = { edition: EDITION_LEGACY, value: "true" }
992       ];
993     }
994   )schema");
995   ASSERT_NE(file, nullptr);
996 
997   const FieldDescriptor* ext = file->extension(0);
998   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
999                                                EDITION_2023, EDITION_2023),
1000               HasError(AllOf(HasSubstr("test.Foo.bool_field"),
1001                              HasSubstr("no feature support"))));
1002 }
1003 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidWithMissingEditionIntroduced)1004 TEST_F(FeatureResolverPoolTest,
1005        CompileDefaultsInvalidWithMissingEditionIntroduced) {
1006   const FileDescriptor* file = ParseSchema(R"schema(
1007     syntax = "proto2";
1008     package test;
1009     import "google/protobuf/descriptor.proto";
1010 
1011     extend google.protobuf.FeatureSet {
1012       optional Foo bar = 9999;
1013     }
1014     message Foo {
1015       optional bool bool_field = 1 [
1016         targets = TARGET_TYPE_FIELD,
1017         feature_support = {},
1018         edition_defaults = { edition: EDITION_LEGACY, value: "true" }
1019       ];
1020     }
1021   )schema");
1022   ASSERT_NE(file, nullptr);
1023 
1024   const FieldDescriptor* ext = file->extension(0);
1025   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
1026                                                EDITION_2023, EDITION_2023),
1027               HasError(AllOf(HasSubstr("test.Foo.bool_field"),
1028                              HasSubstr("it was introduced in"))));
1029 }
1030 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidWithMissingDeprecationWarning)1031 TEST_F(FeatureResolverPoolTest,
1032        CompileDefaultsInvalidWithMissingDeprecationWarning) {
1033   const FileDescriptor* file = ParseSchema(R"schema(
1034     syntax = "proto2";
1035     package test;
1036     import "google/protobuf/descriptor.proto";
1037 
1038     extend google.protobuf.FeatureSet {
1039       optional Foo bar = 9999;
1040     }
1041     message Foo {
1042       optional bool bool_field = 1 [
1043         targets = TARGET_TYPE_FIELD,
1044         feature_support = {
1045           edition_introduced: EDITION_2023
1046           edition_deprecated: EDITION_2023
1047         },
1048         edition_defaults = { edition: EDITION_LEGACY, value: "true" }
1049       ];
1050     }
1051   )schema");
1052   ASSERT_NE(file, nullptr);
1053 
1054   const FieldDescriptor* ext = file->extension(0);
1055   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
1056                                                EDITION_2023, EDITION_2023),
1057               HasError(AllOf(HasSubstr("test.Foo.bool_field"),
1058                              HasSubstr("deprecation warning"))));
1059 }
1060 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidWithMissingDeprecation)1061 TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithMissingDeprecation) {
1062   const FileDescriptor* file = ParseSchema(R"schema(
1063     syntax = "proto2";
1064     package test;
1065     import "google/protobuf/descriptor.proto";
1066 
1067     extend google.protobuf.FeatureSet {
1068       optional Foo bar = 9999;
1069     }
1070     message Foo {
1071       optional bool bool_field = 1 [
1072         targets = TARGET_TYPE_FIELD,
1073         feature_support = {
1074           edition_introduced: EDITION_2023
1075           deprecation_warning: "some message"
1076         },
1077         edition_defaults = { edition: EDITION_LEGACY, value: "true" }
1078       ];
1079     }
1080   )schema");
1081   ASSERT_NE(file, nullptr);
1082 
1083   const FieldDescriptor* ext = file->extension(0);
1084   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
1085                                                EDITION_2023, EDITION_2023),
1086               HasError(AllOf(HasSubstr("test.Foo.bool_field"),
1087                              HasSubstr("is not marked deprecated"))));
1088 }
1089 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidDeprecatedBeforeIntroduced)1090 TEST_F(FeatureResolverPoolTest,
1091        CompileDefaultsInvalidDeprecatedBeforeIntroduced) {
1092   const FileDescriptor* file = ParseSchema(R"schema(
1093     syntax = "proto2";
1094     package test;
1095     import "google/protobuf/descriptor.proto";
1096 
1097     extend google.protobuf.FeatureSet {
1098       optional Foo bar = 9999;
1099     }
1100     message Foo {
1101       optional bool bool_field = 1 [
1102         targets = TARGET_TYPE_FIELD,
1103         feature_support = {
1104           edition_introduced: EDITION_2024
1105           edition_deprecated: EDITION_2023
1106           deprecation_warning: "warning"
1107         },
1108         edition_defaults = { edition: EDITION_LEGACY, value: "true" }
1109       ];
1110     }
1111   )schema");
1112   ASSERT_NE(file, nullptr);
1113 
1114   const FieldDescriptor* ext = file->extension(0);
1115   EXPECT_THAT(
1116       FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023,
1117                                        EDITION_2023),
1118       HasError(AllOf(HasSubstr("test.Foo.bool_field"),
1119                      HasSubstr("deprecated before it was introduced"))));
1120 }
1121 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidDeprecatedAfterRemoved)1122 TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidDeprecatedAfterRemoved) {
1123   const FileDescriptor* file = ParseSchema(R"schema(
1124     syntax = "proto2";
1125     package test;
1126     import "google/protobuf/descriptor.proto";
1127 
1128     extend google.protobuf.FeatureSet {
1129       optional Foo bar = 9999;
1130     }
1131     message Foo {
1132       optional bool bool_field = 1 [
1133         targets = TARGET_TYPE_FIELD,
1134         feature_support = {
1135           edition_introduced: EDITION_2023
1136           edition_deprecated: EDITION_2024
1137           deprecation_warning: "warning"
1138           edition_removed: EDITION_2024
1139         },
1140         edition_defaults = { edition: EDITION_LEGACY, value: "true" }
1141       ];
1142     }
1143   )schema");
1144   ASSERT_NE(file, nullptr);
1145 
1146   const FieldDescriptor* ext = file->extension(0);
1147   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
1148                                                EDITION_2023, EDITION_2023),
1149               HasError(AllOf(HasSubstr("test.Foo.bool_field"),
1150                              HasSubstr("deprecated after it was removed"))));
1151 }
1152 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidRemovedBeforeIntroduced)1153 TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidRemovedBeforeIntroduced) {
1154   const FileDescriptor* file = ParseSchema(R"schema(
1155     syntax = "proto2";
1156     package test;
1157     import "google/protobuf/descriptor.proto";
1158 
1159     extend google.protobuf.FeatureSet {
1160       optional Foo bar = 9999;
1161     }
1162     message Foo {
1163       optional bool bool_field = 1 [
1164         targets = TARGET_TYPE_FIELD,
1165         feature_support = {
1166           edition_introduced: EDITION_2024
1167           edition_removed: EDITION_2023
1168         },
1169         edition_defaults = { edition: EDITION_LEGACY, value: "true" }
1170       ];
1171     }
1172   )schema");
1173   ASSERT_NE(file, nullptr);
1174 
1175   const FieldDescriptor* ext = file->extension(0);
1176   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
1177                                                EDITION_2023, EDITION_2023),
1178               HasError(AllOf(HasSubstr("test.Foo.bool_field"),
1179                              HasSubstr("removed before it was introduced"))));
1180 }
1181 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidMissingLegacyDefaults)1182 TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidMissingLegacyDefaults) {
1183   const FileDescriptor* file = ParseSchema(R"schema(
1184     syntax = "proto2";
1185     package test;
1186     import "google/protobuf/descriptor.proto";
1187 
1188     extend google.protobuf.FeatureSet {
1189       optional Foo bar = 9999;
1190     }
1191     message Foo {
1192       optional bool bool_field = 1 [
1193         targets = TARGET_TYPE_FIELD,
1194         feature_support = {
1195           edition_introduced: EDITION_2024
1196         },
1197         edition_defaults = { edition: EDITION_2024, value: "true" }
1198       ];
1199     }
1200   )schema");
1201   ASSERT_NE(file, nullptr);
1202 
1203   const FieldDescriptor* ext = file->extension(0);
1204   EXPECT_THAT(
1205       FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023,
1206                                        EDITION_2023),
1207       HasError(AllOf(HasSubstr("test.Foo.bool_field"),
1208                      HasSubstr("no default specified for EDITION_LEGACY"))));
1209 }
1210 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidDefaultsBeforeIntroduced)1211 TEST_F(FeatureResolverPoolTest,
1212        CompileDefaultsInvalidDefaultsBeforeIntroduced) {
1213   const FileDescriptor* file = ParseSchema(R"schema(
1214     syntax = "proto2";
1215     package test;
1216     import "google/protobuf/descriptor.proto";
1217 
1218     extend google.protobuf.FeatureSet {
1219       optional Foo bar = 9999;
1220     }
1221     message Foo {
1222       optional bool bool_field = 1 [
1223         targets = TARGET_TYPE_FIELD,
1224         feature_support = {
1225           edition_introduced: EDITION_2024
1226         },
1227         edition_defaults = { edition: EDITION_LEGACY, value: "true" },
1228         edition_defaults = { edition: EDITION_2023, value: "false" }
1229       ];
1230     }
1231   )schema");
1232   ASSERT_NE(file, nullptr);
1233 
1234   const FieldDescriptor* ext = file->extension(0);
1235   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
1236                                                EDITION_2023, EDITION_2023),
1237               HasError(AllOf(HasSubstr("test.Foo.bool_field"),
1238                              HasSubstr("specified for edition 2023"),
1239                              HasSubstr("before it was introduced"))));
1240 }
1241 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidDefaultsAfterRemoved)1242 TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidDefaultsAfterRemoved) {
1243   const FileDescriptor* file = ParseSchema(R"schema(
1244     syntax = "proto2";
1245     package test;
1246     import "google/protobuf/descriptor.proto";
1247 
1248     extend google.protobuf.FeatureSet {
1249       optional Foo bar = 9999;
1250     }
1251     message Foo {
1252       optional bool bool_field = 1 [
1253         targets = TARGET_TYPE_FIELD,
1254         feature_support = {
1255           edition_introduced: EDITION_PROTO2
1256           edition_removed: EDITION_2023
1257         },
1258         edition_defaults = { edition: EDITION_LEGACY, value: "true" },
1259         edition_defaults = { edition: EDITION_2024, value: "true" }
1260       ];
1261     }
1262   )schema");
1263   ASSERT_NE(file, nullptr);
1264 
1265   const FieldDescriptor* ext = file->extension(0);
1266   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
1267                                                EDITION_2023, EDITION_2023),
1268               HasError(AllOf(HasSubstr("test.Foo.bool_field"),
1269                              HasSubstr("specified for edition 2024"),
1270                              HasSubstr("after it was removed"))));
1271 }
1272 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidDefaultsScalarParsingError)1273 TEST_F(FeatureResolverPoolTest,
1274        CompileDefaultsInvalidDefaultsScalarParsingError) {
1275   const FileDescriptor* file = ParseSchema(R"schema(
1276     syntax = "proto2";
1277     package test;
1278     import "google/protobuf/descriptor.proto";
1279 
1280     extend google.protobuf.FeatureSet {
1281       optional Foo bar = 9999;
1282     }
1283     message Foo {
1284       optional bool field_feature = 12 [
1285         targets = TARGET_TYPE_FIELD,
1286         feature_support.edition_introduced = EDITION_2023,
1287         edition_defaults = { edition: EDITION_LEGACY, value: "1.23" }
1288       ];
1289     }
1290   )schema");
1291   ASSERT_NE(file, nullptr);
1292 
1293   const FieldDescriptor* ext = file->extension(0);
1294   EXPECT_THAT(
1295       FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023,
1296                                        EDITION_2023),
1297       HasError(AllOf(HasSubstr("in edition_defaults"), HasSubstr("1.23"))));
1298 }
1299 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidDefaultsScalarParsingErrorSkipped)1300 TEST_F(FeatureResolverPoolTest,
1301        CompileDefaultsInvalidDefaultsScalarParsingErrorSkipped) {
1302   const FileDescriptor* file = ParseSchema(R"schema(
1303     syntax = "proto2";
1304     package test;
1305     import "google/protobuf/descriptor.proto";
1306 
1307     extend google.protobuf.FeatureSet {
1308       optional Foo bar = 9999;
1309     }
1310     message Foo {
1311       optional bool field_feature = 12 [
1312         targets = TARGET_TYPE_FIELD,
1313         feature_support.edition_introduced = EDITION_2023,
1314         edition_defaults = { edition: EDITION_99997_TEST_ONLY, value: "1.5" },
1315         edition_defaults = { edition: EDITION_LEGACY, value: "true" }
1316       ];
1317     }
1318   )schema");
1319   ASSERT_NE(file, nullptr);
1320 
1321   const FieldDescriptor* ext = file->extension(0);
1322   auto defaults = FeatureResolver::CompileDefaults(feature_set_, {ext},
1323                                                    EDITION_2023, EDITION_2023);
1324   ASSERT_OK(defaults);
1325 
1326   auto resolver = FeatureResolver::Create(EDITION_2023, *defaults);
1327   ASSERT_OK(resolver);
1328   FeatureSet parent, child;
1329   EXPECT_OK(resolver->MergeFeatures(parent, child));
1330 }
1331 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidDefaultsTooEarly)1332 TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidDefaultsTooEarly) {
1333   const FileDescriptor* file = ParseSchema(R"schema(
1334     syntax = "proto2";
1335     package test;
1336     import "google/protobuf/descriptor.proto";
1337 
1338     extend google.protobuf.FeatureSet {
1339       optional Foo bar = 9999;
1340     }
1341     message Foo {
1342       optional bool field_feature = 12 [
1343         targets = TARGET_TYPE_FIELD,
1344         feature_support.edition_introduced = EDITION_2023,
1345         edition_defaults = { edition: EDITION_2_TEST_ONLY, value: "true" },
1346         edition_defaults = { edition: EDITION_LEGACY, value: "false" }
1347       ];
1348     }
1349   )schema");
1350   ASSERT_NE(file, nullptr);
1351 
1352   const FieldDescriptor* ext = file->extension(0);
1353   EXPECT_THAT(
1354       FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023,
1355                                        EDITION_2023),
1356       HasError(HasSubstr("Minimum edition 2_TEST_ONLY is not EDITION_LEGACY")));
1357 }
1358 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidValueWithMissingDeprecationWarning)1359 TEST_F(FeatureResolverPoolTest,
1360        CompileDefaultsInvalidValueWithMissingDeprecationWarning) {
1361   const FileDescriptor* file = ParseSchema(R"schema(
1362     syntax = "proto2";
1363     package test;
1364     import "google/protobuf/descriptor.proto";
1365 
1366     extend google.protobuf.FeatureSet {
1367       optional Foo bar = 9999;
1368     }
1369     enum FooValues {
1370       UNKNOWN = 0;
1371       VALUE = 1 [feature_support.edition_deprecated = EDITION_2023];
1372     }
1373     message Foo {
1374       optional FooValues bool_field = 1 [
1375         targets = TARGET_TYPE_FIELD,
1376         feature_support.edition_introduced = EDITION_2023,
1377         edition_defaults = { edition: EDITION_LEGACY, value: "UNKNOWN" }
1378       ];
1379     }
1380   )schema");
1381   ASSERT_NE(file, nullptr);
1382 
1383   const FieldDescriptor* ext = file->extension(0);
1384   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
1385                                                EDITION_2023, EDITION_2023),
1386               HasError(AllOf(HasSubstr("test.VALUE"),
1387                              HasSubstr("deprecation warning"))));
1388 }
1389 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidValueWithMissingDeprecation)1390 TEST_F(FeatureResolverPoolTest,
1391        CompileDefaultsInvalidValueWithMissingDeprecation) {
1392   const FileDescriptor* file = ParseSchema(R"schema(
1393     syntax = "proto2";
1394     package test;
1395     import "google/protobuf/descriptor.proto";
1396 
1397     extend google.protobuf.FeatureSet {
1398       optional Foo bar = 9999;
1399     }
1400     enum FooValues {
1401       UNKNOWN = 0;
1402       VALUE = 1 [feature_support.deprecation_warning = "some message"];
1403     }
1404     message Foo {
1405       optional FooValues bool_field = 1 [
1406         targets = TARGET_TYPE_FIELD,
1407         feature_support.edition_introduced = EDITION_2023,
1408         edition_defaults = { edition: EDITION_LEGACY, value: "UNKNOWN" }
1409       ];
1410     }
1411   )schema");
1412   ASSERT_NE(file, nullptr);
1413 
1414   const FieldDescriptor* ext = file->extension(0);
1415   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
1416                                                EDITION_2023, EDITION_2023),
1417               HasError(AllOf(HasSubstr("test.VALUE"),
1418                              HasSubstr("is not marked deprecated"))));
1419 }
1420 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidValueDeprecatedBeforeIntroduced)1421 TEST_F(FeatureResolverPoolTest,
1422        CompileDefaultsInvalidValueDeprecatedBeforeIntroduced) {
1423   const FileDescriptor* file = ParseSchema(R"schema(
1424     syntax = "proto2";
1425     package test;
1426     import "google/protobuf/descriptor.proto";
1427 
1428     extend google.protobuf.FeatureSet {
1429       optional Foo bar = 9999;
1430     }
1431     enum FooValues {
1432       UNKNOWN = 0;
1433       VALUE = 1 [feature_support = {
1434         edition_introduced: EDITION_2024
1435         edition_deprecated: EDITION_2023
1436         deprecation_warning: "warning"
1437       }];
1438     }
1439     message Foo {
1440       optional FooValues bool_field = 1 [
1441         targets = TARGET_TYPE_FIELD,
1442         feature_support.edition_introduced = EDITION_2023,
1443         edition_defaults = { edition: EDITION_LEGACY, value: "UNKNOWN" }
1444       ];
1445     }
1446   )schema");
1447   ASSERT_NE(file, nullptr);
1448 
1449   const FieldDescriptor* ext = file->extension(0);
1450   EXPECT_THAT(
1451       FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023,
1452                                        EDITION_2023),
1453       HasError(AllOf(HasSubstr("test.VALUE"),
1454                      HasSubstr("deprecated before it was introduced"))));
1455 }
1456 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidValueDeprecatedBeforeIntroducedInherited)1457 TEST_F(FeatureResolverPoolTest,
1458        CompileDefaultsInvalidValueDeprecatedBeforeIntroducedInherited) {
1459   const FileDescriptor* file = ParseSchema(R"schema(
1460     syntax = "proto2";
1461     package test;
1462     import "google/protobuf/descriptor.proto";
1463 
1464     extend google.protobuf.FeatureSet {
1465       optional Foo bar = 9999;
1466     }
1467     enum FooValues {
1468       UNKNOWN = 0;
1469       VALUE = 1 [feature_support = {
1470         edition_deprecated: EDITION_2023
1471         deprecation_warning: "warning"
1472       }];
1473     }
1474     message Foo {
1475       optional FooValues bool_field = 1 [
1476         targets = TARGET_TYPE_FIELD,
1477         feature_support.edition_introduced = EDITION_2024,
1478         edition_defaults = { edition: EDITION_LEGACY, value: "UNKNOWN" }
1479       ];
1480     }
1481   )schema");
1482   ASSERT_NE(file, nullptr);
1483 
1484   const FieldDescriptor* ext = file->extension(0);
1485   EXPECT_THAT(
1486       FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023,
1487                                        EDITION_2023),
1488       HasError(AllOf(HasSubstr("test.VALUE"),
1489                      HasSubstr("deprecated before it was introduced"))));
1490 }
1491 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidValueDeprecatedAfterRemoved)1492 TEST_F(FeatureResolverPoolTest,
1493        CompileDefaultsInvalidValueDeprecatedAfterRemoved) {
1494   const FileDescriptor* file = ParseSchema(R"schema(
1495     syntax = "proto2";
1496     package test;
1497     import "google/protobuf/descriptor.proto";
1498 
1499     extend google.protobuf.FeatureSet {
1500       optional Foo bar = 9999;
1501     }
1502     enum FooValues {
1503       UNKNOWN = 0;
1504       VALUE = 1 [feature_support = {
1505         edition_introduced: EDITION_2023
1506         edition_deprecated: EDITION_2024
1507         deprecation_warning: "warning"
1508         edition_removed: EDITION_2024
1509       }];
1510     }
1511     message Foo {
1512       optional FooValues bool_field = 1 [
1513         targets = TARGET_TYPE_FIELD,
1514         feature_support.edition_introduced = EDITION_2023,
1515         edition_defaults = { edition: EDITION_LEGACY, value: "UNKNOWN" }
1516       ];
1517     }
1518   )schema");
1519   ASSERT_NE(file, nullptr);
1520 
1521   const FieldDescriptor* ext = file->extension(0);
1522   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
1523                                                EDITION_2023, EDITION_2023),
1524               HasError(AllOf(HasSubstr("test.VALUE"),
1525                              HasSubstr("deprecated after it was removed"))));
1526 }
1527 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidValueRemovedBeforeIntroduced)1528 TEST_F(FeatureResolverPoolTest,
1529        CompileDefaultsInvalidValueRemovedBeforeIntroduced) {
1530   const FileDescriptor* file = ParseSchema(R"schema(
1531     syntax = "proto2";
1532     package test;
1533     import "google/protobuf/descriptor.proto";
1534 
1535     extend google.protobuf.FeatureSet {
1536       optional Foo bar = 9999;
1537     }
1538     enum FooValues {
1539       UNKNOWN = 0;
1540       VALUE = 1 [feature_support = {
1541         edition_introduced: EDITION_2024
1542         edition_removed: EDITION_2023
1543       }];
1544     }
1545     message Foo {
1546       optional FooValues bool_field = 1 [
1547         targets = TARGET_TYPE_FIELD,
1548         feature_support.edition_introduced = EDITION_2023,
1549         edition_defaults = { edition: EDITION_LEGACY, value: "UNKNOWN" }
1550       ];
1551     }
1552   )schema");
1553   ASSERT_NE(file, nullptr);
1554 
1555   const FieldDescriptor* ext = file->extension(0);
1556   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
1557                                                EDITION_2023, EDITION_2023),
1558               HasError(AllOf(HasSubstr("test.VALUE"),
1559                              HasSubstr("removed before it was introduced"))));
1560 }
1561 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidValueIntroducedBeforeFeature)1562 TEST_F(FeatureResolverPoolTest,
1563        CompileDefaultsInvalidValueIntroducedBeforeFeature) {
1564   const FileDescriptor* file = ParseSchema(R"schema(
1565     syntax = "proto2";
1566     package test;
1567     import "google/protobuf/descriptor.proto";
1568 
1569     extend google.protobuf.FeatureSet {
1570       optional Foo bar = 9999;
1571     }
1572     enum FooValues {
1573       UNKNOWN = 0;
1574       VALUE = 1 [feature_support = {
1575         edition_introduced: EDITION_2023
1576       }];
1577     }
1578     message Foo {
1579       optional FooValues bool_field = 1 [
1580         targets = TARGET_TYPE_FIELD,
1581         feature_support.edition_introduced = EDITION_2024,
1582         edition_defaults = { edition: EDITION_LEGACY, value: "UNKNOWN" }
1583       ];
1584     }
1585   )schema");
1586   ASSERT_NE(file, nullptr);
1587 
1588   const FieldDescriptor* ext = file->extension(0);
1589   EXPECT_THAT(
1590       FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023,
1591                                        EDITION_2023),
1592       HasError(AllOf(HasSubstr("test.VALUE"), HasSubstr("introduced before"),
1593                      HasSubstr("test.Foo.bool_field"))));
1594 }
1595 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidValueIntroducedAfterFeatureRemoved)1596 TEST_F(FeatureResolverPoolTest,
1597        CompileDefaultsInvalidValueIntroducedAfterFeatureRemoved) {
1598   const FileDescriptor* file = ParseSchema(R"schema(
1599     syntax = "proto2";
1600     package test;
1601     import "google/protobuf/descriptor.proto";
1602 
1603     extend google.protobuf.FeatureSet {
1604       optional Foo bar = 9999;
1605     }
1606     enum FooValues {
1607       UNKNOWN = 0;
1608       VALUE = 1 [feature_support = {
1609         edition_introduced: EDITION_99997_TEST_ONLY
1610       }];
1611     }
1612     message Foo {
1613       optional FooValues bool_field = 1 [
1614         targets = TARGET_TYPE_FIELD,
1615         feature_support.edition_introduced = EDITION_2023,
1616         feature_support.edition_removed = EDITION_2024,
1617         edition_defaults = { edition: EDITION_LEGACY, value: "UNKNOWN" }
1618       ];
1619     }
1620   )schema");
1621   ASSERT_NE(file, nullptr);
1622 
1623   const FieldDescriptor* ext = file->extension(0);
1624   EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
1625                                                EDITION_2023, EDITION_2023),
1626               HasError(AllOf(HasSubstr("test.VALUE"),
1627                              HasSubstr("removed before it was introduced"))));
1628 }
1629 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidValueRemovedAfterFeature)1630 TEST_F(FeatureResolverPoolTest,
1631        CompileDefaultsInvalidValueRemovedAfterFeature) {
1632   const FileDescriptor* file = ParseSchema(R"schema(
1633     syntax = "proto2";
1634     package test;
1635     import "google/protobuf/descriptor.proto";
1636 
1637     extend google.protobuf.FeatureSet {
1638       optional Foo bar = 9999;
1639     }
1640     enum FooValues {
1641       UNKNOWN = 0;
1642       VALUE = 1 [feature_support = {
1643         edition_removed: EDITION_99997_TEST_ONLY
1644       }];
1645     }
1646     message Foo {
1647       optional FooValues bool_field = 1 [
1648         targets = TARGET_TYPE_FIELD,
1649         feature_support.edition_introduced = EDITION_2023,
1650         feature_support.edition_removed = EDITION_2024,
1651         edition_defaults = { edition: EDITION_LEGACY, value: "UNKNOWN" }
1652       ];
1653     }
1654   )schema");
1655   ASSERT_NE(file, nullptr);
1656 
1657   const FieldDescriptor* ext = file->extension(0);
1658   EXPECT_THAT(
1659       FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023,
1660                                        EDITION_2023),
1661       HasError(AllOf(HasSubstr("test.VALUE"), HasSubstr("removed after"),
1662                      HasSubstr("test.Foo.bool_field"))));
1663 }
1664 
TEST_F(FeatureResolverPoolTest,CompileDefaultsInvalidValueDeprecatedAfterFeature)1665 TEST_F(FeatureResolverPoolTest,
1666        CompileDefaultsInvalidValueDeprecatedAfterFeature) {
1667   const FileDescriptor* file = ParseSchema(R"schema(
1668     syntax = "proto2";
1669     package test;
1670     import "google/protobuf/descriptor.proto";
1671 
1672     extend google.protobuf.FeatureSet {
1673       optional Foo bar = 9999;
1674     }
1675     enum FooValues {
1676       UNKNOWN = 0;
1677       VALUE = 1 [feature_support = {
1678         edition_deprecated: EDITION_99997_TEST_ONLY
1679         deprecation_warning: "warning"
1680       }];
1681     }
1682     message Foo {
1683       optional FooValues bool_field = 1 [
1684         targets = TARGET_TYPE_FIELD,
1685         feature_support.edition_introduced = EDITION_2023,
1686         feature_support.edition_deprecated = EDITION_2024,
1687         feature_support.deprecation_warning = "warning",
1688         edition_defaults = { edition: EDITION_LEGACY, value: "UNKNOWN" }
1689       ];
1690     }
1691   )schema");
1692   ASSERT_NE(file, nullptr);
1693 
1694   const FieldDescriptor* ext = file->extension(0);
1695   EXPECT_THAT(
1696       FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023,
1697                                        EDITION_2023),
1698       HasError(AllOf(HasSubstr("test.VALUE"), HasSubstr("deprecated after"),
1699                      HasSubstr("test.Foo.bool_field"))));
1700 }
1701 
TEST_F(FeatureResolverPoolTest,CompileDefaultsMinimumTooEarly)1702 TEST_F(FeatureResolverPoolTest, CompileDefaultsMinimumTooEarly) {
1703   const FileDescriptor* file = ParseSchema(R"schema(
1704     syntax = "proto2";
1705     package test;
1706     import "google/protobuf/descriptor.proto";
1707 
1708     extend google.protobuf.FeatureSet {
1709       optional Foo bar = 9999;
1710     }
1711     message Foo {
1712       optional bool field_feature = 12 [
1713         targets = TARGET_TYPE_FIELD,
1714         feature_support.edition_introduced = EDITION_2023,
1715         edition_defaults = { edition: EDITION_LEGACY, value: "true" }
1716       ];
1717     }
1718   )schema");
1719   ASSERT_NE(file, nullptr);
1720 
1721   const FieldDescriptor* ext = file->extension(0);
1722   EXPECT_THAT(
1723       FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_1_TEST_ONLY,
1724                                        EDITION_99997_TEST_ONLY),
1725       HasError(HasSubstr("edition 1_TEST_ONLY is earlier than the oldest")));
1726 }
1727 
TEST_F(FeatureResolverPoolTest,CompileDefaultsRemovedOnly)1728 TEST_F(FeatureResolverPoolTest, CompileDefaultsRemovedOnly) {
1729   const FileDescriptor* file = ParseSchema(R"schema(
1730     syntax = "proto2";
1731     package test;
1732     import "google/protobuf/descriptor.proto";
1733 
1734     extend google.protobuf.FeatureSet {
1735       optional Foo bar = 9999;
1736     }
1737     enum Bar {
1738       TEST_ENUM_FEATURE_UNKNOWN = 0;
1739       VALUE1 = 1;
1740       VALUE2 = 2;
1741     }
1742     message Foo {
1743       optional Bar file_feature = 1 [
1744         targets = TARGET_TYPE_FIELD,
1745         feature_support.edition_introduced = EDITION_2023,
1746         feature_support.edition_removed = EDITION_99998_TEST_ONLY,
1747         edition_defaults = { edition: EDITION_LEGACY, value: "VALUE1" }
1748       ];
1749     }
1750   )schema");
1751   ASSERT_NE(file, nullptr);
1752 
1753   const FieldDescriptor* ext = file->extension(0);
1754   auto compiled_defaults = FeatureResolver::CompileDefaults(
1755       feature_set_, {ext}, EDITION_99997_TEST_ONLY, EDITION_99999_TEST_ONLY);
1756   ASSERT_OK(compiled_defaults);
1757   const auto& defaults = *compiled_defaults->defaults().rbegin();
1758   EXPECT_THAT(defaults.edition(), EDITION_99998_TEST_ONLY);
1759   EXPECT_THAT(defaults.fixed_features().GetExtension(pb::test).file_feature(),
1760               pb::VALUE1);
1761   EXPECT_FALSE(defaults.overridable_features()
1762                    .GetExtension(pb::test)
1763                    .has_file_feature());
1764 }
1765 
TEST_F(FeatureResolverPoolTest,CompileDefaultsIntroducedOnly)1766 TEST_F(FeatureResolverPoolTest, CompileDefaultsIntroducedOnly) {
1767   const FileDescriptor* file = ParseSchema(R"schema(
1768     syntax = "proto2";
1769     package test;
1770     import "google/protobuf/descriptor.proto";
1771 
1772     extend google.protobuf.FeatureSet {
1773       optional Foo bar = 9999;
1774     }
1775     enum Bar {
1776       TEST_ENUM_FEATURE_UNKNOWN = 0;
1777       VALUE1 = 1;
1778       VALUE2 = 2;
1779     }
1780     message Foo {
1781       optional Bar file_feature = 1 [
1782         targets = TARGET_TYPE_FIELD,
1783         feature_support.edition_introduced = EDITION_99998_TEST_ONLY,
1784         edition_defaults = { edition: EDITION_LEGACY, value: "VALUE1" }
1785       ];
1786     }
1787   )schema");
1788   ASSERT_NE(file, nullptr);
1789 
1790   const FieldDescriptor* ext = file->extension(0);
1791   auto compiled_defaults = FeatureResolver::CompileDefaults(
1792       feature_set_, {ext}, EDITION_99997_TEST_ONLY, EDITION_99999_TEST_ONLY);
1793   ASSERT_OK(compiled_defaults);
1794   const auto& defaults = *compiled_defaults->defaults().rbegin();
1795   EXPECT_THAT(defaults.edition(), EDITION_99998_TEST_ONLY);
1796   EXPECT_THAT(
1797       defaults.overridable_features().GetExtension(pb::test).file_feature(),
1798       pb::VALUE1);
1799   EXPECT_FALSE(
1800       defaults.fixed_features().GetExtension(pb::test).has_file_feature());
1801 }
1802 
TEST_F(FeatureResolverPoolTest,CompileDefaultsMinimumCovered)1803 TEST_F(FeatureResolverPoolTest, CompileDefaultsMinimumCovered) {
1804   const FileDescriptor* file = ParseSchema(R"schema(
1805     syntax = "proto2";
1806     package test;
1807     import "google/protobuf/descriptor.proto";
1808 
1809     extend google.protobuf.FeatureSet {
1810       optional Foo bar = 9999;
1811     }
1812     enum Bar {
1813       TEST_ENUM_FEATURE_UNKNOWN = 0;
1814       VALUE1 = 1;
1815       VALUE2 = 2;
1816       VALUE3 = 3;
1817     }
1818     message Foo {
1819       optional Bar file_feature = 1 [
1820         targets = TARGET_TYPE_FIELD,
1821         feature_support.edition_introduced = EDITION_2023,
1822         edition_defaults = { edition: EDITION_99998_TEST_ONLY, value: "VALUE3" },
1823         edition_defaults = { edition: EDITION_2023, value: "VALUE2" },
1824         edition_defaults = { edition: EDITION_LEGACY, value: "VALUE1" }
1825       ];
1826     }
1827   )schema");
1828   ASSERT_NE(file, nullptr);
1829 
1830   const FieldDescriptor* ext = file->extension(0);
1831   auto defaults = FeatureResolver::CompileDefaults(
1832       feature_set_, {ext}, EDITION_99997_TEST_ONLY, EDITION_99999_TEST_ONLY);
1833   ASSERT_OK(defaults);
1834   EXPECT_THAT(*defaults, EqualsProto(R"pb(
1835     minimum_edition: EDITION_99997_TEST_ONLY
1836     maximum_edition: EDITION_99999_TEST_ONLY
1837     defaults {
1838       edition: EDITION_LEGACY
1839       overridable_features {
1840         [pb.test] {}
1841       }
1842       fixed_features {
1843         field_presence: EXPLICIT
1844         enum_type: CLOSED
1845         repeated_field_encoding: EXPANDED
1846         utf8_validation: NONE
1847         message_encoding: LENGTH_PREFIXED
1848         json_format: LEGACY_BEST_EFFORT
1849         [pb.test] { file_feature: VALUE1 }
1850       }
1851     }
1852     defaults {
1853       edition: EDITION_PROTO3
1854       overridable_features {
1855         [pb.test] {}
1856       }
1857       fixed_features {
1858         field_presence: IMPLICIT
1859         enum_type: OPEN
1860         repeated_field_encoding: PACKED
1861         utf8_validation: VERIFY
1862         message_encoding: LENGTH_PREFIXED
1863         json_format: ALLOW
1864         [pb.test] { file_feature: VALUE1 }
1865       }
1866     }
1867     defaults {
1868       edition: EDITION_2023
1869       overridable_features {
1870         field_presence: EXPLICIT
1871         enum_type: OPEN
1872         repeated_field_encoding: PACKED
1873         utf8_validation: VERIFY
1874         message_encoding: LENGTH_PREFIXED
1875         json_format: ALLOW
1876         [pb.test] { file_feature: VALUE2 }
1877       }
1878       fixed_features {
1879         [pb.test] {}
1880       }
1881     }
1882     defaults {
1883       edition: EDITION_99998_TEST_ONLY
1884       overridable_features {
1885         field_presence: EXPLICIT
1886         enum_type: OPEN
1887         repeated_field_encoding: PACKED
1888         utf8_validation: VERIFY
1889         message_encoding: LENGTH_PREFIXED
1890         json_format: ALLOW
1891         [pb.test] { file_feature: VALUE3 }
1892       }
1893       fixed_features {
1894         [pb.test] {}
1895       }
1896     }
1897   )pb"));
1898 }
1899 
1900 class FeatureUnboundedTypeTest
1901     : public FeatureResolverPoolTest,
1902       public ::testing::WithParamInterface<absl::string_view> {};
1903 
TEST_P(FeatureUnboundedTypeTest,CompileDefaults)1904 TEST_P(FeatureUnboundedTypeTest, CompileDefaults) {
1905   const FileDescriptor* file = ParseSchema(absl::Substitute(R"schema(
1906     syntax = "proto2";
1907     package test;
1908     import "google/protobuf/descriptor.proto";
1909 
1910     extend google.protobuf.FeatureSet {
1911       optional Foo bar = 9999;
1912     }
1913     message SomeMessage {
1914       optional bool value = 1;
1915     }
1916     message Foo {
1917       optional $0 field_feature = 12 [
1918         targets = TARGET_TYPE_FIELD,
1919         feature_support.edition_introduced = EDITION_2023,
1920         edition_defaults = { edition: EDITION_LEGACY, value: "1" }
1921       ];
1922     }
1923   )schema",
1924                                                             GetParam()));
1925   ASSERT_NE(file, nullptr);
1926 
1927   const FieldDescriptor* ext = file->extension(0);
1928   EXPECT_THAT(
1929       FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_1_TEST_ONLY,
1930                                        EDITION_99997_TEST_ONLY),
1931       HasError(HasSubstr("is not an enum or boolean")));
1932 }
1933 
1934 INSTANTIATE_TEST_SUITE_P(FeatureUnboundedTypeTestImpl, FeatureUnboundedTypeTest,
1935                          testing::Values("int32", "int64", "uint32", "string",
1936                                          "bytes", "float", "double",
1937                                          "SomeMessage"));
1938 
1939 }  // namespace
1940 }  // namespace protobuf
1941 }  // namespace google
1942 
1943 #include "google/protobuf/port_undef.inc"
1944