@ -636,11 +636,16 @@ TEST(FeatureResolverLifetimesTest, MultipleErrors) {
TEST ( FeatureResolverLifetimesTest , DynamicPool ) {
DescriptorPool pool ;
FileDescriptorProto file ;
FileDescriptorProto : : GetDescriptor ( ) - > file ( ) - > CopyTo ( & file ) ;
ASSERT_NE ( pool . BuildFile ( file ) , nullptr ) ;
pb : : TestFeatures : : GetDescriptor ( ) - > file ( ) - > CopyTo ( & file ) ;
ASSERT_NE ( pool . BuildFile ( file ) , nullptr ) ;
{
FileDescriptorProto file ;
FileDescriptorProto : : GetDescriptor ( ) - > file ( ) - > CopyTo ( & file ) ;
ASSERT_NE ( pool . BuildFile ( file ) , nullptr ) ;
}
{
FileDescriptorProto file ;
pb : : TestFeatures : : GetDescriptor ( ) - > file ( ) - > CopyTo ( & file ) ;
ASSERT_NE ( pool . BuildFile ( file ) , nullptr ) ;
}
const Descriptor * feature_set =
pool . FindMessageTypeByName ( " google.protobuf.FeatureSet " ) ;
ASSERT_NE ( feature_set , nullptr ) ;
@ -656,9 +661,9 @@ TEST(FeatureResolverLifetimesTest, DynamicPool) {
ElementsAre ( HasSubstr ( " pb.TestFeatures.removed_feature " ) ) ) ;
}
TEST ( FeatureResolverLifetimesTest , EmptyValueSupportInvalid2023 ) {
TEST ( FeatureResolverLifetimesTest , EmptyValueSupportValid ) {
FeatureSet features = ParseTextOrDie ( R " pb(
[ pb . test ] { fil e_feature: VALUE_EMPTY_SUPPORT }
[ pb . test ] { value_lifetim e_feature: VALUE_LIFETIM E_EMPTY_SUPPORT }
) pb " );
auto results = FeatureResolver : : ValidateFeatureLifetimes ( EDITION_2023 ,
features , nullptr ) ;
@ -666,19 +671,86 @@ TEST(FeatureResolverLifetimesTest, EmptyValueSupportInvalid2023) {
EXPECT_THAT ( results . warnings , IsEmpty ( ) ) ;
}
TEST ( FeatureResolverLifetimesTest , ValueSupportInvalid2023 ) {
TEST ( FeatureResolverLifetimesTest , ValueSupportValid ) {
FeatureSet features = ParseTextOrDie ( R " pb(
[ pb . test ] { value_lifetime_feature : VALUE_LIFETIME_SUPPORT }
) pb " );
auto results = FeatureResolver : : ValidateFeatureLifetimes (
EDITION_99997_TEST_ONLY , features , nullptr ) ;
EXPECT_THAT ( results . errors , IsEmpty ( ) ) ;
EXPECT_THAT ( results . warnings , IsEmpty ( ) ) ;
}
TEST ( FeatureResolverLifetimesTest , ValueSupportBeforeIntroduced ) {
FeatureSet features = ParseTextOrDie ( R " pb(
[ pb . test ] { file_feature : VALUE_FUTURE }
[ pb . test ] { value_lifetim e_feature: VALUE_LIFETIM E_FUTURE }
) pb " );
auto results = FeatureResolver : : ValidateFeatureLifetimes ( EDITION_2023 ,
features , nullptr ) ;
EXPECT_THAT ( results . errors ,
ElementsAre ( AllOf (
HasSubstr ( " pb.VALUE_FUTURE " ) ,
HasSubstr ( " pb.VALUE_LIFETIME_ FUTURE " ) ,
HasSubstr ( " introduced until edition 99997_TEST_ONLY " ) ) ) ) ;
EXPECT_THAT ( results . warnings , IsEmpty ( ) ) ;
}
TEST ( FeatureResolverLifetimesTest , ValueSupportAfterRemoved ) {
FeatureSet features = ParseTextOrDie ( R " pb(
[ pb . test ] { value_lifetime_feature : VALUE_LIFETIME_REMOVED }
) pb " );
auto results = FeatureResolver : : ValidateFeatureLifetimes (
EDITION_99997_TEST_ONLY , features , nullptr ) ;
EXPECT_THAT (
results . errors ,
ElementsAre ( AllOf ( HasSubstr ( " pb.VALUE_LIFETIME_REMOVED " ) ,
HasSubstr ( " removed in edition 99997_TEST_ONLY " ) ) ) ) ;
EXPECT_THAT ( results . warnings , IsEmpty ( ) ) ;
}
TEST ( FeatureResolverLifetimesTest , ValueSupportDeprecated ) {
FeatureSet features = ParseTextOrDie ( R " pb(
[ pb . test ] { value_lifetime_feature : VALUE_LIFETIME_DEPRECATED }
) pb " );
auto results = FeatureResolver : : ValidateFeatureLifetimes (
EDITION_99997_TEST_ONLY , features , nullptr ) ;
EXPECT_THAT ( results . errors , IsEmpty ( ) ) ;
EXPECT_THAT (
results . warnings ,
ElementsAre ( AllOf ( HasSubstr ( " pb.VALUE_LIFETIME_DEPRECATED " ) ,
HasSubstr ( " deprecated in edition 99997_TEST_ONLY " ) ,
HasSubstr ( " Custom feature deprecation warning " ) ) ) ) ;
}
TEST ( FeatureResolverLifetimesTest , ValueAndFeatureSupportDeprecated ) {
FeatureSet features = ParseTextOrDie ( R " pb(
[ pb . test ] { value_lifetime_feature : VALUE_LIFETIME_DEPRECATED }
) pb " );
auto results = FeatureResolver : : ValidateFeatureLifetimes (
EDITION_99998_TEST_ONLY , features , nullptr ) ;
EXPECT_THAT ( results . errors , IsEmpty ( ) ) ;
EXPECT_THAT ( results . warnings ,
UnorderedElementsAre (
AllOf ( HasSubstr ( " pb.VALUE_LIFETIME_DEPRECATED " ) ,
HasSubstr ( " deprecated in edition 99997_TEST_ONLY " ) ,
HasSubstr ( " Custom feature deprecation warning " ) ) ,
AllOf ( HasSubstr ( " pb.TestFeatures.value_lifetime_feature " ) ,
HasSubstr ( " deprecated in edition 99998_TEST_ONLY " ) ,
HasSubstr ( " Custom feature deprecation warning " ) ) ) ) ;
}
TEST ( FeatureResolverLifetimesTest , ValueSupportInvalidNumber ) {
FeatureSet features ;
features . MutableExtension ( pb : : test ) - > set_value_lifetime_feature (
static_cast < pb : : ValueLifetimeFeature > ( 1234 ) ) ;
auto results = FeatureResolver : : ValidateFeatureLifetimes ( EDITION_2023 ,
features , nullptr ) ;
EXPECT_THAT (
results . errors ,
ElementsAre ( AllOf ( HasSubstr ( " pb.TestFeatures.value_lifetime_feature " ) ,
HasSubstr ( " 1234 " ) ) ) ) ;
EXPECT_THAT ( results . warnings , IsEmpty ( ) ) ;
}
class FakeErrorCollector : public io : : ErrorCollector {
public :
FakeErrorCollector ( ) = default ;
@ -1284,6 +1356,349 @@ TEST_F(FeatureResolverPoolTest, CompileDefaultsInvalidDefaultsTooEarly) {
HasError ( HasSubstr ( " Minimum edition 2_TEST_ONLY is not EDITION_LEGACY " ) ) ) ;
}
TEST_F ( FeatureResolverPoolTest ,
CompileDefaultsInvalidValueWithMissingDeprecationWarning ) {
const FileDescriptor * file = ParseSchema ( R " schema(
syntax = " proto2 " ;
package test ;
import " google/protobuf/descriptor.proto " ;
extend google . protobuf . FeatureSet {
optional Foo bar = 9999 ;
}
enum FooValues {
UNKNOWN = 0 ;
VALUE = 1 [ feature_support . edition_deprecated = EDITION_2023 ] ;
}
message Foo {
optional FooValues bool_field = 1 [
targets = TARGET_TYPE_FIELD ,
feature_support . edition_introduced = EDITION_2023 ,
edition_defaults = { edition : EDITION_LEGACY , value : " UNKNOWN " }
] ;
}
) 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 ( " test.VALUE " ) ,
HasSubstr ( " deprecation warning " ) ) ) ) ;
}
TEST_F ( FeatureResolverPoolTest ,
CompileDefaultsInvalidValueWithMissingDeprecation ) {
const FileDescriptor * file = ParseSchema ( R " schema(
syntax = " proto2 " ;
package test ;
import " google/protobuf/descriptor.proto " ;
extend google . protobuf . FeatureSet {
optional Foo bar = 9999 ;
}
enum FooValues {
UNKNOWN = 0 ;
VALUE = 1 [ feature_support . deprecation_warning = " some message " ] ;
}
message Foo {
optional FooValues bool_field = 1 [
targets = TARGET_TYPE_FIELD ,
feature_support . edition_introduced = EDITION_2023 ,
edition_defaults = { edition : EDITION_LEGACY , value : " UNKNOWN " }
] ;
}
) 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 ( " test.VALUE " ) ,
HasSubstr ( " is not marked deprecated " ) ) ) ) ;
}
TEST_F ( FeatureResolverPoolTest ,
CompileDefaultsInvalidValueDeprecatedBeforeIntroduced ) {
const FileDescriptor * file = ParseSchema ( R " schema(
syntax = " proto2 " ;
package test ;
import " google/protobuf/descriptor.proto " ;
extend google . protobuf . FeatureSet {
optional Foo bar = 9999 ;
}
enum FooValues {
UNKNOWN = 0 ;
VALUE = 1 [ feature_support = {
edition_introduced : EDITION_2024
edition_deprecated : EDITION_2023
deprecation_warning : " warning "
} ] ;
}
message Foo {
optional FooValues bool_field = 1 [
targets = TARGET_TYPE_FIELD ,
feature_support . edition_introduced = EDITION_2023 ,
edition_defaults = { edition : EDITION_LEGACY , value : " UNKNOWN " }
] ;
}
) 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 ( " test.VALUE " ) ,
HasSubstr ( " deprecated before it was introduced " ) ) ) ) ;
}
TEST_F ( FeatureResolverPoolTest ,
CompileDefaultsInvalidValueDeprecatedBeforeIntroducedInherited ) {
const FileDescriptor * file = ParseSchema ( R " schema(
syntax = " proto2 " ;
package test ;
import " google/protobuf/descriptor.proto " ;
extend google . protobuf . FeatureSet {
optional Foo bar = 9999 ;
}
enum FooValues {
UNKNOWN = 0 ;
VALUE = 1 [ feature_support = {
edition_deprecated : EDITION_2023
deprecation_warning : " warning "
} ] ;
}
message Foo {
optional FooValues bool_field = 1 [
targets = TARGET_TYPE_FIELD ,
feature_support . edition_introduced = EDITION_2024 ,
edition_defaults = { edition : EDITION_LEGACY , value : " UNKNOWN " }
] ;
}
) 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 ( " test.VALUE " ) ,
HasSubstr ( " deprecated before it was introduced " ) ) ) ) ;
}
TEST_F ( FeatureResolverPoolTest ,
CompileDefaultsInvalidValueDeprecatedAfterRemoved ) {
const FileDescriptor * file = ParseSchema ( R " schema(
syntax = " proto2 " ;
package test ;
import " google/protobuf/descriptor.proto " ;
extend google . protobuf . FeatureSet {
optional Foo bar = 9999 ;
}
enum FooValues {
UNKNOWN = 0 ;
VALUE = 1 [ feature_support = {
edition_introduced : EDITION_2023
edition_deprecated : EDITION_2024
deprecation_warning : " warning "
edition_removed : EDITION_2024
} ] ;
}
message Foo {
optional FooValues bool_field = 1 [
targets = TARGET_TYPE_FIELD ,
feature_support . edition_introduced = EDITION_2023 ,
edition_defaults = { edition : EDITION_LEGACY , value : " UNKNOWN " }
] ;
}
) 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 ( " test.VALUE " ) ,
HasSubstr ( " deprecated after it was removed " ) ) ) ) ;
}
TEST_F ( FeatureResolverPoolTest ,
CompileDefaultsInvalidValueRemovedBeforeIntroduced ) {
const FileDescriptor * file = ParseSchema ( R " schema(
syntax = " proto2 " ;
package test ;
import " google/protobuf/descriptor.proto " ;
extend google . protobuf . FeatureSet {
optional Foo bar = 9999 ;
}
enum FooValues {
UNKNOWN = 0 ;
VALUE = 1 [ feature_support = {
edition_introduced : EDITION_2024
edition_removed : EDITION_2023
} ] ;
}
message Foo {
optional FooValues bool_field = 1 [
targets = TARGET_TYPE_FIELD ,
feature_support . edition_introduced = EDITION_2023 ,
edition_defaults = { edition : EDITION_LEGACY , value : " UNKNOWN " }
] ;
}
) 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 ( " test.VALUE " ) ,
HasSubstr ( " removed before it was introduced " ) ) ) ) ;
}
TEST_F ( FeatureResolverPoolTest ,
CompileDefaultsInvalidValueIntroducedBeforeFeature ) {
const FileDescriptor * file = ParseSchema ( R " schema(
syntax = " proto2 " ;
package test ;
import " google/protobuf/descriptor.proto " ;
extend google . protobuf . FeatureSet {
optional Foo bar = 9999 ;
}
enum FooValues {
UNKNOWN = 0 ;
VALUE = 1 [ feature_support = {
edition_introduced : EDITION_2023
} ] ;
}
message Foo {
optional FooValues bool_field = 1 [
targets = TARGET_TYPE_FIELD ,
feature_support . edition_introduced = EDITION_2024 ,
edition_defaults = { edition : EDITION_LEGACY , value : " UNKNOWN " }
] ;
}
) 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 ( " test.VALUE " ) , HasSubstr ( " introduced before " ) ,
HasSubstr ( " test.Foo.bool_field " ) ) ) ) ;
}
TEST_F ( FeatureResolverPoolTest ,
CompileDefaultsInvalidValueIntroducedAfterFeatureRemoved ) {
const FileDescriptor * file = ParseSchema ( R " schema(
syntax = " proto2 " ;
package test ;
import " google/protobuf/descriptor.proto " ;
extend google . protobuf . FeatureSet {
optional Foo bar = 9999 ;
}
enum FooValues {
UNKNOWN = 0 ;
VALUE = 1 [ feature_support = {
edition_introduced : EDITION_99997_TEST_ONLY
} ] ;
}
message Foo {
optional FooValues bool_field = 1 [
targets = TARGET_TYPE_FIELD ,
feature_support . edition_introduced = EDITION_2023 ,
feature_support . edition_removed = EDITION_2024 ,
edition_defaults = { edition : EDITION_LEGACY , value : " UNKNOWN " }
] ;
}
) 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 ( " test.VALUE " ) ,
HasSubstr ( " removed before it was introduced " ) ) ) ) ;
}
TEST_F ( FeatureResolverPoolTest ,
CompileDefaultsInvalidValueRemovedAfterFeature ) {
const FileDescriptor * file = ParseSchema ( R " schema(
syntax = " proto2 " ;
package test ;
import " google/protobuf/descriptor.proto " ;
extend google . protobuf . FeatureSet {
optional Foo bar = 9999 ;
}
enum FooValues {
UNKNOWN = 0 ;
VALUE = 1 [ feature_support = {
edition_removed : EDITION_99997_TEST_ONLY
} ] ;
}
message Foo {
optional FooValues bool_field = 1 [
targets = TARGET_TYPE_FIELD ,
feature_support . edition_introduced = EDITION_2023 ,
feature_support . edition_removed = EDITION_2024 ,
edition_defaults = { edition : EDITION_LEGACY , value : " UNKNOWN " }
] ;
}
) 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 ( " test.VALUE " ) , HasSubstr ( " removed after " ) ,
HasSubstr ( " test.Foo.bool_field " ) ) ) ) ;
}
TEST_F ( FeatureResolverPoolTest ,
CompileDefaultsInvalidValueDeprecatedAfterFeature ) {
const FileDescriptor * file = ParseSchema ( R " schema(
syntax = " proto2 " ;
package test ;
import " google/protobuf/descriptor.proto " ;
extend google . protobuf . FeatureSet {
optional Foo bar = 9999 ;
}
enum FooValues {
UNKNOWN = 0 ;
VALUE = 1 [ feature_support = {
edition_deprecated : EDITION_99997_TEST_ONLY
deprecation_warning : " warning "
} ] ;
}
message Foo {
optional FooValues bool_field = 1 [
targets = TARGET_TYPE_FIELD ,
feature_support . edition_introduced = EDITION_2023 ,
feature_support . edition_deprecated = EDITION_2024 ,
feature_support . deprecation_warning = " warning " ,
edition_defaults = { edition : EDITION_LEGACY , value : " UNKNOWN " }
] ;
}
) 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 ( " test.VALUE " ) , HasSubstr ( " deprecated after " ) ,
HasSubstr ( " test.Foo.bool_field " ) ) ) ) ;
}
TEST_F ( FeatureResolverPoolTest , CompileDefaultsMinimumTooEarly ) {
const FileDescriptor * file = ParseSchema ( R " schema(
syntax = " proto2 " ;