#!/usr/bin/env bash # Discovers and runs all test_*.sh files, printing a combined summary. # # Usage: # ./tests/run_tests.sh # run all suites # ./tests/run_tests.sh [file] # run a specific suite set -uo pipefail TESTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # ---- Colors (self-contained) ---- _R='\033[0;31m'; _G='\033[0;32m'; _B='\033[1m'; _D='\033[2m'; _N='\033[0m' total_pass=0 total_fail=0 failed_suites=() run_suite() { local file="$1" local name name="$(basename "$file")" printf "\n${_B}══════ %s ══════${_N}\n" "$name" local output exit_code=0 output=$(bash "$file" 2>&1) || exit_code=$? echo "$output" # Count results — strip ANSI codes first so the unicode chars are findable local stripped p f stripped=$(printf '%s' "$output" | sed 's/\x1b\[[0-9;]*[mGKHF]//g') p=$(printf '%s\n' "$stripped" | grep -c '✓' || true) f=$(printf '%s\n' "$stripped" | grep -c '✗' || true) total_pass=$((total_pass + p)) total_fail=$((total_fail + f)) if [[ $exit_code -ne 0 ]]; then failed_suites+=("$name") fi } # Collect files to run if [[ $# -gt 0 ]]; then files=("$@") else files=("$TESTS_DIR"/test_*.sh) fi for f in "${files[@]}"; do [[ -f "$f" ]] || { printf "${_R}Not found: %s${_N}\n" "$f" >&2; continue; } run_suite "$f" done # ---- Aggregate summary ---- total=$((total_pass + total_fail)) printf "\n${_B}══════ Summary ══════${_N}\n" printf "Suites run: %d\n" "${#files[@]}" printf "Tests: %d total, " "$total" if [[ $total_fail -eq 0 ]]; then printf "${_G}%d passed${_N}\n" "$total_pass" printf "\n${_G}All tests passed.${_N}\n" exit 0 else printf "${_G}%d passed${_N}, ${_R}%d failed${_N}\n" "$total_pass" "$total_fail" printf "\n${_R}Failed suites:${_N}\n" for s in "${failed_suites[@]}"; do printf " - %s\n" "$s" done exit 1 fi