#!/bin/bash APP_SVNID='$HeadURL$ $LastChangedRevision$' . $(ade-config ade_share_prefix)/include/ade.sh || { echo "${0##*/}: INTERNAL ERROR: failed to load ade.sh" >&2; exit 3; } ADETEST_DEFINED_ERRORS=( "KEY=ADETEST_ERR_MISC; FMT=\"%s\"" "KEY=ADETEST_ERR_ACCESS; FMT=\"%s: can't %s\"" "KEY=ADETEST_ERR_OS; FMT=\"%s: failed\"" ) adetest() { local OPTVAL LISTPATHS RC TEMP ERRSTACK_REF local ISRELABELABLE_FLAG SANDPIT_DIR REF_FILE OUT_FILE DIR TEST_CMDS local VERBOSELEVEL PROGNAME local -a DOLLARAT ERRSTACK_REF="$1" shift # Register application-specific errors ade_register_error_types ADETEST_DEFINED_ERRORS # Defaults for options OPT_ARGSARELISTGENERATORS_FLAG=false OPT_MODE=test OPT_KEEPOUTFILES_FLAG=false OPT_MAKE_CMD=make # Register adetest's options ade_register_options "$ERRSTACK_REF" -o gtrk --longoptions=generate,args-are-generators,test,run,keep-output-files --callback-template="handle_option_%s" || return $? ade_set_callbacks "$ERRSTACK_REF" adetest_usage_help adetest_version adetest_paths || return $? # Process options ade_process_options "$ERRSTACK_REF" NEW_DOLLAR_AT "$@" || return $? set -- "${NEW_DOLLAR_AT[@]}" # Argument processing [ $# -ge 1 ] || ade_show_bad_usage "$ERRSTACK_REF" # The exact interpreation of the arguments (whether as test generators # or actual tests is made a little bir further below) # Guts # There is a good chance this program is running under make and make # will have contaminated the environment. Amongst other things this # leads to make saying 'Entering ' because it sees a non-empty # MAKELEVEL environment variable. We want the behaviour to be the same # when run directly and when run from a Makefile. So we we unset all the # infectious settings. unset MAKEFLAGS unset MAKEOVERRIDES unset MAKELEVEL # # Find the top of the module # MODULE_ROOT_DIR= DIR=$(pwd) while [ "X$DIR" != X ]; do [ ! -d $DIR/tests/ref ] || { MODULE_ROOT_DIR=$DIR; break; } DIR=${DIR%/*} done [ "X$MODULE_ROOT_DIR" != X ] || { ade_error "$ERRSTACK_REF" ADETEST_ERR_MISC "failed to find module root" return $ADE_FAIL } # # For each test ... # ALL_TESTS_PASSED=true # Probably the test generator is specified as just 'testlist' and # that won't be found since it mentions no directories and '.' is # probably not in $PATH; so we need to fix that by adding it. local -a TEST_CMDS ade_debug "$ERRSTACK_REF" 5 "adetest: collecting test commands ..." if $OPT_ARGSARELISTGENERATORS_FLAG; then ade_debug "$ERRSTACK_REF" 5 "adetest: arguments are supposed to to be test list generators; calling ..." TEST_CMDS=($(eval "PATH=$PATH:.; \"\$@\"")) || { ade_error "$ERRSTACK_REF" ADETEST_ERR_MISC "test list generator failed" return $ADE_FAIL } ade_debug "$ERRSTACK_REF" 5 "adetest: TEST_CMDS (generated)=${TEST_CMDS[@]}" else ade_debug "$ERRSTACK_REF" 5 "adetest: arguments are supposed to to be tests" TEST_CMDS=("$@"); ade_debug "$ERRSTACK_REF" 5 "adetest: TEST_CMDS (listed)=${TEST_CMDS[@]}" fi ade_debug "$ERRSTACK_REF" 5 "adetest: TEST_CMDS=\"$TEST_CMDS\"" for TEST_CMD in ${TEST_CMDS[@]}; do ade_debug "$ERRSTACK_REF" 5 "adetest: TEST_CMD=$TEST_CMD ..." # Check for is '..../bin/something' ade_debug "$ERRSTACK_REF" 5 "adetest: validating filename pattern ..." expr "$TEST_CMD" : 'bin/[^/][^/]*$' > /dev/null || expr "$TEST_CMD" : '.*/bin/[^/][^/]*$' > /dev/null || { ade_error "$ERRSTACK_REF" ADETEST_ERR_MISC "$TEST_CMD: isn't in a 'bin' directory" return $ADE_FAIL } # Make the test script ade_debug "$ERRSTACK_REF" 5 "adetest: making the test script with \"cd $(dirname $TEST_CMD) \&\& $OPT_MAKE_CMD -s $(basename $TEST_CMD)\" ..." [ -d $(dirname $TEST_CMD) ] || { ade_error "$ERRSTACK_REF" ADETEST_ERR_ACCESS "$(dirname $TEST_CMD) (test's directory)" "access" return $ADE_FAIL } ( cd $(dirname $TEST_CMD) && $OPT_MAKE_CMD -s $(basename $TEST_CMD) ) || { ade_error "$ERRSTACK_REF" ADETEST_ERR_OS $OPT_MAKE_CMD return $ADE_FAIL } # Check test exists (don't check before the 'make' paragraph above) ade_debug "$ERRSTACK_REF" 5 "adetest: checking test accessibility ..." [ -x "$TEST_CMD" ] || { ade_error "$ERRSTACK_REF" ADETEST_ERR_ACCESS $TEST_CMD "execute" return $ADE_FAIL } # Derive name of reference file and output file REF_FILE=$(echo $TEST_CMD | sed 's/bin\/\([^\/][^\/]*\)$/ref\/\1/') ade_get_progname "$ERRSTACK_REF" PROGNAME OUT_FILE=/var/tmp/$PROGNAME.$$.${TEST_CMD##*/}.out SANDPIT_DIR=/var/tmp/$PROGNAME.$$.${TEST_CMD##*/}.sandpit # Prepare sandbox ade_debug "$ERRSTACK_REF" 5 "adetest: registering sandbox (SANDPIT_DIR=$SANDPIT_DIR) ..." ade_register_temp_file "$ERRSTACK_REF" $SANDPIT_DIR || return $? ade_debug "$ERRSTACK_REF" 5 "adetest: creating sandbox (SANDPIT_DIR=$SANDPIT_DIR) ..." mkdir -p "$SANDPIT_DIR" ade_debug "$ERRSTACK_REF" 5 "adetest: created sandbox (SANDPIT_DIR=$SANDPIT_DIR)" ade_get_verboselevel "$ERRSTACK_REF" VERBOSELEVEL || return $? # Generate reference file if [ $OPT_MODE = generate ]; then ade_debug "$ERRSTACK_REF" 10 "adetest/generate: REF_FILE=$REF_FILE, PWD=$PWD" [ $VERBOSELEVEL -ge 3 ] && printf "%-60s" "${TEST_CMD##*/}:" >&2 ade_register_temp_file "$ERRSTACK_REF" $REF_FILE || return $? touch $REF_FILE 2> /dev/null || { ade_error "$ERRSTACK_REF" ADETEST_ERR_ACCESS $REF_FILE "create" return $ADE_FAIL } execute_test "$ERRSTACK_REF" "$TEST_CMD" $SANDPIT_DIR RC > $REF_FILE || return $? if [ $RC != 0 ]; then [ $VERBOSELEVEL -ge 3 ] && echo "failed (exit code $RC)" ALL_TESTS_PASSED=false else [ $VERBOSELEVEL -ge 3 ] && echo "generated" fi ade_debug "$ERRSTACK_REF" 10 "adetest/generate: about to deregister $REF_FILE ..." ade_deregister_temp_file "$ERRSTACK_REF" $REF_FILE || return $? # Make the test elif [ $OPT_MODE = test ]; then ade_debug "$ERRSTACK_REF" 10 "adetest/test: checking reference file accessibility ..." [ -r $REF_FILE ] || { ade_error "$ERRSTACK_REF" ADETEST_ERR_ACCESS $REF_FILE "access" return $ADE_FAIL } [ $VERBOSELEVEL -ge 3 ] && printf "%-70s" "${TEST_CMD##*/}:" >&2 ade_register_temp_file "$ERRSTACK_REF" $OUT_FILE || return $? touch $OUT_FILE 2> /dev/null || { ade_error "$ERRSTACK_REF" ADETEST_ERR_ACCESS $OUT_FILE "create" return $ADE_FAIL } execute_test "$ERRSTACK_REF" "$TEST_CMD" $SANDPIT_DIR RC > $OUT_FILE || return $? if [ $RC != 0 ]; then [ $VERBOSELEVEL -ge 3 ] && echo "failed (exit code $RC)" >&2 ALL_TESTS_PASSED=false elif diff $REF_FILE $OUT_FILE > /dev/null; then [ $VERBOSELEVEL -ge 3 ] && echo "passed" >&2 else [ $VERBOSELEVEL -ge 3 ] && echo "failed (differs from ref)" >&2 ALL_TESTS_PASSED=false fi if $OPT_KEEPOUTFILES_FLAG; then [ $VERBOSELEVEL -ge 3 ] && { echo " reference: $REF_FILE" >&2 echo " output: $OUT_FILE" >&2 } else rm -f $OUT_FILE fi ade_deregister_temp_file "$ERRSTACK_REF" $OUT_FILE || return $? elif [ $OPT_MODE = run ]; then ade_debug "$ERRSTACK_REF" 10 "adetest/run: " execute_test "$ERRSTACK_REF" "$TEST_CMD" $SANDPIT_DIR RC || return $? if [ $RC != 0 ]; then [ $VERBOSELEVEL -ge 3 ] && echo "failed (exit code $RC)" >&2 ALL_TESTS_PASSED=false fi else ade_internal "$ERRSTACK_REF" "adetest: illegal OPT_MODE=$OPT_MODE" fi # Purge sandbox if $OPT_KEEPOUTFILES_FLAG; then [ $VERBOSELEVEL -ge 3 ] && echo " sandpit: $SANDPIT_DIR" >&2 else rm -fr $SANDPIT_DIR fi ade_deregister_temp_file "$ERRSTACK_REF" $SANDPIT_DIR || return $? done [ $ALL_TESTS_PASSED = true ] || { ade_error "$ERRSTACK_REF" ADETEST_ERR_MISC "at least one test failed" return $ADE_FAIL } return $ADE_OK } handle_option_g() { handle_option_generate "$@" } handle_option_t() { handle_option_test "$@" } handle_option_r() { handle_option_run "$@" } handle_option_k() { handle_option_keep_output_files "$@" } handle_option_generate() { OPT_MODE=generate } handle_option_args_are_generators() { OPT_ARGSARELISTGENERATORS_FLAG=true } handle_option_test() { OPT_MODE=test } handle_option_run() { OPT_MODE=run } handle_option_keep_output_files() { OPT_KEEPOUTFILES_FLAG=true } execute_test() { local ERRSTACK_REF="$1"; shift local TEST_CMD="$1"; shift local SANDPIT_DIR="$1"; shift local RC_REF="$1"; shift local PWD OLD_PWD DIR ABS_DIR PWD=$(pwd) # Make the test command absolute so we can run it from the sandpit. ade_debug "$ERRSTACK_REF" 10 "execute_test: making \"$TEST_CMD\" absolute ..." ade_get_absolute_path "$ERRSTACK_REF" "$TEST_CMD" "" TEST_CMD || { ade_internal "$ERRSTACK_REF" "execute_test: ade_get_absolute_path() failed" return $ADE_FAIL } ade_debug "$ERRSTACK_REF" 10 "execute_test: TEST_CMD=$TEST_CMD" # Make the test configuration directory absolute ade_debug "$ERRSTACK_REF" 10 "execute_test: making test configuration directory absolute ..." # Prepare the test environment ade_debug "$ERRSTACK_REF" 10 "execute_test: preparing the test environment ..." # Add the module's bin and sbin to the $PATH that will be used for the test TEST_PATH=$PATH for DIR in sbin bin; do TEST_PATH="$MODULE_ROOT_DIR/$DIR:$TEST_PATH" done # Add the module's share directory to PERL5LIB (so mkfad can find FAD.pm, etc) TEST_PERL5LIB=$PERL5LIB for DIR in share; do TEST_PERL5LIB="$MODULE_ROOT_DIR/$DIR:$TEST_PERL5LIB" done # some module include tests running dynamically linked commands with libraries provided by the module TEST_LD_LIBRARY_PATH=$(ade-config ade_share_prefix):$LD_LIBRARY_PATH # Determine verbosity level for test # Some of the tests will invoke 'make'; we need to make sure that they invoke the right make. TEST_MAKE_CMD=$OPT_MAKE_CMD # Create the sandpit in which to execute the test ade_debug "$ERRSTACK_REF" 10 "execute_test: creating a sandpit ..." OLD_PWD=$(pwd) cd $SANDPIT_DIR # Call the test ade_debug "$ERRSTACK_REF" 10 "execute_test: calling \"ADETEST_MODROOT=$MODULE_ROOT_DIR MAKE=$TEST_MAKE_CMD LD_LIBRARY_PATH=$TEST_LD_LIBRARY_PATH PATH=$TEST_PATH PERL5LIB=$TEST_PERL5LIB $TEST_CMD 2>&1\" ..." ADETEST_MODROOT=$MODULE_ROOT_DIR MAKE=$TEST_MAKE_CMD LD_LIBRARY_PATH=$TEST_LD_LIBRARY_PATH PATH=$TEST_PATH PERL5LIB=$TEST_PERL5LIB $TEST_CMD 2>&1 eval "$RC_REF=$?" cd $OLD_PWD return $ADE_OK } adetest_usage_help() { local ERRSTACK_REF="$1"; shift local USAGE_TEXT_SHORT_REF="$1"; shift local USAGE_TEXT_LONG_REF="$1"; shift eval "$USAGE_TEXT_SHORT_REF=\"\"" eval "$USAGE_TEXT_LONG_REF=\"\ -g | --generate generate mode -t | --test test mode (default) -r | --run run mode -k | --keep-output-files don't delete test output --args-are-generators args are generators\"" return $ADE_OK } adetest_paths() { local ERRSTACK_REF="$1"; shift local PATHLIST_REF=$1; shift eval "$PATHLIST_REF=\"\ \"" return $ADE_OK } adetest_version() { local ERRSTACK_REF="$1"; shift local VERSION_REF=$1; shift ade_extract_version "$ERRSTACK_REF" "$APP_SVNID" "$VERSION_REF" return $ADE_OK } ade_main adetest "$@"