Target coverage: a klammer states the targets it serves

kdesc gains --coverage, which reports for every klammer the set of targets it
can render to, and — the point of it — which klammers' coverage cannot be
derived and must therefore be declared.  Three rules: coverage is DERIVED
where the definitions determine it (a general body of klammer calls covers
the intersection of what those klammers cover, by a greatest fixpoint after
loading), DECLARED where the engine cannot interpret what decides it (an
@eval body, whose targets are undecidable), and UNKNOWN where nothing is
written — which never means "deliberately unavailable".

Two new spellings in a definition's name.  A comma-separated target list,
"@@table.html,tex :: ...", gives one body several targets; it is surface
syntax, expanded at registration, and each member goes through the
redefinition rules on its own.  And "@@date.* :: ..." writes the general
target out, asserting that the klammer works for EVERY target including ones
not yet defined — a stronger claim than a list of the targets defined today,
and the one target declaration that could be mechanically falsified.

The Standard Klammer Set was swept accordingly: it now has no general
definitions at all, every klammer names the targets it serves, six use ".*",
and tex and pdf are at zero undecided.

kdesc's flags are reorganised on two rules: a flag reached for often gets a
single letter (-k klammers, -t targets, -c characters, -i input), a more
specialised topic a multi-letter name (--argtypes, --katoms, --rewrite,
--optionsets, --coverage, --klammerset, --font); and -v says how much to show
about PROCESSING, never what the RESULT contains — so the katom regex column
is "--katoms full" and the coverage detail "--coverage all".  NOTE: "-k" now
lists klammers (optionally filtered by a name/description search); the katom
table moved to "--katoms".

Fixes carried along: an option written with no value crashed the command with
SIGSEGV instead of reporting the mistake; two required positional arguments
never parsed; kdesc and kdiag printed an error and exited 0; and definition
diagnostics counted registrations rather than what was written, so one line
could be reported as two definitions and then printed twice.

Four new test suites: target_list, coverage, command_option, kdesc.

(from dev 46f54080bd9a)
This commit is contained in:
2026-08-12 17:20:23 +02:00
parent 61987c8b1f
commit 59c1599bc9
49 changed files with 1960 additions and 431 deletions

158
tst/kdesc_test.sh Executable file
View File

@@ -0,0 +1,158 @@
#!/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, --klammerset, --font);
# * -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'; }
# shows NAME PATTERN CMD... — exits 0 and the output matches PATTERN.
shows() {
local name="$1" pattern="$2"; shift 2
local out status
out=$(timeout 60 "$@" 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 timeout 60 "$@" 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
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
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" -i "$DIR/cycle.k" --coverage all
absent "12b. ... hidden without it" \
'All targets, declared \(0\)' "$KDESC" -i "$DIR/cycle.k" --coverage
absent "12c. an empty problem category stays hidden" \
'Declared but never defined \(0\)' "$KDESC" -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" -t -v 1
absent "15. ... and is silent without it" 'input_filenames' "$KDESC" -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
shows "26. an unknown coverage word" 'Unrecognized coverage command' "$KDESC" --coverage nonsense
shows "27. an unknown katom word" 'Unrecognized katom command' "$KDESC" --katoms nonsense
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=$(timeout 60 "$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 --"
timeout 60 "$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