Disallow all feature types except enum and boolean.

Features are designed as temporary migration tools, and any unbounded type leaves the system open to unexpected use.  Features should have a fixed set of values, with well defined behaviors.

PiperOrigin-RevId: 617933544
pull/16252/head
Mike Kruskal 10 months ago committed by Copybara-Service
parent 7793c72eab
commit 0e167e796f
  1. 6
      csharp/src/Google.Protobuf.Test/Reflection/FeatureInheritanceTest.cs
  2. 4
      java/core/src/test/java/com/google/protobuf/DescriptorsTest.java
  3. 6
      python/google/protobuf/internal/descriptor_pool_test.py
  4. 6
      python/google/protobuf/internal/descriptor_test.py
  5. 56
      src/google/protobuf/compiler/code_generator_unittest.cc
  6. 30
      src/google/protobuf/compiler/command_line_interface_unittest.cc
  7. 4
      src/google/protobuf/compiler/mock_code_generator.cc
  8. 375
      src/google/protobuf/descriptor_unittest.cc
  9. 64
      src/google/protobuf/editions/defaults_test.cc
  10. 5
      src/google/protobuf/feature_resolver.cc
  11. 358
      src/google/protobuf/feature_resolver_test.cc
  12. 131
      src/google/protobuf/unittest_features.proto

@ -258,7 +258,8 @@ public class FeatureInheritanceTest
}
private static int GetTestFeature(FeatureSetDescriptor features) =>
(features.Proto.GetExtension(UnittestFeaturesExtensions.Test) ?? new TestFeatures()).IntMultipleFeature;
(int)(features.Proto.GetExtension(UnittestFeaturesExtensions.Test) ?? new TestFeatures())
.MultipleFeature;
private static void SetTestFeature(FileDescriptorProto proto, int value)
{
@ -317,7 +318,8 @@ public class FeatureInheritanceTest
}
private static void SetTestFeature(FeatureSet features, int value) =>
features.SetExtension(UnittestFeaturesExtensions.Test, new TestFeatures { IntMultipleFeature = value });
features.SetExtension(UnittestFeaturesExtensions.Test,
new TestFeatures { MultipleFeature = (Pb.EnumFeature)value });
private static FileDescriptor Build(FileDescriptorProto fileProto) =>
FileDescriptor.BuildFromByteStrings(new[] { fileProto.ToByteString() }, new ExtensionRegistry { UnittestFeaturesExtensions.Test })[0];

@ -1315,12 +1315,12 @@ public class DescriptorsTest {
features.setExtension(
UnittestFeatures.test,
features.getExtension(UnittestFeatures.test).toBuilder()
.setIntMultipleFeature(value)
.setMultipleFeature(UnittestFeatures.EnumFeature.forNumber(value))
.build());
}
int getTestFeature(DescriptorProtos.FeatureSet features) {
return features.getExtension(UnittestFeatures.test).getIntMultipleFeature();
return features.getExtension(UnittestFeatures.test).getMultipleFeature().getNumber();
}
FileDescriptor buildFrom(FileDescriptorProto fileProto) throws Exception {

@ -1145,13 +1145,17 @@ class FeatureSetDefaults(unittest.TestCase):
)
defaults.defaults[0].features.Extensions[
unittest_features_pb2.test
].int_file_feature = 9
].file_feature = unittest_features_pb2.VALUE9
pool.SetFeatureSetDefaults(defaults)
file_desc = descriptor_pb2.FileDescriptorProto(name='some/file.proto')
file = pool.AddSerializedFile(file_desc.SerializeToString())
self.assertTrue(
file._GetFeatures().HasExtension(unittest_features_pb2.test)
)
self.assertEqual(
file._GetFeatures().Extensions[unittest_features_pb2.test].file_feature,
unittest_features_pb2.VALUE9,
)
def testInvalidType(self):
pool = descriptor_pool.DescriptorPool()

@ -1372,14 +1372,14 @@ def GetTestFeature(desc):
return (
desc._GetFeatures()
.Extensions[unittest_features_pb2.test]
.int_multiple_feature
.multiple_feature
)
def SetTestFeature(proto, value):
proto.options.features.Extensions[
unittest_features_pb2.test
].int_multiple_feature = value
].multiple_feature = value
@testing_refleaks.TestCase
@ -1462,7 +1462,7 @@ class FeatureInheritanceTest(unittest.TestCase):
)
defaults.defaults[0].features.Extensions[
unittest_features_pb2.test
].int_multiple_feature = 1
].multiple_feature = 1
ret.pool.SetFeatureSetDefaults(defaults)
ret.file = ret.pool.AddSerializedFile(self.file_proto.SerializeToString())

@ -120,15 +120,15 @@ TEST_F(CodeGeneratorTest, GetUnresolvedSourceFeaturesRoot) {
option features.field_presence = EXPLICIT; // 2023 default
option features.enum_type = CLOSED; // override
option features.(pb.test).int_file_feature = 8;
option features.(pb.test).string_source_feature = "file";
option features.(pb.test).file_feature = VALUE5;
option features.(pb.test).source_feature = VALUE6;
)schema");
ASSERT_THAT(file, NotNull());
EXPECT_THAT(TestGenerator::GetUnresolvedSourceFeatures(*file, pb::test),
google::protobuf::EqualsProto(R"pb(
int_file_feature: 8
string_source_feature: "file"
file_feature: VALUE5
source_feature: VALUE6
)pb"));
}
@ -142,15 +142,15 @@ TEST_F(CodeGeneratorTest, GetUnresolvedSourceFeaturesInherited) {
import "google/protobuf/unittest_features.proto";
option features.enum_type = OPEN;
option features.(pb.test).int_file_feature = 6;
option features.(pb.test).file_feature = VALUE4;
message EditionsMessage {
option features.(pb.test).int_message_feature = 7;
option features.(pb.test).int_multiple_feature = 8;
option features.(pb.test).message_feature = VALUE5;
option features.(pb.test).multiple_feature = VALUE6;
string field = 1 [
features.field_presence = EXPLICIT,
features.(pb.test).int_multiple_feature = 9,
features.(pb.test).string_source_feature = "field"
features.(pb.test).multiple_feature = VALUE3,
features.(pb.test).source_feature = VALUE2
];
}
)schema");
@ -162,8 +162,8 @@ TEST_F(CodeGeneratorTest, GetUnresolvedSourceFeaturesInherited) {
EXPECT_THAT(TestGenerator::GetUnresolvedSourceFeatures(*field, pb::test),
google::protobuf::EqualsProto(R"pb(
int_multiple_feature: 9
string_source_feature: "field"
multiple_feature: VALUE3
source_feature: VALUE2
)pb"));
}
@ -182,8 +182,8 @@ TEST_F(CodeGeneratorTest, GetResolvedSourceFeaturesRoot) {
option features.field_presence = EXPLICIT; // 2023 default
option features.enum_type = CLOSED; // override
option features.(pb.test).int_file_feature = 8;
option features.(pb.test).string_source_feature = "file";
option features.(pb.test).file_feature = VALUE6;
option features.(pb.test).source_feature = VALUE5;
)schema");
ASSERT_THAT(file, NotNull());
@ -195,9 +195,9 @@ TEST_F(CodeGeneratorTest, GetResolvedSourceFeaturesRoot) {
EXPECT_EQ(features.field_presence(), FeatureSet::EXPLICIT);
EXPECT_EQ(features.enum_type(), FeatureSet::CLOSED);
EXPECT_TRUE(ext.has_int_message_feature());
EXPECT_EQ(ext.int_file_feature(), 8);
EXPECT_EQ(ext.string_source_feature(), "file");
EXPECT_EQ(ext.file_feature(), pb::EnumFeature::VALUE6);
EXPECT_EQ(ext.source_feature(), pb::EnumFeature::VALUE5);
EXPECT_EQ(ext.field_feature(), pb::EnumFeature::VALUE1);
}
TEST_F(CodeGeneratorTest, GetResolvedSourceFeaturesInherited) {
@ -214,17 +214,17 @@ TEST_F(CodeGeneratorTest, GetResolvedSourceFeaturesInherited) {
import "google/protobuf/unittest_features.proto";
option features.enum_type = CLOSED;
option features.(pb.test).int_source_feature = 5;
option features.(pb.test).int_file_feature = 6;
option features.(pb.test).source_feature = VALUE5;
option features.(pb.test).file_feature = VALUE6;
message EditionsMessage {
option features.(pb.test).int_message_feature = 7;
option features.(pb.test).int_multiple_feature = 8;
option features.(pb.test).string_source_feature = "message";
option features.(pb.test).message_feature = VALUE4;
option features.(pb.test).multiple_feature = VALUE3;
option features.(pb.test).source_feature2 = VALUE2;
string field = 1 [
features.field_presence = IMPLICIT,
features.(pb.test).int_multiple_feature = 9,
features.(pb.test).string_source_feature = "field"
features.(pb.test).multiple_feature = VALUE5,
features.(pb.test).source_feature2 = VALUE3
];
}
)schema");
@ -239,11 +239,11 @@ TEST_F(CodeGeneratorTest, GetResolvedSourceFeaturesInherited) {
EXPECT_EQ(features.enum_type(), FeatureSet::CLOSED);
EXPECT_EQ(features.field_presence(), FeatureSet::IMPLICIT);
EXPECT_EQ(ext.int_message_feature(), 7);
EXPECT_EQ(ext.int_file_feature(), 6);
EXPECT_EQ(ext.int_multiple_feature(), 9);
EXPECT_EQ(ext.int_source_feature(), 5);
EXPECT_EQ(ext.string_source_feature(), "field");
EXPECT_EQ(ext.message_feature(), pb::EnumFeature::VALUE4);
EXPECT_EQ(ext.file_feature(), pb::EnumFeature::VALUE6);
EXPECT_EQ(ext.multiple_feature(), pb::EnumFeature::VALUE5);
EXPECT_EQ(ext.source_feature(), pb::EnumFeature::VALUE5);
EXPECT_EQ(ext.source_feature2(), pb::EnumFeature::VALUE3);
}
// TODO: Use the gtest versions once that's available in OSS.

@ -1713,8 +1713,8 @@ TEST_F(CommandLineInterfaceTest, Plugin_SourceFeatures) {
package foo;
message Foo {
int32 b = 1 [
features.(pb.test).int_field_feature = 99,
features.(pb.test).int_source_feature = 87
features.(pb.test).field_feature = VALUE6,
features.(pb.test).source_feature = VALUE5
];
}
)schema");
@ -1744,7 +1744,7 @@ TEST_F(CommandLineInterfaceTest, Plugin_SourceFeatures) {
const FeatureSet& features =
request.proto_file(2).message_type(0).field(0).options().features();
EXPECT_THAT(features,
EqualsProto(R"pb([pb.test] { int_field_feature: 99 })pb"));
EqualsProto(R"pb([pb.test] { field_feature: VALUE6 })pb"));
}
{
@ -1755,8 +1755,8 @@ TEST_F(CommandLineInterfaceTest, Plugin_SourceFeatures) {
.options()
.features();
EXPECT_THAT(features, EqualsProto(R"pb([pb.test] {
int_field_feature: 99
int_source_feature: 87
field_feature: VALUE6
source_feature: VALUE5
})pb"));
}
}
@ -1965,20 +1965,20 @@ TEST_F(CommandLineInterfaceTest, EditionDefaultsWithExtension) {
EXPECT_EQ(defaults.defaults(3).edition(), EDITION_99997_TEST_ONLY);
EXPECT_EQ(defaults.defaults(4).edition(), EDITION_99998_TEST_ONLY);
EXPECT_EQ(
defaults.defaults(0).features().GetExtension(pb::test).int_file_feature(),
-2);
defaults.defaults(0).features().GetExtension(pb::test).file_feature(),
pb::EnumFeature::VALUE1);
EXPECT_EQ(
defaults.defaults(1).features().GetExtension(pb::test).int_file_feature(),
-3);
defaults.defaults(1).features().GetExtension(pb::test).file_feature(),
pb::EnumFeature::VALUE2);
EXPECT_EQ(
defaults.defaults(2).features().GetExtension(pb::test).int_file_feature(),
1);
defaults.defaults(2).features().GetExtension(pb::test).file_feature(),
pb::EnumFeature::VALUE3);
EXPECT_EQ(
defaults.defaults(3).features().GetExtension(pb::test).int_file_feature(),
2);
defaults.defaults(3).features().GetExtension(pb::test).file_feature(),
pb::EnumFeature::VALUE4);
EXPECT_EQ(
defaults.defaults(4).features().GetExtension(pb::test).int_file_feature(),
3);
defaults.defaults(4).features().GetExtension(pb::test).file_feature(),
pb::EnumFeature::VALUE5);
}
#ifndef _WIN32

