Skip to content

Commit 7bd9339

Browse files
authored
Merge pull request #5817 from sysown/feature/ci-codecov-unit-tests
ci(coverage): upload unit-test LCOV to Codecov
2 parents 0c44f39 + cda5835 commit 7bd9339

4 files changed

Lines changed: 88 additions & 11 deletions

File tree

.github/workflows/CI-unit-tests-asan-coverage.yml

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,15 @@ on:
4242
types: [ completed ]
4343

4444
concurrency:
45-
group: ${{ github.workflow }}-${{ github.event.workflow_run && github.event.workflow_run.head_branch || github.ref_name }}
45+
# Include `github.event_name` so a workflow_run-triggered run never
46+
# falls into the same group as a workflow_dispatch run on the same
47+
# branch. Without this, dispatching on a feature branch while a
48+
# workflow_run for v3.0 is also active cancels the dispatch even
49+
# though the branches differ -- GitHub's concurrency comparison
50+
# appears to ignore the head_branch suffix in practice. Two
51+
# workflow_runs for the same branch still serialize via this group;
52+
# so do two dispatches for the same branch.
53+
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.workflow_run && github.event.workflow_run.head_branch || github.ref_name }}
4654
cancel-in-progress: true
4755

4856
env:
@@ -61,6 +69,17 @@ jobs:
6169
if: ${{ github.event.workflow_run && github.event.workflow_run.conclusion == 'success' || ! github.event.workflow_run }}
6270
runs-on: ubuntu-24.04
6371
timeout-minutes: 120
72+
# codecov/codecov-action@v4 needs `id-token: write` to mint a
73+
# GitHub OIDC token for tokenless uploads against the Codecov
74+
# GitHub App. Without this, the upload falls back to legacy
75+
# token-based auth and fails on protected target branches with
76+
# `HTTP 400: Token required because branch is protected` even
77+
# though the app is installed. We keep `contents: read` (the
78+
# GitHub default) explicit so granting id-token: write here
79+
# doesn't implicitly widen any other scope.
80+
permissions:
81+
contents: read
82+
id-token: write
6483

6584
steps:
6685

@@ -141,6 +160,37 @@ jobs:
141160
path: coverage/lcov.info
142161
if-no-files-found: warn
143162

163+
- name: Upload coverage to Codecov
164+
# Send the same lcov.info we already archive as a workflow artifact
165+
# to Codecov so PRs get the "this PR changes coverage of touched
166+
# files from N% to M%" comment and main accumulates a historical
167+
# graph at https://app.codecov.io/gh/sysown/proxysql.
168+
#
169+
# `if: always()` so coverage uploads regardless of whether the unit
170+
# tests themselves passed; partial coverage is still useful for
171+
# diagnosing why a PR went red. `fail_ci_if_error: false` so a
172+
# transient Codecov outage never gates a green CI run on a
173+
# third-party SaaS.
174+
if: always()
175+
uses: codecov/codecov-action@v4
176+
with:
177+
files: coverage/lcov.info
178+
flags: unit-tests
179+
name: unit-tests-asan-coverage
180+
# Tokenless upload via GitHub OIDC. Codecov treats every branch
181+
# as "protected" by default and rejects unauthenticated uploads
182+
# with HTTP 400 "Token required because branch is protected" --
183+
# this is Codecov's own branch-protection concept, unrelated to
184+
# GitHub's. The fix is `use_oidc: true`, which makes the action
185+
# mint a GitHub OIDC token (granted by `permissions:
186+
# id-token: write` on this job) and present it to Codecov in
187+
# place of a static upload token. Without `use_oidc: true` the
188+
# action silently falls back to legacy tokenless mode and the
189+
# upload fails.
190+
use_oidc: true
191+
fail_ci_if_error: false
192+
verbose: true
193+
144194
- name: Upload coverage HTML report
145195
if: always()
146196
uses: actions/upload-artifact@v4

