ci: improve CI stability with parallel builds, Valgrind, and coverage
All checks were successful
CI Code / Check spelling (pull_request) Successful in 18s
CI Code / Check coding style (pull_request) Successful in 33s
CI Code / Linux (debian) (pull_request) Successful in 6m48s
CI Code / Linux (ubuntu) (pull_request) Successful in 6m57s
CI Code / Linux (arch) (pull_request) Successful in 12m4s
CI Code / Code Coverage (pull_request) Successful in 15m24s
All checks were successful
CI Code / Check spelling (pull_request) Successful in 18s
CI Code / Check coding style (pull_request) Successful in 33s
CI Code / Linux (debian) (pull_request) Successful in 6m48s
CI Code / Linux (ubuntu) (pull_request) Successful in 6m57s
CI Code / Linux (arch) (pull_request) Successful in 12m4s
CI Code / Code Coverage (pull_request) Successful in 15m24s
This commit is contained in:
192
ci-build.sh
192
ci-build.sh
@@ -180,30 +180,13 @@ case "$ARCH" in
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$ARCH" in
|
||||
linux*)
|
||||
echo
|
||||
echo "--> Building with ./configure -C ${tests[0]} --enable-valgrind $*"
|
||||
echo
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
./configure -C ${tests[0]} --enable-valgrind $*
|
||||
|
||||
$MAKE CC="${CC}"
|
||||
if grep '^ID=' /etc/os-release | grep -q -e debian; then
|
||||
$MAKE check-valgrind
|
||||
else
|
||||
$MAKE check-valgrind || log_content ./test-suite-memcheck.log
|
||||
fi
|
||||
$MAKE distclean
|
||||
;;
|
||||
esac
|
||||
|
||||
# Function to build and test a single configuration
|
||||
build_and_test() {
|
||||
local features="$1"
|
||||
local extra_args="$2"
|
||||
local idx="$3"
|
||||
local run_valgrind="$4"
|
||||
local run_coverage="$5"
|
||||
local build_dir="build-$idx"
|
||||
local log_file="build-$idx.log"
|
||||
|
||||
@@ -211,14 +194,93 @@ build_and_test() {
|
||||
echo "=== Build $idx started at $(date) ==="
|
||||
echo "--> Building in $build_dir with ./configure -C $features $extra_args"
|
||||
|
||||
local start_time=$SECONDS
|
||||
|
||||
mkdir -p "$build_dir"
|
||||
cd "$build_dir"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
../configure -C $features $extra_args
|
||||
if ! ../configure -C $features $extra_args; then
|
||||
echo "ERROR: configure failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
$MAKE CC="${CC}"
|
||||
$MAKE check-functional-parallel
|
||||
if ! $MAKE CC="${CC}"; then
|
||||
echo "ERROR: make failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run unit tests under Valgrind
|
||||
local unit_passed=0 unit_failed=0
|
||||
if [ "$run_valgrind" = "yes" ]; then
|
||||
echo "--> Running unit tests under Valgrind..."
|
||||
# Build unit tests first
|
||||
$MAKE tests/unittests/unittests
|
||||
# Run valgrind directly to capture cmocka output
|
||||
valgrind --error-exitcode=1 --leak-check=full \
|
||||
--suppressions=../prof.supp \
|
||||
tests/unittests/unittests 2>&1 | tee unit-tests-output.log
|
||||
valgrind_exit=${PIPESTATUS[0]}
|
||||
if [ $valgrind_exit -ne 0 ]; then
|
||||
echo "ERROR: Valgrind unit tests failed (exit code $valgrind_exit)"
|
||||
exit 1
|
||||
fi
|
||||
# Extract unit test counts from cmocka output
|
||||
unit_passed=$(grep -E "^\[ PASSED \] [0-9]+ test" unit-tests-output.log | grep -oE "[0-9]+" | head -1)
|
||||
unit_failed=$(grep -E "^\[ FAILED \] [0-9]+ test" unit-tests-output.log | grep -oE "[0-9]+" | head -1)
|
||||
[ -z "$unit_passed" ] && unit_passed=0
|
||||
[ -z "$unit_failed" ] && unit_failed=0
|
||||
echo "UNIT_TESTS: passed=$unit_passed failed=$unit_failed"
|
||||
fi
|
||||
|
||||
# Set build index for port allocation: build 1 uses ports 5230-5233,
|
||||
# build 2 uses 5234-5237, etc. This prevents port conflicts in parallel builds.
|
||||
export PROF_BUILD_INDEX=$idx
|
||||
local func_passed=0 func_failed=0
|
||||
if ! $MAKE check-functional-parallel; then
|
||||
echo "ERROR: functional tests failed"
|
||||
exit 1
|
||||
fi
|
||||
# Extract functional test counts from group logs
|
||||
echo "=== Functional test results ==="
|
||||
for glog in ./test-logs/group*.log; do
|
||||
if [ -f "$glog" ]; then
|
||||
cnt=$(grep -E "^\[ PASSED \] [0-9]+ test" "$glog" | grep -oE "[0-9]+" | head -1)
|
||||
[ -n "$cnt" ] && func_passed=$((func_passed + cnt))
|
||||
cnt=$(grep -E "^\[ FAILED \] [0-9]+ test" "$glog" | grep -oE "[0-9]+" | head -1)
|
||||
[ -n "$cnt" ] && func_failed=$((func_failed + cnt))
|
||||
fi
|
||||
done
|
||||
echo "FUNC_TESTS: passed=$func_passed failed=$func_failed"
|
||||
|
||||
# Collect coverage data if enabled (lines, functions, branches)
|
||||
# Must be done BEFORE make clean which removes .gcda files
|
||||
local cov_lines="n/a" cov_funcs="n/a" cov_branches="n/a"
|
||||
if [ "$run_coverage" = "yes" ]; then
|
||||
echo "--> Collecting coverage data..."
|
||||
if command -v lcov >/dev/null 2>&1; then
|
||||
lcov --capture --directory . --output-file coverage.info \
|
||||
--rc lcov_branch_coverage=1 --ignore-errors inconsistent 2>&1 || true
|
||||
lcov --remove coverage.info '/usr/*' '*/tests/*' --output-file coverage.info \
|
||||
--rc lcov_branch_coverage=1 --ignore-errors inconsistent 2>&1 || true
|
||||
if [ -f coverage.info ] && [ -s coverage.info ]; then
|
||||
local summary
|
||||
summary=$(lcov --summary coverage.info \
|
||||
--rc lcov_branch_coverage=1 --ignore-errors inconsistent 2>&1 || true)
|
||||
cov_lines=$(echo "$summary" | grep -E "lines\.*:" | grep -oE "[0-9]+\.[0-9]+%" | head -1)
|
||||
cov_funcs=$(echo "$summary" | grep -E "functions\.*:" | grep -oE "[0-9]+\.[0-9]+%" | head -1)
|
||||
cov_branches=$(echo "$summary" | grep -E "branches\.*:" | grep -oE "[0-9]+\.[0-9]+%" | head -1)
|
||||
[ -z "$cov_lines" ] && cov_lines="n/a"
|
||||
[ -z "$cov_funcs" ] && cov_funcs="n/a"
|
||||
[ -z "$cov_branches" ] && cov_branches="n/a"
|
||||
else
|
||||
echo "WARNING: coverage.info is empty or not created"
|
||||
fi
|
||||
else
|
||||
echo "WARNING: lcov not found"
|
||||
fi
|
||||
echo "COVERAGE: lines=$cov_lines funcs=$cov_funcs branches=$cov_branches"
|
||||
fi
|
||||
|
||||
./profanity -v
|
||||
$MAKE clean
|
||||
@@ -226,46 +288,94 @@ build_and_test() {
|
||||
cd ..
|
||||
rm -rf "$build_dir"
|
||||
|
||||
local elapsed=$((SECONDS - start_time))
|
||||
local mins=$((elapsed / 60))
|
||||
local secs=$((elapsed % 60))
|
||||
|
||||
echo "=== Build $idx completed at $(date) ==="
|
||||
echo "STATS: unit_passed=$unit_passed unit_failed=$unit_failed func_passed=$func_passed func_failed=$func_failed cov_lines=$cov_lines cov_funcs=$cov_funcs cov_branches=$cov_branches time=${mins}m${secs}s"
|
||||
} > "$log_file" 2>&1
|
||||
}
|
||||
|
||||
# Run all 4 configurations in parallel
|
||||
# Coverage enabled only for build 1 (Full) - it has most code paths
|
||||
echo "Starting parallel builds..."
|
||||
pids=()
|
||||
for idx in 1 2 3 4; do
|
||||
if [ $idx -le ${#tests[@]} ]; then
|
||||
build_and_test "${tests[$((idx-1))]}" "$*" "$idx" &
|
||||
# All builds run Valgrind
|
||||
if [ "$ARCH" = "linux" ]; then
|
||||
run_valgrind="yes"
|
||||
if [ $idx -eq 1 ]; then
|
||||
extra_flags="--enable-valgrind --enable-coverage"
|
||||
run_coverage="yes"
|
||||
else
|
||||
extra_flags="--enable-valgrind"
|
||||
run_coverage="no"
|
||||
fi
|
||||
else
|
||||
extra_flags=""
|
||||
run_valgrind="no"
|
||||
run_coverage="no"
|
||||
fi
|
||||
build_and_test "${tests[$((idx-1))]}" "$* $extra_flags" "$idx" "$run_valgrind" "$run_coverage" &
|
||||
pids+=("$!")
|
||||
echo "Started build $idx (PID: $!)"
|
||||
flags_desc=""
|
||||
[ "$run_valgrind" = "yes" ] && flags_desc="Valgrind"
|
||||
[ "$run_coverage" = "yes" ] && flags_desc="${flags_desc:+$flags_desc+}Coverage"
|
||||
[ -n "$flags_desc" ] && flags_desc=" [+$flags_desc]"
|
||||
echo "Started build $idx (PID: $!)$flags_desc"
|
||||
fi
|
||||
done
|
||||
|
||||
# Wait for all builds and check exit codes
|
||||
failed=0
|
||||
failed_builds=()
|
||||
for i in "${!pids[@]}"; do
|
||||
idx=$((i + 1))
|
||||
if wait "${pids[$i]}"; then
|
||||
echo "✓ Build $idx passed"
|
||||
stats=""
|
||||
if [ -f "build-$idx.log" ]; then
|
||||
stats_line=$(grep "^STATS:" "build-$idx.log" | tail -1)
|
||||
unit_p=$(echo "$stats_line" | grep -oE "unit_passed=[0-9]+" | cut -d= -f2)
|
||||
unit_f=$(echo "$stats_line" | grep -oE "unit_failed=[0-9]+" | cut -d= -f2)
|
||||
func_p=$(echo "$stats_line" | grep -oE "func_passed=[0-9]+" | cut -d= -f2)
|
||||
func_f=$(echo "$stats_line" | grep -oE "func_failed=[0-9]+" | cut -d= -f2)
|
||||
cov_lines=$(echo "$stats_line" | grep -oE "cov_lines=[0-9.]+%|cov_lines=n/a" | cut -d= -f2)
|
||||
cov_funcs=$(echo "$stats_line" | grep -oE "cov_funcs=[0-9.]+%|cov_funcs=n/a" | cut -d= -f2)
|
||||
cov_branches=$(echo "$stats_line" | grep -oE "cov_branches=[0-9.]+%|cov_branches=n/a" | cut -d= -f2)
|
||||
time=$(echo "$stats_line" | grep -oE "time=[0-9]+m[0-9]+s" | cut -d= -f2)
|
||||
|
||||
# Format: passed/failed for each test type
|
||||
unit_str="${unit_p:-0}/${unit_f:-0}"
|
||||
func_str="${func_p:-0}/${func_f:-0}"
|
||||
|
||||
if [ "$cov_lines" != "n/a" ] && [ -n "$cov_lines" ]; then
|
||||
stats=" (unit: $unit_str, func: $func_str, cov: L:$cov_lines F:$cov_funcs B:$cov_branches, ${time:-?})"
|
||||
else
|
||||
stats=" (unit: $unit_str, func: $func_str, ${time:-?})"
|
||||
fi
|
||||
fi
|
||||
echo "✓ Build $idx passed$stats"
|
||||
else
|
||||
echo "✗ Build $idx failed"
|
||||
echo "--- Log for build $idx ---"
|
||||
cat "build-$idx.log"
|
||||
echo "--- End log ---"
|
||||
failed=1
|
||||
failed_builds+=("$idx")
|
||||
fi
|
||||
done
|
||||
|
||||
# Show all logs on success too
|
||||
if [ $failed -eq 0 ]; then
|
||||
echo
|
||||
echo "All builds passed!"
|
||||
for idx in 1 2 3 4; do
|
||||
if [ -f "build-$idx.log" ]; then
|
||||
echo "--- Log for build $idx ---"
|
||||
cat "build-$idx.log"
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo
|
||||
|
||||
# Show failed builds full logs
|
||||
for idx in "${failed_builds[@]}"; do
|
||||
if [ -f "build-$idx.log" ]; then
|
||||
echo "=== Build $idx (FAILED) - full log ==="
|
||||
cat "build-$idx.log"
|
||||
echo
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#failed_builds[@]} -gt 0 ]; then
|
||||
echo "FAILED: builds ${failed_builds[*]}"
|
||||
exit 1
|
||||
else
|
||||
echo "All builds passed!"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user