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