Skip to content

Commit

Permalink
Report mismatch segment for strings of unequal lengths (#915)
Browse files Browse the repository at this point in the history
  • Loading branch information
krajek authored and dennisdoomen committed Sep 20, 2018
1 parent 6c179a3 commit c6e72d6
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 9 deletions.
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);

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)
{
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*");
}

[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

0 comments on commit c6e72d6

Please sign in to comment.