5
Utilities for testing plugins.
11
from warnings import warn
14
from cStringIO import StringIO
16
from StringIO import StringIO
18
__all__ = ['PluginTester', 'run']
21
class PluginTester(object):
22
"""A mixin for testing nose plugins in their runtime environment.
24
Subclass this and mix in unittest.TestCase to run integration/functional
25
tests on your plugin. When setUp() is called, the stub test suite is
26
executed with your plugin so that during an actual test you can inspect the
27
artifacts of how your plugin interacted with the stub test suite.
33
- the argument to send nosetests to activate the plugin
37
- if set, this is the path of the suite to test. otherwise, you
38
will need to use the hook, makeSuite()
42
- the list of plugins to make available during the run. Note
43
that this does not mean these plugins will be *enabled* during
44
the run -- only the plugins enabled by the activate argument
45
or other settings in argv or env will be enabled.
49
- a list of arguments to add to the nosetests command, in addition to
54
- optional dict of environment variables to send nosetests
66
"""returns a suite object of tests to run (unittest.TestSuite())
68
If self.suitepath is None, this must be implemented. The returned suite
69
object will be executed with all plugins activated. It may return
72
Here is an example of a basic suite object you can return ::
75
>>> class SomeTest(unittest.TestCase):
76
... def runTest(self):
77
... raise ValueError("Now do something, plugin!")
79
>>> unittest.TestSuite([SomeTest()]) # doctest: +ELLIPSIS
80
<unittest.TestSuite tests=[<...SomeTest testMethod=runTest>]>
83
raise NotImplementedError
85
def _execPlugin(self):
86
"""execute the plugin on the internal test suite.
88
from nose.config import Config
89
from nose.core import TestProgram
90
from nose.plugins.manager import PluginManager
94
conf = Config(env=self.env,
96
plugins=PluginManager(plugins=self.plugins))
97
if self.ignoreFiles is not None:
98
conf.ignoreFiles = self.ignoreFiles
99
if not self.suitepath:
100
suite = self.makeSuite()
102
self.nose = TestProgram(argv=self.argv, config=conf, suite=suite,
104
self.output = AccessDecorator(stream)
107
"""runs nosetests with the specified test suite, all plugins
110
self.argv = ['nosetests', self.activate]
112
self.argv.extend(self.args)
114
self.argv.append(self.suitepath)
119
class AccessDecorator(object):
122
def __init__(self, stream):
125
self._buf = stream.read()
127
def __contains__(self, val):
128
return val in self._buf
135
def blankline_separated_blocks(text):
137
for line in text.splitlines(True):
146
def remove_stack_traces(out):
147
# this regexp taken from Python 2.5's doctest
148
traceback_re = re.compile(r"""
149
# Grab the traceback header. Different versions of Python have
150
# said different things on the first traceback line.
151
^(?P<hdr> Traceback\ \(
152
(?: most\ recent\ call\ last
156
\s* $ # toss trailing whitespace on the header.
157
(?P<stack> .*?) # don't blink: absorb stuff until...
158
^ (?P<msg> \w+ .*) # a line *starts* with alphanum.
159
""", re.VERBOSE | re.MULTILINE | re.DOTALL)
161
for block in blankline_separated_blocks(out):
162
blocks.append(traceback_re.sub(r"\g<hdr>\n...\n\g<msg>", block))
163
return "".join(blocks)
166
def simplify_warnings(out):
167
warn_re = re.compile(r"""
168
# Cut the file and line no, up to the warning name
170
(?P<category>\w+): \s+ # warning category
171
(?P<detail>.+) $ \n? # warning message
173
""", re.VERBOSE | re.MULTILINE)
174
return warn_re.sub(r"\g<category>: \g<detail>", out)
177
def remove_timings(out):
179
r"Ran (\d+ tests?) in [0-9.]+s", r"Ran \1 in ...s", out)
182
def munge_nose_output_for_doctest(out):
183
"""Modify nose output to make it easy to use in doctests."""
184
out = remove_stack_traces(out)
185
out = simplify_warnings(out)
186
out = remove_timings(out)
192
Specialized version of nose.run for use inside of doctests that
195
This version of run() prints the result output to stdout. Before
196
printing, the output is processed by replacing the timing
197
information with an ellipsis (...), removing traceback stacks, and
198
removing trailing whitespace.
200
Use this version of run wherever you are writing a doctest that
201
tests nose (or unittest) test result output.
203
Note: do not use doctest: +ELLIPSIS when testing nose output,
204
since ellipses ("test_foo ... ok") in your expected test runner
205
output may match multiple lines of output, causing spurious test
209
from nose.config import Config
210
from nose.plugins.manager import PluginManager
213
if 'config' not in kw:
214
plugins = kw.pop('plugins', [])
215
if isinstance(plugins, list):
216
plugins = PluginManager(plugins=plugins)
217
env = kw.pop('env', {})
218
kw['config'] = Config(env=env, plugins=plugins)
220
kw['argv'] = ['nosetests', '-v']
221
kw['config'].stream = buffer
223
# Set up buffering so that all output goes to our buffer,
224
# or warn user if deprecated behavior is active. If this is not
225
# done, prints and warnings will either be out of place or
229
if kw.pop('buffer_all', False):
230
sys.stdout = sys.stderr = buffer
234
warn("The behavior of nose.plugins.plugintest.run() will change in "
235
"the next release of nose. The current behavior does not "
236
"correctly account for output to stdout and stderr. To enable "
237
"correct behavior, use run_buffered() instead, or pass "
238
"the keyword argument buffer_all=True to run().",
239
DeprecationWarning, stacklevel=2)
246
out = buffer.getvalue()
247
print munge_nose_output_for_doctest(out)
250
def run_buffered(*arg, **kw):
251
kw['buffer_all'] = True
254
if __name__ == '__main__':