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

Improve performance of AllBeEquivalentTo #920

14 changes: 12 additions & 2 deletions Src/FluentAssertions/Collections/GenericCollectionAssertions.cs
Expand Up @@ -331,12 +331,22 @@ private bool IsValidProperty<TSelector>(Expression<Func<T, TSelector>> propertyE
/// Zero or more objects to format using the placeholders in <see cref="because" />.
/// </param>
public void AllBeEquivalentTo<TExpectation>(TExpectation expectation,
Func<EquivalencyAssertionOptions<TExpectation>, EquivalencyAssertionOptions<TExpectation>> config, string because = "",
Func<EquivalencyAssertionOptions<TExpectation>, EquivalencyAssertionOptions<TExpectation>> config,
string because = "",
params object[] becauseArgs)
{
TExpectation[] repeatedExpectation = RepeatAsManyAs(expectation, Subject).ToArray();

BeEquivalentTo(repeatedExpectation, config, because, becauseArgs);
// Because we have just manually created the collection based on single element
// we are sure that we can force string ordering, because ordering does not matter in terms
krajek marked this conversation as resolved.
Show resolved Hide resolved
// of correctness. On the other hand we do not want to change ordering rules for nested objects
// in case user needs to use them. Strict ordering improves algorithmic complexity
// from O(n^2) to O(n). For bigger tables it is necessary in order to achieve acceptable
// execution times.
Func<EquivalencyAssertionOptions<TExpectation>, EquivalencyAssertionOptions<TExpectation>> forceStringOrderingConfig =
x => config(x).WithStrictOrderingFor(s => s.SelectedMemberPath == "");

BeEquivalentTo(repeatedExpectation, forceStringOrderingConfig, because, becauseArgs);
}

private static IEnumerable<TExpectation> RepeatAsManyAs<TExpectation>(TExpectation value, IEnumerable<T> enumerable)
Expand Down
35 changes: 33 additions & 2 deletions Tests/Shared.Specs/CollectionEquivalencySpecs.cs
Expand Up @@ -889,7 +889,7 @@ public void When_all_subject_items_are_equivalent_to_expectation_object_it_shoul
}

[Fact]
public void When_all_subject_items_are_not_equivalent_to_expectation_object_it_should_throw()
public void When_some_subject_items_are_not_equivalent_to_expectation_object_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
Expand All @@ -900,14 +900,45 @@ public void When_all_subject_items_are_not_equivalent_to_expectation_object_it_s
// Act
//-----------------------------------------------------------------------------------------------------------
Action action = () => subject.Should().AllBeEquivalentTo(1);

////-----------------------------------------------------------------------------------------------------------
//// Assert
////-----------------------------------------------------------------------------------------------------------
action.Should().Throw<XunitException>().WithMessage(
"Expected item[1] to be 1, but found 2.*Expected item[2] to be 1, but found 3*");
}

[Fact]
public void When_some_subject_items_are_not_equivalent_to_expectation_for_huge_table_execution_time_should_still_be_short()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
const int N = 100000;
krajek marked this conversation as resolved.
Show resolved Hide resolved
var subject = new List<int>(N) {1};
for (int i = 1; i < N; i++)
{
subject.Add(2);
}

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action action = () =>
{
try { subject.Should().AllBeEquivalentTo(1); }
catch (Exception)
{
// ignored, we only care about execution time
krajek marked this conversation as resolved.
Show resolved Hide resolved
}
};

////-----------------------------------------------------------------------------------------------------------
krajek marked this conversation as resolved.
Show resolved Hide resolved
//// Assert
////-----------------------------------------------------------------------------------------------------------
action.ExecutionTime().Should().BeLessThan(100.Seconds());
}

[Fact]
public void
When_an_object_implements_multiple_IEnumerable_interfaces_but_the_declared_type_is_assignable_to_only_one_it_should_respect_the_declared_type
Expand Down