#!/bin/bash # # deftype_test.sh — Regression tests for the four klammer definition modes # and the 9-combination redefinition transition table. # # The four syntactic definition modes (increasing colon count = decreasing # binding strength, the "conservation of syntax" ladder): # # : create, defines its own parameters — error if already defined # :: create, inherits parameters from `.k` — error if already defined # ::: override, inherits parameters from `.k` — replaces existing (warns) # :::: default, defines its own parameters — silently replaced by create # # At the transition level (mac/deftype.cpp) `:` and `::` are both `def_create`; # the three redefinition modes are create / override (`:::`) / default (`::::`). # `defmode_transition(existing, incoming)` governs all 9 (existing x incoming) # combinations. This test pins down every entry of that table plus the basic # behavior of each mode, none of which tst/klammer_test.cpp currently covers. # # {replace,warn,message} semantics (mac/klammer_registry.cpp): # !replace && message -> Definition_error (nonzero exit) # !replace && empty -> silently keep the existing definition # replace && warn -> emit a warning, then replace # replace && !warn -> replace silently # # Usage: ./deftype_test.sh (needs KLAMMERTEXT_HOME set; ktext on PATH) # Exit code: 0 if all tests pass, 1 otherwise. PASS=0 FAIL=0 KTEXT=ktext K=${KLAMMERTEXT_HOME:?KLAMMERTEXT_HOME must be set} ERR=/tmp/deftype_test_err.$$ red=$'\033[31m' green=$'\033[32m' bold=$'\033[1m' reset=$'\033[0m' # strip leading/trailing blank lines and trailing whitespace 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 NAME EXPECTED KTEXT_ARGS... — exit 0, stdout==EXPECTED, and NO warning. check_eq() { local name="$1" expected="$2"; shift 2 local out status err out=$("$KTEXT" "$@" 2>"$ERR"); status=$? err=$(cat "$ERR") out=$(printf '%s' "$out" | trim) if [ $status -ne 0 ]; then echo "${red}FAIL${reset} $name — ktext exited $status" echo " stderr: $(echo "$err" | head -2)"; FAIL=$((FAIL+1)); return fi if printf '%s' "$err" | grep -qiF "warning"; then echo "${red}FAIL${reset} $name — unexpected warning"; FAIL=$((FAIL+1)); return fi if [ "$out" = "$expected" ]; then echo "${green}PASS${reset} $name"; PASS=$((PASS+1)) else echo "${red}FAIL${reset} $name" echo " expected: [$expected]"; echo " got: [$out]"; FAIL=$((FAIL+1)) fi } # check_warn NAME EXPECTED KTEXT_ARGS... — exit 0, stdout==EXPECTED, AND a warning on stderr. check_warn() { local name="$1" expected="$2"; shift 2 local out status err out=$("$KTEXT" "$@" 2>"$ERR"); status=$? err=$(cat "$ERR") out=$(printf '%s' "$out" | trim) if [ $status -ne 0 ]; then echo "${red}FAIL${reset} $name — ktext exited $status"; FAIL=$((FAIL+1)); return fi if ! printf '%s' "$err" | grep -qiF "warning"; then echo "${red}FAIL${reset} $name — expected a warning, got none"; FAIL=$((FAIL+1)); return fi if [ "$out" = "$expected" ]; then echo "${green}PASS${reset} $name"; PASS=$((PASS+1)) else echo "${red}FAIL${reset} $name" echo " expected: [$expected]"; echo " got: [$out]"; FAIL=$((FAIL+1)) fi } # check_error NAME PATTERN KTEXT_ARGS... — nonzero exit and PATTERN in the message. check_error() { local name="$1" pattern="$2"; shift 2 local out status out=$("$KTEXT" "$@" 2>&1); status=$? if [ $status -eq 0 ]; then echo "${red}FAIL${reset} $name — expected an error but ktext succeeded"; FAIL=$((FAIL+1)); return fi if echo "$out" | grep -qF "$pattern"; then echo "${green}PASS${reset} $name"; PASS=$((PASS+1)) else echo "${red}FAIL${reset} $name — expected error to contain [$pattern]" echo " output: $(echo "$out" | head -2)"; FAIL=$((FAIL+1)) fi } echo "${bold}Klammer definition-mode tests${reset}" echo "=============================" echo # --- Each mode defines and applies in isolation --- echo "-- isolated modes --" check_eq " 1. : create defines + applies" "cq" -s '@@x s : c*s* @@ @x q @' -d check_eq " 2. :: instance inherits from .k" "iq" -s '@@x.k s : d @@ @@x :: i*s* @@ @x q @' -d check_error " 3. :: without a .k declaration is an error" "no declarations" -s '@@x :: i*s* @@ @x q @' -d check_eq " 4. :::: default defines + applies" "eq" -s '@@x s :::: e*s* @@ @x q @' -d check_eq " 5. ::: override alone becomes the def" "oq" -s '@@x.k s : d @@ @@x ::: o*s* @@ @x q @' -d # --- The 9-entry transition table: (existing x incoming) --- echo echo "-- transition table (existing + incoming) --" check_error " 6. create + create = error" "already defined" -s '@@x s : one*s* @@ @@x s : two*s* @@ @x q @' -d check_warn " 7. create + override = warn+repl" "twoq" -s '@@x.k s : d @@ @@x :: one*s* @@ @@x ::: two*s* @@ @x q @' -d check_eq " 8. create + default = keep exist" "oneq" -s '@@x s : one*s* @@ @@x s :::: two*s* @@ @x q @' -d check_error " 9. override + create = error" "already overridden" -s '@@x.k s : d @@ @@x ::: one*s* @@ @@x :: two*s* @@ @x q @' -d check_warn "10. override + override = warn+repl" "twoq" -s '@@x.k s : d @@ @@x ::: one*s* @@ @@x ::: two*s* @@ @x q @' -d check_eq "11. override + default = keep exist" "oneq" -s '@@x.k s : d @@ @@x ::: one*s* @@ @@x :::: two*s* @@ @x q @' -d check_eq "12. default + create = replace (silent)" "twoq" -s '@@x s :::: one*s* @@ @@x s : two*s* @@ @x q @' -d check_warn "13. default + override = warn+repl" "twoq" -s '@@x.k s : d @@ @@x :::: one*s* @@ @@x ::: two*s* @@ @x q @' -d check_error "14. default + default = error" "already defined" -s '@@x s :::: one*s* @@ @@x s :::: two*s* @@ @x q @' -d rm -f "$ERR" echo echo "=============================" echo "Results: ${green}$PASS passed${reset}, ${red}$FAIL failed${reset}" [ $FAIL -eq 0 ]