Files
cproof/ci-build.sh
jabber.developer2 e40b89ad92
All checks were successful
CI Code / Check spelling (pull_request) Successful in 18s
CI Code / Code Coverage (pull_request) Has been skipped
CI Code / Check coding style (pull_request) Successful in 30s
CI Code / Linux (ubuntu) (pull_request) Successful in 18m10s
CI Code / Linux (debian) (pull_request) Successful in 18m23s
CI Code / Linux (arch) (pull_request) Successful in 20m2s
ci: improve test failure detection in parallel tests
1. ci-build.sh: Add verify_test_failure_detection() function
   - Tests that single test failures are properly detected
   - Tests that parallel test failures are properly detected
   - Runs at CI start to catch framework issues early

2. Makefile.am: Fix check-functional-parallel exit code handling
   - OLD: used 'wait' without checking individual exit codes
   - NEW: capture each PID and check exit code with 'wait $pid'
   - On failure: print the log file and set failed flag
   - Exit with error if any group failed

This ensures CI correctly fails when:
- Any unit test fails
- Any of the 4 functional test groups fails
- Any parallel build configuration fails
2026-01-17 18:03:07 +03:00

272 lines
8.2 KiB
Bash
Executable File

#!/usr/bin/env bash
log_content()
{
echo
echo "Content of $1:"
cat "$1"
}
error_handler()
{
ERR_CODE=$?
log_content ./config.log
log_content ./test-suite.log
log_content ./test-suite-memcheck.log
echo
echo "Error ${ERR_CODE} with command '${BASH_COMMAND}' on line ${BASH_LINENO[0]}. Exiting."
echo
exit ${ERR_CODE}
}
trap error_handler ERR
# Verify that test failures are properly detected
# This is a meta-test: it runs a deliberately failing test
# and checks that the test framework reports the failure correctly
verify_test_failure_detection()
{
echo
echo "==> Verifying test failure detection..."
# Create a simple failing test
cat > /tmp/test_must_fail.c << 'EOF'
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
static void test_that_must_fail(void **state) {
(void)state;
assert_true(0); // This MUST fail
}
int main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_that_must_fail),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
EOF
# Compile the failing test
if ! gcc -o /tmp/test_must_fail /tmp/test_must_fail.c -lcmocka 2>/dev/null; then
echo "Warning: Could not compile test failure verification (cmocka not available?)"
echo "Skipping test failure detection verification"
return 0
fi
# Test 1: Single failing test detection
echo " Testing single test failure detection..."
if /tmp/test_must_fail > /tmp/test_must_fail.log 2>&1; then
echo "ERROR: Test that should fail returned success (exit code 0)"
echo "This means the test framework is NOT detecting failures correctly!"
echo "--- Test output ---"
cat /tmp/test_must_fail.log
echo "--- End output ---"
rm -f /tmp/test_must_fail /tmp/test_must_fail.c /tmp/test_must_fail.log
exit 1
fi
echo " ✓ Single test failure correctly detected"
# Test 2: Parallel failure detection (simulates check-functional-parallel)
echo " Testing parallel test failure detection..."
failed=0
/tmp/test_must_fail > /tmp/p1.log 2>&1 & pid1=$!
true > /tmp/p2.log 2>&1 & pid2=$! # This passes
/tmp/test_must_fail > /tmp/p3.log 2>&1 & pid3=$!
true > /tmp/p4.log 2>&1 & pid4=$! # This passes
wait $pid1 || failed=$((failed + 1))
wait $pid2 || failed=$((failed + 1))
wait $pid3 || failed=$((failed + 1))
wait $pid4 || failed=$((failed + 1))
if [ $failed -ne 2 ]; then
echo "ERROR: Expected 2 failures in parallel tests, got $failed"
echo "Parallel failure detection is broken!"
rm -f /tmp/test_must_fail /tmp/test_must_fail.c /tmp/test_must_fail.log /tmp/p?.log
exit 1
fi
echo " ✓ Parallel test failures correctly detected (2 of 4 failed as expected)"
rm -f /tmp/test_must_fail /tmp/test_must_fail.c /tmp/test_must_fail.log /tmp/p?.log
echo "✓ Test failure detection verified"
}
num_cores()
{
# Check for cores, for systems with:
# Line 1. Linux w/ coreutils, or...
# Line 2. OpenBSD, FreeBSD, NetBSD or macOS, or...
# Line 3. Fallback for Linux w/o coreutils (glibc).
nproc \
|| sysctl -n hw.ncpu \
|| getconf _NPROCESSORS_ONLN 2>/dev/null
}
# Run test failure detection verification first
verify_test_failure_detection
./bootstrap.sh
tests=()
MAKE="make --quiet -j$(num_cores)"
CC="gcc"
ARCH="$(uname | tr '[:upper:]' '[:lower:]')"
case "$ARCH" in
linux*)
# 4 configurations for parallel CI
tests=(
# 1. Full build (all features enabled)
"--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp
--enable-omemo --enable-plugins --enable-c-plugins
--enable-python-plugins --with-xscreensaver --enable-omemo-qrcode --enable-gdk-pixbuf"
# 2. Minimal build (all optional features disabled)
"--disable-notifications --disable-icons-and-clipboard --disable-otr --disable-pgp
--disable-omemo --disable-plugins --disable-c-plugins
--disable-python-plugins --without-xscreensaver --disable-omemo-qrcode --disable-gdk-pixbuf"
# 3. No encryption (disable otr, pgp, omemo)
"--disable-pgp --disable-otr --disable-omemo --disable-omemo-qrcode"
# 4. Default configuration
""
)
source /etc/profile.d/debuginfod.sh 2>/dev/null || true
;;
darwin*)
# 4 configurations for parallel CI
tests=(
# 1. Full build (all features enabled)
"--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp
--enable-omemo --enable-plugins --enable-c-plugins
--enable-python-plugins"
# 2. Minimal build (all optional features disabled)
"--disable-notifications --disable-icons-and-clipboard --disable-otr --disable-pgp
--disable-omemo --disable-plugins --disable-c-plugins
--disable-python-plugins"
# 3. No encryption (disable otr, pgp, omemo)
"--disable-pgp --disable-otr --disable-omemo"
# 4. Default configuration
""
)
;;
openbsd*)
MAKE="gmake"
# TODO(#1231):
# `-std=gnu99 -fexec-charset=UTF-8` to silence:
# src/event/server_events.c:1453:19: error: universal character names are only valid in C++ and C99
# src/event/server_events.c:1454:19: error: universal character names are only valid in C++ and C99
CC="egcc -std=gnu99 -fexec-charset=UTF-8"
# 4 configurations for parallel CI
tests=(
# 1. Full build (all features enabled)
"--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp
--enable-omemo --enable-plugins --enable-c-plugins
--enable-python-plugins"
# 2. Minimal build (all optional features disabled)
"--disable-notifications --disable-icons-and-clipboard --disable-otr --disable-pgp
--disable-omemo --disable-plugins --disable-c-plugins
--disable-python-plugins"
# 3. No encryption (disable otr, pgp, omemo)
"--disable-pgp --disable-otr --disable-omemo"
# 4. Default configuration
""
)
;;
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 build_dir="build-$idx"
local log_file="build-$idx.log"
{
echo "=== Build $idx started at $(date) ==="
echo "--> Building in $build_dir with ./configure -C $features $extra_args"
mkdir -p "$build_dir"
cd "$build_dir"
# shellcheck disable=SC2086
../configure -C $features $extra_args
$MAKE CC="${CC}"
$MAKE check-functional-parallel
./profanity -v
$MAKE clean
cd ..
rm -rf "$build_dir"
echo "=== Build $idx completed at $(date) ==="
} > "$log_file" 2>&1
}
# Run all 4 configurations in parallel
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" &
pids+=("$!")
echo "Started build $idx (PID: $!)"
fi
done
# Wait for all builds and check exit codes
failed=0
for i in "${!pids[@]}"; do
idx=$((i + 1))
if wait "${pids[$i]}"; then
echo "✓ Build $idx passed"
else
echo "✗ Build $idx failed"
echo "--- Log for build $idx ---"
cat "build-$idx.log"
echo "--- End log ---"
failed=1
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
exit 1
fi