ยปCore Development>Code coverage>Modules/_decimal/tests/deccheck.py

Python code coverage for Modules/_decimal/tests/deccheck.py

#countcontent
1n/a#
2n/a# Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
3n/a#
4n/a# Redistribution and use in source and binary forms, with or without
5n/a# modification, are permitted provided that the following conditions
6n/a# are met:
7n/a#
8n/a# 1. Redistributions of source code must retain the above copyright
9n/a# notice, this list of conditions and the following disclaimer.
10n/a#
11n/a# 2. Redistributions in binary form must reproduce the above copyright
12n/a# notice, this list of conditions and the following disclaimer in the
13n/a# documentation and/or other materials provided with the distribution.
14n/a#
15n/a# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
16n/a# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17n/a# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18n/a# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19n/a# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20n/a# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21n/a# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22n/a# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23n/a# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24n/a# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25n/a# SUCH DAMAGE.
26n/a#
27n/a
28n/a#
29n/a# Usage: python deccheck.py [--short|--medium|--long|--all]
30n/a#
31n/a
32n/aimport sys, random
33n/afrom copy import copy
34n/afrom collections import defaultdict
35n/afrom test.support import import_fresh_module
36n/afrom randdec import randfloat, all_unary, all_binary, all_ternary
37n/afrom randdec import unary_optarg, binary_optarg, ternary_optarg
38n/afrom formathelper import rand_format, rand_locale
39n/afrom _pydecimal import _dec_from_triple
40n/a
41n/aC = import_fresh_module('decimal', fresh=['_decimal'])
42n/aP = import_fresh_module('decimal', blocked=['_decimal'])
43n/aEXIT_STATUS = 0
44n/a
45n/a
46n/a# Contains all categories of Decimal methods.
47n/aFunctions = {
48n/a # Plain unary:
49n/a 'unary': (
50n/a '__abs__', '__bool__', '__ceil__', '__complex__', '__copy__',
51n/a '__floor__', '__float__', '__hash__', '__int__', '__neg__',
52n/a '__pos__', '__reduce__', '__repr__', '__str__', '__trunc__',
53n/a 'adjusted', 'as_integer_ratio', 'as_tuple', 'canonical', 'conjugate',
54n/a 'copy_abs', 'copy_negate', 'is_canonical', 'is_finite', 'is_infinite',
55n/a 'is_nan', 'is_qnan', 'is_signed', 'is_snan', 'is_zero', 'radix'
56n/a ),
57n/a # Unary with optional context:
58n/a 'unary_ctx': (
59n/a 'exp', 'is_normal', 'is_subnormal', 'ln', 'log10', 'logb',
60n/a 'logical_invert', 'next_minus', 'next_plus', 'normalize',
61n/a 'number_class', 'sqrt', 'to_eng_string'
62n/a ),
63n/a # Unary with optional rounding mode and context:
64n/a 'unary_rnd_ctx': ('to_integral', 'to_integral_exact', 'to_integral_value'),
65n/a # Plain binary:
66n/a 'binary': (
67n/a '__add__', '__divmod__', '__eq__', '__floordiv__', '__ge__', '__gt__',
68n/a '__le__', '__lt__', '__mod__', '__mul__', '__ne__', '__pow__',
69n/a '__radd__', '__rdivmod__', '__rfloordiv__', '__rmod__', '__rmul__',
70n/a '__rpow__', '__rsub__', '__rtruediv__', '__sub__', '__truediv__',
71n/a 'compare_total', 'compare_total_mag', 'copy_sign', 'quantize',
72n/a 'same_quantum'
73n/a ),
74n/a # Binary with optional context:
75n/a 'binary_ctx': (
76n/a 'compare', 'compare_signal', 'logical_and', 'logical_or', 'logical_xor',
77n/a 'max', 'max_mag', 'min', 'min_mag', 'next_toward', 'remainder_near',
78n/a 'rotate', 'scaleb', 'shift'
79n/a ),
80n/a # Plain ternary:
81n/a 'ternary': ('__pow__',),
82n/a # Ternary with optional context:
83n/a 'ternary_ctx': ('fma',),
84n/a # Special:
85n/a 'special': ('__format__', '__reduce_ex__', '__round__', 'from_float',
86n/a 'quantize'),
87n/a # Properties:
88n/a 'property': ('real', 'imag')
89n/a}
90n/a
91n/a# Contains all categories of Context methods. The n-ary classification
92n/a# applies to the number of Decimal arguments.
93n/aContextFunctions = {
94n/a # Plain nullary:
95n/a 'nullary': ('context.__hash__', 'context.__reduce__', 'context.radix'),
96n/a # Plain unary:
97n/a 'unary': ('context.abs', 'context.canonical', 'context.copy_abs',
98n/a 'context.copy_decimal', 'context.copy_negate',
99n/a 'context.create_decimal', 'context.exp', 'context.is_canonical',
100n/a 'context.is_finite', 'context.is_infinite', 'context.is_nan',
101n/a 'context.is_normal', 'context.is_qnan', 'context.is_signed',
102n/a 'context.is_snan', 'context.is_subnormal', 'context.is_zero',
103n/a 'context.ln', 'context.log10', 'context.logb',
104n/a 'context.logical_invert', 'context.minus', 'context.next_minus',
105n/a 'context.next_plus', 'context.normalize', 'context.number_class',
106n/a 'context.plus', 'context.sqrt', 'context.to_eng_string',
107n/a 'context.to_integral', 'context.to_integral_exact',
108n/a 'context.to_integral_value', 'context.to_sci_string'
109n/a ),
110n/a # Plain binary:
111n/a 'binary': ('context.add', 'context.compare', 'context.compare_signal',
112n/a 'context.compare_total', 'context.compare_total_mag',
113n/a 'context.copy_sign', 'context.divide', 'context.divide_int',
114n/a 'context.divmod', 'context.logical_and', 'context.logical_or',
115n/a 'context.logical_xor', 'context.max', 'context.max_mag',
116n/a 'context.min', 'context.min_mag', 'context.multiply',
117n/a 'context.next_toward', 'context.power', 'context.quantize',
118n/a 'context.remainder', 'context.remainder_near', 'context.rotate',
119n/a 'context.same_quantum', 'context.scaleb', 'context.shift',
120n/a 'context.subtract'
121n/a ),
122n/a # Plain ternary:
123n/a 'ternary': ('context.fma', 'context.power'),
124n/a # Special:
125n/a 'special': ('context.__reduce_ex__', 'context.create_decimal_from_float')
126n/a}
127n/a
128n/a# Functions that require a restricted exponent range for reasonable runtimes.
129n/aUnaryRestricted = [
130n/a '__ceil__', '__floor__', '__int__', '__trunc__',
131n/a 'as_integer_ratio', 'to_integral', 'to_integral_value'
132n/a]
133n/a
134n/aBinaryRestricted = ['__round__']
135n/a
136n/aTernaryRestricted = ['__pow__', 'context.power']
137n/a
138n/a
139n/a# ======================================================================
140n/a# Unified Context
141n/a# ======================================================================
142n/a
143n/a# Translate symbols.
144n/aCondMap = {
145n/a C.Clamped: P.Clamped,
146n/a C.ConversionSyntax: P.ConversionSyntax,
147n/a C.DivisionByZero: P.DivisionByZero,
148n/a C.DivisionImpossible: P.InvalidOperation,
149n/a C.DivisionUndefined: P.DivisionUndefined,
150n/a C.Inexact: P.Inexact,
151n/a C.InvalidContext: P.InvalidContext,
152n/a C.InvalidOperation: P.InvalidOperation,
153n/a C.Overflow: P.Overflow,
154n/a C.Rounded: P.Rounded,
155n/a C.Subnormal: P.Subnormal,
156n/a C.Underflow: P.Underflow,
157n/a C.FloatOperation: P.FloatOperation,
158n/a}
159n/a
160n/aRoundModes = [C.ROUND_UP, C.ROUND_DOWN, C.ROUND_CEILING, C.ROUND_FLOOR,
161n/a C.ROUND_HALF_UP, C.ROUND_HALF_DOWN, C.ROUND_HALF_EVEN,
162n/a C.ROUND_05UP]
163n/a
164n/a
165n/aclass Context(object):
166n/a """Provides a convenient way of syncing the C and P contexts"""
167n/a
168n/a __slots__ = ['c', 'p']
169n/a
170n/a def __init__(self, c_ctx=None, p_ctx=None):
171n/a """Initialization is from the C context"""
172n/a self.c = C.getcontext() if c_ctx is None else c_ctx
173n/a self.p = P.getcontext() if p_ctx is None else p_ctx
174n/a self.p.prec = self.c.prec
175n/a self.p.Emin = self.c.Emin
176n/a self.p.Emax = self.c.Emax
177n/a self.p.rounding = self.c.rounding
178n/a self.p.capitals = self.c.capitals
179n/a self.settraps([sig for sig in self.c.traps if self.c.traps[sig]])
180n/a self.setstatus([sig for sig in self.c.flags if self.c.flags[sig]])
181n/a self.p.clamp = self.c.clamp
182n/a
183n/a def __str__(self):
184n/a return str(self.c) + '\n' + str(self.p)
185n/a
186n/a def getprec(self):
187n/a assert(self.c.prec == self.p.prec)
188n/a return self.c.prec
189n/a
190n/a def setprec(self, val):
191n/a self.c.prec = val
192n/a self.p.prec = val
193n/a
194n/a def getemin(self):
195n/a assert(self.c.Emin == self.p.Emin)
196n/a return self.c.Emin
197n/a
198n/a def setemin(self, val):
199n/a self.c.Emin = val
200n/a self.p.Emin = val
201n/a
202n/a def getemax(self):
203n/a assert(self.c.Emax == self.p.Emax)
204n/a return self.c.Emax
205n/a
206n/a def setemax(self, val):
207n/a self.c.Emax = val
208n/a self.p.Emax = val
209n/a
210n/a def getround(self):
211n/a assert(self.c.rounding == self.p.rounding)
212n/a return self.c.rounding
213n/a
214n/a def setround(self, val):
215n/a self.c.rounding = val
216n/a self.p.rounding = val
217n/a
218n/a def getcapitals(self):
219n/a assert(self.c.capitals == self.p.capitals)
220n/a return self.c.capitals
221n/a
222n/a def setcapitals(self, val):
223n/a self.c.capitals = val
224n/a self.p.capitals = val
225n/a
226n/a def getclamp(self):
227n/a assert(self.c.clamp == self.p.clamp)
228n/a return self.c.clamp
229n/a
230n/a def setclamp(self, val):
231n/a self.c.clamp = val
232n/a self.p.clamp = val
233n/a
234n/a prec = property(getprec, setprec)
235n/a Emin = property(getemin, setemin)
236n/a Emax = property(getemax, setemax)
237n/a rounding = property(getround, setround)
238n/a clamp = property(getclamp, setclamp)
239n/a capitals = property(getcapitals, setcapitals)
240n/a
241n/a def clear_traps(self):
242n/a self.c.clear_traps()
243n/a for trap in self.p.traps:
244n/a self.p.traps[trap] = False
245n/a
246n/a def clear_status(self):
247n/a self.c.clear_flags()
248n/a self.p.clear_flags()
249n/a
250n/a def settraps(self, lst):
251n/a """lst: C signal list"""
252n/a self.clear_traps()
253n/a for signal in lst:
254n/a self.c.traps[signal] = True
255n/a self.p.traps[CondMap[signal]] = True
256n/a
257n/a def setstatus(self, lst):
258n/a """lst: C signal list"""
259n/a self.clear_status()
260n/a for signal in lst:
261n/a self.c.flags[signal] = True
262n/a self.p.flags[CondMap[signal]] = True
263n/a
264n/a def assert_eq_status(self):
265n/a """assert equality of C and P status"""
266n/a for signal in self.c.flags:
267n/a if self.c.flags[signal] == (not self.p.flags[CondMap[signal]]):
268n/a return False
269n/a return True
270n/a
271n/a
272n/a# We don't want exceptions so that we can compare the status flags.
273n/acontext = Context()
274n/acontext.Emin = C.MIN_EMIN
275n/acontext.Emax = C.MAX_EMAX
276n/acontext.clear_traps()
277n/a
278n/a# When creating decimals, _decimal is ultimately limited by the maximum
279n/a# context values. We emulate this restriction for decimal.py.
280n/amaxcontext = P.Context(
281n/a prec=C.MAX_PREC,
282n/a Emin=C.MIN_EMIN,
283n/a Emax=C.MAX_EMAX,
284n/a rounding=P.ROUND_HALF_UP,
285n/a capitals=1
286n/a)
287n/amaxcontext.clamp = 0
288n/a
289n/adef RestrictedDecimal(value):
290n/a maxcontext.traps = copy(context.p.traps)
291n/a maxcontext.clear_flags()
292n/a if isinstance(value, str):
293n/a value = value.strip()
294n/a dec = maxcontext.create_decimal(value)
295n/a if maxcontext.flags[P.Inexact] or \
296n/a maxcontext.flags[P.Rounded] or \
297n/a maxcontext.flags[P.Clamped] or \
298n/a maxcontext.flags[P.InvalidOperation]:
299n/a return context.p._raise_error(P.InvalidOperation)
300n/a if maxcontext.flags[P.FloatOperation]:
301n/a context.p.flags[P.FloatOperation] = True
302n/a return dec
303n/a
304n/a
305n/a# ======================================================================
306n/a# TestSet: Organize data and events during a single test case
307n/a# ======================================================================
308n/a
309n/aclass RestrictedList(list):
310n/a """List that can only be modified by appending items."""
311n/a def __getattribute__(self, name):
312n/a if name != 'append':
313n/a raise AttributeError("unsupported operation")
314n/a return list.__getattribute__(self, name)
315n/a def unsupported(self, *_):
316n/a raise AttributeError("unsupported operation")
317n/a __add__ = __delattr__ = __delitem__ = __iadd__ = __imul__ = unsupported
318n/a __mul__ = __reversed__ = __rmul__ = __setattr__ = __setitem__ = unsupported
319n/a
320n/aclass TestSet(object):
321n/a """A TestSet contains the original input operands, converted operands,
322n/a Python exceptions that occurred either during conversion or during
323n/a execution of the actual function, and the final results.
324n/a
325n/a For safety, most attributes are lists that only support the append
326n/a operation.
327n/a
328n/a If a function name is prefixed with 'context.', the corresponding
329n/a context method is called.
330n/a """
331n/a def __init__(self, funcname, operands):
332n/a if funcname.startswith("context."):
333n/a self.funcname = funcname.replace("context.", "")
334n/a self.contextfunc = True
335n/a else:
336n/a self.funcname = funcname
337n/a self.contextfunc = False
338n/a self.op = operands # raw operand tuple
339n/a self.context = context # context used for the operation
340n/a self.cop = RestrictedList() # converted C.Decimal operands
341n/a self.cex = RestrictedList() # Python exceptions for C.Decimal
342n/a self.cresults = RestrictedList() # C.Decimal results
343n/a self.pop = RestrictedList() # converted P.Decimal operands
344n/a self.pex = RestrictedList() # Python exceptions for P.Decimal
345n/a self.presults = RestrictedList() # P.Decimal results
346n/a
347n/a
348n/a# ======================================================================
349n/a# SkipHandler: skip known discrepancies
350n/a# ======================================================================
351n/a
352n/aclass SkipHandler:
353n/a """Handle known discrepancies between decimal.py and _decimal.so.
354n/a These are either ULP differences in the power function or
355n/a extremely minor issues."""
356n/a
357n/a def __init__(self):
358n/a self.ulpdiff = 0
359n/a self.powmod_zeros = 0
360n/a self.maxctx = P.Context(Emax=10**18, Emin=-10**18)
361n/a
362n/a def default(self, t):
363n/a return False
364n/a __ge__ = __gt__ = __le__ = __lt__ = __ne__ = __eq__ = default
365n/a __reduce__ = __format__ = __repr__ = __str__ = default
366n/a
367n/a def harrison_ulp(self, dec):
368n/a """ftp://ftp.inria.fr/INRIA/publication/publi-pdf/RR/RR-5504.pdf"""
369n/a a = dec.next_plus()
370n/a b = dec.next_minus()
371n/a return abs(a - b)
372n/a
373n/a def standard_ulp(self, dec, prec):
374n/a return _dec_from_triple(0, '1', dec._exp+len(dec._int)-prec)
375n/a
376n/a def rounding_direction(self, x, mode):
377n/a """Determine the effective direction of the rounding when
378n/a the exact result x is rounded according to mode.
379n/a Return -1 for downwards, 0 for undirected, 1 for upwards,
380n/a 2 for ROUND_05UP."""
381n/a cmp = 1 if x.compare_total(P.Decimal("+0")) >= 0 else -1
382n/a
383n/a if mode in (P.ROUND_HALF_EVEN, P.ROUND_HALF_UP, P.ROUND_HALF_DOWN):
384n/a return 0
385n/a elif mode == P.ROUND_CEILING:
386n/a return 1
387n/a elif mode == P.ROUND_FLOOR:
388n/a return -1
389n/a elif mode == P.ROUND_UP:
390n/a return cmp
391n/a elif mode == P.ROUND_DOWN:
392n/a return -cmp
393n/a elif mode == P.ROUND_05UP:
394n/a return 2
395n/a else:
396n/a raise ValueError("Unexpected rounding mode: %s" % mode)
397n/a
398n/a def check_ulpdiff(self, exact, rounded):
399n/a # current precision
400n/a p = context.p.prec
401n/a
402n/a # Convert infinities to the largest representable number + 1.
403n/a x = exact
404n/a if exact.is_infinite():
405n/a x = _dec_from_triple(exact._sign, '10', context.p.Emax)
406n/a y = rounded
407n/a if rounded.is_infinite():
408n/a y = _dec_from_triple(rounded._sign, '10', context.p.Emax)
409n/a
410n/a # err = (rounded - exact) / ulp(rounded)
411n/a self.maxctx.prec = p * 2
412n/a t = self.maxctx.subtract(y, x)
413n/a if context.c.flags[C.Clamped] or \
414n/a context.c.flags[C.Underflow]:
415n/a # The standard ulp does not work in Underflow territory.
416n/a ulp = self.harrison_ulp(y)
417n/a else:
418n/a ulp = self.standard_ulp(y, p)
419n/a # Error in ulps.
420n/a err = self.maxctx.divide(t, ulp)
421n/a
422n/a dir = self.rounding_direction(x, context.p.rounding)
423n/a if dir == 0:
424n/a if P.Decimal("-0.6") < err < P.Decimal("0.6"):
425n/a return True
426n/a elif dir == 1: # directed, upwards
427n/a if P.Decimal("-0.1") < err < P.Decimal("1.1"):
428n/a return True
429n/a elif dir == -1: # directed, downwards
430n/a if P.Decimal("-1.1") < err < P.Decimal("0.1"):
431n/a return True
432n/a else: # ROUND_05UP
433n/a if P.Decimal("-1.1") < err < P.Decimal("1.1"):
434n/a return True
435n/a
436n/a print("ulp: %s error: %s exact: %s c_rounded: %s"
437n/a % (ulp, err, exact, rounded))
438n/a return False
439n/a
440n/a def bin_resolve_ulp(self, t):
441n/a """Check if results of _decimal's power function are within the
442n/a allowed ulp ranges."""
443n/a # NaNs are beyond repair.
444n/a if t.rc.is_nan() or t.rp.is_nan():
445n/a return False
446n/a
447n/a # "exact" result, double precision, half_even
448n/a self.maxctx.prec = context.p.prec * 2
449n/a
450n/a op1, op2 = t.pop[0], t.pop[1]
451n/a if t.contextfunc:
452n/a exact = getattr(self.maxctx, t.funcname)(op1, op2)
453n/a else:
454n/a exact = getattr(op1, t.funcname)(op2, context=self.maxctx)
455n/a
456n/a # _decimal's rounded result
457n/a rounded = P.Decimal(t.cresults[0])
458n/a
459n/a self.ulpdiff += 1
460n/a return self.check_ulpdiff(exact, rounded)
461n/a
462n/a ############################ Correct rounding #############################
463n/a def resolve_underflow(self, t):
464n/a """In extremely rare cases where the infinite precision result is just
465n/a below etiny, cdecimal does not set Subnormal/Underflow. Example:
466n/a
467n/a setcontext(Context(prec=21, rounding=ROUND_UP, Emin=-55, Emax=85))
468n/a Decimal("1.00000000000000000000000000000000000000000000000"
469n/a "0000000100000000000000000000000000000000000000000"
470n/a "0000000000000025").ln()
471n/a """
472n/a if t.cresults != t.presults:
473n/a return False # Results must be identical.
474n/a if context.c.flags[C.Rounded] and \
475n/a context.c.flags[C.Inexact] and \
476n/a context.p.flags[P.Rounded] and \
477n/a context.p.flags[P.Inexact]:
478n/a return True # Subnormal/Underflow may be missing.
479n/a return False
480n/a
481n/a def exp(self, t):
482n/a """Resolve Underflow or ULP difference."""
483n/a return self.resolve_underflow(t)
484n/a
485n/a def log10(self, t):
486n/a """Resolve Underflow or ULP difference."""
487n/a return self.resolve_underflow(t)
488n/a
489n/a def ln(self, t):
490n/a """Resolve Underflow or ULP difference."""
491n/a return self.resolve_underflow(t)
492n/a
493n/a def __pow__(self, t):
494n/a """Always calls the resolve function. C.Decimal does not have correct
495n/a rounding for the power function."""
496n/a if context.c.flags[C.Rounded] and \
497n/a context.c.flags[C.Inexact] and \
498n/a context.p.flags[P.Rounded] and \
499n/a context.p.flags[P.Inexact]:
500n/a return self.bin_resolve_ulp(t)
501n/a else:
502n/a return False
503n/a power = __rpow__ = __pow__
504n/a
505n/a ############################## Technicalities #############################
506n/a def __float__(self, t):
507n/a """NaN comparison in the verify() function obviously gives an
508n/a incorrect answer: nan == nan -> False"""
509n/a if t.cop[0].is_nan() and t.pop[0].is_nan():
510n/a return True
511n/a return False
512n/a __complex__ = __float__
513n/a
514n/a def __radd__(self, t):
515n/a """decimal.py gives precedence to the first NaN; this is
516n/a not important, as __radd__ will not be called for
517n/a two decimal arguments."""
518n/a if t.rc.is_nan() and t.rp.is_nan():
519n/a return True
520n/a return False
521n/a __rmul__ = __radd__
522n/a
523n/a ################################ Various ##################################
524n/a def __round__(self, t):
525n/a """Exception: Decimal('1').__round__(-100000000000000000000000000)
526n/a Should it really be InvalidOperation?"""
527n/a if t.rc is None and t.rp.is_nan():
528n/a return True
529n/a return False
530n/a
531n/ashandler = SkipHandler()
532n/adef skip_error(t):
533n/a return getattr(shandler, t.funcname, shandler.default)(t)
534n/a
535n/a
536n/a# ======================================================================
537n/a# Handling verification errors
538n/a# ======================================================================
539n/a
540n/aclass VerifyError(Exception):
541n/a """Verification failed."""
542n/a pass
543n/a
544n/adef function_as_string(t):
545n/a if t.contextfunc:
546n/a cargs = t.cop
547n/a pargs = t.pop
548n/a cfunc = "c_func: %s(" % t.funcname
549n/a pfunc = "p_func: %s(" % t.funcname
550n/a else:
551n/a cself, cargs = t.cop[0], t.cop[1:]
552n/a pself, pargs = t.pop[0], t.pop[1:]
553n/a cfunc = "c_func: %s.%s(" % (repr(cself), t.funcname)
554n/a pfunc = "p_func: %s.%s(" % (repr(pself), t.funcname)
555n/a
556n/a err = cfunc
557n/a for arg in cargs:
558n/a err += "%s, " % repr(arg)
559n/a err = err.rstrip(", ")
560n/a err += ")\n"
561n/a
562n/a err += pfunc
563n/a for arg in pargs:
564n/a err += "%s, " % repr(arg)
565n/a err = err.rstrip(", ")
566n/a err += ")"
567n/a
568n/a return err
569n/a
570n/adef raise_error(t):
571n/a global EXIT_STATUS
572n/a
573n/a if skip_error(t):
574n/a return
575n/a EXIT_STATUS = 1
576n/a
577n/a err = "Error in %s:\n\n" % t.funcname
578n/a err += "input operands: %s\n\n" % (t.op,)
579n/a err += function_as_string(t)
580n/a err += "\n\nc_result: %s\np_result: %s\n\n" % (t.cresults, t.presults)
581n/a err += "c_exceptions: %s\np_exceptions: %s\n\n" % (t.cex, t.pex)
582n/a err += "%s\n\n" % str(t.context)
583n/a
584n/a raise VerifyError(err)
585n/a
586n/a
587n/a# ======================================================================
588n/a# Main testing functions
589n/a#
590n/a# The procedure is always (t is the TestSet):
591n/a#
592n/a# convert(t) -> Initialize the TestSet as necessary.
593n/a#
594n/a# Return 0 for early abortion (e.g. if a TypeError
595n/a# occurs during conversion, there is nothing to test).
596n/a#
597n/a# Return 1 for continuing with the test case.
598n/a#
599n/a# callfuncs(t) -> Call the relevant function for each implementation
600n/a# and record the results in the TestSet.
601n/a#
602n/a# verify(t) -> Verify the results. If verification fails, details
603n/a# are printed to stdout.
604n/a# ======================================================================
605n/a
606n/adef convert(t, convstr=True):
607n/a """ t is the testset. At this stage the testset contains a tuple of
608n/a operands t.op of various types. For decimal methods the first
609n/a operand (self) is always converted to Decimal. If 'convstr' is
610n/a true, string operands are converted as well.
611n/a
612n/a Context operands are of type deccheck.Context, rounding mode
613n/a operands are given as a tuple (C.rounding, P.rounding).
614n/a
615n/a Other types (float, int, etc.) are left unchanged.
616n/a """
617n/a for i, op in enumerate(t.op):
618n/a
619n/a context.clear_status()
620n/a
621n/a if op in RoundModes:
622n/a t.cop.append(op)
623n/a t.pop.append(op)
624n/a
625n/a elif not t.contextfunc and i == 0 or \
626n/a convstr and isinstance(op, str):
627n/a try:
628n/a c = C.Decimal(op)
629n/a cex = None
630n/a except (TypeError, ValueError, OverflowError) as e:
631n/a c = None
632n/a cex = e.__class__
633n/a
634n/a try:
635n/a p = RestrictedDecimal(op)
636n/a pex = None
637n/a except (TypeError, ValueError, OverflowError) as e:
638n/a p = None
639n/a pex = e.__class__
640n/a
641n/a t.cop.append(c)
642n/a t.cex.append(cex)
643n/a t.pop.append(p)
644n/a t.pex.append(pex)
645n/a
646n/a if cex is pex:
647n/a if str(c) != str(p) or not context.assert_eq_status():
648n/a raise_error(t)
649n/a if cex and pex:
650n/a # nothing to test
651n/a return 0
652n/a else:
653n/a raise_error(t)
654n/a
655n/a elif isinstance(op, Context):
656n/a t.context = op
657n/a t.cop.append(op.c)
658n/a t.pop.append(op.p)
659n/a
660n/a else:
661n/a t.cop.append(op)
662n/a t.pop.append(op)
663n/a
664n/a return 1
665n/a
666n/adef callfuncs(t):
667n/a """ t is the testset. At this stage the testset contains operand lists
668n/a t.cop and t.pop for the C and Python versions of decimal.
669n/a For Decimal methods, the first operands are of type C.Decimal and
670n/a P.Decimal respectively. The remaining operands can have various types.
671n/a For Context methods, all operands can have any type.
672n/a
673n/a t.rc and t.rp are the results of the operation.
674n/a """
675n/a context.clear_status()
676n/a
677n/a try:
678n/a if t.contextfunc:
679n/a cargs = t.cop
680n/a t.rc = getattr(context.c, t.funcname)(*cargs)
681n/a else:
682n/a cself = t.cop[0]
683n/a cargs = t.cop[1:]
684n/a t.rc = getattr(cself, t.funcname)(*cargs)
685n/a t.cex.append(None)
686n/a except (TypeError, ValueError, OverflowError, MemoryError) as e:
687n/a t.rc = None
688n/a t.cex.append(e.__class__)
689n/a
690n/a try:
691n/a if t.contextfunc:
692n/a pargs = t.pop
693n/a t.rp = getattr(context.p, t.funcname)(*pargs)
694n/a else:
695n/a pself = t.pop[0]
696n/a pargs = t.pop[1:]
697n/a t.rp = getattr(pself, t.funcname)(*pargs)
698n/a t.pex.append(None)
699n/a except (TypeError, ValueError, OverflowError, MemoryError) as e:
700n/a t.rp = None
701n/a t.pex.append(e.__class__)
702n/a
703n/adef verify(t, stat):
704n/a """ t is the testset. At this stage the testset contains the following
705n/a tuples:
706n/a
707n/a t.op: original operands
708n/a t.cop: C.Decimal operands (see convert for details)
709n/a t.pop: P.Decimal operands (see convert for details)
710n/a t.rc: C result
711n/a t.rp: Python result
712n/a
713n/a t.rc and t.rp can have various types.
714n/a """
715n/a t.cresults.append(str(t.rc))
716n/a t.presults.append(str(t.rp))
717n/a if isinstance(t.rc, C.Decimal) and isinstance(t.rp, P.Decimal):
718n/a # General case: both results are Decimals.
719n/a t.cresults.append(t.rc.to_eng_string())
720n/a t.cresults.append(t.rc.as_tuple())
721n/a t.cresults.append(str(t.rc.imag))
722n/a t.cresults.append(str(t.rc.real))
723n/a t.presults.append(t.rp.to_eng_string())
724n/a t.presults.append(t.rp.as_tuple())
725n/a t.presults.append(str(t.rp.imag))
726n/a t.presults.append(str(t.rp.real))
727n/a
728n/a nc = t.rc.number_class().lstrip('+-s')
729n/a stat[nc] += 1
730n/a else:
731n/a # Results from e.g. __divmod__ can only be compared as strings.
732n/a if not isinstance(t.rc, tuple) and not isinstance(t.rp, tuple):
733n/a if t.rc != t.rp:
734n/a raise_error(t)
735n/a stat[type(t.rc).__name__] += 1
736n/a
737n/a # The return value lists must be equal.
738n/a if t.cresults != t.presults:
739n/a raise_error(t)
740n/a # The Python exception lists (TypeError, etc.) must be equal.
741n/a if t.cex != t.pex:
742n/a raise_error(t)
743n/a # The context flags must be equal.
744n/a if not t.context.assert_eq_status():
745n/a raise_error(t)
746n/a
747n/a
748n/a# ======================================================================
749n/a# Main test loops
750n/a#
751n/a# test_method(method, testspecs, testfunc) ->
752n/a#
753n/a# Loop through various context settings. The degree of
754n/a# thoroughness is determined by 'testspec'. For each
755n/a# setting, call 'testfunc'. Generally, 'testfunc' itself
756n/a# a loop, iterating through many test cases generated
757n/a# by the functions in randdec.py.
758n/a#
759n/a# test_n-ary(method, prec, exp_range, restricted_range, itr, stat) ->
760n/a#
761n/a# 'test_unary', 'test_binary' and 'test_ternary' are the
762n/a# main test functions passed to 'test_method'. They deal
763n/a# with the regular cases. The thoroughness of testing is
764n/a# determined by 'itr'.
765n/a#
766n/a# 'prec', 'exp_range' and 'restricted_range' are passed
767n/a# to the test-generating functions and limit the generated
768n/a# values. In some cases, for reasonable run times a
769n/a# maximum exponent of 9999 is required.
770n/a#
771n/a# The 'stat' parameter is passed down to the 'verify'
772n/a# function, which records statistics for the result values.
773n/a# ======================================================================
774n/a
775n/adef log(fmt, args=None):
776n/a if args:
777n/a sys.stdout.write(''.join((fmt, '\n')) % args)
778n/a else:
779n/a sys.stdout.write(''.join((str(fmt), '\n')))
780n/a sys.stdout.flush()
781n/a
782n/adef test_method(method, testspecs, testfunc):
783n/a """Iterate a test function through many context settings."""
784n/a log("testing %s ...", method)
785n/a stat = defaultdict(int)
786n/a for spec in testspecs:
787n/a if 'samples' in spec:
788n/a spec['prec'] = sorted(random.sample(range(1, 101),
789n/a spec['samples']))
790n/a for prec in spec['prec']:
791n/a context.prec = prec
792n/a for expts in spec['expts']:
793n/a emin, emax = expts
794n/a if emin == 'rand':
795n/a context.Emin = random.randrange(-1000, 0)
796n/a context.Emax = random.randrange(prec, 1000)
797n/a else:
798n/a context.Emin, context.Emax = emin, emax
799n/a if prec > context.Emax: continue
800n/a log(" prec: %d emin: %d emax: %d",
801n/a (context.prec, context.Emin, context.Emax))
802n/a restr_range = 9999 if context.Emax > 9999 else context.Emax+99
803n/a for rounding in RoundModes:
804n/a context.rounding = rounding
805n/a context.capitals = random.randrange(2)
806n/a if spec['clamp'] == 'rand':
807n/a context.clamp = random.randrange(2)
808n/a else:
809n/a context.clamp = spec['clamp']
810n/a exprange = context.c.Emax
811n/a testfunc(method, prec, exprange, restr_range,
812n/a spec['iter'], stat)
813n/a log(" result types: %s" % sorted([t for t in stat.items()]))
814n/a
815n/adef test_unary(method, prec, exp_range, restricted_range, itr, stat):
816n/a """Iterate a unary function through many test cases."""
817n/a if method in UnaryRestricted:
818n/a exp_range = restricted_range
819n/a for op in all_unary(prec, exp_range, itr):
820n/a t = TestSet(method, op)
821n/a try:
822n/a if not convert(t):
823n/a continue
824n/a callfuncs(t)
825n/a verify(t, stat)
826n/a except VerifyError as err:
827n/a log(err)
828n/a
829n/a if not method.startswith('__'):
830n/a for op in unary_optarg(prec, exp_range, itr):
831n/a t = TestSet(method, op)
832n/a try:
833n/a if not convert(t):
834n/a continue
835n/a callfuncs(t)
836n/a verify(t, stat)
837n/a except VerifyError as err:
838n/a log(err)
839n/a
840n/adef test_binary(method, prec, exp_range, restricted_range, itr, stat):
841n/a """Iterate a binary function through many test cases."""
842n/a if method in BinaryRestricted:
843n/a exp_range = restricted_range
844n/a for op in all_binary(prec, exp_range, itr):
845n/a t = TestSet(method, op)
846n/a try:
847n/a if not convert(t):
848n/a continue
849n/a callfuncs(t)
850n/a verify(t, stat)
851n/a except VerifyError as err:
852n/a log(err)
853n/a
854n/a if not method.startswith('__'):
855n/a for op in binary_optarg(prec, exp_range, itr):
856n/a t = TestSet(method, op)
857n/a try:
858n/a if not convert(t):
859n/a continue
860n/a callfuncs(t)
861n/a verify(t, stat)
862n/a except VerifyError as err:
863n/a log(err)
864n/a
865n/adef test_ternary(method, prec, exp_range, restricted_range, itr, stat):
866n/a """Iterate a ternary function through many test cases."""
867n/a if method in TernaryRestricted:
868n/a exp_range = restricted_range
869n/a for op in all_ternary(prec, exp_range, itr):
870n/a t = TestSet(method, op)
871n/a try:
872n/a if not convert(t):
873n/a continue
874n/a callfuncs(t)
875n/a verify(t, stat)
876n/a except VerifyError as err:
877n/a log(err)
878n/a
879n/a if not method.startswith('__'):
880n/a for op in ternary_optarg(prec, exp_range, itr):
881n/a t = TestSet(method, op)
882n/a try:
883n/a if not convert(t):
884n/a continue
885n/a callfuncs(t)
886n/a verify(t, stat)
887n/a except VerifyError as err:
888n/a log(err)
889n/a
890n/adef test_format(method, prec, exp_range, restricted_range, itr, stat):
891n/a """Iterate the __format__ method through many test cases."""
892n/a for op in all_unary(prec, exp_range, itr):
893n/a fmt1 = rand_format(chr(random.randrange(0, 128)), 'EeGgn')
894n/a fmt2 = rand_locale()
895n/a for fmt in (fmt1, fmt2):
896n/a fmtop = (op[0], fmt)
897n/a t = TestSet(method, fmtop)
898n/a try:
899n/a if not convert(t, convstr=False):
900n/a continue
901n/a callfuncs(t)
902n/a verify(t, stat)
903n/a except VerifyError as err:
904n/a log(err)
905n/a for op in all_unary(prec, 9999, itr):
906n/a fmt1 = rand_format(chr(random.randrange(0, 128)), 'Ff%')
907n/a fmt2 = rand_locale()
908n/a for fmt in (fmt1, fmt2):
909n/a fmtop = (op[0], fmt)
910n/a t = TestSet(method, fmtop)
911n/a try:
912n/a if not convert(t, convstr=False):
913n/a continue
914n/a callfuncs(t)
915n/a verify(t, stat)
916n/a except VerifyError as err:
917n/a log(err)
918n/a
919n/adef test_round(method, prec, exprange, restricted_range, itr, stat):
920n/a """Iterate the __round__ method through many test cases."""
921n/a for op in all_unary(prec, 9999, itr):
922n/a n = random.randrange(10)
923n/a roundop = (op[0], n)
924n/a t = TestSet(method, roundop)
925n/a try:
926n/a if not convert(t):
927n/a continue
928n/a callfuncs(t)
929n/a verify(t, stat)
930n/a except VerifyError as err:
931n/a log(err)
932n/a
933n/adef test_from_float(method, prec, exprange, restricted_range, itr, stat):
934n/a """Iterate the __float__ method through many test cases."""
935n/a for rounding in RoundModes:
936n/a context.rounding = rounding
937n/a for i in range(1000):
938n/a f = randfloat()
939n/a op = (f,) if method.startswith("context.") else ("sNaN", f)
940n/a t = TestSet(method, op)
941n/a try:
942n/a if not convert(t):
943n/a continue
944n/a callfuncs(t)
945n/a verify(t, stat)
946n/a except VerifyError as err:
947n/a log(err)
948n/a
949n/adef randcontext(exprange):
950n/a c = Context(C.Context(), P.Context())
951n/a c.Emax = random.randrange(1, exprange+1)
952n/a c.Emin = random.randrange(-exprange, 0)
953n/a maxprec = 100 if c.Emax >= 100 else c.Emax
954n/a c.prec = random.randrange(1, maxprec+1)
955n/a c.clamp = random.randrange(2)
956n/a c.clear_traps()
957n/a return c
958n/a
959n/adef test_quantize_api(method, prec, exprange, restricted_range, itr, stat):
960n/a """Iterate the 'quantize' method through many test cases, using
961n/a the optional arguments."""
962n/a for op in all_binary(prec, restricted_range, itr):
963n/a for rounding in RoundModes:
964n/a c = randcontext(exprange)
965n/a quantizeop = (op[0], op[1], rounding, c)
966n/a t = TestSet(method, quantizeop)
967n/a try:
968n/a if not convert(t):
969n/a continue
970n/a callfuncs(t)
971n/a verify(t, stat)
972n/a except VerifyError as err:
973n/a log(err)
974n/a
975n/a
976n/adef check_untested(funcdict, c_cls, p_cls):
977n/a """Determine untested, C-only and Python-only attributes.
978n/a Uncomment print lines for debugging."""
979n/a c_attr = set(dir(c_cls))
980n/a p_attr = set(dir(p_cls))
981n/a intersect = c_attr & p_attr
982n/a
983n/a funcdict['c_only'] = tuple(sorted(c_attr-intersect))
984n/a funcdict['p_only'] = tuple(sorted(p_attr-intersect))
985n/a
986n/a tested = set()
987n/a for lst in funcdict.values():
988n/a for v in lst:
989n/a v = v.replace("context.", "") if c_cls == C.Context else v
990n/a tested.add(v)
991n/a
992n/a funcdict['untested'] = tuple(sorted(intersect-tested))
993n/a
994n/a #for key in ('untested', 'c_only', 'p_only'):
995n/a # s = 'Context' if c_cls == C.Context else 'Decimal'
996n/a # print("\n%s %s:\n%s" % (s, key, funcdict[key]))
997n/a
998n/a
999n/aif __name__ == '__main__':
1000n/a
1001n/a import time
1002n/a
1003n/a randseed = int(time.time())
1004n/a random.seed(randseed)
1005n/a
1006n/a # Set up the testspecs list. A testspec is simply a dictionary
1007n/a # that determines the amount of different contexts that 'test_method'
1008n/a # will generate.
1009n/a base_expts = [(C.MIN_EMIN, C.MAX_EMAX)]
1010n/a if C.MAX_EMAX == 999999999999999999:
1011n/a base_expts.append((-999999999, 999999999))
1012n/a
1013n/a # Basic contexts.
1014n/a base = {
1015n/a 'expts': base_expts,
1016n/a 'prec': [],
1017n/a 'clamp': 'rand',
1018n/a 'iter': None,
1019n/a 'samples': None,
1020n/a }
1021n/a # Contexts with small values for prec, emin, emax.
1022n/a small = {
1023n/a 'prec': [1, 2, 3, 4, 5],
1024n/a 'expts': [(-1, 1), (-2, 2), (-3, 3), (-4, 4), (-5, 5)],
1025n/a 'clamp': 'rand',
1026n/a 'iter': None
1027n/a }
1028n/a # IEEE interchange format.
1029n/a ieee = [
1030n/a # DECIMAL32
1031n/a {'prec': [7], 'expts': [(-95, 96)], 'clamp': 1, 'iter': None},
1032n/a # DECIMAL64
1033n/a {'prec': [16], 'expts': [(-383, 384)], 'clamp': 1, 'iter': None},
1034n/a # DECIMAL128
1035n/a {'prec': [34], 'expts': [(-6143, 6144)], 'clamp': 1, 'iter': None}
1036n/a ]
1037n/a
1038n/a if '--medium' in sys.argv:
1039n/a base['expts'].append(('rand', 'rand'))
1040n/a # 5 random precisions
1041n/a base['samples'] = 5
1042n/a testspecs = [small] + ieee + [base]
1043n/a if '--long' in sys.argv:
1044n/a base['expts'].append(('rand', 'rand'))
1045n/a # 10 random precisions
1046n/a base['samples'] = 10
1047n/a testspecs = [small] + ieee + [base]
1048n/a elif '--all' in sys.argv:
1049n/a base['expts'].append(('rand', 'rand'))
1050n/a # All precisions in [1, 100]
1051n/a base['samples'] = 100
1052n/a testspecs = [small] + ieee + [base]
1053n/a else: # --short
1054n/a rand_ieee = random.choice(ieee)
1055n/a base['iter'] = small['iter'] = rand_ieee['iter'] = 1
1056n/a # 1 random precision and exponent pair
1057n/a base['samples'] = 1
1058n/a base['expts'] = [random.choice(base_expts)]
1059n/a # 1 random precision and exponent pair
1060n/a prec = random.randrange(1, 6)
1061n/a small['prec'] = [prec]
1062n/a small['expts'] = [(-prec, prec)]
1063n/a testspecs = [small, rand_ieee, base]
1064n/a
1065n/a check_untested(Functions, C.Decimal, P.Decimal)
1066n/a check_untested(ContextFunctions, C.Context, P.Context)
1067n/a
1068n/a
1069n/a log("\n\nRandom seed: %d\n\n", randseed)
1070n/a
1071n/a # Decimal methods:
1072n/a for method in Functions['unary'] + Functions['unary_ctx'] + \
1073n/a Functions['unary_rnd_ctx']:
1074n/a test_method(method, testspecs, test_unary)
1075n/a
1076n/a for method in Functions['binary'] + Functions['binary_ctx']:
1077n/a test_method(method, testspecs, test_binary)
1078n/a
1079n/a for method in Functions['ternary'] + Functions['ternary_ctx']:
1080n/a test_method(method, testspecs, test_ternary)
1081n/a
1082n/a test_method('__format__', testspecs, test_format)
1083n/a test_method('__round__', testspecs, test_round)
1084n/a test_method('from_float', testspecs, test_from_float)
1085n/a test_method('quantize', testspecs, test_quantize_api)
1086n/a
1087n/a # Context methods:
1088n/a for method in ContextFunctions['unary']:
1089n/a test_method(method, testspecs, test_unary)
1090n/a
1091n/a for method in ContextFunctions['binary']:
1092n/a test_method(method, testspecs, test_binary)
1093n/a
1094n/a for method in ContextFunctions['ternary']:
1095n/a test_method(method, testspecs, test_ternary)
1096n/a
1097n/a test_method('context.create_decimal_from_float', testspecs, test_from_float)
1098n/a
1099n/a
1100n/a sys.exit(EXIT_STATUS)