blob: 6c8dc6bc4d14aa3831bdb1713cb833741e906069 [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):
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000282 return [e for e in Signals if self.context.flags[e]]
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000283
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000284 def change_precision(self, prec):
285 self.context.prec = prec
286 def change_rounding_method(self, rounding):
287 self.context.rounding = rounding
288 def change_min_exponent(self, exp):
289 self.context.Emin = exp
290 def change_max_exponent(self, exp):
291 self.context.Emax = exp
292 def change_clamp(self, clamp):
293 self.context._clamp = clamp
294
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000295# Dynamically build custom test definition for each file in the test
296# directory and add the definitions to the DecimalTest class. This
297# procedure insures that new files do not get skipped.
298for filename in os.listdir(dir):
299 if '.decTest' not in filename:
300 continue
301 ## XXX buildout to include integer and trim
302 if 'integer' in filename or 'trim' in filename:
303 continue
304 head, tail = filename.split('.')
305 tester = lambda self, f=filename: self.eval_file(dir + f)
306 setattr(DecimalTest, 'test_' + head, tester)
307 del filename, head, tail, tester
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000308
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000309
310
311# The following classes test the behaviour of Decimal according to PEP 327
312
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000313class DecimalExplicitConstructionTest(unittest.TestCase):
314 '''Unit tests for Explicit Construction cases of Decimal.'''
315
316 def test_explicit_empty(self):
317 self.assertEqual(Decimal(), Decimal("0"))
318
319 def test_explicit_from_None(self):
320 self.assertRaises(TypeError, Decimal, None)
321
322 def test_explicit_from_int(self):
323
324 #positive
325 d = Decimal(45)
326 self.assertEqual(str(d), '45')
327
328 #very large positive
329 d = Decimal(500000123)
330 self.assertEqual(str(d), '500000123')
331
332 #negative
333 d = Decimal(-45)
334 self.assertEqual(str(d), '-45')
335
336 #zero
337 d = Decimal(0)
338 self.assertEqual(str(d), '0')
339
340 def test_explicit_from_string(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000341
342 #empty
343 self.assertEqual(str(Decimal('')), 'NaN')
344
345 #int
346 self.assertEqual(str(Decimal('45')), '45')
347
348 #float
349 self.assertEqual(str(Decimal('45.34')), '45.34')
350
351 #engineer notation
352 self.assertEqual(str(Decimal('45e2')), '4.5E+3')
353
354 #just not a number
355 self.assertEqual(str(Decimal('ugly')), 'NaN')
356
357 def test_explicit_from_tuples(self):
358
359 #zero
360 d = Decimal( (0, (0,), 0) )
361 self.assertEqual(str(d), '0')
362
363 #int
364 d = Decimal( (1, (4, 5), 0) )
365 self.assertEqual(str(d), '-45')
366
367 #float
368 d = Decimal( (0, (4, 5, 3, 4), -2) )
369 self.assertEqual(str(d), '45.34')
370
371 #weird
372 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
373 self.assertEqual(str(d), '-4.34913534E-17')
374
375 #wrong number of items
376 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1)) )
377
378 #bad sign
379 self.assertRaises(ValueError, Decimal, (8, (4, 3, 4, 9, 1), 2) )
380
381 #bad exp
382 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 'wrong!') )
383
384 #bad coefficients
385 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) )
386 self.assertRaises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) )
387
388 def test_explicit_from_Decimal(self):
389
390 #positive
391 d = Decimal(45)
392 e = Decimal(d)
393 self.assertEqual(str(e), '45')
394 self.assertNotEqual(id(d), id(e))
395
396 #very large positive
397 d = Decimal(500000123)
398 e = Decimal(d)
399 self.assertEqual(str(e), '500000123')
400 self.assertNotEqual(id(d), id(e))
401
402 #negative
403 d = Decimal(-45)
404 e = Decimal(d)
405 self.assertEqual(str(e), '-45')
406 self.assertNotEqual(id(d), id(e))
407
408 #zero
409 d = Decimal(0)
410 e = Decimal(d)
411 self.assertEqual(str(e), '0')
412 self.assertNotEqual(id(d), id(e))
413
414 def test_explicit_context_create_decimal(self):
415
416 nc = copy.copy(getcontext())
417 nc.prec = 3
418
419 # empty
Raymond Hettingerfed52962004-07-14 15:41:57 +0000420 d = Decimal()
421 self.assertEqual(str(d), '0')
422 d = nc.create_decimal()
423 self.assertEqual(str(d), '0')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000424
425 # from None
426 self.assertRaises(TypeError, nc.create_decimal, None)
427
428 # from int
429 d = nc.create_decimal(456)
430 self.failUnless(isinstance(d, Decimal))
431 self.assertEqual(nc.create_decimal(45678),
432 nc.create_decimal('457E+2'))
433
434 # from string
435 d = Decimal('456789')
436 self.assertEqual(str(d), '456789')
437 d = nc.create_decimal('456789')
438 self.assertEqual(str(d), '4.57E+5')
439
440 # from tuples
441 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
442 self.assertEqual(str(d), '-4.34913534E-17')
443 d = nc.create_decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
444 self.assertEqual(str(d), '-4.35E-17')
445
446 # from Decimal
447 prevdec = Decimal(500000123)
448 d = Decimal(prevdec)
449 self.assertEqual(str(d), '500000123')
450 d = nc.create_decimal(prevdec)
451 self.assertEqual(str(d), '5.00E+8')
452
453
454class DecimalImplicitConstructionTest(unittest.TestCase):
455 '''Unit tests for Implicit Construction cases of Decimal.'''
456
457 def test_implicit_from_None(self):
458 self.assertRaises(TypeError, eval, 'Decimal(5) + None', globals())
459
460 def test_implicit_from_int(self):
461 #normal
462 self.assertEqual(str(Decimal(5) + 45), '50')
463 #exceeding precision
464 self.assertEqual(Decimal(5) + 123456789000, Decimal(123456789000))
465
466 def test_implicit_from_string(self):
467 self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', globals())
468
469 def test_implicit_from_float(self):
470 self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', globals())
471
472 def test_implicit_from_Decimal(self):
473 self.assertEqual(Decimal(5) + Decimal(45), Decimal(50))
474
475
476class DecimalArithmeticOperatorsTest(unittest.TestCase):
477 '''Unit tests for all arithmetic operators, binary and unary.'''
478
479 def test_addition(self):
480
481 d1 = Decimal('-11.1')
482 d2 = Decimal('22.2')
483
484 #two Decimals
485 self.assertEqual(d1+d2, Decimal('11.1'))
486 self.assertEqual(d2+d1, Decimal('11.1'))
487
488 #with other type, left
489 c = d1 + 5
490 self.assertEqual(c, Decimal('-6.1'))
491 self.assertEqual(type(c), type(d1))
492
493 #with other type, right
494 c = 5 + d1
495 self.assertEqual(c, Decimal('-6.1'))
496 self.assertEqual(type(c), type(d1))
497
498 #inline with decimal
499 d1 += d2
500 self.assertEqual(d1, Decimal('11.1'))
501
502 #inline with other type
503 d1 += 5
504 self.assertEqual(d1, Decimal('16.1'))
505
506 def test_subtraction(self):
507
508 d1 = Decimal('-11.1')
509 d2 = Decimal('22.2')
510
511 #two Decimals
512 self.assertEqual(d1-d2, Decimal('-33.3'))
513 self.assertEqual(d2-d1, Decimal('33.3'))
514
515 #with other type, left
516 c = d1 - 5
517 self.assertEqual(c, Decimal('-16.1'))
518 self.assertEqual(type(c), type(d1))
519
520 #with other type, right
521 c = 5 - d1
522 self.assertEqual(c, Decimal('16.1'))
523 self.assertEqual(type(c), type(d1))
524
525 #inline with decimal
526 d1 -= d2
527 self.assertEqual(d1, Decimal('-33.3'))
528
529 #inline with other type
530 d1 -= 5
531 self.assertEqual(d1, Decimal('-38.3'))
532
533 def test_multiplication(self):
534
535 d1 = Decimal('-5')
536 d2 = Decimal('3')
537
538 #two Decimals
539 self.assertEqual(d1*d2, Decimal('-15'))
540 self.assertEqual(d2*d1, Decimal('-15'))
541
542 #with other type, left
543 c = d1 * 5
544 self.assertEqual(c, Decimal('-25'))
545 self.assertEqual(type(c), type(d1))
546
547 #with other type, right
548 c = 5 * d1
549 self.assertEqual(c, Decimal('-25'))
550 self.assertEqual(type(c), type(d1))
551
552 #inline with decimal
553 d1 *= d2
554 self.assertEqual(d1, Decimal('-15'))
555
556 #inline with other type
557 d1 *= 5
558 self.assertEqual(d1, Decimal('-75'))
559
560 def test_division(self):
561
562 d1 = Decimal('-5')
563 d2 = Decimal('2')
564
565 #two Decimals
566 self.assertEqual(d1/d2, Decimal('-2.5'))
567 self.assertEqual(d2/d1, Decimal('-0.4'))
568
569 #with other type, left
570 c = d1 / 4
571 self.assertEqual(c, Decimal('-1.25'))
572 self.assertEqual(type(c), type(d1))
573
574 #with other type, right
575 c = 4 / d1
576 self.assertEqual(c, Decimal('-0.8'))
577 self.assertEqual(type(c), type(d1))
578
579 #inline with decimal
580 d1 /= d2
581 self.assertEqual(d1, Decimal('-2.5'))
582
583 #inline with other type
584 d1 /= 4
585 self.assertEqual(d1, Decimal('-0.625'))
586
587 def test_floor_division(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000588
589 d1 = Decimal('5')
590 d2 = Decimal('2')
591
592 #two Decimals
593 self.assertEqual(d1//d2, Decimal('2'))
594 self.assertEqual(d2//d1, Decimal('0'))
595
596 #with other type, left
597 c = d1 // 4
598 self.assertEqual(c, Decimal('1'))
599 self.assertEqual(type(c), type(d1))
600
601 #with other type, right
602 c = 7 // d1
603 self.assertEqual(c, Decimal('1'))
604 self.assertEqual(type(c), type(d1))
605
606 #inline with decimal
607 d1 //= d2
608 self.assertEqual(d1, Decimal('2'))
609
610 #inline with other type
611 d1 //= 2
612 self.assertEqual(d1, Decimal('1'))
613
614 def test_powering(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000615
616 d1 = Decimal('5')
617 d2 = Decimal('2')
618
619 #two Decimals
620 self.assertEqual(d1**d2, Decimal('25'))
621 self.assertEqual(d2**d1, Decimal('32'))
622
623 #with other type, left
624 c = d1 ** 4
625 self.assertEqual(c, Decimal('625'))
626 self.assertEqual(type(c), type(d1))
627
628 #with other type, right
629 c = 7 ** d1
630 self.assertEqual(c, Decimal('16807'))
631 self.assertEqual(type(c), type(d1))
632
633 #inline with decimal
634 d1 **= d2
635 self.assertEqual(d1, Decimal('25'))
636
637 #inline with other type
638 d1 **= 4
639 self.assertEqual(d1, Decimal('390625'))
640
641 def test_module(self):
642
643 d1 = Decimal('5')
644 d2 = Decimal('2')
645
646 #two Decimals
647 self.assertEqual(d1%d2, Decimal('1'))
648 self.assertEqual(d2%d1, Decimal('2'))
649
650 #with other type, left
651 c = d1 % 4
652 self.assertEqual(c, Decimal('1'))
653 self.assertEqual(type(c), type(d1))
654
655 #with other type, right
656 c = 7 % d1
657 self.assertEqual(c, Decimal('2'))
658 self.assertEqual(type(c), type(d1))
659
660 #inline with decimal
661 d1 %= d2
662 self.assertEqual(d1, Decimal('1'))
663
664 #inline with other type
665 d1 %= 4
666 self.assertEqual(d1, Decimal('1'))
667
668 def test_floor_div_module(self):
669
670 d1 = Decimal('5')
671 d2 = Decimal('2')
672
673 #two Decimals
674 (p, q) = divmod(d1, d2)
675 self.assertEqual(p, Decimal('2'))
676 self.assertEqual(q, Decimal('1'))
677 self.assertEqual(type(p), type(d1))
678 self.assertEqual(type(q), type(d1))
679
680 #with other type, left
681 (p, q) = divmod(d1, 4)
682 self.assertEqual(p, Decimal('1'))
683 self.assertEqual(q, Decimal('1'))
684 self.assertEqual(type(p), type(d1))
685 self.assertEqual(type(q), type(d1))
686
687 #with other type, right
688 (p, q) = divmod(7, d1)
689 self.assertEqual(p, Decimal('1'))
690 self.assertEqual(q, Decimal('2'))
691 self.assertEqual(type(p), type(d1))
692 self.assertEqual(type(q), type(d1))
693
694 def test_unary_operators(self):
695 self.assertEqual(+Decimal(45), Decimal(+45)) # +
696 self.assertEqual(-Decimal(45), Decimal(-45)) # -
697 self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs
698
699
700# The following are two functions used to test threading in the next class
701
702def thfunc1(cls):
703 d1 = Decimal(1)
704 d3 = Decimal(3)
705 cls.assertEqual(d1/d3, Decimal('0.333333333'))
706 cls.synchro.wait()
707 cls.assertEqual(d1/d3, Decimal('0.333333333'))
708 cls.finish1.set()
709 return
710
711def thfunc2(cls):
712 d1 = Decimal(1)
713 d3 = Decimal(3)
714 cls.assertEqual(d1/d3, Decimal('0.333333333'))
715 thiscontext = getcontext()
716 thiscontext.prec = 18
717 cls.assertEqual(d1/d3, Decimal('0.333333333333333333'))
718 cls.synchro.set()
719 cls.finish2.set()
720 return
721
722
723class DecimalUseOfContextTest(unittest.TestCase):
724 '''Unit tests for Use of Context cases in Decimal.'''
725
726 import threading
727 # Take care executing this test from IDLE, there's an issue in threading
728 # that hangs IDLE and I couldn't find it
729
730 def test_threading(self):
731 #Test the "threading isolation" of a Context.
732
733 self.synchro = threading.Event()
734 self.finish1 = threading.Event()
735 self.finish2 = threading.Event()
736
737 th1 = threading.Thread(target=thfunc1, args=(self,))
738 th2 = threading.Thread(target=thfunc2, args=(self,))
739
740 th1.start()
741 th2.start()
742
743 self.finish1.wait()
744 self.finish1.wait()
745 return
746
747
748class DecimalUsabilityTest(unittest.TestCase):
749 '''Unit tests for Usability cases of Decimal.'''
750
751 def test_comparison_operators(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000752
753 da = Decimal('23.42')
754 db = Decimal('23.42')
755 dc = Decimal('45')
756
757 #two Decimals
758 self.failUnless(dc > da)
759 self.failUnless(dc >= da)
760 self.failUnless(da < dc)
761 self.failUnless(da <= dc)
762 self.failUnless(da == db)
763 self.failUnless(da != dc)
764 self.failUnless(da <= db)
765 self.failUnless(da >= db)
766 self.assertEqual(cmp(dc,da), 1)
767 self.assertEqual(cmp(da,dc), -1)
768 self.assertEqual(cmp(da,db), 0)
769
770 #a Decimal and an int
771 self.failUnless(dc > 23)
772 self.failUnless(23 < dc)
773 self.failUnless(dc == 45)
774 self.assertEqual(cmp(dc,23), 1)
775 self.assertEqual(cmp(23,dc), -1)
776 self.assertEqual(cmp(dc,45), 0)
777
778 #a Decimal and uncomparable
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000779 self.assertNotEqual(da, 'ugly')
780 self.assertNotEqual(da, 32.7)
781 self.assertNotEqual(da, object())
782 self.assertNotEqual(da, object)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000783
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000784 # sortable
785 a = map(Decimal, xrange(100))
786 b = a[:]
787 random.shuffle(a)
788 a.sort()
789 self.assertEqual(a, b)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000790
791 def test_copy_and_deepcopy_methods(self):
792 d = Decimal('43.24')
793 c = copy.copy(d)
794 self.assertEqual(id(c), id(d))
795 dc = copy.deepcopy(d)
796 self.assertEqual(id(dc), id(d))
797
798 def test_hash_method(self):
799 #just that it's hashable
800 hash(Decimal(23))
801 #the same hash that to an int
802 self.assertEqual(hash(Decimal(23)), hash(23))
803
804 def test_min_and_max_methods(self):
805
806 d1 = Decimal('15.32')
807 d2 = Decimal('28.5')
808 l1 = 15
809 l2 = 28
810
811 #between Decimals
812 self.failUnless(min(d1,d2) is d1)
813 self.failUnless(min(d2,d1) is d1)
814 self.failUnless(max(d1,d2) is d2)
815 self.failUnless(max(d2,d1) is d2)
816
817 #between Decimal and long
818 self.failUnless(min(d1,l2) is d1)
819 self.failUnless(min(l2,d1) is d1)
820 self.failUnless(max(l1,d2) is d2)
821 self.failUnless(max(d2,l1) is d2)
822
823 def test_as_nonzero(self):
824 #as false
825 self.failIf(Decimal(0))
826 #as true
827 self.failUnless(Decimal('0.372'))
828
829 def test_tostring_methods(self):
830 #Test str and repr methods.
831
832 d = Decimal('15.32')
833 self.assertEqual(str(d), '15.32') # str
834 self.assertEqual(repr(d), 'Decimal("15.32")') # repr
835
836 def test_tonum_methods(self):
837 #Test float, int and long methods.
838
839 d1 = Decimal('66')
840 d2 = Decimal('15.32')
841
842 #int
843 self.assertEqual(int(d1), 66)
844 self.assertEqual(int(d2), 15)
845
846 #long
847 self.assertEqual(long(d1), 66)
848 self.assertEqual(long(d2), 15)
849
850 #float
851 self.assertEqual(float(d1), 66)
852 self.assertEqual(float(d2), 15.32)
853
854 def test_eval_round_trip(self):
855
856 #with zero
857 d = Decimal( (0, (0,), 0) )
858 self.assertEqual(d, eval(repr(d)))
859
860 #int
861 d = Decimal( (1, (4, 5), 0) )
862 self.assertEqual(d, eval(repr(d)))
863
864 #float
865 d = Decimal( (0, (4, 5, 3, 4), -2) )
866 self.assertEqual(d, eval(repr(d)))
867
868 #weird
869 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
870 self.assertEqual(d, eval(repr(d)))
871
872 def test_as_tuple(self):
873
874 #with zero
875 d = Decimal(0)
876 self.assertEqual(d.as_tuple(), (0, (0,), 0) )
877
878 #int
879 d = Decimal(-45)
880 self.assertEqual(d.as_tuple(), (1, (4, 5), 0) )
881
882 #complicated string
883 d = Decimal("-4.34913534E-17")
884 self.assertEqual(d.as_tuple(), (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
885
886 #inf
887 d = Decimal("Infinity")
888 self.assertEqual(d.as_tuple(), (0, (0,), 'F') )
889
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000890 def test_immutability_operations(self):
891 # Do operations and check that it didn't change change internal objects.
892
893 d1 = Decimal('-25e55')
894 b1 = Decimal('-25e55')
895 d2 = Decimal('33e-33')
896 b2 = Decimal('33e-33')
897
898 def checkSameDec(operation, useOther=False):
899 if useOther:
900 eval("d1." + operation + "(d2)")
901 self.assertEqual(d1._sign, b1._sign)
902 self.assertEqual(d1._int, b1._int)
903 self.assertEqual(d1._exp, b1._exp)
904 self.assertEqual(d2._sign, b2._sign)
905 self.assertEqual(d2._int, b2._int)
906 self.assertEqual(d2._exp, b2._exp)
907 else:
908 eval("d1." + operation + "()")
909 self.assertEqual(d1._sign, b1._sign)
910 self.assertEqual(d1._int, b1._int)
911 self.assertEqual(d1._exp, b1._exp)
912 return
913
914 Decimal(d1)
915 self.assertEqual(d1._sign, b1._sign)
916 self.assertEqual(d1._int, b1._int)
917 self.assertEqual(d1._exp, b1._exp)
918
919 checkSameDec("__abs__")
920 checkSameDec("__add__", True)
921 checkSameDec("__div__", True)
922 checkSameDec("__divmod__", True)
923 checkSameDec("__cmp__", True)
924 checkSameDec("__float__")
925 checkSameDec("__floordiv__", True)
926 checkSameDec("__hash__")
927 checkSameDec("__int__")
928 checkSameDec("__long__")
929 checkSameDec("__mod__", True)
930 checkSameDec("__mul__", True)
931 checkSameDec("__neg__")
932 checkSameDec("__nonzero__")
933 checkSameDec("__pos__")
934 checkSameDec("__pow__", True)
935 checkSameDec("__radd__", True)
936 checkSameDec("__rdiv__", True)
937 checkSameDec("__rdivmod__", True)
938 checkSameDec("__repr__")
939 checkSameDec("__rfloordiv__", True)
940 checkSameDec("__rmod__", True)
941 checkSameDec("__rmul__", True)
942 checkSameDec("__rpow__", True)
943 checkSameDec("__rsub__", True)
944 checkSameDec("__str__")
945 checkSameDec("__sub__", True)
946 checkSameDec("__truediv__", True)
947 checkSameDec("adjusted")
948 checkSameDec("as_tuple")
949 checkSameDec("compare", True)
950 checkSameDec("max", True)
951 checkSameDec("min", True)
952 checkSameDec("normalize")
953 checkSameDec("quantize", True)
954 checkSameDec("remainder_near", True)
955 checkSameDec("same_quantum", True)
956 checkSameDec("sqrt")
957 checkSameDec("to_eng_string")
958 checkSameDec("to_integral")
959
960class DecimalPythonAPItests(unittest.TestCase):
961
962 def test_pickle(self):
963 d = Decimal('-3.141590000')
964 p = pickle.dumps(d)
965 e = pickle.loads(p)
966 self.assertEqual(d, e)
967
Raymond Hettinger5548be22004-07-05 18:49:38 +0000968 def test_int(self):
969 data = '1.0 1.1 1.9 2.0 0.0 -1.0 -1.1 -1.9 -2.0'.split()
970 for s in data:
971 # should work the same as for floats
972 self.assertEqual(int(Decimal(s)), int(float(s)))
973 # should work the same as ROUND_DOWN
974 d = Decimal(s)
975 r = Context(prec=1, rounding=ROUND_DOWN).create_decimal(s)
976 self.assertEqual(Decimal(int(d)), r)
977
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000978class ContextAPItests(unittest.TestCase):
979
980 def test_pickle(self):
981 c = Context()
982 e = pickle.loads(pickle.dumps(c))
983 for k in vars(c):
984 v1 = vars(c)[k]
985 v2 = vars(e)[k]
986 self.assertEqual(v1, v2)
987
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000988 def test_equality_with_other_types(self):
989 self.assert_(Decimal(10) in ['a', 1.0, Decimal(10), (1,2), {}])
990 self.assert_(Decimal(10) not in ['a', 1.0, (1,2), {}])
991
Raymond Hettinger955d2b22004-08-08 20:17:45 +0000992 def test_copy(self):
993 # All copies should be deep
994 c = Context()
995 d = c.copy()
996 self.assertNotEqual(id(c), id(d))
997 self.assertNotEqual(id(c.flags), id(d.flags))
998 self.assertNotEqual(id(c.traps), id(d.traps))
999
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001000def test_main(arith=False, verbose=None):
1001 """ Execute the tests.
1002
1003 Runs arithmetic tests if arith is True or if the "decimal" resource
1004 is enables in regrtest.py
1005 """
1006 test_classes = [
1007 DecimalExplicitConstructionTest,
1008 DecimalImplicitConstructionTest,
1009 DecimalArithmeticOperatorsTest,
1010 DecimalUseOfContextTest,
1011 DecimalUsabilityTest,
1012 DecimalPythonAPItests,
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +00001013 ContextAPItests,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001014 ]
1015
1016 if arith or is_resource_enabled('decimal'):
1017 test_classes.extend([DecimalTest])
1018
1019 run_unittest(*test_classes)
1020 import decimal as DecimalModule
1021 run_doctest(DecimalModule, verbose)
1022 return
1023
1024
1025if __name__ == '__main__':
1026 # Calling with no arguments runs all tests.
1027 # Calling with "Skip" will skipover the arithmetic tests.
1028 if len(sys.argv) == 1:
1029 test_main(arith=True, verbose=True)
1030 elif len(sys.argv) == 2:
1031 arith = sys.argv[1].lower() != 'skip'
1032 test_main(arith=arith, verbose=True)
1033 else:
1034 raise ValueError("test called with wrong arguments, use test_Decimal [Skip]")