Skip to content

Commit

Permalink
Merge branch 'release/v1.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Maxime Gélinas committed Jul 18, 2018
2 parents dd000f5 + 2798445 commit 7e0901c
Show file tree
Hide file tree
Showing 29 changed files with 1,400 additions and 11 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ The `SolidStack.Core` namespace is the central point of all SolidStack packages,

Package | Description
------- | -----------
[SolidStack.Core.Guards][solidstack.core.guards-page] | `SolidStack.Core.Guards` is an extremely simple, unambiguous and lightweight [*guard clause*][guard-clauses-url] library.
[SolidStack.Core.Guards][solidstack.core.guards-page] | `SolidStack.Core.Guards` is an extremely simple, unambiguous and lightweight [*guard clause*][guard-clauses-url] library that allow you to write pre-conditions and post-conditions for your methods in a readable way.
[SolidStack.Core.Flow][solidstack.core.flow-page] | `SolidStack.Core.Flow` focuses on encapsulating the branching logic of your code so you can write a linear and much more readable code flow without having to deal with exceptions, null checks and unnecessary conditions.
SolidStack.Core.Equality (coming soon...) | `SolidStack.Core.Equality` is primarily useful when you have to tweak the equality of an object to implement the [*Value Object Pattern*][value-object-pattern-url]. All you have to do is use one of the provided abstract classes and the complex equality logic will be done for you.
[SolidStack.Core.Equality][solidstack.core.equality-page] | `SolidStack.Core.Equality` is primarily useful when you have to tweak the equality of an object to implement the [*Value Object Pattern*][value-object-pattern-url]. All you have to do is use one of the provided abstract classes and the complex equality logic will be done for you.
SolidStack.Core.Construction (coming soon...) | `SolidStack.Core.Construction`'s only responsibility is to help you construct objects. You can use the [*Builder Pattern*][builder-pattern-url] provided implementation to build complex objects in a fluent way.

### SolidStack.Domain (coming soon...)
Expand Down Expand Up @@ -100,6 +100,7 @@ SolidStack is Copyright © 2018 SoftFrame under the [MIT license][license-url].
[nuget-install-url]: http://docs.nuget.org/docs/start-here/installing-nuget
[option-pattern-url]: http://www.codinghelmet.com/?path=howto/understanding-the-option-maybe-functional-type
[repository-pattern-url]: https://martinfowler.com/eaaCatalog/repository.html
[solidstack.core.equality-page]: https://github.com/softframe/solidstack/wiki/SolidStack.Core.Equality
[solidstack.core.guards-page]: https://github.com/softframe/solidstack/wiki/SolidStack.Core.Guards
[solidstack.core.flow-page]: https://github.com/softframe/solidstack/wiki/SolidStack.Core.Flow
[unit-of-work-pattern-url]: https://martinfowler.com/eaaCatalog/unitOfWork.html
Expand Down
15 changes: 15 additions & 0 deletions src/SolidStack.Core.Equality.Testing/AssertionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;

namespace SolidStack.Core.Equality.Testing
{
public static class AssertionExtensions
{
public static EqualityComparerAssertions<T> Should<T>(this IEqualityComparer<T> equalityComparer)
where T : class =>
new EqualityComparerAssertions<T>(equalityComparer);

public static EquatableAssertions<T> Should<T>(this IEquatable<T> equatable) =>
new EquatableAssertions<T>(equatable);
}
}
79 changes: 79 additions & 0 deletions src/SolidStack.Core.Equality.Testing/EqualityComparerAssertions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System.Collections.Generic;
using FluentAssertions;
using FluentAssertions.Execution;
using Moq;

