Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Report mismatch segment for strings of unequal lengths #915

41 changes: 39 additions & 2 deletions Src/FluentAssertions/Primitives/StringEqualityValidator.cs
Expand Up @@ -27,14 +27,51 @@ protected override bool ValidateAgainstSuperfluousWhitespace()

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);
krajek marked this conversation as resolved.
Show resolved Hide resolved

return assertion
.ForCondition(subject.Length == expected.Length)
.ForCondition(equalLength)
.FailWith(
ExpectationDescription + "{0} with a length of {1}{reason}, but {2} has a length of {3}.",
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)
krajek marked this conversation as resolved.
Show resolved Hide resolved
{
return "";
}

int indexOfMismatch = subject.IndexOfFirstMismatch(expected, comparisonMode);

// If there is no difference it means that subject and expected have common prefix
// and the first difference is after just that prefix.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ What do you mean with prefix here?

if (indexOfMismatch == -1)
{
if (subject.Length < expected.Length)
{
// If subject is shorter, we point at its last character
indexOfMismatch = Math.Max(0, subject.Length - 1);
}
else
{
// If subject is longer we point at first character after expected
indexOfMismatch = Math.Max(0, expected.Length);
krajek marked this conversation as resolved.
Show resolved Hide resolved
}
}

string mismatchSegment = subject.IndexedSegmentAt(indexOfMismatch);
return mismatchSegment;
krajek marked this conversation as resolved.
Show resolved Hide resolved
}

protected override void ValidateAgainstMismatch()
{
int indexOfMismatch = subject.IndexOfFirstMismatch(expected, comparisonMode);
Expand Down
2 changes: 1 addition & 1 deletion Tests/Shared.Specs/BasicEquivalencySpecs.cs
Expand Up @@ -644,7 +644,7 @@ public void
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>().WithMessage(
@"Expected member Member1 to be*""different"" with a length of 9, but*"""" has a length of 0.*");
@"Expected member Member1 to be*""different"" with a length of 9, but*"""" has a length of 0*");
krajek marked this conversation as resolved.
Show resolved Hide resolved
}

[Fact]
Expand Down
36 changes: 33 additions & 3 deletions Tests/Shared.Specs/StringAssertionSpecs.cs
Expand Up @@ -66,7 +66,7 @@ public void When_the_expected_string_is_shorter_than_the_actual_string_it_should
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>().WithMessage(
"Expected string to be \"AB\" with a length of 2, but \"ABC\" has a length of 3.");
"Expected string to be \"AB\" with a length of 2, but \"ABC\" has a length of 3, differs near \"C\" (index 2).");
}

[Fact]
Expand All @@ -81,7 +81,37 @@ public void When_the_expected_string_is_longer_than_the_actual_string_it_should_
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>().WithMessage(
"Expected string to be \"ABC\" with a length of 3, but \"AB\" has a length of 2.");
"Expected string to be \"ABC\" with a length of 3, but \"AB\" has a length of 2, differs near \"B\" (index 1).");
}

[Fact]
public void When_the_expected_string_is_empty_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action act = () => "ABC".Should().Be("");

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>().WithMessage(
"Expected string to be \"\" with a length of 0, but \"ABC\" has a length of 3, differs near \"ABC\" (index 0).");
}

[Fact]
public void When_the_subject_string_is_empty_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action act = () => "".Should().Be("ABC");

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>().WithMessage(
"Expected string to be \"ABC\" with a length of 3, but \"\" has a length of 0, differs near \"\" (index 0).");
}

[Fact]
Expand Down Expand Up @@ -1904,7 +1934,7 @@ public void When_non_empty_string_is_expected_to_be_equivalent_to_empty_it_shoul
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>().WithMessage(
"Expected string to be equivalent to \"\" with a length of 0, but \"ABC\" has a length of 3.");
"Expected string to be equivalent to \"\" with a length of 0, but \"ABC\" has a length of 3, differs near \"ABC\" (index 0).");
}

[Fact]
Expand Down
2 changes: 1 addition & 1 deletion docs/_pages/documentation.md
Expand Up @@ -1290,7 +1290,7 @@ The above will batch the two failures, and throw an exception at the point of di
E.g. Exception thrown at point of dispose contains:

Expected value to be 10, but found 5.
Expected string to be "Expected" with a length of 8, but "Actual" has a length of 6.
Expected string to be "Expected" with a length of 8, but "Actual" has a length of 6, differs near "Act" (index 0).

at........

Expand Down