blob: 98b94790fb7951f1f030d9eac649131495c25dd6 [file] [log] [blame]
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001# Copyright (c) 2004 Python Software Foundation.
2# All rights reserved.
3
4# Written by Eric Price <eprice at tjhsst.edu>
5# and Facundo Batista <facundo at taniquetil.com.ar>
6# and Raymond Hettinger <python at rcn.com>
7# and Aahz (aahz at pobox.com)
8# and Tim Peters
9
10"""
11These are the test cases for the Decimal module.
12
13There are two groups of tests, Arithmetic and Behaviour. The former test
14the Decimal arithmetic using the tests provided by Mike Cowlishaw. The latter
15test the pythonic behaviour according to PEP 327.
16
17Cowlishaw's tests can be downloaded from:
18
19 www2.hursley.ibm.com/decimal/dectest.zip
20
21This test module can be called from command line with one parameter (Arithmetic
22or Behaviour) to test each part, or without parameter to test both parts. If
23you're working through IDLE, you can import this test module and call test_main()
24with the corresponding argument.
25"""
Thomas Wouters89f507f2006-12-13 04:49:30 +000026from __future__ import with_statement
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000027
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000028import unittest
29import glob
30import os, sys
31import pickle, copy
32from decimal import *
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000033from test.test_support import (TestSkipped, run_unittest, run_doctest,
34 is_resource_enabled)
Raymond Hettinger0aeac102004-07-05 22:53:03 +000035import random
Raymond Hettinger7e71fa52004-12-18 19:07:19 +000036try:
37 import threading
38except ImportError:
39 threading = None
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000040
Raymond Hettingerfed52962004-07-14 15:41:57 +000041# Useful Test Constant
Guido van Rossumc1f779c2007-07-03 08:25:58 +000042Signals = tuple(getcontext().flags.keys())
Raymond Hettingerfed52962004-07-14 15:41:57 +000043
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000044# Tests are built around these assumed context defaults.
45# test_main() restores the original context.
46def init():
47 global ORIGINAL_CONTEXT
48 ORIGINAL_CONTEXT = getcontext().copy()
49 DefaultContext.prec = 9
50 DefaultContext.rounding = ROUND_HALF_EVEN
51 DefaultContext.traps = dict.fromkeys(Signals, 0)
52 setcontext(DefaultContext)
Raymond Hettinger6ea48452004-07-03 12:26:21 +000053
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000054TESTDATADIR = 'decimaltestdata'
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +000055if __name__ == '__main__':
56 file = sys.argv[0]
57else:
58 file = __file__
59testdir = os.path.dirname(file) or os.curdir
Raymond Hettinger267b8682005-03-27 10:47:39 +000060directory = testdir + os.sep + TESTDATADIR + os.sep
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000061
Raymond Hettinger267b8682005-03-27 10:47:39 +000062skip_expected = not os.path.isdir(directory)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000063
64# Make sure it actually raises errors when not expected and caught in flags
65# Slower, since it runs some things several times.
66EXTENDEDERRORTEST = False
67
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000068#Map the test cases' error names to the actual errors
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000069ErrorNames = {'clamped' : Clamped,
Raymond Hettinger5aa478b2004-07-09 10:02:53 +000070 'conversion_syntax' : InvalidOperation,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000071 'division_by_zero' : DivisionByZero,
Raymond Hettinger5aa478b2004-07-09 10:02:53 +000072 'division_impossible' : InvalidOperation,
73 'division_undefined' : InvalidOperation,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000074 'inexact' : Inexact,
Raymond Hettinger5aa478b2004-07-09 10:02:53 +000075 'invalid_context' : InvalidOperation,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000076 'invalid_operation' : InvalidOperation,
77 'overflow' : Overflow,
78 'rounded' : Rounded,
79 'subnormal' : Subnormal,
80 'underflow' : Underflow}
81
82
83def Nonfunction(*args):
84 """Doesn't do anything."""
85 return None
86
87RoundingDict = {'ceiling' : ROUND_CEILING, #Maps test-case names to roundings.
88 'down' : ROUND_DOWN,
89 'floor' : ROUND_FLOOR,
90 'half_down' : ROUND_HALF_DOWN,
91 'half_even' : ROUND_HALF_EVEN,
92 'half_up' : ROUND_HALF_UP,
Thomas Wouters1b7f8912007-09-19 03:06:30 +000093 'up' : ROUND_UP,
94 '05up' : ROUND_05UP}
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000095
96# Name adapter to be able to change the Decimal and Context
97# interface without changing the test files from Cowlishaw
Guido van Rossum8ce8a782007-11-01 19:42:39 +000098nameAdapter = {'and':'logical_and',
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000099 'apply':'_apply',
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000100 'class':'number_class',
101 'comparesig':'compare_signal',
102 'comparetotal':'compare_total',
103 'comparetotmag':'compare_total_mag',
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000104 'copy':'copy_decimal',
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000105 'copyabs':'copy_abs',
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000106 'copynegate':'copy_negate',
107 'copysign':'copy_sign',
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000108 'divideint':'divide_int',
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000109 'invert':'logical_invert',
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000110 'iscanonical':'is_canonical',
111 'isfinite':'is_finite',
112 'isinfinite':'is_infinite',
113 'isnan':'is_nan',
114 'isnormal':'is_normal',
115 'isqnan':'is_qnan',
116 'issigned':'is_signed',
117 'issnan':'is_snan',
118 'issubnormal':'is_subnormal',
119 'iszero':'is_zero',
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000120 'maxmag':'max_mag',
121 'minmag':'min_mag',
122 'nextminus':'next_minus',
123 'nextplus':'next_plus',
124 'nexttoward':'next_toward',
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000125 'or':'logical_or',
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000126 'reduce':'normalize',
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000127 'remaindernear':'remainder_near',
128 'samequantum':'same_quantum',
129 'squareroot':'sqrt',
130 'toeng':'to_eng_string',
131 'tointegral':'to_integral_value',
132 'tointegralx':'to_integral_exact',
133 'tosci':'to_sci_string',
134 'xor':'logical_xor',
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000135 }
136
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000137# The following functions return True/False rather than a Decimal instance
138
139LOGICAL_FUNCTIONS = (
140 'is_canonical',
141 'is_finite',
142 'is_infinite',
143 'is_nan',
144 'is_normal',
145 'is_qnan',
146 'is_signed',
147 'is_snan',
148 'is_subnormal',
149 'is_zero',
150 'same_quantum',
151 )
152
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000153# For some operations (currently exp, ln, log10, power), the decNumber
154# reference implementation imposes additional restrictions on the
155# context and operands. These restrictions are not part of the
156# specification; however, the effect of these restrictions does show
157# up in some of the testcases. We skip testcases that violate these
158# restrictions, since Decimal behaves differently from decNumber for
159# these testcases so these testcases would otherwise fail.
160
161decNumberRestricted = ('power', 'ln', 'log10', 'exp')
162DEC_MAX_MATH = 999999
163def outside_decNumber_bounds(v, context):
164 if (context.prec > DEC_MAX_MATH or
165 context.Emax > DEC_MAX_MATH or
166 -context.Emin > DEC_MAX_MATH):
167 return True
168 if not v._is_special and v and (
169 len(v._int) > DEC_MAX_MATH or
170 v.adjusted() > DEC_MAX_MATH or
171 v.adjusted() < 1-2*DEC_MAX_MATH):
172 return True
173 return False
174
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000175class DecimalTest(unittest.TestCase):
176 """Class which tests the Decimal class against the test cases.
177
178 Changed for unittest.
179 """
180 def setUp(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000181 self.context = Context()
Raymond Hettingerbf440692004-07-10 14:14:37 +0000182 for key in DefaultContext.traps.keys():
183 DefaultContext.traps[key] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000184 self.ignore_list = ['#']
185 # Basically, a # means return NaN InvalidOperation.
186 # Different from a sNaN in trim
187
188 self.ChangeDict = {'precision' : self.change_precision,
189 'rounding' : self.change_rounding_method,
190 'maxexponent' : self.change_max_exponent,
191 'minexponent' : self.change_min_exponent,
192 'clamp' : self.change_clamp}
193
194 def tearDown(self):
195 """Cleaning up enviroment."""
196 # leaving context in original state
Raymond Hettingerbf440692004-07-10 14:14:37 +0000197 for key in DefaultContext.traps.keys():
198 DefaultContext.traps[key] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000199 return
200
201 def eval_file(self, file):
202 global skip_expected
203 if skip_expected:
204 raise TestSkipped
205 return
Neal Norwitz70967602006-03-17 08:29:44 +0000206 for line in open(file):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000207 line = line.replace('\r\n', '').replace('\n', '')
Raymond Hettinger5aa478b2004-07-09 10:02:53 +0000208 #print line
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000209 try:
210 t = self.eval_line(line)
Guido van Rossumb940e112007-01-10 16:19:56 +0000211 except DecimalException as exception:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000212 #Exception raised where there shoudn't have been one.
213 self.fail('Exception "'+exception.__class__.__name__ + '" raised on line '+line)
214
215 return
216
217 def eval_line(self, s):
218 if s.find(' -> ') >= 0 and s[:2] != '--' and not s.startswith(' --'):
219 s = (s.split('->')[0] + '->' +
220 s.split('->')[1].split('--')[0]).strip()
221 else:
222 s = s.split('--')[0].strip()
223
224 for ignore in self.ignore_list:
225 if s.find(ignore) >= 0:
226 #print s.split()[0], 'NotImplemented--', ignore
227 return
228 if not s:
229 return
230 elif ':' in s:
231 return self.eval_directive(s)
232 else:
233 return self.eval_equation(s)
234
235 def eval_directive(self, s):
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000236 funct, value = (x.strip().lower() for x in s.split(':'))
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000237 if funct == 'rounding':
238 value = RoundingDict[value]
239 else:
240 try:
241 value = int(value)
242 except ValueError:
243 pass
244
245 funct = self.ChangeDict.get(funct, Nonfunction)
246 funct(value)
247
248 def eval_equation(self, s):
249 #global DEFAULT_PRECISION
250 #print DEFAULT_PRECISION
Raymond Hettingered20ad82004-09-04 20:09:13 +0000251
252 if not TEST_ALL and random.random() < 0.90:
253 return
254
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000255 try:
256 Sides = s.split('->')
257 L = Sides[0].strip().split()
258 id = L[0]
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000259 if DEBUG:
260 print("Test ", id, end=" ")
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000261 funct = L[1].lower()
262 valstemp = L[2:]
263 L = Sides[1].strip().split()
264 ans = L[0]
265 exceptions = L[1:]
266 except (TypeError, AttributeError, IndexError):
Raymond Hettingerd87ac8f2004-07-09 10:52:54 +0000267 raise InvalidOperation
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000268 def FixQuotes(val):
269 val = val.replace("''", 'SingleQuote').replace('""', 'DoubleQuote')
270 val = val.replace("'", '').replace('"', '')
271 val = val.replace('SingleQuote', "'").replace('DoubleQuote', '"')
272 return val
273 fname = nameAdapter.get(funct, funct)
274 if fname == 'rescale':
275 return
276 funct = getattr(self.context, fname)
277 vals = []
278 conglomerate = ''
279 quote = 0
280 theirexceptions = [ErrorNames[x.lower()] for x in exceptions]
281
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000282 for exception in Signals:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000283 self.context.traps[exception] = 1 #Catch these bugs...
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000284 for exception in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000285 self.context.traps[exception] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000286 for i, val in enumerate(valstemp):
287 if val.count("'") % 2 == 1:
288 quote = 1 - quote
289 if quote:
290 conglomerate = conglomerate + ' ' + val
291 continue
292 else:
293 val = conglomerate + val
294 conglomerate = ''
295 v = FixQuotes(val)
296 if fname in ('to_sci_string', 'to_eng_string'):
297 if EXTENDEDERRORTEST:
298 for error in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000299 self.context.traps[error] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000300 try:
301 funct(self.context.create_decimal(v))
302 except error:
303 pass
Guido van Rossumb940e112007-01-10 16:19:56 +0000304 except Signals as e:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000305 self.fail("Raised %s in %s when %s disabled" % \
306 (e, s, error))
307 else:
308 self.fail("Did not raise %s in %s" % (error, s))
Raymond Hettingerbf440692004-07-10 14:14:37 +0000309 self.context.traps[error] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000310 v = self.context.create_decimal(v)
311 else:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000312 v = Decimal(v, self.context)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000313 vals.append(v)
314
315 ans = FixQuotes(ans)
316
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000317 # skip tests that are related to bounds imposed in the decNumber
318 # reference implementation
319 if fname in decNumberRestricted:
320 if fname == 'power':
321 if not (vals[1]._isinteger() and
322 -1999999997 <= vals[1] <= 999999999):
323 if outside_decNumber_bounds(vals[0], self.context) or \
324 outside_decNumber_bounds(vals[1], self.context):
325 #print "Skipping test %s" % s
326 return
327 else:
328 if outside_decNumber_bounds(vals[0], self.context):
329 #print "Skipping test %s" % s
330 return
331
332
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000333 if EXTENDEDERRORTEST and fname not in ('to_sci_string', 'to_eng_string'):
334 for error in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000335 self.context.traps[error] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000336 try:
337 funct(*vals)
338 except error:
339 pass
Guido van Rossumb940e112007-01-10 16:19:56 +0000340 except Signals as e:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000341 self.fail("Raised %s in %s when %s disabled" % \
342 (e, s, error))
343 else:
344 self.fail("Did not raise %s in %s" % (error, s))
Raymond Hettingerbf440692004-07-10 14:14:37 +0000345 self.context.traps[error] = 0
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000346 if DEBUG:
347 print("--", self.context)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000348 try:
349 result = str(funct(*vals))
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000350 if fname in LOGICAL_FUNCTIONS:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000351 result = str(int(eval(result))) # 'True', 'False' -> '1', '0'
Guido van Rossumb940e112007-01-10 16:19:56 +0000352 except Signals as error:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000353 self.fail("Raised %s in %s" % (error, s))
354 except: #Catch any error long enough to state the test case.
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000355 print("ERROR:", s)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000356 raise
357
358 myexceptions = self.getexceptions()
Raymond Hettingerbf440692004-07-10 14:14:37 +0000359 self.context.clear_flags()
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000360
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000361 myexceptions.sort(key=repr)
362 theirexceptions.sort(key=repr)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000363
364 self.assertEqual(result, ans,
365 'Incorrect answer for ' + s + ' -- got ' + result)
366 self.assertEqual(myexceptions, theirexceptions,
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000367 'Incorrect flags set in ' + s + ' -- got ' + str(myexceptions))
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000368 return
369
370 def getexceptions(self):
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000371 return [e for e in Signals if self.context.flags[e]]
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000372
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000373 def change_precision(self, prec):
374 self.context.prec = prec
375 def change_rounding_method(self, rounding):
376 self.context.rounding = rounding
377 def change_min_exponent(self, exp):
378 self.context.Emin = exp
379 def change_max_exponent(self, exp):
380 self.context.Emax = exp
381 def change_clamp(self, clamp):
382 self.context._clamp = clamp
383
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000384
385
386# The following classes test the behaviour of Decimal according to PEP 327
387
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000388class DecimalExplicitConstructionTest(unittest.TestCase):
389 '''Unit tests for Explicit Construction cases of Decimal.'''
390
391 def test_explicit_empty(self):
392 self.assertEqual(Decimal(), Decimal("0"))
393
394 def test_explicit_from_None(self):
395 self.assertRaises(TypeError, Decimal, None)
396
397 def test_explicit_from_int(self):
398
399 #positive
400 d = Decimal(45)
401 self.assertEqual(str(d), '45')
402
403 #very large positive
404 d = Decimal(500000123)
405 self.assertEqual(str(d), '500000123')
406
407 #negative
408 d = Decimal(-45)
409 self.assertEqual(str(d), '-45')
410
411 #zero
412 d = Decimal(0)
413 self.assertEqual(str(d), '0')
414
415 def test_explicit_from_string(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000416
417 #empty
418 self.assertEqual(str(Decimal('')), 'NaN')
419
420 #int
421 self.assertEqual(str(Decimal('45')), '45')
422
423 #float
424 self.assertEqual(str(Decimal('45.34')), '45.34')
425
426 #engineer notation
427 self.assertEqual(str(Decimal('45e2')), '4.5E+3')
428
429 #just not a number
430 self.assertEqual(str(Decimal('ugly')), 'NaN')
431
432 def test_explicit_from_tuples(self):
433
434 #zero
435 d = Decimal( (0, (0,), 0) )
436 self.assertEqual(str(d), '0')
437
438 #int
439 d = Decimal( (1, (4, 5), 0) )
440 self.assertEqual(str(d), '-45')
441
442 #float
443 d = Decimal( (0, (4, 5, 3, 4), -2) )
444 self.assertEqual(str(d), '45.34')
445
446 #weird
447 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
448 self.assertEqual(str(d), '-4.34913534E-17')
449
450 #wrong number of items
451 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1)) )
452
453 #bad sign
454 self.assertRaises(ValueError, Decimal, (8, (4, 3, 4, 9, 1), 2) )
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000455 self.assertRaises(ValueError, Decimal, (0., (4, 3, 4, 9, 1), 2) )
456 self.assertRaises(ValueError, Decimal, (Decimal(1), (4, 3, 4, 9, 1), 2))
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000457
458 #bad exp
459 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 'wrong!') )
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000460 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 0.) )
461 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), '1') )
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000462
463 #bad coefficients
464 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) )
465 self.assertRaises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) )
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000466 self.assertRaises(ValueError, Decimal, (1, (4, 10, 4, 9, 1), 2) )
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000467
468 def test_explicit_from_Decimal(self):
469
470 #positive
471 d = Decimal(45)
472 e = Decimal(d)
473 self.assertEqual(str(e), '45')
474 self.assertNotEqual(id(d), id(e))
475
476 #very large positive
477 d = Decimal(500000123)
478 e = Decimal(d)
479 self.assertEqual(str(e), '500000123')
480 self.assertNotEqual(id(d), id(e))
481
482 #negative
483 d = Decimal(-45)
484 e = Decimal(d)
485 self.assertEqual(str(e), '-45')
486 self.assertNotEqual(id(d), id(e))
487
488 #zero
489 d = Decimal(0)
490 e = Decimal(d)
491 self.assertEqual(str(e), '0')
492 self.assertNotEqual(id(d), id(e))
493
494 def test_explicit_context_create_decimal(self):
495
496 nc = copy.copy(getcontext())
497 nc.prec = 3
498
499 # empty
Raymond Hettingerfed52962004-07-14 15:41:57 +0000500 d = Decimal()
501 self.assertEqual(str(d), '0')
502 d = nc.create_decimal()
503 self.assertEqual(str(d), '0')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000504
505 # from None
506 self.assertRaises(TypeError, nc.create_decimal, None)
507
508 # from int
509 d = nc.create_decimal(456)
510 self.failUnless(isinstance(d, Decimal))
511 self.assertEqual(nc.create_decimal(45678),
512 nc.create_decimal('457E+2'))
513
514 # from string
515 d = Decimal('456789')
516 self.assertEqual(str(d), '456789')
517 d = nc.create_decimal('456789')
518 self.assertEqual(str(d), '4.57E+5')
519
520 # from tuples
521 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
522 self.assertEqual(str(d), '-4.34913534E-17')
523 d = nc.create_decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
524 self.assertEqual(str(d), '-4.35E-17')
525
526 # from Decimal
527 prevdec = Decimal(500000123)
528 d = Decimal(prevdec)
529 self.assertEqual(str(d), '500000123')
530 d = nc.create_decimal(prevdec)
531 self.assertEqual(str(d), '5.00E+8')
532
533
534class DecimalImplicitConstructionTest(unittest.TestCase):
535 '''Unit tests for Implicit Construction cases of Decimal.'''
536
537 def test_implicit_from_None(self):
538 self.assertRaises(TypeError, eval, 'Decimal(5) + None', globals())
539
540 def test_implicit_from_int(self):
541 #normal
542 self.assertEqual(str(Decimal(5) + 45), '50')
543 #exceeding precision
544 self.assertEqual(Decimal(5) + 123456789000, Decimal(123456789000))
545
546 def test_implicit_from_string(self):
547 self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', globals())
548
549 def test_implicit_from_float(self):
550 self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', globals())
551
552 def test_implicit_from_Decimal(self):
553 self.assertEqual(Decimal(5) + Decimal(45), Decimal(50))
554
Raymond Hettinger267b8682005-03-27 10:47:39 +0000555 def test_rop(self):
556 # Allow other classes to be trained to interact with Decimals
557 class E:
558 def __divmod__(self, other):
559 return 'divmod ' + str(other)
560 def __rdivmod__(self, other):
561 return str(other) + ' rdivmod'
562 def __lt__(self, other):
563 return 'lt ' + str(other)
564 def __gt__(self, other):
565 return 'gt ' + str(other)
566 def __le__(self, other):
567 return 'le ' + str(other)
568 def __ge__(self, other):
569 return 'ge ' + str(other)
570 def __eq__(self, other):
571 return 'eq ' + str(other)
572 def __ne__(self, other):
573 return 'ne ' + str(other)
574
575 self.assertEqual(divmod(E(), Decimal(10)), 'divmod 10')
576 self.assertEqual(divmod(Decimal(10), E()), '10 rdivmod')
577 self.assertEqual(eval('Decimal(10) < E()'), 'gt 10')
578 self.assertEqual(eval('Decimal(10) > E()'), 'lt 10')
579 self.assertEqual(eval('Decimal(10) <= E()'), 'ge 10')
580 self.assertEqual(eval('Decimal(10) >= E()'), 'le 10')
581 self.assertEqual(eval('Decimal(10) == E()'), 'eq 10')
582 self.assertEqual(eval('Decimal(10) != E()'), 'ne 10')
583
584 # insert operator methods and then exercise them
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000585 oplist = [
586 ('+', '__add__', '__radd__'),
587 ('-', '__sub__', '__rsub__'),
588 ('*', '__mul__', '__rmul__'),
Thomas Woutersdcc6d322006-04-21 11:30:52 +0000589 ('/', '__truediv__', '__rtruediv__'),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000590 ('%', '__mod__', '__rmod__'),
591 ('//', '__floordiv__', '__rfloordiv__'),
592 ('**', '__pow__', '__rpow__')
593 ]
Raymond Hettinger267b8682005-03-27 10:47:39 +0000594
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000595 for sym, lop, rop in oplist:
Raymond Hettinger267b8682005-03-27 10:47:39 +0000596 setattr(E, lop, lambda self, other: 'str' + lop + str(other))
597 setattr(E, rop, lambda self, other: str(other) + rop + 'str')
598 self.assertEqual(eval('E()' + sym + 'Decimal(10)'),
599 'str' + lop + '10')
600 self.assertEqual(eval('Decimal(10)' + sym + 'E()'),
601 '10' + rop + 'str')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000602
603class DecimalArithmeticOperatorsTest(unittest.TestCase):
604 '''Unit tests for all arithmetic operators, binary and unary.'''
605
606 def test_addition(self):
607
608 d1 = Decimal('-11.1')
609 d2 = Decimal('22.2')
610
611 #two Decimals
612 self.assertEqual(d1+d2, Decimal('11.1'))
613 self.assertEqual(d2+d1, Decimal('11.1'))
614
615 #with other type, left
616 c = d1 + 5
617 self.assertEqual(c, Decimal('-6.1'))
618 self.assertEqual(type(c), type(d1))
619
620 #with other type, right
621 c = 5 + d1
622 self.assertEqual(c, Decimal('-6.1'))
623 self.assertEqual(type(c), type(d1))
624
625 #inline with decimal
626 d1 += d2
627 self.assertEqual(d1, Decimal('11.1'))
628
629 #inline with other type
630 d1 += 5
631 self.assertEqual(d1, Decimal('16.1'))
632
633 def test_subtraction(self):
634
635 d1 = Decimal('-11.1')
636 d2 = Decimal('22.2')
637
638 #two Decimals
639 self.assertEqual(d1-d2, Decimal('-33.3'))
640 self.assertEqual(d2-d1, Decimal('33.3'))
641
642 #with other type, left
643 c = d1 - 5
644 self.assertEqual(c, Decimal('-16.1'))
645 self.assertEqual(type(c), type(d1))
646
647 #with other type, right
648 c = 5 - d1
649 self.assertEqual(c, Decimal('16.1'))
650 self.assertEqual(type(c), type(d1))
651
652 #inline with decimal
653 d1 -= d2
654 self.assertEqual(d1, Decimal('-33.3'))
655
656 #inline with other type
657 d1 -= 5
658 self.assertEqual(d1, Decimal('-38.3'))
659
660 def test_multiplication(self):
661
662 d1 = Decimal('-5')
663 d2 = Decimal('3')
664
665 #two Decimals
666 self.assertEqual(d1*d2, Decimal('-15'))
667 self.assertEqual(d2*d1, Decimal('-15'))
668
669 #with other type, left
670 c = d1 * 5
671 self.assertEqual(c, Decimal('-25'))
672 self.assertEqual(type(c), type(d1))
673
674 #with other type, right
675 c = 5 * d1
676 self.assertEqual(c, Decimal('-25'))
677 self.assertEqual(type(c), type(d1))
678
679 #inline with decimal
680 d1 *= d2
681 self.assertEqual(d1, Decimal('-15'))
682
683 #inline with other type
684 d1 *= 5
685 self.assertEqual(d1, Decimal('-75'))
686
687 def test_division(self):
688
689 d1 = Decimal('-5')
690 d2 = Decimal('2')
691
692 #two Decimals
693 self.assertEqual(d1/d2, Decimal('-2.5'))
694 self.assertEqual(d2/d1, Decimal('-0.4'))
695
696 #with other type, left
697 c = d1 / 4
698 self.assertEqual(c, Decimal('-1.25'))
699 self.assertEqual(type(c), type(d1))
700
701 #with other type, right
702 c = 4 / d1
703 self.assertEqual(c, Decimal('-0.8'))
704 self.assertEqual(type(c), type(d1))
705
706 #inline with decimal
707 d1 /= d2
708 self.assertEqual(d1, Decimal('-2.5'))
709
710 #inline with other type
711 d1 /= 4
712 self.assertEqual(d1, Decimal('-0.625'))
713
714 def test_floor_division(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000715
716 d1 = Decimal('5')
717 d2 = Decimal('2')
718
719 #two Decimals
720 self.assertEqual(d1//d2, Decimal('2'))
721 self.assertEqual(d2//d1, Decimal('0'))
722
723 #with other type, left
724 c = d1 // 4
725 self.assertEqual(c, Decimal('1'))
726 self.assertEqual(type(c), type(d1))
727
728 #with other type, right
729 c = 7 // d1
730 self.assertEqual(c, Decimal('1'))
731 self.assertEqual(type(c), type(d1))
732
733 #inline with decimal
734 d1 //= d2
735 self.assertEqual(d1, Decimal('2'))
736
737 #inline with other type
738 d1 //= 2
739 self.assertEqual(d1, Decimal('1'))
740
741 def test_powering(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000742
743 d1 = Decimal('5')
744 d2 = Decimal('2')
745
746 #two Decimals
747 self.assertEqual(d1**d2, Decimal('25'))
748 self.assertEqual(d2**d1, Decimal('32'))
749
750 #with other type, left
751 c = d1 ** 4
752 self.assertEqual(c, Decimal('625'))
753 self.assertEqual(type(c), type(d1))
754
755 #with other type, right
756 c = 7 ** d1
757 self.assertEqual(c, Decimal('16807'))
758 self.assertEqual(type(c), type(d1))
759
760 #inline with decimal
761 d1 **= d2
762 self.assertEqual(d1, Decimal('25'))
763
764 #inline with other type
765 d1 **= 4
766 self.assertEqual(d1, Decimal('390625'))
767
768 def test_module(self):
769
770 d1 = Decimal('5')
771 d2 = Decimal('2')
772
773 #two Decimals
774 self.assertEqual(d1%d2, Decimal('1'))
775 self.assertEqual(d2%d1, Decimal('2'))
776
777 #with other type, left
778 c = d1 % 4
779 self.assertEqual(c, Decimal('1'))
780 self.assertEqual(type(c), type(d1))
781
782 #with other type, right
783 c = 7 % d1
784 self.assertEqual(c, Decimal('2'))
785 self.assertEqual(type(c), type(d1))
786
787 #inline with decimal
788 d1 %= d2
789 self.assertEqual(d1, Decimal('1'))
790
791 #inline with other type
792 d1 %= 4
793 self.assertEqual(d1, Decimal('1'))
794
795 def test_floor_div_module(self):
796
797 d1 = Decimal('5')
798 d2 = Decimal('2')
799
800 #two Decimals
801 (p, q) = divmod(d1, d2)
802 self.assertEqual(p, Decimal('2'))
803 self.assertEqual(q, Decimal('1'))
804 self.assertEqual(type(p), type(d1))
805 self.assertEqual(type(q), type(d1))
806
807 #with other type, left
808 (p, q) = divmod(d1, 4)
809 self.assertEqual(p, Decimal('1'))
810 self.assertEqual(q, Decimal('1'))
811 self.assertEqual(type(p), type(d1))
812 self.assertEqual(type(q), type(d1))
813
814 #with other type, right
815 (p, q) = divmod(7, d1)
816 self.assertEqual(p, Decimal('1'))
817 self.assertEqual(q, Decimal('2'))
818 self.assertEqual(type(p), type(d1))
819 self.assertEqual(type(q), type(d1))
820
821 def test_unary_operators(self):
822 self.assertEqual(+Decimal(45), Decimal(+45)) # +
823 self.assertEqual(-Decimal(45), Decimal(-45)) # -
824 self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs
825
826
827# The following are two functions used to test threading in the next class
828
829def thfunc1(cls):
830 d1 = Decimal(1)
831 d3 = Decimal(3)
832 cls.assertEqual(d1/d3, Decimal('0.333333333'))
833 cls.synchro.wait()
834 cls.assertEqual(d1/d3, Decimal('0.333333333'))
835 cls.finish1.set()
836 return
837
838def thfunc2(cls):
839 d1 = Decimal(1)
840 d3 = Decimal(3)
841 cls.assertEqual(d1/d3, Decimal('0.333333333'))
842 thiscontext = getcontext()
843 thiscontext.prec = 18
844 cls.assertEqual(d1/d3, Decimal('0.333333333333333333'))
845 cls.synchro.set()
846 cls.finish2.set()
847 return
848
849
850class DecimalUseOfContextTest(unittest.TestCase):
851 '''Unit tests for Use of Context cases in Decimal.'''
852
Raymond Hettinger7e71fa52004-12-18 19:07:19 +0000853 try:
854 import threading
855 except ImportError:
856 threading = None
857
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000858 # Take care executing this test from IDLE, there's an issue in threading
859 # that hangs IDLE and I couldn't find it
860
861 def test_threading(self):
862 #Test the "threading isolation" of a Context.
863
864 self.synchro = threading.Event()
865 self.finish1 = threading.Event()
866 self.finish2 = threading.Event()
867
868 th1 = threading.Thread(target=thfunc1, args=(self,))
869 th2 = threading.Thread(target=thfunc2, args=(self,))
870
871 th1.start()
872 th2.start()
873
874 self.finish1.wait()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000875 self.finish2.wait()
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000876 return
877
Raymond Hettinger7e71fa52004-12-18 19:07:19 +0000878 if threading is None:
879 del test_threading
880
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000881
882class DecimalUsabilityTest(unittest.TestCase):
883 '''Unit tests for Usability cases of Decimal.'''
884
885 def test_comparison_operators(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000886
887 da = Decimal('23.42')
888 db = Decimal('23.42')
889 dc = Decimal('45')
890
891 #two Decimals
892 self.failUnless(dc > da)
893 self.failUnless(dc >= da)
894 self.failUnless(da < dc)
895 self.failUnless(da <= dc)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000896 self.assertEqual(da, db)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000897 self.failUnless(da != dc)
898 self.failUnless(da <= db)
899 self.failUnless(da >= db)
900 self.assertEqual(cmp(dc,da), 1)
901 self.assertEqual(cmp(da,dc), -1)
902 self.assertEqual(cmp(da,db), 0)
903
904 #a Decimal and an int
905 self.failUnless(dc > 23)
906 self.failUnless(23 < dc)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000907 self.assertEqual(dc, 45)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000908 self.assertEqual(cmp(dc,23), 1)
909 self.assertEqual(cmp(23,dc), -1)
910 self.assertEqual(cmp(dc,45), 0)
911
912 #a Decimal and uncomparable
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000913 self.assertNotEqual(da, 'ugly')
914 self.assertNotEqual(da, 32.7)
915 self.assertNotEqual(da, object())
916 self.assertNotEqual(da, object)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000917
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000918 # sortable
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000919 a = list(map(Decimal, range(100)))
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000920 b = a[:]
921 random.shuffle(a)
922 a.sort()
923 self.assertEqual(a, b)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000924
925 def test_copy_and_deepcopy_methods(self):
926 d = Decimal('43.24')
927 c = copy.copy(d)
928 self.assertEqual(id(c), id(d))
929 dc = copy.deepcopy(d)
930 self.assertEqual(id(dc), id(d))
931
932 def test_hash_method(self):
933 #just that it's hashable
934 hash(Decimal(23))
Thomas Wouters8ce81f72007-09-20 18:22:40 +0000935
936 test_values = [Decimal(sign*(2**m + n))
937 for m in [0, 14, 15, 16, 17, 30, 31,
938 32, 33, 62, 63, 64, 65, 66]
939 for n in range(-10, 10)
940 for sign in [-1, 1]]
941 test_values.extend([
942 Decimal("-0"), # zeros
943 Decimal("0.00"),
944 Decimal("-0.000"),
945 Decimal("0E10"),
946 Decimal("-0E12"),
947 Decimal("10.0"), # negative exponent
948 Decimal("-23.00000"),
949 Decimal("1230E100"), # positive exponent
950 Decimal("-4.5678E50"),
951 # a value for which hash(n) != hash(n % (2**64-1))
952 # in Python pre-2.6
953 Decimal(2**64 + 2**32 - 1),
954 # selection of values which fail with the old (before
955 # version 2.6) long.__hash__
956 Decimal("1.634E100"),
957 Decimal("90.697E100"),
958 Decimal("188.83E100"),
959 Decimal("1652.9E100"),
960 Decimal("56531E100"),
961 ])
962
963 # check that hash(d) == hash(int(d)) for integral values
964 for value in test_values:
965 self.assertEqual(hash(value), hash(int(value)))
966
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000967 #the same hash that to an int
968 self.assertEqual(hash(Decimal(23)), hash(23))
Raymond Hettingerbea3f6f2005-03-15 04:59:17 +0000969 self.assertRaises(TypeError, hash, Decimal('NaN'))
970 self.assert_(hash(Decimal('Inf')))
971 self.assert_(hash(Decimal('-Inf')))
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000972
973 def test_min_and_max_methods(self):
974
975 d1 = Decimal('15.32')
976 d2 = Decimal('28.5')
977 l1 = 15
978 l2 = 28
979
980 #between Decimals
981 self.failUnless(min(d1,d2) is d1)
982 self.failUnless(min(d2,d1) is d1)
983 self.failUnless(max(d1,d2) is d2)
984 self.failUnless(max(d2,d1) is d2)
985
986 #between Decimal and long
987 self.failUnless(min(d1,l2) is d1)
988 self.failUnless(min(l2,d1) is d1)
989 self.failUnless(max(l1,d2) is d2)
990 self.failUnless(max(d2,l1) is d2)
991
992 def test_as_nonzero(self):
993 #as false
994 self.failIf(Decimal(0))
995 #as true
996 self.failUnless(Decimal('0.372'))
997
998 def test_tostring_methods(self):
999 #Test str and repr methods.
1000
1001 d = Decimal('15.32')
1002 self.assertEqual(str(d), '15.32') # str
1003 self.assertEqual(repr(d), 'Decimal("15.32")') # repr
1004
1005 def test_tonum_methods(self):
1006 #Test float, int and long methods.
1007
1008 d1 = Decimal('66')
1009 d2 = Decimal('15.32')
1010
1011 #int
1012 self.assertEqual(int(d1), 66)
1013 self.assertEqual(int(d2), 15)
1014
1015 #long
Guido van Rossume2a383d2007-01-15 16:59:06 +00001016 self.assertEqual(int(d1), 66)
1017 self.assertEqual(int(d2), 15)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001018
1019 #float
1020 self.assertEqual(float(d1), 66)
1021 self.assertEqual(float(d2), 15.32)
1022
1023 def test_eval_round_trip(self):
1024
1025 #with zero
1026 d = Decimal( (0, (0,), 0) )
1027 self.assertEqual(d, eval(repr(d)))
1028
1029 #int
1030 d = Decimal( (1, (4, 5), 0) )
1031 self.assertEqual(d, eval(repr(d)))
1032
1033 #float
1034 d = Decimal( (0, (4, 5, 3, 4), -2) )
1035 self.assertEqual(d, eval(repr(d)))
1036
1037 #weird
1038 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
1039 self.assertEqual(d, eval(repr(d)))
1040
1041 def test_as_tuple(self):
1042
1043 #with zero
1044 d = Decimal(0)
1045 self.assertEqual(d.as_tuple(), (0, (0,), 0) )
1046
1047 #int
1048 d = Decimal(-45)
1049 self.assertEqual(d.as_tuple(), (1, (4, 5), 0) )
1050
1051 #complicated string
1052 d = Decimal("-4.34913534E-17")
1053 self.assertEqual(d.as_tuple(), (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
1054
1055 #inf
1056 d = Decimal("Infinity")
1057 self.assertEqual(d.as_tuple(), (0, (0,), 'F') )
1058
Guido van Rossum8ce8a782007-11-01 19:42:39 +00001059 #leading zeros in coefficient should be stripped
1060 d = Decimal( (0, (0, 0, 4, 0, 5, 3, 4), -2) )
1061 self.assertEqual(d.as_tuple(), (0, (4, 0, 5, 3, 4), -2) )
1062 d = Decimal( (1, (0, 0, 0), 37) )
1063 self.assertEqual(d.as_tuple(), (1, (0,), 37))
1064 d = Decimal( (1, (), 37) )
1065 self.assertEqual(d.as_tuple(), (1, (0,), 37))
1066
1067 #leading zeros in NaN diagnostic info should be stripped
1068 d = Decimal( (0, (0, 0, 4, 0, 5, 3, 4), 'n') )
1069 self.assertEqual(d.as_tuple(), (0, (4, 0, 5, 3, 4), 'n') )
1070 d = Decimal( (1, (0, 0, 0), 'N') )
1071 self.assertEqual(d.as_tuple(), (1, (), 'N') )
1072 d = Decimal( (1, (), 'n') )
1073 self.assertEqual(d.as_tuple(), (1, (), 'n') )
1074
1075 #coefficient in infinity should be ignored
1076 d = Decimal( (0, (4, 5, 3, 4), 'F') )
1077 self.assertEqual(d.as_tuple(), (0, (0,), 'F'))
1078 d = Decimal( (1, (0, 2, 7, 1), 'F') )
1079 self.assertEqual(d.as_tuple(), (1, (0,), 'F'))
1080
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001081 def test_immutability_operations(self):
1082 # Do operations and check that it didn't change change internal objects.
1083
1084 d1 = Decimal('-25e55')
1085 b1 = Decimal('-25e55')
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001086 d2 = Decimal('33e+33')
1087 b2 = Decimal('33e+33')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001088
1089 def checkSameDec(operation, useOther=False):
1090 if useOther:
1091 eval("d1." + operation + "(d2)")
1092 self.assertEqual(d1._sign, b1._sign)
1093 self.assertEqual(d1._int, b1._int)
1094 self.assertEqual(d1._exp, b1._exp)
1095 self.assertEqual(d2._sign, b2._sign)
1096 self.assertEqual(d2._int, b2._int)
1097 self.assertEqual(d2._exp, b2._exp)
1098 else:
1099 eval("d1." + operation + "()")
1100 self.assertEqual(d1._sign, b1._sign)
1101 self.assertEqual(d1._int, b1._int)
1102 self.assertEqual(d1._exp, b1._exp)
1103 return
1104
1105 Decimal(d1)
1106 self.assertEqual(d1._sign, b1._sign)
1107 self.assertEqual(d1._int, b1._int)
1108 self.assertEqual(d1._exp, b1._exp)
1109
1110 checkSameDec("__abs__")
1111 checkSameDec("__add__", True)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001112 checkSameDec("__divmod__", True)
1113 checkSameDec("__cmp__", True)
1114 checkSameDec("__float__")
1115 checkSameDec("__floordiv__", True)
1116 checkSameDec("__hash__")
1117 checkSameDec("__int__")
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001118 checkSameDec("__mod__", True)
1119 checkSameDec("__mul__", True)
1120 checkSameDec("__neg__")
Jack Diederich4dafcc42006-11-28 19:15:13 +00001121 checkSameDec("__bool__")
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001122 checkSameDec("__pos__")
1123 checkSameDec("__pow__", True)
1124 checkSameDec("__radd__", True)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001125 checkSameDec("__rdivmod__", True)
1126 checkSameDec("__repr__")
1127 checkSameDec("__rfloordiv__", True)
1128 checkSameDec("__rmod__", True)
1129 checkSameDec("__rmul__", True)
1130 checkSameDec("__rpow__", True)
1131 checkSameDec("__rsub__", True)
1132 checkSameDec("__str__")
1133 checkSameDec("__sub__", True)
1134 checkSameDec("__truediv__", True)
1135 checkSameDec("adjusted")
1136 checkSameDec("as_tuple")
1137 checkSameDec("compare", True)
1138 checkSameDec("max", True)
1139 checkSameDec("min", True)
1140 checkSameDec("normalize")
1141 checkSameDec("quantize", True)
1142 checkSameDec("remainder_near", True)
1143 checkSameDec("same_quantum", True)
1144 checkSameDec("sqrt")
1145 checkSameDec("to_eng_string")
1146 checkSameDec("to_integral")
1147
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001148 def test_subclassing(self):
1149 # Different behaviours when subclassing Decimal
1150
1151 class MyDecimal(Decimal):
1152 pass
1153
1154 d1 = MyDecimal(1)
1155 d2 = MyDecimal(2)
1156 d = d1 + d2
1157 self.assertTrue(type(d) is Decimal)
1158
1159 d = d1.max(d2)
1160 self.assertTrue(type(d) is Decimal)
1161
1162
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001163class DecimalPythonAPItests(unittest.TestCase):
1164
1165 def test_pickle(self):
1166 d = Decimal('-3.141590000')
1167 p = pickle.dumps(d)
1168 e = pickle.loads(p)
1169 self.assertEqual(d, e)
1170
Raymond Hettinger5548be22004-07-05 18:49:38 +00001171 def test_int(self):
Raymond Hettinger605ed022004-11-24 07:28:48 +00001172 for x in range(-250, 250):
1173 s = '%0.2f' % (x / 100.0)
Raymond Hettinger5548be22004-07-05 18:49:38 +00001174 # should work the same as for floats
1175 self.assertEqual(int(Decimal(s)), int(float(s)))
Raymond Hettinger605ed022004-11-24 07:28:48 +00001176 # should work the same as to_integral in the ROUND_DOWN mode
Raymond Hettinger5548be22004-07-05 18:49:38 +00001177 d = Decimal(s)
Raymond Hettinger605ed022004-11-24 07:28:48 +00001178 r = d.to_integral(ROUND_DOWN)
Raymond Hettinger5548be22004-07-05 18:49:38 +00001179 self.assertEqual(Decimal(int(d)), r)
1180
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +00001181class ContextAPItests(unittest.TestCase):
1182
1183 def test_pickle(self):
1184 c = Context()
1185 e = pickle.loads(pickle.dumps(c))
1186 for k in vars(c):
1187 v1 = vars(c)[k]
1188 v2 = vars(e)[k]
1189 self.assertEqual(v1, v2)
1190
Raymond Hettinger0aeac102004-07-05 22:53:03 +00001191 def test_equality_with_other_types(self):
1192 self.assert_(Decimal(10) in ['a', 1.0, Decimal(10), (1,2), {}])
1193 self.assert_(Decimal(10) not in ['a', 1.0, (1,2), {}])
1194
Raymond Hettinger955d2b22004-08-08 20:17:45 +00001195 def test_copy(self):
1196 # All copies should be deep
1197 c = Context()
1198 d = c.copy()
1199 self.assertNotEqual(id(c), id(d))
1200 self.assertNotEqual(id(c.flags), id(d.flags))
1201 self.assertNotEqual(id(c.traps), id(d.traps))
1202
Thomas Wouters89f507f2006-12-13 04:49:30 +00001203class WithStatementTest(unittest.TestCase):
1204 # Can't do these as docstrings until Python 2.6
1205 # as doctest can't handle __future__ statements
1206
1207 def test_localcontext(self):
1208 # Use a copy of the current context in the block
1209 orig_ctx = getcontext()
1210 with localcontext() as enter_ctx:
1211 set_ctx = getcontext()
1212 final_ctx = getcontext()
1213 self.assert_(orig_ctx is final_ctx, 'did not restore context correctly')
1214 self.assert_(orig_ctx is not set_ctx, 'did not copy the context')
1215 self.assert_(set_ctx is enter_ctx, '__enter__ returned wrong context')
1216
1217 def test_localcontextarg(self):
1218 # Use a copy of the supplied context in the block
1219 orig_ctx = getcontext()
1220 new_ctx = Context(prec=42)
1221 with localcontext(new_ctx) as enter_ctx:
1222 set_ctx = getcontext()
1223 final_ctx = getcontext()
1224 self.assert_(orig_ctx is final_ctx, 'did not restore context correctly')
1225 self.assert_(set_ctx.prec == new_ctx.prec, 'did not set correct context')
1226 self.assert_(new_ctx is not set_ctx, 'did not copy the context')
1227 self.assert_(set_ctx is enter_ctx, '__enter__ returned wrong context')
1228
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001229class ContextFlags(unittest.TestCase):
1230 def test_flags_irrelevant(self):
1231 # check that the result (numeric result + flags raised) of an
1232 # arithmetic operation doesn't depend on the current flags
1233
1234 context = Context(prec=9, Emin = -999999999, Emax = 999999999,
1235 rounding=ROUND_HALF_EVEN, traps=[], flags=[])
1236
1237 # operations that raise various flags, in the form (function, arglist)
1238 operations = [
1239 (context._apply, [Decimal("100E-1000000009")]),
1240 (context.sqrt, [Decimal(2)]),
1241 (context.add, [Decimal("1.23456789"), Decimal("9.87654321")]),
1242 (context.multiply, [Decimal("1.23456789"), Decimal("9.87654321")]),
1243 (context.subtract, [Decimal("1.23456789"), Decimal("9.87654321")]),
1244 ]
1245
1246 # try various flags individually, then a whole lot at once
1247 flagsets = [[Inexact], [Rounded], [Underflow], [Clamped], [Subnormal],
1248 [Inexact, Rounded, Underflow, Clamped, Subnormal]]
1249
1250 for fn, args in operations:
1251 # find answer and flags raised using a clean context
1252 context.clear_flags()
1253 ans = fn(*args)
1254 flags = [k for k, v in context.flags.items() if v]
1255
1256 for extra_flags in flagsets:
1257 # set flags, before calling operation
1258 context.clear_flags()
1259 for flag in extra_flags:
1260 context._raise_error(flag)
1261 new_ans = fn(*args)
1262
1263 # flags that we expect to be set after the operation
1264 expected_flags = list(flags)
1265 for flag in extra_flags:
1266 if flag not in expected_flags:
1267 expected_flags.append(flag)
1268 expected_flags.sort(key=id)
1269
1270 # flags we actually got
1271 new_flags = [k for k,v in context.flags.items() if v]
1272 new_flags.sort(key=id)
1273
1274 self.assertEqual(ans, new_ans,
1275 "operation produces different answers depending on flags set: " +
1276 "expected %s, got %s." % (ans, new_ans))
1277 self.assertEqual(new_flags, expected_flags,
1278 "operation raises different flags depending on flags set: " +
1279 "expected %s, got %s" % (expected_flags, new_flags))
1280
1281def test_main(arith=False, verbose=None, todo_tests=None, debug=None):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001282 """ Execute the tests.
1283
Raymond Hettingered20ad82004-09-04 20:09:13 +00001284 Runs all arithmetic tests if arith is True or if the "decimal" resource
1285 is enabled in regrtest.py
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001286 """
Raymond Hettingered20ad82004-09-04 20:09:13 +00001287
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001288 init()
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001289 global TEST_ALL, DEBUG
Raymond Hettingered20ad82004-09-04 20:09:13 +00001290 TEST_ALL = arith or is_resource_enabled('decimal')
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001291 DEBUG = debug
Raymond Hettingered20ad82004-09-04 20:09:13 +00001292
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001293 if todo_tests is None:
1294 test_classes = [
1295 DecimalExplicitConstructionTest,
1296 DecimalImplicitConstructionTest,
1297 DecimalArithmeticOperatorsTest,
1298 DecimalUseOfContextTest,
1299 DecimalUsabilityTest,
1300 DecimalPythonAPItests,
1301 ContextAPItests,
1302 DecimalTest,
1303 WithStatementTest,
1304 ContextFlags
1305 ]
1306 else:
1307 test_classes = [DecimalTest]
1308
1309 # Dynamically build custom test definition for each file in the test
1310 # directory and add the definitions to the DecimalTest class. This
1311 # procedure insures that new files do not get skipped.
1312 for filename in os.listdir(directory):
1313 if '.decTest' not in filename or filename.startswith("."):
1314 continue
1315 head, tail = filename.split('.')
1316 if todo_tests is not None and head not in todo_tests:
1317 continue
1318 tester = lambda self, f=filename: self.eval_file(directory + f)
1319 setattr(DecimalTest, 'test_' + head, tester)
1320 del filename, head, tail, tester
1321
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001322
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001323 try:
1324 run_unittest(*test_classes)
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001325 if todo_tests is None:
1326 import decimal as DecimalModule
1327 run_doctest(DecimalModule, verbose)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001328 finally:
1329 setcontext(ORIGINAL_CONTEXT)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001330
1331if __name__ == '__main__':
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001332 import optparse
1333 p = optparse.OptionParser("test_decimal.py [--debug] [{--skip | test1 [test2 [...]]}]")
1334 p.add_option('--debug', '-d', action='store_true', help='shows the test number and context before each test')
1335 p.add_option('--skip', '-s', action='store_true', help='skip over 90% of the arithmetic tests')
1336 (opt, args) = p.parse_args()
1337
1338 if opt.skip:
1339 test_main(arith=False, verbose=True)
1340 elif args:
1341 test_main(arith=True, verbose=True, todo_tests=args, debug=opt.debug)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001342 else:
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001343 test_main(arith=True, verbose=True)