-
Notifications
You must be signed in to change notification settings - Fork 22
203 lines (171 loc) · 7.23 KB
/
test_coverage_regression.yml
File metadata and controls
203 lines (171 loc) · 7.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
name: Test for unit test coverage regression
on:
pull_request:
branches: [ dev ]
workflow_dispatch:
permissions:
contents: read
pull-requests: write
concurrency:
group: pr-coverage-${{ github.run_id }}
cancel-in-progress: true
jobs:
coverage:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- name: Install build dependencies
run: |
sudo ./INCHI-1-TEST/install_build_dependencies.sh
- name: Generate coverage report
uses: ./.github/actions/generate_coverage_report
- name: Test for unit test regression
run: |
set -euo pipefail
cd CMake_build/coverage_build
BASE_SUMMARY_URL="https://iupac-inchi.github.io/InChI/coverage/summary.txt"
BASE_SUMMARY_FILE="$(mktemp)"
PR_SUMMARY_FILE="coverage_reports/summary.txt"
COVERAGE_REPORT_MD="coverage_reports/coverage_report.md"
mkdir -p coverage_reports
trap 'rm -f "$BASE_SUMMARY_FILE"' EXIT
extract_pct() {
local metric="$1"
local file="$2"
awk -v metric="$metric" '
$0 ~ "^[[:space:]]*" metric "\\.*:" {
if (match($0, /[0-9]+([.][0-9]+)?%/)) {
value = substr($0, RSTART, RLENGTH - 1)
print value
}
exit
}
' "$file"
}
parse_metrics() {
local prefix="$1"
local file="$2"
local parse_error="$3"
local lines branches
lines="$(extract_pct lines "$file")"
branches="$(extract_pct branches "$file")"
if [[ -n "$lines" && -n "$branches" ]]; then
printf -v "${prefix}_LINES" '%s' "$lines"
printf -v "${prefix}_BRANCHES" '%s' "$branches"
printf -v "${prefix}_AVAILABLE" 'true'
else
PROBLEMS+=("$parse_error")
fi
}
pct_diff() { awk -v pr="$1" -v base="$2" 'BEGIN {printf "%+.2f", pr-base}'; }
is_lt() { awk -v pr="$1" -v base="$2" 'BEGIN {exit !(pr < base)}'; }
PROBLEMS=()
PR_AVAILABLE=false
BASE_AVAILABLE=false
PR_LINES=""
PR_BRANCHES=""
BASE_LINES=""
BASE_BRANCHES=""
if lcov --summary coverage.lcov --branch-coverage --ignore-errors inconsistent > "$PR_SUMMARY_FILE"; then
parse_metrics PR "$PR_SUMMARY_FILE" "Unable to parse PR coverage metrics from $PR_SUMMARY_FILE"
else
PROBLEMS+=("Unable to generate PR coverage summary from coverage.lcov")
fi
if curl -fsSL --max-time 10 "$BASE_SUMMARY_URL" -o "$BASE_SUMMARY_FILE"; then
parse_metrics BASE "$BASE_SUMMARY_FILE" "Base summary fetched, but required metrics were incomplete"
else
PROBLEMS+=("Base summary fetch failed from $BASE_SUMMARY_URL")
fi
PR_LINES_DISPLAY="${PR_LINES:-N/A}"
PR_BRANCHES_DISPLAY="${PR_BRANCHES:-N/A}"
BASE_LINES_DISPLAY="${BASE_LINES:-N/A}"
BASE_BRANCHES_DISPLAY="${BASE_BRANCHES:-N/A}"
DIFF_LINES="N/A"
DIFF_BRANCHES="N/A"
if [[ "$PR_AVAILABLE" == true && "$BASE_AVAILABLE" == true ]]; then
DIFF_LINES="$(pct_diff "$PR_LINES" "$BASE_LINES")"
DIFF_BRANCHES="$(pct_diff "$PR_BRANCHES" "$BASE_BRANCHES")"
fi
{
echo "### Coverage Regression Summary"
echo
echo "| Metric | PR (%) | Base (%) | Difference (pp) |"
echo "| --- | ---: | ---: | ---: |"
echo "| Lines | ${PR_LINES_DISPLAY} | ${BASE_LINES_DISPLAY} | ${DIFF_LINES} |"
echo "| Branches | ${PR_BRANCHES_DISPLAY} | ${BASE_BRANCHES_DISPLAY} | ${DIFF_BRANCHES} |"
if [[ ${#PROBLEMS[@]} -gt 0 ]]; then
echo
echo "### Problems"
echo
printf -- "- %s\n" "${PROBLEMS[@]}"
fi
echo
echo "Find details on the base coverage at https://iupac-inchi.github.io/InChI/coverage/index.html"
} > "$COVERAGE_REPORT_MD"
if [[ "$PR_AVAILABLE" == false || "$BASE_AVAILABLE" == false ]]; then
echo "Coverage regression check skipped due to missing PR or base metrics."
exit 0
fi
FAIL=false
if is_lt "$PR_LINES" "$BASE_LINES"; then
echo "Line coverage regression: ${PR_LINES}% < base ${BASE_LINES}%"
FAIL=true
fi
if is_lt "$PR_BRANCHES" "$BASE_BRANCHES"; then
echo "Branch coverage regression: ${PR_BRANCHES}% < base ${BASE_BRANCHES}%"
FAIL=true
fi
if [[ "$FAIL" == true ]]; then
exit 1
fi
echo "Coverage OK: lines ${PR_LINES}% >= base ${BASE_LINES}%, branches ${PR_BRANCHES}% >= base ${BASE_BRANCHES}%"
- name: Upload coverage reports
uses: actions/upload-artifact@v7
if: always()
with:
name: coverage-reports-${{ github.run_id }}
path: CMake_build/coverage_build/coverage_reports/
retention-days: 30
- name: Post sticky PR comment (same-repo only)
if: always() && github.event.pull_request.head.repo.full_name == github.repository
uses: actions/github-script@v9
with:
script: |
const fs = require('fs');
const report = fs.readFileSync('CMake_build/coverage_build/coverage_reports/coverage_report.md', 'utf8');
// Build artifact link
const runId = context.runId;
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
const artifactLink = `Find details on this PR's coverage by downloading [coverage-reports-${runId}](${runUrl}) and opening **html/index.html**`;
const body = `## Unit Test Coverage Report\n\n${report}\n\n${artifactLink}`;
const identifier = '<!-- InChI Unit Test Coverage Report -->';
const bodyWithId = `${identifier}\n${body}`;
// Search for existing comment with the identifier
const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
per_page: 100,
});
const existingComment = comments.find(comment =>
comment.body.includes(identifier)
);
if (existingComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existingComment.id,
body: bodyWithId,
});
console.log('Updated existing comment:', existingComment.id);
} else {
// Create new comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: bodyWithId,
});
console.log('Created new comment');
}