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