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