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