Skip to content

Commit 7e0901c

Browse files
author
Maxime Gélinas
committed
Merge branch 'release/v1.2.0'
2 parents dd000f5 + 2798445 commit 7e0901c

29 files changed

Lines changed: 1400 additions & 11 deletions

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ The `SolidStack.Core` namespace is the central point of all SolidStack packages,
2525

2626
Package | Description
2727
------- | -----------
28-
[SolidStack.Core.Guards][solidstack.core.guards-page] | `SolidStack.Core.Guards` is an extremely simple, unambiguous and lightweight [*guard clause*][guard-clauses-url] library.
28+
[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.
2929
[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.
30-
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.
30+
[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.
3131
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.
3232

3333
### SolidStack.Domain (coming soon...)
@@ -100,6 +100,7 @@ SolidStack is Copyright © 2018 SoftFrame under the [MIT license][license-url].
100100
[nuget-install-url]: http://docs.nuget.org/docs/start-here/installing-nuget
101101
[option-pattern-url]: http://www.codinghelmet.com/?path=howto/understanding-the-option-maybe-functional-type
102102
[repository-pattern-url]: https://martinfowler.com/eaaCatalog/repository.html
103+
[solidstack.core.equality-page]: https://github.com/softframe/solidstack/wiki/SolidStack.Core.Equality
103104
[solidstack.core.guards-page]: https://github.com/softframe/solidstack/wiki/SolidStack.Core.Guards
104105
[solidstack.core.flow-page]: https://github.com/softframe/solidstack/wiki/SolidStack.Core.Flow
105106
[unit-of-work-pattern-url]: https://martinfowler.com/eaaCatalog/unitOfWork.html
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace SolidStack.Core.Equality.Testing
5+
{
6+
public static class AssertionExtensions
7+
{
8+
public static EqualityComparerAssertions<T> Should<T>(this IEqualityComparer<T> equalityComparer)
9+
where T : class =>
10+
new EqualityComparerAssertions<T>(equalityComparer);
11+
12+
public static EquatableAssertions<T> Should<T>(this IEquatable<T> equatable) =>
13+
new EquatableAssertions<T>(equatable);
14+
}
15+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using System.Collections.Generic;
2+
using FluentAssertions;
3+
using FluentAssertions.Execution;
4+
using Moq;
5+
6+
namespace SolidStack.Core.Equality.Testing
7+
{
8+
public class EqualityComparerAssertions<T>
9+
where T : class
10+
{
11+
public EqualityComparerAssertions(IEqualityComparer<T> equalityComparer) =>
12+
Subject = equalityComparer;
13+
14+
public IEqualityComparer<T> Subject { get; protected set; }
15+
16+
public AndConstraint<EqualityComparerAssertions<T>> HandleBasicEqualitiesAndInequalites(
17+
string because = "", params object[] becauseArgs)
18+
{
19+
Execute.Assertion
20+
.BecauseOf(because, becauseArgs)
21+
.Given(Mock.Of<T>)
22+
.ForCondition(dummy => Subject.Equals(dummy, dummy))
23+
.FailWith(
24+
"Expected {context:comparer} to evaluate the equality of the same object as {0}{reason}, but found {1}.",
25+
true, false)
26+
.Then
27+
.ForCondition(dummy => !Subject.Equals(null, dummy) && !Subject.Equals(dummy, null))
28+
.FailWith(
29+
"Expected {context:comparer} to evaluate the equality of null and a non-null object as {0}{reason}, but found {1}.",
30+
false, true);
31+
32+
return new AndConstraint<EqualityComparerAssertions<T>>(this);
33+
}
34+
35+
public AndConstraint<EqualityComparerAssertions<T>> InvalidateEqualityOf(
36+
T x, T y, string because = "", params object[] becauseArgs)
37+
{
38+
var xCopy = x;
39+
var yCopy = y;
40+
41+
Execute.Assertion
42+
.BecauseOf(because, becauseArgs)
43+
.ForCondition(!Subject.Equals(x, y))
44+
.FailWith(
45+
"Expected {context:comparer} to evaluate the equality of {0} and {1} as {2}{reason}, but found {3}.",
46+
x, y, false, true)
47+
.Then
48+
.Given(() => new[] {Subject.GetHashCode(x), Subject.GetHashCode(y)})
49+
.ForCondition(hashCodes => hashCodes[0] != hashCodes[1])
50+
.FailWith(
51+
"Expected {context:comparer} to return different hash codes for {0} and {1}{reason}, but found {2} and {3}.",
52+
_ => xCopy, _ => yCopy, hashCodes => hashCodes[0], hashCodes => hashCodes[1]);
53+
54+
return new AndConstraint<EqualityComparerAssertions<T>>(this);
55+
}
56+
57+
public AndConstraint<EqualityComparerAssertions<T>> ValidateEqualityOf(
58+
T x, T y, string because = "", params object[] becauseArgs)
59+
{
60+
var xCopy = x;
61+
var yCopy = y;
62+
63+
Execute.Assertion
64+
.BecauseOf(because, becauseArgs)
65+
.ForCondition(Subject.Equals(x, y))
66+
.FailWith(
67+
"Expected {context:comparer} to evaluate the equality of {0} and {1} as {2}{reason}, but found {3}.",
68+
x, y, true, false)
69+
.Then
70+
.Given(() => new[] {Subject.GetHashCode(x), Subject.GetHashCode(y)})
71+
.ForCondition(hashCodes => hashCodes[0] == hashCodes[1])
72+
.FailWith(
73+
"Expected {context:comparer} to return the same hash code for {0} and {1}{reason}, but found {2} and {3}.",
74+
_ => xCopy, _ => yCopy, hashCodes => hashCodes[0], hashCodes => hashCodes[1]);
75+
76+
return new AndConstraint<EqualityComparerAssertions<T>>(this);
77+
}
78+
}
79+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System;
2+
using System.Reflection;
3+
using FluentAssertions;
4+
using FluentAssertions.Execution;
5+
6+
namespace SolidStack.Core.Equality.Testing
7+
{
8+
public class EquatableAssertions<T>
9+
{
10+
public EquatableAssertions(IEquatable<T> equatable) =>
11+
Subject = equatable;
12+
13+
public IEquatable<T> Subject { get; protected set; }
14+
15+
private TypeInfo SubjectType =>
16+
Subject.GetType().GetTypeInfo();
17+
18+
public AndConstraint<EquatableAssertions<T>> BeTypeSealed(
19+
string because = "", params object[] becauseArgs)
20+
{
21+
Execute.Assertion
22+
.BecauseOf(because, becauseArgs)
23+
.ForCondition(Subject.GetType().IsSealed)
24+
.FailWith($"Expected {{context:{Subject.GetType().Name}}} to be type sealed{{reason}}.");
25+
26+
return new AndConstraint<EquatableAssertions<T>>(this);
27+
}
28+
29+
public AndConstraint<EquatableAssertions<T>> OverrideEquality(
30+
string because = "", params object[] becauseArgs)
31+
{
32+
Execute.Assertion
33+
.BecauseOf(because, becauseArgs)
34+
.ForCondition(OverridesMethod("Equals", new []{typeof(object)}))
35+
.FailWith($"Expected {{context:{Subject.GetType().Name}}} to override Equals(object){{reason}}.", Subject.GetType())
36+
.Then
37+
.ForCondition(OverridesMethod("GetHashCode", Type.EmptyTypes))
38+
.FailWith($"Expected {{context:{Subject.GetType().Name}}} to override GetHashCode(){{reason}}.", Subject.GetType())
39+
.Then
40+
.ForCondition(OverridesOperator("op_Equality"))
41+
.FailWith($"Expected {{context:{Subject.GetType().Name}}} to override equality operator{{reason}}.", Subject.GetType())
42+
.Then
43+
.ForCondition(OverridesOperator("op_Inequality"))
44+
.FailWith($"Expected {{context:{Subject.GetType().Name}}} to override inequality operator{{reason}}.", Subject.GetType());
45+
46+
return new AndConstraint<EquatableAssertions<T>>(this);
47+
}
48+
49+
private bool OverridesMethod(string methodName, Type[] types) =>
50+
SubjectType
51+
.GetMethod(methodName, types)
52+
?.DeclaringType != typeof(object);
53+
54+
private bool OverridesOperator(string operatorName) =>
55+
SubjectType
56+
.GetMethod(
57+
operatorName,
58+
BindingFlags.Instance |
59+
BindingFlags.Static |
60+
BindingFlags.Public |
61+
BindingFlags.FlattenHierarchy) != null;
62+
}
63+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<PackageReference Include="FluentAssertions" Version="5.3.2" />
9+
<PackageReference Include="Moq" Version="4.8.2" />
10+
</ItemGroup>
11+
12+
</Project>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace SolidStack.Core.Equality.Tests.Doubles
2+
{
3+
public class DummyByDynamicMembersEqualityComparable
4+
{
5+
protected dynamic FieldA;
6+
7+
public DummyByDynamicMembersEqualityComparable(dynamic fieldA, dynamic propertyA)
8+
{
9+
FieldA = fieldA;
10+
PropertyA = propertyA;
11+
}
12+
13+
/// <summary>
14+
/// Constructor used to instantiate the class via reflection.
15+
/// </summary>
16+
public DummyByDynamicMembersEqualityComparable()
17+
{
18+
}
19+
20+
public dynamic PropertyA { get; }
21+
}
22+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace SolidStack.Core.Equality.Tests.Doubles
2+
{
3+
public class DummyByKeyEqualityComparable
4+
{
5+
public DummyByKeyEqualityComparable(string id) =>
6+
Id = id;
7+
8+
/// <summary>
9+
/// Constructor used to instantiate the class via reflection.
10+
/// </summary>
11+
public DummyByKeyEqualityComparable()
12+
{
13+
}
14+
15+
public string Id { get; }
16+
}
17+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace SolidStack.Core.Equality.Tests.Doubles
2+
{
3+
public class DummyByKeyEqualityComparableChild : DummyByKeyEqualityComparable
4+
{
5+
public DummyByKeyEqualityComparableChild(string id) :
6+
base(id)
7+
{
8+
}
9+
10+
/// <inheritdoc />
11+
/// <summary>
12+
/// Constructor used to instantiate the class via reflection.
13+
/// </summary>
14+
public DummyByKeyEqualityComparableChild()
15+
{
16+
}
17+
}
18+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace SolidStack.Core.Equality.Tests.Doubles
5+
{
6+
public class DummyByMembersEqualityComparable
7+
{
8+
public int FieldA;
9+
10+
protected IEnumerable<char> FieldB;
11+
12+
public DummyByMembersEqualityComparable(
13+
int fieldA, IEnumerable<char> fieldB,
14+
DateTime propertyA, IEnumerable<bool> propertyB)
15+
{
16+
FieldA = fieldA;
17+
FieldB = fieldB;
18+
PropertyA = propertyA;
19+
PropertyB = propertyB;
20+
}
21+
22+
/// <summary>
23+
/// Constructor used to instantiate the class via reflection.
24+
/// </summary>
25+
public DummyByMembersEqualityComparable()
26+
{
27+
}
28+
29+
public DateTime PropertyA { get; }
30+
31+
protected IEnumerable<bool> PropertyB { get; set; }
32+
}
33+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace SolidStack.Core.Equality.Tests.Doubles
5+
{
6+
public class DummyByMembersEqualityComparableChild : DummyByMembersEqualityComparable
7+
{
8+
protected string FieldC;
9+
10+
public DummyByMembersEqualityComparableChild(
11+
int fieldA, IEnumerable<char> fieldB, string fieldC,
12+
DateTime propertyA, IEnumerable<bool> propertyB, bool propertyC) :
13+
base(fieldA, fieldB, propertyA, propertyB)
14+
{
15+
FieldC = fieldC;
16+
PropertyC = propertyC;
17+
}
18+
19+
/// <inheritdoc />
20+
/// <summary>
21+
/// Constructor used to instantiate the class via reflection.
22+
/// </summary>
23+
public DummyByMembersEqualityComparableChild()
24+
{
25+
}
26+
27+
public bool PropertyC { get; set; }
28+
}
29+
}

0 commit comments

Comments
 (0)