1# Simple test harness infrastructure 2# 3# Copyright 2005 by Rob Landley 4 5# This file defines two main functions, "testcmd" and "optional". The 6# first performs a test, the second enables/disables tests based on 7# configuration options. 8 9# The following environment variables enable optional behavior in "testing": 10# DEBUG - Show every command run by test script. 11# VERBOSE - Print the diff -u of each failed test case. 12# If equal to "fail", stop after first failed test. 13# "nopass" to not show successful tests 14# 15# The "testcmd" function takes five arguments: 16# $1) Description to display when running command 17# $2) Command line arguments to command 18# $3) Expected result (on stdout) 19# $4) Data written to file "input" 20# $5) Data written to stdin 21# 22# The "testing" function is like testcmd but takes a complete command line 23# (I.E. you have to include the command name.) The variable $C is an absolute 24# path to the command being tested, which can bypass shell builtins. 25# 26# The exit value of testcmd is the exit value of the command it ran. 27# 28# The environment variable "FAILCOUNT" contains a cumulative total of the 29# number of failed tests. 30# 31# The "optional" function is used to skip certain tests (by setting the 32# environment variable SKIP), ala: 33# optional CFG_THINGY 34# 35# The "optional" function checks the environment variable "OPTIONFLAGS", 36# which is either empty (in which case it always clears SKIP) or 37# else contains a colon-separated list of features (in which case the function 38# clears SKIP if the flag was found, or sets it to 1 if the flag was not found). 39 40export FAILCOUNT=0 41export SKIP= 42 43# Helper functions 44 45# Check config to see if option is enabled, set SKIP if not. 46 47SHOWPASS=PASS 48SHOWFAIL=FAIL 49SHOWSKIP=SKIP 50 51if tty -s <&1 52then 53 SHOWPASS="$(echo -e "\033[1;32m${SHOWPASS}\033[0m")" 54 SHOWFAIL="$(echo -e "\033[1;31m${SHOWFAIL}\033[0m")" 55 SHOWSKIP="$(echo -e "\033[1;33m${SHOWSKIP}\033[0m")" 56fi 57 58optional() 59{ 60 option=`printf %s "$OPTIONFLAGS" | egrep "(^|:)$1(:|\$)"` 61 # Not set? 62 if [ -z "$1" ] || [ -z "$OPTIONFLAGS" ] || [ ${#option} -ne 0 ] 63 then 64 SKIP="" 65 return 66 fi 67 SKIP=1 68} 69 70skipnot() 71{ 72 if [ -z "$VERBOSE" ] 73 then 74 eval "$@" 2>/dev/null 75 else 76 eval "$@" 77 fi 78 [ $? -eq 0 ] || SKIPNEXT=1 79} 80 81toyonly() 82{ 83 IS_TOYBOX="$("$C" --version 2>/dev/null)" 84 [ "${IS_TOYBOX/toybox/}" == "$IS_TOYBOX" ] && SKIPNEXT=1 85 86 "$@" 87} 88 89wrong_args() 90{ 91 if [ $# -ne 5 ] 92 then 93 printf "%s\n" "Test $NAME has the wrong number of arguments ($# $*)" >&2 94 exit 95 fi 96} 97 98# The testing function 99 100testing() 101{ 102 NAME="$CMDNAME $1" 103 wrong_args "$@" 104 105 [ -z "$1" ] && NAME=$2 106 107 [ -n "$DEBUG" ] && set -x 108 109 if [ -n "$SKIP" -o -n "$SKIP_HOST" -a -n "$TEST_HOST" -o -n "$SKIPNEXT" ] 110 then 111 [ ! -z "$VERBOSE" ] && printf "%s\n" "$SHOWSKIP: $NAME" 112 unset SKIPNEXT 113 return 0 114 fi 115 116 echo -ne "$3" > expected 117 echo -ne "$4" > input 118 echo -ne "$5" | ${EVAL:-eval} -- "$2" > actual 119 RETVAL=$? 120 121 # Catch segfaults 122 [ $RETVAL -gt 128 ] && [ $RETVAL -lt 255 ] && 123 echo "exited with signal (or returned $RETVAL)" >> actual 124 DIFF="$(diff -au${NOSPACE:+w} expected actual)" 125 if [ ! -z "$DIFF" ] 126 then 127 FAILCOUNT=$(($FAILCOUNT+1)) 128 printf "%s\n" "$SHOWFAIL: $NAME" 129 if [ -n "$VERBOSE" ] 130 then 131 [ ! -z "$4" ] && printf "%s\n" "echo -ne \"$4\" > input" 132 printf "%s\n" "echo -ne '$5' |$EVAL $2" 133 printf "%s\n" "$DIFF" 134 [ "$VERBOSE" == fail ] && exit 1 135 fi 136 else 137 [ "$VERBOSE" != "nopass" ] && printf "%s\n" "$SHOWPASS: $NAME" 138 fi 139 rm -f input expected actual 140 141 [ -n "$DEBUG" ] && set +x 142 143 return 0 144} 145 146testcmd() 147{ 148 wrong_args "$@" 149 150 X="$1" 151 [ -z "$X" ] && X="$CMDNAME $2" 152 testing "$X" "\"$C\" $2" "$3" "$4" "$5" 153} 154 155# Recursively grab an executable and all the libraries needed to run it. 156# Source paths beginning with / will be copied into destpath, otherwise 157# the file is assumed to already be there and only its library dependencies 158# are copied. 159 160mkchroot() 161{ 162 [ $# -lt 2 ] && return 163 164 echo -n . 165 166 dest=$1 167 shift 168 for i in "$@" 169 do 170 [ "${i:0:1}" == "/" ] || i=$(which $i) 171 [ -f "$dest/$i" ] && continue 172 if [ -e "$i" ] 173 then 174 d=`echo "$i" | grep -o '.*/'` && 175 mkdir -p "$dest/$d" && 176 cat "$i" > "$dest/$i" && 177 chmod +x "$dest/$i" 178 else 179 echo "Not found: $i" 180 fi 181 mkchroot "$dest" $(ldd "$i" | egrep -o '/.* ') 182 done 183} 184 185# Set up a chroot environment and run commands within it. 186# Needed commands listed on command line 187# Script fed to stdin. 188 189dochroot() 190{ 191 mkdir tmpdir4chroot 192 mount -t ramfs tmpdir4chroot tmpdir4chroot 193 mkdir -p tmpdir4chroot/{etc,sys,proc,tmp,dev} 194 cp -L testing.sh tmpdir4chroot 195 196 # Copy utilities from command line arguments 197 198 echo -n "Setup chroot" 199 mkchroot tmpdir4chroot $* 200 echo 201 202 mknod tmpdir4chroot/dev/tty c 5 0 203 mknod tmpdir4chroot/dev/null c 1 3 204 mknod tmpdir4chroot/dev/zero c 1 5 205 206 # Copy script from stdin 207 208 cat > tmpdir4chroot/test.sh 209 chmod +x tmpdir4chroot/test.sh 210 chroot tmpdir4chroot /test.sh 211 umount -l tmpdir4chroot 212 rmdir tmpdir4chroot 213} 214