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