Skip to content

Commit

Permalink
Avoid allocating sub-arrays in ContainInOrder
Browse files Browse the repository at this point in the history
The added tests are only to ensure behavior has not changed.
  • Loading branch information
jnyrup committed Jul 22, 2022
1 parent 1f4db92 commit 5d6e1ec
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 36 deletions.
70 changes: 34 additions & 36 deletions Src/FluentAssertions/Collections/GenericCollectionAssertions.cs
Expand Up @@ -920,16 +920,15 @@ public AndConstraint<TAssertions> ContainInOrder(params T[] expected)
IList<T> expectedItems = expected.ConvertOrCastToList();
IList<T> actualItems = Subject.ConvertOrCastToList();

int subjectIndex = 0;

Func<T, T, bool> areSameOrEqual = ObjectExtensions.GetComparer<T>();
for (int index = 0; index < expectedItems.Count; index++)
{
T expectedItem = expectedItems[index];
actualItems = actualItems.SkipWhile(actualItem => !areSameOrEqual(actualItem, expectedItem)).ToArray();
if (actualItems.Any())
{
actualItems = actualItems.Skip(1).ToArray();
}
else
subjectIndex = IndexOf(actualItems, expectedItem, subjectIndex, areSameOrEqual);

if (subjectIndex == -1)
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
Expand Down Expand Up @@ -2261,43 +2260,28 @@ public AndConstraint<TAssertions> NotContainInOrder(params T[] unexpected)
}

IList<T> unexpectedItems = unexpected.ConvertOrCastToList();
IList<T> actualItems = Subject.ConvertOrCastToList();

if (unexpectedItems.Count > actualItems.Count)
if (unexpectedItems.Any())
{
return new AndConstraint<TAssertions>((TAssertions)this);
}

var actualItemsSkipped = 0;
Func<T, T, bool> areSameOrEqual = ObjectExtensions.GetComparer<T>();
for (int index = 0; index < unexpectedItems.Count; index++)
{
T unexpectedItem = unexpectedItems[index];
IList<T> actualItems = Subject.ConvertOrCastToList();
int subjectIndex = 0;

actualItems = actualItems.SkipWhile(actualItem =>
Func<T, T, bool> areSameOrEqual = ObjectExtensions.GetComparer<T>();
foreach (var unexpectedItem in unexpectedItems)
{
actualItemsSkipped++;
return !areSameOrEqual(actualItem, unexpectedItem);
}).ToArray();
subjectIndex = IndexOf(actualItems, unexpectedItem, subjectIndex, areSameOrEqual);

if (actualItems.Any())
{
if (index == unexpectedItems.Count - 1)
if (subjectIndex == -1)
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith(
"Expected {context:collection} {0} to not contain items {1} in order{reason}, " +
"but items appeared in order ending at index {2}.",
Subject, unexpected, actualItemsSkipped - 1);
return new AndConstraint<TAssertions>((TAssertions)this);
}

actualItems = actualItems.Skip(1).ToArray();
}
else
{
return new AndConstraint<TAssertions>((TAssertions)this);
}

Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith(
"Expected {context:collection} {0} to not contain items {1} in order{reason}, " +
"but items appeared in order ending at index {2}.",
Subject, unexpected, subjectIndex - 1);
}

return new AndConstraint<TAssertions>((TAssertions)this);
Expand Down Expand Up @@ -3310,4 +3294,18 @@ private AndConstraint<TAssertions> NotBeInOrder(IComparer<T> comparer, SortOrder

return new AndConstraint<TAssertions>((TAssertions)this);
}

private static int IndexOf(IList<T> items, T item, int index, Func<T, T, bool> comparer)
{
for (; index < items.Count; index++)
{
if (comparer(items[index], item))
{
index++;
return index;
}
}

return -1;
}
}
Expand Up @@ -100,6 +100,20 @@ public void When_passing_in_null_while_checking_for_ordered_containment_it_shoul
"Cannot verify ordered containment against a <null> collection.*");
}

[Fact]
public void Collections_contain_the_empty_sequence()
{
// Assert
new[] { 1 }.Should().ContainInOrder(new int[0]);
}

[Fact]
public void Collections_do_not_not_contain_the_empty_sequence()
{
// Assert
new[] { 1 }.Should().NotContainInOrder(new int[0]);
}

[Fact]
public void When_asserting_collection_contains_some_values_in_order_but_collection_is_null_it_should_throw()
{
Expand Down

0 comments on commit 5d6e1ec

Please sign in to comment.