diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 298242f1..0032734e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,18 @@ jobs: python-version: "3.12" is-primary: true + env: + # Collect coverage only on the primary job: it is the only one that + # uploads the reports and feeds the PR comment, while --cov costs + # ~65% extra pytest wall time (worst on Windows). Windows-only + # covered lines measured at 5 (diffing covered lines per file, + # Windows vs the primary artifact): the SIGUSR1 no-op return and the + # drive-letter branch of convert_project_path_to_claude_dir, both in + # cli.py. Those were never in the published report (non-primary + # coverage was computed and discarded), so this changes nothing. + COV: ${{ matrix.is-primary && '--cov=claude_code_log --cov-report=xml --cov-report=html --cov-report=term' || '' }} + COV_APPEND: ${{ matrix.is-primary && '--cov=claude_code_log --cov-append --cov-report=xml --cov-report=html --cov-report=term' || '' }} + steps: - uses: actions/checkout@v4 with: @@ -38,23 +50,23 @@ jobs: - name: Install dependencies run: uv sync --all-extras --dev && uv run playwright install chromium - - name: Run unit tests with coverage + - name: Run unit tests (coverage on primary only) # -p no:playwright: unit tests don't use the browser fixtures; skipping the # plugin avoids a ~1s playwright import per xdist worker, which Windows pays # per spawned worker. See work/xdist-import-cost.md. - run: uv run pytest -p no:playwright -m "not (tui or browser or benchmark)" --cov=claude_code_log --cov-report=xml --cov-report=html --cov-report=term + run: uv run pytest -p no:playwright -m "not (tui or browser or benchmark)" ${{ env.COV }} - - name: Run TUI tests with coverage append - run: uv run pytest -m tui --cov=claude_code_log --cov-append --cov-report=xml --cov-report=html --cov-report=term + - name: Run TUI tests (coverage append on primary only) + run: uv run pytest -m tui ${{ env.COV_APPEND }} - - name: Run browser tests with coverage append - run: uv run pytest -m browser --cov=claude_code_log --cov-append --cov-report=xml --cov-report=html --cov-report=term + - name: Run browser tests (coverage append on primary only) + run: uv run pytest -m browser ${{ env.COV_APPEND }} - name: Run benchmark tests with coverage append (primary only) if: matrix.is-primary env: CLAUDE_CODE_LOG_DEBUG_TIMING: "1" - run: uv run pytest -m benchmark --cov=claude_code_log --cov-append --cov-report=xml --cov-report=html --cov-report=term -v + run: uv run pytest -m benchmark ${{ env.COV_APPEND }} -v - name: Upload coverage HTML report as artifact uses: actions/upload-artifact@v4 diff --git a/pyproject.toml b/pyproject.toml index 2279f763..b6bc1c0f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -100,6 +100,15 @@ target-version = "py310" source = ["claude_code_log"] omit = ["*/tests/*", "*/test/*", "*/__pycache__/*", "*/venv/*", "*/.venv/*"] relative_files = true +# sys.monitoring core (Py 3.12+): ~5x lower tracing overhead than the C +# tracer (unit-test step: +67% -> +14% wall time vs no coverage). On +# Py < 3.12 coverage warns "no-sysmon" once and falls back to the default +# core; data and reports are unaffected. Requires coverage >= 7.9 for the +# [run] core setting. COVERAGE_CORE env var still overrides (e.g. +# COVERAGE_CORE=ctrace to A/B time). NOTE: sysmon cannot measure branch +# coverage before Py 3.14 — if branch = true is ever enabled here, 3.12/ +# 3.13 silently revert to the slow tracer. +core = "sysmon" [tool.coverage.report] exclude_lines = [ @@ -125,7 +134,7 @@ dev = [ "pytest>=8.3.5", "pytest-asyncio>=0.25.3", "pytest-cov>=5.0.0", - "coverage[toml]>=7.6.0", + "coverage[toml]>=7.9", # [run] core setting (added in 7.9); older coverage warns 'unrecognized option' and silently falls back to the slow default core "ruff>=0.11.2", "pytest-xdist[psutil]>=3.6.1", "pyright>=1.1.408", diff --git a/uv.lock b/uv.lock index a73350f5..3fc191e8 100644 --- a/uv.lock +++ b/uv.lock @@ -196,7 +196,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ { name = "claude-code-log-clmail-test", editable = "test/_plugins/clmail" }, - { name = "coverage", extras = ["toml"], specifier = ">=7.6.0" }, + { name = "coverage", extras = ["toml"], specifier = ">=7.9" }, { name = "pyright", specifier = ">=1.1.408" }, { name = "pytest", specifier = ">=8.3.5" }, { name = "pytest-asyncio", specifier = ">=0.25.3" },