206 lines
6.4 KiB
Python
206 lines
6.4 KiB
Python
from __future__ import annotations
|
|
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent.parent / "tools"))
|
|
|
|
from prepare_bluepad32 import ( # noqa: E402
|
|
PatchError,
|
|
check_paths,
|
|
main,
|
|
prepare_bluepad32,
|
|
resolve_paths,
|
|
)
|
|
|
|
|
|
def run_git(repository: Path, *args: str) -> subprocess.CompletedProcess[str]:
|
|
return subprocess.run(
|
|
["git", "-C", str(repository), *args],
|
|
capture_output=True,
|
|
text=True,
|
|
check=True,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def bluepad32_fixture(tmp_path: Path) -> tuple[Path, Path, Path, Path]:
|
|
source = tmp_path / "external" / "bluepad32"
|
|
source.mkdir(parents=True)
|
|
run_git(source, "init")
|
|
run_git(source, "config", "user.email", "test@example.com")
|
|
run_git(source, "config", "user.name", "Test User")
|
|
(source / "test.txt").write_text("line 1\n", encoding="utf-8")
|
|
run_git(source, "add", "test.txt")
|
|
run_git(source, "commit", "-m", "initial")
|
|
|
|
patches = tmp_path / "patches"
|
|
patches.mkdir()
|
|
patch = patches / "bluepad32-sdl3-imu.patch"
|
|
original = (source / "test.txt").read_text(encoding="utf-8")
|
|
(source / "test.txt").write_text(f"{original}line 2\n", encoding="utf-8")
|
|
patch.write_text(run_git(source, "diff", "--", "test.txt").stdout, encoding="utf-8")
|
|
run_git(source, "restore", "test.txt")
|
|
|
|
output = tmp_path / "build-aio" / "_deps" / "bluepad32-src"
|
|
return tmp_path, source, patch, output
|
|
|
|
|
|
def source_status(source: Path) -> str:
|
|
return run_git(source, "status", "--porcelain", "--untracked-files=all").stdout
|
|
|
|
|
|
def test_resolve_paths_uses_project_build_tree(tmp_path: Path) -> None:
|
|
source, patch, output = resolve_paths(tmp_path)
|
|
|
|
assert source == tmp_path / "external" / "bluepad32"
|
|
assert patch == tmp_path / "patches" / "bluepad32-sdl3-imu.patch"
|
|
assert output == tmp_path / "build" / "_deps" / "bluepad32-src"
|
|
|
|
|
|
def test_check_paths_rejects_missing_source(
|
|
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
|
) -> None:
|
|
_, source, patch, output = bluepad32_fixture
|
|
shutil.rmtree(source)
|
|
|
|
with pytest.raises(PatchError, match="bluepad32 directory does not exist"):
|
|
check_paths(source, patch, output)
|
|
|
|
|
|
def test_check_paths_rejects_missing_patch(
|
|
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
|
) -> None:
|
|
_, source, patch, output = bluepad32_fixture
|
|
patch.unlink()
|
|
|
|
with pytest.raises(PatchError, match="patch file does not exist"):
|
|
check_paths(source, patch, output)
|
|
|
|
|
|
def test_check_paths_rejects_non_repository(
|
|
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
|
) -> None:
|
|
_, source, patch, output = bluepad32_fixture
|
|
shutil.rmtree(source / ".git")
|
|
|
|
with pytest.raises(PatchError, match="not a git repository"):
|
|
check_paths(source, patch, output)
|
|
|
|
|
|
def test_check_paths_rejects_output_inside_source(
|
|
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
|
) -> None:
|
|
_, source, patch, _ = bluepad32_fixture
|
|
|
|
with pytest.raises(PatchError, match="outside the Bluepad32 source tree"):
|
|
check_paths(source, patch, source / "patched")
|
|
|
|
|
|
def test_prepare_patches_copy_and_preserves_pristine_source(
|
|
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
|
) -> None:
|
|
_, source, patch, output = bluepad32_fixture
|
|
|
|
result = prepare_bluepad32(source, patch, output)
|
|
|
|
assert result == output
|
|
assert (source / "test.txt").read_text(encoding="utf-8") == "line 1\n"
|
|
assert (output / "test.txt").read_text(encoding="utf-8") == "line 1\nline 2\n"
|
|
assert not (output / ".git").exists()
|
|
assert source_status(source) == ""
|
|
|
|
|
|
def test_prepare_applies_patch_inside_parent_repository(
|
|
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
|
) -> None:
|
|
root, source, patch, output = bluepad32_fixture
|
|
run_git(root, "init")
|
|
|
|
prepare_bluepad32(source, patch, output)
|
|
|
|
assert (output / "test.txt").read_text(encoding="utf-8") == "line 1\nline 2\n"
|
|
assert source_status(source) == ""
|
|
|
|
|
|
def test_prepare_replaces_existing_output_idempotently(
|
|
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
|
) -> None:
|
|
_, source, patch, output = bluepad32_fixture
|
|
prepare_bluepad32(source, patch, output)
|
|
(output / "stale.txt").write_text("stale", encoding="utf-8")
|
|
|
|
prepare_bluepad32(source, patch, output)
|
|
|
|
assert not (output / "stale.txt").exists()
|
|
assert (output / "test.txt").read_text(encoding="utf-8") == "line 1\nline 2\n"
|
|
assert source_status(source) == ""
|
|
|
|
|
|
def test_prepare_rejects_dirty_source_without_replacing_output(
|
|
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
|
) -> None:
|
|
_, source, patch, output = bluepad32_fixture
|
|
output.mkdir(parents=True)
|
|
sentinel = output / "sentinel.txt"
|
|
sentinel.write_text("keep", encoding="utf-8")
|
|
(source / "test.txt").write_text("local edit\n", encoding="utf-8")
|
|
|
|
with pytest.raises(PatchError, match="must be pristine"):
|
|
prepare_bluepad32(source, patch, output)
|
|
|
|
assert sentinel.read_text(encoding="utf-8") == "keep"
|
|
|
|
|
|
def test_prepare_rejects_diverged_source_revision(
|
|
bluepad32_fixture: tuple[Path, Path, Path, Path],
|
|
) -> None:
|
|
_, source, patch, output = bluepad32_fixture
|
|
(source / "test.txt").write_text("different base\n", encoding="utf-8")
|
|
run_git(source, "add", "test.txt")
|
|
run_git(source, "commit", "-m", "diverge")
|
|
|
|
with pytest.raises(PatchError, match="Patch validation failed"):
|
|
prepare_bluepad32(source, patch, output)
|
|
|
|
assert not output.exists()
|
|
|
|
|
|
def test_prepare_uses_default_paths(
|
|
bluepad32_fixture: tuple[Path, Path, Path, Path], monkeypatch: pytest.MonkeyPatch
|
|
) -> None:
|
|
root, source, _, _ = bluepad32_fixture
|
|
monkeypatch.chdir(root)
|
|
|
|
output = prepare_bluepad32()
|
|
|
|
assert output == root / "build" / "_deps" / "bluepad32-src"
|
|
assert (output / "test.txt").read_text(encoding="utf-8") == "line 1\nline 2\n"
|
|
assert source_status(source) == ""
|
|
|
|
|
|
def test_cli_accepts_explicit_output(
|
|
bluepad32_fixture: tuple[Path, Path, Path, Path], monkeypatch: pytest.MonkeyPatch
|
|
) -> None:
|
|
_, source, patch, output = bluepad32_fixture
|
|
monkeypatch.setattr(
|
|
sys,
|
|
"argv",
|
|
[
|
|
"prepare_bluepad32.py",
|
|
"--bluepad32",
|
|
str(source),
|
|
"--patch",
|
|
str(patch),
|
|
"--output",
|
|
str(output),
|
|
],
|
|
)
|
|
|
|
assert main() == 0
|
|
assert (output / "test.txt").read_text(encoding="utf-8") == "line 1\nline 2\n"
|
|
assert source_status(source) == ""
|