diff --git a/changelog/3695.bugfix.rst b/changelog/3695.bugfix.rst new file mode 100644 index 00000000000..42d07336b51 --- /dev/null +++ b/changelog/3695.bugfix.rst @@ -0,0 +1,2 @@ +Fix ``ApproxNumpy`` initialisation argument mixup, ``abs`` and ``rel`` tolerances were flipped causing strange comparsion results. +Add tests to check ``abs`` and ``rel`` tolerances for ``np.array`` and test for expecting ``nan`` with ``np.array()`` diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index d88d1c88bb9..5331d8a84a3 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -211,7 +211,7 @@ def __eq__(self, actual): the pre-specified tolerance. """ if _is_numpy_array(actual): - return ApproxNumpy(actual, self.abs, self.rel, self.nan_ok) == self.expected + return all(a == self for a in actual.flat) # Short-circuit exact equality. if actual == self.expected: diff --git a/testing/python/approx.py b/testing/python/approx.py index 39f10a82116..0509fa67238 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -342,6 +342,68 @@ def test_numpy_array(self): assert actual == approx(list(expected), rel=5e-7, abs=0) assert actual != approx(list(expected), rel=5e-8, abs=0) + def test_numpy_tolerance_args(self): + """ + Check that numpy rel/abs args are handled correctly + for comparison against an np.array + Check both sides of the operator, hopefully it doesn't impact things. + Test all permutations of where the approx and np.array() can show up + """ + np = pytest.importorskip("numpy") + expected = 100. + actual = 99. + abs_diff = expected - actual + rel_diff = (expected - actual) / expected + + tests = [ + (eq, abs_diff, 0), + (eq, 0, rel_diff), + (ne, 0, rel_diff / 2.), # rel diff fail + (ne, abs_diff / 2., 0), # abs diff fail + ] + + for op, _abs, _rel in tests: + assert op(np.array(actual), approx(expected, abs=_abs, rel=_rel)) # a, b + assert op(approx(expected, abs=_abs, rel=_rel), np.array(actual)) # b, a + + assert op(actual, approx(np.array(expected), abs=_abs, rel=_rel)) # a, b + assert op(approx(np.array(expected), abs=_abs, rel=_rel), actual) # b, a + + assert op(np.array(actual), approx(np.array(expected), abs=_abs, rel=_rel)) + assert op(approx(np.array(expected), abs=_abs, rel=_rel), np.array(actual)) + + def test_numpy_expecting_nan(self): + np = pytest.importorskip("numpy") + examples = [ + (eq, nan, nan), + (eq, -nan, -nan), + (eq, nan, -nan), + (ne, 0.0, nan), + (ne, inf, nan), + ] + for op, a, x in examples: + # Nothing is equal to NaN by default. + assert np.array(a) != approx(x) + assert a != approx(np.array(x)) + + # If ``nan_ok=True``, then NaN is equal to NaN. + assert op(np.array(a), approx(x, nan_ok=True)) + assert op(a, approx(np.array(x), nan_ok=True)) + + def test_numpy_expecting_inf(self): + np = pytest.importorskip("numpy") + examples = [ + (eq, inf, inf), + (eq, -inf, -inf), + (ne, inf, -inf), + (ne, 0.0, inf), + (ne, nan, inf), + ] + for op, a, x in examples: + assert op(np.array(a), approx(x)) + assert op(a, approx(np.array(x))) + assert op(np.array(a), approx(np.array(x))) + def test_numpy_array_wrong_shape(self): np = pytest.importorskip("numpy")