From fb79ce952efb6d1a0cf0f1b92813a5efc9b396ce Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Fri, 14 Aug 2026 06:24:55 +0000 Subject: [PATCH 1/9] docs: specify GCOV collector compatibility fix --- ...-14-gcov-collector-compatibility-design.md | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-14-gcov-collector-compatibility-design.md diff --git a/docs/superpowers/specs/2026-08-14-gcov-collector-compatibility-design.md b/docs/superpowers/specs/2026-08-14-gcov-collector-compatibility-design.md new file mode 100644 index 0000000000..e0e34d9836 --- /dev/null +++ b/docs/superpowers/specs/2026-08-14-gcov-collector-compatibility-design.md @@ -0,0 +1,29 @@ +# GCOV collector compatibility + +## Problem + +The coverage build runs with GCC 11.4, so its `.gcno` and `.gcda` files must +be read by the matching GCOV 11 tool. The Ubuntu 24 CI-base image currently +provides GCOV 13 only. Fastcov therefore discovers the data files but cannot +convert them, producing an empty LCOV report while the TAP tests themselves +pass and exercise the implementation. + +## Design + +Keep the coverage build toolchain unchanged. Install `gcov-11` in the CI-base +image and use it explicitly for every fastcov invocation that converts raw +GCOV files. The two conversion paths are the standalone test-runner exit trap +and the multi-group per-group collector. Combining existing LCOV files does +not need GCOV and remains unchanged. + +Before conversion, each path will check that `gcov-11` is available and fail +with an actionable error if it is not. This prevents a successful TAP job from +silently uploading an empty coverage report. + +## Verification + +Add a regression check for the collector contract: the CI-base Dockerfile +must install `gcov-11`, and raw-data fastcov calls must use `-g gcov-11`. +Then run the existing `test_ffto_mysql-t` through the normal isolated runner +with coverage enabled. The test must pass and its LCOV output must include +nonzero coverage for `lib/MySQLFFTO.cpp`. From 9542a9523308678cf56ac433b9c03b2fc50135b8 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Fri, 14 Aug 2026 07:07:54 +0000 Subject: [PATCH 2/9] docs: plan GCOV collector compatibility fix --- ...2026-08-14-gcov-collector-compatibility.md | 213 ++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-14-gcov-collector-compatibility.md diff --git a/docs/superpowers/plans/2026-08-14-gcov-collector-compatibility.md b/docs/superpowers/plans/2026-08-14-gcov-collector-compatibility.md new file mode 100644 index 0000000000..c682a343ae --- /dev/null +++ b/docs/superpowers/plans/2026-08-14-gcov-collector-compatibility.md @@ -0,0 +1,213 @@ +# GCOV Collector Compatibility Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Make TAP coverage conversion use GCOV 11, matching the Ubuntu 22 coverage build, so Codecov receives daemon-side FFTO coverage. + +**Architecture:** Keep ProxySQL's GCC 11 coverage build unchanged. The Ubuntu 24 CI-base image gains `gcov-11`; the standalone and multi-group raw-GCOV conversion paths select it explicitly with fastcov's `-g` option and fail instead of silently writing an empty report when it is unavailable. + +**Tech Stack:** Docker, Ubuntu APT, Bash, fastcov, GCOV, GitHub Actions test infrastructure. + +## Global Constraints + +- Coverage build compiler remains GCC/GCOV 11.4. +- Raw `.gcda` conversion must invoke `fastcov -g gcov-11`. +- LCOV-only combination must remain toolchain-agnostic. +- A missing compatible GCOV binary is a CI failure, not a warning. +- Verify using real MySQL FFTO TAP traffic, not a synthetic packet helper. + +--- + +### Task 1: Add a regression check for the coverage-toolchain contract + +**Files:** +- Create: `test/infra/control/validate-coverage-gcov-toolchain.bash` +- Test: `test/infra/control/validate-coverage-gcov-toolchain.bash` + +**Interfaces:** +- Consumes: `test/infra/docker-base/Dockerfile`, `test/infra/control/run-tests-isolated.bash`, and `test/infra/control/run-multi-group.bash`. +- Produces: exit status 0 only when the image installs `gcov-11` and both raw-data fastcov call sites specify `-g gcov-11`. + +- [ ] **Step 1: Write the failing regression check** + +Create an executable Bash script that resolves the repository root from its own path and checks these exact conditions: + +```bash +#!/usr/bin/env bash +set -euo pipefail + +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" +dockerfile="${root}/test/infra/docker-base/Dockerfile" +runner="${root}/test/infra/control/run-tests-isolated.bash" +multi="${root}/test/infra/control/run-multi-group.bash" + +rg -q '^[[:space:]]*gcov-11[[:space:]\\]*$' "${dockerfile}" +test "$(rg -c 'fastcov -b .* -g gcov-11' "${runner}")" -eq 1 +test "$(rg -c 'fastcov -b .* -g gcov-11' "${multi}")" -eq 1 +``` + +- [ ] **Step 2: Run the check and verify it fails** + +Run: + +```bash +bash test/infra/control/validate-coverage-gcov-toolchain.bash +``` + +Expected: nonzero exit because `gcov-11` is absent and neither conversion command selects it. + +- [ ] **Step 3: Commit the failing-test addition** + +```bash +git add test/infra/control/validate-coverage-gcov-toolchain.bash +git commit -m "test(ci): assert GCOV collector compatibility" +``` + +### Task 2: Use the compiler-matching GCOV for raw coverage conversion + +**Files:** +- Modify: `test/infra/docker-base/Dockerfile:8-33` +- Modify: `test/infra/control/run-tests-isolated.bash:391-400` +- Modify: `test/infra/control/run-multi-group.bash:361-378` +- Test: `test/infra/control/validate-coverage-gcov-toolchain.bash` + +**Interfaces:** +- Consumes: `gcov-11` supplied by the CI-base Docker image. +- Produces: `fastcov` LCOV conversion from GCC 11 `.gcda` data, or an immediate nonzero CI exit with an explicit missing-tool message. + +- [ ] **Step 1: Install the matching reader in CI-base** + +Add `gcov-11` to the `apt-get install` list in `test/infra/docker-base/Dockerfile`, adjacent to `lcov`: + +```dockerfile + lcov \\ + gcov-11 \\ + && pip3 install --break-system-packages fastcov \\ +``` + +- [ ] **Step 2: Make standalone conversion fail clearly and use GCOV 11** + +Immediately before the standalone `fastcov` invocation in the exit trap, add: + +```bash +if ! command -v gcov-11 >/dev/null 2>&1; then + echo ">>> ERROR: gcov-11 is required to decode GCC 11 coverage data" >&2 + exit 1 +fi +``` + +Then change the command to: + +```bash +fastcov -b -g gcov-11 -j$(nproc) -l \\ +``` + +Keep the existing source exclusions and report path unchanged. + +- [ ] **Step 3: Make multi-group conversion fail clearly and use GCOV 11** + +Inside the per-group `docker run ... bash -c` block, replace the conditional fastcov wrapper with: + +```bash +set -e +command -v gcov-11 >/dev/null 2>&1 || { + echo ">>> ERROR: gcov-11 is required to decode GCC 11 coverage data" >&2 + exit 1 +} +cd "${GCOV_DIR}" +fastcov -b -g gcov-11 -j4 -l \\ + -e /usr deps \\ + -d . -o "${GROUP_INFO}" >> "${COVERAGE_LOG}" 2>&1 +``` + +Remove the outer warning-only fallback for that raw-data conversion command so the coverage workflow cannot continue after an empty per-group report. + +- [ ] **Step 4: Run the regression check and verify it passes** + +Run: + +```bash +bash test/infra/control/validate-coverage-gcov-toolchain.bash +``` + +Expected: exit 0. + +- [ ] **Step 5: Build and inspect the CI-base image** + +Run: + +```bash +docker build -t proxysql-ci-base:gcov11 test/infra/docker-base +docker run --rm proxysql-ci-base:gcov11 bash -lc 'gcov-11 --version && fastcov --help | grep -F -- "-g GCOV"' +``` + +Expected: GCOV 11 is available and fastcov supports selecting a GCOV executable. + +- [ ] **Step 6: Commit the compatibility fix** + +```bash +git add test/infra/docker-base/Dockerfile test/infra/control/run-tests-isolated.bash test/infra/control/run-multi-group.bash test/infra/control/validate-coverage-gcov-toolchain.bash +git commit -m "fix(ci): use matching GCOV for TAP coverage" +``` + +### Task 3: Prove end-to-end coverage with real FFTO traffic + +**Files:** +- Test: existing `test/tap/tests/test_ffto_mysql-t.cpp` +- Test: generated `ci_infra_logs//coverage-report/.info` + +**Interfaces:** +- Consumes: the rebuilt `proxysql-ci-base:latest`, GCC 11 ProxySQL coverage binary, and isolated legacy MySQL infrastructure. +- Produces: a passing TAP test and nonzero LCOV lines for `lib/MySQLFFTO.cpp`. + +- [ ] **Step 1: Rebuild/tag the fixed CI-base image for the isolated scripts** + +```bash +docker build -t proxysql-ci-base:latest test/infra/docker-base +``` + +- [ ] **Step 2: Build the existing GCC 11 coverage binary and the one TAP executable** + +```bash +WITHGCOV=1 PROXYSQL40=1 make ubuntu22-tap-genai-gcov +docker run --rm -v "$PWD:/opt/proxysql" proxysql/packaging:build-ubuntu22-v4.0.0 \\ + bash -lc 'cd /opt/proxysql/test/tap/tests && WITHGCOV=1 PROXYSQL40=1 PROXYSQL31=1 PROXYSQLFFTO=1 make test_ffto_mysql-t' +``` + +- [ ] **Step 3: Run only real FFTO TAP traffic under coverage** + +```bash +export INFRA_ID=gcov11-ffto +export TAP_GROUP=legacy-g4 +export INFRA_TYPE=infra-mysql57 +export COVERAGE=1 +test/infra/control/ensure-infras.bash +TEST_PY_TAP_INCL=test_ffto_mysql-t test/infra/control/run-tests-isolated.bash +``` + +Expected: `test_ffto_mysql-t` passes, including its fast-forward-session and query-digest assertions. + +- [ ] **Step 4: Assert that LCOV records FFTO execution** + +```bash +info="ci_infra_logs/${INFRA_ID}/coverage-report/${INFRA_ID}.info" +awk '/^SF:.*lib\/MySQLFFTO\.cpp$/{seen=1} seen && /^LH:/{print; exit}' "${info}" +test -s "${info}" +``` + +Expected: a nonzero `LH:` value for `lib/MySQLFFTO.cpp`. + +- [ ] **Step 5: Tear down only the isolated test resources** + +```bash +INFRA_ID="${INFRA_ID}" TAP_GROUP=legacy-g4 INFRA_TYPE=infra-mysql57 \\ + test/infra/control/destroy-infras.bash +``` + +- [ ] **Step 6: Commit any verification-only script changes, if created** + +```bash +git status --short +``` + +Expected: no generated coverage artifacts or infrastructure logs are staged or committed. From b02d795f36b26e7671635eefec7a24efaf7bb508 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Fri, 14 Aug 2026 07:09:10 +0000 Subject: [PATCH 3/9] test(ci): assert GCOV collector compatibility --- .../control/validate-coverage-gcov-toolchain.bash | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100755 test/infra/control/validate-coverage-gcov-toolchain.bash diff --git a/test/infra/control/validate-coverage-gcov-toolchain.bash b/test/infra/control/validate-coverage-gcov-toolchain.bash new file mode 100755 index 0000000000..702be1584b --- /dev/null +++ b/test/infra/control/validate-coverage-gcov-toolchain.bash @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail + +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" +dockerfile="${root}/test/infra/docker-base/Dockerfile" +runner="${root}/test/infra/control/run-tests-isolated.bash" +multi="${root}/test/infra/control/run-multi-group.bash" + +rg -q '^[[:space:]]*gcov-11[[:space:]\\]*$' "${dockerfile}" +test "$(rg -c 'fastcov -b .* -g gcov-11' "${runner}")" -eq 1 +test "$(rg -c 'fastcov -b .* -g gcov-11' "${multi}")" -eq 1 From 6dec0138d4d69408f1f1560f26c90ebe89b7c8b2 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Fri, 14 Aug 2026 07:17:49 +0000 Subject: [PATCH 4/9] fix(ci): use matching GCOV for TAP coverage --- test/infra/control/run-multi-group.bash | 18 ++++++++++-------- test/infra/control/run-tests-isolated.bash | 6 +++++- .../validate-coverage-gcov-toolchain.bash | 4 ++-- test/infra/docker-base/Dockerfile | 1 + 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/test/infra/control/run-multi-group.bash b/test/infra/control/run-multi-group.bash index 4507a93a7f..6e111fb186 100755 --- a/test/infra/control/run-multi-group.bash +++ b/test/infra/control/run-multi-group.bash @@ -368,14 +368,16 @@ if [ "${COVERAGE}" -eq 1 ]; then -e COVERAGE_LOG="${COVERAGE_LOG}" \ proxysql-ci-base:latest \ bash -c ' - if command -v fastcov >/dev/null 2>&1; then - cd "${GCOV_DIR}" - fastcov -b -j4 -l \ - -e /usr deps \ - -d . -o "${GROUP_INFO}" >> "${COVERAGE_LOG}" 2>&1 || \ - echo ">>> WARNING: fastcov failed for ${GCOV_DIR}" >> "${COVERAGE_LOG}" - fi - ' || echo ">>> WARNING: Coverage generation failed for ${group}" + set -e + command -v gcov-11 >/dev/null 2>&1 || { + echo ">>> ERROR: gcov-11 is required to decode GCC 11 coverage data" >&2 + exit 1 + } + cd "${GCOV_DIR}" + fastcov -b -g gcov-11 -j4 -l \ + -e /usr deps \ + -d . -o "${GROUP_INFO}" >> "${COVERAGE_LOG}" 2>&1 + ' else echo ">>> No .gcda files found for ${group}, skipping" fi diff --git a/test/infra/control/run-tests-isolated.bash b/test/infra/control/run-tests-isolated.bash index d4734a4bde..b5dc800f8e 100755 --- a/test/infra/control/run-tests-isolated.bash +++ b/test/infra/control/run-tests-isolated.bash @@ -395,7 +395,11 @@ docker run \ local coverage_log=\"\${COVERAGE_REPORT_DIR}/coverage-generation.log\" echo \">>> Running fastcov on /gcov...\" cd /gcov - fastcov -b -j\$(nproc) -l \ + if ! command -v gcov-11 >/dev/null 2>&1; then + echo \">>> ERROR: gcov-11 is required to decode GCC 11 coverage data\" >&2 + exit 1 + fi + fastcov -b -g gcov-11 -j\$(nproc) -l \ -e /usr deps \ -d . -o \"\${coverage_file}\" >> \"\${coverage_log}\" 2>&1 || echo \">>> WARNING: Coverage generation failed (see \${coverage_log})\" diff --git a/test/infra/control/validate-coverage-gcov-toolchain.bash b/test/infra/control/validate-coverage-gcov-toolchain.bash index 702be1584b..a95be8d04f 100755 --- a/test/infra/control/validate-coverage-gcov-toolchain.bash +++ b/test/infra/control/validate-coverage-gcov-toolchain.bash @@ -7,5 +7,5 @@ runner="${root}/test/infra/control/run-tests-isolated.bash" multi="${root}/test/infra/control/run-multi-group.bash" rg -q '^[[:space:]]*gcov-11[[:space:]\\]*$' "${dockerfile}" -test "$(rg -c 'fastcov -b .* -g gcov-11' "${runner}")" -eq 1 -test "$(rg -c 'fastcov -b .* -g gcov-11' "${multi}")" -eq 1 +test "$(rg -c 'fastcov -b -g gcov-11' "${runner}")" -eq 1 +test "$(rg -c 'fastcov -b -g gcov-11' "${multi}")" -eq 1 diff --git a/test/infra/docker-base/Dockerfile b/test/infra/docker-base/Dockerfile index 00e5a5d05d..c2245236b7 100755 --- a/test/infra/docker-base/Dockerfile +++ b/test/infra/docker-base/Dockerfile @@ -31,6 +31,7 @@ RUN apt-get update -qq && \ php-cli \ php-mysql \ lcov \ + gcov-11 \ && pip3 install --break-system-packages fastcov \ "psycopg[binary]==${PSYCOPG_VERSION}" \ "mysql-connector-python==${MYSQL_CONNECTOR_PYTHON_VERSION}" \ From f4769818e3bf149de82016984f559cdadf02160e Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Fri, 14 Aug 2026 07:30:28 +0000 Subject: [PATCH 5/9] fix(ci): install GCC 11 coverage reader --- .../2026-08-14-gcov-collector-compatibility.md | 18 +++++++++--------- ...8-14-gcov-collector-compatibility-design.md | 13 +++++++------ .../validate-coverage-gcov-toolchain.bash | 2 +- test/infra/docker-base/Dockerfile | 2 +- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/docs/superpowers/plans/2026-08-14-gcov-collector-compatibility.md b/docs/superpowers/plans/2026-08-14-gcov-collector-compatibility.md index c682a343ae..57263c3175 100644 --- a/docs/superpowers/plans/2026-08-14-gcov-collector-compatibility.md +++ b/docs/superpowers/plans/2026-08-14-gcov-collector-compatibility.md @@ -4,7 +4,7 @@ **Goal:** Make TAP coverage conversion use GCOV 11, matching the Ubuntu 22 coverage build, so Codecov receives daemon-side FFTO coverage. -**Architecture:** Keep ProxySQL's GCC 11 coverage build unchanged. The Ubuntu 24 CI-base image gains `gcov-11`; the standalone and multi-group raw-GCOV conversion paths select it explicitly with fastcov's `-g` option and fail instead of silently writing an empty report when it is unavailable. +**Architecture:** Keep ProxySQL's GCC 11 coverage build unchanged. The Ubuntu 24 CI-base image gains `gcc-11`, which provides `gcov-11`; the standalone and multi-group raw-GCOV conversion paths select that executable explicitly with fastcov's `-g` option and fail instead of silently writing an empty report when it is unavailable. **Tech Stack:** Docker, Ubuntu APT, Bash, fastcov, GCOV, GitHub Actions test infrastructure. @@ -26,7 +26,7 @@ **Interfaces:** - Consumes: `test/infra/docker-base/Dockerfile`, `test/infra/control/run-tests-isolated.bash`, and `test/infra/control/run-multi-group.bash`. -- Produces: exit status 0 only when the image installs `gcov-11` and both raw-data fastcov call sites specify `-g gcov-11`. +- Produces: exit status 0 only when the image installs `gcc-11` and both raw-data fastcov call sites specify `-g gcov-11`. - [ ] **Step 1: Write the failing regression check** @@ -41,9 +41,9 @@ dockerfile="${root}/test/infra/docker-base/Dockerfile" runner="${root}/test/infra/control/run-tests-isolated.bash" multi="${root}/test/infra/control/run-multi-group.bash" -rg -q '^[[:space:]]*gcov-11[[:space:]\\]*$' "${dockerfile}" -test "$(rg -c 'fastcov -b .* -g gcov-11' "${runner}")" -eq 1 -test "$(rg -c 'fastcov -b .* -g gcov-11' "${multi}")" -eq 1 +rg -q '^[[:space:]]*gcc-11[[:space:]\\]*$' "${dockerfile}" +test "$(rg -c 'fastcov -b -g gcov-11' "${runner}")" -eq 1 +test "$(rg -c 'fastcov -b -g gcov-11' "${multi}")" -eq 1 ``` - [ ] **Step 2: Run the check and verify it fails** @@ -54,7 +54,7 @@ Run: bash test/infra/control/validate-coverage-gcov-toolchain.bash ``` -Expected: nonzero exit because `gcov-11` is absent and neither conversion command selects it. +Expected: nonzero exit because `gcc-11` is absent and neither conversion command selects it. - [ ] **Step 3: Commit the failing-test addition** @@ -72,16 +72,16 @@ git commit -m "test(ci): assert GCOV collector compatibility" - Test: `test/infra/control/validate-coverage-gcov-toolchain.bash` **Interfaces:** -- Consumes: `gcov-11` supplied by the CI-base Docker image. +- Consumes: the `gcov-11` executable supplied by `gcc-11` in the CI-base Docker image. - Produces: `fastcov` LCOV conversion from GCC 11 `.gcda` data, or an immediate nonzero CI exit with an explicit missing-tool message. - [ ] **Step 1: Install the matching reader in CI-base** -Add `gcov-11` to the `apt-get install` list in `test/infra/docker-base/Dockerfile`, adjacent to `lcov`: +Add `gcc-11` to the `apt-get install` list in `test/infra/docker-base/Dockerfile`, adjacent to `lcov`. This package supplies `/usr/bin/gcov-11`: ```dockerfile lcov \\ - gcov-11 \\ + gcc-11 \\ && pip3 install --break-system-packages fastcov \\ ``` diff --git a/docs/superpowers/specs/2026-08-14-gcov-collector-compatibility-design.md b/docs/superpowers/specs/2026-08-14-gcov-collector-compatibility-design.md index e0e34d9836..439a20a73c 100644 --- a/docs/superpowers/specs/2026-08-14-gcov-collector-compatibility-design.md +++ b/docs/superpowers/specs/2026-08-14-gcov-collector-compatibility-design.md @@ -10,11 +10,12 @@ pass and exercise the implementation. ## Design -Keep the coverage build toolchain unchanged. Install `gcov-11` in the CI-base -image and use it explicitly for every fastcov invocation that converts raw -GCOV files. The two conversion paths are the standalone test-runner exit trap -and the multi-group per-group collector. Combining existing LCOV files does -not need GCOV and remains unchanged. +Keep the coverage build toolchain unchanged. Install `gcc-11`, which provides +the `gcov-11` executable, in the CI-base image and use that executable +explicitly for every fastcov invocation that converts raw GCOV files. The two +conversion paths are the standalone test-runner exit trap and the multi-group +per-group collector. Combining existing LCOV files does not need GCOV and +remains unchanged. Before conversion, each path will check that `gcov-11` is available and fail with an actionable error if it is not. This prevents a successful TAP job from @@ -23,7 +24,7 @@ silently uploading an empty coverage report. ## Verification Add a regression check for the collector contract: the CI-base Dockerfile -must install `gcov-11`, and raw-data fastcov calls must use `-g gcov-11`. +must install `gcc-11`, and raw-data fastcov calls must use `-g gcov-11`. Then run the existing `test_ffto_mysql-t` through the normal isolated runner with coverage enabled. The test must pass and its LCOV output must include nonzero coverage for `lib/MySQLFFTO.cpp`. diff --git a/test/infra/control/validate-coverage-gcov-toolchain.bash b/test/infra/control/validate-coverage-gcov-toolchain.bash index a95be8d04f..64a114ff16 100755 --- a/test/infra/control/validate-coverage-gcov-toolchain.bash +++ b/test/infra/control/validate-coverage-gcov-toolchain.bash @@ -6,6 +6,6 @@ dockerfile="${root}/test/infra/docker-base/Dockerfile" runner="${root}/test/infra/control/run-tests-isolated.bash" multi="${root}/test/infra/control/run-multi-group.bash" -rg -q '^[[:space:]]*gcov-11[[:space:]\\]*$' "${dockerfile}" +rg -q '^[[:space:]]*gcc-11[[:space:]\\]*$' "${dockerfile}" test "$(rg -c 'fastcov -b -g gcov-11' "${runner}")" -eq 1 test "$(rg -c 'fastcov -b -g gcov-11' "${multi}")" -eq 1 diff --git a/test/infra/docker-base/Dockerfile b/test/infra/docker-base/Dockerfile index c2245236b7..b917d84e37 100755 --- a/test/infra/docker-base/Dockerfile +++ b/test/infra/docker-base/Dockerfile @@ -31,7 +31,7 @@ RUN apt-get update -qq && \ php-cli \ php-mysql \ lcov \ - gcov-11 \ + gcc-11 \ && pip3 install --break-system-packages fastcov \ "psycopg[binary]==${PSYCOPG_VERSION}" \ "mysql-connector-python==${MYSQL_CONNECTOR_PYTHON_VERSION}" \ From 370124a8ee42460fc93c8a8b60eeb480bf21b5ff Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Fri, 14 Aug 2026 08:58:57 +0000 Subject: [PATCH 6/9] fix(ci): harden coverage collection failures --- .../2026-08-14-gcov-collector-compatibility.md | 18 ++++++++++++++---- test/infra/control/run-multi-group.bash | 11 ++++++++++- test/infra/control/run-tests-isolated.bash | 2 +- .../validate-coverage-gcov-toolchain.bash | 12 ++++++++++-- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/docs/superpowers/plans/2026-08-14-gcov-collector-compatibility.md b/docs/superpowers/plans/2026-08-14-gcov-collector-compatibility.md index 57263c3175..f1482dbed2 100644 --- a/docs/superpowers/plans/2026-08-14-gcov-collector-compatibility.md +++ b/docs/superpowers/plans/2026-08-14-gcov-collector-compatibility.md @@ -2,7 +2,7 @@ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. -**Goal:** Make TAP coverage conversion use GCOV 11, matching the Ubuntu 22 coverage build, so Codecov receives daemon-side FFTO coverage. +**Goal:** Make TAP coverage conversion use GCOV 11, matching the Ubuntu 24 `tap-genai-gcov` coverage build, so Codecov receives daemon-side FFTO coverage. **Architecture:** Keep ProxySQL's GCC 11 coverage build unchanged. The Ubuntu 24 CI-base image gains `gcc-11`, which provides `gcov-11`; the standalone and multi-group raw-GCOV conversion paths select that executable explicitly with fastcov's `-g` option and fail instead of silently writing an empty report when it is unavailable. @@ -169,8 +169,8 @@ docker build -t proxysql-ci-base:latest test/infra/docker-base - [ ] **Step 2: Build the existing GCC 11 coverage binary and the one TAP executable** ```bash -WITHGCOV=1 PROXYSQL40=1 make ubuntu22-tap-genai-gcov -docker run --rm -v "$PWD:/opt/proxysql" proxysql/packaging:build-ubuntu22-v4.0.0 \\ +WITHGCOV=1 PROXYSQL40=1 make ubuntu24-tap-genai-gcov +docker run --rm -v "$PWD:/opt/proxysql" proxysql/packaging:build-ubuntu24 \\ bash -lc 'cd /opt/proxysql/test/tap/tests && WITHGCOV=1 PROXYSQL40=1 PROXYSQL31=1 PROXYSQLFFTO=1 make test_ffto_mysql-t' ``` @@ -191,8 +191,18 @@ Expected: `test_ffto_mysql-t` passes, including its fast-forward-session and que ```bash info="ci_infra_logs/${INFRA_ID}/coverage-report/${INFRA_ID}.info" -awk '/^SF:.*lib\/MySQLFFTO\.cpp$/{seen=1} seen && /^LH:/{print; exit}' "${info}" test -s "${info}" +if ! awk ' + /^SF:/ { in_target = ($0 == "SF:lib/MySQLFFTO.cpp"); next } + in_target && /^LH:/ { + found = (substr($0, 4) + 0) > 0 + exit + } + END { exit(found ? 0 : 1) } +' "${info}"; then + echo "Expected nonzero LCOV coverage for lib/MySQLFFTO.cpp" >&2 + exit 1 +fi ``` Expected: a nonzero `LH:` value for `lib/MySQLFFTO.cpp`. diff --git a/test/infra/control/run-multi-group.bash b/test/infra/control/run-multi-group.bash index 6e111fb186..7e92f2cb56 100755 --- a/test/infra/control/run-multi-group.bash +++ b/test/infra/control/run-multi-group.bash @@ -353,6 +353,7 @@ if [ "${COVERAGE}" -eq 1 ]; then # Each group's test-runner already copied .gcno adjacent to .gcda (in its # EXIT trap). We run fastcov sequentially per group — no concurrent gcov. COVERAGE_LOG="${COMBINED_COVERAGE_DIR}/coverage-generation.log" + COVERAGE_FAILED=0 for group in ${TAP_GROUPS}; do infra_id="${group}-${RUN_ID}" gcov_dir="${WORKSPACE}/ci_infra_logs/${infra_id}/gcov" @@ -360,7 +361,7 @@ if [ "${COVERAGE}" -eq 1 ]; then if [ -d "${gcov_dir}" ] && [ "$(find "${gcov_dir}" -name '*.gcda' 2>/dev/null | head -1)" ]; then echo ">>> Generating coverage for ${group} from ${gcov_dir}..." - docker run --rm \ + if ! docker run --rm \ -v "${WORKSPACE}:${WORKSPACE}" \ -e WORKSPACE="${WORKSPACE}" \ -e GCOV_DIR="${gcov_dir}" \ @@ -378,6 +379,10 @@ if [ "${COVERAGE}" -eq 1 ]; then -e /usr deps \ -d . -o "${GROUP_INFO}" >> "${COVERAGE_LOG}" 2>&1 ' + then + echo ">>> ERROR: Coverage generation failed for ${group} (see ${COVERAGE_LOG})" >&2 + COVERAGE_FAILED=1 + fi else echo ">>> No .gcda files found for ${group}, skipping" fi @@ -474,6 +479,10 @@ if [ "${COVERAGE}" -eq 1 ]; then else echo ">>> No coverage files found to combine" fi + if [ "${COVERAGE_FAILED}" -ne 0 ]; then + echo ">>> ERROR: One or more groups failed coverage generation" >&2 + OVERALL_FAILED=1 + fi echo "" fi diff --git a/test/infra/control/run-tests-isolated.bash b/test/infra/control/run-tests-isolated.bash index b5dc800f8e..cc751aa278 100755 --- a/test/infra/control/run-tests-isolated.bash +++ b/test/infra/control/run-tests-isolated.bash @@ -401,7 +401,7 @@ docker run \ fi fastcov -b -g gcov-11 -j\$(nproc) -l \ -e /usr deps \ - -d . -o \"\${coverage_file}\" >> \"\${coverage_log}\" 2>&1 || echo \">>> WARNING: Coverage generation failed (see \${coverage_log})\" + -d . -o \"\${coverage_file}\" >> \"\${coverage_log}\" 2>&1 if [ -f \"\${coverage_file}\" ]; then echo \">>> Coverage report generated: \${coverage_file}\" diff --git a/test/infra/control/validate-coverage-gcov-toolchain.bash b/test/infra/control/validate-coverage-gcov-toolchain.bash index 64a114ff16..8e1938d0c9 100755 --- a/test/infra/control/validate-coverage-gcov-toolchain.bash +++ b/test/infra/control/validate-coverage-gcov-toolchain.bash @@ -7,5 +7,13 @@ runner="${root}/test/infra/control/run-tests-isolated.bash" multi="${root}/test/infra/control/run-multi-group.bash" rg -q '^[[:space:]]*gcc-11[[:space:]\\]*$' "${dockerfile}" -test "$(rg -c 'fastcov -b -g gcov-11' "${runner}")" -eq 1 -test "$(rg -c 'fastcov -b -g gcov-11' "${multi}")" -eq 1 +for file in "${runner}" "${multi}"; do + # `-C` combines existing LCOV files and does not decode raw GCOV data. + # Every other fastcov branch-coverage invocation must choose gcov-11. + raw_fastcov_calls="$(rg -P -c 'fastcov -b(?!.* -C )' "${file}")" + pinned_fastcov_calls="$(rg -c 'fastcov -b -g gcov-11' "${file}")" + test "${raw_fastcov_calls}" -gt 0 + test "${raw_fastcov_calls}" -eq "${pinned_fastcov_calls}" + rg -q 'command -v gcov-11' "${file}" + rg -q 'gcov-11 is required to decode GCC 11 coverage data' "${file}" +done From eb2d1f06ac2f84cdc6668b65f61aecd10aa9dadd Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Fri, 14 Aug 2026 10:56:57 +0000 Subject: [PATCH 7/9] fix(ci): match GCOV reader to coverage build --- ...2026-08-14-gcov-collector-compatibility.md | 223 ------------------ ...-14-gcov-collector-compatibility-design.md | 30 --- test/infra/control/run-multi-group.bash | 10 +- test/infra/control/run-tests-isolated.bash | 10 +- .../validate-coverage-gcov-toolchain.bash | 12 +- test/infra/docker-base/Dockerfile | 1 - 6 files changed, 16 insertions(+), 270 deletions(-) delete mode 100644 docs/superpowers/plans/2026-08-14-gcov-collector-compatibility.md delete mode 100644 docs/superpowers/specs/2026-08-14-gcov-collector-compatibility-design.md diff --git a/docs/superpowers/plans/2026-08-14-gcov-collector-compatibility.md b/docs/superpowers/plans/2026-08-14-gcov-collector-compatibility.md deleted file mode 100644 index f1482dbed2..0000000000 --- a/docs/superpowers/plans/2026-08-14-gcov-collector-compatibility.md +++ /dev/null @@ -1,223 +0,0 @@ -# GCOV Collector Compatibility Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Make TAP coverage conversion use GCOV 11, matching the Ubuntu 24 `tap-genai-gcov` coverage build, so Codecov receives daemon-side FFTO coverage. - -**Architecture:** Keep ProxySQL's GCC 11 coverage build unchanged. The Ubuntu 24 CI-base image gains `gcc-11`, which provides `gcov-11`; the standalone and multi-group raw-GCOV conversion paths select that executable explicitly with fastcov's `-g` option and fail instead of silently writing an empty report when it is unavailable. - -**Tech Stack:** Docker, Ubuntu APT, Bash, fastcov, GCOV, GitHub Actions test infrastructure. - -## Global Constraints - -- Coverage build compiler remains GCC/GCOV 11.4. -- Raw `.gcda` conversion must invoke `fastcov -g gcov-11`. -- LCOV-only combination must remain toolchain-agnostic. -- A missing compatible GCOV binary is a CI failure, not a warning. -- Verify using real MySQL FFTO TAP traffic, not a synthetic packet helper. - ---- - -### Task 1: Add a regression check for the coverage-toolchain contract - -**Files:** -- Create: `test/infra/control/validate-coverage-gcov-toolchain.bash` -- Test: `test/infra/control/validate-coverage-gcov-toolchain.bash` - -**Interfaces:** -- Consumes: `test/infra/docker-base/Dockerfile`, `test/infra/control/run-tests-isolated.bash`, and `test/infra/control/run-multi-group.bash`. -- Produces: exit status 0 only when the image installs `gcc-11` and both raw-data fastcov call sites specify `-g gcov-11`. - -- [ ] **Step 1: Write the failing regression check** - -Create an executable Bash script that resolves the repository root from its own path and checks these exact conditions: - -```bash -#!/usr/bin/env bash -set -euo pipefail - -root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" -dockerfile="${root}/test/infra/docker-base/Dockerfile" -runner="${root}/test/infra/control/run-tests-isolated.bash" -multi="${root}/test/infra/control/run-multi-group.bash" - -rg -q '^[[:space:]]*gcc-11[[:space:]\\]*$' "${dockerfile}" -test "$(rg -c 'fastcov -b -g gcov-11' "${runner}")" -eq 1 -test "$(rg -c 'fastcov -b -g gcov-11' "${multi}")" -eq 1 -``` - -- [ ] **Step 2: Run the check and verify it fails** - -Run: - -```bash -bash test/infra/control/validate-coverage-gcov-toolchain.bash -``` - -Expected: nonzero exit because `gcc-11` is absent and neither conversion command selects it. - -- [ ] **Step 3: Commit the failing-test addition** - -```bash -git add test/infra/control/validate-coverage-gcov-toolchain.bash -git commit -m "test(ci): assert GCOV collector compatibility" -``` - -### Task 2: Use the compiler-matching GCOV for raw coverage conversion - -**Files:** -- Modify: `test/infra/docker-base/Dockerfile:8-33` -- Modify: `test/infra/control/run-tests-isolated.bash:391-400` -- Modify: `test/infra/control/run-multi-group.bash:361-378` -- Test: `test/infra/control/validate-coverage-gcov-toolchain.bash` - -**Interfaces:** -- Consumes: the `gcov-11` executable supplied by `gcc-11` in the CI-base Docker image. -- Produces: `fastcov` LCOV conversion from GCC 11 `.gcda` data, or an immediate nonzero CI exit with an explicit missing-tool message. - -- [ ] **Step 1: Install the matching reader in CI-base** - -Add `gcc-11` to the `apt-get install` list in `test/infra/docker-base/Dockerfile`, adjacent to `lcov`. This package supplies `/usr/bin/gcov-11`: - -```dockerfile - lcov \\ - gcc-11 \\ - && pip3 install --break-system-packages fastcov \\ -``` - -- [ ] **Step 2: Make standalone conversion fail clearly and use GCOV 11** - -Immediately before the standalone `fastcov` invocation in the exit trap, add: - -```bash -if ! command -v gcov-11 >/dev/null 2>&1; then - echo ">>> ERROR: gcov-11 is required to decode GCC 11 coverage data" >&2 - exit 1 -fi -``` - -Then change the command to: - -```bash -fastcov -b -g gcov-11 -j$(nproc) -l \\ -``` - -Keep the existing source exclusions and report path unchanged. - -- [ ] **Step 3: Make multi-group conversion fail clearly and use GCOV 11** - -Inside the per-group `docker run ... bash -c` block, replace the conditional fastcov wrapper with: - -```bash -set -e -command -v gcov-11 >/dev/null 2>&1 || { - echo ">>> ERROR: gcov-11 is required to decode GCC 11 coverage data" >&2 - exit 1 -} -cd "${GCOV_DIR}" -fastcov -b -g gcov-11 -j4 -l \\ - -e /usr deps \\ - -d . -o "${GROUP_INFO}" >> "${COVERAGE_LOG}" 2>&1 -``` - -Remove the outer warning-only fallback for that raw-data conversion command so the coverage workflow cannot continue after an empty per-group report. - -- [ ] **Step 4: Run the regression check and verify it passes** - -Run: - -```bash -bash test/infra/control/validate-coverage-gcov-toolchain.bash -``` - -Expected: exit 0. - -- [ ] **Step 5: Build and inspect the CI-base image** - -Run: - -```bash -docker build -t proxysql-ci-base:gcov11 test/infra/docker-base -docker run --rm proxysql-ci-base:gcov11 bash -lc 'gcov-11 --version && fastcov --help | grep -F -- "-g GCOV"' -``` - -Expected: GCOV 11 is available and fastcov supports selecting a GCOV executable. - -- [ ] **Step 6: Commit the compatibility fix** - -```bash -git add test/infra/docker-base/Dockerfile test/infra/control/run-tests-isolated.bash test/infra/control/run-multi-group.bash test/infra/control/validate-coverage-gcov-toolchain.bash -git commit -m "fix(ci): use matching GCOV for TAP coverage" -``` - -### Task 3: Prove end-to-end coverage with real FFTO traffic - -**Files:** -- Test: existing `test/tap/tests/test_ffto_mysql-t.cpp` -- Test: generated `ci_infra_logs//coverage-report/.info` - -**Interfaces:** -- Consumes: the rebuilt `proxysql-ci-base:latest`, GCC 11 ProxySQL coverage binary, and isolated legacy MySQL infrastructure. -- Produces: a passing TAP test and nonzero LCOV lines for `lib/MySQLFFTO.cpp`. - -- [ ] **Step 1: Rebuild/tag the fixed CI-base image for the isolated scripts** - -```bash -docker build -t proxysql-ci-base:latest test/infra/docker-base -``` - -- [ ] **Step 2: Build the existing GCC 11 coverage binary and the one TAP executable** - -```bash -WITHGCOV=1 PROXYSQL40=1 make ubuntu24-tap-genai-gcov -docker run --rm -v "$PWD:/opt/proxysql" proxysql/packaging:build-ubuntu24 \\ - bash -lc 'cd /opt/proxysql/test/tap/tests && WITHGCOV=1 PROXYSQL40=1 PROXYSQL31=1 PROXYSQLFFTO=1 make test_ffto_mysql-t' -``` - -- [ ] **Step 3: Run only real FFTO TAP traffic under coverage** - -```bash -export INFRA_ID=gcov11-ffto -export TAP_GROUP=legacy-g4 -export INFRA_TYPE=infra-mysql57 -export COVERAGE=1 -test/infra/control/ensure-infras.bash -TEST_PY_TAP_INCL=test_ffto_mysql-t test/infra/control/run-tests-isolated.bash -``` - -Expected: `test_ffto_mysql-t` passes, including its fast-forward-session and query-digest assertions. - -- [ ] **Step 4: Assert that LCOV records FFTO execution** - -```bash -info="ci_infra_logs/${INFRA_ID}/coverage-report/${INFRA_ID}.info" -test -s "${info}" -if ! awk ' - /^SF:/ { in_target = ($0 == "SF:lib/MySQLFFTO.cpp"); next } - in_target && /^LH:/ { - found = (substr($0, 4) + 0) > 0 - exit - } - END { exit(found ? 0 : 1) } -' "${info}"; then - echo "Expected nonzero LCOV coverage for lib/MySQLFFTO.cpp" >&2 - exit 1 -fi -``` - -Expected: a nonzero `LH:` value for `lib/MySQLFFTO.cpp`. - -- [ ] **Step 5: Tear down only the isolated test resources** - -```bash -INFRA_ID="${INFRA_ID}" TAP_GROUP=legacy-g4 INFRA_TYPE=infra-mysql57 \\ - test/infra/control/destroy-infras.bash -``` - -- [ ] **Step 6: Commit any verification-only script changes, if created** - -```bash -git status --short -``` - -Expected: no generated coverage artifacts or infrastructure logs are staged or committed. diff --git a/docs/superpowers/specs/2026-08-14-gcov-collector-compatibility-design.md b/docs/superpowers/specs/2026-08-14-gcov-collector-compatibility-design.md deleted file mode 100644 index 439a20a73c..0000000000 --- a/docs/superpowers/specs/2026-08-14-gcov-collector-compatibility-design.md +++ /dev/null @@ -1,30 +0,0 @@ -# GCOV collector compatibility - -## Problem - -The coverage build runs with GCC 11.4, so its `.gcno` and `.gcda` files must -be read by the matching GCOV 11 tool. The Ubuntu 24 CI-base image currently -provides GCOV 13 only. Fastcov therefore discovers the data files but cannot -convert them, producing an empty LCOV report while the TAP tests themselves -pass and exercise the implementation. - -## Design - -Keep the coverage build toolchain unchanged. Install `gcc-11`, which provides -the `gcov-11` executable, in the CI-base image and use that executable -explicitly for every fastcov invocation that converts raw GCOV files. The two -conversion paths are the standalone test-runner exit trap and the multi-group -per-group collector. Combining existing LCOV files does not need GCOV and -remains unchanged. - -Before conversion, each path will check that `gcov-11` is available and fail -with an actionable error if it is not. This prevents a successful TAP job from -silently uploading an empty coverage report. - -## Verification - -Add a regression check for the collector contract: the CI-base Dockerfile -must install `gcc-11`, and raw-data fastcov calls must use `-g gcov-11`. -Then run the existing `test_ffto_mysql-t` through the normal isolated runner -with coverage enabled. The test must pass and its LCOV output must include -nonzero coverage for `lib/MySQLFFTO.cpp`. diff --git a/test/infra/control/run-multi-group.bash b/test/infra/control/run-multi-group.bash index 7e92f2cb56..775297a101 100755 --- a/test/infra/control/run-multi-group.bash +++ b/test/infra/control/run-multi-group.bash @@ -370,14 +370,14 @@ if [ "${COVERAGE}" -eq 1 ]; then proxysql-ci-base:latest \ bash -c ' set -e - command -v gcov-11 >/dev/null 2>&1 || { - echo ">>> ERROR: gcov-11 is required to decode GCC 11 coverage data" >&2 - exit 1 - } cd "${GCOV_DIR}" - fastcov -b -g gcov-11 -j4 -l \ + fastcov -b -j4 -l \ -e /usr deps \ -d . -o "${GROUP_INFO}" >> "${COVERAGE_LOG}" 2>&1 + if [ ! -s "${GROUP_INFO}" ]; then + echo ">>> ERROR: fastcov produced an empty coverage report for ${GCOV_DIR}" >&2 + exit 1 + fi ' then echo ">>> ERROR: Coverage generation failed for ${group} (see ${COVERAGE_LOG})" >&2 diff --git a/test/infra/control/run-tests-isolated.bash b/test/infra/control/run-tests-isolated.bash index cc751aa278..46c27006fc 100755 --- a/test/infra/control/run-tests-isolated.bash +++ b/test/infra/control/run-tests-isolated.bash @@ -395,13 +395,13 @@ docker run \ local coverage_log=\"\${COVERAGE_REPORT_DIR}/coverage-generation.log\" echo \">>> Running fastcov on /gcov...\" cd /gcov - if ! command -v gcov-11 >/dev/null 2>&1; then - echo \">>> ERROR: gcov-11 is required to decode GCC 11 coverage data\" >&2 - exit 1 - fi - fastcov -b -g gcov-11 -j\$(nproc) -l \ + fastcov -b -j\$(nproc) -l \ -e /usr deps \ -d . -o \"\${coverage_file}\" >> \"\${coverage_log}\" 2>&1 + if [ ! -s \"\${coverage_file}\" ]; then + echo \">>> ERROR: fastcov produced an empty coverage report (see \${coverage_log})\" >&2 + exit 1 + fi if [ -f \"\${coverage_file}\" ]; then echo \">>> Coverage report generated: \${coverage_file}\" diff --git a/test/infra/control/validate-coverage-gcov-toolchain.bash b/test/infra/control/validate-coverage-gcov-toolchain.bash index 8e1938d0c9..2bb233093d 100755 --- a/test/infra/control/validate-coverage-gcov-toolchain.bash +++ b/test/infra/control/validate-coverage-gcov-toolchain.bash @@ -6,14 +6,14 @@ dockerfile="${root}/test/infra/docker-base/Dockerfile" runner="${root}/test/infra/control/run-tests-isolated.bash" multi="${root}/test/infra/control/run-multi-group.bash" -rg -q '^[[:space:]]*gcc-11[[:space:]\\]*$' "${dockerfile}" +! rg -q '^[[:space:]]*gcc-11[[:space:]\\]*$' "${dockerfile}" for file in "${runner}" "${multi}"; do # `-C` combines existing LCOV files and does not decode raw GCOV data. - # Every other fastcov branch-coverage invocation must choose gcov-11. + # Every other fastcov branch-coverage invocation must use the image's + # default gcov, which matches the compiler used by the build handoff. raw_fastcov_calls="$(rg -P -c 'fastcov -b(?!.* -C )' "${file}")" - pinned_fastcov_calls="$(rg -c 'fastcov -b -g gcov-11' "${file}")" test "${raw_fastcov_calls}" -gt 0 - test "${raw_fastcov_calls}" -eq "${pinned_fastcov_calls}" - rg -q 'command -v gcov-11' "${file}" - rg -q 'gcov-11 is required to decode GCC 11 coverage data' "${file}" + ! rg -q 'fastcov -b -g gcov-[0-9]+' "${file}" done +rg -q -- '-s .*coverage_file' "${runner}" +rg -q -- '-s .*GROUP_INFO' "${multi}" diff --git a/test/infra/docker-base/Dockerfile b/test/infra/docker-base/Dockerfile index b917d84e37..00e5a5d05d 100755 --- a/test/infra/docker-base/Dockerfile +++ b/test/infra/docker-base/Dockerfile @@ -31,7 +31,6 @@ RUN apt-get update -qq && \ php-cli \ php-mysql \ lcov \ - gcc-11 \ && pip3 install --break-system-packages fastcov \ "psycopg[binary]==${PSYCOPG_VERSION}" \ "mysql-connector-python==${MYSQL_CONNECTOR_PYTHON_VERSION}" \ From 75f3f76e01e753394e1a3b7e478b85f7e02ce325 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Fri, 14 Aug 2026 18:08:48 +0000 Subject: [PATCH 8/9] fix(ci): flush daemon coverage after TAP groups --- .github/workflows/CI-lint-groups-json.yml | 2 + .../plans/2026-08-14-final-gcov-dump.md | 54 ++++++++ test/infra/control/coverage-exit-status.bash | 11 ++ test/infra/control/dump-proxysql-gcov.bash | 24 ++++ .../control/fixtures/record-mysql-argv.bash | 8 ++ test/infra/control/run-tests-isolated.bash | 24 +++- test/infra/control/test-final-gcov-dump.bash | 131 ++++++++++++++++++ .../validate-coverage-gcov-toolchain.bash | 14 +- test/scripts/bin/proxysql-tester.py | 4 - 9 files changed, 265 insertions(+), 7 deletions(-) create mode 100644 docs/superpowers/plans/2026-08-14-final-gcov-dump.md create mode 100644 test/infra/control/coverage-exit-status.bash create mode 100755 test/infra/control/dump-proxysql-gcov.bash create mode 100755 test/infra/control/fixtures/record-mysql-argv.bash create mode 100755 test/infra/control/test-final-gcov-dump.bash diff --git a/.github/workflows/CI-lint-groups-json.yml b/.github/workflows/CI-lint-groups-json.yml index 5de67b0a2b..fc94a35c44 100644 --- a/.github/workflows/CI-lint-groups-json.yml +++ b/.github/workflows/CI-lint-groups-json.yml @@ -25,6 +25,8 @@ jobs: run: python3 test/tap/groups/lint_groups_json.py - name: Check every TAP source is registered in groups.json run: python3 test/tap/groups/check_groups.py --source + - name: Check coverage collector invariants + run: test/infra/control/validate-coverage-gcov-toolchain.bash - name: Check group infra/workflow coverage (warn-only) # Warns when a group references a missing infra (phantom), or when no # workflow on either branch can select the group at all -- meaning diff --git a/docs/superpowers/plans/2026-08-14-final-gcov-dump.md b/docs/superpowers/plans/2026-08-14-final-gcov-dump.md new file mode 100644 index 0000000000..7eaa8f0c0d --- /dev/null +++ b/docs/superpowers/plans/2026-08-14-final-gcov-dump.md @@ -0,0 +1,54 @@ +# Final GCOV Dump Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Preserve daemon-side coverage from every TAP test by dumping ProxySQL's in-memory GCOV counters exactly once after each isolated TAP group. + +**Architecture:** `run-tests-isolated.bash` already has an EXIT trap that runs after the TAP suite and before ProxySQL is stopped. The trap will issue `PROXYSQL GCOV DUMP`, then decode the resulting GCDA files. The obsolete per-TAP-test dump in `proxysql-tester.py` will be removed because GCC 13 ignores later dumps unless counters are reset. Deliberate dump/decode/reset stages for non-TAP modes remain unchanged. + +**Tech Stack:** Bash, Python TAP harness, GCC 13 GCOV, fastcov/LCOV. + +## Global Constraints + +- Generate real TAP traffic without changing functional tests. +- Dump the long-running ProxySQL process once per isolated TAP group. +- Collect coverage on successful, failed, and timed-out test runs. +- Preserve the original test exit status unless coverage collection itself is the only failure. + +--- + +### Task 1: Add the daemon-dump regression validator + +**Files:** +- Create: `test/infra/control/test-final-gcov-dump.bash` +- Create: `test/infra/control/dump-proxysql-gcov.bash` +- Create: `test/infra/control/coverage-exit-status.bash` +- Create: `test/infra/control/fixtures/record-mysql-argv.bash` +- Modify: `test/infra/control/validate-coverage-gcov-toolchain.bash` +- Modify: `.github/workflows/CI-lint-groups-json.yml` + +- [x] Write a validator requiring the final dump to precede `fastcov`, forbidding per-test dumps, bounding the admin call, and requiring dump failures to propagate. +- [x] Run it and confirm that the current pipeline fails the assertions. +- [x] Wire the validator into the existing lightweight CI lint workflow. + +### Task 2: Dump daemon counters once at group exit + +**Files:** +- Modify: `test/infra/control/run-tests-isolated.bash` +- Modify: `test/scripts/bin/proxysql-tester.py` + +- [x] Remove the dump after each TAP executable. +- [x] Issue one admin dump in the coverage EXIT trap before GCDA decoding. +- [x] Record dump failure without skipping the remaining diagnostic collection. +- [x] Return the test failure when tests failed; otherwise return the coverage failure. +- [x] Run the validator until it passes. + +### Task 3: Verify and publish + +**Files:** +- Verify all modified files. + +- [x] Run the coverage validators, Bash syntax checks, Python compilation, and `git diff --check`. +- [x] Review the final diff against `origin/v3.0` for unrelated changes. +- [ ] Commit and push the focused fix to PR #6062. +- [ ] Inspect the resulting GitHub Actions coverage run and compare daemon-side line coverage with the previous upload. diff --git a/test/infra/control/coverage-exit-status.bash b/test/infra/control/coverage-exit-status.bash new file mode 100644 index 0000000000..8c68dbd313 --- /dev/null +++ b/test/infra/control/coverage-exit-status.bash @@ -0,0 +1,11 @@ +#!/bin/bash + +coverage_exit_status() { + local test_exit="${1:?test exit status required}" + local coverage_exit="${2:?coverage exit status required}" + + if [ "${test_exit}" -ne 0 ]; then + return "${test_exit}" + fi + return "${coverage_exit}" +} diff --git a/test/infra/control/dump-proxysql-gcov.bash b/test/infra/control/dump-proxysql-gcov.bash new file mode 100755 index 0000000000..596ae6a3fb --- /dev/null +++ b/test/infra/control/dump-proxysql-gcov.bash @@ -0,0 +1,24 @@ +#!/bin/bash +set -euo pipefail + +mysql_client="${MYSQL_CLIENT_BIN:-mysql}" +admin_user="${TAP_ADMINUSERNAME:-radmin}" +admin_password="${TAP_ADMINPASSWORD:-radmin}" +admin_host="${TAP_ADMINHOST:-proxysql}" +admin_port="${TAP_ADMINPORT:-6032}" +dump_timeout="${GCOV_DUMP_TIMEOUT_SECONDS:-15}" + +if [[ ! "${dump_timeout}" =~ ^([0-9]+([.][0-9]*)?|[.][0-9]+)$ ]] \ + || [[ "${dump_timeout}" =~ ^0*([.]0*)?$ ]]; then + echo "GCOV_DUMP_TIMEOUT_SECONDS must be a positive number of seconds" >&2 + exit 64 +fi + +exec timeout --signal=TERM --kill-after=5s "${dump_timeout}s" "${mysql_client}" \ + "-u${admin_user}" \ + "-p${admin_password}" \ + "-h${admin_host}" \ + "-P${admin_port}" \ + --batch \ + --skip-column-names \ + -e "PROXYSQL GCOV DUMP" diff --git a/test/infra/control/fixtures/record-mysql-argv.bash b/test/infra/control/fixtures/record-mysql-argv.bash new file mode 100755 index 0000000000..63b1e6bac8 --- /dev/null +++ b/test/infra/control/fixtures/record-mysql-argv.bash @@ -0,0 +1,8 @@ +#!/bin/bash +set -u + +if [ -n "${MYSQL_DELAY_SECONDS:-}" ]; then + sleep "${MYSQL_DELAY_SECONDS}" +fi +printf '%s\n' "$@" > "${MYSQL_RECORD_FILE:?MYSQL_RECORD_FILE is required}" +exit "${MYSQL_EXIT_CODE:-0}" diff --git a/test/infra/control/run-tests-isolated.bash b/test/infra/control/run-tests-isolated.bash index 46c27006fc..12fcff6c13 100755 --- a/test/infra/control/run-tests-isolated.bash +++ b/test/infra/control/run-tests-isolated.bash @@ -334,9 +334,26 @@ docker run \ # when standalone, it runs here. collect_coverage() { local exit_code=\$? + local coverage_exit=0 + trap - EXIT + set +e if [ \"\${COVERAGE_MODE}\" = \"1\" ]; then + ( + set -e + coverage_failed=0 echo \">>> Collecting code coverage data (exit code was: \${exit_code})...\" + # ProxySQL is a long-running process, so its in-memory counters + # are not guaranteed to reach the GCDA files during container + # teardown. GCC 13 also ignores a second __gcov_dump call until + # __gcov_reset is called. Do not dump inside the TAP loop; dump + # once here after the group and before any GCDA decoding starts. + echo \">>> Dumping ProxySQL GCOV counters after the test group...\" + if ! \"${SCRIPT_DIR}/dump-proxysql-gcov.bash\"; then + echo \">>> ERROR: Failed to dump ProxySQL GCOV counters\" >&2 + coverage_failed=1 + fi + if [ -d \"/gcov\" ] && [ \"\$(ls -A /gcov 2>/dev/null)\" ]; then # Match .gcno files to .gcda files by basename and copy # adjacent so fastcov can find them. @@ -444,8 +461,13 @@ docker run \ else echo \">>> WARNING: /gcov directory is empty or missing, skipping coverage\" fi + exit \${coverage_failed} + ) + coverage_exit=\$? fi - exit \${exit_code} + source "${SCRIPT_DIR}/coverage-exit-status.bash" + coverage_exit_status \"\${exit_code}\" \"\${coverage_exit}\" + exit \$? } trap collect_coverage EXIT diff --git a/test/infra/control/test-final-gcov-dump.bash b/test/infra/control/test-final-gcov-dump.bash new file mode 100755 index 0000000000..1ae7127c4c --- /dev/null +++ b/test/infra/control/test-final-gcov-dump.bash @@ -0,0 +1,131 @@ +#!/bin/bash +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +subject="${script_dir}/dump-proxysql-gcov.bash" +fake_mysql="${script_dir}/fixtures/record-mysql-argv.bash" +runner="${script_dir}/run-tests-isolated.bash" +tester="${script_dir}/../../scripts/bin/proxysql-tester.py" +status_helper="${script_dir}/coverage-exit-status.bash" +record_file="$(mktemp)" +trap 'rm -f "${record_file}"' EXIT + +MYSQL_CLIENT_BIN="${fake_mysql}" MYSQL_RECORD_FILE="${record_file}" "${subject}" + +expected=( + -uradmin + -pradmin + -hproxysql + -P6032 + --batch + --skip-column-names + -e + "PROXYSQL GCOV DUMP" +) +mapfile -t actual < "${record_file}" + +if [ "${#actual[@]}" -ne "${#expected[@]}" ]; then + echo "expected ${#expected[@]} mysql arguments, got ${#actual[@]}" >&2 + exit 1 +fi +for i in "${!expected[@]}"; do + if [ "${actual[$i]}" != "${expected[$i]}" ]; then + echo "mysql argument ${i}: expected '${expected[$i]}', got '${actual[$i]}'" >&2 + exit 1 + fi +done + +TAP_ADMINUSERNAME="ci-admin" TAP_ADMINPASSWORD="ci-secret" \ + TAP_ADMINHOST="proxy-under-test" TAP_ADMINPORT="16032" \ + MYSQL_CLIENT_BIN="${fake_mysql}" MYSQL_RECORD_FILE="${record_file}" "${subject}" +mapfile -t actual < "${record_file}" +expected=( + -uci-admin + -pci-secret + -hproxy-under-test + -P16032 + --batch + --skip-column-names + -e + "PROXYSQL GCOV DUMP" +) +for i in "${!expected[@]}"; do + if [ "${actual[$i]}" != "${expected[$i]}" ]; then + echo "custom mysql argument ${i}: expected '${expected[$i]}', got '${actual[$i]}'" >&2 + exit 1 + fi +done + +set +e +MYSQL_CLIENT_BIN="${fake_mysql}" MYSQL_RECORD_FILE="${record_file}" MYSQL_EXIT_CODE=23 "${subject}" +dump_exit=$? +set -e +if [ "${dump_exit}" -ne 23 ]; then + echo "expected mysql failure 23 to propagate, got ${dump_exit}" >&2 + exit 1 +fi + +set +e +MYSQL_CLIENT_BIN="${fake_mysql}" MYSQL_RECORD_FILE="${record_file}" \ + MYSQL_DELAY_SECONDS=2 GCOV_DUMP_TIMEOUT_SECONDS=0.1 "${subject}" +timeout_exit=$? +set -e +if [ "${timeout_exit}" -ne 124 ]; then + echo "expected bounded mysql call to exit 124, got ${timeout_exit}" >&2 + exit 1 +fi + +set +e +MYSQL_CLIENT_BIN="${fake_mysql}" MYSQL_RECORD_FILE="${record_file}" \ + GCOV_DUMP_TIMEOUT_SECONDS=0 "${subject}" 2>/dev/null +zero_timeout_exit=$? +set -e +if [ "${zero_timeout_exit}" -ne 64 ]; then + echo "expected zero timeout to be rejected with exit 64, got ${zero_timeout_exit}" >&2 + exit 1 +fi + +helper_calls=$(rg -F -c 'dump-proxysql-gcov.bash' "${runner}") +if [ "${helper_calls}" -ne 1 ]; then + echo "expected one final dump invocation in isolated runner, got ${helper_calls}" >&2 + exit 1 +fi +dump_line=$(rg -n -F 'dump-proxysql-gcov.bash' "${runner}" | cut -d: -f1) +decode_line=$(rg -n -F 'fastcov -b' "${runner}" | head -n1 | cut -d: -f1) +if [ "${dump_line}" -ge "${decode_line}" ]; then + echo "final daemon dump must run before GCDA decoding" >&2 + exit 1 +fi +if rg -q -F 'self.padmin_command("PROXYSQL GCOV DUMP")' "${tester}"; then + echo "per-test GCOV dumps must remain disabled" >&2 + exit 1 +fi +status_calls=$(rg -F -c 'coverage_exit_status' "${runner}") +if [ "${status_calls}" -ne 1 ]; then + echo "coverage trap must resolve the final exit status exactly once" >&2 + exit 1 +fi + +assert_final_exit() { + local test_exit="$1" + local coverage_exit="$2" + local expected_status="$3" + local actual + + set +e + bash -c 'source "$1"; coverage_exit_status "$2" "$3"' \ + bash "${status_helper}" "${test_exit}" "${coverage_exit}" + actual=$? + set -e + if [ "${actual}" -ne "${expected_status}" ]; then + echo "test exit ${test_exit}, coverage exit ${coverage_exit}: expected ${expected_status}, got ${actual}" >&2 + exit 1 + fi +} + +assert_final_exit 0 0 0 +assert_final_exit 0 1 1 +assert_final_exit 7 0 7 +assert_final_exit 7 1 7 + +echo "final GCOV dump helper tests passed" diff --git a/test/infra/control/validate-coverage-gcov-toolchain.bash b/test/infra/control/validate-coverage-gcov-toolchain.bash index 2bb233093d..b8bf9a8cc7 100755 --- a/test/infra/control/validate-coverage-gcov-toolchain.bash +++ b/test/infra/control/validate-coverage-gcov-toolchain.bash @@ -5,15 +5,25 @@ root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" dockerfile="${root}/test/infra/docker-base/Dockerfile" runner="${root}/test/infra/control/run-tests-isolated.bash" multi="${root}/test/infra/control/run-multi-group.bash" +lint_workflow="${root}/.github/workflows/CI-lint-groups-json.yml" -! rg -q '^[[:space:]]*gcc-11[[:space:]\\]*$' "${dockerfile}" +if rg -q '^[[:space:]]*gcc-11[[:space:]\\]*$' "${dockerfile}"; then + echo "coverage image must use the compiler's default GCOV reader" >&2 + exit 1 +fi for file in "${runner}" "${multi}"; do # `-C` combines existing LCOV files and does not decode raw GCOV data. # Every other fastcov branch-coverage invocation must use the image's # default gcov, which matches the compiler used by the build handoff. raw_fastcov_calls="$(rg -P -c 'fastcov -b(?!.* -C )' "${file}")" test "${raw_fastcov_calls}" -gt 0 - ! rg -q 'fastcov -b -g gcov-[0-9]+' "${file}" + if rg -q 'fastcov -b -g gcov-[0-9]+' "${file}"; then + echo "raw GCOV decoding must not force a versioned reader: ${file}" >&2 + exit 1 + fi done rg -q -- '-s .*coverage_file' "${runner}" rg -q -- '-s .*GROUP_INFO' "${multi}" +rg -q 'validate-coverage-gcov-toolchain\.bash' "${lint_workflow}" + +"${root}/test/infra/control/test-final-gcov-dump.bash" diff --git a/test/scripts/bin/proxysql-tester.py b/test/scripts/bin/proxysql-tester.py index 39550c31d3..22adacbf63 100755 --- a/test/scripts/bin/proxysql-tester.py +++ b/test/scripts/bin/proxysql-tester.py @@ -908,9 +908,6 @@ def disk_usage(): self.padmin_command(f"LOGENTRY '{TAP} test {fo_num+1}/{len(tap_tests)} \'{os.path.basename(fo_cmd)}\' RC: {fop.returncode}'") self.padmin_command(f"PROXYSQL FLUSH LOGS") - # Dump gcov counters for coverage collection - if self.coverage: - self.padmin_command("PROXYSQL GCOV DUMP") log.debug(f"{TAP} test {fo_num+1}/{len(tap_tests)} '{os.path.basename(fo_cmd)}' RC: {fop.returncode}") # if returncode print extra info @@ -1920,4 +1917,3 @@ def main(argv): if __name__ == '__main__': main(sys.argv[1:]) - From 24c4465dded640b3a6cd86053df0593907d3480d Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Fri, 14 Aug 2026 18:14:49 +0000 Subject: [PATCH 9/9] fix(ci): avoid ripgrep in coverage lint --- test/infra/control/test-final-gcov-dump.bash | 10 +++++----- .../control/validate-coverage-gcov-toolchain.bash | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/test/infra/control/test-final-gcov-dump.bash b/test/infra/control/test-final-gcov-dump.bash index 1ae7127c4c..dddf7145fd 100755 --- a/test/infra/control/test-final-gcov-dump.bash +++ b/test/infra/control/test-final-gcov-dump.bash @@ -85,22 +85,22 @@ if [ "${zero_timeout_exit}" -ne 64 ]; then exit 1 fi -helper_calls=$(rg -F -c 'dump-proxysql-gcov.bash' "${runner}") +helper_calls=$(grep -Fc 'dump-proxysql-gcov.bash' "${runner}") if [ "${helper_calls}" -ne 1 ]; then echo "expected one final dump invocation in isolated runner, got ${helper_calls}" >&2 exit 1 fi -dump_line=$(rg -n -F 'dump-proxysql-gcov.bash' "${runner}" | cut -d: -f1) -decode_line=$(rg -n -F 'fastcov -b' "${runner}" | head -n1 | cut -d: -f1) +dump_line=$(grep -nF 'dump-proxysql-gcov.bash' "${runner}" | cut -d: -f1) +decode_line=$(grep -nF 'fastcov -b' "${runner}" | head -n1 | cut -d: -f1) if [ "${dump_line}" -ge "${decode_line}" ]; then echo "final daemon dump must run before GCDA decoding" >&2 exit 1 fi -if rg -q -F 'self.padmin_command("PROXYSQL GCOV DUMP")' "${tester}"; then +if grep -qF 'self.padmin_command("PROXYSQL GCOV DUMP")' "${tester}"; then echo "per-test GCOV dumps must remain disabled" >&2 exit 1 fi -status_calls=$(rg -F -c 'coverage_exit_status' "${runner}") +status_calls=$(grep -Fc 'coverage_exit_status' "${runner}") if [ "${status_calls}" -ne 1 ]; then echo "coverage trap must resolve the final exit status exactly once" >&2 exit 1 diff --git a/test/infra/control/validate-coverage-gcov-toolchain.bash b/test/infra/control/validate-coverage-gcov-toolchain.bash index b8bf9a8cc7..322e05cd4c 100755 --- a/test/infra/control/validate-coverage-gcov-toolchain.bash +++ b/test/infra/control/validate-coverage-gcov-toolchain.bash @@ -7,7 +7,7 @@ runner="${root}/test/infra/control/run-tests-isolated.bash" multi="${root}/test/infra/control/run-multi-group.bash" lint_workflow="${root}/.github/workflows/CI-lint-groups-json.yml" -if rg -q '^[[:space:]]*gcc-11[[:space:]\\]*$' "${dockerfile}"; then +if grep -Eq '^[[:space:]]*gcc-11[[:space:]\\]*$' "${dockerfile}"; then echo "coverage image must use the compiler's default GCOV reader" >&2 exit 1 fi @@ -15,15 +15,15 @@ for file in "${runner}" "${multi}"; do # `-C` combines existing LCOV files and does not decode raw GCOV data. # Every other fastcov branch-coverage invocation must use the image's # default gcov, which matches the compiler used by the build handoff. - raw_fastcov_calls="$(rg -P -c 'fastcov -b(?!.* -C )' "${file}")" + raw_fastcov_calls="$(awk '/fastcov -b/ && !/ -C / {count++} END {print count + 0}' "${file}")" test "${raw_fastcov_calls}" -gt 0 - if rg -q 'fastcov -b -g gcov-[0-9]+' "${file}"; then + if grep -Eq 'fastcov -b -g gcov-[0-9]+' "${file}"; then echo "raw GCOV decoding must not force a versioned reader: ${file}" >&2 exit 1 fi done -rg -q -- '-s .*coverage_file' "${runner}" -rg -q -- '-s .*GROUP_INFO' "${multi}" -rg -q 'validate-coverage-gcov-toolchain\.bash' "${lint_workflow}" +grep -Eq -- '-s .*coverage_file' "${runner}" +grep -Eq -- '-s .*GROUP_INFO' "${multi}" +grep -Eq 'validate-coverage-gcov-toolchain\.bash' "${lint_workflow}" "${root}/test/infra/control/test-final-gcov-dump.bash"