@@ -201,57 +201,89 @@ jobs:
201201 - name : " Filter & Report Coverage"
202202 if : env.HAS_TEST_MAPPINGS == 'true'
203203 run : |
204- set -euo pipefail
205- echo "=== Coverage Analysis ==="
206- echo "Required Coverage: ${REQUIRED_COVERAGE:-}%"
207- echo "Test Files: ${TEST_FILES:-}"
208- echo "CPP Files: ${CPP_FILES:-}"
209-
210- mkdir -p out/coverage/coverage
211-
212- trace_in="out/coverage/coverage/lcov_final.info"
213- trace_filtered="out/coverage/coverage/lcov_filtered.info"
214- html_dir="out/coverage/coverage/html_filtered"
215- html_full="out/coverage/coverage/html"
216-
217- # Prepare patterns from changed files
218- echo "${CPP_FILES:-}" | tr ',' '\n' | sed '/^\s*$/d' > changed_files.txt || true
219- echo "=== Changed files (for filtering) ==="
220- cat changed_files.txt || true
221-
222- # Prepend '*/' so lcov matches full source paths in the tracefile
223- mapfile -t patterns < <(awk '{print "*/"$0}' changed_files.txt 2>/dev/null || true)
224-
225- if [[ -s changed_files.txt && ${#patterns[@]} -gt 0 ]]; then
226- echo "Filtering coverage to changed files..."
227- lcov --extract "$trace_in" "${patterns[@]}" -o "$trace_filtered"
228- genhtml "$trace_filtered" -o "$html_dir"
229- summary_src="$trace_filtered"
230- else
231- echo "No changed files provided; generating full coverage HTML..."
232- genhtml "$trace_in" -o "$html_full"
233- summary_src="$trace_in"
234- fi
204+ set -euo pipefail
205+ echo "=== Coverage Analysis ==="
206+ echo "Required Coverage: ${REQUIRED_COVERAGE:-}%"
207+ echo "Test Files: ${TEST_FILES:-}"
208+ echo "CPP Files: ${CPP_FILES:-}"
209+
210+ mkdir -p out/coverage/coverage
211+
212+ trace_in="out/coverage/coverage/lcov_final.info"
213+ trace_filtered="out/coverage/coverage/lcov_filtered.info"
214+ html_dir="out/coverage/coverage/html_filtered"
215+ html_full="out/coverage/coverage/html"
216+
217+ if [[ ! -s "$trace_in" ]]; then
218+ echo "::error::Missing or empty tracefile: $trace_in"
219+ exit 1
220+ fi
221+
222+ # Prepare patterns from changed files
223+ echo "${CPP_FILES:-}" | tr ',' '\n' | sed '/^\s*$/d' > changed_files.txt || true
224+ echo "=== Changed files (for filtering) ==="
225+ cat changed_files.txt || true
226+
227+ # Build lcov extract patterns (match by suffix path)
228+ mapfile -t patterns < <(awk '{print "*/"$0}' changed_files.txt 2>/dev/null || true)
229+
230+ # Preflight: do any of the changed basenames appear in the trace?
231+ should_filter=false
232+ if [[ -s changed_files.txt && ${#patterns[@]} -gt 0 ]]; then
233+ while IFS= read -r f; do
234+ b="$(basename "$f")"
235+ if grep -Fq "/$b" "$trace_in"; then
236+ should_filter=true
237+ break
238+ fi
239+ done < changed_files.txt
240+ fi
241+
242+ if $should_filter; then
243+ echo "Filtering coverage to changed files..."
244+ # Allow empty results without hard failing; fall back to full trace if empty.
245+ if lcov --extract "$trace_in" "${patterns[@]}" -o "$trace_filtered" --ignore-errors empty; then
246+ if [[ ! -s "$trace_filtered" ]] || ! grep -q '^SF:' "$trace_filtered"; then
247+ echo "No matching records after filter; falling back to full trace."
248+ cp "$trace_in" "$trace_filtered"
249+ fi
250+ else
251+ echo "lcov --extract failed; falling back to full trace."
252+ cp "$trace_in" "$trace_filtered"
253+ fi
254+
255+ genhtml "$trace_filtered" -o "$html_dir" || true
256+ summary_src="$trace_filtered"
257+ else
258+ if [[ -s changed_files.txt ]]; then
259+ echo "None of the changed files appear in the trace; generating full coverage HTML..."
260+ else
261+ echo "No changed files provided; generating full coverage HTML..."
262+ fi
263+ genhtml "$trace_in" -o "$html_full" || true
264+ summary_src="$trace_in"
265+ fi
235266
236- echo "=== COVERAGE SUMMARY ===" | tee coverage_summary.txt
237- lcov --summary "$summary_src" | tee -a coverage_summary.txt || true
267+ echo "=== COVERAGE SUMMARY ===" | tee coverage_summary.txt
268+ lcov --summary "$summary_src" | tee -a coverage_summary.txt || true
238269
239- # Extract overall line coverage percentage
240- PCT=$(lcov --summary "$summary_src" 2>/dev/null | awk '/lines/ {gsub("%","",$2); print $2; exit}')
241- PCT=${PCT:-0}
242- echo "Extracted Coverage: ${PCT}%"
270+ # Extract overall line coverage percentage
271+ PCT=$(lcov --summary "$summary_src" 2>/dev/null | awk '/lines/ {gsub("%","",$2); print $2; exit}')
272+ PCT=${PCT:-0}
273+ echo "Extracted Coverage: ${PCT}%"
243274
244- # Store for potential PR comment
245- echo "ACTUAL_COVERAGE=$PCT" >> $GITHUB_ENV
275+ # Store for potential PR comment
276+ echo "ACTUAL_COVERAGE=$PCT" >> "$GITHUB_ENV"
277+
278+ # Enforce required coverage threshold
279+ if awk -v p="$PCT" -v r="${REQUIRED_COVERAGE:-0}" 'BEGIN {exit (p+0<r+0)}'; then
280+ echo "✅ Coverage requirement MET (${PCT}% >= ${REQUIRED_COVERAGE:-0}%)"
281+ else
282+ echo "❌ Coverage requirement NOT MET (${PCT}% < ${REQUIRED_COVERAGE:-0}%)"
283+ echo "::error::Code coverage (${PCT}%) is below required threshold (${REQUIRED_COVERAGE:-0}%)"
284+ exit 1
285+ fi
246286
247- # Enforce required coverage threshold
248- if awk -v p="$PCT" -v r="${REQUIRED_COVERAGE:-0}" 'BEGIN {exit (p+0<r+0)}'; then
249- echo "✅ Coverage requirement MET (${PCT}% >= ${REQUIRED_COVERAGE:-0}%)"
250- else
251- echo "❌ Coverage requirement NOT MET (${PCT}% < ${REQUIRED_COVERAGE:-0}%)"
252- echo "::error::Code coverage (${PCT}%) is below required threshold (${REQUIRED_COVERAGE:-0}%)"
253- exit 1
254- fi
255287
256288 - name : " Summary Report"
257289 if : always() && env.HAS_CPP_FILES == 'true'
0 commit comments