summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2021-11-21 07:44:08 -0500
committerNed Batchelder <ned@nedbatchelder.com>2021-11-21 08:15:29 -0500
commit0922240cfb5e549a83f56a12629a16ad3fe2b498 (patch)
treeff90dc91a7cf5430fb6c76899be88744ff5f707a
parent63d41f41d7719f1ef744bb4a25295de0e8006b53 (diff)
downloadpython-coveragepy-git-0922240cfb5e549a83f56a12629a16ad3fe2b498.tar.gz
fix(test): make .pth files once per session
The old way, we made and deleted .pth file around each test. This caused problems because pth files are written to a common location shared by all test workers, so tests would fail because pth files were being deleted as other workers were trying to use them. Now we make the pth file once per session. There's still a chance that a worker will be using a pth file just as another worker is ending and removing it.
-rw-r--r--tests/conftest.py51
-rw-r--r--tests/test_arcs.py5
-rw-r--r--tests/test_process.py69
3 files changed, 55 insertions, 70 deletions
diff --git a/tests/conftest.py b/tests/conftest.py
index 581329bb..1bf37298 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -7,9 +7,11 @@ Pytest auto configuration.
This module is run automatically by pytest, to define and enable fixtures.
"""
+import glob
import itertools
import os
import sys
+import sysconfig
import warnings
import pytest
@@ -99,13 +101,14 @@ def pytest_sessionstart():
print("Starting:", file=testtxt)
+WORKER = os.environ.get("PYTEST_XDIST_WORKER", "only")
+
def write_test_name(prefix):
"""For tracking where and when tests are running."""
if TRACK_TESTS: # pragma: debugging
with open(TEST_TXT, "a") as testtxt:
- worker = os.environ.get('PYTEST_XDIST_WORKER', 'none')
test = os.environ.get("PYTEST_CURRENT_TEST", "unknown")
- print(f"{prefix} {worker}: {test}", file=testtxt, flush=True)
+ print(f"{prefix} {WORKER}: {test}", file=testtxt, flush=True)
@pytest.hookimpl(hookwrapper=True)
@@ -144,3 +147,47 @@ def pytest_collection_modifyitems(items):
if TRACK_TESTS: # pragma: debugging
with open("/tmp/items.txt", "w") as f:
print("\n".join(i.nodeid for i in items), file=f)
+
+
+def possible_pth_dirs():
+ """Produce a sequence of directories for trying to write .pth files."""
+ # First look through sys.path, and if we find a .pth file, then it's a good
+ # place to put ours.
+ for pth_dir in sys.path: # pragma: part covered
+ pth_files = glob.glob(os.path.join(pth_dir, "*.pth"))
+ if pth_files:
+ yield pth_dir
+
+ # If we're still looking, then try the Python library directory.
+ # https://github.com/nedbat/coveragepy/issues/339
+ yield sysconfig.get_path("purelib") # pragma: cant happen
+
+
+def find_writable_pth_directory():
+ """Find a place to write a .pth file."""
+ for pth_dir in possible_pth_dirs(): # pragma: part covered
+ try_it = os.path.join(pth_dir, f"touch_{WORKER}.it")
+ try:
+ with open(try_it, "w") as f:
+ f.write("foo")
+ except OSError: # pragma: cant happen
+ continue
+
+ os.remove(try_it)
+ return pth_dir
+
+ return None # pragma: cant happen
+
+
+@pytest.fixture(scope="session", autouse=True)
+def create_pth_file():
+ """Create a .pth file for measuring subprocess coverage."""
+ pth_dir = find_writable_pth_directory()
+ assert pth_dir
+ pth_path = os.path.join(pth_dir, f"subcover_{WORKER}.pth")
+ with open(pth_path, "w") as pth:
+ pth.write("import coverage; coverage.process_startup()\n")
+
+ yield
+
+ os.remove(pth_path)
diff --git a/tests/test_arcs.py b/tests/test_arcs.py
index 817943e9..312efdc7 100644
--- a/tests/test_arcs.py
+++ b/tests/test_arcs.py
@@ -279,7 +279,10 @@ class WithTest(CoverageTest):
arcz=arcz,
)
- @pytest.mark.skipif(env.PYVERSION[:2] >= (3, 11), reason="avoid a 3.11 bug: 45709")
+ @pytest.mark.skipif(
+ (3, 11) <= env.PYVERSION <= (3, 11, 0, 'alpha', 2, 0),
+ reason="avoid a 3.11 bug: 45709"
+ )
# https://github.com/nedbat/coveragepy/issues/1270
def test_raise_through_with(self):
if env.PYBEHAVIOR.exit_through_with:
diff --git a/tests/test_process.py b/tests/test_process.py
index 0cbce427..b332b3a6 100644
--- a/tests/test_process.py
+++ b/tests/test_process.py
@@ -9,9 +9,7 @@ import os.path
import re
import stat
import sys
-import sysconfig
import textwrap
-import time
import pytest
@@ -1340,71 +1338,8 @@ class YankedDirectoryTest(CoverageTest):
assert all(line in out for line in lines)
-def possible_pth_dirs():
- """Produce a sequence of directories for trying to write .pth files."""
- # First look through sys.path, and if we find a .pth file, then it's a good
- # place to put ours.
- for pth_dir in sys.path: # pragma: part covered
- pth_files = glob.glob(os.path.join(pth_dir, "*.pth"))
- if pth_files:
- yield pth_dir
-
- # If we're still looking, then try the Python library directory.
- # https://github.com/nedbat/coveragepy/issues/339
- yield sysconfig.get_path("purelib") # pragma: cant happen
-
-
-def find_writable_pth_directory():
- """Find a place to write a .pth file."""
- for pth_dir in possible_pth_dirs(): # pragma: part covered
- try_it = os.path.join(pth_dir, f"touch_{WORKER}.it")
- try:
- with open(try_it, "w") as f:
- f.write("foo")
- except OSError: # pragma: cant happen
- continue
-
- os.remove(try_it)
- return pth_dir
-
- return None # pragma: cant happen
-
-WORKER = os.environ.get('PYTEST_XDIST_WORKER', '')
-PTH_DIR = find_writable_pth_directory()
-
-
-def persistent_remove(path):
- """Remove a file, and retry for a while if you can't."""
- tries = 100
- while tries: # pragma: part covered
- try:
- os.remove(path)
- except OSError: # pragma: not covered
- tries -= 1
- time.sleep(.05)
- else:
- return
- raise Exception(f"Sorry, couldn't remove {path!r}") # pragma: cant happen
-
-
-class ProcessCoverageMixin:
- """Set up a .pth file to coverage-measure all sub-processes."""
-
- def setUp(self):
- super().setUp()
-
- # Create the .pth file.
- assert PTH_DIR
- pth_contents = "import coverage; coverage.process_startup()\n"
- pth_path = os.path.join(PTH_DIR, f"subcover_{WORKER}.pth")
- with open(pth_path, "w") as pth:
- pth.write(pth_contents)
-
- self.addCleanup(persistent_remove, pth_path)
-
-
@pytest.mark.skipif(env.METACOV, reason="Can't test sub-process pth file during metacoverage")
-class ProcessStartupTest(ProcessCoverageMixin, CoverageTest):
+class ProcessStartupTest(CoverageTest):
"""Test that we can measure coverage in sub-processes."""
def setUp(self):
@@ -1477,7 +1412,7 @@ class ProcessStartupTest(ProcessCoverageMixin, CoverageTest):
assert len(data_files) == 1, msg
-class ProcessStartupWithSourceTest(ProcessCoverageMixin, CoverageTest):
+class ProcessStartupWithSourceTest(CoverageTest):
"""Show that we can configure {[run]source} during process-level coverage.
There are three interesting variables, for a total of eight tests: