Assembled from klammertext-dev f344a37d772c. The commands, the library,
the Standard Klammer Set, the reflection klammerset and the Python
binding build and run natively on Windows (MSYS2 UCRT64 gcc, python.org
Python), and both test tiers pass there. doc/install/windows_source_install.md
is the fifth install guide: a source install parallel to the Linux and
macOS ones, verified by following it on a pristine Windows 11.
Behaviour on every system: paths Klammertext prints use forward slashes;
output is LF; the command line is UTF-8 on Windows; @eval :shell is a
POSIX shell command everywhere -- on Windows it runs the sh Klammertext
ships, BusyBox for Windows (win/busybox/, GPLv2, with its licence and the
source pointer). KLAMMERTEXT_FONTS and KLAMMERTEXT_KLAMMERSETS are lists in
the system's PATH convention (';' on Windows). On Linux and macOS nothing
observable changes.
263 lines
14 KiB
Bash
Executable File
263 lines
14 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
|
|
shows "26. an unknown coverage word" 'Unrecognized coverage command' "$KDESC" --coverage nonsense
|
|
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
|