From 76bca3d8d743a93d4fc744099c1dcbc97dc2f6bd Mon Sep 17 00:00:00 2001 From: Renato Golia Date: Wed, 26 Aug 2020 21:59:27 +0200 Subject: [PATCH 1/5] First draft --- Src/Idioms/EqualityComparerAssertion.cs | 236 ++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 Src/Idioms/EqualityComparerAssertion.cs diff --git a/Src/Idioms/EqualityComparerAssertion.cs b/Src/Idioms/EqualityComparerAssertion.cs new file mode 100644 index 000000000..108d2754c --- /dev/null +++ b/Src/Idioms/EqualityComparerAssertion.cs @@ -0,0 +1,236 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.Idioms +{ + public class EqualityComparerAssertion : CompositeIdiomaticAssertion + { + public EqualityComparerAssertion(ISpecimenBuilder builder) : base(CreateChildAssertions(builder)) { } + + private static IEnumerable CreateChildAssertions(ISpecimenBuilder builder) + { + yield return new EqualityComparerEqualsSelfAssertion(builder); + + yield return new EqualityComparerEqualsSymmetricAssertion(builder); + + yield return new EqualityComparerEqualsTransitiveAssertion(builder); + + yield return new EqualityComparerGetHashCodeAssertion(builder); + + yield return new EqualityComparerEqualsNullAssertion(builder); + } + } + + public class EqualityComparerGetHashCodeAssertion : IdiomaticAssertion + { + public ISpecimenBuilder Builder { get; } + + public EqualityComparerGetHashCodeAssertion(ISpecimenBuilder builder) + { + this.Builder = builder ?? throw new ArgumentNullException(nameof(builder)); + } + + public override void Verify(MethodInfo methodInfo) + { + if (methodInfo is { Name: nameof(IEqualityComparer.GetHashCode), ReflectedType: { } type } && methodInfo.GetParameters().Length == 1 && type.ImplementsGenericInterfaceDefinition(typeof(IEqualityComparer<>))) + { + var argumentType = methodInfo.GetParameters()[0].ParameterType; + + if (!methodInfo.ReflectedType.ImplementsGenericInterface(typeof(IEqualityComparer<>), argumentType)) + { + return; + } + + var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); + + var testSubject = this.Builder.CreateAnonymous(argumentType); + + var firstHashCode = (int)methodInfo.Invoke(comparer, new[] { testSubject }); + + var secondHashCode = (int)methodInfo.Invoke(comparer, new[] { testSubject }); + + if (firstHashCode != secondHashCode) + { + throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, + "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + + "calling GetHashCode(x) should always return same value.", + type.FullName)); + } + } + } + } + + public abstract class EqualityComparerEqualsAssertion : IdiomaticAssertion + { + public ISpecimenBuilder Builder { get; } + + protected EqualityComparerEqualsAssertion(ISpecimenBuilder builder) + { + this.Builder = builder ?? throw new ArgumentNullException(nameof(builder)); + } + + public override void Verify(MethodInfo methodInfo) + { + if (methodInfo is { Name: nameof(IEqualityComparer.Equals), ReflectedType: { } type } && methodInfo.GetParameters().Length == 2 && type.ImplementsGenericInterfaceDefinition(typeof(IEqualityComparer<>))) + { + var argumentType = methodInfo.GetParameters()[0].ParameterType; + + if (!methodInfo.ReflectedType.ImplementsGenericInterface(typeof(IEqualityComparer<>), argumentType)) + { + return; + } + + this.VerifyEquals(methodInfo, argumentType); + } + } + + protected abstract void VerifyEquals(MethodInfo methodInfo, Type argumentType); + } + + public class EqualityComparerEqualsSelfAssertion : EqualityComparerEqualsAssertion + { + public EqualityComparerEqualsSelfAssertion(ISpecimenBuilder builder) : base(builder) { } + + protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) + { + var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); + + var testSubject = this.Builder.CreateAnonymous(argumentType); + + var result = (bool)methodInfo.Invoke(comparer, new[] { testSubject, testSubject }); + + if (!result) + { + throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, + "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + + "calling Equals(x, x) should return true.", + methodInfo.ReflectedType!.FullName)); + } + } + } + + public class EqualityComparerEqualsSymmetricAssertion : EqualityComparerEqualsAssertion + { + public EqualityComparerEqualsSymmetricAssertion(ISpecimenBuilder builder) : base(builder) { } + + protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) + { + var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); + + var firstTestSubject = this.Builder.CreateAnonymous(argumentType); + + var secondTestSubject = this.Builder.CreateAnonymous(argumentType); + + var directResult = (bool)methodInfo.Invoke(comparer, new[] { firstTestSubject, secondTestSubject }); + + var invertedResult = (bool)methodInfo.Invoke(comparer, new[] { secondTestSubject, firstTestSubject }); + + if (directResult != invertedResult) + { + throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, + "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + + "calling Equals(x, y) should return same as Equals(y, x).", + methodInfo.ReflectedType!.FullName)); + } + } + } + + public class EqualityComparerEqualsTransitiveAssertion : EqualityComparerEqualsAssertion + { + public EqualityComparerEqualsTransitiveAssertion(ISpecimenBuilder builder) : base(builder) { } + + protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) + { + var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); + + var firstTestSubject = this.Builder.CreateAnonymous(argumentType); + + var secondTestSubject = this.Builder.CreateAnonymous(argumentType); + + var thirdTestSubject = this.Builder.CreateAnonymous(argumentType); + + var firstResult = (bool)methodInfo.Invoke(comparer, new[] { firstTestSubject, secondTestSubject }); + + var secondResult = (bool)methodInfo.Invoke(comparer, new[] { secondTestSubject, thirdTestSubject }); + + var thirdResult = (bool)methodInfo.Invoke(comparer, new[] { firstTestSubject, thirdTestSubject }); + + if (firstResult != secondResult || firstResult != thirdResult) + { + throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, + "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + + "calling Equals(x, z) should return same as Equals(x, y) and Equals(y, z).", + methodInfo.ReflectedType!.FullName)); + } + } + } + + public class EqualityComparerEqualsNullAssertion : EqualityComparerEqualsAssertion + { + public EqualityComparerEqualsNullAssertion(ISpecimenBuilder builder) : base(builder) { } + + protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) + { + if (methodInfo.ReflectedType!.IsValueType) + { + return; + } + + var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); + + var testSubject = this.Builder.CreateAnonymous(argumentType); + + var result = (bool)methodInfo.Invoke(comparer, new[] { testSubject, null }); + + if (result) + { + throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, + "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + + "calling Equals(x, null) should return false.", + methodInfo.ReflectedType!.FullName)); + } + } + } + + public class EqualityComparerEqualsNullNullAssertion : EqualityComparerEqualsAssertion + { + public EqualityComparerEqualsNullNullAssertion(ISpecimenBuilder builder) : base(builder) { } + + protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) + { + if (methodInfo.ReflectedType!.IsValueType) + { + return; + } + + var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); + + var result = (bool)methodInfo.Invoke(comparer, new object[] { null, null }); + + if (!result) + { + throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, + "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + + "calling Equals(null, null) should return true.", + methodInfo.ReflectedType!.FullName)); + } + } + } + + public class EqualityComparerImplementationException : Exception + { + public EqualityComparerImplementationException(string message) : base(message) { } + } + + internal static class TypeExtensions + { + public static bool ImplementsGenericInterfaceDefinition(this Type type, Type interfaceType) => type.GetInterfaces().Any(i => i.GetGenericTypeDefinition().IsAssignableFrom(interfaceType)); + + public static bool ImplementsGenericInterface(this Type type, Type interfaceType, params Type[] argumentTypes) => type.GetInterfaces().Any(i => i.IsAssignableFrom(interfaceType.MakeGenericType(argumentTypes))); + } +} \ No newline at end of file From 2b0520b49eb2a549cb2e0fad19790967bd6dacec Mon Sep 17 00:00:00 2001 From: Renato Golia Date: Sat, 5 Sep 2020 18:46:37 +0200 Subject: [PATCH 2/5] Fixed FxCop issues --- Src/Idioms/EqualityComparerAssertion.cs | 236 ++---------------- Src/Idioms/EqualityComparerEqualsAssertion.cs | 65 +++++ .../EqualityComparerEqualsNullAssertion.cs | 65 +++++ ...EqualityComparerEqualsNullNullAssertion.cs | 63 +++++ .../EqualityComparerEqualsSelfAssertion.cs | 60 +++++ ...qualityComparerEqualsSymmetricAssertion.cs | 64 +++++ ...ualityComparerEqualsTransitiveAssertion.cs | 68 +++++ .../EqualityComparerGetHashCodeAssertion.cs | 75 ++++++ ...EqualityComparerImplementationException.cs | 56 +++++ Src/Idioms/TypeExtensions.cs | 12 + 10 files changed, 549 insertions(+), 215 deletions(-) create mode 100644 Src/Idioms/EqualityComparerEqualsAssertion.cs create mode 100644 Src/Idioms/EqualityComparerEqualsNullAssertion.cs create mode 100644 Src/Idioms/EqualityComparerEqualsNullNullAssertion.cs create mode 100644 Src/Idioms/EqualityComparerEqualsSelfAssertion.cs create mode 100644 Src/Idioms/EqualityComparerEqualsSymmetricAssertion.cs create mode 100644 Src/Idioms/EqualityComparerEqualsTransitiveAssertion.cs create mode 100644 Src/Idioms/EqualityComparerGetHashCodeAssertion.cs create mode 100644 Src/Idioms/EqualityComparerImplementationException.cs create mode 100644 Src/Idioms/TypeExtensions.cs diff --git a/Src/Idioms/EqualityComparerAssertion.cs b/Src/Idioms/EqualityComparerAssertion.cs index 108d2754c..848528702 100644 --- a/Src/Idioms/EqualityComparerAssertion.cs +++ b/Src/Idioms/EqualityComparerAssertion.cs @@ -1,16 +1,30 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Reflection; +using System.Collections.Generic; using AutoFixture.Kernel; namespace AutoFixture.Idioms { + /// + /// Encapsulates a unit test that verifies that a type implementing implements it correctly. + /// public class EqualityComparerAssertion : CompositeIdiomaticAssertion { - public EqualityComparerAssertion(ISpecimenBuilder builder) : base(CreateChildAssertions(builder)) { } + /// + /// Initializes a new instance of the class. + /// + /// + /// A composer which can create instances required to implement the idiomatic unit test, + /// such as the owner of the property, as well as the value to be assigned and read from + /// the member. + /// + /// + /// + /// will typically be a instance. + /// + /// + public EqualityComparerAssertion(ISpecimenBuilder builder) + : base(CreateChildAssertions(builder)) + { + } private static IEnumerable CreateChildAssertions(ISpecimenBuilder builder) { @@ -25,212 +39,4 @@ private static IEnumerable CreateChildAssertions(ISpecimenB yield return new EqualityComparerEqualsNullAssertion(builder); } } - - public class EqualityComparerGetHashCodeAssertion : IdiomaticAssertion - { - public ISpecimenBuilder Builder { get; } - - public EqualityComparerGetHashCodeAssertion(ISpecimenBuilder builder) - { - this.Builder = builder ?? throw new ArgumentNullException(nameof(builder)); - } - - public override void Verify(MethodInfo methodInfo) - { - if (methodInfo is { Name: nameof(IEqualityComparer.GetHashCode), ReflectedType: { } type } && methodInfo.GetParameters().Length == 1 && type.ImplementsGenericInterfaceDefinition(typeof(IEqualityComparer<>))) - { - var argumentType = methodInfo.GetParameters()[0].ParameterType; - - if (!methodInfo.ReflectedType.ImplementsGenericInterface(typeof(IEqualityComparer<>), argumentType)) - { - return; - } - - var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); - - var testSubject = this.Builder.CreateAnonymous(argumentType); - - var firstHashCode = (int)methodInfo.Invoke(comparer, new[] { testSubject }); - - var secondHashCode = (int)methodInfo.Invoke(comparer, new[] { testSubject }); - - if (firstHashCode != secondHashCode) - { - throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, - "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + - "calling GetHashCode(x) should always return same value.", - type.FullName)); - } - } - } - } - - public abstract class EqualityComparerEqualsAssertion : IdiomaticAssertion - { - public ISpecimenBuilder Builder { get; } - - protected EqualityComparerEqualsAssertion(ISpecimenBuilder builder) - { - this.Builder = builder ?? throw new ArgumentNullException(nameof(builder)); - } - - public override void Verify(MethodInfo methodInfo) - { - if (methodInfo is { Name: nameof(IEqualityComparer.Equals), ReflectedType: { } type } && methodInfo.GetParameters().Length == 2 && type.ImplementsGenericInterfaceDefinition(typeof(IEqualityComparer<>))) - { - var argumentType = methodInfo.GetParameters()[0].ParameterType; - - if (!methodInfo.ReflectedType.ImplementsGenericInterface(typeof(IEqualityComparer<>), argumentType)) - { - return; - } - - this.VerifyEquals(methodInfo, argumentType); - } - } - - protected abstract void VerifyEquals(MethodInfo methodInfo, Type argumentType); - } - - public class EqualityComparerEqualsSelfAssertion : EqualityComparerEqualsAssertion - { - public EqualityComparerEqualsSelfAssertion(ISpecimenBuilder builder) : base(builder) { } - - protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) - { - var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); - - var testSubject = this.Builder.CreateAnonymous(argumentType); - - var result = (bool)methodInfo.Invoke(comparer, new[] { testSubject, testSubject }); - - if (!result) - { - throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, - "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + - "calling Equals(x, x) should return true.", - methodInfo.ReflectedType!.FullName)); - } - } - } - - public class EqualityComparerEqualsSymmetricAssertion : EqualityComparerEqualsAssertion - { - public EqualityComparerEqualsSymmetricAssertion(ISpecimenBuilder builder) : base(builder) { } - - protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) - { - var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); - - var firstTestSubject = this.Builder.CreateAnonymous(argumentType); - - var secondTestSubject = this.Builder.CreateAnonymous(argumentType); - - var directResult = (bool)methodInfo.Invoke(comparer, new[] { firstTestSubject, secondTestSubject }); - - var invertedResult = (bool)methodInfo.Invoke(comparer, new[] { secondTestSubject, firstTestSubject }); - - if (directResult != invertedResult) - { - throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, - "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + - "calling Equals(x, y) should return same as Equals(y, x).", - methodInfo.ReflectedType!.FullName)); - } - } - } - - public class EqualityComparerEqualsTransitiveAssertion : EqualityComparerEqualsAssertion - { - public EqualityComparerEqualsTransitiveAssertion(ISpecimenBuilder builder) : base(builder) { } - - protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) - { - var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); - - var firstTestSubject = this.Builder.CreateAnonymous(argumentType); - - var secondTestSubject = this.Builder.CreateAnonymous(argumentType); - - var thirdTestSubject = this.Builder.CreateAnonymous(argumentType); - - var firstResult = (bool)methodInfo.Invoke(comparer, new[] { firstTestSubject, secondTestSubject }); - - var secondResult = (bool)methodInfo.Invoke(comparer, new[] { secondTestSubject, thirdTestSubject }); - - var thirdResult = (bool)methodInfo.Invoke(comparer, new[] { firstTestSubject, thirdTestSubject }); - - if (firstResult != secondResult || firstResult != thirdResult) - { - throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, - "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + - "calling Equals(x, z) should return same as Equals(x, y) and Equals(y, z).", - methodInfo.ReflectedType!.FullName)); - } - } - } - - public class EqualityComparerEqualsNullAssertion : EqualityComparerEqualsAssertion - { - public EqualityComparerEqualsNullAssertion(ISpecimenBuilder builder) : base(builder) { } - - protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) - { - if (methodInfo.ReflectedType!.IsValueType) - { - return; - } - - var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); - - var testSubject = this.Builder.CreateAnonymous(argumentType); - - var result = (bool)methodInfo.Invoke(comparer, new[] { testSubject, null }); - - if (result) - { - throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, - "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + - "calling Equals(x, null) should return false.", - methodInfo.ReflectedType!.FullName)); - } - } - } - - public class EqualityComparerEqualsNullNullAssertion : EqualityComparerEqualsAssertion - { - public EqualityComparerEqualsNullNullAssertion(ISpecimenBuilder builder) : base(builder) { } - - protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) - { - if (methodInfo.ReflectedType!.IsValueType) - { - return; - } - - var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); - - var result = (bool)methodInfo.Invoke(comparer, new object[] { null, null }); - - if (!result) - { - throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, - "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + - "calling Equals(null, null) should return true.", - methodInfo.ReflectedType!.FullName)); - } - } - } - - public class EqualityComparerImplementationException : Exception - { - public EqualityComparerImplementationException(string message) : base(message) { } - } - - internal static class TypeExtensions - { - public static bool ImplementsGenericInterfaceDefinition(this Type type, Type interfaceType) => type.GetInterfaces().Any(i => i.GetGenericTypeDefinition().IsAssignableFrom(interfaceType)); - - public static bool ImplementsGenericInterface(this Type type, Type interfaceType, params Type[] argumentTypes) => type.GetInterfaces().Any(i => i.IsAssignableFrom(interfaceType.MakeGenericType(argumentTypes))); - } } \ No newline at end of file diff --git a/Src/Idioms/EqualityComparerEqualsAssertion.cs b/Src/Idioms/EqualityComparerEqualsAssertion.cs new file mode 100644 index 000000000..880aeccbc --- /dev/null +++ b/Src/Idioms/EqualityComparerEqualsAssertion.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.Idioms +{ + /// + /// A base class for building classes that encapsulate a unit test that verifies that a type implementing implements it correctly. + /// + public abstract class EqualityComparerEqualsAssertion : IdiomaticAssertion + { + /// + /// Gets the builder supplied by the constructor. + /// + public ISpecimenBuilder Builder { get; } + + /// + /// Initializes a new instance of the class. + /// + /// + /// A composer which can create instances required to implement the idiomatic unit test, + /// such as the owner of the property, as well as the value to be assigned and read from + /// the member. + /// + /// + /// + /// will typically be a instance. + /// + /// + protected EqualityComparerEqualsAssertion(ISpecimenBuilder builder) + { + this.Builder = builder ?? throw new ArgumentNullException(nameof(builder)); + } + + /// + /// Forwards the call to if the supplied method is an implementation of . + /// + /// The method to verify. + public override void Verify(MethodInfo methodInfo) + { + if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); + + if (methodInfo is { Name: nameof(IEqualityComparer.Equals), ReflectedType: { } type } && methodInfo.GetParameters().Length == 2 && type.ImplementsGenericInterfaceDefinition(typeof(IEqualityComparer<>))) + { + var argumentType = methodInfo.GetParameters()[0].ParameterType; + + if (!methodInfo.ReflectedType.ImplementsGenericInterface(typeof(IEqualityComparer<>), argumentType)) + { + return; + } + + this.VerifyEquals(methodInfo, argumentType); + } + } + + /// + /// Abstract method used to verify if the method implements correctly . + /// + /// The method to verify. + /// The argument type of . + protected abstract void VerifyEquals(MethodInfo methodInfo, Type argumentType); + } +} \ No newline at end of file diff --git a/Src/Idioms/EqualityComparerEqualsNullAssertion.cs b/Src/Idioms/EqualityComparerEqualsNullAssertion.cs new file mode 100644 index 000000000..70558ec98 --- /dev/null +++ b/Src/Idioms/EqualityComparerEqualsNullAssertion.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.Idioms +{ + /// + /// Encapsulates a unit test that verifies that a type implementing implements it correctly + /// with respect of the rule: calling Equals(x, null) should return false. + /// + public class EqualityComparerEqualsNullAssertion : EqualityComparerEqualsAssertion + { + /// + /// Initializes a new instance of the class. + /// + /// + /// A composer which can create instances required to implement the idiomatic unit test, + /// such as the owner of the property, as well as the value to be assigned and read from + /// the member. + /// + /// + /// + /// will typically be a instance. + /// + /// + public EqualityComparerEqualsNullAssertion(ISpecimenBuilder builder) + : base(builder) + { + } + + /// + /// Verifies that `calling Equals(x, null) should return false` + /// if the supplied method is an implementation of . + /// + /// The method to verify. + /// The argument type of . + protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) + { + if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); + + if (argumentType == null) throw new ArgumentNullException(nameof(argumentType)); + + if (methodInfo.ReflectedType!.IsValueType) + { + return; + } + + var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); + + var testSubject = this.Builder.CreateAnonymous(argumentType); + + var result = (bool)methodInfo.Invoke(comparer, new[] { testSubject, null }); + + if (result) + { + throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, + "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + + "calling Equals(x, null) should return false.", + methodInfo.ReflectedType!.FullName)); + } + } + } +} \ No newline at end of file diff --git a/Src/Idioms/EqualityComparerEqualsNullNullAssertion.cs b/Src/Idioms/EqualityComparerEqualsNullNullAssertion.cs new file mode 100644 index 000000000..164f972c4 --- /dev/null +++ b/Src/Idioms/EqualityComparerEqualsNullNullAssertion.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.Idioms +{ + /// + /// Encapsulates a unit test that verifies that a type implementing implements it correctly + /// with respect of the rule: calling Equals(null, null) should return true. + /// + public class EqualityComparerEqualsNullNullAssertion : EqualityComparerEqualsAssertion + { + /// + /// Initializes a new instance of the class. + /// + /// + /// A composer which can create instances required to implement the idiomatic unit test, + /// such as the owner of the property, as well as the value to be assigned and read from + /// the member. + /// + /// + /// + /// will typically be a instance. + /// + /// + public EqualityComparerEqualsNullNullAssertion(ISpecimenBuilder builder) + : base(builder) + { + } + + /// + /// Verifies that `calling Equals(null, null) should return true` + /// if the supplied method is an implementation of . + /// + /// The method to verify. + /// The argument type of . + protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) + { + if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); + + if (argumentType == null) throw new ArgumentNullException(nameof(argumentType)); + + if (methodInfo.ReflectedType!.IsValueType) + { + return; + } + + var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); + + var result = (bool)methodInfo.Invoke(comparer, new object[] { null, null }); + + if (!result) + { + throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, + "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + + "calling Equals(null, null) should return true.", + methodInfo.ReflectedType!.FullName)); + } + } + } +} \ No newline at end of file diff --git a/Src/Idioms/EqualityComparerEqualsSelfAssertion.cs b/Src/Idioms/EqualityComparerEqualsSelfAssertion.cs new file mode 100644 index 000000000..fb2115d34 --- /dev/null +++ b/Src/Idioms/EqualityComparerEqualsSelfAssertion.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.Idioms +{ + /// + /// Encapsulates a unit test that verifies that a type implementing implements it correctly + /// with respect of the rule: calling Equals(x, x) should return true. + /// + public class EqualityComparerEqualsSelfAssertion : EqualityComparerEqualsAssertion + { + /// + /// Initializes a new instance of the class. + /// + /// + /// A composer which can create instances required to implement the idiomatic unit test, + /// such as the owner of the property, as well as the value to be assigned and read from + /// the member. + /// + /// + /// + /// will typically be a instance. + /// + /// + public EqualityComparerEqualsSelfAssertion(ISpecimenBuilder builder) + : base(builder) + { + } + + /// + /// Verifies that `calling Equals(x, x) should return true` + /// if the supplied method is an implementation of . + /// + /// The method to verify. + /// The argument type of . + protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) + { + if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); + + if (argumentType == null) throw new ArgumentNullException(nameof(argumentType)); + + var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); + + var testSubject = this.Builder.CreateAnonymous(argumentType); + + var result = (bool)methodInfo.Invoke(comparer, new[] { testSubject, testSubject }); + + if (!result) + { + throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, + "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + + "calling Equals(x, x) should return true.", + methodInfo.ReflectedType!.FullName)); + } + } + } +} \ No newline at end of file diff --git a/Src/Idioms/EqualityComparerEqualsSymmetricAssertion.cs b/Src/Idioms/EqualityComparerEqualsSymmetricAssertion.cs new file mode 100644 index 000000000..3a7a6776e --- /dev/null +++ b/Src/Idioms/EqualityComparerEqualsSymmetricAssertion.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.Idioms +{ + /// + /// Encapsulates a unit test that verifies that a type implementing implements it correctly + /// with respect of the rule: calling Equals(x, y) should return same as Equals(y, x). + /// + public class EqualityComparerEqualsSymmetricAssertion : EqualityComparerEqualsAssertion + { + /// + /// Initializes a new instance of the class. + /// + /// + /// A composer which can create instances required to implement the idiomatic unit test, + /// such as the owner of the property, as well as the value to be assigned and read from + /// the member. + /// + /// + /// + /// will typically be a instance. + /// + /// + public EqualityComparerEqualsSymmetricAssertion(ISpecimenBuilder builder) + : base(builder) + { + } + + /// + /// Verifies that `calling Equals(x, y) should return same as Equals(y, x)` + /// if the supplied method is an implementation of . + /// + /// The method to verify. + /// The argument type of . + protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) + { + if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); + + if (argumentType == null) throw new ArgumentNullException(nameof(argumentType)); + + var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); + + var firstTestSubject = this.Builder.CreateAnonymous(argumentType); + + var secondTestSubject = this.Builder.CreateAnonymous(argumentType); + + var directResult = (bool)methodInfo.Invoke(comparer, new[] { firstTestSubject, secondTestSubject }); + + var invertedResult = (bool)methodInfo.Invoke(comparer, new[] { secondTestSubject, firstTestSubject }); + + if (directResult != invertedResult) + { + throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, + "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + + "calling Equals(x, y) should return same as Equals(y, x).", + methodInfo.ReflectedType!.FullName)); + } + } + } +} \ No newline at end of file diff --git a/Src/Idioms/EqualityComparerEqualsTransitiveAssertion.cs b/Src/Idioms/EqualityComparerEqualsTransitiveAssertion.cs new file mode 100644 index 000000000..4a5b154ba --- /dev/null +++ b/Src/Idioms/EqualityComparerEqualsTransitiveAssertion.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.Idioms +{ + /// + /// Encapsulates a unit test that verifies that a type implementing implements it correctly + /// with respect of the rule: calling Equals(x, z) should return same as Equals(x, y) and Equals(y, z). + /// + public class EqualityComparerEqualsTransitiveAssertion : EqualityComparerEqualsAssertion + { + /// + /// Initializes a new instance of the class. + /// + /// + /// A composer which can create instances required to implement the idiomatic unit test, + /// such as the owner of the property, as well as the value to be assigned and read from + /// the member. + /// + /// + /// + /// will typically be a instance. + /// + /// + public EqualityComparerEqualsTransitiveAssertion(ISpecimenBuilder builder) + : base(builder) + { + } + + /// + /// Verifies that `calling Equals(x, z) should return same as Equals(x, y) and Equals(y, z)` + /// if the supplied method is an implementation of . + /// + /// The method to verify. + /// The argument type of . + protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) + { + if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); + + if (argumentType == null) throw new ArgumentNullException(nameof(argumentType)); + + var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); + + var firstTestSubject = this.Builder.CreateAnonymous(argumentType); + + var secondTestSubject = this.Builder.CreateAnonymous(argumentType); + + var thirdTestSubject = this.Builder.CreateAnonymous(argumentType); + + var firstResult = (bool)methodInfo.Invoke(comparer, new[] { firstTestSubject, secondTestSubject }); + + var secondResult = (bool)methodInfo.Invoke(comparer, new[] { secondTestSubject, thirdTestSubject }); + + var thirdResult = (bool)methodInfo.Invoke(comparer, new[] { firstTestSubject, thirdTestSubject }); + + if (firstResult != secondResult || firstResult != thirdResult) + { + throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, + "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + + "calling Equals(x, z) should return same as Equals(x, y) and Equals(y, z).", + methodInfo.ReflectedType!.FullName)); + } + } + } +} \ No newline at end of file diff --git a/Src/Idioms/EqualityComparerGetHashCodeAssertion.cs b/Src/Idioms/EqualityComparerGetHashCodeAssertion.cs new file mode 100644 index 000000000..870ebfba3 --- /dev/null +++ b/Src/Idioms/EqualityComparerGetHashCodeAssertion.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using AutoFixture.Kernel; + +namespace AutoFixture.Idioms +{ + /// + /// Encapsulates a unit test that verifies that a type implementing implements it correctly + /// with respect of the rule: calling GetHashCode(x) should always return same value. + /// + public class EqualityComparerGetHashCodeAssertion : IdiomaticAssertion + { + /// + /// Gets the builder supplied by the constructor. + /// + public ISpecimenBuilder Builder { get; } + + /// + /// Initializes a new instance of the class. + /// + /// + /// A composer which can create instances required to implement the idiomatic unit test, + /// such as the owner of the property, as well as the value to be assigned and read from + /// the member. + /// + /// + /// + /// will typically be a instance. + /// + /// + public EqualityComparerGetHashCodeAssertion(ISpecimenBuilder builder) + { + this.Builder = builder ?? throw new ArgumentNullException(nameof(builder)); + } + + /// + /// Verifies that `calling GetHashCode(x) should always return same value` + /// if the supplied method is an implementation of . + /// + /// The method to verify. + public override void Verify(MethodInfo methodInfo) + { + if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); + + if (methodInfo is { Name: nameof(IEqualityComparer.GetHashCode), ReflectedType: { } type } && methodInfo.GetParameters().Length == 1 && type.ImplementsGenericInterfaceDefinition(typeof(IEqualityComparer<>))) + { + var argumentType = methodInfo.GetParameters()[0].ParameterType; + + if (!methodInfo.ReflectedType.ImplementsGenericInterface(typeof(IEqualityComparer<>), argumentType)) + { + return; + } + + var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); + + var testSubject = this.Builder.CreateAnonymous(argumentType); + + var firstHashCode = (int)methodInfo.Invoke(comparer, new[] { testSubject }); + + var secondHashCode = (int)methodInfo.Invoke(comparer, new[] { testSubject }); + + if (firstHashCode != secondHashCode) + { + throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, + "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + + "calling GetHashCode(x) should always return same value.", + type.FullName)); + } + } + } + } +} \ No newline at end of file diff --git a/Src/Idioms/EqualityComparerImplementationException.cs b/Src/Idioms/EqualityComparerImplementationException.cs new file mode 100644 index 000000000..61d3d7dac --- /dev/null +++ b/Src/Idioms/EqualityComparerImplementationException.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace AutoFixture.Idioms +{ + /// + /// Represents an error about an ill-behaved implementation of the interface. + /// + [Serializable] + public class EqualityComparerImplementationException : Exception + { + /// + /// Initializes a new instance of teh class. + /// + public EqualityComparerImplementationException() + : base("The implementation of IEqualityComparer is ill-behaved.") + { + } + + /// + /// Initializes a new instance of teh class. + /// + /// The error message that explains the reason for the exception. + public EqualityComparerImplementationException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of teh class. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. + public EqualityComparerImplementationException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of teh class. + /// + /// + /// The that holds the + /// serialized object data about the exception being thrown. + /// + /// + /// The that contains + /// contextual information about the source or destination. + /// + protected EqualityComparerImplementationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/Src/Idioms/TypeExtensions.cs b/Src/Idioms/TypeExtensions.cs new file mode 100644 index 000000000..1d931b83f --- /dev/null +++ b/Src/Idioms/TypeExtensions.cs @@ -0,0 +1,12 @@ +using System; +using System.Linq; + +namespace AutoFixture.Idioms +{ + internal static class TypeExtensions + { + public static bool ImplementsGenericInterfaceDefinition(this Type type, Type interfaceType) => type.GetInterfaces().Any(i => i.GetGenericTypeDefinition().IsAssignableFrom(interfaceType)); + + public static bool ImplementsGenericInterface(this Type type, Type interfaceType, params Type[] argumentTypes) => type.GetInterfaces().Any(i => i.IsAssignableFrom(interfaceType.MakeGenericType(argumentTypes))); + } +} \ No newline at end of file From 4d3b1b4b19a18b9960107c245a89cd8ee5a79035 Mon Sep 17 00:00:00 2001 From: Renato Golia Date: Sat, 5 Sep 2020 21:54:28 +0200 Subject: [PATCH 3/5] Migrated unit tests --- .../EqualityComparerAssertionTest.cs | 153 ++++++++++++++++++ ...EqualityComparerEqualsNullAssertionTest.cs | 136 ++++++++++++++++ ...lityComparerEqualsNullNullAssertionTest.cs | 136 ++++++++++++++++ ...EqualityComparerEqualsSelfAssertionTest.cs | 136 ++++++++++++++++ ...ityComparerEqualsSymmetricAssertionTest.cs | 137 ++++++++++++++++ ...tyComparerEqualsTransitiveAssertionTest.cs | 137 ++++++++++++++++ ...qualityComparerGetHashCodeAssertionTest.cs | 123 ++++++++++++++ 7 files changed, 958 insertions(+) create mode 100644 Src/IdiomsUnitTest/EqualityComparerAssertionTest.cs create mode 100644 Src/IdiomsUnitTest/EqualityComparerEqualsNullAssertionTest.cs create mode 100644 Src/IdiomsUnitTest/EqualityComparerEqualsNullNullAssertionTest.cs create mode 100644 Src/IdiomsUnitTest/EqualityComparerEqualsSelfAssertionTest.cs create mode 100644 Src/IdiomsUnitTest/EqualityComparerEqualsSymmetricAssertionTest.cs create mode 100644 Src/IdiomsUnitTest/EqualityComparerEqualsTransitiveAssertionTest.cs create mode 100644 Src/IdiomsUnitTest/EqualityComparerGetHashCodeAssertionTest.cs diff --git a/Src/IdiomsUnitTest/EqualityComparerAssertionTest.cs b/Src/IdiomsUnitTest/EqualityComparerAssertionTest.cs new file mode 100644 index 000000000..f54a125b8 --- /dev/null +++ b/Src/IdiomsUnitTest/EqualityComparerAssertionTest.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using AutoFixture.Idioms; +using TestTypeFoundation; +using Xunit; + +namespace AutoFixture.IdiomsUnitTest +{ + public class EqualityComparerAssertionTest + { + [Fact] + public void SutIsIdiomaticAssertion() + { + // Arrange + var dummyComposer = new Fixture(); + // Act + var sut = new EqualityComparerAssertion(dummyComposer); + // Assert + Assert.IsAssignableFrom(sut); + } + + [Fact] + public void ConstructWithNullComposerThrows() + { + // Arrange + // Act & Assert + Assert.Throws(() => + new EqualityComparerAssertion(null)); + } + + [Fact] + public void VerifyNullMethodThrows() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerAssertion(dummyComposer); + // Act & Assert + Assert.Throws(() => + sut.Verify((MethodInfo)null)); + } + + [Fact] + public void VerifyNonEqualityComparerDoesNothing() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerAssertion(dummyComposer); + // Act & Assert + Assert.Null(Record.Exception(() => + sut.Verify(typeof(NonEqualityComparer)))); + } + + [Fact] + public void VerifyWellBehavedEqualityComparerDoesNotThrow() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerAssertion(dummyComposer); + // Act & Assert + Assert.Null(Record.Exception(() => + sut.Verify(typeof(WellBehavedEqualityComparer)))); + } + + [Fact] + public void VerifyIllBehavedNullEqualityComparerThrows() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerAssertion(dummyComposer); + // Act & Assert + Assert.Throws(() => + sut.Verify(typeof(IllBehavedNullEqualityComparer))); + } + + [Fact] + public void VerifyIllBehavedSelfEqualityComparerThrows() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerAssertion(dummyComposer); + // Act & Assert + Assert.Throws(() => + sut.Verify(typeof(IllBehavedSelfEqualityComparer))); + } +#pragma warning disable 659 + private class WellBehavedEqualityComparer : IEqualityComparer> + { + public bool Equals(PropertyHolder x, PropertyHolder y) + { + if (ReferenceEquals(x, y)) + return true; + + if (ReferenceEquals(x, null)) + return false; + + if (ReferenceEquals(y, null)) + return false; + + if (x.GetType() != y.GetType()) + return false; + + return x.Property == y.Property; + } + + public int GetHashCode(PropertyHolder obj) + { + return obj.Property; + } + } + + private class IllBehavedNullEqualityComparer : IEqualityComparer> + { + public bool Equals(PropertyHolder x, PropertyHolder y) + { + if (x == null && y == null) + return false; + + if (x == null ^ y == null) + return true; + + return false; + } + + public int GetHashCode(PropertyHolder obj) + { + throw new Exception(); + } + } + + private class IllBehavedSelfEqualityComparer : IEqualityComparer> + { + public bool Equals(PropertyHolder x, PropertyHolder y) + { + if (ReferenceEquals(x, y)) + return false; + + return true; + } + + public int GetHashCode(PropertyHolder obj) + { + throw new Exception(); + } + } + +#pragma warning restore 659 + + private class NonEqualityComparer + { + } + } +} \ No newline at end of file diff --git a/Src/IdiomsUnitTest/EqualityComparerEqualsNullAssertionTest.cs b/Src/IdiomsUnitTest/EqualityComparerEqualsNullAssertionTest.cs new file mode 100644 index 000000000..9c395dd98 --- /dev/null +++ b/Src/IdiomsUnitTest/EqualityComparerEqualsNullAssertionTest.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using AutoFixture.Idioms; +using AutoFixture.Kernel; +using TestTypeFoundation; +using Xunit; + +namespace AutoFixture.IdiomsUnitTest +{ + public class EqualityComparerEqualsNullAssertionTest + { + [Fact] + public void SutIsIdiomaticAssertion() + { + // Arrange + var dummyComposer = new Fixture(); + // Act + var sut = new EqualityComparerEqualsNullAssertion(dummyComposer); + // Assert + Assert.IsAssignableFrom(sut); + } + + [Fact] + public void ComposerIsCorrect() + { + // Arrange + var expectedComposer = new Fixture(); + var sut = new EqualityComparerEqualsNullAssertion(expectedComposer); + // Act + ISpecimenBuilder result = sut.Builder; + // Assert + Assert.Equal(expectedComposer, result); + } + + [Fact] + public void ConstructWithNullComposerThrows() + { + // Arrange + // Act & Assert + Assert.Throws(() => + new EqualityComparerEqualsNullAssertion(null)); + } + + [Fact] + public void VerifyNullMethodThrows() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsNullAssertion(dummyComposer); + // Act & Assert + Assert.Throws(() => + sut.Verify((MethodInfo)null)); + } + + [Fact] + public void VerifyNonEqualityComparerDoesNothing() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsNullAssertion(dummyComposer); + // Act & Assert + Assert.Null(Record.Exception(() => + sut.Verify(typeof(NonEqualityComparer)))); + } + + [Fact] + public void VerifyWellBehavedEqualityComparerDoesNotThrow() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsNullAssertion(dummyComposer); + // Act & Assert + Assert.Null(Record.Exception(() => + sut.Verify(typeof(WellBehavedEqualityComparer)))); + } + + [Fact] + public void VerifyIllBehavedEqualityComparerThrows() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsNullAssertion(dummyComposer); + // Act & Assert + Assert.Throws(() => + sut.Verify(typeof(IllBehavedEqualityComparer))); + } + +#pragma warning disable 659 + private class WellBehavedEqualityComparer : IEqualityComparer> + { + public bool Equals(PropertyHolder x, PropertyHolder y) + { + if (ReferenceEquals(x, y)) + return true; + + if (ReferenceEquals(x, null)) + return false; + + if (ReferenceEquals(y, null)) + return false; + + if (x.GetType() != y.GetType()) + return false; + + return x.Property == y.Property; + } + + public int GetHashCode(PropertyHolder obj) + { + return obj.Property; + } + } + + private class IllBehavedEqualityComparer : IEqualityComparer> + { + public bool Equals(PropertyHolder x, PropertyHolder y) + { + if (x == null ^ y == null) + return true; + + return false; + } + + public int GetHashCode(PropertyHolder obj) + { + throw new Exception(); + } + } +#pragma warning restore 659 + + private class NonEqualityComparer + { + } + } +} \ No newline at end of file diff --git a/Src/IdiomsUnitTest/EqualityComparerEqualsNullNullAssertionTest.cs b/Src/IdiomsUnitTest/EqualityComparerEqualsNullNullAssertionTest.cs new file mode 100644 index 000000000..e35267bc6 --- /dev/null +++ b/Src/IdiomsUnitTest/EqualityComparerEqualsNullNullAssertionTest.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using AutoFixture.Idioms; +using AutoFixture.Kernel; +using TestTypeFoundation; +using Xunit; + +namespace AutoFixture.IdiomsUnitTest +{ + public class EqualityComparerEqualsNullNullAssertionTest + { + [Fact] + public void SutIsIdiomaticAssertion() + { + // Arrange + var dummyComposer = new Fixture(); + // Act + var sut = new EqualityComparerEqualsNullNullAssertion(dummyComposer); + // Assert + Assert.IsAssignableFrom(sut); + } + + [Fact] + public void ComposerIsCorrect() + { + // Arrange + var expectedComposer = new Fixture(); + var sut = new EqualityComparerEqualsNullNullAssertion(expectedComposer); + // Act + ISpecimenBuilder result = sut.Builder; + // Assert + Assert.Equal(expectedComposer, result); + } + + [Fact] + public void ConstructWithNullComposerThrows() + { + // Arrange + // Act & Assert + Assert.Throws(() => + new EqualityComparerEqualsNullNullAssertion(null)); + } + + [Fact] + public void VerifyNullMethodThrows() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsNullNullAssertion(dummyComposer); + // Act & Assert + Assert.Throws(() => + sut.Verify((MethodInfo)null)); + } + + [Fact] + public void VerifyNonEqualityComparerDoesNothing() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsNullNullAssertion(dummyComposer); + // Act & Assert + Assert.Null(Record.Exception(() => + sut.Verify(typeof(NonEqualityComparer)))); + } + + [Fact] + public void VerifyWellBehavedEqualityComparerDoesNotThrow() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsNullNullAssertion(dummyComposer); + // Act & Assert + Assert.Null(Record.Exception(() => + sut.Verify(typeof(WellBehavedEqualityComparer)))); + } + + [Fact] + public void VerifyIllBehavedEqualityComparerThrows() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsNullNullAssertion(dummyComposer); + // Act & Assert + Assert.Throws(() => + sut.Verify(typeof(IllBehavedEqualityComparer))); + } + +#pragma warning disable 659 + private class WellBehavedEqualityComparer : IEqualityComparer> + { + public bool Equals(PropertyHolder x, PropertyHolder y) + { + if (ReferenceEquals(x, y)) + return true; + + if (ReferenceEquals(x, null)) + return false; + + if (ReferenceEquals(y, null)) + return false; + + if (x.GetType() != y.GetType()) + return false; + + return x.Property == y.Property; + } + + public int GetHashCode(PropertyHolder obj) + { + return obj.Property; + } + } + + private class IllBehavedEqualityComparer : IEqualityComparer> + { + public bool Equals(PropertyHolder x, PropertyHolder y) + { + if (x == null && y == null) + return false; + + return true; + } + + public int GetHashCode(PropertyHolder obj) + { + throw new Exception(); + } + } +#pragma warning restore 659 + + private class NonEqualityComparer + { + } + } +} \ No newline at end of file diff --git a/Src/IdiomsUnitTest/EqualityComparerEqualsSelfAssertionTest.cs b/Src/IdiomsUnitTest/EqualityComparerEqualsSelfAssertionTest.cs new file mode 100644 index 000000000..8730c8ab9 --- /dev/null +++ b/Src/IdiomsUnitTest/EqualityComparerEqualsSelfAssertionTest.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using AutoFixture.Idioms; +using AutoFixture.Kernel; +using TestTypeFoundation; +using Xunit; + +namespace AutoFixture.IdiomsUnitTest +{ + public class EqualityComparerEqualsSelfAssertionTest + { + [Fact] + public void SutIsIdiomaticAssertion() + { + // Arrange + var dummyComposer = new Fixture(); + // Act + var sut = new EqualityComparerEqualsSelfAssertion(dummyComposer); + // Assert + Assert.IsAssignableFrom(sut); + } + + [Fact] + public void ComposerIsCorrect() + { + // Arrange + var expectedComposer = new Fixture(); + var sut = new EqualityComparerEqualsSelfAssertion(expectedComposer); + // Act + ISpecimenBuilder result = sut.Builder; + // Assert + Assert.Equal(expectedComposer, result); + } + + [Fact] + public void ConstructWithNullComposerThrows() + { + // Arrange + // Act & Assert + Assert.Throws(() => + new EqualityComparerEqualsSelfAssertion(null)); + } + + [Fact] + public void VerifyNullMethodThrows() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsSelfAssertion(dummyComposer); + // Act & Assert + Assert.Throws(() => + sut.Verify((MethodInfo)null)); + } + + [Fact] + public void VerifyNonEqualityComparerDoesNothing() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsSelfAssertion(dummyComposer); + // Act & Assert + Assert.Null(Record.Exception(() => + sut.Verify(typeof(NonEqualityComparer)))); + } + + [Fact] + public void VerifyWellBehavedEqualityComparerDoesNotThrow() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsSelfAssertion(dummyComposer); + // Act & Assert + Assert.Null(Record.Exception(() => + sut.Verify(typeof(WellBehavedEqualityComparer)))); + } + + [Fact] + public void VerifyIllBehavedEqualityComparerThrows() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsSelfAssertion(dummyComposer); + // Act & Assert + Assert.Throws(() => + sut.Verify(typeof(IllBehavedEqualityComparer))); + } + +#pragma warning disable 659 + private class WellBehavedEqualityComparer : IEqualityComparer> + { + public bool Equals(PropertyHolder x, PropertyHolder y) + { + if (ReferenceEquals(x, y)) + return true; + + if (ReferenceEquals(x, null)) + return false; + + if (ReferenceEquals(y, null)) + return false; + + if (x.GetType() != y.GetType()) + return false; + + return x.Property == y.Property; + } + + public int GetHashCode(PropertyHolder obj) + { + return obj.Property; + } + } + + private class IllBehavedEqualityComparer : IEqualityComparer> + { + public bool Equals(PropertyHolder x, PropertyHolder y) + { + if (ReferenceEquals(x, y)) + return false; + + return true; + } + + public int GetHashCode(PropertyHolder obj) + { + throw new Exception(); + } + } +#pragma warning restore 659 + + private class NonEqualityComparer + { + } + } +} \ No newline at end of file diff --git a/Src/IdiomsUnitTest/EqualityComparerEqualsSymmetricAssertionTest.cs b/Src/IdiomsUnitTest/EqualityComparerEqualsSymmetricAssertionTest.cs new file mode 100644 index 000000000..51a9575fb --- /dev/null +++ b/Src/IdiomsUnitTest/EqualityComparerEqualsSymmetricAssertionTest.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using AutoFixture.Idioms; +using AutoFixture.Kernel; +using TestTypeFoundation; +using Xunit; + +namespace AutoFixture.IdiomsUnitTest +{ + public class EqualityComparerEqualsSymmetricAssertionTest + { + [Fact] + public void SutIsIdiomaticAssertion() + { + // Arrange + var dummyComposer = new Fixture(); + // Act + var sut = new EqualityComparerEqualsSymmetricAssertion(dummyComposer); + // Assert + Assert.IsAssignableFrom(sut); + } + + [Fact] + public void ComposerIsCorrect() + { + // Arrange + var expectedComposer = new Fixture(); + var sut = new EqualityComparerEqualsSymmetricAssertion(expectedComposer); + // Act + ISpecimenBuilder result = sut.Builder; + // Assert + Assert.Equal(expectedComposer, result); + } + + [Fact] + public void ConstructWithNullComposerThrows() + { + // Arrange + // Act & Assert + Assert.Throws(() => + new EqualityComparerEqualsSymmetricAssertion(null)); + } + + [Fact] + public void VerifyNullMethodThrows() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsSymmetricAssertion(dummyComposer); + // Act & Assert + Assert.Throws(() => + sut.Verify((MethodInfo)null)); + } + + [Fact] + public void VerifyNonEqualityComparerDoesNothing() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsSymmetricAssertion(dummyComposer); + // Act & Assert + Assert.Null(Record.Exception(() => + sut.Verify(typeof(NonEqualityComparer)))); + } + + [Fact] + public void VerifyWellBehavedEqualityComparerDoesNotThrow() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsSymmetricAssertion(dummyComposer); + // Act & Assert + Assert.Null(Record.Exception(() => + sut.Verify(typeof(WellBehavedEqualityComparer)))); + } + + [Fact] + public void VerifyIllBehavedEqualityComparerThrows() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsSymmetricAssertion(dummyComposer); + // Act & Assert + Assert.Throws(() => + sut.Verify(typeof(IllBehavedEqualityComparer))); + } + +#pragma warning disable 659 + private class WellBehavedEqualityComparer : IEqualityComparer> + { + public bool Equals(PropertyHolder x, PropertyHolder y) + { + if (ReferenceEquals(x, y)) + return true; + + if (ReferenceEquals(x, null)) + return false; + + if (ReferenceEquals(y, null)) + return false; + + if (x.GetType() != y.GetType()) + return false; + + return x.Property == y.Property; + } + + public int GetHashCode(PropertyHolder obj) + { + return obj.Property; + } + } + + private class IllBehavedEqualityComparer : IEqualityComparer> + { + private static bool flag = false; + + public bool Equals(PropertyHolder x, PropertyHolder y) + { + flag = !flag; + + return flag; + } + + public int GetHashCode(PropertyHolder obj) + { + throw new Exception(); + } + } +#pragma warning restore 659 + + private class NonEqualityComparer + { + } + } +} \ No newline at end of file diff --git a/Src/IdiomsUnitTest/EqualityComparerEqualsTransitiveAssertionTest.cs b/Src/IdiomsUnitTest/EqualityComparerEqualsTransitiveAssertionTest.cs new file mode 100644 index 000000000..fdc478ef2 --- /dev/null +++ b/Src/IdiomsUnitTest/EqualityComparerEqualsTransitiveAssertionTest.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using AutoFixture.Idioms; +using AutoFixture.Kernel; +using TestTypeFoundation; +using Xunit; + +namespace AutoFixture.IdiomsUnitTest +{ + public class EqualityComparerEqualsTransitiveAssertionTest + { + [Fact] + public void SutIsIdiomaticAssertion() + { + // Arrange + var dummyComposer = new Fixture(); + // Act + var sut = new EqualityComparerEqualsTransitiveAssertion(dummyComposer); + // Assert + Assert.IsAssignableFrom(sut); + } + + [Fact] + public void ComposerIsCorrect() + { + // Arrange + var expectedComposer = new Fixture(); + var sut = new EqualityComparerEqualsTransitiveAssertion(expectedComposer); + // Act + ISpecimenBuilder result = sut.Builder; + // Assert + Assert.Equal(expectedComposer, result); + } + + [Fact] + public void ConstructWithNullComposerThrows() + { + // Arrange + // Act & Assert + Assert.Throws(() => + new EqualityComparerEqualsTransitiveAssertion(null)); + } + + [Fact] + public void VerifyNullMethodThrows() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsTransitiveAssertion(dummyComposer); + // Act & Assert + Assert.Throws(() => + sut.Verify((MethodInfo)null)); + } + + [Fact] + public void VerifyNonEqualityComparerDoesNothing() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsTransitiveAssertion(dummyComposer); + // Act & Assert + Assert.Null(Record.Exception(() => + sut.Verify(typeof(NonEqualityComparer)))); + } + + [Fact] + public void VerifyWellBehavedEqualityComparerDoesNotThrow() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsTransitiveAssertion(dummyComposer); + // Act & Assert + Assert.Null(Record.Exception(() => + sut.Verify(typeof(WellBehavedEqualityComparer)))); + } + + [Fact] + public void VerifyIllBehavedEqualityComparerThrows() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerEqualsTransitiveAssertion(dummyComposer); + // Act & Assert + Assert.Throws(() => + sut.Verify(typeof(IllBehavedEqualityComparer))); + } + +#pragma warning disable 659 + private class WellBehavedEqualityComparer : IEqualityComparer> + { + public bool Equals(PropertyHolder x, PropertyHolder y) + { + if (ReferenceEquals(x, y)) + return true; + + if (ReferenceEquals(x, null)) + return false; + + if (ReferenceEquals(y, null)) + return false; + + if (x.GetType() != y.GetType()) + return false; + + return x.Property == y.Property; + } + + public int GetHashCode(PropertyHolder obj) + { + return obj.Property; + } + } + + private class IllBehavedEqualityComparer : IEqualityComparer> + { + private static bool flag = false; + + public bool Equals(PropertyHolder x, PropertyHolder y) + { + flag = !flag; + + return flag; + } + + public int GetHashCode(PropertyHolder obj) + { + throw new Exception(); + } + } +#pragma warning restore 659 + + private class NonEqualityComparer + { + } + } +} \ No newline at end of file diff --git a/Src/IdiomsUnitTest/EqualityComparerGetHashCodeAssertionTest.cs b/Src/IdiomsUnitTest/EqualityComparerGetHashCodeAssertionTest.cs new file mode 100644 index 000000000..192d5bdeb --- /dev/null +++ b/Src/IdiomsUnitTest/EqualityComparerGetHashCodeAssertionTest.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using AutoFixture.Idioms; +using AutoFixture.Kernel; +using TestTypeFoundation; +using Xunit; + +namespace AutoFixture.IdiomsUnitTest +{ + public class EqualityComparerGetHashCodeAssertionTest + { + [Fact] + public void SutIsIdiomaticAssertion() + { + // Arrange + var dummyComposer = new Fixture(); + // Act + var sut = new EqualityComparerGetHashCodeAssertion(dummyComposer); + // Assert + Assert.IsAssignableFrom(sut); + } + + [Fact] + public void ComposerIsCorrect() + { + // Arrange + var expectedComposer = new Fixture(); + var sut = new EqualityComparerGetHashCodeAssertion(expectedComposer); + // Act + ISpecimenBuilder result = sut.Builder; + // Assert + Assert.Equal(expectedComposer, result); + } + + [Fact] + public void ConstructWithNullComposerThrows() + { + // Arrange + // Act & Assert + Assert.Throws(() => + new EqualityComparerGetHashCodeAssertion(null)); + } + + [Fact] + public void VerifyNullMethodThrows() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerGetHashCodeAssertion(dummyComposer); + // Act & Assert + Assert.Throws(() => + sut.Verify((MethodInfo)null)); + } + + [Fact] + public void VerifyNonEqualityComparerDoesNothing() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerGetHashCodeAssertion(dummyComposer); + // Act & Assert + Assert.Null(Record.Exception(() => + sut.Verify(typeof(NonEqualityComparer)))); + } + + [Fact] + public void VerifyWellBehavedEqualityComparerDoesNotThrow() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerGetHashCodeAssertion(dummyComposer); + // Act & Assert + Assert.Null(Record.Exception(() => + sut.Verify(typeof(WellBehavedEqualityComparer)))); + } + + [Fact] + public void VerifyIllBehavedEqualityComparerThrows() + { + // Arrange + var dummyComposer = new Fixture(); + var sut = new EqualityComparerGetHashCodeAssertion(dummyComposer); + // Act & Assert + Assert.Throws(() => + sut.Verify(typeof(IllBehavedEqualityComparer))); + } + +#pragma warning disable 659 + private class WellBehavedEqualityComparer : IEqualityComparer> + { + public bool Equals(PropertyHolder x, PropertyHolder y) + { + throw new Exception(); + } + + public int GetHashCode(PropertyHolder obj) + { + return obj.Property; + } + } + + private class IllBehavedEqualityComparer : IEqualityComparer> + { + private static readonly Random HashCodeGenerator = new Random(); + + public bool Equals(PropertyHolder x, PropertyHolder y) + { + throw new Exception(); + } + + public int GetHashCode(PropertyHolder obj) + { + return HashCodeGenerator.Next(); + } + } +#pragma warning restore 659 + + private class NonEqualityComparer + { + } + } +} \ No newline at end of file From 8d41a0b66864908a592f5bb330a49da04960d225 Mon Sep 17 00:00:00 2001 From: Renato Golia Date: Sat, 5 Sep 2020 22:08:51 +0200 Subject: [PATCH 4/5] Small changes to EqualityComparerEqualsTransitiveAssertion --- Src/Idioms/EqualityComparerEqualsTransitiveAssertion.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Src/Idioms/EqualityComparerEqualsTransitiveAssertion.cs b/Src/Idioms/EqualityComparerEqualsTransitiveAssertion.cs index 4a5b154ba..bdd5e6543 100644 --- a/Src/Idioms/EqualityComparerEqualsTransitiveAssertion.cs +++ b/Src/Idioms/EqualityComparerEqualsTransitiveAssertion.cs @@ -50,13 +50,13 @@ protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) var thirdTestSubject = this.Builder.CreateAnonymous(argumentType); - var firstResult = (bool)methodInfo.Invoke(comparer, new[] { firstTestSubject, secondTestSubject }); + var firstToSecond = (bool)methodInfo.Invoke(comparer, new[] { firstTestSubject, secondTestSubject }); - var secondResult = (bool)methodInfo.Invoke(comparer, new[] { secondTestSubject, thirdTestSubject }); + var secondToThird = (bool)methodInfo.Invoke(comparer, new[] { secondTestSubject, thirdTestSubject }); - var thirdResult = (bool)methodInfo.Invoke(comparer, new[] { firstTestSubject, thirdTestSubject }); + var firstToThird = (bool)methodInfo.Invoke(comparer, new[] { firstTestSubject, thirdTestSubject }); - if (firstResult != secondResult || firstResult != thirdResult) + if ((firstToSecond && secondToThird) != firstToThird) { throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + From c4086ac62ad4abe864db31636d6ee006726fb6ab Mon Sep 17 00:00:00 2001 From: Andrei Ivascu <7030530+aivascu@users.noreply.github.com> Date: Thu, 10 Sep 2020 20:24:14 +0300 Subject: [PATCH 5/5] Adjusted code style --- Src/Idioms/EqualityComparerAssertion.cs | 4 -- Src/Idioms/EqualityComparerEqualsAssertion.cs | 23 +++++----- .../EqualityComparerEqualsNullAssertion.cs | 8 +--- ...EqualityComparerEqualsNullNullAssertion.cs | 7 +-- .../EqualityComparerEqualsSelfAssertion.cs | 2 - ...qualityComparerEqualsSymmetricAssertion.cs | 4 -- ...ualityComparerEqualsTransitiveAssertion.cs | 6 --- .../EqualityComparerGetHashCodeAssertion.cs | 43 +++++++++---------- 8 files changed, 35 insertions(+), 62 deletions(-) diff --git a/Src/Idioms/EqualityComparerAssertion.cs b/Src/Idioms/EqualityComparerAssertion.cs index 848528702..16538d200 100644 --- a/Src/Idioms/EqualityComparerAssertion.cs +++ b/Src/Idioms/EqualityComparerAssertion.cs @@ -29,13 +29,9 @@ public EqualityComparerAssertion(ISpecimenBuilder builder) private static IEnumerable CreateChildAssertions(ISpecimenBuilder builder) { yield return new EqualityComparerEqualsSelfAssertion(builder); - yield return new EqualityComparerEqualsSymmetricAssertion(builder); - yield return new EqualityComparerEqualsTransitiveAssertion(builder); - yield return new EqualityComparerGetHashCodeAssertion(builder); - yield return new EqualityComparerEqualsNullAssertion(builder); } } diff --git a/Src/Idioms/EqualityComparerEqualsAssertion.cs b/Src/Idioms/EqualityComparerEqualsAssertion.cs index 880aeccbc..089f65cc8 100644 --- a/Src/Idioms/EqualityComparerEqualsAssertion.cs +++ b/Src/Idioms/EqualityComparerEqualsAssertion.cs @@ -41,18 +41,9 @@ protected EqualityComparerEqualsAssertion(ISpecimenBuilder builder) public override void Verify(MethodInfo methodInfo) { if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); + if (!IsEqualityComparerEqualsMethod(methodInfo)) return; - if (methodInfo is { Name: nameof(IEqualityComparer.Equals), ReflectedType: { } type } && methodInfo.GetParameters().Length == 2 && type.ImplementsGenericInterfaceDefinition(typeof(IEqualityComparer<>))) - { - var argumentType = methodInfo.GetParameters()[0].ParameterType; - - if (!methodInfo.ReflectedType.ImplementsGenericInterface(typeof(IEqualityComparer<>), argumentType)) - { - return; - } - - this.VerifyEquals(methodInfo, argumentType); - } + this.VerifyEquals(methodInfo, methodInfo.GetParameters()[0].ParameterType); } /// @@ -61,5 +52,15 @@ public override void Verify(MethodInfo methodInfo) /// The method to verify. /// The argument type of . protected abstract void VerifyEquals(MethodInfo methodInfo, Type argumentType); + + private static bool IsEqualityComparerEqualsMethod(MethodInfo methodInfo) + { + return methodInfo is { Name: nameof(IEqualityComparer.Equals), ReflectedType: { } type } + && methodInfo.GetParameters().Length == 2 + && type.ImplementsGenericInterfaceDefinition(typeof(IEqualityComparer<>)) + && type.ImplementsGenericInterface( + typeof(IEqualityComparer<>), + methodInfo.GetParameters()[0].ParameterType); + } } } \ No newline at end of file diff --git a/Src/Idioms/EqualityComparerEqualsNullAssertion.cs b/Src/Idioms/EqualityComparerEqualsNullAssertion.cs index 70558ec98..994843f4f 100644 --- a/Src/Idioms/EqualityComparerEqualsNullAssertion.cs +++ b/Src/Idioms/EqualityComparerEqualsNullAssertion.cs @@ -39,16 +39,10 @@ public EqualityComparerEqualsNullAssertion(ISpecimenBuilder builder) protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) { if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); - if (argumentType == null) throw new ArgumentNullException(nameof(argumentType)); - - if (methodInfo.ReflectedType!.IsValueType) - { - return; - } + if (methodInfo.ReflectedType!.IsValueType) return; var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); - var testSubject = this.Builder.CreateAnonymous(argumentType); var result = (bool)methodInfo.Invoke(comparer, new[] { testSubject, null }); diff --git a/Src/Idioms/EqualityComparerEqualsNullNullAssertion.cs b/Src/Idioms/EqualityComparerEqualsNullNullAssertion.cs index 164f972c4..8bc9265fa 100644 --- a/Src/Idioms/EqualityComparerEqualsNullNullAssertion.cs +++ b/Src/Idioms/EqualityComparerEqualsNullNullAssertion.cs @@ -39,13 +39,8 @@ public EqualityComparerEqualsNullNullAssertion(ISpecimenBuilder builder) protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) { if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); - if (argumentType == null) throw new ArgumentNullException(nameof(argumentType)); - - if (methodInfo.ReflectedType!.IsValueType) - { - return; - } + if (methodInfo.ReflectedType!.IsValueType) return; var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); diff --git a/Src/Idioms/EqualityComparerEqualsSelfAssertion.cs b/Src/Idioms/EqualityComparerEqualsSelfAssertion.cs index fb2115d34..7811c584d 100644 --- a/Src/Idioms/EqualityComparerEqualsSelfAssertion.cs +++ b/Src/Idioms/EqualityComparerEqualsSelfAssertion.cs @@ -39,11 +39,9 @@ public EqualityComparerEqualsSelfAssertion(ISpecimenBuilder builder) protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) { if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); - if (argumentType == null) throw new ArgumentNullException(nameof(argumentType)); var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); - var testSubject = this.Builder.CreateAnonymous(argumentType); var result = (bool)methodInfo.Invoke(comparer, new[] { testSubject, testSubject }); diff --git a/Src/Idioms/EqualityComparerEqualsSymmetricAssertion.cs b/Src/Idioms/EqualityComparerEqualsSymmetricAssertion.cs index 3a7a6776e..822b5b80a 100644 --- a/Src/Idioms/EqualityComparerEqualsSymmetricAssertion.cs +++ b/Src/Idioms/EqualityComparerEqualsSymmetricAssertion.cs @@ -39,17 +39,13 @@ public EqualityComparerEqualsSymmetricAssertion(ISpecimenBuilder builder) protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) { if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); - if (argumentType == null) throw new ArgumentNullException(nameof(argumentType)); var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); - var firstTestSubject = this.Builder.CreateAnonymous(argumentType); - var secondTestSubject = this.Builder.CreateAnonymous(argumentType); var directResult = (bool)methodInfo.Invoke(comparer, new[] { firstTestSubject, secondTestSubject }); - var invertedResult = (bool)methodInfo.Invoke(comparer, new[] { secondTestSubject, firstTestSubject }); if (directResult != invertedResult) diff --git a/Src/Idioms/EqualityComparerEqualsTransitiveAssertion.cs b/Src/Idioms/EqualityComparerEqualsTransitiveAssertion.cs index bdd5e6543..6e4bc3b16 100644 --- a/Src/Idioms/EqualityComparerEqualsTransitiveAssertion.cs +++ b/Src/Idioms/EqualityComparerEqualsTransitiveAssertion.cs @@ -39,21 +39,15 @@ public EqualityComparerEqualsTransitiveAssertion(ISpecimenBuilder builder) protected override void VerifyEquals(MethodInfo methodInfo, Type argumentType) { if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); - if (argumentType == null) throw new ArgumentNullException(nameof(argumentType)); var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); - var firstTestSubject = this.Builder.CreateAnonymous(argumentType); - var secondTestSubject = this.Builder.CreateAnonymous(argumentType); - var thirdTestSubject = this.Builder.CreateAnonymous(argumentType); var firstToSecond = (bool)methodInfo.Invoke(comparer, new[] { firstTestSubject, secondTestSubject }); - var secondToThird = (bool)methodInfo.Invoke(comparer, new[] { secondTestSubject, thirdTestSubject }); - var firstToThird = (bool)methodInfo.Invoke(comparer, new[] { firstTestSubject, thirdTestSubject }); if ((firstToSecond && secondToThird) != firstToThird) diff --git a/Src/Idioms/EqualityComparerGetHashCodeAssertion.cs b/Src/Idioms/EqualityComparerGetHashCodeAssertion.cs index 870ebfba3..dd4ce18a8 100644 --- a/Src/Idioms/EqualityComparerGetHashCodeAssertion.cs +++ b/Src/Idioms/EqualityComparerGetHashCodeAssertion.cs @@ -44,32 +44,31 @@ public EqualityComparerGetHashCodeAssertion(ISpecimenBuilder builder) public override void Verify(MethodInfo methodInfo) { if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); + if (!IsEqualityComparerGetHashCode(methodInfo)) return; - if (methodInfo is { Name: nameof(IEqualityComparer.GetHashCode), ReflectedType: { } type } && methodInfo.GetParameters().Length == 1 && type.ImplementsGenericInterfaceDefinition(typeof(IEqualityComparer<>))) - { - var argumentType = methodInfo.GetParameters()[0].ParameterType; - - if (!methodInfo.ReflectedType.ImplementsGenericInterface(typeof(IEqualityComparer<>), argumentType)) - { - return; - } - - var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); - - var testSubject = this.Builder.CreateAnonymous(argumentType); - - var firstHashCode = (int)methodInfo.Invoke(comparer, new[] { testSubject }); + var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); + var testSubject = this.Builder.CreateAnonymous(methodInfo.GetParameters()[0].ParameterType); - var secondHashCode = (int)methodInfo.Invoke(comparer, new[] { testSubject }); + var firstHashCode = (int)methodInfo.Invoke(comparer, new[] { testSubject }); + var secondHashCode = (int)methodInfo.Invoke(comparer, new[] { testSubject }); - if (firstHashCode != secondHashCode) - { - throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, - "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + - "calling GetHashCode(x) should always return same value.", - type.FullName)); - } + if (firstHashCode != secondHashCode) + { + throw new EqualityComparerImplementationException(string.Format(CultureInfo.CurrentCulture, + "The type '{0}' implements the `IEqualityComparer` interface incorrectly: " + + "calling GetHashCode(x) should always return same value.", + methodInfo.ReflectedType?.FullName)); } } + + private static bool IsEqualityComparerGetHashCode(MethodInfo methodInfo) + { + return methodInfo is { Name: nameof(IEqualityComparer.GetHashCode), ReflectedType: { } type } + && methodInfo.GetParameters().Length == 1 + && type.ImplementsGenericInterfaceDefinition(typeof(IEqualityComparer<>)) + && type.ImplementsGenericInterface( + typeof(IEqualityComparer<>), + methodInfo.GetParameters()[0].ParameterType); + } } } \ No newline at end of file