test_support.py revision 1ba24b4fbb1c675cd2edbdf2b1c0fbb5054b8969
1"""Supporting definitions for the Python regression tests."""
2
3if __name__ != 'test.test_support':
4    raise ImportError, 'test_support must be imported from the test package'
5
6import sys
7
8class Error(Exception):
9    """Base class for regression test exceptions."""
10
11class TestFailed(Error):
12    """Test failed."""
13
14class TestSkipped(Error):
15    """Test skipped.
16
17    This can be raised to indicate that a test was deliberatly
18    skipped, but not because a feature wasn't available.  For
19    example, if some resource can't be used, such as the network
20    appears to be unavailable, this should be raised instead of
21    TestFailed.
22    """
23
24class ResourceDenied(TestSkipped):
25    """Test skipped because it requested a disallowed resource.
26
27    This is raised when a test calls requires() for a resource that
28    has not be enabled.  It is used to distinguish between expected
29    and unexpected skips.
30    """
31
32verbose = 1              # Flag set to 0 by regrtest.py
33use_resources = None       # Flag set to [] by regrtest.py
34
35# _original_stdout is meant to hold stdout at the time regrtest began.
36# This may be "the real" stdout, or IDLE's emulation of stdout, or whatever.
37# The point is to have some flavor of stdout the user can actually see.
38_original_stdout = None
39def record_original_stdout(stdout):
40    global _original_stdout
41    _original_stdout = stdout
42
43def get_original_stdout():
44    return _original_stdout or sys.stdout
45
46def unload(name):
47    try:
48        del sys.modules[name]
49    except KeyError:
50        pass
51
52def forget(modname):
53    '''"Forget" a module was ever imported by removing it from sys.modules and
54    deleting any .pyc and .pyo files.'''
55    unload(modname)
56    import os
57    for dirname in sys.path:
58        try:
59            os.unlink(os.path.join(dirname, modname + os.extsep + 'pyc'))
60        except os.error:
61            pass
62        # Deleting the .pyo file cannot be within the 'try' for the .pyc since
63        # the chance exists that there is no .pyc (and thus the 'try' statement
64        # is exited) but there is a .pyo file.
65        try:
66            os.unlink(os.path.join(dirname, modname + os.extsep + 'pyo'))
67        except os.error:
68            pass
69
70def is_resource_enabled(resource):
71    """Test whether a resource is enabled.  Known resources are set by
72    regrtest.py."""
73    return use_resources is not None and resource in use_resources
74
75def requires(resource, msg=None):
76    """Raise ResourceDenied if the specified resource is not available.
77
78    If the caller's module is __main__ then automatically return True.  The
79    possibility of False being returned occurs when regrtest.py is executing."""
80    # see if the caller's module is __main__ - if so, treat as if
81    # the resource was set
82    if sys._getframe().f_back.f_globals.get("__name__") == "__main__":
83        return
84    if not is_resource_enabled(resource):
85        if msg is None:
86            msg = "Use of the `%s' resource not enabled" % resource
87        raise ResourceDenied(msg)
88
89FUZZ = 1e-6
90
91def fcmp(x, y): # fuzzy comparison function
92    if type(x) == type(0.0) or type(y) == type(0.0):
93        try:
94            x, y = coerce(x, y)
95            fuzz = (abs(x) + abs(y)) * FUZZ
96            if abs(x-y) <= fuzz:
97                return 0
98        except:
99            pass
100    elif type(x) == type(y) and type(x) in (type(()), type([])):
101        for i in range(min(len(x), len(y))):
102            outcome = fcmp(x[i], y[i])
103            if outcome != 0:
104                return outcome
105        return cmp(len(x), len(y))
106    return cmp(x, y)
107
108try:
109    unicode
110    have_unicode = 1
111except NameError:
112    have_unicode = 0
113
114is_jython = sys.platform.startswith('java')
115
116import os
117# Filename used for testing
118if os.name == 'java':
119    # Jython disallows @ in module names
120    TESTFN = '$test'
121elif os.name == 'riscos':
122    TESTFN = 'testfile'
123else:
124    TESTFN = '@test'
125    # Unicode name only used if TEST_FN_ENCODING exists for the platform.
126    if have_unicode:
127        if isinstance('', unicode):
128            # python -U
129            # XXX perhaps unicode() should accept Unicode strings?
130            TESTFN_UNICODE="@test-\xe0\xf2"
131        else:
132            TESTFN_UNICODE=unicode("@test-\xe0\xf2", "latin-1") # 2 latin characters.
133        TESTFN_ENCODING=sys.getfilesystemencoding()
134
135# Make sure we can write to TESTFN, try in /tmp if we can't
136fp = None
137try:
138    fp = open(TESTFN, 'w+')
139except IOError:
140    TMP_TESTFN = os.path.join('/tmp', TESTFN)
141    try:
142        fp = open(TMP_TESTFN, 'w+')
143        TESTFN = TMP_TESTFN
144        del TMP_TESTFN
145    except IOError:
146        print ('WARNING: tests will fail, unable to write to: %s or %s' %
147                (TESTFN, TMP_TESTFN))
148if fp is not None:
149    fp.close()
150    try:
151        os.unlink(TESTFN)
152    except:
153        pass
154del os, fp
155
156from os import unlink
157
158def findfile(file, here=__file__):
159    """Try to find a file on sys.path and the working directory.  If it is not
160    found the argument passed to the function is returned (this does not
161    necessarily signal failure; could still be the legitimate path)."""
162    import os
163    if os.path.isabs(file):
164        return file
165    path = sys.path
166    path = [os.path.dirname(here)] + path
167    for dn in path:
168        fn = os.path.join(dn, file)
169        if os.path.exists(fn): return fn
170    return file
171
172def verify(condition, reason='test failed'):
173    """Verify that condition is true. If not, raise TestFailed.
174
175       The optional argument reason can be given to provide
176       a better error text.
177    """
178
179    if not condition:
180        raise TestFailed(reason)
181
182def vereq(a, b):
183    """Raise TestFailed if a == b is false.
184
185    This is better than verify(a == b) because, in case of failure, the
186    error message incorporates repr(a) and repr(b) so you can see the
187    inputs.
188
189    Note that "not (a == b)" isn't necessarily the same as "a != b"; the
190    former is tested.
191    """
192
193    if not (a == b):
194        raise TestFailed, "%r == %r" % (a, b)
195
196def sortdict(dict):
197    "Like repr(dict), but in sorted order."
198    items = dict.items()
199    items.sort()
200    reprpairs = ["%r: %r" % pair for pair in items]
201    withcommas = ", ".join(reprpairs)
202    return "{%s}" % withcommas
203
204def check_syntax(statement):
205    try:
206        compile(statement, '<string>', 'exec')
207    except SyntaxError:
208        pass
209    else:
210        print 'Missing SyntaxError: "%s"' % statement
211
212
213
214#=======================================================================
215# Preliminary PyUNIT integration.
216
217import unittest
218
219
220class BasicTestRunner:
221    def run(self, test):
222        result = unittest.TestResult()
223        test(result)
224        return result
225
226
227def run_suite(suite, testclass=None):
228    """Run tests from a unittest.TestSuite-derived class."""
229    if verbose:
230        runner = unittest.TextTestRunner(sys.stdout, verbosity=2)
231    else:
232        runner = BasicTestRunner()
233
234    result = runner.run(suite)
235    if not result.wasSuccessful():
236        if len(result.errors) == 1 and not result.failures:
237            err = result.errors[0][1]
238        elif len(result.failures) == 1 and not result.errors:
239            err = result.failures[0][1]
240        else:
241            if testclass is None:
242                msg = "errors occurred; run in verbose mode for details"
243            else:
244                msg = "errors occurred in %s.%s" \
245                      % (testclass.__module__, testclass.__name__)
246            raise TestFailed(msg)
247        raise TestFailed(err)
248
249
250def run_unittest(*classes):
251    """Run tests from unittest.TestCase-derived classes."""
252    suite = unittest.TestSuite()
253    for cls in classes:
254        suite.addTest(unittest.makeSuite(cls))
255    if len(classes)==1:
256        testclass = classes[0]
257    else:
258        testclass = None
259    run_suite(suite, testclass)
260
261
262#=======================================================================
263# doctest driver.
264
265def run_doctest(module, verbosity=None):
266    """Run doctest on the given module.  Return (#failures, #tests).
267
268    If optional argument verbosity is not specified (or is None), pass
269    test_support's belief about verbosity on to doctest.  Else doctest's
270    usual behavior is used (it searches sys.argv for -v).
271    """
272
273    import doctest
274
275    if verbosity is None:
276        verbosity = verbose
277    else:
278        verbosity = None
279
280    # Direct doctest output (normally just errors) to real stdout; doctest
281    # output shouldn't be compared by regrtest.
282    save_stdout = sys.stdout
283    sys.stdout = get_original_stdout()
284    try:
285        f, t = doctest.testmod(module, verbose=verbosity)
286        if f:
287            raise TestFailed("%d of %d doctests failed" % (f, t))
288    finally:
289        sys.stdout = save_stdout
290    if verbose:
291        print 'doctest (%s) ... %d tests with zero failures' % (module.__name__, t)
292    return f, t
293