blob: 83fb337268f9a157b0f8f8b8c243ba25a023913d [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
Guido van Rossum8ce8a782007-11-01 19:42:39 +000098nameAdapter = {'and':'logical_and',
Raymond Hettinger7c85fa42004-07-01 11:01:35 +000099 'apply':'_apply',
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000100 'class':'number_class',
101 'comparesig':'compare_signal',
102 'comparetotal':'compare_total',
103 'comparetotmag':'compare_total_mag',
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000104 'copy':'copy_decimal',
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000105 'copyabs':'copy_abs',
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000106 'copynegate':'copy_negate',
107 'copysign':'copy_sign',
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000108 'divideint':'divide_int',
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000109 'invert':'logical_invert',
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000110 'iscanonical':'is_canonical',
111 'isfinite':'is_finite',
112 'isinfinite':'is_infinite',
113 'isnan':'is_nan',
114 'isnormal':'is_normal',
115 'isqnan':'is_qnan',
116 'issigned':'is_signed',
117 'issnan':'is_snan',
118 'issubnormal':'is_subnormal',
119 'iszero':'is_zero',
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000120 'maxmag':'max_mag',
121 'minmag':'min_mag',
122 'nextminus':'next_minus',
123 'nextplus':'next_plus',
124 'nexttoward':'next_toward',
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000125 'or':'logical_or',
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000126 'reduce':'normalize',
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000127 'remaindernear':'remainder_near',
128 'samequantum':'same_quantum',
129 'squareroot':'sqrt',
130 'toeng':'to_eng_string',
131 'tointegral':'to_integral_value',
132 'tointegralx':'to_integral_exact',
133 'tosci':'to_sci_string',
134 'xor':'logical_xor',
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000135 }
136
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000137# The following functions return True/False rather than a Decimal instance
138
139LOGICAL_FUNCTIONS = (
140 'is_canonical',
141 'is_finite',
142 'is_infinite',
143 'is_nan',
144 'is_normal',
145 'is_qnan',
146 'is_signed',
147 'is_snan',
148 'is_subnormal',
149 'is_zero',
150 'same_quantum',
151 )
152
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000153# For some operations (currently exp, ln, log10, power), the decNumber
154# reference implementation imposes additional restrictions on the
155# context and operands. These restrictions are not part of the
156# specification; however, the effect of these restrictions does show
157# up in some of the testcases. We skip testcases that violate these
158# restrictions, since Decimal behaves differently from decNumber for
159# these testcases so these testcases would otherwise fail.
160
161decNumberRestricted = ('power', 'ln', 'log10', 'exp')
162DEC_MAX_MATH = 999999
163def outside_decNumber_bounds(v, context):
164 if (context.prec > DEC_MAX_MATH or
165 context.Emax > DEC_MAX_MATH or
166 -context.Emin > DEC_MAX_MATH):
167 return True
168 if not v._is_special and v and (
169 len(v._int) > DEC_MAX_MATH or
170 v.adjusted() > DEC_MAX_MATH or
171 v.adjusted() < 1-2*DEC_MAX_MATH):
172 return True
173 return False
174
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000175class DecimalTest(unittest.TestCase):
176 """Class which tests the Decimal class against the test cases.
177
178 Changed for unittest.
179 """
180 def setUp(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000181 self.context = Context()
Raymond Hettingerbf440692004-07-10 14:14:37 +0000182 for key in DefaultContext.traps.keys():
183 DefaultContext.traps[key] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000184 self.ignore_list = ['#']
185 # Basically, a # means return NaN InvalidOperation.
186 # Different from a sNaN in trim
187
188 self.ChangeDict = {'precision' : self.change_precision,
189 'rounding' : self.change_rounding_method,
190 'maxexponent' : self.change_max_exponent,
191 'minexponent' : self.change_min_exponent,
192 'clamp' : self.change_clamp}
193
194 def tearDown(self):
195 """Cleaning up enviroment."""
196 # leaving context in original state
Raymond Hettingerbf440692004-07-10 14:14:37 +0000197 for key in DefaultContext.traps.keys():
198 DefaultContext.traps[key] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000199 return
200
201 def eval_file(self, file):
202 global skip_expected
203 if skip_expected:
204 raise TestSkipped
205 return
Neal Norwitz70967602006-03-17 08:29:44 +0000206 for line in open(file):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000207 line = line.replace('\r\n', '').replace('\n', '')
Raymond Hettinger5aa478b2004-07-09 10:02:53 +0000208 #print line
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000209 try:
210 t = self.eval_line(line)
Guido van Rossumb940e112007-01-10 16:19:56 +0000211 except DecimalException as exception:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000212 #Exception raised where there shoudn't have been one.
213 self.fail('Exception "'+exception.__class__.__name__ + '" raised on line '+line)
214
215 return
216
217 def eval_line(self, s):
218 if s.find(' -> ') >= 0 and s[:2] != '--' and not s.startswith(' --'):
219 s = (s.split('->')[0] + '->' +
220 s.split('->')[1].split('--')[0]).strip()
221 else:
222 s = s.split('--')[0].strip()
223
224 for ignore in self.ignore_list:
225 if s.find(ignore) >= 0:
226 #print s.split()[0], 'NotImplemented--', ignore
227 return
228 if not s:
229 return
230 elif ':' in s:
231 return self.eval_directive(s)
232 else:
233 return self.eval_equation(s)
234
235 def eval_directive(self, s):
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000236 funct, value = (x.strip().lower() for x in s.split(':'))
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000237 if funct == 'rounding':
238 value = RoundingDict[value]
239 else:
240 try:
241 value = int(value)
242 except ValueError:
243 pass
244
245 funct = self.ChangeDict.get(funct, Nonfunction)
246 funct(value)
247
248 def eval_equation(self, s):
249 #global DEFAULT_PRECISION
250 #print DEFAULT_PRECISION
Raymond Hettingered20ad82004-09-04 20:09:13 +0000251
252 if not TEST_ALL and random.random() < 0.90:
253 return
254
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000255 try:
256 Sides = s.split('->')
257 L = Sides[0].strip().split()
258 id = L[0]
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000259 if DEBUG:
260 print("Test ", id, end=" ")
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000261 funct = L[1].lower()
262 valstemp = L[2:]
263 L = Sides[1].strip().split()
264 ans = L[0]
265 exceptions = L[1:]
266 except (TypeError, AttributeError, IndexError):
Raymond Hettingerd87ac8f2004-07-09 10:52:54 +0000267 raise InvalidOperation
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000268 def FixQuotes(val):
269 val = val.replace("''", 'SingleQuote').replace('""', 'DoubleQuote')
270 val = val.replace("'", '').replace('"', '')
271 val = val.replace('SingleQuote', "'").replace('DoubleQuote', '"')
272 return val
273 fname = nameAdapter.get(funct, funct)
274 if fname == 'rescale':
275 return
276 funct = getattr(self.context, fname)
277 vals = []
278 conglomerate = ''
279 quote = 0
280 theirexceptions = [ErrorNames[x.lower()] for x in exceptions]
281
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +0000282 for exception in Signals:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000283 self.context.traps[exception] = 1 #Catch these bugs...
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000284 for exception in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000285 self.context.traps[exception] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000286 for i, val in enumerate(valstemp):
287 if val.count("'") % 2 == 1:
288 quote = 1 - quote
289 if quote:
290 conglomerate = conglomerate + ' ' + val
291 continue
292 else:
293 val = conglomerate + val
294 conglomerate = ''
295 v = FixQuotes(val)
296 if fname in ('to_sci_string', 'to_eng_string'):
297 if EXTENDEDERRORTEST:
298 for error in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000299 self.context.traps[error] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000300 try:
301 funct(self.context.create_decimal(v))
302 except error:
303 pass
Guido van Rossumb940e112007-01-10 16:19:56 +0000304 except Signals as e:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000305 self.fail("Raised %s in %s when %s disabled" % \
306 (e, s, error))
307 else:
308 self.fail("Did not raise %s in %s" % (error, s))
Raymond Hettingerbf440692004-07-10 14:14:37 +0000309 self.context.traps[error] = 0
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000310 v = self.context.create_decimal(v)
311 else:
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000312 v = Decimal(v, self.context)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000313 vals.append(v)
314
315 ans = FixQuotes(ans)
316
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000317 # skip tests that are related to bounds imposed in the decNumber
318 # reference implementation
319 if fname in decNumberRestricted:
320 if fname == 'power':
321 if not (vals[1]._isinteger() and
322 -1999999997 <= vals[1] <= 999999999):
323 if outside_decNumber_bounds(vals[0], self.context) or \
324 outside_decNumber_bounds(vals[1], self.context):
325 #print "Skipping test %s" % s
326 return
327 else:
328 if outside_decNumber_bounds(vals[0], self.context):
329 #print "Skipping test %s" % s
330 return
331
332
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000333 if EXTENDEDERRORTEST and fname not in ('to_sci_string', 'to_eng_string'):
334 for error in theirexceptions:
Raymond Hettingerbf440692004-07-10 14:14:37 +0000335 self.context.traps[error] = 1
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000336 try:
337 funct(*vals)
338 except error:
339 pass
Guido van Rossumb940e112007-01-10 16:19:56 +0000340 except Signals as e:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000341 self.fail("Raised %s in %s when %s disabled" % \
342 (e, s, error))
343 else:
344 self.fail("Did not raise %s in %s" % (error, s))
Raymond Hettingerbf440692004-07-10 14:14:37 +0000345 self.context.traps[error] = 0
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000346 if DEBUG:
347 print("--", self.context)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000348 try:
349 result = str(funct(*vals))
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000350 if fname in LOGICAL_FUNCTIONS:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000351 result = str(int(eval(result))) # 'True', 'False' -> '1', '0'
Guido van Rossumb940e112007-01-10 16:19:56 +0000352 except Signals as error:
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000353 self.fail("Raised %s in %s" % (error, s))
354 except: #Catch any error long enough to state the test case.
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000355 print("ERROR:", s)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000356 raise
357
358 myexceptions = self.getexceptions()
Raymond Hettingerbf440692004-07-10 14:14:37 +0000359 self.context.clear_flags()
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000360
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000361 myexceptions.sort(key=repr)
362 theirexceptions.sort(key=repr)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000363
364 self.assertEqual(result, ans,
365 'Incorrect answer for ' + s + ' -- got ' + result)
366 self.assertEqual(myexceptions, theirexceptions,
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000367 'Incorrect flags set in ' + s + ' -- got ' + str(myexceptions))
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000368 return
369
370 def getexceptions(self):
Raymond Hettingerf63ba432004-08-17 05:42:09 +0000371 return [e for e in Signals if self.context.flags[e]]
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000372
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000373 def change_precision(self, prec):
374 self.context.prec = prec
375 def change_rounding_method(self, rounding):
376 self.context.rounding = rounding
377 def change_min_exponent(self, exp):
378 self.context.Emin = exp
379 def change_max_exponent(self, exp):
380 self.context.Emax = exp
381 def change_clamp(self, clamp):
382 self.context._clamp = clamp
383
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000384
385
386# The following classes test the behaviour of Decimal according to PEP 327
387
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000388class DecimalExplicitConstructionTest(unittest.TestCase):
389 '''Unit tests for Explicit Construction cases of Decimal.'''
390
391 def test_explicit_empty(self):
392 self.assertEqual(Decimal(), Decimal("0"))
393
394 def test_explicit_from_None(self):
395 self.assertRaises(TypeError, Decimal, None)
396
397 def test_explicit_from_int(self):
398
399 #positive
400 d = Decimal(45)
401 self.assertEqual(str(d), '45')
402
403 #very large positive
404 d = Decimal(500000123)
405 self.assertEqual(str(d), '500000123')
406
407 #negative
408 d = Decimal(-45)
409 self.assertEqual(str(d), '-45')
410
411 #zero
412 d = Decimal(0)
413 self.assertEqual(str(d), '0')
414
415 def test_explicit_from_string(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000416
417 #empty
418 self.assertEqual(str(Decimal('')), 'NaN')
419
420 #int
421 self.assertEqual(str(Decimal('45')), '45')
422
423 #float
424 self.assertEqual(str(Decimal('45.34')), '45.34')
425
426 #engineer notation
427 self.assertEqual(str(Decimal('45e2')), '4.5E+3')
428
429 #just not a number
430 self.assertEqual(str(Decimal('ugly')), 'NaN')
431
Christian Heimesa62da1d2008-01-12 19:39:10 +0000432 #leading and trailing whitespace permitted
433 self.assertEqual(str(Decimal('1.3E4 \n')), '1.3E+4')
434 self.assertEqual(str(Decimal(' -7.89')), '-7.89')
435
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000436 def test_explicit_from_tuples(self):
437
438 #zero
439 d = Decimal( (0, (0,), 0) )
440 self.assertEqual(str(d), '0')
441
442 #int
443 d = Decimal( (1, (4, 5), 0) )
444 self.assertEqual(str(d), '-45')
445
446 #float
447 d = Decimal( (0, (4, 5, 3, 4), -2) )
448 self.assertEqual(str(d), '45.34')
449
450 #weird
451 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
452 self.assertEqual(str(d), '-4.34913534E-17')
453
454 #wrong number of items
455 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1)) )
456
457 #bad sign
458 self.assertRaises(ValueError, Decimal, (8, (4, 3, 4, 9, 1), 2) )
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000459 self.assertRaises(ValueError, Decimal, (0., (4, 3, 4, 9, 1), 2) )
460 self.assertRaises(ValueError, Decimal, (Decimal(1), (4, 3, 4, 9, 1), 2))
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000461
462 #bad exp
463 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 'wrong!') )
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000464 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 0.) )
465 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), '1') )
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000466
467 #bad coefficients
468 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) )
469 self.assertRaises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) )
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000470 self.assertRaises(ValueError, Decimal, (1, (4, 10, 4, 9, 1), 2) )
Guido van Rossum0d3fb8a2007-11-26 23:23:18 +0000471 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 'a', 1), 2) )
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000472
473 def test_explicit_from_Decimal(self):
474
475 #positive
476 d = Decimal(45)
477 e = Decimal(d)
478 self.assertEqual(str(e), '45')
479 self.assertNotEqual(id(d), id(e))
480
481 #very large positive
482 d = Decimal(500000123)
483 e = Decimal(d)
484 self.assertEqual(str(e), '500000123')
485 self.assertNotEqual(id(d), id(e))
486
487 #negative
488 d = Decimal(-45)
489 e = Decimal(d)
490 self.assertEqual(str(e), '-45')
491 self.assertNotEqual(id(d), id(e))
492
493 #zero
494 d = Decimal(0)
495 e = Decimal(d)
496 self.assertEqual(str(e), '0')
497 self.assertNotEqual(id(d), id(e))
498
499 def test_explicit_context_create_decimal(self):
500
501 nc = copy.copy(getcontext())
502 nc.prec = 3
503
504 # empty
Raymond Hettingerfed52962004-07-14 15:41:57 +0000505 d = Decimal()
506 self.assertEqual(str(d), '0')
507 d = nc.create_decimal()
508 self.assertEqual(str(d), '0')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000509
510 # from None
511 self.assertRaises(TypeError, nc.create_decimal, None)
512
513 # from int
514 d = nc.create_decimal(456)
515 self.failUnless(isinstance(d, Decimal))
516 self.assertEqual(nc.create_decimal(45678),
517 nc.create_decimal('457E+2'))
518
519 # from string
520 d = Decimal('456789')
521 self.assertEqual(str(d), '456789')
522 d = nc.create_decimal('456789')
523 self.assertEqual(str(d), '4.57E+5')
Christian Heimesa62da1d2008-01-12 19:39:10 +0000524 # leading and trailing whitespace should result in a NaN;
525 # spaces are already checked in Cowlishaw's test-suite, so
526 # here we just check that a trailing newline results in a NaN
527 self.assertEqual(str(nc.create_decimal('3.14\n')), 'NaN')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000528
529 # from tuples
530 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
531 self.assertEqual(str(d), '-4.34913534E-17')
532 d = nc.create_decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
533 self.assertEqual(str(d), '-4.35E-17')
534
535 # from Decimal
536 prevdec = Decimal(500000123)
537 d = Decimal(prevdec)
538 self.assertEqual(str(d), '500000123')
539 d = nc.create_decimal(prevdec)
540 self.assertEqual(str(d), '5.00E+8')
541
542
543class DecimalImplicitConstructionTest(unittest.TestCase):
544 '''Unit tests for Implicit Construction cases of Decimal.'''
545
546 def test_implicit_from_None(self):
547 self.assertRaises(TypeError, eval, 'Decimal(5) + None', globals())
548
549 def test_implicit_from_int(self):
550 #normal
551 self.assertEqual(str(Decimal(5) + 45), '50')
552 #exceeding precision
553 self.assertEqual(Decimal(5) + 123456789000, Decimal(123456789000))
554
555 def test_implicit_from_string(self):
556 self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', globals())
557
558 def test_implicit_from_float(self):
559 self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', globals())
560
561 def test_implicit_from_Decimal(self):
562 self.assertEqual(Decimal(5) + Decimal(45), Decimal(50))
563
Raymond Hettinger267b8682005-03-27 10:47:39 +0000564 def test_rop(self):
565 # Allow other classes to be trained to interact with Decimals
566 class E:
567 def __divmod__(self, other):
568 return 'divmod ' + str(other)
569 def __rdivmod__(self, other):
570 return str(other) + ' rdivmod'
571 def __lt__(self, other):
572 return 'lt ' + str(other)
573 def __gt__(self, other):
574 return 'gt ' + str(other)
575 def __le__(self, other):
576 return 'le ' + str(other)
577 def __ge__(self, other):
578 return 'ge ' + str(other)
579 def __eq__(self, other):
580 return 'eq ' + str(other)
581 def __ne__(self, other):
582 return 'ne ' + str(other)
583
584 self.assertEqual(divmod(E(), Decimal(10)), 'divmod 10')
585 self.assertEqual(divmod(Decimal(10), E()), '10 rdivmod')
586 self.assertEqual(eval('Decimal(10) < E()'), 'gt 10')
587 self.assertEqual(eval('Decimal(10) > E()'), 'lt 10')
588 self.assertEqual(eval('Decimal(10) <= E()'), 'ge 10')
589 self.assertEqual(eval('Decimal(10) >= E()'), 'le 10')
590 self.assertEqual(eval('Decimal(10) == E()'), 'eq 10')
591 self.assertEqual(eval('Decimal(10) != E()'), 'ne 10')
592
593 # insert operator methods and then exercise them
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000594 oplist = [
595 ('+', '__add__', '__radd__'),
596 ('-', '__sub__', '__rsub__'),
597 ('*', '__mul__', '__rmul__'),
Thomas Woutersdcc6d322006-04-21 11:30:52 +0000598 ('/', '__truediv__', '__rtruediv__'),
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000599 ('%', '__mod__', '__rmod__'),
600 ('//', '__floordiv__', '__rfloordiv__'),
601 ('**', '__pow__', '__rpow__')
602 ]
Raymond Hettinger267b8682005-03-27 10:47:39 +0000603
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000604 for sym, lop, rop in oplist:
Raymond Hettinger267b8682005-03-27 10:47:39 +0000605 setattr(E, lop, lambda self, other: 'str' + lop + str(other))
606 setattr(E, rop, lambda self, other: str(other) + rop + 'str')
607 self.assertEqual(eval('E()' + sym + 'Decimal(10)'),
608 'str' + lop + '10')
609 self.assertEqual(eval('Decimal(10)' + sym + 'E()'),
610 '10' + rop + 'str')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000611
612class DecimalArithmeticOperatorsTest(unittest.TestCase):
613 '''Unit tests for all arithmetic operators, binary and unary.'''
614
615 def test_addition(self):
616
617 d1 = Decimal('-11.1')
618 d2 = Decimal('22.2')
619
620 #two Decimals
621 self.assertEqual(d1+d2, Decimal('11.1'))
622 self.assertEqual(d2+d1, Decimal('11.1'))
623
624 #with other type, left
625 c = d1 + 5
626 self.assertEqual(c, Decimal('-6.1'))
627 self.assertEqual(type(c), type(d1))
628
629 #with other type, right
630 c = 5 + d1
631 self.assertEqual(c, Decimal('-6.1'))
632 self.assertEqual(type(c), type(d1))
633
634 #inline with decimal
635 d1 += d2
636 self.assertEqual(d1, Decimal('11.1'))
637
638 #inline with other type
639 d1 += 5
640 self.assertEqual(d1, Decimal('16.1'))
641
642 def test_subtraction(self):
643
644 d1 = Decimal('-11.1')
645 d2 = Decimal('22.2')
646
647 #two Decimals
648 self.assertEqual(d1-d2, Decimal('-33.3'))
649 self.assertEqual(d2-d1, Decimal('33.3'))
650
651 #with other type, left
652 c = d1 - 5
653 self.assertEqual(c, Decimal('-16.1'))
654 self.assertEqual(type(c), type(d1))
655
656 #with other type, right
657 c = 5 - d1
658 self.assertEqual(c, Decimal('16.1'))
659 self.assertEqual(type(c), type(d1))
660
661 #inline with decimal
662 d1 -= d2
663 self.assertEqual(d1, Decimal('-33.3'))
664
665 #inline with other type
666 d1 -= 5
667 self.assertEqual(d1, Decimal('-38.3'))
668
669 def test_multiplication(self):
670
671 d1 = Decimal('-5')
672 d2 = Decimal('3')
673
674 #two Decimals
675 self.assertEqual(d1*d2, Decimal('-15'))
676 self.assertEqual(d2*d1, Decimal('-15'))
677
678 #with other type, left
679 c = d1 * 5
680 self.assertEqual(c, Decimal('-25'))
681 self.assertEqual(type(c), type(d1))
682
683 #with other type, right
684 c = 5 * d1
685 self.assertEqual(c, Decimal('-25'))
686 self.assertEqual(type(c), type(d1))
687
688 #inline with decimal
689 d1 *= d2
690 self.assertEqual(d1, Decimal('-15'))
691
692 #inline with other type
693 d1 *= 5
694 self.assertEqual(d1, Decimal('-75'))
695
696 def test_division(self):
697
698 d1 = Decimal('-5')
699 d2 = Decimal('2')
700
701 #two Decimals
702 self.assertEqual(d1/d2, Decimal('-2.5'))
703 self.assertEqual(d2/d1, Decimal('-0.4'))
704
705 #with other type, left
706 c = d1 / 4
707 self.assertEqual(c, Decimal('-1.25'))
708 self.assertEqual(type(c), type(d1))
709
710 #with other type, right
711 c = 4 / d1
712 self.assertEqual(c, Decimal('-0.8'))
713 self.assertEqual(type(c), type(d1))
714
715 #inline with decimal
716 d1 /= d2
717 self.assertEqual(d1, Decimal('-2.5'))
718
719 #inline with other type
720 d1 /= 4
721 self.assertEqual(d1, Decimal('-0.625'))
722
723 def test_floor_division(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000724
725 d1 = Decimal('5')
726 d2 = Decimal('2')
727
728 #two Decimals
729 self.assertEqual(d1//d2, Decimal('2'))
730 self.assertEqual(d2//d1, Decimal('0'))
731
732 #with other type, left
733 c = d1 // 4
734 self.assertEqual(c, Decimal('1'))
735 self.assertEqual(type(c), type(d1))
736
737 #with other type, right
738 c = 7 // d1
739 self.assertEqual(c, Decimal('1'))
740 self.assertEqual(type(c), type(d1))
741
742 #inline with decimal
743 d1 //= d2
744 self.assertEqual(d1, Decimal('2'))
745
746 #inline with other type
747 d1 //= 2
748 self.assertEqual(d1, Decimal('1'))
749
750 def test_powering(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000751
752 d1 = Decimal('5')
753 d2 = Decimal('2')
754
755 #two Decimals
756 self.assertEqual(d1**d2, Decimal('25'))
757 self.assertEqual(d2**d1, Decimal('32'))
758
759 #with other type, left
760 c = d1 ** 4
761 self.assertEqual(c, Decimal('625'))
762 self.assertEqual(type(c), type(d1))
763
764 #with other type, right
765 c = 7 ** d1
766 self.assertEqual(c, Decimal('16807'))
767 self.assertEqual(type(c), type(d1))
768
769 #inline with decimal
770 d1 **= d2
771 self.assertEqual(d1, Decimal('25'))
772
773 #inline with other type
774 d1 **= 4
775 self.assertEqual(d1, Decimal('390625'))
776
777 def test_module(self):
778
779 d1 = Decimal('5')
780 d2 = Decimal('2')
781
782 #two Decimals
783 self.assertEqual(d1%d2, Decimal('1'))
784 self.assertEqual(d2%d1, Decimal('2'))
785
786 #with other type, left
787 c = d1 % 4
788 self.assertEqual(c, Decimal('1'))
789 self.assertEqual(type(c), type(d1))
790
791 #with other type, right
792 c = 7 % d1
793 self.assertEqual(c, Decimal('2'))
794 self.assertEqual(type(c), type(d1))
795
796 #inline with decimal
797 d1 %= d2
798 self.assertEqual(d1, Decimal('1'))
799
800 #inline with other type
801 d1 %= 4
802 self.assertEqual(d1, Decimal('1'))
803
804 def test_floor_div_module(self):
805
806 d1 = Decimal('5')
807 d2 = Decimal('2')
808
809 #two Decimals
810 (p, q) = divmod(d1, d2)
811 self.assertEqual(p, Decimal('2'))
812 self.assertEqual(q, Decimal('1'))
813 self.assertEqual(type(p), type(d1))
814 self.assertEqual(type(q), type(d1))
815
816 #with other type, left
817 (p, q) = divmod(d1, 4)
818 self.assertEqual(p, Decimal('1'))
819 self.assertEqual(q, Decimal('1'))
820 self.assertEqual(type(p), type(d1))
821 self.assertEqual(type(q), type(d1))
822
823 #with other type, right
824 (p, q) = divmod(7, d1)
825 self.assertEqual(p, Decimal('1'))
826 self.assertEqual(q, Decimal('2'))
827 self.assertEqual(type(p), type(d1))
828 self.assertEqual(type(q), type(d1))
829
830 def test_unary_operators(self):
831 self.assertEqual(+Decimal(45), Decimal(+45)) # +
832 self.assertEqual(-Decimal(45), Decimal(-45)) # -
833 self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs
834
835
836# The following are two functions used to test threading in the next class
837
838def thfunc1(cls):
839 d1 = Decimal(1)
840 d3 = Decimal(3)
841 cls.assertEqual(d1/d3, Decimal('0.333333333'))
842 cls.synchro.wait()
843 cls.assertEqual(d1/d3, Decimal('0.333333333'))
844 cls.finish1.set()
845 return
846
847def thfunc2(cls):
848 d1 = Decimal(1)
849 d3 = Decimal(3)
850 cls.assertEqual(d1/d3, Decimal('0.333333333'))
851 thiscontext = getcontext()
852 thiscontext.prec = 18
853 cls.assertEqual(d1/d3, Decimal('0.333333333333333333'))
854 cls.synchro.set()
855 cls.finish2.set()
856 return
857
858
859class DecimalUseOfContextTest(unittest.TestCase):
860 '''Unit tests for Use of Context cases in Decimal.'''
861
Raymond Hettinger7e71fa52004-12-18 19:07:19 +0000862 try:
863 import threading
864 except ImportError:
865 threading = None
866
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000867 # Take care executing this test from IDLE, there's an issue in threading
868 # that hangs IDLE and I couldn't find it
869
870 def test_threading(self):
871 #Test the "threading isolation" of a Context.
872
873 self.synchro = threading.Event()
874 self.finish1 = threading.Event()
875 self.finish2 = threading.Event()
876
877 th1 = threading.Thread(target=thfunc1, args=(self,))
878 th2 = threading.Thread(target=thfunc2, args=(self,))
879
880 th1.start()
881 th2.start()
882
883 self.finish1.wait()
Thomas Wouters1b7f8912007-09-19 03:06:30 +0000884 self.finish2.wait()
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000885 return
886
Raymond Hettinger7e71fa52004-12-18 19:07:19 +0000887 if threading is None:
888 del test_threading
889
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000890
891class DecimalUsabilityTest(unittest.TestCase):
892 '''Unit tests for Usability cases of Decimal.'''
893
894 def test_comparison_operators(self):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000895
896 da = Decimal('23.42')
897 db = Decimal('23.42')
898 dc = Decimal('45')
899
900 #two Decimals
901 self.failUnless(dc > da)
902 self.failUnless(dc >= da)
903 self.failUnless(da < dc)
904 self.failUnless(da <= dc)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000905 self.assertEqual(da, db)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000906 self.failUnless(da != dc)
907 self.failUnless(da <= db)
908 self.failUnless(da >= db)
909 self.assertEqual(cmp(dc,da), 1)
910 self.assertEqual(cmp(da,dc), -1)
911 self.assertEqual(cmp(da,db), 0)
912
913 #a Decimal and an int
914 self.failUnless(dc > 23)
915 self.failUnless(23 < dc)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000916 self.assertEqual(dc, 45)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000917 self.assertEqual(cmp(dc,23), 1)
918 self.assertEqual(cmp(23,dc), -1)
919 self.assertEqual(cmp(dc,45), 0)
920
921 #a Decimal and uncomparable
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000922 self.assertNotEqual(da, 'ugly')
923 self.assertNotEqual(da, 32.7)
924 self.assertNotEqual(da, object())
925 self.assertNotEqual(da, object)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000926
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000927 # sortable
Guido van Rossumc1f779c2007-07-03 08:25:58 +0000928 a = list(map(Decimal, range(100)))
Raymond Hettinger0aeac102004-07-05 22:53:03 +0000929 b = a[:]
930 random.shuffle(a)
931 a.sort()
932 self.assertEqual(a, b)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000933
934 def test_copy_and_deepcopy_methods(self):
935 d = Decimal('43.24')
936 c = copy.copy(d)
937 self.assertEqual(id(c), id(d))
938 dc = copy.deepcopy(d)
939 self.assertEqual(id(dc), id(d))
940
941 def test_hash_method(self):
942 #just that it's hashable
943 hash(Decimal(23))
Thomas Wouters8ce81f72007-09-20 18:22:40 +0000944
945 test_values = [Decimal(sign*(2**m + n))
946 for m in [0, 14, 15, 16, 17, 30, 31,
947 32, 33, 62, 63, 64, 65, 66]
948 for n in range(-10, 10)
949 for sign in [-1, 1]]
950 test_values.extend([
951 Decimal("-0"), # zeros
952 Decimal("0.00"),
953 Decimal("-0.000"),
954 Decimal("0E10"),
955 Decimal("-0E12"),
956 Decimal("10.0"), # negative exponent
957 Decimal("-23.00000"),
958 Decimal("1230E100"), # positive exponent
959 Decimal("-4.5678E50"),
960 # a value for which hash(n) != hash(n % (2**64-1))
961 # in Python pre-2.6
962 Decimal(2**64 + 2**32 - 1),
963 # selection of values which fail with the old (before
964 # version 2.6) long.__hash__
965 Decimal("1.634E100"),
966 Decimal("90.697E100"),
967 Decimal("188.83E100"),
968 Decimal("1652.9E100"),
969 Decimal("56531E100"),
970 ])
971
972 # check that hash(d) == hash(int(d)) for integral values
973 for value in test_values:
974 self.assertEqual(hash(value), hash(int(value)))
975
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000976 #the same hash that to an int
977 self.assertEqual(hash(Decimal(23)), hash(23))
Raymond Hettingerbea3f6f2005-03-15 04:59:17 +0000978 self.assertRaises(TypeError, hash, Decimal('NaN'))
979 self.assert_(hash(Decimal('Inf')))
980 self.assert_(hash(Decimal('-Inf')))
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000981
Christian Heimes2380ac72008-01-09 00:17:24 +0000982 # check that the value of the hash doesn't depend on the
983 # current context (issue #1757)
984 c = getcontext()
985 old_precision = c.prec
986 x = Decimal("123456789.1")
987
988 c.prec = 6
989 h1 = hash(x)
990 c.prec = 10
991 h2 = hash(x)
992 c.prec = 16
993 h3 = hash(x)
994
995 self.assertEqual(h1, h2)
996 self.assertEqual(h1, h3)
997 c.prec = old_precision
998
Raymond Hettinger7c85fa42004-07-01 11:01:35 +0000999 def test_min_and_max_methods(self):
1000
1001 d1 = Decimal('15.32')
1002 d2 = Decimal('28.5')
1003 l1 = 15
1004 l2 = 28
1005
1006 #between Decimals
1007 self.failUnless(min(d1,d2) is d1)
1008 self.failUnless(min(d2,d1) is d1)
1009 self.failUnless(max(d1,d2) is d2)
1010 self.failUnless(max(d2,d1) is d2)
1011
1012 #between Decimal and long
1013 self.failUnless(min(d1,l2) is d1)
1014 self.failUnless(min(l2,d1) is d1)
1015 self.failUnless(max(l1,d2) is d2)
1016 self.failUnless(max(d2,l1) is d2)
1017
1018 def test_as_nonzero(self):
1019 #as false
1020 self.failIf(Decimal(0))
1021 #as true
1022 self.failUnless(Decimal('0.372'))
1023
1024 def test_tostring_methods(self):
1025 #Test str and repr methods.
1026
1027 d = Decimal('15.32')
1028 self.assertEqual(str(d), '15.32') # str
1029 self.assertEqual(repr(d), 'Decimal("15.32")') # repr
1030
1031 def test_tonum_methods(self):
1032 #Test float, int and long methods.
1033
1034 d1 = Decimal('66')
1035 d2 = Decimal('15.32')
1036
1037 #int
1038 self.assertEqual(int(d1), 66)
1039 self.assertEqual(int(d2), 15)
1040
1041 #long
Guido van Rossume2a383d2007-01-15 16:59:06 +00001042 self.assertEqual(int(d1), 66)
1043 self.assertEqual(int(d2), 15)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001044
1045 #float
1046 self.assertEqual(float(d1), 66)
1047 self.assertEqual(float(d2), 15.32)
1048
1049 def test_eval_round_trip(self):
1050
1051 #with zero
1052 d = Decimal( (0, (0,), 0) )
1053 self.assertEqual(d, eval(repr(d)))
1054
1055 #int
1056 d = Decimal( (1, (4, 5), 0) )
1057 self.assertEqual(d, eval(repr(d)))
1058
1059 #float
1060 d = Decimal( (0, (4, 5, 3, 4), -2) )
1061 self.assertEqual(d, eval(repr(d)))
1062
1063 #weird
1064 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
1065 self.assertEqual(d, eval(repr(d)))
1066
1067 def test_as_tuple(self):
1068
1069 #with zero
1070 d = Decimal(0)
1071 self.assertEqual(d.as_tuple(), (0, (0,), 0) )
1072
1073 #int
1074 d = Decimal(-45)
1075 self.assertEqual(d.as_tuple(), (1, (4, 5), 0) )
1076
1077 #complicated string
1078 d = Decimal("-4.34913534E-17")
1079 self.assertEqual(d.as_tuple(), (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
1080
1081 #inf
1082 d = Decimal("Infinity")
1083 self.assertEqual(d.as_tuple(), (0, (0,), 'F') )
1084
Guido van Rossum8ce8a782007-11-01 19:42:39 +00001085 #leading zeros in coefficient should be stripped
1086 d = Decimal( (0, (0, 0, 4, 0, 5, 3, 4), -2) )
1087 self.assertEqual(d.as_tuple(), (0, (4, 0, 5, 3, 4), -2) )
1088 d = Decimal( (1, (0, 0, 0), 37) )
1089 self.assertEqual(d.as_tuple(), (1, (0,), 37))
1090 d = Decimal( (1, (), 37) )
1091 self.assertEqual(d.as_tuple(), (1, (0,), 37))
1092
1093 #leading zeros in NaN diagnostic info should be stripped
1094 d = Decimal( (0, (0, 0, 4, 0, 5, 3, 4), 'n') )
1095 self.assertEqual(d.as_tuple(), (0, (4, 0, 5, 3, 4), 'n') )
1096 d = Decimal( (1, (0, 0, 0), 'N') )
1097 self.assertEqual(d.as_tuple(), (1, (), 'N') )
1098 d = Decimal( (1, (), 'n') )
1099 self.assertEqual(d.as_tuple(), (1, (), 'n') )
1100
1101 #coefficient in infinity should be ignored
1102 d = Decimal( (0, (4, 5, 3, 4), 'F') )
1103 self.assertEqual(d.as_tuple(), (0, (0,), 'F'))
1104 d = Decimal( (1, (0, 2, 7, 1), 'F') )
1105 self.assertEqual(d.as_tuple(), (1, (0,), 'F'))
1106
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001107 def test_immutability_operations(self):
1108 # Do operations and check that it didn't change change internal objects.
1109
1110 d1 = Decimal('-25e55')
1111 b1 = Decimal('-25e55')
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001112 d2 = Decimal('33e+33')
1113 b2 = Decimal('33e+33')
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001114
1115 def checkSameDec(operation, useOther=False):
1116 if useOther:
1117 eval("d1." + operation + "(d2)")
1118 self.assertEqual(d1._sign, b1._sign)
1119 self.assertEqual(d1._int, b1._int)
1120 self.assertEqual(d1._exp, b1._exp)
1121 self.assertEqual(d2._sign, b2._sign)
1122 self.assertEqual(d2._int, b2._int)
1123 self.assertEqual(d2._exp, b2._exp)
1124 else:
1125 eval("d1." + operation + "()")
1126 self.assertEqual(d1._sign, b1._sign)
1127 self.assertEqual(d1._int, b1._int)
1128 self.assertEqual(d1._exp, b1._exp)
1129 return
1130
1131 Decimal(d1)
1132 self.assertEqual(d1._sign, b1._sign)
1133 self.assertEqual(d1._int, b1._int)
1134 self.assertEqual(d1._exp, b1._exp)
1135
1136 checkSameDec("__abs__")
1137 checkSameDec("__add__", True)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001138 checkSameDec("__divmod__", True)
1139 checkSameDec("__cmp__", True)
1140 checkSameDec("__float__")
1141 checkSameDec("__floordiv__", True)
1142 checkSameDec("__hash__")
1143 checkSameDec("__int__")
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001144 checkSameDec("__mod__", True)
1145 checkSameDec("__mul__", True)
1146 checkSameDec("__neg__")
Jack Diederich4dafcc42006-11-28 19:15:13 +00001147 checkSameDec("__bool__")
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001148 checkSameDec("__pos__")
1149 checkSameDec("__pow__", True)
1150 checkSameDec("__radd__", True)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001151 checkSameDec("__rdivmod__", True)
1152 checkSameDec("__repr__")
1153 checkSameDec("__rfloordiv__", True)
1154 checkSameDec("__rmod__", True)
1155 checkSameDec("__rmul__", True)
1156 checkSameDec("__rpow__", True)
1157 checkSameDec("__rsub__", True)
1158 checkSameDec("__str__")
1159 checkSameDec("__sub__", True)
1160 checkSameDec("__truediv__", True)
1161 checkSameDec("adjusted")
1162 checkSameDec("as_tuple")
1163 checkSameDec("compare", True)
1164 checkSameDec("max", True)
1165 checkSameDec("min", True)
1166 checkSameDec("normalize")
1167 checkSameDec("quantize", True)
1168 checkSameDec("remainder_near", True)
1169 checkSameDec("same_quantum", True)
1170 checkSameDec("sqrt")
1171 checkSameDec("to_eng_string")
1172 checkSameDec("to_integral")
1173
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001174 def test_subclassing(self):
1175 # Different behaviours when subclassing Decimal
1176
1177 class MyDecimal(Decimal):
1178 pass
1179
1180 d1 = MyDecimal(1)
1181 d2 = MyDecimal(2)
1182 d = d1 + d2
1183 self.assertTrue(type(d) is Decimal)
1184
1185 d = d1.max(d2)
1186 self.assertTrue(type(d) is Decimal)
1187
1188
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001189class DecimalPythonAPItests(unittest.TestCase):
1190
1191 def test_pickle(self):
1192 d = Decimal('-3.141590000')
1193 p = pickle.dumps(d)
1194 e = pickle.loads(p)
1195 self.assertEqual(d, e)
1196
Raymond Hettinger5548be22004-07-05 18:49:38 +00001197 def test_int(self):
Raymond Hettinger605ed022004-11-24 07:28:48 +00001198 for x in range(-250, 250):
1199 s = '%0.2f' % (x / 100.0)
Raymond Hettinger5548be22004-07-05 18:49:38 +00001200 # should work the same as for floats
1201 self.assertEqual(int(Decimal(s)), int(float(s)))
Raymond Hettinger605ed022004-11-24 07:28:48 +00001202 # should work the same as to_integral in the ROUND_DOWN mode
Raymond Hettinger5548be22004-07-05 18:49:38 +00001203 d = Decimal(s)
Raymond Hettinger605ed022004-11-24 07:28:48 +00001204 r = d.to_integral(ROUND_DOWN)
Raymond Hettinger5548be22004-07-05 18:49:38 +00001205 self.assertEqual(Decimal(int(d)), r)
1206
Raymond Hettingerd9c0a7a2004-07-03 10:02:28 +00001207class ContextAPItests(unittest.TestCase):
1208
1209 def test_pickle(self):
1210 c = Context()
1211 e = pickle.loads(pickle.dumps(c))
1212 for k in vars(c):
1213 v1 = vars(c)[k]
1214 v2 = vars(e)[k]
1215 self.assertEqual(v1, v2)
1216
Raymond Hettinger0aeac102004-07-05 22:53:03 +00001217 def test_equality_with_other_types(self):
1218 self.assert_(Decimal(10) in ['a', 1.0, Decimal(10), (1,2), {}])
1219 self.assert_(Decimal(10) not in ['a', 1.0, (1,2), {}])
1220
Raymond Hettinger955d2b22004-08-08 20:17:45 +00001221 def test_copy(self):
1222 # All copies should be deep
1223 c = Context()
1224 d = c.copy()
1225 self.assertNotEqual(id(c), id(d))
1226 self.assertNotEqual(id(c.flags), id(d.flags))
1227 self.assertNotEqual(id(c.traps), id(d.traps))
1228
Thomas Wouters89f507f2006-12-13 04:49:30 +00001229class WithStatementTest(unittest.TestCase):
1230 # Can't do these as docstrings until Python 2.6
1231 # as doctest can't handle __future__ statements
1232
1233 def test_localcontext(self):
1234 # Use a copy of the current context in the block
1235 orig_ctx = getcontext()
1236 with localcontext() as enter_ctx:
1237 set_ctx = getcontext()
1238 final_ctx = getcontext()
1239 self.assert_(orig_ctx is final_ctx, 'did not restore context correctly')
1240 self.assert_(orig_ctx is not set_ctx, 'did not copy the context')
1241 self.assert_(set_ctx is enter_ctx, '__enter__ returned wrong context')
1242
1243 def test_localcontextarg(self):
1244 # Use a copy of the supplied context in the block
1245 orig_ctx = getcontext()
1246 new_ctx = Context(prec=42)
1247 with localcontext(new_ctx) as enter_ctx:
1248 set_ctx = getcontext()
1249 final_ctx = getcontext()
1250 self.assert_(orig_ctx is final_ctx, 'did not restore context correctly')
1251 self.assert_(set_ctx.prec == new_ctx.prec, 'did not set correct context')
1252 self.assert_(new_ctx is not set_ctx, 'did not copy the context')
1253 self.assert_(set_ctx is enter_ctx, '__enter__ returned wrong context')
1254
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001255class ContextFlags(unittest.TestCase):
1256 def test_flags_irrelevant(self):
1257 # check that the result (numeric result + flags raised) of an
1258 # arithmetic operation doesn't depend on the current flags
1259
1260 context = Context(prec=9, Emin = -999999999, Emax = 999999999,
1261 rounding=ROUND_HALF_EVEN, traps=[], flags=[])
1262
1263 # operations that raise various flags, in the form (function, arglist)
1264 operations = [
1265 (context._apply, [Decimal("100E-1000000009")]),
1266 (context.sqrt, [Decimal(2)]),
1267 (context.add, [Decimal("1.23456789"), Decimal("9.87654321")]),
1268 (context.multiply, [Decimal("1.23456789"), Decimal("9.87654321")]),
1269 (context.subtract, [Decimal("1.23456789"), Decimal("9.87654321")]),
1270 ]
1271
1272 # try various flags individually, then a whole lot at once
1273 flagsets = [[Inexact], [Rounded], [Underflow], [Clamped], [Subnormal],
1274 [Inexact, Rounded, Underflow, Clamped, Subnormal]]
1275
1276 for fn, args in operations:
1277 # find answer and flags raised using a clean context
1278 context.clear_flags()
1279 ans = fn(*args)
1280 flags = [k for k, v in context.flags.items() if v]
1281
1282 for extra_flags in flagsets:
1283 # set flags, before calling operation
1284 context.clear_flags()
1285 for flag in extra_flags:
1286 context._raise_error(flag)
1287 new_ans = fn(*args)
1288
1289 # flags that we expect to be set after the operation
1290 expected_flags = list(flags)
1291 for flag in extra_flags:
1292 if flag not in expected_flags:
1293 expected_flags.append(flag)
1294 expected_flags.sort(key=id)
1295
1296 # flags we actually got
1297 new_flags = [k for k,v in context.flags.items() if v]
1298 new_flags.sort(key=id)
1299
1300 self.assertEqual(ans, new_ans,
1301 "operation produces different answers depending on flags set: " +
1302 "expected %s, got %s." % (ans, new_ans))
1303 self.assertEqual(new_flags, expected_flags,
1304 "operation raises different flags depending on flags set: " +
1305 "expected %s, got %s" % (expected_flags, new_flags))
1306
1307def test_main(arith=False, verbose=None, todo_tests=None, debug=None):
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001308 """ Execute the tests.
1309
Raymond Hettingered20ad82004-09-04 20:09:13 +00001310 Runs all arithmetic tests if arith is True or if the "decimal" resource
1311 is enabled in regrtest.py
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001312 """
Raymond Hettingered20ad82004-09-04 20:09:13 +00001313
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001314 init()
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001315 global TEST_ALL, DEBUG
Raymond Hettingered20ad82004-09-04 20:09:13 +00001316 TEST_ALL = arith or is_resource_enabled('decimal')
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001317 DEBUG = debug
Raymond Hettingered20ad82004-09-04 20:09:13 +00001318
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001319 if todo_tests is None:
1320 test_classes = [
1321 DecimalExplicitConstructionTest,
1322 DecimalImplicitConstructionTest,
1323 DecimalArithmeticOperatorsTest,
1324 DecimalUseOfContextTest,
1325 DecimalUsabilityTest,
1326 DecimalPythonAPItests,
1327 ContextAPItests,
1328 DecimalTest,
1329 WithStatementTest,
1330 ContextFlags
1331 ]
1332 else:
1333 test_classes = [DecimalTest]
1334
1335 # Dynamically build custom test definition for each file in the test
1336 # directory and add the definitions to the DecimalTest class. This
1337 # procedure insures that new files do not get skipped.
1338 for filename in os.listdir(directory):
1339 if '.decTest' not in filename or filename.startswith("."):
1340 continue
1341 head, tail = filename.split('.')
1342 if todo_tests is not None and head not in todo_tests:
1343 continue
1344 tester = lambda self, f=filename: self.eval_file(directory + f)
1345 setattr(DecimalTest, 'test_' + head, tester)
1346 del filename, head, tail, tester
1347
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001348
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001349 try:
1350 run_unittest(*test_classes)
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001351 if todo_tests is None:
1352 import decimal as DecimalModule
1353 run_doctest(DecimalModule, verbose)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001354 finally:
1355 setcontext(ORIGINAL_CONTEXT)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001356
1357if __name__ == '__main__':
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001358 import optparse
1359 p = optparse.OptionParser("test_decimal.py [--debug] [{--skip | test1 [test2 [...]]}]")
1360 p.add_option('--debug', '-d', action='store_true', help='shows the test number and context before each test')
1361 p.add_option('--skip', '-s', action='store_true', help='skip over 90% of the arithmetic tests')
1362 (opt, args) = p.parse_args()
1363
1364 if opt.skip:
1365 test_main(arith=False, verbose=True)
1366 elif args:
1367 test_main(arith=True, verbose=True, todo_tests=args, debug=opt.debug)
Raymond Hettinger7c85fa42004-07-01 11:01:35 +00001368 else:
Thomas Wouters1b7f8912007-09-19 03:06:30 +00001369 test_main(arith=True, verbose=True)