Merge pull request #2 from dfong/master

new framework for functional tests
This commit is contained in:
wolfcw
2011-07-25 22:15:25 -07:00
9 changed files with 333 additions and 1 deletions

View File

@@ -14,10 +14,14 @@ all: timetest test
timetest: ${OBJ}
${CC} -o $@ ${OBJ} ${LDFLAGS}
test: timetest
test: timetest functest
@echo
@./test.sh
# run functional tests
functest:
./testframe.sh functests
clean:
@rm -f ${OBJ} timetest

39
test/README-testframe.txt Normal file
View File

@@ -0,0 +1,39 @@
# here's how the testframe script works.
#
# Usage for testing:
# usage: testframe.sh DIR
# testframe.sh runs each testsuite script found within DIR.
# (in the context of libfaketime, the DIR is functest.)
# exits with status 0 if all tests succeed.
#
# Interface:
# by convention, each testsuite script (within DIR) must be
# a bash script named test_*.sh. the script must define a
# function named "run". run takes no arguments. run is
# expected to call the framework-provided function
# run_testcase once for each test function. run_testcase
# uses the global vars NFAIL and NSUCC to keep track of how
# many testcases failed/succeeded.
#
# the test function is expected to call something like
# asserteq or assertneq (again, framework-provided).
#
# fine print: for each testsuite, the framework creates a
# subshell and dots in the script. also dotted in are
# testframe.inc and DIR/common.inc (if it exists). the
# testsuite script can make use of any functions defined
# in these inc files. the environment variable
# TESTSUITE_NAME is set to the filename of the testsuite
# script, for possible use in warning or info messages.
#
# see functests/test_true.sh for a simple example of
# a test suite script.
#
# Simple steps to add a new testsuite:
# 1. decide its name - eg, XXX.
# 2. choose a DIR of similar testsuites to put it in, or create a new one.
# 3. create DIR/test_XXX.sh.
# 4. write a run function and testcase functions in DIR/test_XXX.sh.
# 5. within the run function, call run_testcase for each testcase function.
# 6. within each testcase funtion, call assertneq or asserteq, or do
# the equivalent.

52
test/functests/common.inc Normal file
View File

@@ -0,0 +1,52 @@
# libfaketime-specific common support routines for tests
# say which *_fakecmd wrapper to use
platform()
{
# may want to expand the pattern for linuxlike
typeset out=$(uname)
case "$out" in
*OSX*) echo "mac" ;;
*Linux*) echo "linuxlike" ;;
*) echo 1>&2 unsupported platform, uname=\"$out\" ;;
esac
}
# run faked command on a mac
# UNTESTED
mac_fakecmd()
{
typeset timestring="$1"; shift
typeset fakelib=../src/libfaketime.so.1
export DYLD_INSERT_LIBRARIES=$fakelib
export DYLD_FORCE_FLAT_NAMESPACE=1
FAKETIME="$timestring" \
"$@"
}
# run faked command on linuxlike OS
linuxlike_fakecmd()
{
typeset timestring="$1"; shift
typeset fakelib=../src/libfaketime.so.1
export LD_PRELOAD=$fakelib
FAKETIME="$timestring" \
"$@"
}
# run a command with libfaketime using the given timestring
fakecmd()
{
${PLATFORM}_fakecmd "$@"
}
# generate a sequence of numbers from a to b
range()
{
typeset a=$1 b=$2
typeset i=$a
while ((i <= b)); do
echo $i
((i = i+1))
done
}

View File

@@ -0,0 +1,7 @@
# a testsuite that will force failure - for testing purposes
run()
{
run_testcase false
}

View File

@@ -0,0 +1,13 @@
# check that the date doesn't happen to be 0.
run()
{
run_testcase nulltest
}
nulltest()
{
typeset tdate=${I2DATES[0]}
assertneq 0 "$(date +%s)" "($tdate)"
}

View File

@@ -0,0 +1,8 @@
# test suite that always succeds - for testing framework
run()
{
run_testcase true
return 0
}

55
test/functests/test_walkone.sh Executable file
View File

