libtap.subr (8031B)
1 # Shell function library for test cases. 2 # 3 # Note that while many of the functions in this library could benefit from 4 # using "local" to avoid possibly hammering global variables, Solaris /bin/sh 5 # doesn't support local and this library aspires to be portable to Solaris 6 # Bourne shell. Instead, all private variables are prefixed with "tap_". 7 # 8 # This file provides a TAP-compatible shell function library useful for 9 # writing test cases. It is part of C TAP Harness, which can be found at 10 # <http://www.eyrie.org/~eagle/software/c-tap-harness/>. 11 # 12 # Written by Russ Allbery <eagle@eyrie.org> 13 # Copyright 2009, 2010, 2011, 2012, 2016 Russ Allbery <eagle@eyrie.org> 14 # Copyright 2006, 2007, 2008, 2013 15 # The Board of Trustees of the Leland Stanford Junior University 16 # 17 # Permission is hereby granted, free of charge, to any person obtaining a copy 18 # of this software and associated documentation files (the "Software"), to 19 # deal in the Software without restriction, including without limitation the 20 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 21 # sell copies of the Software, and to permit persons to whom the Software is 22 # furnished to do so, subject to the following conditions: 23 # 24 # The above copyright notice and this permission notice shall be included in 25 # all copies or substantial portions of the Software. 26 # 27 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 32 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 33 # IN THE SOFTWARE. 34 35 # Print out the number of test cases we expect to run. 36 plan () { 37 count=1 38 planned="$1" 39 failed=0 40 echo "1..$1" 41 trap finish 0 42 } 43 44 # Prepare for lazy planning. 45 plan_lazy () { 46 count=1 47 planned=0 48 failed=0 49 trap finish 0 50 } 51 52 # Report the test status on exit. 53 finish () { 54 tap_highest=`expr "$count" - 1` 55 if [ "$planned" = 0 ] ; then 56 echo "1..$tap_highest" 57 planned="$tap_highest" 58 fi 59 tap_looks='# Looks like you' 60 if [ "$planned" -gt 0 ] ; then 61 if [ "$planned" -gt "$tap_highest" ] ; then 62 if [ "$planned" -gt 1 ] ; then 63 echo "$tap_looks planned $planned tests but only ran" \ 64 "$tap_highest" 65 else 66 echo "$tap_looks planned $planned test but only ran" \ 67 "$tap_highest" 68 fi 69 elif [ "$planned" -lt "$tap_highest" ] ; then 70 tap_extra=`expr "$tap_highest" - "$planned"` 71 if [ "$planned" -gt 1 ] ; then 72 echo "$tap_looks planned $planned tests but ran" \ 73 "$tap_extra extra" 74 else 75 echo "$tap_looks planned $planned test but ran" \ 76 "$tap_extra extra" 77 fi 78 elif [ "$failed" -gt 0 ] ; then 79 if [ "$failed" -gt 1 ] ; then 80 echo "$tap_looks failed $failed tests of $planned" 81 else 82 echo "$tap_looks failed $failed test of $planned" 83 fi 84 elif [ "$planned" -gt 1 ] ; then 85 echo "# All $planned tests successful or skipped" 86 else 87 echo "# $planned test successful or skipped" 88 fi 89 fi 90 } 91 92 # Skip the entire test suite. Should be run instead of plan. 93 skip_all () { 94 tap_desc="$1" 95 if [ -n "$tap_desc" ] ; then 96 echo "1..0 # skip $tap_desc" 97 else 98 echo "1..0 # skip" 99 fi 100 exit 0 101 } 102 103 # ok takes a test description and a command to run and prints success if that 104 # command is successful, false otherwise. The count starts at 1 and is 105 # updated each time ok is printed. 106 ok () { 107 tap_desc="$1" 108 if [ -n "$tap_desc" ] ; then 109 tap_desc=" - $tap_desc" 110 fi 111 shift 112 if "$@" ; then 113 echo ok "$count$tap_desc" 114 else 115 echo not ok "$count$tap_desc" 116 failed=`expr $failed + 1` 117 fi 118 count=`expr $count + 1` 119 } 120 121 # Skip the next test. Takes the reason why the test is skipped. 122 skip () { 123 echo "ok $count # skip $*" 124 count=`expr $count + 1` 125 } 126 127 # Report the same status on a whole set of tests. Takes the count of tests, 128 # the description, and then the command to run to determine the status. 129 ok_block () { 130 tap_i=$count 131 tap_end=`expr $count + $1` 132 shift 133 while [ "$tap_i" -lt "$tap_end" ] ; do 134 ok "$@" 135 tap_i=`expr $tap_i + 1` 136 done 137 } 138 139 # Skip a whole set of tests. Takes the count and then the reason for skipping 140 # the test. 141 skip_block () { 142 tap_i=$count 143 tap_end=`expr $count + $1` 144 shift 145 while [ "$tap_i" -lt "$tap_end" ] ; do 146 skip "$@" 147 tap_i=`expr $tap_i + 1` 148 done 149 } 150 151 # Portable variant of printf '%s\n' "$*". In the majority of cases, this 152 # function is slower than printf, because the latter is often implemented 153 # as a builtin command. The value of the variable IFS is ignored. 154 # 155 # This macro must not be called via backticks inside double quotes, since this 156 # will result in bizarre escaping behavior and lots of extra backslashes on 157 # Solaris. 158 puts () { 159 cat << EOH 160 $@ 161 EOH 162 } 163 164 # Run a program expected to succeed, and print ok if it does and produces the 165 # correct output. Takes the description, expected exit status, the expected 166 # output, the command to run, and then any arguments for that command. 167 # Standard output and standard error are combined when analyzing the output of 168 # the command. 169 # 170 # If the command may contain system-specific error messages in its output, 171 # add strip_colon_error before the command to post-process its output. 172 ok_program () { 173 tap_desc="$1" 174 shift 175 tap_w_status="$1" 176 shift 177 tap_w_output="$1" 178 shift 179 tap_output=`"$@" 2>&1` 180 tap_status=$? 181 if [ $tap_status = $tap_w_status ] \ 182 && [ x"$tap_output" = x"$tap_w_output" ] ; then 183 ok "$tap_desc" true 184 else 185 echo "# saw: ($tap_status) $tap_output" 186 echo "# not: ($tap_w_status) $tap_w_output" 187 ok "$tap_desc" false 188 fi 189 } 190 191 # Strip a colon and everything after it off the output of a command, as long 192 # as that colon comes after at least one whitespace character. (This is done 193 # to avoid stripping the name of the program from the start of an error 194 # message.) This is used to remove system-specific error messages (coming 195 # from strerror, for example). 196 strip_colon_error() { 197 tap_output=`"$@" 2>&1` 198 tap_status=$? 199 tap_output=`puts "$tap_output" | sed 's/^\([^ ]* [^:]*\):.*/\1/'` 200 puts "$tap_output" 201 return $tap_status 202 } 203 204 # Bail out with an error message. 205 bail () { 206 echo 'Bail out!' "$@" 207 exit 255 208 } 209 210 # Output a diagnostic on standard error, preceded by the required # mark. 211 diag () { 212 echo '#' "$@" 213 } 214 215 # Search for the given file first in $C_TAP_BUILD and then in $C_TAP_SOURCE 216 # and echo the path where the file was found, or the empty string if the file 217 # wasn't found. 218 # 219 # This macro uses puts, so don't run it using backticks inside double quotes 220 # or bizarre quoting behavior will happen with Solaris sh. 221 test_file_path () { 222 if [ -n "$C_TAP_BUILD" ] && [ -f "$C_TAP_BUILD/$1" ] ; then 223 puts "$C_TAP_BUILD/$1" 224 elif [ -n "$C_TAP_SOURCE" ] && [ -f "$C_TAP_SOURCE/$1" ] ; then 225 puts "$C_TAP_SOURCE/$1" 226 else 227 echo '' 228 fi 229 } 230 231 # Create $C_TAP_BUILD/tmp for use by tests for storing temporary files and 232 # return the path (via standard output). 233 # 234 # This macro uses puts, so don't run it using backticks inside double quotes 235 # or bizarre quoting behavior will happen with Solaris sh. 236 test_tmpdir () { 237 if [ -z "$C_TAP_BUILD" ] ; then 238 tap_tmpdir="./tmp" 239 else 240 tap_tmpdir="$C_TAP_BUILD"/tmp 241 fi 242 if [ ! -d "$tap_tmpdir" ] ; then 243 mkdir "$tap_tmpdir" || bail "Error creating $tap_tmpdir" 244 fi 245 puts "$tap_tmpdir" 246 }