xref: /freebsd/sys/contrib/openzfs/tests/test-runner/include/logapi.shlib (revision 45dd2eaac379e5576f745380260470204c49beac)
1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9# or http://www.opensolaris.org/os/licensing.
10# See the License for the specific language governing permissions
11# and limitations under the License.
12#
13# When distributing Covered Code, include this CDDL HEADER in each
14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15# If applicable, add the following below this CDDL HEADER, with the
16# fields enclosed by brackets "[]" replaced with your own identifying
17# information: Portions Copyright [yyyy] [name of copyright owner]
18#
19# CDDL HEADER END
20#
21
22#
23# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24# Use is subject to license terms.
25#
26# Copyright (c) 2012, 2020 by Delphix. All rights reserved.
27#
28
29. ${STF_TOOLS}/include/stf.shlib
30
31# Output an assertion
32#
33# $@ - assertion text
34
35function log_assert
36{
37	_printline ASSERTION: "$@"
38}
39
40# Output a comment
41#
42# $@ - comment text
43
44function log_note
45{
46	_printline NOTE: "$@"
47}
48
49# Execute and print command with status where success equals non-zero result
50#
51# $@ - command to execute
52#
53# return 0 if command fails, otherwise return 1
54
55function log_neg
56{
57	log_neg_expect ""  "$@"
58}
59
60# Execute a positive test and exit $STF_FAIL is test fails
61#
62# $@ - command to execute
63
64function log_must
65{
66	log_pos "$@" || log_fail
67}
68
69# Execute a positive test (expecting no stderr) and exit $STF_FAIL
70# if test fails
71# $@ - command to execute
72
73function log_must_nostderr
74{
75	log_pos_nostderr "$@" || log_fail
76}
77
78# Execute a positive test but retry the command on failure if the output
79# matches an expected pattern.  Otherwise behave like log_must and exit
80# $STF_FAIL is test fails.
81#
82# $1 - retry keyword
83# $2 - retry attempts
84# $3-$@ - command to execute
85#
86function log_must_retry
87{
88	typeset out=""
89	typeset logfile="/tmp/log.$$"
90	typeset status=1
91	typeset expect=$1
92	typeset retry=$2
93	typeset delay=1
94	shift 2
95
96	while [[ -e $logfile ]]; do
97		logfile="$logfile.$$"
98	done
99
100	while (( $retry > 0 )); do
101		"$@" 2>$logfile
102		status=$?
103		out="cat $logfile"
104
105		if (( $status == 0 )); then
106			$out | egrep -i "internal error|assertion failed" \
107				> /dev/null 2>&1
108			# internal error or assertion failed
109			if [[ $? -eq 0 ]]; then
110				print -u2 $($out)
111				_printerror "$@" "internal error or" \
112					" assertion failure exited $status"
113				status=1
114			else
115				[[ -n $LOGAPI_DEBUG ]] && cat $logfile
116				_printsuccess "$@"
117			fi
118			break
119		else
120			$out | grep -i "$expect" > /dev/null 2>&1
121			if (( $? == 0 )); then
122				print -u2 $($out)
123				_printerror "$@" "Retry in $delay seconds"
124				sleep $delay
125
126				(( retry=retry - 1 ))
127				(( delay=delay * 2 ))
128			else
129				break;
130			fi
131		fi
132	done
133
134	if (( $status != 0 )) ; then
135		print -u2 $($out)
136		_printerror "$@" "exited $status"
137	fi
138
139	_recursive_output $logfile "false"
140	return $status
141}
142
143# Execute a positive test and exit $STF_FAIL is test fails after being
144# retried up to 5 times when the command returns the keyword "busy".
145#
146# $@ - command to execute
147function log_must_busy
148{
149	log_must_retry "busy" 5 "$@" || log_fail
150}
151
152# Execute a negative test and exit $STF_FAIL if test passes
153#
154# $@ - command to execute
155
156function log_mustnot
157{
158	log_neg "$@" || log_fail
159}
160
161# Execute a negative test with keyword expected, and exit
162# $STF_FAIL if test passes
163#
164# $1 - keyword expected
165# $2-$@ - command to execute
166
167function log_mustnot_expect
168{
169	log_neg_expect "$@" || log_fail
170}
171
172# Signal numbers are platform-dependent
173case $(uname) in
174Darwin|FreeBSD)
175	SIGBUS=10
176	SIGSEGV=11
177	;;
178illumos|Linux|*)
179	SIGBUS=7
180	SIGSEGV=11
181	;;
182esac
183EXIT_SUCCESS=0
184EXIT_NOTFOUND=127
185EXIT_SIGNAL=256
186EXIT_SIGBUS=$((EXIT_SIGNAL + SIGBUS))
187EXIT_SIGSEGV=$((EXIT_SIGNAL + SIGSEGV))
188
189# Execute and print command with status where success equals non-zero result
190# or output includes expected keyword
191#
192# $1 - keyword expected
193# $2-$@ - command to execute
194#
195# return 0 if command fails, or the output contains the keyword expected,
196# return 1 otherwise
197
198function log_neg_expect
199{
200	typeset out=""
201	typeset logfile="/tmp/log.$$"
202	typeset ret=1
203	typeset expect=$1
204	shift
205
206	while [[ -e $logfile ]]; do
207		logfile="$logfile.$$"
208	done
209
210	"$@" 2>$logfile
211	typeset status=$?
212	out="cat $logfile"
213
214	# unexpected status
215	if (( $status == EXIT_SUCCESS )); then
216		 print -u2 $($out)
217		_printerror "$@" "unexpectedly exited $status"
218	# missing binary
219	elif (( $status == EXIT_NOTFOUND )); then
220		print -u2 $($out)
221		_printerror "$@" "unexpectedly exited $status (File not found)"
222	# bus error - core dump
223	elif (( $status == EXIT_SIGBUS )); then
224		print -u2 $($out)
225		_printerror "$@" "unexpectedly exited $status (Bus Error)"
226	# segmentation violation - core dump
227	elif (( $status == EXIT_SIGSEGV )); then
228		print -u2 $($out)
229		_printerror "$@" "unexpectedly exited $status (SEGV)"
230	else
231		$out | egrep -i "internal error|assertion failed" \
232			> /dev/null 2>&1
233		# internal error or assertion failed
234		if (( $? == 0 )); then
235			print -u2 $($out)
236			_printerror "$@" "internal error or assertion failure" \
237				" exited $status"
238		elif [[ -n $expect ]] ; then
239			$out | grep -i "$expect" > /dev/null 2>&1
240			if (( $? == 0 )); then
241				ret=0
242			else
243				print -u2 $($out)
244				_printerror "$@" "unexpectedly exited $status"
245			fi
246		else
247			ret=0
248		fi
249
250		if (( $ret == 0 )); then
251			[[ -n $LOGAPI_DEBUG ]] && cat $logfile
252			_printsuccess "$@" "exited $status"
253		fi
254	fi
255	_recursive_output $logfile "false"
256	return $ret
257}
258
259# Execute and print command with status where success equals zero result
260#
261# $@ command to execute
262#
263# return command exit status
264
265function log_pos
266{
267	typeset out=""
268	typeset logfile="/tmp/log.$$"
269
270	while [[ -e $logfile ]]; do
271		logfile="$logfile.$$"
272	done
273
274	"$@" 2>$logfile
275	typeset status=$?
276	out="cat $logfile"
277
278	if (( $status != 0 )) ; then
279		print -u2 $($out)
280		_printerror "$@" "exited $status"
281	else
282		$out | egrep -i "internal error|assertion failed" \
283			> /dev/null 2>&1
284		# internal error or assertion failed
285		if [[ $? -eq 0 ]]; then
286			print -u2 $($out)
287			_printerror "$@" "internal error or assertion failure" \
288				" exited $status"
289			status=1
290		else
291			[[ -n $LOGAPI_DEBUG ]] && cat $logfile
292			_printsuccess "$@"
293		fi
294	fi
295	_recursive_output $logfile "false"
296	return $status
297}
298
299# Execute and print command with status where success equals zero result
300# and no stderr output
301#
302# $@ command to execute
303#
304# return 0 if command succeeds and no stderr output
305# return 1 othersie
306
307function log_pos_nostderr
308{
309	typeset out=""
310	typeset logfile="/tmp/log.$$"
311
312	while [[ -e $logfile ]]; do
313		logfile="$logfile.$$"
314	done
315
316	"$@" 2>$logfile
317	typeset status=$?
318	out="cat $logfile"
319	typeset out_msg=$($out)
320
321	if (( $status != 0 )) ; then
322		print -u2 $out_msg
323		_printerror "$@" "exited $status"
324	else
325		if [[ ! -z "$out_msg" ]]; then
326			print -u2 $out_msg
327			_printerror "$@" "message in stderr" \
328				" exited $status"
329			status=1
330		else
331			[[ -n $LOGAPI_DEBUG ]] && cat $logfile
332			_printsuccess "$@"
333		fi
334	fi
335	_recursive_output $logfile "false"
336	return $status
337}
338
339# Set an exit handler
340#
341# $@ - function(s) to perform on exit
342
343function log_onexit
344{
345	_CLEANUP=("$*")
346}
347
348# Push an exit handler on the cleanup stack
349#
350# $@ - function(s) to perform on exit
351
352function log_onexit_push
353{
354	_CLEANUP+=("$*")
355}
356
357# Pop an exit handler off the cleanup stack
358
359function log_onexit_pop
360{
361	_CLEANUP=("${_CLEANUP[@]:0:${#_CLEANUP[@]}-1}")
362}
363
364#
365# Exit functions
366#
367
368# Perform cleanup and exit $STF_PASS
369#
370# $@ - message text
371
372function log_pass
373{
374	_endlog $STF_PASS "$@"
375}
376
377# Perform cleanup and exit $STF_FAIL
378#
379# $@ - message text
380
381function log_fail
382{
383	_endlog $STF_FAIL "$@"
384}
385
386# Perform cleanup and exit $STF_UNRESOLVED
387#
388# $@ - message text
389
390function log_unresolved
391{
392	_endlog $STF_UNRESOLVED "$@"
393}
394
395# Perform cleanup and exit $STF_NOTINUSE
396#
397# $@ - message text
398
399function log_notinuse
400{
401	_endlog $STF_NOTINUSE "$@"
402}
403
404# Perform cleanup and exit $STF_UNSUPPORTED
405#
406# $@ - message text
407
408function log_unsupported
409{
410	_endlog $STF_UNSUPPORTED "$@"
411}
412
413# Perform cleanup and exit $STF_UNTESTED
414#
415# $@ - message text
416
417function log_untested
418{
419	_endlog $STF_UNTESTED "$@"
420}
421
422# Perform cleanup and exit $STF_UNINITIATED
423#
424# $@ - message text
425
426function log_uninitiated
427{
428	_endlog $STF_UNINITIATED "$@"
429}
430
431# Perform cleanup and exit $STF_NORESULT
432#
433# $@ - message text
434
435function log_noresult
436{
437	_endlog $STF_NORESULT "$@"
438}
439
440# Perform cleanup and exit $STF_WARNING
441#
442# $@ - message text
443
444function log_warning
445{
446	_endlog $STF_WARNING "$@"
447}
448
449# Perform cleanup and exit $STF_TIMED_OUT
450#
451# $@ - message text
452
453function log_timed_out
454{
455	_endlog $STF_TIMED_OUT "$@"
456}
457
458# Perform cleanup and exit $STF_OTHER
459#
460# $@ - message text
461
462function log_other
463{
464	_endlog $STF_OTHER "$@"
465}
466
467function set_main_pid
468{
469	_MAINPID=$1
470}
471
472#
473# Internal functions
474#
475
476# Execute custom callback scripts on test failure
477#
478# callback script paths are stored in TESTFAIL_CALLBACKS, delimited by ':'.
479
480function _execute_testfail_callbacks
481{
482	typeset callback
483
484	print "$TESTFAIL_CALLBACKS:" | while read -d ":" callback; do
485		if [[ -n "$callback" ]] ; then
486			log_note "Performing test-fail callback ($callback)"
487			$callback
488		fi
489	done
490}
491
492# Perform cleanup and exit
493#
494# $1 - stf exit code
495# $2-$n - message text
496
497function _endlog
498{
499	typeset logfile="/tmp/log.$$"
500	_recursive_output $logfile
501
502	typeset exitcode=$1
503	shift
504	(( ${#@} > 0 )) && _printline "$@"
505
506	#
507	# If we're running in a subshell then just exit and let
508	# the parent handle the failures
509	#
510	if [[ -n "$_MAINPID" && $$ != "$_MAINPID" ]]; then
511		log_note "subshell exited: "$_MAINPID
512		exit $exitcode
513	fi
514
515	if [[ $exitcode == $STF_FAIL ]] ; then
516		_execute_testfail_callbacks
517	fi
518
519	typeset stack=("${_CLEANUP[@]}")
520	log_onexit ""
521	typeset i=${#stack[@]}
522	while (( i-- )); do
523		typeset cleanup="${stack[i]}"
524		log_note "Performing local cleanup via log_onexit ($cleanup)"
525		$cleanup
526	done
527
528	exit $exitcode
529}
530
531# Output a formatted line
532#
533# $@ - message text
534
535function _printline
536{
537	print "$@"
538}
539
540# Output an error message
541#
542# $@ - message text
543
544function _printerror
545{
546	_printline ERROR: "$@"
547}
548
549# Output a success message
550#
551# $@ - message text
552
553function _printsuccess
554{
555	_printline SUCCESS: "$@"
556}
557
558# Output logfiles recursively
559#
560# $1 - start file
561# $2 - indicate whether output the start file itself, default as yes.
562
563function _recursive_output #logfile
564{
565	typeset logfile=$1
566
567	while [[ -e $logfile ]]; do
568		if [[ -z $2 || $logfile != $1 ]]; then
569			cat $logfile
570		fi
571		rm -f $logfile
572		logfile="$logfile.$$"
573	done
574}
575