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