@ -234,9 +234,9 @@ bool MockCodeGenerator::Generate(const FileDescriptor* file,
const FeatureSet& features = GetResolvedSourceFeatures(descriptor);
ABSL_CHECK(features.HasExtension(pb::test))
<< "Test features were not resolved properly";
ABSL_CHECK(features.GetExtension(pb::test).has_int_file_feature())
ABSL_CHECK(features.GetExtension(pb::test).has_file_feature())
<< "Test features were not resolved properly";
ABSL_CHECK(features.GetExtension(pb::test).has_int_source_feature())
ABSL_CHECK(features.GetExtension(pb::test).has_source_feature())
<< "Test features were not resolved properly";
});
}

File diff suppressed because it is too large Load Diff

@ -52,11 +52,9 @@ TEST(DefaultsTest, Check2023) {
EXPECT_EQ(defaults->defaults()[2].edition(), EDITION_2023);
EXPECT_EQ(defaults->defaults()[2].features().field_presence(),
FeatureSet::EXPLICIT);
EXPECT_EQ(defaults->defaults()[2]
.features()
.GetExtension(pb::test)
.int_file_feature(),
1);
EXPECT_EQ(
defaults->defaults()[2].features().GetExtension(pb::test).file_feature(),
pb::VALUE3);
}
TEST(DefaultsTest, CheckFuture) {
@ -71,19 +69,15 @@ TEST(DefaultsTest, CheckFuture) {
EXPECT_EQ(defaults->defaults()[2].edition(), EDITION_2023);
EXPECT_EQ(defaults->defaults()[2].features().field_presence(),
FeatureSet::EXPLICIT);
EXPECT_EQ(defaults->defaults()[2]
.features()
.GetExtension(pb::test)
.int_file_feature(),
1);
EXPECT_EQ(
defaults->defaults()[2].features().GetExtension(pb::test).file_feature(),
pb::VALUE3);
EXPECT_EQ(defaults->defaults()[3].edition(), EDITION_99997_TEST_ONLY);
EXPECT_EQ(defaults->defaults()[3].features().field_presence(),
FeatureSet::EXPLICIT);
EXPECT_EQ(defaults->defaults()[3]
.features()
.GetExtension(pb::test)
.int_file_feature(),
2);
EXPECT_EQ(
defaults->defaults()[3].features().GetExtension(pb::test).file_feature(),
pb::VALUE4);
}
TEST(DefaultsTest, CheckFarFuture) {
@ -98,27 +92,21 @@ TEST(DefaultsTest, CheckFarFuture) {
EXPECT_EQ(defaults->defaults()[2].edition(), EDITION_2023);
EXPECT_EQ(defaults->defaults()[2].features().field_presence(),
FeatureSet::EXPLICIT);
EXPECT_EQ(defaults->defaults()[2]
.features()
.GetExtension(pb::test)
.int_file_feature(),
1);
EXPECT_EQ(
defaults->defaults()[2].features().GetExtension(pb::test).file_feature(),
pb::VALUE3);
EXPECT_EQ(defaults->defaults()[3].edition(), EDITION_99997_TEST_ONLY);
EXPECT_EQ(defaults->defaults()[3].features().field_presence(),
FeatureSet::EXPLICIT);
EXPECT_EQ(defaults->defaults()[3]
.features()
.GetExtension(pb::test)
.int_file_feature(),
2);
EXPECT_EQ(
defaults->defaults()[3].features().GetExtension(pb::test).file_feature(),
pb::VALUE4);
EXPECT_EQ(defaults->defaults()[4].edition(), EDITION_99998_TEST_ONLY);
EXPECT_EQ(defaults->defaults()[4].features().field_presence(),
FeatureSet::EXPLICIT);
EXPECT_EQ(defaults->defaults()[4]
.features()
.GetExtension(pb::test)
.int_file_feature(),
3);
EXPECT_EQ(
defaults->defaults()[4].features().GetExtension(pb::test).file_feature(),
pb::VALUE5);
}
TEST(DefaultsTest, Embedded) {
@ -135,11 +123,9 @@ TEST(DefaultsTest, Embedded) {
EXPECT_EQ(defaults.defaults()[2].edition(), EDITION_2023);
EXPECT_EQ(defaults.defaults()[2].features().field_presence(),
FeatureSet::EXPLICIT);
EXPECT_EQ(defaults.defaults()[2]
.features()
.GetExtension(pb::test)
.int_file_feature(),
1);
EXPECT_EQ(
defaults.defaults()[2].features().GetExtension(pb::test).file_feature(),
pb::VALUE3);
}
TEST(DefaultsTest, EmbeddedBase64) {
@ -159,11 +145,9 @@ TEST(DefaultsTest, EmbeddedBase64) {
EXPECT_EQ(defaults.defaults()[2].edition(), EDITION_2023);
EXPECT_EQ(defaults.defaults()[2].features().field_presence(),
FeatureSet::EXPLICIT);
EXPECT_EQ(defaults.defaults()[2]
.features()
.GetExtension(pb::test)
.int_file_feature(),
1);
EXPECT_EQ(
defaults.defaults()[2].features().GetExtension(pb::test).file_feature(),
pb::VALUE3);
}
} // namespace

@ -69,6 +69,11 @@ absl::Status ValidateDescriptor(const Descriptor& descriptor) {
return Error("Feature field ", field.full_name(),
" is an unsupported repeated field.");
}
if (field.type() != FieldDescriptor::TYPE_ENUM &&
field.type() != FieldDescriptor::TYPE_BOOL) {
return Error("Feature field ", field.full_name(),
" is not an enum or boolean.");
}
if (field.options().targets().empty()) {
return Error("Feature field ", field.full_name(),
" has no target specified.");

@ -19,6 +19,7 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "google/protobuf/compiler/parser.h"
#include "google/protobuf/cpp_features.pb.h"
#include "google/protobuf/descriptor.h"
@ -127,21 +128,16 @@ TEST(FeatureResolverTest, DefaultsTest2023) {
EXPECT_EQ(merged->message_encoding(), FeatureSet::LENGTH_PREFIXED);
const pb::TestFeatures& ext = merged->GetExtension(pb::test);
EXPECT_EQ(ext.int_file_feature(), 1);
EXPECT_EQ(ext.int_extension_range_feature(), 1);
EXPECT_EQ(ext.int_message_feature(), 1);
EXPECT_EQ(ext.int_field_feature(), 1);
EXPECT_EQ(ext.int_oneof_feature(), 1);
EXPECT_EQ(ext.int_enum_feature(), 1);
EXPECT_EQ(ext.int_enum_entry_feature(), 1);
EXPECT_EQ(ext.int_service_feature(), 1);
EXPECT_EQ(ext.int_method_feature(), 1);
EXPECT_EQ(ext.bool_field_feature(), false);
EXPECT_FLOAT_EQ(ext.float_field_feature(), 1.1);
EXPECT_THAT(ext.message_field_feature(),
EqualsProto("bool_field: true int_field: 1 float_field: 1.5 "
"string_field: '2023'"));
EXPECT_EQ(ext.enum_field_feature(), pb::TestFeatures::ENUM_VALUE1);
EXPECT_EQ(ext.file_feature(), pb::VALUE3);
EXPECT_EQ(ext.extension_range_feature(), pb::VALUE1);
EXPECT_EQ(ext.message_feature(), pb::VALUE1);
EXPECT_EQ(ext.field_feature(), pb::VALUE1);
EXPECT_EQ(ext.oneof_feature(), pb::VALUE1);
EXPECT_EQ(ext.enum_feature(), pb::VALUE1);
EXPECT_EQ(ext.enum_entry_feature(), pb::VALUE1);
EXPECT_EQ(ext.service_feature(), pb::VALUE1);
EXPECT_EQ(ext.method_feature(), pb::VALUE1);
EXPECT_FALSE(ext.bool_field_feature());
}
TEST(FeatureResolverTest, DefaultsTestMessageExtension) {
@ -157,21 +153,16 @@ TEST(FeatureResolverTest, DefaultsTestMessageExtension) {
const pb::TestFeatures& ext =
merged->GetExtension(pb::TestMessage::test_message);
EXPECT_EQ(ext.int_file_feature(), 1);
EXPECT_EQ(ext.int_extension_range_feature(), 1);
EXPECT_EQ(ext.int_message_feature(), 1);
EXPECT_EQ(ext.int_field_feature(), 1);
EXPECT_EQ(ext.int_oneof_feature(), 1);
EXPECT_EQ(ext.int_enum_feature(), 1);
EXPECT_EQ(ext.int_enum_entry_feature(), 1);
EXPECT_EQ(ext.int_service_feature(), 1);
EXPECT_EQ(ext.int_method_feature(), 1);
EXPECT_EQ(ext.bool_field_feature(), false);
EXPECT_FLOAT_EQ(ext.float_field_feature(), 1.1);
EXPECT_THAT(ext.message_field_feature(),
EqualsProto("bool_field: true int_field: 1 float_field: 1.5 "
"string_field: '2023'"));
EXPECT_EQ(ext.enum_field_feature(), pb::TestFeatures::ENUM_VALUE1);
EXPECT_EQ(ext.file_feature(), pb::VALUE3);
EXPECT_EQ(ext.extension_range_feature(), pb::VALUE1);
EXPECT_EQ(ext.message_feature(), pb::VALUE1);
EXPECT_EQ(ext.field_feature(), pb::VALUE1);
EXPECT_EQ(ext.oneof_feature(), pb::VALUE1);
EXPECT_EQ(ext.enum_feature(), pb::VALUE1);
EXPECT_EQ(ext.enum_entry_feature(), pb::VALUE1);
EXPECT_EQ(ext.service_feature(), pb::VALUE1);
EXPECT_EQ(ext.method_feature(), pb::VALUE1);
EXPECT_FALSE(ext.bool_field_feature());
}
TEST(FeatureResolverTest, DefaultsTestNestedExtension) {
@ -187,21 +178,16 @@ TEST(FeatureResolverTest, DefaultsTestNestedExtension) {
const pb::TestFeatures& ext =
merged->GetExtension(pb::TestMessage::Nested::test_nested);
EXPECT_EQ(ext.int_file_feature(), 1);
EXPECT_EQ(ext.int_extension_range_feature(), 1);
EXPECT_EQ(ext.int_message_feature(), 1);
EXPECT_EQ(ext.int_field_feature(), 1);
EXPECT_EQ(ext.int_oneof_feature(), 1);
EXPECT_EQ(ext.int_enum_feature(), 1);
EXPECT_EQ(ext.int_enum_entry_feature(), 1);
EXPECT_EQ(ext.int_service_feature(), 1);
EXPECT_EQ(ext.int_method_feature(), 1);
EXPECT_EQ(ext.bool_field_feature(), false);
EXPECT_FLOAT_EQ(ext.float_field_feature(), 1.1);
EXPECT_THAT(ext.message_field_feature(),
EqualsProto("bool_field: true int_field: 1 float_field: 1.5 "
"string_field: '2023'"));
EXPECT_EQ(ext.enum_field_feature(), pb::TestFeatures::ENUM_VALUE1);
EXPECT_EQ(ext.file_feature(), pb::VALUE3);
EXPECT_EQ(ext.extension_range_feature(), pb::VALUE1);
EXPECT_EQ(ext.message_feature(), pb::VALUE1);
EXPECT_EQ(ext.field_feature(), pb::VALUE1);
EXPECT_EQ(ext.oneof_feature(), pb::VALUE1);
EXPECT_EQ(ext.enum_feature(), pb::VALUE1);
EXPECT_EQ(ext.enum_entry_feature(), pb::VALUE1);
EXPECT_EQ(ext.service_feature(), pb::VALUE1);
EXPECT_EQ(ext.method_feature(), pb::VALUE1);
EXPECT_FALSE(ext.bool_field_feature());
}
TEST(FeatureResolverTest, DefaultsGeneratedPoolCustom) {
@ -222,7 +208,7 @@ TEST(FeatureResolverTest, DefaultsGeneratedPoolCustom) {
EXPECT_EQ(merged.field_presence(), FeatureSet::EXPLICIT);
EXPECT_TRUE(merged.HasExtension(pb::test));
EXPECT_EQ(merged.GetExtension(pb::test).int_file_feature(), 1);
EXPECT_EQ(merged.GetExtension(pb::test).file_feature(), pb::VALUE3);
EXPECT_FALSE(merged.HasExtension(pb::cpp));
}
@ -245,11 +231,8 @@ TEST(FeatureResolverTest, DefaultsFarFuture) {
ASSERT_OK(merged);
pb::TestFeatures ext = merged->GetExtension(pb::test);
EXPECT_EQ(ext.int_file_feature(), 3);
EXPECT_THAT(ext.message_field_feature(),
EqualsProto("bool_field: true int_field: 2 float_field: 1.5 "
"string_field: '2024'"));
EXPECT_EQ(ext.enum_field_feature(), pb::TestFeatures::ENUM_VALUE3);
EXPECT_EQ(ext.file_feature(), pb::VALUE5);
EXPECT_TRUE(ext.bool_field_feature());
}
TEST(FeatureResolverTest, DefaultsMiddleEdition) {
@ -258,43 +241,8 @@ TEST(FeatureResolverTest, DefaultsMiddleEdition) {
ASSERT_OK(merged);
pb::TestFeatures ext = merged->GetExtension(pb::test);
EXPECT_EQ(ext.int_file_feature(), 2);
EXPECT_EQ(ext.enum_field_feature(), pb::TestFeatures::ENUM_VALUE2);
}
TEST(FeatureResolverTest, DefaultsMessageMerge) {
{
absl::StatusOr<FeatureSet> merged = GetDefaults(EDITION_2023, pb::test);
ASSERT_OK(merged);
pb::TestFeatures ext = merged->GetExtension(pb::test);
EXPECT_THAT(ext.message_field_feature(),
EqualsProto(R"pb(bool_field: true
int_field: 1
float_field: 1.5
string_field: '2023')pb"));
}
{
absl::StatusOr<FeatureSet> merged =
GetDefaults(EDITION_99997_TEST_ONLY, pb::test);
ASSERT_OK(merged);
pb::TestFeatures ext = merged->GetExtension(pb::test);
EXPECT_THAT(ext.message_field_feature(),
EqualsProto(R"pb(bool_field: true
int_field: 2
float_field: 1.5
string_field: '2023')pb"));
}
{
absl::StatusOr<FeatureSet> merged =
GetDefaults(EDITION_99998_TEST_ONLY, pb::test);
ASSERT_OK(merged);
pb::TestFeatures ext = merged->GetExtension(pb::test);
EXPECT_THAT(ext.message_field_feature(),
EqualsProto(R"pb(bool_field: true
int_field: 2
float_field: 1.5
string_field: '2024')pb"));
}
EXPECT_EQ(ext.file_feature(), pb::VALUE4);
EXPECT_TRUE(ext.bool_field_feature());
}
TEST(FeatureResolverTest, CreateFromUnsortedDefaults) {
@ -412,11 +360,7 @@ TEST(FeatureResolverTest, MergeFeaturesChildOverrideComplex) {
FeatureSet child = ParseTextOrDie(R"pb(
field_presence: IMPLICIT
repeated_field_encoding: EXPANDED
[pb.test] {
int_field_feature: 5
enum_field_feature: ENUM_VALUE4
message_field_feature { int_field: 10 }
}
[pb.test] { field_feature: VALUE5 }
)pb");
absl::StatusOr<FeatureSet> merged =
resolver->MergeFeatures(FeatureSet(), child);
@ -428,13 +372,8 @@ TEST(FeatureResolverTest, MergeFeaturesChildOverrideComplex) {
EXPECT_EQ(merged->message_encoding(), FeatureSet::LENGTH_PREFIXED);
pb::TestFeatures ext = merged->GetExtension(pb::test);
EXPECT_EQ(ext.int_file_feature(), 1);
EXPECT_EQ(ext.int_field_feature(), 5);
EXPECT_FLOAT_EQ(ext.float_field_feature(), 1.1);
EXPECT_THAT(ext.message_field_feature(),
EqualsProto("bool_field: true int_field: 10 float_field: 1.5 "
"string_field: '2023'"));
EXPECT_EQ(ext.enum_field_feature(), pb::TestFeatures::ENUM_VALUE4);
EXPECT_EQ(ext.file_feature(), pb::VALUE3);
EXPECT_EQ(ext.field_feature(), pb::VALUE5);
}
TEST(FeatureResolverTest, MergeFeaturesParentOverrides) {
@ -444,18 +383,11 @@ TEST(FeatureResolverTest, MergeFeaturesParentOverrides) {
FeatureSet parent = ParseTextOrDie(R"pb(
field_presence: IMPLICIT
repeated_field_encoding: EXPANDED
[pb.test] {
int_field_feature: 5
enum_field_feature: ENUM_VALUE4
message_field_feature { int_field: 10 string_field: "parent" }
}
[pb.test] { message_feature: VALUE2 field_feature: VALUE5 }
)pb");
FeatureSet child = ParseTextOrDie(R"pb(
repeated_field_encoding: PACKED
[pb.test] {
int_field_feature: 9
message_field_feature { bool_field: false int_field: 9 }
}
[pb.test] { field_feature: VALUE7 }
)pb");
absl::StatusOr<FeatureSet> merged = resolver->MergeFeatures(parent, child);
ASSERT_OK(merged);
@ -466,21 +398,16 @@ TEST(FeatureResolverTest, MergeFeaturesParentOverrides) {
EXPECT_EQ(merged->message_encoding(), FeatureSet::LENGTH_PREFIXED);
pb::TestFeatures ext = merged->GetExtension(pb::test);
EXPECT_EQ(ext.int_file_feature(), 1);
EXPECT_EQ(ext.int_extension_range_feature(), 1);
EXPECT_EQ(ext.int_message_feature(), 1);
EXPECT_EQ(ext.int_field_feature(), 9);
EXPECT_EQ(ext.int_oneof_feature(), 1);
EXPECT_EQ(ext.int_enum_feature(), 1);
EXPECT_EQ(ext.int_enum_entry_feature(), 1);
EXPECT_EQ(ext.int_service_feature(), 1);
EXPECT_EQ(ext.int_method_feature(), 1);
EXPECT_EQ(ext.bool_field_feature(), false);
EXPECT_FLOAT_EQ(ext.float_field_feature(), 1.1);
EXPECT_THAT(ext.message_field_feature(),
EqualsProto("bool_field: false int_field: 9 float_field: 1.5 "
"string_field: 'parent'"));
EXPECT_EQ(ext.enum_field_feature(), pb::TestFeatures::ENUM_VALUE4);
EXPECT_EQ(ext.file_feature(), pb::VALUE3);
EXPECT_EQ(ext.extension_range_feature(), pb::VALUE1);
EXPECT_EQ(ext.message_feature(), pb::VALUE2);
EXPECT_EQ(ext.field_feature(), pb::VALUE7);
EXPECT_EQ(ext.oneof_feature(), pb::VALUE1);
EXPECT_EQ(ext.enum_feature(), pb::VALUE1);
EXPECT_EQ(ext.enum_entry_feature(), pb::VALUE1);
EXPECT_EQ(ext.service_feature(), pb::VALUE1);
EXPECT_EQ(ext.method_feature(), pb::VALUE1);
EXPECT_FALSE(ext.bool_field_feature());
}
TEST(FeatureResolverTest, MergeFeaturesUnknownEnumFeature) {
@ -510,13 +437,13 @@ TEST(FeatureResolverTest, MergeFeaturesExtensionEnumUnknown) {
SetupFeatureResolver(EDITION_2023, pb::test);
ASSERT_OK(resolver);
FeatureSet child = ParseTextOrDie(R"pb(
[pb.test] { enum_field_feature: TEST_ENUM_FEATURE_UNKNOWN }
[pb.test] { field_feature: TEST_ENUM_FEATURE_UNKNOWN }
)pb");
absl::StatusOr<FeatureSet> merged =
resolver->MergeFeatures(FeatureSet(), child);
ASSERT_OK(merged);
EXPECT_EQ(merged->GetExtension(pb::test).enum_field_feature(),
pb::TestFeatures::TEST_ENUM_FEATURE_UNKNOWN);
EXPECT_EQ(merged->GetExtension(pb::test).field_feature(),
pb::TEST_ENUM_FEATURE_UNKNOWN);
}
TEST(FeatureResolverTest, MergeFeaturesDistantPast) {
@ -737,8 +664,8 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithMissingTarget) {
optional Foo bar = 9999;
}
message Foo {
optional int32 int_field = 1 [
edition_defaults = { edition: EDITION_2023, value: "1" }
optional bool bool_field = 1 [
edition_defaults = { edition: EDITION_2023, value: "true" }
];
}
)schema");
@ -747,107 +674,10 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidWithMissingTarget) {
const FieldDescriptor* ext = file->extension(0);
EXPECT_THAT(FeatureResolver::CompileDefaults(feature_set_, {ext},
EDITION_2023, EDITION_2023),
HasError(AllOf(HasSubstr("test.Foo.int_field"),
HasError(AllOf(HasSubstr("test.Foo.bool_field"),
HasSubstr("no target specified"))));
}
TEST_F(FeatureResolverPoolTest,
CompileDefaultsInvalidDefaultsMessageParsingError) {
const FileDescriptor* file = ParseSchema(R"schema(
syntax = "proto2";
package test;
import "google/protobuf/descriptor.proto";
extend google.protobuf.FeatureSet {
optional Foo bar = 9999;
}
message Foo {
message MessageFeature {
optional int32 int_field = 1;
}
optional MessageFeature message_field_feature = 12 [
targets = TARGET_TYPE_FIELD,
edition_defaults = { edition: EDITION_PROTO2, value: "9987" }
];
}
)schema");
ASSERT_NE(file, nullptr);
const FieldDescriptor* ext = file->extension(0);
EXPECT_THAT(
FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023,
EDITION_2023),
HasError(AllOf(HasSubstr("in edition_defaults"), HasSubstr("9987"))));
}
TEST_F(FeatureResolverPoolTest,
CompileDefaultsInvalidDefaultsMessageParsingErrorMerged) {
const FileDescriptor* file = ParseSchema(R"schema(
syntax = "proto2";
package test;
import "google/protobuf/descriptor.proto";
extend google.protobuf.FeatureSet {
optional Foo bar = 9999;
}
message Foo {
message MessageFeature {
optional int32 int_field = 1;
}
optional MessageFeature message_field_feature = 12 [
targets = TARGET_TYPE_FIELD,
edition_defaults = { edition: EDITION_99998_TEST_ONLY, value: "int_field: 2" },
edition_defaults = { edition: EDITION_99997_TEST_ONLY, value: "int_field: 1" },
edition_defaults = { edition: EDITION_PROTO2, value: "" },
edition_defaults = { edition: EDITION_2023, value: "9987" }
];
}
)schema");
ASSERT_NE(file, nullptr);
const FieldDescriptor* ext = file->extension(0);
EXPECT_THAT(
FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_2023,
EDITION_99998_TEST_ONLY),
HasError(AllOf(HasSubstr("in edition_defaults"), HasSubstr("9987"))));
}
TEST_F(FeatureResolverPoolTest,
CompileDefaultsInvalidDefaultsMessageParsingErrorSkipped) {
const FileDescriptor* file = ParseSchema(R"schema(
syntax = "proto2";
package test;
import "google/protobuf/descriptor.proto";
extend google.protobuf.FeatureSet {
optional Foo bar = 9999;
}
message Foo {
message MessageFeature {
optional int32 int_field = 1;
}
optional MessageFeature message_field_feature = 12 [
targets = TARGET_TYPE_FIELD,
edition_defaults = { edition: EDITION_99997_TEST_ONLY, value: "int_field: 2" },
edition_defaults = { edition: EDITION_2023, value: "int_field: 1" },
edition_defaults = { edition: EDITION_99998_TEST_ONLY, value: "9987" },
edition_defaults = { edition: EDITION_PROTO2, value: "" }
];
}
)schema");
ASSERT_NE(file, nullptr);
const FieldDescriptor* ext = file->extension(0);
auto defaults = FeatureResolver::CompileDefaults(
feature_set_, {ext}, EDITION_2023, EDITION_99997_TEST_ONLY);
ASSERT_OK(defaults);
auto resolver = FeatureResolver::Create(EDITION_2023, *defaults);
ASSERT_OK(resolver);
FeatureSet parent, child;
EXPECT_OK(resolver->MergeFeatures(parent, child));
}
TEST_F(FeatureResolverPoolTest,
CompileDefaultsInvalidDefaultsScalarParsingError) {
const FileDescriptor* file = ParseSchema(R"schema(
@ -859,7 +689,7 @@ TEST_F(FeatureResolverPoolTest,
optional Foo bar = 9999;
}
message Foo {
optional int32 int_field_feature = 12 [
optional bool field_feature = 12 [
targets = TARGET_TYPE_FIELD,
edition_defaults = { edition: EDITION_PROTO2, value: "1.23" }
];
@ -885,10 +715,10 @@ TEST_F(FeatureResolverPoolTest,
optional Foo bar = 9999;
}
message Foo {
optional int32 int_field_feature = 12 [
optional bool field_feature = 12 [
targets = TARGET_TYPE_FIELD,
edition_defaults = { edition: EDITION_99997_TEST_ONLY, value: "1.5" },
edition_defaults = { edition: EDITION_PROTO2, value: "1" }
edition_defaults = { edition: EDITION_PROTO2, value: "true" }
];
}
)schema");
@ -915,9 +745,9 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidDefaultsTooEarly) {
optional Foo bar = 9999;
}
message Foo {
optional int32 int_field_feature = 12 [
optional bool field_feature = 12 [
targets = TARGET_TYPE_FIELD,
edition_defaults = { edition: EDITION_2_TEST_ONLY, value: "1" }
edition_defaults = { edition: EDITION_2_TEST_ONLY, value: "true" }
];
}
)schema");
@ -940,9 +770,9 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsMinimumTooEarly) {
optional Foo bar = 9999;
}
message Foo {
optional int32 int_field_feature = 12 [
optional bool field_feature = 12 [
targets = TARGET_TYPE_FIELD,
edition_defaults = { edition: EDITION_PROTO2, value: "1" }
edition_defaults = { edition: EDITION_PROTO2, value: "true" }
];
}
)schema");
@ -964,12 +794,18 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsMinimumCovered) {
extend google.protobuf.FeatureSet {
optional Foo bar = 9999;
}
enum Bar {
TEST_ENUM_FEATURE_UNKNOWN = 0;
VALUE1 = 1;
VALUE2 = 2;
VALUE3 = 3;
}
message Foo {
optional int32 int_file_feature = 1 [
optional Bar file_feature = 1 [
targets = TARGET_TYPE_FIELD,
edition_defaults = { edition: EDITION_99998_TEST_ONLY, value: "2" },
edition_defaults = { edition: EDITION_2023, value: "1" },
edition_defaults = { edition: EDITION_PROTO2, value: "0" }
edition_defaults = { edition: EDITION_99998_TEST_ONLY, value: "VALUE3" },
edition_defaults = { edition: EDITION_2023, value: "VALUE2" },
edition_defaults = { edition: EDITION_PROTO2, value: "VALUE1" }
];
}
)schema");
@ -992,7 +828,7 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsMinimumCovered) {
utf8_validation: NONE
message_encoding: LENGTH_PREFIXED
json_format: LEGACY_BEST_EFFORT
[pb.test] { int_file_feature: 0 }
[pb.test] { file_feature: VALUE1 }
}
}
defaults {
@ -1004,7 +840,7 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsMinimumCovered) {
utf8_validation: VERIFY
message_encoding: LENGTH_PREFIXED
json_format: ALLOW
[pb.test] { int_file_feature: 0 }
[pb.test] { file_feature: VALUE1 }
}
}
defaults {
@ -1016,7 +852,7 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsMinimumCovered) {
utf8_validation: VERIFY
message_encoding: LENGTH_PREFIXED
json_format: ALLOW
[pb.test] { int_file_feature: 1 }
[pb.test] { file_feature: VALUE2 }
}
}
defaults {
@ -1028,12 +864,50 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsMinimumCovered) {
utf8_validation: VERIFY
message_encoding: LENGTH_PREFIXED
json_format: ALLOW
[pb.test] { int_file_feature: 2 }
[pb.test] { file_feature: VALUE3 }
}
}
)pb"));
}
class FeatureUnboundedTypeTest
: public FeatureResolverPoolTest,
public ::testing::WithParamInterface<absl::string_view> {};
TEST_P(FeatureUnboundedTypeTest, CompileDefaults) {
const FileDescriptor* file = ParseSchema(absl::Substitute(R"schema(
syntax = "proto2";
package test;
import "google/protobuf/descriptor.proto";
extend google.protobuf.FeatureSet {
optional Foo bar = 9999;
}
message SomeMessage {
optional bool value = 1;
}
message Foo {
optional $0 field_feature = 12 [
targets = TARGET_TYPE_FIELD,
edition_defaults = { edition: EDITION_PROTO2, value: "1" }
];
}
)schema",
GetParam()));
ASSERT_NE(file, nullptr);
const FieldDescriptor* ext = file->extension(0);
EXPECT_THAT(
FeatureResolver::CompileDefaults(feature_set_, {ext}, EDITION_1_TEST_ONLY,
EDITION_99997_TEST_ONLY),
HasError(HasSubstr("is not an enum or boolean")));
}
INSTANTIATE_TEST_SUITE_P(FeatureUnboundedTypeTestImpl, FeatureUnboundedTypeTest,
testing::Values("int32", "int64", "uint32", "string",
"bytes", "float", "double",
"SomeMessage"));
} // namespace
} // namespace protobuf
} // namespace google

@ -26,57 +26,76 @@ message TestMessage {
}
}
enum EnumFeature {
TEST_ENUM_FEATURE_UNKNOWN = 0;
VALUE1 = 1;
VALUE2 = 2;
VALUE3 = 3;
VALUE4 = 4;
VALUE5 = 5;
VALUE6 = 6;
VALUE7 = 7;
VALUE8 = 8;
VALUE9 = 9;
VALUE10 = 10;
VALUE11 = 11;
VALUE12 = 12;
VALUE13 = 13;
VALUE14 = 14;
VALUE15 = 15;
}
message TestFeatures {
optional int32 int_file_feature = 1 [
optional EnumFeature file_feature = 1 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_FILE,
edition_defaults = { edition: EDITION_PROTO2, value: "-2" },
edition_defaults = { edition: EDITION_PROTO3, value: "-3" },
edition_defaults = { edition: EDITION_2023, value: "1" },
edition_defaults = { edition: EDITION_99997_TEST_ONLY, value: "2" },
edition_defaults = { edition: EDITION_99998_TEST_ONLY, value: "3" }
edition_defaults = { edition: EDITION_PROTO2, value: "VALUE1" },
edition_defaults = { edition: EDITION_PROTO3, value: "VALUE2" },
edition_defaults = { edition: EDITION_2023, value: "VALUE3" },
edition_defaults = { edition: EDITION_99997_TEST_ONLY, value: "VALUE4" },
edition_defaults = { edition: EDITION_99998_TEST_ONLY, value: "VALUE5" }
];
optional int32 int_extension_range_feature = 2 [
optional EnumFeature extension_range_feature = 2 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_EXTENSION_RANGE,
edition_defaults = { edition: EDITION_PROTO2, value: "1" }
edition_defaults = { edition: EDITION_PROTO2, value: "VALUE1" }
];
optional int32 int_message_feature = 3 [
optional EnumFeature message_feature = 3 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_MESSAGE,
edition_defaults = { edition: EDITION_PROTO2, value: "1" }
edition_defaults = { edition: EDITION_PROTO2, value: "VALUE1" }
];
optional int32 int_field_feature = 4 [
optional EnumFeature field_feature = 4 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_FIELD,
edition_defaults = { edition: EDITION_PROTO2, value: "1" }
edition_defaults = { edition: EDITION_PROTO2, value: "VALUE1" }
];
optional int32 int_oneof_feature = 5 [
optional EnumFeature oneof_feature = 5 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_ONEOF,
edition_defaults = { edition: EDITION_PROTO2, value: "1" }
edition_defaults = { edition: EDITION_PROTO2, value: "VALUE1" }
];
optional int32 int_enum_feature = 6 [
optional EnumFeature enum_feature = 6 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_ENUM,
edition_defaults = { edition: EDITION_PROTO2, value: "1" }
edition_defaults = { edition: EDITION_PROTO2, value: "VALUE1" }
];
optional int32 int_enum_entry_feature = 7 [
optional EnumFeature enum_entry_feature = 7 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_ENUM_ENTRY,
edition_defaults = { edition: EDITION_PROTO2, value: "1" }
edition_defaults = { edition: EDITION_PROTO2, value: "VALUE1" }
];
optional int32 int_service_feature = 8 [
optional EnumFeature service_feature = 8 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_SERVICE,
edition_defaults = { edition: EDITION_PROTO2, value: "1" }
edition_defaults = { edition: EDITION_PROTO2, value: "VALUE1" }
];
optional int32 int_method_feature = 9 [
optional EnumFeature method_feature = 9 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_METHOD,
edition_defaults = { edition: EDITION_PROTO2, value: "1" }
edition_defaults = { edition: EDITION_PROTO2, value: "VALUE1" }
];
optional int32 int_multiple_feature = 10 [
optional EnumFeature multiple_feature = 10 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_FILE,
targets = TARGET_TYPE_FIELD,
@ -87,7 +106,7 @@ message TestFeatures {
targets = TARGET_TYPE_METHOD,
targets = TARGET_TYPE_ONEOF,
targets = TARGET_TYPE_EXTENSION_RANGE,
edition_defaults = { edition: EDITION_PROTO2, value: "1" }
edition_defaults = { edition: EDITION_PROTO2, value: "VALUE1" }
];
optional bool bool_field_feature = 11 [
@ -96,64 +115,8 @@ message TestFeatures {
edition_defaults = { edition: EDITION_PROTO2, value: "false" },
edition_defaults = { edition: EDITION_99997_TEST_ONLY, value: "true" }
];
optional float float_field_feature = 12 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_FIELD,
edition_defaults = { edition: EDITION_PROTO2, value: "1.1" },
edition_defaults = { edition: EDITION_99997_TEST_ONLY, value: "1.2" }
];
message MessageFeature {
optional bool bool_field = 1;
optional int32 int_field = 2;
optional float float_field = 3;
optional string string_field = 4;
}
optional MessageFeature message_field_feature = 13 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_FIELD,
edition_defaults = {
edition: EDITION_99997_TEST_ONLY,
value: "int_field: 2"
},
edition_defaults = {
edition: EDITION_99998_TEST_ONLY,
value: "string_field: '2024'"
},
edition_defaults = {
edition: EDITION_2023,
value: "bool_field: true int_field: 1 float_field: 1.5 string_field: '2023'"
},
edition_defaults = { edition: EDITION_PROTO2, value: "" }
];
enum EnumFeature {
TEST_ENUM_FEATURE_UNKNOWN = 0;
ENUM_VALUE1 = 1;
ENUM_VALUE2 = 2;
ENUM_VALUE3 = 3;
ENUM_VALUE4 = 4;
ENUM_VALUE5 = 5;
}
optional EnumFeature enum_field_feature = 14 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_FIELD,
edition_defaults = {
edition: EDITION_PROTO2,
value: "TEST_ENUM_FEATURE_UNKNOWN"
},
edition_defaults = { edition: EDITION_2023, value: "ENUM_VALUE1" },
edition_defaults = {
edition: EDITION_99997_TEST_ONLY,
value: "ENUM_VALUE2"
},
edition_defaults = {
edition: EDITION_99998_TEST_ONLY,
value: "ENUM_VALUE3"
}
];
optional int32 int_source_feature = 15 [
optional EnumFeature source_feature = 15 [
retention = RETENTION_SOURCE,
targets = TARGET_TYPE_FILE,
targets = TARGET_TYPE_FIELD,
@ -164,10 +127,10 @@ message TestFeatures {
targets = TARGET_TYPE_METHOD,
targets = TARGET_TYPE_ONEOF,
targets = TARGET_TYPE_EXTENSION_RANGE,
edition_defaults = { edition: EDITION_PROTO2, value: "1" }
edition_defaults = { edition: EDITION_PROTO2, value: "VALUE1" }
];
optional string string_source_feature = 16 [
optional EnumFeature source_feature2 = 16 [
retention = RETENTION_SOURCE,
targets = TARGET_TYPE_FILE,
targets = TARGET_TYPE_FIELD,
@ -178,6 +141,6 @@ message TestFeatures {
targets = TARGET_TYPE_METHOD,
targets = TARGET_TYPE_ONEOF,
targets = TARGET_TYPE_EXTENSION_RANGE,
edition_defaults = { edition: EDITION_PROTO2, value: "'2023'" }
edition_defaults = { edition: EDITION_PROTO2, value: "VALUE1" }
];
}

Loading…
Cancel
Save