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