Skip to content

Commit

Permalink
Merge pull request #2075 from jnyrup/string_sorting
Browse files Browse the repository at this point in the history
Order strings with ordinal comparison
  • Loading branch information
jnyrup committed Dec 29, 2022
2 parents 34ebe76 + 28ef506 commit 6a03535
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 11 deletions.
19 changes: 11 additions & 8 deletions Src/FluentAssertions/Collections/GenericCollectionAssertions.cs
Expand Up @@ -395,7 +395,7 @@ public AndConstraint<TAssertions> BeEmpty(string because = "", params object[] b
public AndConstraint<SubsequentOrderingAssertions<T>> BeInAscendingOrder<TSelector>(
Expression<Func<T, TSelector>> propertyExpression, string because = "", params object[] becauseArgs)
{
return BeInAscendingOrder(propertyExpression, Comparer<TSelector>.Default, because, becauseArgs);
return BeInAscendingOrder(propertyExpression, GetComparer<TSelector>(), because, becauseArgs);
}

/// <summary>
Expand Down Expand Up @@ -467,7 +467,7 @@ public AndConstraint<TAssertions> BeEmpty(string because = "", params object[] b
/// </remarks>
public AndConstraint<SubsequentOrderingAssertions<T>> BeInAscendingOrder(string because = "", params object[] becauseArgs)
{
return BeInAscendingOrder(Comparer<T>.Default, because, becauseArgs);
return BeInAscendingOrder(GetComparer<T>(), because, becauseArgs);
}

/// <summary>
Expand Down Expand Up @@ -512,7 +512,7 @@ public AndConstraint<SubsequentOrderingAssertions<T>> BeInAscendingOrder(Func<T,
public AndConstraint<SubsequentOrderingAssertions<T>> BeInDescendingOrder<TSelector>(
Expression<Func<T, TSelector>> propertyExpression, string because = "", params object[] becauseArgs)
{
return BeInDescendingOrder(propertyExpression, Comparer<TSelector>.Default, because, becauseArgs);
return BeInDescendingOrder(propertyExpression, GetComparer<TSelector>(), because, becauseArgs);
}

/// <summary>
Expand Down Expand Up @@ -584,7 +584,7 @@ public AndConstraint<SubsequentOrderingAssertions<T>> BeInAscendingOrder(Func<T,
/// </remarks>
public AndConstraint<SubsequentOrderingAssertions<T>> BeInDescendingOrder(string because = "", params object[] becauseArgs)
{
return BeInDescendingOrder(Comparer<T>.Default, because, becauseArgs);
return BeInDescendingOrder(GetComparer<T>(), because, becauseArgs);
}

/// <summary>
Expand Down Expand Up @@ -1788,7 +1788,7 @@ public AndConstraint<TAssertions> NotBeEmpty(string because = "", params object[
public AndConstraint<TAssertions> NotBeInAscendingOrder<TSelector>(
Expression<Func<T, TSelector>> propertyExpression, string because = "", params object[] becauseArgs)
{
return NotBeInAscendingOrder(propertyExpression, Comparer<TSelector>.Default, because, becauseArgs);
return NotBeInAscendingOrder(propertyExpression, GetComparer<TSelector>(), because, becauseArgs);
}

/// <summary>
Expand Down Expand Up @@ -1860,7 +1860,7 @@ public AndConstraint<TAssertions> NotBeEmpty(string because = "", params object[
/// </remarks>
public AndConstraint<TAssertions> NotBeInAscendingOrder(string because = "", params object[] becauseArgs)
{
return NotBeInAscendingOrder(Comparer<T>.Default, because, becauseArgs);
return NotBeInAscendingOrder(GetComparer<T>(), because, becauseArgs);
}

/// <summary>
Expand Down Expand Up @@ -1904,7 +1904,7 @@ public AndConstraint<TAssertions> NotBeInAscendingOrder(Func<T, T, int> comparis
public AndConstraint<TAssertions> NotBeInDescendingOrder<TSelector>(
Expression<Func<T, TSelector>> propertyExpression, string because = "", params object[] becauseArgs)
{
return NotBeInDescendingOrder(propertyExpression, Comparer<TSelector>.Default, because, becauseArgs);
return NotBeInDescendingOrder(propertyExpression, GetComparer<TSelector>(), because, becauseArgs);
}

/// <summary>
Expand Down Expand Up @@ -1976,7 +1976,7 @@ public AndConstraint<TAssertions> NotBeInAscendingOrder(Func<T, T, int> comparis
/// </remarks>
public AndConstraint<TAssertions> NotBeInDescendingOrder(string because = "", params object[] becauseArgs)
{
return NotBeInDescendingOrder(Comparer<T>.Default, because, becauseArgs);
return NotBeInDescendingOrder(GetComparer<T>(), because, becauseArgs);
}

/// <summary>
Expand Down Expand Up @@ -3480,4 +3480,7 @@ private static int ConsecutiveItemCount(IList<T> actualItems, IList<T> expectedI

return expectedItems.Count;
}

private protected static IComparer<TItem> GetComparer<TItem>() =>
typeof(TItem) == typeof(string) ? (IComparer<TItem>)(object)StringComparer.Ordinal : Comparer<TItem>.Default;
}
Expand Up @@ -63,7 +63,7 @@ public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, IO
public AndConstraint<SubsequentOrderingAssertions<T>> ThenBeInAscendingOrder<TSelector>(
Expression<Func<T, TSelector>> propertyExpression, string because = "", params object[] becauseArgs)
{
return ThenBeInAscendingOrder(propertyExpression, Comparer<TSelector>.Default, because, becauseArgs);
return ThenBeInAscendingOrder(propertyExpression, GetComparer<TSelector>(), because, becauseArgs);
}

/// <summary>
Expand Down Expand Up @@ -114,7 +114,7 @@ public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, IO
public AndConstraint<SubsequentOrderingAssertions<T>> ThenBeInDescendingOrder<TSelector>(
Expression<Func<T, TSelector>> propertyExpression, string because = "", params object[] becauseArgs)
{
return ThenBeInDescendingOrder(propertyExpression, Comparer<TSelector>.Default, because, becauseArgs);
return ThenBeInDescendingOrder(propertyExpression, GetComparer<TSelector>(), because, becauseArgs);
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion Src/FluentAssertions/Formatting/DefaultValueFormatter.cs
Expand Up @@ -76,7 +76,7 @@ private void WriteTypeAndMemberValues(object obj, FormattedObjectGraph formatted
formattedGraph.AddLine("{");

MemberInfo[] members = GetMembers(type);
using var iterator = new Iterator<MemberInfo>(members.OrderBy(mi => mi.Name));
using var iterator = new Iterator<MemberInfo>(members.OrderBy(mi => mi.Name, StringComparer.Ordinal));
while (iterator.MoveNext())
{
WriteMemberValueTextFor(obj, iterator.Current, formattedGraph, formatChild);
Expand Down
17 changes: 17 additions & 0 deletions Tests/FluentAssertions.Specs/Primitives/StringComparisonSpecs.cs
Expand Up @@ -333,6 +333,23 @@ public void Matching_strings_for_equivalence_ignores_the_culture(string subject,
subject.Should().MatchEquivalentOf(expected);
}

[CulturedFact("en-US")]
public void Culture_is_ignored_when_sorting_strings()
{
using var _ = new AssertionScope();
new[] { "A", "a" }.Should().BeInAscendingOrder()
.And.BeInAscendingOrder(e => e)
.And.ThenBeInAscendingOrder(e => e)
.And.NotBeInDescendingOrder()
.And.NotBeInDescendingOrder(e => e);

new[] { "a", "A" }.Should().BeInDescendingOrder()
.And.BeInDescendingOrder(e => e)
.And.ThenBeInDescendingOrder(e => e)
.And.NotBeInAscendingOrder()
.And.NotBeInAscendingOrder(e => e);
}

private const string LowerCaseI = "i";
private const string UpperCaseI = "I";

Expand Down
1 change: 1 addition & 0 deletions docs/_pages/releases.md
Expand Up @@ -22,6 +22,7 @@ sidebar:
* Quering properties on classes, e.g. `typeof(MyClass).Properties()`, now also includes static properties - [#2054](https://github.com/fluentassertions/fluentassertions/pull/2054)
* Nested AssertionScopes now print the inner scope reportables - [#2044](https://github.com/fluentassertions/fluentassertions/pull/2044)
* Throw `ArgumentException` instead of `ArgumentNullException` when a required `string` argument is empty - [#2023](https://github.com/fluentassertions/fluentassertions/pull/2023)
* Assertions on the ordering of a collection of `string`s now uses ordinal comparison when an `IComparer<T>` is not provided - [#2075](https://github.com/fluentassertions/fluentassertions/pull/2075)

## 6.8.0

Expand Down

0 comments on commit 6a03535

Please sign in to comment.