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