@@ -67,6 +67,7 @@ class TestGenerator : public CodeGenerator {
6767 }
6868
6969 // Expose the protected methods for testing.
70+ using CodeGenerator::GetResolvedSourceFeatureExtension;
7071 using CodeGenerator::GetResolvedSourceFeatures;
7172 using CodeGenerator::GetUnresolvedSourceFeatures;
7273
@@ -246,6 +247,143 @@ TEST_F(CodeGeneratorTest, GetResolvedSourceFeaturesInherited) {
246247 EXPECT_EQ (ext.source_feature2 (), pb::EnumFeature::VALUE3);
247248}
248249
250+ TEST_F (CodeGeneratorTest, GetResolvedSourceFeatureExtension) {
251+ TestGenerator generator;
252+ generator.set_feature_extensions ({GetExtensionReflection (pb::test)});
253+ ASSERT_OK (pool_.SetFeatureSetDefaults (*generator.BuildFeatureSetDefaults ()));
254+
255+ ASSERT_THAT (BuildFile (DescriptorProto::descriptor ()->file ()), NotNull ());
256+ ASSERT_THAT (BuildFile (pb::TestMessage::descriptor ()->file ()), NotNull ());
257+ auto file = BuildFile (R"schema(
258+ edition = "2023";
259+ package proto2_unittest;
260+
261+ import "google/protobuf/unittest_features.proto";
262+
263+ option features.(pb.test).file_feature = VALUE6;
264+ option features.(pb.test).source_feature = VALUE5;
265+ )schema" );
266+ ASSERT_THAT (file, NotNull ());
267+ const pb::TestFeatures& ext1 =
268+ TestGenerator::GetResolvedSourceFeatureExtension (*file, pb::test);
269+ const pb::TestFeatures& ext2 =
270+ TestGenerator::GetResolvedSourceFeatures (*file).GetExtension (pb::test);
271+
272+ // Since the pool provides the feature set defaults, there should be no
273+ // difference between the two results.
274+ EXPECT_EQ (ext1.enum_feature (), pb::EnumFeature::VALUE1);
275+ EXPECT_EQ (ext1.field_feature (), pb::EnumFeature::VALUE1);
276+ EXPECT_EQ (ext1.file_feature (), pb::EnumFeature::VALUE6);
277+ EXPECT_EQ (ext1.source_feature (), pb::EnumFeature::VALUE5);
278+ EXPECT_EQ (ext2.enum_feature (), ext1.enum_feature ());
279+ EXPECT_EQ (ext2.field_feature (), ext1.field_feature ());
280+ EXPECT_EQ (ext2.file_feature (), ext1.file_feature ());
281+ EXPECT_EQ (ext2.source_feature (), ext1.source_feature ());
282+ }
283+
284+ TEST_F (CodeGeneratorTest, GetResolvedSourceFeatureExtensionEditedDefaults) {
285+ FeatureSetDefaults defaults = ParseTextOrDie (R"pb(
286+ minimum_edition: EDITION_PROTO2
287+ maximum_edition: EDITION_2024
288+ defaults {
289+ edition: EDITION_LEGACY
290+ overridable_features {}
291+ fixed_features {
292+ field_presence: EXPLICIT
293+ enum_type: CLOSED
294+ repeated_field_encoding: EXPANDED
295+ utf8_validation: NONE
296+ message_encoding: LENGTH_PREFIXED
297+ json_format: LEGACY_BEST_EFFORT
298+ enforce_naming_style: STYLE_LEGACY
299+ default_symbol_visibility: EXPORT_ALL
300+ }
301+ }
302+ defaults {
303+ edition: EDITION_2023
304+ overridable_features {
305+ field_presence: EXPLICIT
306+ enum_type: OPEN
307+ repeated_field_encoding: PACKED
308+ utf8_validation: VERIFY
309+ message_encoding: LENGTH_PREFIXED
310+ json_format: ALLOW
311+ [pb.test] {
312+ file_feature: VALUE3
313+ field_feature: VALUE15
314+ enum_feature: VALUE14
315+ source_feature: VALUE1
316+ }
317+ }
318+ fixed_features {
319+ enforce_naming_style: STYLE_LEGACY
320+ default_symbol_visibility: EXPORT_ALL
321+ }
322+ }
323+ )pb" );
324+ ASSERT_OK (pool_.SetFeatureSetDefaults (defaults));
325+
326+ ASSERT_THAT (BuildFile (DescriptorProto::descriptor ()->file ()), NotNull ());
327+ ASSERT_THAT (BuildFile (pb::TestMessage::descriptor ()->file ()), NotNull ());
328+ auto file = BuildFile (R"schema(
329+ edition = "2023";
330+ package proto2_unittest;
331+
332+ import "google/protobuf/unittest_features.proto";
333+
334+ option features.(pb.test).file_feature = VALUE6;
335+ option features.(pb.test).source_feature = VALUE5;
336+ )schema" );
337+ ASSERT_THAT (file, NotNull ());
338+ const pb::TestFeatures& ext =
339+ TestGenerator::GetResolvedSourceFeatureExtension (*file, pb::test);
340+
341+ // Since the pool provides the modified feature set defaults, the result
342+ // should be the one reflecting the pool's defaults.
343+ EXPECT_EQ (ext.enum_feature (), pb::EnumFeature::VALUE14);
344+ EXPECT_EQ (ext.field_feature (), pb::EnumFeature::VALUE15);
345+ EXPECT_EQ (ext.file_feature (), pb::EnumFeature::VALUE6);
346+ EXPECT_EQ (ext.source_feature (), pb::EnumFeature::VALUE5);
347+ }
348+
349+ TEST_F (CodeGeneratorTest,
350+ GetResolvedSourceFeatureExtensionDefaultsFromFeatureSetExtension) {
351+ // Make sure feature set defaults are empty in the pool.
352+ TestGenerator generator;
353+ generator.set_feature_extensions ({});
354+ ASSERT_OK (pool_.SetFeatureSetDefaults (*generator.BuildFeatureSetDefaults ()));
355+
356+ ASSERT_THAT (BuildFile (DescriptorProto::descriptor ()->file ()), NotNull ());
357+ ASSERT_THAT (BuildFile (pb::TestMessage::descriptor ()->file ()), NotNull ());
358+ auto file = BuildFile (R"schema(
359+ edition = "2023";
360+ package proto2_unittest;
361+
362+ import "google/protobuf/unittest_features.proto";
363+
364+ option features.(pb.test).file_feature = VALUE6;
365+ option features.(pb.test).source_feature = VALUE5;
366+ )schema" );
367+ ASSERT_THAT (file, NotNull ());
368+
369+ const pb::TestFeatures& ext1 =
370+ TestGenerator::GetResolvedSourceFeatureExtension (*file, pb::test);
371+ const pb::TestFeatures& ext2 =
372+ TestGenerator::GetResolvedSourceFeatures (*file).GetExtension (pb::test);
373+
374+ // No defaults were added to the pool, but they should be still present in the
375+ // result. On the other hand, features that are explicitly set should be also
376+ // present.
377+ EXPECT_EQ (ext1.enum_feature (), pb::EnumFeature::VALUE1);
378+ EXPECT_EQ (ext1.field_feature (), pb::EnumFeature::VALUE1);
379+ EXPECT_EQ (ext1.file_feature (), pb::EnumFeature::VALUE6);
380+ EXPECT_EQ (ext1.source_feature (), pb::EnumFeature::VALUE5);
381+ EXPECT_EQ (ext2.enum_feature (), pb::EnumFeature::TEST_ENUM_FEATURE_UNKNOWN);
382+ EXPECT_EQ (ext2.field_feature (), pb::EnumFeature::TEST_ENUM_FEATURE_UNKNOWN);
383+ EXPECT_EQ (ext2.file_feature (), pb::EnumFeature::VALUE6);
384+ EXPECT_EQ (ext2.source_feature (), pb::EnumFeature::VALUE5);
385+ }
386+
249387// TODO: Use the gtest versions once that's available in OSS.
250388MATCHER_P (HasError, msg_matcher, " " ) {
251389 return arg.status ().code () == absl::StatusCode::kFailedPrecondition &&
0 commit comments