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