2026-08-12 17:20:23 +02:00
|
|
|
#!/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,
|
2026-09-18 20:02:44 +02:00
|
|
|
# --coverage, --klammersets, --typeface);
|
2026-08-12 17:20:23 +02:00
|
|
|
# * -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'; }
|
|
|
|
|
|
2026-08-16 01:37:59 +02:00
|
|
|
# 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
|
|
|
|
|
|
2026-08-12 17:20:23 +02:00
|
|
|
# shows NAME PATTERN CMD... — exits 0 and the output matches PATTERN.
|
|
|
|
|
shows() {
|
|
|
|
|
local name="$1" pattern="$2"; shift 2
|
|
|
|
|
local out status
|
2026-08-16 01:37:59 +02:00
|
|
|
out=$(limited "$@" 2>&1 | plain); status=$?
|
2026-08-12 17:20:23 +02:00
|
|
|
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
|
2026-08-16 01:37:59 +02:00
|
|
|
if limited "$@" 2>&1 | plain | grep -Eq -- "$pattern"; then
|
2026-08-12 17:20:23 +02:00
|
|
|
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
|
2026-08-25 23:30:55 +02:00
|
|
|
# -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).
|
|
|
|
|
shows " 4b. ... and shows the characters" $'\xf0\x93\x8a\xaa' "$KDESC" -u
|
2026-08-12 17:20:23 +02:00
|
|
|
|
|
|
|
|
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
|
2026-09-21 22:37:13 +02:00
|
|
|
# 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
|
2026-08-12 17:20:23 +02:00
|
|
|
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" \
|
2026-08-16 01:37:59 +02:00
|
|
|
'All targets, declared \(0\)' "$KDESC" --klammersets none -i "$DIR/cycle.k" --coverage all
|
2026-08-12 17:20:23 +02:00
|
|
|
absent "12b. ... hidden without it" \
|
2026-08-16 01:37:59 +02:00
|
|
|
'All targets, declared \(0\)' "$KDESC" --klammersets none -i "$DIR/cycle.k" --coverage
|
2026-08-12 17:20:23 +02:00
|
|
|
absent "12c. an empty problem category stays hidden" \
|
2026-08-16 01:37:59 +02:00
|
|
|
'Declared but never defined \(0\)' "$KDESC" --klammersets none -i "$DIR/cycle.k" --coverage all
|
2026-08-12 17:20:23 +02:00
|
|
|
absent "13. -v adds no result detail" 'Regex' "$KDESC" --katoms -v 3
|
|
|
|
|
|
|
|
|
|
echo
|
|
|
|
|
echo "-- -v is about processing only --"
|
2026-08-16 01:37:59 +02:00
|
|
|
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
|
2026-08-12 17:20:23 +02:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
shows "26. an unknown coverage word" 'Unrecognized coverage command' "$KDESC" --coverage nonsense
|
|
|
|
|
shows "27. an unknown katom word" 'Unrecognized katom command' "$KDESC" --katoms nonsense
|
2026-09-18 20:02:44 +02:00
|
|
|
# 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
|
2026-08-12 17:20:23 +02:00
|
|
|
|
2026-08-16 01:37:59 +02:00
|
|
|
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 /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 /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" \
|
2026-09-12 18:11:48 +02:00
|
|
|
'@wave +ansi html pdf tex txt +from @i' "$KDESC" -i "$DERIVE" --coverage
|
2026-08-16 01:37:59 +02:00
|
|
|
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 /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 /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"
|
|
|
|
|
|
2026-08-12 17:20:23 +02:00
|
|
|
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.
|
2026-08-16 01:37:59 +02:00
|
|
|
usage=$(limited "$KDESC" 2>&1 | plain)
|
2026-08-12 17:20:23 +02:00
|
|
|
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 --"
|
2026-08-16 01:37:59 +02:00
|
|
|
limited "$KDESC" -k zzqq >/dev/null 2>&1
|
2026-08-12 17:20:23 +02:00
|
|
|
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
|