285 lines
10 KiB
Bash
285 lines
10 KiB
Bash
|
|
#!/bin/bash
|
||
|
|
#
|
||
|
|
# klammerset_test.sh — Regression tests for the @@@klammerset system command.
|
||
|
|
#
|
||
|
|
# @@@klammerset declares a Klammerset: a named, logically related group of
|
||
|
|
# klammer definitions. The declaration is operative — processing it reads
|
||
|
|
# the :requires files and then the :files, in list order, at the point of the
|
||
|
|
# declaration; relative names resolve against the declaring file's directory,
|
||
|
|
# never the cwd. A repeated declaration of an already-registered symbol is
|
||
|
|
# skipped (loaded once), which is what makes :requires idempotent. The
|
||
|
|
# klammers themselves live in the Machine's flat Klammer_registry; the
|
||
|
|
# Klammerset holds metadata and the file list only.
|
||
|
|
#
|
||
|
|
# Engine tier: no SKS. Fixtures live in tst/klammerset/ and define their own
|
||
|
|
# target ("fix") inline.
|
||
|
|
#
|
||
|
|
# Usage: ./klammerset_test.sh
|
||
|
|
# Exit code: 0 if all tests pass, 1 otherwise.
|
||
|
|
|
||
|
|
PASS=0
|
||
|
|
FAIL=0
|
||
|
|
KTEXT=ktext
|
||
|
|
K=${KLAMMERTEXT_HOME:?KLAMMERTEXT_HOME must be set}
|
||
|
|
FIX=$K/tst/klammerset
|
||
|
|
|
||
|
|
red=$'\033[31m'
|
||
|
|
green=$'\033[32m'
|
||
|
|
bold=$'\033[1m'
|
||
|
|
reset=$'\033[0m'
|
||
|
|
|
||
|
|
# strip leading/trailing blank lines and surrounding whitespace
|
||
|
|
trim() { awk '{ sub(/[ \t\r]+$/, "") } { line[NR]=$0 } END { f=1; while (f<=NR && line[f]=="") f++; l=NR; while (l>=1 && line[l]=="") l--; for (i=f;i<=l;i++) print line[i] }'; }
|
||
|
|
|
||
|
|
# check_eq TEST_NAME EXPECTED KTEXT_ARGS...
|
||
|
|
# Runs ktext, expects exit status 0, and compares trimmed stdout to EXPECTED.
|
||
|
|
check_eq() {
|
||
|
|
local test_name="$1"
|
||
|
|
local expected="$2"
|
||
|
|
shift 2
|
||
|
|
|
||
|
|
local output status
|
||
|
|
output=$("$KTEXT" "$@" 2>/tmp/klammerset_test_err.$$)
|
||
|
|
status=$?
|
||
|
|
output=$(printf '%s' "$output" | trim)
|
||
|
|
|
||
|
|
if [ $status -ne 0 ]; then
|
||
|
|
echo "${red}FAIL${reset} $test_name — ktext exited $status"
|
||
|
|
echo " stderr: $(head -3 /tmp/klammerset_test_err.$$)"
|
||
|
|
FAIL=$((FAIL + 1))
|
||
|
|
return
|
||
|
|
fi
|
||
|
|
if [ "$output" = "$expected" ]; then
|
||
|
|
echo "${green}PASS${reset} $test_name"
|
||
|
|
PASS=$((PASS + 1))
|
||
|
|
else
|
||
|
|
echo "${red}FAIL${reset} $test_name"
|
||
|
|
echo " expected: [$expected]"
|
||
|
|
echo " got: [$output]"
|
||
|
|
FAIL=$((FAIL + 1))
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
# check_contains TEST_NAME SUBSTRING KTEXT_ARGS...
|
||
|
|
# Runs ktext, expects exit status 0, and checks that the combined output
|
||
|
|
# contains SUBSTRING.
|
||
|
|
check_contains() {
|
||
|
|
local test_name="$1"
|
||
|
|
local needle="$2"
|
||
|
|
shift 2
|
||
|
|
|
||
|
|
local output status
|
||
|
|
output=$("$KTEXT" "$@" 2>&1)
|
||
|
|
status=$?
|
||
|
|
|
||
|
|
if [ $status -ne 0 ]; then
|
||
|
|
echo "${red}FAIL${reset} $test_name — ktext exited $status"
|
||
|
|
echo " output: $(echo "$output" | head -3)"
|
||
|
|
FAIL=$((FAIL + 1))
|
||
|
|
return
|
||
|
|
fi
|
||
|
|
if printf '%s' "$output" | grep -qF "$needle"; then
|
||
|
|
echo "${green}PASS${reset} $test_name"
|
||
|
|
PASS=$((PASS + 1))
|
||
|
|
else
|
||
|
|
echo "${red}FAIL${reset} $test_name"
|
||
|
|
echo " expected output to contain: [$needle]"
|
||
|
|
echo " got: $(echo "$output" | head -5)"
|
||
|
|
FAIL=$((FAIL + 1))
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
# check_fails TEST_NAME SUBSTRING KTEXT_ARGS...
|
||
|
|
# Runs ktext, expects a NONZERO exit status, and checks that the combined
|
||
|
|
# output contains SUBSTRING.
|
||
|
|
check_fails() {
|
||
|
|
local test_name="$1"
|
||
|
|
local needle="$2"
|
||
|
|
shift 2
|
||
|
|
|
||
|
|
local output status
|
||
|
|
output=$("$KTEXT" "$@" 2>&1)
|
||
|
|
status=$?
|
||
|
|
|
||
|
|
if [ $status -eq 0 ]; then
|
||
|
|
echo "${red}FAIL${reset} $test_name — expected an error, ktext exited 0"
|
||
|
|
FAIL=$((FAIL + 1))
|
||
|
|
return
|
||
|
|
fi
|
||
|
|
if printf '%s' "$output" | grep -qF "$needle"; then
|
||
|
|
echo "${green}PASS${reset} $test_name"
|
||
|
|
PASS=$((PASS + 1))
|
||
|
|
else
|
||
|
|
echo "${red}FAIL${reset} $test_name"
|
||
|
|
echo " expected error output to contain: [$needle]"
|
||
|
|
echo " got: $(echo "$output" | head -5)"
|
||
|
|
FAIL=$((FAIL + 1))
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
echo "${bold}@@@klammerset tests${reset}"
|
||
|
|
echo "======================="
|
||
|
|
echo
|
||
|
|
|
||
|
|
# --- Loading ---------------------------------------------------------------
|
||
|
|
|
||
|
|
# 1. :files load in list order: klammers.k defines @greet for the target
|
||
|
|
# that base.k declares, so base.k must have been read first.
|
||
|
|
check_eq "1. :files load in order (target before klammer)" \
|
||
|
|
"Hello World" \
|
||
|
|
-k "$FIX/decl.k" -s '@greet World @' -t fix
|
||
|
|
|
||
|
|
# 2. :requires loads the dependency before the set's own files.
|
||
|
|
check_eq "2. :requires loads the dependency" \
|
||
|
|
"--" \
|
||
|
|
-k "$FIX/decl.k" -s '@dash@' -t fix
|
||
|
|
|
||
|
|
# 3. Program order: a definition AFTER the declaration in the declaring file
|
||
|
|
# is available (there is no :text argument; the declaring file's own
|
||
|
|
# content plays that role).
|
||
|
|
check_eq "3. trailing definition in the declaring file" \
|
||
|
|
"AFTER" \
|
||
|
|
-k "$FIX/decl.k" -s '@after@' -t fix
|
||
|
|
|
||
|
|
# 4. Relative :files names resolve against the DECLARING file's directory,
|
||
|
|
# not the cwd (run from an unrelated directory).
|
||
|
|
output=$( (cd /tmp && "$KTEXT" -k "$FIX/decl.k" -s '@greet Elsewhere @' -t fix) 2>/dev/null | trim )
|
||
|
|
if [ "$output" = "Hello Elsewhere" ]; then
|
||
|
|
echo "${green}PASS${reset} 4. :files resolve against the declaring file's directory"
|
||
|
|
PASS=$((PASS + 1))
|
||
|
|
else
|
||
|
|
echo "${red}FAIL${reset} 4. :files resolve against the declaring file's directory"
|
||
|
|
echo " expected: [Hello Elsewhere]"
|
||
|
|
echo " got: [$output]"
|
||
|
|
FAIL=$((FAIL + 1))
|
||
|
|
fi
|
||
|
|
|
||
|
|
# 5. A filename with a space in the :files list (standalone "/" separator).
|
||
|
|
check_eq "5. spacey filename in :files" \
|
||
|
|
"SPACEY" \
|
||
|
|
-k "$FIX/spacey.k" -s '@spacey@' -t fix
|
||
|
|
|
||
|
|
# --- The already-loaded guard ------------------------------------------------
|
||
|
|
|
||
|
|
# 6. A second declaration of an already-registered symbol is skipped, not an
|
||
|
|
# error: wrapper.k requires decl.k (registers "kit") and then decl2.k
|
||
|
|
# (re-declares "kit"); the wrapper still loads and kit's klammers work.
|
||
|
|
check_eq "6. duplicate declaration is skipped, not an error" \
|
||
|
|
"Hello Again" \
|
||
|
|
-k "$FIX/wrapper.k" -s '@greet Again @' -t fix
|
||
|
|
|
||
|
|
# 7. ...and the skipped declaration's files are NOT loaded.
|
||
|
|
check_fails "7. skipped declaration loads none of its files" \
|
||
|
|
"only_dup" \
|
||
|
|
-k "$FIX/wrapper.k" -s '@only_dup@' -t fix
|
||
|
|
|
||
|
|
# --- Introspection -----------------------------------------------------------
|
||
|
|
|
||
|
|
# 8. -m lists the registered klammersets.
|
||
|
|
check_contains "8. -m shows the klammerset symbol" \
|
||
|
|
"kit" \
|
||
|
|
-k "$FIX/decl.k" -s 'x' -t fix -m
|
||
|
|
|
||
|
|
check_contains "9. -m shows the required klammerset too" \
|
||
|
|
"Utility klammers for engine tests" \
|
||
|
|
-k "$FIX/decl.k" -s 'x' -t fix -m
|
||
|
|
|
||
|
|
# --- Errors --------------------------------------------------------------------
|
||
|
|
|
||
|
|
# 10. A listed file that does not exist is a clean klammerset error.
|
||
|
|
check_fails "10. missing file in :files" \
|
||
|
|
"missing.k" \
|
||
|
|
-k "$FIX/bad_file.k" -s 'x' -t fix
|
||
|
|
|
||
|
|
# 11. A symbol must be an identifier (starts with a letter; letters, digits,
|
||
|
|
# underscores).
|
||
|
|
check_fails "11. invalid symbol rejected" \
|
||
|
|
"not valid" \
|
||
|
|
-k none -s '@@@klammerset 9bad | Bad symbol @@@' -d
|
||
|
|
|
||
|
|
# --- The search path (symbol -> <dir>/<symbol>/<symbol>.k) -------------------
|
||
|
|
|
||
|
|
# Runtime fixtures: a document directory holding a local klammerset, and a
|
||
|
|
# separate directory serving as a KLAMMERTEXT_KLAMMERSETS stage.
|
||
|
|
DOCDIR=$(mktemp -d /tmp/klammerset_doc.XXXXXX)
|
||
|
|
ENVDIR=$(mktemp -d /tmp/klammerset_env.XXXXXX)
|
||
|
|
trap 'rm -rf "$DOCDIR" "$ENVDIR" /tmp/klammerset_test_err.$$' EXIT
|
||
|
|
|
||
|
|
mkdir -p "$DOCDIR/locset" "$ENVDIR/envset" "$ENVDIR/locset"
|
||
|
|
printf '@@@klammerset locset | Document-local set @@@\n@@@target fixL | Fixture @@@\n@@local_k.fixL : LOCAL @@\n' \
|
||
|
|
> "$DOCDIR/locset/locset.k"
|
||
|
|
printf '@local_k@\n' > "$DOCDIR/doc.kt"
|
||
|
|
printf '@@@klammerset envset | Installed set @@@\n@@@target fixE | Fixture @@@\n@@env_k.fixE : ENV @@\n' \
|
||
|
|
> "$ENVDIR/envset/envset.k"
|
||
|
|
printf '@@@klammerset locset | Shadow candidate @@@\n@@@target fixL | Fixture @@@\n@@local_k.fixL : ENV-SHADOWED @@\n' \
|
||
|
|
> "$ENVDIR/locset/locset.k"
|
||
|
|
|
||
|
|
# 12. Stage 1 for a file input: the document's directory.
|
||
|
|
check_eq "12. symbol resolves in the document's directory" \
|
||
|
|
"LOCAL" \
|
||
|
|
"$DOCDIR/doc.kt" -k locset -t fixL -d
|
||
|
|
|
||
|
|
# 13. Stage 1 for string input: the cwd stands in for the document.
|
||
|
|
output=$( (cd "$DOCDIR" && "$KTEXT" -s '@local_k@' -k locset -t fixL) 2>/dev/null | trim )
|
||
|
|
if [ "$output" = "LOCAL" ]; then
|
||
|
|
echo "${green}PASS${reset} 13. cwd stands in for the document (string input)"
|
||
|
|
PASS=$((PASS + 1))
|
||
|
|
else
|
||
|
|
echo "${red}FAIL${reset} 13. cwd stands in for the document (string input)"
|
||
|
|
echo " expected: [LOCAL] got: [$output]"
|
||
|
|
FAIL=$((FAIL + 1))
|
||
|
|
fi
|
||
|
|
|
||
|
|
# 14. Stage 2: the KLAMMERTEXT_KLAMMERSETS directories.
|
||
|
|
output=$(KLAMMERTEXT_KLAMMERSETS=$ENVDIR "$KTEXT" -s '@env_k@' -k envset -t fixE 2>/dev/null | trim)
|
||
|
|
if [ "$output" = "ENV" ]; then
|
||
|
|
echo "${green}PASS${reset} 14. symbol resolves in KLAMMERTEXT_KLAMMERSETS"
|
||
|
|
PASS=$((PASS + 1))
|
||
|
|
else
|
||
|
|
echo "${red}FAIL${reset} 14. symbol resolves in KLAMMERTEXT_KLAMMERSETS"
|
||
|
|
echo " expected: [ENV] got: [$output]"
|
||
|
|
FAIL=$((FAIL + 1))
|
||
|
|
fi
|
||
|
|
|
||
|
|
# 15. Shadowing: the document-local set wins over the installed one.
|
||
|
|
output=$(KLAMMERTEXT_KLAMMERSETS=$ENVDIR "$KTEXT" "$DOCDIR/doc.kt" -k locset -t fixL -d 2>/dev/null | trim)
|
||
|
|
if [ "$output" = "LOCAL" ]; then
|
||
|
|
echo "${green}PASS${reset} 15. document-local set shadows the installed one"
|
||
|
|
PASS=$((PASS + 1))
|
||
|
|
else
|
||
|
|
echo "${red}FAIL${reset} 15. document-local set shadows the installed one"
|
||
|
|
echo " expected: [LOCAL] got: [$output]"
|
||
|
|
FAIL=$((FAIL + 1))
|
||
|
|
fi
|
||
|
|
|
||
|
|
# 16. :requires by symbol, resolved with the declaring directory as the
|
||
|
|
# local stage (the required set sits inside the declaring set's dir).
|
||
|
|
mkdir -p "$DOCDIR/kit2/locset"
|
||
|
|
cp "$DOCDIR/locset/locset.k" "$DOCDIR/kit2/locset/locset.k"
|
||
|
|
printf '@@@klammerset kit2 | Requires by symbol :requires locset @@@\n' > "$DOCDIR/kit2/kit2.k"
|
||
|
|
check_eq "16. :requires accepts a symbol (declaring-dir stage)" \
|
||
|
|
"LOCAL" \
|
||
|
|
-k "$DOCDIR/kit2/kit2.k" -s '@local_k@' -t fixL
|
||
|
|
|
||
|
|
# 17. Stage 3: $KLAMMERTEXT_HOME — the kdesc listing enumerates sks
|
||
|
|
# (sks/sks.k already satisfies the <symbol>/<symbol>.k convention).
|
||
|
|
output=$(kdesc --klammerset 2>/dev/null)
|
||
|
|
if printf '%s' "$output" | grep -q 'sks/sks\.k'; then
|
||
|
|
echo "${green}PASS${reset} 17. kdesc --klammerset lists sks from KLAMMERTEXT_HOME"
|
||
|
|
PASS=$((PASS + 1))
|
||
|
|
else
|
||
|
|
echo "${red}FAIL${reset} 17. kdesc --klammerset lists sks from KLAMMERTEXT_HOME"
|
||
|
|
echo " got: $(echo "$output" | head -3)"
|
||
|
|
FAIL=$((FAIL + 1))
|
||
|
|
fi
|
||
|
|
|
||
|
|
# 18. An unknown symbol is a clean error naming the search directories.
|
||
|
|
check_fails "18. unknown symbol names the search path" \
|
||
|
|
"was not found" \
|
||
|
|
-s 'x' -k nosuchset -d
|
||
|
|
|
||
|
|
echo
|
||
|
|
echo "======================="
|
||
|
|
echo "Results: ${green}$PASS passed${reset}, $([ $FAIL -gt 0 ] && echo "${red}$FAIL failed${reset}" || echo "0 failed")"
|
||
|
|
[ $FAIL -eq 0 ]
|