Fix tests incorrectly identified if worker crashes during teardown stage
Fix #124
This commit is contained in:
1
changelog/124.bugfix
Normal file
1
changelog/124.bugfix
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Fix issue where tests were being incorrectly identified if a worker crashed during the ``teardown`` stage of the test.
|
||||||
@@ -471,18 +471,34 @@ def test_funcarg_teardown_failure(testdir):
|
|||||||
assert result.ret
|
assert result.ret
|
||||||
|
|
||||||
|
|
||||||
def test_crashing_item(testdir):
|
@pytest.mark.parametrize('when', ['setup', 'call', 'teardown'])
|
||||||
|
def test_crashing_item(testdir, when):
|
||||||
|
"""Ensure crashing item is correctly reported during all testing stages"""
|
||||||
|
code = dict(setup='', call='', teardown='')
|
||||||
|
code[when] = 'py.process.kill(os.getpid())'
|
||||||
p = testdir.makepyfile("""
|
p = testdir.makepyfile("""
|
||||||
import py
|
|
||||||
import os
|
import os
|
||||||
def test_crash():
|
import py
|
||||||
py.process.kill(os.getpid())
|
import pytest
|
||||||
def test_noncrash():
|
|
||||||
|
@pytest.fixture
|
||||||
|
def fix():
|
||||||
|
{setup}
|
||||||
|
yield
|
||||||
|
{teardown}
|
||||||
|
|
||||||
|
def test_crash(fix):
|
||||||
|
{call}
|
||||||
pass
|
pass
|
||||||
""")
|
|
||||||
|
def test_ok():
|
||||||
|
pass
|
||||||
|
""".format(**code))
|
||||||
|
passes = 2 if when == 'teardown' else 1
|
||||||
result = testdir.runpytest("-n2", p)
|
result = testdir.runpytest("-n2", p)
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*crashed*test_crash*", "*1 failed*1 passed*"
|
"*crashed*test_crash*",
|
||||||
|
"*1 failed*%d passed*" % passes,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -230,18 +230,19 @@ class DSession:
|
|||||||
nodeid=nodeid, location=location)
|
nodeid=nodeid, location=location)
|
||||||
|
|
||||||
def slave_testreport(self, node, rep):
|
def slave_testreport(self, node, rep):
|
||||||
"""Emitted when a node calls the pytest_runtest_logreport hook.
|
"""Emitted when a node calls the pytest_runtest_logreport hook."""
|
||||||
|
|
||||||
If the node indicates it is finished with a test item, remove
|
|
||||||
the item from the pending list in the scheduler.
|
|
||||||
"""
|
|
||||||
if rep.when == "call" or (rep.when == "setup" and not rep.passed):
|
|
||||||
self.sched.mark_test_complete(node, rep.item_index, rep.duration)
|
|
||||||
# self.report_line("testreport %s: %s" %(rep.id, rep.status))
|
|
||||||
rep.node = node
|
rep.node = node
|
||||||
self.config.hook.pytest_runtest_logreport(report=rep)
|
self.config.hook.pytest_runtest_logreport(report=rep)
|
||||||
self._handlefailures(rep)
|
self._handlefailures(rep)
|
||||||
|
|
||||||
|
def slave_runtest_protocol_complete(self, node, item_index, duration):
|
||||||
|
"""
|
||||||
|
Emitted when a node fires the 'runtest_protocol_complete' event,
|
||||||
|
signalling that a test has completed the runtestprotocol and should be
|
||||||
|
removed from the pending list in the scheduler.
|
||||||
|
"""
|
||||||
|
self.sched.mark_test_complete(node, item_index, duration)
|
||||||
|
|
||||||
def slave_collectreport(self, node, rep):
|
def slave_collectreport(self, node, rep):
|
||||||
"""Emitted when a node calls the pytest_collectreport hook."""
|
"""Emitted when a node calls the pytest_collectreport hook."""
|
||||||
if rep.failed:
|
if rep.failed:
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@@ -59,23 +60,29 @@ class SlaveInteractor:
|
|||||||
self.log("items to run:", torun)
|
self.log("items to run:", torun)
|
||||||
# only run if we have an item and a next item
|
# only run if we have an item and a next item
|
||||||
while len(torun) >= 2:
|
while len(torun) >= 2:
|
||||||
self.run_tests(torun)
|
self.run_one_test(torun)
|
||||||
if name == "shutdown":
|
if name == "shutdown":
|
||||||
if torun:
|
if torun:
|
||||||
self.run_tests(torun)
|
self.run_one_test(torun)
|
||||||
break
|
break
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def run_tests(self, torun):
|
def run_one_test(self, torun):
|
||||||
items = self.session.items
|
items = self.session.items
|
||||||
self.item_index = torun.pop(0)
|
self.item_index = torun.pop(0)
|
||||||
|
item = items[self.item_index]
|
||||||
if torun:
|
if torun:
|
||||||
nextitem = items[torun[0]]
|
nextitem = items[torun[0]]
|
||||||
else:
|
else:
|
||||||
nextitem = None
|
nextitem = None
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
self.config.hook.pytest_runtest_protocol(
|
self.config.hook.pytest_runtest_protocol(
|
||||||
item=items[self.item_index],
|
item=item,
|
||||||
nextitem=nextitem)
|
nextitem=nextitem)
|
||||||
|
duration = time.time() - start
|
||||||
|
self.sendevent("runtest_protocol_complete", item_index=self.item_index,
|
||||||
|
duration=duration)
|
||||||
|
|
||||||
def pytest_collection_finish(self, session):
|
def pytest_collection_finish(self, session):
|
||||||
self.sendevent(
|
self.sendevent(
|
||||||
|
|||||||
@@ -315,6 +315,8 @@ class SlaveController(object):
|
|||||||
self.notify_inproc(eventname, node=self, rep=rep)
|
self.notify_inproc(eventname, node=self, rep=rep)
|
||||||
elif eventname == "collectionfinish":
|
elif eventname == "collectionfinish":
|
||||||
self.notify_inproc(eventname, node=self, ids=kwargs['ids'])
|
self.notify_inproc(eventname, node=self, ids=kwargs['ids'])
|
||||||
|
elif eventname == "runtest_protocol_complete":
|
||||||
|
self.notify_inproc(eventname, node=self, **kwargs)
|
||||||
elif eventname == "logwarning":
|
elif eventname == "logwarning":
|
||||||
self.notify_inproc(eventname, message=kwargs['message'],
|
self.notify_inproc(eventname, message=kwargs['message'],
|
||||||
code=kwargs['code'], nodeid=kwargs['nodeid'],
|
code=kwargs['code'], nodeid=kwargs['nodeid'],
|
||||||
|
|||||||
Reference in New Issue
Block a user