diff --git a/Justfile b/Justfile index 3be1ee1..2d12c0e 100644 --- a/Justfile +++ b/Justfile @@ -101,52 +101,80 @@ ci *args: check-craftos check @just test-timeout # Run CraftOS-PC headless integration tests. Pass `--pretty` for grouped output. +[positional-arguments] test *args: - @if [ -f .env.test ]; then set -a; . ./.env.test; set +a; fi; \ - pretty=0; \ - verbose=0; \ - timeout_seconds="${TRAP_CCLIBS_TEST_TIMEOUT_SECONDS:-3}"; \ - case "$timeout_seconds" in ''|*[!0-9]*) printf '%s\n' 'TRAP_CCLIBS_TEST_TIMEOUT_SECONDS must be a positive integer' >&2; exit 1 ;; esac; \ - if [ "$timeout_seconds" -lt 1 ]; then printf '%s\n' 'TRAP_CCLIBS_TEST_TIMEOUT_SECONDS must be >= 1' >&2; exit 1; fi; \ - for a in {{args}}; do case "$a" in --pretty) pretty=1 ;; --verbose|-v) pretty=1; verbose=1 ;; esac; done; \ - rom_arg=""; \ - if [ "$(uname -s)" = "Darwin" ]; then \ - rom_arg="--rom /Applications/CraftOS-PC.app/Contents/Resources"; \ - fi; \ - repo='{{justfile_directory()}}'; \ - mount_arg="--mount-ro /apis=$repo/apis --mount-ro /programs=$repo/programs --mount-ro /startup=$repo/startup --mount-ro /tests=$repo/tests"; \ - tmp="$(mktemp)"; \ - data_dir="$(mktemp -d)"; \ - output_path="$data_dir/computer/0/trapos-test-output"; \ - exec_code="shell.run('/programs/runtest.lua', '--shutdown')"; \ - if [ "$pretty" -eq 1 ]; then exec_code="shell.run('/programs/runtest.lua', '--pretty', '--output', '/trapos-test-output', '--shutdown')"; fi; \ - if [ "$verbose" -eq 1 ]; then exec_code="shell.run('/programs/runtest.lua', '--verbose', '--output', '/trapos-test-output', '--shutdown')"; fi; \ - craftos --directory "$data_dir" --headless $rom_arg $mount_arg --exec "$exec_code" >"$tmp" 2>&1 & \ - pid="$!"; \ - ( sleep "$timeout_seconds"; kill -TERM "$pid" >/dev/null 2>&1 ) & \ - watchdog="$!"; \ - wait "$pid" >/dev/null 2>&1; \ - status="$?"; \ - kill "$watchdog" >/dev/null 2>&1 || true; \ - wait "$watchdog" >/dev/null 2>&1 || true; \ - if grep -q __TRAPOS_TEST_OK__ "$tmp"; then \ - if [ "$pretty" -eq 1 ] && [ -f "$output_path" ]; then cat "$output_path"; fi; \ - rm -f "$tmp"; \ - rm -rf "$data_dir"; \ - else \ - red=$(printf '\033[31m'); reset=$(printf '\033[0m'); \ - if [ "$status" -eq 143 ]; then \ - printf '%s\n' "${red}FAIL${reset} CraftOS integration tests timed out after ${timeout_seconds}s" >&2; \ - else \ - printf '%s\n' "${red}FAIL${reset} CraftOS integration tests did not print __TRAPOS_TEST_OK__" >&2; \ - fi; \ - if [ -f "$output_path" ]; then cat "$output_path" >&2; fi; \ - cat "$tmp" >&2; \ - rm -f "$tmp"; \ - rm -rf "$data_dir"; \ - exit 1; \ - fi; \ - printf '%s\n' 'OK: CraftOS integration tests passed' + #!/usr/bin/env bash + set -uo pipefail + + if [ -f .env.test ]; then set -a; . ./.env.test; set +a; fi + + pretty=0 + has_output=0 + timeout_seconds="${TRAP_CCLIBS_TEST_TIMEOUT_SECONDS:-3}" + case "$timeout_seconds" in ''|*[!0-9]*) printf '%s\n' 'TRAP_CCLIBS_TEST_TIMEOUT_SECONDS must be a positive integer' >&2; exit 1 ;; esac + if [ "$timeout_seconds" -lt 1 ]; then printf '%s\n' 'TRAP_CCLIBS_TEST_TIMEOUT_SECONDS must be >= 1' >&2; exit 1; fi + + runtest_args=("$@") + for a in "$@"; do + case "$a" in + --pretty|--verbose|-v) pretty=1 ;; + --output) has_output=1 ;; + esac + done + if [ "$pretty" -eq 1 ] && [ "$has_output" -eq 0 ]; then + runtest_args+=(--output /trapos-test-output) + fi + runtest_args+=(--shutdown) + + lua_quote() { + local value="$1" + value="${value//\\/\\\\}" + value="${value//\'/\\\'}" + printf "'%s'" "$value" + } + + exec_code="shell.run('/programs/runtest.lua'" + for arg in "${runtest_args[@]}"; do + exec_code+=", $(lua_quote "$arg")" + done + exec_code+=")" + + rom_arg=() + if [ "$(uname -s)" = "Darwin" ]; then + rom_arg=(--rom /Applications/CraftOS-PC.app/Contents/Resources) + fi + repo='{{justfile_directory()}}' + mount_arg=(--mount-ro "/apis=$repo/apis" --mount-ro "/programs=$repo/programs" --mount-ro "/startup=$repo/startup" --mount-ro "/tests=$repo/tests") + tmp="$(mktemp)" + data_dir="$(mktemp -d)" + output_path="$data_dir/computer/0/trapos-test-output" + + craftos --directory "$data_dir" --headless "${rom_arg[@]}" "${mount_arg[@]}" --exec "$exec_code" >"$tmp" 2>&1 & + pid="$!" + ( sleep "$timeout_seconds"; kill -TERM "$pid" >/dev/null 2>&1 ) & + watchdog="$!" + wait "$pid" >/dev/null 2>&1 + status="$?" + kill "$watchdog" >/dev/null 2>&1 || true + wait "$watchdog" >/dev/null 2>&1 || true + if grep -q __TRAPOS_TEST_OK__ "$tmp"; then + if [ "$pretty" -eq 1 ] && [ -f "$output_path" ]; then cat "$output_path"; fi + rm -f "$tmp" + rm -rf "$data_dir" + else + red=$(printf '\033[31m'); reset=$(printf '\033[0m') + if [ "$status" -eq 143 ]; then + printf '%s\n' "${red}FAIL${reset} CraftOS integration tests timed out after ${timeout_seconds}s" >&2 + else + printf '%s\n' "${red}FAIL${reset} CraftOS integration tests did not print __TRAPOS_TEST_OK__" >&2 + fi + if [ -f "$output_path" ]; then cat "$output_path" >&2; fi + cat "$tmp" >&2 + rm -f "$tmp" + rm -rf "$data_dir" + exit 1 + fi + printf '%s\n' 'OK: CraftOS integration tests passed' # Harness self-test: run a tests/harness fixture and assert which timeout layer # caught it. `expect` is "lua" (libtest cancels the case) or "shell" (the shell