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

46 changes: 44 additions & 2 deletions Src/FluentAssertions/Primitives/StringEqualityValidator.cs
Expand Up @@ -27,14 +27,56 @@ 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 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);
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: 31 additions & 5 deletions Tests/Shared.Specs/StringAssertionSpecs.cs
Expand Up @@ -65,8 +65,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.");
act.Should().Throw<XunitException>().WithMessage("*index 2*");
}

[Fact]
Expand All @@ -80,8 +79,35 @@ 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.");
act.Should().Throw<XunitException>().WithMessage("*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("*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("*index 0*");
}

[Fact]
Expand Down Expand Up @@ -1904,7 +1930,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