Files
klammertext/tst/kdesc_test.sh
T
ackandClaude Fable 5.1 d14da90bbf kdesc --coverage <target>: one target's view (from dev 931ea5490a5d)
For the designer building a target: the klammers that cover it, those
that do so with an empty definition (a decision that the klammer has no
meaning there), and the undecided ones (no definition). One klammer per
line, alphabetical; "all" adds the explanations; an unknown word names
the missing target.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-26 23:00:57 +02:00

282 lines
15 KiB
Bash
Executable File

#!/bin/bash
#
# kdesc_test.sh — the kdesc command's interface.
#
# The flag structure was reorganised 2026-08-11
# (notes/modifying_the_kdesc_arguments.md) on two rules:
#
# * a flag a user reaches for often gets a single letter (-k klammers,
# -t targets, -c character codes, -i input); a more specialised topic gets
# a multi-letter name (--argtypes, --katoms, --rewrite, --optionsets,
# --coverage, --klammersets, --typeface);
# * -v says how much to show about the command's PROCESSING and never what
# its RESULT contains. So the katom regex column is "--katoms full" and
# the coverage file column is "--coverage all", not verbosity levels.
#
# "-k" is the one rename that changes a name's MEANING rather than retiring
# it: it used to show katom types (now --katoms). A name-based guard cannot
# catch that -- the old spelling still works and does something else -- so
# these tests pin the new meaning down.
#
# Usage: ./kdesc_test.sh (needs KLAMMERTEXT_HOME set; kdesc on PATH)
# Exit code: 0 if all tests pass, 1 otherwise.
PASS=0
FAIL=0
KDESC=kdesc
K=${KLAMMERTEXT_HOME:?KLAMMERTEXT_HOME must be set}
DIR="$(cd "$(dirname "$0")" && pwd)/coverage"
red=$'\033[31m'
green=$'\033[32m'
bold=$'\033[1m'
reset=$'\033[0m'
plain() { sed 's/\x1b\[[0-9;]*m//g'; }
# GNU coreutils' "timeout" is NOT present on macOS, and Homebrew's is named
# "gtimeout", so a bare "timeout" made this suite fail WHOLESALE there -- every
# case, because the command never ran at all (found on Olion, 2026-08-16). The
# guard is a safety net against a hung command, not part of what is being
# tested, so it is optional: bound the command where the tool exists, run it
# directly where it does not.
if command -v timeout >/dev/null 2>&1; then
limited() { timeout 60 "$@"; }
elif command -v gtimeout >/dev/null 2>&1; then
limited() { gtimeout 60 "$@"; }
else
limited() { "$@"; }
fi
# shows NAME PATTERN CMD... — exits 0 and the output matches PATTERN.
shows() {
local name="$1" pattern="$2"; shift 2
local out status
out=$(limited "$@" 2>&1 | plain); status=$?
if [ $status -gt 128 ]; then
echo "${red}FAIL${reset} $name — died by signal $((status-128))"; FAIL=$((FAIL+1)); return
fi
if printf '%s' "$out" | grep -Eq -- "$pattern"; then
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
else
echo "${red}FAIL${reset} $name — no line matching [$pattern]"
echo " got: $(printf '%s' "$out" | head -2)"; FAIL=$((FAIL+1))
fi
}
# absent NAME PATTERN CMD... — the output does NOT match PATTERN.
absent() {
local name="$1" pattern="$2"; shift 2
if limited "$@" 2>&1 | plain | grep -Eq -- "$pattern"; then
echo "${red}FAIL${reset} $name — unexpected match [$pattern]"; FAIL=$((FAIL+1))
else
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
fi
}
echo "${bold}kdesc interface tests${reset}"
echo "====================="
echo
echo "-- the single-letter flags --"
shows " 1. -k lists klammers" '@table' "$KDESC" -k
shows " 2. -t lists targets" 'html' "$KDESC" -t
shows " 3. -c lists character codes" '.' "$KDESC" -c
shows " 4. -i reads the named input" '@x' "$KDESC" -i "$DIR/split.k" -k
# -u describes how to write a Unicode character. Asserted by OUTCOME, on
# the two things the section must carry however its prose is rewritten:
# the ^...^ notation it teaches, and a rendered character beside its code
# point -- the whole point of the section is that a reader sees the glyph
# they are about to write. It does NOT pin the prose, which is meant to
# grow (as of 2026-08-25 the section predates the sequence form and the
# typeface klammer, and does not yet mention either).
shows " 4a. -u describes the Unicode syntax" '\^0418\^' "$KDESC" -u
# The rendered glyph, as UTF-8 BYTE escapes: macOS ships bash 3.2, which
# expands $'\xHH' but not $'\U...' (that difference failed this suite's
# sibling on Olion, 2026-08-23).
# Byte semantics for this one grep: MSYS2's grep under its UTF-8 locale does
# not match a four-byte character that the C locale matches. Only here --
# under LC_ALL=C a ".{72,}" width test (10a) would count bytes, not characters.
LC_ALL=C shows " 4b. ... and shows the characters" $'\xf0\x93\x8a\xaa' "$KDESC" -u
echo
echo "-- the multi-letter topics --"
shows " 5. --katoms lists katom types" 'katom' "$KDESC" --katoms
shows " 6. --argtypes lists argtypes" 'fraction' "$KDESC" --argtypes
shows " 7. --rewrite lists rewrites" '.' "$KDESC" --rewrite
shows " 8. --optionsets lists sets" 'caption_args' "$KDESC" --optionsets
echo
echo "-- result detail is a word, not a verbosity level --"
shows " 9. --katoms full adds the regex column" 'Regex' "$KDESC" --katoms full
absent "10. --katoms alone omits it" 'Regex' "$KDESC" --katoms
# The regex goes through @c, which processes caret forms: a bare "[^^@#|]+"
# displayed as "[^@#|]+", a different regex, until the generator quoted its
# carets. These two are the patterns that carry a caret before a special.
shows " 9a. ... a regex keeps a caret before a special" '\[\^\^@#\|\]\+' "$KDESC" --katoms full
shows " 9b. ... and a caret before a colon" '\[@\|#\^:\*\]' "$KDESC" --katoms full
shows " 9c. the types are grouped under titles" '^ +Klammer definition *$' "$KDESC" --katoms
# The table is wider than the text by the klammer's own argument, so the
# paragraphs above it keep the text width (72) while a table row exceeds it.
absent "10a. the prose above the table keeps the text width" \
'^[A-Za-z].{72,}$' "$KDESC" --katoms
shows "10b. ... while the table is wider than the text" '^ +Index .{72,}$' "$KDESC" --katoms
shows "11. --coverage all adds the file column" 'sks/table/table\.k' "$KDESC" --coverage all
absent "12. --coverage alone omits it" 'sks/table/table\.k' "$KDESC" --coverage
# "all" is one word for both kinds of missing information: if you want some of
# it you probably want all of it. It shows an empty REPORTING category -- a
# designer's checklist of the shapes a klammer set can have -- but not an
# empty PROBLEM one, which would state the opposite of the "Needs attention"
# banner above it.
# Against a FIXTURE, not the SKS: which categories are empty there changes as
# the klammer set is worked on, and a test pinned to that state fails for a
# reason that has nothing to do with what it is testing. (It did: adopting
# ".*" in the SKS made "All targets, declared" non-empty.)
shows "12a. --coverage all shows an empty reporting category" \
'All targets, declared \(0\)' "$KDESC" --klammersets none -i "$DIR/cycle.k" --coverage all
absent "12b. ... hidden without it" \
'All targets, declared \(0\)' "$KDESC" --klammersets none -i "$DIR/cycle.k" --coverage
absent "12c. an empty problem category stays hidden" \
'Declared but never defined \(0\)' "$KDESC" --klammersets none -i "$DIR/cycle.k" --coverage all
absent "13. -v adds no result detail" 'Regex' "$KDESC" --katoms -v 3
echo
echo "-- -v is about processing only --"
shows "14. -v names the input it read" 'input_filenames' "$KDESC" -i "$DIR/clean.k" -t -v 1
absent "15. ... and is silent without it" 'input_filenames' "$KDESC" -i "$DIR/clean.k" -t
echo
echo "-- the klammer search --"
shows "16. a name matches" '@table' "$KDESC" -k table
absent "17. ... and others are excluded" '@document' "$KDESC" -k table
shows "18. matching is case-insensitive" '@table' "$KDESC" -k TABLE
shows "19. a description word matches" '@' "$KDESC" -k verbatim
# The words after -k are one phrase, and whitespace is collapsed on both
# sides -- a description written across several lines in a .k file must still
# match a phrase typed on one.
shows "20. several words are one phrase" '@' "$KDESC" -k displayed verbatim
shows "21. ... with runs of space collapsed" '@' "$KDESC" -k displayed verbatim
shows "22. no match says so" 'No klammer names or descriptions contained "zzqq"\.' "$KDESC" -k zzqq
absent "23. ... and lists nothing" '^ @' "$KDESC" -k zzqq
echo
echo "-- the subcommand words --"
shows "24. --coverage help explains" 'Coverage commands' "$KDESC" --coverage help
shows "25. --katoms help explains" 'Katom commands' "$KDESC" --katoms help
# Since 2026-09-26 a word after --coverage is a TARGET name, checked once the
# klammersets are loaded, so an unknown word is answered as a missing target.
shows "26. an unknown coverage word" 'No target "nonsense" is defined' "$KDESC" --coverage nonsense
# --coverage <target> (2026-09-26): one target's view -- covered, covered by
# an EMPTY definition (a decision), undecided (no definition, nothing decided).
shows "26a. --coverage <target> is that target's view" \
'Klammer coverage of target "ta"' "$KDESC" --klammersets none -i "$DIR/target_view.k" --coverage ta
shows "26b. ... counting the empty definition apart" \
'3 klammers: 2 covered \(1 by an empty definition\), 1 undecided' "$KDESC" --klammersets none -i "$DIR/target_view.k" --coverage ta
shows "26c. ... the empty one under its own heading" \
'^ @nothing$' "$KDESC" --klammersets none -i "$DIR/target_view.k" --coverage ta
shows "26d. ... the undefined one as undecided" \
'^ @elsewhere$' "$KDESC" --klammersets none -i "$DIR/target_view.k" --coverage ta
shows "26e. ... \"all\" adds the explanation" \
'decision, not a gap' "$KDESC" --klammersets none -i "$DIR/target_view.k" --coverage ta all
shows "26f. a target that is not defined is named" \
'No target "tz" is defined' "$KDESC" --klammersets none -i "$DIR/target_view.k" --coverage tz
shows "26g. two target words are one too many" \
'Unrecognized coverage command' "$KDESC" --klammersets none -i "$DIR/target_view.k" --coverage ta tb
shows "27. an unknown katom word" 'Unrecognized katom command' "$KDESC" --katoms nonsense
# The help text and the error must name the flag by its CURRENT spelling:
# both still said --font for three weeks after the 2026-08-25 rename, and # retired-ok
# the retired-name guard's entries did not match the bare word.
shows "27a. --typeface help names --typeface install" '--typeface install' "$KDESC" --typeface help
shows "27b. an unknown typeface word names the flag" 'Unrecognized typeface command: --typeface' "$KDESC" --typeface nonsense
# --typeface lists the font store as one @table per search directory,
# rendered from ref/ (@klammertext_fonts, 2026-09-23). The search path is
# DESCRIBED, not validated: a directory that does not exist is a row, never
# an error -- resolve_font skips one silently, and a fresh install has no
# ~/.klammertext/fonts yet, so an error there would fail every new user's
# first "kdesc --typeface". A typeface an earlier directory also holds is
# shadowed, and the LATER row says by which directory.
FONTS=$(mktemp -d "${TMPDIR:-/tmp}"/kdesc_fonts.XXXXXX)
shows "27c. --typeface lists the default set" 'Klammertext default typefaces' "$KDESC" --typeface
shows "27d. a missing search directory is a row" 'directory does not exist' \
env KLAMMERTEXT_FONTS="$FONTS/none" "$KDESC" --typeface
env KLAMMERTEXT_FONTS="$FONTS/none" "$KDESC" --typeface >/dev/null 2>&1
if [ $? -eq 0 ]; then
echo "${green}PASS${reset} 27e. ... and exits 0"; PASS=$((PASS+1))
else
echo "${red}FAIL${reset} 27e. a missing search directory exits nonzero"; FAIL=$((FAIL+1))
fi
shows "27f. an empty search directory says so" 'no fonts' env KLAMMERTEXT_FONTS="$FONTS" "$KDESC" --typeface
mkdir -p "$FONTS/crimson-pro"
printf "@font-face { font-family: 'Crimson Pro'; }\n" > "$FONTS/crimson-pro.css"
shows "27g. an installed typeface shadows the default one, and the default row says so" \
"^ *Crimson Pro .*$FONTS" env KLAMMERTEXT_FONTS="$FONTS" "$KDESC" --typeface
# Under a search path that installs nothing (the machine's own fonts may
# shadow a default one, so the default path is not a controlled case).
absent "27h. ... and a table with nothing shadowed has no such column" 'Shadowed by' \
env KLAMMERTEXT_FONTS="$FONTS/none" "$KDESC" --typeface
echo
echo "-- provenance: -i shows what the INPUT defines --"
# A klammerset is loaded so the input can be ANALYSED against it, but it is
# not what the user asked about. A designer wants the klammers this file
# defines; a reader of an unfamiliar document wants the custom ones it
# carries. So "-i" restricts every listing to definitions from outside the
# loaded klammersets -- while the klammersets stay loaded.
PROV=$(mktemp "${TMPDIR:-/tmp}"/kdesc_prov.XXXXXX.k)
printf '@@shout.k s : Say it loudly @@\n@@shout.html :: <b>*s*</b> @@\n' > "$PROV"
shows "32. -i lists the input's own klammer" '@shout' "$KDESC" -i "$PROV" -k
absent "33. ... and not the klammerset's" '@table' "$KDESC" -i "$PROV" -k
shows "34. without -i the klammerset is shown" '@table' "$KDESC" -k
# The klammerset is still LOADED: a coverage derived from a klammer it
# supplies proves the analysis saw it even though the report does not.
DERIVE=$(mktemp "${TMPDIR:-/tmp}"/kdesc_derive.XXXXXX.k)
printf '@@wave.k s : Wave @@\n@@wave :: @i *s* @ @@\n' > "$DERIVE"
shows "35. coverage is derived through the unlisted klammerset" \
'@wave +ansi html pdf tex txt +from @i' "$KDESC" -i "$DERIVE" --coverage
absent "36. ... and the klammerset is not reported" '@table' "$KDESC" -i "$DERIVE" --coverage
# A klammer the input REDEFINES came from the klammerset, but the input
# changed it -- which is exactly what a reader needs to know.
REDEF=$(mktemp "${TMPDIR:-/tmp}"/kdesc_redef.XXXXXX.k)
printf '@@i.html ::: <em class="mine">*text*</em> @@\n' > "$REDEF"
shows "37. a redefined klammer is the input's too" '@i' "$KDESC" -i "$REDEF" -k
# An input that declares its own klammerset is still the input.
DECL=$(mktemp -d "${TMPDIR:-/tmp}"/kdesc_decl.XXXXXX)
mkdir -p "$DECL/own"
printf '@@@klammerset own | A designer set @@@\n@@yell.k s : Yell @@\n@@yell.html :: <b>*s*</b> @@\n' \
> "$DECL/own/own.k"
shows "38. an input declaring a klammerset is not filtered out" \
'@yell' "$KDESC" -i "$DECL/own/own.k" -k
rm -rf "$PROV" "$DERIVE" "$REDEF" "$DECL"
echo
echo "-- the usage text --"
shows "28. -k shows its optional argument" '\-k \[<text>\]' "$KDESC"
shows "29. -i shows its filename" '\-i <filename>' "$KDESC"
# Ordered by likely use: the single letters come before the long names.
# Line numbers, not a multi-line pattern -- grep is line-oriented.
usage=$(limited "$KDESC" 2>&1 | plain)
k_line=$(printf '%s\n' "$usage" | grep -n -- '-k \[<text>\]' | head -1 | cut -d: -f1)
katoms_line=$(printf '%s\n' "$usage" | grep -n -- '--katoms' | head -1 | cut -d: -f1)
if [ -n "$k_line" ] && [ -n "$katoms_line" ] && [ "$k_line" -lt "$katoms_line" ]; then
echo "${green}PASS${reset} 30. -k is listed before --katoms"; PASS=$((PASS+1))
else
echo "${red}FAIL${reset} 30. usage order: -k at line $k_line, --katoms at $katoms_line"
FAIL=$((FAIL+1))
fi
echo
echo "-- a no-result search is not an error --"
limited "$KDESC" -k zzqq >/dev/null 2>&1
if [ $? -eq 0 ]; then
echo "${green}PASS${reset} 31. finding nothing exits 0"; PASS=$((PASS+1))
else
echo "${red}FAIL${reset} 31. finding nothing exits nonzero"; FAIL=$((FAIL+1))
fi
echo
echo "====================="
echo "Results: ${PASS} passed, ${FAIL} failed"
[ "$FAIL" -eq 0 ] || exit 1
exit 0