diff --git a/changes/390.bugfix.rst b/changes/390.bugfix.rst new file mode 100644 index 0000000..bf1eec9 --- /dev/null +++ b/changes/390.bugfix.rst @@ -0,0 +1 @@ +Fall back to the default delay and backoff factor with a warning when configured values are NaN or positive infinity, preventing internal errors during reruns. diff --git a/src/pytest_rerunfailures.py b/src/pytest_rerunfailures.py index db3974a..2ed749c 100644 --- a/src/pytest_rerunfailures.py +++ b/src/pytest_rerunfailures.py @@ -1,6 +1,7 @@ import hashlib import importlib.metadata import inspect +import math import os import platform import re @@ -340,6 +341,11 @@ def get_reruns_delay(item): warnings.warn( "Delay time between re-runs cannot be < 0. Using default value: 0" ) + elif not math.isfinite(delay): + delay = 0 + warnings.warn( + "Delay time between re-runs must be finite. Using default value: 0" + ) return delay @@ -370,6 +376,11 @@ def get_reruns_delay_backoff_factor(item): warnings.warn( "Rerun delay backoff factor cannot be < 0. Using default value: 1.0" ) + elif not math.isfinite(factor): + factor = 1.0 + warnings.warn( + "Rerun delay backoff factor must be finite. Using default value: 1.0" + ) return factor diff --git a/tests/test_pytest_rerunfailures.py b/tests/test_pytest_rerunfailures.py index 2094448..18dd5ec 100644 --- a/tests/test_pytest_rerunfailures.py +++ b/tests/test_pytest_rerunfailures.py @@ -955,6 +955,52 @@ def test_fail_two(): assert_outcomes(result, passed=0, failed=1, rerun=2) +@pytest.mark.parametrize("value", ["nan", "inf"]) +@pytest.mark.parametrize("source", ["cli", "ini", "marker", "positional_marker"]) +@pytest.mark.parametrize( + ("setting", "warning"), + [ + ( + "reruns_delay", + "Delay time between re-runs must be finite. Using default value: 0", + ), + ( + "reruns_delay_backoff_factor", + "Rerun delay backoff factor must be finite. Using default value: 1.0", + ), + ], +) +def test_reruns_with_nonfinite_delay(testdir, setting, warning, source, value): + args = ["--reruns", "2"] + marker = "" + if source == "cli": + args.append(f"--{setting.replace('_', '-')}={value}") + elif source == "ini": + testdir.makeini(f"[pytest]\n{setting} = {value}") + elif source == "marker": + marker = f"@pytest.mark.flaky(reruns=2, {setting}=float({value!r}))" + elif setting == "reruns_delay": + marker = f"@pytest.mark.flaky(2, float({value!r}))" + else: + marker = f"@pytest.mark.flaky(2, 0, float({value!r}))" + + testdir.makepyfile( + f""" + import pytest + + {marker} + def test_recovery(request): + assert request.node.execution_count == 3 + """ + ) + + result = testdir.runpytest(*args) + + assert result.ret == pytest.ExitCode.OK + assert_outcomes(result, passed=1, rerun=2) + result.stdout.fnmatch_lines(f"*UserWarning: {warning}") + + def test_reruns_with_delay_backoff_factor(testdir): testdir.makepyfile( """