Import of code from c-tap-harness
[openafs.git] / src / external / c-tap-harness / tests / tap / libtap.sh
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 # <https://www.eyrie.org/~eagle/software/c-tap-harness/>.
11 #
12 # Written by Russ Allbery <eagle@eyrie.org>
13 # Copyright 2009-2012, 2016 Russ Allbery <eagle@eyrie.org>
14 # Copyright 2006-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 # SPDX-License-Identifier: MIT
36
37 # Print out the number of test cases we expect to run.
38 plan () {
39     count=1
40     planned="$1"
41     failed=0
42     echo "1..$1"
43     trap finish 0
44 }
45
46 # Prepare for lazy planning.
47 plan_lazy () {
48     count=1
49     planned=0
50     failed=0
51     trap finish 0
52 }
53
54 # Report the test status on exit.
55 finish () {
56     tap_highest=`expr "$count" - 1`
57     if [ "$planned" = 0 ] ; then
58         echo "1..$tap_highest"
59         planned="$tap_highest"
60     fi
61     tap_looks='# Looks like you'
62     if [ "$planned" -gt 0 ] ; then
63         if [ "$planned" -gt "$tap_highest" ] ; then
64             if [ "$planned" -gt 1 ] ; then
65                 echo "$tap_looks planned $planned tests but only ran" \
66                     "$tap_highest"
67             else
68                 echo "$tap_looks planned $planned test but only ran" \
69                     "$tap_highest"
70             fi
71         elif [ "$planned" -lt "$tap_highest" ] ; then
72             tap_extra=`expr "$tap_highest" - "$planned"`
73             if [ "$planned" -gt 1 ] ; then
74                 echo "$tap_looks planned $planned tests but ran" \
75                     "$tap_extra extra"
76             else
77                 echo "$tap_looks planned $planned test but ran" \
78                     "$tap_extra extra"
79             fi
80         elif [ "$failed" -gt 0 ] ; then
81             if [ "$failed" -gt 1 ] ; then
82                 echo "$tap_looks failed $failed tests of $planned"
83             else
84                 echo "$tap_looks failed $failed test of $planned"
85             fi
86         elif [ "$planned" -gt 1 ] ; then
87             echo "# All $planned tests successful or skipped"
88         else
89             echo "# $planned test successful or skipped"
90         fi
91     fi
92 }
93
94 # Skip the entire test suite.  Should be run instead of plan.
95 skip_all () {
96     tap_desc="$1"
97     if [ -n "$tap_desc" ] ; then
98         echo "1..0 # skip $tap_desc"
99     else
100         echo "1..0 # skip"
101     fi
102     exit 0
103 }
104
105 # ok takes a test description and a command to run and prints success if that
106 # command is successful, false otherwise.  The count starts at 1 and is
107 # updated each time ok is printed.
108 ok () {
109     tap_desc="$1"
110     if [ -n "$tap_desc" ] ; then
111         tap_desc=" - $tap_desc"
112     fi
113     shift
114     if "$@" ; then
115         echo ok "$count$tap_desc"
116     else
117         echo not ok "$count$tap_desc"
118         failed=`expr $failed + 1`
119     fi
120     count=`expr $count + 1`
121 }
122
123 # Skip the next test.  Takes the reason why the test is skipped.
124 skip () {
125     echo "ok $count # skip $*"
126     count=`expr $count + 1`
127 }
128
129 # Report the same status on a whole set of tests.  Takes the count of tests,
130 # the description, and then the command to run to determine the status.
131 ok_block () {
132     tap_i=$count
133     tap_end=`expr $count + $1`
134     shift
135     while [ "$tap_i" -lt "$tap_end" ] ; do
136         ok "$@"
137         tap_i=`expr $tap_i + 1`
138     done
139 }
140
141 # Skip a whole set of tests.  Takes the count and then the reason for skipping
142 # the test.
143 skip_block () {
144     tap_i=$count
145     tap_end=`expr $count + $1`
146     shift
147     while [ "$tap_i" -lt "$tap_end" ] ; do
148         skip "$@"
149         tap_i=`expr $tap_i + 1`
150     done
151 }
152
153 # Portable variant of printf '%s\n' "$*".  In the majority of cases, this
154 # function is slower than printf, because the latter is often implemented
155 # as a builtin command.  The value of the variable IFS is ignored.
156 #
157 # This macro must not be called via backticks inside double quotes, since this
158 # will result in bizarre escaping behavior and lots of extra backslashes on
159 # Solaris.
160 puts () {
161     cat << EOH
162 $@
163 EOH
164 }
165
166 # Run a program expected to succeed, and print ok if it does and produces the
167 # correct output.  Takes the description, expected exit status, the expected
168 # output, the command to run, and then any arguments for that command.
169 # Standard output and standard error are combined when analyzing the output of
170 # the command.
171 #
172 # If the command may contain system-specific error messages in its output,
173 # add strip_colon_error before the command to post-process its output.
174 ok_program () {
175     tap_desc="$1"
176     shift
177     tap_w_status="$1"
178     shift
179     tap_w_output="$1"
180     shift
181     tap_output=`"$@" 2>&1`
182     tap_status=$?
183     if [ $tap_status = $tap_w_status ] \
184         && [ x"$tap_output" = x"$tap_w_output" ] ; then
185         ok "$tap_desc" true
186     else
187         echo "#  saw: ($tap_status) $tap_output"
188         echo "#  not: ($tap_w_status) $tap_w_output"
189         ok "$tap_desc" false
190     fi
191 }
192
193 # Strip a colon and everything after it off the output of a command, as long
194 # as that colon comes after at least one whitespace character.  (This is done
195 # to avoid stripping the name of the program from the start of an error
196 # message.)  This is used to remove system-specific error messages (coming
197 # from strerror, for example).
198 strip_colon_error() {
199     tap_output=`"$@" 2>&1`
200     tap_status=$?
201     tap_output=`puts "$tap_output" | sed 's/^\([^ ]* [^:]*\):.*/\1/'`
202     puts "$tap_output"
203     return $tap_status
204 }
205
206 # Bail out with an error message.
207 bail () {
208     echo 'Bail out!' "$@"
209     exit 255
210 }
211
212 # Output a diagnostic on standard error, preceded by the required # mark.
213 diag () {
214     echo '#' "$@"
215 }
216
217 # Search for the given file first in $C_TAP_BUILD and then in $C_TAP_SOURCE
218 # and echo the path where the file was found, or the empty string if the file
219 # wasn't found.
220 #
221 # This macro uses puts, so don't run it using backticks inside double quotes
222 # or bizarre quoting behavior will happen with Solaris sh.
223 test_file_path () {
224     if [ -n "$C_TAP_BUILD" ] && [ -f "$C_TAP_BUILD/$1" ] ; then
225         puts "$C_TAP_BUILD/$1"
226     elif [ -n "$C_TAP_SOURCE" ] && [ -f "$C_TAP_SOURCE/$1" ] ; then
227         puts "$C_TAP_SOURCE/$1"
228     else
229         echo ''
230     fi
231 }
232
233 # Create $C_TAP_BUILD/tmp for use by tests for storing temporary files and
234 # return the path (via standard output).
235 #
236 # This macro uses puts, so don't run it using backticks inside double quotes
237 # or bizarre quoting behavior will happen with Solaris sh.
238 test_tmpdir () {
239     if [ -z "$C_TAP_BUILD" ] ; then
240         tap_tmpdir="./tmp"
241     else
242         tap_tmpdir="$C_TAP_BUILD"/tmp
243     fi
244     if [ ! -d "$tap_tmpdir" ] ; then
245         mkdir "$tap_tmpdir" || bail "Error creating $tap_tmpdir"
246     fi
247     puts "$tap_tmpdir"
248 }