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