#!/bin/bash # # rest_test.sh — Regression tests for the bar structure of a rest argument. # # A rest argument collects everything after the declared positional # arguments, bars included, and hands the structure on; a klammer reads the # bar count as dimension (| elements, || rows). The one thing the ENGINE # decides is where the structure begins and ends: # # * exactly ONE written leading bar is stripped — the bar that separates # the rest from what precedes it: the last positional argument, the # options, or the klammer name in the opening-bar idiom (`@table |` with # the rows on the lines below). Every further leading bar delimits an # EMPTY FIRST ELEMENT the writer meant: `@table | | M | F ||` has an # empty corner cell. Decided 2026-10-01; before it every leading bar # was trimmed, so an empty first element could not be written at all, # and a Markdown table's empty header corner shifted the header row one # column left. # * trailing bars and whitespace are trimmed (the customary dangling # separator before a closing @), as before. # * an empty element between two bars is kept wherever it stands. # # The rest(N) cast (kutil.rest_split) is SKS Python and is tested at that # tier; here the value is read back raw through *name* substitution. # # Engine tier: --klammersets none, fixtures inline. # # Usage: ./rest_test.sh (LSan suppressions come from env/runtime.env) # Exit code: 0 if all tests pass, 1 otherwise. PASS=0 FAIL=0 KTEXT=ktext K=${KLAMMERTEXT_HOME:?KLAMMERTEXT_HOME must be set} red=$'\033[31m' green=$'\033[32m' reset=$'\033[0m' trim() { awk '{ sub(/[ \t\r]+$/, "") } { line[NR]=$0 } END { f=1; while (f<=NR && line[f]=="") f++; l=NR; while (l>=1 && line[l]=="") l--; for (i=f;i<=l;i++) print line[i] }'; } # check_eq TEST_NAME EXPECTED INPUT # The fixtures: @zz takes only a rest, @yy one positional then a rest. # Each prints its arguments in brackets, so the bar structure is visible. check_eq() { local test_name="$1" expected="$2" input="$3" local output status output=$("$KTEXT" --klammersets none -d -s "@@zz items.rest :x 0 : [*items*] @@ @@yy first | second.rest : [*first*][*second*] @@ $input" 2>/tmp/rest_test_err.$$) status=$? output=$(printf '%s' "$output" | trim) if [ $status -ne 0 ]; then echo "${red}FAIL${reset} $test_name — ktext exited $status" echo " stderr: $(head -3 /tmp/rest_test_err.$$)" FAIL=$((FAIL + 1)) return fi if [ "$output" = "$expected" ]; then echo "${green}PASS${reset} $test_name" PASS=$((PASS + 1)) else echo "${red}FAIL${reset} $test_name" echo " expected: [$expected]" echo " got: [$output]" FAIL=$((FAIL + 1)) fi } echo "-- the leading bar --" check_eq " 1. no leading bar: the elements as written" '[M | F]' '@zz M | F @' check_eq " 2. the opening-bar idiom: one bar stripped" '[M | F]' '@zz | M | F @' check_eq " 3. a second leading bar is an empty first element" '[| M | F]' '@zz | | M | F @' check_eq " 4. the idiom with the rows below, empty corner cell" '[| M | F || Nom | der]' '@zz | | M | F || Nom | der @' check_eq " 5. options first, then the idiom bar" '[M | F]' '@zz :x 1 | M | F @' check_eq " 6. options first, then an empty first element" '[| M | F]' '@zz :x 1 | | M | F @' echo echo "-- after a positional argument --" check_eq " 7. the bar after the positional is the separator" '[a][b | c]' '@yy a | b | c @' check_eq " 8. a second bar there is an empty first element" '[a][| b | c]' '@yy a | | b | c @' echo echo "-- the middle and the end --" check_eq " 9. an empty element between bars is kept" '[M | | F]' '@zz M | | F @' check_eq "10. one trailing bar is trimmed" '[M | F]' '@zz M | F | @' check_eq "11. a row separator is never the stripped bar" '[|| M | F]' '@zz || M | F @' echo echo "================================" echo "Passed: $PASS Failed: $FAIL" rm -f /tmp/rest_test_err.$$ [ $FAIL -eq 0 ]