From e24c33ff909575c3c42ddf143abb0b28c7f8d980 Mon Sep 17 00:00:00 2001 From: Illia Polosukhin Date: Wed, 4 Mar 2026 20:05:46 +0000 Subject: [PATCH] fix(ci): flush profraw coverage data in E2E teardown (#550) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ironclaw binary only handles SIGINT (via tokio::signal::ctrl_c), not SIGTERM. When conftest.py sent SIGTERM during teardown, the OS killed the process immediately without running atexit handlers, so LLVM never flushed .profraw files. cargo llvm-cov report then found zero profraw files and failed. - Send SIGINT instead of SIGTERM so the existing ctrl_c handler triggers graceful shutdown → main() returns → atexit runs → profraw flushed - Increase shutdown wait from 5s to 10s for graceful cleanup - Add a diagnostic step to verify profraw files exist before the report step, making future issues visible in CI logs Co-authored-by: Claude Opus 4.6 --- .github/workflows/coverage.yml | 12 ++++++++++++ tests/e2e/conftest.py | 7 +++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 1b08c60c..87080d72 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -131,6 +131,18 @@ jobs: RUST_LOG: ironclaw=info RUST_BACKTRACE: "1" + - name: Verify profraw files exist + if: always() + run: | + echo "LLVM_PROFILE_FILE=${LLVM_PROFILE_FILE}" + echo "CARGO_LLVM_COV_TARGET_DIR=${CARGO_LLVM_COV_TARGET_DIR}" + profraw_count=$(find target/ -name '*.profraw' 2>/dev/null | wc -l) + echo "Found ${profraw_count} .profraw files under target/" + find target/ -name '*.profraw' 2>/dev/null || true + if [ "$profraw_count" -eq 0 ]; then + echo "::warning::No .profraw files found — coverage report will fail" + fi + - name: Generate coverage report if: always() run: cargo llvm-cov report --lcov --output-path e2e-coverage.info diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py index af885fb7..23a16657 100644 --- a/tests/e2e/conftest.py +++ b/tests/e2e/conftest.py @@ -133,9 +133,12 @@ async def ironclaw_server(ironclaw_binary, mock_llm_server): ) finally: if proc.returncode is None: - proc.send_signal(signal.SIGTERM) + # Use SIGINT (not SIGTERM) so tokio's ctrl_c handler triggers a + # graceful shutdown. This lets the LLVM coverage runtime run its + # atexit handler and flush .profraw files for cargo-llvm-cov. + proc.send_signal(signal.SIGINT) try: - await asyncio.wait_for(proc.wait(), timeout=5) + await asyncio.wait_for(proc.wait(), timeout=10) except asyncio.TimeoutError: proc.kill()