#!/bin/bash # # command_option_test.sh — Command-line option parsing (mac/argv.cpp). # # The commands' own argument handling, which no suite covered: argv_test.cpp # exercises Argv but asserts nothing, so two defects lived there unnoticed. # # * An option declared with opt() takes a value. Written LAST with nothing # after it -- "kdesc -v", "ktext doc.kt -t" -- parse_optional() read one # past the end of the word vector and the command died with SIGSEGV, # naming nothing. Two bounds checks sat commented out at that spot; they # would have returned a half-parsed option instead of reporting the # mistake. It is now a located argument error. # * kdesc and kdiag printed an error and then exited 0, so a script could # not tell a failed run from a successful one. Both return 1 now, as # ktext already did. # # Also here: two required positional arguments. parse_positional() consumes # one per required name, but regex_split_prefix() requires its match at # position 0 and returned the remainder with the separating space intact, so # the second was always "not found". Latent, because no shipped command # declares two -- tst/argv_test.cpp is the only program that does. # # Usage: ./command_option_test.sh (needs KLAMMERTEXT_HOME set; commands on PATH) # Exit code: 0 if all tests pass, 1 otherwise. PASS=0 FAIL=0 K=${KLAMMERTEXT_HOME:?KLAMMERTEXT_HOME must be set} TSTDIR="$(cd "$(dirname "$0")" && pwd)" red=$'\033[31m' green=$'\033[32m' bold=$'\033[1m' reset=$'\033[0m' # A signal death is not an error exit: 128+n, and the point of these tests is # the difference. Report it distinctly so a crash can never read as a pass. died_by_signal() { [ "$1" -gt 128 ]; } # error NAME PATTERN CMD... — nonzero exit, no signal, PATTERN in the output. error() { local name="$1" pattern="$2"; shift 2 local out status out=$("$@" 2>&1); status=$? if died_by_signal $status; then echo "${red}FAIL${reset} $name — died by signal $((status-128))"; FAIL=$((FAIL+1)); return fi if [ $status -eq 0 ]; then echo "${red}FAIL${reset} $name — reported an error but exited 0"; FAIL=$((FAIL+1)); return fi if printf '%s' "$out" | grep -qF -- "$pattern"; then echo "${green}PASS${reset} $name"; PASS=$((PASS+1)) else echo "${red}FAIL${reset} $name — expected [$pattern]" echo " got: $(printf '%s' "$out" | head -2)"; FAIL=$((FAIL+1)) fi } # ok NAME CMD... — exits 0 and does not die by signal. ok() { local name="$1"; shift local status "$@" >/dev/null 2>&1; status=$? if died_by_signal $status; then echo "${red}FAIL${reset} $name — died by signal $((status-128))"; FAIL=$((FAIL+1)) elif [ $status -ne 0 ]; then echo "${red}FAIL${reset} $name — exit $status"; FAIL=$((FAIL+1)) else echo "${green}PASS${reset} $name"; PASS=$((PASS+1)) fi } echo "${bold}Command option tests${reset}" echo "====================" echo echo "-- an option written last, with no value --" error " 1. kdesc -v" "needs a value" kdesc -v error " 2. kdesc with a flag first" "needs a value" kdesc --katoms -v error " 3. kdiag -v" "needs a value" kdiag -v error " 4. ktext -t" "needs a value" ktext -s '@i-x' -t error " 5. the message names the option" "-v " kdesc -v error " 6. ... and how to get the usage" "for the list of arguments" kdesc -v echo echo "-- the same options WITH a value still work --" # NOT "kdesc -v 1": show_usage() treats exactly " -v " as a # request for the usage text, which exits 1 by design. Give it another flag. ok " 7. kdesc -c -v 1" kdesc -c -v 1 ok " 8. kdesc --katoms -v 1" kdesc --katoms -v 1 ok " 9. kdiag -v 1 '@i-x'" kdiag -v 1 '@i-x' ok "10. ktext -t html" ktext -s '@i-x' -t html -d echo echo "-- an error exit is nonzero, not a printed message and exit 0 --" error "11. kdesc reports failure" "needs a value" kdesc -v error "12. kdiag reports failure" "needs a value" kdiag -v echo echo "-- two required positional arguments --" # argv_test is the only program declaring two; it prints what it parsed. if [ -x "$TSTDIR/argv_test" ]; then out=$("$TSTDIR/argv_test" first second --bool 2>&1 | sed 's/\x1b\[[0-9;]*m//g') for expect in " : first" " : second"; do if printf '%s' "$out" | grep -qF "$expect"; then echo "${green}PASS${reset} 13/14. positional [$expect]"; PASS=$((PASS+1)) else echo "${red}FAIL${reset} 13/14. positional [$expect] not parsed"; FAIL=$((FAIL+1)) fi done else echo "SKIP 13/14. two positionals (argv_test not built; run make -C tst)" fi echo echo "====================" echo "Results: ${PASS} passed, ${FAIL} failed" [ "$FAIL" -eq 0 ] || exit 1 exit 0