add new pytest_handlecrashitem hook

allows handling and rescheduling crash tests

PR #651

Co-authored-by: xoviat <xoviat@users.noreply.github.com>
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
This commit is contained in:
xoviat
2021-04-27 07:07:49 -05:00
committed by GitHub
parent f5665436e9
commit f861f1667d
7 changed files with 77 additions and 0 deletions

View File

@@ -0,0 +1 @@
Added new ``pytest_handlecrashitem`` hook to allow handling and rescheduling crashed items.

View File

@@ -343,6 +343,12 @@ class DSession:
nodeid, (fspath, None, fspath), (), "failed", msg, "???"
)
rep.node = worker
self.config.hook.pytest_handlecrashitem(
crashitem=nodeid,
report=rep,
sched=self.sched,
)
self.config.hook.pytest_runtest_logreport(report=rep)

View File

@@ -64,3 +64,20 @@ def pytest_xdist_auto_num_workers(config):
.. versionadded:: 2.1
"""
@pytest.mark.firstresult
def pytest_handlecrashitem(crashitem, report, sched):
"""
Handle a crashitem, modifying the report if necessary.
The scheduler is provided as a parameter to reschedule the test if desired with
`sched.mark_test_pending`.
def pytest_handlecrashitem(crashitem, report, sched):
if should_rerun(crashitem):
sched.mark_test_pending(crashitem)
report.outcome = "rerun"
.. versionadded:: 2.2.1
"""

View File

@@ -101,6 +101,14 @@ class EachScheduling:
def mark_test_complete(self, node, item_index, duration=0):
self.node2pending[node].remove(item_index)
def mark_test_pending(self, item):
self.pending.insert(
0,
self.collection.index(item),
)
for node in self.node2pending:
self.check_schedule(node)
def remove_node(self, node):
# KeyError if we didn't get an add_node() yet
pending = self.node2pending.pop(node)

View File

@@ -151,6 +151,14 @@ class LoadScheduling:
self.node2pending[node].remove(item_index)
self.check_schedule(node, duration=duration)
def mark_test_pending(self, item):
self.pending.insert(
0,
self.collection.index(item),
)
for node in self.node2pending:
self.check_schedule(node)
def check_schedule(self, node, duration=0):
"""Maybe schedule new items on the node

View File

@@ -243,6 +243,9 @@ class LoadScopeScheduling:
self.assigned_work[node][scope][nodeid] = True
self._reschedule(node)
def mark_test_pending(self, item):
raise NotImplementedError()
def _assign_work_unit(self, node):
"""Assign a work unit to a node."""
assert self.workqueue

View File

@@ -60,3 +60,37 @@ class TestHooks:
["*HOOK: gw0 test_a, test_b, test_c", "*HOOK: gw1 test_a, test_b, test_c"]
)
res.stdout.fnmatch_lines(["*3 passed*"])
class TestCrashItem:
@pytest.fixture(autouse=True)
def create_test_file(self, testdir):
testdir.makepyfile(
"""
import os
def test_a(): pass
def test_b(): os._exit(1)
def test_c(): pass
def test_d(): pass
"""
)
def test_handlecrashitem(self, testdir):
"""Test pytest_handlecrashitem hook."""
testdir.makeconftest(
"""
test_runs = 0
def pytest_handlecrashitem(crashitem, report, sched):
global test_runs
if test_runs == 0:
sched.mark_test_pending(crashitem)
test_runs = 1
else:
print("HOOK: pytest_handlecrashitem")
"""
)
res = testdir.runpytest("-n2", "-s")
res.stdout.fnmatch_lines_random(["*HOOK: pytest_handlecrashitem"])
res.stdout.fnmatch_lines(["*3 passed*"])