[7.2.x] Dont update cache from xdist worker
This commit is contained in:
		
							parent
							
								
									211d08e9bc
								
							
						
					
					
						commit
						f22fbbf9f1
					
				
							
								
								
									
										1
									
								
								AUTHORS
								
								
								
								
							
							
						
						
									
										1
									
								
								AUTHORS
								
								
								
								
							| 
						 | 
				
			
			@ -311,6 +311,7 @@ Samuel Searles-Bryant
 | 
			
		|||
Samuele Pedroni
 | 
			
		||||
Sanket Duthade
 | 
			
		||||
Sankt Petersbug
 | 
			
		||||
Saravanan Padmanaban
 | 
			
		||||
Segev Finer
 | 
			
		||||
Serhii Mozghovyi
 | 
			
		||||
Seth Junot
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +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.
 | 
			
		||||
| 
						 | 
				
			
			@ -48,6 +48,10 @@ def pytest_configure(config: Config) -> None:
 | 
			
		|||
def pytest_sessionfinish(session: Session) -> None:
 | 
			
		||||
    if not session.config.getoption("stepwise"):
 | 
			
		||||
        assert session.config.cache is not None
 | 
			
		||||
        if hasattr(session.config, "workerinput"):
 | 
			
		||||
            # Do not update cache if this process is a xdist worker to prevent
 | 
			
		||||
            # race conditions (#10641).
 | 
			
		||||
            return
 | 
			
		||||
        # Clear the list of failing tests if the plugin is not active.
 | 
			
		||||
        session.config.cache.set(STEPWISE_CACHE_DIR, [])
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -119,4 +123,8 @@ class StepwisePlugin:
 | 
			
		|||
        return None
 | 
			
		||||
 | 
			
		||||
    def pytest_sessionfinish(self) -> None:
 | 
			
		||||
        if hasattr(self.config, "workerinput"):
 | 
			
		||||
            # Do not update cache if this process is a xdist worker to prevent
 | 
			
		||||
            # race conditions (#10641).
 | 
			
		||||
            return
 | 
			
		||||
        self.cache.set(STEPWISE_CACHE_DIR, self.lastfailed)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,10 @@
 | 
			
		|||
from pathlib import Path
 | 
			
		||||
 | 
			
		||||
import pytest
 | 
			
		||||
from _pytest.cacheprovider import Cache
 | 
			
		||||
from _pytest.monkeypatch import MonkeyPatch
 | 
			
		||||
from _pytest.pytester import Pytester
 | 
			
		||||
from _pytest.stepwise import STEPWISE_CACHE_DIR
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
| 
						 | 
				
			
			@ -278,3 +282,76 @@ def test_stepwise_skip_is_independent(pytester: Pytester) -> None:
 | 
			
		|||
def test_sw_skip_help(pytester: Pytester) -> None:
 | 
			
		||||
    result = pytester.runpytest("-h")
 | 
			
		||||
    result.stdout.fnmatch_lines("*Implicitly enables --stepwise.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_stepwise_xdist_dont_store_lastfailed(pytester: Pytester) -> None:
 | 
			
		||||
    pytester.makefile(
 | 
			
		||||
        ext=".ini",
 | 
			
		||||
        pytest=f"[pytest]\ncache_dir = {pytester.path}\n",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    pytester.makepyfile(
 | 
			
		||||
        conftest="""
 | 
			
		||||
import pytest
 | 
			
		||||
 | 
			
		||||
@pytest.hookimpl(tryfirst=True)
 | 
			
		||||
def pytest_configure(config) -> None:
 | 
			
		||||
    config.workerinput = True
 | 
			
		||||
"""
 | 
			
		||||
    )
 | 
			
		||||
    pytester.makepyfile(
 | 
			
		||||
        test_one="""
 | 
			
		||||
def test_one():
 | 
			
		||||
    assert False
 | 
			
		||||
"""
 | 
			
		||||
    )
 | 
			
		||||
    result = pytester.runpytest("--stepwise")
 | 
			
		||||
    assert result.ret == pytest.ExitCode.INTERRUPTED
 | 
			
		||||
 | 
			
		||||
    stepwise_cache_file = (
 | 
			
		||||
        pytester.path / Cache._CACHE_PREFIX_VALUES / STEPWISE_CACHE_DIR
 | 
			
		||||
    )
 | 
			
		||||
    assert not Path(stepwise_cache_file).exists()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_disabled_stepwise_xdist_dont_clear_cache(pytester: Pytester) -> None:
 | 
			
		||||
    pytester.makefile(
 | 
			
		||||
        ext=".ini",
 | 
			
		||||
        pytest=f"[pytest]\ncache_dir = {pytester.path}\n",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    stepwise_cache_file = (
 | 
			
		||||
        pytester.path / Cache._CACHE_PREFIX_VALUES / STEPWISE_CACHE_DIR
 | 
			
		||||
    )
 | 
			
		||||
    stepwise_cache_dir = stepwise_cache_file.parent
 | 
			
		||||
    stepwise_cache_dir.mkdir(exist_ok=True, parents=True)
 | 
			
		||||
 | 
			
		||||
    stepwise_cache_file_relative = f"{Cache._CACHE_PREFIX_VALUES}/{STEPWISE_CACHE_DIR}"
 | 
			
		||||
 | 
			
		||||
    expected_value = '"test_one.py::test_one"'
 | 
			
		||||
    content = {f"{stepwise_cache_file_relative}": expected_value}
 | 
			
		||||
 | 
			
		||||
    pytester.makefile(ext="", **content)
 | 
			
		||||
 | 
			
		||||
    pytester.makepyfile(
 | 
			
		||||
        conftest="""
 | 
			
		||||
import pytest
 | 
			
		||||
 | 
			
		||||
@pytest.hookimpl(tryfirst=True)
 | 
			
		||||
def pytest_configure(config) -> None:
 | 
			
		||||
    config.workerinput = True
 | 
			
		||||
"""
 | 
			
		||||
    )
 | 
			
		||||
    pytester.makepyfile(
 | 
			
		||||
        test_one="""
 | 
			
		||||
def test_one():
 | 
			
		||||
    assert True
 | 
			
		||||
"""
 | 
			
		||||
    )
 | 
			
		||||
    result = pytester.runpytest()
 | 
			
		||||
    assert result.ret == 0
 | 
			
		||||
 | 
			
		||||
    assert Path(stepwise_cache_file).exists()
 | 
			
		||||
    with stepwise_cache_file.open() as file_handle:
 | 
			
		||||
        observed_value = file_handle.readlines()
 | 
			
		||||
    assert [expected_value] == observed_value
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue