From 14ff25e0656e2fdf783f41ff00706ab65110ccff Mon Sep 17 00:00:00 2001 From: Artur Krajewski Date: Wed, 19 Sep 2018 21:25:13 +0200 Subject: [PATCH 1/9] Force strinct ordering for collection created by AllBeEquivalentTo. Improves algorithmic complexity from O(n^2) to O(n) --- .../GenericCollectionAssertions.cs | 14 ++++++-- .../CollectionEquivalencySpecs.cs | 35 +++++++++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs index c2a03c741f..4f6aad6248 100644 --- a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs +++ b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs @@ -331,12 +331,22 @@ private bool IsValidProperty(Expression> propertyE /// Zero or more objects to format using the placeholders in . /// public void AllBeEquivalentTo(TExpectation expectation, - Func, EquivalencyAssertionOptions> config, string because = "", + Func, EquivalencyAssertionOptions> 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 + // 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> forceStringOrderingConfig = + x => config(x).WithStrictOrderingFor(s => s.SelectedMemberPath == ""); + + BeEquivalentTo(repeatedExpectation, forceStringOrderingConfig, because, becauseArgs); } private static IEnumerable RepeatAsManyAs(TExpectation value, IEnumerable enumerable) diff --git a/Tests/Shared.Specs/CollectionEquivalencySpecs.cs b/Tests/Shared.Specs/CollectionEquivalencySpecs.cs index e34c9838e0..9b553ded5b 100644 --- a/Tests/Shared.Specs/CollectionEquivalencySpecs.cs +++ b/Tests/Shared.Specs/CollectionEquivalencySpecs.cs @@ -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 @@ -900,7 +900,7 @@ public void When_all_subject_items_are_not_equivalent_to_expectation_object_it_s // Act //----------------------------------------------------------------------------------------------------------- Action action = () => subject.Should().AllBeEquivalentTo(1); - + ////----------------------------------------------------------------------------------------------------------- //// Assert ////----------------------------------------------------------------------------------------------------------- @@ -908,6 +908,37 @@ public void When_all_subject_items_are_not_equivalent_to_expectation_object_it_s "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; + var subject = new List(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 + } + }; + + ////----------------------------------------------------------------------------------------------------------- + //// 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 From 502977ce76f54a7d641b5d9b7178ba84db952416 Mon Sep 17 00:00:00 2001 From: Artur Krajewski Date: Sun, 23 Sep 2018 11:34:20 +0200 Subject: [PATCH 2/9] Extract comparison of item for strictly matched collection to new scope --- .../Equivalency/EnumerableEquivalencyValidator.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs b/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs index 880b5141b1..889e64dd96 100644 --- a/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs +++ b/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs @@ -139,7 +139,10 @@ private string[] TryToMatch(object subject, T expectation, int expectationInd private void StrictlyMatchAgainst(object[] subjects, T expectation, int expectationIndex) { - parent.AssertEqualityUsing(context.CreateForCollectionItem(expectationIndex.ToString(), subjects[expectationIndex], expectation)); + using (var scope = new AssertionScope()) + { + parent.AssertEqualityUsing(context.CreateForCollectionItem(expectationIndex.ToString(), subjects[expectationIndex], expectation)); + } } } } From 0248f6963e6fd01a72440ce92a086ec78627afa8 Mon Sep 17 00:00:00 2001 From: Artur Krajewski Date: Sun, 23 Sep 2018 17:53:15 +0200 Subject: [PATCH 3/9] Extract `OrderingRules.IsOrderingStrictFor` before the loop I found out during profiling that repeated call to this method was taking up to 10% of the execution time. --- .../EnumerableEquivalencyValidator.cs | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs b/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs index 889e64dd96..a71af11820 100644 --- a/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs +++ b/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs @@ -62,22 +62,27 @@ private void AssertElementGraphEquivalency(object[] subjects, T[] expectation unmatchedSubjectIndexes = new List(subjects.Length); unmatchedSubjectIndexes.AddRange(Enumerable.Range(0, subjects.Length)); - foreach (int index in Enumerable.Range(0, expectations.Length)) + if (OrderingRules.IsOrderingStrictFor(context)) { - T expectation = expectations[index]; - - if (!OrderingRules.IsOrderingStrictFor(context)) + foreach (int index in Enumerable.Range(0, expectations.Length)) { - using (context.TraceBlock(path => $"Finding the best match of {expectation} within all items in {subjects} at {path}[{index}]")) + T expectation = expectations[index]; + + using (context.TraceBlock(path => $"Strictly comparing expectation {expectation} at {path} to item with index {index} in {subjects}")) { - LooselyMatchAgainst(subjects, expectation, index); + StrictlyMatchAgainst(subjects, expectation, index); } } - else + } + else + { + foreach (int index in Enumerable.Range(0, expectations.Length)) { - using (context.TraceBlock(path => $"Strictly comparing expectation {expectation} at {path} to item with index {index} in {subjects}")) + T expectation = expectations[index]; + + using (context.TraceBlock(path => $"Finding the best match of {expectation} within all items in {subjects} at {path}[{index}]")) { - StrictlyMatchAgainst(subjects, expectation, index); + LooselyMatchAgainst(subjects, expectation, index); } } } From 1ed5ecd9e98843ce6aa5decc9e5482f70dea9075 Mon Sep 17 00:00:00 2001 From: Artur Krajewski Date: Sun, 23 Sep 2018 20:51:17 +0200 Subject: [PATCH 4/9] Fail fast after 10 items are detected to be incorrect in `EnumerableEquivalencyValidator` --- .../EnumerableEquivalencyValidator.cs | 24 ++++++++++++++++--- .../Execution/AssertionScope.cs | 6 +++++ .../CollectionEquivalencySpecs.cs | 23 +++++++++++++++++- 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs b/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs index a71af11820..512291758a 100644 --- a/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs +++ b/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs @@ -64,13 +64,23 @@ private void AssertElementGraphEquivalency(object[] subjects, T[] expectation if (OrderingRules.IsOrderingStrictFor(context)) { + int failedCount = 0; foreach (int index in Enumerable.Range(0, expectations.Length)) { T expectation = expectations[index]; using (context.TraceBlock(path => $"Strictly comparing expectation {expectation} at {path} to item with index {index} in {subjects}")) { - StrictlyMatchAgainst(subjects, expectation, index); + bool succeed = StrictlyMatchAgainst(subjects, expectation, index); + if(!succeed) + { + failedCount++; + if(failedCount >= 10) + { + //trace; + break; + } + } } } } @@ -142,11 +152,19 @@ private string[] TryToMatch(object subject, T expectation, int expectationInd } } - private void StrictlyMatchAgainst(object[] subjects, T expectation, int expectationIndex) + private bool StrictlyMatchAgainst(object[] subjects, T expectation, int expectationIndex) { using (var scope = new AssertionScope()) { - parent.AssertEqualityUsing(context.CreateForCollectionItem(expectationIndex.ToString(), subjects[expectationIndex], expectation)); + object subject = subjects[expectationIndex]; + string indexString = expectationIndex.ToString(); + var equivalencyValidationContext = context + .CreateForCollectionItem(indexString, subject, expectation); + + parent.AssertEqualityUsing(equivalencyValidationContext); + + bool failed = scope.AnyFailures(); + return !failed; } } } diff --git a/Src/FluentAssertions/Execution/AssertionScope.cs b/Src/FluentAssertions/Execution/AssertionScope.cs index 6c5d11a782..fae3106b85 100644 --- a/Src/FluentAssertions/Execution/AssertionScope.cs +++ b/Src/FluentAssertions/Execution/AssertionScope.cs @@ -284,6 +284,12 @@ public string[] Discard() return assertionStrategy.DiscardFailures().ToArray(); } + public bool AnyFailures() + { + return assertionStrategy.FailureMessages.Any(); + } + + /// /// Gets data associated with the current scope and identified by . /// diff --git a/Tests/Shared.Specs/CollectionEquivalencySpecs.cs b/Tests/Shared.Specs/CollectionEquivalencySpecs.cs index 9b553ded5b..94aff3d39d 100644 --- a/Tests/Shared.Specs/CollectionEquivalencySpecs.cs +++ b/Tests/Shared.Specs/CollectionEquivalencySpecs.cs @@ -908,6 +908,27 @@ public void When_some_subject_items_are_not_equivalent_to_expectation_object_it_ "Expected item[1] to be 1, but found 2.*Expected item[2] to be 1, but found 3*"); } + [Fact] + public void When_more_than_10_subjects_items_are_not_equivalent_to_expectation_only_10_are_reported() + { + //----------------------------------------------------------------------------------------------------------- + // Arrange + //----------------------------------------------------------------------------------------------------------- + var subject = Enumerable.Repeat(2, 11); + + //----------------------------------------------------------------------------------------------------------- + // Act + //----------------------------------------------------------------------------------------------------------- + Action action = () => subject.Should().AllBeEquivalentTo(1); + + ////----------------------------------------------------------------------------------------------------------- + //// Assert + ////----------------------------------------------------------------------------------------------------------- + action.Should().Throw().Which + .Message.Should().Contain("item[9] to be 1, but found 2") + .And.NotContain("item[10]"); + } + [Fact] public void When_some_subject_items_are_not_equivalent_to_expectation_for_huge_table_execution_time_should_still_be_short() { @@ -936,7 +957,7 @@ public void When_some_subject_items_are_not_equivalent_to_expectation_for_huge_t ////----------------------------------------------------------------------------------------------------------- //// Assert ////----------------------------------------------------------------------------------------------------------- - action.ExecutionTime().Should().BeLessThan(100.Seconds()); + action.ExecutionTime().Should().BeLessThan(1.Seconds()); } [Fact] From 5cfc85c2cc8ba1f33d724abd5bf19b1eb312b1a8 Mon Sep 17 00:00:00 2001 From: Artur Krajewski Date: Sun, 23 Sep 2018 21:03:41 +0200 Subject: [PATCH 5/9] Add trace message in case of fast fail comparing enumerable equivalency --- .../Equivalency/EnumerableEquivalencyValidator.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs b/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs index 512291758a..944322ef48 100644 --- a/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs +++ b/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs @@ -74,10 +74,11 @@ private void AssertElementGraphEquivalency(object[] subjects, T[] expectation bool succeed = StrictlyMatchAgainst(subjects, expectation, index); if(!succeed) { + const int limit = 10; failedCount++; if(failedCount >= 10) { - //trace; + context.TraceSingle(path => $"Fail failing strict order comparison of collection after {limit} items failed at {path}"); break; } } From 7df628ddfb01e9103192f9753a806118ccd974fa Mon Sep 17 00:00:00 2001 From: Artur Krajewski Date: Sun, 23 Sep 2018 21:19:00 +0200 Subject: [PATCH 6/9] Extract methods from `EnumerableEquivalencyValidator.AssertElementGraphEquivalency` --- .../EnumerableEquivalencyValidator.cs | 58 ++++++++++++------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs b/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs index 944322ef48..bedde837b6 100644 --- a/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs +++ b/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs @@ -11,6 +11,8 @@ namespace FluentAssertions.Equivalency /// internal class EnumerableEquivalencyValidator { + private const int FailedItemsFastFailThreshold = 10; + #region Private Definitions private readonly IEquivalencyValidator parent; @@ -64,37 +66,49 @@ private void AssertElementGraphEquivalency(object[] subjects, T[] expectation if (OrderingRules.IsOrderingStrictFor(context)) { - int failedCount = 0; - foreach (int index in Enumerable.Range(0, expectations.Length)) - { - T expectation = expectations[index]; + AssertElementGraphEquivalencyWithStrictOrdering(subjects, expectations); + } + else + { + AssertElementGraphEquivalencyWithLooseOrdering(subjects, expectations); + } + } + + private void AssertElementGraphEquivalencyWithStrictOrdering(object[] subjects, T[] expectations) + { + int failedCount = 0; + foreach (int index in Enumerable.Range(0, expectations.Length)) + { + T expectation = expectations[index]; - using (context.TraceBlock(path => $"Strictly comparing expectation {expectation} at {path} to item with index {index} in {subjects}")) + using (context.TraceBlock(path => + $"Strictly comparing expectation {expectation} at {path} to item with index {index} in {subjects}")) + { + bool succeed = StrictlyMatchAgainst(subjects, expectation, index); + if (!succeed) { - bool succeed = StrictlyMatchAgainst(subjects, expectation, index); - if(!succeed) + failedCount++; + if (failedCount >= 10) { - const int limit = 10; - failedCount++; - if(failedCount >= 10) - { - context.TraceSingle(path => $"Fail failing strict order comparison of collection after {limit} items failed at {path}"); - break; - } + context.TraceSingle(path => + $"Fail failing strict order comparison of collection after {FailedItemsFastFailThreshold} items failed at {path}"); + break; } } } } - else + } + + private void AssertElementGraphEquivalencyWithLooseOrdering(object[] subjects, T[] expectations) + { + foreach (int index in Enumerable.Range(0, expectations.Length)) { - foreach (int index in Enumerable.Range(0, expectations.Length)) + T expectation = expectations[index]; + + using (context.TraceBlock(path => + $"Finding the best match of {expectation} within all items in {subjects} at {path}[{index}]")) { - T expectation = expectations[index]; - - using (context.TraceBlock(path => $"Finding the best match of {expectation} within all items in {subjects} at {path}[{index}]")) - { - LooselyMatchAgainst(subjects, expectation, index); - } + LooselyMatchAgainst(subjects, expectation, index); } } } From 92c1acc2a3b6f96f9a51a1ced5878702994daa5b Mon Sep 17 00:00:00 2001 From: Artur Krajewski Date: Sun, 23 Sep 2018 22:02:50 +0200 Subject: [PATCH 7/9] Fast fail enumerable equivalency with loose ordering after 10 failed items --- .../EnumerableEquivalencyValidator.cs | 20 +++++++++++++---- .../CollectionEquivalencySpecs.cs | 22 +++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs b/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs index bedde837b6..1975c8e85e 100644 --- a/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs +++ b/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs @@ -88,7 +88,7 @@ private void AssertElementGraphEquivalencyWithStrictOrdering(object[] subject if (!succeed) { failedCount++; - if (failedCount >= 10) + if (failedCount >= FailedItemsFastFailThreshold) { context.TraceSingle(path => $"Fail failing strict order comparison of collection after {FailedItemsFastFailThreshold} items failed at {path}"); @@ -101,6 +101,7 @@ private void AssertElementGraphEquivalencyWithStrictOrdering(object[] subject private void AssertElementGraphEquivalencyWithLooseOrdering(object[] subjects, T[] expectations) { + int failedCount = 0; foreach (int index in Enumerable.Range(0, expectations.Length)) { T expectation = expectations[index]; @@ -108,19 +109,28 @@ private void AssertElementGraphEquivalencyWithLooseOrdering(object[] subjects using (context.TraceBlock(path => $"Finding the best match of {expectation} within all items in {subjects} at {path}[{index}]")) { - LooselyMatchAgainst(subjects, expectation, index); + bool succeed = LooselyMatchAgainst(subjects, expectation, index); + if (!succeed) + { + failedCount++; + if (failedCount >= FailedItemsFastFailThreshold) + { + context.TraceSingle(path => + $"Fail failing loose order comparison of collection after {FailedItemsFastFailThreshold} items failed at {path}"); + break; + } + } } } } private List unmatchedSubjectIndexes; - private void LooselyMatchAgainst(IList subjects, T expectation, int expectationIndex) + private bool LooselyMatchAgainst(IList subjects, T expectation, int expectationIndex) { var results = new AssertionResultSet(); int index = 0; GetTraceMessage getMessage = path => $"Comparing subject at {path}[{index}] with the expectation at {path}[{expectationIndex}]"; - int count = subjects.Count; int indexToBeRemoved = -1; for (var metaIndex = 0; metaIndex < unmatchedSubjectIndexes.Count; metaIndex++) @@ -155,6 +165,8 @@ private void LooselyMatchAgainst(IList subjects, T expectation, int e { AssertionScope.Current.AddPreFormattedFailure(failure); } + + return indexToBeRemoved != -1; } private string[] TryToMatch(object subject, T expectation, int expectationIndex) diff --git a/Tests/Shared.Specs/CollectionEquivalencySpecs.cs b/Tests/Shared.Specs/CollectionEquivalencySpecs.cs index 94aff3d39d..bff43aae27 100644 --- a/Tests/Shared.Specs/CollectionEquivalencySpecs.cs +++ b/Tests/Shared.Specs/CollectionEquivalencySpecs.cs @@ -324,6 +324,28 @@ public void When_a_collection_does_not_match_it_should_include_items_in_message( .WithMessage("Expected*but*{1, 2}*1 item(s) less than*{3, 2, 1}*"); } + [Fact] + public void When_collection_of_same_count_does_not_match_it_should_include_at_most_10_items_in_message() + { + //----------------------------------------------------------------------------------------------------------- + // Arrange + //----------------------------------------------------------------------------------------------------------- + const int commonLength = 11; + var subject = new int[commonLength] { 0, 1, 2, 2, 4, 5, 6, 7, 8, 9, 10 }; + var expectation = Enumerable.Repeat(20, commonLength).ToArray(); + + //----------------------------------------------------------------------------------------------------------- + // Act + //----------------------------------------------------------------------------------------------------------- + Action action = () => subject.Should().BeEquivalentTo(expectation); + + //----------------------------------------------------------------------------------------------------------- + // Assert + //----------------------------------------------------------------------------------------------------------- + action.Should().Throw().Which + .Message.Should().Contain("[9]").And.NotContain("[10]"); + } + [Fact] public void When_a_nullable_collection_does_not_match_it_should_throw() { From 051ade9963659f0fe6412d879d2c76dff53cf741 Mon Sep 17 00:00:00 2001 From: Artur Krajewski Date: Wed, 26 Sep 2018 06:47:48 +0200 Subject: [PATCH 8/9] Fixes after code reviews --- .../Collections/GenericCollectionAssertions.cs | 2 +- .../Equivalency/EnumerableEquivalencyValidator.cs | 14 +++++++------- Src/FluentAssertions/Execution/AssertionScope.cs | 2 +- Tests/Shared.Specs/CollectionEquivalencySpecs.cs | 3 ++- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs index 4f6aad6248..12e9a60e5c 100644 --- a/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs +++ b/Src/FluentAssertions/Collections/GenericCollectionAssertions.cs @@ -338,7 +338,7 @@ private bool IsValidProperty(Expression> propertyE TExpectation[] repeatedExpectation = RepeatAsManyAs(expectation, Subject).ToArray(); // 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 + // we are sure that we can force strict ordering, because ordering does not matter in terms // 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 diff --git a/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs b/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs index 1975c8e85e..94dc07a06e 100644 --- a/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs +++ b/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs @@ -84,8 +84,8 @@ private void AssertElementGraphEquivalencyWithStrictOrdering(object[] subject using (context.TraceBlock(path => $"Strictly comparing expectation {expectation} at {path} to item with index {index} in {subjects}")) { - bool succeed = StrictlyMatchAgainst(subjects, expectation, index); - if (!succeed) + bool succeeded = StrictlyMatchAgainst(subjects, expectation, index); + if (!succeeded) { failedCount++; if (failedCount >= FailedItemsFastFailThreshold) @@ -109,8 +109,8 @@ private void AssertElementGraphEquivalencyWithLooseOrdering(object[] subjects using (context.TraceBlock(path => $"Finding the best match of {expectation} within all items in {subjects} at {path}[{index}]")) { - bool succeed = LooselyMatchAgainst(subjects, expectation, index); - if (!succeed) + bool succeeded = LooselyMatchAgainst(subjects, expectation, index); + if (!succeeded) { failedCount++; if (failedCount >= FailedItemsFastFailThreshold) @@ -185,12 +185,12 @@ private bool StrictlyMatchAgainst(object[] subjects, T expectation, int expec { object subject = subjects[expectationIndex]; string indexString = expectationIndex.ToString(); - var equivalencyValidationContext = context - .CreateForCollectionItem(indexString, subject, expectation); + IEquivalencyValidationContext equivalencyValidationContext = + context.CreateForCollectionItem(indexString, subject, expectation); parent.AssertEqualityUsing(equivalencyValidationContext); - bool failed = scope.AnyFailures(); + bool failed = scope.HasFailures(); return !failed; } } diff --git a/Src/FluentAssertions/Execution/AssertionScope.cs b/Src/FluentAssertions/Execution/AssertionScope.cs index fae3106b85..89e4dda631 100644 --- a/Src/FluentAssertions/Execution/AssertionScope.cs +++ b/Src/FluentAssertions/Execution/AssertionScope.cs @@ -284,7 +284,7 @@ public string[] Discard() return assertionStrategy.DiscardFailures().ToArray(); } - public bool AnyFailures() + public bool HasFailures() { return assertionStrategy.FailureMessages.Any(); } diff --git a/Tests/Shared.Specs/CollectionEquivalencySpecs.cs b/Tests/Shared.Specs/CollectionEquivalencySpecs.cs index bff43aae27..5ba64abf9a 100644 --- a/Tests/Shared.Specs/CollectionEquivalencySpecs.cs +++ b/Tests/Shared.Specs/CollectionEquivalencySpecs.cs @@ -331,7 +331,8 @@ public void When_collection_of_same_count_does_not_match_it_should_include_at_mo // Arrange //----------------------------------------------------------------------------------------------------------- const int commonLength = 11; - var subject = new int[commonLength] { 0, 1, 2, 2, 4, 5, 6, 7, 8, 9, 10 }; + // Subjects contains different values, because we want to distinguish them in the assertion message + var subject = new int[commonLength] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var expectation = Enumerable.Repeat(20, commonLength).ToArray(); //----------------------------------------------------------------------------------------------------------- From dce8193581766d4321bffec8df31f942f04483bd Mon Sep 17 00:00:00 2001 From: Artur Krajewski Date: Wed, 26 Sep 2018 06:56:20 +0200 Subject: [PATCH 9/9] Fix wording in trace message for aborting comparison of collections --- .../Equivalency/EnumerableEquivalencyValidator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs b/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs index 94dc07a06e..f8d5b52ce8 100644 --- a/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs +++ b/Src/FluentAssertions/Equivalency/EnumerableEquivalencyValidator.cs @@ -91,7 +91,7 @@ private void AssertElementGraphEquivalencyWithStrictOrdering(object[] subject if (failedCount >= FailedItemsFastFailThreshold) { context.TraceSingle(path => - $"Fail failing strict order comparison of collection after {FailedItemsFastFailThreshold} items failed at {path}"); + $"Aborting strict order comparison of collections after {FailedItemsFastFailThreshold} items failed at {path}"); break; } }