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