]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-121039: add Floats/ComplexesAreIdenticalMixin to test.support.testcase...
authorSergey B Kirpichev <skirpichev@gmail.com>
Wed, 11 Sep 2024 12:06:40 +0000 (15:06 +0300)
committerGitHub <noreply@github.com>
Wed, 11 Sep 2024 12:06:40 +0000 (14:06 +0200)
* [3.12] gh-121039: add Floats/ComplexesAreIdenticalMixin to test.support.testcase (GH-121071)
(cherry picked from commit 8ef8354ef15e00d484ac2ded9442b789c24b11e0)

Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
Lib/test/support/testcase.py
Lib/test/test_capi/test_getargs.py
Lib/test/test_cmath.py
Lib/test/test_complex.py
Lib/test/test_float.py

index 1e4363b15783eb0a92e1662bfdaaacac0c4b3c28..fad1e4cb3499c07ba7ca7fc8bdc36be5cfcaaa58 100644 (file)
@@ -1,3 +1,6 @@
+from math import copysign, isnan
+
+
 class ExceptionIsLikeMixin:
     def assertExceptionIsLike(self, exc, template):
         """
@@ -23,3 +26,40 @@ class ExceptionIsLikeMixin:
             self.assertEqual(len(exc.exceptions), len(template.exceptions))
             for e, t in zip(exc.exceptions, template.exceptions):
                 self.assertExceptionIsLike(e, t)
+
+
+class FloatsAreIdenticalMixin:
+    def assertFloatsAreIdentical(self, x, y):
+        """Fail unless floats x and y are identical, in the sense that:
+        (1) both x and y are nans, or
+        (2) both x and y are infinities, with the same sign, or
+        (3) both x and y are zeros, with the same sign, or
+        (4) x and y are both finite and nonzero, and x == y
+
+        """
+        msg = 'floats {!r} and {!r} are not identical'
+
+        if isnan(x) or isnan(y):
+            if isnan(x) and isnan(y):
+                return
+        elif x == y:
+            if x != 0.0:
+                return
+            # both zero; check that signs match
+            elif copysign(1.0, x) == copysign(1.0, y):
+                return
+            else:
+                msg += ': zeros have different signs'
+        self.fail(msg.format(x, y))
+
+
+class ComplexesAreIdenticalMixin(FloatsAreIdenticalMixin):
+    def assertComplexesAreIdentical(self, x, y):
+        """Fail unless complex numbers x and y have equal values and signs.
+
+        In particular, if x and y both have real (or imaginary) part
+        zero, but the zeros have different signs, this test will fail.
+
+        """
+        self.assertFloatsAreIdentical(x.real, y.real)
+        self.assertFloatsAreIdentical(x.imag, y.imag)
index 69bd0f4c5f197c8f10284fa3f9ca8b4c49f7efae..132ba350a92c697c8d6d76caba0bc9320f6ff9d2 100644 (file)
@@ -6,6 +6,7 @@ from test import support
 from test.support import import_helper
 from test.support import script_helper
 from test.support import warnings_helper
+from test.support.testcase import FloatsAreIdenticalMixin
 # Skip this test if the _testcapi module isn't available.
 _testcapi = import_helper.import_module('_testcapi')
 from _testcapi import getargs_keywords, getargs_keyword_only
@@ -436,11 +437,7 @@ class LongLong_TestCase(unittest.TestCase):
         self.assertEqual(VERY_LARGE & ULLONG_MAX, getargs_K(VERY_LARGE))
 
 
-class Float_TestCase(unittest.TestCase):
-    def assertEqualWithSign(self, actual, expected):
-        self.assertEqual(actual, expected)
-        self.assertEqual(math.copysign(1, actual), math.copysign(1, expected))
-
+class Float_TestCase(unittest.TestCase, FloatsAreIdenticalMixin):
     def test_f(self):
         from _testcapi import getargs_f
         self.assertEqual(getargs_f(4.25), 4.25)
@@ -462,10 +459,10 @@ class Float_TestCase(unittest.TestCase):
             self.assertEqual(getargs_f(DBL_MAX), INF)
             self.assertEqual(getargs_f(-DBL_MAX), -INF)
         if FLT_MIN > DBL_MIN:
-            self.assertEqualWithSign(getargs_f(DBL_MIN), 0.0)
-            self.assertEqualWithSign(getargs_f(-DBL_MIN), -0.0)
-        self.assertEqualWithSign(getargs_f(0.0), 0.0)
-        self.assertEqualWithSign(getargs_f(-0.0), -0.0)
+            self.assertFloatsAreIdentical(getargs_f(DBL_MIN), 0.0)
+            self.assertFloatsAreIdentical(getargs_f(-DBL_MIN), -0.0)
+        self.assertFloatsAreIdentical(getargs_f(0.0), 0.0)
+        self.assertFloatsAreIdentical(getargs_f(-0.0), -0.0)
         r = getargs_f(NAN)
         self.assertNotEqual(r, r)
 
@@ -494,8 +491,8 @@ class Float_TestCase(unittest.TestCase):
             self.assertEqual(getargs_d(x), x)
         self.assertRaises(OverflowError, getargs_d, 1<<DBL_MAX_EXP)
         self.assertRaises(OverflowError, getargs_d, -1<<DBL_MAX_EXP)
-        self.assertEqualWithSign(getargs_d(0.0), 0.0)
-        self.assertEqualWithSign(getargs_d(-0.0), -0.0)
+        self.assertFloatsAreIdentical(getargs_d(0.0), 0.0)
+        self.assertFloatsAreIdentical(getargs_d(-0.0), -0.0)
         r = getargs_d(NAN)
         self.assertNotEqual(r, r)
 
@@ -519,10 +516,10 @@ class Float_TestCase(unittest.TestCase):
             self.assertEqual(getargs_D(c), c)
             c = complex(1.0, x)
             self.assertEqual(getargs_D(c), c)
-        self.assertEqualWithSign(getargs_D(complex(0.0, 1.0)).real, 0.0)
-        self.assertEqualWithSign(getargs_D(complex(-0.0, 1.0)).real, -0.0)
-        self.assertEqualWithSign(getargs_D(complex(1.0, 0.0)).imag, 0.0)
-        self.assertEqualWithSign(getargs_D(complex(1.0, -0.0)).imag, -0.0)
+        self.assertFloatsAreIdentical(getargs_D(complex(0.0, 1.0)).real, 0.0)
+        self.assertFloatsAreIdentical(getargs_D(complex(-0.0, 1.0)).real, -0.0)
+        self.assertFloatsAreIdentical(getargs_D(complex(1.0, 0.0)).imag, 0.0)
+        self.assertFloatsAreIdentical(getargs_D(complex(1.0, -0.0)).imag, -0.0)
 
 
 class Paradox:
index 57f80d5d8cd0161e7994f955cd8c8a260f48eadb..a96a5780b31b6f134d74ce5e196d177953d67821 100644 (file)
@@ -1,4 +1,5 @@
 from test.support import requires_IEEE_754, cpython_only, import_helper
+from test.support.testcase import ComplexesAreIdenticalMixin
 from test.test_math import parse_testfile, test_file
 import test.test_math as test_math
 import unittest
@@ -49,7 +50,7 @@ complex_nans = [complex(x, y) for x, y in [
         (INF, NAN)
         ]]
 
-class CMathTests(unittest.TestCase):
+class CMathTests(ComplexesAreIdenticalMixin, unittest.TestCase):
     # list of all functions in cmath
     test_functions = [getattr(cmath, fname) for fname in [
             'acos', 'acosh', 'asin', 'asinh', 'atan', 'atanh',
@@ -65,39 +66,6 @@ class CMathTests(unittest.TestCase):
     def tearDown(self):
         self.test_values.close()
 
-    def assertFloatIdentical(self, x, y):
-        """Fail unless floats x and y are identical, in the sense that:
-        (1) both x and y are nans, or
-        (2) both x and y are infinities, with the same sign, or
-        (3) both x and y are zeros, with the same sign, or
-        (4) x and y are both finite and nonzero, and x == y
-
-        """
-        msg = 'floats {!r} and {!r} are not identical'
-
-        if math.isnan(x) or math.isnan(y):
-            if math.isnan(x) and math.isnan(y):
-                return
-        elif x == y:
-            if x != 0.0:
-                return
-            # both zero; check that signs match
-            elif math.copysign(1.0, x) == math.copysign(1.0, y):
-                return
-            else:
-                msg += ': zeros have different signs'
-        self.fail(msg.format(x, y))
-
-    def assertComplexIdentical(self, x, y):
-        """Fail unless complex numbers x and y have equal values and signs.
-
-        In particular, if x and y both have real (or imaginary) part
-        zero, but the zeros have different signs, this test will fail.
-
-        """
-        self.assertFloatIdentical(x.real, y.real)
-        self.assertFloatIdentical(x.imag, y.imag)
-
     def rAssertAlmostEqual(self, a, b, rel_err = 2e-15, abs_err = 5e-323,
                            msg=None):
         """Fail if the two floating-point numbers are not almost equal.
