#!/bin/bash
# Validate the WHOLE bundle: every exercise, both builds, both symptoms.
#
#   ./run-all.sh            full sweep
#   ./run-all.sh 03 05      run only the named exercises
#   ./run-all.sh --list     print the exercises and stop
#
# For each exercise this runs its check.sh, which:
#   * compiles the broken and the fixed source EACH ALONE in a fresh temporary
#     directory, and fails on any unexpected warning;
#   * applies solution.patch to a copy of the broken file and requires the
#     result to equal the fixed file byte for byte;
#   * runs the broken build and asserts its documented symptom;
#   * runs the fixed build and asserts its documented success;
#   * repeats every timing-sensitive or probabilistic measurement.
#
# Nothing here can wedge your machine. Every fixture carries its own watchdog
# thread that calls _exit(75), and every invocation is additionally wrapped in
# an external hard timeout. Exit status 75 from a fixture means its watchdog
# fired. Exercise 08's broken build is KILLED BY SIGPIPE on purpose and its
# expected shell status is 141; a run that exited 0 would be the failure.
#
# This script contains no absolute paths and reads nothing outside its own
# directory, so an extracted copy of the archive works anywhere on disk.
set -u
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "$HERE/lib/common.sh"

SELECT=()
for arg in "$@"; do
  case "$arg" in
    -h|--help) sed -n '2,24p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
    --list) for d in "$HERE"/exercises/*/; do basename "$d"; done; exit 0 ;;
    *) SELECT+=("$arg") ;;
  esac
done

lab_require clang swiftc patch

printf '\n\033[1mHeap, scheduling, I/O and IPC — fixing exercises\033[0m\n'
printf '  %s\n' "$(swift --version 2>&1 | head -1)"
printf '  %s\n' "$(clang --version 2>&1 | head -1)"
printf '  macOS %s (%s), %s, %s cores\n' \
  "$(sw_vers -productVersion)" "$(sw_vers -buildVersion)" \
  "$(sysctl -n machdep.cpu.brand_string)" "$(sysctl -n hw.ncpu)"
printf '  page size %s bytes\n' "$(sysctl -n hw.pagesize)"
printf '  started %s\n' "$(date '+%Y-%m-%d %H:%M:%S %Z')"

RUN=0; FAILED=0; FAILED_NAMES=""
START=$(date +%s)

for check in "$HERE"/exercises/*/check.sh; do
  [ -x "$check" ] || continue
  name="$(basename "$(dirname "$check")")"
  if [ "${#SELECT[@]}" -gt 0 ]; then
    match=0
    for want in "${SELECT[@]}"; do case "$name" in "$want"*|*"$want"*) match=1 ;; esac; done
    [ "$match" -eq 1 ] || continue
  fi
  RUN=$((RUN + 1))
  printf '\n\033[1m────────────────────────────────────────────────────────────\033[0m\n'
  printf '\033[1m %s\033[0m\n' "$name"
  printf '\033[1m────────────────────────────────────────────────────────────\033[0m\n'
  if "$check"; then
    printf '  \033[32m%s: all assertions passed\033[0m\n' "$name"
  else
    FAILED=$((FAILED + 1)); FAILED_NAMES="$FAILED_NAMES $name"
    printf '  \033[31m%s: FAILED\033[0m\n' "$name"
  fi
done

ELAPSED=$(( $(date +%s) - START ))

printf '\n\033[1m════════════════════════════════════════════════════════════\033[0m\n'
if [ "$RUN" -eq 0 ]; then
  printf ' no exercises matched\n'
  exit 2
elif [ "$FAILED" -eq 0 ]; then
  printf ' \033[32mBUNDLE OK\033[0m — %d of %d exercises validated in %ds\n' "$RUN" "$RUN" "$ELAPSED"
  printf ' every broken build failed as documented; every fixed build succeeded\n'
  exit 0
else
  printf ' \033[31mBUNDLE FAILED\033[0m — %d of %d exercises failed in %ds:%s\n' \
    "$FAILED" "$RUN" "$ELAPSED" "$FAILED_NAMES"
  exit 1
fi
