libatf-sh.subr revision 1.1.1.2
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_Machine=$(atf-config -t atf_machine) 37 38# The list of all test cases defined by the test program. 39Defined_Test_Cases= 40 41# Values for the expect property. 42Expect=pass 43Expect_Reason= 44 45# A boolean variable that indicates whether we are parsing a test case's 46# head or not. 47Parsing_Head=false 48 49# The program name. 50Prog_Name=${0##*/} 51 52# The file to which the test case will print its result. 53Results_File= 54 55# The test program's source directory: i.e. where its auxiliary data files 56# and helper utilities can be found. Can be overriden through the '-s' flag. 57Source_Dir="$(dirname ${0})" 58 59# Indicates the test case we are currently processing. 60Test_Case= 61 62# List of meta-data variables for the current test case. 63Test_Case_Vars= 64 65# The list of all test cases provided by the test program. 66# Subset of ${Defined_Test_Cases}. 67Test_Cases= 68Test_Cases_With_Cleanup= 69 70# ------------------------------------------------------------------------ 71# PUBLIC INTERFACE 72# ------------------------------------------------------------------------ 73 74# 75# atf_add_test_case tc-name 76# 77# Adds the given test case to the list of test cases that form the test 78# program. The name provided here must be accompanied by two functions 79# named after it: <tc-name>_head and <tc-name>_body, and optionally by 80# a <tc-name>_cleanup function. 81# 82atf_add_test_case() 83{ 84 _atf_is_tc_defined "${1}" || \ 85 _atf_error 128 "Test case ${1} was not correctly defined by" \ 86 "this test program" 87 Test_Cases="${Test_Cases} ${1}" 88} 89 90# 91# atf_check cmd expcode expout experr 92# 93# Executes atf-check with given arguments and automatically calls 94# atf_fail in case of failure. 95# 96atf_check() 97{ 98 atf-check "${@}" || \ 99 atf_fail "atf-check failed; see the output of the test for details" 100} 101 102# 103# atf_check_equal expr1 expr2 104# 105# Checks that expr1's value matches expr2's and, if not, raises an 106# error. Ideally expr1 and expr2 should be provided quoted (not 107# expanded) so that the error message is helpful; otherwise it will 108# only show the values, not the expressions themselves. 109# 110atf_check_equal() 111{ 112 eval _val1=\"${1}\" 113 eval _val2=\"${2}\" 114 test "${_val1}" = "${_val2}" || \ 115 atf_fail "${1} != ${2} (${_val1} != ${_val2})" 116} 117 118# 119# atf_config_get varname [defvalue] 120# 121# Prints the value of a configuration variable. If it is not 122# defined, prints the given default value. 123# 124atf_config_get() 125{ 126 _varname="__tc_config_var_$(_atf_normalize ${1})" 127 if [ ${#} -eq 1 ]; then 128 eval _value=\"\${${_varname}-__unset__}\" 129 [ "${_value}" = __unset__ ] && \ 130 _atf_error 1 "Could not find configuration variable \`${1}'" 131 echo ${_value} 132 elif [ ${#} -eq 2 ]; then 133 eval echo \${${_varname}-${2}} 134 else 135 _atf_error 1 "Incorrect number of parameters for atf_config_get" 136 fi 137} 138 139# 140# atf_config_has varname 141# 142# Returns a boolean indicating if the given configuration variable is 143# defined or not. 144# 145atf_config_has() 146{ 147 _varname="__tc_config_var_$(_atf_normalize ${1})" 148 eval _value=\"\${${_varname}-__unset__}\" 149 [ "${_value}" != __unset__ ] 150} 151 152# 153# atf_expect_death reason 154# 155# Sets the expectations to 'death'. 156# 157atf_expect_death() 158{ 159 _atf_validate_expect 160 161 Expect=death 162 _atf_create_resfile expected_death -1 "${@}" 163} 164 165# 166# atf_expect_timeout reason 167# 168# Sets the expectations to 'timeout'. 169# 170atf_expect_timeout() 171{ 172 _atf_validate_expect 173 174 Expect=timeout 175 _atf_create_resfile expected_timeout -1 "${@}" 176} 177 178# 179# atf_expect_exit exitcode reason 180# 181# Sets the expectations to 'exit'. 182# 183atf_expect_exit() 184{ 185 _exitcode="${1}"; shift 186 187 _atf_validate_expect 188 189 Expect=exit 190 _atf_create_resfile expected_exit "${_exitcode}" "${@}" 191} 192 193# 194# atf_expect_fail reason 195# 196# Sets the expectations to 'fail'. 197# 198atf_expect_fail() 199{ 200 _atf_validate_expect 201 202 Expect=fail 203 Expect_Reason="${*}" 204} 205 206# 207# atf_expect_pass 208# 209# Sets the expectations to 'pass'. 210# 211atf_expect_pass() 212{ 213 _atf_validate_expect 214 215 Expect=pass 216 Expect_Reason= 217} 218 219# 220# atf_expect_signal signo reason 221# 222# Sets the expectations to 'signal'. 223# 224atf_expect_signal() 225{ 226 _signo="${1}"; shift 227 228 _atf_validate_expect 229 230 Expect=signal 231 _atf_create_resfile expected_signal "${_signo}" "${@}" 232} 233 234# 235# atf_expected_failure msg1 [.. msgN] 236# 237# Makes the test case report an expected failure with the given error 238# message. Multiple words can be provided, which are concatenated with 239# a single blank space. 240# 241atf_expected_failure() 242{ 243 _atf_create_resfile expected_failure -1 "${Expect_Reason}:" "${@}" 244 exit 0 245} 246 247# 248# atf_fail msg1 [.. msgN] 249# 250# Makes the test case fail with the given error message. Multiple 251# words can be provided, in which case they are joined by a single 252# blank space. 253# 254atf_fail() 255{ 256 case "${Expect}" in 257 fail) 258 atf_expected_failure "${@}" 259 ;; 260 pass) 261 _atf_create_resfile failed -1 "${@}" 262 exit 1 263 ;; 264 *) 265 _atf_error 128 "Unreachable" 266 ;; 267 esac 268} 269 270# 271# atf_get varname 272# 273# Prints the value of a test case-specific variable. Given that one 274# should not get the value of non-existent variables, it is fine to 275# always use this function as 'val=$(atf_get var)'. 276# 277atf_get() 278{ 279 eval echo \${__tc_var_${Test_Case}_$(_atf_normalize ${1})} 280} 281 282# 283# atf_get_srcdir 284# 285# Prints the value of the test case's source directory. 286# 287atf_get_srcdir() 288{ 289 _atf_internal_get srcdir 290} 291 292# 293# atf_init_test_cases 294# 295# The function in charge of registering the test cases that have to 296# be made available to the user. Must be redefined. 297# 298atf_init_test_cases() 299{ 300 _atf_error 128 "No test cases defined" 301} 302 303# 304# atf_pass 305# 306# Makes the test case pass. Shouldn't be used in general, as a test 307# case that does not explicitly fail is assumed to pass. 308# 309atf_pass() 310{ 311 case "${Expect}" in 312 fail) 313 Expect=pass 314 atf_fail "Test case was expecting a failure but got a pass instead" 315 ;; 316 pass) 317 _atf_create_resfile passed -1 318 exit 0 319 ;; 320 *) 321 _atf_error 128 "Unreachable" 322 ;; 323 esac 324} 325 326# 327# atf_require_prog prog 328# 329# Checks that the given program name (either provided as an absolute 330# path or as a plain file name) can be found. If it is not available, 331# automatically skips the test case with an appropriate message. 332# 333# Relative paths are not allowed because the test case cannot predict 334# where it will be executed from. 335# 336atf_require_prog() 337{ 338 _prog= 339 case ${1} in 340 /*) 341 _prog="${1}" 342 [ -x ${_prog} ] || \ 343 atf_skip "The required program ${1} could not be found" 344 ;; 345 */*) 346 atf_fail "atf_require_prog does not accept relative path names \`${1}'" 347 ;; 348 *) 349 _prog=$(_atf_find_in_path "${1}") 350 [ -n "${_prog}" ] || \ 351 atf_skip "The required program ${1} could not be found" \ 352 "in the PATH" 353 ;; 354 esac 355} 356 357# 358# atf_set varname val1 [.. valN] 359# 360# Sets the test case's variable 'varname' to the specified values 361# which are concatenated using a single blank space. This function 362# is supposed to be called form the test case's head only. 363# 364atf_set() 365{ 366 ${Parsing_Head} || \ 367 _atf_error 128 "atf_set called from the test case's body" 368 369 Test_Case_Vars="${Test_Case_Vars} ${1}" 370 _var=$(_atf_normalize ${1}); shift 371 eval __tc_var_${Test_Case}_${_var}=\"\${*}\" 372} 373 374# 375# atf_skip msg1 [.. msgN] 376# 377# Skips the test case because of the reason provided. Multiple words 378# can be given, in which case they are joined by a single blank space. 379# 380atf_skip() 381{ 382 _atf_create_resfile skipped -1 "${@}" 383 exit 0 384} 385 386# 387# atf_test_case tc-name cleanup 388# 389# Defines a new test case named tc-name. The name provided here must be 390# accompanied by two functions named after it: <tc-name>_head and 391# <tc-name>_body. If cleanup is set to 'cleanup', then this also expects 392# a <tc-name>_cleanup function to be defined. 393# 394atf_test_case() 395{ 396 Defined_Test_Cases="${Defined_Test_Cases} ${1}" 397 398 eval "${1}_head() { :; }" 399 eval "${1}_body() { :; }" 400 if [ "${2}" = cleanup ]; then 401 Test_Cases_With_Cleanup="${Test_Cases_With_Cleanup} ${1}" 402 eval "${1}_cleanup() { :; }" 403 else 404 eval "${1}_cleanup() { 405 _atf_error 1 'Test case ${1} declared without a cleanup routine'; }" 406 fi 407} 408 409# ------------------------------------------------------------------------ 410# PRIVATE INTERFACE 411# ------------------------------------------------------------------------ 412 413# 414# _atf_config_set varname val1 [.. valN] 415# 416# Sets the test case's private variable 'varname' to the specified 417# values which are concatenated using a single blank space. 418# 419_atf_config_set() 420{ 421 _var=$(_atf_normalize ${1}); shift 422 eval __tc_config_var_${_var}=\"\${*}\" 423 Config_Vars="${Config_Vars} __tc_config_var_${_var}" 424} 425 426# 427# _atf_config_set_str varname=val 428# 429# Sets the test case's private variable 'varname' to the specified 430# value. The parameter is of the form 'varname=val'. 431# 432_atf_config_set_from_str() 433{ 434 _oldifs=${IFS} 435 IFS='=' 436 set -- ${*} 437 _var=${1} 438 shift 439 _val="${@}" 440 IFS=${_oldifs} 441 _atf_config_set "${_var}" "${_val}" 442} 443 444# 445# _atf_create_resfile result arg [reason ...] 446# 447# Creates the results file. 448# 449_atf_create_resfile() 450{ 451 _result="${1}"; shift 452 if [ "${1}" -eq -1 ]; then 453 _arg="" 454 shift 455 else 456 _arg="(${1})" 457 shift 458 fi 459 if [ ${#} -gt 0 ]; then 460 _reason=": ${*}" 461 else 462 _reason="" 463 fi 464 465 if [ -n "${Results_File}" ]; then 466 echo "${_result}${_arg}${_reason}" >"${Results_File}" || \ 467 _atf_error 128 "Cannot create results file '${Results_File}'" 468 else 469 echo "${_result}${_arg}${_reason}" 470 fi 471} 472 473# 474# _atf_ensure_boolean var 475# 476# Ensures that the test case defined the variable 'var' to a boolean 477# value. 478# 479_atf_ensure_boolean() 480{ 481 _atf_ensure_not_empty ${1} 482 483 case $(atf_get ${1}) in 484 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) 485 atf_set ${1} true 486 ;; 487 [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]) 488 atf_set ${1} false 489 ;; 490 *) 491 _atf_error 128 "Invalid value for boolean variable \`${1}'" 492 ;; 493 esac 494} 495 496# 497# _atf_ensure_not_empty var 498# 499# Ensures that the test case defined the variable 'var' to a non-empty 500# value. 501# 502_atf_ensure_not_empty() 503{ 504 [ -n "$(atf_get ${1})" ] || \ 505 _atf_error 128 "Undefined or empty variable \`${1}'" 506} 507 508# 509# _atf_error error_code [msg1 [.. msgN]] 510# 511# Prints the given error message (which can be composed of multiple 512# arguments, in which case are joined by a single space) and exits 513# with the specified error code. 514# 515# This must not be used by test programs themselves (hence making 516# the function private) to indicate a test case's failure. They 517# have to use the atf_fail function. 518# 519_atf_error() 520{ 521 _error_code="${1}"; shift 522 523 echo "${Prog_Name}: ERROR:" "$@" 1>&2 524 exit ${_error_code} 525} 526 527# 528# _atf_find_in_path program 529# 530# Looks for a program in the path and prints the full path to it or 531# nothing if it could not be found. It also returns true in case of 532# success. 533# 534_atf_find_in_path() 535{ 536 _prog="${1}" 537 538 _oldifs=${IFS} 539 IFS=: 540 for _dir in ${PATH} 541 do 542 if [ -x ${_dir}/${_prog} ]; then 543 IFS=${_oldifs} 544 echo ${_dir}/${_prog} 545 return 0 546 fi 547 done 548 IFS=${_oldifs} 549 550 return 1 551} 552 553# 554# _atf_has_tc name 555# 556# Returns true if the given test case exists. 557# 558_atf_has_tc() 559{ 560 for _tc in ${Test_Cases}; do 561 if [ ${_tc} = ${1} ]; then 562 return 0 563 fi 564 done 565 return 1 566} 567 568# 569# _atf_get_bool varname 570# 571# Evaluates a test case-specific variable as a boolean and returns its 572# value. 573# 574_atf_get_bool() 575{ 576 eval $(atf_get ${1}) 577} 578 579# 580# _atf_internal_get varname 581# 582# Prints the value of a test case-specific internal variable. Given 583# that one should not get the value of non-existent variables, it is 584# fine to always use this function as 'val=$(_atf_internal_get var)'. 585# 586_atf_internal_get() 587{ 588 eval echo \${__tc_internal_var_${Test_Case}_${1}} 589} 590 591# 592# _atf_internal_set varname val1 [.. valN] 593# 594# Sets the test case's private variable 'varname' to the specified 595# values which are concatenated using a single blank space. 596# 597_atf_internal_set() 598{ 599 _var=${1}; shift 600 eval __tc_internal_var_${Test_Case}_${_var}=\"\${*}\" 601} 602 603# 604# _atf_list_tcs 605# 606# Describes all test cases and prints the list to the standard output. 607# 608_atf_list_tcs() 609{ 610 echo 'Content-Type: application/X-atf-tp; version="1"' 611 echo 612 613 set -- ${Test_Cases} 614 while [ ${#} -gt 0 ]; do 615 _atf_parse_head ${1} 616 617 echo "ident: $(atf_get ident)" 618 for _var in ${Test_Case_Vars}; do 619 [ "${_var}" != "ident" ] && echo "${_var}: $(atf_get ${_var})" 620 done 621 622 [ ${#} -gt 1 ] && echo 623 shift 624 done 625} 626 627# 628# _atf_normalize str 629# 630# Normalizes a string so that it is a valid shell variable name. 631# 632_atf_normalize() 633{ 634 echo ${1} | tr .- __ 635} 636 637# 638# _atf_parse_head tcname 639# 640# Evaluates a test case's head to gather its variables and prepares the 641# test program to run it. 642# 643_atf_parse_head() 644{ 645 ${Parsing_Head} && _atf_error 128 "_atf_parse_head called recursively" 646 Parsing_Head=true 647 648 Test_Case="${1}" 649 Test_Case_Vars= 650 651 if _atf_has_cleanup "${1}"; then 652 atf_set has.cleanup "true" 653 fi 654 655 atf_set ident "${1}" 656 ${1}_head 657 _atf_ensure_not_empty ident 658 test $(atf_get ident) = "${1}" || \ 659 _atf_error 128 "Test case redefined ident" 660 661 Parsing_Head=false 662} 663 664# 665# _atf_run_tc tc 666# 667# Runs the specified test case. Prints its exit status to the 668# standard output and returns a boolean indicating if the test was 669# successful or not. 670# 671_atf_run_tc() 672{ 673 case ${1} in 674 *:*) 675 _tcname=${1%%:*} 676 _tcpart=${1#*:} 677 678 if [ "${_tcpart}" != body -a "${_tcpart}" != cleanup ]; then 679 _atf_syntax_error "Unknown test case part \`${_tcpart}'" 680 fi 681 ;; 682 683 *) 684 _tcname=${1} 685 _tcpart=body 686 ;; 687 esac 688 689 if _atf_has_tc ${_tcname}; then 690 _atf_parse_head ${_tcname} 691 692 _atf_internal_set srcdir "${Source_Dir}" 693 694 case ${_tcpart} in 695 body) 696 ${_tcname}_body 697 _atf_validate_expect 698 _atf_create_resfile passed -1 699 ;; 700 cleanup) 701 if _atf_has_cleanup "${_tcname}"; then 702 ${_tcname}_cleanup 703 fi 704 ;; 705 *) 706 _atf_error 128 "Unknown test case part" 707 ;; 708 esac 709 else 710 _atf_syntax_error "Unknown test case \`${1}'" 711 fi 712} 713 714# 715# _atf_sighup_handler 716# 717# Handler for the SIGHUP signal that registers its occurrence so that 718# it can be processed at a later stage. 719# 720_atf_sighup_handler() 721{ 722 Held_Signals="${Held_Signals} SIGHUP" 723} 724 725# 726# _atf_sigint_handler 727# 728# Handler for the SIGINT signal that registers its occurrence so that 729# it can be processed at a later stage. 730# 731_atf_sigint_handler() 732{ 733 Held_Signals="${Held_Signals} SIGINT" 734} 735 736# 737# _atf_sigterm_handler 738# 739# Handler for the SIGTERM signal that registers its occurrence so that 740# it can be processed at a later stage. 741# 742_atf_sigterm_handler() 743{ 744 Held_Signals="${Held_Signals} SIGTERM" 745} 746 747# 748# _atf_syntax_error msg1 [.. msgN] 749# 750# Formats and prints a syntax error message and terminates the 751# program prematurely. 752# 753_atf_syntax_error() 754{ 755 echo "${Prog_Name}: ERROR: ${@}" 1>&2 756 echo "${Prog_Name}: See atf-test-program(1) for usage details." 1>&2 757 exit 1 758} 759 760# 761# _atf_is_tc_defined tc-name 762# 763# Returns a boolean indicating if the given test case was defined by the 764# test program or not. 765# 766_atf_is_tc_defined() 767{ 768 for _tc in ${Defined_Test_Cases}; do 769 [ ${_tc} = ${1} ] && return 0 770 done 771 return 1 772} 773 774# 775# _atf_has_cleanup tc-name 776# 777# Returns a boolean indicating if the given test case has a cleanup 778# routine or not. 779# 780_atf_has_cleanup() 781{ 782 for _tc in ${Test_Cases_With_Cleanup}; do 783 [ ${_tc} = ${1} ] && return 0 784 done 785 return 1 786} 787 788# 789# _atf_validate_expect 790# 791# Ensures that the current test case state is correct regarding the expect 792# status. 793# 794_atf_validate_expect() 795{ 796 case "${Expect}" in 797 death) 798 Expect=pass 799 atf_fail "Test case was expected to terminate abruptly but it" \ 800 "continued execution" 801 ;; 802 exit) 803 Expect=pass 804 atf_fail "Test case was expected to exit cleanly but it continued" \ 805 "execution" 806 ;; 807 fail) 808 Expect=pass 809 atf_fail "Test case was expecting a failure but none were raised" 810 ;; 811 pass) 812 ;; 813 signal) 814 Expect=pass 815 atf_fail "Test case was expected to receive a termination signal" \ 816 "but it continued execution" 817 ;; 818 timeout) 819 Expect=pass 820 atf_fail "Test case was expected to hang but it continued execution" 821 ;; 822 *) 823 _atf_error 128 "Unreachable" 824 ;; 825 esac 826} 827 828# 829# _atf_warning [msg1 [.. msgN]] 830# 831# Prints the given warning message (which can be composed of multiple 832# arguments, in which case are joined by a single space). 833# 834# This must not be used by test programs themselves (hence making 835# the function private). 836# 837_atf_warning() 838{ 839 echo "${Prog_Name}: WARNING:" "$@" 1>&2 840} 841 842# 843# main [options] test_case 844# 845# Test program's entry point. 846# 847main() 848{ 849 # Process command-line options first. 850 _numargs=${#} 851 _lflag=false 852 while getopts :lr:s:v: arg; do 853 case ${arg} in 854 l) 855 _lflag=true 856 ;; 857 858 r) 859 Results_File=${OPTARG} 860 ;; 861 862 s) 863 Source_Dir=${OPTARG} 864 ;; 865 866 v) 867 _atf_config_set_from_str "${OPTARG}" 868 ;; 869 870 \?) 871 _atf_syntax_error "Unknown option -${OPTARG}." 872 # NOTREACHED 873 ;; 874 esac 875 done 876 shift `expr ${OPTIND} - 1` 877 878 # First of all, make sure that the source directory is correct. It 879 # doesn't matter if the user did not change it, because the default 880 # value may not work. (TODO: It possibly should, even though it is 881 # not a big deal because atf-run deals with this.) 882 case ${Source_Dir} in 883 /*) 884 ;; 885 *) 886 Source_Dir=$(pwd)/${Source_Dir} 887 ;; 888 esac 889 [ -f ${Source_Dir}/${Prog_Name} ] || \ 890 _atf_error 1 "Cannot find the test program in the source" \ 891 "directory \`${Source_Dir}'" 892 893 # Set some global variables useful to the user. Not specific to the 894 # test case because they may be needed during initialization too. 895 # XXX I'm not too fond on this though. Sure, it is very useful in some 896 # situations -- such as in NetBSD's fs/tmpfs/* tests where each test 897 # program includes a helper subroutines file -- but there are also 898 # other, maybe better ways to achieve the same. Because, for example, 899 # at the moment it is not possible to detect failures in the inclusion 900 # and report them nicely. Plus this change is difficult to implement 901 # in the current C++ API. 902 _atf_internal_set srcdir "${Source_Dir}" 903 904 # Call the test program's hook to register all available test cases. 905 atf_init_test_cases 906 907 # Run or list test cases. 908 if `${_lflag}`; then 909 if [ ${#} -gt 0 ]; then 910 _atf_syntax_error "Cannot provide test case names with -l" 911 fi 912 _atf_list_tcs 913 else 914 if [ ${#} -eq 0 ]; then 915 _atf_syntax_error "Must provide a test case name" 916 elif [ ${#} -gt 1 ]; then 917 _atf_syntax_error "Cannot provide more than one test case name" 918 else 919 _atf_run_tc "${1}" 920 fi 921 fi 922} 923 924# vim: syntax=sh:expandtab:shiftwidth=4:softtabstop=4 925