diff --git a/README.md b/README.md index a1dbee7..520d5f0 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,8 @@ In.AllLoadedAssemblies().Methods() // Filter by parameters In.AllLoadedAssemblies().Methods() .WithoutParameters() - .WithParameter() + .WithParameter() // Parameter of type string or a subtype + .WithExactParameter() // Parameter of exactly type string .WithParameter("count") .WithParameterCount(2) @@ -185,7 +186,8 @@ In.AllLoadedAssemblies().Public.Events() // Constructors In.AllLoadedAssemblies().Public.Constructors() .WithoutParameters() - .WithParameter() + .WithParameter() // Parameter of type string or a subtype + .WithExactParameter() // Parameter of exactly type string .WithParameterCount(1) .With() ``` diff --git a/Source/aweXpect.Reflection/Filters/ConstructorFilters.WithExactParameter.cs b/Source/aweXpect.Reflection/Filters/ConstructorFilters.WithExactParameter.cs new file mode 100644 index 0000000..120d59a --- /dev/null +++ b/Source/aweXpect.Reflection/Filters/ConstructorFilters.WithExactParameter.cs @@ -0,0 +1,77 @@ +using System; +using System.Linq; +using System.Reflection; +using aweXpect.Core; +using aweXpect.Options; +using aweXpect.Reflection.Collections; +using aweXpect.Reflection.Helpers; +using aweXpect.Reflection.Options; + +namespace aweXpect.Reflection; + +public static partial class ConstructorFilters +{ + /// + /// Filter for constructors with a parameter of exact type . + /// + public static ConstructorsWithParameter WithExactParameter(this Filtered.Constructors @this) + { + Type parameterType = typeof(T); + CollectionIndexOptions collectionIndexOptions = new(); + ParameterFilterOptions parameterFilterOptions = new(p => p.ParameterType.IsOrInheritsFrom(parameterType, true), + () => $"of exact type {Formatter.Format(parameterType)}"); + IAsyncChangeableFilter filter = Filter.Suffix( + constructorInfo => + { + ParameterInfo[] parameters = constructorInfo.GetParameters(); + return parameters.AnyAsync(async (p, i) => + { + bool? isIndexInRange = collectionIndexOptions.Match switch + { + CollectionIndexOptions.IMatchFromBeginning fromBeginning => fromBeginning.MatchesIndex(i), + CollectionIndexOptions.IMatchFromEnd fromEnd => fromEnd.MatchesIndex(i, parameters.Length), + _ => false, + }; + return isIndexInRange == true && + await parameterFilterOptions.Matches(p); + }); + }, + () + => $"with parameter {parameterFilterOptions.GetDescription()}{collectionIndexOptions.Match.GetDescription()} "); + return new ConstructorsWithParameter(@this.Which(filter), collectionIndexOptions, parameterFilterOptions); + } + + /// + /// Filter for constructors with a parameter of exact type with the + /// name. + /// + public static ConstructorsWithNamedParameter WithExactParameter(this Filtered.Constructors @this, string expected) + { + Type parameterType = typeof(T); + StringEqualityOptions stringEqualityOptions = new(); + ParameterFilterOptions parameterFilterOptions = new(p => p.ParameterType.IsOrInheritsFrom(parameterType, true), + () => $"of exact type {Formatter.Format(parameterType)}"); + parameterFilterOptions.AddPredicate(p => stringEqualityOptions.AreConsideredEqual(p.Name, expected), + () => $"name {stringEqualityOptions.GetExpectation(expected, ExpectationGrammars.None)}"); + CollectionIndexOptions collectionIndexOptions = new(); + IAsyncChangeableFilter filter = Filter.Suffix( + constructorInfo => + { + ParameterInfo[] parameters = constructorInfo.GetParameters(); + return parameters.AnyAsync(async (p, i) => + { + bool? isIndexInRange = collectionIndexOptions.Match switch + { + CollectionIndexOptions.IMatchFromBeginning fromBeginning => fromBeginning.MatchesIndex(i), + CollectionIndexOptions.IMatchFromEnd fromEnd => fromEnd.MatchesIndex(i, parameters.Length), + _ => false, + }; + return isIndexInRange == true && + await parameterFilterOptions.Matches(p); + }); + }, + () + => $"with parameter {parameterFilterOptions.GetDescription()}{collectionIndexOptions.Match.GetDescription()} "); + return new ConstructorsWithNamedParameter(@this.Which(filter), collectionIndexOptions, parameterFilterOptions, stringEqualityOptions); + } +} diff --git a/Source/aweXpect.Reflection/Filters/ConstructorFilters.WithParameter.cs b/Source/aweXpect.Reflection/Filters/ConstructorFilters.WithParameter.cs index 538ca16..a12ee44 100644 --- a/Source/aweXpect.Reflection/Filters/ConstructorFilters.WithParameter.cs +++ b/Source/aweXpect.Reflection/Filters/ConstructorFilters.WithParameter.cs @@ -20,7 +20,7 @@ public static ConstructorsWithParameter WithParameter(this Filtered.Constr { Type parameterType = typeof(T); CollectionIndexOptions collectionIndexOptions = new(); - ParameterFilterOptions parameterFilterOptions = new(p => p.ParameterType == parameterType, + ParameterFilterOptions parameterFilterOptions = new(p => p.ParameterType.IsOrInheritsFrom(parameterType), () => $"of type {Formatter.Format(parameterType)}"); IAsyncChangeableFilter filter = Filter.Suffix( constructorInfo => @@ -51,7 +51,7 @@ public static ConstructorsWithNamedParameter WithParameter(this Filtered.C { Type parameterType = typeof(T); StringEqualityOptions stringEqualityOptions = new(); - ParameterFilterOptions parameterFilterOptions = new(p => p.ParameterType == parameterType, + ParameterFilterOptions parameterFilterOptions = new(p => p.ParameterType.IsOrInheritsFrom(parameterType), () => $"of type {Formatter.Format(parameterType)}"); parameterFilterOptions.AddPredicate(p => stringEqualityOptions.AreConsideredEqual(p.Name, expected), () => $"name {stringEqualityOptions.GetExpectation(expected, ExpectationGrammars.None)}"); diff --git a/Source/aweXpect.Reflection/Filters/MethodFilters.WithExactParameter.cs b/Source/aweXpect.Reflection/Filters/MethodFilters.WithExactParameter.cs new file mode 100644 index 0000000..6d447ca --- /dev/null +++ b/Source/aweXpect.Reflection/Filters/MethodFilters.WithExactParameter.cs @@ -0,0 +1,77 @@ +using System; +using System.Linq; +using System.Reflection; +using aweXpect.Core; +using aweXpect.Options; +using aweXpect.Reflection.Collections; +using aweXpect.Reflection.Helpers; +using aweXpect.Reflection.Options; + +namespace aweXpect.Reflection; + +public static partial class MethodFilters +{ + /// + /// Filter for methods with a parameter of exact type . + /// + public static MethodsWithParameter WithExactParameter(this Filtered.Methods @this) + { + Type parameterType = typeof(T); + CollectionIndexOptions collectionIndexOptions = new(); + ParameterFilterOptions parameterFilterOptions = new(p => p.ParameterType.IsOrInheritsFrom(parameterType, true), + () => $"of exact type {Formatter.Format(parameterType)}"); + IAsyncChangeableFilter filter = Filter.Suffix( + methodInfo => + { + ParameterInfo[] parameters = methodInfo.GetParameters(); + return parameters.AnyAsync(async (p, i) => + { + bool? isIndexInRange = collectionIndexOptions.Match switch + { + CollectionIndexOptions.IMatchFromBeginning fromBeginning => fromBeginning.MatchesIndex(i), + CollectionIndexOptions.IMatchFromEnd fromEnd => fromEnd.MatchesIndex(i, parameters.Length), + _ => false, + }; + return isIndexInRange == true && + await parameterFilterOptions.Matches(p); + }); + }, + () + => $"with parameter {parameterFilterOptions.GetDescription()}{collectionIndexOptions.Match.GetDescription()} "); + return new MethodsWithParameter(@this.Which(filter), collectionIndexOptions, parameterFilterOptions); + } + + /// + /// Filter for methods with a parameter of exact type with the + /// name. + /// + public static MethodsWithNamedParameter WithExactParameter(this Filtered.Methods @this, string expected) + { + Type parameterType = typeof(T); + StringEqualityOptions stringEqualityOptions = new(); + ParameterFilterOptions parameterFilterOptions = new(p => p.ParameterType.IsOrInheritsFrom(parameterType, true), + () => $"of exact type {Formatter.Format(parameterType)}"); + parameterFilterOptions.AddPredicate(p => stringEqualityOptions.AreConsideredEqual(p.Name, expected), + () => $"name {stringEqualityOptions.GetExpectation(expected, ExpectationGrammars.None)}"); + CollectionIndexOptions collectionIndexOptions = new(); + IAsyncChangeableFilter filter = Filter.Suffix( + methodInfo => + { + ParameterInfo[] parameters = methodInfo.GetParameters(); + return parameters.AnyAsync(async (p, i) => + { + bool? isIndexInRange = collectionIndexOptions.Match switch + { + CollectionIndexOptions.IMatchFromBeginning fromBeginning => fromBeginning.MatchesIndex(i), + CollectionIndexOptions.IMatchFromEnd fromEnd => fromEnd.MatchesIndex(i, parameters.Length), + _ => false, + }; + return isIndexInRange == true && + await parameterFilterOptions.Matches(p); + }); + }, + () + => $"with parameter {parameterFilterOptions.GetDescription()}{collectionIndexOptions.Match.GetDescription()} "); + return new MethodsWithNamedParameter(@this.Which(filter), collectionIndexOptions, parameterFilterOptions, stringEqualityOptions); + } +} diff --git a/Source/aweXpect.Reflection/Filters/MethodFilters.WithParameter.cs b/Source/aweXpect.Reflection/Filters/MethodFilters.WithParameter.cs index e95be69..1d84c93 100644 --- a/Source/aweXpect.Reflection/Filters/MethodFilters.WithParameter.cs +++ b/Source/aweXpect.Reflection/Filters/MethodFilters.WithParameter.cs @@ -20,7 +20,7 @@ public static MethodsWithParameter WithParameter(this Filtered.Methods @th { Type parameterType = typeof(T); CollectionIndexOptions collectionIndexOptions = new(); - ParameterFilterOptions parameterFilterOptions = new(p => p.ParameterType == parameterType, + ParameterFilterOptions parameterFilterOptions = new(p => p.ParameterType.IsOrInheritsFrom(parameterType), () => $"of type {Formatter.Format(parameterType)}"); IAsyncChangeableFilter filter = Filter.Suffix( methodInfo => @@ -51,7 +51,7 @@ public static MethodsWithNamedParameter WithParameter(this Filtered.Method { Type parameterType = typeof(T); StringEqualityOptions stringEqualityOptions = new(); - ParameterFilterOptions parameterFilterOptions = new(p => p.ParameterType == parameterType, + ParameterFilterOptions parameterFilterOptions = new(p => p.ParameterType.IsOrInheritsFrom(parameterType), () => $"of type {Formatter.Format(parameterType)}"); parameterFilterOptions.AddPredicate(p => stringEqualityOptions.AreConsideredEqual(p.Name, expected), () => $"name {stringEqualityOptions.GetExpectation(expected, ExpectationGrammars.None)}"); diff --git a/Tests/aweXpect.Reflection.Api.Tests/Expected/aweXpect.Reflection_net10.0.txt b/Tests/aweXpect.Reflection.Api.Tests/Expected/aweXpect.Reflection_net10.0.txt index c619247..8f6fa8e 100644 --- a/Tests/aweXpect.Reflection.Api.Tests/Expected/aweXpect.Reflection_net10.0.txt +++ b/Tests/aweXpect.Reflection.Api.Tests/Expected/aweXpect.Reflection_net10.0.txt @@ -31,6 +31,8 @@ namespace aweXpect.Reflection where TAttribute : System.Attribute { } public static aweXpect.Reflection.ConstructorFilters.ConstructorsWith With(this aweXpect.Reflection.Collections.Filtered.Constructors @this, System.Func? predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") where TAttribute : System.Attribute { } + public static aweXpect.Reflection.ConstructorFilters.ConstructorsWithParameter WithExactParameter(this aweXpect.Reflection.Collections.Filtered.Constructors @this) { } + public static aweXpect.Reflection.ConstructorFilters.ConstructorsWithNamedParameter WithExactParameter(this aweXpect.Reflection.Collections.Filtered.Constructors @this, string expected) { } public static aweXpect.Reflection.ConstructorFilters.ConstructorsWithNamedParameter WithParameter(this aweXpect.Reflection.Collections.Filtered.Constructors @this, string expected) { } public static aweXpect.Reflection.ConstructorFilters.ConstructorsWithParameter WithParameter(this aweXpect.Reflection.Collections.Filtered.Constructors @this) { } public static aweXpect.Reflection.ConstructorFilters.ConstructorsWithNamedParameter WithParameter(this aweXpect.Reflection.Collections.Filtered.Constructors @this, string expected) { } @@ -197,6 +199,8 @@ namespace aweXpect.Reflection where TAttribute : System.Attribute { } public static aweXpect.Reflection.MethodFilters.MethodsWith With(this aweXpect.Reflection.Collections.Filtered.Methods @this, System.Func? predicate, bool inherit = true, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") where TAttribute : System.Attribute { } + public static aweXpect.Reflection.MethodFilters.MethodsWithParameter WithExactParameter(this aweXpect.Reflection.Collections.Filtered.Methods @this) { } + public static aweXpect.Reflection.MethodFilters.MethodsWithNamedParameter WithExactParameter(this aweXpect.Reflection.Collections.Filtered.Methods @this, string expected) { } public static aweXpect.Reflection.Collections.Filtered.Methods.StringEqualityResultType WithName(this aweXpect.Reflection.Collections.Filtered.Methods @this, string expected) { } public static aweXpect.Reflection.MethodFilters.MethodsWithNamedParameter WithParameter(this aweXpect.Reflection.Collections.Filtered.Methods @this, string expected) { } public static aweXpect.Reflection.MethodFilters.MethodsWithParameter WithParameter(this aweXpect.Reflection.Collections.Filtered.Methods @this) { } diff --git a/Tests/aweXpect.Reflection.Api.Tests/Expected/aweXpect.Reflection_net8.0.txt b/Tests/aweXpect.Reflection.Api.Tests/Expected/aweXpect.Reflection_net8.0.txt index df7ec5f..9b95809 100644 --- a/Tests/aweXpect.Reflection.Api.Tests/Expected/aweXpect.Reflection_net8.0.txt +++ b/Tests/aweXpect.Reflection.Api.Tests/Expected/aweXpect.Reflection_net8.0.txt @@ -31,6 +31,8 @@ namespace aweXpect.Reflection where TAttribute : System.Attribute { } public static aweXpect.Reflection.ConstructorFilters.ConstructorsWith With(this aweXpect.Reflection.Collections.Filtered.Constructors @this, System.Func? predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") where TAttribute : System.Attribute { } + public static aweXpect.Reflection.ConstructorFilters.ConstructorsWithParameter WithExactParameter(this aweXpect.Reflection.Collections.Filtered.Constructors @this) { } + public static aweXpect.Reflection.ConstructorFilters.ConstructorsWithNamedParameter WithExactParameter(this aweXpect.Reflection.Collections.Filtered.Constructors @this, string expected) { } public static aweXpect.Reflection.ConstructorFilters.ConstructorsWithNamedParameter WithParameter(this aweXpect.Reflection.Collections.Filtered.Constructors @this, string expected) { } public static aweXpect.Reflection.ConstructorFilters.ConstructorsWithParameter WithParameter(this aweXpect.Reflection.Collections.Filtered.Constructors @this) { } public static aweXpect.Reflection.ConstructorFilters.ConstructorsWithNamedParameter WithParameter(this aweXpect.Reflection.Collections.Filtered.Constructors @this, string expected) { } @@ -197,6 +199,8 @@ namespace aweXpect.Reflection where TAttribute : System.Attribute { } public static aweXpect.Reflection.MethodFilters.MethodsWith With(this aweXpect.Reflection.Collections.Filtered.Methods @this, System.Func? predicate, bool inherit = true, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") where TAttribute : System.Attribute { } + public static aweXpect.Reflection.MethodFilters.MethodsWithParameter WithExactParameter(this aweXpect.Reflection.Collections.Filtered.Methods @this) { } + public static aweXpect.Reflection.MethodFilters.MethodsWithNamedParameter WithExactParameter(this aweXpect.Reflection.Collections.Filtered.Methods @this, string expected) { } public static aweXpect.Reflection.Collections.Filtered.Methods.StringEqualityResultType WithName(this aweXpect.Reflection.Collections.Filtered.Methods @this, string expected) { } public static aweXpect.Reflection.MethodFilters.MethodsWithNamedParameter WithParameter(this aweXpect.Reflection.Collections.Filtered.Methods @this, string expected) { } public static aweXpect.Reflection.MethodFilters.MethodsWithParameter WithParameter(this aweXpect.Reflection.Collections.Filtered.Methods @this) { } diff --git a/Tests/aweXpect.Reflection.Api.Tests/Expected/aweXpect.Reflection_netstandard2.0.txt b/Tests/aweXpect.Reflection.Api.Tests/Expected/aweXpect.Reflection_netstandard2.0.txt index 300559a..baf0087 100644 --- a/Tests/aweXpect.Reflection.Api.Tests/Expected/aweXpect.Reflection_netstandard2.0.txt +++ b/Tests/aweXpect.Reflection.Api.Tests/Expected/aweXpect.Reflection_netstandard2.0.txt @@ -31,6 +31,8 @@ namespace aweXpect.Reflection where TAttribute : System.Attribute { } public static aweXpect.Reflection.ConstructorFilters.ConstructorsWith With(this aweXpect.Reflection.Collections.Filtered.Constructors @this, System.Func? predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") where TAttribute : System.Attribute { } + public static aweXpect.Reflection.ConstructorFilters.ConstructorsWithParameter WithExactParameter(this aweXpect.Reflection.Collections.Filtered.Constructors @this) { } + public static aweXpect.Reflection.ConstructorFilters.ConstructorsWithNamedParameter WithExactParameter(this aweXpect.Reflection.Collections.Filtered.Constructors @this, string expected) { } public static aweXpect.Reflection.ConstructorFilters.ConstructorsWithNamedParameter WithParameter(this aweXpect.Reflection.Collections.Filtered.Constructors @this, string expected) { } public static aweXpect.Reflection.ConstructorFilters.ConstructorsWithParameter WithParameter(this aweXpect.Reflection.Collections.Filtered.Constructors @this) { } public static aweXpect.Reflection.ConstructorFilters.ConstructorsWithNamedParameter WithParameter(this aweXpect.Reflection.Collections.Filtered.Constructors @this, string expected) { } @@ -197,6 +199,8 @@ namespace aweXpect.Reflection where TAttribute : System.Attribute { } public static aweXpect.Reflection.MethodFilters.MethodsWith With(this aweXpect.Reflection.Collections.Filtered.Methods @this, System.Func? predicate, bool inherit = true, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") where TAttribute : System.Attribute { } + public static aweXpect.Reflection.MethodFilters.MethodsWithParameter WithExactParameter(this aweXpect.Reflection.Collections.Filtered.Methods @this) { } + public static aweXpect.Reflection.MethodFilters.MethodsWithNamedParameter WithExactParameter(this aweXpect.Reflection.Collections.Filtered.Methods @this, string expected) { } public static aweXpect.Reflection.Collections.Filtered.Methods.StringEqualityResultType WithName(this aweXpect.Reflection.Collections.Filtered.Methods @this, string expected) { } public static aweXpect.Reflection.MethodFilters.MethodsWithNamedParameter WithParameter(this aweXpect.Reflection.Collections.Filtered.Methods @this, string expected) { } public static aweXpect.Reflection.MethodFilters.MethodsWithParameter WithParameter(this aweXpect.Reflection.Collections.Filtered.Methods @this) { } diff --git a/Tests/aweXpect.Reflection.Tests/Filters/ConstructorFilters.WithExactParameter.Tests.cs b/Tests/aweXpect.Reflection.Tests/Filters/ConstructorFilters.WithExactParameter.Tests.cs new file mode 100644 index 0000000..1f9e5e8 --- /dev/null +++ b/Tests/aweXpect.Reflection.Tests/Filters/ConstructorFilters.WithExactParameter.Tests.cs @@ -0,0 +1,73 @@ +using aweXpect.Reflection.Collections; + +namespace aweXpect.Reflection.Tests.Filters; + +public sealed partial class ConstructorFilters +{ + public sealed class WithExactParameter + { + public sealed class Tests + { + [Fact] + public async Task ShouldFilterForConstructorsWithParameterOfExactType() + { + Filtered.Constructors constructors = In.Type() + .Constructors().WithExactParameter(); + + await That(constructors).IsEqualTo([ + typeof(TestClass).GetConstructor([typeof(DummyBase),])!, + ]).InAnyOrder(); + await That(constructors.GetDescription()) + .IsEqualTo("constructors with parameter of exact type ").AsPrefix(); + } + + [Fact] + public async Task ShouldNotIncludeConstructorsWithParameterOfDerivedType() + { + Filtered.Constructors exactConstructors = In.Type() + .Constructors().WithExactParameter(); + Filtered.Constructors assignableConstructors = In.Type() + .Constructors().WithParameter(); + + await That(exactConstructors).IsEqualTo([ + typeof(TestClass).GetConstructor([typeof(DummyBase),])!, + ]).InAnyOrder(); + await That(assignableConstructors).IsEqualTo([ + typeof(TestClass).GetConstructor([typeof(DummyBase),])!, + typeof(TestClass).GetConstructor([typeof(Dummy),])!, + ]).InAnyOrder(); + } + + [Fact] + public async Task WithName_ShouldFilterForConstructorsWithParameterOfExactTypeAndName() + { + Filtered.Constructors constructors = In.Type() + .Constructors().WithExactParameter("parameter"); + + await That(constructors).IsEqualTo([ + typeof(TestClass).GetConstructor([typeof(DummyBase),])!, + ]).InAnyOrder(); + await That(constructors.GetDescription()) + .IsEqualTo("constructors with parameter of exact type ").AsPrefix(); + } + } + + // ReSharper disable UnusedMember.Local + // ReSharper disable UnusedParameter.Local + private class TestClass + { + public TestClass(DummyBase parameter) { } + public TestClass(Dummy parameter) { } + } + // ReSharper restore UnusedParameter.Local + // ReSharper restore UnusedMember.Local + + private class DummyBase + { + } + + private class Dummy : DummyBase + { + } + } +} diff --git a/Tests/aweXpect.Reflection.Tests/Filters/ConstructorFilters.WithParameter.Tests.cs b/Tests/aweXpect.Reflection.Tests/Filters/ConstructorFilters.WithParameter.Tests.cs index 025fade..e9cc778 100644 --- a/Tests/aweXpect.Reflection.Tests/Filters/ConstructorFilters.WithParameter.Tests.cs +++ b/Tests/aweXpect.Reflection.Tests/Filters/ConstructorFilters.WithParameter.Tests.cs @@ -36,8 +36,11 @@ await That(constructors.GetDescription()) [Fact] public async Task WithParameterAtIndex_WhenIndexIsNegative_ShouldThrowArgumentOutOfRangeException() { - void Act() => In.Type() - .Constructors().WithParameter().AtIndex(-1); + void Act() + { + In.Type() + .Constructors().WithParameter().AtIndex(-1); + } await That(Act).Throws() .WithParamName("index").And @@ -174,6 +177,20 @@ await That(constructors.GetDescription()) .IsEqualTo("constructors with parameter of type string in").AsPrefix(); } + [Fact] + public async Task WithParameterOfType_ShouldIncludeConstructorsWithParameterOfDerivedType() + { + Filtered.Constructors constructors = In.Type() + .Constructors().WithParameter(); + + await That(constructors).IsEqualTo([ + typeof(TestClassWithInheritedConstructorParameters).GetConstructor([typeof(BaseParameter),])!, + typeof(TestClassWithInheritedConstructorParameters).GetConstructor([typeof(DerivedParameter),])!, + ]).InAnyOrder(); + await That(constructors.GetDescription()) + .IsEqualTo("constructors with parameter of type ").AsPrefix(); + } + [Fact] public async Task WithParameterOfTypeAndName_ShouldFilterForConstructorsWithParameterOfSpecificTypeAndName() { @@ -217,6 +234,14 @@ await That(constructors.GetDescription()) } } + private class BaseParameter + { + } + + private class DerivedParameter : BaseParameter + { + } + // ReSharper disable UnusedMember.Local // ReSharper disable UnusedParameter.Local private class TestClassWithConstructorParameters @@ -227,6 +252,12 @@ public TestClassWithConstructorParameters(int id, string name) { } public TestClassWithConstructorParameters(int id = 42) { } public TestClassWithConstructorParameters(string id, int value = 100) { } } + + private class TestClassWithInheritedConstructorParameters + { + public TestClassWithInheritedConstructorParameters(BaseParameter parameter) { } + public TestClassWithInheritedConstructorParameters(DerivedParameter parameter) { } + } // ReSharper restore UnusedParameter.Local // ReSharper restore UnusedMember.Local } diff --git a/Tests/aweXpect.Reflection.Tests/Filters/MethodFilters.WithExactParameter.Tests.cs b/Tests/aweXpect.Reflection.Tests/Filters/MethodFilters.WithExactParameter.Tests.cs new file mode 100644 index 0000000..dc02581 --- /dev/null +++ b/Tests/aweXpect.Reflection.Tests/Filters/MethodFilters.WithExactParameter.Tests.cs @@ -0,0 +1,74 @@ +using aweXpect.Reflection.Collections; + +namespace aweXpect.Reflection.Tests.Filters; + +public sealed partial class MethodFilters +{ + public sealed class WithExactParameter + { + public sealed class Tests + { + [Fact] + public async Task ShouldFilterForMethodsWithParameterOfExactType() + { + Filtered.Methods methods = In.Type() + .Methods().WithExactParameter(); + + await That(methods).IsEqualTo([ + typeof(TestClass).GetMethod(nameof(TestClass.MethodWithDummyBase))!, + ]).InAnyOrder(); + await That(methods.GetDescription()) + .IsEqualTo("methods with parameter of exact type ").AsPrefix(); + } + + [Fact] + public async Task ShouldNotIncludeMethodsWithParameterOfDerivedType() + { + Filtered.Methods exactMethods = In.Type() + .Methods().WithExactParameter(); + Filtered.Methods assignableMethods = In.Type() + .Methods().WithParameter(); + + await That(exactMethods).IsEqualTo([ + typeof(TestClass).GetMethod(nameof(TestClass.MethodWithDummyBase))!, + ]).InAnyOrder(); + await That(assignableMethods).IsEqualTo([ + typeof(TestClass).GetMethod(nameof(TestClass.MethodWithDummyBase))!, + typeof(TestClass).GetMethod(nameof(TestClass.MethodWithDummy))!, + ]).InAnyOrder(); + } + + [Fact] + public async Task WithName_ShouldFilterForMethodsWithParameterOfExactTypeAndName() + { + Filtered.Methods methods = In.Type() + .Methods().WithExactParameter("parameter"); + + await That(methods).IsEqualTo([ + typeof(TestClass).GetMethod(nameof(TestClass.MethodWithDummyBase))!, + ]).InAnyOrder(); + await That(methods.GetDescription()) + .IsEqualTo("methods with parameter of exact type ").AsPrefix(); + } + } + +#pragma warning disable CA1822 + // ReSharper disable UnusedParameter.Local + private class TestClass + { + public void MethodWithDummyBase(DummyBase parameter) { } + public void MethodWithDummy(Dummy parameter) { } + public void MethodWithString(string parameter) { } + } + // ReSharper restore UnusedParameter.Local +#pragma warning restore CA1822 + + private class DummyBase + { + } + + private class Dummy : DummyBase + { + } + } +} diff --git a/Tests/aweXpect.Reflection.Tests/Filters/MethodFilters.WithParameter.Tests.cs b/Tests/aweXpect.Reflection.Tests/Filters/MethodFilters.WithParameter.Tests.cs index 153ce0c..d9cd5a0 100644 --- a/Tests/aweXpect.Reflection.Tests/Filters/MethodFilters.WithParameter.Tests.cs +++ b/Tests/aweXpect.Reflection.Tests/Filters/MethodFilters.WithParameter.Tests.cs @@ -38,8 +38,11 @@ await That(methods.GetDescription()) [Fact] public async Task WithParameterAtIndex_WhenIndexIsNegative_ShouldThrowArgumentOutOfRangeException() { - void Act() => In.Type() - .Methods().WithParameter().AtIndex(-1); + void Act() + { + In.Type() + .Methods().WithParameter().AtIndex(-1); + } await That(Act).Throws() .WithParamName("index").And @@ -196,6 +199,22 @@ await That(methods.GetDescription()) .IsEqualTo("methods with parameter of type string in").AsPrefix(); } + [Fact] + public async Task WithParameterOfType_ShouldIncludeMethodsWithParameterOfDerivedType() + { + Filtered.Methods methods = In.Type() + .Methods().WithParameter(); + + await That(methods).IsEqualTo([ + typeof(TestClassWithInheritedParameters).GetMethod( + nameof(TestClassWithInheritedParameters.MethodWithBaseParameter))!, + typeof(TestClassWithInheritedParameters).GetMethod( + nameof(TestClassWithInheritedParameters.MethodWithDerivedParameter))!, + ]).InAnyOrder(); + await That(methods.GetDescription()) + .IsEqualTo("methods with parameter of type ").AsPrefix(); + } + [Fact] public async Task WithParameterOfTypeAndName_ShouldFilterForMethodsWithParameterOfSpecificTypeAndName() { @@ -244,6 +263,14 @@ await That(methods.GetDescription()) } } + private class BaseParameter + { + } + + private class DerivedParameter : BaseParameter + { + } + #pragma warning disable CA1822 // ReSharper disable UnusedParameter.Local private class TestClassWithMethodParameters @@ -254,6 +281,12 @@ public void Method2(int id, string name) { } public void Method3(int id = 42) { } public void Method4(string id, int value = 100) { } } + + private class TestClassWithInheritedParameters + { + public void MethodWithBaseParameter(BaseParameter parameter) { } + public void MethodWithDerivedParameter(DerivedParameter parameter) { } + } // ReSharper restore UnusedParameter.Local #pragma warning restore CA1822 }