Skip to content

Commit

Permalink
Fixed FxCop issues
Browse files Browse the repository at this point in the history
  • Loading branch information
Kralizek committed Sep 5, 2020
1 parent 76bca3d commit 2b0520b
Show file tree
Hide file tree
Showing 10 changed files with 549 additions and 215 deletions.
236 changes: 21 additions & 215 deletions 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
{
/// <summary>
/// Encapsulates a unit test that verifies that a type implementing <see cref="IEqualityComparer{T}"/> implements it correctly.
/// </summary>
public class EqualityComparerAssertion : CompositeIdiomaticAssertion
{
public EqualityComparerAssertion(ISpecimenBuilder builder) : base(CreateChildAssertions(builder)) { }
/// <summary>
/// Initializes a new instance of the <see cref="EqualityComparerAssertion"/> class.
/// </summary>
/// <param name="builder">
/// 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.
/// </param>
/// <remarks>
/// <para>
/// <paramref name="builder" /> will typically be a <see cref="Fixture" /> instance.
/// </para>
/// </remarks>
public EqualityComparerAssertion(ISpecimenBuilder builder)
: base(CreateChildAssertions(builder))
{
}

private static IEnumerable<IIdiomaticAssertion> CreateChildAssertions(ISpecimenBuilder builder)
{
Expand All @@ -25,212 +39,4 @@ private static IEnumerable<IIdiomaticAssertion> 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<T>` 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<T>` 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<T>` 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<T>` 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<T>` 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<T>` 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)));
}
}
65 changes: 65 additions & 0 deletions 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
{
/// <summary>
/// A base class for building classes that encapsulate a unit test that verifies that a type implementing <see cref="IEqualityComparer{T}"/> implements it correctly.
/// </summary>
public abstract class EqualityComparerEqualsAssertion : IdiomaticAssertion
{
/// <summary>
/// Gets the builder supplied by the constructor.
/// </summary>
public ISpecimenBuilder Builder { get; }

/// <summary>
/// Initializes a new instance of the <see cref="EqualityComparerEqualsAssertion"/> class.
/// </summary>
/// <param name="builder">
/// 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.
/// </param>
/// <remarks>
/// <para>
/// <paramref name="builder" /> will typically be a <see cref="Fixture" /> instance.
/// </para>
/// </remarks>
protected EqualityComparerEqualsAssertion(ISpecimenBuilder builder)
{
this.Builder = builder ?? throw new ArgumentNullException(nameof(builder));
}

/// <summary>
/// Forwards the call to <see cref="VerifyEquals"/> if the supplied method is an implementation of <see cref="IEqualityComparer{T}.Equals(T,T)"/>.
/// </summary>
/// <param name="methodInfo">The method to verify.</param>
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);
}
}

/// <summary>
/// Abstract method used to verify if the method implements correctly <see cref="IEqualityComparer{T}.Equals(T,T)"/>.
/// </summary>
/// <param name="methodInfo">The method to verify.</param>
/// <param name="argumentType">The argument type of <see cref="IEqualityComparer{T}.Equals(T,T)"/>.</param>
protected abstract void VerifyEquals(MethodInfo methodInfo, Type argumentType);
}
}
65 changes: 65 additions & 0 deletions 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
{
/// <summary>
/// Encapsulates a unit test that verifies that a type implementing <see cref="IEqualityComparer{T}"/> implements it correctly
/// with respect of the rule: calling Equals(x, null) should return false.
/// </summary>
public class EqualityComparerEqualsNullAssertion : EqualityComparerEqualsAssertion
{
/// <summary>
/// Initializes a new instance of the <see cref="EqualityComparerEqualsNullAssertion"/> class.
/// </summary>
/// <param name="builder">
/// 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.
/// </param>
/// <remarks>
/// <para>
/// <paramref name="builder" /> will typically be a <see cref="Fixture" /> instance.
/// </para>
/// </remarks>
public EqualityComparerEqualsNullAssertion(ISpecimenBuilder builder)
: base(builder)
{
}

/// <summary>
/// Verifies that `calling Equals(x, null) should return false`
/// if the supplied method is an implementation of <see cref="IEqualityComparer{T}.Equals(T,T)"/>.
/// </summary>
/// <param name="methodInfo">The method to verify.</param>
/// <param name="argumentType">The argument type of <see cref="IEqualityComparer{T}.Equals(T,T)"/>.</param>
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<T>` interface incorrectly: " +
"calling Equals(x, null) should return false.",
methodInfo.ReflectedType!.FullName));
}
}
}
}

0 comments on commit 2b0520b

Please sign in to comment.