Skip to content

Commit 9fee7ea

Browse files
committed
Update test.math_cmath to count/log the discrepancies
test_cmath is skip-free. The case test_cmath_matches_math tests more functions, and test_specific_values no longer exits at the first failure, but counts them. Error detail useful to cmath development is reported in verbose mode.
1 parent a2c7579 commit 9fee7ea

File tree

3 files changed

+89
-86
lines changed

3 files changed

+89
-86
lines changed

Lib/test/test_cmath.py

Lines changed: 82 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from test.test_support import run_unittest, is_jython
1+
from test.test_support import run_unittest, verbose
22
from test.test_math import parse_testfile, test_file
33
import unittest
44
import cmath, math
@@ -52,7 +52,6 @@ class CMathTests(unittest.TestCase):
5252
'cos', 'cosh', 'exp', 'log', 'log10', 'sin', 'sinh',
5353
'sqrt', 'tan', 'tanh']]
5454
# test first and second arguments independently for 2-argument log
55-
5655
test_functions.append(lambda x : cmath.log(x, 1729. + 0j))
5756
test_functions.append(lambda x : cmath.log(14.-27j, x))
5857

@@ -244,24 +243,30 @@ def test_cmath_matches_math(self):
244243
unit_interval = test_values + [-x for x in test_values] + \
245244
[0., 1., -1.]
246245

246+
# test_values for acosh, atanh
247+
ge_one = [1.] + [1./x for x in test_values]
248+
unit_open = test_values + [-x for x in test_values] + [0.]
249+
247250
# test_values for log, log10, sqrt
248-
positive = test_values + [1.] + [1./x for x in test_values]
251+
positive = test_values + ge_one
249252
nonnegative = [0.] + positive
250253

251254
# test_values for functions defined on the whole real line
252255
real_line = [0.] + positive + [-x for x in positive]
253256

