blob: 34f034b850bc3c8262c945329c4a62168da1db46 [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
506 for sym, lop, rop in (
507 ('+', '__add__', '__radd__'),
508 ('-', '__sub__', '__rsub__'),
509 ('*', '__mul__', '__rmul__'),
510 ('/', '__div__', '__rdiv__'),
511 ('%', '__mod__', '__rmod__'),
512 ('//', '__floordiv__', '__rfloordiv__'),
513 ('**', '__pow__', '__rpow__'),
514 ):
515
516 setattr(E, lop, lambda self, other: 'str' + lop + str(other))
517 setattr(E, rop, lambda self, other: str(other) + rop + 'str')
518 self.assertEqual(eval('E()' + sym + 'Decimal(10)'),
519 'str' + lop + '10')
520 self.assertEqual(eval('Decimal(10)' + sym + 'E()'),
521 '10' + rop + 'str')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000522
523class DecimalArithmeticOperatorsTest(unittest.TestCase):
524 '''Unit tests for all arithmetic operators, binary and unary.'''
525
526 def test_addition(self):
527
528 d1 = Decimal('-11.1')
529 d2 = Decimal('22.2')
530
531 #two Decimals
532 self.assertEqual(d1+d2, Decimal('11.1'))
533 self.assertEqual(d2+d1, Decimal('11.1'))
534
535 #with other type, left
536 c = d1 + 5
537 self.assertEqual(c, Decimal('-6.1'))
538 self.assertEqual(type(c), type(d1))
539
540 #with other type, right
541 c = 5 + d1
542 self.assertEqual(c, Decimal('-6.1'))
543 self.assertEqual(type(c), type(d1))
544
545 #inline with decimal
546 d1 += d2
547 self.assertEqual(d1, Decimal('11.1'))
548
549 #inline with other type
550 d1 += 5
551 self.assertEqual(d1, Decimal('16.1'))
552
553 def test_subtraction(self):
554
555 d1 = Decimal('-11.1')
556 d2 = Decimal('22.2')
557
558 #two Decimals
559 self.assertEqual(d1-d2, Decimal('-33.3'))
560 self.assertEqual(d2-d1, Decimal('33.3'))
561
562 #with other type, left
563 c = d1 - 5
564 self.assertEqual(c, Decimal('-16.1'))
565 self.assertEqual(type(c), type(d1))
566
567 #with other type, right
568 c = 5 - d1
569 self.assertEqual(c, Decimal('16.1'))
570 self.assertEqual(type(c), type(d1))
571
572 #inline with decimal
573 d1 -= d2
574 self.assertEqual(d1, Decimal('-33.3'))
575
576 #inline with other type
577 d1 -= 5
578 self.assertEqual(d1, Decimal('-38.3'))
579
580 def test_multiplication(self):
581
582 d1 = Decimal('-5')
583 d2 = Decimal('3')
584
585 #two Decimals
586 self.assertEqual(d1*d2, Decimal('-15'))
587 self.assertEqual(d2*d1, Decimal('-15'))
588
589 #with other type, left
590 c = d1 * 5
591 self.assertEqual(c, Decimal('-25'))
592 self.assertEqual(type(c), type(d1))
593
594 #with other type, right
595 c = 5 * d1
596 self.assertEqual(c, Decimal('-25'))
597 self.assertEqual(type(c), type(d1))
598
599 #inline with decimal
600 d1 *= d2
601 self.assertEqual(d1, Decimal('-15'))
602
603 #inline with other type
604 d1 *= 5
605 self.assertEqual(d1, Decimal('-75'))
606
607 def test_division(self):
608
609 d1 = Decimal('-5')
610 d2 = Decimal('2')
611
612 #two Decimals
613 self.assertEqual(d1/d2, Decimal('-2.5'))
614 self.assertEqual(d2/d1, Decimal('-0.4'))
615
616 #with other type, left
617 c = d1 / 4
618 self.assertEqual(c, Decimal('-1.25'))
619 self.assertEqual(type(c), type(d1))
620
621 #with other type, right
622 c = 4 / d1
623 self.assertEqual(c, Decimal('-0.8'))
624 self.assertEqual(type(c), type(d1))
625
626 #inline with decimal
627 d1 /= d2
628 self.assertEqual(d1, Decimal('-2.5'))
629
630 #inline with other type
631 d1 /= 4
632 self.assertEqual(d1, Decimal('-0.625'))
633
634 def test_floor_division(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000635
636 d1 = Decimal('5')
637 d2 = Decimal('2')
638
639 #two Decimals
640 self.assertEqual(d1//d2, Decimal('2'))
641 self.assertEqual(d2//d1, Decimal('0'))
642
643 #with other type, left
644 c = d1 // 4
645 self.assertEqual(c, Decimal('1'))
646 self.assertEqual(type(c), type(d1))
647
648 #with other type, right
649 c = 7 // d1
650 self.assertEqual(c, Decimal('1'))
651 self.assertEqual(type(c), type(d1))
652
653 #inline with decimal
654 d1 //= d2
655 self.assertEqual(d1, Decimal('2'))
656
657 #inline with other type
658 d1 //= 2
659 self.assertEqual(d1, Decimal('1'))
660
661 def test_powering(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000662
663 d1 = Decimal('5')
664 d2 = Decimal('2')
665
666 #two Decimals
667 self.assertEqual(d1**d2, Decimal('25'))
668 self.assertEqual(d2**d1, Decimal('32'))
669
670 #with other type, left
671 c = d1 ** 4
672 self.assertEqual(c, Decimal('625'))
673 self.assertEqual(type(c), type(d1))
674
675 #with other type, right
676 c = 7 ** d1
677 self.assertEqual(c, Decimal('16807'))
678 self.assertEqual(type(c), type(d1))
679
680 #inline with decimal
681 d1 **= d2
682 self.assertEqual(d1, Decimal('25'))
683
684 #inline with other type
685 d1 **= 4
686 self.assertEqual(d1, Decimal('390625'))
687
688 def test_module(self):
689
690 d1 = Decimal('5')
691 d2 = Decimal('2')
692
693 #two Decimals
694 self.assertEqual(d1%d2, Decimal('1'))
695 self.assertEqual(d2%d1, Decimal('2'))
696
697 #with other type, left
698 c = d1 % 4
699 self.assertEqual(c, Decimal('1'))
700 self.assertEqual(type(c), type(d1))
701
702 #with other type, right
703 c = 7 % d1
704 self.assertEqual(c, Decimal('2'))
705 self.assertEqual(type(c), type(d1))
706
707 #inline with decimal
708 d1 %= d2
709 self.assertEqual(d1, Decimal('1'))
710
711 #inline with other type
712 d1 %= 4
713 self.assertEqual(d1, Decimal('1'))
714
715 def test_floor_div_module(self):
716
717 d1 = Decimal('5')
718 d2 = Decimal('2')
719
720 #two Decimals
721 (p, q) = divmod(d1, d2)
722 self.assertEqual(p, Decimal('2'))
723 self.assertEqual(q, Decimal('1'))
724 self.assertEqual(type(p), type(d1))
725 self.assertEqual(type(q), type(d1))
726
727 #with other type, left
728 (p, q) = divmod(d1, 4)
729 self.assertEqual(p, Decimal('1'))
730 self.assertEqual(q, Decimal('1'))
731 self.assertEqual(type(p), type(d1))
732 self.assertEqual(type(q), type(d1))
733
734 #with other type, right
735 (p, q) = divmod(7, d1)
736 self.assertEqual(p, Decimal('1'))
737 self.assertEqual(q, Decimal('2'))
738 self.assertEqual(type(p), type(d1))
739 self.assertEqual(type(q), type(d1))
740
741 def test_unary_operators(self):
742 self.assertEqual(+Decimal(45), Decimal(+45)) # +
743 self.assertEqual(-Decimal(45), Decimal(-45)) # -
744 self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs
745
746
747# The following are two functions used to test threading in the next class
748
749def thfunc1(cls):
750 d1 = Decimal(1)
751 d3 = Decimal(3)
752 cls.assertEqual(d1/d3, Decimal('0.333333333'))
753 cls.synchro.wait()
754 cls.assertEqual(d1/d3, Decimal('0.333333333'))
755 cls.finish1.set()
756 return
757
758def thfunc2(cls):
759 d1 = Decimal(1)
760 d3 = Decimal(3)
761 cls.assertEqual(d1/d3, Decimal('0.333333333'))
762 thiscontext = getcontext()
763 thiscontext.prec = 18
764 cls.assertEqual(d1/d3, Decimal('0.333333333333333333'))
765 cls.synchro.set()
766 cls.finish2.set()
767 return
768
769
770class DecimalUseOfContextTest(unittest.TestCase):
771 '''Unit tests for Use of Context cases in Decimal.'''
772
Raymond Hettinger7e71fa52004-12-18 19:07:19 +0000773 try:
774 import threading
775 except ImportError:
776 threading = None
777
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000778 # Take care executing this test from IDLE, there's an issue in threading
779 # that hangs IDLE and I couldn't find it
780
781 def test_threading(self):
782 #Test the "threading isolation" of a Context.
783
784 self.synchro = threading.Event()
785 self.finish1 = threading.Event()
786 self.finish2 = threading.Event()
787
788 th1 = threading.Thread(target=thfunc1, args=(self,))
789 th2 = threading.Thread(target=thfunc2, args=(self,))
790
791 th1.start()
792 th2.start()
793
794 self.finish1.wait()
795 self.finish1.wait()
796 return
797
Raymond Hettinger7e71fa52004-12-18 19:07:19 +0000798 if threading is None:
799 del test_threading
800
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000801
802class DecimalUsabilityTest(unittest.TestCase):
803 '''Unit tests for Usability cases of Decimal.'''
804
805 def test_comparison_operators(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000806
807 da = Decimal('23.42')
808 db = Decimal('23.42')
809 dc = Decimal('45')
810
811 #two Decimals
812 self.failUnless(dc > da)
813 self.failUnless(dc >= da)
814 self.failUnless(da < dc)
815 self.failUnless(da <= dc)
816 self.failUnless(da == db)
817 self.failUnless(da != dc)
818 self.failUnless(da <= db)
819 self.failUnless(da >= db)
820 self.assertEqual(cmp(dc,da), 1)
821 self.assertEqual(cmp(da,dc), -1)
822 self.assertEqual(cmp(da,db), 0)
823
824 #a Decimal and an int
825 self.failUnless(dc > 23)
826 self.failUnless(23 < dc)
827 self.failUnless(dc == 45)
828 self.assertEqual(cmp(dc,23), 1)
829 self.assertEqual(cmp(23,dc), -1)
830 self.assertEqual(cmp(dc,45), 0)
831
832 #a Decimal and uncomparable
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000833 self.assertNotEqual(da, 'ugly')
834 self.assertNotEqual(da, 32.7)
835 self.assertNotEqual(da, object())
836 self.assertNotEqual(da, object)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000837
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000838 # sortable
839 a = map(Decimal, xrange(100))
840 b = a[:]
841 random.shuffle(a)
842 a.sort()
843 self.assertEqual(a, b)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000844
845 def test_copy_and_deepcopy_methods(self):
846 d = Decimal('43.24')
847 c = copy.copy(d)
848 self.assertEqual(id(c), id(d))
849 dc = copy.deepcopy(d)
850 self.assertEqual(id(dc), id(d))
851
852 def test_hash_method(self):
853 #just that it's hashable
854 hash(Decimal(23))
855 #the same hash that to an int
856 self.assertEqual(hash(Decimal(23)), hash(23))
Raymond Hettingerbea3f6f2005-03-15 04:59:17 +0000857 self.assertRaises(TypeError, hash, Decimal('NaN'))
858 self.assert_(hash(Decimal('Inf')))
859 self.assert_(hash(Decimal('-Inf')))
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000860
861 def test_min_and_max_methods(self):
862
863 d1 = Decimal('15.32')
864 d2 = Decimal('28.5')
865 l1 = 15
866 l2 = 28
867
868 #between Decimals
869 self.failUnless(min(d1,d2) is d1)
870 self.failUnless(min(d2,d1) is d1)
871 self.failUnless(max(d1,d2) is d2)
872 self.failUnless(max(d2,d1) is d2)
873
874 #between Decimal and long
875 self.failUnless(min(d1,l2) is d1)
876 self.failUnless(min(l2,d1) is d1)
877 self.failUnless(max(l1,d2) is d2)
878 self.failUnless(max(d2,l1) is d2)
879
880 def test_as_nonzero(self):
881 #as false
882 self.failIf(Decimal(0))
883 #as true
884 self.failUnless(Decimal('0.372'))
885
886 def test_tostring_methods(self):
887 #Test str and repr methods.
888
889 d = Decimal('15.32')
890 self.assertEqual(str(d), '15.32') # str
891 self.assertEqual(repr(d), 'Decimal("15.32")') # repr
892
893 def test_tonum_methods(self):
894 #Test float, int and long methods.
895
896 d1 = Decimal('66')
897 d2 = Decimal('15.32')
898
899 #int
900 self.assertEqual(int(d1), 66)
901 self.assertEqual(int(d2), 15)
902
903 #long
904 self.assertEqual(long(d1), 66)
905 self.assertEqual(long(d2), 15)
906
907 #float
908 self.assertEqual(float(d1), 66)
909 self.assertEqual(float(d2), 15.32)
910
911 def test_eval_round_trip(self):
912
913 #with zero
914 d = Decimal( (0, (0,), 0) )
915 self.assertEqual(d, eval(repr(d)))
916
917 #int
918 d = Decimal( (1, (4, 5), 0) )
919 self.assertEqual(d, eval(repr(d)))
920
921 #float
922 d = Decimal( (0, (4, 5, 3, 4), -2) )
923 self.assertEqual(d, eval(repr(d)))
924
925 #weird
926 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
927 self.assertEqual(d, eval(repr(d)))
928
929 def test_as_tuple(self):
930
931 #with zero
932 d = Decimal(0)
933 self.assertEqual(d.as_tuple(), (0, (0,), 0) )
934
935 #int
936 d = Decimal(-45)
937 self.assertEqual(d.as_tuple(), (1, (4, 5), 0) )
938
939 #complicated string
940 d = Decimal("-4.34913534E-17")
941 self.assertEqual(d.as_tuple(), (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
942
943 #inf
944 d = Decimal("Infinity")
945 self.assertEqual(d.as_tuple(), (0, (0,), 'F') )
946
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000947 def test_immutability_operations(self):
948 # Do operations and check that it didn't change change internal objects.
949
950 d1 = Decimal('-25e55')
951 b1 = Decimal('-25e55')
952 d2 = Decimal('33e-33')
953 b2 = Decimal('33e-33')
954
955 def checkSameDec(operation, useOther=False):
956 if useOther:
957 eval("d1." + operation + "(d2)")
958 self.assertEqual(d1._sign, b1._sign)
959 self.assertEqual(d1._int, b1._int)
960 self.assertEqual(d1._exp, b1._exp)
961 self.assertEqual(d2._sign, b2._sign)
962 self.assertEqual(d2._int, b2._int)
963 self.assertEqual(d2._exp, b2._exp)
964 else:
965 eval("d1." + operation + "()")
966 self.assertEqual(d1._sign, b1._sign)
967 self.assertEqual(d1._int, b1._int)
968 self.assertEqual(d1._exp, b1._exp)
969 return
970
971 Decimal(d1)
972 self.assertEqual(d1._sign, b1._sign)
973 self.assertEqual(d1._int, b1._int)
974 self.assertEqual(d1._exp, b1._exp)
975
976 checkSameDec("__abs__")
977 checkSameDec("__add__", True)
978 checkSameDec("__div__", True)
979 checkSameDec("__divmod__", True)
980 checkSameDec("__cmp__", True)
981 checkSameDec("__float__")
982 checkSameDec("__floordiv__", True)
983 checkSameDec("__hash__")
984 checkSameDec("__int__")
985 checkSameDec("__long__")
986 checkSameDec("__mod__", True)
987 checkSameDec("__mul__", True)
988 checkSameDec("__neg__")
989 checkSameDec("__nonzero__")
990 checkSameDec("__pos__")
991 checkSameDec("__pow__", True)
992 checkSameDec("__radd__", True)
993 checkSameDec("__rdiv__", True)
994 checkSameDec("__rdivmod__", True)
995 checkSameDec("__repr__")
996 checkSameDec("__rfloordiv__", True)
997 checkSameDec("__rmod__", True)
998 checkSameDec("__rmul__", True)
999 checkSameDec("__rpow__", True)
1000 checkSameDec("__rsub__", True)
1001 checkSameDec("__str__")
1002 checkSameDec("__sub__", True)
1003 checkSameDec("__truediv__", True)
1004 checkSameDec("adjusted")
1005 checkSameDec("as_tuple")
1006 checkSameDec("compare", True)
1007 checkSameDec("max", True)
1008 checkSameDec("min", True)
1009 checkSameDec("normalize")
1010 checkSameDec("quantize", True)
1011 checkSameDec("remainder_near", True)
1012 checkSameDec("same_quantum", True)
1013 checkSameDec("sqrt")
1014 checkSameDec("to_eng_string")
1015 checkSameDec("to_integral")
1016
1017class DecimalPythonAPItests(unittest.TestCase):
1018
1019 def test_pickle(self):
1020 d = Decimal('-3.141590000')
1021 p = pickle.dumps(d)
1022 e = pickle.loads(p)
1023 self.assertEqual(d, e)
1024
Raymond Hettinger5548be22004-07-05 18:49:38 +00001025 def test_int(self):
Raymond Hettinger605ed022004-11-24 07:28:48 +00001026 for x in range(-250, 250):
1027 s = '%0.2f' % (x / 100.0)
Raymond Hettinger5548be22004-07-05 18:49:38 +00001028 # should work the same as for floats
1029 self.assertEqual(int(Decimal(s)), int(float(s)))
Raymond Hettinger605ed022004-11-24 07:28:48 +00001030 # should work the same as to_integral in the ROUND_DOWN mode
Raymond Hettinger5548be22004-07-05 18:49:38 +00001031 d = Decimal(s)
Raymond Hettinger605ed022004-11-24 07:28:48 +00001032 r = d.to_integral(ROUND_DOWN)
Raymond Hettinger5548be22004-07-05 18:49:38 +00001033 self.assertEqual(Decimal(int(d)), r)
1034
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +00001035class ContextAPItests(unittest.TestCase):
1036
1037 def test_pickle(self):
1038 c = Context()
1039 e = pickle.loads(pickle.dumps(c))
1040 for k in vars(c):
1041 v1 = vars(c)[k]
1042 v2 = vars(e)[k]
1043 self.assertEqual(v1, v2)
1044
Raymond Hettinger0aeac102004-07-05 22:53:03 +00001045 def test_equality_with_other_types(self):
1046 self.assert_(Decimal(10) in ['a', 1.0, Decimal(10), (1,2), {}])
1047 self.assert_(Decimal(10) not in ['a', 1.0, (1,2), {}])
1048
Raymond Hettinger955d2b22004-08-08 20:17:45 +00001049 def test_copy(self):
1050 # All copies should be deep
1051 c = Context()
1052 d = c.copy()
1053 self.assertNotEqual(id(c), id(d))
1054 self.assertNotEqual(id(c.flags), id(d.flags))
1055 self.assertNotEqual(id(c.traps), id(d.traps))
1056
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001057def test_main(arith=False, verbose=None):
1058 """ Execute the tests.
1059
Raymond Hettingered20ad82004-09-04 20:09:13 +00001060 Runs all arithmetic tests if arith is True or if the "decimal" resource
1061 is enabled in regrtest.py
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001062 """
Raymond Hettingered20ad82004-09-04 20:09:13 +00001063
1064 global TEST_ALL
1065 TEST_ALL = arith or is_resource_enabled('decimal')
1066
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001067 test_classes = [
1068 DecimalExplicitConstructionTest,
1069 DecimalImplicitConstructionTest,
1070 DecimalArithmeticOperatorsTest,
1071 DecimalUseOfContextTest,
1072 DecimalUsabilityTest,
1073 DecimalPythonAPItests,
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +00001074 ContextAPItests,
Raymond Hettingered20ad82004-09-04 20:09:13 +00001075 DecimalTest,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001076 ]
1077
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001078 run_unittest(*test_classes)
1079 import decimal as DecimalModule
1080 run_doctest(DecimalModule, verbose)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001081
1082
1083if __name__ == '__main__':
1084 # Calling with no arguments runs all tests.
Raymond Hettingered20ad82004-09-04 20:09:13 +00001085 # Calling with "Skip" will skip over 90% of the arithmetic tests.
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001086 if len(sys.argv) == 1:
1087 test_main(arith=True, verbose=True)
1088 elif len(sys.argv) == 2:
1089 arith = sys.argv[1].lower() != 'skip'
1090 test_main(arith=arith, verbose=True)
1091 else:
1092 raise ValueError("test called with wrong arguments, use test_Decimal [Skip]")