Skip to content

Commit a5c26f3

Browse files
committed
pytest_plugin(fix[git_repo]): isolate clone from cache
why: The first consumer of git_repo/hg_repo got a live handle to the session-scoped master_copy, so its mutations (e.g. adding a remote) leaked into every later test that copied the cache. Surfaced as order-dependent failures; a real isolation bug for plugin consumers. what: - Build master_copy once as a pristine read-only cache - Always return an isolated copytree, including for the first consumer - Apply the same fix to git_repo and hg_repo
1 parent e4b4639 commit a5c26f3

2 files changed

Lines changed: 36 additions & 26 deletions

File tree

CHANGES

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ variable, so constructing any second registry mutated the shared
2929
{data}`~libvcs.url.registry.registry`. Each registry now owns its own parser
3030
map, leaving the global one untouched.
3131

32+
#### `git_repo` and `hg_repo` fixtures return isolated clones
33+
34+
The first consumer of the `git_repo` / `hg_repo` pytest fixtures received a live
35+
handle to the session-cached master checkout, so mutating that checkout (adding
36+
a remote, switching branches) polluted the cache for every later test. The
37+
master copy is now treated as a pristine, read-only cache and every consumer —
38+
including the first — gets its own copytree.
39+
3240
### Development
3341

3442
#### Opt-in parallel test runs

src/libvcs/pytest_plugin.py

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -719,26 +719,27 @@ def git_repo(
719719
new_checkout_path = projects_path / remote_repo_name
720720
master_copy = remote_repos_path / "git_repo"
721721

722-
if master_copy.exists():
723-
shutil.copytree(master_copy, new_checkout_path)
724-
return GitSync(
722+
# Build the master copy once as a pristine, read-only cache. Every consumer
723+
# gets an isolated copytree of it (including the first), so a test that
724+
# mutates its checkout cannot pollute the cache for later tests.
725+
if not master_copy.exists():
726+
GitSync(
725727
url=f"file://{git_remote_repo}",
726-
path=str(new_checkout_path),
727-
)
728-
729-
git_repo = GitSync(
728+
path=master_copy,
729+
remotes={
730+
"origin": GitRemote(
731+
name="origin",
732+
push_url=f"file://{git_remote_repo}",
733+
fetch_url=f"file://{git_remote_repo}",
734+
),
735+
},
736+
).obtain()
737+
738+
shutil.copytree(master_copy, new_checkout_path)
739+
return GitSync(
730740
url=f"file://{git_remote_repo}",
731-
path=master_copy,
732-
remotes={
733-
"origin": GitRemote(
734-
name="origin",
735-
push_url=f"file://{git_remote_repo}",
736-
fetch_url=f"file://{git_remote_repo}",
737-
),
738-
},
741+
path=str(new_checkout_path),
739742
)
740-
git_repo.obtain()
741-
return git_repo
742743

743744

744745
@pytest.fixture
@@ -753,19 +754,20 @@ def hg_repo(
753754
new_checkout_path = projects_path / remote_repo_name
754755
master_copy = remote_repos_path / "hg_repo"
755756

756-
if master_copy.exists():
757-
shutil.copytree(master_copy, new_checkout_path)
758-
return HgSync(
757+
# Build the master copy once as a pristine, read-only cache. Every consumer
758+
# gets an isolated copytree of it (including the first), so a test that
759+
# mutates its checkout cannot pollute the cache for later tests.
760+
if not master_copy.exists():
761+
HgSync(
759762
url=f"file://{hg_remote_repo}",
760-
path=str(new_checkout_path),
761-
)
763+
path=master_copy,
764+
).obtain()
762765

763-
hg_repo = HgSync(
766+
shutil.copytree(master_copy, new_checkout_path)
767+
return HgSync(
764768
url=f"file://{hg_remote_repo}",
765-
path=master_copy,
769+
path=str(new_checkout_path),
766770
)
767-
hg_repo.obtain()
768-
return hg_repo
769771

770772

771773
@pytest.fixture

0 commit comments

Comments
 (0)