405 lines
12 KiB
Bash
405 lines
12 KiB
Bash
|
# $NetBSD: t_ps.sh,v 1.2 2014/01/16 04:16:32 mlelstv Exp $
|
||
|
#
|
||
|
# Copyright (c) 2007 The NetBSD Foundation, Inc.
|
||
|
# All rights reserved.
|
||
|
#
|
||
|
# Redistribution and use in source and binary forms, with or without
|
||
|
# modification, are permitted provided that the following conditions
|
||
|
# are met:
|
||
|
# 1. Redistributions of source code must retain the above copyright
|
||
|
# notice, this list of conditions and the following disclaimer.
|
||
|
# 2. Redistributions in binary form must reproduce the above copyright
|
||
|
# notice, this list of conditions and the following disclaimer in the
|
||
|
# documentation and/or other materials provided with the distribution.
|
||
|
#
|
||
|
# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||
|
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||
|
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||
|
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||
|
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||
|
#
|
||
|
|
||
|
# the implementation of "ps" to test
|
||
|
: ${TEST_PS:="ps"}
|
||
|
# tab and newline characters
|
||
|
tab="$(printf '\t')"
|
||
|
# nl="$(printf '\n')" doesn't work
|
||
|
nl='
|
||
|
'
|
||
|
|
||
|
#
|
||
|
# Parse the "keywords" file into a load of shell variables
|
||
|
#
|
||
|
setup_keywords()
|
||
|
{
|
||
|
# Set variables representing the header text
|
||
|
# for all normal keywords (except aliases), and
|
||
|
# for regular expressions to match the text in left- or
|
||
|
# right-justified columns.
|
||
|
# For example, head_text_p_cpu="%CPU" head_regexp_p_cpu=" *%CPU".
|
||
|
while read keyword heading flag
|
||
|
do
|
||
|
case "$keyword" in
|
||
|
''|\#*) continue
|
||
|
;;
|
||
|
esac
|
||
|
[ x"$flag" = x"ALIAS" ] && continue
|
||
|
kvar="${keyword}"
|
||
|
case "${keyword}" in
|
||
|
%*) kvar="p_${keyword#%}"
|
||
|
;;
|
||
|
esac
|
||
|
eval head_text_${kvar}=\'"${heading}"\'
|
||
|
case "${flag}" in
|
||
|
'') # right justified
|
||
|
eval head_regexp_${kvar}=\'" *${heading}"\'
|
||
|
;;
|
||
|
LJUST) # left justified
|
||
|
eval head_regexp_${kvar}=\'"${heading} *"\'
|
||
|
;;
|
||
|
*) atf_fail "unknown flag in keywords"
|
||
|
;;
|
||
|
esac
|
||
|
done <"$(atf_get_srcdir)/keywords"
|
||
|
|
||
|
# Now do the aliases.
|
||
|
while read keyword heading flag
|
||
|
do
|
||
|
case "$keyword" in
|
||
|
''|\#*) continue
|
||
|
;;
|
||
|
esac
|
||
|
[ x"$flag" != x"ALIAS" ] && continue
|
||
|
kvar="${keyword}"
|
||
|
avar="${heading}"
|
||
|
case "${keyword}" in
|
||
|
%*) kvar="p_${keyword#%}"
|
||
|
;;
|
||
|
esac
|
||
|
case "${heading}" in
|
||
|
%*) avar="p_${heading#%}"
|
||
|
;;
|
||
|
esac
|
||
|
eval head_text_${kvar}=\"\$head_text_${avar}\"
|
||
|
eval head_regexp_${kvar}=\"\$head_regexp_${avar}\"
|
||
|
done <"$(atf_get_srcdir)/keywords"
|
||
|
|
||
|
# default sets of keywords
|
||
|
default_keywords='pid tty stat time command'
|
||
|
j_keywords='user pid ppid pgid sess jobc state tt time command'
|
||
|
l_keywords='uid pid ppid cpu pri nice vsz rss wchan state tt time command'
|
||
|
s_keywords='uid pid ppid cpu lid nlwp pri nice vsz rss wchan lstate tt ltime command'
|
||
|
u_keywords='user pid %cpu %mem vsz rss tt state start time command'
|
||
|
v_keywords='pid state time sl re pagein vsz rss lim tsiz %cpu %mem command'
|
||
|
}
|
||
|
|
||
|
# Convert a list of keywords like "pid comm" to a regexp
|
||
|
# like " *PID COMMAND *"
|
||
|
heading_keywords_to_regexp()
|
||
|
{
|
||
|
local keywords="$1"
|
||
|
local regexp
|
||
|
regexp="$(echo "$keywords" | \
|
||
|
sed -E -e 's/\%/p_/g' -e 's/(^| )/\1\$head_regexp_/g')"
|
||
|
eval regexp=\""${regexp}"\"
|
||
|
regexp="^${regexp}\$"
|
||
|
echo "$regexp"
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Check that a string matches a regexp; use the specified id
|
||
|
# in error or success messages.
|
||
|
#
|
||
|
check_regexp() {
|
||
|
local id="$1" string="$2" regexp="$3"
|
||
|
if ! expr "$string" : "$regexp" >/dev/null
|
||
|
then
|
||
|
atf_fail "${id}: expected [${regexp}], got [${string}]"
|
||
|
false
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Run "ps $args -p $$"; check that only one line is printed,
|
||
|
# without a preceding header line.
|
||
|
#
|
||
|
check_no_heading_line()
|
||
|
{
|
||
|
local args="$1"
|
||
|
local output="$(eval "${TEST_PS} $args -p $$")"
|
||
|
case "$output" in
|
||
|
*"$nl"*)
|
||
|
local firstline="${output%%${nl}*}"
|
||
|
atf_fail "check_no_heading_line [$args] got [$firstline]"
|
||
|
;;
|
||
|
*)
|
||
|
;;
|
||
|
esac
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Run "ps $args"; check that the heading matches the expected regexp.
|
||
|
#
|
||
|
check_heading_regexp()
|
||
|
{
|
||
|
args="$1"
|
||
|
regexp="$2"
|
||
|
actual="$( eval "${TEST_PS} $args" | sed -e 1q )"
|
||
|
check_regexp "heading [$args]" "${actual}" "${regexp}"
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Run "ps $args"; check that the heading matches a regexp constructed
|
||
|
# from the specified keywords.
|
||
|
#
|
||
|
check_heading_keywords()
|
||
|
{
|
||
|
args="$1"
|
||
|
keywords="$2"
|
||
|
check_heading_regexp "$args" "$(heading_keywords_to_regexp "$keywords")"
|
||
|
}
|
||
|
|
||
|
#
|
||
|
# Try several variations on "ps $flag", "ps -$flag", etc.,
|
||
|
# and check that the heading always has the correct keywords.
|
||
|
#
|
||
|
check_heading_variations()
|
||
|
{
|
||
|
flag="$1"
|
||
|
keywords="$2"
|
||
|
for args in "$flag" "-$flag" "-$flag$flag -$flag"; do
|
||
|
check_heading_keywords "$args" "$keywords"
|
||
|
done
|
||
|
}
|
||
|
|
||
|
atf_test_case default_columns
|
||
|
default_columns_head()
|
||
|
{
|
||
|
atf_set "descr" "Checks that the default set of columns is correct" \
|
||
|
"and also check that the columns printed by the -j," \
|
||
|
"-l, -s, -u and -v flags alone are correct"
|
||
|
}
|
||
|
default_columns_body()
|
||
|
{
|
||
|
setup_keywords
|
||
|
check_heading_keywords '' "$default_keywords"
|
||
|
check_heading_variations 'j' "$j_keywords"
|
||
|
check_heading_variations 'l' "$l_keywords"
|
||
|
check_heading_variations 's' "$s_keywords"
|
||
|
check_heading_variations 'u' "$u_keywords"
|
||
|
check_heading_variations 'v' "$v_keywords"
|
||
|
}
|
||
|
|
||
|
atf_test_case minus_O
|
||
|
minus_O_head()
|
||
|
{
|
||
|
atf_set "descr" "Checks that 'ps -O foo' inserts columns just after" \
|
||
|
"the pid column"
|
||
|
}
|
||
|
minus_O_body()
|
||
|
{
|
||
|
setup_keywords
|
||
|
check_heading_keywords '-O %cpu,%mem' \
|
||
|
"$(echo "${default_keywords}" | sed -e 's/pid/pid %cpu %mem/')"
|
||
|
check_heading_keywords '-O %cpu -O %mem' \
|
||
|
"$(echo "${default_keywords}" | sed -e 's/pid/pid %cpu %mem/')"
|
||
|
check_heading_keywords '-O%cpu -O%mem' \
|
||
|
"$(echo "${default_keywords}" | sed -e 's/pid/pid %cpu %mem/')"
|
||
|
}
|
||
|
|
||
|
atf_test_case minus_o
|
||
|
minus_o_head()
|
||
|
{
|
||
|
atf_set "descr" "Checks simple cases of 'ps -o foo' to control which" \
|
||
|
"columns are printed; this does not test header" \
|
||
|
"overriding via 'ps -o foo=BAR'"
|
||
|
}
|
||
|
minus_o_body()
|
||
|
{
|
||
|
setup_keywords
|
||
|
# Keywords for "-o name" override the default display
|
||
|
check_heading_keywords '-o pid,%cpu,%mem' \
|
||
|
"pid %cpu %mem"
|
||
|
check_heading_keywords '-o pid -o %cpu,%mem' \
|
||
|
"pid %cpu %mem"
|
||
|
check_heading_keywords '-opid -o %cpu,%mem' \
|
||
|
"pid %cpu %mem"
|
||
|
# Space works like comma
|
||
|
check_heading_keywords '-opid -o "%cpu %mem"' \
|
||
|
"pid %cpu %mem"
|
||
|
# Check missing pid
|
||
|
check_heading_keywords '-o comm' \
|
||
|
"comm"
|
||
|
# Check pid present but not first
|
||
|
check_heading_keywords '-o comm,pid' \
|
||
|
"comm pid"
|
||
|
}
|
||
|
|
||
|
atf_test_case override_heading_simple
|
||
|
override_heading_simple_head()
|
||
|
{
|
||
|
atf_set "descr" "Tests simple uses of header overriding via" \
|
||
|
"'ps -o foo=BAR'. This does not test columns " \
|
||
|
"with null headings, or headings with embedded" \
|
||
|
"space, ',' or '='."
|
||
|
}
|
||
|
override_heading_simple_body()
|
||
|
{
|
||
|
setup_keywords
|
||
|
check_heading_regexp '-o pid=PPP -o comm' \
|
||
|
'^ *PPP '"${head_text_comm}"'$' # no trailing space
|
||
|
check_heading_regexp '-o pid=PPP -o comm=CCC' \
|
||
|
'^ *PPP CCC$'
|
||
|
check_heading_regexp '-o pid,comm=CCC' \
|
||
|
'^'"${head_regexp_pid}"' CCC$'
|
||
|
check_heading_regexp '-o pid -o comm=CCC' \
|
||
|
'^'"${head_regexp_pid}"' CCC$'
|
||
|
# Check missing pid
|
||
|
check_heading_regexp '-o comm=CCC' \
|
||
|
'^CCC$'
|
||
|
# Check pid present but not first
|
||
|
check_heading_regexp '-o comm=CCC -o pid=PPP' \
|
||
|
'^CCC *PPP$'
|
||
|
check_heading_regexp '-o comm,pid=PPP' \
|
||
|
'^'"${head_regexp_comm}"' *PPP$'
|
||
|
}
|
||
|
|
||
|
atf_test_case override_heading_embedded_specials
|
||
|
override_heading_embedded_specials_head()
|
||
|
{
|
||
|
atf_set "descr" "Tests header overriding with embedded space," \
|
||
|
"',' or '='. Everything after the first '='" \
|
||
|
"is part of the heading."
|
||
|
}
|
||
|
override_heading_embedded_specials_body()
|
||
|
{
|
||
|
setup_keywords
|
||
|
# Check embedded "," or "=" in override header.
|
||
|
check_heading_regexp '-o comm,pid==' \
|
||
|
'^'"${head_regexp_comm}"' *=$'
|
||
|
check_heading_regexp '-o comm,pid=,' \
|
||
|
'^'"${head_regexp_comm}"' *,$'
|
||
|
check_heading_regexp '-o pid=PPP,comm' \
|
||
|
'^ *PPP,comm$' # not like '-o pid=PPP -o comm'
|
||
|
check_heading_regexp '-o pid=PPP,comm=CCC' \
|
||
|
'^ *PPP,comm=CCC$' # not like '-o pid=PPP -o comm=CCC'
|
||
|
check_heading_regexp '-o comm,pid=PPP,QQQ' \
|
||
|
'^'"${head_regexp_comm}"' *PPP,QQQ$'
|
||
|
check_heading_regexp '-o comm,pid=ppid,tty=state' \
|
||
|
'^'"${head_regexp_comm}"' *ppid,tty=state$'
|
||
|
# Check embedded space or tab in override header.
|
||
|
check_heading_regexp '-o comm,pid="PPP QQQ"' \
|
||
|
'^'"${head_regexp_comm}"' *PPP QQQ$'
|
||
|
check_heading_regexp '-o comm,pid="PPP${tab}QQQ"' \
|
||
|
'^'"${head_regexp_comm}"' *PPP'"${tab}"'QQQ$'
|
||
|
}
|
||
|
|
||
|
atf_test_case override_heading_some_null
|
||
|
override_heading_some_null_head()
|
||
|
{
|
||
|
atf_set "descr" "Tests simple uses of null column headings" \
|
||
|
"overriding via 'ps -o foo=BAR -o baz='. This" \
|
||
|
"does not test the case where all columns have" \
|
||
|
"null headings."
|
||
|
}
|
||
|
override_heading_some_null_body()
|
||
|
{
|
||
|
setup_keywords
|
||
|
check_heading_regexp '-o pid=PPP -o comm=' \
|
||
|
'^ *PPP *$'
|
||
|
check_heading_regexp '-o pid= -o comm=CCC' \
|
||
|
'^ * CCC$'
|
||
|
check_heading_regexp '-o pid -o comm=' \
|
||
|
'^'"${head_regexp_pid}"' *$'
|
||
|
# Check missing pid
|
||
|
check_heading_regexp '-o ppid= -o comm=CCC' \
|
||
|
'^ * CCC$'
|
||
|
check_heading_regexp '-o ppid=PPP -o comm=' \
|
||
|
'^ *PPP *$'
|
||
|
# Check pid present but not first
|
||
|
check_heading_regexp '-o comm= -o pid=PPP' \
|
||
|
'^ * PPP$'
|
||
|
check_heading_regexp '-o comm,pid=' \
|
||
|
'^'"${head_regexp_comm}"' *$'
|
||
|
# A field with a null custom heading retains a minimum width
|
||
|
# derived from the default heading. This does not apply
|
||
|
# to a field with a very short (non-null) custom heading.
|
||
|
#
|
||
|
# We choose "holdcnt" as a column whose width is likely to be
|
||
|
# determined entirely by the header width, because the values
|
||
|
# are likely to be very small.
|
||
|
check_heading_regexp '-o holdcnt -o holdcnt -o holdcnt' \
|
||
|
'^HOLDCNT HOLDCNT HOLDCNT$'
|
||
|
check_heading_regexp '-o holdcnt -o holdcnt= -o holdcnt' \
|
||
|
'^HOLDCNT HOLDCNT$'
|
||
|
check_heading_regexp '-o holdcnt -o holdcnt=HH -o holdcnt' \
|
||
|
'^HOLDCNT HH HOLDCNT$'
|
||
|
}
|
||
|
|
||
|
atf_test_case override_heading_all_null
|
||
|
override_heading_all_null_head()
|
||
|
{
|
||
|
atf_set "descr" "Tests the use of 'ps -o foo= -o bar=' (with a" \
|
||
|
"null heading for every column). The heading" \
|
||
|
"should not be printed at all in this case."
|
||
|
}
|
||
|
override_heading_all_null_body()
|
||
|
{
|
||
|
setup_keywords
|
||
|
# A heading with a space is not a null heading,
|
||
|
# so should not be suppressed
|
||
|
check_heading_regexp '-o comm=" "' \
|
||
|
'^ *$'
|
||
|
# Null headings should be suppressed
|
||
|
check_no_heading_line '-o pid= -o comm='
|
||
|
check_no_heading_line '-o pid= -o comm='
|
||
|
# Check missing pid
|
||
|
check_no_heading_line '-o ppid='
|
||
|
check_no_heading_line '-o comm='
|
||
|
check_no_heading_line '-o command='
|
||
|
check_no_heading_line '-o ppid= -o comm='
|
||
|
check_no_heading_line '-o comm= -o ppid='
|
||
|
# Check pid present but not first
|
||
|
check_no_heading_line '-o comm= -o pid='
|
||
|
check_no_heading_line '-o ppid= -o pid= -o command='
|
||
|
}
|
||
|
|
||
|
atf_test_case duplicate_column
|
||
|
duplicate_column_head()
|
||
|
{
|
||
|
atf_set "descr" "Tests the use of -o options to display the" \
|
||
|
"same column more than once"
|
||
|
}
|
||
|
duplicate_column_body()
|
||
|
{
|
||
|
setup_keywords
|
||
|
# two custom headers
|
||
|
check_heading_regexp '-o pid=PPP -o pid=QQQ' \
|
||
|
'^ *PPP *QQQ$'
|
||
|
# one custom header, before and after default header
|
||
|
check_heading_regexp '-o pid=PPP -o pid' \
|
||
|
'^ *PPP '"${head_regexp_pid}"'$'
|
||
|
check_heading_regexp '-o pid -o pid=QQQ' \
|
||
|
'^'"${head_regexp_pid}"' *QQQ$'
|
||
|
# custom headers both before and after default header
|
||
|
check_heading_regexp '-o pid=PPP -o pid -o pid=QQQ' \
|
||
|
'^ *PPP '"${head_regexp_pid}"' *QQQ$'
|
||
|
}
|
||
|
|
||
|
atf_init_test_cases() {
|
||
|
atf_add_test_case default_columns
|
||
|
atf_add_test_case minus_O
|
||
|
atf_add_test_case minus_o
|
||
|
atf_add_test_case override_heading_simple
|
||
|
atf_add_test_case override_heading_embedded_specials
|
||
|
atf_add_test_case override_heading_some_null
|
||
|
atf_add_test_case override_heading_all_null
|
||
|
atf_add_test_case duplicate_column
|
||
|
}
|