@@ -555,7 +523,7 @@ class CMathTests(unittest.TestCase):
     @requires_IEEE_754
     def testTanhSign(self):
         for z in complex_zeros:
-            self.assertComplexIdentical(cmath.tanh(z), z)
+            self.assertComplexesAreIdentical(cmath.tanh(z), z)
 
     # The algorithm used for atan and atanh makes use of the system
     # log1p function; If that system function doesn't respect the sign
@@ -564,12 +532,12 @@ class CMathTests(unittest.TestCase):
     @requires_IEEE_754
     def testAtanSign(self):
         for z in complex_zeros:
-            self.assertComplexIdentical(cmath.atan(z), z)
+            self.assertComplexesAreIdentical(cmath.atan(z), z)
 
     @requires_IEEE_754
     def testAtanhSign(self):
         for z in complex_zeros:
-            self.assertComplexIdentical(cmath.atanh(z), z)
+            self.assertComplexesAreIdentical(cmath.atanh(z), z)
 
 
 class IsCloseTests(test_math.IsCloseTests):
index 7625f2d6fc8750b5e4a335f4e4dd33a06f0d1538..4272c2afcdc274c93a7c11d44c45ea2cfa81ebdd 100644 (file)
@@ -1,6 +1,7 @@
 import unittest
 import sys
 from test import support
