Skip to content

Commit ce6aacf

Browse files
committed
add new flag --only-rerun to rerun only certain failures
1 parent e037356 commit ce6aacf

2 files changed

Lines changed: 52 additions & 1 deletion

File tree

pytest_rerunfailures.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import pkg_resources
2+
import re
23
import time
34
import warnings
45

@@ -30,6 +31,14 @@ def pytest_addoption(parser):
3031
group = parser.getgroup(
3132
"rerunfailures",
3233
"re-run failing tests to eliminate flaky failures")
34+
group._addoption(
35+
'--only-rerun',
36+
action='store',
37+
dest='only_rerun',
38+
type=str,
39+
default=None,
40+
help='Optional comma seperated list. If passed, only rerun errors matching the regexes provided.'
41+
)
3342
group._addoption(
3443
'--reruns',
3544
action="store",
@@ -173,6 +182,21 @@ def _remove_failed_setup_state_from_session(item):
173182
setup_state.stack = list()
174183

175184

185+
def _should_fail_on_error(session_config, report):
186+
if report.outcome != 'failed':
187+
return False
188+
189+
rerun_errors = session_config.option.only_rerun
190+
if not rerun_errors:
191+
return False
192+
193+
for rerun_regex in rerun_errors.split(','):
194+
if re.search(rerun_regex, report.longrepr.reprcrash.message):
195+
return False
196+
197+
return True
198+
199+
176200
def pytest_runtest_protocol(item, nextitem):
177201
"""
178202
Note: when teardown fails, two reports are generated for the case, one for
@@ -200,9 +224,10 @@ def pytest_runtest_protocol(item, nextitem):
200224
reports = runtestprotocol(item, nextitem=nextitem, log=False)
201225

202226
for report in reports: # 3 reports: setup, call, teardown
227+
is_terminal_error = _should_fail_on_error(item.session.config, report)
203228
report.rerun = item.execution_count - 1
204229
xfail = hasattr(report, 'wasxfail')
205-
if item.execution_count > reruns or not report.failed or xfail:
230+
if item.execution_count > reruns or not report.failed or xfail or is_terminal_error:
206231
# last run or no failure detected, log normally
207232
item.ihook.pytest_runtest_logreport(report=report)
208233
else:

test_pytest_rerunfailures.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,3 +407,29 @@ def pytest_runtest_logfinish(nodeid, location):
407407
""".format(hook_message))
408408
result = testdir.runpytest('--reruns', '1', '-s')
409409
result.stdout.fnmatch_lines(hook_message)
410+
411+
@pytest.mark.parametrize(
412+
"file_text, only_rerun_text, should_rerun",
413+
[
414+
('def test_only_rerun(): raise AssertionError("ERR")', 'AssertionError', True),
415+
('def test_only_rerun(): raise AssertionError("ERR")', 'Assertion*', True),
416+
('def test_only_rerun(): raise AssertionError("ERR")', 'Assertion', True),
417+
('def test_only_rerun(): raise AssertionError("ERR")', 'ValueError', False),
418+
('def test_only_rerun(): raise AssertionError("ERR")', 'AssertionError,ValueError', True),
419+
('def test_only_rerun(): raise AssertionError("ERR")', 'AssertionError ValueError', False),
420+
('def test_only_rerun(): raise AssertionError("ERR")', '', True),
421+
('def test_only_rerun(): raise AssertionError("ERR")', 'AssertionError: ', True),
422+
('def test_only_rerun(): raise AssertionError("ERR")', 'AssertionError: ERR', True),
423+
('def test_only_rerun(): raise AssertionError("ERR")', 'ERR', True),
424+
]
425+
)
426+
def test_pytest_only_rerun(testdir, file_text, only_rerun_text, should_rerun):
427+
testdir.makepyfile(file_text)
428+
429+
num_failed = 1
430+
num_passed = 0
431+
num_reruns = 1
432+
num_reruns_actual = num_reruns if should_rerun else 0
433+
434+
result = testdir.runpytest('--reruns', str(num_reruns), '--only-rerun', only_rerun_text)
435+
assert_outcomes(result, passed=num_passed, failed=num_failed, rerun=num_reruns_actual)

0 commit comments

Comments
 (0)