Files
klammertext/tst/target_test.sh
Andy Kopra 37b6ba1c4f Verbatim-safe typography, ^-punctuation quoting, full-range ^UUUU^, polyglot html
Typographic transforms (---, quote pairs, ~) no longer touch verbatim
text: @c/@code/@source_listing content and ^'...'^ spans show exactly the
characters written. "^" before any punctuation character quotes it in
every target (the apostrophe excepted: ^' opens a literal span), with the
new :resolve option on @@@target declaring per-target renderings. The
^UUUU^ code-point form accepts 4-6 hex digits, the full Unicode range.
The html output and transform spellings are polyglot (XML-valid), in
preparation for an EPUB target. New suites: transform_test, character_test
(engine), typography_test (SKS).

(from dev 07ce5ea86a0a)
2026-08-23 20:48:26 +02:00

134 lines
5.1 KiB
Bash
Executable File

#!/bin/bash
#
# target_test.sh — The @@@target system command's :after_apply phase list.
#
# Added 2026-08-22, when the txt target became the first to declare TWO
# phases and found the list separator undocumented and unguarded: two bare
# specs written without " ; " were glued into one Python call and failed at
# render time with a SyntaxError located at "phase, line 1" rather than at
# the declaration. What is pinned here:
#
# * several phases separated by " ; " run, in order, each seeing its
# predecessor's result;
# * a bare spec containing whitespace is a DEFINITION-TIME error naming
# the " ; " convention (the guard in Target::add_after_apply);
# * a mode-tagged spec (":cpp <library> <function>") is exempt from the
# whitespace guard -- its library is a filename, and filenames may
# contain spaces, which is why the separator is ";" at all.
#
# SKS-independent: --klammersets none, an inline fixture target, and a
# fixture Python module placed next to the input file (the module-resolution
# rule adds the input file's directory to sys.path).
#
# NOT to be confused with "target_test", the C++ diagnostic program built
# from target_test.cpp in this directory (asserts nothing; see the `smoke`
# target in tst/Makefile). The ".sh" is the only distinguisher -- the third
# such collision, after eval_test and state_test.
#
# Usage: ./target_test.sh (needs ktext on PATH)
# Exit code: 0 if all tests pass, 1 otherwise.
PASS=0
FAIL=0
KTEXT=ktext
red=$'\033[31m'
green=$'\033[32m'
bold=$'\033[1m'
reset=$'\033[0m'
DIR=$(mktemp -d)
trap 'rm -rf "$DIR"' EXIT
# Two phases that mark their passage; order is observable in the output.
cat > "$DIR/phasefix.py" <<'EOF'
def one(text, K=None):
return text + "+ONE"
def two(text, K=None):
return text + "+TWO"
def width(text, K=None):
# K is the state's "class K"; a fixture state variable proves the phase
# receives it (attributes arrive as strings unless the argtype is typed).
return text + "|" + str(K.Fixture_width)
EOF
check_eq() { # check_eq NAME EXPECTED INPUT_FILE_TEXT TARGET_DECL
local name="$1" expected="$2" body="$3" decl="$4"
printf '%s' "$body" > "$DIR/in.kt"
local out status
out=$("$KTEXT" "$DIR/in.kt" --klammersets none -s "$decl" -t t -d 2>/dev/null)
status=$?
out=$(printf '%s' "$out" | tr -d '\n')
if [ $status -eq 0 ] && [ "$out" = "$expected" ]; then
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
else
echo "${red}FAIL${reset} $name"
echo " expected: [$expected]"; echo " got: [$out] (exit $status)"
FAIL=$((FAIL+1))
fi
}
check_def_error() { # check_def_error NAME PATTERN TARGET_DECL
local name="$1" pattern="$2" decl="$3"
local out status
out=$("$KTEXT" --klammersets none -s "$decl hello" -t t -d 2>&1)
status=$?
if [ $status -eq 0 ]; then
echo "${red}FAIL${reset} $name — expected a definition error but ktext succeeded"
FAIL=$((FAIL+1)); return
fi
if printf '%s' "$out" | tr '\n' ' ' | tr -s ' ' | grep -qF "$pattern"; then
echo "${green}PASS${reset} $name"; PASS=$((PASS+1))
else
echo "${red}FAIL${reset} $name — expected error to contain [$pattern]"
echo " got: $(printf '%s' "$out" | sed 's/\x1b\[[0-9;]*m//g' | tr '\n' ' ' | head -c 200)"
FAIL=$((FAIL+1))
fi
}
echo "${bold}@@@target :after_apply tests${reset}"
echo "============================"
echo
echo "-- several phases: ';'-separated, run in declared order --"
check_eq " 1. one phase runs" \
"text+ONE" "text" \
'@@@target t | test | :after_apply phasefix.one @@@'
check_eq " 2. two phases run in order (ONE then TWO)" \
"text+ONE+TWO" "text" \
'@@@target t | test | :after_apply phasefix.one ; phasefix.two @@@'
check_eq " 3. ... and reversing the list reverses the order" \
"text+TWO+ONE" "text" \
'@@@target t | test | :after_apply phasefix.two ; phasefix.one @@@'
echo
echo "-- a phase receives the state as K --"
check_eq " 4. K.<state variable> is readable inside a phase" \
"text|42" "text" \
'@@@state Fixture_width :value 42 @@@ @@@target t | test | :after_apply phasefix.width @@@'
echo
echo "-- the missing separator is a DEFINITION-time error --"
check_def_error " 5. two bare specs without ';' are rejected at the declaration" \
"separated by \" ; \"" \
'@@@target t | test | :after_apply phasefix.one phasefix.two @@@'
check_def_error " 6. ... and the error names the offending spec" \
"phasefix.one phasefix.two" \
'@@@target t | test | :after_apply phasefix.one phasefix.two @@@'
echo
echo "-- a mode-tagged spec may contain spaces (its library is a filename) --"
# Declaration-time only: the :cpp library is never dlopened unless the
# target renders, so declaring it under another name proves the exemption
# without needing a real library.
check_eq " 7. a ':cpp lib func' spec passes the declaration guard" \
"text+ONE" "text" \
'@@@target u | unused | :after_apply ^:cpp some lib func @@@ @@@target t | test | :after_apply phasefix.one @@@'
echo
echo "============================"
echo "Results: ${green}$PASS passed${reset}, ${red}$FAIL failed${reset}"
[ $FAIL -eq 0 ]