254257
test_functions = {
255-
# FIXME uncomment tests for Jython
256-
#'acos' : unit_interval,
257-
#'asin' : unit_interval,
258-
#'atan' : real_line,
259-
#'cos' : real_line,
260-
#'cosh' : real_line,
258+
'acos' : unit_interval,
259+
'asin' : unit_interval,
260+
'atan' : real_line,
261+
'acosh' : ge_one, # Jython added
262+
'asinh' : real_line, # Jython added
263+
'atanh' : unit_open, # Jython added
264+
'cos' : real_line,
265+
'cosh' : real_line,
261266
'exp' : real_line,
262267
'log' : positive,
263268
'log10' : positive,
264-
#'sin' : real_line,
269+
'sin' : real_line,
265270
'sinh' : real_line,
266271
'sqrt' : nonnegative,
267272
'tan' : real_line,
@@ -273,7 +278,7 @@ def test_cmath_matches_math(self):
273278
for v in values:
274279
z = complex_fn(v)
275280
self.rAssertAlmostEqual(float_fn(v), z.real)
276-
self.rAssertAlmostEqual(0., z.imag)
281+
self.assertEqual(0., z.imag)
277282

278283
# test two-argument version of log with various bases
279284
for base in [0.5, 2., 10.]:
@@ -282,10 +287,9 @@ def test_cmath_matches_math(self):
282287
self.rAssertAlmostEqual(math.log(v, base), z.real)
283288
self.assertEqual(0., z.imag)
284289

285-
@unittest.skipIf(is_jython, "FIXME: not working in Jython")
286290
def test_specific_values(self):
287291
if not float.__getformat__("double").startswith("IEEE"):
288-
return
292+
self.skipTest('needs IEEE double')
289293

290294
def rect_complex(z):
291295
"""Wrapped version of rect that accepts a complex number instead of
@@ -297,78 +301,88 @@ def polar_complex(z):
297301
two floats."""
298302
return complex(*polar(z))
299303

304+
raised_fmt = '\n' \
305+
'{}: {}(complex({!r}, {!r}))\n' \
306+
'Raised: {!r}\n' \
307+
'Expected: complex({!r}, {!r})'
308+
not_raised_fmt = '\n' \
309+
'{} not raised in test {}: {}(complex({!r}, {!r}))\n' \
310+
'Received: complex({!r}, {!r})'
311+
value_fmt = '\n' \
312+
'{}: {}(complex({!r}, {!r}))\n' \
313+
'Expected: complex({!r}, {!r})\n' \
314+
'Received: complex({!r}, {!r})\n' \
315+
'Received value insufficiently close to expected value.'
316+
failures = 0
317+
300318
for id, fn, ar, ai, er, ei, flags in parse_testfile(test_file):
301319
arg = complex(ar, ai)
302-
expected = complex(er, ei)
303320
if fn == 'rect':
304321
function = rect_complex
305322
elif fn == 'polar':
306323
function = polar_complex
307324
else:
308325
function = getattr(cmath, fn)
309-
if 'divide-by-zero' in flags or 'invalid' in flags:
310-
try:
326+
327+
try:
328+
# Catch and count failing test cases locally
329+
if 'divide-by-zero' in flags or 'invalid' in flags:
311330
try:
312331
actual = function(arg)
313332
except ValueError:
314-
continue
333+
pass
315334
else:
316-
self.fail('ValueError not raised in test '
317-
'{}: {}(complex({!r}, {!r}))'.format(id, fn, ar, ai))
318-
except AssertionError, ex:
319-
print "Got", function, ex
320-
except BaseException, ex:
321-
print "Got", function, ex
335+
failures += 1
336+
self.fail(not_raised_fmt.format('ValueError',
337+
id, fn, ar, ai, actual.real, actual.imag))
322338

323-
try:
324-
if 'overflow' in flags:
339+
elif 'overflow' in flags:
325340
try:
326341
actual = function(arg)
327342
except OverflowError:
328-
continue
329-
except BaseException, ex:
330-
print "\nGot", function, ex
343+
pass
331344
else:
332-
self.fail('OverflowError not raised in test '
333-
'{}: {}(complex({!r}, {!r}))'.format(id, fn, ar, ai))
334-
except AssertionError, ex:
335-
print "\nGot", function, ex
345+
failures += 1
346+
self.fail(not_raised_fmt.format('OverflowError',
347+
id, fn, ar, ai, actual.real, actual.imag))
348+
349+
else :
350+
actual = function(arg)
351+
352+
# Make sign of expected conform to actual, where ignored.
353+
exr, exi = er, ei
354+
if 'ignore-real-sign' in flags:
355+
exr = math.copysign(er, actual.real)
356+
if 'ignore-imag-sign' in flags:
357+
exi = math.copysign(ei, actual.imag)
358+
359+
# for the real part of the log function, we allow an
360+
# absolute error of up to 2e-15.
361+
if fn in ('log', 'log10'):
362+
real_abs_err = 2e-15
363+
else:
364+
real_abs_err = 5e-323
336365

337-
try:
338-
actual = function(arg)
339-
except BaseException, ex:
340-
print "\nGot", function, ex
341-
342-
if 'ignore-real-sign' in flags:
343-
actual = complex(abs(actual.real), actual.imag)
344-
expected = complex(abs(expected.real), expected.imag)
345-
if 'ignore-imag-sign' in flags:
346-
actual = complex(actual.real, abs(actual.imag))
347-
expected = complex(expected.real, abs(expected.imag))
348-
349-
# for the real part of the log function, we allow an
350-
# absolute error of up to 2e-15.
351-
if fn in ('log', 'log10'):
352-
real_abs_err = 2e-15
353-
else:
354-
real_abs_err = 5e-323
366+
error_message = value_fmt.format(id, fn, ar, ai, er, ei,
367+
actual.real, actual.imag)
368+
self.rAssertAlmostEqual(exr, actual.real,
369+
abs_err=real_abs_err,
370+
msg=error_message)
371+
self.rAssertAlmostEqual(exi, actual.imag,
372+
msg=error_message)
373+
374+
except AssertionError as ex:
375+
failures += 1
376+
if verbose :
377+
print(ex)
378+
except BaseException as ex:
379+
failures += 1
380+
if verbose :
381+
print(raised_fmt.format(id, fn, ar, ai, ex, er, ei))
382+
383+
if failures :
384+
self.fail('{} discrepancies'.format(failures))
355385

356-
error_message = (
357-
'{}: {}(complex({!r}, {!r}))\n'
358-
'Expected: complex({!r}, {!r})\n'
359-
'Received: complex({!r}, {!r})\n'
360-
'Received value insufficiently close to expected value.'
361-
).format(id, fn, ar, ai,
362-
expected.real, expected.imag,
363-
actual.real, actual.imag)
364-
try:
365-
self.rAssertAlmostEqual(expected.real, actual.real,
366-
abs_err=real_abs_err,
367-
msg=error_message)
368-
self.rAssertAlmostEqual(expected.imag, actual.imag,
369-
msg=error_message)
370-
except AssertionError, ex:
371-
print "\nGot", ex, error_message
372386

373387
def assertCISEqual(self, a, b):
374388
eps = 1E-7
@@ -446,6 +460,8 @@ def test_abs(self):
446460
self.assertTrue(math.isnan(abs(complex(2.3, NAN))))
447461
self.assertEqual(abs(complex(INF, NAN)), INF)
448462
self.assertTrue(math.isnan(abs(complex(NAN, NAN))))
463+
464+
# result overflows
449465
if float.__getformat__("double").startswith("IEEE"):
450466
self.assertRaises(OverflowError, abs, complex(1.4e308, 1.4e308))
451467

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ Jython NEWS
33
Jython 2.7b4
44
Bugs Fixed
55
- [ 2037 ] Byte-string containing elements greater than 255
6+
- [ 2244 ] Weak testing of real math module
7+
- [ 2237 ] Test failures with float and complex
68

79
Jython 2.7b3
810
Bugs Fixed

src/org/python/modules/cmath.java

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,11 @@ private static PyComplex exceptNaN(PyComplex result, PyComplex arg) throws PyExc
11451145
}
11461146
}
11471147

