#!/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 #include #include #include 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