Home | History | Annotate | Line # | Download | only in ps
t_ps.sh revision 1.1.4.1
      1 # $NetBSD: t_ps.sh,v 1.1.4.1 2014/08/20 00:04:45 tls 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
     31 tab="$(printf '\t')"
     32 # nl="$(printf '\n')" doesn't work
     33 nl='
     34 '
     35 
     36 #
     37 # Parse the "keywords" file into a load of shell variables
     38 #
     39 setup_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 *"
    104 heading_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 #
    119 check_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 #
    132 check_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 #
    149 check_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 #
    161 check_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 #
    172 check_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 
    181 atf_test_case default_columns
    182 default_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 }
    188 default_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 
    199 atf_test_case minus_O
    200 minus_O_head()
    201 {
    202 	atf_set "descr" "Checks that 'ps -O foo' inserts columns just after" \
    203 	                "the pid column"
    204 }
    205 minus_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 
    216 atf_test_case minus_o
    217 minus_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 }
    223 minus_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 
    244 atf_test_case override_heading_simple
    245 override_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 }
    252 override_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 
    273 atf_test_case override_heading_embedded_specials
    274 override_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 }
    280 override_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 
    303 atf_test_case override_heading_some_null
    304 override_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 }
    311 override_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 
    345 atf_test_case override_heading_all_null
    346 override_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 }
    352 override_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 
    373 atf_test_case duplicate_column
    374 duplicate_column_head()
    375 {
    376 	atf_set "descr" "Tests the use of -o options to display the" \
    377 	                "same column more than once"
    378 }
    379 duplicate_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 
    395 atf_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