forked from FirebirdSQL/php-firebird
-
-
Notifications
You must be signed in to change notification settings - Fork 0
executable file
·276 lines (235 loc) · 9.89 KB
/
Copy pathcoverage.yml
File metadata and controls
executable file
·276 lines (235 loc) · 9.89 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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
name: Linux Code Coverage
on:
push:
paths:
- '**.c'
- '**.cpp'
- '**.h'
- '**.hpp'
- 'tests/**'
- 'config.m4'
- '.github/workflows/coverage.yml'
pull_request:
paths:
- '**.c'
- '**.cpp'
- '**.h'
- '**.hpp'
- 'tests/**'
- 'config.m4'
- '.github/workflows/coverage.yml'
workflow_dispatch:
schedule:
- cron: '0 0 * * *'
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name != 'schedule' }}
jobs:
coverage:
name: Code Coverage (PHP 8.3 / Firebird 5.0)
runs-on: ubuntu-24.04
timeout-minutes: 35
# Run Firebird as a service container
services:
firebird:
image: firebirdsql/firebird:5
env:
ISC_PASSWORD: masterkey
FIREBIRD_DATABASE: test.fdb
FIREBIRD_PASSWORD: masterkey
FIREBIRD_USER: SYSDBA
ports:
- 3050:3050
container:
image: php:8.3-cli-bookworm
env:
ISC_USER: SYSDBA
ISC_PASSWORD: masterkey
FIREBIRD_HOST: firebird
# FIREBIRD_DB_DIR gives each test a unique DB via /tmp, preventing
# metadata lock conflicts when tests do RECREATE TABLE from different
# connections (per AGENTS.md CI parity rule #2).
FIREBIRD_DB_DIR: /tmp
FIREBIRD_DB_PATH: /var/lib/firebird/data/test.fdb
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Ensure VERSION.txt
uses: ./.github/actions/version
- name: Install Firebird client
uses: ./.github/actions/install-firebird-client
with:
firebird-major-version: '5'
extra-apt-packages: lcov
- name: Build firebird extension with coverage
uses: ./.github/actions/build-extension
with:
build-pdo-fbird: 'false'
ld-library-path-extra: /opt/firebird-client/lib
cflags-extra: -I/opt/firebird-client/include -O0 -g --coverage
cxxflags-extra: -I/opt/firebird-client/include -O0 -g --coverage
ldflags-extra: -L/opt/firebird-client/lib --coverage
- name: Build pdo_fbird extension
shell: bash
run: |
set -eu
cd "${BUILD_DIR}/pdo_fbird"
phpize
./configure --with-pdo-fbird --with-firebird=/opt/firebird-client
make -j"$(nproc)"
echo "pdo_fbird built successfully"
- name: Verify extension and connection
uses: ./.github/actions/verify-extension
with:
mode: full
extension-path: ${{ env.BUILD_DIR }}/modules/firebird.so
ld-library-path-extra: /opt/firebird-client/lib
- name: Install Composer dependencies
run: |
EXPECTED_SIGNATURE="$(curl -sS https://composer.github.io/installer.sig)"
curl -sS https://getcomposer.org/installer -o /tmp/composer-setup.php
ACTUAL_SIGNATURE="$(php -r "echo hash_file('sha384', '/tmp/composer-setup.php');")"
[ "$EXPECTED_SIGNATURE" = "$ACTUAL_SIGNATURE" ] || { echo "ERROR: Invalid Composer installer signature"; exit 1; }
php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer
rm -f /tmp/composer-setup.php
composer install --prefer-dist --no-progress
- name: Run PHPT tests
env:
NO_INTERACTION: 1
REPORT_EXIT_STATUS: 1
TEST_PHP_EXECUTABLE: php
FBIRD_SO: ${{ env.BUILD_DIR }}/modules/firebird.so
shell: bash
run: |
set -euo pipefail
# jane: run-tests.php is generated by phpize in BUILD_DIR (out-of-tree
# build). Copy it to GITHUB_WORKSPACE so tests run from the source tree.
cp "${BUILD_DIR}/run-tests.php" .
EXTENSION_PATH="${BUILD_DIR}/modules/firebird.so"
PDO_FBIRD_PATH="${BUILD_DIR}/pdo_fbird/modules/pdo_fbird.so"
# Build extension flags - load both firebird and pdo_fbird explicitly.
# Per AGENTS.md: `make test` only loads firebird.so via the Makefile,
# and PHP_TEST_SHARED_EXTENSIONS env var is NOT read by the Makefile's
# test target, causing ALL pdo_fbird tests to silently SKIP.
# Fix: use run-tests.php directly with -d extension= flags (like ci.yml).
EXT_FLAGS="-d extension=${EXTENSION_PATH}"
if [ -f "$PDO_FBIRD_PATH" ]; then
EXT_FLAGS="${EXT_FLAGS} -d extension=${PDO_FBIRD_PATH}"
fi
# Add pcntl if available (guard against double-load per AGENTS.md)
if ! php -r 'exit(extension_loaded("pcntl") ? 0 : 1);' 2>/dev/null; then
PCNTL_SO=$(find $(php-config --extension-dir) -name "pcntl.so" 2>/dev/null || true)
if [ -n "$PCNTL_SO" ]; then
EXT_FLAGS="${EXT_FLAGS} -d extension=${PCNTL_SO}"
fi
fi
echo "Running all PHPT tests..."
# Use run-tests.php directly (not make test) so both extensions load.
# --set-timeout 15 kills hanging tests in 15s instead of default 60s.
php run-tests.php ${EXT_FLAGS} \
-p "$(which php)" \
--set-timeout 15 \
tests/ 2>&1 | tee test_output.txt || true
# Parse test results
FAILED_COUNT=$(grep -oP 'Tests failed\s*:\s*\K\d+' test_output.txt | head -1 || echo "0")
WARNED_COUNT=$(grep -oP 'Tests warned\s*:\s*\K\d+' test_output.txt | head -1 || echo "0")
LEAKED_COUNT=$(grep -oP 'Tests leaked\s*:\s*\K\d+' test_output.txt | head -1 || echo "0")
echo "Test Stats -> Failed: ${FAILED_COUNT}, Warned: ${WARNED_COUNT}, Leaked: ${LEAKED_COUNT}"
# Fail only on actual failures and memory leaks.
# "Tests warned" = passed on retry attempt (not a real failure).
if [ "${FAILED_COUNT}" -gt 0 ] || [ "${LEAKED_COUNT}" -gt 0 ]; then
echo "❌ Test Failure Detected"
echo "::group::Test Failure Details"
if grep -q "FAILED TEST SUMMARY" test_output.txt; then
grep -A 100 "FAILED TEST SUMMARY" test_output.txt | head -50 || true
fi
echo "::endgroup::"
exit 1
fi
echo "✅ All tests passed successfully"
- name: Capture Coverage & Enforce Gate
env:
COVERAGE_THRESHOLD: 54.0
run: |
set -eu
mkdir -p coverage
echo "=== Debug: Listing gcno/gcda files ==="
find "${BUILD_DIR}" -name "*.gcno" -o -name "*.gcda" 2>/dev/null | head -20 || true
# jane: coverage data (.gcno/.gcda) is in BUILD_DIR (out-of-tree build).
# Use lcov --directory BUILD_DIR to capture from the correct location.
# Run gcov from BUILD_DIR where source files (.c) and .gcda files are.
cd "${BUILD_DIR}"
# Run gcov on source files
echo "Running gcov on source files..."
for src in *.c; do
if [ -f "$src" ]; then
gcov -o . "$src" 2>/dev/null || true
fi
done
if [ -d ".libs" ]; then
for gcda in .libs/*.gcda; do
if [ -f "$gcda" ]; then
base=$(basename "$gcda" .gcda)
if [ -f "${base}.c" ]; then
gcov -o .libs "${base}.c" 2>/dev/null || true
fi
fi
done
fi
# Capture coverage using lcov
echo "Capturing coverage data with lcov..."
lcov --directory . --capture --output-file "${GITHUB_WORKSPACE}/coverage/lcov.info" \
--rc lcov_branch_coverage=0 \
--no-external \
--ignore-errors gcov \
2>&1 || true
if [ ! -s "${GITHUB_WORKSPACE}/coverage/lcov.info" ]; then
echo "Warning: No coverage data captured"
echo "TN:" > "${GITHUB_WORKSPACE}/coverage/lcov.info"
fi
# Filter out system headers, PHP headers, and tests
lcov --remove "${GITHUB_WORKSPACE}/coverage/lcov.info" \
'/usr/*' \
'*/php-src/*' \
'*/tests/*' \
'*/build/*' \
'*/modules/*' \
--output-file "${GITHUB_WORKSPACE}/coverage/lcov_filtered.info" \
--rc lcov_branch_coverage=0 \
2>&1 || true
if [ ! -s "${GITHUB_WORKSPACE}/coverage/lcov_filtered.info" ]; then
echo "Warning: No coverage data after filtering"
echo "TN:" > "${GITHUB_WORKSPACE}/coverage/lcov_filtered.info"
fi
# Generate HTML report
genhtml "${GITHUB_WORKSPACE}/coverage/lcov_filtered.info" --output-directory "${GITHUB_WORKSPACE}/coverage/html" \
--rc lcov_branch_coverage=0 \
2>&1 || true
echo "Coverage summary:"
COVERAGE_OUTPUT=$(lcov --summary "${GITHUB_WORKSPACE}/coverage/lcov_filtered.info")
echo "$COVERAGE_OUTPUT"
COVERAGE=$(echo "$COVERAGE_OUTPUT" | grep -oP "lines\.*: \K[0-9.]+")
if [ -z "$COVERAGE" ]; then
echo "Error: Could not parse coverage percentage"
exit 1
fi
echo "Current Line Coverage: ${COVERAGE}%"
echo "Required Threshold: ${COVERAGE_THRESHOLD}%"
# Use awk for floating point comparison (no python needed)
if awk "BEGIN {exit ($COVERAGE >= $COVERAGE_THRESHOLD) ? 0 : 1}"; then
echo "✅ Coverage Gate Passed (${COVERAGE}% >= ${COVERAGE_THRESHOLD}%)"
else
echo "❌ Coverage dropped below baseline (${COVERAGE}% < ${COVERAGE_THRESHOLD}%)"
exit 1
fi
- name: Cleanup Raw Coverage Files
run: |
rm -f coverage/*.info test_output.txt
- name: Upload Coverage Artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: coverage-report
path: coverage/html/