From 345ad03b0156f01aca74d63ae73fcb02fb2634f9 Mon Sep 17 00:00:00 2001 From: Hugh Bellamy Date: Wed, 6 Feb 2019 15:00:06 +0000 Subject: [PATCH] Add more ComponentModel.Annotations tests (dotnet/corefx#35052) * Add more ComponentModel.Annotations tests * Address PR feedback Commit migrated from https://github.com/dotnet/corefx/commit/c13a641e12c23c3ac07a78964478a9a4b3137cc2 --- ...atedMetadataTypeTypeDescriptionProviderTests.cs | 324 ++++++++++++++++++++- .../CustomValidationAttributeTests.cs | 14 +- .../DisplayFormatAttributeTests.netcoreapp.cs | 21 +- .../DataAnnotations/MetadataTypeAttributeTests.cs | 26 +- .../DataAnnotations/ValidationAttributeTests.cs | 134 ++++----- .../DataAnnotations/ValidatorTests.cs | 26 ++ 6 files changed, 439 insertions(+), 106 deletions(-) diff --git a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/AssociatedMetadataTypeTypeDescriptionProviderTests.cs b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/AssociatedMetadataTypeTypeDescriptionProviderTests.cs index 193c00a..5542c2d 100644 --- a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/AssociatedMetadataTypeTypeDescriptionProviderTests.cs +++ b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/AssociatedMetadataTypeTypeDescriptionProviderTests.cs @@ -9,33 +9,337 @@ namespace System.ComponentModel.DataAnnotations.Tests public class AssociatedMetadataTypeTypeDescriptionProviderTests { [Fact] - public static void Constructor_NullType_ThrowsArgumentNullException() + public void Ctor_NullType_ThrowsArgumentNullException() { AssertExtensions.Throws("type", () => new AssociatedMetadataTypeTypeDescriptionProvider(null)); } [Fact] - public static void Constructor_NullAssociatedMetadataType_ThrowsArgumentNullException() + public void Ctor_NullAssociatedMetadataType_ThrowsArgumentNullException() { AssertExtensions.Throws("associatedMetadataType", () => new AssociatedMetadataTypeTypeDescriptionProvider(typeof(string), null)); } [Fact] - public static void Validate_GetTypeDescriptor_ReturnsTypeDescriptor_ForTypeWithCustomDescription() + public void GetTypeDescriptor_MetadataHasFieldsNotPresentOnClass_ThrowsInvalidOperationException() { - var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(SomeClassWithMetadata)); - ICustomTypeDescriptor typeDescriptor = provider.GetTypeDescriptor(typeof(SomeClassWithMetadata), null); - PropertyDescriptorCollection props = typeDescriptor.GetProperties(); - PropertyDescriptor firstNameProp = props[nameof(SomeClassWithMetadata.FirstName)]; - string displayName = firstNameProp.DisplayName; + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithInvalidMetadata)); + Assert.Throws(() => provider.GetTypeDescriptor(typeof(ClassWithInvalidMetadata), null)); + } + + [Fact] + public void GetTypeDescriptorGetAttributes_NoAssociatedMetadataTypeWithoutMetadataTypeAttribute_ReturnsExpected() + { + // Perform multiple times to test static caching behaviour. + for (int i = 0; i < 2; i++) + { + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithMetadata)); + ICustomTypeDescriptor typeDescriptor = provider.GetTypeDescriptor(typeof(ClassWithMetadata), null); + AttributeCollection attributes = typeDescriptor.GetAttributes(); + Assert.Equal(1, attributes.Count); + Assert.Equal("typeName", Assert.IsType(attributes[typeof(TypeConverterAttribute)]).ConverterTypeName); + } + } + + [Fact] + public void GetTypeDescriptorGetAttributes_NoAssociatedMetadataTypeWithMetadataTypeAttribute_ReturnsExpected() + { + // Perform multiple times to test static caching behaviour. + for (int i = 0; i < 2; i++) + { + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithMetadataOnAnotherClass)); + ICustomTypeDescriptor typeDescriptor = provider.GetTypeDescriptor(typeof(ClassWithMetadataOnAnotherClass), null); + AttributeCollection attributes = typeDescriptor.GetAttributes(); + Assert.Equal(2, attributes.Count); + Assert.Equal(typeof(ClassWithMetadata), Assert.IsType(attributes[typeof(MetadataTypeAttribute)]).MetadataClassType); + Assert.Equal("typeName", Assert.IsType(attributes[typeof(TypeConverterAttribute)]).ConverterTypeName); + } + } + + [Fact] + public void GetTypeDescriptorGetAttributes_WithAssociatedMetadataType_ReturnsExpected() + { + // Perform multiple times to test static caching behaviour. + for (int i = 0; i < 2; i++) + { + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithMetadataOnAnotherClass), typeof(ClassWithAttributes)); + ICustomTypeDescriptor typeDescriptor = provider.GetTypeDescriptor(typeof(ClassWithMetadataOnAnotherClass), null); + AttributeCollection attributes = typeDescriptor.GetAttributes(); + Assert.Equal(2, attributes.Count); + Assert.Equal(typeof(ClassWithMetadata), Assert.IsType(attributes[typeof(MetadataTypeAttribute)]).MetadataClassType); + Assert.Equal(EditorBrowsableState.Always, Assert.IsType(attributes[typeof(EditorBrowsableAttribute)]).State); + } + } + + [Fact] + public void GetTypeDescriptorGetAttributes_SameAssociatedMetadataType_ReturnsExpected() + { + // Perform multiple times to test static caching behaviour. + for (int i = 0; i < 2; i++) + { + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithMetadata), typeof(ClassWithMetadata)); + ICustomTypeDescriptor typeDescriptor = provider.GetTypeDescriptor(typeof(ClassWithMetadata)); + AttributeCollection attributes = typeDescriptor.GetAttributes(); + Assert.Equal("typeName", Assert.IsType(attributes[typeof(TypeConverterAttribute)]).ConverterTypeName); + } + } + + [Fact] + public void GetTypeDescriptorGetAttributes_SelfAssociatedMetadataType_ReturnsExpected() + { + // Perform multiple times to test static caching behaviour. + for (int i = 0; i < 2; i++) + { + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithSelfAssociatedMetadata)); + ICustomTypeDescriptor typeDescriptor = provider.GetTypeDescriptor(typeof(ClassWithSelfAssociatedMetadata)); + AttributeCollection attributes = typeDescriptor.GetAttributes(); + Assert.Equal("typeName", Assert.IsType(attributes[typeof(TypeConverterAttribute)]).ConverterTypeName); + } + } + + [Fact] + public void GetTypeDescriptorGetProperties_NoAssociatedMetadataTypeWithoutMetadataTypeAttribute_ReturnsExpected() + { + // Perform multiple times to test static caching behaviour. + for (int i = 0; i < 2; i++) + { + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithMetadata)); + ICustomTypeDescriptor typeDescriptor = provider.GetTypeDescriptor(typeof(ClassWithMetadata), null); + PropertyDescriptorCollection properties = typeDescriptor.GetProperties(); + PropertyDescriptor firstNameProperty = properties[nameof(ClassWithMetadata.FirstName)]; + PropertyDescriptor lastNameProperty = properties[nameof(ClassWithMetadata.LastName)]; + + Assert.Equal("First name", firstNameProperty.DisplayName); + Assert.Equal(typeof(ClassWithMetadata), firstNameProperty.ComponentType); + Assert.Equal(typeof(string), firstNameProperty.PropertyType); + Assert.True(firstNameProperty.IsReadOnly); + Assert.False(lastNameProperty.SupportsChangeEvents); + + Assert.Equal("LastName", lastNameProperty.DisplayName); + Assert.Equal(typeof(ClassWithMetadata), lastNameProperty.ComponentType); + Assert.Equal(typeof(string), lastNameProperty.PropertyType); + Assert.False(lastNameProperty.IsReadOnly); + Assert.False(lastNameProperty.SupportsChangeEvents); + } + } + + [Fact] + public void GetTypeDescriptorGetProperties_NoAssociatedMetadataTypeWithMetadataTypeAttribute_ReturnsExpected() + { + // Perform multiple times to test static caching behaviour. + for (int i = 0; i < 2; i++) + { + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithMetadata)); + ICustomTypeDescriptor typeDescriptor = provider.GetTypeDescriptor(typeof(ClassWithMetadata), null); + PropertyDescriptorCollection properties = typeDescriptor.GetProperties(new Attribute[] { new RequiredAttribute() }); + PropertyDescriptor firstNameProperty = properties[nameof(ClassWithMetadata.FirstName)]; + PropertyDescriptor lastNameProperty = properties[nameof(ClassWithMetadata.LastName)]; + + Assert.Equal("First name", firstNameProperty.DisplayName); + Assert.Equal(typeof(ClassWithMetadata), firstNameProperty.ComponentType); + Assert.Equal(typeof(string), firstNameProperty.PropertyType); + Assert.True(firstNameProperty.IsReadOnly); + Assert.False(firstNameProperty.SupportsChangeEvents); + + Assert.Equal("LastName", lastNameProperty.DisplayName); + Assert.Equal(typeof(ClassWithMetadata), lastNameProperty.ComponentType); + Assert.Equal(typeof(string), lastNameProperty.PropertyType); + Assert.False(lastNameProperty.IsReadOnly); + Assert.False(lastNameProperty.SupportsChangeEvents); + } + } + + [Theory] + [InlineData(typeof(ClassWithSelfAssociatedMetadata), "Last name")] + [InlineData(typeof(ClassWithMetadata), "LastName")] + [InlineData(typeof(EmptyClass), "LastName")] + public void GetTypeDescriptorGetProperties_WithAssociatedMetadataType_ReturnsExpected(Type associatedMetadataType, string expectedLastName) + { + // Perform multiple times to test static caching behaviour. + for (int i = 0; i < 2; i++) + { + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithMetadata), associatedMetadataType); + ICustomTypeDescriptor typeDescriptor = provider.GetTypeDescriptor(typeof(ClassWithMetadata), null); + PropertyDescriptorCollection properties = typeDescriptor.GetProperties(); + PropertyDescriptor firstNameProperty = properties[nameof(ClassWithMetadata.FirstName)]; + PropertyDescriptor lastNameProperty = properties[nameof(ClassWithMetadata.LastName)]; + + Assert.Equal("First name", firstNameProperty.DisplayName); + Assert.Equal(typeof(ClassWithMetadata), firstNameProperty.ComponentType); + Assert.Equal(typeof(string), firstNameProperty.PropertyType); + Assert.True(firstNameProperty.IsReadOnly); + Assert.False(firstNameProperty.SupportsChangeEvents); + + Assert.Equal(expectedLastName, lastNameProperty.DisplayName); + Assert.Equal(typeof(ClassWithMetadata), lastNameProperty.ComponentType); + Assert.Equal(typeof(string), lastNameProperty.PropertyType); + Assert.False(lastNameProperty.IsReadOnly); + Assert.False(lastNameProperty.SupportsChangeEvents); + } + } + + [Fact] + public void GetTypeDescriptorGetProperties_WithMetadataTypeAttribute_ReturnsExpected() + { + // Perform multiple times to test static caching behaviour. + for (int i = 0; i < 2; i++) + { + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithMetadataOnAnotherClass)); + ICustomTypeDescriptor typeDescriptor = provider.GetTypeDescriptor(typeof(ClassWithMetadataOnAnotherClass), null); + PropertyDescriptorCollection properties = typeDescriptor.GetProperties(); + PropertyDescriptor firstNameProperty = properties[nameof(ClassWithMetadataOnAnotherClass.FirstName)]; + PropertyDescriptor lastNameProperty = properties[nameof(ClassWithMetadataOnAnotherClass.LastName)]; + + Assert.Equal("First name", firstNameProperty.DisplayName); + Assert.Equal(typeof(ClassWithMetadataOnAnotherClass), firstNameProperty.ComponentType); + Assert.Equal(typeof(string), firstNameProperty.PropertyType); + Assert.True(firstNameProperty.IsReadOnly); + Assert.False(firstNameProperty.SupportsChangeEvents); + + Assert.Equal("Last name", lastNameProperty.DisplayName); + Assert.Equal(typeof(ClassWithMetadataOnAnotherClass), lastNameProperty.ComponentType); + Assert.Equal(typeof(string), lastNameProperty.PropertyType); + Assert.False(lastNameProperty.IsReadOnly); + Assert.False(lastNameProperty.SupportsChangeEvents); + } + } + + [Fact] + public void GetTypeDescriptorGetPropertiesWrappedPropertyDescriptor_GetValue_Success() + { + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithMetadataOnAnotherClass)); + PropertyDescriptorCollection properties = provider.GetTypeDescriptor(typeof(ClassWithMetadataOnAnotherClass), null).GetProperties(); + PropertyDescriptor descriptor = properties[nameof(ClassWithMetadataOnAnotherClass.FirstName)]; + + var component = new ClassWithMetadataOnAnotherClass + { + FirstName = "value" + }; + Assert.Equal("value", descriptor.GetValue(component)); + } - Assert.Equal("First name", displayName); + [Fact] + public void GetTypeDescriptorGetPropertiesWrappedPropertyDescriptor_SetValue_Success() + { + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithMetadataOnAnotherClass)); + PropertyDescriptorCollection properties = provider.GetTypeDescriptor(typeof(ClassWithMetadataOnAnotherClass), null).GetProperties(); + PropertyDescriptor descriptor = properties[nameof(ClassWithMetadataOnAnotherClass.FirstName)]; + + var component = new ClassWithMetadataOnAnotherClass(); + descriptor.SetValue(component, "value"); + Assert.Equal("value", descriptor.GetValue(component)); + Assert.Equal("value", component.FirstName); + } + + [Fact] + public void GetTypeDescriptorGetPropertiesWrappedPropertyDescriptor_ResetValue_Success() + { + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithMetadataOnAnotherClass)); + PropertyDescriptorCollection properties = provider.GetTypeDescriptor(typeof(ClassWithMetadataOnAnotherClass), null).GetProperties(); + PropertyDescriptor descriptor = properties[nameof(ClassWithMetadataOnAnotherClass.FirstName)]; + + var component = new ClassWithMetadataOnAnotherClass + { + FirstName = "value" + }; + descriptor.ResetValue(component); + Assert.Equal("value", descriptor.GetValue(component)); + Assert.Equal("value", component.FirstName); + } + + [Fact] + public void GetTypeDescriptorGetPropertiesWrappedPropertyDescriptor_CanResetValue_Success() + { + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithMetadataOnAnotherClass)); + PropertyDescriptorCollection properties = provider.GetTypeDescriptor(typeof(ClassWithMetadataOnAnotherClass), null).GetProperties(); + PropertyDescriptor descriptor = properties[nameof(ClassWithMetadataOnAnotherClass.FirstName)]; + + var component = new ClassWithMetadataOnAnotherClass(); + Assert.False(descriptor.CanResetValue(component)); + } + + [Fact] + public void GetTypeDescriptorGetPropertiesWrappedPropertyDescriptor_ShouldSerializeValue_Success() + { + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithMetadataOnAnotherClass)); + PropertyDescriptorCollection properties = provider.GetTypeDescriptor(typeof(ClassWithMetadataOnAnotherClass), null).GetProperties(); + PropertyDescriptor descriptor = properties[nameof(ClassWithMetadataOnAnotherClass.FirstName)]; + + var component = new ClassWithMetadataOnAnotherClass(); + Assert.True(descriptor.ShouldSerializeValue(component)); } - class SomeClassWithMetadata + [Fact] + public void GetTypeDescriptorGetPropertiesWrappedPropertyDescriptor_AddValueChanged_Success() + { + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithMetadataOnAnotherClass)); + PropertyDescriptorCollection properties = provider.GetTypeDescriptor(typeof(ClassWithMetadataOnAnotherClass), null).GetProperties(); + PropertyDescriptor descriptor = properties[nameof(ClassWithMetadataOnAnotherClass.FirstName)]; + + var component = new ClassWithMetadataOnAnotherClass(); + int callCount = 0; + EventHandler handler = (sender, e) => callCount++; + descriptor.AddValueChanged(component, handler); + descriptor.SetValue(component, "value"); + Assert.Equal(1, callCount); + } + + [Fact] + public void GetTypeDescriptorGetPropertiesWrappedPropertyDescriptor_RemoveValueChanged_Success() { + var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(ClassWithMetadataOnAnotherClass)); + PropertyDescriptorCollection properties = provider.GetTypeDescriptor(typeof(ClassWithMetadataOnAnotherClass), null).GetProperties(); + PropertyDescriptor descriptor = properties[nameof(ClassWithMetadataOnAnotherClass.FirstName)]; + + var component = new ClassWithMetadataOnAnotherClass(); + int callCount = 0; + EventHandler handler = (sender, e) => callCount++; + descriptor.AddValueChanged(component, handler); + descriptor.RemoveValueChanged(component, handler); + descriptor.SetValue(component, "value"); + Assert.Equal(0, callCount); + } + + [TypeConverter("typeName")] + private class ClassWithMetadata + { + [ReadOnly(true)] [DisplayName("First name")] public string FirstName { get; set; } + + public string LastName { get; set; } + } + + [MetadataType(typeof(ClassWithMetadata))] + private class ClassWithMetadataOnAnotherClass + { + public string FirstName { get; set; } + + [DisplayName("Last name")] + public string LastName { get; set; } + } + + [MetadataType(typeof(ClassWithMetadata))] + private class ClassWithInvalidMetadata + { + public string FirstName { get; set; } + } + + [TypeConverter("typeName")] + [MetadataType(typeof(ClassWithSelfAssociatedMetadata))] + private class ClassWithSelfAssociatedMetadata + { + [DisplayName("Last name")] + public string LastName { get; set; } + } + + private class EmptyClass + { + } + + [EditorBrowsable(EditorBrowsableState.Always)] + private class ClassWithAttributes + { } } } diff --git a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/CustomValidationAttributeTests.cs b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/CustomValidationAttributeTests.cs index 309021a..86c7bc8b 100644 --- a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/CustomValidationAttributeTests.cs +++ b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/CustomValidationAttributeTests.cs @@ -35,18 +35,22 @@ namespace System.ComponentModel.DataAnnotations.Tests protected override IEnumerable InvalidValues() { + yield return new TestCase(GetAttribute(nameof(CustomValidator.CorrectValidationMethodOneArg)), null); yield return new TestCase(GetAttribute(nameof(CustomValidator.CorrectValidationMethodOneArg)), new TestClass("AnyString")); yield return new TestCase(GetAttribute(nameof(CustomValidator.CorrectValidationMethodTwoArgs)), "AnyString"); yield return new TestCase(GetAttribute(nameof(CustomValidator.CorrectValidationMethodOneArgStronglyTyped)), new TestClass("AnyString")); yield return new TestCase(GetAttribute(nameof(CustomValidator.CorrectValidationMethodTwoArgsStronglyTyped)), "AnyString"); + yield return new TestCase(GetAttribute(nameof(CustomValidator.CorrectValidationMethodOneArgGenericStruct)), null); + yield return new TestCase(GetAttribute(nameof(CustomValidator.CorrectValidationMethodOneArgNullable)), new TestStruct()); yield return new TestCase(GetAttribute(nameof(CustomValidator.CorrectValidationMethodTwoArgsWithFirstNullable)), new TestStruct() { Value = "Invalid Value" }); yield return new TestCase(GetAttribute(nameof(CustomValidator.CorrectValidationMethodIntegerArg)), null); yield return new TestCase(GetAttribute(nameof(CustomValidator.CorrectValidationMethodIntegerArg)), new TestClass("NotInt")); yield return new TestCase(GetAttribute(nameof(CustomValidator.CorrectValidationMethodIntegerArg)), new DateTime(2014, 3, 19)); + yield return new TestCase(GetAttribute(nameof(CustomValidator.CorrectValidationMethodOneArgDateTime)), null); yield return new TestCase(GetAttribute(nameof(CustomValidator.CorrectValidationMethodOneArgDateTime)), "abcdef"); // Implements IConvertible (throws NotSupportedException - is caught) @@ -62,7 +66,7 @@ namespace System.ComponentModel.DataAnnotations.Tests [InlineData(null, null)] [InlineData(typeof(string), "")] [InlineData(typeof(int), " \t\r\n")] - public static void Constructor(Type validatorType, string method) + public static void Ctor_Type_String(Type validatorType, string method) { CustomValidationAttribute attribute = new CustomValidationAttribute(validatorType, method); Assert.Equal(validatorType, attribute.ValidatorType); @@ -123,6 +127,7 @@ namespace System.ComponentModel.DataAnnotations.Tests yield return new object[] { typeof(CustomValidator), nameof(CustomValidator.ValidationMethodWithNoArgs) }; yield return new object[] { typeof(CustomValidator), nameof(CustomValidator.ValidationMethodWithByRefArg) }; yield return new object[] { typeof(CustomValidator), nameof(CustomValidator.ValidationMethodTwoArgsButSecondIsNotValidationContext) }; + yield return new object[] { typeof(CustomValidator), nameof(CustomValidator.ValidationMethodThreeArgs) }; } [Theory] @@ -234,6 +239,11 @@ namespace System.ComponentModel.DataAnnotations.Tests return new ValidationResult("Validation failed - neither null nor Value=\"Valid Value\""); } + public static ValidationResult CorrectValidationMethodOneArgGenericStruct(GenericStruct testStruct) + { + return ValidationResult.Success; + } + public static ValidationResult CorrectValidationMethodTwoArgsWithFirstNullable(TestStruct? testStruct, ValidationContext context) { if (testStruct == null) { return ValidationResult.Success; } @@ -260,5 +270,7 @@ namespace System.ComponentModel.DataAnnotations.Tests { public string Value { get; set; } } + + public struct GenericStruct { } } } diff --git a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/DisplayFormatAttributeTests.netcoreapp.cs b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/DisplayFormatAttributeTests.netcoreapp.cs index 170bcb5..32c2247 100644 --- a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/DisplayFormatAttributeTests.netcoreapp.cs +++ b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/DisplayFormatAttributeTests.netcoreapp.cs @@ -49,14 +49,23 @@ namespace System.ComponentModel.DataAnnotations.Tests [Theory] [InlineData(typeof(FakeResourceType), "Resource3")] + [InlineData(typeof(FakeResourceType), nameof(FakeResourceType.IntProperty))] [InlineData(typeof(FakeResourceType), nameof(FakeResourceType.InstanceProperty))] + [InlineData(typeof(FakeResourceType), "PrivateInstanceProperty")] + [InlineData(typeof(FakeResourceType), "ProtectedInstanceProperty")] + [InlineData(typeof(FakeResourceType), nameof(FakeResourceType.InternalInstanceProperty))] + [InlineData(typeof(FakeResourceType), nameof(FakeResourceType.ProtectedInternalInstanceProperty))] [InlineData(typeof(FakeResourceType), nameof(FakeResourceType.SetOnlyProperty))] + [InlineData(typeof(FakeResourceType), nameof(FakeResourceType.PrivateGetProperty))] [InlineData(typeof(FakeResourceType), "NonPublicGetProperty")] [InlineData(typeof(string), "foo")] public void GetNullDisplayText_InvalidResourceType_ThrowsInvalidOperationException(Type nullDisplayTextResourceType, string nullDisplayText) { - var attribute = new DisplayFormatAttribute { NullDisplayTextResourceType = nullDisplayTextResourceType }; - attribute.NullDisplayText = nullDisplayText; + var attribute = new DisplayFormatAttribute + { + NullDisplayTextResourceType = nullDisplayTextResourceType, + NullDisplayText = nullDisplayText + }; Assert.Throws(() => attribute.GetNullDisplayText()); } @@ -75,8 +84,16 @@ namespace System.ComponentModel.DataAnnotations.Tests public static string Resource2 => "Resource2Text"; public string InstanceProperty => "InstanceProperty"; + private string PrivateInstanceProperty => "InstanceProperty"; + protected string ProtectedInstanceProperty => "InstanceProperty"; + protected internal string ProtectedInternalInstanceProperty => "InstanceProperty"; + internal string InternalInstanceProperty => "InstanceProperty"; + public static string SetOnlyProperty { set => Assert.NotNull(value); } + public string PrivateGetProperty { private get; set; } private static string NonPublicGetProperty => "NonPublicGetProperty"; + + public static int IntProperty { get; set; } } } } diff --git a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/MetadataTypeAttributeTests.cs b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/MetadataTypeAttributeTests.cs index dc8ffc2..27140d7 100644 --- a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/MetadataTypeAttributeTests.cs +++ b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/MetadataTypeAttributeTests.cs @@ -8,27 +8,19 @@ namespace System.ComponentModel.DataAnnotations.Tests { public class MetadataTypeAttributeTests { - [Fact] - public static void Validate_GetTypeDescriptor_ReturnsMetadataType() + [Theory] + [InlineData(typeof(int))] + public void Ctor_Type(Type metadataClassType) { - var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(SomeClassWithMetadataOnAnotherClass)); - ICustomTypeDescriptor typeDescriptor = provider.GetTypeDescriptor(typeof(SomeClassWithMetadataOnAnotherClass), null); - PropertyDescriptorCollection props = typeDescriptor.GetProperties(); - PropertyDescriptor firstNameProp = props[nameof(SomeClassWithMetadataOnAnotherClass.FirstName)]; - string displayName = firstNameProp.DisplayName; - Assert.Equal("First name", displayName); + var attribute = new MetadataTypeAttribute(metadataClassType); + Assert.Equal(metadataClassType, attribute.MetadataClassType); } - class MetadataForAnotherClass - { - [DisplayName("First name")] - public string FirstName { get; set; } - } - - [MetadataType(typeof(MetadataForAnotherClass))] - class SomeClassWithMetadataOnAnotherClass + [Fact] + public void MetadataClassType_GetWithoutType_ThrowsInvalidOperationException() { - public string FirstName { get; set; } + var attribute = new MetadataTypeAttribute(null); + Assert.Throws(() => attribute.MetadataClassType); } } } diff --git a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/ValidationAttributeTests.cs b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/ValidationAttributeTests.cs index 8c0e7e6..3a34881 100644 --- a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/ValidationAttributeTests.cs +++ b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/ValidationAttributeTests.cs @@ -124,109 +124,79 @@ namespace System.ComponentModel.DataAnnotations.Tests attribute.Validate("Valid 2-Args Value", s_testValidationContext); } - // FormatErrorMessage_throws_InvalidOperationException_if_ErrorMessage_and_ErrorMessageResourceName_are_both_null_or_empty - [Fact] - public static void TestThrowIfNullOrEmptyErrorMessage() + [Theory] + [InlineData("SomeErrorMessage", "SomeErrorMessage")] + [InlineData("SomeErrorMessage with name <{0}>", "SomeErrorMessage with name ")] + public void FormatErrorMessage_HasErrorMessage_ReturnsExpected(string errorMessage, string expected) { var attribute = new ValidationAttributeOverrideBothIsValids(); - attribute.ErrorMessage = null; + attribute.ErrorMessage = errorMessage; attribute.ErrorMessageResourceName = null; - Assert.Throws(() => attribute.FormatErrorMessage("Name to put in error message does not matter")); - - attribute.ErrorMessage = string.Empty; - attribute.ErrorMessageResourceName = string.Empty; - Assert.Throws(() => attribute.FormatErrorMessage("Name to put in error message does not matter")); + attribute.ErrorMessageResourceType = null; + Assert.Equal(expected, attribute.FormatErrorMessage("name")); } - - // FormatErrorMessage_throws_InvalidOperationException_if_ErrorMessage_and_ErrorMessageResourceName_are_both_set - [Fact] - public static void TestThrowIfErrorMessageAndNameBothSet() + + [Theory] + [InlineData(nameof(ValidationAttributeOverrideBothIsValids.PublicErrorMessageTestProperty), typeof(ValidationAttributeOverrideBothIsValids), "Error Message from PublicErrorMessageTestProperty")] + [InlineData(nameof(ValidationAttributeOverrideBothIsValids.PublicErrorMessageTestPropertyWithName), typeof(ValidationAttributeOverrideBothIsValids), "Error Message from PublicErrorMessageTestProperty With Name ")] + [InlineData(nameof(ValidationAttributeOverrideBothIsValids.StaticInternalProperty), typeof(ValidationAttributeOverrideBothIsValids), "ErrorMessage")] + public void FormatErrorMessage_HasResourceProperty_ReturnsExpected(string resourceName, Type resourceType, string expected) { var attribute = new ValidationAttributeOverrideBothIsValids(); - attribute.ErrorMessage = "SomeErrorMessage"; - attribute.ErrorMessageResourceName = "SomeErrorMessageResourceName"; - Assert.Throws(() => attribute.FormatErrorMessage("Name to put in error message does not matter")); + attribute.ErrorMessage = string.Empty; + attribute.ErrorMessageResourceName = resourceName; + attribute.ErrorMessageResourceType = resourceType; + Assert.Equal(expected, attribute.FormatErrorMessage("name")); } - // FormatErrorMessage_throws_InvalidOperationException_if_ErrorMessageResourceName_set_but_ErrorMessageResourceType_is_not - [Fact] - public static void TestFormatErrorMessageThrow() + [Theory] + [InlineData(null)] + [InlineData("")] + public void FormatErrorMessage_NullOrEmptyErrorMessageAndName_ThrowsInvalidOperationException(string value) { var attribute = new ValidationAttributeOverrideBothIsValids(); - attribute.ErrorMessageResourceName = "SomeErrorMessageResourceName"; - attribute.ErrorMessageResourceType = null; - Assert.Throws(() => attribute.FormatErrorMessage("Name to put in error message does not matter")); + attribute.ErrorMessage = value; + attribute.ErrorMessageResourceName = value; + Assert.Throws(() => attribute.FormatErrorMessage("name")); } - // FormatErrorMessage_throws_InvalidOperationException_if_ErrorMessageResourceType_set_but_ErrorMessageResourceName_is_not( [Fact] - public static void TestFormatErrorMessageThrow01() + public void FormatErrorMessage_BothErrorMessageAndResourceName_ThrowsInvalidOperationException() { var attribute = new ValidationAttributeOverrideBothIsValids(); - attribute.ErrorMessageResourceName = string.Empty; - attribute.ErrorMessageResourceType = typeof(string); + attribute.ErrorMessage = "SomeErrorMessage"; + attribute.ErrorMessageResourceName = "SomeErrorMessageResourceName"; Assert.Throws(() => attribute.FormatErrorMessage("Name to put in error message does not matter")); } - // FormatErrorMessage_returns_ErrorMessage_if_only_ErrorMessage_is_set [Fact] - public static void TestFormatErrorMessage() + public void FormatErrorMessage_BothErrorMessageAndResourceType_ThrowsInvalidOperationException() { var attribute = new ValidationAttributeOverrideBothIsValids(); attribute.ErrorMessage = "SomeErrorMessage"; - attribute.ErrorMessageResourceName = null; - attribute.ErrorMessageResourceType = null; - Assert.Equal("SomeErrorMessage", attribute.FormatErrorMessage("Name to put in error message does not matter - no placeholder")); - } - - // FormatErrorMessage_returns_ErrorMessage_with_name_if_only_ErrorMessage_is_set - [Fact] - public static void TestFormatErrorMessage01() - { - var attribute = new ValidationAttributeOverrideBothIsValids(); - attribute.ErrorMessage = "SomeErrorMessage with name <{0}> here"; - attribute.ErrorMessageResourceName = null; - attribute.ErrorMessageResourceType = null; - Assert.Equal( - string.Format("SomeErrorMessage with name <{0}> here", "Error Message Name"), - attribute.FormatErrorMessage("Error Message Name")); - } - - // FormatErrorMessage_throws_InvalidOperationException_if_ErrorMessageResourceType_and_ErrorMessageResourceName_point_to_non_existent_resource - [Fact] - public static void TestFormatErrorMessage02() - { - var attribute = new ValidationAttributeOverrideBothIsValids(); - attribute.ErrorMessageResourceName = "NonExistentErrorMessageTestProperty"; - attribute.ErrorMessageResourceType = typeof(ValidationAttributeOverrideBothIsValids); + attribute.ErrorMessageResourceType = typeof(int); Assert.Throws(() => attribute.FormatErrorMessage("Name to put in error message does not matter")); } - // FormatErrorMessage_returns_ErrorMessage_from_resource_if_only_ErrorMessageResourceName_and_ErrorMessageResourceType_are_set - [Fact] - public static void TestFormatErrorMessage03() + [Theory] + [InlineData("ResourceName", null)] + [InlineData(null, typeof(int))] + [InlineData("", typeof(int))] + [InlineData("NoSuchProperty", typeof(int))] + [InlineData(nameof(ValidationAttributeOverrideBothIsValids.GetSetProperty), typeof(ValidationAttributeOverrideBothIsValids))] + [InlineData(nameof(ValidationAttributeOverrideBothIsValids.GetOnlyProperty), typeof(ValidationAttributeOverrideBothIsValids))] + [InlineData(nameof(ValidationAttributeOverrideBothIsValids.SetOnlyProperty), typeof(ValidationAttributeOverrideBothIsValids))] + [InlineData(nameof(ValidationAttributeOverrideBothIsValids.StaticSetOnlyProperty), typeof(ValidationAttributeOverrideBothIsValids))] + [InlineData(nameof(ValidationAttributeOverrideBothIsValids.StaticIntProperty), typeof(ValidationAttributeOverrideBothIsValids))] + [InlineData("StaticPrivateProperty", typeof(ValidationAttributeOverrideBothIsValids))] + [InlineData("StaticProtectedProperty", typeof(ValidationAttributeOverrideBothIsValids))] + [InlineData(nameof(ValidationAttributeOverrideBothIsValids.StaticProtectedInternalProperty), typeof(ValidationAttributeOverrideBothIsValids))] + public void FormatErrorMessage_InvalidResourceNameAndResourceType_ThrowsInvalidOperationException(string resourceName, Type resourceType) { var attribute = new ValidationAttributeOverrideBothIsValids(); - attribute.ErrorMessage = string.Empty; - attribute.ErrorMessageResourceName = "PublicErrorMessageTestProperty"; - attribute.ErrorMessageResourceType = typeof(ValidationAttributeOverrideBothIsValids); - Assert.Equal(ValidationAttributeOverrideBothIsValids.PublicErrorMessageTestProperty, - attribute.FormatErrorMessage("Name to put in error message does not matter - no placeholder")); - } - - // FormatErrorMessage_returns_ErrorMessage_from_resource_with_name_if_only_ErrorMessageResourceName_and_ErrorMessageResourceType_are_set - [Fact] - public static void TestFormatErrorMessage04() - { - var attribute = new ValidationAttributeOverrideBothIsValids(); - attribute.ErrorMessage = string.Empty; - attribute.ErrorMessageResourceName = "PublicErrorMessageTestPropertyWithName"; - attribute.ErrorMessageResourceType = typeof(ValidationAttributeOverrideBothIsValids); - Assert.Equal( - string.Format( - ValidationAttributeOverrideBothIsValids.PublicErrorMessageTestPropertyWithName, - "Error Message Name"), - attribute.FormatErrorMessage("Error Message Name")); + attribute.ErrorMessageResourceName = resourceName; + attribute.ErrorMessageResourceType = resourceType; + Assert.Throws(() => attribute.FormatErrorMessage("Name to put in error message does not matter")); } // Validate_object_string_throws_exception_with_ValidationResult_ErrorMessage_matching_ErrorMessage_passed_in @@ -387,6 +357,18 @@ namespace System.ComponentModel.DataAnnotations.Tests get { return "Error Message from PublicErrorMessageTestProperty With Name <{0}>"; } } + public string GetSetProperty { get; set; } + public string GetOnlyProperty { get; } + public string SetOnlyProperty { set { } } + + public static string StaticSetOnlyProperty { set { } } + public static int StaticIntProperty { get; set; } + + private static string StaticPrivateProperty { get; set; } = "ErrorMessage"; + private static string StaticProtectedProperty { get; set; } = "ErrorMessage"; + internal static string StaticInternalProperty { get; set; } = "ErrorMessage"; + protected internal static string StaticProtectedInternalProperty { get; set; } = "ErrorMessage"; + public override bool IsValid(object value) { var valueAsString = value as string; diff --git a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/ValidatorTests.cs b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/ValidatorTests.cs index c820e38..003ec18 100644 --- a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/ValidatorTests.cs +++ b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/ValidatorTests.cs @@ -953,6 +953,32 @@ namespace System.ComponentModel.DataAnnotations.Tests public int NonNullableProperty { get; set; } public int? NullableProperty { get; set; } + + // Private properties should not be validated. + + [Required] + private string PrivateSetOnlyProperty { set { } } + + [Required] + protected string ProtectedSetOnlyProperty { set { } } + + [Required] + internal string InternalSetOnlyProperty { set { } } + + [Required] + protected internal string ProtectedInternalSetOnlyProperty { set { } } + + [Required] + private string PrivateGetOnlyProperty { get; } + + [Required] + protected string ProtectedGetOnlyProperty { get; } + + [Required] + internal string InternalGetOnlyProperty { get; } + + [Required] + protected internal string ProtectedInternalGetOnlyProperty { get; } } public enum TestEnum -- 2.7.4