Files
klammertext/tst/verbosity_test.sh

156 lines
6.2 KiB
Bash
Raw Normal View History

An output policy for the three commands, and @cond as a true special form A snapshot of the development tree. The substantial changes since the last one: COMMAND OUTPUT POLICY. The three commands display text in exactly three cases, and each owns a stream: LOGGING under "-v" greater than 0 and an ERROR before termination go to STDERR; OUTPUT THE USER ASKED FOR goes to STDOUT. For ktext that output is a document, so "ktext doc.kt -d | ..." is now safe -- logging used to share the stream and land inside the document. A bare command prints its usage and succeeds rather than failing. Colour is emitted only to a terminal, per stream, and NO_COLOR is honoured. "-v 1" reports every decision whose outcome you could not have read off your own input: the klammerset that was loaded and from which file, a font's directory, a ":files" name's file, how "-o" was expanded. Higher levels are the trace. The commands no longer warn and continue: an anomaly is an error, described with its location. Two exceptions remain, each for a stated reason -- a condition that is expected and temporary by design, and a judgment that is a heuristic rather than exact. @cond IS NOW A TRUE SPECIAL FORM, resolved at APPLICATION time rather than when the file is read. Two consequences for a writer: * a state variable reaches the predicate. "@@@state Flag :value true @@@ @cond *Flag* | T | F @" renders "T"; it used to see the literal "*Flag*" and silently take the false branch. The document now behaves like a klammer body, whose arguments are bound before its conditionals are decided. * nothing in a discarded branch happens -- it is not read, not evaluated, not expanded. An @eval in the branch not taken used to run anyway. Its predicate relation is total and strict: true, True, 1; false, False, 0, and empty; anything else is an error at the @cond rather than silently false. @eval REACHING OUTSIDE. ":shell" and ":haskell" now keep the command's standard error out of the document (it appears under "-v 1") and treat a nonzero exit as an error naming what the command reported. A command that exits nonzero on purpose -- "grep" finding no match -- says so with "|| true". KLAMMER SETS. Several combine: "--klammersets a b c" loads all three in the order given, sharing one namespace, with the definition modes deciding collisions. "none" means none and may not be combined with other symbols. A klammerset with symbol X is declared in a file X/X.k, which is what lets two sets require the same third set without loading it twice. TESTS. Four new suites: the kdiag command's interface, the @eval primitive's contract with the outside world, and verbosity at both tiers. Three suites that could not run on macOS at all now do. Assembled from dev commit 6c8ee6c22fca.
2026-08-16 01:37:59 +02:00
#!/bin/bash
#
# verbosity_test.sh — what "-v" says, and what it must not say.
#
# The output policy (CLAUDE.md, "Command output policy") fixes what each
# verbosity level is FOR, and a policy with no test drifts back — which is how
# the suites this session repaired got where they were. The rule:
#
# -v 0 silent. Nothing but the result.
# -v 1 every decision whose outcome the user COULD NOT HAVE READ OFF THEIR
# OWN INPUT: a klammerset symbol resolved to a file, a default applied,
# "-o" expanded into a directory and a basename, a filename regrouped,
# a klammer overridden by a definition in another klammer set.
# -v 2+ the trace: what the command DID, for someone reading the code.
#
# The boundary is the part worth testing. Andy's first formulation was "-v 1
# describes a change of state", and taken literally that swallows -v 2 — every
# definition is a change of state. So the test for -v 1 is not "did something
# happen" but "could the user have predicted the outcome from what they wrote".
# Registering a klammer the user wrote, in the file they wrote, is NOT -v 1
# material; resolving a symbol through a three-stage search path with shadowing
# is.
#
# Engine tier: no klammerset beyond what a case loads deliberately. The SKS
# half of this policy — font resolution and ":files" resolution — is in
# sks/tst/verbosity_test.sh, because those are @document's decisions.
#
# Usage: ./verbosity_test.sh (needs KLAMMERTEXT_HOME set; commands on PATH)
# Exit code: 0 if all tests pass, 1 otherwise.
PASS=0
FAIL=0
KTEXT=ktext
K=${KLAMMERTEXT_HOME:?KLAMMERTEXT_HOME must be set}
red=$'\033[31m'
green=$'\033[32m'
bold=$'\033[1m'
reset=$'\033[0m'
OUTF=$(mktemp /tmp/verb_out.XXXXXX)
ERRF=$(mktemp /tmp/verb_err.XXXXXX)
TMPD=$(mktemp -d /tmp/verb_dir.XXXXXX)
trap 'rm -f "$OUTF" "$ERRF"; rm -rf "$TMPD"' EXIT
plain() { sed 's/\x1b\[[0-9;]*m//g'; }
pass() { echo "${green}PASS${reset} $1"; PASS=$((PASS + 1)); }
fail() { echo "${red}FAIL${reset} $1"; [ -n "$2" ] && echo " $2"; FAIL=$((FAIL + 1)); }
# run CMD ARGS... — fills OUTF/ERRF, sets STATUS.
STATUS=0
run() { "$@" >"$OUTF" 2>"$ERRF"; STATUS=$?; }
# reports NAME PATTERN — ERRF matches PATTERN (the log is on stderr, always).
reports() {
if plain < "$ERRF" | grep -Eq -- "$2"; then pass "$1"
else fail "$1" "no [$2] in: $(plain < "$ERRF" | head -3 | tr '\n' ' ')"; fi
}
absent() {
if plain < "$ERRF" | grep -Eq -- "$2"; then fail "$1" "unexpected [$2]"
else pass "$1"; fi
}
echo "${bold}Verbosity tests${reset}"
echo "==============="
echo
echo "-- -v 0 is silent --"
run "$KTEXT" --klammersets none -s 'hello' -d
if [ $STATUS -eq 0 ] && [ "$(cat "$OUTF")" = "hello" ] && [ ! -s "$ERRF" ]; then
pass " 1. a successful run says nothing but its result"
else
fail " 1. exit $STATUS, stdout [$(cat "$OUTF")], stderr [$(head -1 "$ERRF")]"
fi
echo
echo "-- -v 1 answers \"what did the command think I asked for?\" --"
run "$KTEXT" --klammersets none -s 'hello' -d -v 1
reports " 2. the resolved argument list is shown" '\-s +: hello'
reports " 3. ... including a default the user did not write" '\-v +: 1'
# The document is on stdout and nothing shares it, whatever -v says.
if [ "$(cat "$OUTF")" = "hello" ]; then
pass " 4. and the result is still the only thing on stdout"
else
fail " 4. stdout carried [$(head -c 60 "$OUTF")]"
fi
echo
echo "-- -v 1 reports DERIVED values --"
# The output path: the user gave a target and an input name; the directory, the
# basename and the final filename were all constructed.
printf 'hello\n' > "$TMPD/doc.kt"
run "$KTEXT" "$TMPD/doc.kt" -t txt --klammersets none -v 1
reports " 5. the output directory it constructed" 'Output directory: '
reports " 6. the output basename it constructed" 'Output basename: doc'
reports " 7. the output filename it constructed" 'Output filename: .*doc\.txt'
# A klammerset symbol resolves through a three-stage search path with
# shadowing, so the FILE it landed on is the derived value. Both branches are
# checked: the default (no --klammersets at all) reported nothing until
# 2026-08-15, which was the commonest case of all.
run "$KTEXT" -s 'x' -d -v 1
reports " 8. the DEFAULT klammerset names the file it loaded" 'Klammerset \(default\): .*sks/sks\.k'
mkdir -p "$TMPD/kset"
printf '@@@klammerset kset | A set @@@\n' > "$TMPD/kset/kset.k"
run env KLAMMERTEXT_KLAMMERSETS="$TMPD" "$KTEXT" -s 'x' -d --klammersets kset -v 1
reports " 9. a symbol names the file it resolved to" 'Klammerset "kset": .*kset/kset\.k'
run "$KTEXT" -s 'x' -d --klammersets none -v 1
reports "10. and \"none\" says that none was loaded" 'Klammersets: none'
# An override: the definition being replaced usually lives somewhere the user
# cannot see, which is what earns it a place here rather than a warning.
run "$KTEXT" --klammersets none -t fx -d -v 1 \
-s '@@@target fx | F @@@ @@w.k : W @@ @@w.fx :: A @@ @@w.fx ::: B @@ @w@'
reports "11. a klammer overridden by a later definition" 'overridden'
echo
echo "-- the boundary: -v 2 is the trace, -v 1 is not --"
run "$KTEXT" --klammersets none -s 'hello' -d -v 1
v1=$(wc -l < "$ERRF")
run "$KTEXT" --klammersets none -s 'hello' -d -v 2
v2=$(wc -l < "$ERRF")
if [ "$v2" -gt "$v1" ]; then
pass "12. -v 2 says more than -v 1 ($v1 lines -> $v2)"
else
fail "12. -v 2 added nothing ($v1 -> $v2)"
fi
# The trace names the code; the -v 1 report never should. A source location in
# a level-1 line means a msg() or a stray log level, not a decision.
run "$KTEXT" --klammersets none -s 'hello' -d -v 1
absent "13. -v 1 does not name source files" '\[[a-z_]+\.cpp:[0-9]+\]'
run "$KTEXT" --klammersets none -s 'hello' -d -v 2
reports "14. ... while -v 2 does" '\[[a-z_]+\.(cpp|h):[0-9]+\]'
echo
echo "-- the other two commands honour -v as well --"
run kdesc --katoms -v 1
if [ $STATUS -eq 0 ] && [ -s "$OUTF" ]; then
pass "15. kdesc -v 1 still produces its result on stdout"
else
fail "15. exit $STATUS, stdout $(wc -c < "$OUTF") bytes"
fi
run kdiag -v 1 '@i-x'
if [ $STATUS -eq 0 ] && [ -s "$OUTF" ]; then
pass "16. kdiag -v 1 still produces its result on stdout"
else
fail "16. exit $STATUS, stdout $(wc -c < "$OUTF") bytes"
fi
echo
echo "==============="
echo "Results: ${PASS} passed, ${FAIL} failed"
[ "$FAIL" -eq 0 ] || exit 1
exit 0