From d1f2ade11e8cb631d0712c91879a47ab0964a38f Mon Sep 17 00:00:00 2001 From: Abdol Baset Sedighi <118891407+94sedighi@users.noreply.github.com> Date: Thu, 8 Dec 2022 17:14:19 +0100 Subject: [PATCH] Add `ThatAre[Not]Abstract`, `ThatAre[Not]Static` and `ThatAre[Not]Virtual` to `PropertyInfoSelector` (#2054) --- .../Common/PropertyInfoExtensions.cs | 12 ++ .../Types/PropertyInfoSelector.cs | 75 ++++++++++- .../FluentAssertions/net47.verified.txt | 8 +- .../FluentAssertions/net6.0.verified.txt | 6 + .../netcoreapp2.1.verified.txt | 6 + .../netcoreapp3.0.verified.txt | 6 + .../netstandard2.0.verified.txt | 6 + .../netstandard2.1.verified.txt | 6 + .../Types/PropertyInfoSelectorSpecs.cs | 126 ++++++++++++++++-- docs/_pages/releases.md | 4 + 10 files changed, 245 insertions(+), 10 deletions(-) diff --git a/Src/FluentAssertions/Common/PropertyInfoExtensions.cs b/Src/FluentAssertions/Common/PropertyInfoExtensions.cs index d48c6be526..ec2f738dd2 100644 --- a/Src/FluentAssertions/Common/PropertyInfoExtensions.cs +++ b/Src/FluentAssertions/Common/PropertyInfoExtensions.cs @@ -9,4 +9,16 @@ internal static bool IsVirtual(this PropertyInfo property) MethodInfo methodInfo = property.GetGetMethod(nonPublic: true) ?? property.GetSetMethod(nonPublic: true); return !methodInfo.IsNonVirtual(); } + + internal static bool IsStatic(this PropertyInfo property) + { + MethodInfo methodInfo = property.GetGetMethod(nonPublic: true) ?? property.GetSetMethod(nonPublic: true); + return methodInfo.IsStatic; + } + + internal static bool IsAbstract(this PropertyInfo property) + { + MethodInfo methodInfo = property.GetGetMethod(nonPublic: true) ?? property.GetSetMethod(nonPublic: true); + return methodInfo.IsAbstract; + } } diff --git a/Src/FluentAssertions/Types/PropertyInfoSelector.cs b/Src/FluentAssertions/Types/PropertyInfoSelector.cs index 48e7fd39c7..fd5e6e6dd7 100644 --- a/Src/FluentAssertions/Types/PropertyInfoSelector.cs +++ b/Src/FluentAssertions/Types/PropertyInfoSelector.cs @@ -35,7 +35,8 @@ public PropertyInfoSelector(IEnumerable types) Guard.ThrowIfArgumentContainsNull(types, nameof(types)); selectedProperties = types.SelectMany(t => t - .GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)); + .GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance + | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)); } /// @@ -55,6 +56,78 @@ public PropertyInfoSelector ThatArePublicOrInternal } } + /// + /// Only select the properties that are abstract + /// + public PropertyInfoSelector ThatAreAbstract + { + get + { + selectedProperties = selectedProperties.Where(property => property.IsAbstract()); + return this; + } + } + + /// + /// Only select the properties that are not abstract + /// + public PropertyInfoSelector ThatAreNotAbstract + { + get + { + selectedProperties = selectedProperties.Where(property => !property.IsAbstract()); + return this; + } + } + + /// + /// Only select the properties that are static + /// + public PropertyInfoSelector ThatAreStatic + { + get + { + selectedProperties = selectedProperties.Where(property => property.IsStatic()); + return this; + } + } + + /// + /// Only select the properties that are not static + /// + public PropertyInfoSelector ThatAreNotStatic + { + get + { + selectedProperties = selectedProperties.Where(property => !property.IsStatic()); + return this; + } + } + + /// + /// Only select the properties that are virtual + /// + public PropertyInfoSelector ThatAreVirtual + { + get + { + selectedProperties = selectedProperties.Where(property => property.IsVirtual()); + return this; + } + } + + /// + /// Only select the properties that are not virtual + /// + public PropertyInfoSelector ThatAreNotVirtual + { + get + { + selectedProperties = selectedProperties.Where(property => !property.IsVirtual()); + return this; + } + } + /// /// Only select the properties that are decorated with an attribute of the specified type. /// diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt index d9ee0bbaf3..a0974ab0cd 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt @@ -2526,7 +2526,13 @@ namespace FluentAssertions.Types { public PropertyInfoSelector(System.Collections.Generic.IEnumerable types) { } public PropertyInfoSelector(System.Type type) { } + public FluentAssertions.Types.PropertyInfoSelector ThatAreAbstract { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotAbstract { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotStatic { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotVirtual { get; } public FluentAssertions.Types.PropertyInfoSelector ThatArePublicOrInternal { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreStatic { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreVirtual { get; } public System.Collections.Generic.IEnumerator GetEnumerator() { } public FluentAssertions.Types.PropertyInfoSelector NotOfType() { } public FluentAssertions.Types.PropertyInfoSelector OfType() { } @@ -2765,4 +2771,4 @@ namespace FluentAssertions.Xml public bool CanHandle(object value) { } public void Format(object value, FluentAssertions.Formatting.FormattedObjectGraph formattedGraph, FluentAssertions.Formatting.FormattingContext context, FluentAssertions.Formatting.FormatChild formatChild) { } } -} +} \ No newline at end of file diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt index 645fd533da..dca9743e33 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt @@ -2656,7 +2656,13 @@ namespace FluentAssertions.Types { public PropertyInfoSelector(System.Collections.Generic.IEnumerable types) { } public PropertyInfoSelector(System.Type type) { } + public FluentAssertions.Types.PropertyInfoSelector ThatAreAbstract { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotAbstract { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotStatic { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotVirtual { get; } public FluentAssertions.Types.PropertyInfoSelector ThatArePublicOrInternal { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreStatic { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreVirtual { get; } public System.Collections.Generic.IEnumerator GetEnumerator() { } public FluentAssertions.Types.PropertyInfoSelector NotOfType() { } public FluentAssertions.Types.PropertyInfoSelector OfType() { } diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.verified.txt index 719ef3ffd9..bb6fced08a 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.verified.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.verified.txt @@ -2528,7 +2528,13 @@ namespace FluentAssertions.Types { public PropertyInfoSelector(System.Collections.Generic.IEnumerable types) { } public PropertyInfoSelector(System.Type type) { } + public FluentAssertions.Types.PropertyInfoSelector ThatAreAbstract { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotAbstract { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotStatic { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotVirtual { get; } public FluentAssertions.Types.PropertyInfoSelector ThatArePublicOrInternal { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreStatic { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreVirtual { get; } public System.Collections.Generic.IEnumerator GetEnumerator() { } public FluentAssertions.Types.PropertyInfoSelector NotOfType() { } public FluentAssertions.Types.PropertyInfoSelector OfType() { } diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.verified.txt index 719ef3ffd9..bb6fced08a 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.verified.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.verified.txt @@ -2528,7 +2528,13 @@ namespace FluentAssertions.Types { public PropertyInfoSelector(System.Collections.Generic.IEnumerable types) { } public PropertyInfoSelector(System.Type type) { } + public FluentAssertions.Types.PropertyInfoSelector ThatAreAbstract { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotAbstract { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotStatic { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotVirtual { get; } public FluentAssertions.Types.PropertyInfoSelector ThatArePublicOrInternal { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreStatic { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreVirtual { get; } public System.Collections.Generic.IEnumerator GetEnumerator() { } public FluentAssertions.Types.PropertyInfoSelector NotOfType() { } public FluentAssertions.Types.PropertyInfoSelector OfType() { } diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt index db65bed2b7..87be0d1293 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt @@ -2477,7 +2477,13 @@ namespace FluentAssertions.Types { public PropertyInfoSelector(System.Collections.Generic.IEnumerable types) { } public PropertyInfoSelector(System.Type type) { } + public FluentAssertions.Types.PropertyInfoSelector ThatAreAbstract { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotAbstract { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotStatic { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotVirtual { get; } public FluentAssertions.Types.PropertyInfoSelector ThatArePublicOrInternal { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreStatic { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreVirtual { get; } public System.Collections.Generic.IEnumerator GetEnumerator() { } public FluentAssertions.Types.PropertyInfoSelector NotOfType() { } public FluentAssertions.Types.PropertyInfoSelector OfType() { } diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt index 719ef3ffd9..bb6fced08a 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt @@ -2528,7 +2528,13 @@ namespace FluentAssertions.Types { public PropertyInfoSelector(System.Collections.Generic.IEnumerable types) { } public PropertyInfoSelector(System.Type type) { } + public FluentAssertions.Types.PropertyInfoSelector ThatAreAbstract { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotAbstract { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotStatic { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreNotVirtual { get; } public FluentAssertions.Types.PropertyInfoSelector ThatArePublicOrInternal { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreStatic { get; } + public FluentAssertions.Types.PropertyInfoSelector ThatAreVirtual { get; } public System.Collections.Generic.IEnumerator GetEnumerator() { } public FluentAssertions.Types.PropertyInfoSelector NotOfType() { } public FluentAssertions.Types.PropertyInfoSelector OfType() { } diff --git a/Tests/FluentAssertions.Specs/Types/PropertyInfoSelectorSpecs.cs b/Tests/FluentAssertions.Specs/Types/PropertyInfoSelectorSpecs.cs index f78182652c..b20b6535ff 100644 --- a/Tests/FluentAssertions.Specs/Types/PropertyInfoSelectorSpecs.cs +++ b/Tests/FluentAssertions.Specs/Types/PropertyInfoSelectorSpecs.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using FluentAssertions.Types; using Internal.Main.Test; @@ -73,15 +74,91 @@ public void When_selecting_properties_from_types_in_an_assembly_it_should_return public void When_selecting_properties_that_are_public_or_internal_it_should_return_only_the_applicable_properties() { // Arrange - Type type = typeof(TestClassForPropertySelector); + Type type = typeof(TestClassForPropertySelectorWithInternalAndPublicProperties); // Act IEnumerable properties = type.Properties().ThatArePublicOrInternal.ToArray(); // Assert - const int PublicPropertyCount = 3; - const int InternalPropertyCount = 1; - properties.Should().HaveCount(PublicPropertyCount + InternalPropertyCount); + properties.Should().HaveCount(2); + } + + [Fact] + public void When_selecting_properties_that_are_abstract_it_should_return_only_the_applicable_properties() + { + // Arrange + Type type = typeof(TestClassForPropertySelector); + + // Act + IEnumerable properties = type.Properties().ThatAreAbstract.ToArray(); + + // Assert + properties.Should().HaveCount(2); + } + + [Fact] + public void When_selecting_properties_that_are_not_abstract_it_should_return_only_the_applicable_properties() + { + // Arrange + Type type = typeof(TestClassForPropertySelector); + + // Act + IEnumerable properties = type.Properties().ThatAreNotAbstract.ToArray(); + + // Assert + properties.Should().HaveCount(10); + } + + [Fact] + public void When_selecting_properties_that_are_static_it_should_return_only_the_applicable_properties() + { + // Arrange + Type type = typeof(TestClassForPropertySelector); + + // Act + IEnumerable properties = type.Properties().ThatAreStatic.ToArray(); + + // Assert + properties.Should().HaveCount(4); + } + + [Fact] + public void When_selecting_properties_that_are_not_static_it_should_return_only_the_applicable_properties() + { + // Arrange + Type type = typeof(TestClassForPropertySelector); + + // Act + IEnumerable properties = type.Properties().ThatAreNotStatic.ToArray(); + + // Assert + properties.Should().HaveCount(8); + } + + [Fact] + public void When_selecting_properties_that_are_virtual_it_should_return_only_the_applicable_properties() + { + // Arrange + Type type = typeof(TestClassForPropertySelector); + + // Act + IEnumerable properties = type.Properties().ThatAreVirtual.ToArray(); + + // Assert + properties.Should().HaveCount(7); + } + + [Fact] + public void When_selecting_properties_that_are_not_virtual_it_should_return_only_the_applicable_properties() + { + // Arrange + Type type = typeof(TestClassForPropertySelector); + + // Act + IEnumerable properties = type.Properties().ThatAreNotVirtual.ToArray(); + + // Assert + properties.Should().HaveCount(5); } [Fact] @@ -123,7 +200,7 @@ public void When_selecting_methods_that_return_a_specific_type_it_should_return_ IEnumerable properties = type.Properties().OfType().ToArray(); // Assert - properties.Should().HaveCount(2); + properties.Should().HaveCount(8); } [Fact] @@ -271,15 +348,48 @@ public void When_selecting_properties_return_types_it_should_return_the_correct_ // Assert returnTypes.Should() - .BeEquivalentTo(new[] { typeof(string), typeof(string), typeof(int), typeof(int), typeof(int), typeof(int) }); + .BeEquivalentTo(new[] + { + typeof(string), typeof(string), typeof(string), typeof(string) + , typeof(string), typeof(string), typeof(string) + , typeof(string), typeof(int), typeof(int), typeof(int), typeof(int) + }); } } #region Internal classes used in unit tests -internal class TestClassForPropertySelector +internal class TestClassForPropertySelectorWithInternalAndPublicProperties { - public virtual string PublicVirtualStringProperty { get; set; } + public static string PublicStaticStringProperty { get; } + + internal static string InternalStaticStringProperty { get; set; } + + protected static string ProtectedStaticStringProperty { get; set; } + + private static string PrivateStaticStringProperty { get; set; } +} + +internal abstract class TestClassForPropertySelector +{ + private static string myPrivateStaticStringField; + + public static string PublicStaticStringProperty { set => myPrivateStaticStringField = value; } + + internal static string InternalStaticStringProperty { get; set; } + + protected static string ProtectedStaticStringProperty { get; set; } + + private static string PrivateStaticStringProperty { get; set; } + + // An abstract method/property is implicitly a virtual method/property. + public abstract string PublicAbstractStringProperty { get; set; } + + public abstract string PublicAbstractStringPropertyWithSetterOnly { set; } + + private string myPrivateStringField; + + public virtual string PublicVirtualStringProperty { set => myPrivateStringField = value; } [DummyProperty] public virtual string PublicVirtualStringPropertyWithAttribute { get; set; } diff --git a/docs/_pages/releases.md b/docs/_pages/releases.md index 3b4d584c8c..db4567c6a4 100644 --- a/docs/_pages/releases.md +++ b/docs/_pages/releases.md @@ -10,9 +10,13 @@ sidebar: ## Unreleased ### What's new +* Added `ThatAre[Not]Abstract`, `ThatAre[Not]Static` and `ThatAre[Not]Virtual` properties for filtering in `PropertyInfoSelector.cs` - [#2054](https://github.com/fluentassertions/fluentassertions/pull/2054) * Added `BeOneOf` methods for object comparisons and `IComparable`s - [#2028](https://github.com/fluentassertions/fluentassertions/pull/2028) * Added `BeCloseTo` and `NotBeCloseTo` to `TimeOnly` - [#2030](https://github.com/fluentassertions/fluentassertions/pull/2030) +### Fixes +* Quering properties on classes, e.g. `typeof(MyClass).Properties()`, now also includes static properties - [#2054](https://github.com/fluentassertions/fluentassertions/pull/2054) + ## 6.8.0 ### What's new