/
StringEqualityValidator.cs
109 lines (96 loc) · 4.44 KB
/
StringEqualityValidator.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
using System;
using FluentAssertions.Common;
namespace FluentAssertions.Primitives
{
internal class StringEqualityValidator : StringValidator
{
private readonly StringComparison comparisonMode;
public StringEqualityValidator(string subject, string expected, StringComparison comparisonMode, string because,
object[] becauseArgs)
: base(subject, expected, because, becauseArgs)
{
this.comparisonMode = comparisonMode;
}
protected override bool ValidateAgainstSuperfluousWhitespace()
{
return assertion
.ForCondition(!((expected.Length > subject.Length) && expected.TrimEnd().Equals(subject, comparisonMode)))
.FailWith(ExpectationDescription + "{0}{reason}, but it misses some extra whitespace at the end.", expected)
.Then
.ForCondition(!((subject.Length > expected.Length) && subject.TrimEnd().Equals(expected, comparisonMode)))
.FailWith(ExpectationDescription + "{0}{reason}, but it has unexpected whitespace at the end.", expected)
.SourceSucceeded;
}
protected override bool ValidateAgainstLengthDifferences()
{
// Logic is a little bit convoluted because I want to avoid calculation
// of mismatch segment in case of equalLength == true for performance reason.
// If lazy version of FailWith would be introduced, calculation of mismatch
// segment can be moved directly to FailWith's argument
bool equalLength = subject.Length == expected.Length;
string mismatchSegment = GetMismatchSegmentForStringsOfDifferentLengths(equalLength);
return assertion
.ForCondition(equalLength)
.FailWith(
ExpectationDescription + "{0} with a length of {1}{reason}, but {2} has a length of {3}, differs near " + mismatchSegment + ".",
expected, expected.Length, subject, subject.Length)
.SourceSucceeded;
}
private string GetMismatchSegmentForStringsOfDifferentLengths(bool equalLength)
{
if (equalLength)
{
return "";
}
int indexOfMismatch = subject.IndexOfFirstMismatch(expected, comparisonMode);
// If there is no difference it means that either
// * subject starts with expected or
// * expected starts with subject
if (indexOfMismatch == -1)
{
// If subject is shorter we are sure that expected starts with subject
if (subject.Length < expected.Length)
{
// Subject is shorter so we point at its last character.
// We would like to point at next character as it is the real
// index of first mismatch, but we need to point at character existing in
// subject, so the next best thing is the last subject character.
indexOfMismatch = Math.Max(0, subject.Length - 1);
}
else
{
// If subject is longer we are sure that subject starts with expected
// and we point at first character after expected.
indexOfMismatch = expected.Length;
}
}
return subject.IndexedSegmentAt(indexOfMismatch);
}
protected override void ValidateAgainstMismatch()
{
int indexOfMismatch = subject.IndexOfFirstMismatch(expected, comparisonMode);
if (indexOfMismatch != -1)
{
assertion.FailWith(
ExpectationDescription + "{0}{reason}, but {1} differs near " + subject.IndexedSegmentAt(indexOfMismatch) + ".",
expected, subject);
}
}
protected override string ExpectationDescription
{
get
{
string predicateDescription = IgnoreCase ? "be equivalent to" : "be";
return "Expected {context:string} to " + predicateDescription + " ";
}
}
private bool IgnoreCase
{
get
{
return (comparisonMode == StringComparison.CurrentCultureIgnoreCase) ||
(comparisonMode == StringComparison.OrdinalIgnoreCase);
}
}
}
}