/
MultiDimensionalArrayEquivalencyStep.cs
150 lines (128 loc) · 4.87 KB
/
MultiDimensionalArrayEquivalencyStep.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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions.Execution;
namespace FluentAssertions.Equivalency
{
/// <summary>
/// Supports recursively comparing two multi-dimensional arrays for equivalency using strict order for the array items
/// themselves.
/// </summary>
internal class MultiDimensionalArrayEquivalencyStep : IEquivalencyStep
{
public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config)
{
Array array = context.Expectation as Array;
return array?.Rank > 1;
}
public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent,
IEquivalencyAssertionOptions config)
{
Array expectationAsArray = (Array)context.Expectation;
if (AreComparable(context, expectationAsArray))
{
Digit digit = BuildDigitsRepresentingAllIndices(expectationAsArray);
do
{
object subject = ((Array)context.Subject).GetValue(digit.Indices);
string listOfIndices = string.Join(",", digit.Indices);
object expectation = expectationAsArray.GetValue(digit.Indices);
IEquivalencyValidationContext itemContext = context.CreateForCollectionItem(
listOfIndices,
subject,
expectation);
parent.AssertEqualityUsing(itemContext);
}
while (digit.Increment());
}
return true;
}
private static Digit BuildDigitsRepresentingAllIndices(Array subjectAsArray)
{
return Enumerable
.Range(0, subjectAsArray.Rank)
.Reverse()
.Aggregate((Digit)null, (next, rank) => new Digit(subjectAsArray.GetLength(rank), next));
}
private static bool AreComparable(IEquivalencyValidationContext context, Array expectationAsArray)
{
return
IsArray(context.Subject) &&
HaveSameRank(context.Subject, expectationAsArray) &&
HaveSameDimensions(context.Subject, expectationAsArray);
}
private static bool IsArray(object type)
{
return AssertionScope.Current
.ForCondition(!(type is null))
.FailWith("Cannot compare a multi-dimensional array to {0}.", new object[] { null })
.Then
.ForCondition(type is Array)
.FailWith("Cannot compare a multi-dimensional array to something else.");
}
private static bool HaveSameDimensions(object subject, Array expectation)
{
bool sameDimensions = true;
for (int dimension = 0; dimension < expectation.Rank; dimension++)
{
int actualLength = ((Array)subject).GetLength(dimension);
int expectedLength = expectation.GetLength(dimension);
sameDimensions &= AssertionScope.Current
.ForCondition(expectedLength == actualLength)
.FailWith("Expected dimension {0} to contain {1} item(s), but found {2}.", dimension, expectedLength,
actualLength);
}
return sameDimensions;
}
private static bool HaveSameRank(object subject, Array expectation)
{
var subjectAsArray = (Array)subject;
return AssertionScope.Current
.ForCondition(subjectAsArray.Rank == expectation.Rank)
.FailWith("Expected {context:array} to have {0} dimension(s), but it has {1}.", expectation.Rank,
subjectAsArray.Rank);
}
}
internal class Digit
{
private readonly int length;
private readonly Digit nextDigit;
private int index;
public Digit(int length, Digit nextDigit)
{
this.length = length;
this.nextDigit = nextDigit;
}
public int[] Indices
{
get
{
var indices = new List<int>();
Digit digit = this;
while (digit != null)
{
indices.Add(digit.index);
digit = digit.nextDigit;
}
return indices.ToArray();
}
}
public bool Increment()
{
bool success = nextDigit?.Increment() == true;
if (!success)
{
if (index < (length - 1))
{
index++;
success = true;
}
else
{
index = 0;
}
}
return success;
}
}
}