From b2cb93e06d4f8bf6415e282ac01c55209029fb74 Mon Sep 17 00:00:00 2001 From: holger krekel Date: Mon, 22 Apr 2013 10:35:48 +0200 Subject: [PATCH] allow re-running of a test item (as exercised by the pytest-rerunfailures plugins) by re-initializing and removing request/funcargs information in runtestprotocol() - which is a slightly odd place to add funcarg-related functionality but it allows all pytest_runtest_setup/teardown hooks to properly see a valid request/funcarg content on test items. --- CHANGELOG | 3 +++ _pytest/__init__.py | 2 +- _pytest/python.py | 17 +++++++++++------ _pytest/runner.py | 8 ++++++++ setup.py | 2 +- testing/python/integration.py | 32 ++++++++++++++++++++++++++++++++ testing/test_tmpdir.py | 1 + 7 files changed, 57 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 38031a3c3..4d3d99b9f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ Changes between 2.3.4 and 2.3.5dev ----------------------------------- +- allow re-running of test items / helps to fix pytest-reruntests plugin + and also should help to keep less fixture/resource references alive + - put captured stdout/stderr into junitxml output even for passing tests (thanks Adam Goucher) diff --git a/_pytest/__init__.py b/_pytest/__init__.py index 56e8690af..c1b1da136 100644 --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.5.dev8' +__version__ = '2.3.5.dev16' diff --git a/_pytest/python.py b/_pytest/python.py index 22f61aec0..34e7fffbd 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -917,20 +917,25 @@ class Function(FunctionMixin, pytest.Item, FuncargnamesCompatAttr): self.cls, funcargs=not isyield) self.fixturenames = fi.names_closure - if isyield: - assert not callspec, ( + if callspec is not None: + self.callspec = callspec + self._initrequest() + + def _initrequest(self): + if self._isyieldedfunction(): + assert not hasattr(self, "callspec"), ( "yielded functions (deprecated) cannot have funcargs") self.funcargs = {} else: - if callspec is not None: - self.callspec = callspec - self.funcargs = callspec.funcargs or {} + if hasattr(self, "callspec"): + callspec = self.callspec + self.funcargs = callspec.funcargs.copy() self._genid = callspec.id if hasattr(callspec, "param"): self.param = callspec.param else: self.funcargs = {} - self._request = req = FixtureRequest(self) + self._request = FixtureRequest(self) @property def function(self): diff --git a/_pytest/runner.py b/_pytest/runner.py index 6014d6d9d..27f9c5f90 100644 --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -63,12 +63,20 @@ def pytest_runtest_protocol(item, nextitem): return True def runtestprotocol(item, log=True, nextitem=None): + hasrequest = hasattr(item, "_request") + if hasrequest and not item._request: + item._initrequest() 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, nextitem=nextitem)) + # after all teardown hooks have been called + # want funcargs and request info to go away + if hasrequest: + item._request = False + item.funcargs = None return reports def pytest_runtest_setup(item): diff --git a/setup.py b/setup.py index 946495ec6..8563872cd 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ def main(): name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.5.dev8', + version='2.3.5.dev16', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff --git a/testing/python/integration.py b/testing/python/integration.py index a191254c6..c716c7ceb 100644 --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -117,3 +117,35 @@ class TestMockDecoration: """) reprec = testdir.inline_run() reprec.assertoutcome(passed=2) + + +class TestReRunTests: + def test_rerun(self, testdir): + testdir.makeconftest(""" + from _pytest.runner import runtestprotocol + def pytest_runtest_protocol(item, nextitem): + runtestprotocol(item, log=False, nextitem=nextitem) + runtestprotocol(item, log=True, nextitem=nextitem) + """) + testdir.makepyfile(""" + import pytest + count = 0 + req = None + @pytest.fixture + def fix(request): + global count, req + assert request != req + req = request + print ("fix count %s" % count) + count += 1 + def test_fix(fix): + pass + """) + result = testdir.runpytest("-s") + result.stdout.fnmatch_lines(""" + *fix count 0* + *fix count 1* + """) + result.stdout.fnmatch_lines(""" + *2 passed* + """) diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 31dd1776d..164ee68c6 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -16,6 +16,7 @@ def test_funcarg(testdir): # pytest_unconfigure has deleted the TempdirHandler already config = item.config config._tmpdirhandler = TempdirHandler(config) + item._initrequest() p = tmpdir(item._request) assert p.check() bn = p.basename.strip("0123456789")