gh-89392: Make test_decimal discoverable (GH-106209)

This commit is contained in:
Serhiy Storchaka 2023-06-29 12:53:22 +03:00 committed by GitHub
parent 08c08d21b0
commit 0e24499129
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -20,7 +20,7 @@
This test module can be called from command line with one parameter (Arithmetic
or Behaviour) to test each part, or without parameter to test both parts. If
you're working through IDLE, you can import this test module and call test_main()
you're working through IDLE, you can import this test module and call test()
with the corresponding argument.
"""
@ -32,7 +32,7 @@
import unittest
import numbers
import locale
from test.support import (run_unittest, run_doctest, is_resource_enabled,
from test.support import (is_resource_enabled,
requires_IEEE_754, requires_docstrings,
requires_legacy_unicode_capi, check_sanitizer)
from test.support import (TestFailed,
@ -62,6 +62,7 @@
fractions = {C:cfractions, P:pfractions}
sys.modules['decimal'] = orig_sys_decimal
requires_cdecimal = unittest.skipUnless(C, "test requires C version")
# Useful Test Constant
Signals = {
@ -99,7 +100,7 @@ def assert_signals(cls, context, attr, expected):
]
# Tests are built around these assumed context defaults.
# test_main() restores the original context.
# test() restores the original context.
ORIGINAL_CONTEXT = {
C: C.getcontext().copy() if C else None,
P: P.getcontext().copy()
@ -133,7 +134,7 @@ def init(m):
EXTRA_FUNCTIONALITY, "test requires regular build")
class IBMTestCases(unittest.TestCase):
class IBMTestCases:
"""Class which tests the Decimal class against the IBM test cases."""
def setUp(self):
@ -488,14 +489,10 @@ def change_max_exponent(self, exp):
def change_clamp(self, clamp):
self.context.clamp = clamp
class CIBMTestCases(IBMTestCases):
decimal = C
class PyIBMTestCases(IBMTestCases):
decimal = P
# The following classes test the behaviour of Decimal according to PEP 327
class ExplicitConstructionTest(unittest.TestCase):
class ExplicitConstructionTest:
'''Unit tests for Explicit Construction cases of Decimal.'''
def test_explicit_empty(self):
@ -838,12 +835,13 @@ def test_unicode_digits(self):
for input, expected in test_values.items():
self.assertEqual(str(Decimal(input)), expected)
class CExplicitConstructionTest(ExplicitConstructionTest):
@requires_cdecimal
class CExplicitConstructionTest(ExplicitConstructionTest, unittest.TestCase):
decimal = C
class PyExplicitConstructionTest(ExplicitConstructionTest):
class PyExplicitConstructionTest(ExplicitConstructionTest, unittest.TestCase):
decimal = P
class ImplicitConstructionTest(unittest.TestCase):
class ImplicitConstructionTest:
'''Unit tests for Implicit Construction cases of Decimal.'''
def test_implicit_from_None(self):
@ -920,12 +918,13 @@ def __ne__(self, other):
self.assertEqual(eval('Decimal(10)' + sym + 'E()'),
'10' + rop + 'str')
class CImplicitConstructionTest(ImplicitConstructionTest):
@requires_cdecimal
class CImplicitConstructionTest(ImplicitConstructionTest, unittest.TestCase):
decimal = C
class PyImplicitConstructionTest(ImplicitConstructionTest):
class PyImplicitConstructionTest(ImplicitConstructionTest, unittest.TestCase):
decimal = P
class FormatTest(unittest.TestCase):
class FormatTest:
'''Unit tests for the format function.'''
def test_formatting(self):
Decimal = self.decimal.Decimal
@ -1262,12 +1261,13 @@ def __init__(self, a):
a = A.from_float(42)
self.assertEqual(self.decimal.Decimal, a.a_type)
class CFormatTest(FormatTest):
@requires_cdecimal
class CFormatTest(FormatTest, unittest.TestCase):
decimal = C
class PyFormatTest(FormatTest):
class PyFormatTest(FormatTest, unittest.TestCase):
decimal = P
class ArithmeticOperatorsTest(unittest.TestCase):
class ArithmeticOperatorsTest:
'''Unit tests for all arithmetic operators, binary and unary.'''
def test_addition(self):
@ -1523,14 +1523,17 @@ def test_nan_comparisons(self):
equality_ops = operator.eq, operator.ne
# results when InvalidOperation is not trapped
for x, y in qnan_pairs + snan_pairs:
for op in order_ops + equality_ops:
got = op(x, y)
expected = True if op is operator.ne else False
self.assertIs(expected, got,
"expected {0!r} for operator.{1}({2!r}, {3!r}); "
"got {4!r}".format(
expected, op.__name__, x, y, got))
with localcontext() as ctx:
ctx.traps[InvalidOperation] = 0
for x, y in qnan_pairs + snan_pairs:
for op in order_ops + equality_ops:
got = op(x, y)
expected = True if op is operator.ne else False
self.assertIs(expected, got,
"expected {0!r} for operator.{1}({2!r}, {3!r}); "
"got {4!r}".format(
expected, op.__name__, x, y, got))
# repeat the above, but this time trap the InvalidOperation
with localcontext() as ctx:
@ -1562,9 +1565,10 @@ def test_copy_sign(self):
self.assertEqual(Decimal(1).copy_sign(-2), d)
self.assertRaises(TypeError, Decimal(1).copy_sign, '-2')
class CArithmeticOperatorsTest(ArithmeticOperatorsTest):
@requires_cdecimal
class CArithmeticOperatorsTest(ArithmeticOperatorsTest, unittest.TestCase):
decimal = C
class PyArithmeticOperatorsTest(ArithmeticOperatorsTest):
class PyArithmeticOperatorsTest(ArithmeticOperatorsTest, unittest.TestCase):
decimal = P
# The following are two functions used to test threading in the next class
@ -1654,7 +1658,7 @@ def thfunc2(cls):
@threading_helper.requires_working_threading()
class ThreadingTest(unittest.TestCase):
class ThreadingTest:
'''Unit tests for thread local contexts in Decimal.'''
# Take care executing this test from IDLE, there's an issue in threading
@ -1699,13 +1703,14 @@ def test_threading(self):
DefaultContext.Emin = save_emin
class CThreadingTest(ThreadingTest):
@requires_cdecimal
class CThreadingTest(ThreadingTest, unittest.TestCase):
decimal = C
class PyThreadingTest(ThreadingTest):
class PyThreadingTest(ThreadingTest, unittest.TestCase):
decimal = P
class UsabilityTest(unittest.TestCase):
class UsabilityTest:
'''Unit tests for Usability cases of Decimal.'''
def test_comparison_operators(self):
@ -2521,9 +2526,10 @@ def test_conversions_from_int(self):
self.assertEqual(Decimal(-12).fma(45, Decimal(67)),
Decimal(-12).fma(Decimal(45), Decimal(67)))
class CUsabilityTest(UsabilityTest):
@requires_cdecimal
class CUsabilityTest(UsabilityTest, unittest.TestCase):
decimal = C
class PyUsabilityTest(UsabilityTest):
class PyUsabilityTest(UsabilityTest, unittest.TestCase):
decimal = P
def setUp(self):
@ -2535,7 +2541,7 @@ def tearDown(self):
sys.set_int_max_str_digits(self._previous_int_limit)
super().tearDown()
class PythonAPItests(unittest.TestCase):
class PythonAPItests:
def test_abc(self):
Decimal = self.decimal.Decimal
@ -2884,12 +2890,13 @@ def test_exception_hierarchy(self):
self.assertTrue(issubclass(decimal.DivisionUndefined, ZeroDivisionError))
self.assertTrue(issubclass(decimal.InvalidContext, InvalidOperation))
class CPythonAPItests(PythonAPItests):
@requires_cdecimal
class CPythonAPItests(PythonAPItests, unittest.TestCase):
decimal = C
class PyPythonAPItests(PythonAPItests):
class PyPythonAPItests(PythonAPItests, unittest.TestCase):
decimal = P
class ContextAPItests(unittest.TestCase):
class ContextAPItests:
def test_none_args(self):
Context = self.decimal.Context
@ -3635,12 +3642,13 @@ def test_to_integral_value(self):
self.assertRaises(TypeError, c.to_integral_value, '10')
self.assertRaises(TypeError, c.to_integral_value, 10, 'x')
class CContextAPItests(ContextAPItests):
@requires_cdecimal
class CContextAPItests(ContextAPItests, unittest.TestCase):
decimal = C
class PyContextAPItests(ContextAPItests):
class PyContextAPItests(ContextAPItests, unittest.TestCase):
decimal = P
class ContextWithStatement(unittest.TestCase):
class ContextWithStatement:
# Can't do these as docstrings until Python 2.6
# as doctest can't handle __future__ statements
@ -3704,9 +3712,13 @@ def test_localcontext_kwargs(self):
def test_local_context_kwargs_does_not_overwrite_existing_argument(self):
ctx = self.decimal.getcontext()
ctx.prec = 28
orig_prec = ctx.prec
with self.decimal.localcontext(prec=10) as ctx2:
self.assertEqual(ctx.prec, 28)
self.assertEqual(ctx2.prec, 10)
self.assertEqual(ctx.prec, orig_prec)
with self.decimal.localcontext(prec=20) as ctx2:
self.assertEqual(ctx2.prec, 20)
self.assertEqual(ctx.prec, orig_prec)
def test_nested_with_statements(self):
# Use a copy of the supplied context in the block
@ -3800,12 +3812,13 @@ def test_with_statements_gc3(self):
self.assertEqual(c4.prec, 4)
del c4
class CContextWithStatement(ContextWithStatement):
@requires_cdecimal
class CContextWithStatement(ContextWithStatement, unittest.TestCase):
decimal = C
class PyContextWithStatement(ContextWithStatement):
class PyContextWithStatement(ContextWithStatement, unittest.TestCase):
decimal = P
class ContextFlags(unittest.TestCase):
class ContextFlags:
def test_flags_irrelevant(self):
# check that the result (numeric result + flags raised) of an
@ -4072,12 +4085,13 @@ def test_float_operation_default(self):
self.assertTrue(context.traps[FloatOperation])
self.assertTrue(context.traps[Inexact])
class CContextFlags(ContextFlags):
@requires_cdecimal
class CContextFlags(ContextFlags, unittest.TestCase):
decimal = C
class PyContextFlags(ContextFlags):
class PyContextFlags(ContextFlags, unittest.TestCase):
decimal = P
class SpecialContexts(unittest.TestCase):
class SpecialContexts:
"""Test the context templates."""
def test_context_templates(self):
@ -4157,12 +4171,13 @@ def test_default_context(self):
if ex:
raise ex
class CSpecialContexts(SpecialContexts):
@requires_cdecimal
class CSpecialContexts(SpecialContexts, unittest.TestCase):
decimal = C
class PySpecialContexts(SpecialContexts):
class PySpecialContexts(SpecialContexts, unittest.TestCase):
decimal = P
class ContextInputValidation(unittest.TestCase):
class ContextInputValidation:
def test_invalid_context(self):
Context = self.decimal.Context
@ -4224,12 +4239,13 @@ def test_invalid_context(self):
self.assertRaises(TypeError, Context, flags=(0,1))
self.assertRaises(TypeError, Context, traps=(1,0))
class CContextInputValidation(ContextInputValidation):
@requires_cdecimal
class CContextInputValidation(ContextInputValidation, unittest.TestCase):
decimal = C
class PyContextInputValidation(ContextInputValidation):
class PyContextInputValidation(ContextInputValidation, unittest.TestCase):
decimal = P
class ContextSubclassing(unittest.TestCase):
class ContextSubclassing:
def test_context_subclassing(self):
decimal = self.decimal
@ -4338,12 +4354,14 @@ def __init__(self, prec=None, rounding=None, Emin=None, Emax=None,
for signal in OrderedSignals[decimal]:
self.assertFalse(c.traps[signal])
class CContextSubclassing(ContextSubclassing):
@requires_cdecimal
class CContextSubclassing(ContextSubclassing, unittest.TestCase):
decimal = C
class PyContextSubclassing(ContextSubclassing):
class PyContextSubclassing(ContextSubclassing, unittest.TestCase):
decimal = P
@skip_if_extra_functionality
@requires_cdecimal
class CheckAttributes(unittest.TestCase):
def test_module_attributes(self):
@ -4373,7 +4391,7 @@ def test_decimal_attributes(self):
y = [s for s in dir(C.Decimal(9)) if '__' in s or not s.startswith('_')]
self.assertEqual(set(x) - set(y), set())
class Coverage(unittest.TestCase):
class Coverage:
def test_adjusted(self):
Decimal = self.decimal.Decimal
@ -4630,9 +4648,10 @@ def test_copy(self):
y = c.copy_sign(x, 1)
self.assertEqual(y, -x)
class CCoverage(Coverage):
@requires_cdecimal
class CCoverage(Coverage, unittest.TestCase):
decimal = C
class PyCoverage(Coverage):
class PyCoverage(Coverage, unittest.TestCase):
decimal = P
def setUp(self):
@ -4885,6 +4904,7 @@ def test_constants(self):
self.assertEqual(C.DecTraps,
C.DecErrors|C.DecOverflow|C.DecUnderflow)
@requires_cdecimal
class CWhitebox(unittest.TestCase):
"""Whitebox testing for _decimal"""
@ -5663,7 +5683,7 @@ def test_maxcontext_exact_arith(self):
@requires_docstrings
@unittest.skipUnless(C, "test requires C version")
@requires_cdecimal
class SignatureTest(unittest.TestCase):
"""Function signatures"""
@ -5799,52 +5819,10 @@ def doit(ty):
doit('Context')
all_tests = [
CExplicitConstructionTest, PyExplicitConstructionTest,
CImplicitConstructionTest, PyImplicitConstructionTest,
CFormatTest, PyFormatTest,
CArithmeticOperatorsTest, PyArithmeticOperatorsTest,
CThreadingTest, PyThreadingTest,
CUsabilityTest, PyUsabilityTest,
CPythonAPItests, PyPythonAPItests,
CContextAPItests, PyContextAPItests,
CContextWithStatement, PyContextWithStatement,
CContextFlags, PyContextFlags,
CSpecialContexts, PySpecialContexts,
CContextInputValidation, PyContextInputValidation,
CContextSubclassing, PyContextSubclassing,
CCoverage, PyCoverage,
CFunctionality, PyFunctionality,
CWhitebox, PyWhitebox,
CIBMTestCases, PyIBMTestCases,
]
# Delete C tests if _decimal.so is not present.
if not C:
all_tests = all_tests[1::2]
else:
all_tests.insert(0, CheckAttributes)
all_tests.insert(1, SignatureTest)
def test_main(arith=None, verbose=None, todo_tests=None, debug=None):
""" Execute the tests.
Runs all arithmetic tests if arith is True or if the "decimal" resource
is enabled in regrtest.py
"""
init(C)
init(P)
global TEST_ALL, DEBUG
TEST_ALL = arith if arith is not None else is_resource_enabled('decimal')
DEBUG = debug
if todo_tests is None:
test_classes = all_tests
else:
test_classes = [CIBMTestCases, PyIBMTestCases]
def load_tests(loader, tests, pattern):
if TODO_TESTS is not None:
# Run only Arithmetic tests
tests = loader.suiteClass()
# Dynamically build custom test definition for each file in the test
# directory and add the definitions to the DecimalTest class. This
# procedure insures that new files do not get skipped.
@ -5852,34 +5830,69 @@ def test_main(arith=None, verbose=None, todo_tests=None, debug=None):
if '.decTest' not in filename or filename.startswith("."):
continue
head, tail = filename.split('.')
if todo_tests is not None and head not in todo_tests:
if TODO_TESTS is not None and head not in TODO_TESTS:
continue
tester = lambda self, f=filename: self.eval_file(directory + f)
setattr(CIBMTestCases, 'test_' + head, tester)
setattr(PyIBMTestCases, 'test_' + head, tester)
setattr(IBMTestCases, 'test_' + head, tester)
del filename, head, tail, tester
for prefix, mod in ('C', C), ('Py', P):
if not mod:
continue
test_class = type(prefix + 'IBMTestCases',
(IBMTestCases, unittest.TestCase),
{'decimal': mod})
tests.addTest(loader.loadTestsFromTestCase(test_class))
if TODO_TESTS is None:
from doctest import DocTestSuite, IGNORE_EXCEPTION_DETAIL
for mod in C, P:
if not mod:
continue
def setUp(slf, mod=mod):
sys.modules['decimal'] = mod
def tearDown(slf):
sys.modules['decimal'] = orig_sys_decimal
optionflags = IGNORE_EXCEPTION_DETAIL if mod is C else 0
sys.modules['decimal'] = mod
tests.addTest(DocTestSuite(mod, setUp=setUp, tearDown=tearDown,
optionflags=optionflags))
sys.modules['decimal'] = orig_sys_decimal
return tests
def setUpModule():
init(C)
init(P)
global TEST_ALL
TEST_ALL = ARITH if ARITH is not None else is_resource_enabled('decimal')
def tearDownModule():
if C: C.setcontext(ORIGINAL_CONTEXT[C])
P.setcontext(ORIGINAL_CONTEXT[P])
if not C:
warnings.warn('C tests skipped: no module named _decimal.',
UserWarning)
if not orig_sys_decimal is sys.modules['decimal']:
raise TestFailed("Internal error: unbalanced number of changes to "
"sys.modules['decimal'].")
try:
run_unittest(*test_classes)
if todo_tests is None:
from doctest import IGNORE_EXCEPTION_DETAIL
savedecimal = sys.modules['decimal']
if C:
sys.modules['decimal'] = C
run_doctest(C, verbose, optionflags=IGNORE_EXCEPTION_DETAIL)
sys.modules['decimal'] = P
run_doctest(P, verbose)
sys.modules['decimal'] = savedecimal
finally:
if C: C.setcontext(ORIGINAL_CONTEXT[C])
P.setcontext(ORIGINAL_CONTEXT[P])
if not C:
warnings.warn('C tests skipped: no module named _decimal.',
UserWarning)
if not orig_sys_decimal is sys.modules['decimal']:
raise TestFailed("Internal error: unbalanced number of changes to "
"sys.modules['decimal'].")
ARITH = None
TEST_ALL = True
TODO_TESTS = None
DEBUG = False
def test(arith=None, verbose=None, todo_tests=None, debug=None):
""" Execute the tests.
Runs all arithmetic tests if arith is True or if the "decimal" resource
is enabled in regrtest.py
"""
global ARITH, TODO_TESTS, DEBUG
ARITH = arith
TODO_TESTS = todo_tests
DEBUG = debug
unittest.main(__name__, verbosity=2 if verbose else 1, exit=False, argv=[__name__])
if __name__ == '__main__':
@ -5890,8 +5903,8 @@ def test_main(arith=None, verbose=None, todo_tests=None, debug=None):
(opt, args) = p.parse_args()
if opt.skip:
test_main(arith=False, verbose=True)
test(arith=False, verbose=True)
elif args:
test_main(arith=True, verbose=True, todo_tests=args, debug=opt.debug)
test(arith=True, verbose=True, todo_tests=args, debug=opt.debug)
else:
test_main(arith=True, verbose=True)
test(arith=True, verbose=True)