runtest.sh 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. # Simple test harness infrastructure
  2. #
  3. # Copyright 2005 by Rob Landley
  4. # This file defines two main functions, "testcmd" and "optional". The
  5. # first performs a test, the second enables/disables tests based on
  6. # configuration options.
  7. # The following environment variables enable optional behavior in "testing":
  8. # DEBUG - Show every command run by test script.
  9. # VERBOSE - "all" continue after failed test
  10. # "fail" show diff and stop at first failed test
  11. # "nopass" don't show successful tests
  12. # "quiet" don't show diff -u for failures
  13. # "spam" show passing test command lines
  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. export FAILCOUNT=0
  40. export SKIP=
  41. # Helper functions
  42. # Check config to see if option is enabled, set SKIP if not.
  43. SHOWPASS=PASS
  44. SHOWFAIL=FAIL
  45. SHOWSKIP=SKIP
  46. if tty -s <&1
  47. then
  48. SHOWPASS="$(echo -e "\033[1;32m${SHOWPASS}\033[0m")"
  49. SHOWFAIL="$(echo -e "\033[1;31m${SHOWFAIL}\033[0m")"
  50. SHOWSKIP="$(echo -e "\033[1;33m${SHOWSKIP}\033[0m")"
  51. fi
  52. optional()
  53. {
  54. option=`printf %s "$OPTIONFLAGS" | egrep "(^|:)$1(:|\$)"`
  55. # Not set?
  56. if [ -z "$1" ] || [ -z "$OPTIONFLAGS" ] || [ ${#option} -ne 0 ]
  57. then
  58. unset SKIP
  59. return
  60. fi
  61. SKIP=1
  62. }
  63. verbose_has()
  64. {
  65. [ "${VERBOSE/$1/}" != "$VERBOSE" ]
  66. }
  67. skipnot()
  68. {
  69. if verbose_has quiet
  70. then
  71. eval "$@" 2>/dev/null
  72. else
  73. eval "$@"
  74. fi
  75. [ $? -eq 0 ] || SKIPNEXT=1
  76. }
  77. toyonly()
  78. {
  79. IS_TOYBOX="$("$C" --version 2>/dev/null)"
  80. # Ideally we'd just check for "toybox", but toybox sed lies to make autoconf
  81. # happy, so we have at least two things to check for.
  82. case "$IS_TOYBOX" in
  83. toybox*) ;;
  84. This\ is\ not\ GNU*) ;;
  85. *) SKIPNEXT=1 ;;
  86. esac
  87. "$@"
  88. }
  89. wrong_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. # Announce success
  98. do_pass()
  99. {
  100. ! verbose_has nopass && printf "%s\n" "$SHOWPASS: $NAME"
  101. }
  102. # The testing function
  103. testing()
  104. {
  105. NAME="$CMDNAME $1"
  106. wrong_args "$@"
  107. [ -z "$1" ] && NAME=$2
  108. [ -n "$DEBUG" ] && set -x
  109. if [ -n "$SKIP" -o -n "$SKIP_HOST" -a -n "$TEST_HOST" -o -n "$SKIPNEXT" ]
  110. then
  111. verbose_has quiet && printf "%s\n" "$SHOWSKIP: $NAME"
  112. unset SKIPNEXT
  113. return 0
  114. fi
  115. echo -ne "$3" > expected
  116. [ ! -z "$4" ] && echo -ne "$4" > input || rm -f input
  117. echo -ne "$5" | ${EVAL:-eval --} "$2" > actual
  118. RETVAL=$?
  119. # Catch segfaults
  120. [ $RETVAL -gt 128 ] && [ $RETVAL -lt 255 ] &&
  121. echo "exited with signal (or returned $RETVAL)" >> actual
  122. DIFF="$(diff -au${NOSPACE:+w} expected actual)"
  123. if [ -n "$DIFF" ]
  124. then
  125. FAILCOUNT=$(($FAILCOUNT+1))
  126. printf "%s\n" "$SHOWFAIL: $NAME"
  127. else
  128. ! verbose_has nopass && printf "%s\n" "$SHOWPASS: $NAME"
  129. fi
  130. if ! verbose_has quiet && { [ -n "$DIFF" ] || verbose_has spam; }
  131. then
  132. [ ! -z "$4" ] && printf "%s\n" "echo -ne \"$4\" > input"
  133. printf "%s\n" "echo -ne '$5' |$EVAL $2"
  134. [ -n "$DIFF" ] && printf "%s\n" "$DIFF"
  135. fi
  136. [ -n "$DIFF" ] && ! verbose_has all && exit 1
  137. rm -f input expected actual
  138. [ -n "$DEBUG" ] && set +x
  139. return 0
  140. }
  141. testcmd()
  142. {
  143. wrong_args "$@"
  144. X="$1"
  145. [ -z "$X" ] && X="$CMDNAME $2"
  146. testing "$X" "\"$C\" $2" "$3" "$4" "$5"
  147. }
  148. # Announce failure and handle fallout for txpect
  149. do_fail()
  150. {
  151. FAILCOUNT=$(($FAILCOUNT+1))
  152. printf "%s\n" "$SHOWFAIL: $NAME"
  153. if [ ! -z "$CASE" ]
  154. then
  155. echo "Expected '$CASE'"
  156. echo "Got '$A'"
  157. fi
  158. ! verbose_has all && exit 1
  159. }
  160. # txpect NAME COMMAND [I/O/E/Xstring]...
  161. # Run COMMAND and interact with it: send I strings to input, read O or E
  162. # strings from stdout or stderr (empty string is "read line of input here"),
  163. # X means close stdin/stdout/stderr and match return code (blank means nonzero)
  164. txpect()
  165. {
  166. local NAME CASE VERBOSITY LEN A B X O
  167. # Run command with redirection through fifos
  168. NAME="$CMDNAME $1"
  169. CASE=
  170. VERBOSITY=
  171. if [ $# -lt 2 ] || ! mkfifo in-$$ out-$$ err-$$
  172. then
  173. do_fail
  174. return
  175. fi
  176. eval "$2" <in-$$ >out-$$ 2>err-$$ &
  177. shift 2
  178. : {IN}>in-$$ {OUT}<out-$$ {ERR}<err-$$ && rm in-$$ out-$$ err-$$
  179. [ $? -ne 0 ] && { do_fail;return;}
  180. # Loop through challenge/response pairs, with 2 second timeout
  181. while [ $# -gt 0 ]
  182. do
  183. VERBOSITY="$VERBOSITY"$'\n'"$1" LEN=$((${#1}-1)) CASE="$1" A= B=
  184. verbose_has spam && echo "txpect $CASE"
  185. case ${1::1} in
  186. # send input to child
  187. I) printf %s "${1:1}" >&$IN || { do_fail;break;} ;;
  188. R) LEN=0; B=1; ;&
  189. # check output from child
  190. [OE])
  191. [ $LEN == 0 ] && LARG="" || LARG="-rN $LEN"
  192. O=$OUT A=
  193. [ "${1:$B:1}" == 'E' ] && O=$ERR
  194. read -t2 $LARG A <&$O
  195. X=$?
  196. verbose_has spam && echo "txgot $X '$A'"
  197. VERBOSITY="$VERBOSITY"$'\n'"$A"
  198. if [ $LEN -eq 0 ]
  199. then
  200. [ -z "$A" -o "$X" -ne 0 ] && { do_fail;break;}
  201. else
  202. if [ ${1::1} == 'R' ] && [[ "$A" =~ "${1:2}" ]]; then true
  203. elif [ ${1::1} != 'R' ] && [ "$A" == "${1:1}" ]; then true
  204. else
  205. # Append the rest of the output if there is any.
  206. read -t.1 B <&$O
  207. A="$A$B"
  208. read -t.1 -rN 9999 B<&$ERR
  209. do_fail;break;
  210. fi
  211. fi
  212. ;;
  213. # close I/O and wait for exit
  214. X)
  215. exec {IN}<&- {OUT}<&- {ERR}<&-
  216. wait
  217. A=$?
  218. if [ -z "$LEN" ]
  219. then
  220. [ $A -eq 0 ] && { do_fail;break;} # any error
  221. else
  222. [ $A != "${1:1}" ] && { do_fail;break;} # specific value
  223. fi
  224. ;;
  225. *) do_fail; break ;;
  226. esac
  227. shift
  228. done
  229. # In case we already closed it
  230. exec {IN}<&- {OUT}<&- {ERR}<&-
  231. if [ $# -eq 0 ]
  232. then
  233. do_pass
  234. else
  235. ! verbose_has quiet && echo "$VERBOSITY" >&2
  236. fi
  237. }
  238. # Recursively grab an executable and all the libraries needed to run it.
  239. # Source paths beginning with / will be copied into destpath, otherwise
  240. # the file is assumed to already be there and only its library dependencies
  241. # are copied.
  242. mkchroot()
  243. {
  244. [ $# -lt 2 ] && return
  245. echo -n .
  246. dest=$1
  247. shift
  248. for i in "$@"
  249. do
  250. [ "${i:0:1}" == "/" ] || i=$(which $i)
  251. [ -f "$dest/$i" ] && continue
  252. if [ -e "$i" ]
  253. then
  254. d=`echo "$i" | grep -o '.*/'` &&
  255. mkdir -p "$dest/$d" &&
  256. cat "$i" > "$dest/$i" &&
  257. chmod +x "$dest/$i"
  258. else
  259. echo "Not found: $i"
  260. fi
  261. mkchroot "$dest" $(ldd "$i" | egrep -o '/.* ')
  262. done
  263. }
  264. # Set up a chroot environment and run commands within it.
  265. # Needed commands listed on command line
  266. # Script fed to stdin.
  267. dochroot()
  268. {
  269. mkdir tmpdir4chroot
  270. mount -t ramfs tmpdir4chroot tmpdir4chroot
  271. mkdir -p tmpdir4chroot/{etc,sys,proc,tmp,dev}
  272. cp -L testing.sh tmpdir4chroot
  273. # Copy utilities from command line arguments
  274. echo -n "Setup chroot"
  275. mkchroot tmpdir4chroot $*
  276. echo
  277. mknod tmpdir4chroot/dev/tty c 5 0
  278. mknod tmpdir4chroot/dev/null c 1 3
  279. mknod tmpdir4chroot/dev/zero c 1 5
  280. # Copy script from stdin
  281. cat > tmpdir4chroot/test.sh
  282. chmod +x tmpdir4chroot/test.sh
  283. chroot tmpdir4chroot /test.sh
  284. umount -l tmpdir4chroot
  285. rmdir tmpdir4chroot
  286. }