247 lines
7.8 KiB
Bash
247 lines
7.8 KiB
Bash
|
# Shell function library for test cases.
|
||
|
#
|
||
|
# Note that while many of the functions in this library could benefit from
|
||
|
# using "local" to avoid possibly hammering global variables, Solaris /bin/sh
|
||
|
# doesn't support local and this library aspires to be portable to Solaris
|
||
|
# Bourne shell. Instead, all private variables are prefixed with "tap_".
|
||
|
#
|
||
|
# This file provides a TAP-compatible shell function library useful for
|
||
|
# writing test cases. It is part of C TAP Harness, which can be found at
|
||
|
# <https://www.eyrie.org/~eagle/software/c-tap-harness/>.
|
||
|
#
|
||
|
# Written by Russ Allbery <eagle@eyrie.org>
|
||
|
# Copyright 2009, 2010, 2011, 2012, 2016 Russ Allbery <eagle@eyrie.org>
|
||
|
# Copyright 2006, 2007, 2008, 2013
|
||
|
# The Board of Trustees of the Leland Stanford Junior University
|
||
|
#
|
||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
# of this software and associated documentation files (the "Software"), to
|
||
|
# deal in the Software without restriction, including without limitation the
|
||
|
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||
|
# sell copies of the Software, and to permit persons to whom the Software is
|
||
|
# furnished to do so, subject to the following conditions:
|
||
|
#
|
||
|
# The above copyright notice and this permission notice shall be included in
|
||
|
# all copies or substantial portions of the Software.
|
||
|
#
|
||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||
|
# IN THE SOFTWARE.
|
||
|
|
||
|
# Print out the number of test cases we expect to run.
|
||
|
plan () {
|
||
|
count=1
|
||
|
planned="$1"
|
||
|
failed=0
|
||
|
echo "1..$1"
|
||
|
trap finish 0
|
||
|
}
|
||
|
|
||
|
# Prepare for lazy planning.
|
||
|
plan_lazy () {
|
||
|
count=1
|
||
|
planned=0
|
||
|
failed=0
|
||
|
trap finish 0
|
||
|
}
|
||
|
|
||
|
# Report the test status on exit.
|
||
|
finish () {
|
||
|
tap_highest=`expr "$count" - 1`
|
||
|
if [ "$planned" = 0 ] ; then
|
||
|
echo "1..$tap_highest"
|
||
|
planned="$tap_highest"
|
||
|
fi
|
||
|
tap_looks='# Looks like you'
|
||
|
if [ "$planned" -gt 0 ] ; then
|
||
|
if [ "$planned" -gt "$tap_highest" ] ; then
|
||
|
if [ "$planned" -gt 1 ] ; then
|
||
|
echo "$tap_looks planned $planned tests but only ran" \
|
||
|
"$tap_highest"
|
||
|
else
|
||
|
echo "$tap_looks planned $planned test but only ran" \
|
||
|
"$tap_highest"
|
||
|
fi
|
||
|
elif [ "$planned" -lt "$tap_highest" ] ; then
|
||
|
tap_extra=`expr "$tap_highest" - "$planned"`
|
||
|
if [ "$planned" -gt 1 ] ; then
|
||
|
echo "$tap_looks planned $planned tests but ran" \
|
||
|
"$tap_extra extra"
|
||
|
else
|
||
|
echo "$tap_looks planned $planned test but ran" \
|
||
|
"$tap_extra extra"
|
||
|
fi
|
||
|
elif [ "$failed" -gt 0 ] ; then
|
||
|
if [ "$failed" -gt 1 ] ; then
|
||
|
echo "$tap_looks failed $failed tests of $planned"
|
||
|
else
|
||
|
echo "$tap_looks failed $failed test of $planned"
|
||
|
fi
|
||
|
elif [ "$planned" -gt 1 ] ; then
|
||
|
echo "# All $planned tests successful or skipped"
|
||
|
else
|
||
|
echo "# $planned test successful or skipped"
|
||
|
fi
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
# Skip the entire test suite. Should be run instead of plan.
|
||
|
skip_all () {
|
||
|
tap_desc="$1"
|
||
|
if [ -n "$tap_desc" ] ; then
|
||
|
echo "1..0 # skip $tap_desc"
|
||
|
else
|
||
|
echo "1..0 # skip"
|
||
|
fi
|
||
|
exit 0
|
||
|
}
|
||
|
|
||
|
# ok takes a test description and a command to run and prints success if that
|
||
|
# command is successful, false otherwise. The count starts at 1 and is
|
||
|
# updated each time ok is printed.
|
||
|
ok () {
|
||
|
tap_desc="$1"
|
||
|
if [ -n "$tap_desc" ] ; then
|
||
|
tap_desc=" - $tap_desc"
|
||
|
fi
|
||
|
shift
|
||
|
if "$@" ; then
|
||
|
echo ok "$count$tap_desc"
|
||
|
else
|
||
|
echo not ok "$count$tap_desc"
|
||
|
failed=`expr $failed + 1`
|
||
|
fi
|
||
|
count=`expr $count + 1`
|
||
|
}
|
||
|
|
||
|
# Skip the next test. Takes the reason why the test is skipped.
|
||
|
skip () {
|
||
|
echo "ok $count # skip $*"
|
||
|
count=`expr $count + 1`
|
||
|
}
|
||
|
|
||
|
# Report the same status on a whole set of tests. Takes the count of tests,
|
||
|
# the description, and then the command to run to determine the status.
|
||
|
ok_block () {
|
||
|
tap_i=$count
|
||
|
tap_end=`expr $count + $1`
|
||
|
shift
|
||
|
while [ "$tap_i" -lt "$tap_end" ] ; do
|
||
|
ok "$@"
|
||
|
tap_i=`expr $tap_i + 1`
|
||
|
done
|
||
|
}
|
||
|
|
||
|
# Skip a whole set of tests. Takes the count and then the reason for skipping
|
||
|
# the test.
|
||
|
skip_block () {
|
||
|
tap_i=$count
|
||
|
tap_end=`expr $count + $1`
|
||
|
shift
|
||
|
while [ "$tap_i" -lt "$tap_end" ] ; do
|
||
|
skip "$@"
|
||
|
tap_i=`expr $tap_i + 1`
|
||
|
done
|
||
|
}
|
||
|
|
||
|
# Portable variant of printf '%s\n' "$*". In the majority of cases, this
|
||
|
# function is slower than printf, because the latter is often implemented
|
||
|
# as a builtin command. The value of the variable IFS is ignored.
|
||
|
#
|
||
|
# This macro must not be called via backticks inside double quotes, since this
|
||
|
# will result in bizarre escaping behavior and lots of extra backslashes on
|
||
|
# Solaris.
|
||
|
puts () {
|
||
|
cat << EOH
|
||
|
$@
|
||
|
EOH
|
||
|
}
|
||
|
|
||
|
# Run a program expected to succeed, and print ok if it does and produces the
|
||
|
# correct output. Takes the description, expected exit status, the expected
|
||
|
# output, the command to run, and then any arguments for that command.
|
||
|
# Standard output and standard error are combined when analyzing the output of
|
||
|
# the command.
|
||
|
#
|
||
|
# If the command may contain system-specific error messages in its output,
|
||
|
# add strip_colon_error before the command to post-process its output.
|
||
|
ok_program () {
|
||
|
tap_desc="$1"
|
||
|
shift
|
||
|
tap_w_status="$1"
|
||
|
shift
|
||
|
tap_w_output="$1"
|
||
|
shift
|
||
|
tap_output=`"$@" 2>&1`
|
||
|
tap_status=$?
|
||
|
if [ $tap_status = $tap_w_status ] \
|
||
|
&& [ x"$tap_output" = x"$tap_w_output" ] ; then
|
||
|
ok "$tap_desc" true
|
||
|
else
|
||
|
echo "# saw: ($tap_status) $tap_output"
|
||
|
echo "# not: ($tap_w_status) $tap_w_output"
|
||
|
ok "$tap_desc" false
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
# Strip a colon and everything after it off the output of a command, as long
|
||
|
# as that colon comes after at least one whitespace character. (This is done
|
||
|
# to avoid stripping the name of the program from the start of an error
|
||
|
# message.) This is used to remove system-specific error messages (coming
|
||
|
# from strerror, for example).
|
||
|
strip_colon_error() {
|
||
|
tap_output=`"$@" 2>&1`
|
||
|
tap_status=$?
|
||
|
tap_output=`puts "$tap_output" | sed 's/^\([^ ]* [^:]*\):.*/\1/'`
|
||
|
puts "$tap_output"
|
||
|
return $tap_status
|
||
|
}
|
||
|
|
||
|
# Bail out with an error message.
|
||
|
bail () {
|
||
|
echo 'Bail out!' "$@"
|
||
|
exit 255
|
||
|
}
|
||
|
|
||
|
# Output a diagnostic on standard error, preceded by the required # mark.
|
||
|
diag () {
|
||
|
echo '#' "$@"
|
||
|
}
|
||
|
|
||
|
# Search for the given file first in $C_TAP_BUILD and then in $C_TAP_SOURCE
|
||
|
# and echo the path where the file was found, or the empty string if the file
|
||
|
# wasn't found.
|
||
|
#
|
||
|
# This macro uses puts, so don't run it using backticks inside double quotes
|
||
|
# or bizarre quoting behavior will happen with Solaris sh.
|
||
|
test_file_path () {
|
||
|
if [ -n "$C_TAP_BUILD" ] && [ -f "$C_TAP_BUILD/$1" ] ; then
|
||
|
puts "$C_TAP_BUILD/$1"
|
||
|
elif [ -n "$C_TAP_SOURCE" ] && [ -f "$C_TAP_SOURCE/$1" ] ; then
|
||
|
puts "$C_TAP_SOURCE/$1"
|
||
|
else
|
||
|
echo ''
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
# Create $C_TAP_BUILD/tmp for use by tests for storing temporary files and
|
||
|
# return the path (via standard output).
|
||
|
#
|
||
|
# This macro uses puts, so don't run it using backticks inside double quotes
|
||
|
# or bizarre quoting behavior will happen with Solaris sh.
|
||
|
test_tmpdir () {
|
||
|
if [ -z "$C_TAP_BUILD" ] ; then
|
||
|
tap_tmpdir="./tmp"
|
||
|
else
|
||
|
tap_tmpdir="$C_TAP_BUILD"/tmp
|
||
|
fi
|
||
|
if [ ! -d "$tap_tmpdir" ] ; then
|
||
|
mkdir "$tap_tmpdir" || bail "Error creating $tap_tmpdir"
|
||
|
fi
|
||
|
puts "$tap_tmpdir"
|
||
|
}
|