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