Fixes in linepsacing doc.
[docutils.git] / test / package_unittest.py
blob019d6fb381b842cb2a68d0bc8be69c36c01e08a5
1 #! /usr/bin/env python
3 # Author: Garth Kidd
4 # Contact: garth@deadlybloodyserious.com
5 # Revision: $Revision$
6 # Date: $Date$
7 # Copyright: This module has been placed in the public domain.
9 """
10 This module extends unittest.py with `loadTestModules()`, by loading multiple
11 test modules from a directory. Optionally, test packages are also loaded,
12 recursively.
13 """
15 import sys
16 import os
17 import getopt
18 import types
19 import unittest
20 import re
23 # So that individual test modules can share a bit of state,
24 # `package_unittest` acts as an intermediary for the following
25 # variables:
26 debug = 0
27 verbosity = 1
29 USAGE = """\
30 Usage: test_whatever [options]
32 Options:
33 -h, --help Show this message
34 -v, --verbose Verbose output
35 -q, --quiet Minimal output
36 -d, --debug Debug mode
37 """
39 def usageExit(msg=None):
40 """Print usage and exit."""
41 if msg:
42 print msg
43 print USAGE
44 sys.exit(2)
46 def parseArgs(argv=sys.argv):
47 """Parse command line arguments and set TestFramework state.
49 State is to be acquired by test_* modules by a grotty hack:
50 ``from TestFramework import *``. For this stylistic
51 transgression, I expect to be first up against the wall
52 when the revolution comes. --Garth"""
53 global verbosity, debug
54 try:
55 options, args = getopt.getopt(argv[1:], 'hHvqd',
56 ['help', 'verbose', 'quiet', 'debug'])
57 for opt, value in options:
58 if opt in ('-h', '-H', '--help'):
59 usageExit()
60 if opt in ('-q', '--quiet'):
61 verbosity = 0
62 if opt in ('-v', '--verbose'):
63 verbosity = 2
64 if opt in ('-d', '--debug'):
65 debug =1
66 if len(args) != 0:
67 usageExit("No command-line arguments supported yet.")
68 except getopt.error, msg:
69 self.usageExit(msg)
71 def loadTestModules(path, name='', packages=None):
72 """
73 Return a test suite composed of all the tests from modules in a directory.
75 Search for modules in directory `path`, beginning with `name`. If
76 `packages` is true, search subdirectories (also beginning with `name`)
77 recursively. Subdirectories must be Python packages; they must contain an
78 '__init__.py' module.
79 """
80 testLoader = unittest.defaultTestLoader
81 testSuite = unittest.TestSuite()
82 testModules = []
83 path = os.path.abspath(path) # current working dir if `path` empty
84 paths = [path]
85 while paths:
86 p = paths.pop(0)
87 files = os.listdir(p)
88 for filename in files:
89 if filename.startswith(name):
90 fullpath = os.path.join(p, filename)
91 if filename.endswith('.py'):
92 fullpath = fullpath[len(path)+1:]
93 testModules.append(path2mod(fullpath))
94 elif packages and os.path.isdir(fullpath) and \
95 os.path.isfile(os.path.join(fullpath, '__init__.py')):
96 paths.append(fullpath)
97 # Import modules and add their tests to the suite.
98 sys.path.insert(0, path)
99 for mod in testModules:
100 if debug:
101 print >>sys.stderr, "importing %s" % mod
102 module = import_module(mod)
103 # if there's a suite defined, incorporate its contents
104 try:
105 suite = getattr(module, 'suite')
106 except AttributeError:
107 # Look for individual tests
108 moduleTests = testLoader.loadTestsFromModule(module)
109 # unittest.TestSuite.addTests() doesn't work as advertised,
110 # as it can't load tests from another TestSuite, so we have
111 # to cheat:
112 testSuite.addTest(moduleTests)
113 continue
114 if type(suite) == types.FunctionType:
115 testSuite.addTest(suite())
116 elif type(suite) == types.InstanceType \
117 and isinstance(suite, unittest.TestSuite):
118 testSuite.addTest(suite)
119 else:
120 raise AssertionError, "don't understand suite (%s)" % mod
121 sys.path.pop(0)
122 return testSuite
124 def path2mod(path):
125 """Convert a file path to a dotted module name."""
126 return path[:-3].replace(os.sep, '.')
128 def import_module(name):
129 """Import a dotted-path module name, and return the final component."""
130 mod = __import__(name)
131 components = name.split('.')
132 for comp in components[1:]:
133 mod = getattr(mod, comp)
134 return mod
136 def main(suite=None):
138 Shared `main` for any individual test_* file.
140 suite -- TestSuite to run. If not specified, look for any globally defined
141 tests and run them.
143 parseArgs()
144 if suite is None:
145 # Load any globally defined tests.
146 suite = unittest.defaultTestLoader.loadTestsFromModule(
147 __import__('__main__'))
148 if debug:
149 print >>sys.stderr, "Debug: Suite=%s" % suite
150 testRunner = unittest.TextTestRunner(verbosity=verbosity)
151 # run suites (if we were called from test_all) or suite...
152 if type(suite) == type([]):
153 for s in suite:
154 testRunner.run(s)
155 else:
156 testRunner.run(suite)