#!/usr/bin/env bash # test_harness.sh — Common CLI safety/test harness for the scripts/ suite # Usage: ./scripts/test_harness.sh [--verbose] [--ci] [directory] # # Discovers .sh, .py, and .yaml files in the target directory and validates them: # - .sh : runs shellcheck (or SKIPS if unavailable) # - .py : runs python3 -m py_compile # - .yaml: validates with python3 yaml.safe_load # # Exit codes: 0 = all pass, 1 = any fail set -euo pipefail # --- Defaults --- VERBOSE=0 CI_MODE=0 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" TARGET_DIR="${SCRIPT_DIR}" # --- Colors (disabled in CI) --- RED="" GREEN="" YELLOW="" CYAN="" RESET="" if [[ -t 1 && "${CI:-}" != "true" ]]; then RED=$'\033[0;31m' GREEN=$'\033[0;32m' YELLOW=$'\033[0;33m' CYAN=$'\033[0;36m' RESET=$'\033[0m' fi # --- Argument parsing --- while [[ $# -gt 0 ]]; do case "$1" in --verbose|-v) VERBOSE=1; shift ;; --ci) CI_MODE=1; shift ;; -*) echo "Unknown option: $1" >&2; exit 2 ;; *) TARGET_DIR="$1"; shift ;; esac done # --- Counters --- PASS=0 FAIL=0 SKIP=0 TOTAL=0 # --- Helpers --- log_verbose() { if [[ "${VERBOSE}" -eq 1 ]]; then echo " ${CYAN}[DEBUG]${RESET} $*" fi } record_pass() { ((PASS++)) ((TOTAL++)) echo "${GREEN}PASS${RESET} $1" } record_fail() { ((FAIL++)) ((TOTAL++)) echo "${RED}FAIL${RESET} $1" if [[ -n "${2:-}" ]]; then echo " ${2}" fi } record_skip() { ((SKIP++)) ((TOTAL++)) echo "${YELLOW}SKIP${RESET} $1 — $2" } # --- Checkers --- check_shell_file() { local file="$1" local rel="${file#${TARGET_DIR}/}" if command -v shellcheck &>/dev/null; then log_verbose "Running shellcheck on ${rel}" local output if output=$(shellcheck -x -S warning "${file}" 2>&1); then record_pass "${rel}" else record_fail "${rel}" "${output}" fi else record_skip "${rel}" "shellcheck not installed" fi } check_python_file() { local file="$1" local rel="${file#${TARGET_DIR}/}" log_verbose "Running py_compile on ${rel}" local output if output=$(python3 -m py_compile "${file}" 2>&1); then record_pass "${rel}" else record_fail "${rel}" "${output}" fi } check_yaml_file() { local file="$1" local rel="${file#${TARGET_DIR}/}" log_verbose "Validating YAML: ${rel}" local output if output=$(python3 -c "import yaml; yaml.safe_load(open('${file}'))" 2>&1); then record_pass "${rel}" else record_fail "${rel}" "${output}" fi } # --- Main --- echo "" echo "=== scripts/ test harness ===" echo "Target: ${TARGET_DIR}" echo "" if [[ ! -d "${TARGET_DIR}" ]]; then echo "Error: target directory '${TARGET_DIR}' not found" >&2 exit 1 fi # Check python3 availability if ! command -v python3 &>/dev/null; then echo "${RED}Error: python3 is required but not found${RESET}" >&2 exit 1 fi # Check PyYAML availability if ! python3 -c "import yaml" 2>/dev/null; then echo "${YELLOW}Warning: PyYAML not installed — YAML checks will be skipped${RESET}" >&2 YAML_AVAILABLE=0 else YAML_AVAILABLE=1 fi # Discover and check .sh files sh_files=() while IFS= read -r -d '' f; do sh_files+=("$f") done < <(find "${TARGET_DIR}" -maxdepth 1 -name "*.sh" ! -name "test_harness.sh" ! -name "test_runner.sh" -print0 | sort -z) for f in "${sh_files[@]:-}"; do [[ -n "$f" ]] && check_shell_file "$f" done # Discover and check .py files py_files=() while IFS= read -r -d '' f; do py_files+=("$f") done < <(find "${TARGET_DIR}" -maxdepth 1 -name "*.py" -print0 | sort -z) for f in "${py_files[@]:-}"; do [[ -n "$f" ]] && check_python_file "$f" done # Discover and check .yaml files in target dir yaml_files=() while IFS= read -r -d '' f; do yaml_files+=("$f") done < <(find "${TARGET_DIR}" -maxdepth 1 -name "*.yaml" -print0 | sort -z) if [[ "${YAML_AVAILABLE}" -eq 1 ]]; then for f in "${yaml_files[@]:-}"; do [[ -n "$f" ]] && check_yaml_file "$f" done else for f in "${yaml_files[@]:-}"; do [[ -n "$f" ]] && record_skip "${f#${TARGET_DIR}/}" "PyYAML not installed" done fi # --- Summary --- echo "" echo "=== Results ===" echo " ${GREEN}PASS${RESET}: ${PASS}" echo " ${RED}FAIL${RESET}: ${FAIL}" echo " ${YELLOW}SKIP${RESET}: ${SKIP}" echo " Total: ${TOTAL}" echo "" if [[ "${FAIL}" -gt 0 ]]; then echo "${RED}FAILED${RESET} — ${FAIL} file(s) did not pass validation." exit 1 else echo "${GREEN}ALL CLEAR${RESET} — all checked files passed." exit 0 fi