Replaced my dumb way of calculating seconds to midnight with Tim Peters' much more...
[python.git] / Lib / test / test_support.py
blob2d08f4dde01ba3b272b542fc5dbc3520c0dcbda0
1 """Supporting definitions for the Python regression tests."""
3 if __name__ != 'test.test_support':
4 raise ImportError, 'test_support must be imported from the test package'
6 import sys
8 class Error(Exception):
9 """Base class for regression test exceptions."""
11 class TestFailed(Error):
12 """Test failed."""
14 class TestSkipped(Error):
15 """Test skipped.
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 """
24 class ResourceDenied(TestSkipped):
25 """Test skipped because it requested a disallowed resource.
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 """
32 verbose = 1 # Flag set to 0 by regrtest.py
33 use_resources = None # Flag set to [] by regrtest.py
34 max_memuse = 0 # Disable bigmem tests (they will still be run with
35 # small sizes, to make sure they work.)
37 # _original_stdout is meant to hold stdout at the time regrtest began.
38 # This may be "the real" stdout, or IDLE's emulation of stdout, or whatever.
39 # The point is to have some flavor of stdout the user can actually see.
40 _original_stdout = None
41 def record_original_stdout(stdout):
42 global _original_stdout
43 _original_stdout = stdout
45 def get_original_stdout():
46 return _original_stdout or sys.stdout
48 def unload(name):
49 try:
50 del sys.modules[name]
51 except KeyError:
52 pass
54 def unlink(filename):
55 import os
56 try:
57 os.unlink(filename)
58 except OSError:
59 pass
61 def forget(modname):
62 '''"Forget" a module was ever imported by removing it from sys.modules and
63 deleting any .pyc and .pyo files.'''
64 unload(modname)
65 import os
66 for dirname in sys.path:
67 unlink(os.path.join(dirname, modname + os.extsep + 'pyc'))
68 # Deleting the .pyo file cannot be within the 'try' for the .pyc since
69 # the chance exists that there is no .pyc (and thus the 'try' statement
70 # is exited) but there is a .pyo file.
71 unlink(os.path.join(dirname, modname + os.extsep + 'pyo'))
73 def is_resource_enabled(resource):
74 """Test whether a resource is enabled. Known resources are set by
75 regrtest.py."""
76 return use_resources is not None and resource in use_resources
78 def requires(resource, msg=None):
79 """Raise ResourceDenied if the specified resource is not available.
81 If the caller's module is __main__ then automatically return True. The
82 possibility of False being returned occurs when regrtest.py is executing."""
83 # see if the caller's module is __main__ - if so, treat as if
84 # the resource was set
85 if sys._getframe().f_back.f_globals.get("__name__") == "__main__":
86 return
87 if not is_resource_enabled(resource):
88 if msg is None:
89 msg = "Use of the `%s' resource not enabled" % resource
90 raise ResourceDenied(msg)
92 FUZZ = 1e-6
94 def fcmp(x, y): # fuzzy comparison function
95 if type(x) == type(0.0) or type(y) == type(0.0):
96 try:
97 x, y = coerce(x, y)
98 fuzz = (abs(x) + abs(y)) * FUZZ
99 if abs(x-y) <= fuzz:
100 return 0
101 except:
102 pass
103 elif type(x) == type(y) and type(x) in (type(()), type([])):
104 for i in range(min(len(x), len(y))):
105 outcome = fcmp(x[i], y[i])
106 if outcome != 0:
107 return outcome
108 return cmp(len(x), len(y))
109 return cmp(x, y)
111 try:
112 unicode
113 have_unicode = 1
114 except NameError:
115 have_unicode = 0
117 is_jython = sys.platform.startswith('java')
119 import os
120 # Filename used for testing
121 if os.name == 'java':
122 # Jython disallows @ in module names
123 TESTFN = '$test'
124 elif os.name == 'riscos':
125 TESTFN = 'testfile'
126 else:
127 TESTFN = '@test'
128 # Unicode name only used if TEST_FN_ENCODING exists for the platform.
129 if have_unicode:
130 # Assuming sys.getfilesystemencoding()!=sys.getdefaultencoding()
131 # TESTFN_UNICODE is a filename that can be encoded using the
132 # file system encoding, but *not* with the default (ascii) encoding
133 if isinstance('', unicode):
134 # python -U
135 # XXX perhaps unicode() should accept Unicode strings?
136 TESTFN_UNICODE = "@test-\xe0\xf2"
137 else:
138 # 2 latin characters.
139 TESTFN_UNICODE = unicode("@test-\xe0\xf2", "latin-1")
140 TESTFN_ENCODING = sys.getfilesystemencoding()
141 # TESTFN_UNICODE_UNENCODEABLE is a filename that should *not* be
142 # able to be encoded by *either* the default or filesystem encoding.
143 # This test really only makes sense on Windows NT platforms
144 # which have special Unicode support in posixmodule.
145 if (not hasattr(sys, "getwindowsversion") or
146 sys.getwindowsversion()[3] < 2): # 0=win32s or 1=9x/ME
147 TESTFN_UNICODE_UNENCODEABLE = None
148 else:
149 # Japanese characters (I think - from bug 846133)
150 TESTFN_UNICODE_UNENCODEABLE = eval('u"@test-\u5171\u6709\u3055\u308c\u308b"')
151 try:
152 # XXX - Note - should be using TESTFN_ENCODING here - but for
153 # Windows, "mbcs" currently always operates as if in
154 # errors=ignore' mode - hence we get '?' characters rather than
155 # the exception. 'Latin1' operates as we expect - ie, fails.
156 # See [ 850997 ] mbcs encoding ignores errors
157 TESTFN_UNICODE_UNENCODEABLE.encode("Latin1")
158 except UnicodeEncodeError:
159 pass
160 else:
161 print \
162 'WARNING: The filename %r CAN be encoded by the filesystem. ' \
163 'Unicode filename tests may not be effective' \
164 % TESTFN_UNICODE_UNENCODEABLE
166 # Make sure we can write to TESTFN, try in /tmp if we can't
167 fp = None
168 try:
169 fp = open(TESTFN, 'w+')
170 except IOError:
171 TMP_TESTFN = os.path.join('/tmp', TESTFN)
172 try:
173 fp = open(TMP_TESTFN, 'w+')
174 TESTFN = TMP_TESTFN
175 del TMP_TESTFN
176 except IOError:
177 print ('WARNING: tests will fail, unable to write to: %s or %s' %
178 (TESTFN, TMP_TESTFN))
179 if fp is not None:
180 fp.close()
181 unlink(TESTFN)
182 del os, fp
184 def findfile(file, here=__file__):
185 """Try to find a file on sys.path and the working directory. If it is not
186 found the argument passed to the function is returned (this does not
187 necessarily signal failure; could still be the legitimate path)."""
188 import os
189 if os.path.isabs(file):
190 return file
191 path = sys.path
192 path = [os.path.dirname(here)] + path
193 for dn in path:
194 fn = os.path.join(dn, file)
195 if os.path.exists(fn): return fn
196 return file
198 def verify(condition, reason='test failed'):
199 """Verify that condition is true. If not, raise TestFailed.
201 The optional argument reason can be given to provide
202 a better error text.
205 if not condition:
206 raise TestFailed(reason)
208 def vereq(a, b):
209 """Raise TestFailed if a == b is false.
211 This is better than verify(a == b) because, in case of failure, the
212 error message incorporates repr(a) and repr(b) so you can see the
213 inputs.
215 Note that "not (a == b)" isn't necessarily the same as "a != b"; the
216 former is tested.
219 if not (a == b):
220 raise TestFailed, "%r == %r" % (a, b)
222 def sortdict(dict):
223 "Like repr(dict), but in sorted order."
224 items = dict.items()
225 items.sort()
226 reprpairs = ["%r: %r" % pair for pair in items]
227 withcommas = ", ".join(reprpairs)
228 return "{%s}" % withcommas
230 def check_syntax(statement):
231 try:
232 compile(statement, '<string>', 'exec')
233 except SyntaxError:
234 pass
235 else:
236 print 'Missing SyntaxError: "%s"' % statement
238 def open_urlresource(url):
239 import urllib, urlparse
240 import os.path
242 filename = urlparse.urlparse(url)[2].split('/')[-1] # '/': it's URL!
244 for path in [os.path.curdir, os.path.pardir]:
245 fn = os.path.join(path, filename)
246 if os.path.exists(fn):
247 return open(fn)
249 requires('urlfetch')
250 print >> get_original_stdout(), '\tfetching %s ...' % url
251 fn, _ = urllib.urlretrieve(url, filename)
252 return open(fn)
254 #=======================================================================
255 # Decorator for running a function in a different locale, correctly resetting
256 # it afterwards.
258 def run_with_locale(catstr, *locales):
259 def decorator(func):
260 def inner(*args, **kwds):
261 try:
262 import locale
263 category = getattr(locale, catstr)
264 orig_locale = locale.setlocale(category)
265 except AttributeError:
266 # if the test author gives us an invalid category string
267 raise
268 except:
269 # cannot retrieve original locale, so do nothing
270 locale = orig_locale = None
271 else:
272 for loc in locales:
273 try:
274 locale.setlocale(category, loc)
275 break
276 except:
277 pass
279 # now run the function, resetting the locale on exceptions
280 try:
281 return func(*args, **kwds)
282 finally:
283 if locale and orig_locale:
284 locale.setlocale(category, orig_locale)
285 inner.func_name = func.func_name
286 inner.__doc__ = func.__doc__
287 return inner
288 return decorator
290 #=======================================================================
291 # Big-memory-test support. Separate from 'resources' because memory use should be configurable.
293 # Some handy shorthands. Note that these are used for byte-limits as well
294 # as size-limits, in the various bigmem tests
295 _1M = 1024*1024
296 _1G = 1024 * _1M
297 _2G = 2 * _1G
299 def set_memlimit(limit):
300 import re
301 global max_memuse
302 sizes = {
303 'k': 1024,
304 'm': _1M,
305 'g': _1G,
306 't': 1024*_1G,
308 m = re.match(r'(\d+(\.\d+)?) (K|M|G|T)b?$', limit,
309 re.IGNORECASE | re.VERBOSE)
310 if m is None:
311 raise ValueError('Invalid memory limit %r' % (limit,))
312 memlimit = int(float(m.group(1)) * sizes[m.group(3).lower()])
313 if memlimit < 2.5*_1G:
314 raise ValueError('Memory limit %r too low to be useful' % (limit,))
315 max_memuse = memlimit
317 def bigmemtest(minsize, memuse, overhead=5*_1M):
318 """Decorator for bigmem tests.
320 'minsize' is the minimum useful size for the test (in arbitrary,
321 test-interpreted units.) 'memuse' is the number of 'bytes per size' for
322 the test, or a good estimate of it. 'overhead' specifies fixed overhead,
323 independant of the testsize, and defaults to 5Mb.
325 The decorator tries to guess a good value for 'size' and passes it to
326 the decorated test function. If minsize * memuse is more than the
327 allowed memory use (as defined by max_memuse), the test is skipped.
328 Otherwise, minsize is adjusted upward to use up to max_memuse.
330 def decorator(f):
331 def wrapper(self):
332 if not max_memuse:
333 # If max_memuse is 0 (the default),
334 # we still want to run the tests with size set to a few kb,
335 # to make sure they work. We still want to avoid using
336 # too much memory, though, but we do that noisily.
337 maxsize = 5147
338 self.failIf(maxsize * memuse + overhead > 20 * _1M)
339 else:
340 maxsize = int((max_memuse - overhead) / memuse)
341 if maxsize < minsize:
342 # Really ought to print 'test skipped' or something
343 if verbose:
344 sys.stderr.write("Skipping %s because of memory "
345 "constraint\n" % (f.__name__,))
346 return
347 # Try to keep some breathing room in memory use
348 maxsize = max(maxsize - 50 * _1M, minsize)
349 return f(self, maxsize)
350 wrapper.minsize = minsize
351 wrapper.memuse = memuse
352 wrapper.overhead = overhead
353 return wrapper
354 return decorator
356 #=======================================================================
357 # Preliminary PyUNIT integration.
359 import unittest
362 class BasicTestRunner:
363 def run(self, test):
364 result = unittest.TestResult()
365 test(result)
366 return result
369 def run_suite(suite, testclass=None):
370 """Run tests from a unittest.TestSuite-derived class."""
371 if verbose:
372 runner = unittest.TextTestRunner(sys.stdout, verbosity=2)
373 else:
374 runner = BasicTestRunner()
376 result = runner.run(suite)
377 if not result.wasSuccessful():
378 if len(result.errors) == 1 and not result.failures:
379 err = result.errors[0][1]
380 elif len(result.failures) == 1 and not result.errors:
381 err = result.failures[0][1]
382 else:
383 if testclass is None:
384 msg = "errors occurred; run in verbose mode for details"
385 else:
386 msg = "errors occurred in %s.%s" \
387 % (testclass.__module__, testclass.__name__)
388 raise TestFailed(msg)
389 raise TestFailed(err)
392 def run_unittest(*classes):
393 """Run tests from unittest.TestCase-derived classes."""
394 suite = unittest.TestSuite()
395 for cls in classes:
396 if isinstance(cls, (unittest.TestSuite, unittest.TestCase)):
397 suite.addTest(cls)
398 else:
399 suite.addTest(unittest.makeSuite(cls))
400 if len(classes)==1:
401 testclass = classes[0]
402 else:
403 testclass = None
404 run_suite(suite, testclass)
407 #=======================================================================
408 # doctest driver.
410 def run_doctest(module, verbosity=None):
411 """Run doctest on the given module. Return (#failures, #tests).
413 If optional argument verbosity is not specified (or is None), pass
414 test_support's belief about verbosity on to doctest. Else doctest's
415 usual behavior is used (it searches sys.argv for -v).
418 import doctest
420 if verbosity is None:
421 verbosity = verbose
422 else:
423 verbosity = None
425 # Direct doctest output (normally just errors) to real stdout; doctest
426 # output shouldn't be compared by regrtest.
427 save_stdout = sys.stdout
428 sys.stdout = get_original_stdout()
429 try:
430 f, t = doctest.testmod(module, verbose=verbosity)
431 if f:
432 raise TestFailed("%d of %d doctests failed" % (f, t))
433 finally:
434 sys.stdout = save_stdout
435 if verbose:
436 print 'doctest (%s) ... %d tests with zero failures' % (module.__name__, t)
437 return f, t