1823b8294SYaroslav Brustinov"""Implements nose test program and collector.
2823b8294SYaroslav Brustinov"""
3823b8294SYaroslav Brustinovfrom __future__ import generators
4823b8294SYaroslav Brustinov
5823b8294SYaroslav Brustinovimport logging
6823b8294SYaroslav Brustinovimport os
7823b8294SYaroslav Brustinovimport sys
8823b8294SYaroslav Brustinovimport time
9823b8294SYaroslav Brustinovimport unittest
10823b8294SYaroslav Brustinov
11823b8294SYaroslav Brustinovfrom nose.config import Config, all_config_files
12823b8294SYaroslav Brustinovfrom nose.loader import defaultTestLoader
13823b8294SYaroslav Brustinovfrom nose.plugins.manager import PluginManager, DefaultPluginManager, \
14823b8294SYaroslav Brustinov     RestrictedPluginManager
15823b8294SYaroslav Brustinovfrom nose.result import TextTestResult
16823b8294SYaroslav Brustinovfrom nose.suite import FinalizingSuiteWrapper
17823b8294SYaroslav Brustinovfrom nose.util import isclass, tolist
18823b8294SYaroslav Brustinov
19823b8294SYaroslav Brustinov
20823b8294SYaroslav Brustinovlog = logging.getLogger('nose.core')
21823b8294SYaroslav Brustinovcompat_24 = sys.version_info >= (2, 4)
22823b8294SYaroslav Brustinov
23823b8294SYaroslav Brustinov__all__ = ['TestProgram', 'main', 'run', 'run_exit', 'runmodule', 'collector',
24823b8294SYaroslav Brustinov           'TextTestRunner']
25823b8294SYaroslav Brustinov
26823b8294SYaroslav Brustinov
27823b8294SYaroslav Brustinovclass TextTestRunner(unittest.TextTestRunner):
28823b8294SYaroslav Brustinov    """Test runner that uses nose's TextTestResult to enable errorClasses,
29823b8294SYaroslav Brustinov    as well as providing hooks for plugins to override or replace the test
30823b8294SYaroslav Brustinov    output stream, results, and the test case itself.
31823b8294SYaroslav Brustinov    """
32823b8294SYaroslav Brustinov    def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1,
33823b8294SYaroslav Brustinov                 config=None):
34823b8294SYaroslav Brustinov        if config is None:
35823b8294SYaroslav Brustinov            config = Config()
36823b8294SYaroslav Brustinov        self.config = config
37823b8294SYaroslav Brustinov        unittest.TextTestRunner.__init__(self, stream, descriptions, verbosity)
38823b8294SYaroslav Brustinov
39823b8294SYaroslav Brustinov
40823b8294SYaroslav Brustinov    def _makeResult(self):
41823b8294SYaroslav Brustinov        return TextTestResult(self.stream,
42823b8294SYaroslav Brustinov                              self.descriptions,
43823b8294SYaroslav Brustinov                              self.verbosity,
44823b8294SYaroslav Brustinov                              self.config)
45823b8294SYaroslav Brustinov
46823b8294SYaroslav Brustinov    def run(self, test):
47823b8294SYaroslav Brustinov        """Overrides to provide plugin hooks and defer all output to
48823b8294SYaroslav Brustinov        the test result class.
49823b8294SYaroslav Brustinov        """
50823b8294SYaroslav Brustinov        wrapper = self.config.plugins.prepareTest(test)
51823b8294SYaroslav Brustinov        if wrapper is not None:
52823b8294SYaroslav Brustinov            test = wrapper
53823b8294SYaroslav Brustinov
54823b8294SYaroslav Brustinov        # plugins can decorate or capture the output stream
55823b8294SYaroslav Brustinov        wrapped = self.config.plugins.setOutputStream(self.stream)
56823b8294SYaroslav Brustinov        if wrapped is not None:
57823b8294SYaroslav Brustinov            self.stream = wrapped
58823b8294SYaroslav Brustinov
59823b8294SYaroslav Brustinov        result = self._makeResult()
60823b8294SYaroslav Brustinov        start = time.time()
61823b8294SYaroslav Brustinov        try:
62823b8294SYaroslav Brustinov            test(result)
63823b8294SYaroslav Brustinov        except KeyboardInterrupt:
64823b8294SYaroslav Brustinov            pass
65823b8294SYaroslav Brustinov        stop = time.time()
66823b8294SYaroslav Brustinov        result.printErrors()
67823b8294SYaroslav Brustinov        result.printSummary(start, stop)
68823b8294SYaroslav Brustinov        self.config.plugins.finalize(result)
69823b8294SYaroslav Brustinov        return result
70823b8294SYaroslav Brustinov
71823b8294SYaroslav Brustinov
72823b8294SYaroslav Brustinovclass TestProgram(unittest.TestProgram):
73823b8294SYaroslav Brustinov    """Collect and run tests, returning success or failure.
74823b8294SYaroslav Brustinov
75823b8294SYaroslav Brustinov    The arguments to TestProgram() are the same as to
76823b8294SYaroslav Brustinov    :func:`main()` and :func:`run()`:
77823b8294SYaroslav Brustinov
78823b8294SYaroslav Brustinov    * module: All tests are in this module (default: None)
79823b8294SYaroslav Brustinov    * defaultTest: Tests to load (default: '.')
80823b8294SYaroslav Brustinov    * argv: Command line arguments (default: None; sys.argv is read)
81823b8294SYaroslav Brustinov    * testRunner: Test runner instance (default: None)
82823b8294SYaroslav Brustinov    * testLoader: Test loader instance (default: None)
83823b8294SYaroslav Brustinov    * env: Environment; ignored if config is provided (default: None;
84823b8294SYaroslav Brustinov      os.environ is read)
85823b8294SYaroslav Brustinov    * config: :class:`nose.config.Config` instance (default: None)
86823b8294SYaroslav Brustinov    * suite: Suite or list of tests to run (default: None). Passing a
87823b8294SYaroslav Brustinov      suite or lists of tests will bypass all test discovery and
88823b8294SYaroslav Brustinov      loading. *ALSO NOTE* that if you pass a unittest.TestSuite
89823b8294SYaroslav Brustinov      instance as the suite, context fixtures at the class, module and
90823b8294SYaroslav Brustinov      package level will not be used, and many plugin hooks will not
91823b8294SYaroslav Brustinov      be called. If you want normal nose behavior, either pass a list
92823b8294SYaroslav Brustinov      of tests, or a fully-configured :class:`nose.suite.ContextSuite`.
93823b8294SYaroslav Brustinov    * exit: Exit after running tests and printing report (default: True)
94823b8294SYaroslav Brustinov    * plugins: List of plugins to use; ignored if config is provided
95823b8294SYaroslav Brustinov      (default: load plugins with DefaultPluginManager)
96823b8294SYaroslav Brustinov    * addplugins: List of **extra** plugins to use. Pass a list of plugin
97823b8294SYaroslav Brustinov      instances in this argument to make custom plugins available while
98823b8294SYaroslav Brustinov      still using the DefaultPluginManager.
99823b8294SYaroslav Brustinov    """
100823b8294SYaroslav Brustinov    verbosity = 1
101823b8294SYaroslav Brustinov
102823b8294SYaroslav Brustinov    def __init__(self, module=None, defaultTest='.', argv=None,
103823b8294SYaroslav Brustinov                 testRunner=None, testLoader=None, env=None, config=None,
104823b8294SYaroslav Brustinov                 suite=None, exit=True, plugins=None, addplugins=None):
105823b8294SYaroslav Brustinov        if env is None:
106823b8294SYaroslav Brustinov            env = os.environ
107823b8294SYaroslav Brustinov        if config is None:
108823b8294SYaroslav Brustinov            config = self.makeConfig(env, plugins)
109823b8294SYaroslav Brustinov        if addplugins:
110823b8294SYaroslav Brustinov            config.plugins.addPlugins(extraplugins=addplugins)
111823b8294SYaroslav Brustinov        self.config = config
112823b8294SYaroslav Brustinov        self.suite = suite
113823b8294SYaroslav Brustinov        self.exit = exit
114823b8294SYaroslav Brustinov        extra_args = {}
115823b8294SYaroslav Brustinov        version = sys.version_info[0:2]
116823b8294SYaroslav Brustinov        if version >= (2,7) and version != (3,0):
117823b8294SYaroslav Brustinov            extra_args['exit'] = exit
118823b8294SYaroslav Brustinov        unittest.TestProgram.__init__(
119823b8294SYaroslav Brustinov            self, module=module, defaultTest=defaultTest,
120823b8294SYaroslav Brustinov            argv=argv, testRunner=testRunner, testLoader=testLoader,
121823b8294SYaroslav Brustinov            **extra_args)
122823b8294SYaroslav Brustinov
123823b8294SYaroslav Brustinov    def getAllConfigFiles(self, env=None):
124823b8294SYaroslav Brustinov        env = env or {}
125823b8294SYaroslav Brustinov        if env.get('NOSE_IGNORE_CONFIG_FILES', False):
126823b8294SYaroslav Brustinov            return []
127823b8294SYaroslav Brustinov        else:
128823b8294SYaroslav Brustinov            return all_config_files()
129823b8294SYaroslav Brustinov
130823b8294SYaroslav Brustinov    def makeConfig(self, env, plugins=None):
131823b8294SYaroslav Brustinov        """Load a Config, pre-filled with user config files if any are
132823b8294SYaroslav Brustinov        found.
133823b8294SYaroslav Brustinov        """
134823b8294SYaroslav Brustinov        cfg_files = self.getAllConfigFiles(env)
135823b8294SYaroslav Brustinov        if plugins:
136823b8294SYaroslav Brustinov            manager = PluginManager(plugins=plugins)
137823b8294SYaroslav Brustinov        else:
138823b8294SYaroslav Brustinov            manager = DefaultPluginManager()
139823b8294SYaroslav Brustinov        return Config(
140823b8294SYaroslav Brustinov            env=env, files=cfg_files, plugins=manager)
141823b8294SYaroslav Brustinov
142823b8294SYaroslav Brustinov    def parseArgs(self, argv):
143823b8294SYaroslav Brustinov        """Parse argv and env and configure running environment.
144823b8294SYaroslav Brustinov        """
145823b8294SYaroslav Brustinov        self.config.configure(argv, doc=self.usage())
146823b8294SYaroslav Brustinov        log.debug("configured %s", self.config)
147823b8294SYaroslav Brustinov
148823b8294SYaroslav Brustinov        # quick outs: version, plugins (optparse would have already
149823b8294SYaroslav Brustinov        # caught and exited on help)
150823b8294SYaroslav Brustinov        if self.config.options.version:
151823b8294SYaroslav Brustinov            from nose import __version__
152823b8294SYaroslav Brustinov            sys.stdout = sys.__stdout__
153823b8294SYaroslav Brustinov            print "%s version %s" % (os.path.basename(sys.argv[0]), __version__)
154823b8294SYaroslav Brustinov            sys.exit(0)
155823b8294SYaroslav Brustinov
156823b8294SYaroslav Brustinov        if self.config.options.showPlugins:
157823b8294SYaroslav Brustinov            self.showPlugins()
158823b8294SYaroslav Brustinov            sys.exit(0)
159823b8294SYaroslav Brustinov
160823b8294SYaroslav Brustinov        if self.testLoader is None:
161823b8294SYaroslav Brustinov            self.testLoader = defaultTestLoader(config=self.config)
162823b8294SYaroslav Brustinov        elif isclass(self.testLoader):
163823b8294SYaroslav Brustinov            self.testLoader = self.testLoader(config=self.config)
164823b8294SYaroslav Brustinov        plug_loader = self.config.plugins.prepareTestLoader(self.testLoader)
165823b8294SYaroslav Brustinov        if plug_loader is not None:
166823b8294SYaroslav Brustinov            self.testLoader = plug_loader
167823b8294SYaroslav Brustinov        log.debug("test loader is %s", self.testLoader)
168823b8294SYaroslav Brustinov
169823b8294SYaroslav Brustinov        # FIXME if self.module is a string, add it to self.testNames? not sure
170823b8294SYaroslav Brustinov
171823b8294SYaroslav Brustinov        if self.config.testNames:
172823b8294SYaroslav Brustinov            self.testNames = self.config.testNames
173823b8294SYaroslav Brustinov        else:
174823b8294SYaroslav Brustinov            self.testNames = tolist(self.defaultTest)
175823b8294SYaroslav Brustinov        log.debug('defaultTest %s', self.defaultTest)
176823b8294SYaroslav Brustinov        log.debug('Test names are %s', self.testNames)
177823b8294SYaroslav Brustinov        if self.config.workingDir is not None:
178823b8294SYaroslav Brustinov            os.chdir(self.config.workingDir)
179823b8294SYaroslav Brustinov        self.createTests()
180823b8294SYaroslav Brustinov
181823b8294SYaroslav Brustinov    def createTests(self):
182823b8294SYaroslav Brustinov        """Create the tests to run. If a self.suite
183823b8294SYaroslav Brustinov        is set, then that suite will be used. Otherwise, tests will be
184823b8294SYaroslav Brustinov        loaded from the given test names (self.testNames) using the
185823b8294SYaroslav Brustinov        test loader.
186823b8294SYaroslav Brustinov        """
187823b8294SYaroslav Brustinov        log.debug("createTests called with %s", self.suite)
188823b8294SYaroslav Brustinov        if self.suite is not None:
189823b8294SYaroslav Brustinov            # We were given an explicit suite to run. Make sure it's
190823b8294SYaroslav Brustinov            # loaded and wrapped correctly.
191823b8294SYaroslav Brustinov            self.test = self.testLoader.suiteClass(self.suite)
192823b8294SYaroslav Brustinov        else:
193823b8294SYaroslav Brustinov            self.test = self.testLoader.loadTestsFromNames(self.testNames)
194823b8294SYaroslav Brustinov
195823b8294SYaroslav Brustinov    def runTests(self):
196823b8294SYaroslav Brustinov        """Run Tests. Returns true on success, false on failure, and sets
197823b8294SYaroslav Brustinov        self.success to the same value.
198823b8294SYaroslav Brustinov        """
199823b8294SYaroslav Brustinov        log.debug("runTests called")
200823b8294SYaroslav Brustinov        if self.testRunner is None:
201823b8294SYaroslav Brustinov            self.testRunner = TextTestRunner(stream=self.config.stream,
202823b8294SYaroslav Brustinov                                             verbosity=self.config.verbosity,
203823b8294SYaroslav Brustinov                                             config=self.config)
204823b8294SYaroslav Brustinov        plug_runner = self.config.plugins.prepareTestRunner(self.testRunner)
205823b8294SYaroslav Brustinov        if plug_runner is not None:
206823b8294SYaroslav Brustinov            self.testRunner = plug_runner
207823b8294SYaroslav Brustinov        result = self.testRunner.run(self.test)
208823b8294SYaroslav Brustinov        self.success = result.wasSuccessful()
209823b8294SYaroslav Brustinov        if self.exit:
210823b8294SYaroslav Brustinov            sys.exit(not self.success)
211823b8294SYaroslav Brustinov        return self.success
212823b8294SYaroslav Brustinov
213823b8294SYaroslav Brustinov    def showPlugins(self):
214823b8294SYaroslav Brustinov        """Print list of available plugins.
215823b8294SYaroslav Brustinov        """
216823b8294SYaroslav Brustinov        import textwrap
217823b8294SYaroslav Brustinov
218823b8294SYaroslav Brustinov        class DummyParser:
219823b8294SYaroslav Brustinov            def __init__(self):
220823b8294SYaroslav Brustinov                self.options = []
221823b8294SYaroslav Brustinov            def add_option(self, *arg, **kw):
222823b8294SYaroslav Brustinov                self.options.append((arg, kw.pop('help', '')))
223823b8294SYaroslav Brustinov
224823b8294SYaroslav Brustinov        v = self.config.verbosity
225823b8294SYaroslav Brustinov        self.config.plugins.sort()
226823b8294SYaroslav Brustinov        for p in self.config.plugins:
227823b8294SYaroslav Brustinov            print "Plugin %s" % p.name
228823b8294SYaroslav Brustinov            if v >= 2:
229823b8294SYaroslav Brustinov                print "  score: %s" % p.score
230823b8294SYaroslav Brustinov                print '\n'.join(textwrap.wrap(p.help().strip(),
231823b8294SYaroslav Brustinov                                              initial_indent='  ',
232823b8294SYaroslav Brustinov                                              subsequent_indent='  '))
233823b8294SYaroslav Brustinov                if v >= 3:
234823b8294SYaroslav Brustinov                    parser = DummyParser()
235823b8294SYaroslav Brustinov                    p.addOptions(parser)
236823b8294SYaroslav Brustinov                    if len(parser.options):
237823b8294SYaroslav Brustinov                        print
238823b8294SYaroslav Brustinov                        print "  Options:"
239823b8294SYaroslav Brustinov                        for opts, help in parser.options:
240823b8294SYaroslav Brustinov                            print '  %s' % (', '.join(opts))
241823b8294SYaroslav Brustinov                            if help:
242823b8294SYaroslav Brustinov                                print '\n'.join(
243823b8294SYaroslav Brustinov                                    textwrap.wrap(help.strip(),
244823b8294SYaroslav Brustinov                                                  initial_indent='    ',
245823b8294SYaroslav Brustinov                                                  subsequent_indent='    '))
246823b8294SYaroslav Brustinov                print
247823b8294SYaroslav Brustinov
248823b8294SYaroslav Brustinov    def usage(cls):
249823b8294SYaroslav Brustinov        import nose
250823b8294SYaroslav Brustinov        try:
251823b8294SYaroslav Brustinov            ld = nose.__loader__
252823b8294SYaroslav Brustinov            text = ld.get_data(os.path.join(
253823b8294SYaroslav Brustinov                os.path.dirname(__file__), 'usage.txt'))
254823b8294SYaroslav Brustinov        except AttributeError:
255823b8294SYaroslav Brustinov            f = open(os.path.join(
256823b8294SYaroslav Brustinov                os.path.dirname(__file__), 'usage.txt'), 'r')
257823b8294SYaroslav Brustinov            try:
258823b8294SYaroslav Brustinov                text = f.read()
259823b8294SYaroslav Brustinov            finally:
260823b8294SYaroslav Brustinov                f.close()
261823b8294SYaroslav Brustinov        # Ensure that we return str, not bytes.
262823b8294SYaroslav Brustinov        if not isinstance(text, str):
263823b8294SYaroslav Brustinov            text = text.decode('utf-8')
264823b8294SYaroslav Brustinov        return text
265823b8294SYaroslav Brustinov    usage = classmethod(usage)
266823b8294SYaroslav Brustinov
267823b8294SYaroslav Brustinov# backwards compatibility
268823b8294SYaroslav Brustinovrun_exit = main = TestProgram
269823b8294SYaroslav Brustinov
270823b8294SYaroslav Brustinov
271823b8294SYaroslav Brustinovdef run(*arg, **kw):
272823b8294SYaroslav Brustinov    """Collect and run tests, returning success or failure.
273823b8294SYaroslav Brustinov
274823b8294SYaroslav Brustinov    The arguments to `run()` are the same as to `main()`:
275823b8294SYaroslav Brustinov
276823b8294SYaroslav Brustinov    * module: All tests are in this module (default: None)
277823b8294SYaroslav Brustinov    * defaultTest: Tests to load (default: '.')
278823b8294SYaroslav Brustinov    * argv: Command line arguments (default: None; sys.argv is read)
279823b8294SYaroslav Brustinov    * testRunner: Test runner instance (default: None)
280823b8294SYaroslav Brustinov    * testLoader: Test loader instance (default: None)
281823b8294SYaroslav Brustinov    * env: Environment; ignored if config is provided (default: None;
282823b8294SYaroslav Brustinov      os.environ is read)
283823b8294SYaroslav Brustinov    * config: :class:`nose.config.Config` instance (default: None)
284823b8294SYaroslav Brustinov    * suite: Suite or list of tests to run (default: None). Passing a
285823b8294SYaroslav Brustinov      suite or lists of tests will bypass all test discovery and
286823b8294SYaroslav Brustinov      loading. *ALSO NOTE* that if you pass a unittest.TestSuite
287823b8294SYaroslav Brustinov      instance as the suite, context fixtures at the class, module and
288823b8294SYaroslav Brustinov      package level will not be used, and many plugin hooks will not
289823b8294SYaroslav Brustinov      be called. If you want normal nose behavior, either pass a list
290823b8294SYaroslav Brustinov      of tests, or a fully-configured :class:`nose.suite.ContextSuite`.
291823b8294SYaroslav Brustinov    * plugins: List of plugins to use; ignored if config is provided
292823b8294SYaroslav Brustinov      (default: load plugins with DefaultPluginManager)
293823b8294SYaroslav Brustinov    * addplugins: List of **extra** plugins to use. Pass a list of plugin
294823b8294SYaroslav Brustinov      instances in this argument to make custom plugins available while
295823b8294SYaroslav Brustinov      still using the DefaultPluginManager.
296823b8294SYaroslav Brustinov
297823b8294SYaroslav Brustinov    With the exception that the ``exit`` argument is always set
298823b8294SYaroslav Brustinov    to False.
299823b8294SYaroslav Brustinov    """
300823b8294SYaroslav Brustinov    kw['exit'] = False
301823b8294SYaroslav Brustinov    return TestProgram(*arg, **kw).success
302823b8294SYaroslav Brustinov
303823b8294SYaroslav Brustinov
304823b8294SYaroslav Brustinovdef runmodule(name='__main__', **kw):
305823b8294SYaroslav Brustinov    """Collect and run tests in a single module only. Defaults to running
306823b8294SYaroslav Brustinov    tests in __main__. Additional arguments to TestProgram may be passed
307823b8294SYaroslav Brustinov    as keyword arguments.
308823b8294SYaroslav Brustinov    """
309823b8294SYaroslav Brustinov    main(defaultTest=name, **kw)
310823b8294SYaroslav Brustinov
311823b8294SYaroslav Brustinov
312823b8294SYaroslav Brustinovdef collector():
313823b8294SYaroslav Brustinov    """TestSuite replacement entry point. Use anywhere you might use a
314823b8294SYaroslav Brustinov    unittest.TestSuite. The collector will, by default, load options from
315823b8294SYaroslav Brustinov    all config files and execute loader.loadTestsFromNames() on the
316823b8294SYaroslav Brustinov    configured testNames, or '.' if no testNames are configured.
317823b8294SYaroslav Brustinov    """
318823b8294SYaroslav Brustinov    # plugins that implement any of these methods are disabled, since
319823b8294SYaroslav Brustinov    # we don't control the test runner and won't be able to run them
320823b8294SYaroslav Brustinov    # finalize() is also not called, but plugins that use it aren't disabled,
321823b8294SYaroslav Brustinov    # because capture needs it.
322823b8294SYaroslav Brustinov    setuptools_incompat = ('report', 'prepareTest',
323823b8294SYaroslav Brustinov                           'prepareTestLoader', 'prepareTestRunner',
324823b8294SYaroslav Brustinov                           'setOutputStream')
325823b8294SYaroslav Brustinov
326823b8294SYaroslav Brustinov    plugins = RestrictedPluginManager(exclude=setuptools_incompat)
327823b8294SYaroslav Brustinov    conf = Config(files=all_config_files(),
328823b8294SYaroslav Brustinov                  plugins=plugins)
329823b8294SYaroslav Brustinov    conf.configure(argv=['collector'])
330823b8294SYaroslav Brustinov    loader = defaultTestLoader(conf)
331823b8294SYaroslav Brustinov
332823b8294SYaroslav Brustinov    if conf.testNames:
333823b8294SYaroslav Brustinov        suite = loader.loadTestsFromNames(conf.testNames)
334823b8294SYaroslav Brustinov    else:
335823b8294SYaroslav Brustinov        suite = loader.loadTestsFromNames(('.',))
336823b8294SYaroslav Brustinov    return FinalizingSuiteWrapper(suite, plugins.finalize)
337823b8294SYaroslav Brustinov
338823b8294SYaroslav Brustinov
339823b8294SYaroslav Brustinov
340823b8294SYaroslav Brustinovif __name__ == '__main__':
341823b8294SYaroslav Brustinov    main()
342