update docs, leave out internal plugins
--HG-- branch : 1.0.x
This commit is contained in:
		
							parent
							
								
									3e226f9392
								
							
						
					
					
						commit
						7fabb3df69
					
				|  | @ -633,6 +633,10 @@ div.heading, h1 { | |||
|     border-bottom: 1px solid #8CACBB; | ||||
| } | ||||
| 
 | ||||
| h2 { | ||||
|     border-bottom: 1px dotted #8CACBB; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| h1, h2, h3, h4, h5, h6 { | ||||
|     color: Black; | ||||
|  | @ -648,11 +652,10 @@ h1, h2, h3, h4, h5, h6 { | |||
| 
 | ||||
| 
 | ||||
| h1 { font-size: 145%; } | ||||
| h2 { font-size: 135%; } | ||||
| h3 { font-size: 125%; } | ||||
| h4 { font-size: 120%; } | ||||
| h5 { font-size: 110%; } | ||||
| h6 { font-size: 80%; } | ||||
| h2 { font-size: 115%; } | ||||
| h3 { font-size: 105%; } | ||||
| h4 { font-size: 100%; } | ||||
| h5 { font-size: 100%; } | ||||
| 
 | ||||
| h1 a { text-decoration: None;} | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,6 +4,9 @@ pytest_doctest plugin | |||
| 
 | ||||
| collect and execute doctests from modules and test files. | ||||
| 
 | ||||
| .. contents:: | ||||
|   :local: | ||||
| 
 | ||||
| Usage | ||||
| ------------- | ||||
| 
 | ||||
|  | @ -22,218 +25,19 @@ command line options | |||
| ``--doctest-modules`` | ||||
|     search all python files for doctests | ||||
| 
 | ||||
| Getting and improving this plugin | ||||
| --------------------------------- | ||||
| Start improving this plugin in 30 seconds | ||||
| ========================================= | ||||
| 
 | ||||
| 
 | ||||
| Do you find the above documentation or the plugin itself lacking, | ||||
| not fit for what you need?  Here is a **30 seconds guide** | ||||
| to get you started on improving the plugin: | ||||
| Do you find the above documentation or the plugin itself lacking?  | ||||
| 
 | ||||
| 1. Download `pytest_doctest.py`_ plugin source code  | ||||
| 2. put it somewhere as ``pytest_doctest.py`` into your import path  | ||||
| 3. a subsequent test run will now use your local version!  | ||||
| 3. a subsequent ``py.test`` run will use your local version | ||||
| 
 | ||||
| Further information: extend_ documentation, other plugins_ or contact_.   | ||||
| 
 | ||||
| For your convenience here is also an inlined version of ``pytest_doctest.py``: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     """ | ||||
|     collect and execute doctests from modules and test files.  | ||||
|      | ||||
|     Usage | ||||
|     ------------- | ||||
|      | ||||
|     By default all files matching the ``test_*.txt`` pattern will  | ||||
|     be run with the ``doctest`` module.  If you issue:: | ||||
|      | ||||
|         py.test --doctest-modules | ||||
|      | ||||
|     all python files in your projects will be doctest-run  | ||||
|     as well.  | ||||
|     """ | ||||
|      | ||||
|     import py | ||||
|     from py.__.code.excinfo import Repr, ReprFileLocation | ||||
|      | ||||
|     def pytest_addoption(parser): | ||||
|         group = parser.addgroup("doctest options") | ||||
|         group.addoption("--doctest-modules",  | ||||
|             action="store_true", default=False, | ||||
|             help="search all python files for doctests",  | ||||
|             dest="doctestmodules") | ||||
|          | ||||
|     def pytest_collect_file(path, parent): | ||||
|         if path.ext == ".py": | ||||
|             if parent.config.getvalue("doctestmodules"): | ||||
|                 return DoctestModule(path, parent) | ||||
|         if path.check(fnmatch="test_*.txt"): | ||||
|             return DoctestTextfile(path, parent) | ||||
|      | ||||
|     class ReprFailDoctest(Repr): | ||||
|         def __init__(self, reprlocation, lines): | ||||
|             self.reprlocation = reprlocation | ||||
|             self.lines = lines | ||||
|         def toterminal(self, tw): | ||||
|             for line in self.lines: | ||||
|                 tw.line(line) | ||||
|             self.reprlocation.toterminal(tw) | ||||
|                   | ||||
|     class DoctestItem(py.test.collect.Item): | ||||
|         def __init__(self, path, parent): | ||||
|             name = self.__class__.__name__ + ":" + path.basename | ||||
|             super(DoctestItem, self).__init__(name=name, parent=parent) | ||||
|             self.fspath = path  | ||||
|      | ||||
|         def repr_failure(self, excinfo, outerr): | ||||
|             if excinfo.errisinstance(py.compat.doctest.DocTestFailure): | ||||
|                 doctestfailure = excinfo.value | ||||
|                 example = doctestfailure.example | ||||
|                 test = doctestfailure.test | ||||
|                 filename = test.filename  | ||||
|                 lineno = test.lineno + example.lineno + 1 | ||||
|                 message = excinfo.type.__name__ | ||||
|                 reprlocation = ReprFileLocation(filename, lineno, message) | ||||
|                 checker = py.compat.doctest.OutputChecker()  | ||||
|                 REPORT_UDIFF = py.compat.doctest.REPORT_UDIFF | ||||
|                 filelines = py.path.local(filename).readlines(cr=0) | ||||
|                 i = max(test.lineno, max(0, lineno - 10)) # XXX?  | ||||
|                 lines = [] | ||||
|                 for line in filelines[i:lineno]: | ||||
|                     lines.append("%03d %s" % (i+1, line)) | ||||
|                     i += 1 | ||||
|                 lines += checker.output_difference(example,  | ||||
|                         doctestfailure.got, REPORT_UDIFF).split("\n") | ||||
|                 return ReprFailDoctest(reprlocation, lines) | ||||
|             elif excinfo.errisinstance(py.compat.doctest.UnexpectedException): | ||||
|                 excinfo = py.code.ExceptionInfo(excinfo.value.exc_info) | ||||
|                 return super(DoctestItem, self).repr_failure(excinfo, outerr) | ||||
|             else:  | ||||
|                 return super(DoctestItem, self).repr_failure(excinfo, outerr) | ||||
|      | ||||
|     class DoctestTextfile(DoctestItem): | ||||
|         def runtest(self): | ||||
|             if not self._deprecated_testexecution(): | ||||
|                 failed, tot = py.compat.doctest.testfile( | ||||
|                     str(self.fspath), module_relative=False,  | ||||
|                     raise_on_error=True, verbose=0) | ||||
|      | ||||
|     class DoctestModule(DoctestItem): | ||||
|         def runtest(self): | ||||
|             module = self.fspath.pyimport() | ||||
|             failed, tot = py.compat.doctest.testmod( | ||||
|                 module, raise_on_error=True, verbose=0) | ||||
|      | ||||
|      | ||||
|     # | ||||
|     # Plugin tests | ||||
|     # | ||||
|      | ||||
|     class TestDoctests: | ||||
|      | ||||
|         def test_collect_testtextfile(self, testdir): | ||||
|             testdir.maketxtfile(whatever="") | ||||
|             checkfile = testdir.maketxtfile(test_something=""" | ||||
|                 alskdjalsdk | ||||
|                 >>> i = 5 | ||||
|                 >>> i-1 | ||||
|                 4 | ||||
|             """) | ||||
|             for x in (testdir.tmpdir, checkfile):  | ||||
|                 #print "checking that %s returns custom items" % (x,)  | ||||
|                 items, reprec = testdir.inline_genitems(x) | ||||
|                 assert len(items) == 1 | ||||
|                 assert isinstance(items[0], DoctestTextfile) | ||||
|      | ||||
|         def test_collect_module(self, testdir): | ||||
|             path = testdir.makepyfile(whatever="#") | ||||
|             for p in (path, testdir.tmpdir):  | ||||
|                 items, reprec = testdir.inline_genitems(p, '--doctest-modules') | ||||
|                 assert len(items) == 1 | ||||
|                 assert isinstance(items[0], DoctestModule) | ||||
|      | ||||
|         def test_simple_doctestfile(self, testdir): | ||||
|             p = testdir.maketxtfile(test_doc=""" | ||||
|                 >>> x = 1 | ||||
|                 >>> x == 1 | ||||
|                 False | ||||
|             """) | ||||
|             reprec = testdir.inline_run(p) | ||||
|             reprec.assertoutcome(failed=1) | ||||
|      | ||||
|         def test_doctest_unexpected_exception(self, testdir): | ||||
|             from py.__.test.outcome import Failed  | ||||
|      | ||||
|             p = testdir.maketxtfile(""" | ||||
|                 >>> i = 0 | ||||
|                 >>> i = 1  | ||||
|                 >>> x | ||||
|                 2 | ||||
|             """) | ||||
|             reprec = testdir.inline_run(p) | ||||
|             call = reprec.getcall("pytest_runtest_logreport") | ||||
|             assert call.rep.failed | ||||
|             assert call.rep.longrepr  | ||||
|             # XXX  | ||||
|             #testitem, = items | ||||
|             #excinfo = py.test.raises(Failed, "testitem.runtest()") | ||||
|             #repr = testitem.repr_failure(excinfo, ("", "")) | ||||
|             #assert repr.reprlocation  | ||||
|      | ||||
|         def test_doctestmodule(self, testdir): | ||||
|             p = testdir.makepyfile(""" | ||||
|                 ''' | ||||
|                     >>> x = 1 | ||||
|                     >>> x == 1 | ||||
|                     False | ||||
|      | ||||
|                 ''' | ||||
|             """) | ||||
|             reprec = testdir.inline_run(p, "--doctest-modules") | ||||
|             reprec.assertoutcome(failed=1)  | ||||
|      | ||||
|         def test_doctestmodule_external(self, testdir): | ||||
|             p = testdir.makepyfile(""" | ||||
|                 # | ||||
|                 def somefunc(): | ||||
|                     ''' | ||||
|                         >>> i = 0 | ||||
|                         >>> i + 1 | ||||
|                         2 | ||||
|                     ''' | ||||
|             """) | ||||
|             result = testdir.runpytest(p, "--doctest-modules") | ||||
|             result.stdout.fnmatch_lines([ | ||||
|                 '004 *>>> i = 0', | ||||
|                 '005 *>>> i + 1', | ||||
|                 '*Expected:', | ||||
|                 "*    2", | ||||
|                 "*Got:", | ||||
|                 "*    1", | ||||
|                 "*:5: DocTestFailure" | ||||
|             ]) | ||||
|              | ||||
|      | ||||
|         def test_txtfile_failing(self, testdir): | ||||
|             p = testdir.maketxtfile(""" | ||||
|                 >>> i = 0 | ||||
|                 >>> i + 1 | ||||
|                 2 | ||||
|             """) | ||||
|             result = testdir.runpytest(p) | ||||
|             result.stdout.fnmatch_lines([ | ||||
|                 '001 >>> i = 0', | ||||
|                 '002 >>> i + 1', | ||||
|                 'Expected:', | ||||
|                 "    2", | ||||
|                 "Got:", | ||||
|                 "    1", | ||||
|                 "*test_txtfile_failing.txt:2: DocTestFailure" | ||||
|             ]) | ||||
| 
 | ||||
| .. _`pytest_doctest.py`: http://bitbucket.org/hpk42/py-trunk/raw/ea1f958813ebbff45161fdb468a6204be5396112/py/test/plugin/pytest_doctest.py | ||||
| .. _`pytest_doctest.py`: http://bitbucket.org/hpk42/py-trunk/raw/85fe614ab05f301f206935d11a477df184cbbce6/py/test/plugin/pytest_doctest.py | ||||
| .. _`extend`: ../extend.html | ||||
| .. _`plugins`: index.html | ||||
| .. _`contact`: ../../contact.html | ||||
|  |  | |||
|  | @ -1,86 +0,0 @@ | |||
| 
 | ||||
| pytest_execnetcleanup plugin | ||||
| ============================ | ||||
| 
 | ||||
| cleanup execnet gateways during test function runs. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| Getting and improving this plugin | ||||
| --------------------------------- | ||||
| 
 | ||||
| 
 | ||||
| Do you find the above documentation or the plugin itself lacking, | ||||
| not fit for what you need?  Here is a **30 seconds guide** | ||||
| to get you started on improving the plugin: | ||||
| 
 | ||||
| 1. Download `pytest_execnetcleanup.py`_ plugin source code  | ||||
| 2. put it somewhere as ``pytest_execnetcleanup.py`` into your import path  | ||||
| 3. a subsequent test run will now use your local version!  | ||||
| 
 | ||||
| Further information: extend_ documentation, other plugins_ or contact_.   | ||||
| 
 | ||||
| For your convenience here is also an inlined version of ``pytest_execnetcleanup.py``: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     """ | ||||
|     cleanup execnet gateways during test function runs. | ||||
|     """ | ||||
|     import py | ||||
|      | ||||
|     pytest_plugins = "xfail" | ||||
|      | ||||
|     def pytest_configure(config): | ||||
|         config.pluginmanager.register(Execnetcleanup()) | ||||
|      | ||||
|     class Execnetcleanup: | ||||
|         _gateways = None | ||||
|         def __init__(self, debug=False): | ||||
|             self._debug = debug  | ||||
|      | ||||
|         def pyexecnet_gateway_init(self, gateway): | ||||
|             if self._gateways is not None: | ||||
|                 self._gateways.append(gateway) | ||||
|              | ||||
|         def pyexecnet_gateway_exit(self, gateway): | ||||
|             if self._gateways is not None: | ||||
|                 self._gateways.remove(gateway) | ||||
|      | ||||
|         def pytest_sessionstart(self, session): | ||||
|             self._gateways = [] | ||||
|      | ||||
|         def pytest_sessionfinish(self, session, exitstatus): | ||||
|             l = [] | ||||
|             for gw in self._gateways: | ||||
|                 gw.exit() | ||||
|                 l.append(gw) | ||||
|             #for gw in l: | ||||
|             #    gw.join() | ||||
|              | ||||
|         def pytest_pyfunc_call(self, __call__, pyfuncitem): | ||||
|             if self._gateways is not None: | ||||
|                 gateways = self._gateways[:] | ||||
|                 res = __call__.execute(firstresult=True) | ||||
|                 while len(self._gateways) > len(gateways): | ||||
|                     self._gateways[-1].exit() | ||||
|                 return res | ||||
|        | ||||
|     def test_execnetplugin(testdir): | ||||
|         reprec = testdir.inline_runsource(""" | ||||
|             import py | ||||
|             import sys | ||||
|             def test_hello(): | ||||
|                 sys._gw = py.execnet.PopenGateway() | ||||
|             def test_world(): | ||||
|                 assert hasattr(sys, '_gw') | ||||
|                 py.test.raises(KeyError, "sys._gw.exit()") # already closed  | ||||
|                  | ||||
|         """, "-s", "--debug") | ||||
|         reprec.assertoutcome(passed=2) | ||||
| 
 | ||||
| .. _`pytest_execnetcleanup.py`: http://bitbucket.org/hpk42/py-trunk/raw/ea1f958813ebbff45161fdb468a6204be5396112/py/test/plugin/pytest_execnetcleanup.py | ||||
| .. _`extend`: ../extend.html | ||||
| .. _`plugins`: index.html | ||||
| .. _`contact`: ../../contact.html | ||||
| .. _`checkout the py.test development version`: ../../download.html#checkout | ||||
|  | @ -4,6 +4,9 @@ pytest_figleaf plugin | |||
| 
 | ||||
| write and report coverage data with 'figleaf'. | ||||
| 
 | ||||
| .. contents:: | ||||
|   :local: | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| command line options | ||||
|  | @ -17,93 +20,19 @@ command line options | |||
| ``--figleaf-html=FIGLEAFHTML`` | ||||
|     path to the coverage html dir. | ||||
| 
 | ||||
| Getting and improving this plugin | ||||
| --------------------------------- | ||||
| Start improving this plugin in 30 seconds | ||||
| ========================================= | ||||
| 
 | ||||
| 
 | ||||
| Do you find the above documentation or the plugin itself lacking, | ||||
| not fit for what you need?  Here is a **30 seconds guide** | ||||
| to get you started on improving the plugin: | ||||
| Do you find the above documentation or the plugin itself lacking?  | ||||
| 
 | ||||
| 1. Download `pytest_figleaf.py`_ plugin source code  | ||||
| 2. put it somewhere as ``pytest_figleaf.py`` into your import path  | ||||
| 3. a subsequent test run will now use your local version!  | ||||
| 3. a subsequent ``py.test`` run will use your local version | ||||
| 
 | ||||
| Further information: extend_ documentation, other plugins_ or contact_.   | ||||
| 
 | ||||
| For your convenience here is also an inlined version of ``pytest_figleaf.py``: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     """ | ||||
|     write and report coverage data with 'figleaf'.  | ||||
|      | ||||
|     """ | ||||
|     import py | ||||
|      | ||||
|     figleaf = py.test.importorskip("figleaf.annotate_html") | ||||
|      | ||||
|     def pytest_addoption(parser): | ||||
|         group = parser.addgroup('figleaf options') | ||||
|         group.addoption('-F', action='store_true', default=False, | ||||
|                 dest = 'figleaf', | ||||
|                 help=('trace python coverage with figleaf and write HTML ' | ||||
|                      'for files below the current working dir')) | ||||
|         group.addoption('--figleaf-data', action='store', default='.figleaf', | ||||
|                 dest='figleafdata', | ||||
|                 help='path to coverage tracing file.') | ||||
|         group.addoption('--figleaf-html', action='store', default='html', | ||||
|                 dest='figleafhtml',  | ||||
|                 help='path to the coverage html dir.') | ||||
|      | ||||
|     def pytest_configure(config): | ||||
|         figleaf.start() | ||||
|      | ||||
|     def pytest_terminal_summary(terminalreporter): | ||||
|         config = terminalreporter.config | ||||
|         datafile = py.path.local(config.getvalue('figleafdata')) | ||||
|         tw = terminalreporter._tw | ||||
|         tw.sep('-', 'figleaf') | ||||
|         tw.line('Writing figleaf data to %s' % (datafile)) | ||||
|         figleaf.stop() | ||||
|         figleaf.write_coverage(str(datafile)) | ||||
|         coverage = get_coverage(datafile, config) | ||||
|         reportdir = py.path.local(config.getvalue('figleafhtml')) | ||||
|         tw.line('Writing figleaf html to file://%s' % (reportdir)) | ||||
|         figleaf.annotate_html.prepare_reportdir(str(reportdir)) | ||||
|         exclude = [] | ||||
|         figleaf.annotate_html.report_as_html(coverage,  | ||||
|                 str(reportdir), exclude, {}) | ||||
|      | ||||
|     def get_coverage(datafile, config): | ||||
|         # basepath = config.topdir | ||||
|         basepath = py.path.local() | ||||
|         data = figleaf.read_coverage(str(datafile)) | ||||
|         d = {} | ||||
|         coverage = figleaf.combine_coverage(d, data) | ||||
|         for path in coverage.keys(): | ||||
|             if not py.path.local(path).relto(basepath): | ||||
|                 del coverage[path] | ||||
|         return coverage | ||||
|      | ||||
|      | ||||
|     def test_functional(testdir): | ||||
|         py.test.importorskip("figleaf") | ||||
|         testdir.plugins.append("figleaf") | ||||
|         testdir.makepyfile(""" | ||||
|             def f():     | ||||
|                 x = 42 | ||||
|             def test_whatever(): | ||||
|                 pass | ||||
|             """) | ||||
|         result = testdir.runpytest('-F') | ||||
|         assert result.ret == 0 | ||||
|         assert result.stdout.fnmatch_lines([ | ||||
|             '*figleaf html*' | ||||
|             ]) | ||||
|         #print result.stdout.str() | ||||
| 
 | ||||
| .. _`pytest_figleaf.py`: http://bitbucket.org/hpk42/py-trunk/raw/ea1f958813ebbff45161fdb468a6204be5396112/py/test/plugin/pytest_figleaf.py | ||||
| .. _`pytest_figleaf.py`: http://bitbucket.org/hpk42/py-trunk/raw/85fe614ab05f301f206935d11a477df184cbbce6/py/test/plugin/pytest_figleaf.py | ||||
| .. _`extend`: ../extend.html | ||||
| .. _`plugins`: index.html | ||||
| .. _`contact`: ../../contact.html | ||||
|  |  | |||
|  | @ -1,72 +0,0 @@ | |||
| 
 | ||||
| pytest_hooklog plugin | ||||
| ===================== | ||||
| 
 | ||||
| log invocations of extension hooks to a file. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| command line options | ||||
| -------------------- | ||||
| 
 | ||||
| 
 | ||||
| ``--hooklog=HOOKLOG`` | ||||
|     write hook calls to the given file. | ||||
| 
 | ||||
| Getting and improving this plugin | ||||
| --------------------------------- | ||||
| 
 | ||||
| 
 | ||||
| Do you find the above documentation or the plugin itself lacking, | ||||
| not fit for what you need?  Here is a **30 seconds guide** | ||||
| to get you started on improving the plugin: | ||||
| 
 | ||||
| 1. Download `pytest_hooklog.py`_ plugin source code  | ||||
| 2. put it somewhere as ``pytest_hooklog.py`` into your import path  | ||||
| 3. a subsequent test run will now use your local version!  | ||||
| 
 | ||||
| Further information: extend_ documentation, other plugins_ or contact_.   | ||||
| 
 | ||||
| For your convenience here is also an inlined version of ``pytest_hooklog.py``: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     """ log invocations of extension hooks to a file. """  | ||||
|     import py | ||||
|      | ||||
|     def pytest_addoption(parser): | ||||
|         parser.addoption("--hooklog", dest="hooklog", default=None,  | ||||
|             help="write hook calls to the given file.") | ||||
|      | ||||
|     def pytest_configure(config): | ||||
|         hooklog = config.getvalue("hooklog") | ||||
|         if hooklog: | ||||
|             assert not config.pluginmanager.comregistry.logfile | ||||
|             config.pluginmanager.comregistry.logfile = open(hooklog, 'w') | ||||
|      | ||||
|     def pytest_unconfigure(config): | ||||
|         f = config.pluginmanager.comregistry.logfile | ||||
|         if f: | ||||
|             f.close() | ||||
|             config.pluginmanager.comregistry.logfile = None | ||||
|      | ||||
|     # =============================================================================== | ||||
|     # plugin tests  | ||||
|     # =============================================================================== | ||||
|      | ||||
|     def test_functional(testdir): | ||||
|         testdir.makepyfile(""" | ||||
|             def test_pass(): | ||||
|                 pass | ||||
|         """) | ||||
|         testdir.runpytest("--hooklog=hook.log") | ||||
|         s = testdir.tmpdir.join("hook.log").read() | ||||
|         assert s.find("pytest_sessionstart") != -1 | ||||
|         assert s.find("ItemTestReport") != -1 | ||||
|         assert s.find("sessionfinish") != -1 | ||||
| 
 | ||||
| .. _`pytest_hooklog.py`: http://bitbucket.org/hpk42/py-trunk/raw/ea1f958813ebbff45161fdb468a6204be5396112/py/test/plugin/pytest_hooklog.py | ||||
| .. _`extend`: ../extend.html | ||||
| .. _`plugins`: index.html | ||||
| .. _`contact`: ../../contact.html | ||||
| .. _`checkout the py.test development version`: ../../download.html#checkout | ||||
|  | @ -8,7 +8,7 @@ figleaf_ write and report coverage data with 'figleaf'. | |||
| 
 | ||||
| monkeypatch_ safely patch object attributes, dicts and environment variables. | ||||
| 
 | ||||
| iocapture_ convenient capturing of writes to stdout/stderror streams | ||||
| iocapture_ convenient capturing of writes to stdout/stderror streams and file descriptors. | ||||
| 
 | ||||
| recwarn_ helpers for asserting deprecation and other warnings. | ||||
| 
 | ||||
|  | @ -32,23 +32,7 @@ pocoo_ submit failure information to paste.pocoo.org | |||
| 
 | ||||
| resultlog_ resultlog plugin for machine-readable logging of test results. | ||||
| 
 | ||||
| terminal_ terminal reporting of the full testing process. | ||||
| 
 | ||||
| 
 | ||||
| internal plugins / core functionality | ||||
| ===================================== | ||||
| 
 | ||||
| pdb_ interactive debugging with the Python Debugger. | ||||
| 
 | ||||
| keyword_ py.test.mark / keyword plugin | ||||
| 
 | ||||
| hooklog_ log invocations of extension hooks to a file. | ||||
| 
 | ||||
| runner_ collect and run test items and create reports. | ||||
| 
 | ||||
| execnetcleanup_ cleanup execnet gateways during test function runs. | ||||
| 
 | ||||
| pytester_ funcargs and support code for testing py.test's own functionality. | ||||
| terminal_ Implements terminal reporting of the full testing process. | ||||
| 
 | ||||
| 
 | ||||
| .. _`xfail`: xfail.html | ||||
|  | @ -63,9 +47,3 @@ pytester_ funcargs and support code for testing py.test's own functionality. | |||
| .. _`pocoo`: pocoo.html | ||||
| .. _`resultlog`: resultlog.html | ||||
| .. _`terminal`: terminal.html | ||||
| .. _`pdb`: pdb.html | ||||
| .. _`keyword`: keyword.html | ||||
| .. _`hooklog`: hooklog.html | ||||
| .. _`runner`: runner.html | ||||
| .. _`execnetcleanup`: execnetcleanup.html | ||||
| .. _`pytester`: pytester.html | ||||
|  |  | |||
|  | @ -2,9 +2,10 @@ | |||
| pytest_iocapture plugin | ||||
| ======================= | ||||
| 
 | ||||
| convenient capturing of writes to stdout/stderror streams | ||||
| convenient capturing of writes to stdout/stderror streams and file descriptors. | ||||
| 
 | ||||
| and file descriptors.  | ||||
| .. contents:: | ||||
|   :local: | ||||
| 
 | ||||
| Example Usage | ||||
| ---------------------- | ||||
|  | @ -29,6 +30,7 @@ The ``reset()`` call returns a tuple and will restart | |||
| capturing so that you can successively check for output.  | ||||
| After the test function finishes the original streams | ||||
| will be restored. | ||||
| 
 | ||||
| .. _`capsys funcarg`: | ||||
| 
 | ||||
| 
 | ||||
|  | @ -38,6 +40,7 @@ the 'capsys' test function argument | |||
| captures writes to sys.stdout/sys.stderr and makes  | ||||
| them available successively via a ``capsys.reset()`` method  | ||||
| which returns a ``(out, err)`` tuple of captured strings.  | ||||
| 
 | ||||
| .. _`capfd funcarg`: | ||||
| 
 | ||||
| 
 | ||||
|  | @ -48,123 +51,19 @@ captures writes to file descriptors 1 and 2 and makes | |||
| them available successively via a ``capsys.reset()`` method  | ||||
| which returns a ``(out, err)`` tuple of captured strings.  | ||||
| 
 | ||||
| Getting and improving this plugin | ||||
| --------------------------------- | ||||
| Start improving this plugin in 30 seconds | ||||
| ========================================= | ||||
| 
 | ||||
| 
 | ||||
| Do you find the above documentation or the plugin itself lacking, | ||||
| not fit for what you need?  Here is a **30 seconds guide** | ||||
| to get you started on improving the plugin: | ||||
| Do you find the above documentation or the plugin itself lacking?  | ||||
| 
 | ||||
| 1. Download `pytest_iocapture.py`_ plugin source code  | ||||
| 2. put it somewhere as ``pytest_iocapture.py`` into your import path  | ||||
| 3. a subsequent test run will now use your local version!  | ||||
| 3. a subsequent ``py.test`` run will use your local version | ||||
| 
 | ||||
| Further information: extend_ documentation, other plugins_ or contact_.   | ||||
| 
 | ||||
| For your convenience here is also an inlined version of ``pytest_iocapture.py``: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     """ | ||||
|     convenient capturing of writes to stdout/stderror streams  | ||||
|     and file descriptors.  | ||||
|      | ||||
|     Example Usage | ||||
|     ---------------------- | ||||
|      | ||||
|     You can use the `capsys funcarg`_ to capture writes  | ||||
|     to stdout and stderr streams by using it in a test  | ||||
|     likes this: | ||||
|      | ||||
|     .. sourcecode:: python | ||||
|      | ||||
|         def test_myoutput(capsys): | ||||
|             print "hello"  | ||||
|             print >>sys.stderr, "world" | ||||
|             out, err = capsys.reset() | ||||
|             assert out == "hello\\n" | ||||
|             assert err == "world\\n" | ||||
|             print "next" | ||||
|             out, err = capsys.reset() | ||||
|             assert out == "next\\n"  | ||||
|      | ||||
|     The ``reset()`` call returns a tuple and will restart  | ||||
|     capturing so that you can successively check for output.  | ||||
|     After the test function finishes the original streams | ||||
|     will be restored.  | ||||
|     """ | ||||
|      | ||||
|     import py | ||||
|      | ||||
|     def pytest_funcarg__capsys(request): | ||||
|         """captures writes to sys.stdout/sys.stderr and makes  | ||||
|         them available successively via a ``capsys.reset()`` method  | ||||
|         which returns a ``(out, err)`` tuple of captured strings.  | ||||
|         """  | ||||
|         capture = Capture(py.io.StdCapture) | ||||
|         request.addfinalizer(capture.finalize) | ||||
|         return capture  | ||||
|      | ||||
|     def pytest_funcarg__capfd(request): | ||||
|         """captures writes to file descriptors 1 and 2 and makes  | ||||
|         them available successively via a ``capsys.reset()`` method  | ||||
|         which returns a ``(out, err)`` tuple of captured strings.  | ||||
|         """  | ||||
|         capture = Capture(py.io.StdCaptureFD) | ||||
|         request.addfinalizer(capture.finalize) | ||||
|         return capture  | ||||
|      | ||||
|     def pytest_pyfunc_call(pyfuncitem): | ||||
|         if hasattr(pyfuncitem, 'funcargs'): | ||||
|             for funcarg, value in pyfuncitem.funcargs.items(): | ||||
|                 if funcarg == "capsys" or funcarg == "capfd": | ||||
|                     value.reset() | ||||
|      | ||||
|     class Capture: | ||||
|         _capture = None | ||||
|         def __init__(self, captureclass): | ||||
|             self._captureclass = captureclass | ||||
|      | ||||
|         def finalize(self): | ||||
|             if self._capture: | ||||
|                 self._capture.reset() | ||||
|      | ||||
|         def reset(self): | ||||
|             res = None | ||||
|             if self._capture: | ||||
|                 res = self._capture.reset() | ||||
|             self._capture = self._captureclass() | ||||
|             return res  | ||||
|      | ||||
|     class TestCapture: | ||||
|         def test_std_functional(self, testdir):         | ||||
|             reprec = testdir.inline_runsource(""" | ||||
|                 def test_hello(capsys): | ||||
|                     print 42 | ||||
|                     out, err = capsys.reset() | ||||
|                     assert out.startswith("42") | ||||
|             """) | ||||
|             reprec.assertoutcome(passed=1) | ||||
|              | ||||
|         def test_stdfd_functional(self, testdir):         | ||||
|             reprec = testdir.inline_runsource(""" | ||||
|                 def test_hello(capfd): | ||||
|                     import os | ||||
|                     os.write(1, "42") | ||||
|                     out, err = capfd.reset() | ||||
|                     assert out.startswith("42") | ||||
|             """) | ||||
|             reprec.assertoutcome(passed=1) | ||||
|      | ||||
|         def test_funcall_yielded_no_funcargs(self, testdir):         | ||||
|             reprec = testdir.inline_runsource(""" | ||||
|                 def test_hello(): | ||||
|                     yield lambda: None | ||||
|             """) | ||||
|             reprec.assertoutcome(passed=1) | ||||
| 
 | ||||
| .. _`pytest_iocapture.py`: http://bitbucket.org/hpk42/py-trunk/raw/ea1f958813ebbff45161fdb468a6204be5396112/py/test/plugin/pytest_iocapture.py | ||||
| .. _`pytest_iocapture.py`: http://bitbucket.org/hpk42/py-trunk/raw/85fe614ab05f301f206935d11a477df184cbbce6/py/test/plugin/pytest_iocapture.py | ||||
| .. _`extend`: ../extend.html | ||||
| .. _`plugins`: index.html | ||||
| .. _`contact`: ../../contact.html | ||||
|  |  | |||
|  | @ -1,110 +0,0 @@ | |||
| 
 | ||||
| pytest_keyword plugin | ||||
| ===================== | ||||
| 
 | ||||
| py.test.mark / keyword plugin | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| Getting and improving this plugin | ||||
| --------------------------------- | ||||
| 
 | ||||
| 
 | ||||
| Do you find the above documentation or the plugin itself lacking, | ||||
| not fit for what you need?  Here is a **30 seconds guide** | ||||
| to get you started on improving the plugin: | ||||
| 
 | ||||
| 1. Download `pytest_keyword.py`_ plugin source code  | ||||
| 2. put it somewhere as ``pytest_keyword.py`` into your import path  | ||||
| 3. a subsequent test run will now use your local version!  | ||||
| 
 | ||||
| Further information: extend_ documentation, other plugins_ or contact_.   | ||||
| 
 | ||||
| For your convenience here is also an inlined version of ``pytest_keyword.py``: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     """ | ||||
|         py.test.mark / keyword plugin  | ||||
|     """ | ||||
|     import py | ||||
|      | ||||
|     def pytest_namespace(): | ||||
|         mark = KeywordDecorator({}) | ||||
|         return {'mark': mark} | ||||
|      | ||||
|     class KeywordDecorator: | ||||
|         """ decorator for setting function attributes. """ | ||||
|         def __init__(self, keywords, lastname=None): | ||||
|             self._keywords = keywords | ||||
|             self._lastname = lastname | ||||
|      | ||||
|         def __call__(self, func=None, **kwargs): | ||||
|             if func is None: | ||||
|                 kw = self._keywords.copy() | ||||
|                 kw.update(kwargs) | ||||
|                 return KeywordDecorator(kw) | ||||
|             elif not hasattr(func, 'func_dict'): | ||||
|                 kw = self._keywords.copy() | ||||
|                 name = self._lastname | ||||
|                 if name is None: | ||||
|                     name = "mark" | ||||
|                 kw[name] = func | ||||
|                 return KeywordDecorator(kw) | ||||
|             func.func_dict.update(self._keywords) | ||||
|             return func  | ||||
|      | ||||
|         def __getattr__(self, name): | ||||
|             if name[0] == "_": | ||||
|                 raise AttributeError(name) | ||||
|             kw = self._keywords.copy() | ||||
|             kw[name] = True | ||||
|             return self.__class__(kw, lastname=name) | ||||
|      | ||||
|     def test_pytest_mark_getattr(): | ||||
|         mark = KeywordDecorator({}) | ||||
|         def f(): pass | ||||
|      | ||||
|         mark.hello(f) | ||||
|         assert f.hello == True | ||||
|      | ||||
|         mark.hello("test")(f) | ||||
|         assert f.hello == "test" | ||||
|      | ||||
|         py.test.raises(AttributeError, "mark._hello") | ||||
|         py.test.raises(AttributeError, "mark.__str__") | ||||
|      | ||||
|     def test_pytest_mark_call(): | ||||
|         mark = KeywordDecorator({}) | ||||
|         def f(): pass | ||||
|         mark(x=3)(f) | ||||
|         assert f.x == 3 | ||||
|         def g(): pass | ||||
|         mark(g) | ||||
|         assert not g.func_dict | ||||
|      | ||||
|         mark.hello(f) | ||||
|         assert f.hello == True | ||||
|      | ||||
|         mark.hello("test")(f) | ||||
|         assert f.hello == "test" | ||||
|      | ||||
|         mark("x1")(f) | ||||
|         assert f.mark == "x1" | ||||
|      | ||||
|     def test_mark_plugin(testdir): | ||||
|         p = testdir.makepyfile(""" | ||||
|             import py | ||||
|             pytest_plugins = "keyword"  | ||||
|             @py.test.mark.hello | ||||
|             def test_hello(): | ||||
|                 assert hasattr(test_hello, 'hello') | ||||
|         """) | ||||
|         result = testdir.runpytest(p) | ||||
|         assert result.stdout.fnmatch_lines(["*passed*"]) | ||||
| 
 | ||||
| .. _`pytest_keyword.py`: http://bitbucket.org/hpk42/py-trunk/raw/ea1f958813ebbff45161fdb468a6204be5396112/py/test/plugin/pytest_keyword.py | ||||
| .. _`extend`: ../extend.html | ||||
| .. _`plugins`: index.html | ||||
| .. _`contact`: ../../contact.html | ||||
| .. _`checkout the py.test development version`: ../../download.html#checkout | ||||
|  | @ -4,6 +4,9 @@ pytest_monkeypatch plugin | |||
| 
 | ||||
| safely patch object attributes, dicts and environment variables. | ||||
| 
 | ||||
| .. contents:: | ||||
|   :local: | ||||
| 
 | ||||
| Usage | ||||
| ---------------- | ||||
| 
 | ||||
|  | @ -26,6 +29,7 @@ modifications will be reverted.  See the `monkeypatch blog post`_ | |||
| for an extensive discussion.  | ||||
| 
 | ||||
| .. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/ | ||||
| 
 | ||||
| .. _`monkeypatch funcarg`: | ||||
| 
 | ||||
| 
 | ||||
|  | @ -42,147 +46,19 @@ helper methods to modify objects, dictionaries or os.environ:: | |||
| All such modifications will be undone when the requesting  | ||||
| test function finished its execution.  | ||||
| 
 | ||||
| Getting and improving this plugin | ||||
| --------------------------------- | ||||
| Start improving this plugin in 30 seconds | ||||
| ========================================= | ||||
| 
 | ||||
| 
 | ||||
| Do you find the above documentation or the plugin itself lacking, | ||||
| not fit for what you need?  Here is a **30 seconds guide** | ||||
| to get you started on improving the plugin: | ||||
| Do you find the above documentation or the plugin itself lacking?  | ||||
| 
 | ||||
| 1. Download `pytest_monkeypatch.py`_ plugin source code  | ||||
| 2. put it somewhere as ``pytest_monkeypatch.py`` into your import path  | ||||
| 3. a subsequent test run will now use your local version!  | ||||
| 3. a subsequent ``py.test`` run will use your local version | ||||
| 
 | ||||
| Further information: extend_ documentation, other plugins_ or contact_.   | ||||
| 
 | ||||
| For your convenience here is also an inlined version of ``pytest_monkeypatch.py``: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     """ | ||||
|     safely patch object attributes, dicts and environment variables.  | ||||
|      | ||||
|     Usage | ||||
|     ---------------- | ||||
|      | ||||
|     Use the `monkeypatch funcarg`_ to safely patch the environment | ||||
|     variables, object attributes or dictionaries.  For example, if you want | ||||
|     to set the environment variable ``ENV1`` and patch the | ||||
|     ``os.path.abspath`` function to return a particular value during a test | ||||
|     function execution you can write it down like this: | ||||
|      | ||||
|     .. sourcecode:: python  | ||||
|      | ||||
|         def test_mytest(monkeypatch): | ||||
|             monkeypatch.setenv('ENV1', 'myval') | ||||
|             monkeypatch.setattr(os.path, 'abspath', lambda x: '/') | ||||
|             ... # your test code  | ||||
|      | ||||
|     The function argument will do the modifications and memorize the  | ||||
|     old state.  After the test function finished execution all  | ||||
|     modifications will be reverted.  See the `monkeypatch blog post`_  | ||||
|     for an extensive discussion.  | ||||
|      | ||||
|     .. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/ | ||||
|     """ | ||||
|      | ||||
|     import os | ||||
|      | ||||
|     def pytest_funcarg__monkeypatch(request): | ||||
|         """The returned ``monkeypatch`` funcarg provides three  | ||||
|         helper methods to modify objects, dictionaries or os.environ:: | ||||
|      | ||||
|             monkeypatch.setattr(obj, name, value)   | ||||
|             monkeypatch.setitem(mapping, name, value)  | ||||
|             monkeypatch.setenv(name, value)  | ||||
|      | ||||
|         All such modifications will be undone when the requesting  | ||||
|         test function finished its execution.  | ||||
|         """ | ||||
|         monkeypatch = MonkeyPatch() | ||||
|         request.addfinalizer(monkeypatch.finalize) | ||||
|         return monkeypatch | ||||
|      | ||||
|     notset = object() | ||||
|      | ||||
|     class MonkeyPatch: | ||||
|         def __init__(self): | ||||
|             self._setattr = [] | ||||
|             self._setitem = [] | ||||
|      | ||||
|         def setattr(self, obj, name, value): | ||||
|             self._setattr.insert(0, (obj, name, getattr(obj, name, notset))) | ||||
|             setattr(obj, name, value) | ||||
|      | ||||
|         def setitem(self, dictionary, name, value): | ||||
|             self._setitem.insert(0, (dictionary, name, dictionary.get(name, notset))) | ||||
|             dictionary[name] = value | ||||
|      | ||||
|         def setenv(self, name, value): | ||||
|             self.setitem(os.environ, name, str(value))         | ||||
|      | ||||
|         def finalize(self): | ||||
|             for obj, name, value in self._setattr: | ||||
|                 if value is not notset: | ||||
|                     setattr(obj, name, value) | ||||
|                 else: | ||||
|                     delattr(obj, name) | ||||
|             for dictionary, name, value in self._setitem: | ||||
|                 if value is notset: | ||||
|                     del dictionary[name] | ||||
|                 else: | ||||
|                     dictionary[name] = value | ||||
|      | ||||
|      | ||||
|     def test_setattr(): | ||||
|         class A: | ||||
|             x = 1 | ||||
|         monkeypatch = MonkeyPatch() | ||||
|         monkeypatch.setattr(A, 'x', 2) | ||||
|         assert A.x == 2 | ||||
|         monkeypatch.setattr(A, 'x', 3) | ||||
|         assert A.x == 3 | ||||
|         monkeypatch.finalize() | ||||
|         assert A.x == 1 | ||||
|      | ||||
|         monkeypatch.setattr(A, 'y', 3) | ||||
|         assert A.y == 3 | ||||
|         monkeypatch.finalize() | ||||
|         assert not hasattr(A, 'y') | ||||
|           | ||||
|      | ||||
|     def test_setitem(): | ||||
|         d = {'x': 1} | ||||
|         monkeypatch = MonkeyPatch() | ||||
|         monkeypatch.setitem(d, 'x', 2) | ||||
|         monkeypatch.setitem(d, 'y', 1700) | ||||
|         assert d['x'] == 2 | ||||
|         assert d['y'] == 1700 | ||||
|         monkeypatch.setitem(d, 'x', 3) | ||||
|         assert d['x'] == 3 | ||||
|         monkeypatch.finalize() | ||||
|         assert d['x'] == 1 | ||||
|         assert 'y' not in d | ||||
|      | ||||
|     def test_setenv(): | ||||
|         monkeypatch = MonkeyPatch() | ||||
|         monkeypatch.setenv('XYZ123', 2) | ||||
|         import os | ||||
|         assert os.environ['XYZ123'] == "2" | ||||
|         monkeypatch.finalize() | ||||
|         assert 'XYZ123' not in os.environ | ||||
|      | ||||
|     def test_monkeypatch_plugin(testdir): | ||||
|         reprec = testdir.inline_runsource(""" | ||||
|             pytest_plugins = 'pytest_monkeypatch',  | ||||
|             def test_method(monkeypatch): | ||||
|                 assert monkeypatch.__class__.__name__ == "MonkeyPatch" | ||||
|         """) | ||||
|         res = reprec.countoutcomes() | ||||
|         assert tuple(res) == (1, 0, 0), res | ||||
| 
 | ||||
| .. _`pytest_monkeypatch.py`: http://bitbucket.org/hpk42/py-trunk/raw/ea1f958813ebbff45161fdb468a6204be5396112/py/test/plugin/pytest_monkeypatch.py | ||||
| .. _`pytest_monkeypatch.py`: http://bitbucket.org/hpk42/py-trunk/raw/85fe614ab05f301f206935d11a477df184cbbce6/py/test/plugin/pytest_monkeypatch.py | ||||
| .. _`extend`: ../extend.html | ||||
| .. _`plugins`: index.html | ||||
| .. _`contact`: ../../contact.html | ||||
|  |  | |||
|  | @ -1,192 +0,0 @@ | |||
| 
 | ||||
| pytest_pdb plugin | ||||
| ================= | ||||
| 
 | ||||
| interactive debugging with the Python Debugger. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| command line options | ||||
| -------------------- | ||||
| 
 | ||||
| 
 | ||||
| ``--pdb`` | ||||
|     start pdb (the Python debugger) on errors. | ||||
| 
 | ||||
| Getting and improving this plugin | ||||
| --------------------------------- | ||||
| 
 | ||||
| 
 | ||||
| Do you find the above documentation or the plugin itself lacking, | ||||
| not fit for what you need?  Here is a **30 seconds guide** | ||||
| to get you started on improving the plugin: | ||||
| 
 | ||||
| 1. Download `pytest_pdb.py`_ plugin source code  | ||||
| 2. put it somewhere as ``pytest_pdb.py`` into your import path  | ||||
| 3. a subsequent test run will now use your local version!  | ||||
| 
 | ||||
| Further information: extend_ documentation, other plugins_ or contact_.   | ||||
| 
 | ||||
| For your convenience here is also an inlined version of ``pytest_pdb.py``: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     """ | ||||
|     interactive debugging with the Python Debugger. | ||||
|     """ | ||||
|     import py | ||||
|     import pdb, sys, linecache | ||||
|     from py.__.test.outcome import Skipped | ||||
|      | ||||
|     def pytest_addoption(parser): | ||||
|         group = parser.getgroup("general")  | ||||
|         group._addoption('--pdb', | ||||
|                    action="store_true", dest="usepdb", default=False, | ||||
|                    help="start pdb (the Python debugger) on errors.") | ||||
|      | ||||
|      | ||||
|     def pytest_configure(config): | ||||
|         if config.option.usepdb: | ||||
|             if config.getvalue("looponfail"): | ||||
|                 raise config.Error("--pdb incompatible with --looponfail.") | ||||
|             if config.option.dist != "no": | ||||
|                 raise config.Error("--pdb incomptaible with distributing tests.") | ||||
|             config.pluginmanager.register(PdbInvoke()) | ||||
|      | ||||
|     class PdbInvoke: | ||||
|         def pytest_runtest_makereport(self, item, call): | ||||
|             if call.excinfo and not call.excinfo.errisinstance(Skipped):  | ||||
|                 tw = py.io.TerminalWriter() | ||||
|                 repr = call.excinfo.getrepr() | ||||
|                 repr.toterminal(tw)  | ||||
|                 post_mortem(call.excinfo._excinfo[2]) | ||||
|      | ||||
|     class Pdb(py.std.pdb.Pdb): | ||||
|         def do_list(self, arg): | ||||
|             self.lastcmd = 'list' | ||||
|             last = None | ||||
|             if arg: | ||||
|                 try: | ||||
|                     x = eval(arg, {}, {}) | ||||
|                     if type(x) == type(()): | ||||
|                         first, last = x | ||||
|                         first = int(first) | ||||
|                         last = int(last) | ||||
|                         if last < first: | ||||
|                             # Assume it's a count | ||||
|                             last = first + last | ||||
|                     else: | ||||
|                         first = max(1, int(x) - 5) | ||||
|                 except: | ||||
|                     print '*** Error in argument:', repr(arg) | ||||
|                     return | ||||
|             elif self.lineno is None: | ||||
|                 first = max(1, self.curframe.f_lineno - 5) | ||||
|             else: | ||||
|                 first = self.lineno + 1 | ||||
|             if last is None: | ||||
|                 last = first + 10 | ||||
|             filename = self.curframe.f_code.co_filename | ||||
|             breaklist = self.get_file_breaks(filename) | ||||
|             try: | ||||
|                 for lineno in range(first, last+1): | ||||
|                     # start difference from normal do_line | ||||
|                     line = self._getline(filename, lineno) | ||||
|                     # end difference from normal do_line | ||||
|                     if not line: | ||||
|                         print '[EOF]' | ||||
|                         break | ||||
|                     else: | ||||
|                         s = repr(lineno).rjust(3) | ||||
|                         if len(s) < 4: s = s + ' ' | ||||
|                         if lineno in breaklist: s = s + 'B' | ||||
|                         else: s = s + ' ' | ||||
|                         if lineno == self.curframe.f_lineno: | ||||
|                             s = s + '->' | ||||
|                         print s + '\t' + line, | ||||
|                         self.lineno = lineno | ||||
|             except KeyboardInterrupt: | ||||
|                 pass | ||||
|         do_l = do_list | ||||
|      | ||||
|         def _getline(self, filename, lineno): | ||||
|             if hasattr(filename, "__source__"): | ||||
|                 try: | ||||
|                     return filename.__source__.lines[lineno - 1] + "\n" | ||||
|                 except IndexError: | ||||
|                     return None | ||||
|             return linecache.getline(filename, lineno) | ||||
|      | ||||
|         def get_stack(self, f, t): | ||||
|             # Modified from bdb.py to be able to walk the stack beyond generators, | ||||
|             # which does not work in the normal pdb :-( | ||||
|             stack, i = pdb.Pdb.get_stack(self, f, t) | ||||
|             if f is None: | ||||
|                 i = max(0, len(stack) - 1) | ||||
|             return stack, i | ||||
|      | ||||
|     def post_mortem(t): | ||||
|         # modified from pdb.py for the new get_stack() implementation | ||||
|         p = Pdb() | ||||
|         p.reset() | ||||
|         p.interaction(None, t) | ||||
|      | ||||
|     def set_trace(): | ||||
|         # again, a copy of the version in pdb.py | ||||
|         Pdb().set_trace(sys._getframe().f_back) | ||||
|      | ||||
|      | ||||
|     class TestPDB:  | ||||
|         def pytest_funcarg__pdblist(self, request): | ||||
|             monkeypatch = request.getfuncargvalue("monkeypatch") | ||||
|             pdblist = [] | ||||
|             def mypdb(*args): | ||||
|                 pdblist.append(args) | ||||
|             monkeypatch.setitem(globals(), 'post_mortem', mypdb) | ||||
|             return pdblist  | ||||
|      | ||||
|         def test_incompatibility_messages(self, testdir): | ||||
|             Error = py.test.config.Error | ||||
|             py.test.raises(Error, "testdir.parseconfigure('--pdb', '--looponfail')") | ||||
|             py.test.raises(Error, "testdir.parseconfigure('--pdb', '-n 3')") | ||||
|             py.test.raises(Error, "testdir.parseconfigure('--pdb', '-d')") | ||||
|               | ||||
|         def test_pdb_on_fail(self, testdir, pdblist): | ||||
|             rep = testdir.inline_runsource1('--pdb', """ | ||||
|                 def test_func():  | ||||
|                     assert 0 | ||||
|             """) | ||||
|             assert rep.failed | ||||
|             assert len(pdblist) == 1 | ||||
|             tb = py.code.Traceback(pdblist[0][0]) | ||||
|             assert tb[-1].name == "test_func" | ||||
|      | ||||
|         def test_pdb_on_skip(self, testdir, pdblist): | ||||
|             rep = testdir.inline_runsource1('--pdb', """ | ||||
|                 import py | ||||
|                 def test_func(): | ||||
|                     py.test.skip("hello") | ||||
|             """) | ||||
|             assert rep.skipped  | ||||
|             assert len(pdblist) == 0 | ||||
|      | ||||
|         def test_pdb_interaction(self, testdir): | ||||
|             p1 = testdir.makepyfile(""" | ||||
|                 def test_1(): | ||||
|                     i = 0 | ||||
|                     assert i == 1 | ||||
|             """) | ||||
|             child = testdir.spawn_pytest("--pdb %s" % p1) | ||||
|             #child.expect(".*def test_1.*") | ||||
|             child.expect(".*i = 0.*") | ||||
|             child.expect("(Pdb)") | ||||
|             child.sendeof() | ||||
|             child.expect("1 failed") | ||||
|             if child.isalive():  | ||||
|                 child.wait() | ||||
| 
 | ||||
| .. _`pytest_pdb.py`: http://bitbucket.org/hpk42/py-trunk/raw/ea1f958813ebbff45161fdb468a6204be5396112/py/test/plugin/pytest_pdb.py | ||||
| .. _`extend`: ../extend.html | ||||
| .. _`plugins`: index.html | ||||
| .. _`contact`: ../../contact.html | ||||
| .. _`checkout the py.test development version`: ../../download.html#checkout | ||||
										
											Binary file not shown.
										
									
								
							|  | @ -1,683 +0,0 @@ | |||
| 
 | ||||
| pytest_pytester plugin | ||||
| ====================== | ||||
| 
 | ||||
| funcargs and support code for testing py.test's own functionality. | ||||
| 
 | ||||
| 
 | ||||
| .. _`testdir funcarg`: | ||||
| 
 | ||||
| 
 | ||||
| the 'testdir' test function argument | ||||
| ------------------------------------ | ||||
| 
 | ||||
| XXX missing docstring | ||||
| .. _`reportrecorder funcarg`: | ||||
| 
 | ||||
| 
 | ||||
| the 'reportrecorder' test function argument | ||||
| ------------------------------------------- | ||||
| 
 | ||||
| XXX missing docstring | ||||
| .. _`venv funcarg`: | ||||
| 
 | ||||
| 
 | ||||
| the 'venv' test function argument | ||||
| --------------------------------- | ||||
| 
 | ||||
| XXX missing docstring | ||||
| .. _`linecomp funcarg`: | ||||
| 
 | ||||
| 
 | ||||
| the 'linecomp' test function argument | ||||
| ------------------------------------- | ||||
| 
 | ||||
| XXX missing docstring | ||||
| .. _`py_setup funcarg`: | ||||
| 
 | ||||
| 
 | ||||
| the 'py_setup' test function argument | ||||
| ------------------------------------- | ||||
| 
 | ||||
| XXX missing docstring | ||||
| .. _`LineMatcher funcarg`: | ||||
| 
 | ||||
| 
 | ||||
| the 'LineMatcher' test function argument | ||||
| ---------------------------------------- | ||||
| 
 | ||||
| XXX missing docstring | ||||
| 
 | ||||
| Getting and improving this plugin | ||||
| --------------------------------- | ||||
| 
 | ||||
| 
 | ||||
| Do you find the above documentation or the plugin itself lacking, | ||||
| not fit for what you need?  Here is a **30 seconds guide** | ||||
| to get you started on improving the plugin: | ||||
| 
 | ||||
| 1. Download `pytest_pytester.py`_ plugin source code  | ||||
| 2. put it somewhere as ``pytest_pytester.py`` into your import path  | ||||
| 3. a subsequent test run will now use your local version!  | ||||
| 
 | ||||
| Further information: extend_ documentation, other plugins_ or contact_.   | ||||
| 
 | ||||
| For your convenience here is also an inlined version of ``pytest_pytester.py``: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     """ | ||||
|     funcargs and support code for testing py.test's own functionality.  | ||||
|     """ | ||||
|      | ||||
|     import py | ||||
|     import sys, os | ||||
|     import inspect | ||||
|     from py.__.test.config import Config as pytestConfig | ||||
|     import hookspec | ||||
|     import subprocess | ||||
|      | ||||
|     pytest_plugins = '_pytest' | ||||
|      | ||||
|     def pytest_funcarg__linecomp(request): | ||||
|         return LineComp() | ||||
|      | ||||
|     def pytest_funcarg__LineMatcher(request): | ||||
|         return LineMatcher | ||||
|      | ||||
|     def pytest_funcarg__testdir(request): | ||||
|         tmptestdir = TmpTestdir(request) | ||||
|         return tmptestdir | ||||
|      | ||||
|     def pytest_funcarg__reportrecorder(request): | ||||
|         reprec = ReportRecorder(py._com.comregistry) | ||||
|         request.addfinalizer(lambda: reprec.comregistry.unregister(reprec)) | ||||
|         return reprec | ||||
|      | ||||
|     class RunResult: | ||||
|         def __init__(self, ret, outlines, errlines): | ||||
|             self.ret = ret | ||||
|             self.outlines = outlines | ||||
|             self.errlines = errlines | ||||
|             self.stdout = LineMatcher(outlines) | ||||
|             self.stderr = LineMatcher(errlines) | ||||
|      | ||||
|     class TmpTestdir: | ||||
|         def __init__(self, request): | ||||
|             self.request = request | ||||
|             self._pytest = request.getfuncargvalue("_pytest") | ||||
|             # XXX remove duplication with tmpdir plugin  | ||||
|             basetmp = request.config.ensuretemp("testdir") | ||||
|             name = request.function.__name__ | ||||
|             for i in range(100): | ||||
|                 try: | ||||
|                     tmpdir = basetmp.mkdir(name + str(i)) | ||||
|                 except py.error.EEXIST: | ||||
|                     continue | ||||
|                 break | ||||
|             # we need to create another subdir | ||||
|             # because Directory.collect() currently loads | ||||
|             # conftest.py from sibling directories | ||||
|             self.tmpdir = tmpdir.mkdir(name) | ||||
|             self.plugins = [] | ||||
|             self._syspathremove = [] | ||||
|             self.chdir() # always chdir | ||||
|             assert hasattr(self, '_olddir') | ||||
|             self.request.addfinalizer(self.finalize) | ||||
|      | ||||
|         def __repr__(self): | ||||
|             return "<TmpTestdir %r>" % (self.tmpdir,) | ||||
|      | ||||
|         def Config(self, comregistry=None, topdir=None): | ||||
|             if topdir is None: | ||||
|                 topdir = self.tmpdir.dirpath() | ||||
|             return pytestConfig(comregistry, topdir=topdir) | ||||
|      | ||||
|         def finalize(self): | ||||
|             for p in self._syspathremove: | ||||
|                 py.std.sys.path.remove(p) | ||||
|             if hasattr(self, '_olddir'): | ||||
|                 self._olddir.chdir() | ||||
|      | ||||
|         def getreportrecorder(self, obj): | ||||
|             if isinstance(obj, py._com.Registry): | ||||
|                 registry = obj | ||||
|             elif hasattr(obj, 'comregistry'): | ||||
|                 registry = obj.comregistry | ||||
|             elif hasattr(obj, 'pluginmanager'): | ||||
|                 registry = obj.pluginmanager.comregistry | ||||
|             elif hasattr(obj, 'config'): | ||||
|                 registry = obj.config.pluginmanager.comregistry | ||||
|             else: | ||||
|                 raise ValueError("obj %r provides no comregistry" %(obj,)) | ||||
|             assert isinstance(registry, py._com.Registry) | ||||
|             reprec = ReportRecorder(registry) | ||||
|             reprec.hookrecorder = self._pytest.gethookrecorder(hookspec, registry) | ||||
|             reprec.hook = reprec.hookrecorder.hook | ||||
|             return reprec | ||||
|      | ||||
|         def chdir(self): | ||||
|             old = self.tmpdir.chdir() | ||||
|             if not hasattr(self, '_olddir'): | ||||
|                 self._olddir = old  | ||||
|      | ||||
|         def _makefile(self, ext, args, kwargs): | ||||
|             items = kwargs.items() | ||||
|             if args: | ||||
|                 source = "\n".join(map(str, args)) | ||||
|                 basename = self.request.function.__name__ | ||||
|                 items.insert(0, (basename, source)) | ||||
|             ret = None | ||||
|             for name, value in items: | ||||
|                 p = self.tmpdir.join(name).new(ext=ext) | ||||
|                 source = py.code.Source(value) | ||||
|                 p.write(str(py.code.Source(value)).lstrip()) | ||||
|                 if ret is None: | ||||
|                     ret = p | ||||
|             return ret  | ||||
|      | ||||
|      | ||||
|         def makefile(self, ext, *args, **kwargs): | ||||
|             return self._makefile(ext, args, kwargs) | ||||
|      | ||||
|         def makeconftest(self, source): | ||||
|             return self.makepyfile(conftest=source) | ||||
|      | ||||
|         def makepyfile(self, *args, **kwargs): | ||||
|             return self._makefile('.py', args, kwargs) | ||||
|      | ||||
|         def maketxtfile(self, *args, **kwargs): | ||||
|             return self._makefile('.txt', args, kwargs) | ||||
|      | ||||
|         def syspathinsert(self, path=None): | ||||
|             if path is None: | ||||
|                 path = self.tmpdir | ||||
|             py.std.sys.path.insert(0, str(path)) | ||||
|             self._syspathremove.append(str(path)) | ||||
|                  | ||||
|         def mkdir(self, name): | ||||
|             return self.tmpdir.mkdir(name) | ||||
|          | ||||
|         def genitems(self, colitems): | ||||
|             return list(self.session.genitems(colitems)) | ||||
|      | ||||
|         def inline_genitems(self, *args): | ||||
|             #config = self.parseconfig(*args) | ||||
|             config = self.parseconfig(*args) | ||||
|             session = config.initsession() | ||||
|             rec = self.getreportrecorder(config) | ||||
|             colitems = [config.getfsnode(arg) for arg in config.args] | ||||
|             items = list(session.genitems(colitems)) | ||||
|             return items, rec  | ||||
|      | ||||
|         def runitem(self, source): | ||||
|             # used from runner functional tests  | ||||
|             item = self.getitem(source) | ||||
|             # the test class where we are called from wants to provide the runner  | ||||
|             testclassinstance = self.request.function.im_self | ||||
|             runner = testclassinstance.getrunner() | ||||
|             return runner(item) | ||||
|      | ||||
|         def inline_runsource(self, source, *cmdlineargs): | ||||
|             p = self.makepyfile(source) | ||||
|             l = list(cmdlineargs) + [p] | ||||
|             return self.inline_run(*l) | ||||
|      | ||||
|         def inline_runsource1(self, *args): | ||||
|             args = list(args) | ||||
|             source = args.pop() | ||||
|             p = self.makepyfile(source) | ||||
|             l = list(args) + [p] | ||||
|             reprec = self.inline_run(*l) | ||||
|             reports = reprec.getreports("pytest_runtest_logreport") | ||||
|             assert len(reports) == 1, reports  | ||||
|             return reports[0] | ||||
|      | ||||
|         def inline_run(self, *args): | ||||
|             config = self.parseconfig(*args) | ||||
|             config.pluginmanager.do_configure(config) | ||||
|             session = config.initsession() | ||||
|             reprec = self.getreportrecorder(config) | ||||
|             session.main() | ||||
|             config.pluginmanager.do_unconfigure(config) | ||||
|             return reprec  | ||||
|      | ||||
|         def config_preparse(self): | ||||
|             config = self.Config() | ||||
|             for plugin in self.plugins: | ||||
|                 if isinstance(plugin, str): | ||||
|                     config.pluginmanager.import_plugin(plugin) | ||||
|                 else: | ||||
|                     if isinstance(plugin, dict): | ||||
|                         plugin = PseudoPlugin(plugin)  | ||||
|                     if not config.pluginmanager.isregistered(plugin): | ||||
|                         config.pluginmanager.register(plugin) | ||||
|             #print "config.pluginmanager.impname2plugin", config.pluginmanager.impname2plugin | ||||
|             return config | ||||
|      | ||||
|         def parseconfig(self, *args): | ||||
|             if not args: | ||||
|                 args = (self.tmpdir,) | ||||
|             config = self.config_preparse() | ||||
|             args = list(args) + ["--basetemp=%s" % self.tmpdir.dirpath('basetemp')] | ||||
|             config.parse(args) | ||||
|             return config  | ||||
|      | ||||
|         def parseconfigure(self, *args): | ||||
|             config = self.parseconfig(*args) | ||||
|             config.pluginmanager.do_configure(config) | ||||
|             return config | ||||
|      | ||||
|         def getitem(self,  source, funcname="test_func"): | ||||
|             modcol = self.getmodulecol(source) | ||||
|             moditems = modcol.collect() | ||||
|             for item in modcol.collect(): | ||||
|                 if item.name == funcname: | ||||
|                     return item  | ||||
|             else: | ||||
|                 assert 0, "%r item not found in module:\n%s" %(funcname, source) | ||||
|      | ||||
|         def getitems(self,  source): | ||||
|             modcol = self.getmodulecol(source) | ||||
|             return list(modcol.config.initsession().genitems([modcol])) | ||||
|             #assert item is not None, "%r item not found in module:\n%s" %(funcname, source) | ||||
|             #return item  | ||||
|      | ||||
|         def getfscol(self,  path, configargs=()): | ||||
|             self.config = self.parseconfig(path, *configargs) | ||||
|             self.session = self.config.initsession() | ||||
|             return self.config.getfsnode(path) | ||||
|      | ||||
|         def getmodulecol(self,  source, configargs=(), withinit=False): | ||||
|             kw = {self.request.function.__name__: py.code.Source(source).strip()} | ||||
|             path = self.makepyfile(**kw) | ||||
|             if withinit: | ||||
|                 self.makepyfile(__init__ = "#") | ||||
|             self.config = self.parseconfig(path, *configargs) | ||||
|             self.session = self.config.initsession() | ||||
|             #self.config.pluginmanager.do_configure(config=self.config) | ||||
|             # XXX  | ||||
|             self.config.pluginmanager.import_plugin("runner")  | ||||
|             plugin = self.config.pluginmanager.getplugin("runner")  | ||||
|             plugin.pytest_configure(config=self.config) | ||||
|      | ||||
|             return self.config.getfsnode(path) | ||||
|      | ||||
|         def prepare(self): | ||||
|             p = self.tmpdir.join("conftest.py")  | ||||
|             if not p.check(): | ||||
|                 plugins = [x for x in self.plugins if isinstance(x, str)] | ||||
|                 if not plugins: | ||||
|                     return | ||||
|                 p.write("import py ; pytest_plugins = %r" % plugins) | ||||
|             else: | ||||
|                 if self.plugins: | ||||
|                     print "warning, ignoring reusing existing con", p  | ||||
|      | ||||
|         def popen(self, cmdargs, stdout, stderr, **kw): | ||||
|             if not hasattr(py.std, 'subprocess'): | ||||
|                 py.test.skip("no subprocess module") | ||||
|             env = os.environ.copy() | ||||
|             env['PYTHONPATH'] = ":".join(filter(None, [ | ||||
|                 str(os.getcwd()), env.get('PYTHONPATH', '')])) | ||||
|             kw['env'] = env | ||||
|             #print "env", env | ||||
|             return py.std.subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) | ||||
|      | ||||
|         def run(self, *cmdargs): | ||||
|             self.prepare() | ||||
|             old = self.tmpdir.chdir() | ||||
|             #print "chdir", self.tmpdir | ||||
|             try: | ||||
|                 return self._run(*cmdargs) | ||||
|             finally: | ||||
|                 old.chdir() | ||||
|      | ||||
|         def _run(self, *cmdargs): | ||||
|             cmdargs = map(str, cmdargs) | ||||
|             p1 = py.path.local("stdout") | ||||
|             p2 = py.path.local("stderr") | ||||
|             print "running", cmdargs, "curdir=", py.path.local() | ||||
|             f1 = p1.open("w") | ||||
|             f2 = p2.open("w") | ||||
|             popen = self.popen(cmdargs, stdout=f1, stderr=f2,  | ||||
|                 close_fds=(sys.platform != "win32")) | ||||
|             ret = popen.wait() | ||||
|             f1.close() | ||||
|             f2.close() | ||||
|             out, err = p1.readlines(cr=0), p2.readlines(cr=0) | ||||
|             if err: | ||||
|                 for line in err:  | ||||
|                     print >>py.std.sys.stderr, line | ||||
|             if out: | ||||
|                 for line in out:  | ||||
|                     print >>py.std.sys.stdout, line | ||||
|             return RunResult(ret, out, err) | ||||
|      | ||||
|         def runpybin(self, scriptname, *args): | ||||
|             fullargs = self._getpybinargs(scriptname) + args | ||||
|             return self.run(*fullargs) | ||||
|      | ||||
|         def _getpybinargs(self, scriptname): | ||||
|             bindir = py.path.local(py.__file__).dirpath("bin") | ||||
|             script = bindir.join(scriptname) | ||||
|             assert script.check() | ||||
|             return py.std.sys.executable, script | ||||
|      | ||||
|         def runpytest(self, *args): | ||||
|             p = py.path.local.make_numbered_dir(prefix="runpytest-",  | ||||
|                 keep=None, rootdir=self.tmpdir) | ||||
|             args = ('--basetemp=%s' % p, ) + args  | ||||
|             return self.runpybin("py.test", *args) | ||||
|      | ||||
|         def spawn_pytest(self, string, expect_timeout=10.0): | ||||
|             pexpect = py.test.importorskip("pexpect", "2.3") | ||||
|             basetemp = self.tmpdir.mkdir("pexpect") | ||||
|             invoke = "%s %s" % self._getpybinargs("py.test") | ||||
|             cmd = "%s --basetemp=%s %s" % (invoke, basetemp, string) | ||||
|             child = pexpect.spawn(cmd, logfile=basetemp.join("spawn.out").open("w")) | ||||
|             child.timeout = expect_timeout | ||||
|             return child | ||||
|      | ||||
|     class PseudoPlugin: | ||||
|         def __init__(self, vars): | ||||
|             self.__dict__.update(vars)  | ||||
|      | ||||
|     class ReportRecorder(object): | ||||
|         def __init__(self, comregistry): | ||||
|             self.comregistry = comregistry | ||||
|             comregistry.register(self) | ||||
|      | ||||
|         def getcall(self, name): | ||||
|             return self.hookrecorder.getcall(name) | ||||
|      | ||||
|         def popcall(self, name): | ||||
|             return self.hookrecorder.popcall(name) | ||||
|      | ||||
|         def getcalls(self, names): | ||||
|             """ return list of ParsedCall instances matching the given eventname. """ | ||||
|             return self.hookrecorder.getcalls(names) | ||||
|      | ||||
|         # functionality for test reports  | ||||
|      | ||||
|         def getreports(self, names="pytest_runtest_logreport pytest_collectreport"): | ||||
|             return [x.rep for x in self.getcalls(names)] | ||||
|      | ||||
|         def matchreport(self, inamepart="", names="pytest_runtest_logreport pytest_collectreport"): | ||||
|             """ return a testreport whose dotted import path matches """ | ||||
|             l = [] | ||||
|             for rep in self.getreports(names=names): | ||||
|                 colitem = rep.getnode() | ||||
|                 if not inamepart or inamepart in colitem.listnames(): | ||||
|                     l.append(rep) | ||||
|             if not l: | ||||
|                 raise ValueError("could not find test report matching %r: no test reports at all!" % | ||||
|                     (inamepart,)) | ||||
|             if len(l) > 1: | ||||
|                 raise ValueError("found more than one testreport matching %r: %s" %( | ||||
|                                  inamepart, l)) | ||||
|             return l[0] | ||||
|      | ||||
|         def getfailures(self, names='pytest_runtest_logreport pytest_collectreport'): | ||||
|             return [rep for rep in self.getreports(names) if rep.failed] | ||||
|      | ||||
|         def getfailedcollections(self): | ||||
|             return self.getfailures('pytest_collectreport') | ||||
|      | ||||
|         def listoutcomes(self): | ||||
|             passed = [] | ||||
|             skipped = [] | ||||
|             failed = [] | ||||
|             for rep in self.getreports("pytest_runtest_logreport"): | ||||
|                 if rep.passed:  | ||||
|                     if rep.when == "call":  | ||||
|                         passed.append(rep)  | ||||
|                 elif rep.skipped:  | ||||
|                     skipped.append(rep)  | ||||
|                 elif rep.failed: | ||||
|                     failed.append(rep)  | ||||
|             return passed, skipped, failed  | ||||
|      | ||||
|         def countoutcomes(self): | ||||
|             return map(len, self.listoutcomes()) | ||||
|      | ||||
|         def assertoutcome(self, passed=0, skipped=0, failed=0): | ||||
|             realpassed, realskipped, realfailed = self.listoutcomes() | ||||
|             assert passed == len(realpassed) | ||||
|             assert skipped == len(realskipped) | ||||
|             assert failed == len(realfailed) | ||||
|      | ||||
|         def clear(self): | ||||
|             self.hookrecorder.calls[:] = [] | ||||
|      | ||||
|         def unregister(self): | ||||
|             self.comregistry.unregister(self) | ||||
|             self.hookrecorder.finish_recording() | ||||
|      | ||||
|     def test_reportrecorder(testdir): | ||||
|         registry = py._com.Registry() | ||||
|         recorder = testdir.getreportrecorder(registry) | ||||
|         assert not recorder.getfailures() | ||||
|         item = testdir.getitem("def test_func(): pass") | ||||
|         class rep: | ||||
|             excinfo = None | ||||
|             passed = False | ||||
|             failed = True  | ||||
|             skipped = False | ||||
|             when = "call"  | ||||
|      | ||||
|         recorder.hook.pytest_runtest_logreport(rep=rep) | ||||
|         failures = recorder.getfailures() | ||||
|         assert failures == [rep] | ||||
|         failures = recorder.getfailures() | ||||
|         assert failures == [rep] | ||||
|      | ||||
|         class rep: | ||||
|             excinfo = None | ||||
|             passed = False | ||||
|             failed = False | ||||
|             skipped = True | ||||
|             when = "call"  | ||||
|         rep.passed = False | ||||
|         rep.skipped = True | ||||
|         recorder.hook.pytest_runtest_logreport(rep=rep) | ||||
|      | ||||
|         modcol = testdir.getmodulecol("") | ||||
|         rep = modcol.config.hook.pytest_make_collect_report(collector=modcol) | ||||
|         rep.passed = False | ||||
|         rep.failed = True | ||||
|         rep.skipped = False | ||||
|         recorder.hook.pytest_collectreport(rep=rep) | ||||
|      | ||||
|         passed, skipped, failed = recorder.listoutcomes() | ||||
|         assert not passed and skipped and failed | ||||
|      | ||||
|         numpassed, numskipped, numfailed = recorder.countoutcomes() | ||||
|         assert numpassed == 0 | ||||
|         assert numskipped == 1 | ||||
|         assert numfailed == 1 | ||||
|         assert len(recorder.getfailedcollections()) == 1 | ||||
|      | ||||
|         recorder.unregister() | ||||
|         recorder.clear()  | ||||
|         recorder.hook.pytest_runtest_logreport(rep=rep) | ||||
|         py.test.raises(ValueError, "recorder.getfailures()") | ||||
|      | ||||
|     class LineComp: | ||||
|         def __init__(self): | ||||
|             self.stringio = py.std.StringIO.StringIO() | ||||
|      | ||||
|         def assert_contains_lines(self, lines2): | ||||
|             """ assert that lines2 are contained (linearly) in lines1.  | ||||
|                 return a list of extralines found. | ||||
|             """ | ||||
|             __tracebackhide__ = True | ||||
|             val = self.stringio.getvalue() | ||||
|             self.stringio.truncate(0)  # remove what we got  | ||||
|             lines1 = val.split("\n") | ||||
|             return LineMatcher(lines1).fnmatch_lines(lines2) | ||||
|                  | ||||
|     class LineMatcher: | ||||
|         def __init__(self,  lines): | ||||
|             self.lines = lines | ||||
|      | ||||
|         def str(self): | ||||
|             return "\n".join(self.lines) | ||||
|      | ||||
|         def fnmatch_lines(self, lines2): | ||||
|             if isinstance(lines2, str): | ||||
|                 lines2 = py.code.Source(lines2) | ||||
|             if isinstance(lines2, py.code.Source): | ||||
|                 lines2 = lines2.strip().lines | ||||
|      | ||||
|             from fnmatch import fnmatch | ||||
|             __tracebackhide__ = True | ||||
|             lines1 = self.lines[:] | ||||
|             nextline = None | ||||
|             extralines = [] | ||||
|             for line in lines2: | ||||
|                 nomatchprinted = False | ||||
|                 while lines1: | ||||
|                     nextline = lines1.pop(0) | ||||
|                     if line == nextline: | ||||
|                         print "exact match:", repr(line) | ||||
|                         break  | ||||
|                     elif fnmatch(nextline, line): | ||||
|                         print "fnmatch:", repr(line) | ||||
|                         print "   with:", repr(nextline) | ||||
|                         break | ||||
|                     else: | ||||
|                         if not nomatchprinted: | ||||
|                             print "nomatch:", repr(line) | ||||
|                             nomatchprinted = True | ||||
|                         print "    and:", repr(nextline) | ||||
|                     extralines.append(nextline) | ||||
|                 else: | ||||
|                     if line != nextline: | ||||
|                         #__tracebackhide__ = True | ||||
|                         raise AssertionError("expected line not found: %r" % line) | ||||
|             extralines.extend(lines1) | ||||
|             return extralines  | ||||
|      | ||||
|     def test_parseconfig(testdir): | ||||
|         config1 = testdir.parseconfig() | ||||
|         config2 = testdir.parseconfig() | ||||
|         assert config2 != config1 | ||||
|         assert config1 != py.test.config | ||||
|      | ||||
|     def test_testdir_runs_with_plugin(testdir): | ||||
|         testdir.makepyfile(""" | ||||
|             pytest_plugins = "pytest_pytester"  | ||||
|             def test_hello(testdir): | ||||
|                 assert 1 | ||||
|         """) | ||||
|         result = testdir.runpytest() | ||||
|         assert result.stdout.fnmatch_lines([ | ||||
|             "*1 passed*" | ||||
|         ]) | ||||
|      | ||||
|     # | ||||
|     # experimental funcargs for venv/install-tests | ||||
|     # | ||||
|      | ||||
|     def pytest_funcarg__venv(request): | ||||
|         p = request.config.mktemp(request.function.__name__, numbered=True) | ||||
|         venv = VirtualEnv(str(p))  | ||||
|         venv.create() | ||||
|         return venv  | ||||
|         | ||||
|     def pytest_funcarg__py_setup(request): | ||||
|         rootdir = py.path.local(py.__file__).dirpath().dirpath() | ||||
|         setup = rootdir.join('setup.py') | ||||
|         if not setup.check(): | ||||
|             py.test.skip("not found: %r" % setup) | ||||
|         return SetupBuilder(setup) | ||||
|      | ||||
|     class SetupBuilder: | ||||
|         def __init__(self, setup_path): | ||||
|             self.setup_path = setup_path | ||||
|      | ||||
|         def make_sdist(self, destdir=None): | ||||
|             temp = py.path.local.mkdtemp() | ||||
|             try: | ||||
|                 args = ['python', str(self.setup_path), 'sdist',  | ||||
|                         '--dist-dir', str(temp)] | ||||
|                 subprocess.check_call(args) | ||||
|                 l = temp.listdir('py-*') | ||||
|                 assert len(l) == 1 | ||||
|                 sdist = l[0] | ||||
|                 if destdir is None: | ||||
|                     destdir = self.setup_path.dirpath('build') | ||||
|                     assert destdir.check() | ||||
|                 else: | ||||
|                     destdir = py.path.local(destdir) | ||||
|                 target = destdir.join(sdist.basename) | ||||
|                 sdist.copy(target) | ||||
|                 return target  | ||||
|             finally: | ||||
|                 temp.remove() | ||||
|      | ||||
|     # code taken from Ronny Pfannenschmidt's virtualenvmanager  | ||||
|      | ||||
|     class VirtualEnv(object): | ||||
|         def __init__(self, path): | ||||
|             #XXX: supply the python executable | ||||
|             self.path = path | ||||
|      | ||||
|         def __repr__(self): | ||||
|             return "<VirtualEnv at %r>" %(self.path) | ||||
|      | ||||
|         def _cmd(self, name): | ||||
|             return os.path.join(self.path, 'bin', name) | ||||
|      | ||||
|         @property | ||||
|         def valid(self): | ||||
|             return os.path.exists(self._cmd('python')) | ||||
|      | ||||
|         def create(self, sitepackages=False): | ||||
|             args = ['virtualenv', self.path] | ||||
|             if not sitepackages: | ||||
|                 args.append('--no-site-packages') | ||||
|             subprocess.check_call(args) | ||||
|      | ||||
|         def makegateway(self): | ||||
|             python = self._cmd('python') | ||||
|             return py.execnet.makegateway("popen//python=%s" %(python,)) | ||||
|      | ||||
|         def pcall(self, cmd, *args, **kw): | ||||
|             assert self.valid | ||||
|             return subprocess.call([ | ||||
|                     self._cmd(cmd) | ||||
|                 ] + list(args), | ||||
|                 **kw) | ||||
|      | ||||
|      | ||||
|         def easy_install(self, *packages, **kw): | ||||
|             args = [] | ||||
|             if 'index' in kw: | ||||
|                 index = kw['index'] | ||||
|                 if isinstance(index, (list, tuple)): | ||||
|                     for i in index: | ||||
|                         args.extend(['-i', i]) | ||||
|                 else: | ||||
|                     args.extend(['-i', index]) | ||||
|      | ||||
|             args.extend(packages) | ||||
|             self.pcall('easy_install', *args) | ||||
|      | ||||
|      | ||||
|         @property | ||||
|         def has_pip(self): | ||||
|             return os.path.exists(self._cmd('pip')) | ||||
|      | ||||
|         def pip_install(self, *packages): | ||||
|             if not self.has_pip: | ||||
|                 self.easy_install('pip') | ||||
|      | ||||
|             self.pcall('pip', *packages) | ||||
| 
 | ||||
| .. _`pytest_pytester.py`: http://bitbucket.org/hpk42/py-trunk/raw/ea1f958813ebbff45161fdb468a6204be5396112/py/test/plugin/pytest_pytester.py | ||||
| .. _`extend`: ../extend.html | ||||
| .. _`plugins`: index.html | ||||
| .. _`contact`: ../../contact.html | ||||
| .. _`checkout the py.test development version`: ../../download.html#checkout | ||||
|  | @ -4,207 +4,61 @@ pytest_recwarn plugin | |||
| 
 | ||||
| helpers for asserting deprecation and other warnings. | ||||
| 
 | ||||
| **recwarn**: function argument where one can call recwarn.pop() to get | ||||
| the last warning that would have been shown.  | ||||
| .. contents:: | ||||
|   :local: | ||||
| 
 | ||||
| Example usage  | ||||
| --------------------- | ||||
| 
 | ||||
| You can use the ``recwarn`` funcarg to track  | ||||
| warnings within a test function: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     def test_hello(recwarn): | ||||
|         from warnings import warn | ||||
|         warn("hello", DeprecationWarning) | ||||
|         w = recwarn.pop(DeprecationWarning) | ||||
|         assert issubclass(w.category, DeprecationWarning) | ||||
|         assert 'hello' in str(w.message) | ||||
|         assert w.filename | ||||
|         assert w.lineno | ||||
| 
 | ||||
| You can also call a global helper for checking | ||||
| taht a certain function call yields a Deprecation | ||||
| warning: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     import py | ||||
|              | ||||
|     def test_global(): | ||||
|         py.test.deprecated_call(myfunction, 17) | ||||
| 
 | ||||
| **py.test.deprecated_call(func, *args, **kwargs)**: assert that the given function call triggers a deprecation warning. | ||||
| .. _`recwarn funcarg`: | ||||
| 
 | ||||
| 
 | ||||
| the 'recwarn' test function argument | ||||
| ------------------------------------ | ||||
| 
 | ||||
|  check that warnings have been raised.  | ||||
| Return a WarningsRecorder instance that provides these methods: | ||||
| 
 | ||||
| Getting and improving this plugin | ||||
| --------------------------------- | ||||
| * ``pop(category=None)``: return last warning matching the category. | ||||
| * ``clear()``: clear list of warnings  | ||||
| 
 | ||||
| Start improving this plugin in 30 seconds | ||||
| ========================================= | ||||
| 
 | ||||
| 
 | ||||
| Do you find the above documentation or the plugin itself lacking, | ||||
| not fit for what you need?  Here is a **30 seconds guide** | ||||
| to get you started on improving the plugin: | ||||
| Do you find the above documentation or the plugin itself lacking?  | ||||
| 
 | ||||
| 1. Download `pytest_recwarn.py`_ plugin source code  | ||||
| 2. put it somewhere as ``pytest_recwarn.py`` into your import path  | ||||
| 3. a subsequent test run will now use your local version!  | ||||
| 3. a subsequent ``py.test`` run will use your local version | ||||
| 
 | ||||
| Further information: extend_ documentation, other plugins_ or contact_.   | ||||
| 
 | ||||
| For your convenience here is also an inlined version of ``pytest_recwarn.py``: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     """ | ||||
|     helpers for asserting deprecation and other warnings.  | ||||
|      | ||||
|     **recwarn**: function argument where one can call recwarn.pop() to get | ||||
|     the last warning that would have been shown.  | ||||
|      | ||||
|     **py.test.deprecated_call(func, *args, **kwargs)**: assert that the given function call triggers a deprecation warning.  | ||||
|     """ | ||||
|      | ||||
|     import py | ||||
|     import os | ||||
|      | ||||
|     def pytest_funcarg__recwarn(request): | ||||
|         """ check that warnings have been raised. """  | ||||
|         warnings = WarningsRecorder() | ||||
|         request.addfinalizer(warnings.finalize) | ||||
|         return warnings | ||||
|      | ||||
|     def pytest_namespace(): | ||||
|         return {'deprecated_call': deprecated_call} | ||||
|      | ||||
|     def deprecated_call(func, *args, **kwargs): | ||||
|         """ assert that calling func(*args, **kwargs) | ||||
|             triggers a DeprecationWarning.  | ||||
|         """  | ||||
|         warningmodule = py.std.warnings | ||||
|         l = [] | ||||
|         oldwarn_explicit = getattr(warningmodule, 'warn_explicit') | ||||
|         def warn_explicit(*args, **kwargs):  | ||||
|             l.append(args)  | ||||
|             oldwarn_explicit(*args, **kwargs) | ||||
|         oldwarn = getattr(warningmodule, 'warn') | ||||
|         def warn(*args, **kwargs):  | ||||
|             l.append(args)  | ||||
|             oldwarn(*args, **kwargs) | ||||
|              | ||||
|         warningmodule.warn_explicit = warn_explicit | ||||
|         warningmodule.warn = warn | ||||
|         try: | ||||
|             ret = func(*args, **kwargs) | ||||
|         finally: | ||||
|             warningmodule.warn_explicit = warn_explicit | ||||
|             warningmodule.warn = warn | ||||
|         if not l: | ||||
|             #print warningmodule | ||||
|             raise AssertionError("%r did not produce DeprecationWarning" %(func,)) | ||||
|         return ret | ||||
|      | ||||
|      | ||||
|     class RecordedWarning: | ||||
|         def __init__(self, message, category, filename, lineno, line): | ||||
|             self.message = message | ||||
|             self.category = category | ||||
|             self.filename = filename | ||||
|             self.lineno = lineno | ||||
|             self.line = line | ||||
|      | ||||
|     class WarningsRecorder: | ||||
|         def __init__(self): | ||||
|             warningmodule = py.std.warnings | ||||
|             self.list = [] | ||||
|             def showwarning(message, category, filename, lineno, line=0): | ||||
|                 self.list.append(RecordedWarning( | ||||
|                     message, category, filename, lineno, line)) | ||||
|                 try: | ||||
|                     self.old_showwarning(message, category,  | ||||
|                         filename, lineno, line=line) | ||||
|                 except TypeError: | ||||
|                     # < python2.6  | ||||
|                     self.old_showwarning(message, category, filename, lineno) | ||||
|             self.old_showwarning = warningmodule.showwarning | ||||
|             warningmodule.showwarning = showwarning | ||||
|      | ||||
|         def pop(self, cls=Warning): | ||||
|             """ pop the first recorded warning, raise exception if not exists.""" | ||||
|             for i, w in py.builtin.enumerate(self.list): | ||||
|                 if issubclass(w.category, cls): | ||||
|                     return self.list.pop(i) | ||||
|             __tracebackhide__ = True | ||||
|             assert 0, "%r not found in %r" %(cls, self.list) | ||||
|      | ||||
|         #def resetregistry(self): | ||||
|         #    import warnings | ||||
|         #    warnings.onceregistry.clear() | ||||
|         #    warnings.__warningregistry__.clear() | ||||
|      | ||||
|         def clear(self):  | ||||
|             self.list[:] = [] | ||||
|      | ||||
|         def finalize(self): | ||||
|             py.std.warnings.showwarning = self.old_showwarning | ||||
|      | ||||
|     def test_WarningRecorder(): | ||||
|         showwarning = py.std.warnings.showwarning | ||||
|         rec = WarningsRecorder() | ||||
|         assert py.std.warnings.showwarning != showwarning | ||||
|         assert not rec.list | ||||
|         py.std.warnings.warn_explicit("hello", UserWarning, "xyz", 13) | ||||
|         assert len(rec.list) == 1 | ||||
|         py.std.warnings.warn(DeprecationWarning("hello")) | ||||
|         assert len(rec.list) == 2 | ||||
|         warn = rec.pop() | ||||
|         assert str(warn.message) == "hello" | ||||
|         l = rec.list | ||||
|         rec.clear() | ||||
|         assert len(rec.list) == 0 | ||||
|         assert l is rec.list | ||||
|         py.test.raises(AssertionError, "rec.pop()") | ||||
|         rec.finalize() | ||||
|         assert showwarning == py.std.warnings.showwarning | ||||
|      | ||||
|     def test_recwarn_functional(testdir): | ||||
|         reprec = testdir.inline_runsource(""" | ||||
|             pytest_plugins = 'pytest_recwarn',  | ||||
|             import warnings | ||||
|             oldwarn = warnings.showwarning | ||||
|             def test_method(recwarn): | ||||
|                 assert warnings.showwarning != oldwarn | ||||
|                 warnings.warn("hello") | ||||
|                 warn = recwarn.pop() | ||||
|                 assert isinstance(warn.message, UserWarning) | ||||
|             def test_finalized(): | ||||
|                 assert warnings.showwarning == oldwarn | ||||
|         """) | ||||
|         res = reprec.countoutcomes() | ||||
|         assert tuple(res) == (2, 0, 0), res | ||||
|              | ||||
|     # | ||||
|     # ============ test py.test.deprecated_call() ============== | ||||
|     # | ||||
|      | ||||
|     def dep(i): | ||||
|         if i == 0: | ||||
|             py.std.warnings.warn("is deprecated", DeprecationWarning) | ||||
|         return 42 | ||||
|      | ||||
|     reg = {} | ||||
|     def dep_explicit(i): | ||||
|         if i == 0: | ||||
|             py.std.warnings.warn_explicit("dep_explicit", category=DeprecationWarning,  | ||||
|                                           filename="hello", lineno=3) | ||||
|      | ||||
|     def test_deprecated_call_raises(): | ||||
|         excinfo = py.test.raises(AssertionError,  | ||||
|                        "py.test.deprecated_call(dep, 3)") | ||||
|         assert str(excinfo).find("did not produce") != -1  | ||||
|      | ||||
|     def test_deprecated_call(): | ||||
|         py.test.deprecated_call(dep, 0) | ||||
|      | ||||
|     def test_deprecated_call_ret(): | ||||
|         ret = py.test.deprecated_call(dep, 0) | ||||
|         assert ret == 42 | ||||
|      | ||||
|     def test_deprecated_call_preserves(): | ||||
|         r = py.std.warnings.onceregistry.copy() | ||||
|         f = py.std.warnings.filters[:] | ||||
|         test_deprecated_call_raises() | ||||
|         test_deprecated_call() | ||||
|         assert r == py.std.warnings.onceregistry | ||||
|         assert f == py.std.warnings.filters | ||||
|      | ||||
|     def test_deprecated_explicit_call_raises(): | ||||
|         py.test.raises(AssertionError,  | ||||
|                        "py.test.deprecated_call(dep_explicit, 3)") | ||||
|      | ||||
|     def test_deprecated_explicit_call(): | ||||
|         py.test.deprecated_call(dep_explicit, 0) | ||||
|         py.test.deprecated_call(dep_explicit, 0) | ||||
| 
 | ||||
| .. _`pytest_recwarn.py`: http://bitbucket.org/hpk42/py-trunk/raw/ea1f958813ebbff45161fdb468a6204be5396112/py/test/plugin/pytest_recwarn.py | ||||
| .. _`pytest_recwarn.py`: http://bitbucket.org/hpk42/py-trunk/raw/85fe614ab05f301f206935d11a477df184cbbce6/py/test/plugin/pytest_recwarn.py | ||||
| .. _`extend`: ../extend.html | ||||
| .. _`plugins`: index.html | ||||
| .. _`contact`: ../../contact.html | ||||
|  |  | |||
|  | @ -4,6 +4,9 @@ pytest_restdoc plugin | |||
| 
 | ||||
| perform ReST syntax, local and remote reference tests on .rst/.txt files. | ||||
| 
 | ||||
| .. contents:: | ||||
|   :local: | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| command line options | ||||
|  | @ -17,514 +20,19 @@ command line options | |||
| ``--forcegen`` | ||||
|     force generation of html files. | ||||
| 
 | ||||
| Getting and improving this plugin | ||||
| --------------------------------- | ||||
| Start improving this plugin in 30 seconds | ||||
| ========================================= | ||||
| 
 | ||||
| 
 | ||||
| Do you find the above documentation or the plugin itself lacking, | ||||
| not fit for what you need?  Here is a **30 seconds guide** | ||||
| to get you started on improving the plugin: | ||||
| Do you find the above documentation or the plugin itself lacking?  | ||||
| 
 | ||||
| 1. Download `pytest_restdoc.py`_ plugin source code  | ||||
| 2. put it somewhere as ``pytest_restdoc.py`` into your import path  | ||||
| 3. a subsequent test run will now use your local version!  | ||||
| 3. a subsequent ``py.test`` run will use your local version | ||||
| 
 | ||||
| Further information: extend_ documentation, other plugins_ or contact_.   | ||||
| 
 | ||||
| For your convenience here is also an inlined version of ``pytest_restdoc.py``: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     """ | ||||
|     perform ReST syntax, local and remote reference tests on .rst/.txt files.  | ||||
|     """ | ||||
|     import py | ||||
|      | ||||
|     def pytest_addoption(parser): | ||||
|         group = parser.addgroup("ReST", "ReST documentation check options") | ||||
|         group.addoption('-R', '--urlcheck', | ||||
|                action="store_true", dest="urlcheck", default=False,  | ||||
|                help="urlopen() remote links found in ReST text files.")  | ||||
|         group.addoption('--urltimeout', action="store", metavar="secs", | ||||
|             type="int", dest="urlcheck_timeout", default=5, | ||||
|             help="timeout in seconds for remote urlchecks") | ||||
|         group.addoption('--forcegen', | ||||
|                action="store_true", dest="forcegen", default=False, | ||||
|                help="force generation of html files.") | ||||
|      | ||||
|     def pytest_collect_file(path, parent): | ||||
|         if path.ext in (".txt", ".rst"): | ||||
|             project = getproject(path) | ||||
|             if project is not None: | ||||
|                 return ReSTFile(path, parent=parent, project=project) | ||||
|      | ||||
|     def getproject(path): | ||||
|         for parent in path.parts(reverse=True): | ||||
|             confrest = parent.join("confrest.py") | ||||
|             if confrest.check(): | ||||
|                 Project = confrest.pyimport().Project | ||||
|                 return Project(parent) | ||||
|      | ||||
|     class ReSTFile(py.test.collect.File): | ||||
|         def __init__(self, fspath, parent, project=None): | ||||
|             super(ReSTFile, self).__init__(fspath=fspath, parent=parent) | ||||
|             if project is None: | ||||
|                 project = getproject(fspath) | ||||
|                 assert project is not None | ||||
|             self.project = project | ||||
|      | ||||
|         def collect(self): | ||||
|             return [ | ||||
|                 ReSTSyntaxTest(self.project, "ReSTSyntax", parent=self), | ||||
|                 LinkCheckerMaker("checklinks", parent=self), | ||||
|                 DoctestText("doctest", parent=self), | ||||
|             ] | ||||
|      | ||||
|     def deindent(s, sep='\n'): | ||||
|         leastspaces = -1 | ||||
|         lines = s.split(sep) | ||||
|         for line in lines: | ||||
|             if not line.strip(): | ||||
|                 continue | ||||
|             spaces = len(line) - len(line.lstrip()) | ||||
|             if leastspaces == -1 or spaces < leastspaces: | ||||
|                 leastspaces = spaces | ||||
|         if leastspaces == -1: | ||||
|             return s | ||||
|         for i, line in py.builtin.enumerate(lines): | ||||
|             if not line.strip(): | ||||
|                 lines[i] = '' | ||||
|             else: | ||||
|                 lines[i] = line[leastspaces:] | ||||
|         return sep.join(lines) | ||||
|      | ||||
|     class ReSTSyntaxTest(py.test.collect.Item):  | ||||
|         def __init__(self, project, *args, **kwargs): | ||||
|             super(ReSTSyntaxTest, self).__init__(*args, **kwargs) | ||||
|             self.project = project | ||||
|      | ||||
|         def reportinfo(self): | ||||
|             return self.fspath, None, "syntax check" | ||||
|      | ||||
|         def runtest(self): | ||||
|             self.restcheck(py.path.svnwc(self.fspath)) | ||||
|      | ||||
|         def restcheck(self, path): | ||||
|             py.test.importorskip("docutils") | ||||
|             self.register_linkrole() | ||||
|             from docutils.utils import SystemMessage | ||||
|             try:  | ||||
|                 self._checkskip(path, self.project.get_htmloutputpath(path)) | ||||
|                 self.project.process(path) | ||||
|             except KeyboardInterrupt:  | ||||
|                 raise  | ||||
|             except SystemMessage:  | ||||
|                 # we assume docutils printed info on stdout  | ||||
|                 py.test.fail("docutils processing failed, see captured stderr")  | ||||
|      | ||||
|         def register_linkrole(self): | ||||
|             from py.__.rest import directive | ||||
|             directive.register_linkrole('api', self.resolve_linkrole) | ||||
|             directive.register_linkrole('source', self.resolve_linkrole) | ||||
|      | ||||
|             # XXX fake sphinx' "toctree" and refs | ||||
|             directive.register_linkrole('ref', self.resolve_linkrole) | ||||
|              | ||||
|             from docutils.parsers.rst import directives | ||||
|             def toctree_directive(name, arguments, options, content, lineno, | ||||
|                           content_offset, block_text, state, state_machine): | ||||
|                 return [] | ||||
|             toctree_directive.content = 1 | ||||
|             toctree_directive.options = {'maxdepth': int, 'glob': directives.flag, | ||||
|                                  'hidden': directives.flag} | ||||
|             directives.register_directive('toctree', toctree_directive) | ||||
|             self.register_pygments() | ||||
|      | ||||
|         def register_pygments(self): | ||||
|             # taken from pygments-main/external/rst-directive.py  | ||||
|             try: | ||||
|                 from pygments.formatters import HtmlFormatter | ||||
|             except ImportError: | ||||
|                 def pygments_directive(name, arguments, options, content, lineno, | ||||
|                                        content_offset, block_text, state, state_machine): | ||||
|                     return [] | ||||
|             else: | ||||
|                 # The default formatter | ||||
|                 DEFAULT = HtmlFormatter(noclasses=True) | ||||
|                 # Add name -> formatter pairs for every variant you want to use | ||||
|                 VARIANTS = { | ||||
|                     # 'linenos': HtmlFormatter(noclasses=INLINESTYLES, linenos=True), | ||||
|                 } | ||||
|      | ||||
|                 from docutils import nodes | ||||
|                 from docutils.parsers.rst import directives | ||||
|      | ||||
|                 from pygments import highlight | ||||
|                 from pygments.lexers import get_lexer_by_name, TextLexer | ||||
|      | ||||
|                 def pygments_directive(name, arguments, options, content, lineno, | ||||
|                                        content_offset, block_text, state, state_machine): | ||||
|                     try: | ||||
|                         lexer = get_lexer_by_name(arguments[0]) | ||||
|                     except ValueError: | ||||
|                         # no lexer found - use the text one instead of an exception | ||||
|                         lexer = TextLexer() | ||||
|                     # take an arbitrary option if more than one is given | ||||
|                     formatter = options and VARIANTS[options.keys()[0]] or DEFAULT | ||||
|                     parsed = highlight(u'\n'.join(content), lexer, formatter) | ||||
|                     return [nodes.raw('', parsed, format='html')] | ||||
|      | ||||
|             pygments_directive.arguments = (1, 0, 1) | ||||
|             pygments_directive.content = 1 | ||||
|             pygments_directive.options = dict([(key, directives.flag) for key in VARIANTS]) | ||||
|      | ||||
|             directives.register_directive('sourcecode', pygments_directive) | ||||
|      | ||||
|         def resolve_linkrole(self, name, text, check=True): | ||||
|             apigen_relpath = self.project.apigen_relpath | ||||
|          | ||||
|             if name == 'api': | ||||
|                 if text == 'py': | ||||
|                     return ('py', apigen_relpath + 'api/index.html') | ||||
|                 else: | ||||
|                     assert text.startswith('py.'), ( | ||||
|                         'api link "%s" does not point to the py package') % (text,) | ||||
|                     dotted_name = text | ||||
|                     if dotted_name.find('(') > -1: | ||||
|                         dotted_name = dotted_name[:text.find('(')] | ||||
|                     # remove pkg root | ||||
|                     path = dotted_name.split('.')[1:] | ||||
|                     dotted_name = '.'.join(path) | ||||
|                     obj = py | ||||
|                     if check: | ||||
|                         for chunk in path: | ||||
|                             try: | ||||
|                                 obj = getattr(obj, chunk) | ||||
|                             except AttributeError: | ||||
|                                 raise AssertionError( | ||||
|                                     'problem with linkrole :api:`%s`: can not resolve ' | ||||
|                                     'dotted name %s' % (text, dotted_name,)) | ||||
|                     return (text, apigen_relpath + 'api/%s.html' % (dotted_name,)) | ||||
|             elif name == 'source': | ||||
|                 assert text.startswith('py/'), ('source link "%s" does not point ' | ||||
|                                                 'to the py package') % (text,) | ||||
|                 relpath = '/'.join(text.split('/')[1:]) | ||||
|                 if check: | ||||
|                     pkgroot = py.__pkg__.getpath() | ||||
|                     abspath = pkgroot.join(relpath) | ||||
|                     assert pkgroot.join(relpath).check(), ( | ||||
|                             'problem with linkrole :source:`%s`: ' | ||||
|                             'path %s does not exist' % (text, relpath)) | ||||
|                 if relpath.endswith('/') or not relpath: | ||||
|                     relpath += 'index.html' | ||||
|                 else: | ||||
|                     relpath += '.html' | ||||
|                 return (text, apigen_relpath + 'source/%s' % (relpath,)) | ||||
|             elif name == 'ref': | ||||
|                 return ("", "")  | ||||
|      | ||||
|         def _checkskip(self, lpath, htmlpath=None): | ||||
|             if not self.config.getvalue("forcegen"): | ||||
|                 lpath = py.path.local(lpath) | ||||
|                 if htmlpath is not None: | ||||
|                     htmlpath = py.path.local(htmlpath) | ||||
|                 if lpath.ext == '.txt':  | ||||
|                     htmlpath = htmlpath or lpath.new(ext='.html') | ||||
|                     if htmlpath.check(file=1) and htmlpath.mtime() >= lpath.mtime():  | ||||
|                         py.test.skip("html file is up to date, use --forcegen to regenerate") | ||||
|                         #return [] # no need to rebuild  | ||||
|      | ||||
|     class DoctestText(py.test.collect.Item):  | ||||
|         def reportinfo(self): | ||||
|             return self.fspath, None, "doctest" | ||||
|      | ||||
|         def runtest(self):  | ||||
|             content = self._normalize_linesep() | ||||
|             newcontent = self.config.hook.pytest_doctest_prepare_content(content=content) | ||||
|             if newcontent is not None: | ||||
|                 content = newcontent  | ||||
|             s = content  | ||||
|             l = [] | ||||
|             prefix = '.. >>> ' | ||||
|             mod = py.std.types.ModuleType(self.fspath.purebasename)  | ||||
|             skipchunk = False | ||||
|             for line in deindent(s).split('\n'): | ||||
|                 stripped = line.strip() | ||||
|                 if skipchunk and line.startswith(skipchunk): | ||||
|                     print "skipping", line | ||||
|                     continue | ||||
|                 skipchunk = False  | ||||
|                 if stripped.startswith(prefix): | ||||
|                     try: | ||||
|                         exec py.code.Source(stripped[len(prefix):]).compile() in \ | ||||
|                             mod.__dict__ | ||||
|                     except ValueError, e: | ||||
|                         if e.args and e.args[0] == "skipchunk": | ||||
|                             skipchunk = " " * (len(line) - len(line.lstrip())) | ||||
|                         else: | ||||
|                             raise | ||||
|                 else: | ||||
|                     l.append(line) | ||||
|             docstring = "\n".join(l) | ||||
|             mod.__doc__ = docstring  | ||||
|             failed, tot = py.compat.doctest.testmod(mod, verbose=1) | ||||
|             if failed:  | ||||
|                 py.test.fail("doctest %s: %s failed out of %s" %( | ||||
|                              self.fspath, failed, tot)) | ||||
|      | ||||
|         def _normalize_linesep(self): | ||||
|             # XXX quite nasty... but it works (fixes win32 issues) | ||||
|             s = self.fspath.read() | ||||
|             linesep = '\n' | ||||
|             if '\r' in s: | ||||
|                 if '\n' not in s: | ||||
|                     linesep = '\r' | ||||
|                 else: | ||||
|                     linesep = '\r\n' | ||||
|             s = s.replace(linesep, '\n') | ||||
|             return s | ||||
|              | ||||
|     class LinkCheckerMaker(py.test.collect.Collector):  | ||||
|         def collect(self): | ||||
|             return list(self.genlinkchecks()) | ||||
|      | ||||
|         def genlinkchecks(self): | ||||
|             path = self.fspath | ||||
|             # generating functions + args as single tests  | ||||
|             timeout = self.config.getvalue("urlcheck_timeout") | ||||
|             for lineno, line in py.builtin.enumerate(path.readlines()):  | ||||
|                 line = line.strip() | ||||
|                 if line.startswith('.. _'):  | ||||
|                     if line.startswith('.. _`'): | ||||
|                         delim = '`:' | ||||
|                     else: | ||||
|                         delim = ':' | ||||
|                     l = line.split(delim, 1) | ||||
|                     if len(l) != 2:  | ||||
|                         continue | ||||
|                     tryfn = l[1].strip()  | ||||
|                     name = "%s:%d" %(tryfn, lineno) | ||||
|                     if tryfn.startswith('http:') or tryfn.startswith('https'):  | ||||
|                         if self.config.getvalue("urlcheck"): | ||||
|                             yield CheckLink(name, parent=self,  | ||||
|                                 args=(tryfn, path, lineno, timeout), checkfunc=urlcheck) | ||||
|                     elif tryfn.startswith('webcal:'): | ||||
|                         continue | ||||
|                     else:  | ||||
|                         i = tryfn.find('#')  | ||||
|                         if i != -1:  | ||||
|                             checkfn = tryfn[:i] | ||||
|                         else:  | ||||
|                             checkfn = tryfn  | ||||
|                         if checkfn.strip() and (1 or checkfn.endswith('.html')):  | ||||
|                             yield CheckLink(name, parent=self,  | ||||
|                                 args=(tryfn, path, lineno), checkfunc=localrefcheck) | ||||
|              | ||||
|     class CheckLink(py.test.collect.Item): | ||||
|         def __init__(self, name, parent, args, checkfunc): | ||||
|             super(CheckLink, self).__init__(name, parent) | ||||
|             self.args = args | ||||
|             self.checkfunc = checkfunc | ||||
|      | ||||
|         def runtest(self): | ||||
|             return self.checkfunc(*self.args) | ||||
|      | ||||
|         def reportinfo(self, basedir=None): | ||||
|             return (self.fspath, self.args[2], "checklink: %s" % self.args[0]) | ||||
|      | ||||
|     def urlcheck(tryfn, path, lineno, TIMEOUT_URLOPEN):  | ||||
|         old = py.std.socket.getdefaulttimeout() | ||||
|         py.std.socket.setdefaulttimeout(TIMEOUT_URLOPEN) | ||||
|         try: | ||||
|             try:  | ||||
|                 print "trying remote", tryfn | ||||
|                 py.std.urllib2.urlopen(tryfn) | ||||
|             finally: | ||||
|                 py.std.socket.setdefaulttimeout(old) | ||||
|         except (py.std.urllib2.URLError, py.std.urllib2.HTTPError), e:  | ||||
|             if getattr(e, 'code', None) in (401, 403): # authorization required, forbidden | ||||
|                 py.test.skip("%s: %s" %(tryfn, str(e))) | ||||
|             else: | ||||
|                 py.test.fail("remote reference error %r in %s:%d\n%s" %( | ||||
|                              tryfn, path.basename, lineno+1, e)) | ||||
|      | ||||
|     def localrefcheck(tryfn, path, lineno):  | ||||
|         # assume it should be a file  | ||||
|         i = tryfn.find('#') | ||||
|         if tryfn.startswith('javascript:'): | ||||
|             return # don't check JS refs | ||||
|         if i != -1:  | ||||
|             anchor = tryfn[i+1:] | ||||
|             tryfn = tryfn[:i] | ||||
|         else:  | ||||
|             anchor = '' | ||||
|         fn = path.dirpath(tryfn)  | ||||
|         ishtml = fn.ext == '.html'  | ||||
|         fn = ishtml and fn.new(ext='.txt') or fn | ||||
|         print "filename is", fn  | ||||
|         if not fn.check(): # not ishtml or not fn.check():  | ||||
|             if not py.path.local(tryfn).check(): # the html could be there  | ||||
|                 py.test.fail("reference error %r in %s:%d" %( | ||||
|                               tryfn, path.basename, lineno+1)) | ||||
|         if anchor:  | ||||
|             source = unicode(fn.read(), 'latin1') | ||||
|             source = source.lower().replace('-', ' ') # aehem | ||||
|      | ||||
|             anchor = anchor.replace('-', ' ')  | ||||
|             match2 = ".. _`%s`:" % anchor  | ||||
|             match3 = ".. _%s:" % anchor  | ||||
|             candidates = (anchor, match2, match3) | ||||
|             print "candidates", repr(candidates) | ||||
|             for line in source.split('\n'):  | ||||
|                 line = line.strip() | ||||
|                 if line in candidates:  | ||||
|                     break  | ||||
|             else:  | ||||
|                 py.test.fail("anchor reference error %s#%s in %s:%d" %( | ||||
|                     tryfn, anchor, path.basename, lineno+1)) | ||||
|      | ||||
|      | ||||
|     # | ||||
|     # PLUGIN tests | ||||
|     # | ||||
|      | ||||
|     def test_deindent(): | ||||
|         assert deindent('foo') == 'foo' | ||||
|         assert deindent('foo\n  bar') == 'foo\n  bar' | ||||
|         assert deindent('  foo\n  bar\n') == 'foo\nbar\n' | ||||
|         assert deindent('  foo\n\n  bar\n') == 'foo\n\nbar\n' | ||||
|         assert deindent(' foo\n  bar\n') == 'foo\n bar\n' | ||||
|         assert deindent('  foo\n bar\n') == ' foo\nbar\n' | ||||
|      | ||||
|     class TestApigenLinkRole: | ||||
|         disabled = True | ||||
|      | ||||
|         # these tests are moved here from the former py/doc/conftest.py | ||||
|         def test_resolve_linkrole(self): | ||||
|             from py.__.doc.conftest import get_apigen_relpath | ||||
|             apigen_relpath = get_apigen_relpath() | ||||
|      | ||||
|             assert resolve_linkrole('api', 'py.foo.bar', False) == ( | ||||
|                 'py.foo.bar', apigen_relpath + 'api/foo.bar.html') | ||||
|             assert resolve_linkrole('api', 'py.foo.bar()', False) == ( | ||||
|                 'py.foo.bar()', apigen_relpath + 'api/foo.bar.html') | ||||
|             assert resolve_linkrole('api', 'py', False) == ( | ||||
|                 'py', apigen_relpath + 'api/index.html') | ||||
|             py.test.raises(AssertionError, 'resolve_linkrole("api", "foo.bar")') | ||||
|             assert resolve_linkrole('source', 'py/foo/bar.py', False) == ( | ||||
|                 'py/foo/bar.py', apigen_relpath + 'source/foo/bar.py.html') | ||||
|             assert resolve_linkrole('source', 'py/foo/', False) == ( | ||||
|                 'py/foo/', apigen_relpath + 'source/foo/index.html') | ||||
|             assert resolve_linkrole('source', 'py/', False) == ( | ||||
|                 'py/', apigen_relpath + 'source/index.html') | ||||
|             py.test.raises(AssertionError, 'resolve_linkrole("source", "/foo/bar/")') | ||||
|      | ||||
|         def test_resolve_linkrole_check_api(self): | ||||
|             assert resolve_linkrole('api', 'py.test.ensuretemp') | ||||
|             py.test.raises(AssertionError, "resolve_linkrole('api', 'py.foo.baz')") | ||||
|      | ||||
|         def test_resolve_linkrole_check_source(self): | ||||
|             assert resolve_linkrole('source', 'py/path/common.py') | ||||
|             py.test.raises(AssertionError, | ||||
|                            "resolve_linkrole('source', 'py/foo/bar.py')") | ||||
|      | ||||
|      | ||||
|     class TestDoctest: | ||||
|         def pytest_funcarg__testdir(self, request): | ||||
|             testdir = request.getfuncargvalue("testdir") | ||||
|             assert request.module.__name__ == __name__ | ||||
|             testdir.makepyfile(confrest="from py.__.misc.rest import Project") | ||||
|             for p in testdir.plugins: | ||||
|                 if p == globals(): | ||||
|                     break | ||||
|             else: | ||||
|                 testdir.plugins.append(globals()) | ||||
|             return testdir | ||||
|          | ||||
|         def test_doctest_extra_exec(self, testdir): | ||||
|             xtxt = testdir.maketxtfile(x=""" | ||||
|                 hello:: | ||||
|                     .. >>> raise ValueError  | ||||
|                        >>> None | ||||
|             """) | ||||
|             reprec = testdir.inline_run(xtxt) | ||||
|             passed, skipped, failed = reprec.countoutcomes() | ||||
|             assert failed == 1 | ||||
|      | ||||
|         def test_doctest_basic(self, testdir):  | ||||
|             xtxt = testdir.maketxtfile(x=""" | ||||
|                 ..  | ||||
|                    >>> from os.path import abspath  | ||||
|      | ||||
|                 hello world  | ||||
|      | ||||
|                    >>> assert abspath  | ||||
|                    >>> i=3 | ||||
|                    >>> print i | ||||
|                    3 | ||||
|      | ||||
|                 yes yes | ||||
|      | ||||
|                     >>> i | ||||
|                     3 | ||||
|      | ||||
|                 end | ||||
|             """) | ||||
|             reprec = testdir.inline_run(xtxt) | ||||
|             passed, skipped, failed = reprec.countoutcomes() | ||||
|             assert failed == 0  | ||||
|             assert passed + skipped == 2 | ||||
|      | ||||
|         def test_doctest_eol(self, testdir):  | ||||
|             ytxt = testdir.maketxtfile(y=".. >>> 1 + 1\r\n   2\r\n\r\n") | ||||
|             reprec = testdir.inline_run(ytxt) | ||||
|             passed, skipped, failed = reprec.countoutcomes() | ||||
|             assert failed == 0  | ||||
|             assert passed + skipped == 2 | ||||
|      | ||||
|         def test_doctest_indentation(self, testdir): | ||||
|             footxt = testdir.maketxtfile(foo= | ||||
|                 '..\n  >>> print "foo\\n  bar"\n  foo\n    bar\n') | ||||
|             reprec = testdir.inline_run(footxt) | ||||
|             passed, skipped, failed = reprec.countoutcomes() | ||||
|             assert failed == 0 | ||||
|             assert skipped + passed == 2  | ||||
|      | ||||
|         def test_js_ignore(self, testdir): | ||||
|             xtxt = testdir.maketxtfile(xtxt=""" | ||||
|                 `blah`_ | ||||
|      | ||||
|                 .. _`blah`: javascript:some_function() | ||||
|             """) | ||||
|             reprec = testdir.inline_run(xtxt) | ||||
|             passed, skipped, failed = reprec.countoutcomes() | ||||
|             assert failed == 0 | ||||
|             assert skipped + passed == 3  | ||||
|      | ||||
|         def test_pytest_doctest_prepare_content(self, testdir): | ||||
|             l = [] | ||||
|             class MyPlugin: | ||||
|                 def pytest_doctest_prepare_content(self, content): | ||||
|                     l.append(content) | ||||
|                     return content.replace("False", "True") | ||||
|      | ||||
|             testdir.plugins.append(MyPlugin()) | ||||
|      | ||||
|             xtxt = testdir.maketxtfile(x=""" | ||||
|                 hello: | ||||
|      | ||||
|                     >>> 2 == 2 | ||||
|                     False | ||||
|      | ||||
|             """) | ||||
|             reprec = testdir.inline_run(xtxt) | ||||
|             assert len(l) == 1 | ||||
|             passed, skipped, failed = reprec.countoutcomes() | ||||
|             assert passed >= 1 | ||||
|             assert not failed  | ||||
|             assert skipped <= 1 | ||||
| 
 | ||||
| .. _`pytest_restdoc.py`: http://bitbucket.org/hpk42/py-trunk/raw/ea1f958813ebbff45161fdb468a6204be5396112/py/test/plugin/pytest_restdoc.py | ||||
| .. _`pytest_restdoc.py`: http://bitbucket.org/hpk42/py-trunk/raw/85fe614ab05f301f206935d11a477df184cbbce6/py/test/plugin/pytest_restdoc.py | ||||
| .. _`extend`: ../extend.html | ||||
| .. _`plugins`: index.html | ||||
| .. _`contact`: ../../contact.html | ||||
|  |  | |||
|  | @ -4,6 +4,9 @@ pytest_resultlog plugin | |||
| 
 | ||||
| resultlog plugin for machine-readable logging of test results. | ||||
| 
 | ||||
| .. contents:: | ||||
|   :local: | ||||
| 
 | ||||
| Useful for buildbot integration code. | ||||
| 
 | ||||
| command line options | ||||
|  | @ -13,268 +16,19 @@ command line options | |||
| ``--resultlog=path`` | ||||
|     path for machine-readable result log. | ||||
| 
 | ||||
| Getting and improving this plugin | ||||
| --------------------------------- | ||||
| Start improving this plugin in 30 seconds | ||||
| ========================================= | ||||
| 
 | ||||
| 
 | ||||
| Do you find the above documentation or the plugin itself lacking, | ||||
| not fit for what you need?  Here is a **30 seconds guide** | ||||
| to get you started on improving the plugin: | ||||
| Do you find the above documentation or the plugin itself lacking?  | ||||
| 
 | ||||
| 1. Download `pytest_resultlog.py`_ plugin source code  | ||||
| 2. put it somewhere as ``pytest_resultlog.py`` into your import path  | ||||
| 3. a subsequent test run will now use your local version!  | ||||
| 3. a subsequent ``py.test`` run will use your local version | ||||
| 
 | ||||
| Further information: extend_ documentation, other plugins_ or contact_.   | ||||
| 
 | ||||
| For your convenience here is also an inlined version of ``pytest_resultlog.py``: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     """resultlog plugin for machine-readable logging of test results.  | ||||
|        Useful for buildbot integration code.  | ||||
|     """  | ||||
|      | ||||
|     import py | ||||
|      | ||||
|     def pytest_addoption(parser): | ||||
|         group = parser.addgroup("resultlog", "resultlog plugin options") | ||||
|         group.addoption('--resultlog', action="store", dest="resultlog", metavar="path", default=None, | ||||
|                help="path for machine-readable result log.") | ||||
|      | ||||
|     def pytest_configure(config): | ||||
|         resultlog = config.option.resultlog | ||||
|         if resultlog: | ||||
|             logfile = open(resultlog, 'w', 1) # line buffered | ||||
|             config._resultlog = ResultLog(logfile)  | ||||
|             config.pluginmanager.register(config._resultlog) | ||||
|      | ||||
|     def pytest_unconfigure(config): | ||||
|         resultlog = getattr(config, '_resultlog', None) | ||||
|         if resultlog: | ||||
|             resultlog.logfile.close() | ||||
|             del config._resultlog  | ||||
|             config.pluginmanager.unregister(resultlog) | ||||
|      | ||||
|     def generic_path(item): | ||||
|         chain = item.listchain() | ||||
|         gpath = [chain[0].name] | ||||
|         fspath = chain[0].fspath | ||||
|         fspart = False | ||||
|         for node in chain[1:]: | ||||
|             newfspath = node.fspath | ||||
|             if newfspath == fspath: | ||||
|                 if fspart: | ||||
|                     gpath.append(':') | ||||
|                     fspart = False | ||||
|                 else: | ||||
|                     gpath.append('.')             | ||||
|             else: | ||||
|                 gpath.append('/') | ||||
|                 fspart = True | ||||
|             name = node.name | ||||
|             if name[0] in '([': | ||||
|                 gpath.pop() | ||||
|             gpath.append(name) | ||||
|             fspath = newfspath | ||||
|         return ''.join(gpath) | ||||
|              | ||||
|     class ResultLog(object): | ||||
|         def __init__(self, logfile): | ||||
|             self.logfile = logfile # preferably line buffered | ||||
|      | ||||
|         def write_log_entry(self, testpath, shortrepr, longrepr): | ||||
|             print >>self.logfile, "%s %s" % (shortrepr, testpath) | ||||
|             for line in longrepr.splitlines(): | ||||
|                 print >>self.logfile, " %s" % line | ||||
|      | ||||
|         def log_outcome(self, node, shortrepr, longrepr): | ||||
|             testpath = generic_path(node) | ||||
|             self.write_log_entry(testpath, shortrepr, longrepr)  | ||||
|      | ||||
|         def pytest_runtest_logreport(self, rep): | ||||
|             code = rep.shortrepr  | ||||
|             if rep.passed: | ||||
|                 longrepr = "" | ||||
|             elif rep.failed: | ||||
|                 longrepr = str(rep.longrepr)  | ||||
|             elif rep.skipped: | ||||
|                 longrepr = str(rep.longrepr.reprcrash.message) | ||||
|             self.log_outcome(rep.item, code, longrepr)  | ||||
|      | ||||
|         def pytest_collectreport(self, rep): | ||||
|             if not rep.passed: | ||||
|                 if rep.failed:  | ||||
|                     code = "F" | ||||
|                 else: | ||||
|                     assert rep.skipped | ||||
|                     code = "S" | ||||
|                 longrepr = str(rep.longrepr.reprcrash) | ||||
|                 self.log_outcome(rep.collector, code, longrepr)     | ||||
|      | ||||
|         def pytest_internalerror(self, excrepr): | ||||
|             path = excrepr.reprcrash.path  | ||||
|             self.write_log_entry(path, '!', str(excrepr)) | ||||
|      | ||||
|      | ||||
|     # =============================================================================== | ||||
|     # | ||||
|     # plugin tests  | ||||
|     # | ||||
|     # =============================================================================== | ||||
|      | ||||
|     import os, StringIO | ||||
|      | ||||
|     def test_generic_path(): | ||||
|         from py.__.test.collect import Node, Item, FSCollector | ||||
|         p1 = Node('a') | ||||
|         assert p1.fspath is None | ||||
|         p2 = Node('B', parent=p1) | ||||
|         p3 = Node('()', parent = p2) | ||||
|         item = Item('c', parent = p3) | ||||
|      | ||||
|         res = generic_path(item) | ||||
|         assert res == 'a.B().c' | ||||
|      | ||||
|         p0 = FSCollector('proj/test') | ||||
|         p1 = FSCollector('proj/test/a', parent=p0) | ||||
|         p2 = Node('B', parent=p1) | ||||
|         p3 = Node('()', parent = p2) | ||||
|         p4 = Node('c', parent=p3) | ||||
|         item = Item('[1]', parent = p4) | ||||
|      | ||||
|         res = generic_path(item) | ||||
|         assert res == 'test/a:B().c[1]' | ||||
|      | ||||
|     def test_write_log_entry(): | ||||
|         reslog = ResultLog(None) | ||||
|         reslog.logfile = StringIO.StringIO() | ||||
|         reslog.write_log_entry('name', '.', '')   | ||||
|         entry = reslog.logfile.getvalue() | ||||
|         assert entry[-1] == '\n'         | ||||
|         entry_lines = entry.splitlines() | ||||
|         assert len(entry_lines) == 1 | ||||
|         assert entry_lines[0] == '. name' | ||||
|      | ||||
|         reslog.logfile = StringIO.StringIO() | ||||
|         reslog.write_log_entry('name', 's', 'Skipped')   | ||||
|         entry = reslog.logfile.getvalue() | ||||
|         assert entry[-1] == '\n'         | ||||
|         entry_lines = entry.splitlines() | ||||
|         assert len(entry_lines) == 2 | ||||
|         assert entry_lines[0] == 's name' | ||||
|         assert entry_lines[1] == ' Skipped' | ||||
|      | ||||
|         reslog.logfile = StringIO.StringIO() | ||||
|         reslog.write_log_entry('name', 's', 'Skipped\n')   | ||||
|         entry = reslog.logfile.getvalue() | ||||
|         assert entry[-1] == '\n'         | ||||
|         entry_lines = entry.splitlines() | ||||
|         assert len(entry_lines) == 2 | ||||
|         assert entry_lines[0] == 's name' | ||||
|         assert entry_lines[1] == ' Skipped' | ||||
|      | ||||
|         reslog.logfile = StringIO.StringIO() | ||||
|         longrepr = ' tb1\n tb 2\nE tb3\nSome Error' | ||||
|         reslog.write_log_entry('name', 'F', longrepr) | ||||
|         entry = reslog.logfile.getvalue() | ||||
|         assert entry[-1] == '\n'         | ||||
|         entry_lines = entry.splitlines() | ||||
|         assert len(entry_lines) == 5 | ||||
|         assert entry_lines[0] == 'F name' | ||||
|         assert entry_lines[1:] == [' '+line for line in longrepr.splitlines()] | ||||
|      | ||||
|          | ||||
|     class TestWithFunctionIntegration: | ||||
|         # XXX (hpk) i think that the resultlog plugin should | ||||
|         # provide a Parser object so that one can remain  | ||||
|         # ignorant regarding formatting details.   | ||||
|         def getresultlog(self, testdir, arg): | ||||
|             resultlog = testdir.tmpdir.join("resultlog") | ||||
|             testdir.plugins.append("resultlog") | ||||
|             args = ["--resultlog=%s" % resultlog] + [arg] | ||||
|             testdir.runpytest(*args) | ||||
|             return filter(None, resultlog.readlines(cr=0)) | ||||
|              | ||||
|         def test_collection_report(self, testdir): | ||||
|             ok = testdir.makepyfile(test_collection_ok="") | ||||
|             skip = testdir.makepyfile(test_collection_skip="import py ; py.test.skip('hello')") | ||||
|             fail = testdir.makepyfile(test_collection_fail="XXX") | ||||
|             lines = self.getresultlog(testdir, ok)  | ||||
|             assert not lines | ||||
|      | ||||
|             lines = self.getresultlog(testdir, skip) | ||||
|             assert len(lines) == 2 | ||||
|             assert lines[0].startswith("S ") | ||||
|             assert lines[0].endswith("test_collection_skip.py") | ||||
|             assert lines[1].startswith(" ") | ||||
|             assert lines[1].endswith("test_collection_skip.py:1: Skipped: 'hello'") | ||||
|      | ||||
|             lines = self.getresultlog(testdir, fail) | ||||
|             assert lines | ||||
|             assert lines[0].startswith("F ") | ||||
|             assert lines[0].endswith("test_collection_fail.py"), lines[0] | ||||
|             for x in lines[1:]: | ||||
|                 assert x.startswith(" ") | ||||
|             assert "XXX" in "".join(lines[1:]) | ||||
|      | ||||
|         def test_log_test_outcomes(self, testdir): | ||||
|             mod = testdir.makepyfile(test_mod=""" | ||||
|                 import py  | ||||
|                 def test_pass(): pass | ||||
|                 def test_skip(): py.test.skip("hello") | ||||
|                 def test_fail(): raise ValueError("val") | ||||
|             """) | ||||
|             lines = self.getresultlog(testdir, mod) | ||||
|             assert len(lines) >= 3 | ||||
|             assert lines[0].startswith(". ") | ||||
|             assert lines[0].endswith("test_pass") | ||||
|             assert lines[1].startswith("s "), lines[1] | ||||
|             assert lines[1].endswith("test_skip")  | ||||
|             assert lines[2].find("hello") != -1 | ||||
|             | ||||
|             assert lines[3].startswith("F ") | ||||
|             assert lines[3].endswith("test_fail") | ||||
|             tb = "".join(lines[4:]) | ||||
|             assert tb.find("ValueError") != -1  | ||||
|      | ||||
|         def test_internal_exception(self): | ||||
|             # they are produced for example by a teardown failing | ||||
|             # at the end of the run | ||||
|             try: | ||||
|                 raise ValueError | ||||
|             except ValueError: | ||||
|                 excinfo = py.code.ExceptionInfo() | ||||
|             reslog = ResultLog(StringIO.StringIO())         | ||||
|             reslog.pytest_internalerror(excinfo.getrepr()) | ||||
|             entry = reslog.logfile.getvalue() | ||||
|             entry_lines = entry.splitlines() | ||||
|      | ||||
|             assert entry_lines[0].startswith('! ') | ||||
|             assert os.path.basename(__file__)[:-1] in entry_lines[0] #.py/.pyc | ||||
|             assert entry_lines[-1][0] == ' ' | ||||
|             assert 'ValueError' in entry   | ||||
|      | ||||
|     def test_generic(testdir, LineMatcher): | ||||
|         testdir.plugins.append("resultlog") | ||||
|         testdir.makepyfile(""" | ||||
|             import py | ||||
|             def test_pass(): | ||||
|                 pass | ||||
|             def test_fail(): | ||||
|                 assert 0 | ||||
|             def test_skip(): | ||||
|                 py.test.skip("") | ||||
|         """) | ||||
|         testdir.runpytest("--resultlog=result.log") | ||||
|         lines = testdir.tmpdir.join("result.log").readlines(cr=0) | ||||
|         LineMatcher(lines).fnmatch_lines([ | ||||
|             ". *:test_pass",  | ||||
|             "F *:test_fail",  | ||||
|             "s *:test_skip",  | ||||
|         ]) | ||||
| 
 | ||||
| .. _`pytest_resultlog.py`: http://bitbucket.org/hpk42/py-trunk/raw/ea1f958813ebbff45161fdb468a6204be5396112/py/test/plugin/pytest_resultlog.py | ||||
| .. _`pytest_resultlog.py`: http://bitbucket.org/hpk42/py-trunk/raw/85fe614ab05f301f206935d11a477df184cbbce6/py/test/plugin/pytest_resultlog.py | ||||
| .. _`extend`: ../extend.html | ||||
| .. _`plugins`: index.html | ||||
| .. _`contact`: ../../contact.html | ||||
|  |  | |||
|  | @ -1,304 +0,0 @@ | |||
| 
 | ||||
| pytest_runner plugin | ||||
| ==================== | ||||
| 
 | ||||
| collect and run test items and create reports. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| command line options | ||||
| -------------------- | ||||
| 
 | ||||
| 
 | ||||
| ``--boxed`` | ||||
|     box each test run in a separate process | ||||
| 
 | ||||
| Getting and improving this plugin | ||||
| --------------------------------- | ||||
| 
 | ||||
| 
 | ||||
| Do you find the above documentation or the plugin itself lacking, | ||||
| not fit for what you need?  Here is a **30 seconds guide** | ||||
| to get you started on improving the plugin: | ||||
| 
 | ||||
| 1. Download `pytest_runner.py`_ plugin source code  | ||||
| 2. put it somewhere as ``pytest_runner.py`` into your import path  | ||||
| 3. a subsequent test run will now use your local version!  | ||||
| 
 | ||||
| Further information: extend_ documentation, other plugins_ or contact_.   | ||||
| 
 | ||||
| For your convenience here is also an inlined version of ``pytest_runner.py``: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     """  | ||||
|     collect and run test items and create reports.  | ||||
|     """ | ||||
|      | ||||
|     import py | ||||
|      | ||||
|     from py.__.test.outcome import Skipped | ||||
|      | ||||
|     # | ||||
|     # pytest plugin hooks  | ||||
|      | ||||
|     def pytest_addoption(parser): | ||||
|         group = parser.getgroup("general")  | ||||
|         group.addoption('--boxed', | ||||
|                    action="store_true", dest="boxed", default=False, | ||||
|                    help="box each test run in a separate process")  | ||||
|      | ||||
|     # XXX move to pytest_sessionstart and fix py.test owns tests  | ||||
|     def pytest_configure(config): | ||||
|         config._setupstate = SetupState() | ||||
|      | ||||
|     def pytest_sessionfinish(session, exitstatus): | ||||
|         # XXX see above | ||||
|         if hasattr(session.config, '_setupstate'): | ||||
|             session.config._setupstate.teardown_all() | ||||
|         # prevent logging module atexit handler from choking on  | ||||
|         # its attempt to close already closed streams  | ||||
|         # see http://bugs.python.org/issue6333 | ||||
|         mod = py.std.sys.modules.get("logging", None) | ||||
|         if mod is not None:  | ||||
|             mod.raiseExceptions = False  | ||||
|      | ||||
|     def pytest_make_collect_report(collector): | ||||
|         call = collector.config.guardedcall( | ||||
|             lambda: collector._memocollect() | ||||
|         ) | ||||
|         result = None | ||||
|         if not call.excinfo: | ||||
|             result = call.result | ||||
|         return CollectReport(collector, result, call.excinfo, call.outerr) | ||||
|      | ||||
|         return report  | ||||
|      | ||||
|     def pytest_runtest_protocol(item): | ||||
|         if item.config.getvalue("boxed"): | ||||
|             reports = forked_run_report(item)  | ||||
|             for rep in reports: | ||||
|                 item.config.hook.pytest_runtest_logreport(rep=rep) | ||||
|         else: | ||||
|             runtestprotocol(item) | ||||
|         return True | ||||
|      | ||||
|     def runtestprotocol(item, log=True): | ||||
|         rep = call_and_report(item, "setup", log) | ||||
|         reports = [rep] | ||||
|         if rep.passed: | ||||
|             reports.append(call_and_report(item, "call", log)) | ||||
|         reports.append(call_and_report(item, "teardown", log)) | ||||
|         return reports | ||||
|      | ||||
|     def pytest_runtest_setup(item): | ||||
|         item.config._setupstate.prepare(item) | ||||
|      | ||||
|     def pytest_runtest_call(item): | ||||
|         if not item._deprecated_testexecution(): | ||||
|             item.runtest() | ||||
|      | ||||
|     def pytest_runtest_makereport(item, call): | ||||
|         return ItemTestReport(item, call.excinfo, call.when, call.outerr) | ||||
|      | ||||
|     def pytest_runtest_teardown(item): | ||||
|         item.config._setupstate.teardown_exact(item) | ||||
|      | ||||
|     # | ||||
|     # Implementation | ||||
|      | ||||
|     def call_and_report(item, when, log=True): | ||||
|         call = RuntestHookCall(item, when) | ||||
|         hook = item.config.hook | ||||
|         report = hook.pytest_runtest_makereport(item=item, call=call) | ||||
|         if log and (when == "call" or not report.passed): | ||||
|             hook.pytest_runtest_logreport(rep=report)  | ||||
|         return report | ||||
|      | ||||
|      | ||||
|     class RuntestHookCall: | ||||
|         excinfo = None  | ||||
|         _prefix = "pytest_runtest_" | ||||
|         def __init__(self, item, when): | ||||
|             self.when = when  | ||||
|             hookname = self._prefix + when  | ||||
|             hook = getattr(item.config.hook, hookname) | ||||
|             capture = item.config._getcapture() | ||||
|             try: | ||||
|                 try: | ||||
|                     self.result = hook(item=item) | ||||
|                 except KeyboardInterrupt: | ||||
|                     raise | ||||
|                 except: | ||||
|                     self.excinfo = py.code.ExceptionInfo() | ||||
|             finally: | ||||
|                 self.outerr = capture.reset() | ||||
|      | ||||
|     def forked_run_report(item): | ||||
|         # for now, we run setup/teardown in the subprocess  | ||||
|         # XXX optionally allow sharing of setup/teardown  | ||||
|         EXITSTATUS_TESTEXIT = 4 | ||||
|         from py.__.test.dist.mypickle import ImmutablePickler | ||||
|         ipickle = ImmutablePickler(uneven=0) | ||||
|         ipickle.selfmemoize(item.config) | ||||
|         # XXX workaround the issue that 2.6 cannot pickle  | ||||
|         # instances of classes defined in global conftest.py files | ||||
|         ipickle.selfmemoize(item)  | ||||
|         def runforked(): | ||||
|             try: | ||||
|                 reports = runtestprotocol(item, log=False) | ||||
|             except KeyboardInterrupt:  | ||||
|                 py.std.os._exit(EXITSTATUS_TESTEXIT) | ||||
|             return ipickle.dumps(reports) | ||||
|      | ||||
|         ff = py.process.ForkedFunc(runforked) | ||||
|         result = ff.waitfinish() | ||||
|         if result.retval is not None: | ||||
|             return ipickle.loads(result.retval) | ||||
|         else: | ||||
|             if result.exitstatus == EXITSTATUS_TESTEXIT: | ||||
|                 py.test.exit("forked test item %s raised Exit" %(item,)) | ||||
|             return [report_process_crash(item, result)] | ||||
|      | ||||
|     def report_process_crash(item, result): | ||||
|         path, lineno = item._getfslineno() | ||||
|         info = "%s:%s: running the test CRASHED with signal %d" %( | ||||
|                 path, lineno, result.signal) | ||||
|         return ItemTestReport(item, excinfo=info, when="???") | ||||
|      | ||||
|     class BaseReport(object): | ||||
|         def __repr__(self): | ||||
|             l = ["%s=%s" %(key, value) | ||||
|                for key, value in self.__dict__.items()] | ||||
|             return "<%s %s>" %(self.__class__.__name__, " ".join(l),) | ||||
|      | ||||
|         def toterminal(self, out): | ||||
|             longrepr = self.longrepr  | ||||
|             if hasattr(longrepr, 'toterminal'): | ||||
|                 longrepr.toterminal(out) | ||||
|             else: | ||||
|                 out.line(str(longrepr)) | ||||
|         | ||||
|     class ItemTestReport(BaseReport): | ||||
|         failed = passed = skipped = False | ||||
|      | ||||
|         def __init__(self, item, excinfo=None, when=None, outerr=None): | ||||
|             self.item = item  | ||||
|             self.when = when | ||||
|             self.outerr = outerr | ||||
|             if item and when != "setup": | ||||
|                 self.keywords = item.readkeywords()  | ||||
|             else: | ||||
|                 # if we fail during setup it might mean  | ||||
|                 # we are not able to access the underlying object | ||||
|                 # this might e.g. happen if we are unpickled  | ||||
|                 # and our parent collector did not collect us  | ||||
|                 # (because it e.g. skipped for platform reasons) | ||||
|                 self.keywords = {}   | ||||
|             if not excinfo: | ||||
|                 self.passed = True | ||||
|                 self.shortrepr = "."  | ||||
|             else: | ||||
|                 if not isinstance(excinfo, py.code.ExceptionInfo): | ||||
|                     self.failed = True | ||||
|                     shortrepr = "?" | ||||
|                     longrepr = excinfo  | ||||
|                 elif excinfo.errisinstance(Skipped): | ||||
|                     self.skipped = True  | ||||
|                     shortrepr = "s" | ||||
|                     longrepr = self.item._repr_failure_py(excinfo, outerr) | ||||
|                 else: | ||||
|                     self.failed = True | ||||
|                     shortrepr = self.item.shortfailurerepr | ||||
|                     if self.when == "call": | ||||
|                         longrepr = self.item.repr_failure(excinfo, outerr) | ||||
|                     else: # exception in setup or teardown  | ||||
|                         longrepr = self.item._repr_failure_py(excinfo, outerr) | ||||
|                         shortrepr = shortrepr.lower() | ||||
|                 self.shortrepr = shortrepr  | ||||
|                 self.longrepr = longrepr  | ||||
|      | ||||
|         def getnode(self): | ||||
|             return self.item  | ||||
|      | ||||
|     class CollectReport(BaseReport): | ||||
|         skipped = failed = passed = False  | ||||
|      | ||||
|         def __init__(self, collector, result, excinfo=None, outerr=None): | ||||
|             self.collector = collector  | ||||
|             if not excinfo: | ||||
|                 self.passed = True | ||||
|                 self.result = result  | ||||
|             else: | ||||
|                 self.outerr = outerr | ||||
|                 self.longrepr = self.collector._repr_failure_py(excinfo, outerr) | ||||
|                 if excinfo.errisinstance(Skipped): | ||||
|                     self.skipped = True | ||||
|                     self.reason = str(excinfo.value) | ||||
|                 else: | ||||
|                     self.failed = True | ||||
|      | ||||
|         def getnode(self): | ||||
|             return self.collector  | ||||
|      | ||||
|     class SetupState(object): | ||||
|         """ shared state for setting up/tearing down test items or collectors. """ | ||||
|         def __init__(self): | ||||
|             self.stack = [] | ||||
|             self._finalizers = {} | ||||
|      | ||||
|         def addfinalizer(self, finalizer, colitem): | ||||
|             """ attach a finalizer to the given colitem.  | ||||
|             if colitem is None, this will add a finalizer that  | ||||
|             is called at the end of teardown_all().  | ||||
|             """ | ||||
|             assert callable(finalizer) | ||||
|             #assert colitem in self.stack | ||||
|             self._finalizers.setdefault(colitem, []).append(finalizer) | ||||
|      | ||||
|         def _pop_and_teardown(self): | ||||
|             colitem = self.stack.pop() | ||||
|             self._teardown_with_finalization(colitem) | ||||
|      | ||||
|         def _callfinalizers(self, colitem): | ||||
|             finalizers = self._finalizers.pop(colitem, None) | ||||
|             while finalizers: | ||||
|                 fin = finalizers.pop() | ||||
|                 fin() | ||||
|      | ||||
|         def _teardown_with_finalization(self, colitem):  | ||||
|             self._callfinalizers(colitem)  | ||||
|             if colitem:  | ||||
|                 colitem.teardown() | ||||
|             for colitem in self._finalizers: | ||||
|                 assert colitem is None or colitem in self.stack | ||||
|      | ||||
|         def teardown_all(self):  | ||||
|             while self.stack:  | ||||
|                 self._pop_and_teardown() | ||||
|             self._teardown_with_finalization(None) | ||||
|             assert not self._finalizers | ||||
|      | ||||
|         def teardown_exact(self, item): | ||||
|             if item == self.stack[-1]: | ||||
|                 self._pop_and_teardown() | ||||
|             else: | ||||
|                 self._callfinalizers(item) | ||||
|           | ||||
|         def prepare(self, colitem):  | ||||
|             """ setup objects along the collector chain to the test-method | ||||
|                 and teardown previously setup objects.""" | ||||
|             needed_collectors = colitem.listchain()  | ||||
|             while self.stack:  | ||||
|                 if self.stack == needed_collectors[:len(self.stack)]:  | ||||
|                     break  | ||||
|                 self._pop_and_teardown() | ||||
|             for col in needed_collectors[len(self.stack):]:  | ||||
|                 col.setup()  | ||||
|                 self.stack.append(col)  | ||||
| 
 | ||||
| .. _`pytest_runner.py`: http://bitbucket.org/hpk42/py-trunk/raw/ea1f958813ebbff45161fdb468a6204be5396112/py/test/plugin/pytest_runner.py | ||||
| .. _`extend`: ../extend.html | ||||
| .. _`plugins`: index.html | ||||
| .. _`contact`: ../../contact.html | ||||
| .. _`checkout the py.test development version`: ../../download.html#checkout | ||||
|  | @ -2,430 +2,26 @@ | |||
| pytest_terminal plugin | ||||
| ====================== | ||||
| 
 | ||||
| terminal reporting of the full testing process. | ||||
| Implements terminal reporting of the full testing process. | ||||
| 
 | ||||
| .. contents:: | ||||
|   :local: | ||||
| 
 | ||||
| This is a good source for looking at the various reporting hooks. | ||||
| 
 | ||||
| Start improving this plugin in 30 seconds | ||||
| ========================================= | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| Getting and improving this plugin | ||||
| --------------------------------- | ||||
| 
 | ||||
| 
 | ||||
| Do you find the above documentation or the plugin itself lacking, | ||||
| not fit for what you need?  Here is a **30 seconds guide** | ||||
| to get you started on improving the plugin: | ||||
| Do you find the above documentation or the plugin itself lacking?  | ||||
| 
 | ||||
| 1. Download `pytest_terminal.py`_ plugin source code  | ||||
| 2. put it somewhere as ``pytest_terminal.py`` into your import path  | ||||
| 3. a subsequent test run will now use your local version!  | ||||
| 3. a subsequent ``py.test`` run will use your local version | ||||
| 
 | ||||
| Further information: extend_ documentation, other plugins_ or contact_.   | ||||
| 
 | ||||
| For your convenience here is also an inlined version of ``pytest_terminal.py``: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     """ | ||||
|     terminal reporting of the full testing process. | ||||
|     """ | ||||
|     import py | ||||
|     import sys | ||||
|      | ||||
|     def pytest_configure(config): | ||||
|         if config.option.collectonly: | ||||
|             reporter = CollectonlyReporter(config) | ||||
|         else: | ||||
|             reporter = TerminalReporter(config) | ||||
|         # XXX see remote.py's XXX  | ||||
|         for attr in 'pytest_terminal_hasmarkup', 'pytest_terminal_fullwidth': | ||||
|             if hasattr(config, attr): | ||||
|                 #print "SETTING TERMINAL OPTIONS", attr, getattr(config, attr) | ||||
|                 name = attr.split("_")[-1] | ||||
|                 assert hasattr(self.reporter._tw, name), name | ||||
|                 setattr(reporter._tw, name, getattr(config, attr)) | ||||
|         config.pluginmanager.register(reporter) | ||||
|      | ||||
|     class TerminalReporter: | ||||
|         def __init__(self, config, file=None): | ||||
|             self.config = config  | ||||
|             self.stats = {}        | ||||
|             self.curdir = py.path.local() | ||||
|             if file is None: | ||||
|                 file = py.std.sys.stdout | ||||
|             self._tw = py.io.TerminalWriter(file) | ||||
|             self.currentfspath = None  | ||||
|             self.gateway2info = {} | ||||
|      | ||||
|         def write_fspath_result(self, fspath, res): | ||||
|             fspath = self.curdir.bestrelpath(fspath) | ||||
|             if fspath != self.currentfspath: | ||||
|                 self._tw.line() | ||||
|                 relpath = self.curdir.bestrelpath(fspath) | ||||
|                 self._tw.write(relpath + " ") | ||||
|                 self.currentfspath = fspath | ||||
|             self._tw.write(res) | ||||
|      | ||||
|         def write_ensure_prefix(self, prefix, extra="", **kwargs): | ||||
|             if self.currentfspath != prefix: | ||||
|                 self._tw.line() | ||||
|                 self.currentfspath = prefix  | ||||
|                 self._tw.write(prefix) | ||||
|             if extra: | ||||
|                 self._tw.write(extra, **kwargs) | ||||
|                 self.currentfspath = -2 | ||||
|      | ||||
|         def ensure_newline(self): | ||||
|             if self.currentfspath:  | ||||
|                 self._tw.line() | ||||
|                 self.currentfspath = None | ||||
|      | ||||
|         def write_line(self, line, **markup): | ||||
|             line = str(line) | ||||
|             self.ensure_newline() | ||||
|             self._tw.line(line, **markup) | ||||
|      | ||||
|         def write_sep(self, sep, title=None, **markup): | ||||
|             self.ensure_newline() | ||||
|             self._tw.sep(sep, title, **markup) | ||||
|      | ||||
|         def getcategoryletterword(self, rep): | ||||
|             res = self.config.hook.pytest_report_teststatus(rep=rep) | ||||
|             if res: | ||||
|                 return res | ||||
|             for cat in 'skipped failed passed ???'.split(): | ||||
|                 if getattr(rep, cat, None): | ||||
|                     break  | ||||
|             return cat, self.getoutcomeletter(rep), self.getoutcomeword(rep) | ||||
|      | ||||
|         def getoutcomeletter(self, rep): | ||||
|             return rep.shortrepr  | ||||
|      | ||||
|         def getoutcomeword(self, rep): | ||||
|             if rep.passed:  | ||||
|                 return "PASS", dict(green=True) | ||||
|             elif rep.failed:  | ||||
|                 return "FAIL", dict(red=True) | ||||
|             elif rep.skipped:  | ||||
|                 return "SKIP" | ||||
|             else:  | ||||
|                 return "???", dict(red=True) | ||||
|      | ||||
|         def pytest_internalerror(self, excrepr): | ||||
|             for line in str(excrepr).split("\n"): | ||||
|                 self.write_line("INTERNALERROR> " + line) | ||||
|      | ||||
|         def pyexecnet_gwmanage_newgateway(self, gateway, platinfo): | ||||
|             #self.write_line("%s instantiated gateway from spec %r" %(gateway.id, gateway.spec._spec)) | ||||
|             d = {} | ||||
|             d['version'] = repr_pythonversion(platinfo.version_info) | ||||
|             d['id'] = gateway.id | ||||
|             d['spec'] = gateway.spec._spec  | ||||
|             d['platform'] = platinfo.platform  | ||||
|             if self.config.option.verbose: | ||||
|                 d['extra'] = "- " + platinfo.executable | ||||
|             else: | ||||
|                 d['extra'] = "" | ||||
|             d['cwd'] = platinfo.cwd | ||||
|             infoline = ("%(id)s %(spec)s -- platform %(platform)s, " | ||||
|                             "Python %(version)s " | ||||
|                             "cwd: %(cwd)s" | ||||
|                             "%(extra)s" % d) | ||||
|             self.write_line(infoline) | ||||
|             self.gateway2info[gateway] = infoline | ||||
|      | ||||
|         def pyexecnet_gwmanage_rsyncstart(self, source, gateways): | ||||
|             targets = ", ".join([gw.id for gw in gateways]) | ||||
|             msg = "rsyncstart: %s -> %s" %(source, targets) | ||||
|             if not self.config.option.verbose: | ||||
|                 msg += " # use --verbose to see rsync progress" | ||||
|             self.write_line(msg) | ||||
|      | ||||
|         def pyexecnet_gwmanage_rsyncfinish(self, source, gateways): | ||||
|             targets = ", ".join([gw.id for gw in gateways]) | ||||
|             self.write_line("rsyncfinish: %s -> %s" %(source, targets)) | ||||
|      | ||||
|         def pytest_plugin_registered(self, plugin): | ||||
|             if self.config.option.traceconfig:  | ||||
|                 msg = "PLUGIN registered: %s" %(plugin,) | ||||
|                 # XXX this event may happen during setup/teardown time  | ||||
|                 #     which unfortunately captures our output here  | ||||
|                 #     which garbles our output if we use self.write_line  | ||||
|                 self.write_line(msg) | ||||
|      | ||||
|         def pytest_testnodeready(self, node): | ||||
|             self.write_line("%s txnode ready to receive tests" %(node.gateway.id,)) | ||||
|      | ||||
|         def pytest_testnodedown(self, node, error): | ||||
|             if error: | ||||
|                 self.write_line("%s node down, error: %s" %(node.gateway.id, error)) | ||||
|      | ||||
|         def pytest_trace(self, category, msg): | ||||
|             if self.config.option.debug or \ | ||||
|                self.config.option.traceconfig and category.find("config") != -1: | ||||
|                 self.write_line("[%s] %s" %(category, msg)) | ||||
|      | ||||
|         def pytest_rescheduleitems(self, items): | ||||
|             if self.config.option.debug: | ||||
|                 self.write_sep("!", "RESCHEDULING %s " %(items,)) | ||||
|      | ||||
|         def pytest_deselected(self, items): | ||||
|             self.stats.setdefault('deselected', []).append(items) | ||||
|      | ||||
|         def pytest_itemstart(self, item, node=None): | ||||
|             if self.config.option.dist != "no": | ||||
|                 # for dist-testing situations itemstart means we  | ||||
|                 # queued the item for sending, not interesting (unless debugging)  | ||||
|                 if self.config.option.debug: | ||||
|                     line = self._reportinfoline(item) | ||||
|                     extra = "" | ||||
|                     if node: | ||||
|                         extra = "-> " + str(node.gateway.id) | ||||
|                     self.write_ensure_prefix(line, extra) | ||||
|             else: | ||||
|                 if self.config.option.verbose: | ||||
|                     line = self._reportinfoline(item) | ||||
|                     self.write_ensure_prefix(line, "")  | ||||
|                 else: | ||||
|                     # ensure that the path is printed before the  | ||||
|                     # 1st test of a module starts running | ||||
|                     fspath, lineno, msg = self._getreportinfo(item) | ||||
|                     self.write_fspath_result(fspath, "") | ||||
|      | ||||
|         def pytest_runtest_logreport(self, rep): | ||||
|             if rep.passed and rep.when in ("setup", "teardown"): | ||||
|                 return  | ||||
|             fspath = rep.item.fspath  | ||||
|             cat, letter, word = self.getcategoryletterword(rep) | ||||
|             if isinstance(word, tuple): | ||||
|                 word, markup = word | ||||
|             else: | ||||
|                 markup = {} | ||||
|             self.stats.setdefault(cat, []).append(rep) | ||||
|             if not self.config.option.verbose: | ||||
|                 fspath, lineno, msg = self._getreportinfo(rep.item) | ||||
|                 self.write_fspath_result(fspath, letter) | ||||
|             else: | ||||
|                 line = self._reportinfoline(rep.item) | ||||
|                 if not hasattr(rep, 'node'): | ||||
|                     self.write_ensure_prefix(line, word, **markup) | ||||
|                 else: | ||||
|                     self.ensure_newline() | ||||
|                     if hasattr(rep, 'node'): | ||||
|                         self._tw.write("%s " % rep.node.gateway.id) | ||||
|                     self._tw.write(word, **markup) | ||||
|                     self._tw.write(" " + line) | ||||
|                     self.currentfspath = -2 | ||||
|      | ||||
|         def pytest_collectreport(self, rep): | ||||
|             if not rep.passed: | ||||
|                 if rep.failed: | ||||
|                     self.stats.setdefault("failed", []).append(rep) | ||||
|                     msg = rep.longrepr.reprcrash.message  | ||||
|                     self.write_fspath_result(rep.collector.fspath, "F") | ||||
|                 elif rep.skipped: | ||||
|                     self.stats.setdefault("skipped", []).append(rep) | ||||
|                     self.write_fspath_result(rep.collector.fspath, "S") | ||||
|      | ||||
|         def pytest_sessionstart(self, session): | ||||
|             self.write_sep("=", "test session starts", bold=True) | ||||
|             self._sessionstarttime = py.std.time.time() | ||||
|      | ||||
|             verinfo = ".".join(map(str, sys.version_info[:3])) | ||||
|             msg = "python: platform %s -- Python %s" % (sys.platform, verinfo) | ||||
|             if self.config.option.verbose or self.config.option.debug: | ||||
|                 msg += " -- " + str(sys.executable) | ||||
|                 msg += " -- pytest-%s" % (py.__version__) | ||||
|             self.write_line(msg) | ||||
|      | ||||
|             if self.config.option.debug or self.config.option.traceconfig: | ||||
|                 rev = py.__pkg__.getrev() | ||||
|                 self.write_line("using py lib: %s <rev %s>" % ( | ||||
|                                py.path.local(py.__file__).dirpath(), rev)) | ||||
|             if self.config.option.traceconfig: | ||||
|                 plugins = [] | ||||
|                 for plugin in self.config.pluginmanager.comregistry: | ||||
|                     name = plugin.__class__.__name__ | ||||
|                     if name.endswith("Plugin"): | ||||
|                         name = name[:-6] | ||||
|                         #if name == "Conftest": | ||||
|                         #    XXX get filename  | ||||
|                         plugins.append(name) | ||||
|                     else: | ||||
|                         plugins.append(str(plugin)) | ||||
|      | ||||
|                 plugins = ", ".join(plugins)  | ||||
|                 self.write_line("active plugins: %s" %(plugins,)) | ||||
|             for i, testarg in py.builtin.enumerate(self.config.args): | ||||
|                 self.write_line("test object %d: %s" %(i+1, testarg)) | ||||
|      | ||||
|         def pytest_sessionfinish(self, __call__, session, exitstatus): | ||||
|             __call__.execute()  | ||||
|             self._tw.line("") | ||||
|             if exitstatus in (0, 1, 2): | ||||
|                 self.summary_failures() | ||||
|                 self.summary_skips() | ||||
|                 self.config.hook.pytest_terminal_summary(terminalreporter=self) | ||||
|             if exitstatus == 2: | ||||
|                 self._report_keyboardinterrupt() | ||||
|             self.summary_deselected() | ||||
|             self.summary_stats() | ||||
|      | ||||
|         def pytest_keyboard_interrupt(self, excinfo): | ||||
|             self._keyboardinterrupt_memo = excinfo.getrepr() | ||||
|      | ||||
|         def _report_keyboardinterrupt(self): | ||||
|             self.write_sep("!", "KEYBOARD INTERRUPT") | ||||
|             excrepr = self._keyboardinterrupt_memo | ||||
|             if self.config.option.verbose: | ||||
|                 excrepr.toterminal(self._tw) | ||||
|             else: | ||||
|                 excrepr.reprcrash.toterminal(self._tw) | ||||
|      | ||||
|         def pytest_looponfailinfo(self, failreports, rootdirs): | ||||
|             if failreports: | ||||
|                 self.write_sep("#", "LOOPONFAILING", red=True) | ||||
|                 for report in failreports: | ||||
|                     try: | ||||
|                         loc = report.longrepr.reprcrash | ||||
|                     except AttributeError: | ||||
|                         loc = str(report.longrepr)[:50] | ||||
|                     self.write_line(loc, red=True) | ||||
|             self.write_sep("#", "waiting for changes") | ||||
|             for rootdir in rootdirs: | ||||
|                 self.write_line("### Watching:   %s" %(rootdir,), bold=True) | ||||
|      | ||||
|         def _reportinfoline(self, item): | ||||
|             fspath, lineno, msg = self._getreportinfo(item) | ||||
|             if fspath: | ||||
|                 fspath = self.curdir.bestrelpath(fspath) | ||||
|             if lineno is not None: | ||||
|                 lineno += 1 | ||||
|             if fspath and lineno and msg: | ||||
|                 line = "%(fspath)s:%(lineno)s: %(msg)s" | ||||
|             elif fspath and msg: | ||||
|                 line = "%(fspath)s: %(msg)s" | ||||
|             elif fspath and lineno: | ||||
|                 line = "%(fspath)s:%(lineno)s" | ||||
|             else: | ||||
|                 line = "[noreportinfo]" | ||||
|             return line % locals() + " " | ||||
|              | ||||
|         def _getfailureheadline(self, rep): | ||||
|             if hasattr(rep, "collector"): | ||||
|                 return str(rep.collector.fspath) | ||||
|             else: | ||||
|                 fspath, lineno, msg = self._getreportinfo(rep.item) | ||||
|                 return msg | ||||
|      | ||||
|         def _getreportinfo(self, item): | ||||
|             try: | ||||
|                 return item.__reportinfo | ||||
|             except AttributeError: | ||||
|                 pass | ||||
|             reportinfo = item.config.hook.pytest_report_iteminfo(item=item) | ||||
|             # cache on item | ||||
|             item.__reportinfo = reportinfo | ||||
|             return reportinfo | ||||
|      | ||||
|         # | ||||
|         # summaries for sessionfinish  | ||||
|         # | ||||
|      | ||||
|         def summary_failures(self): | ||||
|             if 'failed' in self.stats and self.config.option.tbstyle != "no": | ||||
|                 self.write_sep("=", "FAILURES") | ||||
|                 for rep in self.stats['failed']: | ||||
|                     msg = self._getfailureheadline(rep) | ||||
|                     self.write_sep("_", msg) | ||||
|                     if hasattr(rep, 'node'): | ||||
|                         self.write_line(self.gateway2info.get( | ||||
|                             rep.node.gateway, "node %r (platinfo not found? strange)") | ||||
|                                 [:self._tw.fullwidth-1]) | ||||
|                     rep.toterminal(self._tw) | ||||
|      | ||||
|         def summary_stats(self): | ||||
|             session_duration = py.std.time.time() - self._sessionstarttime | ||||
|      | ||||
|             keys = "failed passed skipped deselected".split() | ||||
|             parts = [] | ||||
|             for key in keys: | ||||
|                 val = self.stats.get(key, None) | ||||
|                 if val: | ||||
|                     parts.append("%d %s" %(len(val), key)) | ||||
|             line = ", ".join(parts) | ||||
|             # XXX coloring | ||||
|             self.write_sep("=", "%s in %.2f seconds" %(line, session_duration)) | ||||
|      | ||||
|         def summary_deselected(self): | ||||
|             if 'deselected' in self.stats: | ||||
|                 self.write_sep("=", "%d tests deselected by %r" %( | ||||
|                     len(self.stats['deselected']), self.config.option.keyword), bold=True) | ||||
|      | ||||
|         def summary_skips(self): | ||||
|             if 'skipped' in self.stats: | ||||
|                 if 'failed' not in self.stats: #  or self.config.option.showskipsummary: | ||||
|                     fskips = folded_skips(self.stats['skipped']) | ||||
|                     if fskips: | ||||
|                         self.write_sep("_", "skipped test summary") | ||||
|                         for num, fspath, lineno, reason in fskips: | ||||
|                             self._tw.line("%s:%d: [%d] %s" %(fspath, lineno, num, reason)) | ||||
|      | ||||
|     class CollectonlyReporter: | ||||
|         INDENT = "  " | ||||
|      | ||||
|         def __init__(self, config, out=None): | ||||
|             self.config = config  | ||||
|             if out is None: | ||||
|                 out = py.std.sys.stdout | ||||
|             self.out = py.io.TerminalWriter(out) | ||||
|             self.indent = "" | ||||
|             self._failed = [] | ||||
|      | ||||
|         def outindent(self, line): | ||||
|             self.out.line(self.indent + str(line)) | ||||
|      | ||||
|         def pytest_internalerror(self, excrepr): | ||||
|             for line in str(excrepr).split("\n"): | ||||
|                 self.out.line("INTERNALERROR> " + line) | ||||
|      | ||||
|         def pytest_collectstart(self, collector): | ||||
|             self.outindent(collector) | ||||
|             self.indent += self.INDENT  | ||||
|          | ||||
|         def pytest_itemstart(self, item, node=None): | ||||
|             self.outindent(item) | ||||
|      | ||||
|         def pytest_collectreport(self, rep): | ||||
|             if not rep.passed: | ||||
|                 self.outindent("!!! %s !!!" % rep.longrepr.reprcrash.message) | ||||
|                 self._failed.append(rep) | ||||
|             self.indent = self.indent[:-len(self.INDENT)] | ||||
|      | ||||
|         def pytest_sessionfinish(self, session, exitstatus): | ||||
|             if self._failed: | ||||
|                 self.out.sep("!", "collection failures") | ||||
|             for rep in self._failed: | ||||
|                 rep.toterminal(self.out) | ||||
|                      | ||||
|     def folded_skips(skipped): | ||||
|         d = {} | ||||
|         for event in skipped: | ||||
|             entry = event.longrepr.reprcrash  | ||||
|             key = entry.path, entry.lineno, entry.message | ||||
|             d.setdefault(key, []).append(event) | ||||
|         l = [] | ||||
|         for key, events in d.iteritems():  | ||||
|             l.append((len(events),) + key) | ||||
|         return l  | ||||
|      | ||||
|     def repr_pythonversion(v=None): | ||||
|         if v is None: | ||||
|             v = sys.version_info | ||||
|         try: | ||||
|             return "%s.%s.%s-%s-%s" % v | ||||
|         except (TypeError, ValueError): | ||||
|             return str(v) | ||||
| 
 | ||||
| .. _`pytest_terminal.py`: http://bitbucket.org/hpk42/py-trunk/raw/ea1f958813ebbff45161fdb468a6204be5396112/py/test/plugin/pytest_terminal.py | ||||
| .. _`pytest_terminal.py`: http://bitbucket.org/hpk42/py-trunk/raw/85fe614ab05f301f206935d11a477df184cbbce6/py/test/plugin/pytest_terminal.py | ||||
| .. _`extend`: ../extend.html | ||||
| .. _`plugins`: index.html | ||||
| .. _`contact`: ../../contact.html | ||||
|  |  | |||
|  | @ -4,6 +4,9 @@ pytest_unittest plugin | |||
| 
 | ||||
| automatically discover and run traditional "unittest.py" style tests. | ||||
| 
 | ||||
| .. contents:: | ||||
|   :local: | ||||
| 
 | ||||
| Usage | ||||
| ---------------- | ||||
| 
 | ||||
|  | @ -16,146 +19,19 @@ This plugin is enabled by default. | |||
| 
 | ||||
| .. _`unittest.py style`: http://docs.python.org/library/unittest.html | ||||
| 
 | ||||
| Getting and improving this plugin | ||||
| --------------------------------- | ||||
| Start improving this plugin in 30 seconds | ||||
| ========================================= | ||||
| 
 | ||||
| 
 | ||||
| Do you find the above documentation or the plugin itself lacking, | ||||
| not fit for what you need?  Here is a **30 seconds guide** | ||||
| to get you started on improving the plugin: | ||||
| Do you find the above documentation or the plugin itself lacking?  | ||||
| 
 | ||||
| 1. Download `pytest_unittest.py`_ plugin source code  | ||||
| 2. put it somewhere as ``pytest_unittest.py`` into your import path  | ||||
| 3. a subsequent test run will now use your local version!  | ||||
| 3. a subsequent ``py.test`` run will use your local version | ||||
| 
 | ||||
| Further information: extend_ documentation, other plugins_ or contact_.   | ||||
| 
 | ||||
| For your convenience here is also an inlined version of ``pytest_unittest.py``: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     """ | ||||
|     automatically discover and run traditional "unittest.py" style tests.  | ||||
|      | ||||
|     Usage | ||||
|     ---------------- | ||||
|      | ||||
|     This plugin collects and runs Python `unittest.py style`_ tests.  | ||||
|     It will automatically collect ``unittest.TestCase`` subclasses  | ||||
|     and their ``test`` methods from the test modules of a project | ||||
|     (usually following the ``test_*.py`` pattern).  | ||||
|      | ||||
|     This plugin is enabled by default.  | ||||
|      | ||||
|     .. _`unittest.py style`: http://docs.python.org/library/unittest.html | ||||
|     """ | ||||
|     import py | ||||
|     import sys | ||||
|      | ||||
|     def pytest_pycollect_makeitem(collector, name, obj): | ||||
|         if 'unittest' not in sys.modules: | ||||
|             return # nobody could have possibly derived a subclass  | ||||
|         if py.std.inspect.isclass(obj) and issubclass(obj, py.std.unittest.TestCase): | ||||
|             return UnitTestCase(name, parent=collector) | ||||
|      | ||||
|     class UnitTestCase(py.test.collect.Class): | ||||
|         def collect(self): | ||||
|             return [UnitTestCaseInstance("()", self)] | ||||
|      | ||||
|         def setup(self): | ||||
|             pass | ||||
|      | ||||
|         def teardown(self): | ||||
|             pass | ||||
|      | ||||
|     _dummy = object() | ||||
|     class UnitTestCaseInstance(py.test.collect.Instance): | ||||
|         def collect(self): | ||||
|             loader = py.std.unittest.TestLoader() | ||||
|             names = loader.getTestCaseNames(self.obj.__class__) | ||||
|             l = [] | ||||
|             for name in names: | ||||
|                 callobj = getattr(self.obj, name) | ||||
|                 if callable(callobj): | ||||
|                     l.append(UnitTestFunction(name, parent=self)) | ||||
|             return l | ||||
|      | ||||
|         def _getobj(self): | ||||
|             x = self.parent.obj | ||||
|             return self.parent.obj(methodName='run') | ||||
|              | ||||
|     class UnitTestFunction(py.test.collect.Function): | ||||
|         def __init__(self, name, parent, args=(), obj=_dummy, sort_value=None): | ||||
|             super(UnitTestFunction, self).__init__(name, parent) | ||||
|             self._args = args | ||||
|             if obj is not _dummy: | ||||
|                 self._obj = obj | ||||
|             self._sort_value = sort_value | ||||
|      | ||||
|         def runtest(self): | ||||
|             target = self.obj | ||||
|             args = self._args | ||||
|             target(*args) | ||||
|      | ||||
|         def setup(self): | ||||
|             instance = self.obj.im_self | ||||
|             instance.setUp() | ||||
|      | ||||
|         def teardown(self): | ||||
|             instance = self.obj.im_self | ||||
|             instance.tearDown() | ||||
|      | ||||
|      | ||||
|     def test_simple_unittest(testdir): | ||||
|         testpath = testdir.makepyfile(""" | ||||
|             import unittest | ||||
|             pytest_plugins = "pytest_unittest" | ||||
|             class MyTestCase(unittest.TestCase): | ||||
|                 def testpassing(self): | ||||
|                     self.assertEquals('foo', 'foo') | ||||
|                 def test_failing(self): | ||||
|                     self.assertEquals('foo', 'bar') | ||||
|         """) | ||||
|         reprec = testdir.inline_run(testpath) | ||||
|         assert reprec.matchreport("testpassing").passed | ||||
|         assert reprec.matchreport("test_failing").failed  | ||||
|      | ||||
|     def test_setup(testdir): | ||||
|         testpath = testdir.makepyfile(test_two=""" | ||||
|             import unittest | ||||
|             pytest_plugins = "pytest_unittest" # XXX  | ||||
|             class MyTestCase(unittest.TestCase): | ||||
|                 def setUp(self): | ||||
|                     self.foo = 1 | ||||
|                 def test_setUp(self): | ||||
|                     self.assertEquals(1, self.foo) | ||||
|         """) | ||||
|         reprec = testdir.inline_run(testpath) | ||||
|         rep = reprec.matchreport("test_setUp") | ||||
|         assert rep.passed | ||||
|      | ||||
|     def test_teardown(testdir): | ||||
|         testpath = testdir.makepyfile(test_three=""" | ||||
|             import unittest | ||||
|             pytest_plugins = "pytest_unittest" # XXX  | ||||
|             class MyTestCase(unittest.TestCase): | ||||
|                 l = [] | ||||
|                 def test_one(self): | ||||
|                     pass | ||||
|                 def tearDown(self): | ||||
|                     self.l.append(None) | ||||
|             class Second(unittest.TestCase): | ||||
|                 def test_check(self): | ||||
|                     self.assertEquals(MyTestCase.l, [None]) | ||||
|         """) | ||||
|         reprec = testdir.inline_run(testpath) | ||||
|         passed, skipped, failed = reprec.countoutcomes() | ||||
|         print "COUNTS", passed, skipped, failed | ||||
|         assert failed == 0, failed | ||||
|         assert passed == 2 | ||||
|         assert passed + skipped + failed == 2 | ||||
| 
 | ||||
| .. _`pytest_unittest.py`: http://bitbucket.org/hpk42/py-trunk/raw/ea1f958813ebbff45161fdb468a6204be5396112/py/test/plugin/pytest_unittest.py | ||||
| .. _`pytest_unittest.py`: http://bitbucket.org/hpk42/py-trunk/raw/85fe614ab05f301f206935d11a477df184cbbce6/py/test/plugin/pytest_unittest.py | ||||
| .. _`extend`: ../extend.html | ||||
| .. _`plugins`: index.html | ||||
| .. _`contact`: ../../contact.html | ||||
|  |  | |||
|  | @ -4,6 +4,9 @@ pytest_xfail plugin | |||
| 
 | ||||
| mark python tests as expected-to-fail and report them separately. | ||||
| 
 | ||||
| .. contents:: | ||||
|   :local: | ||||
| 
 | ||||
| usage | ||||
| ------------ | ||||
| 
 | ||||
|  | @ -18,116 +21,19 @@ This test will be executed but no traceback will be reported | |||
| when it fails. Instead terminal reporting will list it in the  | ||||
| "expected to fail" section or "unexpectedly passing" section. | ||||
| 
 | ||||
| Getting and improving this plugin | ||||
| --------------------------------- | ||||
| Start improving this plugin in 30 seconds | ||||
| ========================================= | ||||
| 
 | ||||
| 
 | ||||
| Do you find the above documentation or the plugin itself lacking, | ||||
| not fit for what you need?  Here is a **30 seconds guide** | ||||
| to get you started on improving the plugin: | ||||
| Do you find the above documentation or the plugin itself lacking?  | ||||
| 
 | ||||
| 1. Download `pytest_xfail.py`_ plugin source code  | ||||
| 2. put it somewhere as ``pytest_xfail.py`` into your import path  | ||||
| 3. a subsequent test run will now use your local version!  | ||||
| 3. a subsequent ``py.test`` run will use your local version | ||||
| 
 | ||||
| Further information: extend_ documentation, other plugins_ or contact_.   | ||||
| 
 | ||||
| For your convenience here is also an inlined version of ``pytest_xfail.py``: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     """ | ||||
|     mark python tests as expected-to-fail and report them separately.  | ||||
|      | ||||
|     usage | ||||
|     ------------ | ||||
|      | ||||
|     Use the generic mark decorator to add the 'xfail' keyword to your  | ||||
|     test function:: | ||||
|      | ||||
|         @py.test.mark.xfail | ||||
|         def test_hello(): | ||||
|             ... | ||||
|      | ||||
|     This test will be executed but no traceback will be reported  | ||||
|     when it fails. Instead terminal reporting will list it in the  | ||||
|     "expected to fail" section or "unexpectedly passing" section.   | ||||
|     """ | ||||
|      | ||||
|     import py | ||||
|      | ||||
|     pytest_plugins = ['keyword'] | ||||
|      | ||||
|     def pytest_runtest_makereport(__call__, item, call): | ||||
|         if call.when != "call": | ||||
|             return | ||||
|         if hasattr(item, 'obj') and hasattr(item.obj, 'func_dict'): | ||||
|             if 'xfail' in item.obj.func_dict: | ||||
|                 res = __call__.execute(firstresult=True) | ||||
|                 if call.excinfo: | ||||
|                     res.skipped = True | ||||
|                     res.failed = res.passed = False | ||||
|                 else: | ||||
|                     res.skipped = res.passed = False | ||||
|                     res.failed = True | ||||
|                 return res  | ||||
|      | ||||
|     def pytest_report_teststatus(rep): | ||||
|         """ return shortletter and verbose word. """ | ||||
|         if 'xfail' in rep.keywords:  | ||||
|             if rep.skipped: | ||||
|                 return "xfailed", "x", "xfail" | ||||
|             elif rep.failed: | ||||
|                 return "xpassed", "P", "xpass"  | ||||
|      | ||||
|     # called by the terminalreporter instance/plugin | ||||
|     def pytest_terminal_summary(terminalreporter): | ||||
|         tr = terminalreporter | ||||
|         xfailed = tr.stats.get("xfailed") | ||||
|         if xfailed: | ||||
|             tr.write_sep("_", "expected failures") | ||||
|             for event in xfailed: | ||||
|                 entry = event.longrepr.reprcrash  | ||||
|                 key = entry.path, entry.lineno, entry.message | ||||
|                 reason = event.longrepr.reprcrash.message | ||||
|                 modpath = event.item.getmodpath(includemodule=True) | ||||
|                 #tr._tw.line("%s %s:%d: %s" %(modpath, entry.path, entry.lineno, entry.message)) | ||||
|                 tr._tw.line("%s %s:%d: " %(modpath, entry.path, entry.lineno)) | ||||
|      | ||||
|         xpassed = terminalreporter.stats.get("xpassed") | ||||
|         if xpassed: | ||||
|             tr.write_sep("_", "UNEXPECTEDLY PASSING TESTS") | ||||
|             for event in xpassed: | ||||
|                 tr._tw.line("%s: xpassed" %(event.item,)) | ||||
|      | ||||
|      | ||||
|     # =============================================================================== | ||||
|     # | ||||
|     # plugin tests  | ||||
|     # | ||||
|     # =============================================================================== | ||||
|      | ||||
|     def test_xfail(testdir, linecomp): | ||||
|         p = testdir.makepyfile(test_one=""" | ||||
|             import py | ||||
|             @py.test.mark.xfail | ||||
|             def test_this(): | ||||
|                 assert 0 | ||||
|      | ||||
|             @py.test.mark.xfail | ||||
|             def test_that(): | ||||
|                 assert 1 | ||||
|         """) | ||||
|         result = testdir.runpytest(p) | ||||
|         extra = result.stdout.fnmatch_lines([ | ||||
|             "*expected failures*", | ||||
|             "*test_one.test_this*test_one.py:4*", | ||||
|             "*UNEXPECTEDLY PASSING*", | ||||
|             "*test_that*", | ||||
|         ]) | ||||
|         assert result.ret == 1 | ||||
| 
 | ||||
| .. _`pytest_xfail.py`: http://bitbucket.org/hpk42/py-trunk/raw/ea1f958813ebbff45161fdb468a6204be5396112/py/test/plugin/pytest_xfail.py | ||||
| .. _`pytest_xfail.py`: http://bitbucket.org/hpk42/py-trunk/raw/85fe614ab05f301f206935d11a477df184cbbce6/py/test/plugin/pytest_xfail.py | ||||
| .. _`extend`: ../extend.html | ||||
| .. _`plugins`: index.html | ||||
| .. _`contact`: ../../contact.html | ||||
|  |  | |||
|  | @ -10,9 +10,10 @@ plugins = [ | |||
|             'unittest doctest oejskit restdoc'), | ||||
|     ('Plugins for generic reporting and failure logging',  | ||||
|             'pocoo resultlog terminal',), | ||||
|     ('internal plugins / core functionality',  | ||||
|         'pdb keyword hooklog runner execnetcleanup pytester', | ||||
|     ) | ||||
|     #('internal plugins / core functionality',  | ||||
|     #    #'pdb keyword hooklog runner execnetcleanup # pytester', | ||||
|     #    'pdb keyword hooklog runner execnetcleanup' # pytester', | ||||
|     #) | ||||
| ] | ||||
| 
 | ||||
