blob: a4e4041c3110d1e762c16a7f21534066e944b049 [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
Raymond Hettingered20ad82004-09-04 20:09:13 +0000184
185 if not TEST_ALL and random.random() < 0.90:
186 return
187
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000188 try:
189 Sides = s.split('->')
190 L = Sides[0].strip().split()
191 id = L[0]
192# print id,
193 funct = L[1].lower()
194 valstemp = L[2:]
195 L = Sides[1].strip().split()
196 ans = L[0]
197 exceptions = L[1:]
198 except (TypeError, AttributeError, IndexError):
Raymond Hettingerd87ac8f2004-07-09 10:52:54 +0000199 raise InvalidOperation
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000200 def FixQuotes(val):
201 val = val.replace("''", 'SingleQuote').replace('""', 'DoubleQuote')
202 val = val.replace("'", '').replace('"', '')
203 val = val.replace('SingleQuote', "'").replace('DoubleQuote', '"')
204 return val
205 fname = nameAdapter.get(funct, funct)
206 if fname == 'rescale':
207 return
208 funct = getattr(self.context, fname)
209 vals = []
210 conglomerate = ''
211 quote = 0
212 theirexceptions = [ErrorNames[x.lower()] for x in exceptions]
213
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000214 for exception in Signals:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000215 self.context.traps[exception] = 1 #Catch these bugs...
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000216 for exception in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000217 self.context.traps[exception] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000218 for i, val in enumerate(valstemp):
219 if val.count("'") % 2 == 1:
220 quote = 1 - quote
221 if quote:
222 conglomerate = conglomerate + ' ' + val
223 continue
224 else:
225 val = conglomerate + val
226 conglomerate = ''
227 v = FixQuotes(val)
228 if fname in ('to_sci_string', 'to_eng_string'):
229 if EXTENDEDERRORTEST:
230 for error in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000231 self.context.traps[error] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000232 try:
233 funct(self.context.create_decimal(v))
234 except error:
235 pass
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000236 except Signals, e:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000237 self.fail("Raised %s in %s when %s disabled" % \
238 (e, s, error))
239 else:
240 self.fail("Did not raise %s in %s" % (error, s))
Raymond Hettingerbf440692004-07-10 14:14:37 +0000241 self.context.traps[error] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000242 v = self.context.create_decimal(v)
243 else:
244 v = Decimal(v)
245 vals.append(v)
246
247 ans = FixQuotes(ans)
248
249 if EXTENDEDERRORTEST and fname not in ('to_sci_string', 'to_eng_string'):
250 for error in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000251 self.context.traps[error] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000252 try:
253 funct(*vals)
254 except error:
255 pass
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000256 except Signals, e:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000257 self.fail("Raised %s in %s when %s disabled" % \
258 (e, s, error))
259 else:
260 self.fail("Did not raise %s in %s" % (error, s))
Raymond Hettingerbf440692004-07-10 14:14:37 +0000261 self.context.traps[error] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000262 try:
263 result = str(funct(*vals))
264 if fname == 'same_quantum':
265 result = str(int(eval(result))) # 'True', 'False' -> '1', '0'
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000266 except Signals, error:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000267 self.fail("Raised %s in %s" % (error, s))
268 except: #Catch any error long enough to state the test case.
269 print "ERROR:", s
270 raise
271
272 myexceptions = self.getexceptions()
Raymond Hettingerbf440692004-07-10 14:14:37 +0000273 self.context.clear_flags()
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000274
275 myexceptions.sort()
276 theirexceptions.sort()
277
278 self.assertEqual(result, ans,
279 'Incorrect answer for ' + s + ' -- got ' + result)
280 self.assertEqual(myexceptions, theirexceptions,
281 'Incorrect flags set in ' + s + ' -- got ' \
282 + str(myexceptions))
283 return
284
285 def getexceptions(self):
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000286 return [e for e in Signals if self.context.flags[e]]
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000287
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
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000299# Dynamically build custom test definition for each file in the test
300# directory and add the definitions to the DecimalTest class. This
301# procedure insures that new files do not get skipped.
302for filename in os.listdir(dir):
303 if '.decTest' not in filename:
304 continue
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000305 head, tail = filename.split('.')
306 tester = lambda self, f=filename: self.eval_file(dir + f)
307 setattr(DecimalTest, 'test_' + head, tester)
308 del filename, head, tail, tester
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000309
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000310
311
312# The following classes test the behaviour of Decimal according to PEP 327
313
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000314class DecimalExplicitConstructionTest(unittest.TestCase):
315 '''Unit tests for Explicit Construction cases of Decimal.'''
316
317 def test_explicit_empty(self):
318 self.assertEqual(Decimal(), Decimal("0"))
319
320 def test_explicit_from_None(self):
321 self.assertRaises(TypeError, Decimal, None)
322
323 def test_explicit_from_int(self):
324
325 #positive
326 d = Decimal(45)
327 self.assertEqual(str(d), '45')
328
329 #very large positive
330 d = Decimal(500000123)
331 self.assertEqual(str(d), '500000123')
332
333 #negative
334 d = Decimal(-45)
335 self.assertEqual(str(d), '-45')
336
337 #zero
338 d = Decimal(0)
339 self.assertEqual(str(d), '0')
340
341 def test_explicit_from_string(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000342
343 #empty
344 self.assertEqual(str(Decimal('')), 'NaN')
345
346 #int
347 self.assertEqual(str(Decimal('45')), '45')
348
349 #float
350 self.assertEqual(str(Decimal('45.34')), '45.34')
351
352 #engineer notation
353 self.assertEqual(str(Decimal('45e2')), '4.5E+3')
354
355 #just not a number
356 self.assertEqual(str(Decimal('ugly')), 'NaN')
357
358 def test_explicit_from_tuples(self):
359
360 #zero
361 d = Decimal( (0, (0,), 0) )
362 self.assertEqual(str(d), '0')
363
364 #int
365 d = Decimal( (1, (4, 5), 0) )
366 self.assertEqual(str(d), '-45')
367
368 #float
369 d = Decimal( (0, (4, 5, 3, 4), -2) )
370 self.assertEqual(str(d), '45.34')
371
372 #weird
373 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
374 self.assertEqual(str(d), '-4.34913534E-17')
375
376 #wrong number of items
377 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1)) )
378
379 #bad sign
380 self.assertRaises(ValueError, Decimal, (8, (4, 3, 4, 9, 1), 2) )
381
382 #bad exp
383 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 'wrong!') )
384
385 #bad coefficients
386 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) )
387 self.assertRaises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) )
388
389 def test_explicit_from_Decimal(self):
390
391 #positive
392 d = Decimal(45)
393 e = Decimal(d)
394 self.assertEqual(str(e), '45')
395 self.assertNotEqual(id(d), id(e))
396
397 #very large positive
398 d = Decimal(500000123)
399 e = Decimal(d)
400 self.assertEqual(str(e), '500000123')
401 self.assertNotEqual(id(d), id(e))
402
403 #negative
404 d = Decimal(-45)
405 e = Decimal(d)
406 self.assertEqual(str(e), '-45')
407 self.assertNotEqual(id(d), id(e))
408
409 #zero
410 d = Decimal(0)
411 e = Decimal(d)
412 self.assertEqual(str(e), '0')
413 self.assertNotEqual(id(d), id(e))
414
415 def test_explicit_context_create_decimal(self):
416
417 nc = copy.copy(getcontext())
418 nc.prec = 3
419
420 # empty
Raymond Hettingerfed52962004-07-14 15:41:57 +0000421 d = Decimal()
422 self.assertEqual(str(d), '0')
423 d = nc.create_decimal()
424 self.assertEqual(str(d), '0')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000425
426 # from None
427 self.assertRaises(TypeError, nc.create_decimal, None)
428
429 # from int
430 d = nc.create_decimal(456)
431 self.failUnless(isinstance(d, Decimal))
432 self.assertEqual(nc.create_decimal(45678),
433 nc.create_decimal('457E+2'))
434
435 # from string
436 d = Decimal('456789')
437 self.assertEqual(str(d), '456789')
438 d = nc.create_decimal('456789')
439 self.assertEqual(str(d), '4.57E+5')
440
441 # from tuples
442 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
443 self.assertEqual(str(d), '-4.34913534E-17')
444 d = nc.create_decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
445 self.assertEqual(str(d), '-4.35E-17')
446
447 # from Decimal
448 prevdec = Decimal(500000123)
449 d = Decimal(prevdec)
450 self.assertEqual(str(d), '500000123')
451 d = nc.create_decimal(prevdec)
452 self.assertEqual(str(d), '5.00E+8')
453
454
455class DecimalImplicitConstructionTest(unittest.TestCase):
456 '''Unit tests for Implicit Construction cases of Decimal.'''
457
458 def test_implicit_from_None(self):
459 self.assertRaises(TypeError, eval, 'Decimal(5) + None', globals())
460
461 def test_implicit_from_int(self):
462 #normal
463 self.assertEqual(str(Decimal(5) + 45), '50')
464 #exceeding precision
465 self.assertEqual(Decimal(5) + 123456789000, Decimal(123456789000))
466
467 def test_implicit_from_string(self):
468 self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', globals())
469
470 def test_implicit_from_float(self):
471 self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', globals())
472
473 def test_implicit_from_Decimal(self):
474 self.assertEqual(Decimal(5) + Decimal(45), Decimal(50))
475
476
477class DecimalArithmeticOperatorsTest(unittest.TestCase):
478 '''Unit tests for all arithmetic operators, binary and unary.'''
479
480 def test_addition(self):
481
482 d1 = Decimal('-11.1')
483 d2 = Decimal('22.2')
484
485 #two Decimals
486 self.assertEqual(d1+d2, Decimal('11.1'))
487 self.assertEqual(d2+d1, Decimal('11.1'))
488
489 #with other type, left
490 c = d1 + 5
491 self.assertEqual(c, Decimal('-6.1'))
492 self.assertEqual(type(c), type(d1))
493
494 #with other type, right
495 c = 5 + d1
496 self.assertEqual(c, Decimal('-6.1'))
497 self.assertEqual(type(c), type(d1))
498
499 #inline with decimal
500 d1 += d2
501 self.assertEqual(d1, Decimal('11.1'))
502
503 #inline with other type
504 d1 += 5
505 self.assertEqual(d1, Decimal('16.1'))
506
507 def test_subtraction(self):
508
509 d1 = Decimal('-11.1')
510 d2 = Decimal('22.2')
511
512 #two Decimals
513 self.assertEqual(d1-d2, Decimal('-33.3'))
514 self.assertEqual(d2-d1, Decimal('33.3'))
515
516 #with other type, left
517 c = d1 - 5
518 self.assertEqual(c, Decimal('-16.1'))
519 self.assertEqual(type(c), type(d1))
520
521 #with other type, right
522 c = 5 - d1
523 self.assertEqual(c, Decimal('16.1'))
524 self.assertEqual(type(c), type(d1))
525
526 #inline with decimal
527 d1 -= d2
528 self.assertEqual(d1, Decimal('-33.3'))
529
530 #inline with other type
531 d1 -= 5
532 self.assertEqual(d1, Decimal('-38.3'))
533
534 def test_multiplication(self):
535
536 d1 = Decimal('-5')
537 d2 = Decimal('3')
538
539 #two Decimals
540 self.assertEqual(d1*d2, Decimal('-15'))
541 self.assertEqual(d2*d1, Decimal('-15'))
542
543 #with other type, left
544 c = d1 * 5
545 self.assertEqual(c, Decimal('-25'))
546 self.assertEqual(type(c), type(d1))
547
548 #with other type, right
549 c = 5 * d1
550 self.assertEqual(c, Decimal('-25'))
551 self.assertEqual(type(c), type(d1))
552
553 #inline with decimal
554 d1 *= d2
555 self.assertEqual(d1, Decimal('-15'))
556
557 #inline with other type
558 d1 *= 5
559 self.assertEqual(d1, Decimal('-75'))
560
561 def test_division(self):
562
563 d1 = Decimal('-5')
564 d2 = Decimal('2')
565
566 #two Decimals
567 self.assertEqual(d1/d2, Decimal('-2.5'))
568 self.assertEqual(d2/d1, Decimal('-0.4'))
569
570 #with other type, left
571 c = d1 / 4
572 self.assertEqual(c, Decimal('-1.25'))
573 self.assertEqual(type(c), type(d1))
574
575 #with other type, right
576 c = 4 / d1
577 self.assertEqual(c, Decimal('-0.8'))
578 self.assertEqual(type(c), type(d1))
579
580 #inline with decimal
581 d1 /= d2
582 self.assertEqual(d1, Decimal('-2.5'))
583
584 #inline with other type
585 d1 /= 4
586 self.assertEqual(d1, Decimal('-0.625'))
587
588 def test_floor_division(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000589
590 d1 = Decimal('5')
591 d2 = Decimal('2')
592
593 #two Decimals
594 self.assertEqual(d1//d2, Decimal('2'))
595 self.assertEqual(d2//d1, Decimal('0'))
596
597 #with other type, left
598 c = d1 // 4
599 self.assertEqual(c, Decimal('1'))
600 self.assertEqual(type(c), type(d1))
601
602 #with other type, right
603 c = 7 // d1
604 self.assertEqual(c, Decimal('1'))
605 self.assertEqual(type(c), type(d1))
606
607 #inline with decimal
608 d1 //= d2
609 self.assertEqual(d1, Decimal('2'))
610
611 #inline with other type
612 d1 //= 2
613 self.assertEqual(d1, Decimal('1'))
614
615 def test_powering(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000616
617 d1 = Decimal('5')
618 d2 = Decimal('2')
619
620 #two Decimals
621 self.assertEqual(d1**d2, Decimal('25'))
622 self.assertEqual(d2**d1, Decimal('32'))
623
624 #with other type, left
625 c = d1 ** 4
626 self.assertEqual(c, Decimal('625'))
627 self.assertEqual(type(c), type(d1))
628
629 #with other type, right
630 c = 7 ** d1
631 self.assertEqual(c, Decimal('16807'))
632 self.assertEqual(type(c), type(d1))
633
634 #inline with decimal
635 d1 **= d2
636 self.assertEqual(d1, Decimal('25'))
637
638 #inline with other type
639 d1 **= 4
640 self.assertEqual(d1, Decimal('390625'))
641
642 def test_module(self):
643
644 d1 = Decimal('5')
645 d2 = Decimal('2')
646
647 #two Decimals
648 self.assertEqual(d1%d2, Decimal('1'))
649 self.assertEqual(d2%d1, Decimal('2'))
650
651 #with other type, left
652 c = d1 % 4
653 self.assertEqual(c, Decimal('1'))
654 self.assertEqual(type(c), type(d1))
655
656 #with other type, right
657 c = 7 % d1
658 self.assertEqual(c, Decimal('2'))
659 self.assertEqual(type(c), type(d1))
660
661 #inline with decimal
662 d1 %= d2
663 self.assertEqual(d1, Decimal('1'))
664
665 #inline with other type
666 d1 %= 4
667 self.assertEqual(d1, Decimal('1'))
668
669 def test_floor_div_module(self):
670
671 d1 = Decimal('5')
672 d2 = Decimal('2')
673
674 #two Decimals
675 (p, q) = divmod(d1, d2)
676 self.assertEqual(p, Decimal('2'))
677 self.assertEqual(q, Decimal('1'))
678 self.assertEqual(type(p), type(d1))
679 self.assertEqual(type(q), type(d1))
680
681 #with other type, left
682 (p, q) = divmod(d1, 4)
683 self.assertEqual(p, Decimal('1'))
684 self.assertEqual(q, Decimal('1'))
685 self.assertEqual(type(p), type(d1))
686 self.assertEqual(type(q), type(d1))
687
688 #with other type, right
689 (p, q) = divmod(7, d1)
690 self.assertEqual(p, Decimal('1'))
691 self.assertEqual(q, Decimal('2'))
692 self.assertEqual(type(p), type(d1))
693 self.assertEqual(type(q), type(d1))
694
695 def test_unary_operators(self):
696 self.assertEqual(+Decimal(45), Decimal(+45)) # +
697 self.assertEqual(-Decimal(45), Decimal(-45)) # -
698 self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs
699
700
701# The following are two functions used to test threading in the next class
702
703def thfunc1(cls):
704 d1 = Decimal(1)
705 d3 = Decimal(3)
706 cls.assertEqual(d1/d3, Decimal('0.333333333'))
707 cls.synchro.wait()
708 cls.assertEqual(d1/d3, Decimal('0.333333333'))
709 cls.finish1.set()
710 return
711
712def thfunc2(cls):
713 d1 = Decimal(1)
714 d3 = Decimal(3)
715 cls.assertEqual(d1/d3, Decimal('0.333333333'))
716 thiscontext = getcontext()
717 thiscontext.prec = 18
718 cls.assertEqual(d1/d3, Decimal('0.333333333333333333'))
719 cls.synchro.set()
720 cls.finish2.set()
721 return
722
723
724class DecimalUseOfContextTest(unittest.TestCase):
725 '''Unit tests for Use of Context cases in Decimal.'''
726
727 import threading
728 # Take care executing this test from IDLE, there's an issue in threading
729 # that hangs IDLE and I couldn't find it
730
731 def test_threading(self):
732 #Test the "threading isolation" of a Context.
733
734 self.synchro = threading.Event()
735 self.finish1 = threading.Event()
736 self.finish2 = threading.Event()
737
738 th1 = threading.Thread(target=thfunc1, args=(self,))
739 th2 = threading.Thread(target=thfunc2, args=(self,))
740
741 th1.start()
742 th2.start()
743
744 self.finish1.wait()
745 self.finish1.wait()
746 return
747
748
749class DecimalUsabilityTest(unittest.TestCase):
750 '''Unit tests for Usability cases of Decimal.'''
751
752 def test_comparison_operators(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000753
754 da = Decimal('23.42')
755 db = Decimal('23.42')
756 dc = Decimal('45')
757
758 #two Decimals
759 self.failUnless(dc > da)
760 self.failUnless(dc >= da)
761 self.failUnless(da < dc)
762 self.failUnless(da <= dc)
763 self.failUnless(da == db)
764 self.failUnless(da != dc)
765 self.failUnless(da <= db)
766 self.failUnless(da >= db)
767 self.assertEqual(cmp(dc,da), 1)
768 self.assertEqual(cmp(da,dc), -1)
769 self.assertEqual(cmp(da,db), 0)
770
771 #a Decimal and an int
772 self.failUnless(dc > 23)
773 self.failUnless(23 < dc)
774 self.failUnless(dc == 45)
775 self.assertEqual(cmp(dc,23), 1)
776 self.assertEqual(cmp(23,dc), -1)
777 self.assertEqual(cmp(dc,45), 0)
778
779 #a Decimal and uncomparable
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000780 self.assertNotEqual(da, 'ugly')
781 self.assertNotEqual(da, 32.7)
782 self.assertNotEqual(da, object())
783 self.assertNotEqual(da, object)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000784
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000785 # sortable
786 a = map(Decimal, xrange(100))
787 b = a[:]
788 random.shuffle(a)
789 a.sort()
790 self.assertEqual(a, b)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000791
792 def test_copy_and_deepcopy_methods(self):
793 d = Decimal('43.24')
794 c = copy.copy(d)
795 self.assertEqual(id(c), id(d))
796 dc = copy.deepcopy(d)
797 self.assertEqual(id(dc), id(d))
798
799 def test_hash_method(self):
800 #just that it's hashable
801 hash(Decimal(23))
802 #the same hash that to an int
803 self.assertEqual(hash(Decimal(23)), hash(23))
804
805 def test_min_and_max_methods(self):
806
807 d1 = Decimal('15.32')
808 d2 = Decimal('28.5')
809 l1 = 15
810 l2 = 28
811
812 #between Decimals
813 self.failUnless(min(d1,d2) is d1)
814 self.failUnless(min(d2,d1) is d1)
815 self.failUnless(max(d1,d2) is d2)
816 self.failUnless(max(d2,d1) is d2)
817
818 #between Decimal and long
819 self.failUnless(min(d1,l2) is d1)
820 self.failUnless(min(l2,d1) is d1)
821 self.failUnless(max(l1,d2) is d2)
822 self.failUnless(max(d2,l1) is d2)
823
824 def test_as_nonzero(self):
825 #as false
826 self.failIf(Decimal(0))
827 #as true
828 self.failUnless(Decimal('0.372'))
829
830 def test_tostring_methods(self):
831 #Test str and repr methods.
832
833 d = Decimal('15.32')
834 self.assertEqual(str(d), '15.32') # str
835 self.assertEqual(repr(d), 'Decimal("15.32")') # repr
836
837 def test_tonum_methods(self):
838 #Test float, int and long methods.
839
840 d1 = Decimal('66')
841 d2 = Decimal('15.32')
842
843 #int
844 self.assertEqual(int(d1), 66)
845 self.assertEqual(int(d2), 15)
846
847 #long
848 self.assertEqual(long(d1), 66)
849 self.assertEqual(long(d2), 15)
850
851 #float
852 self.assertEqual(float(d1), 66)
853 self.assertEqual(float(d2), 15.32)
854
855 def test_eval_round_trip(self):
856
857 #with zero
858 d = Decimal( (0, (0,), 0) )
859 self.assertEqual(d, eval(repr(d)))
860
861 #int
862 d = Decimal( (1, (4, 5), 0) )
863 self.assertEqual(d, eval(repr(d)))
864
865 #float
866 d = Decimal( (0, (4, 5, 3, 4), -2) )
867 self.assertEqual(d, eval(repr(d)))
868
869 #weird
870 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
871 self.assertEqual(d, eval(repr(d)))
872
873 def test_as_tuple(self):
874
875 #with zero
876 d = Decimal(0)
877 self.assertEqual(d.as_tuple(), (0, (0,), 0) )
878
879 #int
880 d = Decimal(-45)
881 self.assertEqual(d.as_tuple(), (1, (4, 5), 0) )
882
883 #complicated string
884 d = Decimal("-4.34913534E-17")
885 self.assertEqual(d.as_tuple(), (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
886
887 #inf
888 d = Decimal("Infinity")
889 self.assertEqual(d.as_tuple(), (0, (0,), 'F') )
890
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000891 def test_immutability_operations(self):
892 # Do operations and check that it didn't change change internal objects.
893
894 d1 = Decimal('-25e55')
895 b1 = Decimal('-25e55')
896 d2 = Decimal('33e-33')
897 b2 = Decimal('33e-33')
898
899 def checkSameDec(operation, useOther=False):
900 if useOther:
901 eval("d1." + operation + "(d2)")
902 self.assertEqual(d1._sign, b1._sign)
903 self.assertEqual(d1._int, b1._int)
904 self.assertEqual(d1._exp, b1._exp)
905 self.assertEqual(d2._sign, b2._sign)
906 self.assertEqual(d2._int, b2._int)
907 self.assertEqual(d2._exp, b2._exp)
908 else:
909 eval("d1." + operation + "()")
910 self.assertEqual(d1._sign, b1._sign)
911 self.assertEqual(d1._int, b1._int)
912 self.assertEqual(d1._exp, b1._exp)
913 return
914
915 Decimal(d1)
916 self.assertEqual(d1._sign, b1._sign)
917 self.assertEqual(d1._int, b1._int)
918 self.assertEqual(d1._exp, b1._exp)
919
920 checkSameDec("__abs__")
921 checkSameDec("__add__", True)
922 checkSameDec("__div__", True)
923 checkSameDec("__divmod__", True)
924 checkSameDec("__cmp__", True)
925 checkSameDec("__float__")
926 checkSameDec("__floordiv__", True)
927 checkSameDec("__hash__")
928 checkSameDec("__int__")
929 checkSameDec("__long__")
930 checkSameDec("__mod__", True)
931 checkSameDec("__mul__", True)
932 checkSameDec("__neg__")
933 checkSameDec("__nonzero__")
934 checkSameDec("__pos__")
935 checkSameDec("__pow__", True)
936 checkSameDec("__radd__", True)
937 checkSameDec("__rdiv__", True)
938 checkSameDec("__rdivmod__", True)
939 checkSameDec("__repr__")
940 checkSameDec("__rfloordiv__", True)
941 checkSameDec("__rmod__", True)
942 checkSameDec("__rmul__", True)
943 checkSameDec("__rpow__", True)
944 checkSameDec("__rsub__", True)
945 checkSameDec("__str__")
946 checkSameDec("__sub__", True)
947 checkSameDec("__truediv__", True)
948 checkSameDec("adjusted")
949 checkSameDec("as_tuple")
950 checkSameDec("compare", True)
951 checkSameDec("max", True)
952 checkSameDec("min", True)
953 checkSameDec("normalize")
954 checkSameDec("quantize", True)
955 checkSameDec("remainder_near", True)
956 checkSameDec("same_quantum", True)
957 checkSameDec("sqrt")
958 checkSameDec("to_eng_string")
959 checkSameDec("to_integral")
960
961class DecimalPythonAPItests(unittest.TestCase):
962
963 def test_pickle(self):
964 d = Decimal('-3.141590000')
965 p = pickle.dumps(d)
966 e = pickle.loads(p)
967 self.assertEqual(d, e)
968
Raymond Hettinger5548be22004-07-05 18:49:38 +0000969 def test_int(self):
970 data = '1.0 1.1 1.9 2.0 0.0 -1.0 -1.1 -1.9 -2.0'.split()
971 for s in data:
972 # should work the same as for floats
973 self.assertEqual(int(Decimal(s)), int(float(s)))
974 # should work the same as ROUND_DOWN
975 d = Decimal(s)
976 r = Context(prec=1, rounding=ROUND_DOWN).create_decimal(s)
977 self.assertEqual(Decimal(int(d)), r)
978
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000979class ContextAPItests(unittest.TestCase):
980
981 def test_pickle(self):
982 c = Context()
983 e = pickle.loads(pickle.dumps(c))
984 for k in vars(c):
985 v1 = vars(c)[k]
986 v2 = vars(e)[k]
987 self.assertEqual(v1, v2)
988
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000989 def test_equality_with_other_types(self):
990 self.assert_(Decimal(10) in ['a', 1.0, Decimal(10), (1,2), {}])
991 self.assert_(Decimal(10) not in ['a', 1.0, (1,2), {}])
992
Raymond Hettinger955d2b22004-08-08 20:17:45 +0000993 def test_copy(self):
994 # All copies should be deep
995 c = Context()
996 d = c.copy()
997 self.assertNotEqual(id(c), id(d))
998 self.assertNotEqual(id(c.flags), id(d.flags))
999 self.assertNotEqual(id(c.traps), id(d.traps))
1000
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001001def test_main(arith=False, verbose=None):
1002 """ Execute the tests.
1003
Raymond Hettingered20ad82004-09-04 20:09:13 +00001004 Runs all arithmetic tests if arith is True or if the "decimal" resource
1005 is enabled in regrtest.py
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001006 """
Raymond Hettingered20ad82004-09-04 20:09:13 +00001007
1008 global TEST_ALL
1009 TEST_ALL = arith or is_resource_enabled('decimal')
1010
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001011 test_classes = [
1012 DecimalExplicitConstructionTest,
1013 DecimalImplicitConstructionTest,
1014 DecimalArithmeticOperatorsTest,
1015 DecimalUseOfContextTest,
1016 DecimalUsabilityTest,
1017 DecimalPythonAPItests,
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +00001018 ContextAPItests,
Raymond Hettingered20ad82004-09-04 20:09:13 +00001019 DecimalTest,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001020 ]
1021
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001022 run_unittest(*test_classes)
1023 import decimal as DecimalModule
1024 run_doctest(DecimalModule, verbose)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001025
1026
1027if __name__ == '__main__':
1028 # Calling with no arguments runs all tests.
Raymond Hettingered20ad82004-09-04 20:09:13 +00001029 # Calling with "Skip" will skip over 90% of the arithmetic tests.
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001030 if len(sys.argv) == 1:
1031 test_main(arith=True, verbose=True)
1032 elif len(sys.argv) == 2:
1033 arith = sys.argv[1].lower() != 'skip'
1034 test_main(arith=arith, verbose=True)
1035 else:
1036 raise ValueError("test called with wrong arguments, use test_Decimal [Skip]")