-
-
Notifications
You must be signed in to change notification settings - Fork 540
/
StructuralEqualityEquivalencyStep.cs
105 lines (91 loc) · 4.4 KB
/
StructuralEqualityEquivalencyStep.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions.Execution;
namespace FluentAssertions.Equivalency.Steps
{
public class StructuralEqualityEquivalencyStep : IEquivalencyStep
{
public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationContext context,
IEquivalencyValidator nestedValidator)
{
if (!context.CurrentNode.IsRoot && !context.Options.IsRecursive)
{
return EquivalencyResult.ContinueWithNext;
}
bool expectationIsNotNull = AssertionScope.Current
.ForCondition(comparands.Expectation is not null)
.BecauseOf(context.Reason)
.FailWith(
"Expected {context:subject} to be <null>{reason}, but found {0}.",
comparands.Subject);
bool subjectIsNotNull = AssertionScope.Current
.ForCondition(comparands.Subject is not null)
.BecauseOf(context.Reason)
.FailWith(
"Expected {context:object} to be {0}{reason}, but found {1}.",
comparands.Expectation,
comparands.Subject);
if (expectationIsNotNull && subjectIsNotNull)
{
IMember[] selectedMembers = GetMembersFromExpectation(context.CurrentNode, comparands, context.Options).ToArray();
if (context.CurrentNode.IsRoot && !selectedMembers.Any())
{
throw new InvalidOperationException(
"No members were found for comparison. " +
"Please specify some members to include in the comparison or choose a more meaningful assertion.");
}
foreach (IMember selectedMember in selectedMembers)
{
AssertMemberEquality(comparands, context, nestedValidator, selectedMember, context.Options);
}
}
return EquivalencyResult.AssertionCompleted;
}
private static void AssertMemberEquality(Comparands comparands, IEquivalencyValidationContext context,
IEquivalencyValidator parent, IMember selectedMember, IEquivalencyAssertionOptions options)
{
IMember matchingMember = FindMatchFor(selectedMember, context.CurrentNode, comparands.Subject, options);
if (matchingMember is not null)
{
if (!options.ExcludeNonBrowsable || matchingMember.IsBrowsable)
{
var nestedComparands = new Comparands
{
Subject = matchingMember.GetValue(comparands.Subject),
Expectation = selectedMember.GetValue(comparands.Expectation),
CompileTimeType = selectedMember.Type
};
if (selectedMember.Name != matchingMember.Name)
{
// In case the matching process selected a different member on the subject,
// adjust the current member so that assertion failures report the proper name.
selectedMember.Name = matchingMember.Name;
}
parent.RecursivelyAssertEquality(nestedComparands, context.AsNestedMember(selectedMember));
}
}
}
private static IMember FindMatchFor(IMember selectedMember, INode currentNode, object subject,
IEquivalencyAssertionOptions config)
{
IEnumerable<IMember> query =
from rule in config.MatchingRules
let match = rule.Match(selectedMember, subject, currentNode, config)
where match is not null
select match;
return query.FirstOrDefault();
}
private static IEnumerable<IMember> GetMembersFromExpectation(INode currentNode, Comparands comparands,
IEquivalencyAssertionOptions options)
{
IEnumerable<IMember> members = Enumerable.Empty<IMember>();
foreach (IMemberSelectionRule rule in options.SelectionRules)
{
members = rule.SelectMembers(currentNode, members,
new MemberSelectionContext(comparands.CompileTimeType, comparands.RuntimeType, options));
}
return members;
}
}
}