Fix: tap array subscript has type char warning
[urcu.git] / tests / utils / tap.sh
1 #!/bin/bash
2 #
3 # Copyright 2010 Patrick LeBoutillier <patrick.leboutillier@gmail.com>
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18
19 _version='1.01'
20
21 _plan_set=0
22 _no_plan=0
23 _skip_all=0
24 _test_died=0
25 _expected_tests=0
26 _executed_tests=0
27 _failed_tests=0
28 TODO=
29
30
31 usage(){
32 cat <<'USAGE'
33 tap-functions: A TAP-producing BASH library
34
35 PLAN:
36 plan_no_plan
37 plan_skip_all [REASON]
38 plan_tests NB_TESTS
39
40 TEST:
41 ok RESULT [NAME]
42 okx COMMAND
43 is RESULT EXPECTED [NAME]
44 isnt RESULT EXPECTED [NAME]
45 like RESULT PATTERN [NAME]
46 unlike RESULT PATTERN [NAME]
47 pass [NAME]
48 fail [NAME]
49
50 SKIP:
51 skip [CONDITION] [REASON] [NB_TESTS=1]
52
53 skip $feature_not_present "feature not present" 2 || {
54 is $a "a"
55 is $b "b"
56 }
57
58 TODO:
59 Specify TODO mode by setting $TODO:
60 TODO="not implemented yet"
61 ok $result "some not implemented test"
62 unset TODO
63
64 OTHER:
65 diag MSG
66
67 EXAMPLE:
68 #!/bin/bash
69
70 . tap-functions
71
72 plan_tests 7
73
74 me=$USER
75 is $USER $me "I am myself"
76 like $HOME $me "My home is mine"
77 like "`id`" $me "My id matches myself"
78
79 /bin/ls $HOME 1>&2
80 ok $? "/bin/ls $HOME"
81 # Same thing using okx shortcut
82 okx /bin/ls $HOME
83
84 [[ "`id -u`" != "0" ]]
85 i_am_not_root=$?
86 skip $i_am_not_root "Must be root" || {
87 okx ls /root
88 }
89
90 TODO="figure out how to become root..."
91 okx [ "$HOME" == "/root" ]
92 unset TODO
93 USAGE
94 exit
95 }
96
97 opt=
98 set_u=
99 while getopts ":sx" opt ; do
100 case $_opt in
101 u) set_u=1 ;;
102 *) usage ;;
103 esac
104 done
105 shift $(( OPTIND - 1 ))
106 # Don't allow uninitialized variables if requested
107 [[ -n "$set_u" ]] && set -u
108 unset opt set_u
109
110 # Used to call _cleanup on shell exit
111 trap _exit EXIT
112
113
114 plan_no_plan(){
115 (( _plan_set != 0 )) && "You tried to plan twice!"
116
117 _plan_set=1
118 _no_plan=1
119
120 return 0
121 }
122
123
124 plan_skip_all(){
125 local reason=${1:-''}
126
127 (( _plan_set != 0 )) && _die "You tried to plan twice!"
128
129 _print_plan 0 "Skip $reason"
130
131 _skip_all=1
132 _plan_set=1
133 _exit 0
134
135 return 0
136 }
137
138 plan_tests(){
139 local tests=${1:?}
140
141 (( _plan_set != 0 )) && _die "You tried to plan twice!"
142 (( tests == 0 )) && _die "You said to run 0 tests! You've got to run something."
143
144 _print_plan $tests
145 _expected_tests=$tests
146 _plan_set=1
147
148 return $tests
149 }
150
151
152 _print_plan(){
153 local tests=${1:?}
154 local directive=${2:-''}
155
156 echo -n "1..$tests"
157 [[ -n "$directive" ]] && echo -n " # $directive"
158 echo
159 }
160
161
162 pass(){
163 local name=$1
164 ok 0 "$name"
165 }
166
167
168 fail(){
169 local name=$1
170 ok 1 "$name"
171 }
172
173 # This is the workhorse method that actually
174 # prints the tests result.
175 ok(){
176 local result=${1:?}
177 local name=${2:-''}
178
179 (( _plan_set == 0 )) && _die "You tried to run a test without a plan! Gotta have a plan."
180
181 _executed_tests=$(( $_executed_tests + 1 ))
182
183 if [[ -n "$name" ]] ; then
184 if _matches "$name" "^[0-9]+$" ; then
185 diag " You named your test '$name'. You shouldn't use numbers for your test names."
186 diag " Very confusing."
187 fi
188 fi
189
190 if (( result != 0 )) ; then
191 echo -n "not "
192 _failed_tests=$(( _failed_tests + 1 ))
193 fi
194 echo -n "ok $_executed_tests"
195
196 if [[ -n "$name" ]] ; then
197 local ename=${name//\#/\\#}
198 echo -n " - $ename"
199 fi
200
201 if [[ -n "$TODO" ]] ; then
202 echo -n " # TODO $TODO" ;
203 if (( result != 0 )) ; then
204 _failed_tests=$(( _failed_tests - 1 ))
205 fi
206 fi
207
208 echo
209 if (( result != 0 )) ; then
210 local file='tap-functions'
211 local func=
212 local line=
213
214 local i=0
215 local bt=$(caller $i)
216 while _matches "$bt" "tap-functions$" ; do
217 i=$(( $i + 1 ))
218 bt=$(caller $i)
219 done
220 local backtrace=
221 eval $(caller $i | (read line func file ; echo "backtrace=\"$file:$func() at line $line.\""))
222
223 local t=
224 [[ -n "$TODO" ]] && t="(TODO) "
225
226 if [[ -n "$name" ]] ; then
227 diag " Failed ${t}test '$name'"
228 diag " in $backtrace"
229 else
230 diag " Failed ${t}test in $backtrace"
231 fi
232 fi
233
234 return $result
235 }
236
237
238 okx(){
239 local command="$@"
240
241 local line=
242 diag "Output of '$command':"
243 "$@" | while read line ; do
244 diag "$line"
245 done
246 ok ${PIPESTATUS[0]} "$command"
247 }
248
249
250 _equals(){
251 local result=${1:?}
252 local expected=${2:?}
253
254 if [[ "$result" == "$expected" ]] ; then
255 return 0
256 else
257 return 1
258 fi
259 }
260
261
262 # Thanks to Aaron Kangas for the patch to allow regexp matching
263 # under bash < 3.
264 _bash_major_version=${BASH_VERSION%%.*}
265 _matches(){
266 local result=${1:?}
267 local pattern=${2:?}
268
269 if [[ -z "$result" || -z "$pattern" ]] ; then
270 return 1
271 else
272 if (( _bash_major_version >= 3 )) ; then
273 [[ "$result" =~ "$pattern" ]]
274 else
275 echo "$result" | egrep -q "$pattern"
276 fi
277 fi
278 }
279
280
281 _is_diag(){
282 local result=${1:?}
283 local expected=${2:?}
284
285 diag " got: '$result'"
286 diag " expected: '$expected'"
287 }
288
289
290 is(){
291 local result=${1:?}
292 local expected=${2:?}
293 local name=${3:-''}
294
295 _equals "$result" "$expected"
296 (( $? == 0 ))
297 ok $? "$name"
298 local r=$?
299 (( r != 0 )) && _is_diag "$result" "$expected"
300 return $r
301 }
302
303
304 isnt(){
305 local result=${1:?}
306 local expected=${2:?}
307 local name=${3:-''}
308
309 _equals "$result" "$expected"
310 (( $? != 0 ))
311 ok $? "$name"
312 local r=$?
313 (( r != 0 )) && _is_diag "$result" "$expected"
314 return $r
315 }
316
317
318 like(){
319 local result=${1:?}
320 local pattern=${2:?}
321 local name=${3:-''}
322
323 _matches "$result" "$pattern"
324 (( $? == 0 ))
325 ok $? "$name"
326 local r=$?
327 (( r != 0 )) && diag " '$result' doesn't match '$pattern'"
328 return $r
329 }
330
331
332 unlike(){
333 local result=${1:?}
334 local pattern=${2:?}
335 local name=${3:-''}
336
337 _matches "$result" "$pattern"
338 (( $? != 0 ))
339 ok $? "$name"
340 local r=$?
341 (( r != 0 )) && diag " '$result' matches '$pattern'"
342 return $r
343 }
344
345
346 skip(){
347 local condition=${1:?}
348 local reason=${2:-''}
349 local n=${3:-1}
350
351 if (( condition == 0 )) ; then
352 local i=
353 for (( i=0 ; i<$n ; i++ )) ; do
354 _executed_tests=$(( _executed_tests + 1 ))
355 echo "ok $_executed_tests # skip: $reason"
356 done
357 return 0
358 else
359 return
360 fi
361 }
362
363
364 diag(){
365 local msg=${1:?}
366
367 if [[ -n "$msg" ]] ; then
368 echo "# $msg"
369 fi
370
371 return 1
372 }
373
374
375 _die(){
376 local reason=${1:-'<unspecified error>'}
377
378 echo "$reason" >&2
379 _test_died=1
380 _exit 255
381 }
382
383
384 BAIL_OUT(){
385 local reason=${1:-''}
386
387 echo "Bail out! $reason" >&2
388 _exit 255
389 }
390
391
392 _cleanup(){
393 local rc=0
394
395 if (( _plan_set == 0 )) ; then
396 diag "Looks like your test died before it could output anything."
397 return $rc
398 fi
399
400 if (( _test_died != 0 )) ; then
401 diag "Looks like your test died just after $_executed_tests."
402 return $rc
403 fi
404
405 if (( _skip_all == 0 && _no_plan != 0 )) ; then
406 _print_plan $_executed_tests
407 fi
408
409 local s=
410 if (( _no_plan == 0 && _expected_tests < _executed_tests )) ; then
411 s= ; (( _expected_tests > 1 )) && s=s
412 local extra=$(( _executed_tests - _expected_tests ))
413 diag "Looks like you planned $_expected_tests test$s but ran $extra extra."
414 rc=1 ;
415 fi
416
417 if (( _no_plan == 0 && _expected_tests > _executed_tests )) ; then
418 s= ; (( _expected_tests > 1 )) && s=s
419 diag "Looks like you planned $_expected_tests test$s but only ran $_executed_tests."
420 fi
421
422 if (( _failed_tests > 0 )) ; then
423 s= ; (( _failed_tests > 1 )) && s=s
424 diag "Looks like you failed $_failed_tests test$s of $_executed_tests."
425 fi
426
427 return $rc
428 }
429
430
431 _exit_status(){
432 if (( _no_plan != 0 || _plan_set == 0 )) ; then
433 return $_failed_tests
434 fi
435
436 if (( _expected_tests < _executed_tests )) ; then
437 return $(( _executed_tests - _expected_tests ))
438 fi
439
440 return $(( _failed_tests + ( _expected_tests - _executed_tests )))
441 }
442
443
444 _exit(){
445 local rc=${1:-''}
446 if [[ -z "$rc" ]] ; then
447 _exit_status
448 rc=$?
449 fi
450
451 _cleanup
452 local alt_rc=$?
453 (( alt_rc != 0 )) && rc=$alt_rc
454 trap - EXIT
455 exit $rc
456 }
This page took 0.038615 seconds and 4 git commands to generate.