1# Copyright (c) 2007 The NetBSD Foundation, Inc. 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions 6# are met: 7# 1. Redistributions of source code must retain the above copyright 8# notice, this list of conditions and the following disclaimer. 9# 2. Redistributions in binary form must reproduce the above copyright 10# notice, this list of conditions and the following disclaimer in the 11# documentation and/or other materials provided with the distribution. 12# 13# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND 14# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 15# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 16# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17# IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY 18# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 20# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 22# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 24# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 26# ------------------------------------------------------------------------ 27# GLOBAL VARIABLES 28# ------------------------------------------------------------------------ 29 30# Values for the expect property. 31Expect=pass 32Expect_Reason= 33 34# A boolean variable that indicates whether we are parsing a test case's 35# head or not. 36Parsing_Head=false 37 38# The program name. 39Prog_Name=${0##*/} 40 41# The file to which the test case will print its result. 42Results_File= 43 44# The test program's source directory: i.e. where its auxiliary data files 45# and helper utilities can be found. Can be overriden through the '-s' flag. 46Source_Dir="$(dirname ${0})" 47 48# Indicates the test case we are currently processing. 49Test_Case= 50 51# List of meta-data variables for the current test case. 52Test_Case_Vars= 53 54# The list of all test cases provided by the test program. 55Test_Cases= 56 57# ------------------------------------------------------------------------ 58# PUBLIC INTERFACE 59# ------------------------------------------------------------------------ 60 61# 62# atf_add_test_case tc-name 63# 64# Adds the given test case to the list of test cases that form the test 65# program. The name provided here must be accompanied by two functions 66# named after it: <tc-name>_head and <tc-name>_body, and optionally by 67# a <tc-name>_cleanup function. 68# 69atf_add_test_case() 70{ 71 Test_Cases="${Test_Cases} ${1}" 72} 73 74# 75# atf_check cmd expcode expout experr 76# 77# Executes atf-check with given arguments and automatically calls 78# atf_fail in case of failure. 79# 80atf_check() 81{ 82 ${Atf_Check} "${@}" || \ 83 atf_fail "atf-check failed; see the output of the test for details" 84} 85 86# 87# atf_check_equal expected_expression actual_expression 88# 89# Checks that expected_expression's value matches actual_expression's 90# and, if not, raises an error. Ideally expected_expression and 91# actual_expression should be provided quoted (not expanded) so that 92# the error message is helpful; otherwise it will only show the values, 93# not the expressions themselves. 94# 95atf_check_equal() 96{ 97 eval _val1=\"${1}\" 98 eval _val2=\"${2}\" 99 test "${_val1}" = "${_val2}" || \ 100 atf_fail "${1} != ${2} (${_val1} != ${_val2})" 101} 102 103# 104# atf_check_not_equal expected_expression actual_expression 105# 106# Checks that expected_expression's value does not match actual_expression's 107# and, if it does, raises an error. Ideally expected_expression and 108# actual_expression should be provided quoted (not expanded) so that 109# the error message is helpful; otherwise it will only show the values, 110# not the expressions themselves. 111# 112atf_check_not_equal() 113{ 114 eval _val1=\"${1}\" 115 eval _val2=\"${2}\" 116 test "${_val1}" != "${_val2}" || \ 117 atf_fail "${1} == ${2} (${_val1} == ${_val2})" 118} 119 120# 121# atf_config_get varname [defvalue] 122# 123# Prints the value of a configuration variable. If it is not 124# defined, prints the given default value. 125# 126atf_config_get() 127{ 128 _varname="__tc_config_var_$(_atf_normalize ${1})" 129 if [ ${#} -eq 1 ]; then 130 eval _value=\"\${${_varname}-__unset__}\" 131 [ "${_value}" = __unset__ ] && \ 132 _atf_error 1 "Could not find configuration variable \`${1}'" 133 echo ${_value} 134 elif [ ${#} -eq 2 ]; then 135 eval echo \${${_varname}-${2}} 136 else 137 _atf_error 1 "Incorrect number of parameters for atf_config_get" 138 fi 139} 140 141# 142# atf_config_has varname 143# 144# Returns a boolean indicating if the given configuration variable is 145# defined or not. 146# 147atf_config_has() 148{ 149 _varname="__tc_config_var_$(_atf_normalize ${1})" 150 eval _value=\"\${${_varname}-__unset__}\" 151 [ "${_value}" != __unset__ ] 152} 153 154# 155# atf_expect_death reason 156# 157# Sets the expectations to 'death'. 158# 159atf_expect_death() 160{ 161 _atf_validate_expect 162 163 Expect=death 164 _atf_create_resfile "expected_death: ${*}" 165} 166 167# 168# atf_expect_timeout reason 169# 170# Sets the expectations to 'timeout'. 171# 172atf_expect_timeout() 173{ 174 _atf_validate_expect 175 176 Expect=timeout 177 _atf_create_resfile "expected_timeout: ${*}" 178} 179 180# 181# atf_expect_exit exitcode reason 182# 183# Sets the expectations to 'exit'. 184# 185atf_expect_exit() 186{ 187 _exitcode="${1}"; shift 188 189 _atf_validate_expect 190 191 Expect=exit 192 if [ "${_exitcode}" = "-1" ]; then 193 _atf_create_resfile "expected_exit: ${*}" 194 else 195 _atf_create_resfile "expected_exit(${_exitcode}): ${*}" 196 fi 197} 198 199# 200# atf_expect_fail reason 201# 202# Sets the expectations to 'fail'. 203# 204atf_expect_fail() 205{ 206 _atf_validate_expect 207 208 Expect=fail 209 Expect_Reason="${*}" 210} 211 212# 213# atf_expect_pass 214# 215# Sets the expectations to 'pass'. 216# 217atf_expect_pass() 218{ 219 _atf_validate_expect 220 221 Expect=pass 222 Expect_Reason= 223} 224 225# 226# atf_expect_signal signo reason 227# 228# Sets the expectations to 'signal'. 229# 230atf_expect_signal() 231{ 232 _signo="${1}"; shift 233 234 _atf_validate_expect 235 236 Expect=signal 237 if [ "${_signo}" = "-1" ]; then 238 _atf_create_resfile "expected_signal: ${*}" 239 else 240 _atf_create_resfile "expected_signal(${_signo}): ${*}" 241 fi 242} 243 244# 245# atf_expected_failure msg1 [.. msgN] 246# 247# Makes the test case report an expected failure with the given error 248# message. Multiple words can be provided, which are concatenated with 249# a single blank space. 250# 251atf_expected_failure() 252{ 253 _atf_create_resfile "expected_failure: ${Expect_Reason}: ${*}" 254 exit 0 255} 256 257# 258# atf_fail msg1 [.. msgN] 259# 260# Makes the test case fail with the given error message. Multiple 261# words can be provided, in which case they are joined by a single 262# blank space. 263# 264atf_fail() 265{ 266 case "${Expect}" in 267 fail) 268 atf_expected_failure "${@}" 269 ;; 270 pass) 271 _atf_create_resfile "failed: ${*}" 272 exit 1 273 ;; 274 *) 275 _atf_error 128 "Unreachable" 276 ;; 277 esac 278} 279 280# 281# atf_get varname 282# 283# Prints the value of a test case-specific variable. Given that one 284# should not get the value of non-existent variables, it is fine to 285# always use this function as 'val=$(atf_get var)'. 286# 287atf_get() 288{ 289 eval echo \${__tc_var_${Test_Case}_$(_atf_normalize ${1})} 290} 291 292# 293# atf_get_srcdir 294# 295# Prints the value of the test case's source directory. 296# 297atf_get_srcdir() 298{ 299 echo ${Source_Dir} 300} 301 302# 303# atf_pass 304# 305# Makes the test case pass. Shouldn't be used in general, as a test 306# case that does not explicitly fail is assumed to pass. 307# 308atf_pass() 309{ 310 case "${Expect}" in 311 fail) 312 Expect=pass 313 atf_fail "Test case was expecting a failure but got a pass instead" 314 ;; 315 pass) 316 _atf_create_resfile passed 317 exit 0 318 ;; 319 *) 320 _atf_error 128 "Unreachable" 321 ;; 322 esac 323} 324 325# 326# atf_require_prog prog 327# 328# Checks that the given program name (either provided as an absolute 329# path or as a plain file name) can be found. If it is not available, 330# automatically skips the test case with an appropriate message. 331# 332# Relative paths are not allowed because the test case cannot predict 333# where it will be executed from. 334# 335atf_require_prog() 336{ 337 _prog= 338 case ${1} in 339 /*) 340 _prog="${1}" 341 [ -x ${_prog} ] || \ 342 atf_skip "The required program ${1} could not be found" 343 ;; 344 */*) 345 atf_fail "atf_require_prog does not accept relative path names \`${1}'" 346 ;; 347 *) 348 _prog=$(_atf_find_in_path "${1}") 349 [ -n "${_prog}" ] || \ 350 atf_skip "The required program ${1} could not be found" \ 351 "in the PATH" 352 ;; 353 esac 354} 355 356# 357# atf_require_kmod kmod 358# 359# Checks that the given kmod is loaded. If it is not, automatically 360# skips the test case with an appropriate message. 361# 362atf_require_kmod() 363{ 364 kldstat -q "${1}" || \ 365 atf_skip "The required kmod ${1} is not loaded" 366} 367 368# 369# atf_set varname val1 [.. valN] 370# 371# Sets the test case's variable 'varname' to the specified values 372# which are concatenated using a single blank space. This function 373# is supposed to be called from the test case's head only. 374# 375atf_set() 376{ 377 ${Parsing_Head} || \ 378 _atf_error 128 "atf_set called from the test case's body" 379 380 Test_Case_Vars="${Test_Case_Vars} ${1}" 381 _var=$(_atf_normalize ${1}); shift 382 eval __tc_var_${Test_Case}_${_var}=\"\${*}\" 383} 384 385# 386# atf_skip msg1 [.. msgN] 387# 388# Skips the test case because of the reason provided. Multiple words 389# can be given, in which case they are joined by a single blank space. 390# 391atf_skip() 392{ 393 _atf_create_resfile "skipped: ${*}" 394 exit 0 395} 396 397# 398# atf_test_case tc-name cleanup 399# 400# Defines a new test case named tc-name. The name provided here must be 401# accompanied by two functions named after it: <tc-name>_head and 402# <tc-name>_body. If cleanup is set to 'cleanup', then this also expects 403# a <tc-name>_cleanup function to be defined. 404# 405atf_test_case() 406{ 407 eval "${1}_head() { :; }" 408 eval "${1}_body() { atf_fail 'Test case not implemented'; }" 409 if [ "${2}" = cleanup ]; then 410 eval __has_cleanup_${1}=true 411 eval "${1}_cleanup() { :; }" 412 else 413 eval "${1}_cleanup() { 414 _atf_error 1 'Test case ${1} declared without a cleanup routine'; }" 415 fi 416} 417 418# ------------------------------------------------------------------------ 419# PRIVATE INTERFACE 420# ------------------------------------------------------------------------ 421 422# 423# _atf_config_set varname val1 [.. valN] 424# 425# Sets the test case's private variable 'varname' to the specified 426# values which are concatenated using a single blank space. 427# 428_atf_config_set() 429{ 430 _var=$(_atf_normalize ${1}); shift 431 eval __tc_config_var_${_var}=\"\${*}\" 432 Config_Vars="${Config_Vars} __tc_config_var_${_var}" 433} 434 435# 436# _atf_config_set_str varname=val 437# 438# Sets the test case's private variable 'varname' to the specified 439# value. The parameter is of the form 'varname=val'. 440# 441_atf_config_set_from_str() 442{ 443 _oldifs=${IFS} 444 IFS='=' 445 set -- ${*} 446 _var=${1} 447 shift 448 _val="${@}" 449 IFS=${_oldifs} 450 _atf_config_set "${_var}" "${_val}" 451} 452 453# 454# _atf_create_resfile contents 455# 456# Creates the results file. 457# 458_atf_create_resfile() 459{ 460 if [ -n "${Results_File}" ]; then 461 echo "${*}" >"${Results_File}" || \ 462 _atf_error 128 "Cannot create results file '${Results_File}'" 463 else 464 echo "${*}" 465 fi 466} 467 468# 469# _atf_error error_code [msg1 [.. msgN]] 470# 471# Prints the given error message (which can be composed of multiple 472# arguments, in which case are joined by a single space) and exits 473# with the specified error code. 474# 475# This must not be used by test programs themselves (hence making 476# the function private) to indicate a test case's failure. They 477# have to use the atf_fail function. 478# 479_atf_error() 480{ 481 _error_code="${1}"; shift 482 483 echo "${Prog_Name}: ERROR:" "$@" 1>&2 484 exit ${_error_code} 485} 486 487# 488# _atf_warning msg1 [.. msgN] 489# 490# Prints the given warning message (which can be composed of multiple 491# arguments, in which case are joined by a single space). 492# 493_atf_warning() 494{ 495 echo "${Prog_Name}: WARNING:" "$@" 1>&2 496} 497 498# 499# _atf_find_in_path program 500# 501# Looks for a program in the path and prints the full path to it or 502# nothing if it could not be found. It also returns true in case of 503# success. 504# 505_atf_find_in_path() 506{ 507 _prog="${1}" 508 509 _oldifs=${IFS} 510 IFS=: 511 for _dir in ${PATH} 512 do 513 if [ -x ${_dir}/${_prog} ]; then 514 IFS=${_oldifs} 515 echo ${_dir}/${_prog} 516 return 0 517 fi 518 done 519 IFS=${_oldifs} 520 521 return 1 522} 523 524# 525# _atf_has_tc name 526# 527# Returns true if the given test case exists. 528# 529_atf_has_tc() 530{ 531 for _tc in ${Test_Cases}; do 532 [ "${_tc}" != "${1}" ] || return 0 533 done 534 return 1 535} 536 537# 538# _atf_list_tcs 539# 540# Describes all test cases and prints the list to the standard output. 541# 542_atf_list_tcs() 543{ 544 echo 'Content-Type: application/X-atf-tp; version="1"' 545 echo 546 547 set -- ${Test_Cases} 548 while [ ${#} -gt 0 ]; do 549 _atf_parse_head ${1} 550 551 echo "ident: $(atf_get ident)" 552 for _var in ${Test_Case_Vars}; do 553 [ "${_var}" != "ident" ] && echo "${_var}: $(atf_get ${_var})" 554 done 555 556 [ ${#} -gt 1 ] && echo 557 shift 558 done 559} 560 561# 562# _atf_normalize str 563# 564# Normalizes a string so that it is a valid shell variable name. 565# 566_atf_normalize() 567{ 568 # Check if the string contains any of the forbidden characters using 569 # POSIX parameter expansion (the ${var//} string substitution is 570 # unfortunately not supported in POSIX sh) and only use tr(1) then. 571 # tr(1) is generally not a builtin, so doing the substring check first 572 # avoids unnecessary fork()+execve() calls. As this function is called 573 # many times in each test script startup, those overheads add up 574 # (especially when running on emulated platforms such as QEMU). 575 if [ "${1#*[.-]}" != "$1" ]; then 576 echo "$1" | tr .- __ 577 else 578 echo "$1" 579 fi 580} 581 582# 583# _atf_parse_head tcname 584# 585# Evaluates a test case's head to gather its variables and prepares the 586# test program to run it. 587# 588_atf_parse_head() 589{ 590 Parsing_Head=true 591 592 Test_Case="${1}" 593 Test_Case_Vars= 594 595 if _atf_has_cleanup "${1}"; then 596 atf_set has.cleanup "true" 597 fi 598 599 ${1}_head 600 atf_set ident "${1}" 601 602 Parsing_Head=false 603} 604 605# 606# _atf_run_tc tc 607# 608# Runs the specified test case. Prints its exit status to the 609# standard output and returns a boolean indicating if the test was 610# successful or not. 611# 612_atf_run_tc() 613{ 614 case ${1} in 615 *:*) 616 _tcname=${1%%:*} 617 _tcpart=${1#*:} 618 619 if [ "${_tcpart}" != body -a "${_tcpart}" != cleanup ]; then 620 _atf_syntax_error "Unknown test case part \`${_tcpart}'" 621 fi 622 ;; 623 624 *) 625 _tcname=${1} 626 _tcpart=body 627 ;; 628 esac 629 630 _atf_has_tc "${_tcname}" || _atf_syntax_error "Unknown test case \`${1}'" 631 632 if [ "${__RUNNING_INSIDE_ATF_RUN}" != "internal-yes-value" ]; then 633 _atf_warning "Running test cases outside of kyua(1) is unsupported" 634 _atf_warning "No isolation nor timeout control is being applied;" \ 635 "you may get unexpected failures; see atf-test-case(4)" 636 fi 637 638 _atf_parse_head ${_tcname} 639 640 case ${_tcpart} in 641 body) 642 if ${_tcname}_body; then 643 _atf_validate_expect 644 _atf_create_resfile passed 645 else 646 Expect=pass 647 atf_fail "Test case body returned a non-ok exit code, but" \ 648 "this is not allowed" 649 fi 650 ;; 651 cleanup) 652 if _atf_has_cleanup "${_tcname}"; then 653 ${_tcname}_cleanup || _atf_error 128 "The test case cleanup" \ 654 "returned a non-ok exit code, but this is not allowed" 655 fi 656 ;; 657 *) 658 _atf_error 128 "Unknown test case part" 659 ;; 660 esac 661} 662 663# 664# _atf_syntax_error msg1 [.. msgN] 665# 666# Formats and prints a syntax error message and terminates the 667# program prematurely. 668# 669_atf_syntax_error() 670{ 671 echo "${Prog_Name}: ERROR: ${@}" 1>&2 672 echo "${Prog_Name}: See atf-test-program(1) for usage details." 1>&2 673 exit 1 674} 675 676# 677# _atf_has_cleanup tc-name 678# 679# Returns a boolean indicating if the given test case has a cleanup 680# routine or not. 681# 682_atf_has_cleanup() 683{ 684 _found=true 685 eval "[ x\"\${__has_cleanup_${1}}\" = xtrue ] || _found=false" 686 [ "${_found}" = true ] 687} 688 689# 690# _atf_validate_expect 691# 692# Ensures that the current test case state is correct regarding the expect 693# status. 694# 695_atf_validate_expect() 696{ 697 case "${Expect}" in 698 death) 699 Expect=pass 700 atf_fail "Test case was expected to terminate abruptly but it" \ 701 "continued execution" 702 ;; 703 exit) 704 Expect=pass 705 atf_fail "Test case was expected to exit cleanly but it continued" \ 706 "execution" 707 ;; 708 fail) 709 Expect=pass 710 atf_fail "Test case was expecting a failure but none were raised" 711 ;; 712 pass) 713 ;; 714 signal) 715 Expect=pass 716 atf_fail "Test case was expected to receive a termination signal" \ 717 "but it continued execution" 718 ;; 719 timeout) 720 Expect=pass 721 atf_fail "Test case was expected to hang but it continued execution" 722 ;; 723 *) 724 _atf_error 128 "Unreachable" 725 ;; 726 esac 727} 728 729# 730# _atf_warning [msg1 [.. msgN]] 731# 732# Prints the given warning message (which can be composed of multiple 733# arguments, in which case are joined by a single space). 734# 735# This must not be used by test programs themselves (hence making 736# the function private). 737# 738_atf_warning() 739{ 740 echo "${Prog_Name}: WARNING:" "$@" 1>&2 741} 742 743# 744# main [options] test_case 745# 746# Test program's entry point. 747# 748main() 749{ 750 # Process command-line options first. 751 _numargs=${#} 752 _lflag=false 753 while getopts :lr:s:v: arg; do 754 case ${arg} in 755 l) 756 _lflag=true 757 ;; 758 759 r) 760 Results_File=${OPTARG} 761 ;; 762 763 s) 764 Source_Dir=${OPTARG} 765 ;; 766 767 v) 768 _atf_config_set_from_str "${OPTARG}" 769 ;; 770 771 \?) 772 _atf_syntax_error "Unknown option -${OPTARG}." 773 # NOTREACHED 774 ;; 775 esac 776 done 777 shift $((OPTIND - 1)) 778 779 case ${Source_Dir} in 780 /*) 781 ;; 782 *) 783 Source_Dir=$(pwd)/${Source_Dir} 784 ;; 785 esac 786 [ -f ${Source_Dir}/${Prog_Name} ] || \ 787 _atf_error 1 "Cannot find the test program in the source" \ 788 "directory \`${Source_Dir}'" 789 790 # Call the test program's hook to register all available test cases. 791 atf_init_test_cases 792 793 # Run or list test cases. 794 if `${_lflag}`; then 795 if [ ${#} -gt 0 ]; then 796 _atf_syntax_error "Cannot provide test case names with -l" 797 fi 798 _atf_list_tcs 799 else 800 if [ ${#} -eq 0 ]; then 801 _atf_syntax_error "Must provide a test case name" 802 elif [ ${#} -gt 1 ]; then 803 _atf_syntax_error "Cannot provide more than one test case name" 804 else 805 _atf_run_tc "${1}" 806 fi 807 fi 808} 809 810# vim: syntax=sh:expandtab:shiftwidth=4:softtabstop=4 811