#!/usr/bin/env bash set -euo pipefail ROOT="$(cd "$(dirname "$0")/../.." && pwd)" BIN="${NYASH_BIN:-$ROOT/target/release/hakorune}" if [ ! -x "$BIN" ]; then echo "[TEST] hakorune not built: $BIN" >&2 echo "Run: cargo build --release" >&2 exit 2 fi TARGET_DIR="$ROOT/tools/hako_check/tests" fail=0 run_case() { local dir="$1" local expected="$dir/expected.json" local input_ok="$dir/ok.hako" local input_ng="$dir/ng.hako" if [ ! -f "$expected" ]; then echo "[TEST] skip (no expected): $dir"; return; fi if [ ! -f "$input_ok" ] && [ ! -f "$input_ng" ]; then echo "[TEST] skip (no inputs): $dir"; return; fi local tmp_out="/tmp/hako_test_$$.json" # Build a tiny wrapper program to call HakoAnalyzerBox.run with constructed argv local path_ok text_ok local path_ng text_ng if [ -f "$input_ok" ]; then path_ok="$input_ok" text_ok="$(sed 's/\r$//' "$input_ok")" else : fi if [ -f "$input_ng" ]; then path_ng="$input_ng" text_ng="$(sed 's/\r$//' "$input_ng")" else : fi # Build argv array for analyzer CLI (preserve newlines in text) ARGS=( --debug --format json-lsp ) if [ -f "$input_ok" ]; then ARGS+=( --source-file "$path_ok" "$text_ok" ); fi if [ -f "$input_ng" ]; then ARGS+=( --source-file "$path_ng" "$text_ng" ); fi # Directly invoke analyzer CLI with args via '--', avoid wrapper/FS NYASH_DISABLE_NY_COMPILER=1 HAKO_DISABLE_NY_COMPILER=1 \ NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_SEAM_TOLERANT=1 HAKO_PARSER_SEAM_TOLERANT=1 \ NYASH_ENABLE_USING=1 HAKO_ENABLE_USING=1 NYASH_USING_AST=1 \ NYASH_JSON_ONLY=1 \ "$BIN" --backend vm tools/hako_check/cli.hako -- "${ARGS[@]}" >"$tmp_out" 2>&1 || true # Extract diagnostics JSON (one-line or pretty block) tmp_json="/tmp/hako_test_json_$$.json" json_line=$(grep -m1 '^\{"diagnostics"' "$tmp_out" || true) if [ -n "$json_line" ] && echo "$json_line" | grep -q '\]}' ; then echo "$json_line" > "$tmp_json" else json_block=$(awk '/^\{"diagnostics"/{f=1} f{print} /\]\}/{exit}' "$tmp_out" ) if [ -z "$json_block" ]; then echo "[TEST/ERROR] no diagnostics JSON found; possible VM error. log head:" >&2 sed -n '1,120p' "$tmp_out" >&2 || true json_block='{"diagnostics":[]}' fi printf "%s\n" "$json_block" > "$tmp_json" fi # Normalize absolute paths to basenames for stable comparison tmp_norm="/tmp/hako_test_norm_$$.json" cp "$tmp_json" "$tmp_norm" if [ -f "$input_ok" ]; then base_ok="$(basename "$input_ok")"; abs_ok="$input_ok" sed -i "s#\"file\":\"$abs_ok\"#\"file\":\"$base_ok\"#g" "$tmp_norm" sed -i "s#${abs_ok//\//\/}#${base_ok//\//\/}#g" "$tmp_norm" fi if [ -f "$input_ng" ]; then base_ng="$(basename "$input_ng")"; abs_ng="$input_ng" sed -i "s#\"file\":\"$abs_ng\"#\"file\":\"$base_ng\"#g" "$tmp_norm" sed -i "s#${abs_ng//\//\/}#${base_ng//\//\/}#g" "$tmp_norm" fi # Align trailing blank line behavior to expected (tolerate one extra blank line) if [ -f "$expected" ]; then if [ -z "$(tail -n1 "$tmp_norm")" ]; then :; else if [ -z "$(tail -n1 "$expected")" ]; then printf "\n" >> "$tmp_norm"; fi fi fi # Replace absolute path occurrences in message with PLACEHOLDER if [ -f "$input_ng" ]; then sed -i "s#${abs_ng//\//\/}#PLACEHOLDER#g" "$tmp_norm" fi if ! diff -u "$expected" "$tmp_norm" >/dev/null; then echo "[TEST/FAIL] $dir" >&2 diff -u "$expected" "$tmp_norm" || true fail=$((fail+1)) else echo "[TEST/OK] $dir" fi rm -f "$tmp_out" "$tmp_norm" "$tmp_json" } # Handle arguments: if provided, test only specified dirs; otherwise test all if [ $# -gt 0 ]; then # Test only specified directories for arg in "$@"; do # Convert relative path to absolute if [[ "$arg" == /* ]]; then d="$arg" else d="$ROOT/$arg" fi [ -d "$d" ] || { echo "[TEST] not a directory: $d" >&2; continue; } run_case "$d" done else # Test all directories for d in "$TARGET_DIR"/*; do [ -d "$d" ] || continue run_case "$d" done fi if [ $fail -ne 0 ]; then echo "[TEST/SUMMARY] failures=$fail" >&2 exit 1 fi echo "[TEST/SUMMARY] all green" exit 0