Skip to content

Commit 1d43f8b

Browse files
authored
Fix NaN behavior for Assert.AreEqual/AreNotEqual (#4536)
1 parent 3f21675 commit 1d43f8b

File tree

2 files changed

+738
-6
lines changed

2 files changed

+738
-6
lines changed

src/TestFramework/TestFramework/Assertions/Assert.AreEqual.cs

+74-6
Original file line numberDiff line numberDiff line change
@@ -647,12 +647,44 @@ private static bool AreEqualFailing(string? expected, string? actual, bool ignor
647647
=> CompareInternal(expected, actual, ignoreCase, culture) != 0;
648648

649649
private static bool AreEqualFailing(float expected, float actual, float delta)
650-
=> float.IsNaN(expected) || float.IsNaN(actual) || float.IsNaN(delta) ||
651-
Math.Abs(expected - actual) > delta;
650+
{
651+
if (float.IsNaN(delta) || delta < 0)
652+
{
653+
// NaN and negative values don't make sense as a delta value.
654+
throw new ArgumentOutOfRangeException(nameof(delta));
655+
}
656+
657+
if (expected.Equals(actual))
658+
{
659+
return false;
660+
}
661+
662+
// If both floats are NaN, then they were considered equal in the previous check.
663+
// If only one of them is NaN, then they are not equal regardless of the value of delta.
664+
// Then, the subtraction comparison to delta isn't involving NaNs.
665+
return float.IsNaN(expected) || float.IsNaN(actual) ||
666+
Math.Abs(expected - actual) > delta;
667+
}
652668

653669
private static bool AreEqualFailing(double expected, double actual, double delta)
654-
=> double.IsNaN(expected) || double.IsNaN(actual) || double.IsNaN(delta) ||
655-
Math.Abs(expected - actual) > delta;
670+
{
671+
if (double.IsNaN(delta) || delta < 0)
672+
{
673+
// NaN and negative values don't make sense as a delta value.
674+
throw new ArgumentOutOfRangeException(nameof(delta));
675+
}
676+
677+
if (expected.Equals(actual))
678+
{
679+
return false;
680+
}
681+
682+
// If both doubles are NaN, then they were considered equal in the previous check.
683+
// If only one of them is NaN, then they are not equal regardless of the value of delta.
684+
// Then, the subtraction comparison to delta isn't involving NaNs.
685+
return double.IsNaN(expected) || double.IsNaN(actual) ||
686+
Math.Abs(expected - actual) > delta;
687+
}
656688

657689
private static bool AreEqualFailing(decimal expected, decimal actual, decimal delta)
658690
=> Math.Abs(expected - actual) > delta;
@@ -1086,7 +1118,25 @@ public static void AreNotEqual(float notExpected, float actual, float delta, [St
10861118
}
10871119

10881120
private static bool AreNotEqualFailing(float notExpected, float actual, float delta)
1089-
=> Math.Abs(notExpected - actual) <= delta;
1121+
{
1122+
if (float.IsNaN(delta) || delta < 0)
1123+
{
1124+
// NaN and negative values don't make sense as a delta value.
1125+
throw new ArgumentOutOfRangeException(nameof(delta));
1126+
}
1127+
1128+
if (float.IsNaN(notExpected) && float.IsNaN(actual))
1129+
{
1130+
// If both notExpected and actual are NaN, then AreNotEqual should fail.
1131+
return true;
1132+
}
1133+
1134+
// Note: if both notExpected and actual are NaN, that was handled separately above.
1135+
// Now, if both are numerics, then the logic is good.
1136+
// And, if only one of them is NaN, we know they are not equal, meaning AreNotEqual shouldn't fail.
1137+
// And in this case we will correctly be returning false, because NaN <= anything is always false.
1138+
return Math.Abs(notExpected - actual) <= delta;
1139+
}
10901140

10911141
/// <summary>
10921142
/// Tests whether the specified decimals are equal and throws an exception
@@ -1645,7 +1695,25 @@ public static void AreNotEqual(double notExpected, double actual, double delta,
16451695
}
16461696

16471697
private static bool AreNotEqualFailing(double notExpected, double actual, double delta)
1648-
=> Math.Abs(notExpected - actual) <= delta;
1698+
{
1699+
if (double.IsNaN(delta) || delta < 0)
1700+
{
1701+
// NaN and negative values don't make sense as a delta value.
1702+
throw new ArgumentOutOfRangeException(nameof(delta));
1703+
}
1704+
1705+
if (double.IsNaN(notExpected) && double.IsNaN(actual))
1706+
{
1707+
// If both notExpected and actual are NaN, then AreNotEqual should fail.
1708+
return true;
1709+
}
1710+
1711+
// Note: if both notExpected and actual are NaN, that was handled separately above.
1712+
// Now, if both are numerics, then the logic is good.
1713+
// And, if only one of them is NaN, we know they are not equal, meaning AreNotEqual shouldn't fail.
1714+
// And in this case we will correctly be returning false, because NaN <= anything is always false.
1715+
return Math.Abs(notExpected - actual) <= delta;
1716+
}
16491717

16501718
[DoesNotReturn]
16511719
private static void ThrowAssertAreNotEqualFailed<T>(T notExpected, T actual, T delta, string userMessage)

0 commit comments

Comments
 (0)