Fix testsuite for the new continutation syntax using a '!' after the event count
[nobug.git] / tests / test.sh
blob2b239ebed1adde2fe7d0f75ac81c242ffd151272
1 #!/usr/bin/env bash
2 # Copyright (C) Lumiera.org
3 # 2007, 2008, 2009, 2010, Christian Thaeter <ct@pipapo.org>
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License as
7 # published by the Free Software Foundation; either version 2 of the
8 # License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 # TESTMODE=FULL yet unimplemented
20 # run all tests, PLANNED which fail count as error
22 # TESTMODE=FAST
23 # run only tests which recently failed
25 # TESTMODE=FIRSTFAIL
26 # stop testing on the first failure
28 #intro Test.sh
29 #intro =======
30 #intro Christian Thäter
31 #intro
32 #intro A shell script driving software tests.
33 #intro
35 #=intro
36 #=tests Writing tests
37 # =run
38 #=config
39 #=configf
40 # =make
41 # =control
42 #=valgrind
43 #=libtool
45 #_ Index
46 #_ -----
48 #=index
51 LC_ALL=C
53 #config HEAD- Configuration; configuration; configure tests
54 #config
55 #config PARA LOGSUPPRESS; LOGSUPPRESS; suppress certain lines from stderr
56 #config LOGSUPPRESS='^\(\*\*[0-9]*\*\* \)\?[0-9]\{10,\}: \(TRACE\|INFO\|NOTICE\|WARNING\|ERR\):'
57 #config
58 #config Programms sometimes emit additional diagnostics on stderr which is volatile and not necessary for
59 #config validating the output the `LOGSUPRESS` variable can be set to a regex to filter this things out.
60 #config The default as shown above filters some NoBug annotations and non fatal logging out.
61 #config
62 LOGSUPPRESS='^\(\*\*[0-9]*\*\* \)\?[0-9]\{10,\}[:!] \(TRACE\|INFO\|NOTICE\|WARNING\|ERR\):'
64 #config PARA Resource Limits; ulimit; constrain resource limits
65 #config It is possible to set some limits for tests to protect the system against really broken cases.
66 #config Since running under valgrind takes consinderable more resources there are separate variants for
67 #config limits when running under valgrind.
68 #config
69 #config LIMIT_CPU=5
70 #config Maximal CPU time the test may take after it will be killed with SIGXCPU. This protects agaist Lifelocks.
71 #config
72 #config LIMIT_TIME=10
73 #config Maximal wall-time a test may take after this it will be killed with SIGKILL. Protects against Deadlocks.
74 #config
75 #config LIMIT_VSZ=524288
76 #config Maximal virtual memory size the process may map, allocations/mappings will fail when this limit is reached.
77 #config Protects against memory leaks.
78 #config
79 #config LIMIT_VG_CPU=20
80 #config LIMIT_VG_TIME=30
81 #config LIMIT_VG_VSZ=524288
82 #config Same variables again with limits when running under valgrind.
83 #config
84 LIMIT_CPU=5
85 LIMIT_TIME=10
86 LIMIT_VSZ=524288
87 LIMIT_VG_CPU=20
88 LIMIT_VG_TIME=30
89 LIMIT_VG_VSZ=524288
92 #configf HEAD~ Configuration Files; configuration files; define variables to configure the test
93 #configf
94 #configf `test.sh` reads config files from the following location if they are exist
95 #configf * 'test.conf' from the current directory
96 #configf * '$srcdir/test.conf' `$srcdir` is set by autotools
97 #configf * '$srcdir/tests/test.conf' `tests/` is suspected as default directory for tests
98 #configf * '$TEST_CONF' a user defineable variable to point to a config file
99 #configf
100 test -f 'test.conf' && source test.conf
101 test -n "$srcdir" -a -e "$srcdir/test.conf" && source "$srcdir/test.conf"
102 test -n "$srcdir" -a -e "$srcdir/tests/test.conf" && source "$srcdir/tests/test.conf"
103 test -n "$TEST_CONF" -a -e "$TEST_CONF" && source "$TEST_CONF"
107 arg0="$0"
108 TESTDIR="$(dirname "$arg0")"
111 #libtool HEAD Libtool; libtool; support for libtool
112 #libtool When test.sh detects the presence of './libtool' it runs all tests with
113 #libtool `./libtool --mode=execute`.
114 #libtool
115 LIBTOOL_EX=
116 if test -x ./libtool; then
117 LIBTOOL_EX="./libtool --mode=execute"
120 #valgrind HEAD- Valgrind; valgrind; valgrind support
121 #valgrind Test are run under valgrind supervision by default, if not disabled.
122 #valgrind
123 #valgrind PARA VALGRINDFLAGS; VALGRINDFLAGS; control valgrind options
124 #valgrind VALGRINDFLAGS="--leak-check=yes --show-reachable=yes"
125 #valgrind
126 #valgrind `VALGRINDFLAGS` define the options which are passed to valgrind. This can be used to override
127 #valgrind the defaults or switching the valgrind tool. The special case `VALGRINDFLAGS=DISABLE` will disable
128 #valgrind valgrind for the tests.
129 #valgrind
130 #valgrind HEAD~ Generating Valgrind Suppression Files; vgsuppression; ignore false positives
131 #valgrind When there is a 'vgsuppression' executable in the current dir (build by something external) then
132 #valgrind test.sh uses this to generate a local 'vgsuppression.supp' file and uses that to suppress
133 #valgrind all errors generated by 'vgsuppression'. The Idea here is that one adds code which triggers known
134 #valgrind false positives in 'vgsuppression'. Care must be taken that this file is simple and does
135 #valgrind not generate true positives.
136 #valgrind
137 ulimit -S -t ${LIMIT_CPU:-5} -v ${LIMIT_VSZ:-524288}
138 valgrind=""
139 LIMIT_TIME_REAL="$LIMIT_TIME"
140 if [ "$VALGRINDFLAGS" = 'DISABLE' ]; then
141 echo "valgrind explicit disabled"
142 else
143 if [ "$(which valgrind)" ]; then
144 ulimit -S -t ${ULIMIT_VG_CPU:-20} -v ${ULIMIT_VG_VSZ:-524288}
145 LIMIT_TIME_REAL="$LIMIT_VG_TIME"
146 if [[ -x 'vgsuppression' ]]; then
147 if [[ 'vgsuppression' -nt 'vgsuppression.supp' ]]; then
148 echo 'generating valgrind supression file'
150 $LIBTOOL_EX $(which valgrind) ${VALGRINDFLAGS:---leak-check=yes --show-reachable=yes} -q --gen-suppressions=all vgsuppression 2>&1 \
151 | awk '/^{/ {i = 1;} /^}/ {i = 0; print $0;} {if (i == 1) print $0;}' >vgsuppression.supp
153 valgrind="$(which valgrind) ${VALGRINDFLAGS:---leak-check=yes --show-reachable=no} --suppressions=vgsuppression.supp -q"
154 else
155 valgrind="$(which valgrind) ${VALGRINDFLAGS:---leak-check=yes --show-reachable=no -q}"
157 else
158 echo "no valgrind found, go without it"
162 echo
163 echo "================ ${0##*/} ================"
165 TESTCNT=0
166 SKIPCNT=0
167 FAILCNT=0
170 # the old testlog if existing will be used to check for previous test states
171 if test -f ,testlog; then
172 mv ,testlog ,testlog.pre
173 else
174 touch ,testlog.pre
177 date >,testlog
179 function compare_template() # template plainfile
181 local template
182 local line
183 local miss
184 local lineno=1
185 local templateno=1
187 IFS='' read -u 3 -r template || return 0
188 IFS='' read -u 4 -r line || { echo "no output"; return 1; }
189 while true; do
190 local cmd="${template%%:*}:"
191 local arg="${template#*: }"
193 case $cmd in
194 'regex_cont:')
195 if [[ $line =~ $arg ]]; then
196 IFS='' read -u 4 -r line ||
197 if IFS='' read -u 3 -r template; then
198 echo "premature end in output, expecting $template:$templateno"
199 return 1
200 else
201 return 0
203 : $((++lineno))
204 miss=0
205 else
206 if [[ $((++miss)) -gt 1 ]]; then
207 echo -e "'$line':$lineno\ndoes not match\n$template:$templateno"
208 return 1
210 IFS='' read -u 3 -r template || { echo "more output than expected: '$line':$lineno"; return 1; }
211 : $((++templateno))
214 'literal:')
215 if [[ "$line" = "$arg" ]]; then
216 IFS='' read -u 3 -r template && IFS='' read -u 4 -r line || {
217 return 1
219 else
220 echo -e "'$line':$lineno\ndoes not match\n$template:$templateno"
221 return 1
225 echo "UNKOWN MATCH COMMAND '$cmd'" 1>&2
226 exit
228 esac
229 done
230 } 3<"$1" 4<"$2"
233 #tests HEAD- Writing Tests; tests; how to write testsuites
234 #tests Tests are nothing more than bash scripts with some functions from the test.sh
235 #tests framework defined. Test.sh looks in the current directory for all files which ending in .test
236 #tests and runs them in alphabetical order. The selection of this tests can be constrained with the
237 #tests `TESTSUITES` environment variable.
238 #tests
239 #tests HEAD~ Testsuites; test files; writing tests
240 #tests It is common to start the name of the '.test' files with a 2 digi number to give them a proper
241 #tests order: '10foo.test', '20bar.test' and so on. Each such test should only test a certain aspect of
242 #tests the system. You have to select the testing binary with the `TESTING` function and then write
243 #tests certain TEST's defining how the test should react. Since tests are shell scripts it is possible
244 #tests to add some supplemental commands there to set and clean up the given test environment.
245 #tests
246 #tests HEAD^ TESTING; TESTING; set the test binary
247 #tests TESTING "message" test_program
248 #tests
249 #tests Selects the test binary for the follwing tests, prints an informal message.
250 #tests
251 #tests `message`::
252 #tests message to be printed
253 #tests `test_program`::
254 #tests an existing program to drive the tests or a shell function
255 #tests
256 #tests ----
257 #tests TESTING "Testing a.out" ./a.out
258 #tests ----
259 #tests
260 function TESTING()
262 echo
263 echo "$1"
264 echo -e "\n#### $1, $TESTFILE, $2" >>,testlog
266 TESTBIN="$2"
269 #tests HEAD^ TEST; TEST; single test
270 #tests TEST "title" arguments.. <<END
271 #tests
272 #tests Runs a single test
273 #tests
274 #tests `title`::
275 #tests describes this test and is also used as identifier for this test,
276 #tests must be unique for all your tests
277 #tests `arguments`::
278 #tests the following arguments are passed to the test program
279 #tests `<<END .. END`::
280 #tests a list of control commands expected in and outputs is given as 'heredoc'.
281 #tests
282 #tests Each line of the test specification in the heredoc starts with an arbitary number of spaces
283 #tests followed by a command, followed by a colon and a space, followed by additional arguments or
284 #tests being an empty or comment line.
285 #tests
286 #tests
287 #tests HEAD+ Test Commands; commands; define expected in and outputs
288 #tests
289 #tests PARA in; in; stdin data for a test
290 #tests in: text
291 #tests
292 #tests Send `text` to stdin of the test binary. If no `in:` commands are given, nothing is send to the
293 #tests tests input.
294 #tests
295 #tests PARA out; out; expected stdout (regex) from a test
296 #tests out: regex
297 #tests
298 #tests Expect `regex` on stdout. This regexes have a 'triggering' semantic. That means it is tried to match
299 #tests a given regex on as much lines as possible (`.*` will match any remaining output), if the match fails,
300 #tests the next expected output line is tried. When that fails too the test is aborted and counted as failure.
301 #tests
302 #tests When no `out:` or `out-lit:` commands are given, then stdout is not checked, any output is ignored.
303 #tests
304 #tests PARA err; err; expected stderr (regex) from a test
305 #tests out: regex
306 #tests
307 #tests Same as 'out:' but expects data on stderr. When no `err:` or `err-lit:` commands are given, then stdout is
308 #tests not checked, any output there is ignored.
309 #tests
310 #tests PARA out-lit; out-lit; expected stdout (literal) from a test
311 #tests out-lit: text
312 #tests
313 #tests Expect `text` on stdout, must match exactly or will fail.
314 #tests
315 #tests PARA err-lit; err-lit; expected stderr (literal) from a test
316 #tests err-lit: text
317 #tests
318 #tests Same as 'out-lit:' but expects data on stderr.
319 #tests
320 #tests PARA return; return; expected exit value of a test
321 #tests return: value
322 #tests
323 #tests Expects `value` as exit code of the tested program. The check can be negated by prepending the value with
324 #tests an exclamation mark, `return: !0` expects any exist code except zero.
325 #tests
326 #tests If no `return:` command is given then a zero (success) return from the test program is expected.
327 #tests
328 #tests HEAD+ Conditional Tests; conditional tests; switch tests on conditions
329 #tests Sometimes tests need to be adapted to the environment/platform they are running on. This can be archived
330 #tests with common if-else-elseif-endif statements. This statements can be nested.
331 #tests
332 #tests PARA if; if; conditional test
333 #tests if: check
334 #tests
335 #tests Executes `check` as shell command, if its return is zero (success) then the following test parts are used.
336 #tests
337 #tests PARA else; else; conditional alternative
338 #tests else:
339 #tests
340 #tests If the previous `if` failed then the following test parts are included in the test, otherwise they
341 #tests are excluded.
342 #tests
343 #tests PARA elseif; elseif; conditional alternative with test
344 #tests elseif: check
345 #tests
346 #tests Composition of else and if, only includes the following test parts if the if's and elseif's before failed
347 #tests and `check` succeeded.
348 #tests
349 #tests PARA endif; endif; end of conditonal test part
350 #tests endif:
351 #tests
352 #tests Ends an `if` statement.
353 #tests
354 #tests HEAD+ Other Elements;;
355 #tests
356 #tests PARA msg; msg; print a diagnostic message
357 #tests msg: message..
358 #tests
359 #tests Prints `message` while processing the test suite.
360 #tests
361 #tests PARA comments; comments; adding comments to tests
362 #tests # anything
363 #tests
364 #tests Lines starting with the hash mark and empty lines count as comment and are not used.
365 #tests
366 function TEST()
368 name="$1"
369 shift
370 rm -f ,send_stdin
371 rm -f ,expect_stdout
372 rm -f ,expect_stderr
373 expect_return=0
375 local valgrind="$valgrind"
376 if [ "$VALGRINDFLAGS" = 'DISABLE' ]; then
377 valgrind=
380 local condstack="1"
381 while read -r line; do
382 local cmd="${line%%:*}:"
383 local arg="${line#*: }"
385 if [[ ! "$arg" ]]; then
386 arg='^$'
389 case $cmd in
390 'if:')
391 if $arg; then
392 condstack="1$condstack"
393 else
394 condstack="0$condstack"
397 'elseif:')
398 if [[ "${condstack:0:1}" = "0" ]]; then
399 if $arg; then
400 condstack="1${condstack:1}"
401 else
402 condstack="0${condstack:1}"
404 else
405 condstack="2${condstack:1}"
408 'else:')
409 if [[ "${condstack:0:1}" != "0" ]]; then
410 condstack="0${condstack:1}"
411 else
412 condstack="1${condstack:1}"
415 'endif:')
416 condstack="${condstack:1}"
419 if [[ "${condstack:0:1}" = "1" ]]; then
420 case $cmd in
421 'msg:')
422 echo "MSG $arg"
424 'in:')
425 echo "$arg" >>,send_stdin
427 'out:')
428 echo "regex_cont: $arg" >>,expect_stdout
430 'err:')
431 echo "regex_cont: $arg" >>,expect_stderr
433 'out-lit:')
434 echo "literal: $arg" >>,expect_stdout
436 'err-lit:')
437 echo "literal: $arg" >>,expect_stderr
439 'return:')
440 expect_return="$arg"
442 '#'*|':')
446 echo "UNKOWN TEST COMMAND '$cmd'" 1>&2
447 exit
449 esac
452 esac
453 done
454 echo -n "TEST $name: "
455 echo -en "\nTEST $name: $* " >>,testlog
457 case "$TESTMODE" in
458 *FAST*)
459 if grep "^TEST $name: .* FAILED" ,testlog.pre >&/dev/null; then
460 MSGOK=" (fixed)"
461 MSGFAIL=" (still broken)"
462 elif grep "^TEST $name: .* \\(SKIPPED (ok)\\|OK\\)" ,testlog.pre >&/dev/null; then
463 echo ".. SKIPPED (ok)"
464 echo ".. SKIPPED (ok)" >>,testlog
465 SKIPCNT=$(($SKIPCNT + 1))
466 TESTCNT=$(($TESTCNT + 1))
467 return
468 else
469 MSGOK=" (new)"
470 MSGFAIL=" (new)"
474 MSGOK=""
475 MSGFAIL=""
477 esac
479 TESTCNT=$(($TESTCNT + 1))
481 fails=0
483 echo -n >,testtmp
485 local CALL
486 if declare -F | grep $TESTBIN >&/dev/null; then
487 CALL=
488 elif test -x $TESTBIN; then
489 CALL="env $LIBTOOL_EX $valgrind"
490 else
491 CALL='-'
492 echo -n >,stdout
493 echo "test binary '$TESTBIN' not found" >,stderr
494 ((fails+=1))
497 if test "$CALL" != '-'; then
498 if test -f ,send_stdin; then
500 $CALL $TESTBIN "$@" <,send_stdin 2>,stderr >,stdout
501 echo $? >,return
503 else
505 $CALL $TESTBIN "$@" 2>,stderr >,stdout
506 echo $? >,return
508 fi &>/dev/null
509 pid=$!
511 # watchdog
512 ( sleep $LIMIT_TIME_REAL && kill -KILL $pid ) &>/dev/null &
513 wpid=$!
514 wait $pid
515 return=$(<,return)
516 if [[ "$return" -le 128 ]]; then
517 kill -INT $wpid >&/dev/null
520 if test -f ,expect_stdout; then
521 grep -v "$LOGSUPPRESS" <,stdout >,tmp
522 if ! compare_template ,expect_stdout ,tmp >>,cmptmp; then
523 echo "unexpected data on stdout" >>,testtmp
524 cat ,cmptmp >>,testtmp
525 ((fails+=1))
527 rm ,tmp ,cmptmp
530 if test -f ,expect_stderr; then
531 grep -v "$LOGSUPPRESS" <,stderr >,tmp
532 cat ,tmp >>,testtmp
533 if ! compare_template ,expect_stderr ,tmp >>,cmptmp; then
534 echo "unexpected data on stderr" >>,testtmp
535 cat ,cmptmp >>,testtmp
536 ((fails+=1))
538 rm ,tmp ,cmptmp
541 if [[ "${expect_return:0:1}" = '!' ]]; then
542 if [[ "${expect_return#\!}" = "$return" ]]; then
543 echo "unexpected return value $return, expected $expect_return" >>,testtmp
544 ((fails+=1))
546 else
547 if [[ "${expect_return}" != "$return" ]]; then
548 echo "unexpected return value $return, expected $expect_return" >>,testtmp
549 ((fails+=1))
554 if test $fails -eq 0; then
555 echo ".. OK$MSGOK"
556 echo ".. OK$MSGOK" >>,testlog
557 else
558 echo ".. FAILED$MSGFAIL";
559 echo ".. FAILED$MSGFAIL" >>,testlog
560 cat ,testtmp >>,testlog
561 rm ,testtmp
562 echo "stderr was:" >>,testlog
563 cat ,stderr >>,testlog
564 echo END >>,testlog
565 FAILCNT=$(($FAILCNT + 1))
566 case $TESTMODE in
567 *FIRSTFAIL*)
568 break 2
570 esac
574 #tests HEAD^ PLANNED; PLANNED; deactivated test
575 #tests PLANNED "title" arguments.. <<END
576 #tests
577 #tests Skip a single test.
578 #tests
579 #tests `title`::
580 #tests describes this test and is also used as identifier for this test,
581 #tests must be unique for all your tests
582 #tests `arguments`::
583 #tests the following arguments are passed to the test program
584 #tests `<<END .. END`::
585 #tests a list of control commands expected in and outputs is given as 'heredoc'.
586 #tests
587 #tests `PLANNED` acts as dropin replacement for `TEST`. Each such test is skipped (and counted as skipped)
588 #tests This can be used to specify tests in advance and activate them as soon development goes on or
589 #tests deactivate intentional broken tests to be fixed later.
590 #tests
591 function PLANNED()
593 echo -n "PLANNED $1: "
594 echo -en "\nPLANNED $* " >>,testlog
595 echo ".. SKIPPED (planned)"
596 echo ".. SKIPPED (planned)" >>,testlog
597 SKIPCNT=$(($SKIPCNT + 1))
598 TESTCNT=$(($TESTCNT + 1))
601 function RUNTESTS()
603 if test \( ! "${TESTSUITES/*,*/}" \) -a "$TESTSUITES"; then
604 TESTSUITES="{$TESTSUITES}"
606 for t in $(eval echo "$TESTDIR/*$TESTSUITES*.tests"); do
607 echo "$t"
608 done | sort | uniq | {
609 while read TESTFILE; do
610 echo
611 echo "### $TESTFILE" >&2
612 if test -f $TESTFILE; then
613 source $TESTFILE
615 done
616 echo
617 if [ $FAILCNT = 0 ]; then
618 echo " ... PASSED $(($TESTCNT - $SKIPCNT)) TESTS, $SKIPCNT SKIPPED"
619 #rm ,testlog
620 else
621 echo " ... SUCCEDED $(($TESTCNT - $FAILCNT - $SKIPCNT)) TESTS"
622 echo " ... FAILED $FAILCNT TESTS"
623 echo " ... SKIPPED $SKIPCNT TESTS"
624 echo " see ',testlog' for details"
625 exit 1
630 TESTSUITES="${TESTSUITES}${1:+${TESTSUITES:+,}$1}"
632 RUNTESTS