diff --git a/Src/FluentAssertions/Primitives/StringEqualityValidator.cs b/Src/FluentAssertions/Primitives/StringEqualityValidator.cs index 5b749649e4..9bfc29ebc3 100644 --- a/Src/FluentAssertions/Primitives/StringEqualityValidator.cs +++ b/Src/FluentAssertions/Primitives/StringEqualityValidator.cs @@ -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); diff --git a/Tests/Shared.Specs/BasicEquivalencySpecs.cs b/Tests/Shared.Specs/BasicEquivalencySpecs.cs index 23965aaeef..06992c36ff 100644 --- a/Tests/Shared.Specs/BasicEquivalencySpecs.cs +++ b/Tests/Shared.Specs/BasicEquivalencySpecs.cs @@ -644,7 +644,7 @@ public void // Assert //----------------------------------------------------------------------------------------------------------- act.Should().Throw().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] diff --git a/Tests/Shared.Specs/StringAssertionSpecs.cs b/Tests/Shared.Specs/StringAssertionSpecs.cs index f980fb63ba..7d56e4e959 100644 --- a/Tests/Shared.Specs/StringAssertionSpecs.cs +++ b/Tests/Shared.Specs/StringAssertionSpecs.cs @@ -65,8 +65,7 @@ public void When_the_expected_string_is_shorter_than_the_actual_string_it_should //----------------------------------------------------------------------------------------------------------- // Assert //----------------------------------------------------------------------------------------------------------- - act.Should().Throw().WithMessage( - "Expected string to be \"AB\" with a length of 2, but \"ABC\" has a length of 3."); + act.Should().Throw().WithMessage("*index 2*"); } [Fact] @@ -80,8 +79,35 @@ public void When_the_expected_string_is_longer_than_the_actual_string_it_should_ //----------------------------------------------------------------------------------------------------------- // Assert //----------------------------------------------------------------------------------------------------------- - act.Should().Throw().WithMessage( - "Expected string to be \"ABC\" with a length of 3, but \"AB\" has a length of 2."); + act.Should().Throw().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().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().WithMessage("*index 0*"); } [Fact] @@ -1904,7 +1930,7 @@ public void When_non_empty_string_is_expected_to_be_equivalent_to_empty_it_shoul // Assert //----------------------------------------------------------------------------------------------------------- act.Should().Throw().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] diff --git a/docs/_pages/documentation.md b/docs/_pages/documentation.md index 40b6977e6d..60e46554d5 100644 --- a/docs/_pages/documentation.md +++ b/docs/_pages/documentation.md @@ -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........