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