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