1# $NetBSD: t_ps.sh,v 1.2 2014/01/16 04:16:32 mlelstv Exp $ 2# 3# Copyright (c) 2007 The NetBSD Foundation, Inc. 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 16# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 17# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 19# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25# POSSIBILITY OF SUCH DAMAGE. 26# 27 28# the implementation of "ps" to test 29: ${TEST_PS:="ps"} 30# tab and newline characters 31tab="$(printf '\t')" 32# nl="$(printf '\n')" doesn't work 33nl=' 34' 35 36# 37# Parse the "keywords" file into a load of shell variables 38# 39setup_keywords() 40{ 41 # Set variables representing the header text 42 # for all normal keywords (except aliases), and 43 # for regular expressions to match the text in left- or 44 # right-justified columns. 45 # For example, head_text_p_cpu="%CPU" head_regexp_p_cpu=" *%CPU". 46 while read keyword heading flag 47 do 48 case "$keyword" in 49 ''|\#*) continue 50 ;; 51 esac 52 [ x"$flag" = x"ALIAS" ] && continue 53 kvar="${keyword}" 54 case "${keyword}" in 55 %*) kvar="p_${keyword#%}" 56 ;; 57 esac 58 eval head_text_${kvar}=\'"${heading}"\' 59 case "${flag}" in 60 '') # right justified 61 eval head_regexp_${kvar}=\'" *${heading}"\' 62 ;; 63 LJUST) # left justified 64 eval head_regexp_${kvar}=\'"${heading} *"\' 65 ;; 66 *) atf_fail "unknown flag in keywords" 67 ;; 68 esac 69 done <"$(atf_get_srcdir)/keywords" 70 71 # Now do the aliases. 72 while read keyword heading flag 73 do 74 case "$keyword" in 75 ''|\#*) continue 76 ;; 77 esac 78 [ x"$flag" != x"ALIAS" ] && continue 79 kvar="${keyword}" 80 avar="${heading}" 81 case "${keyword}" in 82 %*) kvar="p_${keyword#%}" 83 ;; 84 esac 85 case "${heading}" in 86 %*) avar="p_${heading#%}" 87 ;; 88 esac 89 eval head_text_${kvar}=\"\$head_text_${avar}\" 90 eval head_regexp_${kvar}=\"\$head_regexp_${avar}\" 91 done <"$(atf_get_srcdir)/keywords" 92 93 # default sets of keywords 94 default_keywords='pid tty stat time command' 95 j_keywords='user pid ppid pgid sess jobc state tt time command' 96 l_keywords='uid pid ppid cpu pri nice vsz rss wchan state tt time command' 97 s_keywords='uid pid ppid cpu lid nlwp pri nice vsz rss wchan lstate tt ltime command' 98 u_keywords='user pid %cpu %mem vsz rss tt state start time command' 99 v_keywords='pid state time sl re pagein vsz rss lim tsiz %cpu %mem command' 100} 101 102# Convert a list of keywords like "pid comm" to a regexp 103# like " *PID COMMAND *" 104heading_keywords_to_regexp() 105{ 106 local keywords="$1" 107 local regexp 108 regexp="$(echo "$keywords" | \ 109 sed -E -e 's/\%/p_/g' -e 's/(^| )/\1\$head_regexp_/g')" 110 eval regexp=\""${regexp}"\" 111 regexp="^${regexp}\$" 112 echo "$regexp" 113} 114 115# 116# Check that a string matches a regexp; use the specified id 117# in error or success messages. 118# 119check_regexp() { 120 local id="$1" string="$2" regexp="$3" 121 if ! expr "$string" : "$regexp" >/dev/null 122 then 123 atf_fail "${id}: expected [${regexp}], got [${string}]" 124 false 125 fi 126} 127 128# 129# Run "ps $args -p $$"; check that only one line is printed, 130# without a preceding header line. 131# 132check_no_heading_line() 133{ 134 local args="$1" 135 local output="$(eval "${TEST_PS} $args -p $$")" 136 case "$output" in 137 *"$nl"*) 138 local firstline="${output%%${nl}*}" 139 atf_fail "check_no_heading_line [$args] got [$firstline]" 140 ;; 141 *) 142 ;; 143 esac 144} 145 146# 147# Run "ps $args"; check that the heading matches the expected regexp. 148# 149check_heading_regexp() 150{ 151 args="$1" 152 regexp="$2" 153 actual="$( eval "${TEST_PS} $args" | sed -e 1q )" 154 check_regexp "heading [$args]" "${actual}" "${regexp}" 155} 156 157# 158# Run "ps $args"; check that the heading matches a regexp constructed 159# from the specified keywords. 160# 161check_heading_keywords() 162{ 163 args="$1" 164 keywords="$2" 165 check_heading_regexp "$args" "$(heading_keywords_to_regexp "$keywords")" 166} 167 168# 169# Try several variations on "ps $flag", "ps -$flag", etc., 170# and check that the heading always has the correct keywords. 171# 172check_heading_variations() 173{ 174 flag="$1" 175 keywords="$2" 176 for args in "$flag" "-$flag" "-$flag$flag -$flag"; do 177 check_heading_keywords "$args" "$keywords" 178 done 179} 180 181atf_test_case default_columns 182default_columns_head() 183{ 184 atf_set "descr" "Checks that the default set of columns is correct" \ 185 "and also check that the columns printed by the -j," \ 186 "-l, -s, -u and -v flags alone are correct" 187} 188default_columns_body() 189{ 190 setup_keywords 191 check_heading_keywords '' "$default_keywords" 192 check_heading_variations 'j' "$j_keywords" 193 check_heading_variations 'l' "$l_keywords" 194 check_heading_variations 's' "$s_keywords" 195 check_heading_variations 'u' "$u_keywords" 196 check_heading_variations 'v' "$v_keywords" 197} 198 199atf_test_case minus_O 200minus_O_head() 201{ 202 atf_set "descr" "Checks that 'ps -O foo' inserts columns just after" \ 203 "the pid column" 204} 205minus_O_body() 206{ 207 setup_keywords 208 check_heading_keywords '-O %cpu,%mem' \ 209 "$(echo "${default_keywords}" | sed -e 's/pid/pid %cpu %mem/')" 210 check_heading_keywords '-O %cpu -O %mem' \ 211 "$(echo "${default_keywords}" | sed -e 's/pid/pid %cpu %mem/')" 212 check_heading_keywords '-O%cpu -O%mem' \ 213 "$(echo "${default_keywords}" | sed -e 's/pid/pid %cpu %mem/')" 214} 215 216atf_test_case minus_o 217minus_o_head() 218{ 219 atf_set "descr" "Checks simple cases of 'ps -o foo' to control which" \ 220 "columns are printed; this does not test header" \ 221 "overriding via 'ps -o foo=BAR'" 222} 223minus_o_body() 224{ 225 setup_keywords 226 # Keywords for "-o name" override the default display 227 check_heading_keywords '-o pid,%cpu,%mem' \ 228 "pid %cpu %mem" 229 check_heading_keywords '-o pid -o %cpu,%mem' \ 230 "pid %cpu %mem" 231 check_heading_keywords '-opid -o %cpu,%mem' \ 232 "pid %cpu %mem" 233 # Space works like comma 234 check_heading_keywords '-opid -o "%cpu %mem"' \ 235 "pid %cpu %mem" 236 # Check missing pid 237 check_heading_keywords '-o comm' \ 238 "comm" 239 # Check pid present but not first 240 check_heading_keywords '-o comm,pid' \ 241 "comm pid" 242} 243 244atf_test_case override_heading_simple 245override_heading_simple_head() 246{ 247 atf_set "descr" "Tests simple uses of header overriding via" \ 248 "'ps -o foo=BAR'. This does not test columns " \ 249 "with null headings, or headings with embedded" \ 250 "space, ',' or '='." 251} 252override_heading_simple_body() 253{ 254 setup_keywords 255 check_heading_regexp '-o pid=PPP -o comm' \ 256 '^ *PPP '"${head_text_comm}"'$' # no trailing space 257 check_heading_regexp '-o pid=PPP -o comm=CCC' \ 258 '^ *PPP CCC$' 259 check_heading_regexp '-o pid,comm=CCC' \ 260 '^'"${head_regexp_pid}"' CCC$' 261 check_heading_regexp '-o pid -o comm=CCC' \ 262 '^'"${head_regexp_pid}"' CCC$' 263 # Check missing pid 264 check_heading_regexp '-o comm=CCC' \ 265 '^CCC$' 266 # Check pid present but not first 267 check_heading_regexp '-o comm=CCC -o pid=PPP' \ 268 '^CCC *PPP$' 269 check_heading_regexp '-o comm,pid=PPP' \ 270 '^'"${head_regexp_comm}"' *PPP$' 271} 272 273atf_test_case override_heading_embedded_specials 274override_heading_embedded_specials_head() 275{ 276 atf_set "descr" "Tests header overriding with embedded space," \ 277 "',' or '='. Everything after the first '='" \ 278 "is part of the heading." 279} 280override_heading_embedded_specials_body() 281{ 282 setup_keywords 283 # Check embedded "," or "=" in override header. 284 check_heading_regexp '-o comm,pid==' \ 285 '^'"${head_regexp_comm}"' *=$' 286 check_heading_regexp '-o comm,pid=,' \ 287 '^'"${head_regexp_comm}"' *,$' 288 check_heading_regexp '-o pid=PPP,comm' \ 289 '^ *PPP,comm$' # not like '-o pid=PPP -o comm' 290 check_heading_regexp '-o pid=PPP,comm=CCC' \ 291 '^ *PPP,comm=CCC$' # not like '-o pid=PPP -o comm=CCC' 292 check_heading_regexp '-o comm,pid=PPP,QQQ' \ 293 '^'"${head_regexp_comm}"' *PPP,QQQ$' 294 check_heading_regexp '-o comm,pid=ppid,tty=state' \ 295 '^'"${head_regexp_comm}"' *ppid,tty=state$' 296 # Check embedded space or tab in override header. 297 check_heading_regexp '-o comm,pid="PPP QQQ"' \ 298 '^'"${head_regexp_comm}"' *PPP QQQ$' 299 check_heading_regexp '-o comm,pid="PPP${tab}QQQ"' \ 300 '^'"${head_regexp_comm}"' *PPP'"${tab}"'QQQ$' 301} 302 303atf_test_case override_heading_some_null 304override_heading_some_null_head() 305{ 306 atf_set "descr" "Tests simple uses of null column headings" \ 307 "overriding via 'ps -o foo=BAR -o baz='. This" \ 308 "does not test the case where all columns have" \ 309 "null headings." 310} 311override_heading_some_null_body() 312{ 313 setup_keywords 314 check_heading_regexp '-o pid=PPP -o comm=' \ 315 '^ *PPP *$' 316 check_heading_regexp '-o pid= -o comm=CCC' \ 317 '^ * CCC$' 318 check_heading_regexp '-o pid -o comm=' \ 319 '^'"${head_regexp_pid}"' *$' 320 # Check missing pid 321 check_heading_regexp '-o ppid= -o comm=CCC' \ 322 '^ * CCC$' 323 check_heading_regexp '-o ppid=PPP -o comm=' \ 324 '^ *PPP *$' 325 # Check pid present but not first 326 check_heading_regexp '-o comm= -o pid=PPP' \ 327 '^ * PPP$' 328 check_heading_regexp '-o comm,pid=' \ 329 '^'"${head_regexp_comm}"' *$' 330 # A field with a null custom heading retains a minimum width 331 # derived from the default heading. This does not apply 332 # to a field with a very short (non-null) custom heading. 333 # 334 # We choose "holdcnt" as a column whose width is likely to be 335 # determined entirely by the header width, because the values 336 # are likely to be very small. 337 check_heading_regexp '-o holdcnt -o holdcnt -o holdcnt' \ 338 '^HOLDCNT HOLDCNT HOLDCNT$' 339 check_heading_regexp '-o holdcnt -o holdcnt= -o holdcnt' \ 340 '^HOLDCNT HOLDCNT$' 341 check_heading_regexp '-o holdcnt -o holdcnt=HH -o holdcnt' \ 342 '^HOLDCNT HH HOLDCNT$' 343} 344 345atf_test_case override_heading_all_null 346override_heading_all_null_head() 347{ 348 atf_set "descr" "Tests the use of 'ps -o foo= -o bar=' (with a" \ 349 "null heading for every column). The heading" \ 350 "should not be printed at all in this case." 351} 352override_heading_all_null_body() 353{ 354 setup_keywords 355 # A heading with a space is not a null heading, 356 # so should not be suppressed 357 check_heading_regexp '-o comm=" "' \ 358 '^ *$' 359 # Null headings should be suppressed 360 check_no_heading_line '-o pid= -o comm=' 361 check_no_heading_line '-o pid= -o comm=' 362 # Check missing pid 363 check_no_heading_line '-o ppid=' 364 check_no_heading_line '-o comm=' 365 check_no_heading_line '-o command=' 366 check_no_heading_line '-o ppid= -o comm=' 367 check_no_heading_line '-o comm= -o ppid=' 368 # Check pid present but not first 369 check_no_heading_line '-o comm= -o pid=' 370 check_no_heading_line '-o ppid= -o pid= -o command=' 371} 372 373atf_test_case duplicate_column 374duplicate_column_head() 375{ 376 atf_set "descr" "Tests the use of -o options to display the" \ 377 "same column more than once" 378} 379duplicate_column_body() 380{ 381 setup_keywords 382 # two custom headers 383 check_heading_regexp '-o pid=PPP -o pid=QQQ' \ 384 '^ *PPP *QQQ$' 385 # one custom header, before and after default header 386 check_heading_regexp '-o pid=PPP -o pid' \ 387 '^ *PPP '"${head_regexp_pid}"'$' 388 check_heading_regexp '-o pid -o pid=QQQ' \ 389 '^'"${head_regexp_pid}"' *QQQ$' 390 # custom headers both before and after default header 391 check_heading_regexp '-o pid=PPP -o pid -o pid=QQQ' \ 392 '^ *PPP '"${head_regexp_pid}"' *QQQ$' 393} 394 395atf_init_test_cases() { 396 atf_add_test_case default_columns 397 atf_add_test_case minus_O 398 atf_add_test_case minus_o 399 atf_add_test_case override_heading_simple 400 atf_add_test_case override_heading_embedded_specials 401 atf_add_test_case override_heading_some_null 402 atf_add_test_case override_heading_all_null 403 atf_add_test_case duplicate_column 404} 405