1148+
/**
1149+
* Raise <code>ValueError</code> if <code>result</code> is a <code>NaN</code>, but neither
1150+
* <code>a</code> nor <code>b</code> is <code>NaN</code>. Same as
1151+
* {@link #exceptNaN(PyComplex, PyComplex)}.
1152+
*/
11481153
private static PyComplex exceptNaN(PyComplex result, double a, double b) throws PyException {
11491154
if ((Double.isNaN(result.real) || Double.isNaN(result.imag))
11501155
&& !(Double.isNaN(a) || Double.isNaN(b))) {
@@ -1154,24 +1159,4 @@ private static PyComplex exceptNaN(PyComplex result, double a, double b) throws
11541159
}
11551160
}
11561161

1157-
/**
1158-
* Turn an infinite result into a thrown <code>OverflowError</code>, a math range error, if the
1159-
* original argument was not itself infinite. A <code>PyComplex</code> is infinite if either
1160-
* component is infinite.
1161-
*
1162-
* @param result to return (if we return)
1163-
* @param arg to include in check
1164-
* @return result if <code>arg</code> was infinite or <code>result</code> was not infinite
1165-
* @throws PyException (ValueError) if <code>result</code> was infinite and <code>arg</code> was
1166-
* not infinite
1167-
*/
1168-
private static PyComplex exceptInf(PyComplex result, PyComplex arg) {
1169-
if ((Double.isInfinite(result.real) || Double.isInfinite(result.imag))
1170-
&& !(Double.isInfinite(arg.real) || Double.isInfinite(arg.imag))) {
1171-
throw math.mathRangeError();
1172-
} else {
1173-
return result;
1174-
}
1175-
}
1176-
11771162
}

0 commit comments

Comments
 (0)