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

Allow IEqualityComparer in string (Not)BeEquivalentTo assertion #2372

24 changes: 0 additions & 24 deletions Src/FluentAssertions/Common/StringExtensions.cs
Expand Up @@ -25,30 +25,6 @@ public static int IndexOfFirstMismatch(this string value, string expected, IEqua
return -1;
dennisdoomen marked this conversation as resolved.
Show resolved Hide resolved
}

/// <summary>
/// Finds the first index at which the <paramref name="value"/> does not match the <paramref name="expected"/>
/// string anymore, accounting for the specified <paramref name="stringComparison"/>.
/// </summary>
public static int IndexOfFirstMismatch(this string value, string expected, StringComparison stringComparison)
{
Func<char, char, bool> comparer = GetCharComparer(stringComparison);

for (int index = 0; index < value.Length; index++)
{
if (index >= expected.Length || !comparer(value[index], expected[index]))
{
return index;
}
}

return -1;
}

private static Func<char, char, bool> GetCharComparer(StringComparison stringComparison) =>
stringComparison == StringComparison.Ordinal
? (x, y) => x == y
: (x, y) => char.ToUpperInvariant(x) == char.ToUpperInvariant(y);

/// <summary>
/// Gets the quoted three characters at the specified index of a string, including the index itself.
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions Src/FluentAssertions/Primitives/StringAssertions.cs
Expand Up @@ -704,7 +704,7 @@ public AndConstraint<TAssertions> StartWith(string expected, string because = ""
Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot compare start of string with <null>.");

var stringStartValidator = new StringValidator(
new StringStartStrategy(StringComparison.Ordinal),
new StringStartStrategy(StringComparer.Ordinal),
because, becauseArgs);

stringStartValidator.Validate(Subject, expected);
Expand Down Expand Up @@ -757,7 +757,7 @@ public AndConstraint<TAssertions> NotStartWith(string unexpected, string because
Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot compare string start equivalence with <null>.");

var stringStartValidator = new StringValidator(
new StringStartStrategy(StringComparison.OrdinalIgnoreCase),
new StringStartStrategy(StringComparer.OrdinalIgnoreCase),
because, becauseArgs);

stringStartValidator.Validate(Subject, expected);
Expand Down
21 changes: 10 additions & 11 deletions Src/FluentAssertions/Primitives/StringStartStrategy.cs
@@ -1,30 +1,29 @@
using System;
using System.Collections.Generic;
using FluentAssertions.Common;
using FluentAssertions.Execution;

namespace FluentAssertions.Primitives;

internal class StringStartStrategy : IStringComparisonStrategy
{
private readonly StringComparison stringComparison;
private readonly IEqualityComparer<string> comparer;
private readonly bool ignoringCase;

public StringStartStrategy(StringComparison stringComparison)
public StringStartStrategy(IEqualityComparer<string> comparer)
{
this.stringComparison = stringComparison;
this.comparer = comparer;
ignoringCase = comparer.Equals("A", "a");
}

public string ExpectationDescription
{
get
{
string predicateDescription = IgnoreCase ? "start with equivalent of" : "start with";
string predicateDescription = ignoringCase ? "start with equivalent of" : "start with";
return "Expected {context:string} to " + predicateDescription + " ";
}
}

private bool IgnoreCase
=> stringComparison == StringComparison.OrdinalIgnoreCase;

public void ValidateAgainstMismatch(IAssertionScope assertion, string subject, string expected)
{
if (!assertion
Expand All @@ -34,13 +33,13 @@ public void ValidateAgainstMismatch(IAssertionScope assertion, string subject, s
return;
}

if (subject.StartsWith(expected, stringComparison))
int indexOfMismatch = subject.IndexOfFirstMismatch(expected, comparer);

if (indexOfMismatch < 0 || indexOfMismatch >= expected.Length)
{
return;
}

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

assertion.FailWith(
ExpectationDescription + "{0}{reason}, but {1} differs near " + subject.IndexedSegmentAt(indexOfMismatch) +
".",
Expand Down
Expand Up @@ -26,6 +26,20 @@ public void When_asserting_string_starts_with_the_same_value_it_should_not_throw
action.Should().NotThrow();
}

[Fact]
public void When_expected_string_is_the_same_value_it_should_not_throw()
{
// Arrange
string value = "ABC";

// Act
Action action = () =>
value.Should().StartWith(value);

// Assert
action.Should().NotThrow();
vbreuss marked this conversation as resolved.
Show resolved Hide resolved
}

[Fact]
public void When_string_does_not_start_with_expected_phrase_it_should_throw()
{
Expand Down