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