| externals = { | ||||
|  | @ -152,6 +153,9 @@ class PluginDoc(RestWriter): | |||
|         self.h1("%s plugin" % self.name) # : %s" %(self.name, self.oneliner)) | ||||
|         self.Print(self.oneliner) | ||||
|         self.Print() | ||||
|         self.Print(".. contents::") | ||||
|         self.Print("  :local:") | ||||
|         self.Print() | ||||
| 
 | ||||
|         self.Print(moduledoc) | ||||
|      | ||||
|  | @ -170,15 +174,13 @@ class PluginDoc(RestWriter): | |||
|         #self.links.append((basename,  | ||||
|         #    "http://bitbucket.org/hpk42/py-trunk/src/tip/py/test/plugin/" + | ||||
|         #    basename)) | ||||
|         self.h2("Getting and improving this plugin") | ||||
|         self.h1("Start improving this plugin in 30 seconds") | ||||
|         self.para(py.code.Source(""" | ||||
|             Do you find the above documentation or the plugin itself lacking, | ||||
|             not fit for what you need?  Here is a **30 seconds guide** | ||||
|             to get you started on improving the plugin: | ||||
|             Do you find the above documentation or the plugin itself lacking?  | ||||
| 
 | ||||
|             1. Download `%s`_ plugin source code  | ||||
|             2. put it somewhere as ``%s`` into your import path  | ||||
|             3. a subsequent test run will now use your local version!  | ||||
|             3. a subsequent ``py.test`` run will use your local version | ||||
| 
 | ||||
|             Further information: extend_ documentation, other plugins_ or contact_.   | ||||
|         """ % (basename, basename))) | ||||
|  | @ -194,14 +196,15 @@ class PluginDoc(RestWriter): | |||
|         self.links.append(('contact', '../../contact.html')) | ||||
|         self.links.append(('checkout the py.test development version',  | ||||
|             '../../download.html#checkout')) | ||||
|          | ||||
|         #self.h2("plugin source code")  | ||||
|         self.Print() | ||||
|         self.para("For your convenience here is also an inlined version " | ||||
|                   "of ``%s``:" %basename) | ||||
|         #self(or copy-paste from below) | ||||
|         self.Print() | ||||
|         self.sourcecode(py.code.Source(plugin)) | ||||
|         | ||||
|         if 0: # this breaks the page layout and makes large doc files | ||||
|             #self.h2("plugin source code")  | ||||
|             self.Print() | ||||
|             self.para("For your convenience here is also an inlined version " | ||||
|                       "of ``%s``:" %basename) | ||||
|             #self(or copy-paste from below) | ||||
|             self.Print() | ||||
|             self.sourcecode(py.code.Source(plugin)) | ||||
| 
 | ||||
|     def emit_funcargs(self, plugin): | ||||
|         funcargfuncs = [] | ||||
|  | @ -213,6 +216,7 @@ class PluginDoc(RestWriter): | |||
|             return | ||||
|         for func in funcargfuncs: | ||||
|             argname = func.__name__[len(prefix):] | ||||
|             self.Print() | ||||
|             self.Print(".. _`%s funcarg`:" % argname) | ||||
|             self.Print() | ||||
|             self.h2("the %r test function argument" % argname) | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| """ | ||||
| convenient capturing of writes to stdout/stderror streams  | ||||
| and file descriptors.  | ||||
| convenient capturing of writes to stdout/stderror streams and file descriptors.  | ||||
| 
 | ||||
| Example Usage | ||||
| ---------------------- | ||||
|  |  | |||
|  | @ -1,17 +1,46 @@ | |||
| """ | ||||
| helpers for asserting deprecation and other warnings.  | ||||
| 
 | ||||
| **recwarn**: function argument where one can call recwarn.pop() to get | ||||
| the last warning that would have been shown.  | ||||
| Example usage  | ||||
| --------------------- | ||||
| 
 | ||||
| **py.test.deprecated_call(func, *args, **kwargs)**: assert that the given function call triggers a deprecation warning.  | ||||
| You can use the ``recwarn`` funcarg to track  | ||||
| warnings within a test function: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     def test_hello(recwarn): | ||||
|         from warnings import warn | ||||
|         warn("hello", DeprecationWarning) | ||||
|         w = recwarn.pop(DeprecationWarning) | ||||
|         assert issubclass(w.category, DeprecationWarning) | ||||
|         assert 'hello' in str(w.message) | ||||
|         assert w.filename | ||||
|         assert w.lineno | ||||
| 
 | ||||
| You can also call a global helper for checking | ||||
| taht a certain function call yields a Deprecation | ||||
| warning: | ||||
| 
 | ||||
| .. sourcecode:: python | ||||
| 
 | ||||
|     import py | ||||
|              | ||||
|     def test_global(): | ||||
|         py.test.deprecated_call(myfunction, 17) | ||||
|          | ||||
|          | ||||
| """ | ||||
| 
 | ||||
| import py | ||||
| import os | ||||
| 
 | ||||
| def pytest_funcarg__recwarn(request): | ||||
|     """ check that warnings have been raised. """  | ||||
|     """Return a WarningsRecorder instance that provides these methods: | ||||
| 
 | ||||
|     * ``pop(category=None)``: return last warning matching the category. | ||||
|     * ``clear()``: clear list of warnings  | ||||
|     """ | ||||
|     warnings = WarningsRecorder() | ||||
|     request.addfinalizer(warnings.finalize) | ||||
|     return warnings | ||||
|  |  | |||
|  | @ -1,5 +1,7 @@ | |||
| """ | ||||
| terminal reporting of the full testing process. | ||||
| Implements terminal reporting of the full testing process. | ||||
| 
 | ||||
| This is a good source for looking at the various reporting hooks.  | ||||
| """ | ||||
| import py | ||||
| import sys | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue