#!/bin/sh # tests/run-tests.sh — lightweight test runner for session-tools. # # Each test file (tests/test_*.sh) defines one or more functions starting # with `test_`. The runner sources every file, finds those functions, and # invokes them with PS4 trace + per-test pass/fail tally. Exits non-zero on # any failure. # # Conventions: # - Use the `assert_eq [msg]` helper. # - Tests should be isolated: each function builds its own fixtures. # - Tests must not require network or sudo. set -u ROOT=$(cd "$(dirname "$0")/.." && pwd) TESTS_DIR=$ROOT/tests pass=0; fail=0; failed_names="" # --------------------------------------------------------------------------- # Assertion helpers (available to every test file) # --------------------------------------------------------------------------- assert_eq() { _exp=$1; _got=$2; _msg=${3:-} if [ "$_exp" = "$_got" ]; then return 0; fi printf ' ✗ assertion failed%s\n expected: %s\n actual: %s\n' \ "${_msg:+: $_msg}" "$_exp" "$_got" >&2 return 1 } assert_contains() { _hay=$1; _needle=$2; _msg=${3:-} case $_hay in *"$_needle"*) return 0 ;; esac printf ' ✗ assertion failed%s\n haystack: %s\n missing: %s\n' \ "${_msg:+: $_msg}" "$_hay" "$_needle" >&2 return 1 } assert_exit() { _exp=$1; shift "$@" >/dev/null 2>&1 _got=$? if [ "$_exp" -eq "$_got" ]; then return 0; fi printf ' ✗ exit assertion failed\n expected: %s\n actual: %s\n cmd: %s\n' \ "$_exp" "$_got" "$*" >&2 return 1 } # --------------------------------------------------------------------------- # Runner # --------------------------------------------------------------------------- for tf in "$TESTS_DIR"/test_*.sh; do [ -f "$tf" ] || continue printf '\n── %s\n' "$(basename "$tf")" # shellcheck disable=SC1090 . "$tf" # Find every `test_*` function defined by this file. POSIX has no # introspection, but the loaded source declared them; we grep the file # for `test_()`. for name in $(grep -oE '^test_[A-Za-z0-9_]+\b' "$tf" | sort -u); do # Skip if grep matched a comment. if ! type "$name" >/dev/null 2>&1; then continue; fi if ( set -e; "$name" ); then printf ' ✓ %s\n' "$name" pass=$((pass + 1)) else failed_names="$failed_names $name" fail=$((fail + 1)) fi # Unset so the next file's same-named test (if any) reloads cleanly. unset -f "$name" 2>/dev/null || true done done printf '\n────────────────\n %d passed, %d failed\n' "$pass" "$fail" if [ "$fail" -gt 0 ]; then printf ' failed:%s\n' "$failed_names" exit 1 fi