From d1ce1c248727225502a01d346cce874ebae448aa Mon Sep 17 00:00:00 2001 From: Mathias Laurin Date: Mon, 13 Apr 2026 16:13:51 +0200 Subject: [PATCH] e2e: Drop bash and run them entirely in pytest --- .github/workflows/e2e.yml | 19 ---------- justfile | 5 +-- scripts/run-e2e-tests | 80 --------------------------------------- tests/test_e2e.py | 65 +++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 103 deletions(-) delete mode 100644 .github/workflows/e2e.yml delete mode 100755 scripts/run-e2e-tests create mode 100644 tests/test_e2e.py diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml deleted file mode 100644 index c44b840..0000000 --- a/.github/workflows/e2e.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: e2e - -on: - push: - branches: [main] - pull_request: - workflow_dispatch: - - -jobs: - e2e: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: ./.github/actions/setup-python - with: - python-version: '3.12' - - name: run e2e tests - run: ./scripts/run-e2e-tests diff --git a/justfile b/justfile index 852d905..9ee1564 100644 --- a/justfile +++ b/justfile @@ -20,9 +20,6 @@ lint: bandit: uv run bandit --configfile=pyproject.toml --quiet --recursive --severity-level=medium py_import_cycles tests -e2e: - ./scripts/run-e2e-tests - format: uv run ruff format uv run ruff check --fix @@ -30,4 +27,4 @@ format: docs: @echo "TODO: generate documentation" -ci: test check-format mypy lint bandit e2e +ci: test check-format mypy lint bandit diff --git a/scripts/run-e2e-tests b/scripts/run-e2e-tests deleted file mode 100755 index e829a5f..0000000 --- a/scripts/run-e2e-tests +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash - -REPO_PATH=$(dirname "$(dirname "$(realpath "$0")")") - -echo "===== Cleanup pycache =====" -find "${REPO_PATH}/e2e_test_inputs" -name __pycache__ -exec rm -rf {} \+ - -cd "$REPO_PATH" || exit 1 - -for project in ./e2e_test_inputs/*; do - project=${project#./} - - echo "===== Test $project =====" - if [ -f "$REPO_PATH/$project/skip_main" ]; then - echo "Skip testing main due to:" - cat "$REPO_PATH/$project/skip_main" - else - echo "Test main..." - uv run python3 "$project/main.py" 1>/dev/null || { echo "Test $project failed (execution of main)." >&2; exit 1; } - fi - - num_cycles="$(cat "$REPO_PATH/$project"/found_cycles)" - - echo "Test cycles (--packages)..." - packages="" - while read -r line; do - for word in $line; do - packages="$packages $REPO_PATH/$project/$word" - done - done <"$REPO_PATH/$project"/packages - - # shellcheck disable=SC2086 - pkgs_result=$(PYTHONPATH="." ./run-main --packages $packages --strategy johnson 2>&1) - if [ "$pkgs_result" != "Found $num_cycles import cycles" ]; then - echo "Test '--packages' $project failed (expect $num_cycles cycles, got '$pkgs_result')." >&2 - echo "Run 'PYTHONPATH=\".\" ./run-main --packages $packages --strategy johnson'" >&2 - exit 1 - fi - - echo "Test cycles (--files, absolute)..." - abs_files="" - while read -r line; do - for word in $line; do - abs_files="$abs_files $REPO_PATH/$project::$word" - done - done <"$REPO_PATH/$project"/files - - # shellcheck disable=SC2086 - abs_files_result=$(PYTHONPATH="." ./run-main --files $abs_files --strategy johnson 2>&1) - if [ "$abs_files_result" != "Found $num_cycles import cycles" ]; then - echo "Test '--files, absolute' $project failed (expect $num_cycles cycles, got '$abs_files_result')." >&2 - echo "Run 'PYTHONPATH=\".\" ./run-main --files $abs_files --strategy johnson'" >&2 - exit 1 - fi - - echo "Test cycles (--files, relative)..." - rel_files="" - while read -r line; do - for word in $line; do - rel_files="$rel_files $project::$word" - done - done <"$REPO_PATH/$project"/files - - # shellcheck disable=SC2086 - rel_files_result=$(PYTHONPATH="." ./run-main --files $rel_files --strategy johnson 2>&1) - if [ "$rel_files_result" != "Found $num_cycles import cycles" ]; then - echo "Test '--files, relative' $project failed (expect $num_cycles cycles, got '$rel_files_result')." >&2 - echo "Run 'PYTHONPATH=\".\" ./run-main --files $rel_files --strategy johnson'" >&2 - exit 1 - fi -done - -echo "===== Self test =====" -echo "Test cycles..." - -result=$(PYTHONPATH="." ./run-main --packages "$REPO_PATH/py_import_cycles" --strategy johnson 2>&1) -if [ "$result" != "Found 0 import cycles" ]; then - echo "Self test failed (expect 0 cycles, got '$result')." >&2 - exit 1 -fi diff --git a/tests/test_e2e.py b/tests/test_e2e.py new file mode 100644 index 0000000..e0fbfca --- /dev/null +++ b/tests/test_e2e.py @@ -0,0 +1,65 @@ +import shutil +import subprocess +from pathlib import Path + +import pytest + +REPO = Path(__file__).parent.parent +E2E_DIR = REPO / "e2e_test_inputs" +PROJECTS = sorted(p for p in E2E_DIR.iterdir() if p.is_dir()) + + +def run_tool(*args: str) -> str: + result = subprocess.run( + ["uv", "run", "-m", "py_import_cycles", *args], + capture_output=True, + text=True, + cwd=REPO, + ) + return result.stderr.strip() + + +@pytest.fixture(scope="session", autouse=True) +def clean_e2e_pycache() -> None: + for d in E2E_DIR.rglob("__pycache__"): + shutil.rmtree(d) + + +@pytest.mark.parametrize("project", PROJECTS, ids=lambda p: p.name) +def test_main(project: Path) -> None: + if (skip := project / "skip_main").exists(): + pytest.skip(skip.read_text().strip()) + result = subprocess.run(["uv", "run", "python", str(project / "main.py")], cwd=REPO) + assert result.returncode == 0 + + +@pytest.mark.parametrize("project", PROJECTS, ids=lambda p: p.name) +def test_packages(project: Path) -> None: + expected = int((project / "found_cycles").read_text()) + packages = [str(project / w) for w in (project / "packages").read_text().split()] + assert ( + run_tool("--packages", *packages, "--strategy", "johnson") + == f"Found {expected} import cycles" + ) + + +@pytest.mark.parametrize("project", PROJECTS, ids=lambda p: p.name) +def test_files_absolute(project: Path) -> None: + expected = int((project / "found_cycles").read_text()) + files = [f"{project}::{w}" for w in (project / "files").read_text().split()] + assert run_tool("--files", *files, "--strategy", "johnson") == f"Found {expected} import cycles" + + +@pytest.mark.parametrize("project", PROJECTS, ids=lambda p: p.name) +def test_files_relative(project: Path) -> None: + expected = int((project / "found_cycles").read_text()) + rel = project.relative_to(REPO) + files = [f"{rel}::{w}" for w in (project / "files").read_text().split()] + assert run_tool("--files", *files, "--strategy", "johnson") == f"Found {expected} import cycles" + + +def test_self() -> None: + assert ( + run_tool("--packages", str(REPO / "py_import_cycles"), "--strategy", "johnson") + == "Found 0 import cycles" + )