blob: 0a23808f5f7f7bddbc198a5d6cd9f0b680717cf9 [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 Hettingerfed52962004-07-14 15:41:57 +000038# Useful Test Constant
39Signals = getcontext().flags.keys()
40
Raymond Hettinger6ea48452004-07-03 12:26:21 +000041# Tests are built around these assumed context defaults
42DefaultContext.prec=9
43DefaultContext.rounding=ROUND_HALF_EVEN
Raymond Hettingerbf440692004-07-10 14:14:37 +000044DefaultContext.traps=dict.fromkeys(Signals, 0)
Raymond Hettinger6ea48452004-07-03 12:26:21 +000045setcontext(DefaultContext)
46
47
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000048TESTDATADIR = 'decimaltestdata'
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +000049if __name__ == '__main__':
50 file = sys.argv[0]
51else:
52 file = __file__
53testdir = os.path.dirname(file) or os.curdir
54dir = testdir + os.sep + TESTDATADIR + os.sep
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000055
56skip_expected = not os.path.isdir(dir)
57
58# Make sure it actually raises errors when not expected and caught in flags
59# Slower, since it runs some things several times.
60EXTENDEDERRORTEST = False
61
62
63#Map the test cases' error names to the actual errors
64
65ErrorNames = {'clamped' : Clamped,
Raymond Hettinger5aa478b2004-07-09 10:02:53 +000066 'conversion_syntax' : InvalidOperation,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000067 'division_by_zero' : DivisionByZero,
Raymond Hettinger5aa478b2004-07-09 10:02:53 +000068 'division_impossible' : InvalidOperation,
69 'division_undefined' : InvalidOperation,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000070 'inexact' : Inexact,
Raymond Hettinger5aa478b2004-07-09 10:02:53 +000071 'invalid_context' : InvalidOperation,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000072 'invalid_operation' : InvalidOperation,
73 'overflow' : Overflow,
74 'rounded' : Rounded,
75 'subnormal' : Subnormal,
76 'underflow' : Underflow}
77
78
79def Nonfunction(*args):
80 """Doesn't do anything."""
81 return None
82
83RoundingDict = {'ceiling' : ROUND_CEILING, #Maps test-case names to roundings.
84 'down' : ROUND_DOWN,
85 'floor' : ROUND_FLOOR,
86 'half_down' : ROUND_HALF_DOWN,
87 'half_even' : ROUND_HALF_EVEN,
88 'half_up' : ROUND_HALF_UP,
89 'up' : ROUND_UP}
90
91# Name adapter to be able to change the Decimal and Context
92# interface without changing the test files from Cowlishaw
93nameAdapter = {'toeng':'to_eng_string',
94 'tosci':'to_sci_string',
95 'samequantum':'same_quantum',
96 'tointegral':'to_integral',
97 'remaindernear':'remainder_near',
98 'divideint':'divide_int',
99 'squareroot':'sqrt',
100 'apply':'_apply',
101 }
102
103class DecimalTest(unittest.TestCase):
104 """Class which tests the Decimal class against the test cases.
105
106 Changed for unittest.
107 """
108 def setUp(self):
109 global dir
110 self.context = Context()
Raymond Hettingerbf440692004-07-10 14:14:37 +0000111 for key in DefaultContext.traps.keys():
112 DefaultContext.traps[key] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000113 self.ignore_list = ['#']
114 # Basically, a # means return NaN InvalidOperation.
115 # Different from a sNaN in trim
116
117 self.ChangeDict = {'precision' : self.change_precision,
118 'rounding' : self.change_rounding_method,
119 'maxexponent' : self.change_max_exponent,
120 'minexponent' : self.change_min_exponent,
121 'clamp' : self.change_clamp}
122
123 def tearDown(self):
124 """Cleaning up enviroment."""
125 # leaving context in original state
Raymond Hettingerbf440692004-07-10 14:14:37 +0000126 for key in DefaultContext.traps.keys():
127 DefaultContext.traps[key] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000128 return
129
130 def eval_file(self, file):
131 global skip_expected
132 if skip_expected:
133 raise TestSkipped
134 return
135 for line in open(file).xreadlines():
136 line = line.replace('\r\n', '').replace('\n', '')
Raymond Hettinger5aa478b2004-07-09 10:02:53 +0000137 #print line
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000138 try:
139 t = self.eval_line(line)
Raymond Hettingerd87ac8f2004-07-09 10:52:54 +0000140 except InvalidOperation:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000141 print 'Error in test cases:'
142 print line
143 continue
144 except DecimalException, exception:
145 #Exception raised where there shoudn't have been one.
146 self.fail('Exception "'+exception.__class__.__name__ + '" raised on line '+line)
147
148 return
149
150 def eval_line(self, s):
151 if s.find(' -> ') >= 0 and s[:2] != '--' and not s.startswith(' --'):
152 s = (s.split('->')[0] + '->' +
153 s.split('->')[1].split('--')[0]).strip()
154 else:
155 s = s.split('--')[0].strip()
156
157 for ignore in self.ignore_list:
158 if s.find(ignore) >= 0:
159 #print s.split()[0], 'NotImplemented--', ignore
160 return
161 if not s:
162 return
163 elif ':' in s:
164 return self.eval_directive(s)
165 else:
166 return self.eval_equation(s)
167
168 def eval_directive(self, s):
169 funct, value = map(lambda x: x.strip().lower(), s.split(':'))
170 if funct == 'rounding':
171 value = RoundingDict[value]
172 else:
173 try:
174 value = int(value)
175 except ValueError:
176 pass
177
178 funct = self.ChangeDict.get(funct, Nonfunction)
179 funct(value)
180
181 def eval_equation(self, s):
182 #global DEFAULT_PRECISION
183 #print DEFAULT_PRECISION
184 try:
185 Sides = s.split('->')
186 L = Sides[0].strip().split()
187 id = L[0]
188# print id,
189 funct = L[1].lower()
190 valstemp = L[2:]
191 L = Sides[1].strip().split()
192 ans = L[0]
193 exceptions = L[1:]
194 except (TypeError, AttributeError, IndexError):
Raymond Hettingerd87ac8f2004-07-09 10:52:54 +0000195 raise InvalidOperation
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000196 def FixQuotes(val):
197 val = val.replace("''", 'SingleQuote').replace('""', 'DoubleQuote')
198 val = val.replace("'", '').replace('"', '')
199 val = val.replace('SingleQuote', "'").replace('DoubleQuote', '"')
200 return val
201 fname = nameAdapter.get(funct, funct)
202 if fname == 'rescale':
203 return
204 funct = getattr(self.context, fname)
205 vals = []
206 conglomerate = ''
207 quote = 0
208 theirexceptions = [ErrorNames[x.lower()] for x in exceptions]
209
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000210 for exception in Signals:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000211 self.context.traps[exception] = 1 #Catch these bugs...
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000212 for exception in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000213 self.context.traps[exception] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000214 for i, val in enumerate(valstemp):
215 if val.count("'") % 2 == 1:
216 quote = 1 - quote
217 if quote:
218 conglomerate = conglomerate + ' ' + val
219 continue
220 else:
221 val = conglomerate + val
222 conglomerate = ''
223 v = FixQuotes(val)
224 if fname in ('to_sci_string', 'to_eng_string'):
225 if EXTENDEDERRORTEST:
226 for error in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000227 self.context.traps[error] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000228 try:
229 funct(self.context.create_decimal(v))
230 except error:
231 pass
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000232 except Signals, e:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000233 self.fail("Raised %s in %s when %s disabled" % \
234 (e, s, error))
235 else:
236 self.fail("Did not raise %s in %s" % (error, s))
Raymond Hettingerbf440692004-07-10 14:14:37 +0000237 self.context.traps[error] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000238 v = self.context.create_decimal(v)
239 else:
240 v = Decimal(v)
241 vals.append(v)
242
243 ans = FixQuotes(ans)
244
245 if EXTENDEDERRORTEST and fname not in ('to_sci_string', 'to_eng_string'):
246 for error in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000247 self.context.traps[error] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000248 try:
249 funct(*vals)
250 except error:
251 pass
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000252 except Signals, e:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000253 self.fail("Raised %s in %s when %s disabled" % \
254 (e, s, error))
255 else:
256 self.fail("Did not raise %s in %s" % (error, s))
Raymond Hettingerbf440692004-07-10 14:14:37 +0000257 self.context.traps[error] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000258 try:
259 result = str(funct(*vals))
260 if fname == 'same_quantum':
261 result = str(int(eval(result))) # 'True', 'False' -> '1', '0'
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000262 except Signals, error:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000263 self.fail("Raised %s in %s" % (error, s))
264 except: #Catch any error long enough to state the test case.
265 print "ERROR:", s
266 raise
267
268 myexceptions = self.getexceptions()
Raymond Hettingerbf440692004-07-10 14:14:37 +0000269 self.context.clear_flags()
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000270
271 myexceptions.sort()
272 theirexceptions.sort()
273
274 self.assertEqual(result, ans,
275 'Incorrect answer for ' + s + ' -- got ' + result)
276 self.assertEqual(myexceptions, theirexceptions,
277 'Incorrect flags set in ' + s + ' -- got ' \
278 + str(myexceptions))
279 return
280
281 def getexceptions(self):
282 L = []
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000283 for exception in Signals:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000284 if self.context.flags[exception]:
285 L.append(exception)
286 return L
287
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000288 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
Raymond Hettingerfed52962004-07-14 15:41:57 +0000486 d = Decimal()
487 self.assertEqual(str(d), '0')
488 d = nc.create_decimal()
489 self.assertEqual(str(d), '0')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000490
491 # from None
492 self.assertRaises(TypeError, nc.create_decimal, None)
493
494 # from int
495 d = nc.create_decimal(456)
496 self.failUnless(isinstance(d, Decimal))
497 self.assertEqual(nc.create_decimal(45678),
498 nc.create_decimal('457E+2'))
499
500 # from string
501 d = Decimal('456789')
502 self.assertEqual(str(d), '456789')
503 d = nc.create_decimal('456789')
504 self.assertEqual(str(d), '4.57E+5')
505
506 # from tuples
507 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
508 self.assertEqual(str(d), '-4.34913534E-17')
509 d = nc.create_decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
510 self.assertEqual(str(d), '-4.35E-17')
511
512 # from Decimal
513 prevdec = Decimal(500000123)
514 d = Decimal(prevdec)
515 self.assertEqual(str(d), '500000123')
516 d = nc.create_decimal(prevdec)
517 self.assertEqual(str(d), '5.00E+8')
518
519
520class DecimalImplicitConstructionTest(unittest.TestCase):
521 '''Unit tests for Implicit Construction cases of Decimal.'''
522
523 def test_implicit_from_None(self):
524 self.assertRaises(TypeError, eval, 'Decimal(5) + None', globals())
525
526 def test_implicit_from_int(self):
527 #normal
528 self.assertEqual(str(Decimal(5) + 45), '50')
529 #exceeding precision
530 self.assertEqual(Decimal(5) + 123456789000, Decimal(123456789000))
531
532 def test_implicit_from_string(self):
533 self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', globals())
534
535 def test_implicit_from_float(self):
536 self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', globals())
537
538 def test_implicit_from_Decimal(self):
539 self.assertEqual(Decimal(5) + Decimal(45), Decimal(50))
540
541
542class DecimalArithmeticOperatorsTest(unittest.TestCase):
543 '''Unit tests for all arithmetic operators, binary and unary.'''
544
545 def test_addition(self):
546
547 d1 = Decimal('-11.1')
548 d2 = Decimal('22.2')
549
550 #two Decimals
551 self.assertEqual(d1+d2, Decimal('11.1'))
552 self.assertEqual(d2+d1, Decimal('11.1'))
553
554 #with other type, left
555 c = d1 + 5
556 self.assertEqual(c, Decimal('-6.1'))
557 self.assertEqual(type(c), type(d1))
558
559 #with other type, right
560 c = 5 + d1
561 self.assertEqual(c, Decimal('-6.1'))
562 self.assertEqual(type(c), type(d1))
563
564 #inline with decimal
565 d1 += d2
566 self.assertEqual(d1, Decimal('11.1'))
567
568 #inline with other type
569 d1 += 5
570 self.assertEqual(d1, Decimal('16.1'))
571
572 def test_subtraction(self):
573
574 d1 = Decimal('-11.1')
575 d2 = Decimal('22.2')
576
577 #two Decimals
578 self.assertEqual(d1-d2, Decimal('-33.3'))
579 self.assertEqual(d2-d1, Decimal('33.3'))
580
581 #with other type, left
582 c = d1 - 5
583 self.assertEqual(c, Decimal('-16.1'))
584 self.assertEqual(type(c), type(d1))
585
586 #with other type, right
587 c = 5 - d1
588 self.assertEqual(c, Decimal('16.1'))
589 self.assertEqual(type(c), type(d1))
590
591 #inline with decimal
592 d1 -= d2
593 self.assertEqual(d1, Decimal('-33.3'))
594
595 #inline with other type
596 d1 -= 5
597 self.assertEqual(d1, Decimal('-38.3'))
598
599 def test_multiplication(self):
600
601 d1 = Decimal('-5')
602 d2 = Decimal('3')
603
604 #two Decimals
605 self.assertEqual(d1*d2, Decimal('-15'))
606 self.assertEqual(d2*d1, Decimal('-15'))
607
608 #with other type, left
609 c = d1 * 5
610 self.assertEqual(c, Decimal('-25'))
611 self.assertEqual(type(c), type(d1))
612
613 #with other type, right
614 c = 5 * d1
615 self.assertEqual(c, Decimal('-25'))
616 self.assertEqual(type(c), type(d1))
617
618 #inline with decimal
619 d1 *= d2
620 self.assertEqual(d1, Decimal('-15'))
621
622 #inline with other type
623 d1 *= 5
624 self.assertEqual(d1, Decimal('-75'))
625
626 def test_division(self):
627
628 d1 = Decimal('-5')
629 d2 = Decimal('2')
630
631 #two Decimals
632 self.assertEqual(d1/d2, Decimal('-2.5'))
633 self.assertEqual(d2/d1, Decimal('-0.4'))
634
635 #with other type, left
636 c = d1 / 4
637 self.assertEqual(c, Decimal('-1.25'))
638 self.assertEqual(type(c), type(d1))
639
640 #with other type, right
641 c = 4 / d1
642 self.assertEqual(c, Decimal('-0.8'))
643 self.assertEqual(type(c), type(d1))
644
645 #inline with decimal
646 d1 /= d2
647 self.assertEqual(d1, Decimal('-2.5'))
648
649 #inline with other type
650 d1 /= 4
651 self.assertEqual(d1, Decimal('-0.625'))
652
653 def test_floor_division(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000654
655 d1 = Decimal('5')
656 d2 = Decimal('2')
657
658 #two Decimals
659 self.assertEqual(d1//d2, Decimal('2'))
660 self.assertEqual(d2//d1, Decimal('0'))
661
662 #with other type, left
663 c = d1 // 4
664 self.assertEqual(c, Decimal('1'))
665 self.assertEqual(type(c), type(d1))
666
667 #with other type, right
668 c = 7 // d1
669 self.assertEqual(c, Decimal('1'))
670 self.assertEqual(type(c), type(d1))
671
672 #inline with decimal
673 d1 //= d2
674 self.assertEqual(d1, Decimal('2'))
675
676 #inline with other type
677 d1 //= 2
678 self.assertEqual(d1, Decimal('1'))
679
680 def test_powering(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000681
682 d1 = Decimal('5')
683 d2 = Decimal('2')
684
685 #two Decimals
686 self.assertEqual(d1**d2, Decimal('25'))
687 self.assertEqual(d2**d1, Decimal('32'))
688
689 #with other type, left
690 c = d1 ** 4
691 self.assertEqual(c, Decimal('625'))
692 self.assertEqual(type(c), type(d1))
693
694 #with other type, right
695 c = 7 ** d1
696 self.assertEqual(c, Decimal('16807'))
697 self.assertEqual(type(c), type(d1))
698
699 #inline with decimal
700 d1 **= d2
701 self.assertEqual(d1, Decimal('25'))
702
703 #inline with other type
704 d1 **= 4
705 self.assertEqual(d1, Decimal('390625'))
706
707 def test_module(self):
708
709 d1 = Decimal('5')
710 d2 = Decimal('2')
711
712 #two Decimals
713 self.assertEqual(d1%d2, Decimal('1'))
714 self.assertEqual(d2%d1, Decimal('2'))
715
716 #with other type, left
717 c = d1 % 4
718 self.assertEqual(c, Decimal('1'))
719 self.assertEqual(type(c), type(d1))
720
721 #with other type, right
722 c = 7 % d1
723 self.assertEqual(c, Decimal('2'))
724 self.assertEqual(type(c), type(d1))
725
726 #inline with decimal
727 d1 %= d2
728 self.assertEqual(d1, Decimal('1'))
729
730 #inline with other type
731 d1 %= 4
732 self.assertEqual(d1, Decimal('1'))
733
734 def test_floor_div_module(self):
735
736 d1 = Decimal('5')
737 d2 = Decimal('2')
738
739 #two Decimals
740 (p, q) = divmod(d1, d2)
741 self.assertEqual(p, Decimal('2'))
742 self.assertEqual(q, Decimal('1'))
743 self.assertEqual(type(p), type(d1))
744 self.assertEqual(type(q), type(d1))
745
746 #with other type, left
747 (p, q) = divmod(d1, 4)
748 self.assertEqual(p, Decimal('1'))
749 self.assertEqual(q, Decimal('1'))
750 self.assertEqual(type(p), type(d1))
751 self.assertEqual(type(q), type(d1))
752
753 #with other type, right
754 (p, q) = divmod(7, d1)
755 self.assertEqual(p, Decimal('1'))
756 self.assertEqual(q, Decimal('2'))
757 self.assertEqual(type(p), type(d1))
758 self.assertEqual(type(q), type(d1))
759
760 def test_unary_operators(self):
761 self.assertEqual(+Decimal(45), Decimal(+45)) # +
762 self.assertEqual(-Decimal(45), Decimal(-45)) # -
763 self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs
764
765
766# The following are two functions used to test threading in the next class
767
768def thfunc1(cls):
769 d1 = Decimal(1)
770 d3 = Decimal(3)
771 cls.assertEqual(d1/d3, Decimal('0.333333333'))
772 cls.synchro.wait()
773 cls.assertEqual(d1/d3, Decimal('0.333333333'))
774 cls.finish1.set()
775 return
776
777def thfunc2(cls):
778 d1 = Decimal(1)
779 d3 = Decimal(3)
780 cls.assertEqual(d1/d3, Decimal('0.333333333'))
781 thiscontext = getcontext()
782 thiscontext.prec = 18
783 cls.assertEqual(d1/d3, Decimal('0.333333333333333333'))
784 cls.synchro.set()
785 cls.finish2.set()
786 return
787
788
789class DecimalUseOfContextTest(unittest.TestCase):
790 '''Unit tests for Use of Context cases in Decimal.'''
791
792 import threading
793 # Take care executing this test from IDLE, there's an issue in threading
794 # that hangs IDLE and I couldn't find it
795
796 def test_threading(self):
797 #Test the "threading isolation" of a Context.
798
799 self.synchro = threading.Event()
800 self.finish1 = threading.Event()
801 self.finish2 = threading.Event()
802
803 th1 = threading.Thread(target=thfunc1, args=(self,))
804 th2 = threading.Thread(target=thfunc2, args=(self,))
805
806 th1.start()
807 th2.start()
808
809 self.finish1.wait()
810 self.finish1.wait()
811 return
812
813
814class DecimalUsabilityTest(unittest.TestCase):
815 '''Unit tests for Usability cases of Decimal.'''
816
817 def test_comparison_operators(self):
818 '''Testing ==, !=, <, >, <=, >=, cmp.'''
819
820 da = Decimal('23.42')
821 db = Decimal('23.42')
822 dc = Decimal('45')
823
824 #two Decimals
825 self.failUnless(dc > da)
826 self.failUnless(dc >= da)
827 self.failUnless(da < dc)
828 self.failUnless(da <= dc)
829 self.failUnless(da == db)
830 self.failUnless(da != dc)
831 self.failUnless(da <= db)
832 self.failUnless(da >= db)
833 self.assertEqual(cmp(dc,da), 1)
834 self.assertEqual(cmp(da,dc), -1)
835 self.assertEqual(cmp(da,db), 0)
836
837 #a Decimal and an int
838 self.failUnless(dc > 23)
839 self.failUnless(23 < dc)
840 self.failUnless(dc == 45)
841 self.assertEqual(cmp(dc,23), 1)
842 self.assertEqual(cmp(23,dc), -1)
843 self.assertEqual(cmp(dc,45), 0)
844
845 #a Decimal and uncomparable
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000846 self.assertNotEqual(da, 'ugly')
847 self.assertNotEqual(da, 32.7)
848 self.assertNotEqual(da, object())
849 self.assertNotEqual(da, object)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000850
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000851 # sortable
852 a = map(Decimal, xrange(100))
853 b = a[:]
854 random.shuffle(a)
855 a.sort()
856 self.assertEqual(a, b)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000857
858 def test_copy_and_deepcopy_methods(self):
859 d = Decimal('43.24')
860 c = copy.copy(d)
861 self.assertEqual(id(c), id(d))
862 dc = copy.deepcopy(d)
863 self.assertEqual(id(dc), id(d))
864
865 def test_hash_method(self):
866 #just that it's hashable
867 hash(Decimal(23))
868 #the same hash that to an int
869 self.assertEqual(hash(Decimal(23)), hash(23))
870
871 def test_min_and_max_methods(self):
872
873 d1 = Decimal('15.32')
874 d2 = Decimal('28.5')
875 l1 = 15
876 l2 = 28
877
878 #between Decimals
879 self.failUnless(min(d1,d2) is d1)
880 self.failUnless(min(d2,d1) is d1)
881 self.failUnless(max(d1,d2) is d2)
882 self.failUnless(max(d2,d1) is d2)
883
884 #between Decimal and long
885 self.failUnless(min(d1,l2) is d1)
886 self.failUnless(min(l2,d1) is d1)
887 self.failUnless(max(l1,d2) is d2)
888 self.failUnless(max(d2,l1) is d2)
889
890 def test_as_nonzero(self):
891 #as false
892 self.failIf(Decimal(0))
893 #as true
894 self.failUnless(Decimal('0.372'))
895
896 def test_tostring_methods(self):
897 #Test str and repr methods.
898
899 d = Decimal('15.32')
900 self.assertEqual(str(d), '15.32') # str
901 self.assertEqual(repr(d), 'Decimal("15.32")') # repr
902
903 def test_tonum_methods(self):
904 #Test float, int and long methods.
905
906 d1 = Decimal('66')
907 d2 = Decimal('15.32')
908
909 #int
910 self.assertEqual(int(d1), 66)
911 self.assertEqual(int(d2), 15)
912
913 #long
914 self.assertEqual(long(d1), 66)
915 self.assertEqual(long(d2), 15)
916
917 #float
918 self.assertEqual(float(d1), 66)
919 self.assertEqual(float(d2), 15.32)
920
921 def test_eval_round_trip(self):
922
923 #with zero
924 d = Decimal( (0, (0,), 0) )
925 self.assertEqual(d, eval(repr(d)))
926
927 #int
928 d = Decimal( (1, (4, 5), 0) )
929 self.assertEqual(d, eval(repr(d)))
930
931 #float
932 d = Decimal( (0, (4, 5, 3, 4), -2) )
933 self.assertEqual(d, eval(repr(d)))
934
935 #weird
936 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
937 self.assertEqual(d, eval(repr(d)))
938
939 def test_as_tuple(self):
940
941 #with zero
942 d = Decimal(0)
943 self.assertEqual(d.as_tuple(), (0, (0,), 0) )
944
945 #int
946 d = Decimal(-45)
947 self.assertEqual(d.as_tuple(), (1, (4, 5), 0) )
948
949 #complicated string
950 d = Decimal("-4.34913534E-17")
951 self.assertEqual(d.as_tuple(), (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
952
953 #inf
954 d = Decimal("Infinity")
955 self.assertEqual(d.as_tuple(), (0, (0,), 'F') )
956
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000957 def test_immutability_operations(self):
958 # Do operations and check that it didn't change change internal objects.
959
960 d1 = Decimal('-25e55')
961 b1 = Decimal('-25e55')
962 d2 = Decimal('33e-33')
963 b2 = Decimal('33e-33')
964
965 def checkSameDec(operation, useOther=False):
966 if useOther:
967 eval("d1." + operation + "(d2)")
968 self.assertEqual(d1._sign, b1._sign)
969 self.assertEqual(d1._int, b1._int)
970 self.assertEqual(d1._exp, b1._exp)
971 self.assertEqual(d2._sign, b2._sign)
972 self.assertEqual(d2._int, b2._int)
973 self.assertEqual(d2._exp, b2._exp)
974 else:
975 eval("d1." + operation + "()")
976 self.assertEqual(d1._sign, b1._sign)
977 self.assertEqual(d1._int, b1._int)
978 self.assertEqual(d1._exp, b1._exp)
979 return
980
981 Decimal(d1)
982 self.assertEqual(d1._sign, b1._sign)
983 self.assertEqual(d1._int, b1._int)
984 self.assertEqual(d1._exp, b1._exp)
985
986 checkSameDec("__abs__")
987 checkSameDec("__add__", True)
988 checkSameDec("__div__", True)
989 checkSameDec("__divmod__", True)
990 checkSameDec("__cmp__", True)
991 checkSameDec("__float__")
992 checkSameDec("__floordiv__", True)
993 checkSameDec("__hash__")
994 checkSameDec("__int__")
995 checkSameDec("__long__")
996 checkSameDec("__mod__", True)
997 checkSameDec("__mul__", True)
998 checkSameDec("__neg__")
999 checkSameDec("__nonzero__")
1000 checkSameDec("__pos__")
1001 checkSameDec("__pow__", True)
1002 checkSameDec("__radd__", True)
1003 checkSameDec("__rdiv__", True)
1004 checkSameDec("__rdivmod__", True)
1005 checkSameDec("__repr__")
1006 checkSameDec("__rfloordiv__", True)
1007 checkSameDec("__rmod__", True)
1008 checkSameDec("__rmul__", True)
1009 checkSameDec("__rpow__", True)
1010 checkSameDec("__rsub__", True)
1011 checkSameDec("__str__")
1012 checkSameDec("__sub__", True)
1013 checkSameDec("__truediv__", True)
1014 checkSameDec("adjusted")
1015 checkSameDec("as_tuple")
1016 checkSameDec("compare", True)
1017 checkSameDec("max", True)
1018 checkSameDec("min", True)
1019 checkSameDec("normalize")
1020 checkSameDec("quantize", True)
1021 checkSameDec("remainder_near", True)
1022 checkSameDec("same_quantum", True)
1023 checkSameDec("sqrt")
1024 checkSameDec("to_eng_string")
1025 checkSameDec("to_integral")
1026
1027class DecimalPythonAPItests(unittest.TestCase):
1028
1029 def test_pickle(self):
1030 d = Decimal('-3.141590000')
1031 p = pickle.dumps(d)
1032 e = pickle.loads(p)
1033 self.assertEqual(d, e)
1034
Raymond Hettinger5548be22004-07-05 18:49:38 +00001035 def test_int(self):
1036 data = '1.0 1.1 1.9 2.0 0.0 -1.0 -1.1 -1.9 -2.0'.split()
1037 for s in data:
1038 # should work the same as for floats
1039 self.assertEqual(int(Decimal(s)), int(float(s)))
1040 # should work the same as ROUND_DOWN
1041 d = Decimal(s)
1042 r = Context(prec=1, rounding=ROUND_DOWN).create_decimal(s)
1043 self.assertEqual(Decimal(int(d)), r)
1044
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +00001045class ContextAPItests(unittest.TestCase):
1046
1047 def test_pickle(self):
1048 c = Context()
1049 e = pickle.loads(pickle.dumps(c))
1050 for k in vars(c):
1051 v1 = vars(c)[k]
1052 v2 = vars(e)[k]
1053 self.assertEqual(v1, v2)
1054
Raymond Hettinger0aeac102004-07-05 22:53:03 +00001055 def test_equality_with_other_types(self):
1056 self.assert_(Decimal(10) in ['a', 1.0, Decimal(10), (1,2), {}])
1057 self.assert_(Decimal(10) not in ['a', 1.0, (1,2), {}])
1058
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001059def test_main(arith=False, verbose=None):
1060 """ Execute the tests.
1061
1062 Runs arithmetic tests if arith is True or if the "decimal" resource
1063 is enables in regrtest.py
1064 """
1065 test_classes = [
1066 DecimalExplicitConstructionTest,
1067 DecimalImplicitConstructionTest,
1068 DecimalArithmeticOperatorsTest,
1069 DecimalUseOfContextTest,
1070 DecimalUsabilityTest,
1071 DecimalPythonAPItests,
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +00001072 ContextAPItests,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001073 ]
1074
1075 if arith or is_resource_enabled('decimal'):
1076 test_classes.extend([DecimalTest])
1077
1078 run_unittest(*test_classes)
1079 import decimal as DecimalModule
1080 run_doctest(DecimalModule, verbose)
1081 return
1082
1083
1084if __name__ == '__main__':
1085 # Calling with no arguments runs all tests.
1086 # Calling with "Skip" will skipover the arithmetic tests.
1087 if len(sys.argv) == 1:
1088 test_main(arith=True, verbose=True)
1089 elif len(sys.argv) == 2:
1090 arith = sys.argv[1].lower() != 'skip'
1091 test_main(arith=arith, verbose=True)
1092 else:
1093 raise ValueError("test called with wrong arguments, use test_Decimal [Skip]")