1 | """Implements nose test program and collector. |
---|
2 | """ |
---|
3 | from __future__ import generators |
---|
4 | |
---|
5 | import logging |
---|
6 | import os |
---|
7 | import sys |
---|
8 | import time |
---|
9 | import unittest |
---|
10 | |
---|
11 | from nose.config import Config, all_config_files |
---|
12 | from nose.loader import defaultTestLoader |
---|
13 | from nose.plugins.manager import PluginManager, DefaultPluginManager, \ |
---|
14 | RestrictedPluginManager |
---|
15 | from nose.result import TextTestResult |
---|
16 | from nose.suite import FinalizingSuiteWrapper |
---|
17 | from nose.util import isclass, tolist |
---|
18 | |
---|
19 | |
---|
20 | log = logging.getLogger('nose.core') |
---|
21 | compat_24 = sys.version_info >= (2, 4) |
---|
22 | |
---|
23 | __all__ = ['TestProgram', 'main', 'run', 'run_exit', 'runmodule', 'collector', |
---|
24 | 'TextTestRunner'] |
---|
25 | |
---|
26 | |
---|
27 | class TextTestRunner(unittest.TextTestRunner): |
---|
28 | """Test runner that uses nose's TextTestResult to enable errorClasses, |
---|
29 | as well as providing hooks for plugins to override or replace the test |
---|
30 | output stream, results, and the test case itself. |
---|
31 | """ |
---|
32 | def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1, |
---|
33 | config=None): |
---|
34 | if config is None: |
---|
35 | config = Config() |
---|
36 | self.config = config |
---|
37 | unittest.TextTestRunner.__init__(self, stream, descriptions, verbosity) |
---|
38 | |
---|
39 | |
---|
40 | def _makeResult(self): |
---|
41 | return TextTestResult(self.stream, |
---|
42 | self.descriptions, |
---|
43 | self.verbosity, |
---|
44 | self.config) |
---|
45 | |
---|
46 | def run(self, test): |
---|
47 | """Overrides to provide plugin hooks and defer all output to |
---|
48 | the test result class. |
---|
49 | """ |
---|
50 | wrapper = self.config.plugins.prepareTest(test) |
---|
51 | if wrapper is not None: |
---|
52 | test = wrapper |
---|
53 | |
---|
54 | # plugins can decorate or capture the output stream |
---|
55 | wrapped = self.config.plugins.setOutputStream(self.stream) |
---|
56 | if wrapped is not None: |
---|
57 | self.stream = wrapped |
---|
58 | |
---|
59 | result = self._makeResult() |
---|
60 | start = time.time() |
---|
61 | test(result) |
---|
62 | stop = time.time() |
---|
63 | result.printErrors() |
---|
64 | result.printSummary(start, stop) |
---|
65 | self.config.plugins.finalize(result) |
---|
66 | return result |
---|
67 | |
---|
68 | |
---|
69 | class TestProgram(unittest.TestProgram): |
---|
70 | """Collect and run tests, returning success or failure. |
---|
71 | |
---|
72 | The arguments to TestProgram() are the same as to |
---|
73 | :func:`main()` and :func:`run()`: |
---|
74 | |
---|
75 | * module: All tests are in this module (default: None) |
---|
76 | * defaultTest: Tests to load (default: '.') |
---|
77 | * argv: Command line arguments (default: None; sys.argv is read) |
---|
78 | * testRunner: Test runner instance (default: None) |
---|
79 | * testLoader: Test loader instance (default: None) |
---|
80 | * env: Environment; ignored if config is provided (default: None; |
---|
81 | os.environ is read) |
---|
82 | * config: :class:`nose.config.Config` instance (default: None) |
---|
83 | * suite: Suite or list of tests to run (default: None). Passing a |
---|
84 | suite or lists of tests will bypass all test discovery and |
---|
85 | loading. *ALSO NOTE* that if you pass a unittest.TestSuite |
---|
86 | instance as the suite, context fixtures at the class, module and |
---|
87 | package level will not be used, and many plugin hooks will not |
---|
88 | be called. If you want normal nose behavior, either pass a list |
---|
89 | of tests, or a fully-configured :class:`nose.suite.ContextSuite`. |
---|
90 | * exit: Exit after running tests and printing report (default: True) |
---|
91 | * plugins: List of plugins to use; ignored if config is provided |
---|
92 | (default: load plugins with DefaultPluginManager) |
---|
93 | * addplugins: List of **extra** plugins to use. Pass a list of plugin |
---|
94 | instances in this argument to make custom plugins available while |
---|
95 | still using the DefaultPluginManager. |
---|
96 | """ |
---|
97 | verbosity = 1 |
---|
98 | |
---|
99 | def __init__(self, module=None, defaultTest='.', argv=None, |
---|
100 | testRunner=None, testLoader=None, env=None, config=None, |
---|
101 | suite=None, exit=True, plugins=None, addplugins=None): |
---|
102 | if env is None: |
---|
103 | env = os.environ |
---|
104 | if config is None: |
---|
105 | config = self.makeConfig(env, plugins) |
---|
106 | if addplugins: |
---|
107 | config.plugins.addPlugins(addplugins) |
---|
108 | self.config = config |
---|
109 | self.suite = suite |
---|
110 | self.exit = exit |
---|
111 | unittest.TestProgram.__init__( |
---|
112 | self, module=module, defaultTest=defaultTest, |
---|
113 | argv=argv, testRunner=testRunner, testLoader=testLoader) |
---|
114 | |
---|
115 | def makeConfig(self, env, plugins=None): |
---|
116 | """Load a Config, pre-filled with user config files if any are |
---|
117 | found. |
---|
118 | """ |
---|
119 | cfg_files = all_config_files() |
---|
120 | if plugins: |
---|
121 | manager = PluginManager(plugins=plugins) |
---|
122 | else: |
---|
123 | manager = DefaultPluginManager() |
---|
124 | return Config( |
---|
125 | env=env, files=cfg_files, plugins=manager) |
---|
126 | |
---|
127 | def parseArgs(self, argv): |
---|
128 | """Parse argv and env and configure running environment. |
---|
129 | """ |
---|
130 | self.config.configure(argv, doc=self.usage()) |
---|
131 | log.debug("configured %s", self.config) |
---|
132 | |
---|
133 | # quick outs: version, plugins (optparse would have already |
---|
134 | # caught and exited on help) |
---|
135 | if self.config.options.version: |
---|
136 | from nose import __version__ |
---|
137 | sys.stdout = sys.__stdout__ |
---|
138 | print "%s version %s" % (os.path.basename(sys.argv[0]), __version__) |
---|
139 | sys.exit(0) |
---|
140 | |
---|
141 | if self.config.options.showPlugins: |
---|
142 | self.showPlugins() |
---|
143 | sys.exit(0) |
---|
144 | |
---|
145 | if self.testLoader is None: |
---|
146 | self.testLoader = defaultTestLoader(config=self.config) |
---|
147 | elif isclass(self.testLoader): |
---|
148 | self.testLoader = self.testLoader(config=self.config) |
---|
149 | plug_loader = self.config.plugins.prepareTestLoader(self.testLoader) |
---|
150 | if plug_loader is not None: |
---|
151 | self.testLoader = plug_loader |
---|
152 | log.debug("test loader is %s", self.testLoader) |
---|
153 | |
---|
154 | # FIXME if self.module is a string, add it to self.testNames? not sure |
---|
155 | |
---|
156 | if self.config.testNames: |
---|
157 | self.testNames = self.config.testNames |
---|
158 | else: |
---|
159 | self.testNames = tolist(self.defaultTest) |
---|
160 | log.debug('defaultTest %s', self.defaultTest) |
---|
161 | log.debug('Test names are %s', self.testNames) |
---|
162 | if self.config.workingDir is not None: |
---|
163 | os.chdir(self.config.workingDir) |
---|
164 | self.createTests() |
---|
165 | |
---|
166 | def createTests(self): |
---|
167 | """Create the tests to run. If a self.suite |
---|
168 | is set, then that suite will be used. Otherwise, tests will be |
---|
169 | loaded from the given test names (self.testNames) using the |
---|
170 | test loader. |
---|
171 | """ |
---|
172 | log.debug("createTests called with %s", self.suite) |
---|
173 | if self.suite is not None: |
---|
174 | # We were given an explicit suite to run. Make sure it's |
---|
175 | # loaded and wrapped correctly. |
---|
176 | self.test = self.testLoader.suiteClass(self.suite) |
---|
177 | else: |
---|
178 | self.test = self.testLoader.loadTestsFromNames(self.testNames) |
---|
179 | |
---|
180 | def runTests(self): |
---|
181 | """Run Tests. Returns true on success, false on failure, and sets |
---|
182 | self.success to the same value. |
---|
183 | """ |
---|
184 | log.debug("runTests called") |
---|
185 | if self.testRunner is None: |
---|
186 | self.testRunner = TextTestRunner(stream=self.config.stream, |
---|
187 | verbosity=self.config.verbosity, |
---|
188 | config=self.config) |
---|
189 | plug_runner = self.config.plugins.prepareTestRunner(self.testRunner) |
---|
190 | if plug_runner is not None: |
---|
191 | self.testRunner = plug_runner |
---|
192 | result = self.testRunner.run(self.test) |
---|
193 | self.success = result.wasSuccessful() |
---|
194 | if self.exit: |
---|
195 | sys.exit(not self.success) |
---|
196 | return self.success |
---|
197 | |
---|
198 | def showPlugins(self): |
---|
199 | """Print list of available plugins. |
---|
200 | """ |
---|
201 | import textwrap |
---|
202 | |
---|
203 | class DummyParser: |
---|
204 | def __init__(self): |
---|
205 | self.options = [] |
---|
206 | def add_option(self, *arg, **kw): |
---|
207 | self.options.append((arg, kw.pop('help', ''))) |
---|
208 | |
---|
209 | v = self.config.verbosity |
---|
210 | self.config.plugins.sort() |
---|
211 | for p in self.config.plugins: |
---|
212 | print "Plugin %s" % p.name |
---|
213 | if v >= 2: |
---|
214 | print " score: %s" % p.score |
---|
215 | print '\n'.join(textwrap.wrap(p.help().strip(), |
---|
216 | initial_indent=' ', |
---|
217 | subsequent_indent=' ')) |
---|
218 | if v >= 3: |
---|
219 | print |
---|
220 | print " Options:" |
---|
221 | parser = DummyParser() |
---|
222 | p.addOptions(parser) |
---|
223 | for opts, help in parser.options: |
---|
224 | print ' %s' % (', '.join(opts)) |
---|
225 | if help: |
---|
226 | print '\n'.join( |
---|
227 | textwrap.wrap(help.strip(), |
---|
228 | initial_indent=' ', |
---|
229 | subsequent_indent=' ')) |
---|
230 | print |
---|
231 | |
---|
232 | def usage(cls): |
---|
233 | return open(os.path.join( |
---|
234 | os.path.dirname(__file__), 'usage.txt'), 'r').read() |
---|
235 | usage = classmethod(usage) |
---|
236 | |
---|
237 | # backwards compatibility |
---|
238 | run_exit = main = TestProgram |
---|
239 | |
---|
240 | |
---|
241 | def run(*arg, **kw): |
---|
242 | """Collect and run tests, returning success or failure. |
---|
243 | |
---|
244 | The arguments to `run()` are the same as to `main()`: |
---|
245 | |
---|
246 | * module: All tests are in this module (default: None) |
---|
247 | * defaultTest: Tests to load (default: '.') |
---|
248 | * argv: Command line arguments (default: None; sys.argv is read) |
---|
249 | * testRunner: Test runner instance (default: None) |
---|
250 | * testLoader: Test loader instance (default: None) |
---|
251 | * env: Environment; ignored if config is provided (default: None; |
---|
252 | os.environ is read) |
---|
253 | * config: :class:`nose.config.Config` instance (default: None) |
---|
254 | * suite: Suite or list of tests to run (default: None). Passing a |
---|
255 | suite or lists of tests will bypass all test discovery and |
---|
256 | loading. *ALSO NOTE* that if you pass a unittest.TestSuite |
---|
257 | instance as the suite, context fixtures at the class, module and |
---|
258 | package level will not be used, and many plugin hooks will not |
---|
259 | be called. If you want normal nose behavior, either pass a list |
---|
260 | of tests, or a fully-configured :class:`nose.suite.ContextSuite`. |
---|
261 | * plugins: List of plugins to use; ignored if config is provided |
---|
262 | (default: load plugins with DefaultPluginManager) |
---|
263 | * addplugins: List of **extra** plugins to use. Pass a list of plugin |
---|
264 | instances in this argument to make custom plugins available while |
---|
265 | still using the DefaultPluginManager. |
---|
266 | |
---|
267 | With the exception that the ``exit`` argument is always set |
---|
268 | to False. |
---|
269 | """ |
---|
270 | kw['exit'] = False |
---|
271 | return TestProgram(*arg, **kw).success |
---|
272 | |
---|
273 | |
---|
274 | def runmodule(name='__main__', **kw): |
---|
275 | """Collect and run tests in a single module only. Defaults to running |
---|
276 | tests in __main__. Additional arguments to TestProgram may be passed |
---|
277 | as keyword arguments. |
---|
278 | """ |
---|
279 | main(defaultTest=name, **kw) |
---|
280 | |
---|
281 | |
---|
282 | def collector(): |
---|
283 | """TestSuite replacement entry point. Use anywhere you might use a |
---|
284 | unittest.TestSuite. The collector will, by default, load options from |
---|
285 | all config files and execute loader.loadTestsFromNames() on the |
---|
286 | configured testNames, or '.' if no testNames are configured. |
---|
287 | """ |
---|
288 | # plugins that implement any of these methods are disabled, since |
---|
289 | # we don't control the test runner and won't be able to run them |
---|
290 | # finalize() is also not called, but plugins that use it aren't disabled, |
---|
291 | # because capture needs it. |
---|
292 | setuptools_incompat = ('report', 'prepareTest', |
---|
293 | 'prepareTestLoader', 'prepareTestRunner', |
---|
294 | 'setOutputStream') |
---|
295 | |
---|
296 | plugins = RestrictedPluginManager(exclude=setuptools_incompat) |
---|
297 | conf = Config(files=all_config_files(), |
---|
298 | plugins=plugins) |
---|
299 | conf.configure(argv=['collector']) |
---|
300 | loader = defaultTestLoader(conf) |
---|
301 | |
---|
302 | if conf.testNames: |
---|
303 | suite = loader.loadTestsFromNames(conf.testNames) |
---|
304 | else: |
---|
305 | suite = loader.loadTestsFromNames(('.',)) |
---|
306 | return FinalizingSuiteWrapper(suite, plugins.finalize) |
---|
307 | |
---|
308 | |
---|
309 | |
---|
310 | if __name__ == '__main__': |
---|
311 | main() |
---|