1.\" Copyright (c) 2008 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.Dd May 11, 2025 26.Dt ATF-SH 3 27.Os 28.Sh NAME 29.Nm atf_add_test_case , 30.Nm atf_check , 31.Nm atf_check_equal , 32.Nm atf_check_not_equal , 33.Nm atf_config_get , 34.Nm atf_config_has , 35.Nm atf_expect_death , 36.Nm atf_expect_exit , 37.Nm atf_expect_fail , 38.Nm atf_expect_pass , 39.Nm atf_expect_signal , 40.Nm atf_expect_timeout , 41.Nm atf_fail , 42.Nm atf_get , 43.Nm atf_get_srcdir , 44.Nm atf_init_test_cases , 45.Nm atf_pass , 46.Nm atf_require_kmod , 47.Nm atf_require_prog , 48.Nm atf_set , 49.Nm atf_skip , 50.Nm atf_test_case 51.Nd POSIX shell API to write ATF-based test programs 52.Sh SYNOPSIS 53.Nm atf_add_test_case 54.Qq name 55.Nm atf_check 56.Qq command 57.Nm atf_check_equal 58.Qq expected_expression 59.Qq actual_expression 60.Nm atf_check_not_equal 61.Qq expected_expression 62.Qq actual_expression 63.Nm atf_config_get 64.Qq var_name 65.Nm atf_config_has 66.Qq var_name 67.Nm atf_expect_death 68.Qq reason 69.Qq ... 70.Nm atf_expect_exit 71.Qq exitcode 72.Qq reason 73.Qq ... 74.Nm atf_expect_fail 75.Qq reason 76.Qq ... 77.Nm atf_expect_pass 78.Qq 79.Nm atf_expect_signal 80.Qq signo 81.Qq reason 82.Qq ... 83.Nm atf_expect_timeout 84.Qq reason 85.Qq ... 86.Nm atf_fail 87.Qq reason 88.Nm atf_get 89.Qq var_name 90.Nm atf_get_srcdir 91.Nm atf_init_test_cases 92.Qq name 93.Nm atf_pass 94.Nm atf_require_kmod 95.Qq kmod_name 96.Nm atf_require_prog 97.Qq prog_name 98.Nm atf_set 99.Qq var_name 100.Qq value 101.Nm atf_skip 102.Qq reason 103.Nm atf_test_case 104.Qq name 105.Qq cleanup 106.Sh DESCRIPTION 107ATF 108provides a simple but powerful interface to easily write test programs in 109the POSIX shell language. 110These are extremely helpful given that they are trivial to write due to the 111language simplicity and the great deal of available external tools, so they 112are often ideal to test other applications at the user level. 113.Pp 114Test programs written using this library must be run using the 115.Xr atf-sh 1 116interpreter by putting the following on their very first line: 117.Bd -literal -offset indent 118#! /usr/bin/env atf-sh 119.Ed 120.Pp 121Shell-based test programs always follow this template: 122.Bd -literal -offset indent 123atf_test_case tc1 124tc1_head() { 125 ... first test case's header ... 126} 127tc1_body() { 128 ... first test case's body ... 129} 130 131atf_test_case tc2 cleanup 132tc2_head() { 133 ... second test case's header ... 134} 135tc2_body() { 136 ... second test case's body ... 137} 138tc2_cleanup() { 139 ... second test case's cleanup ... 140} 141 142\&... additional test cases ... 143 144atf_init_test_cases() { 145 atf_add_test_case tc1 146 atf_add_test_case tc2 147 ... add additional test cases ... 148} 149.Ed 150.Ss Definition of test cases 151Test cases have an identifier and are composed of three different parts: 152the header, the body and an optional cleanup routine, all of which are 153described in 154.Xr atf-test-case 4 . 155To define test cases, one can use the 156.Nm atf_test_case 157function, which takes a first parameter specifying the test case's 158name and instructs the library to set things up to accept it as a valid 159test case. 160The second parameter is optional and, if provided, must be 161.Sq cleanup ; 162providing this parameter allows defining a cleanup routine for the test 163case. 164It is important to note that this function 165.Em does not 166set the test case up for execution when the program is run. 167In order to do so, a later registration is needed through the 168.Nm atf_add_test_case 169function detailed in 170.Sx Program initialization . 171.Pp 172Later on, one must define the three parts of the body by providing two 173or three functions (remember that the cleanup routine is optional). 174These functions are named after the test case's identifier, and are 175.Nm \*(Ltid\*(Gt_head , 176.Nm \*(Ltid\*(Gt_body 177and 178.Nm \*(Ltid\*(Gt_cleanup . 179None of these take parameters when executed. 180.Ss Program initialization 181The test program must define an 182.Nm atf_init_test_cases 183function, which is in charge of registering the test cases that will be 184executed at run time by using the 185.Nm atf_add_test_case 186function, which takes the name of a test case as its single parameter. 187This main function should not do anything else, except maybe sourcing 188auxiliary source files that define extra variables and functions. 189.Ss Configuration variables 190The test case has read-only access to the current configuration variables 191through the 192.Nm atf_config_has 193and 194.Nm atf_config_get 195methods. 196The former takes a single parameter specifying a variable name and returns 197a boolean indicating whether the variable is defined or not. 198The latter can take one or two parameters. 199If it takes only one, it specifies the variable from which to get the 200value, and this variable must be defined. 201If it takes two, the second one specifies a default value to be returned 202if the variable is not available. 203.Ss Access to the source directory 204It is possible to get the path to the test case's source directory from 205anywhere in the test program by using the 206.Nm atf_get_srcdir 207function. 208It is interesting to note that this can be used inside 209.Nm atf_init_test_cases 210to silently include additional helper files from the source directory. 211.Ss Requiring kernel modules 212Aside from the 213.Va require.kmods 214meta-data variable available in the header only, one can also check for 215additional kernel modules in the test case's body by using the 216.Nm atf_require_kmod 217function, which takes the name of a single kernel module. 218If it is not found, the test case will be automatically skipped. 219.Ss Requiring programs 220Aside from the 221.Va require.progs 222meta-data variable available in the header only, one can also check for 223additional programs in the test case's body by using the 224.Nm atf_require_prog 225function, which takes the base name or full path of a single binary. 226Relative paths are forbidden. 227If it is not found, the test case will be automatically skipped. 228.Ss Test case finalization 229The test case finalizes either when the body reaches its end, at which 230point the test is assumed to have 231.Em passed , 232or at any explicit call to 233.Nm atf_pass , 234.Nm atf_fail 235or 236.Nm atf_skip . 237These three functions terminate the execution of the test case immediately. 238The cleanup routine will be processed afterwards in a completely automated 239way, regardless of the test case's termination reason. 240.Pp 241.Nm atf_pass 242does not take any parameters. 243.Nm atf_fail 244and 245.Nm atf_skip 246take a single string parameter that describes why the test case failed or 247was skipped, respectively. 248It is very important to provide a clear error message in both cases so that 249the user can quickly know why the test did not pass. 250.Ss Expectations 251Everything explained in the previous section changes when the test case 252expectations are redefined by the programmer. 253.Pp 254Each test case has an internal state called 255.Sq expect 256that describes what the test case expectations are at any point in time. 257The value of this property can change during execution by any of: 258.Bl -tag -width indent 259.It Nm atf_expect_death Qo reason Qc Qo ... Qc 260Expects the test case to exit prematurely regardless of the nature of the 261exit. 262.It Nm atf_expect_exit Qo exitcode Qc Qo reason Qc Qo ... Qc 263Expects the test case to exit cleanly. 264If 265.Va exitcode 266is not 267.Sq -1 , 268the runtime engine will validate that the exit code of the test case 269matches the one provided in this call. 270Otherwise, the exact value will be ignored. 271.It Nm atf_expect_fail Qo reason Qc 272Any failure raised in this mode is recorded, but such failures do not report 273the test case as failed; instead, the test case finalizes cleanly and is 274reported as 275.Sq expected failure ; 276this report includes the provided 277.Fa reason 278as part of it. 279If no error is raised while running in this mode, then the test case is 280reported as 281.Sq failed . 282.Pp 283This mode is useful to reproduce actual known bugs in tests. 284Whenever the developer fixes the bug later on, the test case will start 285reporting a failure, signaling the developer that the test case must be 286adjusted to the new conditions. 287In this situation, it is useful, for example, to set 288.Fa reason 289as the bug number for tracking purposes. 290.It Nm atf_expect_pass 291This is the normal mode of execution. 292In this mode, any failure is reported as such to the user and the test case 293is marked as 294.Sq failed . 295.It Nm atf_expect_signal Qo signo Qc Qo reason Qc Qo ... Qc 296Expects the test case to terminate due to the reception of a signal. 297If 298.Va signo 299is not 300.Sq -1 , 301the runtime engine will validate that the signal that terminated the test 302case matches the one provided in this call. 303Otherwise, the exact value will be ignored. 304.It Nm atf_expect_timeout Qo reason Qc Qo ... Qc 305Expects the test case to execute for longer than its timeout. 306.El 307.Ss Helper functions for common checks 308.Bl -tag -width indent 309.It Nm atf_check Qo [options] Qc Qo command Qc Qo [args] Qc 310Executes a command, performs checks on its exit code and its output, and 311fails the test case if any of the checks is not successful. 312This function is particularly useful in integration tests that verify the 313correct functioning of a binary. 314.Pp 315Internally, this function is just a wrapper over the 316.Xr atf-check 1 317tool (whose manual page provides all details on the calling syntax). 318You should always use the 319.Nm atf_check 320function instead of the 321.Xr atf-check 1 322tool in your scripts; the latter is not even in the path. 323.It Nm atf_check_equal Qo expected_expression Qc Qo actual_expression Qc 324This function takes two expressions, evaluates them and, if their 325results differ, aborts the test case with an appropriate failure message. 326The common style is to put the expected value in the first parameter and the 327actual value in the second parameter. 328.It Nm atf_check_not_equal Qo expected_expression Qc Qo actual_expression Qc 329This function takes two expressions, evaluates them and, if their 330results are equal, aborts the test case with an appropriate failure message. 331The common style is to put the expected value in the first parameter and the 332actual value in the second parameter. 333.El 334.Sh EXAMPLES 335The following shows a complete test program with a single test case that 336validates the addition operator: 337.Bd -literal -offset indent 338atf_test_case addition 339addition_head() { 340 atf_set "descr" "Sample tests for the addition operator" 341} 342addition_body() { 343 atf_check_equal 0 $((0 + 0)) 344 atf_check_equal 1 $((0 + 1)) 345 atf_check_equal 1 $((1 + 0)) 346 347 atf_check_equal 2 $((1 + 1)) 348 349 atf_check_equal 300 $((100 + 200)) 350} 351 352atf_init_test_cases() { 353 atf_add_test_case addition 354} 355.Ed 356.Pp 357This other example shows how to include a file with extra helper functions 358in the test program: 359.Bd -literal -offset indent 360\&... definition of test cases ... 361 362atf_init_test_cases() { 363 . $(atf_get_srcdir)/helper_functions.sh 364 365 atf_add_test_case foo1 366 atf_add_test_case foo2 367} 368.Ed 369.Pp 370This example demonstrates the use of the very useful 371.Nm atf_check 372function: 373.Bd -literal -offset indent 374# Check for silent output 375atf_check -s exit:0 -o empty -e empty true 376 377# Check for silent output and failure 378atf_check -s exit:1 -o empty -e empty false 379 380# Check for known stdout and silent stderr 381echo foo >expout 382atf_check -s exit:0 -o file:expout -e empty echo foo 383 384# Generate a file for later inspection 385atf_check -s exit:0 -o save:stdout -e empty ls 386grep foo ls || atf_fail "foo file not found in listing" 387 388# Or just do the match along the way 389atf_check -s exit:0 -o match:"^foo$" -e empty ls 390.Ed 391.Sh SEE ALSO 392.Xr atf-check 1 , 393.Xr atf-sh 1 , 394.Xr atf-test-program 1 , 395.Xr atf-test-case 4 396