From ea15324e30a185d87a0bc86344ea3d135a0c9224 Mon Sep 17 00:00:00 2001 From: don fong Date: Mon, 25 Jul 2011 17:04:09 -0700 Subject: add support for new framework for functional tests --- test/Makefile | 6 ++- test/README-testframe.txt | 39 +++++++++++++++ test/functests/common.inc | 52 ++++++++++++++++++++ test/functests/dont_test_false.sh | 7 +++ test/functests/test_null.sh | 13 +++++ test/functests/test_true.sh | 8 ++++ test/functests/test_walkone.sh | 55 ++++++++++++++++++++++ test/testframe.inc | 55 ++++++++++++++++++++++ test/testframe.sh | 99 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 test/README-testframe.txt create mode 100644 test/functests/common.inc create mode 100644 test/functests/dont_test_false.sh create mode 100644 test/functests/test_null.sh create mode 100644 test/functests/test_true.sh create mode 100755 test/functests/test_walkone.sh create mode 100644 test/testframe.inc create mode 100755 test/testframe.sh (limited to 'test') diff --git a/test/Makefile b/test/Makefile index 10877c1..e92e85f 100644 --- a/test/Makefile +++ b/test/Makefile @@ -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 diff --git a/test/README-testframe.txt b/test/README-testframe.txt new file mode 100644 index 0000000..f049040 --- /dev/null +++ b/test/README-testframe.txt @@ -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. diff --git a/test/functests/common.inc b/test/functests/common.inc new file mode 100644 index 0000000..a011982 --- /dev/null +++ b/test/functests/common.inc @@ -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 +} diff --git a/test/functests/dont_test_false.sh b/test/functests/dont_test_false.sh new file mode 100644 index 0000000..b05f06c --- /dev/null +++ b/test/functests/dont_test_false.sh @@ -0,0 +1,7 @@ +# a testsuite that will force failure - for testing purposes + +run() +{ + run_testcase false +} + diff --git a/test/functests/test_null.sh b/test/functests/test_null.sh new file mode 100644 index 0000000..09e7560 --- /dev/null +++ b/test/functests/test_null.sh @@ -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)" +} diff --git a/test/functests/test_true.sh b/test/functests/test_true.sh new file mode 100644 index 0000000..5953f5c --- /dev/null +++ b/test/functests/test_true.sh @@ -0,0 +1,8 @@ +# test suite that always succeds - for testing framework + +run() +{ + run_testcase true + return 0 +} + diff --git a/test/functests/test_walkone.sh b/test/functests/test_walkone.sh new file mode 100755 index 0000000..1902926 --- /dev/null +++ b/test/functests/test_walkone.sh @@ -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)" +} diff --git a/test/testframe.inc b/test/testframe.inc new file mode 100644 index 0000000..b5f66a3 --- /dev/null +++ b/test/testframe.inc @@ -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 +} diff --git a/test/testframe.sh b/test/testframe.sh new file mode 100755 index 0000000..22975b6 --- /dev/null +++ b/test/testframe.sh @@ -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 "${@:-.}" -- cgit v1.2.1