namespace SolidStack.Core.Equality.Testing
{
public class EqualityComparerAssertions<T>
where T : class
{
public EqualityComparerAssertions(IEqualityComparer<T> equalityComparer) =>
Subject = equalityComparer;

public IEqualityComparer<T> Subject { get; protected set; }

public AndConstraint<EqualityComparerAssertions<T>> HandleBasicEqualitiesAndInequalites(
string because = "", params object[] becauseArgs)
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.Given(Mock.Of<T>)
.ForCondition(dummy => Subject.Equals(dummy, dummy))
.FailWith(
"Expected {context:comparer} to evaluate the equality of the same object as {0}{reason}, but found {1}.",
true, false)
.Then
.ForCondition(dummy => !Subject.Equals(null, dummy) && !Subject.Equals(dummy, null))
.FailWith(
"Expected {context:comparer} to evaluate the equality of null and a non-null object as {0}{reason}, but found {1}.",
false, true);

return new AndConstraint<EqualityComparerAssertions<T>>(this);
}

public AndConstraint<EqualityComparerAssertions<T>> InvalidateEqualityOf(
T x, T y, string because = "", params object[] becauseArgs)
{
var xCopy = x;
var yCopy = y;

Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(!Subject.Equals(x, y))
.FailWith(
"Expected {context:comparer} to evaluate the equality of {0} and {1} as {2}{reason}, but found {3}.",
x, y, false, true)
.Then
.Given(() => new[] {Subject.GetHashCode(x), Subject.GetHashCode(y)})
.ForCondition(hashCodes => hashCodes[0] != hashCodes[1])
.FailWith(
"Expected {context:comparer} to return different hash codes for {0} and {1}{reason}, but found {2} and {3}.",
_ => xCopy, _ => yCopy, hashCodes => hashCodes[0], hashCodes => hashCodes[1]);

return new AndConstraint<EqualityComparerAssertions<T>>(this);
}

public AndConstraint<EqualityComparerAssertions<T>> ValidateEqualityOf(
T x, T y, string because = "", params object[] becauseArgs)
{
var xCopy = x;
var yCopy = y;

Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(Subject.Equals(x, y))
.FailWith(
"Expected {context:comparer} to evaluate the equality of {0} and {1} as {2}{reason}, but found {3}.",
x, y, true, false)
.Then
.Given(() => new[] {Subject.GetHashCode(x), Subject.GetHashCode(y)})
.ForCondition(hashCodes => hashCodes[0] == hashCodes[1])
.FailWith(
"Expected {context:comparer} to return the same hash code for {0} and {1}{reason}, but found {2} and {3}.",
_ => xCopy, _ => yCopy, hashCodes => hashCodes[0], hashCodes => hashCodes[1]);

return new AndConstraint<EqualityComparerAssertions<T>>(this);
}
}
}
63 changes: 63 additions & 0 deletions src/SolidStack.Core.Equality.Testing/EquatableAssertions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;
using System.Reflection;
using FluentAssertions;
using FluentAssertions.Execution;

namespace SolidStack.Core.Equality.Testing
{
public class EquatableAssertions<T>
{
public EquatableAssertions(IEquatable<T> equatable) =>
Subject = equatable;

public IEquatable<T> Subject { get; protected set; }

private TypeInfo SubjectType =>
Subject.GetType().GetTypeInfo();

public AndConstraint<EquatableAssertions<T>> BeTypeSealed(
string because = "", params object[] becauseArgs)
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(Subject.GetType().IsSealed)
.FailWith($"Expected {{context:{Subject.GetType().Name}}} to be type sealed{{reason}}.");

return new AndConstraint<EquatableAssertions<T>>(this);
}

public AndConstraint<EquatableAssertions<T>> OverrideEquality(
string because = "", params object[] becauseArgs)
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.ForCondition(OverridesMethod("Equals", new []{typeof(object)}))
.FailWith($"Expected {{context:{Subject.GetType().Name}}} to override Equals(object){{reason}}.", Subject.GetType())
.Then
.ForCondition(OverridesMethod("GetHashCode", Type.EmptyTypes))
.FailWith($"Expected {{context:{Subject.GetType().Name}}} to override GetHashCode(){{reason}}.", Subject.GetType())
.Then
.ForCondition(OverridesOperator("op_Equality"))
.FailWith($"Expected {{context:{Subject.GetType().Name}}} to override equality operator{{reason}}.", Subject.GetType())
.Then
.ForCondition(OverridesOperator("op_Inequality"))
.FailWith($"Expected {{context:{Subject.GetType().Name}}} to override inequality operator{{reason}}.", Subject.GetType());

return new AndConstraint<EquatableAssertions<T>>(this);
}

private bool OverridesMethod(string methodName, Type[] types) =>
SubjectType
.GetMethod(methodName, types)
?.DeclaringType != typeof(object);

