blob: 40c44661e0646d2d8d09c9b1a131b620cfd03310 [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"""
26
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000027import unittest
28import glob
29import os, sys
30import pickle, copy
31from decimal import *
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000032from test.test_support import (TestSkipped, run_unittest, run_doctest,
33 is_resource_enabled)
Raymond Hettinger0aeac102004-07-05 22:53:03 +000034import random
Raymond Hettinger7e71fa52004-12-18 19:07:19 +000035try:
36 import threading
37except ImportError:
38 threading = None
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000039
Raymond Hettingerfed52962004-07-14 15:41:57 +000040# Useful Test Constant
41Signals = getcontext().flags.keys()
42
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000043# Tests are built around these assumed context defaults.
44# test_main() restores the original context.
45def init():
46 global ORIGINAL_CONTEXT
47 ORIGINAL_CONTEXT = getcontext().copy()
48 DefaultContext.prec = 9
49 DefaultContext.rounding = ROUND_HALF_EVEN
50 DefaultContext.traps = dict.fromkeys(Signals, 0)
51 setcontext(DefaultContext)
Raymond Hettinger6ea48452004-07-03 12:26:21 +000052
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000053TESTDATADIR = 'decimaltestdata'
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +000054if __name__ == '__main__':
55 file = sys.argv[0]
56else:
57 file = __file__
58testdir = os.path.dirname(file) or os.curdir
Raymond Hettinger267b8682005-03-27 10:47:39 +000059directory = testdir + os.sep + TESTDATADIR + os.sep
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000060
Raymond Hettinger267b8682005-03-27 10:47:39 +000061skip_expected = not os.path.isdir(directory)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000062
63# Make sure it actually raises errors when not expected and caught in flags
64# Slower, since it runs some things several times.
65EXTENDEDERRORTEST = False
66
67
68#Map the test cases' error names to the actual errors
69
70ErrorNames = {'clamped' : Clamped,
Raymond Hettinger5aa478b2004-07-09 10:02:53 +000071 'conversion_syntax' : InvalidOperation,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000072 'division_by_zero' : DivisionByZero,
Raymond Hettinger5aa478b2004-07-09 10:02:53 +000073 'division_impossible' : InvalidOperation,
74 'division_undefined' : InvalidOperation,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000075 'inexact' : Inexact,
Raymond Hettinger5aa478b2004-07-09 10:02:53 +000076 'invalid_context' : InvalidOperation,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000077 'invalid_operation' : InvalidOperation,
78 'overflow' : Overflow,
79 'rounded' : Rounded,
80 'subnormal' : Subnormal,
81 'underflow' : Underflow}
82
83
84def Nonfunction(*args):
85 """Doesn't do anything."""
86 return None
87
88RoundingDict = {'ceiling' : ROUND_CEILING, #Maps test-case names to roundings.
89 'down' : ROUND_DOWN,
90 'floor' : ROUND_FLOOR,
91 'half_down' : ROUND_HALF_DOWN,
92 'half_even' : ROUND_HALF_EVEN,
93 'half_up' : ROUND_HALF_UP,
94 'up' : ROUND_UP}
95
96# Name adapter to be able to change the Decimal and Context
97# interface without changing the test files from Cowlishaw
98nameAdapter = {'toeng':'to_eng_string',
99 'tosci':'to_sci_string',
100 'samequantum':'same_quantum',
101 'tointegral':'to_integral',
102 'remaindernear':'remainder_near',
103 'divideint':'divide_int',
104 'squareroot':'sqrt',
105 'apply':'_apply',
106 }
107
108class DecimalTest(unittest.TestCase):
109 """Class which tests the Decimal class against the test cases.
110
111 Changed for unittest.
112 """
113 def setUp(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000114 self.context = Context()
Raymond Hettingerbf440692004-07-10 14:14:37 +0000115 for key in DefaultContext.traps.keys():
116 DefaultContext.traps[key] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000117 self.ignore_list = ['#']
118 # Basically, a # means return NaN InvalidOperation.
119 # Different from a sNaN in trim
120
121 self.ChangeDict = {'precision' : self.change_precision,
122 'rounding' : self.change_rounding_method,
123 'maxexponent' : self.change_max_exponent,
124 'minexponent' : self.change_min_exponent,
125 'clamp' : self.change_clamp}
126
127 def tearDown(self):
128 """Cleaning up enviroment."""
129 # leaving context in original state
Raymond Hettingerbf440692004-07-10 14:14:37 +0000130 for key in DefaultContext.traps.keys():
131 DefaultContext.traps[key] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000132 return
133
134 def eval_file(self, file):
135 global skip_expected
136 if skip_expected:
137 raise TestSkipped
138 return
Neal Norwitz70967602006-03-17 08:29:44 +0000139 for line in open(file):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000140 line = line.replace('\r\n', '').replace('\n', '')
Raymond Hettinger5aa478b2004-07-09 10:02:53 +0000141 #print line
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000142 try:
143 t = self.eval_line(line)
Raymond Hettingerd87ac8f2004-07-09 10:52:54 +0000144 except InvalidOperation:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000145 print 'Error in test cases:'
146 print line
147 continue
148 except DecimalException, exception:
149 #Exception raised where there shoudn't have been one.
150 self.fail('Exception "'+exception.__class__.__name__ + '" raised on line '+line)
151
152 return
153
154 def eval_line(self, s):
155 if s.find(' -> ') >= 0 and s[:2] != '--' and not s.startswith(' --'):
156 s = (s.split('->')[0] + '->' +
157 s.split('->')[1].split('--')[0]).strip()
158 else:
159 s = s.split('--')[0].strip()
160
161 for ignore in self.ignore_list:
162 if s.find(ignore) >= 0:
163 #print s.split()[0], 'NotImplemented--', ignore
164 return
165 if not s:
166 return
167 elif ':' in s:
168 return self.eval_directive(s)
169 else:
170 return self.eval_equation(s)
171
172 def eval_directive(self, s):
173 funct, value = map(lambda x: x.strip().lower(), s.split(':'))
174 if funct == 'rounding':
175 value = RoundingDict[value]
176 else:
177 try:
178 value = int(value)
179 except ValueError:
180 pass
181
182 funct = self.ChangeDict.get(funct, Nonfunction)
183 funct(value)
184
185 def eval_equation(self, s):
186 #global DEFAULT_PRECISION
187 #print DEFAULT_PRECISION
Raymond Hettingered20ad82004-09-04 20:09:13 +0000188
189 if not TEST_ALL and random.random() < 0.90:
190 return
191
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000192 try:
193 Sides = s.split('->')
194 L = Sides[0].strip().split()
195 id = L[0]
196# print id,
197 funct = L[1].lower()
198 valstemp = L[2:]
199 L = Sides[1].strip().split()
200 ans = L[0]
201 exceptions = L[1:]
202 except (TypeError, AttributeError, IndexError):
Raymond Hettingerd87ac8f2004-07-09 10:52:54 +0000203 raise InvalidOperation
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000204 def FixQuotes(val):
205 val = val.replace("''", 'SingleQuote').replace('""', 'DoubleQuote')
206 val = val.replace("'", '').replace('"', '')
207 val = val.replace('SingleQuote', "'").replace('DoubleQuote', '"')
208 return val
209 fname = nameAdapter.get(funct, funct)
210 if fname == 'rescale':
211 return
212 funct = getattr(self.context, fname)
213 vals = []
214 conglomerate = ''
215 quote = 0
216 theirexceptions = [ErrorNames[x.lower()] for x in exceptions]
217
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000218 for exception in Signals:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000219 self.context.traps[exception] = 1 #Catch these bugs...
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000220 for exception in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000221 self.context.traps[exception] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000222 for i, val in enumerate(valstemp):
223 if val.count("'") % 2 == 1:
224 quote = 1 - quote
225 if quote:
226 conglomerate = conglomerate + ' ' + val
227 continue
228 else:
229 val = conglomerate + val
230 conglomerate = ''
231 v = FixQuotes(val)
232 if fname in ('to_sci_string', 'to_eng_string'):
233 if EXTENDEDERRORTEST:
234 for error in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000235 self.context.traps[error] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000236 try:
237 funct(self.context.create_decimal(v))
238 except error:
239 pass
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000240 except Signals, e:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000241 self.fail("Raised %s in %s when %s disabled" % \
242 (e, s, error))
243 else:
244 self.fail("Did not raise %s in %s" % (error, s))
Raymond Hettingerbf440692004-07-10 14:14:37 +0000245 self.context.traps[error] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000246 v = self.context.create_decimal(v)
247 else:
248 v = Decimal(v)
249 vals.append(v)
250
251 ans = FixQuotes(ans)
252
253 if EXTENDEDERRORTEST and fname not in ('to_sci_string', 'to_eng_string'):
254 for error in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000255 self.context.traps[error] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000256 try:
257 funct(*vals)
258 except error:
259 pass
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000260 except Signals, e:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000261 self.fail("Raised %s in %s when %s disabled" % \
262 (e, s, error))
263 else:
264 self.fail("Did not raise %s in %s" % (error, s))
Raymond Hettingerbf440692004-07-10 14:14:37 +0000265 self.context.traps[error] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000266 try:
267 result = str(funct(*vals))
268 if fname == 'same_quantum':
269 result = str(int(eval(result))) # 'True', 'False' -> '1', '0'
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000270 except Signals, error:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000271 self.fail("Raised %s in %s" % (error, s))
272 except: #Catch any error long enough to state the test case.
273 print "ERROR:", s
274 raise
275
276 myexceptions = self.getexceptions()
Raymond Hettingerbf440692004-07-10 14:14:37 +0000277 self.context.clear_flags()
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000278
279 myexceptions.sort()
280 theirexceptions.sort()
281
282 self.assertEqual(result, ans,
283 'Incorrect answer for ' + s + ' -- got ' + result)
284 self.assertEqual(myexceptions, theirexceptions,
285 'Incorrect flags set in ' + s + ' -- got ' \
286 + str(myexceptions))
287 return
288
289 def getexceptions(self):
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000290 return [e for e in Signals if self.context.flags[e]]
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000291
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000292 def change_precision(self, prec):
293 self.context.prec = prec
294 def change_rounding_method(self, rounding):
295 self.context.rounding = rounding
296 def change_min_exponent(self, exp):
297 self.context.Emin = exp
298 def change_max_exponent(self, exp):
299 self.context.Emax = exp
300 def change_clamp(self, clamp):
301 self.context._clamp = clamp
302
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000303# Dynamically build custom test definition for each file in the test
304# directory and add the definitions to the DecimalTest class. This
305# procedure insures that new files do not get skipped.
Raymond Hettinger267b8682005-03-27 10:47:39 +0000306for filename in os.listdir(directory):
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000307 if '.decTest' not in filename:
308 continue
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000309 head, tail = filename.split('.')
Raymond Hettinger267b8682005-03-27 10:47:39 +0000310 tester = lambda self, f=filename: self.eval_file(directory + f)
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000311 setattr(DecimalTest, 'test_' + head, tester)
312 del filename, head, tail, tester
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000313
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000314
315
316# The following classes test the behaviour of Decimal according to PEP 327
317
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000318class DecimalExplicitConstructionTest(unittest.TestCase):
319 '''Unit tests for Explicit Construction cases of Decimal.'''
320
321 def test_explicit_empty(self):
322 self.assertEqual(Decimal(), Decimal("0"))
323
324 def test_explicit_from_None(self):
325 self.assertRaises(TypeError, Decimal, None)
326
327 def test_explicit_from_int(self):
328
329 #positive
330 d = Decimal(45)
331 self.assertEqual(str(d), '45')
332
333 #very large positive
334 d = Decimal(500000123)
335 self.assertEqual(str(d), '500000123')
336
337 #negative
338 d = Decimal(-45)
339 self.assertEqual(str(d), '-45')
340
341 #zero
342 d = Decimal(0)
343 self.assertEqual(str(d), '0')
344
345 def test_explicit_from_string(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000346
347 #empty
348 self.assertEqual(str(Decimal('')), 'NaN')
349
350 #int
351 self.assertEqual(str(Decimal('45')), '45')
352
353 #float
354 self.assertEqual(str(Decimal('45.34')), '45.34')
355
356 #engineer notation
357 self.assertEqual(str(Decimal('45e2')), '4.5E+3')
358
359 #just not a number
360 self.assertEqual(str(Decimal('ugly')), 'NaN')
361
362 def test_explicit_from_tuples(self):
363
364 #zero
365 d = Decimal( (0, (0,), 0) )
366 self.assertEqual(str(d), '0')
367
368 #int
369 d = Decimal( (1, (4, 5), 0) )
370 self.assertEqual(str(d), '-45')
371
372 #float
373 d = Decimal( (0, (4, 5, 3, 4), -2) )
374 self.assertEqual(str(d), '45.34')
375
376 #weird
377 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
378 self.assertEqual(str(d), '-4.34913534E-17')
379
380 #wrong number of items
381 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1)) )
382
383 #bad sign
384 self.assertRaises(ValueError, Decimal, (8, (4, 3, 4, 9, 1), 2) )
385
386 #bad exp
387 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 'wrong!') )
388
389 #bad coefficients
390 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) )
391 self.assertRaises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) )
392
393 def test_explicit_from_Decimal(self):
394
395 #positive
396 d = Decimal(45)
397 e = Decimal(d)
398 self.assertEqual(str(e), '45')
399 self.assertNotEqual(id(d), id(e))
400
401 #very large positive
402 d = Decimal(500000123)
403 e = Decimal(d)
404 self.assertEqual(str(e), '500000123')
405 self.assertNotEqual(id(d), id(e))
406
407 #negative
408 d = Decimal(-45)
409 e = Decimal(d)
410 self.assertEqual(str(e), '-45')
411 self.assertNotEqual(id(d), id(e))
412
413 #zero
414 d = Decimal(0)
415 e = Decimal(d)
416 self.assertEqual(str(e), '0')
417 self.assertNotEqual(id(d), id(e))
418
419 def test_explicit_context_create_decimal(self):
420
421 nc = copy.copy(getcontext())
422 nc.prec = 3
423
424 # empty
Raymond Hettingerfed52962004-07-14 15:41:57 +0000425 d = Decimal()
426 self.assertEqual(str(d), '0')
427 d = nc.create_decimal()
428 self.assertEqual(str(d), '0')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000429
430 # from None
431 self.assertRaises(TypeError, nc.create_decimal, None)
432
433 # from int
434 d = nc.create_decimal(456)
435 self.failUnless(isinstance(d, Decimal))
436 self.assertEqual(nc.create_decimal(45678),
437 nc.create_decimal('457E+2'))
438
439 # from string
440 d = Decimal('456789')
441 self.assertEqual(str(d), '456789')
442 d = nc.create_decimal('456789')
443 self.assertEqual(str(d), '4.57E+5')
444
445 # from tuples
446 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
447 self.assertEqual(str(d), '-4.34913534E-17')
448 d = nc.create_decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
449 self.assertEqual(str(d), '-4.35E-17')
450
451 # from Decimal
452 prevdec = Decimal(500000123)
453 d = Decimal(prevdec)
454 self.assertEqual(str(d), '500000123')
455 d = nc.create_decimal(prevdec)
456 self.assertEqual(str(d), '5.00E+8')
457
458
459class DecimalImplicitConstructionTest(unittest.TestCase):
460 '''Unit tests for Implicit Construction cases of Decimal.'''
461
462 def test_implicit_from_None(self):
463 self.assertRaises(TypeError, eval, 'Decimal(5) + None', globals())
464
465 def test_implicit_from_int(self):
466 #normal
467 self.assertEqual(str(Decimal(5) + 45), '50')
468 #exceeding precision
469 self.assertEqual(Decimal(5) + 123456789000, Decimal(123456789000))
470
471 def test_implicit_from_string(self):
472 self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', globals())
473
474 def test_implicit_from_float(self):
475 self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', globals())
476
477 def test_implicit_from_Decimal(self):
478 self.assertEqual(Decimal(5) + Decimal(45), Decimal(50))
479
Raymond Hettinger267b8682005-03-27 10:47:39 +0000480 def test_rop(self):
481 # Allow other classes to be trained to interact with Decimals
482 class E:
483 def __divmod__(self, other):
484 return 'divmod ' + str(other)
485 def __rdivmod__(self, other):
486 return str(other) + ' rdivmod'
487 def __lt__(self, other):
488 return 'lt ' + str(other)
489 def __gt__(self, other):
490 return 'gt ' + str(other)
491 def __le__(self, other):
492 return 'le ' + str(other)
493 def __ge__(self, other):
494 return 'ge ' + str(other)
495 def __eq__(self, other):
496 return 'eq ' + str(other)
497 def __ne__(self, other):
498 return 'ne ' + str(other)
499
500 self.assertEqual(divmod(E(), Decimal(10)), 'divmod 10')
501 self.assertEqual(divmod(Decimal(10), E()), '10 rdivmod')
502 self.assertEqual(eval('Decimal(10) < E()'), 'gt 10')
503 self.assertEqual(eval('Decimal(10) > E()'), 'lt 10')
504 self.assertEqual(eval('Decimal(10) <= E()'), 'ge 10')
505 self.assertEqual(eval('Decimal(10) >= E()'), 'le 10')
506 self.assertEqual(eval('Decimal(10) == E()'), 'eq 10')
507 self.assertEqual(eval('Decimal(10) != E()'), 'ne 10')
508
509 # insert operator methods and then exercise them
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000510 oplist = [
511 ('+', '__add__', '__radd__'),
512 ('-', '__sub__', '__rsub__'),
513 ('*', '__mul__', '__rmul__'),
Thomas Woutersdcc6d322006-04-21 11:30:52 +0000514 ('/', '__truediv__', '__rtruediv__'),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000515 ('%', '__mod__', '__rmod__'),
516 ('//', '__floordiv__', '__rfloordiv__'),
517 ('**', '__pow__', '__rpow__')
518 ]
Raymond Hettinger267b8682005-03-27 10:47:39 +0000519
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000520 for sym, lop, rop in oplist:
Raymond Hettinger267b8682005-03-27 10:47:39 +0000521 setattr(E, lop, lambda self, other: 'str' + lop + str(other))
522 setattr(E, rop, lambda self, other: str(other) + rop + 'str')
523 self.assertEqual(eval('E()' + sym + 'Decimal(10)'),
524 'str' + lop + '10')
525 self.assertEqual(eval('Decimal(10)' + sym + 'E()'),
526 '10' + rop + 'str')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000527
528class DecimalArithmeticOperatorsTest(unittest.TestCase):
529 '''Unit tests for all arithmetic operators, binary and unary.'''
530
531 def test_addition(self):
532
533 d1 = Decimal('-11.1')
534 d2 = Decimal('22.2')
535
536 #two Decimals
537 self.assertEqual(d1+d2, Decimal('11.1'))
538 self.assertEqual(d2+d1, Decimal('11.1'))
539
540 #with other type, left
541 c = d1 + 5
542 self.assertEqual(c, Decimal('-6.1'))
543 self.assertEqual(type(c), type(d1))
544
545 #with other type, right
546 c = 5 + d1
547 self.assertEqual(c, Decimal('-6.1'))
548 self.assertEqual(type(c), type(d1))
549
550 #inline with decimal
551 d1 += d2
552 self.assertEqual(d1, Decimal('11.1'))
553
554 #inline with other type
555 d1 += 5
556 self.assertEqual(d1, Decimal('16.1'))
557
558 def test_subtraction(self):
559
560 d1 = Decimal('-11.1')
561 d2 = Decimal('22.2')
562
563 #two Decimals
564 self.assertEqual(d1-d2, Decimal('-33.3'))
565 self.assertEqual(d2-d1, Decimal('33.3'))
566
567 #with other type, left
568 c = d1 - 5
569 self.assertEqual(c, Decimal('-16.1'))
570 self.assertEqual(type(c), type(d1))
571
572 #with other type, right
573 c = 5 - d1
574 self.assertEqual(c, Decimal('16.1'))
575 self.assertEqual(type(c), type(d1))
576
577 #inline with decimal
578 d1 -= d2
579 self.assertEqual(d1, Decimal('-33.3'))
580
581 #inline with other type
582 d1 -= 5
583 self.assertEqual(d1, Decimal('-38.3'))
584
585 def test_multiplication(self):
586
587 d1 = Decimal('-5')
588 d2 = Decimal('3')
589
590 #two Decimals
591 self.assertEqual(d1*d2, Decimal('-15'))
592 self.assertEqual(d2*d1, Decimal('-15'))
593
594 #with other type, left
595 c = d1 * 5
596 self.assertEqual(c, Decimal('-25'))
597 self.assertEqual(type(c), type(d1))
598
599 #with other type, right
600 c = 5 * d1
601 self.assertEqual(c, Decimal('-25'))
602 self.assertEqual(type(c), type(d1))
603
604 #inline with decimal
605 d1 *= d2
606 self.assertEqual(d1, Decimal('-15'))
607
608 #inline with other type
609 d1 *= 5
610 self.assertEqual(d1, Decimal('-75'))
611
612 def test_division(self):
613
614 d1 = Decimal('-5')
615 d2 = Decimal('2')
616
617 #two Decimals
618 self.assertEqual(d1/d2, Decimal('-2.5'))
619 self.assertEqual(d2/d1, Decimal('-0.4'))
620
621 #with other type, left
622 c = d1 / 4
623 self.assertEqual(c, Decimal('-1.25'))
624 self.assertEqual(type(c), type(d1))
625
626 #with other type, right
627 c = 4 / d1
628 self.assertEqual(c, Decimal('-0.8'))
629 self.assertEqual(type(c), type(d1))
630
631 #inline with decimal
632 d1 /= d2
633 self.assertEqual(d1, Decimal('-2.5'))
634
635 #inline with other type
636 d1 /= 4
637 self.assertEqual(d1, Decimal('-0.625'))
638
639 def test_floor_division(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000640
641 d1 = Decimal('5')
642 d2 = Decimal('2')
643
644 #two Decimals
645 self.assertEqual(d1//d2, Decimal('2'))
646 self.assertEqual(d2//d1, Decimal('0'))
647
648 #with other type, left
649 c = d1 // 4
650 self.assertEqual(c, Decimal('1'))
651 self.assertEqual(type(c), type(d1))
652
653 #with other type, right
654 c = 7 // d1
655 self.assertEqual(c, Decimal('1'))
656 self.assertEqual(type(c), type(d1))
657
658 #inline with decimal
659 d1 //= d2
660 self.assertEqual(d1, Decimal('2'))
661
662 #inline with other type
663 d1 //= 2
664 self.assertEqual(d1, Decimal('1'))
665
666 def test_powering(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000667
668 d1 = Decimal('5')
669 d2 = Decimal('2')
670
671 #two Decimals
672 self.assertEqual(d1**d2, Decimal('25'))
673 self.assertEqual(d2**d1, Decimal('32'))
674
675 #with other type, left
676 c = d1 ** 4
677 self.assertEqual(c, Decimal('625'))
678 self.assertEqual(type(c), type(d1))
679
680 #with other type, right
681 c = 7 ** d1
682 self.assertEqual(c, Decimal('16807'))
683 self.assertEqual(type(c), type(d1))
684
685 #inline with decimal
686 d1 **= d2
687 self.assertEqual(d1, Decimal('25'))
688
689 #inline with other type
690 d1 **= 4
691 self.assertEqual(d1, Decimal('390625'))
692
693 def test_module(self):
694
695 d1 = Decimal('5')
696 d2 = Decimal('2')
697
698 #two Decimals
699 self.assertEqual(d1%d2, Decimal('1'))
700 self.assertEqual(d2%d1, Decimal('2'))
701
702 #with other type, left
703 c = d1 % 4
704 self.assertEqual(c, Decimal('1'))
705 self.assertEqual(type(c), type(d1))
706
707 #with other type, right
708 c = 7 % d1
709 self.assertEqual(c, Decimal('2'))
710 self.assertEqual(type(c), type(d1))
711
712 #inline with decimal
713 d1 %= d2
714 self.assertEqual(d1, Decimal('1'))
715
716 #inline with other type
717 d1 %= 4
718 self.assertEqual(d1, Decimal('1'))
719
720 def test_floor_div_module(self):
721
722 d1 = Decimal('5')
723 d2 = Decimal('2')
724
725 #two Decimals
726 (p, q) = divmod(d1, d2)
727 self.assertEqual(p, Decimal('2'))
728 self.assertEqual(q, Decimal('1'))
729 self.assertEqual(type(p), type(d1))
730 self.assertEqual(type(q), type(d1))
731
732 #with other type, left
733 (p, q) = divmod(d1, 4)
734 self.assertEqual(p, Decimal('1'))
735 self.assertEqual(q, Decimal('1'))
736 self.assertEqual(type(p), type(d1))
737 self.assertEqual(type(q), type(d1))
738
739 #with other type, right
740 (p, q) = divmod(7, d1)
741 self.assertEqual(p, Decimal('1'))
742 self.assertEqual(q, Decimal('2'))
743 self.assertEqual(type(p), type(d1))
744 self.assertEqual(type(q), type(d1))
745
746 def test_unary_operators(self):
747 self.assertEqual(+Decimal(45), Decimal(+45)) # +
748 self.assertEqual(-Decimal(45), Decimal(-45)) # -
749 self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs
750
751
752# The following are two functions used to test threading in the next class
753
754def thfunc1(cls):
755 d1 = Decimal(1)
756 d3 = Decimal(3)
757 cls.assertEqual(d1/d3, Decimal('0.333333333'))
758 cls.synchro.wait()
759 cls.assertEqual(d1/d3, Decimal('0.333333333'))
760 cls.finish1.set()
761 return
762
763def thfunc2(cls):
764 d1 = Decimal(1)
765 d3 = Decimal(3)
766 cls.assertEqual(d1/d3, Decimal('0.333333333'))
767 thiscontext = getcontext()
768 thiscontext.prec = 18
769 cls.assertEqual(d1/d3, Decimal('0.333333333333333333'))
770 cls.synchro.set()
771 cls.finish2.set()
772 return
773
774
775class DecimalUseOfContextTest(unittest.TestCase):
776 '''Unit tests for Use of Context cases in Decimal.'''
777
Raymond Hettinger7e71fa52004-12-18 19:07:19 +0000778 try:
779 import threading
780 except ImportError:
781 threading = None
782
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000783 # Take care executing this test from IDLE, there's an issue in threading
784 # that hangs IDLE and I couldn't find it
785
786 def test_threading(self):
787 #Test the "threading isolation" of a Context.
788
789 self.synchro = threading.Event()
790 self.finish1 = threading.Event()
791 self.finish2 = threading.Event()
792
793 th1 = threading.Thread(target=thfunc1, args=(self,))
794 th2 = threading.Thread(target=thfunc2, args=(self,))
795
796 th1.start()
797 th2.start()
798
799 self.finish1.wait()
800 self.finish1.wait()
801 return
802
Raymond Hettinger7e71fa52004-12-18 19:07:19 +0000803 if threading is None:
804 del test_threading
805
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000806
807class DecimalUsabilityTest(unittest.TestCase):
808 '''Unit tests for Usability cases of Decimal.'''
809
810 def test_comparison_operators(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000811
812 da = Decimal('23.42')
813 db = Decimal('23.42')
814 dc = Decimal('45')
815
816 #two Decimals
817 self.failUnless(dc > da)
818 self.failUnless(dc >= da)
819 self.failUnless(da < dc)
820 self.failUnless(da <= dc)
821 self.failUnless(da == db)
822 self.failUnless(da != dc)
823 self.failUnless(da <= db)
824 self.failUnless(da >= db)
825 self.assertEqual(cmp(dc,da), 1)
826 self.assertEqual(cmp(da,dc), -1)
827 self.assertEqual(cmp(da,db), 0)
828
829 #a Decimal and an int
830 self.failUnless(dc > 23)
831 self.failUnless(23 < dc)
832 self.failUnless(dc == 45)
833 self.assertEqual(cmp(dc,23), 1)
834 self.assertEqual(cmp(23,dc), -1)
835 self.assertEqual(cmp(dc,45), 0)
836
837 #a Decimal and uncomparable
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000838 self.assertNotEqual(da, 'ugly')
839 self.assertNotEqual(da, 32.7)
840 self.assertNotEqual(da, object())
841 self.assertNotEqual(da, object)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000842
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000843 # sortable
844 a = map(Decimal, xrange(100))
845 b = a[:]
846 random.shuffle(a)
847 a.sort()
848 self.assertEqual(a, b)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000849
850 def test_copy_and_deepcopy_methods(self):
851 d = Decimal('43.24')
852 c = copy.copy(d)
853 self.assertEqual(id(c), id(d))
854 dc = copy.deepcopy(d)
855 self.assertEqual(id(dc), id(d))
856
857 def test_hash_method(self):
858 #just that it's hashable
859 hash(Decimal(23))
860 #the same hash that to an int
861 self.assertEqual(hash(Decimal(23)), hash(23))
Raymond Hettingerbea3f6f2005-03-15 04:59:17 +0000862 self.assertRaises(TypeError, hash, Decimal('NaN'))
863 self.assert_(hash(Decimal('Inf')))
864 self.assert_(hash(Decimal('-Inf')))
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000865
866 def test_min_and_max_methods(self):
867
868 d1 = Decimal('15.32')
869 d2 = Decimal('28.5')
870 l1 = 15
871 l2 = 28
872
873 #between Decimals
874 self.failUnless(min(d1,d2) is d1)
875 self.failUnless(min(d2,d1) is d1)
876 self.failUnless(max(d1,d2) is d2)
877 self.failUnless(max(d2,d1) is d2)
878
879 #between Decimal and long
880 self.failUnless(min(d1,l2) is d1)
881 self.failUnless(min(l2,d1) is d1)
882 self.failUnless(max(l1,d2) is d2)
883 self.failUnless(max(d2,l1) is d2)
884
885 def test_as_nonzero(self):
886 #as false
887 self.failIf(Decimal(0))
888 #as true
889 self.failUnless(Decimal('0.372'))
890
891 def test_tostring_methods(self):
892 #Test str and repr methods.
893
894 d = Decimal('15.32')
895 self.assertEqual(str(d), '15.32') # str
896 self.assertEqual(repr(d), 'Decimal("15.32")') # repr
897
898 def test_tonum_methods(self):
899 #Test float, int and long methods.
900
901 d1 = Decimal('66')
902 d2 = Decimal('15.32')
903
904 #int
905 self.assertEqual(int(d1), 66)
906 self.assertEqual(int(d2), 15)
907
908 #long
909 self.assertEqual(long(d1), 66)
910 self.assertEqual(long(d2), 15)
911
912 #float
913 self.assertEqual(float(d1), 66)
914 self.assertEqual(float(d2), 15.32)
915
916 def test_eval_round_trip(self):
917
918 #with zero
919 d = Decimal( (0, (0,), 0) )
920 self.assertEqual(d, eval(repr(d)))
921
922 #int
923 d = Decimal( (1, (4, 5), 0) )
924 self.assertEqual(d, eval(repr(d)))
925
926 #float
927 d = Decimal( (0, (4, 5, 3, 4), -2) )
928 self.assertEqual(d, eval(repr(d)))
929
930 #weird
931 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
932 self.assertEqual(d, eval(repr(d)))
933
934 def test_as_tuple(self):
935
936 #with zero
937 d = Decimal(0)
938 self.assertEqual(d.as_tuple(), (0, (0,), 0) )
939
940 #int
941 d = Decimal(-45)
942 self.assertEqual(d.as_tuple(), (1, (4, 5), 0) )
943
944 #complicated string
945 d = Decimal("-4.34913534E-17")
946 self.assertEqual(d.as_tuple(), (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
947
948 #inf
949 d = Decimal("Infinity")
950 self.assertEqual(d.as_tuple(), (0, (0,), 'F') )
951
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000952 def test_immutability_operations(self):
953 # Do operations and check that it didn't change change internal objects.
954
955 d1 = Decimal('-25e55')
956 b1 = Decimal('-25e55')
957 d2 = Decimal('33e-33')
958 b2 = Decimal('33e-33')
959
960 def checkSameDec(operation, useOther=False):
961 if useOther:
962 eval("d1." + operation + "(d2)")
963 self.assertEqual(d1._sign, b1._sign)
964 self.assertEqual(d1._int, b1._int)
965 self.assertEqual(d1._exp, b1._exp)
966 self.assertEqual(d2._sign, b2._sign)
967 self.assertEqual(d2._int, b2._int)
968 self.assertEqual(d2._exp, b2._exp)
969 else:
970 eval("d1." + operation + "()")
971 self.assertEqual(d1._sign, b1._sign)
972 self.assertEqual(d1._int, b1._int)
973 self.assertEqual(d1._exp, b1._exp)
974 return
975
976 Decimal(d1)
977 self.assertEqual(d1._sign, b1._sign)
978 self.assertEqual(d1._int, b1._int)
979 self.assertEqual(d1._exp, b1._exp)
980
981 checkSameDec("__abs__")
982 checkSameDec("__add__", True)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000983 checkSameDec("__divmod__", True)
984 checkSameDec("__cmp__", True)
985 checkSameDec("__float__")
986 checkSameDec("__floordiv__", True)
987 checkSameDec("__hash__")
988 checkSameDec("__int__")
989 checkSameDec("__long__")
990 checkSameDec("__mod__", True)
991 checkSameDec("__mul__", True)
992 checkSameDec("__neg__")
993 checkSameDec("__nonzero__")
994 checkSameDec("__pos__")
995 checkSameDec("__pow__", True)
996 checkSameDec("__radd__", True)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000997 checkSameDec("__rdivmod__", True)
998 checkSameDec("__repr__")
999 checkSameDec("__rfloordiv__", True)
1000 checkSameDec("__rmod__", True)
1001 checkSameDec("__rmul__", True)
1002 checkSameDec("__rpow__", True)
1003 checkSameDec("__rsub__", True)
1004 checkSameDec("__str__")
1005 checkSameDec("__sub__", True)
1006 checkSameDec("__truediv__", True)
1007 checkSameDec("adjusted")
1008 checkSameDec("as_tuple")
1009 checkSameDec("compare", True)
1010 checkSameDec("max", True)
1011 checkSameDec("min", True)
1012 checkSameDec("normalize")
1013 checkSameDec("quantize", True)
1014 checkSameDec("remainder_near", True)
1015 checkSameDec("same_quantum", True)
1016 checkSameDec("sqrt")
1017 checkSameDec("to_eng_string")
1018 checkSameDec("to_integral")
1019
1020class DecimalPythonAPItests(unittest.TestCase):
1021
1022 def test_pickle(self):
1023 d = Decimal('-3.141590000')
1024 p = pickle.dumps(d)
1025 e = pickle.loads(p)
1026 self.assertEqual(d, e)
1027
Raymond Hettinger5548be22004-07-05 18:49:38 +00001028 def test_int(self):
Raymond Hettinger605ed022004-11-24 07:28:48 +00001029 for x in range(-250, 250):
1030 s = '%0.2f' % (x / 100.0)
Raymond Hettinger5548be22004-07-05 18:49:38 +00001031 # should work the same as for floats
1032 self.assertEqual(int(Decimal(s)), int(float(s)))
Raymond Hettinger605ed022004-11-24 07:28:48 +00001033 # should work the same as to_integral in the ROUND_DOWN mode
Raymond Hettinger5548be22004-07-05 18:49:38 +00001034 d = Decimal(s)
Raymond Hettinger605ed022004-11-24 07:28:48 +00001035 r = d.to_integral(ROUND_DOWN)
Raymond Hettinger5548be22004-07-05 18:49:38 +00001036 self.assertEqual(Decimal(int(d)), r)
1037
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +00001038class ContextAPItests(unittest.TestCase):
1039
1040 def test_pickle(self):
1041 c = Context()
1042 e = pickle.loads(pickle.dumps(c))
1043 for k in vars(c):
1044 v1 = vars(c)[k]
1045 v2 = vars(e)[k]
1046 self.assertEqual(v1, v2)
1047
Raymond Hettinger0aeac102004-07-05 22:53:03 +00001048 def test_equality_with_other_types(self):
1049 self.assert_(Decimal(10) in ['a', 1.0, Decimal(10), (1,2), {}])
1050 self.assert_(Decimal(10) not in ['a', 1.0, (1,2), {}])
1051
Raymond Hettinger955d2b22004-08-08 20:17:45 +00001052 def test_copy(self):
1053 # All copies should be deep
1054 c = Context()
1055 d = c.copy()
1056 self.assertNotEqual(id(c), id(d))
1057 self.assertNotEqual(id(c.flags), id(d.flags))
1058 self.assertNotEqual(id(c.traps), id(d.traps))
1059
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001060def test_main(arith=False, verbose=None):
1061 """ Execute the tests.
1062
Raymond Hettingered20ad82004-09-04 20:09:13 +00001063 Runs all arithmetic tests if arith is True or if the "decimal" resource
1064 is enabled in regrtest.py
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001065 """
Raymond Hettingered20ad82004-09-04 20:09:13 +00001066
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001067 init()
Raymond Hettingered20ad82004-09-04 20:09:13 +00001068 global TEST_ALL
1069 TEST_ALL = arith or is_resource_enabled('decimal')
1070
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001071 test_classes = [
1072 DecimalExplicitConstructionTest,
1073 DecimalImplicitConstructionTest,
1074 DecimalArithmeticOperatorsTest,
1075 DecimalUseOfContextTest,
1076 DecimalUsabilityTest,
1077 DecimalPythonAPItests,
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +00001078 ContextAPItests,
Raymond Hettingered20ad82004-09-04 20:09:13 +00001079 DecimalTest,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001080 ]
1081
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001082 try:
1083 run_unittest(*test_classes)
1084 import decimal as DecimalModule
1085 run_doctest(DecimalModule, verbose)
1086 finally:
1087 setcontext(ORIGINAL_CONTEXT)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001088
1089if __name__ == '__main__':
1090 # Calling with no arguments runs all tests.
Raymond Hettingered20ad82004-09-04 20:09:13 +00001091 # Calling with "Skip" will skip over 90% of the arithmetic tests.
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001092 if len(sys.argv) == 1:
1093 test_main(arith=True, verbose=True)
1094 elif len(sys.argv) == 2:
1095 arith = sys.argv[1].lower() != 'skip'
1096 test_main(arith=arith, verbose=True)
1097 else:
1098 raise ValueError("test called with wrong arguments, use test_Decimal [Skip]")