github.com/weaveworks/common@v0.0.0-20230728070032-dd9e68f319d5/tools/integration/assert.sh (about) 1 #!/bin/bash 2 # assert.sh 1.1 - bash unit testing framework 3 # Copyright (C) 2009-2015 Robert Lehmann 4 # 5 # http://github.com/lehmannro/assert.sh 6 # 7 # This program is free software: you can redistribute it and/or modify 8 # it under the terms of the GNU Lesser General Public License as published 9 # by the Free Software Foundation, either version 3 of the License, or 10 # (at your option) any later version. 11 # 12 # This program is distributed in the hope that it will be useful, 13 # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 # GNU Lesser General Public License for more details. 16 # 17 # You should have received a copy of the GNU Lesser General Public License 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 19 20 export DISCOVERONLY=${DISCOVERONLY:-} 21 export DEBUG=${DEBUG:-} 22 export STOP=${STOP:-} 23 export INVARIANT=${INVARIANT:-} 24 export CONTINUE=${CONTINUE:-} 25 26 args="$(getopt -n "$0" -l \ 27 verbose,help,stop,discover,invariant,continue vhxdic "$@")" \ 28 || exit -1 29 for arg in $args; do 30 case "$arg" in 31 -h) 32 echo "$0 [-vxidc]" \ 33 "[--verbose] [--stop] [--invariant] [--discover] [--continue]" 34 echo "$(sed 's/./ /g' <<<"$0") [-h] [--help]" 35 exit 0 36 ;; 37 --help) 38 cat <<EOF 39 Usage: $0 [options] 40 Language-agnostic unit tests for subprocesses. 41 42 Options: 43 -v, --verbose generate output for every individual test case 44 -x, --stop stop running tests after the first failure 45 -i, --invariant do not measure timings to remain invariant between runs 46 -d, --discover collect test suites only, do not run any tests 47 -c, --continue do not modify exit code to test suite status 48 -h show brief usage information and exit 49 --help show this help message and exit 50 EOF 51 exit 0 52 ;; 53 -v | --verbose) 54 DEBUG=1 55 ;; 56 -x | --stop) 57 STOP=1 58 ;; 59 -i | --invariant) 60 INVARIANT=1 61 ;; 62 -d | --discover) 63 DISCOVERONLY=1 64 ;; 65 -c | --continue) 66 CONTINUE=1 67 ;; 68 esac 69 done 70 71 _indent=$'\n\t' # local format helper 72 73 _assert_reset() { 74 tests_ran=0 75 tests_failed=0 76 tests_errors=() 77 tests_starttime="$(date +%s%N)" # nanoseconds_since_epoch 78 } 79 80 assert_end() { 81 # assert_end [suite ..] 82 tests_endtime="$(date +%s%N)" 83 # required visible decimal place for seconds (leading zeros if needed) 84 tests_time="$( 85 printf "%010d" "$((${tests_endtime/%N/000000000} - ${tests_starttime/%N/000000000}))" 86 )" # in ns 87 tests="$tests_ran ${*:+$* }tests" 88 [[ -n "$DISCOVERONLY" ]] && echo "collected $tests." && _assert_reset && return 89 [[ -n "$DEBUG" ]] && echo 90 # to get report_time split tests_time on 2 substrings: 91 # ${tests_time:0:${#tests_time}-9} - seconds 92 # ${tests_time:${#tests_time}-9:3} - milliseconds 93 if [[ -z "$INVARIANT" ]]; then 94 idx=$((${#tests_time} - 9)) 95 report_time=" in ${tests_time:0:${idx}}.${tests_time:${idx}:3}s" 96 else 97 report_time= 98 fi 99 if [[ "$tests_failed" -eq 0 ]]; then 100 echo "all $tests passed$report_time." 101 else 102 for error in "${tests_errors[@]}"; do echo "$error"; done 103 echo "$tests_failed of $tests failed$report_time." 104 fi 105 [[ $tests_failed -gt 0 ]] && tests_suite_status=1 106 _assert_reset 107 } 108 109 assert() { 110 # assert <command> <expected stdout> [stdin] 111 ((tests_ran++)) || : 112 [[ -z "$DISCOVERONLY" ]] || return 113 expected=$(echo -ne "${2:-}") 114 result="$(eval "$1" 2>/dev/null <<<"${3:-}")" || true 115 if [[ "$result" == "$expected" ]]; then 116 [[ -z "$DEBUG" ]] || echo -n . 117 return 118 fi 119 result="$(sed -e :a -e '$!N;s/\n/\\n/;ta' <<<"$result")" 120 [[ -z "$result" ]] && result="nothing" || result="\"$result\"" 121 [[ -z "$2" ]] && expected="nothing" || expected="\"$2\"" 122 _assert_fail "expected $expected${_indent}got $result" "$1" "$3" 123 } 124 125 assert_raises() { 126 # assert_raises <command> <expected code> [stdin] 127 ((tests_ran++)) || : 128 [[ -z "$DISCOVERONLY" ]] || return 129 status=0 130 (eval "$1" <<<"${3:-}") >/dev/null 2>&1 || status=$? 131 expected=${2:-0} 132 if [[ "$status" -eq "$expected" ]]; then 133 [[ -z "$DEBUG" ]] || echo -n . 134 return 135 fi 136 _assert_fail "program terminated with code $status instead of $expected" "$1" "$3" 137 } 138 139 _assert_fail() { 140 # _assert_fail <failure> <command> <stdin> 141 [[ -n "$DEBUG" ]] && echo -n X 142 report="test #$tests_ran \"$2${3:+ <<< $3}\" failed:${_indent}$1" 143 if [[ -n "$STOP" ]]; then 144 [[ -n "$DEBUG" ]] && echo 145 echo "$report" 146 exit 1 147 fi 148 tests_errors[$tests_failed]="$report" 149 ((tests_failed++)) || : 150 } 151 152 skip_if() { 153 # skip_if <command ..> 154 (eval "$@") >/dev/null 2>&1 && status=0 || status=$? 155 [[ "$status" -eq 0 ]] || return 156 skip 157 } 158 159 skip() { 160 # skip (no arguments) 161 shopt -q extdebug && tests_extdebug=0 || tests_extdebug=1 162 shopt -q -o errexit && tests_errexit=0 || tests_errexit=1 163 # enable extdebug so returning 1 in a DEBUG trap handler skips next command 164 shopt -s extdebug 165 # disable errexit (set -e) so we can safely return 1 without causing exit 166 set +o errexit 167 tests_trapped=0 168 trap _skip DEBUG 169 } 170 _skip() { 171 if [[ $tests_trapped -eq 0 ]]; then 172 # DEBUG trap for command we want to skip. Do not remove the handler 173 # yet because *after* the command we need to reset extdebug/errexit (in 174 # another DEBUG trap.) 175 tests_trapped=1 176 [[ -z "$DEBUG" ]] || echo -n s 177 return 1 178 else 179 trap - DEBUG 180 [[ $tests_extdebug -eq 0 ]] || shopt -u extdebug 181 [[ $tests_errexit -eq 1 ]] || set -o errexit 182 return 0 183 fi 184 } 185 186 _assert_reset 187 : ${tests_suite_status:=0} # remember if any of the tests failed so far 188 _assert_cleanup() { 189 local status=$? 190 # modify exit code if it's not already non-zero 191 [[ $status -eq 0 && -z $CONTINUE ]] && exit $tests_suite_status 192 } 193 trap _assert_cleanup EXIT