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