blob: 7bfb13c385e0bc98a1f387c513279a3b0a1c8a5e [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
35import threading
36
Raymond Hettinger6ea48452004-07-03 12:26:21 +000037# Tests are built around these assumed context defaults
38DefaultContext.prec=9
39DefaultContext.rounding=ROUND_HALF_EVEN
40DefaultContext.trap_enablers=dict.fromkeys(Signals, 0)
41setcontext(DefaultContext)
42
43
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000044TESTDATADIR = 'decimaltestdata'
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +000045if __name__ == '__main__':
46 file = sys.argv[0]
47else:
48 file = __file__
49testdir = os.path.dirname(file) or os.curdir
50dir = testdir + os.sep + TESTDATADIR + os.sep
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000051
52skip_expected = not os.path.isdir(dir)
53
54# Make sure it actually raises errors when not expected and caught in flags
55# Slower, since it runs some things several times.
56EXTENDEDERRORTEST = False
57
58
59#Map the test cases' error names to the actual errors
60
61ErrorNames = {'clamped' : Clamped,
62 'conversion_syntax' : ConversionSyntax,
63 'division_by_zero' : DivisionByZero,
64 'division_impossible' : DivisionImpossible,
65 'division_undefined' : DivisionUndefined,
66 'inexact' : Inexact,
67 'invalid_context' : InvalidContext,
68 'invalid_operation' : InvalidOperation,
69 'overflow' : Overflow,
70 'rounded' : Rounded,
71 'subnormal' : Subnormal,
72 'underflow' : Underflow}
73
74
75def Nonfunction(*args):
76 """Doesn't do anything."""
77 return None
78
79RoundingDict = {'ceiling' : ROUND_CEILING, #Maps test-case names to roundings.
80 'down' : ROUND_DOWN,
81 'floor' : ROUND_FLOOR,
82 'half_down' : ROUND_HALF_DOWN,
83 'half_even' : ROUND_HALF_EVEN,
84 'half_up' : ROUND_HALF_UP,
85 'up' : ROUND_UP}
86
87# Name adapter to be able to change the Decimal and Context
88# interface without changing the test files from Cowlishaw
89nameAdapter = {'toeng':'to_eng_string',
90 'tosci':'to_sci_string',
91 'samequantum':'same_quantum',
92 'tointegral':'to_integral',
93 'remaindernear':'remainder_near',
94 'divideint':'divide_int',
95 'squareroot':'sqrt',
96 'apply':'_apply',
97 }
98
99class DecimalTest(unittest.TestCase):
100 """Class which tests the Decimal class against the test cases.
101
102 Changed for unittest.
103 """
104 def setUp(self):
105 global dir
106 self.context = Context()
107 for key in DefaultContext.trap_enablers.keys():
108 DefaultContext.trap_enablers[key] = 1
109 self.ignore_list = ['#']
110 # Basically, a # means return NaN InvalidOperation.
111 # Different from a sNaN in trim
112
113 self.ChangeDict = {'precision' : self.change_precision,
114 'rounding' : self.change_rounding_method,
115 'maxexponent' : self.change_max_exponent,
116 'minexponent' : self.change_min_exponent,
117 'clamp' : self.change_clamp}
118
119 def tearDown(self):
120 """Cleaning up enviroment."""
121 # leaving context in original state
122 for key in DefaultContext.trap_enablers.keys():
123 DefaultContext.trap_enablers[key] = 0
124 return
125
126 def eval_file(self, file):
127 global skip_expected
128 if skip_expected:
129 raise TestSkipped
130 return
131 for line in open(file).xreadlines():
132 line = line.replace('\r\n', '').replace('\n', '')
133 try:
134 t = self.eval_line(line)
135 except ConversionSyntax:
136 print 'Error in test cases:'
137 print line
138 continue
139 except DecimalException, exception:
140 #Exception raised where there shoudn't have been one.
141 self.fail('Exception "'+exception.__class__.__name__ + '" raised on line '+line)
142
143 return
144
145 def eval_line(self, s):
146 if s.find(' -> ') >= 0 and s[:2] != '--' and not s.startswith(' --'):
147 s = (s.split('->')[0] + '->' +
148 s.split('->')[1].split('--')[0]).strip()
149 else:
150 s = s.split('--')[0].strip()
151
152 for ignore in self.ignore_list:
153 if s.find(ignore) >= 0:
154 #print s.split()[0], 'NotImplemented--', ignore
155 return
156 if not s:
157 return
158 elif ':' in s:
159 return self.eval_directive(s)
160 else:
161 return self.eval_equation(s)
162
163 def eval_directive(self, s):
164 funct, value = map(lambda x: x.strip().lower(), s.split(':'))
165 if funct == 'rounding':
166 value = RoundingDict[value]
167 else:
168 try:
169 value = int(value)
170 except ValueError:
171 pass
172
173 funct = self.ChangeDict.get(funct, Nonfunction)
174 funct(value)
175
176 def eval_equation(self, s):
177 #global DEFAULT_PRECISION
178 #print DEFAULT_PRECISION
179 try:
180 Sides = s.split('->')
181 L = Sides[0].strip().split()
182 id = L[0]
183# print id,
184 funct = L[1].lower()
185 valstemp = L[2:]
186 L = Sides[1].strip().split()
187 ans = L[0]
188 exceptions = L[1:]
189 except (TypeError, AttributeError, IndexError):
190 raise ConversionSyntax
191 def FixQuotes(val):
192 val = val.replace("''", 'SingleQuote').replace('""', 'DoubleQuote')
193 val = val.replace("'", '').replace('"', '')
194 val = val.replace('SingleQuote', "'").replace('DoubleQuote', '"')
195 return val
196 fname = nameAdapter.get(funct, funct)
197 if fname == 'rescale':
198 return
199 funct = getattr(self.context, fname)
200 vals = []
201 conglomerate = ''
202 quote = 0
203 theirexceptions = [ErrorNames[x.lower()] for x in exceptions]
204
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000205 for exception in Signals:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000206 self.context.trap_enablers[exception] = 1 #Catch these bugs...
207 for exception in theirexceptions:
208 self.context.trap_enablers[exception] = 0
209 for i, val in enumerate(valstemp):
210 if val.count("'") % 2 == 1:
211 quote = 1 - quote
212 if quote:
213 conglomerate = conglomerate + ' ' + val
214 continue
215 else:
216 val = conglomerate + val
217 conglomerate = ''
218 v = FixQuotes(val)
219 if fname in ('to_sci_string', 'to_eng_string'):
220 if EXTENDEDERRORTEST:
221 for error in theirexceptions:
222 self.context.trap_enablers[error] = 1
223 try:
224 funct(self.context.create_decimal(v))
225 except error:
226 pass
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000227 except Signals, e:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000228 self.fail("Raised %s in %s when %s disabled" % \
229 (e, s, error))
230 else:
231 self.fail("Did not raise %s in %s" % (error, s))
232 self.context.trap_enablers[error] = 0
233 v = self.context.create_decimal(v)
234 else:
235 v = Decimal(v)
236 vals.append(v)
237
238 ans = FixQuotes(ans)
239
240 if EXTENDEDERRORTEST and fname not in ('to_sci_string', 'to_eng_string'):
241 for error in theirexceptions:
242 self.context.trap_enablers[error] = 1
243 try:
244 funct(*vals)
245 except error:
246 pass
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000247 except Signals, e:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000248 self.fail("Raised %s in %s when %s disabled" % \
249 (e, s, error))
250 else:
251 self.fail("Did not raise %s in %s" % (error, s))
252 self.context.trap_enablers[error] = 0
253 try:
254 result = str(funct(*vals))
255 if fname == 'same_quantum':
256 result = str(int(eval(result))) # 'True', 'False' -> '1', '0'
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000257 except Signals, error:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000258 self.fail("Raised %s in %s" % (error, s))
259 except: #Catch any error long enough to state the test case.
260 print "ERROR:", s
261 raise
262
263 myexceptions = self.getexceptions()
264 self.resetflags()
265
266 myexceptions.sort()
267 theirexceptions.sort()
268
269 self.assertEqual(result, ans,
270 'Incorrect answer for ' + s + ' -- got ' + result)
271 self.assertEqual(myexceptions, theirexceptions,
272 'Incorrect flags set in ' + s + ' -- got ' \
273 + str(myexceptions))
274 return
275
276 def getexceptions(self):
277 L = []
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000278 for exception in Signals:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000279 if self.context.flags[exception]:
280 L.append(exception)
281 return L
282
283 def resetflags(self):
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000284 for exception in Signals:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000285 self.context.flags[exception] = 0
286
287 def change_precision(self, prec):
288 self.context.prec = prec
289 def change_rounding_method(self, rounding):
290 self.context.rounding = rounding
291 def change_min_exponent(self, exp):
292 self.context.Emin = exp
293 def change_max_exponent(self, exp):
294 self.context.Emax = exp
295 def change_clamp(self, clamp):
296 self.context._clamp = clamp
297
298 def test_abs(self):
299 self.eval_file(dir + 'abs' + '.decTest')
300
301 def test_add(self):
302 self.eval_file(dir + 'add' + '.decTest')
303
304 def test_base(self):
305 self.eval_file(dir + 'base' + '.decTest')
306
307 def test_clamp(self):
308 self.eval_file(dir + 'clamp' + '.decTest')
309
310 def test_compare(self):
311 self.eval_file(dir + 'compare' + '.decTest')
312
313 def test_divide(self):
314 self.eval_file(dir + 'divide' + '.decTest')
315
316 def test_divideint(self):
317 self.eval_file(dir + 'divideint' + '.decTest')
318
319 def test_inexact(self):
320 self.eval_file(dir + 'inexact' + '.decTest')
321
322 def test_max(self):
323 self.eval_file(dir + 'max' + '.decTest')
324
325 def test_min(self):
326 self.eval_file(dir + 'min' + '.decTest')
327
328 def test_minus(self):
329 self.eval_file(dir + 'minus' + '.decTest')
330
331 def test_multiply(self):
332 self.eval_file(dir+'multiply'+'.decTest')
333
334 def test_normalize(self):
335 self.eval_file(dir + 'normalize' + '.decTest')
336
337 def test_plus(self):
338 self.eval_file(dir + 'plus' + '.decTest')
339
340 def test_power(self):
341 self.eval_file(dir + 'power' + '.decTest')
342
343 def test_quantize(self):
344 self.eval_file(dir + 'quantize' + '.decTest')
345
346 def test_randomBound32(self):
347 self.eval_file(dir + 'randomBound32' + '.decTest')
348
349 def test_randoms(self):
350 self.eval_file(dir + 'randoms' + '.decTest')
351
352 def test_remainder(self):
353 self.eval_file(dir + 'remainder' + '.decTest')
354
355 def test_remainderNear(self):
356 self.eval_file(dir + 'remainderNear' + '.decTest')
357
358 def test_rounding(self):
359 self.eval_file(dir + 'rounding' + '.decTest')
360
361 def test_samequantum(self):
362 self.eval_file(dir + 'samequantum' + '.decTest')
363
364 def test_squareroot(self):
365 self.eval_file(dir + 'squareroot' + '.decTest')
366
367 def test_subtract(self):
368 self.eval_file(dir + 'subtract' + '.decTest')
369
370 def test_tointegral(self):
371 self.eval_file(dir + 'tointegral' + '.decTest')
372
373
374# The following classes test the behaviour of Decimal according to PEP 327
375
376
377class DecimalExplicitConstructionTest(unittest.TestCase):
378 '''Unit tests for Explicit Construction cases of Decimal.'''
379
380 def test_explicit_empty(self):
381 self.assertEqual(Decimal(), Decimal("0"))
382
383 def test_explicit_from_None(self):
384 self.assertRaises(TypeError, Decimal, None)
385
386 def test_explicit_from_int(self):
387
388 #positive
389 d = Decimal(45)
390 self.assertEqual(str(d), '45')
391
392 #very large positive
393 d = Decimal(500000123)
394 self.assertEqual(str(d), '500000123')
395
396 #negative
397 d = Decimal(-45)
398 self.assertEqual(str(d), '-45')
399
400 #zero
401 d = Decimal(0)
402 self.assertEqual(str(d), '0')
403
404 def test_explicit_from_string(self):
405 '''Explicit construction with string.'''
406
407 #empty
408 self.assertEqual(str(Decimal('')), 'NaN')
409
410 #int
411 self.assertEqual(str(Decimal('45')), '45')
412
413 #float
414 self.assertEqual(str(Decimal('45.34')), '45.34')
415
416 #engineer notation
417 self.assertEqual(str(Decimal('45e2')), '4.5E+3')
418
419 #just not a number
420 self.assertEqual(str(Decimal('ugly')), 'NaN')
421
422 def test_explicit_from_tuples(self):
423
424 #zero
425 d = Decimal( (0, (0,), 0) )
426 self.assertEqual(str(d), '0')
427
428 #int
429 d = Decimal( (1, (4, 5), 0) )
430 self.assertEqual(str(d), '-45')
431
432 #float
433 d = Decimal( (0, (4, 5, 3, 4), -2) )
434 self.assertEqual(str(d), '45.34')
435
436 #weird
437 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
438 self.assertEqual(str(d), '-4.34913534E-17')
439
440 #wrong number of items
441 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1)) )
442
443 #bad sign
444 self.assertRaises(ValueError, Decimal, (8, (4, 3, 4, 9, 1), 2) )
445
446 #bad exp
447 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 'wrong!') )
448
449 #bad coefficients
450 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) )
451 self.assertRaises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) )
452
453 def test_explicit_from_Decimal(self):
454
455 #positive
456 d = Decimal(45)
457 e = Decimal(d)
458 self.assertEqual(str(e), '45')
459 self.assertNotEqual(id(d), id(e))
460
461 #very large positive
462 d = Decimal(500000123)
463 e = Decimal(d)
464 self.assertEqual(str(e), '500000123')
465 self.assertNotEqual(id(d), id(e))
466
467 #negative
468 d = Decimal(-45)
469 e = Decimal(d)
470 self.assertEqual(str(e), '-45')
471 self.assertNotEqual(id(d), id(e))
472
473 #zero
474 d = Decimal(0)
475 e = Decimal(d)
476 self.assertEqual(str(e), '0')
477 self.assertNotEqual(id(d), id(e))
478
479 def test_explicit_context_create_decimal(self):
480
481 nc = copy.copy(getcontext())
482 nc.prec = 3
483
484 # empty
485 self.assertRaises(TypeError, nc.create_decimal)
486
487 # from None
488 self.assertRaises(TypeError, nc.create_decimal, None)
489
490 # from int
491 d = nc.create_decimal(456)
492 self.failUnless(isinstance(d, Decimal))
493 self.assertEqual(nc.create_decimal(45678),
494 nc.create_decimal('457E+2'))
495
496 # from string
497 d = Decimal('456789')
498 self.assertEqual(str(d), '456789')
499 d = nc.create_decimal('456789')
500 self.assertEqual(str(d), '4.57E+5')
501
502 # from tuples
503 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
504 self.assertEqual(str(d), '-4.34913534E-17')
505 d = nc.create_decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
506 self.assertEqual(str(d), '-4.35E-17')
507
508 # from Decimal
509 prevdec = Decimal(500000123)
510 d = Decimal(prevdec)
511 self.assertEqual(str(d), '500000123')
512 d = nc.create_decimal(prevdec)
513 self.assertEqual(str(d), '5.00E+8')
514
515
516class DecimalImplicitConstructionTest(unittest.TestCase):
517 '''Unit tests for Implicit Construction cases of Decimal.'''
518
519 def test_implicit_from_None(self):
520 self.assertRaises(TypeError, eval, 'Decimal(5) + None', globals())
521
522 def test_implicit_from_int(self):
523 #normal
524 self.assertEqual(str(Decimal(5) + 45), '50')
525 #exceeding precision
526 self.assertEqual(Decimal(5) + 123456789000, Decimal(123456789000))
527
528 def test_implicit_from_string(self):
529 self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', globals())
530
531 def test_implicit_from_float(self):
532 self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', globals())
533
534 def test_implicit_from_Decimal(self):
535 self.assertEqual(Decimal(5) + Decimal(45), Decimal(50))
536
537
538class DecimalArithmeticOperatorsTest(unittest.TestCase):
539 '''Unit tests for all arithmetic operators, binary and unary.'''
540
541 def test_addition(self):
542
543 d1 = Decimal('-11.1')
544 d2 = Decimal('22.2')
545
546 #two Decimals
547 self.assertEqual(d1+d2, Decimal('11.1'))
548 self.assertEqual(d2+d1, Decimal('11.1'))
549
550 #with other type, left
551 c = d1 + 5
552 self.assertEqual(c, Decimal('-6.1'))
553 self.assertEqual(type(c), type(d1))
554
555 #with other type, right
556 c = 5 + d1
557 self.assertEqual(c, Decimal('-6.1'))
558 self.assertEqual(type(c), type(d1))
559
560 #inline with decimal
561 d1 += d2
562 self.assertEqual(d1, Decimal('11.1'))
563
564 #inline with other type
565 d1 += 5
566 self.assertEqual(d1, Decimal('16.1'))
567
568 def test_subtraction(self):
569
570 d1 = Decimal('-11.1')
571 d2 = Decimal('22.2')
572
573 #two Decimals
574 self.assertEqual(d1-d2, Decimal('-33.3'))
575 self.assertEqual(d2-d1, Decimal('33.3'))
576
577 #with other type, left
578 c = d1 - 5
579 self.assertEqual(c, Decimal('-16.1'))
580 self.assertEqual(type(c), type(d1))
581
582 #with other type, right
583 c = 5 - d1
584 self.assertEqual(c, Decimal('16.1'))
585 self.assertEqual(type(c), type(d1))
586
587 #inline with decimal
588 d1 -= d2
589 self.assertEqual(d1, Decimal('-33.3'))
590
591 #inline with other type
592 d1 -= 5
593 self.assertEqual(d1, Decimal('-38.3'))
594
595 def test_multiplication(self):
596
597 d1 = Decimal('-5')
598 d2 = Decimal('3')
599
600 #two Decimals
601 self.assertEqual(d1*d2, Decimal('-15'))
602 self.assertEqual(d2*d1, Decimal('-15'))
603
604 #with other type, left
605 c = d1 * 5
606 self.assertEqual(c, Decimal('-25'))
607 self.assertEqual(type(c), type(d1))
608
609 #with other type, right
610 c = 5 * d1
611 self.assertEqual(c, Decimal('-25'))
612 self.assertEqual(type(c), type(d1))
613
614 #inline with decimal
615 d1 *= d2
616 self.assertEqual(d1, Decimal('-15'))
617
618 #inline with other type
619 d1 *= 5
620 self.assertEqual(d1, Decimal('-75'))
621
622 def test_division(self):
623
624 d1 = Decimal('-5')
625 d2 = Decimal('2')
626
627 #two Decimals
628 self.assertEqual(d1/d2, Decimal('-2.5'))
629 self.assertEqual(d2/d1, Decimal('-0.4'))
630
631 #with other type, left
632 c = d1 / 4
633 self.assertEqual(c, Decimal('-1.25'))
634 self.assertEqual(type(c), type(d1))
635
636 #with other type, right
637 c = 4 / d1
638 self.assertEqual(c, Decimal('-0.8'))
639 self.assertEqual(type(c), type(d1))
640
641 #inline with decimal
642 d1 /= d2
643 self.assertEqual(d1, Decimal('-2.5'))
644
645 #inline with other type
646 d1 /= 4
647 self.assertEqual(d1, Decimal('-0.625'))
648
649 def test_floor_division(self):
650 '''Test floor division in all its ways.'''
651
652 d1 = Decimal('5')
653 d2 = Decimal('2')
654
655 #two Decimals
656 self.assertEqual(d1//d2, Decimal('2'))
657 self.assertEqual(d2//d1, Decimal('0'))
658
659 #with other type, left
660 c = d1 // 4
661 self.assertEqual(c, Decimal('1'))
662 self.assertEqual(type(c), type(d1))
663
664 #with other type, right
665 c = 7 // d1
666 self.assertEqual(c, Decimal('1'))
667 self.assertEqual(type(c), type(d1))
668
669 #inline with decimal
670 d1 //= d2
671 self.assertEqual(d1, Decimal('2'))
672
673 #inline with other type
674 d1 //= 2
675 self.assertEqual(d1, Decimal('1'))
676
677 def test_powering(self):
678 '''Test powering in all its ways.'''
679
680 d1 = Decimal('5')
681 d2 = Decimal('2')
682
683 #two Decimals
684 self.assertEqual(d1**d2, Decimal('25'))
685 self.assertEqual(d2**d1, Decimal('32'))
686
687 #with other type, left
688 c = d1 ** 4
689 self.assertEqual(c, Decimal('625'))
690 self.assertEqual(type(c), type(d1))
691
692 #with other type, right
693 c = 7 ** d1
694 self.assertEqual(c, Decimal('16807'))
695 self.assertEqual(type(c), type(d1))
696
697 #inline with decimal
698 d1 **= d2
699 self.assertEqual(d1, Decimal('25'))
700
701 #inline with other type
702 d1 **= 4
703 self.assertEqual(d1, Decimal('390625'))
704
705 def test_module(self):
706
707 d1 = Decimal('5')
708 d2 = Decimal('2')
709
710 #two Decimals
711 self.assertEqual(d1%d2, Decimal('1'))
712 self.assertEqual(d2%d1, Decimal('2'))
713
714 #with other type, left
715 c = d1 % 4
716 self.assertEqual(c, Decimal('1'))
717 self.assertEqual(type(c), type(d1))
718
719 #with other type, right
720 c = 7 % d1
721 self.assertEqual(c, Decimal('2'))
722 self.assertEqual(type(c), type(d1))
723
724 #inline with decimal
725 d1 %= d2
726 self.assertEqual(d1, Decimal('1'))
727
728 #inline with other type
729 d1 %= 4
730 self.assertEqual(d1, Decimal('1'))
731
732 def test_floor_div_module(self):
733
734 d1 = Decimal('5')
735 d2 = Decimal('2')
736
737 #two Decimals
738 (p, q) = divmod(d1, d2)
739 self.assertEqual(p, Decimal('2'))
740 self.assertEqual(q, Decimal('1'))
741 self.assertEqual(type(p), type(d1))
742 self.assertEqual(type(q), type(d1))
743
744 #with other type, left
745 (p, q) = divmod(d1, 4)
746 self.assertEqual(p, Decimal('1'))
747 self.assertEqual(q, Decimal('1'))
748 self.assertEqual(type(p), type(d1))
749 self.assertEqual(type(q), type(d1))
750
751 #with other type, right
752 (p, q) = divmod(7, d1)
753 self.assertEqual(p, Decimal('1'))
754 self.assertEqual(q, Decimal('2'))
755 self.assertEqual(type(p), type(d1))
756 self.assertEqual(type(q), type(d1))
757
758 def test_unary_operators(self):
759 self.assertEqual(+Decimal(45), Decimal(+45)) # +
760 self.assertEqual(-Decimal(45), Decimal(-45)) # -
761 self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs
762
763
764# The following are two functions used to test threading in the next class
765
766def thfunc1(cls):
767 d1 = Decimal(1)
768 d3 = Decimal(3)
769 cls.assertEqual(d1/d3, Decimal('0.333333333'))
770 cls.synchro.wait()
771 cls.assertEqual(d1/d3, Decimal('0.333333333'))
772 cls.finish1.set()
773 return
774
775def thfunc2(cls):
776 d1 = Decimal(1)
777 d3 = Decimal(3)
778 cls.assertEqual(d1/d3, Decimal('0.333333333'))
779 thiscontext = getcontext()
780 thiscontext.prec = 18
781 cls.assertEqual(d1/d3, Decimal('0.333333333333333333'))
782 cls.synchro.set()
783 cls.finish2.set()
784 return
785
786
787class DecimalUseOfContextTest(unittest.TestCase):
788 '''Unit tests for Use of Context cases in Decimal.'''
789
790 import threading
791 # Take care executing this test from IDLE, there's an issue in threading
792 # that hangs IDLE and I couldn't find it
793
794 def test_threading(self):
795 #Test the "threading isolation" of a Context.
796
797 self.synchro = threading.Event()
798 self.finish1 = threading.Event()
799 self.finish2 = threading.Event()
800
801 th1 = threading.Thread(target=thfunc1, args=(self,))
802 th2 = threading.Thread(target=thfunc2, args=(self,))
803
804 th1.start()
805 th2.start()
806
807 self.finish1.wait()
808 self.finish1.wait()
809 return
810
811
812class DecimalUsabilityTest(unittest.TestCase):
813 '''Unit tests for Usability cases of Decimal.'''
814
815 def test_comparison_operators(self):
816 '''Testing ==, !=, <, >, <=, >=, cmp.'''
817
818 da = Decimal('23.42')
819 db = Decimal('23.42')
820 dc = Decimal('45')
821
822 #two Decimals
823 self.failUnless(dc > da)
824 self.failUnless(dc >= da)
825 self.failUnless(da < dc)
826 self.failUnless(da <= dc)
827 self.failUnless(da == db)
828 self.failUnless(da != dc)
829 self.failUnless(da <= db)
830 self.failUnless(da >= db)
831 self.assertEqual(cmp(dc,da), 1)
832 self.assertEqual(cmp(da,dc), -1)
833 self.assertEqual(cmp(da,db), 0)
834
835 #a Decimal and an int
836 self.failUnless(dc > 23)
837 self.failUnless(23 < dc)
838 self.failUnless(dc == 45)
839 self.assertEqual(cmp(dc,23), 1)
840 self.assertEqual(cmp(23,dc), -1)
841 self.assertEqual(cmp(dc,45), 0)
842
843 #a Decimal and uncomparable
844 try: da == 'ugly'
845 except TypeError: pass
846 else: self.fail('Did not raised an error!')
847
848 try: da == '32.7'
849 except TypeError: pass
850 else: self.fail('Did not raised an error!')
851
852 try: da == object
853 except TypeError: pass
854 else: self.fail('Did not raised an error!')
855
856 def test_copy_and_deepcopy_methods(self):
857 d = Decimal('43.24')
858 c = copy.copy(d)
859 self.assertEqual(id(c), id(d))
860 dc = copy.deepcopy(d)
861 self.assertEqual(id(dc), id(d))
862
863 def test_hash_method(self):
864 #just that it's hashable
865 hash(Decimal(23))
866 #the same hash that to an int
867 self.assertEqual(hash(Decimal(23)), hash(23))
868
869 def test_min_and_max_methods(self):
870
871 d1 = Decimal('15.32')
872 d2 = Decimal('28.5')
873 l1 = 15
874 l2 = 28
875
876 #between Decimals
877 self.failUnless(min(d1,d2) is d1)
878 self.failUnless(min(d2,d1) is d1)
879 self.failUnless(max(d1,d2) is d2)
880 self.failUnless(max(d2,d1) is d2)
881
882 #between Decimal and long
883 self.failUnless(min(d1,l2) is d1)
884 self.failUnless(min(l2,d1) is d1)
885 self.failUnless(max(l1,d2) is d2)
886 self.failUnless(max(d2,l1) is d2)
887
888 def test_as_nonzero(self):
889 #as false
890 self.failIf(Decimal(0))
891 #as true
892 self.failUnless(Decimal('0.372'))
893
894 def test_tostring_methods(self):
895 #Test str and repr methods.
896
897 d = Decimal('15.32')
898 self.assertEqual(str(d), '15.32') # str
899 self.assertEqual(repr(d), 'Decimal("15.32")') # repr
900
901 def test_tonum_methods(self):
902 #Test float, int and long methods.
903
904 d1 = Decimal('66')
905 d2 = Decimal('15.32')
906
907 #int
908 self.assertEqual(int(d1), 66)
909 self.assertEqual(int(d2), 15)
910
911 #long
912 self.assertEqual(long(d1), 66)
913 self.assertEqual(long(d2), 15)
914
915 #float
916 self.assertEqual(float(d1), 66)
917 self.assertEqual(float(d2), 15.32)
918
919 def test_eval_round_trip(self):
920
921 #with zero
922 d = Decimal( (0, (0,), 0) )
923 self.assertEqual(d, eval(repr(d)))
924
925 #int
926 d = Decimal( (1, (4, 5), 0) )
927 self.assertEqual(d, eval(repr(d)))
928
929 #float
930 d = Decimal( (0, (4, 5, 3, 4), -2) )
931 self.assertEqual(d, eval(repr(d)))
932
933 #weird
934 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
935 self.assertEqual(d, eval(repr(d)))
936
937 def test_as_tuple(self):
938
939 #with zero
940 d = Decimal(0)
941 self.assertEqual(d.as_tuple(), (0, (0,), 0) )
942
943 #int
944 d = Decimal(-45)
945 self.assertEqual(d.as_tuple(), (1, (4, 5), 0) )
946
947 #complicated string
948 d = Decimal("-4.34913534E-17")
949 self.assertEqual(d.as_tuple(), (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
950
951 #inf
952 d = Decimal("Infinity")
953 self.assertEqual(d.as_tuple(), (0, (0,), 'F') )
954
955 def test_immutability_onpurpose(self):
956 #Try to change internal objects and see if immutable.
957
958 d = Decimal(42)
959
960 #you can get the attributes...
961 d.exp
962 d.int
963 d.sign
964
965 #...but not change them!
966 try:
967 d.exp = 20
968 d.int = 3
969 d.sign = 1
970 except AttributeError:
971 pass
972 else:
973 self.fail('Did not raised an error!')
974
975 #some new attribute
976 try:
977 d.newone = None
978 except AttributeError:
979 pass
980 else:
981 self.fail('Did not raised an error!')
982
983 def test_immutability_operations(self):
984 # Do operations and check that it didn't change change internal objects.
985
986 d1 = Decimal('-25e55')
987 b1 = Decimal('-25e55')
988 d2 = Decimal('33e-33')
989 b2 = Decimal('33e-33')
990
991 def checkSameDec(operation, useOther=False):
992 if useOther:
993 eval("d1." + operation + "(d2)")
994 self.assertEqual(d1._sign, b1._sign)
995 self.assertEqual(d1._int, b1._int)
996 self.assertEqual(d1._exp, b1._exp)
997 self.assertEqual(d2._sign, b2._sign)
998 self.assertEqual(d2._int, b2._int)
999 self.assertEqual(d2._exp, b2._exp)
1000 else:
1001 eval("d1." + operation + "()")
1002 self.assertEqual(d1._sign, b1._sign)
1003 self.assertEqual(d1._int, b1._int)
1004 self.assertEqual(d1._exp, b1._exp)
1005 return
1006
1007 Decimal(d1)
1008 self.assertEqual(d1._sign, b1._sign)
1009 self.assertEqual(d1._int, b1._int)
1010 self.assertEqual(d1._exp, b1._exp)
1011
1012 checkSameDec("__abs__")
1013 checkSameDec("__add__", True)
1014 checkSameDec("__div__", True)
1015 checkSameDec("__divmod__", True)
1016 checkSameDec("__cmp__", True)
1017 checkSameDec("__float__")
1018 checkSameDec("__floordiv__", True)
1019 checkSameDec("__hash__")
1020 checkSameDec("__int__")
1021 checkSameDec("__long__")
1022 checkSameDec("__mod__", True)
1023 checkSameDec("__mul__", True)
1024 checkSameDec("__neg__")
1025 checkSameDec("__nonzero__")
1026 checkSameDec("__pos__")
1027 checkSameDec("__pow__", True)
1028 checkSameDec("__radd__", True)
1029 checkSameDec("__rdiv__", True)
1030 checkSameDec("__rdivmod__", True)
1031 checkSameDec("__repr__")
1032 checkSameDec("__rfloordiv__", True)
1033 checkSameDec("__rmod__", True)
1034 checkSameDec("__rmul__", True)
1035 checkSameDec("__rpow__", True)
1036 checkSameDec("__rsub__", True)
1037 checkSameDec("__str__")
1038 checkSameDec("__sub__", True)
1039 checkSameDec("__truediv__", True)
1040 checkSameDec("adjusted")
1041 checkSameDec("as_tuple")
1042 checkSameDec("compare", True)
1043 checkSameDec("max", True)
1044 checkSameDec("min", True)
1045 checkSameDec("normalize")
1046 checkSameDec("quantize", True)
1047 checkSameDec("remainder_near", True)
1048 checkSameDec("same_quantum", True)
1049 checkSameDec("sqrt")
1050 checkSameDec("to_eng_string")
1051 checkSameDec("to_integral")
1052
1053class DecimalPythonAPItests(unittest.TestCase):
1054
1055 def test_pickle(self):
1056 d = Decimal('-3.141590000')
1057 p = pickle.dumps(d)
1058 e = pickle.loads(p)
1059 self.assertEqual(d, e)
1060
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +00001061class ContextAPItests(unittest.TestCase):
1062
1063 def test_pickle(self):
1064 c = Context()
1065 e = pickle.loads(pickle.dumps(c))
1066 for k in vars(c):
1067 v1 = vars(c)[k]
1068 v2 = vars(e)[k]
1069 self.assertEqual(v1, v2)
1070
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001071def test_main(arith=False, verbose=None):
1072 """ Execute the tests.
1073
1074 Runs arithmetic tests if arith is True or if the "decimal" resource
1075 is enables in regrtest.py
1076 """
1077 test_classes = [
1078 DecimalExplicitConstructionTest,
1079 DecimalImplicitConstructionTest,
1080 DecimalArithmeticOperatorsTest,
1081 DecimalUseOfContextTest,
1082 DecimalUsabilityTest,
1083 DecimalPythonAPItests,
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +00001084 ContextAPItests,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001085 ]
1086
1087 if arith or is_resource_enabled('decimal'):
1088 test_classes.extend([DecimalTest])
1089
1090 run_unittest(*test_classes)
1091 import decimal as DecimalModule
1092 run_doctest(DecimalModule, verbose)
1093 return
1094
1095
1096if __name__ == '__main__':
1097 # Calling with no arguments runs all tests.
1098 # Calling with "Skip" will skipover the arithmetic tests.
1099 if len(sys.argv) == 1:
1100 test_main(arith=True, verbose=True)
1101 elif len(sys.argv) == 2:
1102 arith = sys.argv[1].lower() != 'skip'
1103 test_main(arith=arith, verbose=True)
1104 else:
1105 raise ValueError("test called with wrong arguments, use test_Decimal [Skip]")