[py-svn] py-trunk commit 91cf219b3d46: fix sessionstart/sessionfinish handling at the slave side, set "session.nodeid" to id of the slave and make sure "final" teardown failures are reported nicely. fixes issue66.
commits-noreply at bitbucket.org
commits-noreply at bitbucket.org
Mon Jan 11 17:16:56 CET 2010
# HG changeset patch -- Bitbucket.org
# Project py-trunk
# URL http://bitbucket.org/hpk42/py-trunk/overview/
# User holger krekel <holger at merlinux.eu>
# Date 1263226147 -3600
# Node ID 91cf219b3d4659119d7e7df3162c1de0e6332b69
# Parent 0d2090ccd84c7180d64e051c2c23a2f8ee00261d
fix sessionstart/sessionfinish handling at the slave side, set "session.nodeid" to id of the slave and make sure "final" teardown failures are reported nicely. fixes issue66.
--- a/ISSUES.txt
+++ b/ISSUES.txt
@@ -42,16 +42,6 @@ test: testing/pytest/dist/test_dsession.
Call gateway group termination with a small timeout if available.
Should make dist-testing less likely to leave lost processes.
-dist-testing: fix session hook / setup calling
------------------------------------------------------
-tags: bug 1.2
-
-Currently pytest_sessionstart and finish are called
-on the master node and not on the slaves. Call
-it on slaves and provide a session.nodeid which defaults
-to None for the master and contains the gateway id
-for slaves.
-
have --report=xfailed[-detail] report the actual tracebacks
------------------------------------------------------------------
tags: feature
--- a/py/impl/test/dist/txnode.py
+++ b/py/impl/test/dist/txnode.py
@@ -3,6 +3,7 @@
"""
import py
from py.impl.test.dist.mypickle import PickleChannel
+from py.impl.test import outcome
class TXNode(object):
""" Represents a Test Execution environment in the controlling process.
@@ -55,12 +56,12 @@ class TXNode(object):
elif eventname == "slavefinished":
self._down = True
self.notify("pytest_testnodedown", error=None, node=self)
- elif eventname == "pytest_runtest_logreport":
- rep = kwargs['report']
- rep.node = self
- self.notify("pytest_runtest_logreport", report=rep)
+ elif eventname in ("pytest_runtest_logreport",
+ "pytest__teardown_final_logerror"):
+ kwargs['report'].node = self
+ self.notify(eventname, **kwargs)
else:
- self.notify(eventname, *args, **kwargs)
+ self.notify(eventname, **kwargs)
except KeyboardInterrupt:
# should not land in receiver-thread
raise
@@ -99,7 +100,7 @@ def install_slave(gateway, config):
basetemp = py.path.local.make_numbered_dir(prefix="slave-",
keep=0, rootdir=popenbase)
basetemp = str(basetemp)
- channel.send((config, basetemp))
+ channel.send((config, basetemp, gateway.id))
return channel
class SlaveNode(object):
@@ -115,9 +116,12 @@ class SlaveNode(object):
def pytest_runtest_logreport(self, report):
self.sendevent("pytest_runtest_logreport", report=report)
+ def pytest__teardown_final_logerror(self, report):
+ self.sendevent("pytest__teardown_final_logerror", report=report)
+
def run(self):
channel = self.channel
- self.config, basetemp = channel.receive()
+ self.config, basetemp, self.nodeid = channel.receive()
if basetemp:
self.config.basetemp = py.path.local(basetemp)
self.config.pluginmanager.do_configure(self.config)
@@ -125,22 +129,27 @@ class SlaveNode(object):
self.runner = self.config.pluginmanager.getplugin("pytest_runner")
self.sendevent("slaveready")
try:
+ self.config.hook.pytest_sessionstart(session=self)
while 1:
task = channel.receive()
if task is None:
- self.sendevent("slavefinished")
break
if isinstance(task, list):
for item in task:
self.run_single(item=item)
else:
self.run_single(item=task)
+ self.config.hook.pytest_sessionfinish(
+ session=self,
+ exitstatus=outcome.EXIT_OK)
except KeyboardInterrupt:
raise
except:
er = py.code.ExceptionInfo().getrepr(funcargs=True, showlocals=True)
self.sendevent("pytest_internalerror", excrepr=er)
raise
+ else:
+ self.sendevent("slavefinished")
def run_single(self, item):
call = self.runner.CallInfo(item._checkcollectable, when='setup')
--- a/py/impl/test/dist/dsession.py
+++ b/py/impl/test/dist/dsession.py
@@ -127,6 +127,12 @@ class DSession(Session):
elif eventname == "pytest_runtest_logreport":
# might be some teardown report
self.config.hook.pytest_runtest_logreport(**kwargs)
+ elif eventname == "pytest_internalerror":
+ self.config.hook.pytest_internalerror(**kwargs)
+ loopstate.exitstatus = outcome.EXIT_INTERNALERROR
+ elif eventname == "pytest__teardown_final_logerror":
+ self.config.hook.pytest__teardown_final_logerror(**kwargs)
+ loopstate.exitstatus = outcome.EXIT_TESTSFAILED
if not self.node2pending:
# finished
if loopstate.testsfailed:
--- a/py/plugin/pytest_runner.py
+++ b/py/plugin/pytest_runner.py
@@ -68,6 +68,8 @@ def pytest_runtest_teardown(item):
def pytest__teardown_final(session):
call = CallInfo(session.config._setupstate.teardown_all, when="teardown")
if call.excinfo:
+ ntraceback = call.excinfo.traceback .cut(excludepath=py._pydir)
+ call.excinfo.traceback = ntraceback.filter()
rep = TeardownErrorReport(call.excinfo)
return rep
--- a/py/impl/test/session.py
+++ b/py/impl/test/session.py
@@ -13,10 +13,7 @@ Item = py.test.collect.Item
Collector = py.test.collect.Collector
class Session(object):
- """
- Session drives the collection and running of tests
- and generates test events for reporters.
- """
+ nodeid = ""
def __init__(self, config):
self.config = config
self.pluginmanager = config.pluginmanager # shortcut
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -64,6 +64,10 @@ Changes between 1.X and 1.1.1
- fix assert reinterpreation that sees a call containing "keyword=..."
+- fix issue66: invoke pytest_sessionstart and pytest_sessionfinish
+ hooks on slaves during dist-testing, report module/session teardown
+ hooks correctly.
+
- fix issue65: properly handle dist-testing if no
execnet/py lib installed remotely.
--- a/testing/pytest/dist/test_dsession.py
+++ b/testing/pytest/dist/test_dsession.py
@@ -440,19 +440,66 @@ def test_teardownfails_one_function(test
"*1 passed*1 error*"
])
- at py.test.mark.xfail
+ at py.test.mark.xfail
def test_terminate_on_hangingnode(testdir):
p = testdir.makeconftest("""
def pytest__teardown_final(session):
- if session.nodeid: # running on slave
+ if session.nodeid == "my": # running on slave
import time
- time.sleep(2)
+ time.sleep(3)
""")
- result = testdir.runpytest(p, '--dist=each', '--tx=popen')
+ result = testdir.runpytest(p, '--dist=each', '--tx=popen//id=my')
assert result.duration < 2.0
result.stdout.fnmatch_lines([
- "*0 passed*",
+ "*killed*my*",
])
+def test_session_hooks(testdir):
+ testdir.makeconftest("""
+ import sys
+ def pytest_sessionstart(session):
+ sys.pytestsessionhooks = session
+ def pytest_sessionfinish(session):
+ f = open(session.nodeid or "master", 'w')
+ f.write("xy")
+ f.close()
+ # let's fail on the slave
+ if session.nodeid:
+ raise ValueError(42)
+ """)
+ p = testdir.makepyfile("""
+ import sys
+ def test_hello():
+ assert hasattr(sys, 'pytestsessionhooks')
+ """)
+ result = testdir.runpytest(p, "--dist=each", "--tx=popen//id=my1")
+ result.stdout.fnmatch_lines([
+ "*ValueError*",
+ "*1 passed*",
+ ])
+ assert result.ret
+ d = result.parseoutcomes()
+ assert d['passed'] == 1
+ assert testdir.tmpdir.join("my1").check()
+ assert testdir.tmpdir.join("master").check()
+
+def test_funcarg_teardown_failure(testdir):
+ p = testdir.makepyfile("""
+ def pytest_funcarg__myarg(request):
+ def teardown(val):
+ raise ValueError(val)
+ return request.cached_setup(setup=lambda: 42, teardown=teardown,
+ scope="module")
+ def test_hello(myarg):
+ pass
+ """)
+ result = testdir.runpytest(p, "-n1")
+ assert result.ret
+ result.stdout.fnmatch_lines([
+ "*ValueError*42*",
+ "*1 passed*1 error*",
+ ])
+
+
More information about the pytest-commit
mailing list