private bool OverridesOperator(string operatorName) =>
SubjectType
.GetMethod(
operatorName,
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.FlattenHierarchy) != null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="5.3.2" />
<PackageReference Include="Moq" Version="4.8.2" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace SolidStack.Core.Equality.Tests.Doubles
{
public class DummyByDynamicMembersEqualityComparable
{
protected dynamic FieldA;

public DummyByDynamicMembersEqualityComparable(dynamic fieldA, dynamic propertyA)
{
FieldA = fieldA;
PropertyA = propertyA;
}

/// <summary>
/// Constructor used to instantiate the class via reflection.
/// </summary>
public DummyByDynamicMembersEqualityComparable()
{
}

public dynamic PropertyA { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace SolidStack.Core.Equality.Tests.Doubles
{
public class DummyByKeyEqualityComparable
{
public DummyByKeyEqualityComparable(string id) =>
Id = id;

/// <summary>
/// Constructor used to instantiate the class via reflection.
/// </summary>
public DummyByKeyEqualityComparable()
{
}

public string Id { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace SolidStack.Core.Equality.Tests.Doubles
{
public class DummyByKeyEqualityComparableChild : DummyByKeyEqualityComparable
{
public DummyByKeyEqualityComparableChild(string id) :
base(id)
{
}

/// <inheritdoc />
/// <summary>
/// Constructor used to instantiate the class via reflection.
/// </summary>
public DummyByKeyEqualityComparableChild()
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;

namespace SolidStack.Core.Equality.Tests.Doubles
{
public class DummyByMembersEqualityComparable
{
public int FieldA;

protected IEnumerable<char> FieldB;

public DummyByMembersEqualityComparable(
int fieldA, IEnumerable<char> fieldB,
DateTime propertyA, IEnumerable<bool> propertyB)
{
FieldA = fieldA;
FieldB = fieldB;
PropertyA = propertyA;
PropertyB = propertyB;
}

/// <summary>
/// Constructor used to instantiate the class via reflection.
/// </summary>
public DummyByMembersEqualityComparable()
{
}

public DateTime PropertyA { get; }

protected IEnumerable<bool> PropertyB { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;

namespace SolidStack.Core.Equality.Tests.Doubles
{
public class DummyByMembersEqualityComparableChild : DummyByMembersEqualityComparable
{
protected string FieldC;

public DummyByMembersEqualityComparableChild(
int fieldA, IEnumerable<char> fieldB, string fieldC,
DateTime propertyA, IEnumerable<bool> propertyB, bool propertyC) :
base(fieldA, fieldB, propertyA, propertyB)
{
FieldC = fieldC;
PropertyC = propertyC;
}

/// <inheritdoc />
/// <summary>
/// Constructor used to instantiate the class via reflection.
/// </summary>
public DummyByMembersEqualityComparableChild()
{
}

public bool PropertyC { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace SolidStack.Core.Equality.Tests.Doubles
{
public class DummyByTaggedMembersEqualityComparable
{
[EqualityMember]
public int FieldA;

protected string FieldB;

public DummyByTaggedMembersEqualityComparable(int fieldA, string fieldB, bool propertyA, char propertyB)
{
FieldA = fieldA;
FieldB = fieldB;
PropertyA = propertyA;
PropertyB = propertyB;
}

/// <summary>
/// Constructor used to instantiate the class via reflection.
/// </summary>
public DummyByTaggedMembersEqualityComparable()
{
}

public bool PropertyA { get; }

[EqualityMember]
protected char PropertyB { get; set; }
}
}
16 changes: 16 additions & 0 deletions src/SolidStack.Core.Equality.Tests/Doubles/EquatableStub.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;

namespace SolidStack.Core.Equality.Tests.Doubles
{
public sealed class EquatableStub : Equatable<EquatableStub>
{
public EquatableStub(Func<IEqualityComparer<EquatableStub>> equalityComparerAccessor) =>
EqualityComparerAccessor = equalityComparerAccessor;

private Func<IEqualityComparer<EquatableStub>> EqualityComparerAccessor { get; }

protected override IEqualityComparer<EquatableStub> GetEqualityComparer() =>
EqualityComparerAccessor();
}
}
Loading

0 comments on commit 7e0901c

Please sign in to comment.