Skip to content

Commit

Permalink
xunit/xunit#2828: Prefer IEquatable<> over custom collection equality
Browse files Browse the repository at this point in the history
  • Loading branch information
bradwilson committed Nov 28, 2023
1 parent c35ef46 commit 6e0a7cd
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 14 deletions.
20 changes: 18 additions & 2 deletions EqualityAsserts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,15 @@ partial class Assert
{
try
{
if (CollectionTracker.AreCollectionsEqual(expectedTracker, actualTracker, itemComparer, itemComparer == AssertEqualityComparer<T>.DefaultInnerComparer, out mismatchedIndex))
bool result;

// Call AssertEqualityComparer.Equals because it checks for IEquatable<> before using CollectionTracker
if (aec != null)
result = aec.Equals(expected, expectedTracker, actual, actualTracker, out mismatchedIndex);
else
result = CollectionTracker.AreCollectionsEqual(expectedTracker, actualTracker, itemComparer, itemComparer == AssertEqualityComparer<T>.DefaultInnerComparer, out mismatchedIndex);

if (result)
return;
}
catch (Exception ex)
Expand Down Expand Up @@ -651,7 +659,15 @@ partial class Assert
{
try
{
if (!CollectionTracker.AreCollectionsEqual(expectedTracker, actualTracker, itemComparer, itemComparer == AssertEqualityComparer<T>.DefaultInnerComparer, out mismatchedIndex))
bool result;

// Call AssertEqualityComparer.Equals because it checks for IEquatable<> before using CollectionTracker
if (aec != null)
result = aec.Equals(expected, expectedTracker, actual, actualTracker, out mismatchedIndex);
else
result = CollectionTracker.AreCollectionsEqual(expectedTracker, actualTracker, itemComparer, itemComparer == AssertEqualityComparer<T>.DefaultInnerComparer, out mismatchedIndex);

if (!result)
return;

// For NotEqual that doesn't throw, pointers are irrelevant, because
Expand Down
53 changes: 41 additions & 12 deletions Sdk/AssertEqualityComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,23 +126,46 @@ public AssertEqualityComparer(IEqualityComparer innerComparer = null)
T y)
#endif
{
// Null?
if (x == null && y == null)
return true;
if (x == null || y == null)
return false;
int? _;

#if !XUNIT_FRAMEWORK
// Collections?
#if XUNIT_FRAMEWORK
return Equals(x, y, out _);
#else
using (var xTracker = x.AsNonStringTracker())
using (var yTracker = y.AsNonStringTracker())
{
int? _;
return Equals(x, xTracker, y, yTracker, out _);
#endif
}

if (xTracker != null && yTracker != null)
return CollectionTracker.AreCollectionsEqual(xTracker, yTracker, InnerComparer, InnerComparer == DefaultInnerComparer, out _);
}
internal bool Equals(
#if XUNIT_NULLABLE
[AllowNull] T x,
#if !XUNIT_FRAMEWORK
CollectionTracker? xTracker,
#endif
[AllowNull] T y,
#if !XUNIT_FRAMEWORK
CollectionTracker? yTracker,
#endif
#else
T x,
#if !XUNIT_FRAMEWORK
CollectionTracker xTracker,
#endif
T y,
#if !XUNIT_FRAMEWORK
CollectionTracker yTracker,
#endif
#endif
out int? mismatchedIndex)
{
mismatchedIndex = null;

// Null?
if (x == null && y == null)
return true;
if (x == null || y == null)
return false;

// Implements IEquatable<T>?
var equatable = x as IEquatable<T>;
Expand Down Expand Up @@ -171,6 +194,12 @@ public AssertEqualityComparer(IEqualityComparer innerComparer = null)
}
}

#if !XUNIT_FRAMEWORK
// Special case collections (before IStructuralEquatable because arrays implement that in a way we don't want to call)
if (xTracker != null && yTracker != null)
return CollectionTracker.AreCollectionsEqual(xTracker, yTracker, InnerComparer, InnerComparer == DefaultInnerComparer, out mismatchedIndex);
#endif

// Implements IStructuralEquatable?
var structuralEquatable = x as IStructuralEquatable;
if (structuralEquatable != null && structuralEquatable.Equals(y, new TypeErasedEqualityComparer(innerComparer.Value)))
Expand Down

0 comments on commit 6e0a7cd

Please sign in to comment.