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.
206 lines
9.8 KiB
Bash
Executable File
206 lines
9.8 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, --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'; }
|
|
|
|
# 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
|
|
|
|
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" --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
|
|
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 "-- 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" \
|
|
'@wave +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 /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"
|
|
|
|
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
|