root/galaxy-central/eggs/nose-0.11.1-py2.6.egg/nose/plugins/plugintest.py @ 3

リビジョン 3, 10.8 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1"""
2Testing Plugins
3===============
4
5The plugin interface is well-tested enough to safely unit test your
6use of its hooks with some level of confidence. However, there is also
7a mixin for unittest.TestCase called PluginTester that's designed to
8test plugins in their native runtime environment.
9
10Here's a simple example with a do-nothing plugin and a composed suite.
11
12    >>> import unittest
13    >>> from nose.plugins import Plugin, PluginTester
14    >>> class FooPlugin(Plugin):
15    ...     pass
16    >>> class TestPluginFoo(PluginTester, unittest.TestCase):
17    ...     activate = '--with-foo'
18    ...     plugins = [FooPlugin()]
19    ...     def test_foo(self):
20    ...         for line in self.output:
21    ...             # i.e. check for patterns
22    ...             pass
23    ...
24    ...         # or check for a line containing ...
25    ...         assert "ValueError" in self.output
26    ...     def makeSuite(self):
27    ...         class TC(unittest.TestCase):
28    ...             def runTest(self):
29    ...                 raise ValueError("I hate foo")
30    ...         return unittest.TestSuite([TC()])
31    ...
32    >>> res = unittest.TestResult()
33    >>> case = TestPluginFoo('test_foo')
34    >>> case(res)
35    >>> res.errors
36    []
37    >>> res.failures
38    []
39    >>> res.wasSuccessful()
40    True
41    >>> res.testsRun
42    1
43
44And here is a more complex example of testing a plugin that has extra
45arguments and reads environment variables.
46   
47    >>> import unittest, os
48    >>> from nose.plugins import Plugin, PluginTester
49    >>> class FancyOutputter(Plugin):
50    ...     name = "fancy"
51    ...     def configure(self, options, conf):
52    ...         Plugin.configure(self, options, conf)
53    ...         if not self.enabled:
54    ...             return
55    ...         self.fanciness = 1
56    ...         if options.more_fancy:
57    ...             self.fanciness = 2
58    ...         if 'EVEN_FANCIER' in self.env:
59    ...             self.fanciness = 3
60    ...
61    ...     def options(self, parser, env=os.environ):
62    ...         self.env = env
63    ...         parser.add_option('--more-fancy', action='store_true')
64    ...         Plugin.options(self, parser, env=env)
65    ...
66    ...     def report(self, stream):
67    ...         stream.write("FANCY " * self.fanciness)
68    ...
69    >>> class TestFancyOutputter(PluginTester, unittest.TestCase):
70    ...     activate = '--with-fancy' # enables the plugin
71    ...     plugins = [FancyOutputter()]
72    ...     args = ['--more-fancy']
73    ...     env = {'EVEN_FANCIER': '1'}
74    ...
75    ...     def test_fancy_output(self):
76    ...         assert "FANCY FANCY FANCY" in self.output, (
77    ...                                         "got: %s" % self.output)
78    ...     def makeSuite(self):
79    ...         class TC(unittest.TestCase):
80    ...             def runTest(self):
81    ...                 raise ValueError("I hate fancy stuff")
82    ...         return unittest.TestSuite([TC()])
83    ...
84    >>> res = unittest.TestResult()
85    >>> case = TestFancyOutputter('test_fancy_output')
86    >>> case(res)
87    >>> res.errors
88    []
89    >>> res.failures
90    []
91    >>> res.wasSuccessful()
92    True
93    >>> res.testsRun
94    1
95
96"""
97
98import re
99import sys
100from warnings import warn
101
102try:
103    from cStringIO import StringIO
104except ImportError:
105    from StringIO import StringIO
106   
107__all__ = ['PluginTester', 'run']
108
109
110class PluginTester(object):
111    """A mixin for testing nose plugins in their runtime environment.
112   
113    Subclass this and mix in unittest.TestCase to run integration/functional
114    tests on your plugin.  When setUp() is called, the stub test suite is
115    executed with your plugin so that during an actual test you can inspect the
116    artifacts of how your plugin interacted with the stub test suite.
117   
118    - activate
119   
120      - the argument to send nosetests to activate the plugin
121     
122    - suitepath
123   
124      - if set, this is the path of the suite to test. Otherwise, you
125        will need to use the hook, makeSuite()
126     
127    - plugins
128
129      - the list of plugins to make available during the run. Note
130        that this does not mean these plugins will be *enabled* during
131        the run -- only the plugins enabled by the activate argument
132        or other settings in argv or env will be enabled.
133   
134    - args
135 
136      - a list of arguments to add to the nosetests command, in addition to
137        the activate argument
138   
139    - env
140   
141      - optional dict of environment variables to send nosetests
142
143    """
144    activate = None
145    suitepath = None
146    args = None
147    env = {}
148    argv = None
149    plugins = []
150    ignoreFiles = None
151   
152    def makeSuite(self):
153        """returns a suite object of tests to run (unittest.TestSuite())
154       
155        If self.suitepath is None, this must be implemented. The returned suite
156        object will be executed with all plugins activated.  It may return
157        None.
158       
159        Here is an example of a basic suite object you can return ::
160       
161            >>> import unittest
162            >>> class SomeTest(unittest.TestCase):
163            ...     def runTest(self):
164            ...         raise ValueError("Now do something, plugin!")
165            ...
166            >>> unittest.TestSuite([SomeTest()]) # doctest: +ELLIPSIS
167            <unittest.TestSuite tests=[<...SomeTest testMethod=runTest>]>
168       
169        """
170        raise NotImplementedError
171   
172    def _execPlugin(self):
173        """execute the plugin on the internal test suite.
174        """
175        from nose.config import Config
176        from nose.core import TestProgram
177        from nose.plugins.manager import PluginManager
178       
179        suite = None
180        stream = StringIO()
181        conf = Config(env=self.env,
182                      stream=stream,
183                      plugins=PluginManager(plugins=self.plugins))
184        if self.ignoreFiles is not None:
185            conf.ignoreFiles = self.ignoreFiles
186        if not self.suitepath:
187            suite = self.makeSuite()
188           
189        self.nose = TestProgram(argv=self.argv, config=conf, suite=suite,
190                                exit=False)
191        self.output = AccessDecorator(stream)
192                               
193    def setUp(self):
194        """runs nosetests with the specified test suite, all plugins
195        activated.
196        """
197        self.argv = ['nosetests', self.activate]
198        if self.args:
199            self.argv.extend(self.args)
200        if self.suitepath:
201            self.argv.append(self.suitepath)           
202
203        self._execPlugin()
204
205
206class AccessDecorator(object):
207    stream = None
208    _buf = None
209    def __init__(self, stream):
210        self.stream = stream
211        stream.seek(0)
212        self._buf = stream.read()
213        stream.seek(0)
214    def __contains__(self, val):
215        return val in self._buf
216    def __iter__(self):
217        return self.stream
218    def __str__(self):
219        return self._buf
220
221
222def blankline_separated_blocks(text):
223    block = []
224    for line in text.splitlines(True):
225        block.append(line)
226        if not line.strip():
227            yield "".join(block)
228            block = []
229    if block:
230        yield "".join(block)
231
232
233def remove_stack_traces(out):
234    # this regexp taken from Python 2.5's doctest
235    traceback_re = re.compile(r"""
236        # Grab the traceback header.  Different versions of Python have
237        # said different things on the first traceback line.
238        ^(?P<hdr> Traceback\ \(
239            (?: most\ recent\ call\ last
240            |   innermost\ last
241            ) \) :
242        )
243        \s* $                # toss trailing whitespace on the header.
244        (?P<stack> .*?)      # don't blink: absorb stuff until...
245        ^ (?P<msg> \w+ .*)   #     a line *starts* with alphanum.
246        """, re.VERBOSE | re.MULTILINE | re.DOTALL)
247    blocks = []
248    for block in blankline_separated_blocks(out):
249        blocks.append(traceback_re.sub(r"\g<hdr>\n...\n\g<msg>", block))
250    return "".join(blocks)
251
252
253def simplify_warnings(out):
254    warn_re = re.compile(r"""
255        # Cut the file and line no, up to the warning name
256        ^.*:\d+:\s
257        (?P<category>\w+): \s+        # warning category
258        (?P<detail>.+) $ \n?          # warning message
259        ^ .* $                        # stack frame
260        """, re.VERBOSE | re.MULTILINE)
261    return warn_re.sub(r"\g<category>: \g<detail>", out)
262
263
264def remove_timings(out):
265    return re.sub(
266        r"Ran (\d+ tests?) in [0-9.]+s", r"Ran \1 in ...s", out)
267
268
269def munge_nose_output_for_doctest(out):
270    """Modify nose output to make it easy to use in doctests."""
271    out = remove_stack_traces(out)
272    out = simplify_warnings(out)
273    out = remove_timings(out)
274    return out.strip()
275
276
277def run(*arg, **kw):
278    """
279    Specialized version of nose.run for use inside of doctests that
280    test test runs.
281
282    This version of run() prints the result output to stdout.  Before
283    printing, the output is processed by replacing the timing
284    information with an ellipsis (...), removing traceback stacks, and
285    removing trailing whitespace.
286
287    Use this version of run wherever you are writing a doctest that
288    tests nose (or unittest) test result output.
289
290    Note: do not use doctest: +ELLIPSIS when testing nose output,
291    since ellipses ("test_foo ... ok") in your expected test runner
292    output may match multiple lines of output, causing spurious test
293    passes!
294    """
295    from nose import run
296    from nose.config import Config
297    from nose.plugins.manager import PluginManager
298
299    buffer = StringIO()
300    if 'config' not in kw:
301        plugins = kw.pop('plugins', [])
302        if isinstance(plugins, list):
303            plugins = PluginManager(plugins=plugins)
304        env = kw.pop('env', {})
305        kw['config'] = Config(env=env, plugins=plugins)
306    if 'argv' not in kw:
307        kw['argv'] = ['nosetests', '-v']
308    kw['config'].stream = buffer
309   
310    # Set up buffering so that all output goes to our buffer,
311    # or warn user if deprecated behavior is active. If this is not
312    # done, prints and warnings will either be out of place or
313    # disappear.
314    stderr = sys.stderr
315    stdout = sys.stdout
316    if kw.pop('buffer_all', False):
317        sys.stdout = sys.stderr = buffer
318        restore = True
319    else:
320        restore = False
321        warn("The behavior of nose.plugins.plugintest.run() will change in "
322             "the next release of nose. The current behavior does not "
323             "correctly account for output to stdout and stderr. To enable "
324             "correct behavior, use run_buffered() instead, or pass "
325             "the keyword argument buffer_all=True to run().",
326             DeprecationWarning, stacklevel=2)
327    try:
328        run(*arg, **kw)
329    finally:
330        if restore:
331            sys.stderr = stderr
332            sys.stdout = stdout
333    out = buffer.getvalue()
334    print munge_nose_output_for_doctest(out)
335
336   
337def run_buffered(*arg, **kw):
338    kw['buffer_all'] = True
339    run(*arg, **kw)
340
341if __name__ == '__main__':
342    import doctest
343    doctest.testmod()
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。