/
niceSOFT
/
python3-setuptools_scm
Обзор
Документация
Войти
/
niceSOFT
/
python3-setuptools_scm
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
vcs-versioning/testing_vcs/test_jj.py
242 строки
7 KB
pre-commit-ci[bot]
style: pre-commit fixes
03 авг 2026, 21:57
03 авг 2026, 21:57
8a32949
Код
Авторство
О чём код?
"""Tests for the Jujutsu (jj) VCS backend.""" from __future__ import annotations import os from pathlib import Path from typing import Any from unittest.mock import patch import pytest from vcs_versioning import Configuration from vcs_versioning._backends import _jj as jj from vcs_versioning._backends._discover_vcs import discover from vcs_versioning._file_finders._jj import jj_find_files from vcs_versioning._run_cmd import CommandNotFoundError, CompletedProcess, run from vcs_versioning.test_api import DebugMode, WorkDir @pytest.fixture def wd(wd: WorkDir, debug_mode: DebugMode) -> WorkDir: """Set up jj for jj-specific tests.""" debug_mode.disable() wd.setup_jj() debug_mode.enable() return wd def test_jj_gone(wd: WorkDir, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("PATH", str(wd.cwd / "not-existing")) with pytest.raises(CommandNotFoundError, match=r"jj"): jj.parse(wd.cwd, Configuration()) def test_version_from_jj(wd: WorkDir) -> None: # Initial empty repo -- no commits, no files, jj considers it clean # but we force dirty=True when node is None (no meaningful commit) wd.expect_parse(tag="0.0", distance=0, dirty=True) wd.commit_testfile() wd.expect_parse(tag="0.0", distance=1, dirty=False, node_prefix="j") # Tag the commit (after jj commit, the tagged commit is @-) wd("jj tag set v0.1 -r @-") wd.expect_parse(tag="0.1", distance=0, dirty=False, exact=True) # Dirty state -- write without committing. # In jj, the working copy IS a commit. When it has changes, jj # snapshots them into @, so distance=1 (one non-empty commit ahead). wd.write("test.txt", "test2") wd.expect_parse(tag="0.1", distance=1, dirty=True) # Commit the change and tag wd.commit_testfile() wd("jj tag set version-0.2 -r @-") wd.expect_parse(tag="0.2", distance=0, dirty=False, exact=True) def test_jj_no_tags(wd: WorkDir) -> None: wd.commit_testfile() wd.commit_testfile() wd.commit_testfile() version = jj.parse(wd.cwd, Configuration()) assert version is not None assert version.distance >= 3 assert str(version.tag) == "0.0" def test_jj_exact_tag(wd: WorkDir) -> None: wd.commit_testfile() wd("jj tag set v1.0.0 -r @-") version = jj.parse(wd.cwd, Configuration()) assert version is not None assert version.distance == 0 assert str(version.tag) == "1.0.0" def test_jj_dirty_state(wd: WorkDir) -> None: wd.commit_testfile() wd("jj tag set v1.0.0 -r @-") # Clean state version = jj.parse(wd.cwd, Configuration()) assert version is not None assert not version.dirty # Dirty state -- write a file (jj auto-tracks it) wd.write("dirty.txt", "dirty content") version = jj.parse(wd.cwd, Configuration()) assert version is not None assert version.dirty def test_jj_distance(wd: WorkDir) -> None: wd.commit_testfile() wd("jj tag set v1.0.0 -r @-") wd.commit_testfile() wd.commit_testfile() version = jj.parse(wd.cwd, Configuration()) assert version is not None assert version.distance == 2 assert str(version.tag) == "1.0.0" @pytest.mark.issue(1477) def test_jj_distance_counts_merges_like_git(wd: WorkDir) -> None: """Merges are empty in jj terms but git counts them -- so do we.""" wd.commit_testfile() wd("jj tag set v1.0.0 -r @-") wd.commit_testfile() main = wd("jj log --no-graph -r @- -T commit_id") wd("jj new v1.0.0 -m side") wd.write("side.txt", "side") wd.add_and_commit("side") side = wd("jj log --no-graph -r @- -T commit_id") wd(f"jj new {main} {side} -m merge") wd("jj commit -m merged") version = jj.parse(wd.cwd, Configuration()) assert version is not None assert version.distance == int(wd("git rev-list --count v1.0.0..HEAD")) assert version.node == "j" + wd("git rev-parse HEAD")[:12] @pytest.mark.issue(1477) def test_jj_avoids_scanning_the_whole_history( wd: WorkDir, monkeypatch: pytest.MonkeyPatch ) -> None: """``empty()`` costs one commit diff per candidate -- keep it bounded. An unbounded ``empty()`` filter turns version inference into a minutes-long operation on large repositories. """ wd.commit_testfile() wd("jj tag set v1.0.0 -r @-") wd.commit_testfile() commands: list[list[str]] = [] def record(cmd: list[str], cwd: Path, **kwargs: Any) -> CompletedProcess: commands.append([str(part) for part in cmd]) return run(cmd, cwd, **kwargs) monkeypatch.setattr("vcs_versioning._backends._jj._run", record) assert jj.parse(wd.cwd, Configuration()) is not None revsets = [ cmd[index + 1] for cmd in commands for index, arg in enumerate(cmd) if arg == "-r" ] assert revsets bound = f"ancestors(@, {jj.ANCESTOR_SCAN_LIMIT})" assert all("empty()" not in revset or bound in revset for revset in revsets) # the working copy is snapshotted once, the history queries reuse it snapshotting = [cmd for cmd in commands if "--ignore-working-copy" not in cmd] assert len(snapshotting) == 1 def test_jj_branch_detection(wd: WorkDir) -> None: wd.commit_testfile() wd("jj bookmark create test-branch -r @-") version = jj.parse(wd.cwd, Configuration()) assert version is not None # The bookmark is on @-, not @, so branch may or may not be set # depending on jj's model. Just verify the call doesn't fail. assert version.branch is None or isinstance(version.branch, str) def test_jj_node_prefix(wd: WorkDir) -> None: """jj nodes should be prefixed with 'j' to distinguish from git's 'g'.""" wd.commit_testfile() version = jj.parse(wd.cwd, Configuration()) assert version is not None assert version.node is not None assert version.node.startswith("j") def test_jj_file_finder(wd: WorkDir) -> None: wd.commit_testfile() files = jj_find_files(str(wd.cwd)) assert len(files) > 0 basenames = [os.path.basename(f) for f in files] assert "test.txt" in basenames def test_jj_file_finder_no_repo(tmp_path: Path) -> None: files = jj_find_files(str(tmp_path)) assert files == [] def test_jj_missing_binary_errors(tmp_path: Path) -> None: """When .jj/ exists but jj is not on PATH, discovery should error.""" (tmp_path / ".jj").mkdir() config = Configuration(root=tmp_path) with patch( "vcs_versioning._backends._discover_vcs.has_command", return_value=False ), pytest.raises(LookupError, match="jj.*not available"): discover(tmp_path, config=config) def test_jj_colocated_prefers_jj(wd: WorkDir) -> None: """In a colocated repo (.jj + .git), jj should be preferred.""" wd.commit_testfile() wd("jj tag set v2.0.0 -r @-") resolved = wd.cwd.resolve() assert (resolved / ".jj").is_dir() assert (resolved / ".git").exists() config = Configuration(root=resolved) result = discover(resolved, config=config) assert result is not None assert type(result).__name__ == "JjWorkdir" def test_jj_disable_jj_falls_back_to_git(wd: WorkDir) -> None: """DISABLE_JJ=1 should skip jj and fall back to git in colocated repos.""" from vcs_versioning._environment import VcsEnvironment wd.commit_testfile() resolved = wd.cwd.resolve() assert (resolved / ".jj").is_dir() assert (resolved / ".git").exists() env = VcsEnvironment(disable_jj=True) config = env.build_config(root=resolved) result = discover(resolved, config=config) assert result is not None assert type(result).__name__ == "GitWorkdir"