diff --git a/Src/Idioms/EqualityComparerAssertion.cs b/Src/Idioms/EqualityComparerAssertion.cs new file mode 100644 index 000000000..16538d200 --- /dev/null +++ b/Src/Idioms/EqualityComparerAssertion.cs @@ -0,0 +1,38 @@ +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 + { + /// + /// 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) + { + 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); + } + } +} \ No newline at end of file diff --git a/Src/Idioms/EqualityComparerEqualsAssertion.cs b/Src/Idioms/EqualityComparerEqualsAssertion.cs new file mode 100644 index 000000000..089f65cc8 --- /dev/null +++ b/Src/Idioms/EqualityComparerEqualsAssertion.cs @@ -0,0 +1,66 @@ +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 (!IsEqualityComparerEqualsMethod(methodInfo)) return; + + this.VerifyEquals(methodInfo, methodInfo.GetParameters()[0].ParameterType); + } + + /// + /// 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); + + 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 new file mode 100644 index 000000000..994843f4f --- /dev/null +++ b/Src/Idioms/EqualityComparerEqualsNullAssertion.cs @@ -0,0 +1,59 @@ +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..8bc9265fa --- /dev/null +++ b/Src/Idioms/EqualityComparerEqualsNullNullAssertion.cs @@ -0,0 +1,58 @@ +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..7811c584d --- /dev/null +++ b/Src/Idioms/EqualityComparerEqualsSelfAssertion.cs @@ -0,0 +1,58 @@ +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..822b5b80a --- /dev/null +++ b/Src/Idioms/EqualityComparerEqualsSymmetricAssertion.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, 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..6e4bc3b16 --- /dev/null +++ b/Src/Idioms/EqualityComparerEqualsTransitiveAssertion.cs @@ -0,0 +1,62 @@ +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 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) + { + 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..dd4ce18a8 --- /dev/null +++ b/Src/Idioms/EqualityComparerGetHashCodeAssertion.cs @@ -0,0 +1,74 @@ +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 (!IsEqualityComparerGetHashCode(methodInfo)) return; + + var comparer = this.Builder.CreateAnonymous(methodInfo.ReflectedType); + var testSubject = this.Builder.CreateAnonymous(methodInfo.GetParameters()[0].ParameterType); + + 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.", + 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 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 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