blob: 513ba167272dac2caef17e41715c6f3204feea73 [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"""
Thomas Wouters89f507f2006-12-13 04:49:30 +000026from __future__ import with_statement
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000027
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000028import unittest
29import glob
30import os, sys
31import pickle, copy
32from decimal import *
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000033from test.test_support import (TestSkipped, run_unittest, run_doctest,
34 is_resource_enabled)
Raymond Hettinger0aeac102004-07-05 22:53:03 +000035import random
Raymond Hettinger7e71fa52004-12-18 19:07:19 +000036try:
37 import threading
38except ImportError:
39 threading = None
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000040
Raymond Hettingerfed52962004-07-14 15:41:57 +000041# Useful Test Constant
Guido van Rossumc1f779c2007-07-03 08:25:58 +000042Signals = tuple(getcontext().flags.keys())
Raymond Hettingerfed52962004-07-14 15:41:57 +000043
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000044# Tests are built around these assumed context defaults.
45# test_main() restores the original context.
46def init():
47 global ORIGINAL_CONTEXT
48 ORIGINAL_CONTEXT = getcontext().copy()
49 DefaultContext.prec = 9
50 DefaultContext.rounding = ROUND_HALF_EVEN
51 DefaultContext.traps = dict.fromkeys(Signals, 0)
52 setcontext(DefaultContext)
Raymond Hettinger6ea48452004-07-03 12:26:21 +000053
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000054TESTDATADIR = 'decimaltestdata'
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +000055if __name__ == '__main__':
56 file = sys.argv[0]
57else:
58 file = __file__
59testdir = os.path.dirname(file) or os.curdir
Raymond Hettinger267b8682005-03-27 10:47:39 +000060directory = testdir + os.sep + TESTDATADIR + os.sep
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000061
Raymond Hettinger267b8682005-03-27 10:47:39 +000062skip_expected = not os.path.isdir(directory)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000063
64# Make sure it actually raises errors when not expected and caught in flags
65# Slower, since it runs some things several times.
66EXTENDEDERRORTEST = False
67
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000068#Map the test cases' error names to the actual errors
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000069ErrorNames = {'clamped' : Clamped,
Raymond Hettinger5aa478b2004-07-09 10:02:53 +000070 'conversion_syntax' : InvalidOperation,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000071 'division_by_zero' : DivisionByZero,
Raymond Hettinger5aa478b2004-07-09 10:02:53 +000072 'division_impossible' : InvalidOperation,
73 'division_undefined' : InvalidOperation,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000074 'inexact' : Inexact,
Raymond Hettinger5aa478b2004-07-09 10:02:53 +000075 'invalid_context' : InvalidOperation,
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000076 'invalid_operation' : InvalidOperation,
77 'overflow' : Overflow,
78 'rounded' : Rounded,
79 'subnormal' : Subnormal,
80 'underflow' : Underflow}
81
82
83def Nonfunction(*args):
84 """Doesn't do anything."""
85 return None
86
87RoundingDict = {'ceiling' : ROUND_CEILING, #Maps test-case names to roundings.
88 'down' : ROUND_DOWN,
89 'floor' : ROUND_FLOOR,
90 'half_down' : ROUND_HALF_DOWN,
91 'half_even' : ROUND_HALF_EVEN,
92 'half_up' : ROUND_HALF_UP,
Thomas Wouters1b7f8912007-09-19 03:06:30 +000093 'up' : ROUND_UP,
94 '05up' : ROUND_05UP}
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000095
96# Name adapter to be able to change the Decimal and Context
97# interface without changing the test files from Cowlishaw
98nameAdapter = {'toeng':'to_eng_string',
99 'tosci':'to_sci_string',
100 'samequantum':'same_quantum',
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000101 'tointegral':'to_integral_value',
102 'tointegralx':'to_integral_exact',
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000103 'remaindernear':'remainder_near',
104 'divideint':'divide_int',
105 'squareroot':'sqrt',
106 'apply':'_apply',
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000107 'class':'number_class',
108 'comparesig':'compare_signal',
109 'comparetotal':'compare_total',
110 'comparetotmag':'compare_total_mag',
111 'copyabs':'copy_abs',
112 'copy':'copy_decimal',
113 'copynegate':'copy_negate',
114 'copysign':'copy_sign',
115 'and':'logical_and',
116 'or':'logical_or',
117 'xor':'logical_xor',
118 'invert':'logical_invert',
119 'maxmag':'max_mag',
120 'minmag':'min_mag',
121 'nextminus':'next_minus',
122 'nextplus':'next_plus',
123 'nexttoward':'next_toward',
124 'reduce':'normalize',
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000125 }
126
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000127# For some operations (currently exp, ln, log10, power), the decNumber
128# reference implementation imposes additional restrictions on the
129# context and operands. These restrictions are not part of the
130# specification; however, the effect of these restrictions does show
131# up in some of the testcases. We skip testcases that violate these
132# restrictions, since Decimal behaves differently from decNumber for
133# these testcases so these testcases would otherwise fail.
134
135decNumberRestricted = ('power', 'ln', 'log10', 'exp')
136DEC_MAX_MATH = 999999
137def outside_decNumber_bounds(v, context):
138 if (context.prec > DEC_MAX_MATH or
139 context.Emax > DEC_MAX_MATH or
140 -context.Emin > DEC_MAX_MATH):
141 return True
142 if not v._is_special and v and (
143 len(v._int) > DEC_MAX_MATH or
144 v.adjusted() > DEC_MAX_MATH or
145 v.adjusted() < 1-2*DEC_MAX_MATH):
146 return True
147 return False
148
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000149class DecimalTest(unittest.TestCase):
150 """Class which tests the Decimal class against the test cases.
151
152 Changed for unittest.
153 """
154 def setUp(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000155 self.context = Context()
Raymond Hettingerbf440692004-07-10 14:14:37 +0000156 for key in DefaultContext.traps.keys():
157 DefaultContext.traps[key] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000158 self.ignore_list = ['#']
159 # Basically, a # means return NaN InvalidOperation.
160 # Different from a sNaN in trim
161
162 self.ChangeDict = {'precision' : self.change_precision,
163 'rounding' : self.change_rounding_method,
164 'maxexponent' : self.change_max_exponent,
165 'minexponent' : self.change_min_exponent,
166 'clamp' : self.change_clamp}
167
168 def tearDown(self):
169 """Cleaning up enviroment."""
170 # leaving context in original state
Raymond Hettingerbf440692004-07-10 14:14:37 +0000171 for key in DefaultContext.traps.keys():
172 DefaultContext.traps[key] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000173 return
174
175 def eval_file(self, file):
176 global skip_expected
177 if skip_expected:
178 raise TestSkipped
179 return
Neal Norwitz70967602006-03-17 08:29:44 +0000180 for line in open(file):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000181 line = line.replace('\r\n', '').replace('\n', '')
Raymond Hettinger5aa478b2004-07-09 10:02:53 +0000182 #print line
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000183 try:
184 t = self.eval_line(line)
Guido van Rossumb940e112007-01-10 16:19:56 +0000185 except DecimalException as exception:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000186 #Exception raised where there shoudn't have been one.
187 self.fail('Exception "'+exception.__class__.__name__ + '" raised on line '+line)
188
189 return
190
191 def eval_line(self, s):
192 if s.find(' -> ') >= 0 and s[:2] != '--' and not s.startswith(' --'):
193 s = (s.split('->')[0] + '->' +
194 s.split('->')[1].split('--')[0]).strip()
195 else:
196 s = s.split('--')[0].strip()
197
198 for ignore in self.ignore_list:
199 if s.find(ignore) >= 0:
200 #print s.split()[0], 'NotImplemented--', ignore
201 return
202 if not s:
203 return
204 elif ':' in s:
205 return self.eval_directive(s)
206 else:
207 return self.eval_equation(s)
208
209 def eval_directive(self, s):
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000210 funct, value = (x.strip().lower() for x in s.split(':'))
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000211 if funct == 'rounding':
212 value = RoundingDict[value]
213 else:
214 try:
215 value = int(value)
216 except ValueError:
217 pass
218
219 funct = self.ChangeDict.get(funct, Nonfunction)
220 funct(value)
221
222 def eval_equation(self, s):
223 #global DEFAULT_PRECISION
224 #print DEFAULT_PRECISION
Raymond Hettingered20ad82004-09-04 20:09:13 +0000225
226 if not TEST_ALL and random.random() < 0.90:
227 return
228
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000229 try:
230 Sides = s.split('->')
231 L = Sides[0].strip().split()
232 id = L[0]
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000233 if DEBUG:
234 print("Test ", id, end=" ")
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000235 funct = L[1].lower()
236 valstemp = L[2:]
237 L = Sides[1].strip().split()
238 ans = L[0]
239 exceptions = L[1:]
240 except (TypeError, AttributeError, IndexError):
Raymond Hettingerd87ac8f2004-07-09 10:52:54 +0000241 raise InvalidOperation
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000242 def FixQuotes(val):
243 val = val.replace("''", 'SingleQuote').replace('""', 'DoubleQuote')
244 val = val.replace("'", '').replace('"', '')
245 val = val.replace('SingleQuote', "'").replace('DoubleQuote', '"')
246 return val
247 fname = nameAdapter.get(funct, funct)
248 if fname == 'rescale':
249 return
250 funct = getattr(self.context, fname)
251 vals = []
252 conglomerate = ''
253 quote = 0
254 theirexceptions = [ErrorNames[x.lower()] for x in exceptions]
255
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000256 for exception in Signals:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000257 self.context.traps[exception] = 1 #Catch these bugs...
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000258 for exception in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000259 self.context.traps[exception] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000260 for i, val in enumerate(valstemp):
261 if val.count("'") % 2 == 1:
262 quote = 1 - quote
263 if quote:
264 conglomerate = conglomerate + ' ' + val
265 continue
266 else:
267 val = conglomerate + val
268 conglomerate = ''
269 v = FixQuotes(val)
270 if fname in ('to_sci_string', 'to_eng_string'):
271 if EXTENDEDERRORTEST:
272 for error in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000273 self.context.traps[error] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000274 try:
275 funct(self.context.create_decimal(v))
276 except error:
277 pass
Guido van Rossumb940e112007-01-10 16:19:56 +0000278 except Signals as e:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000279 self.fail("Raised %s in %s when %s disabled" % \
280 (e, s, error))
281 else:
282 self.fail("Did not raise %s in %s" % (error, s))
Raymond Hettingerbf440692004-07-10 14:14:37 +0000283 self.context.traps[error] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000284 v = self.context.create_decimal(v)
285 else:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000286 v = Decimal(v, self.context)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000287 vals.append(v)
288
289 ans = FixQuotes(ans)
290
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000291 # skip tests that are related to bounds imposed in the decNumber
292 # reference implementation
293 if fname in decNumberRestricted:
294 if fname == 'power':
295 if not (vals[1]._isinteger() and
296 -1999999997 <= vals[1] <= 999999999):
297 if outside_decNumber_bounds(vals[0], self.context) or \
298 outside_decNumber_bounds(vals[1], self.context):
299 #print "Skipping test %s" % s
300 return
301 else:
302 if outside_decNumber_bounds(vals[0], self.context):
303 #print "Skipping test %s" % s
304 return
305
306
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000307 if EXTENDEDERRORTEST and fname not in ('to_sci_string', 'to_eng_string'):
308 for error in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000309 self.context.traps[error] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000310 try:
311 funct(*vals)
312 except error:
313 pass
Guido van Rossumb940e112007-01-10 16:19:56 +0000314 except Signals as e:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000315 self.fail("Raised %s in %s when %s disabled" % \
316 (e, s, error))
317 else:
318 self.fail("Did not raise %s in %s" % (error, s))
Raymond Hettingerbf440692004-07-10 14:14:37 +0000319 self.context.traps[error] = 0
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000320 if DEBUG:
321 print("--", self.context)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000322 try:
323 result = str(funct(*vals))
324 if fname == 'same_quantum':
325 result = str(int(eval(result))) # 'True', 'False' -> '1', '0'
Guido van Rossumb940e112007-01-10 16:19:56 +0000326 except Signals as error:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000327 self.fail("Raised %s in %s" % (error, s))
328 except: #Catch any error long enough to state the test case.
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000329 print("ERROR:", s)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000330 raise
331
332 myexceptions = self.getexceptions()
Raymond Hettingerbf440692004-07-10 14:14:37 +0000333 self.context.clear_flags()
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000334
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000335 myexceptions.sort(key=repr)
336 theirexceptions.sort(key=repr)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000337
338 self.assertEqual(result, ans,
339 'Incorrect answer for ' + s + ' -- got ' + result)
340 self.assertEqual(myexceptions, theirexceptions,
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000341 'Incorrect flags set in ' + s + ' -- got ' + str(myexceptions))
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000342 return
343
344 def getexceptions(self):
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000345 return [e for e in Signals if self.context.flags[e]]
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000346
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000347 def change_precision(self, prec):
348 self.context.prec = prec
349 def change_rounding_method(self, rounding):
350 self.context.rounding = rounding
351 def change_min_exponent(self, exp):
352 self.context.Emin = exp
353 def change_max_exponent(self, exp):
354 self.context.Emax = exp
355 def change_clamp(self, clamp):
356 self.context._clamp = clamp
357
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000358
359
360# The following classes test the behaviour of Decimal according to PEP 327
361
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000362class DecimalExplicitConstructionTest(unittest.TestCase):
363 '''Unit tests for Explicit Construction cases of Decimal.'''
364
365 def test_explicit_empty(self):
366 self.assertEqual(Decimal(), Decimal("0"))
367
368 def test_explicit_from_None(self):
369 self.assertRaises(TypeError, Decimal, None)
370
371 def test_explicit_from_int(self):
372
373 #positive
374 d = Decimal(45)
375 self.assertEqual(str(d), '45')
376
377 #very large positive
378 d = Decimal(500000123)
379 self.assertEqual(str(d), '500000123')
380
381 #negative
382 d = Decimal(-45)
383 self.assertEqual(str(d), '-45')
384
385 #zero
386 d = Decimal(0)
387 self.assertEqual(str(d), '0')
388
389 def test_explicit_from_string(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000390
391 #empty
392 self.assertEqual(str(Decimal('')), 'NaN')
393
394 #int
395 self.assertEqual(str(Decimal('45')), '45')
396
397 #float
398 self.assertEqual(str(Decimal('45.34')), '45.34')
399
400 #engineer notation
401 self.assertEqual(str(Decimal('45e2')), '4.5E+3')
402
403 #just not a number
404 self.assertEqual(str(Decimal('ugly')), 'NaN')
405
406 def test_explicit_from_tuples(self):
407
408 #zero
409 d = Decimal( (0, (0,), 0) )
410 self.assertEqual(str(d), '0')
411
412 #int
413 d = Decimal( (1, (4, 5), 0) )
414 self.assertEqual(str(d), '-45')
415
416 #float
417 d = Decimal( (0, (4, 5, 3, 4), -2) )
418 self.assertEqual(str(d), '45.34')
419
420 #weird
421 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
422 self.assertEqual(str(d), '-4.34913534E-17')
423
424 #wrong number of items
425 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1)) )
426
427 #bad sign
428 self.assertRaises(ValueError, Decimal, (8, (4, 3, 4, 9, 1), 2) )
429
430 #bad exp
431 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 'wrong!') )
432
433 #bad coefficients
434 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) )
435 self.assertRaises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) )
436
437 def test_explicit_from_Decimal(self):
438
439 #positive
440 d = Decimal(45)
441 e = Decimal(d)
442 self.assertEqual(str(e), '45')
443 self.assertNotEqual(id(d), id(e))
444
445 #very large positive
446 d = Decimal(500000123)
447 e = Decimal(d)
448 self.assertEqual(str(e), '500000123')
449 self.assertNotEqual(id(d), id(e))
450
451 #negative
452 d = Decimal(-45)
453 e = Decimal(d)
454 self.assertEqual(str(e), '-45')
455 self.assertNotEqual(id(d), id(e))
456
457 #zero
458 d = Decimal(0)
459 e = Decimal(d)
460 self.assertEqual(str(e), '0')
461 self.assertNotEqual(id(d), id(e))
462
463 def test_explicit_context_create_decimal(self):
464
465 nc = copy.copy(getcontext())
466 nc.prec = 3
467
468 # empty
Raymond Hettingerfed52962004-07-14 15:41:57 +0000469 d = Decimal()
470 self.assertEqual(str(d), '0')
471 d = nc.create_decimal()
472 self.assertEqual(str(d), '0')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000473
474 # from None
475 self.assertRaises(TypeError, nc.create_decimal, None)
476
477 # from int
478 d = nc.create_decimal(456)
479 self.failUnless(isinstance(d, Decimal))
480 self.assertEqual(nc.create_decimal(45678),
481 nc.create_decimal('457E+2'))
482
483 # from string
484 d = Decimal('456789')
485 self.assertEqual(str(d), '456789')
486 d = nc.create_decimal('456789')
487 self.assertEqual(str(d), '4.57E+5')
488
489 # from tuples
490 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
491 self.assertEqual(str(d), '-4.34913534E-17')
492 d = nc.create_decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
493 self.assertEqual(str(d), '-4.35E-17')
494
495 # from Decimal
496 prevdec = Decimal(500000123)
497 d = Decimal(prevdec)
498 self.assertEqual(str(d), '500000123')
499 d = nc.create_decimal(prevdec)
500 self.assertEqual(str(d), '5.00E+8')
501
502
503class DecimalImplicitConstructionTest(unittest.TestCase):
504 '''Unit tests for Implicit Construction cases of Decimal.'''
505
506 def test_implicit_from_None(self):
507 self.assertRaises(TypeError, eval, 'Decimal(5) + None', globals())
508
509 def test_implicit_from_int(self):
510 #normal
511 self.assertEqual(str(Decimal(5) + 45), '50')
512 #exceeding precision
513 self.assertEqual(Decimal(5) + 123456789000, Decimal(123456789000))
514
515 def test_implicit_from_string(self):
516 self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', globals())
517
518 def test_implicit_from_float(self):
519 self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', globals())
520
521 def test_implicit_from_Decimal(self):
522 self.assertEqual(Decimal(5) + Decimal(45), Decimal(50))
523
Raymond Hettinger267b8682005-03-27 10:47:39 +0000524 def test_rop(self):
525 # Allow other classes to be trained to interact with Decimals
526 class E:
527 def __divmod__(self, other):
528 return 'divmod ' + str(other)
529 def __rdivmod__(self, other):
530 return str(other) + ' rdivmod'
531 def __lt__(self, other):
532 return 'lt ' + str(other)
533 def __gt__(self, other):
534 return 'gt ' + str(other)
535 def __le__(self, other):
536 return 'le ' + str(other)
537 def __ge__(self, other):
538 return 'ge ' + str(other)
539 def __eq__(self, other):
540 return 'eq ' + str(other)
541 def __ne__(self, other):
542 return 'ne ' + str(other)
543
544 self.assertEqual(divmod(E(), Decimal(10)), 'divmod 10')
545 self.assertEqual(divmod(Decimal(10), E()), '10 rdivmod')
546 self.assertEqual(eval('Decimal(10) < E()'), 'gt 10')
547 self.assertEqual(eval('Decimal(10) > E()'), 'lt 10')
548 self.assertEqual(eval('Decimal(10) <= E()'), 'ge 10')
549 self.assertEqual(eval('Decimal(10) >= E()'), 'le 10')
550 self.assertEqual(eval('Decimal(10) == E()'), 'eq 10')
551 self.assertEqual(eval('Decimal(10) != E()'), 'ne 10')
552
553 # insert operator methods and then exercise them
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000554 oplist = [
555 ('+', '__add__', '__radd__'),
556 ('-', '__sub__', '__rsub__'),
557 ('*', '__mul__', '__rmul__'),
Thomas Woutersdcc6d322006-04-21 11:30:52 +0000558 ('/', '__truediv__', '__rtruediv__'),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000559 ('%', '__mod__', '__rmod__'),
560 ('//', '__floordiv__', '__rfloordiv__'),
561 ('**', '__pow__', '__rpow__')
562 ]
Raymond Hettinger267b8682005-03-27 10:47:39 +0000563
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000564 for sym, lop, rop in oplist:
Raymond Hettinger267b8682005-03-27 10:47:39 +0000565 setattr(E, lop, lambda self, other: 'str' + lop + str(other))
566 setattr(E, rop, lambda self, other: str(other) + rop + 'str')
567 self.assertEqual(eval('E()' + sym + 'Decimal(10)'),
568 'str' + lop + '10')
569 self.assertEqual(eval('Decimal(10)' + sym + 'E()'),
570 '10' + rop + 'str')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000571
572class DecimalArithmeticOperatorsTest(unittest.TestCase):
573 '''Unit tests for all arithmetic operators, binary and unary.'''
574
575 def test_addition(self):
576
577 d1 = Decimal('-11.1')
578 d2 = Decimal('22.2')
579
580 #two Decimals
581 self.assertEqual(d1+d2, Decimal('11.1'))
582 self.assertEqual(d2+d1, Decimal('11.1'))
583
584 #with other type, left
585 c = d1 + 5
586 self.assertEqual(c, Decimal('-6.1'))
587 self.assertEqual(type(c), type(d1))
588
589 #with other type, right
590 c = 5 + d1
591 self.assertEqual(c, Decimal('-6.1'))
592 self.assertEqual(type(c), type(d1))
593
594 #inline with decimal
595 d1 += d2
596 self.assertEqual(d1, Decimal('11.1'))
597
598 #inline with other type
599 d1 += 5
600 self.assertEqual(d1, Decimal('16.1'))
601
602 def test_subtraction(self):
603
604 d1 = Decimal('-11.1')
605 d2 = Decimal('22.2')
606
607 #two Decimals
608 self.assertEqual(d1-d2, Decimal('-33.3'))
609 self.assertEqual(d2-d1, Decimal('33.3'))
610
611 #with other type, left
612 c = d1 - 5
613 self.assertEqual(c, Decimal('-16.1'))
614 self.assertEqual(type(c), type(d1))
615
616 #with other type, right
617 c = 5 - d1
618 self.assertEqual(c, Decimal('16.1'))
619 self.assertEqual(type(c), type(d1))
620
621 #inline with decimal
622 d1 -= d2
623 self.assertEqual(d1, Decimal('-33.3'))
624
625 #inline with other type
626 d1 -= 5
627 self.assertEqual(d1, Decimal('-38.3'))
628
629 def test_multiplication(self):
630
631 d1 = Decimal('-5')
632 d2 = Decimal('3')
633
634 #two Decimals
635 self.assertEqual(d1*d2, Decimal('-15'))
636 self.assertEqual(d2*d1, Decimal('-15'))
637
638 #with other type, left
639 c = d1 * 5
640 self.assertEqual(c, Decimal('-25'))
641 self.assertEqual(type(c), type(d1))
642
643 #with other type, right
644 c = 5 * d1
645 self.assertEqual(c, Decimal('-25'))
646 self.assertEqual(type(c), type(d1))
647
648 #inline with decimal
649 d1 *= d2
650 self.assertEqual(d1, Decimal('-15'))
651
652 #inline with other type
653 d1 *= 5
654 self.assertEqual(d1, Decimal('-75'))
655
656 def test_division(self):
657
658 d1 = Decimal('-5')
659 d2 = Decimal('2')
660
661 #two Decimals
662 self.assertEqual(d1/d2, Decimal('-2.5'))
663 self.assertEqual(d2/d1, Decimal('-0.4'))
664
665 #with other type, left
666 c = d1 / 4
667 self.assertEqual(c, Decimal('-1.25'))
668 self.assertEqual(type(c), type(d1))
669
670 #with other type, right
671 c = 4 / d1
672 self.assertEqual(c, Decimal('-0.8'))
673 self.assertEqual(type(c), type(d1))
674
675 #inline with decimal
676 d1 /= d2
677 self.assertEqual(d1, Decimal('-2.5'))
678
679 #inline with other type
680 d1 /= 4
681 self.assertEqual(d1, Decimal('-0.625'))
682
683 def test_floor_division(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000684
685 d1 = Decimal('5')
686 d2 = Decimal('2')
687
688 #two Decimals
689 self.assertEqual(d1//d2, Decimal('2'))
690 self.assertEqual(d2//d1, Decimal('0'))
691
692 #with other type, left
693 c = d1 // 4
694 self.assertEqual(c, Decimal('1'))
695 self.assertEqual(type(c), type(d1))
696
697 #with other type, right
698 c = 7 // d1
699 self.assertEqual(c, Decimal('1'))
700 self.assertEqual(type(c), type(d1))
701
702 #inline with decimal
703 d1 //= d2
704 self.assertEqual(d1, Decimal('2'))
705
706 #inline with other type
707 d1 //= 2
708 self.assertEqual(d1, Decimal('1'))
709
710 def test_powering(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000711
712 d1 = Decimal('5')
713 d2 = Decimal('2')
714
715 #two Decimals
716 self.assertEqual(d1**d2, Decimal('25'))
717 self.assertEqual(d2**d1, Decimal('32'))
718
719 #with other type, left
720 c = d1 ** 4
721 self.assertEqual(c, Decimal('625'))
722 self.assertEqual(type(c), type(d1))
723
724 #with other type, right
725 c = 7 ** d1
726 self.assertEqual(c, Decimal('16807'))
727 self.assertEqual(type(c), type(d1))
728
729 #inline with decimal
730 d1 **= d2
731 self.assertEqual(d1, Decimal('25'))
732
733 #inline with other type
734 d1 **= 4
735 self.assertEqual(d1, Decimal('390625'))
736
737 def test_module(self):
738
739 d1 = Decimal('5')
740 d2 = Decimal('2')
741
742 #two Decimals
743 self.assertEqual(d1%d2, Decimal('1'))
744 self.assertEqual(d2%d1, Decimal('2'))
745
746 #with other type, left
747 c = d1 % 4
748 self.assertEqual(c, Decimal('1'))
749 self.assertEqual(type(c), type(d1))
750
751 #with other type, right
752 c = 7 % d1
753 self.assertEqual(c, Decimal('2'))
754 self.assertEqual(type(c), type(d1))
755
756 #inline with decimal
757 d1 %= d2
758 self.assertEqual(d1, Decimal('1'))
759
760 #inline with other type
761 d1 %= 4
762 self.assertEqual(d1, Decimal('1'))
763
764 def test_floor_div_module(self):
765
766 d1 = Decimal('5')
767 d2 = Decimal('2')
768
769 #two Decimals
770 (p, q) = divmod(d1, d2)
771 self.assertEqual(p, Decimal('2'))
772 self.assertEqual(q, Decimal('1'))
773 self.assertEqual(type(p), type(d1))
774 self.assertEqual(type(q), type(d1))
775
776 #with other type, left
777 (p, q) = divmod(d1, 4)
778 self.assertEqual(p, Decimal('1'))
779 self.assertEqual(q, Decimal('1'))
780 self.assertEqual(type(p), type(d1))
781 self.assertEqual(type(q), type(d1))
782
783 #with other type, right
784 (p, q) = divmod(7, d1)
785 self.assertEqual(p, Decimal('1'))
786 self.assertEqual(q, Decimal('2'))
787 self.assertEqual(type(p), type(d1))
788 self.assertEqual(type(q), type(d1))
789
790 def test_unary_operators(self):
791 self.assertEqual(+Decimal(45), Decimal(+45)) # +
792 self.assertEqual(-Decimal(45), Decimal(-45)) # -
793 self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs
794
795
796# The following are two functions used to test threading in the next class
797
798def thfunc1(cls):
799 d1 = Decimal(1)
800 d3 = Decimal(3)
801 cls.assertEqual(d1/d3, Decimal('0.333333333'))
802 cls.synchro.wait()
803 cls.assertEqual(d1/d3, Decimal('0.333333333'))
804 cls.finish1.set()
805 return
806
807def thfunc2(cls):
808 d1 = Decimal(1)
809 d3 = Decimal(3)
810 cls.assertEqual(d1/d3, Decimal('0.333333333'))
811 thiscontext = getcontext()
812 thiscontext.prec = 18
813 cls.assertEqual(d1/d3, Decimal('0.333333333333333333'))
814 cls.synchro.set()
815 cls.finish2.set()
816 return
817
818
819class DecimalUseOfContextTest(unittest.TestCase):
820 '''Unit tests for Use of Context cases in Decimal.'''
821
Raymond Hettinger7e71fa52004-12-18 19:07:19 +0000822 try:
823 import threading
824 except ImportError:
825 threading = None
826
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000827 # Take care executing this test from IDLE, there's an issue in threading
828 # that hangs IDLE and I couldn't find it
829
830 def test_threading(self):
831 #Test the "threading isolation" of a Context.
832
833 self.synchro = threading.Event()
834 self.finish1 = threading.Event()
835 self.finish2 = threading.Event()
836
837 th1 = threading.Thread(target=thfunc1, args=(self,))
838 th2 = threading.Thread(target=thfunc2, args=(self,))
839
840 th1.start()
841 th2.start()
842
843 self.finish1.wait()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000844 self.finish2.wait()
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000845 return
846
Raymond Hettinger7e71fa52004-12-18 19:07:19 +0000847 if threading is None:
848 del test_threading
849
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000850
851class DecimalUsabilityTest(unittest.TestCase):
852 '''Unit tests for Usability cases of Decimal.'''
853
854 def test_comparison_operators(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000855
856 da = Decimal('23.42')
857 db = Decimal('23.42')
858 dc = Decimal('45')
859
860 #two Decimals
861 self.failUnless(dc > da)
862 self.failUnless(dc >= da)
863 self.failUnless(da < dc)
864 self.failUnless(da <= dc)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000865 self.assertEqual(da, db)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000866 self.failUnless(da != dc)
867 self.failUnless(da <= db)
868 self.failUnless(da >= db)
869 self.assertEqual(cmp(dc,da), 1)
870 self.assertEqual(cmp(da,dc), -1)
871 self.assertEqual(cmp(da,db), 0)
872
873 #a Decimal and an int
874 self.failUnless(dc > 23)
875 self.failUnless(23 < dc)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000876 self.assertEqual(dc, 45)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000877 self.assertEqual(cmp(dc,23), 1)
878 self.assertEqual(cmp(23,dc), -1)
879 self.assertEqual(cmp(dc,45), 0)
880
881 #a Decimal and uncomparable
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000882 self.assertNotEqual(da, 'ugly')
883 self.assertNotEqual(da, 32.7)
884 self.assertNotEqual(da, object())
885 self.assertNotEqual(da, object)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000886
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000887 # sortable
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000888 a = list(map(Decimal, range(100)))
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000889 b = a[:]
890 random.shuffle(a)
891 a.sort()
892 self.assertEqual(a, b)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000893
894 def test_copy_and_deepcopy_methods(self):
895 d = Decimal('43.24')
896 c = copy.copy(d)
897 self.assertEqual(id(c), id(d))
898 dc = copy.deepcopy(d)
899 self.assertEqual(id(dc), id(d))
900
901 def test_hash_method(self):
902 #just that it's hashable
903 hash(Decimal(23))
Thomas Wouters8ce81f72007-09-20 18:22:40 +0000904
905 test_values = [Decimal(sign*(2**m + n))
906 for m in [0, 14, 15, 16, 17, 30, 31,
907 32, 33, 62, 63, 64, 65, 66]
908 for n in range(-10, 10)
909 for sign in [-1, 1]]
910 test_values.extend([
911 Decimal("-0"), # zeros
912 Decimal("0.00"),
913 Decimal("-0.000"),
914 Decimal("0E10"),
915 Decimal("-0E12"),
916 Decimal("10.0"), # negative exponent
917 Decimal("-23.00000"),
918 Decimal("1230E100"), # positive exponent
919 Decimal("-4.5678E50"),
920 # a value for which hash(n) != hash(n % (2**64-1))
921 # in Python pre-2.6
922 Decimal(2**64 + 2**32 - 1),
923 # selection of values which fail with the old (before
924 # version 2.6) long.__hash__
925 Decimal("1.634E100"),
926 Decimal("90.697E100"),
927 Decimal("188.83E100"),
928 Decimal("1652.9E100"),
929 Decimal("56531E100"),
930 ])
931
932 # check that hash(d) == hash(int(d)) for integral values
933 for value in test_values:
934 self.assertEqual(hash(value), hash(int(value)))
935
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000936 #the same hash that to an int
937 self.assertEqual(hash(Decimal(23)), hash(23))
Raymond Hettingerbea3f6f2005-03-15 04:59:17 +0000938 self.assertRaises(TypeError, hash, Decimal('NaN'))
939 self.assert_(hash(Decimal('Inf')))
940 self.assert_(hash(Decimal('-Inf')))
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000941
942 def test_min_and_max_methods(self):
943
944 d1 = Decimal('15.32')
945 d2 = Decimal('28.5')
946 l1 = 15
947 l2 = 28
948
949 #between Decimals
950 self.failUnless(min(d1,d2) is d1)
951 self.failUnless(min(d2,d1) is d1)
952 self.failUnless(max(d1,d2) is d2)
953 self.failUnless(max(d2,d1) is d2)
954
955 #between Decimal and long
956 self.failUnless(min(d1,l2) is d1)
957 self.failUnless(min(l2,d1) is d1)
958 self.failUnless(max(l1,d2) is d2)
959 self.failUnless(max(d2,l1) is d2)
960
961 def test_as_nonzero(self):
962 #as false
963 self.failIf(Decimal(0))
964 #as true
965 self.failUnless(Decimal('0.372'))
966
967 def test_tostring_methods(self):
968 #Test str and repr methods.
969
970 d = Decimal('15.32')
971 self.assertEqual(str(d), '15.32') # str
972 self.assertEqual(repr(d), 'Decimal("15.32")') # repr
973
974 def test_tonum_methods(self):
975 #Test float, int and long methods.
976
977 d1 = Decimal('66')
978 d2 = Decimal('15.32')
979
980 #int
981 self.assertEqual(int(d1), 66)
982 self.assertEqual(int(d2), 15)
983
984 #long
Guido van Rossume2a383d2007-01-15 16:59:06 +0000985 self.assertEqual(int(d1), 66)
986 self.assertEqual(int(d2), 15)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000987
988 #float
989 self.assertEqual(float(d1), 66)
990 self.assertEqual(float(d2), 15.32)
991
992 def test_eval_round_trip(self):
993
994 #with zero
995 d = Decimal( (0, (0,), 0) )
996 self.assertEqual(d, eval(repr(d)))
997
998 #int
999 d = Decimal( (1, (4, 5), 0) )
1000 self.assertEqual(d, eval(repr(d)))
1001
1002 #float
1003 d = Decimal( (0, (4, 5, 3, 4), -2) )
1004 self.assertEqual(d, eval(repr(d)))
1005
1006 #weird
1007 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
1008 self.assertEqual(d, eval(repr(d)))
1009
1010 def test_as_tuple(self):
1011
1012 #with zero
1013 d = Decimal(0)
1014 self.assertEqual(d.as_tuple(), (0, (0,), 0) )
1015
1016 #int
1017 d = Decimal(-45)
1018 self.assertEqual(d.as_tuple(), (1, (4, 5), 0) )
1019
1020 #complicated string
1021 d = Decimal("-4.34913534E-17")
1022 self.assertEqual(d.as_tuple(), (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
1023
1024 #inf
1025 d = Decimal("Infinity")
1026 self.assertEqual(d.as_tuple(), (0, (0,), 'F') )
1027
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001028 def test_immutability_operations(self):
1029 # Do operations and check that it didn't change change internal objects.
1030
1031 d1 = Decimal('-25e55')
1032 b1 = Decimal('-25e55')
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001033 d2 = Decimal('33e+33')
1034 b2 = Decimal('33e+33')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001035
1036 def checkSameDec(operation, useOther=False):
1037 if useOther:
1038 eval("d1." + operation + "(d2)")
1039 self.assertEqual(d1._sign, b1._sign)
1040 self.assertEqual(d1._int, b1._int)
1041 self.assertEqual(d1._exp, b1._exp)
1042 self.assertEqual(d2._sign, b2._sign)
1043 self.assertEqual(d2._int, b2._int)
1044 self.assertEqual(d2._exp, b2._exp)
1045 else:
1046 eval("d1." + operation + "()")
1047 self.assertEqual(d1._sign, b1._sign)
1048 self.assertEqual(d1._int, b1._int)
1049 self.assertEqual(d1._exp, b1._exp)
1050 return
1051
1052 Decimal(d1)
1053 self.assertEqual(d1._sign, b1._sign)
1054 self.assertEqual(d1._int, b1._int)
1055 self.assertEqual(d1._exp, b1._exp)
1056
1057 checkSameDec("__abs__")
1058 checkSameDec("__add__", True)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001059 checkSameDec("__divmod__", True)
1060 checkSameDec("__cmp__", True)
1061 checkSameDec("__float__")
1062 checkSameDec("__floordiv__", True)
1063 checkSameDec("__hash__")
1064 checkSameDec("__int__")
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001065 checkSameDec("__mod__", True)
1066 checkSameDec("__mul__", True)
1067 checkSameDec("__neg__")
Jack Diederich4dafcc42006-11-28 19:15:13 +00001068 checkSameDec("__bool__")
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001069 checkSameDec("__pos__")
1070 checkSameDec("__pow__", True)
1071 checkSameDec("__radd__", True)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001072 checkSameDec("__rdivmod__", True)
1073 checkSameDec("__repr__")
1074 checkSameDec("__rfloordiv__", True)
1075 checkSameDec("__rmod__", True)
1076 checkSameDec("__rmul__", True)
1077 checkSameDec("__rpow__", True)
1078 checkSameDec("__rsub__", True)
1079 checkSameDec("__str__")
1080 checkSameDec("__sub__", True)
1081 checkSameDec("__truediv__", True)
1082 checkSameDec("adjusted")
1083 checkSameDec("as_tuple")
1084 checkSameDec("compare", True)
1085 checkSameDec("max", True)
1086 checkSameDec("min", True)
1087 checkSameDec("normalize")
1088 checkSameDec("quantize", True)
1089 checkSameDec("remainder_near", True)
1090 checkSameDec("same_quantum", True)
1091 checkSameDec("sqrt")
1092 checkSameDec("to_eng_string")
1093 checkSameDec("to_integral")
1094
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001095 def test_subclassing(self):
1096 # Different behaviours when subclassing Decimal
1097
1098 class MyDecimal(Decimal):
1099 pass
1100
1101 d1 = MyDecimal(1)
1102 d2 = MyDecimal(2)
1103 d = d1 + d2
1104 self.assertTrue(type(d) is Decimal)
1105
1106 d = d1.max(d2)
1107 self.assertTrue(type(d) is Decimal)
1108
1109
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001110class DecimalPythonAPItests(unittest.TestCase):
1111
1112 def test_pickle(self):
1113 d = Decimal('-3.141590000')
1114 p = pickle.dumps(d)
1115 e = pickle.loads(p)
1116 self.assertEqual(d, e)
1117
Raymond Hettinger5548be22004-07-05 18:49:38 +00001118 def test_int(self):
Raymond Hettinger605ed022004-11-24 07:28:48 +00001119 for x in range(-250, 250):
1120 s = '%0.2f' % (x / 100.0)
Raymond Hettinger5548be22004-07-05 18:49:38 +00001121 # should work the same as for floats
1122 self.assertEqual(int(Decimal(s)), int(float(s)))
Raymond Hettinger605ed022004-11-24 07:28:48 +00001123 # should work the same as to_integral in the ROUND_DOWN mode
Raymond Hettinger5548be22004-07-05 18:49:38 +00001124 d = Decimal(s)
Raymond Hettinger605ed022004-11-24 07:28:48 +00001125 r = d.to_integral(ROUND_DOWN)
Raymond Hettinger5548be22004-07-05 18:49:38 +00001126 self.assertEqual(Decimal(int(d)), r)
1127
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +00001128class ContextAPItests(unittest.TestCase):
1129
1130 def test_pickle(self):
1131 c = Context()
1132 e = pickle.loads(pickle.dumps(c))
1133 for k in vars(c):
1134 v1 = vars(c)[k]
1135 v2 = vars(e)[k]
1136 self.assertEqual(v1, v2)
1137
Raymond Hettinger0aeac102004-07-05 22:53:03 +00001138 def test_equality_with_other_types(self):
1139 self.assert_(Decimal(10) in ['a', 1.0, Decimal(10), (1,2), {}])
1140 self.assert_(Decimal(10) not in ['a', 1.0, (1,2), {}])
1141
Raymond Hettinger955d2b22004-08-08 20:17:45 +00001142 def test_copy(self):
1143 # All copies should be deep
1144 c = Context()
1145 d = c.copy()
1146 self.assertNotEqual(id(c), id(d))
1147 self.assertNotEqual(id(c.flags), id(d.flags))
1148 self.assertNotEqual(id(c.traps), id(d.traps))
1149
Thomas Wouters89f507f2006-12-13 04:49:30 +00001150class WithStatementTest(unittest.TestCase):
1151 # Can't do these as docstrings until Python 2.6
1152 # as doctest can't handle __future__ statements
1153
1154 def test_localcontext(self):
1155 # Use a copy of the current context in the block
1156 orig_ctx = getcontext()
1157 with localcontext() as enter_ctx:
1158 set_ctx = getcontext()
1159 final_ctx = getcontext()
1160 self.assert_(orig_ctx is final_ctx, 'did not restore context correctly')
1161 self.assert_(orig_ctx is not set_ctx, 'did not copy the context')
1162 self.assert_(set_ctx is enter_ctx, '__enter__ returned wrong context')
1163
1164 def test_localcontextarg(self):
1165 # Use a copy of the supplied context in the block
1166 orig_ctx = getcontext()
1167 new_ctx = Context(prec=42)
1168 with localcontext(new_ctx) as enter_ctx:
1169 set_ctx = getcontext()
1170 final_ctx = getcontext()
1171 self.assert_(orig_ctx is final_ctx, 'did not restore context correctly')
1172 self.assert_(set_ctx.prec == new_ctx.prec, 'did not set correct context')
1173 self.assert_(new_ctx is not set_ctx, 'did not copy the context')
1174 self.assert_(set_ctx is enter_ctx, '__enter__ returned wrong context')
1175
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001176class ContextFlags(unittest.TestCase):
1177 def test_flags_irrelevant(self):
1178 # check that the result (numeric result + flags raised) of an
1179 # arithmetic operation doesn't depend on the current flags
1180
1181 context = Context(prec=9, Emin = -999999999, Emax = 999999999,
1182 rounding=ROUND_HALF_EVEN, traps=[], flags=[])
1183
1184 # operations that raise various flags, in the form (function, arglist)
1185 operations = [
1186 (context._apply, [Decimal("100E-1000000009")]),
1187 (context.sqrt, [Decimal(2)]),
1188 (context.add, [Decimal("1.23456789"), Decimal("9.87654321")]),
1189 (context.multiply, [Decimal("1.23456789"), Decimal("9.87654321")]),
1190 (context.subtract, [Decimal("1.23456789"), Decimal("9.87654321")]),
1191 ]
1192
1193 # try various flags individually, then a whole lot at once
1194 flagsets = [[Inexact], [Rounded], [Underflow], [Clamped], [Subnormal],
1195 [Inexact, Rounded, Underflow, Clamped, Subnormal]]
1196
1197 for fn, args in operations:
1198 # find answer and flags raised using a clean context
1199 context.clear_flags()
1200 ans = fn(*args)
1201 flags = [k for k, v in context.flags.items() if v]
1202
1203 for extra_flags in flagsets:
1204 # set flags, before calling operation
1205 context.clear_flags()
1206 for flag in extra_flags:
1207 context._raise_error(flag)
1208 new_ans = fn(*args)
1209
1210 # flags that we expect to be set after the operation
1211 expected_flags = list(flags)
1212 for flag in extra_flags:
1213 if flag not in expected_flags:
1214 expected_flags.append(flag)
1215 expected_flags.sort(key=id)
1216
1217 # flags we actually got
1218 new_flags = [k for k,v in context.flags.items() if v]
1219 new_flags.sort(key=id)
1220
1221 self.assertEqual(ans, new_ans,
1222 "operation produces different answers depending on flags set: " +
1223 "expected %s, got %s." % (ans, new_ans))
1224 self.assertEqual(new_flags, expected_flags,
1225 "operation raises different flags depending on flags set: " +
1226 "expected %s, got %s" % (expected_flags, new_flags))
1227
1228def test_main(arith=False, verbose=None, todo_tests=None, debug=None):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001229 """ Execute the tests.
1230
Raymond Hettingered20ad82004-09-04 20:09:13 +00001231 Runs all arithmetic tests if arith is True or if the "decimal" resource
1232 is enabled in regrtest.py
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001233 """
Raymond Hettingered20ad82004-09-04 20:09:13 +00001234
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001235 init()
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001236 global TEST_ALL, DEBUG
Raymond Hettingered20ad82004-09-04 20:09:13 +00001237 TEST_ALL = arith or is_resource_enabled('decimal')
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001238 DEBUG = debug
Raymond Hettingered20ad82004-09-04 20:09:13 +00001239
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001240 if todo_tests is None:
1241 test_classes = [
1242 DecimalExplicitConstructionTest,
1243 DecimalImplicitConstructionTest,
1244 DecimalArithmeticOperatorsTest,
1245 DecimalUseOfContextTest,
1246 DecimalUsabilityTest,
1247 DecimalPythonAPItests,
1248 ContextAPItests,
1249 DecimalTest,
1250 WithStatementTest,
1251 ContextFlags
1252 ]
1253 else:
1254 test_classes = [DecimalTest]
1255
1256 # Dynamically build custom test definition for each file in the test
1257 # directory and add the definitions to the DecimalTest class. This
1258 # procedure insures that new files do not get skipped.
1259 for filename in os.listdir(directory):
1260 if '.decTest' not in filename or filename.startswith("."):
1261 continue
1262 head, tail = filename.split('.')
1263 if todo_tests is not None and head not in todo_tests:
1264 continue
1265 tester = lambda self, f=filename: self.eval_file(directory + f)
1266 setattr(DecimalTest, 'test_' + head, tester)
1267 del filename, head, tail, tester
1268
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001269
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001270 try:
1271 run_unittest(*test_classes)
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001272 if todo_tests is None:
1273 import decimal as DecimalModule
1274 run_doctest(DecimalModule, verbose)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001275 finally:
1276 setcontext(ORIGINAL_CONTEXT)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001277
1278if __name__ == '__main__':
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001279 import optparse
1280 p = optparse.OptionParser("test_decimal.py [--debug] [{--skip | test1 [test2 [...]]}]")
1281 p.add_option('--debug', '-d', action='store_true', help='shows the test number and context before each test')
1282 p.add_option('--skip', '-s', action='store_true', help='skip over 90% of the arithmetic tests')
1283 (opt, args) = p.parse_args()
1284
1285 if opt.skip:
1286 test_main(arith=False, verbose=True)
1287 elif args:
1288 test_main(arith=True, verbose=True, todo_tests=args, debug=opt.debug)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001289 else:
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001290 test_main(arith=True, verbose=True)