Skip to content

Commit cb07711

Browse files
authored
Merge pull request #10656 from pytest-dev/backport-10641-to-7.2.x
[7.2.x] Dont update cache from xdist worker
2 parents 9440702 + f22fbbf commit cb07711

File tree

4 files changed

+87
-0
lines changed

4 files changed

+87
-0
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ Samuel Searles-Bryant
311311
Samuele Pedroni
312312
Sanket Duthade
313313
Sankt Petersbug
314+
Saravanan Padmanaban
314315
Segev Finer
315316
Serhii Mozghovyi
316317
Seth Junot

changelog/10641.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a race condition when creating or updating the stepwise plugin's cache, which could occur when multiple xdist worker nodes try to simultaneously update the stepwise plugin's cache.

src/_pytest/stepwise.py

+8
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ def pytest_configure(config: Config) -> None:
4848
def pytest_sessionfinish(session: Session) -> None:
4949
if not session.config.getoption("stepwise"):
5050
assert session.config.cache is not None
51+
if hasattr(session.config, "workerinput"):
52+
# Do not update cache if this process is a xdist worker to prevent
53+
# race conditions (#10641).
54+
return
5155
# Clear the list of failing tests if the plugin is not active.
5256
session.config.cache.set(STEPWISE_CACHE_DIR, [])
5357

@@ -119,4 +123,8 @@ def pytest_report_collectionfinish(self) -> Optional[str]:
119123
return None
120124

121125
def pytest_sessionfinish(self) -> None:
126+
if hasattr(self.config, "workerinput"):
127+
# Do not update cache if this process is a xdist worker to prevent
128+
# race conditions (#10641).
129+
return
122130
self.cache.set(STEPWISE_CACHE_DIR, self.lastfailed)

testing/test_stepwise.py

+77
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
from pathlib import Path
2+
13
import pytest
4+
from _pytest.cacheprovider import Cache
25
from _pytest.monkeypatch import MonkeyPatch
36
from _pytest.pytester import Pytester
7+
from _pytest.stepwise import STEPWISE_CACHE_DIR
48

59

610
@pytest.fixture
@@ -278,3 +282,76 @@ def test_three():
278282
def test_sw_skip_help(pytester: Pytester) -> None:
279283
result = pytester.runpytest("-h")
280284
result.stdout.fnmatch_lines("*Implicitly enables --stepwise.")
285+
286+
287+
def test_stepwise_xdist_dont_store_lastfailed(pytester: Pytester) -> None:
288+
pytester.makefile(
289+
ext=".ini",
290+
pytest=f"[pytest]\ncache_dir = {pytester.path}\n",
291+
)
292+
293+
pytester.makepyfile(
294+
conftest="""
295+
import pytest
296+
297+
@pytest.hookimpl(tryfirst=True)
298+
def pytest_configure(config) -> None:
299+
config.workerinput = True
300+
"""
301+
)
302+
pytester.makepyfile(
303+
test_one="""
304+
def test_one():
305+
assert False
306+
"""
307+
)
308+
result = pytester.runpytest("--stepwise")
309+
assert result.ret == pytest.ExitCode.INTERRUPTED
310+
311+
stepwise_cache_file = (
312+
pytester.path / Cache._CACHE_PREFIX_VALUES / STEPWISE_CACHE_DIR
313+
)
314+
assert not Path(stepwise_cache_file).exists()
315+
316+
317+
def test_disabled_stepwise_xdist_dont_clear_cache(pytester: Pytester) -> None:
318+
pytester.makefile(
319+
ext=".ini",
320+
pytest=f"[pytest]\ncache_dir = {pytester.path}\n",
321+
)
322+
323+
stepwise_cache_file = (
324+
pytester.path / Cache._CACHE_PREFIX_VALUES / STEPWISE_CACHE_DIR
325+
)
326+
stepwise_cache_dir = stepwise_cache_file.parent
327+
stepwise_cache_dir.mkdir(exist_ok=True, parents=True)
328+
329+
stepwise_cache_file_relative = f"{Cache._CACHE_PREFIX_VALUES}/{STEPWISE_CACHE_DIR}"
330+
331+
expected_value = '"test_one.py::test_one"'
332+
content = {f"{stepwise_cache_file_relative}": expected_value}
333+
334+
pytester.makefile(ext="", **content)
335+
336+
pytester.makepyfile(
337+
conftest="""
338+
import pytest
339+
340+
@pytest.hookimpl(tryfirst=True)
341+
def pytest_configure(config) -> None:
342+
config.workerinput = True
343+
"""
344+
)
345+
pytester.makepyfile(
346+
test_one="""
347+
def test_one():
348+
assert True
349+
"""
350+
)
351+
result = pytester.runpytest()
352+
assert result.ret == 0
353+
354+
assert Path(stepwise_cache_file).exists()
355+
with stepwise_cache_file.open() as file_handle:
356+
observed_value = file_handle.readlines()
357+
assert [expected_value] == observed_value

0 commit comments

Comments
 (0)