#!/bin/sh AWK="mawk" colordiff=$(command -v colordiff 2>&-) help_shtest() { cat <> "${errfile}" } nl="$(printf '\n ')"; nl="${nl% }" test_parser() { # parse_state: # empty - not in any state # command/auxcommand # 1 multiline state_multiline=1 # exitcode # 2 output state_output=2 # 3 ready to execute state_ready=3 case "${line}" in "${auxcommand_prefix}"*) command="${line#${auxcommand_prefix}}" cmdlineno="${lineno}" parse_state="${state_ready}" auxcommand=1 dump_line ;; "${command_prefix}"*) if [ -z "${parse_state}" ]; then command="${line#${command_prefix}}" cmdlineno="${lineno}" parse_state="${state_multiline}" test_count=$(expr ${test_count} + 1) dump_line else buffer="${line}" parse_state="${state_ready}" fi ;; "${multiline_prefix}"*) case "${parse_state}" in "${state_multiline}") command="${command:+${command}${nl}}${line#${multiline_prefix}}" ;; '') next ;; "${state_output}") output="${output:+${output}${nl}}${line#${shtest_indent}}" ;; esac dump_line ;; "${exitcode_prefix}"*) case "${parse_state}" in "${state_multiline}") exitcode="${line#${exitcode_prefix}}" parse_state="${state_output}" ;; '') next ;; *) parse_state="${state_output}" output="${output:+${output}${nl}}${line#${shtest_indent}}" ;; esac [ -z "${shtest_clear}" ] && dump_line ;; "${shtest_indent}"*) case "${parse_state}" in "${state_output}") output="${output:+${output}${nl}}${line#${shtest_indent}}" parse_state="${state_output}" ;; '') dump_line; return ;; *) parse_state="${state_output}" output="${output:+${output}${nl}}${line#${shtest_indent}}" ;; esac # this isn't dumped by default ;; '') if [ -n "${parse_state}" ]; then parse_state="${state_ready}" buffer=''; fullbuffer=1 else dump_line fi ;; *) if [ -n "${parse_state}" ]; then parse_state="${state_ready}" buffer="${line}" else dump_line fi ;; esac } report() { [ -n "${shtest_quite}" ] || printf >&2 "%s\n" "$*" } process_result() { local e if [ -n "${shtest_refill}" ]; then sed "s/^/${shtest_indent}/" "${result}" >> "${errfile}" return fi e=$(printf "%s\n" "${output}" | sed 's/\\/\\\\/g') ${AWK} -v output="$e" -- ' BEGIN { split(output,o,"\n")} { if (o[NR] ~ / \(re\)$/) { sub(" \(re\)$","",o[NR]); if ($0 !~ "^" o[NR] "$" ) exit NR next } else if ($0 != o[NR]) exit NR } END { t=NR+1; if (o[t]!="") exit t} ' "${result}" 2>/dev/null e=$? if [ "$e" -ne 0 ]; then diff_result=$( printf "%s\n" "${output}" | diff -u "${result}" - ) printf "%s\n" "${diff_result}" >> "${errfile}" [ -n "${shtest_quite}" ] && return 1 printf "Test failed: %s\n" "${command}" printf "Test failed at %s line:\n" "${cmdlineno}" printf "%s\n" "${diff_result}" | \ if [ -n "${shtest_colordiff}" -a -n "${colordiff}" ]; then colordiff; else cat; fi return 1 else sed "s/^/${shtest_indent}/" "${result}" >> "${errfile}" fi } runcommand() { if [ -n "${shtest_discard}" ]; then printf '%s %s' "${custom_env}" "${command}" | eval "${shtest_shell}" 2>/dev/null cmdexitcode="$?" else printf '%s %s' "${custom_env}" "${command}" | eval "${shtest_shell}" 2>&1 cmdexitcode="$?" fi } test_result() { if [ "${parse_state}" = "${state_ready}" -a -n "${command}" ]; then if [ -z "${shtest_clear}" ]; then [ -z "${auxcommand}" -a -n "${shtest_showexec}" ] && \ report "Executing ${command}" if [ -n "${auxcommand}" ]; then runcommand else runcommand > "${result}" fi if [ -z "${auxcommand}" ]; then process_result || { test_failed=$(expr ${test_failed} + 1 ) [ -z "${shtest_force}" -a -z "${shtest_refill}" ] && exit ${test_count} } if [ -z "${shtest_refill}" ]; then case "${cmdexitcode}" in 80) exit 0 ;; 83) exit 83 ;; "${exitcode}") : ;; *) test_unhandledexit=$( expr ${test_unhandledexit} + 1) report "Unhandled exit code ${cmdexitcode} " \ "(expected ${exitcode}) at line ${cmdlineno}" [ -z "${shtest_force}" -a -n "${shtest_brick}" ] && exit 81 ;; esac fi fi fi command=''; output=''; exitcode='0'; auxcommand=''; parse_state='' fi } # args: # 1 -- file with tests run_test() { local IFS command buffer fullbuffer output exitcode auxcommand line errfile lineno cmdlineno test_count test_failed parse_state lineno=0; cmdlineno=0; test_count=0; test_failed=0; test_unhandledexit=0 errfile="${1%.t}.err"; : > "${errfile}" command=''; buffer=''; fullbuffer=''; output=''; exitcode='0'; auxcommand=''; parse_state='' report "Starting tests from $1" result=$(mktemp || { report "Can't create temp file!"; exit 82 ; } ) trap "rm ${result} 2>/dev/null" INT TERM EXIT backifs="${IFS}" IFS="" while :; do if [ -n "${buffer}" -o -n "${fullbuffer}" ]; then line="${buffer}"; buffer=''; fullbuffer='' else lineno=$(expr ${lineno} + 1) read -r line || break fi test_parser test_result done < "$1" parse_state="${state_ready}" test_result rm "${result}" 2>/dev/null report "Finished ${test_count} test from $1, ${test_failed} failed, " \ "${test_unhandledexit} unhandled exit codes" if [ -n "${shtest_refill}" -o -n "${shtest_clear}" ]; then mv "${errfile}" "$1" [ "$?" -ne 0 ] && report "Can't move refilled tests file back!" elif [ "${test_failed}" = "0" ]; then rm "${errfile}" 2>/dev/null fi } # # main # # for test_suite in "$@"; do run_test "${test_suite}" done exit 0