+from test.support.testcase import ComplexesAreIdenticalMixin
 from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
                                INVALID_UNDERSCORE_LITERALS)
 
@@ -41,7 +42,7 @@ class WithComplex:
     def __complex__(self):
         return self.value
 
-class ComplexTest(unittest.TestCase):
+class ComplexTest(ComplexesAreIdenticalMixin, unittest.TestCase):
 
     def assertAlmostEqual(self, a, b):
         if isinstance(a, complex):
@@ -70,29 +71,6 @@ class ComplexTest(unittest.TestCase):
         # check that relative difference < eps
         self.assertTrue(abs((x-y)/y) < eps)
 
-    def assertFloatsAreIdentical(self, x, y):
-        """assert that floats x and y are identical, in the sense that:
-        (1) both x and y are nans, or
-        (2) both x and y are infinities, with the same sign, or
-        (3) both x and y are zeros, with the same sign, or
-        (4) x and y are both finite and nonzero, and x == y
-
-        """
-        msg = 'floats {!r} and {!r} are not identical'
-
-        if isnan(x) or isnan(y):
-            if isnan(x) and isnan(y):
-                return
-        elif x == y:
-            if x != 0.0:
-                return
-            # both zero; check that signs match
-            elif copysign(1.0, x) == copysign(1.0, y):
-                return
-            else:
-                msg += ': zeros have different signs'
-        self.fail(msg.format(x, y))
-
     def assertClose(self, x, y, eps=1e-9):
         """Return true iff complexes x and y "are close"."""
         self.assertCloseAbs(x.real, y.real, eps)
@@ -728,8 +706,7 @@ class ComplexTest(unittest.TestCase):
             for y in vals:
                 z = complex(x, y)
                 roundtrip = complex(repr(z))
-                self.assertFloatsAreIdentical(z.real, roundtrip.real)
-                self.assertFloatsAreIdentical(z.imag, roundtrip.imag)
+                self.assertComplexesAreIdentical(z, roundtrip)
 
         # if we predefine some constants, then eval(repr(z)) should
         # also work, except that it might change the sign of zeros
index 32aaf3a80ab64b22b6728bacf5e86152256ef807..74c7c17993d9a6989f81356c4f4b1b881d144542 100644 (file)
@@ -8,6 +8,7 @@ import time
 import unittest
 
 from test import support
+from test.support.testcase import FloatsAreIdenticalMixin
 from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
                                INVALID_UNDERSCORE_LITERALS)
 from math import isinf, isnan, copysign, ldexp
@@ -1052,21 +1053,14 @@ class InfNanTest(unittest.TestCase):
 
 fromHex = float.fromhex
 toHex = float.hex
-class HexFloatTestCase(unittest.TestCase):
+class HexFloatTestCase(FloatsAreIdenticalMixin, unittest.TestCase):
     MAX = fromHex('0x.fffffffffffff8p+1024')  # max normal
     MIN = fromHex('0x1p-1022')                # min normal
     TINY = fromHex('0x0.0000000000001p-1022') # min subnormal
     EPS = fromHex('0x0.0000000000001p0') # diff between 1.0 and next float up
 
     def identical(self, x, y):
-        # check that floats x and y are identical, or that both
-        # are NaNs
-        if isnan(x) or isnan(y):
-            if isnan(x) == isnan(y):
-                return
-        elif x == y and (x != 0.0 or copysign(1.0, x) == copysign(1.0, y)):
-            return
-        self.fail('%r not identical to %r' % (x, y))
+        self.assertFloatsAreIdentical(x, y)
 
     def test_ends(self):
         self.identical(self.MIN, ldexp(1.0, -1022))