From 092da26c432eda6ffc28da8831e630a3e4ba8d37 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 23 Jul 2025 22:15:41 +0000 Subject: [PATCH] Consider NaN a match for itself Closes #2520 A `double.nan` is not equal to itself with the `==` operator, but for the purposes of testing it's easier to consider a `NaN` correct where a `NaN` was expected. This matches behavior in several other testing frameworks in other languages. This is unlikely to cause existing tests to fail or change semantics since a test is unlikely to use non-equality as a positive indication of a `NaN` result. --- pkgs/matcher/CHANGELOG.md | 3 ++- pkgs/matcher/lib/src/equals_matcher.dart | 10 +++++++-- pkgs/matcher/test/core_matchers_test.dart | 26 +++++++++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/pkgs/matcher/CHANGELOG.md b/pkgs/matcher/CHANGELOG.md index 18d0d6b54..dfb0c9ef0 100644 --- a/pkgs/matcher/CHANGELOG.md +++ b/pkgs/matcher/CHANGELOG.md @@ -1,9 +1,10 @@ ## 0.12.18-wip +* Add `isSorted` and related matchers for iterables. +* Consider `NaN` to be equal to itself in `equals`. * Remove some dynamic invocations. * Add explicit casts from `dynamic` values. * Require Dart 3.7 -* Add `isSorted` and related matchers for iterables. ## 0.12.17 diff --git a/pkgs/matcher/lib/src/equals_matcher.dart b/pkgs/matcher/lib/src/equals_matcher.dart index 4cb54118c..d1f5d39ad 100644 --- a/pkgs/matcher/lib/src/equals_matcher.dart +++ b/pkgs/matcher/lib/src/equals_matcher.dart @@ -209,9 +209,15 @@ class _DeepMatcher extends Matcher { } }); } else { - // Otherwise, test for equality. + // Otherwise, test for equality, or both values NaN try { - if (expected == actual) return null; + if (expected == actual || + (expected is num && + expected.isNaN && + actual is num && + actual.isNaN)) { + return null; + } } catch (e) { // TODO(gram): Add a test for this case. return _Mismatch( diff --git a/pkgs/matcher/test/core_matchers_test.dart b/pkgs/matcher/test/core_matchers_test.dart index 0a764312d..f481471d9 100644 --- a/pkgs/matcher/test/core_matchers_test.dart +++ b/pkgs/matcher/test/core_matchers_test.dart @@ -95,6 +95,32 @@ void main() { ); }); + test('equals with NaN', () { + final a = double.nan; + final b = 0; + shouldPass(a, equals(a)); + shouldFail(a, equals(b), 'Expected: <0> Actual: '); + shouldFail(b, equals(a), 'Expected: Actual: <0>'); + }); + + test('equals with NaN in a collection', () { + final a = {double.nan}; + final b = {0}; + shouldPass(a, equals(a)); + shouldFail( + a, + equals(b), + 'Expected: Set:[0] Actual: Set:[NaN] ' + 'Which: does not contain <0>', + ); + shouldFail( + b, + equals(a), + 'Expected: Set:[NaN] Actual: Set:[0] ' + 'Which: does not contain ', + ); + }); + test('anything', () { var a = {}; shouldPass(0, anything);