lib/MySQL_Session.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1863,9 +1863,15 @@ bool MySQL_Session::handler_again___verify_backend_multi_statement() {
18631863
* status values.
18641864
* @note This method is primarily used to maintain a history of the session's previous states for later reference or
18651865
* recovery purposes.
1866-
* @note The LCOV_EXCL_START and LCOV_EXCL_STOP directives are used to exclude the assert statement from code coverage
1867-
* analysis because the condition should not occur during normal execution and is included as a safeguard against
1868-
* programming errors.
1866+
* @note The lcov exclude-block directives wrapping the `assert(0)` below
1867+
* remove that line from coverage analysis -- the condition should never
1868+
* occur during normal execution and is only there as a safeguard against
1869+
* programming errors. (The literal directive strings are deliberately not
1870+
* spelled out in this comment because lcov's geninfo scans every source
1871+
* line for them and treats the doc occurrence as a real exclude marker,
1872+
* which double-opens the exclude block on the next assert and breaks
1873+
* coverage capture for the whole file with a "overlapping exclude
1874+
* directives" error.)
18691875
*/
18701876
void MySQL_Session::set_previous_status_mode3(bool allow_execute) {
18711877
switch(status) {

lib/PgSQL_Session.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6378,9 +6378,15 @@ bool PgSQL_Session::is_in_transaction() const {
63786378
* status values.
63796379
* @note This method is primarily used to maintain a history of the session's previous states for later reference or
63806380
* recovery purposes.
6381-
* @note The LCOV_EXCL_START and LCOV_EXCL_STOP directives are used to exclude the assert statement from code coverage
6382-
* analysis because the condition should not occur during normal execution and is included as a safeguard against
6383-
* programming errors.
6381+
* @note The lcov exclude-block directives wrapping the `assert(0)` below
6382+
* remove that line from coverage analysis -- the condition should never
6383+
* occur during normal execution and is only there as a safeguard against
6384+
* programming errors. (The literal directive strings are deliberately not
6385+
* spelled out in this comment because lcov's geninfo scans every source
6386+
* line for them and treats the doc occurrence as a real exclude marker,
6387+
* which double-opens the exclude block at line 6399 and breaks coverage
6388+
* capture for the whole file with a "overlapping exclude directives"
6389+
* error.)
63846390
*/
63856391
void PgSQL_Session::set_previous_status_mode3(bool allow_execute) {
63866392
switch (status) {

test/infra/control/run-unit-tests-asan-coverage.bash

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,22 @@ echo "==> Capturing baseline coverage snapshot (--initial)"
6363
# coverage for every instrumented source line, so unrun code paths
6464
# show as 0% in the merged report (rather than being absent
6565
# entirely).
66+
#
67+
# Capture from lib/ only. src/ contains the proxysql binary's
68+
# main.cpp and a small number of helpers; unit tests link only
69+
# against libproxysql.a (from lib/) so there is never a .gcda in
70+
# src/ from this workflow. Keeping `--directory src` here makes
71+
# lcov 2.x abort with "no .gcda files found in src" (an `empty`-
72+
# class error, not covered by --ignore-errors gcov,source), which
73+
# wipes the output file and breaks the whole capture chain.
74+
#
75+
# `empty` is added to --ignore-errors as belt-and-suspenders so a
76+
# future test reorganisation that produces a partially-empty
77+
# directory tree doesn't recur the same silent failure.
6678
lcov --quiet --capture --initial \
67-
--directory lib --directory src \
79+
--directory lib \
6880
--output-file coverage/lcov-base.info \
69-
--ignore-errors gcov,source || true
81+
--ignore-errors gcov,source,empty || true
7082

7183
echo "==> Running unit tests under ASAN"
7284
# Iterate every executable under test/tap/tests/unit/. We
@@ -107,10 +119,13 @@ if [ ${#FAILED[@]} -gt 0 ]; then
107119
fi
108120

109121
echo "==> Capturing post-test coverage"
122+
# Mirror the baseline-capture scope above: lib/ only, empty added to
123+
# --ignore-errors. See the baseline-capture comment for the full
124+
# rationale.
110125
lcov --quiet --capture \
111-
--directory lib --directory src \
126+
--directory lib \
112127
--output-file coverage/lcov-tests.info \
113-
--ignore-errors gcov,source,mismatch || true
128+
--ignore-errors gcov,source,mismatch,empty || true
114129

115130
if [ -s coverage/lcov-base.info ] && [ -s coverage/lcov-tests.info ]; then
116131
lcov --quiet \

0 commit comments

Comments
 (0)