2000-07-24 14:55:00 +08:00
|
|
|
"""Supporting definitions for the Python regression test."""
|
|
|
|
|
2001-03-22 02:26:33 +08:00
|
|
|
import sys
|
|
|
|
|
2000-07-24 14:55:00 +08:00
|
|
|
class Error(Exception):
|
2000-10-24 01:22:08 +08:00
|
|
|
"""Base class for regression test exceptions."""
|
2000-07-24 14:55:00 +08:00
|
|
|
|
|
|
|
class TestFailed(Error):
|
2000-10-24 01:22:08 +08:00
|
|
|
"""Test failed."""
|
2000-07-24 14:55:00 +08:00
|
|
|
|
|
|
|
class TestSkipped(Error):
|
2000-10-24 01:22:08 +08:00
|
|
|
"""Test skipped.
|
2000-07-24 14:55:00 +08:00
|
|
|
|
2000-10-24 01:22:08 +08:00
|
|
|
This can be raised to indicate that a test was deliberatly
|
|
|
|
skipped, but not because a feature wasn't available. For
|
|
|
|
example, if some resource can't be used, such as the network
|
|
|
|
appears to be unavailable, this should be raised instead of
|
|
|
|
TestFailed.
|
|
|
|
"""
|
1992-01-28 01:00:37 +08:00
|
|
|
|
2001-08-21 06:29:23 +08:00
|
|
|
verbose = 1 # Flag set to 0 by regrtest.py
|
2001-09-07 00:09:41 +08:00
|
|
|
use_resources = None # Flag set to [] by regrtest.py
|
1996-12-20 10:58:22 +08:00
|
|
|
|
2001-09-26 04:05:11 +08:00
|
|
|
# _original_stdout is meant to hold stdout at the time regrtest began.
|
|
|
|
# This may be "the real" stdout, or IDLE's emulation of stdout, or whatever.
|
|
|
|
# The point is to have some flavor of stdout the user can actually see.
|
|
|
|
_original_stdout = None
|
|
|
|
def record_original_stdout(stdout):
|
|
|
|
global _original_stdout
|
|
|
|
_original_stdout = stdout
|
|
|
|
|
|
|
|
def get_original_stdout():
|
|
|
|
return _original_stdout or sys.stdout
|
|
|
|
|
1992-01-28 01:00:37 +08:00
|
|
|
def unload(name):
|
2000-10-24 01:22:08 +08:00
|
|
|
try:
|
|
|
|
del sys.modules[name]
|
|
|
|
except KeyError:
|
|
|
|
pass
|
1992-01-28 01:00:37 +08:00
|
|
|
|
|
|
|
def forget(modname):
|
2000-10-24 01:22:08 +08:00
|
|
|
unload(modname)
|
2001-03-22 02:26:33 +08:00
|
|
|
import os
|
2000-10-24 01:22:08 +08:00
|
|
|
for dirname in sys.path:
|
|
|
|
try:
|
|
|
|
os.unlink(os.path.join(dirname, modname + '.pyc'))
|
|
|
|
except os.error:
|
|
|
|
pass
|
1992-01-28 01:00:37 +08:00
|
|
|
|
2001-08-21 06:29:23 +08:00
|
|
|
def requires(resource, msg=None):
|
2001-09-07 00:09:41 +08:00
|
|
|
if use_resources is not None and resource not in use_resources:
|
2001-08-21 06:29:23 +08:00
|
|
|
if msg is None:
|
|
|
|
msg = "Use of the `%s' resource not enabled" % resource
|
|
|
|
raise TestSkipped(msg)
|
|
|
|
|
1993-01-26 21:04:43 +08:00
|
|
|
FUZZ = 1e-6
|
|
|
|
|
|
|
|
def fcmp(x, y): # fuzzy comparison function
|
2000-10-24 01:22:08 +08:00
|
|
|
if type(x) == type(0.0) or type(y) == type(0.0):
|
|
|
|
try:
|
|
|
|
x, y = coerce(x, y)
|
|
|
|
fuzz = (abs(x) + abs(y)) * FUZZ
|
|
|
|
if abs(x-y) <= fuzz:
|
|
|
|
return 0
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
elif type(x) == type(y) and type(x) in (type(()), type([])):
|
|
|
|
for i in range(min(len(x), len(y))):
|
|
|
|
outcome = fcmp(x[i], y[i])
|
2000-12-13 07:11:42 +08:00
|
|
|
if outcome != 0:
|
2000-10-24 01:22:08 +08:00
|
|
|
return outcome
|
|
|
|
return cmp(len(x), len(y))
|
|
|
|
return cmp(x, y)
|
1993-01-26 21:04:43 +08:00
|
|
|
|
2001-08-18 02:39:25 +08:00
|
|
|
try:
|
|
|
|
unicode
|
|
|
|
have_unicode = 1
|
|
|
|
except NameError:
|
|
|
|
have_unicode = 0
|
|
|
|
|
2001-03-13 17:31:07 +08:00
|
|
|
import os
|
2001-03-24 02:04:02 +08:00
|
|
|
# Filename used for testing
|
|
|
|
if os.name == 'java':
|
|
|
|
# Jython disallows @ in module names
|
|
|
|
TESTFN = '$test'
|
|
|
|
elif os.name != 'riscos':
|
|
|
|
TESTFN = '@test'
|
2001-05-13 16:04:26 +08:00
|
|
|
# Unicode name only used if TEST_FN_ENCODING exists for the platform.
|
2001-08-18 02:39:25 +08:00
|
|
|
if have_unicode:
|
|
|
|
TESTFN_UNICODE=unicode("@test-\xe0\xf2", "latin-1") # 2 latin characters.
|
|
|
|
if os.name=="nt":
|
|
|
|
TESTFN_ENCODING="mbcs"
|
2001-03-13 17:31:07 +08:00
|
|
|
else:
|
2001-03-24 02:04:02 +08:00
|
|
|
TESTFN = 'test'
|
2001-03-13 17:31:07 +08:00
|
|
|
del os
|
|
|
|
|
1992-01-28 01:00:37 +08:00
|
|
|
from os import unlink
|
1998-04-24 04:13:30 +08:00
|
|
|
|
|
|
|
def findfile(file, here=__file__):
|
2000-10-24 01:22:08 +08:00
|
|
|
import os
|
|
|
|
if os.path.isabs(file):
|
|
|
|
return file
|
|
|
|
path = sys.path
|
|
|
|
path = [os.path.dirname(here)] + path
|
|
|
|
for dn in path:
|
|
|
|
fn = os.path.join(dn, file)
|
|
|
|
if os.path.exists(fn): return fn
|
|
|
|
return file
|
2001-01-18 03:11:13 +08:00
|
|
|
|
|
|
|
def verify(condition, reason='test failed'):
|
2001-01-20 03:01:56 +08:00
|
|
|
"""Verify that condition is true. If not, raise TestFailed.
|
2001-01-18 10:22:22 +08:00
|
|
|
|
2001-01-21 03:12:54 +08:00
|
|
|
The optional argument reason can be given to provide
|
2001-01-19 13:59:21 +08:00
|
|
|
a better error text.
|
2001-01-18 10:22:22 +08:00
|
|
|
"""
|
2001-01-19 13:59:21 +08:00
|
|
|
|
2001-01-18 10:22:22 +08:00
|
|
|
if not condition:
|
2001-01-20 03:01:56 +08:00
|
|
|
raise TestFailed(reason)
|
2001-02-19 23:35:26 +08:00
|
|
|
|
Get rid of the superstitious "~" in dict hashing's "i = (~hash) & mask".
The comment following used to say:
/* We use ~hash instead of hash, as degenerate hash functions, such
as for ints <sigh>, can have lots of leading zeros. It's not
really a performance risk, but better safe than sorry.
12-Dec-00 tim: so ~hash produces lots of leading ones instead --
what's the gain? */
That is, there was never a good reason for doing it. And to the contrary,
as explained on Python-Dev last December, it tended to make the *sum*
(i + incr) & mask (which is the first table index examined in case of
collison) the same "too often" across distinct hashes.
Changing to the simpler "i = hash & mask" reduced the number of string-dict
collisions (== # number of times we go around the lookup for-loop) from about
6 million to 5 million during a full run of the test suite (these are
approximate because the test suite does some random stuff from run to run).
The number of collisions in non-string dicts also decreased, but not as
dramatically.
Note that this may, for a given dict, change the order (wrt previous
releases) of entries exposed by .keys(), .values() and .items(). A number
of std tests suffered bogus failures as a result. For dicts keyed by
small ints, or (less so) by characters, the order is much more likely to be
in increasing order of key now; e.g.,
>>> d = {}
>>> for i in range(10):
... d[i] = i
...
>>> d
{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}
>>>
Unfortunately. people may latch on to that in small examples and draw a
bogus conclusion.
test_support.py
Moved test_extcall's sortdict() into test_support, made it stronger,
and imported sortdict into other std tests that needed it.
test_unicode.py
Excluced cp875 from the "roundtrip over range(128)" test, because
cp875 doesn't have a well-defined inverse for unicode("?", "cp875").
See Python-Dev for excruciating details.
Cookie.py
Chaged various output functions to sort dicts before building
strings from them.
test_extcall
Fiddled the expected-result file. This remains sensitive to native
dict ordering, because, e.g., if there are multiple errors in a
keyword-arg dict (and test_extcall sets up many cases like that), the
specific error Python complains about first depends on native dict
ordering.
2001-05-13 08:19:31 +08:00
|
|
|
def sortdict(dict):
|
|
|
|
"Like repr(dict), but in sorted order."
|
|
|
|
items = dict.items()
|
|
|
|
items.sort()
|
|
|
|
reprpairs = ["%r: %r" % pair for pair in items]
|
|
|
|
withcommas = ", ".join(reprpairs)
|
|
|
|
return "{%s}" % withcommas
|
|
|
|
|
2001-02-19 23:35:26 +08:00
|
|
|
def check_syntax(statement):
|
|
|
|
try:
|
|
|
|
compile(statement, '<string>', 'exec')
|
|
|
|
except SyntaxError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
print 'Missing SyntaxError: "%s"' % statement
|
2001-03-22 02:26:33 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#=======================================================================
|
|
|
|
# Preliminary PyUNIT integration.
|
|
|
|
|
|
|
|
import unittest
|
|
|
|
|
|
|
|
|
2001-03-22 16:45:36 +08:00
|
|
|
class BasicTestRunner:
|
2001-03-22 02:26:33 +08:00
|
|
|
def run(self, test):
|
2001-03-22 16:45:36 +08:00
|
|
|
result = unittest.TestResult()
|
2001-03-22 02:26:33 +08:00
|
|
|
test(result)
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
2001-10-05 03:46:07 +08:00
|
|
|
def run_suite(suite, testclass=None):
|
2001-09-20 14:31:22 +08:00
|
|
|
"""Run tests from a unittest.TestSuite-derived class."""
|
2001-03-22 02:26:33 +08:00
|
|
|
if verbose:
|
2001-03-23 12:21:17 +08:00
|
|
|
runner = unittest.TextTestRunner(sys.stdout, verbosity=2)
|
2001-03-22 02:26:33 +08:00
|
|
|
else:
|
2001-03-22 16:45:36 +08:00
|
|
|
runner = BasicTestRunner()
|
2001-03-22 02:26:33 +08:00
|
|
|
|
2001-03-22 16:45:36 +08:00
|
|
|
result = runner.run(suite)
|
|
|
|
if not result.wasSuccessful():
|
2001-07-17 02:51:32 +08:00
|
|
|
if len(result.errors) == 1 and not result.failures:
|
|
|
|
err = result.errors[0][1]
|
|
|
|
elif len(result.failures) == 1 and not result.errors:
|
|
|
|
err = result.failures[0][1]
|
|
|
|
else:
|
2001-10-05 03:46:07 +08:00
|
|
|
if testclass is None:
|
|
|
|
msg = "errors occurred; run in verbose mode for details"
|
|
|
|
else:
|
|
|
|
msg = "errors occurred in %s.%s" \
|
|
|
|
% (testclass.__module__, testclass.__name__)
|
|
|
|
raise TestFailed(msg)
|
2001-09-08 11:37:56 +08:00
|
|
|
raise TestFailed(err)
|
2001-09-09 14:12:01 +08:00
|
|
|
|
2001-09-20 14:30:41 +08:00
|
|
|
|
|
|
|
def run_unittest(testclass):
|
|
|
|
"""Run tests from a unittest.TestCase-derived class."""
|
2001-10-05 03:46:07 +08:00
|
|
|
run_suite(unittest.makeSuite(testclass), testclass)
|
2001-09-20 14:30:41 +08:00
|
|
|
|
|
|
|
|
2001-09-09 14:12:01 +08:00
|
|
|
#=======================================================================
|
|
|
|
# doctest driver.
|
|
|
|
|
|
|
|
def run_doctest(module, verbosity=None):
|
2001-10-03 12:08:26 +08:00
|
|
|
"""Run doctest on the given module. Return (#failures, #tests).
|
2001-09-09 14:12:01 +08:00
|
|
|
|
|
|
|
If optional argument verbosity is not specified (or is None), pass
|
2001-09-10 09:39:21 +08:00
|
|
|
test_support's belief about verbosity on to doctest. Else doctest's
|
|
|
|
usual behavior is used (it searches sys.argv for -v).
|
2001-09-09 14:12:01 +08:00
|
|
|
"""
|
|
|
|
|
|
|
|
import doctest
|
|
|
|
|
|
|
|
if verbosity is None:
|
|
|
|
verbosity = verbose
|
|
|
|
else:
|
|
|
|
verbosity = None
|
|
|
|
|
2001-09-26 03:13:20 +08:00
|
|
|
# Direct doctest output (normally just errors) to real stdout; doctest
|
|
|
|
# output shouldn't be compared by regrtest.
|
|
|
|
save_stdout = sys.stdout
|
2001-09-26 04:05:11 +08:00
|
|
|
sys.stdout = get_original_stdout()
|
2001-09-26 03:13:20 +08:00
|
|
|
try:
|
|
|
|
f, t = doctest.testmod(module, verbose=verbosity)
|
|
|
|
if f:
|
|
|
|
raise TestFailed("%d of %d doctests failed" % (f, t))
|
2001-10-03 12:08:26 +08:00
|
|
|
return f, t
|
2001-09-26 03:13:20 +08:00
|
|
|
finally:
|
|
|
|
sys.stdout = save_stdout
|