@@ -0,0 +1,55 @@
# walking-1 test.
# sourced in from testframe.sh.
#
# this script defines a suite of functional tests
# that verifies the correct operation of libfaketime
# with the date command.
run()
{
init
for i in $(range 0 30); do
run_testcase test_with_i $i
done
}
# ----- support routines
init()
{
typeset testsuite="$1"
PLATFORM=$(platform)
if [ -z "$PLATFORM" ]; then
echo "$testsuite: unknown platform! quitting"
return 1
fi
echo "# PLATFORM=$PLATFORM"
return 0
}
# run date cmd under faketime, print time in secs
fakedate()
{
#
# let the time format be raw seconds since Epoch
# for both input to libfaketime, and output of the date cmd.
#
typeset fmt='%s'
export FAKETIME_FMT=$fmt
fakecmd "$1" date +$fmt
}
pow()
{
dc -e "$1 $2 ^ p"
}
# run a fakedate test with a given time t
test_with_i()
{
typeset i="$1"
typeset t=$(pow 2 $i)
asserteq $(fakedate $t) $t "(secs since Epoch)"
}

55
test/testframe.inc Normal file
View File

@@ -0,0 +1,55 @@
# framework common functions for use in test suites and test cases
#
# run a test and keep stats on success/failure.
# arguments: a command, possibly a shell function.
# return value: 0 on success, 1 on failure.
# side effects: increments global var NSUCC on success, NFAIL on failure.
#
run_testcase()
{
if "$@"; then
((NSUCC++))
return 0
else
((NFAIL++))
return 1
fi
}
#
# verbosely check that the test output matches the expected value.
# arguments: the test output, the expected value, and a description.
# return value: 0 on if test output equals expected value; 1 otherwise.
# side effects: prints a descriptive message.
#
asserteq()
{
typeset out="$1" expected="$2" desc="$3"
echo -n "out=$out $desc"
if [ "$out" = "$expected" ]; then
echo " - ok"
return 0
else
echo " expected=$expected - bad"
return 1
fi
}
#
# verbosely check that the test output doesn't match the reference value.
# return value: 1 on if test output equals expected value; 0 if not equal.
# side effects: prints descriptive message.
#
assertneq()
{
typeset out="$1" ref="$2" desc="$3"
echo -n "out=$out $desc"
if [ "$out" = "$ref" ]; then
echo " ref=$ref - bad"
return 1
else
echo " ref=$ref - ok"
return 0
fi
}

99
test/testframe.sh Executable file
View File

@@ -0,0 +1,99 @@
#! /bin/bash
# testframe.sh DIR
# bare-bones testing framework.
# run the test suites in the given DIR;
# exit with nonzero status if any of them failed.
# see README.testframe.txt for details.
#
# echo labelled error/warning message to stderr
report()
{
echo $PROG: $* 1>&2
}
# echo OK or BAD depending on argument (0 or not)
status_word()
{
if [ "$1" -eq 0 ]; then
echo OK
else
echo BAD
fi
}
# run the given testsuite, return nonzero if any testcase failed.
run_testsuite()
{
typeset testsuite="$1"
NFAIL=0
NSUCC=0
# add testsuite dir to PATH for convenience
typeset dir=$(dirname $testsuite)
PATH=$dir:$PATH
. testframe.inc
if [ -f $dir/common.inc ]; then
. $dir/common.inc
fi
. $testsuite
export TESTSUITE_NAME=$testsuite
echo ""
echo "# Begin $testsuite"
run
typeset runstat=$?
echo "# $testsuite summary: $NSUCC succeeded, $NFAIL failed"
if [ $runstat -ne 0 ]; then
((NFAIL++))
report "error: $testsuite run exit_status=$runstat!"
fi
echo "# End $testsuite -" $(status_word $NFAIL)
[ $NFAIL -eq 0 ]
}
#
# list all testsuite scripts in the given directories.
# a testsuite file must be a bash script whose name is of the form test_*.sh .
#
list_testsuites()
{
for dir in "$@"; do
ls $dir/test_*.sh 2>/dev/null
done
}
main()
{
TS_NFAIL=0
TS_NSUCC=0
echo "# Begin Test Suites in $*"
typeset testsuites=$(list_testsuites "$@")
if [ -z "$testsuites" ]; then
report "error: no testsuites found"
exit 1
fi
for testsuite in $testsuites; do
if run_testsuite $testsuite; then
((TS_NSUCC++))
else
((TS_NFAIL++))
fi
done
echo ""
echo "# Test Suites summary: $TS_NSUCC succeeded, $TS_NFAIL failed"
echo "# End Test Suites -" $(status_word $TS_NFAIL)
[ $TS_NFAIL -eq 0 ]
}
# ----- start of mainline code
PROG=${0##*/}
main "${@:-.}"