blob: c39a8e31b712ab5456e5dc33e55b8618a458548a [file] [log] [blame]
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001# Copyright (c) 2004 Python Software Foundation.
2# All rights reserved.
3
4# Written by Eric Price <eprice at tjhsst.edu>
5# and Facundo Batista <facundo at taniquetil.com.ar>
6# and Raymond Hettinger <python at rcn.com>
7# and Aahz (aahz at pobox.com)
8# and Tim Peters
9
10"""
11These are the test cases for the Decimal module.
12
13There are two groups of tests, Arithmetic and Behaviour. The former test
14the Decimal arithmetic using the tests provided by Mike Cowlishaw. The latter
15test the pythonic behaviour according to PEP 327.
16
17Cowlishaw's tests can be downloaded from:
18
19 www2.hursley.ibm.com/decimal/dectest.zip
20
21This test module can be called from command line with one parameter (Arithmetic
22or Behaviour) to test each part, or without parameter to test both parts. If
23you're working through IDLE, you can import this test module and call test_main()
24with the corresponding argument.
25"""
Thomas Wouters89f507f2006-12-13 04:49:30 +000026from __future__ import with_statement
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000027
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000028import unittest
29import glob
30import os, sys
31import pickle, copy
32from decimal import *
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000033from test.test_support import (TestSkipped, run_unittest, run_doctest,
34 is_resource_enabled)
Raymond Hettinger0aeac102004-07-05 22:53:03 +000035import random
Raymond Hettinger7e71fa52004-12-18 19:07:19 +000036try:
37 import threading
38except ImportError:
39 threading = None
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000040
Raymond Hettingerfed52962004-07-14 15:41:57 +000041# Useful Test Constant
42Signals = getcontext().flags.keys()
43
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000044# Tests are built around these assumed context defaults.
45# test_main() restores the original context.
46def init():
47 global ORIGINAL_CONTEXT
48 ORIGINAL_CONTEXT = getcontext().copy()
49 DefaultContext.prec = 9
50 DefaultContext.rounding = ROUND_HALF_EVEN
51 DefaultContext.traps = dict.fromkeys(Signals, 0)
52 setcontext(DefaultContext)
Raymond Hettinger6ea48452004-07-03 12:26:21 +000053
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000054TESTDATADIR = 'decimaltestdata'
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +000055if __name__ == '__main__':
56 file = sys.argv[0]
57else:
58 file = __file__
59testdir = os.path.dirname(file) or os.curdir
Raymond Hettinger267b8682005-03-27 10:47:39 +000060directory = testdir + os.sep + TESTDATADIR + os.sep
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000061
Raymond Hettinger267b8682005-03-27 10:47:39 +000062skip_expected = not os.path.isdir(directory)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000063
64# Make sure it actually raises errors when not expected and caught in flags
65# Slower, since it runs some things several times.
66EXTENDEDERRORTEST = False
67
68
69#Map the test cases' error names to the actual errors
70
71ErrorNames = {'clamped' : Clamped,
Raymond Hettinger5aa478b2004-07-09 10:02:53 +000072 'conversion_syntax' : InvalidOperation,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000073 'division_by_zero' : DivisionByZero,
Raymond Hettinger5aa478b2004-07-09 10:02:53 +000074 'division_impossible' : InvalidOperation,
75 'division_undefined' : InvalidOperation,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000076 'inexact' : Inexact,
Raymond Hettinger5aa478b2004-07-09 10:02:53 +000077 'invalid_context' : InvalidOperation,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000078 'invalid_operation' : InvalidOperation,
79 'overflow' : Overflow,
80 'rounded' : Rounded,
81 'subnormal' : Subnormal,
82 'underflow' : Underflow}
83
84
85def Nonfunction(*args):
86 """Doesn't do anything."""
87 return None
88
89RoundingDict = {'ceiling' : ROUND_CEILING, #Maps test-case names to roundings.
90 'down' : ROUND_DOWN,
91 'floor' : ROUND_FLOOR,
92 'half_down' : ROUND_HALF_DOWN,
93 'half_even' : ROUND_HALF_EVEN,
94 'half_up' : ROUND_HALF_UP,
95 'up' : ROUND_UP}
96
97# Name adapter to be able to change the Decimal and Context
98# interface without changing the test files from Cowlishaw
99nameAdapter = {'toeng':'to_eng_string',
100 'tosci':'to_sci_string',
101 'samequantum':'same_quantum',
102 'tointegral':'to_integral',
103 'remaindernear':'remainder_near',
104 'divideint':'divide_int',
105 'squareroot':'sqrt',
106 'apply':'_apply',
107 }
108
109class DecimalTest(unittest.TestCase):
110 """Class which tests the Decimal class against the test cases.
111
112 Changed for unittest.
113 """
114 def setUp(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000115 self.context = Context()
Raymond Hettingerbf440692004-07-10 14:14:37 +0000116 for key in DefaultContext.traps.keys():
117 DefaultContext.traps[key] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000118 self.ignore_list = ['#']
119 # Basically, a # means return NaN InvalidOperation.
120 # Different from a sNaN in trim
121
122 self.ChangeDict = {'precision' : self.change_precision,
123 'rounding' : self.change_rounding_method,
124 'maxexponent' : self.change_max_exponent,
125 'minexponent' : self.change_min_exponent,
126 'clamp' : self.change_clamp}
127
128 def tearDown(self):
129 """Cleaning up enviroment."""
130 # leaving context in original state
Raymond Hettingerbf440692004-07-10 14:14:37 +0000131 for key in DefaultContext.traps.keys():
132 DefaultContext.traps[key] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000133 return
134
135 def eval_file(self, file):
136 global skip_expected
137 if skip_expected:
138 raise TestSkipped
139 return
Neal Norwitz70967602006-03-17 08:29:44 +0000140 for line in open(file):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000141 line = line.replace('\r\n', '').replace('\n', '')
Raymond Hettinger5aa478b2004-07-09 10:02:53 +0000142 #print line
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000143 try:
144 t = self.eval_line(line)
Raymond Hettingerd87ac8f2004-07-09 10:52:54 +0000145 except InvalidOperation:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000146 print('Error in test cases:')
147 print(line)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000148 continue
Guido van Rossumb940e112007-01-10 16:19:56 +0000149 except DecimalException as exception:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000150 #Exception raised where there shoudn't have been one.
151 self.fail('Exception "'+exception.__class__.__name__ + '" raised on line '+line)
152
153 return
154
155 def eval_line(self, s):
156 if s.find(' -> ') >= 0 and s[:2] != '--' and not s.startswith(' --'):
157 s = (s.split('->')[0] + '->' +
158 s.split('->')[1].split('--')[0]).strip()
159 else:
160 s = s.split('--')[0].strip()
161
162 for ignore in self.ignore_list:
163 if s.find(ignore) >= 0:
164 #print s.split()[0], 'NotImplemented--', ignore
165 return
166 if not s:
167 return
168 elif ':' in s:
169 return self.eval_directive(s)
170 else:
171 return self.eval_equation(s)
172
173 def eval_directive(self, s):
174 funct, value = map(lambda x: x.strip().lower(), s.split(':'))
175 if funct == 'rounding':
176 value = RoundingDict[value]
177 else:
178 try:
179 value = int(value)
180 except ValueError:
181 pass
182
183 funct = self.ChangeDict.get(funct, Nonfunction)
184 funct(value)
185
186 def eval_equation(self, s):
187 #global DEFAULT_PRECISION
188 #print DEFAULT_PRECISION
Raymond Hettingered20ad82004-09-04 20:09:13 +0000189
190 if not TEST_ALL and random.random() < 0.90:
191 return
192
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000193 try:
194 Sides = s.split('->')
195 L = Sides[0].strip().split()
196 id = L[0]
197# print id,
198 funct = L[1].lower()
199 valstemp = L[2:]
200 L = Sides[1].strip().split()
201 ans = L[0]
202 exceptions = L[1:]
203 except (TypeError, AttributeError, IndexError):
Raymond Hettingerd87ac8f2004-07-09 10:52:54 +0000204 raise InvalidOperation
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000205 def FixQuotes(val):
206 val = val.replace("''", 'SingleQuote').replace('""', 'DoubleQuote')
207 val = val.replace("'", '').replace('"', '')
208 val = val.replace('SingleQuote', "'").replace('DoubleQuote', '"')
209 return val
210 fname = nameAdapter.get(funct, funct)
211 if fname == 'rescale':
212 return
213 funct = getattr(self.context, fname)
214 vals = []
215 conglomerate = ''
216 quote = 0
217 theirexceptions = [ErrorNames[x.lower()] for x in exceptions]
218
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000219 for exception in Signals:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000220 self.context.traps[exception] = 1 #Catch these bugs...
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000221 for exception in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000222 self.context.traps[exception] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000223 for i, val in enumerate(valstemp):
224 if val.count("'") % 2 == 1:
225 quote = 1 - quote
226 if quote:
227 conglomerate = conglomerate + ' ' + val
228 continue
229 else:
230 val = conglomerate + val
231 conglomerate = ''
232 v = FixQuotes(val)
233 if fname in ('to_sci_string', 'to_eng_string'):
234 if EXTENDEDERRORTEST:
235 for error in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000236 self.context.traps[error] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000237 try:
238 funct(self.context.create_decimal(v))
239 except error:
240 pass
Guido van Rossumb940e112007-01-10 16:19:56 +0000241 except Signals as e:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000242 self.fail("Raised %s in %s when %s disabled" % \
243 (e, s, error))
244 else:
245 self.fail("Did not raise %s in %s" % (error, s))
Raymond Hettingerbf440692004-07-10 14:14:37 +0000246 self.context.traps[error] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000247 v = self.context.create_decimal(v)
248 else:
249 v = Decimal(v)
250 vals.append(v)
251
252 ans = FixQuotes(ans)
253
254 if EXTENDEDERRORTEST and fname not in ('to_sci_string', 'to_eng_string'):
255 for error in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000256 self.context.traps[error] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000257 try:
258 funct(*vals)
259 except error:
260 pass
Guido van Rossumb940e112007-01-10 16:19:56 +0000261 except Signals as e:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000262 self.fail("Raised %s in %s when %s disabled" % \
263 (e, s, error))
264 else:
265 self.fail("Did not raise %s in %s" % (error, s))
Raymond Hettingerbf440692004-07-10 14:14:37 +0000266 self.context.traps[error] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000267 try:
268 result = str(funct(*vals))
269 if fname == 'same_quantum':
270 result = str(int(eval(result))) # 'True', 'False' -> '1', '0'
Guido van Rossumb940e112007-01-10 16:19:56 +0000271 except Signals as error:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000272 self.fail("Raised %s in %s" % (error, s))
273 except: #Catch any error long enough to state the test case.
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000274 print("ERROR:", s)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000275 raise
276
277 myexceptions = self.getexceptions()
Raymond Hettingerbf440692004-07-10 14:14:37 +0000278 self.context.clear_flags()
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000279
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000280 myexceptions.sort(key=repr)
281 theirexceptions.sort(key=repr)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000282
283 self.assertEqual(result, ans,
284 'Incorrect answer for ' + s + ' -- got ' + result)
285 self.assertEqual(myexceptions, theirexceptions,
286 'Incorrect flags set in ' + s + ' -- got ' \
287 + str(myexceptions))
288 return
289
290 def getexceptions(self):
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000291 return [e for e in Signals if self.context.flags[e]]
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000292
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000293 def change_precision(self, prec):
294 self.context.prec = prec
295 def change_rounding_method(self, rounding):
296 self.context.rounding = rounding
297 def change_min_exponent(self, exp):
298 self.context.Emin = exp
299 def change_max_exponent(self, exp):
300 self.context.Emax = exp
301 def change_clamp(self, clamp):
302 self.context._clamp = clamp
303
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000304# Dynamically build custom test definition for each file in the test
305# directory and add the definitions to the DecimalTest class. This
306# procedure insures that new files do not get skipped.
Raymond Hettinger267b8682005-03-27 10:47:39 +0000307for filename in os.listdir(directory):
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000308 if '.decTest' not in filename:
309 continue
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000310 head, tail = filename.split('.')
Raymond Hettinger267b8682005-03-27 10:47:39 +0000311 tester = lambda self, f=filename: self.eval_file(directory + f)
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000312 setattr(DecimalTest, 'test_' + head, tester)
313 del filename, head, tail, tester
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000314
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000315
316
317# The following classes test the behaviour of Decimal according to PEP 327
318
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000319class DecimalExplicitConstructionTest(unittest.TestCase):
320 '''Unit tests for Explicit Construction cases of Decimal.'''
321
322 def test_explicit_empty(self):
323 self.assertEqual(Decimal(), Decimal("0"))
324
325 def test_explicit_from_None(self):
326 self.assertRaises(TypeError, Decimal, None)
327
328 def test_explicit_from_int(self):
329
330 #positive
331 d = Decimal(45)
332 self.assertEqual(str(d), '45')
333
334 #very large positive
335 d = Decimal(500000123)
336 self.assertEqual(str(d), '500000123')
337
338 #negative
339 d = Decimal(-45)
340 self.assertEqual(str(d), '-45')
341
342 #zero
343 d = Decimal(0)
344 self.assertEqual(str(d), '0')
345
346 def test_explicit_from_string(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000347
348 #empty
349 self.assertEqual(str(Decimal('')), 'NaN')
350
351 #int
352 self.assertEqual(str(Decimal('45')), '45')
353
354 #float
355 self.assertEqual(str(Decimal('45.34')), '45.34')
356
357 #engineer notation
358 self.assertEqual(str(Decimal('45e2')), '4.5E+3')
359
360 #just not a number
361 self.assertEqual(str(Decimal('ugly')), 'NaN')
362
363 def test_explicit_from_tuples(self):
364
365 #zero
366 d = Decimal( (0, (0,), 0) )
367 self.assertEqual(str(d), '0')
368
369 #int
370 d = Decimal( (1, (4, 5), 0) )
371 self.assertEqual(str(d), '-45')
372
373 #float
374 d = Decimal( (0, (4, 5, 3, 4), -2) )
375 self.assertEqual(str(d), '45.34')
376
377 #weird
378 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
379 self.assertEqual(str(d), '-4.34913534E-17')
380
381 #wrong number of items
382 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1)) )
383
384 #bad sign
385 self.assertRaises(ValueError, Decimal, (8, (4, 3, 4, 9, 1), 2) )
386
387 #bad exp
388 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 'wrong!') )
389
390 #bad coefficients
391 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) )
392 self.assertRaises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) )
393
394 def test_explicit_from_Decimal(self):
395
396 #positive
397 d = Decimal(45)
398 e = Decimal(d)
399 self.assertEqual(str(e), '45')
400 self.assertNotEqual(id(d), id(e))
401
402 #very large positive
403 d = Decimal(500000123)
404 e = Decimal(d)
405 self.assertEqual(str(e), '500000123')
406 self.assertNotEqual(id(d), id(e))
407
408 #negative
409 d = Decimal(-45)
410 e = Decimal(d)
411 self.assertEqual(str(e), '-45')
412 self.assertNotEqual(id(d), id(e))
413
414 #zero
415 d = Decimal(0)
416 e = Decimal(d)
417 self.assertEqual(str(e), '0')
418 self.assertNotEqual(id(d), id(e))
419
420 def test_explicit_context_create_decimal(self):
421
422 nc = copy.copy(getcontext())
423 nc.prec = 3
424
425 # empty
Raymond Hettingerfed52962004-07-14 15:41:57 +0000426 d = Decimal()
427 self.assertEqual(str(d), '0')
428 d = nc.create_decimal()
429 self.assertEqual(str(d), '0')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000430
431 # from None
432 self.assertRaises(TypeError, nc.create_decimal, None)
433
434 # from int
435 d = nc.create_decimal(456)
436 self.failUnless(isinstance(d, Decimal))
437 self.assertEqual(nc.create_decimal(45678),
438 nc.create_decimal('457E+2'))
439
440 # from string
441 d = Decimal('456789')
442 self.assertEqual(str(d), '456789')
443 d = nc.create_decimal('456789')
444 self.assertEqual(str(d), '4.57E+5')
445
446 # from tuples
447 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
448 self.assertEqual(str(d), '-4.34913534E-17')
449 d = nc.create_decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
450 self.assertEqual(str(d), '-4.35E-17')
451
452 # from Decimal
453 prevdec = Decimal(500000123)
454 d = Decimal(prevdec)
455 self.assertEqual(str(d), '500000123')
456 d = nc.create_decimal(prevdec)
457 self.assertEqual(str(d), '5.00E+8')
458
459
460class DecimalImplicitConstructionTest(unittest.TestCase):
461 '''Unit tests for Implicit Construction cases of Decimal.'''
462
463 def test_implicit_from_None(self):
464 self.assertRaises(TypeError, eval, 'Decimal(5) + None', globals())
465
466 def test_implicit_from_int(self):
467 #normal
468 self.assertEqual(str(Decimal(5) + 45), '50')
469 #exceeding precision
470 self.assertEqual(Decimal(5) + 123456789000, Decimal(123456789000))
471
472 def test_implicit_from_string(self):
473 self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', globals())
474
475 def test_implicit_from_float(self):
476 self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', globals())
477
478 def test_implicit_from_Decimal(self):
479 self.assertEqual(Decimal(5) + Decimal(45), Decimal(50))
480
Raymond Hettinger267b8682005-03-27 10:47:39 +0000481 def test_rop(self):
482 # Allow other classes to be trained to interact with Decimals
483 class E:
484 def __divmod__(self, other):
485 return 'divmod ' + str(other)
486 def __rdivmod__(self, other):
487 return str(other) + ' rdivmod'
488 def __lt__(self, other):
489 return 'lt ' + str(other)
490 def __gt__(self, other):
491 return 'gt ' + str(other)
492 def __le__(self, other):
493 return 'le ' + str(other)
494 def __ge__(self, other):
495 return 'ge ' + str(other)
496 def __eq__(self, other):
497 return 'eq ' + str(other)
498 def __ne__(self, other):
499 return 'ne ' + str(other)
500
501 self.assertEqual(divmod(E(), Decimal(10)), 'divmod 10')
502 self.assertEqual(divmod(Decimal(10), E()), '10 rdivmod')
503 self.assertEqual(eval('Decimal(10) < E()'), 'gt 10')
504 self.assertEqual(eval('Decimal(10) > E()'), 'lt 10')
505 self.assertEqual(eval('Decimal(10) <= E()'), 'ge 10')
506 self.assertEqual(eval('Decimal(10) >= E()'), 'le 10')
507 self.assertEqual(eval('Decimal(10) == E()'), 'eq 10')
508 self.assertEqual(eval('Decimal(10) != E()'), 'ne 10')
509
510 # insert operator methods and then exercise them
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000511 oplist = [
512 ('+', '__add__', '__radd__'),
513 ('-', '__sub__', '__rsub__'),
514 ('*', '__mul__', '__rmul__'),
Thomas Woutersdcc6d322006-04-21 11:30:52 +0000515 ('/', '__truediv__', '__rtruediv__'),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000516 ('%', '__mod__', '__rmod__'),
517 ('//', '__floordiv__', '__rfloordiv__'),
518 ('**', '__pow__', '__rpow__')
519 ]
Raymond Hettinger267b8682005-03-27 10:47:39 +0000520
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000521 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
Guido van Rossume2a383d2007-01-15 16:59:06 +0000910 self.assertEqual(int(d1), 66)
911 self.assertEqual(int(d2), 15)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000912
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)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000984 checkSameDec("__divmod__", True)
985 checkSameDec("__cmp__", True)
986 checkSameDec("__float__")
987 checkSameDec("__floordiv__", True)
988 checkSameDec("__hash__")
989 checkSameDec("__int__")
990 checkSameDec("__long__")
991 checkSameDec("__mod__", True)
992 checkSameDec("__mul__", True)
993 checkSameDec("__neg__")
Jack Diederich4dafcc42006-11-28 19:15:13 +0000994 checkSameDec("__bool__")
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000995 checkSameDec("__pos__")
996 checkSameDec("__pow__", True)
997 checkSameDec("__radd__", True)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000998 checkSameDec("__rdivmod__", True)
999 checkSameDec("__repr__")
1000 checkSameDec("__rfloordiv__", True)
1001 checkSameDec("__rmod__", True)
1002 checkSameDec("__rmul__", True)
1003 checkSameDec("__rpow__", True)
1004 checkSameDec("__rsub__", True)
1005 checkSameDec("__str__")
1006 checkSameDec("__sub__", True)
1007 checkSameDec("__truediv__", True)
1008 checkSameDec("adjusted")
1009 checkSameDec("as_tuple")
1010 checkSameDec("compare", True)
1011 checkSameDec("max", True)
1012 checkSameDec("min", True)
1013 checkSameDec("normalize")
1014 checkSameDec("quantize", True)
1015 checkSameDec("remainder_near", True)
1016 checkSameDec("same_quantum", True)
1017 checkSameDec("sqrt")
1018 checkSameDec("to_eng_string")
1019 checkSameDec("to_integral")
1020
1021class DecimalPythonAPItests(unittest.TestCase):
1022
1023 def test_pickle(self):
1024 d = Decimal('-3.141590000')
1025 p = pickle.dumps(d)
1026 e = pickle.loads(p)
1027 self.assertEqual(d, e)
1028
Raymond Hettinger5548be22004-07-05 18:49:38 +00001029 def test_int(self):
Raymond Hettinger605ed022004-11-24 07:28:48 +00001030 for x in range(-250, 250):
1031 s = '%0.2f' % (x / 100.0)
Raymond Hettinger5548be22004-07-05 18:49:38 +00001032 # should work the same as for floats
1033 self.assertEqual(int(Decimal(s)), int(float(s)))
Raymond Hettinger605ed022004-11-24 07:28:48 +00001034 # should work the same as to_integral in the ROUND_DOWN mode
Raymond Hettinger5548be22004-07-05 18:49:38 +00001035 d = Decimal(s)
Raymond Hettinger605ed022004-11-24 07:28:48 +00001036 r = d.to_integral(ROUND_DOWN)
Raymond Hettinger5548be22004-07-05 18:49:38 +00001037 self.assertEqual(Decimal(int(d)), r)
1038
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +00001039class ContextAPItests(unittest.TestCase):
1040
1041 def test_pickle(self):
1042 c = Context()
1043 e = pickle.loads(pickle.dumps(c))
1044 for k in vars(c):
1045 v1 = vars(c)[k]
1046 v2 = vars(e)[k]
1047 self.assertEqual(v1, v2)
1048
Raymond Hettinger0aeac102004-07-05 22:53:03 +00001049 def test_equality_with_other_types(self):
1050 self.assert_(Decimal(10) in ['a', 1.0, Decimal(10), (1,2), {}])
1051 self.assert_(Decimal(10) not in ['a', 1.0, (1,2), {}])
1052
Raymond Hettinger955d2b22004-08-08 20:17:45 +00001053 def test_copy(self):
1054 # All copies should be deep
1055 c = Context()
1056 d = c.copy()
1057 self.assertNotEqual(id(c), id(d))
1058 self.assertNotEqual(id(c.flags), id(d.flags))
1059 self.assertNotEqual(id(c.traps), id(d.traps))
1060
Thomas Wouters89f507f2006-12-13 04:49:30 +00001061class WithStatementTest(unittest.TestCase):
1062 # Can't do these as docstrings until Python 2.6
1063 # as doctest can't handle __future__ statements
1064
1065 def test_localcontext(self):
1066 # Use a copy of the current context in the block
1067 orig_ctx = getcontext()
1068 with localcontext() as enter_ctx:
1069 set_ctx = getcontext()
1070 final_ctx = getcontext()
1071 self.assert_(orig_ctx is final_ctx, 'did not restore context correctly')
1072 self.assert_(orig_ctx is not set_ctx, 'did not copy the context')
1073 self.assert_(set_ctx is enter_ctx, '__enter__ returned wrong context')
1074
1075 def test_localcontextarg(self):
1076 # Use a copy of the supplied context in the block
1077 orig_ctx = getcontext()
1078 new_ctx = Context(prec=42)
1079 with localcontext(new_ctx) as enter_ctx:
1080 set_ctx = getcontext()
1081 final_ctx = getcontext()
1082 self.assert_(orig_ctx is final_ctx, 'did not restore context correctly')
1083 self.assert_(set_ctx.prec == new_ctx.prec, 'did not set correct context')
1084 self.assert_(new_ctx is not set_ctx, 'did not copy the context')
1085 self.assert_(set_ctx is enter_ctx, '__enter__ returned wrong context')
1086
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001087def test_main(arith=False, verbose=None):
1088 """ Execute the tests.
1089
Raymond Hettingered20ad82004-09-04 20:09:13 +00001090 Runs all arithmetic tests if arith is True or if the "decimal" resource
1091 is enabled in regrtest.py
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001092 """
Raymond Hettingered20ad82004-09-04 20:09:13 +00001093
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001094 init()
Raymond Hettingered20ad82004-09-04 20:09:13 +00001095 global TEST_ALL
1096 TEST_ALL = arith or is_resource_enabled('decimal')
1097
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001098 test_classes = [
1099 DecimalExplicitConstructionTest,
1100 DecimalImplicitConstructionTest,
1101 DecimalArithmeticOperatorsTest,
1102 DecimalUseOfContextTest,
1103 DecimalUsabilityTest,
1104 DecimalPythonAPItests,
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +00001105 ContextAPItests,
Raymond Hettingered20ad82004-09-04 20:09:13 +00001106 DecimalTest,
Thomas Wouters89f507f2006-12-13 04:49:30 +00001107 WithStatementTest,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001108 ]
1109
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001110 try:
1111 run_unittest(*test_classes)
1112 import decimal as DecimalModule
1113 run_doctest(DecimalModule, verbose)
1114 finally:
1115 setcontext(ORIGINAL_CONTEXT)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001116
1117if __name__ == '__main__':
1118 # Calling with no arguments runs all tests.
Raymond Hettingered20ad82004-09-04 20:09:13 +00001119 # Calling with "Skip" will skip over 90% of the arithmetic tests.
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001120 if len(sys.argv) == 1:
1121 test_main(arith=True, verbose=True)
1122 elif len(sys.argv) == 2:
1123 arith = sys.argv[1].lower() != 'skip'
1124 test_main(arith=arith, verbose=True)
1125 else:
1126 raise ValueError("test called with wrong arguments, use test_Decimal [Skip]")