#!/usr/bin/env bash # SPDX-License-Identifier: LGPL-3.0-only WITH OCaml-LGPL-linking-exception set -u declare -r SELF=$(basename $0) declare testme="sidparse.native" declare verbose=no declare current_test="" declare -A tests=() declare -a failed=() declare -r default_timeout=5 # s declare -r tput_bin=$(command -v tput) if [ -t 1 ] && [ -n "${tput_bin:-}" ]; then declare -rA colors=( \ [blk]=$("${tput_bin}" setaf 0) \ [red]=$("${tput_bin}" setaf 1) \ [grn]=$("${tput_bin}" setaf 2) \ [ylw]=$("${tput_bin}" setaf 3) \ [blu]=$("${tput_bin}" setaf 4) \ [pur]=$("${tput_bin}" setaf 5) \ [cyn]=$("${tput_bin}" setaf 6) \ [wht]=$("${tput_bin}" setaf 7) \ [nil]="" \ [rst]=$("${tput_bin}" sgr0) \ ) msg () { local col="${colors[$1]}" local tag="$2" local msg="$3" printf "[${col}%s${colors[rst]}] %s\n" "${tag}" "${msg}" } else msg () { local _void="$1" local tag="$2" local msg="$3" printf "[%s] %s\n" "${tag}" "${msg}" } fi msg_noise () { if [ "${verbose:-uhgno}" = yes ]; then msg pur info "$*" >&2 fi } msg_error () { msg red error "$*" >&2 } msg_fatal () { msg_error $@ exit -1 } msg_failed () { local tag="$1" shift msg red "${tag}" "$*" >&2 } msg_good () { msg grn nice "$*" >&2 } fail () { local name="$1" local condition="$2" local cmd="" shift 2 if [ $# -ne 0 ]; then cmd=": [$*]" fi local entry="${name} (${condition})${cmd}" failed+=( "${entry}" ) } fail_ret () { local name="$1" local expect="$2" local retval="$3" local reason="$4" local cmd="" shift 4 if [ $# -ne 0 ]; then cmd=": [$@]" fi local entry="$(printf "%s (returned=%d, expected=%d, reason=%s)%s" \ "${name}" ${retval} ${expect} "${reason}" \ "${cmd}" )" failed+=( "${entry}" ) } exit_handler () { if [ -n "${current_test}" ]; then msg_error "caught exit while test “${current_test}” was in progress" fail "${current_test}" exit fi current_test="" } trap exit_handler exit assert_equal () { local name"$1" local a="$2" local b="$3" if [ "$a" != "$b" ]; then fail "${name}" "failed comparison; [$a] <> [$b]" fi } assert_int_equal () { local name"$1" local a=$2 local b=$3 if [ $a -ne $b ]; then fail "${name}" "failed integer comparison; $a <> $b" fi } register_test () { local name="$1" if [ -n "${tests[${name}]:-}" ]; then msg_fatal "cannot register test «${name}»: already registered" fi tests["${name}"]="test_${name}" } test_parse_simple () { local name="$1" local ret local cmd=( "./${testme}" S-1-1-0 ) timeout ${default_timeout} ${cmd[@]} &>/dev/null ret=$? case ${ret} in 0) msg_noise "${name}: success" ;; 124) fail_ret "${name}" 0 ${ret} "timeout" ${cmd[@]} ;; 127) fail_ret "${name}" 0 ${ret} "command" ${cmd[@]} ;; *) fail_ret "${name}" 0 ${ret} "invalid" ${cmd[@]} ;; esac } register_test parse_simple test_parse_stdin () { local name="$1" local ret local cmd=( "./${testme}" ) timeout ${default_timeout} ${cmd[@]} &>/dev/null <<-STOPTHAT S-1-0-0 S-1-1-0 S-1-42-2187-1337 STOPTHAT ret=$? case ${ret} in 0) msg_noise "${name}: success" ;; 124) fail_ret "${name}" 0 ${ret} "timeout" ${cmd[@]} ;; 127) fail_ret "${name}" 0 ${ret} "command" ${cmd[@]} ;; *) fail_ret "${name}" 0 ${ret} "invalid" ${cmd[@]} ;; esac } register_test parse_stdin test_parse_garbage_fail () { local name="$1" local ret local cmd=( "./${testme}" ) timeout ${default_timeout} ${cmd[@]} "Ook!" &>/dev/null ret=$? case ${ret} in 255) msg_noise "${name}: success" ;; 0) fail_ret "${name}" 255 ${ret} "unexpected" ${cmd[@]} ;; 124) fail_ret "${name}" 255 ${ret} "timeout" ${cmd[@]} ;; 127) fail_ret "${name}" 255 ${ret} "command" ${cmd[@]} ;; *) fail_ret "${name}" 255 ${ret} "invalid" ${cmd[@]} ;; esac } register_test parse_garbage_fail test_count_output () { local name="$1" local ret local count local cmd=( "./${testme}" \ "S-1-42-1337" \ "S-1-1-2-3-4-5-6-7-8-9" \ ) count=$( timeout ${default_timeout} ${cmd[@]} \ |wc -l ) ret=$? case ${ret} in 0) msg_noise "${name}: success" ;; 124) fail_ret "${name}" 0 ${ret} "timeout" ${cmd[@]} ;; 127) fail_ret "${name}" 0 ${ret} "command" ${cmd[@]} ;; *) fail_ret "${name}" 0 ${ret} "invalid" ${cmd[@]} ;; esac assert_int_equal "${name}" ${count} 3 } register_test count_output test_count_output_quiet () { # no output at all, even errors local name="$1" local count local cmd=( "./${testme}" --quiet \ "S-1-42-1337" \ "Smite you with thunderbolts!" \ "S-1-1-2-3-4-5-6-7-8-9" \ ) count=$( timeout ${default_timeout} ${cmd[@]} \ |&wc -c ) assert_int_equal "${name}" ${count} 0 } register_test count_output_quiet test_count_output_validate () { # only error lines local name="$1" local count local wcfd local cmd=( "./${testme}" --validate \ "S-1-42-1337" \ "Smite you with thunderbolts!" \ "S-1-1-2-3-4-5-6-7-8-9" \ ) count=$( timeout ${default_timeout} "${cmd[@]}" \ |&wc -l ) assert_int_equal "${name}" ${count} 2 } register_test count_output_validate test_parse_stdin_empty () { local name="$1" local ret local cmd=( "./${testme}" ) <<<"" timeout ${default_timeout} "${cmd[@]}" &>/dev/null ret=$? case ${ret} in 0) msg_noise "${name}: success" ;; 124) fail_ret "${name}" 0 ${ret} "timeout" ${cmd[@]} ;; *) fail_ret "${name}" 0 ${ret} "invalid" ${cmd[@]} ;; esac } register_test parse_stdin_empty report () { local n=${#failed[@]} if [ $n -eq 0 ]; then msg_good "all tests completed successfully" return fi msg_failed report "detected $n failures" for f in "${failed[@]}"; do msg_failed failure "$f" done } exit_with_status () { [[ ${#failed[@]} -ne 0 ]] && exit -1 || exit 0 } parse_argv () { while [ -n "${1:-}" ]; do case "$1" in -v|--verbose) verbose=yes ;; -*) msg_fatal "unknown argument “$1”" ;; *) testme="$1" ;; esac shift done } run_test () { local i=$1 local name="$2" msg_noise "[$i] test «$name»" current_test="${name}" "test_${name}" "${name}" current_test="" } main () { local i=0 parse_argv $@ msg_noise "${SELF}: commencing tests for «${testme}»" for tst in ${!tests[@]}; do (( ++i )) run_test $i "${tst}" done report msg_noise "${SELF}: completed $i tests for «${testme}»" exit_with_status } main $@