event_machine: protect IO#close from EBADF
[rainbows.git] / t / my-tap-lib.sh
blob3270095ba4527eff5c054027e11192f6a1f27038
1 #!/bin/sh
2 # Copyright (c) 2009 Eric Wong <normalperson@yhbt.net>
4 # TAP-producing shell library for POSIX-compliant Bourne shells We do
5 # not _rely_ on Bourne Again features, though we will use "set -o
6 # pipefail" from ksh93 or bash 3 if available
8 # Only generic, non-project/non-language-specific stuff goes here. We
9 # only have POSIX dependencies for the core tests (without --verbose),
10 # though we'll enable useful non-POSIX things if they're available.
12 # This test library is intentionally unforgiving, it does not support
13 # skipping tests nor continuing after any failure. Any failures
14 # immediately halt execution as do any references to undefined
15 # variables.
17 # When --verbose is specified, we always prefix stdout/stderr
18 # output with "#" to avoid confusing TAP consumers. Otherwise
19 # the normal stdout/stderr streams are redirected to /dev/null
21 # dup normal stdout(fd=1) and stderr (fd=2) to fd=3 and fd=4 respectively
22 # normal TAP output goes to fd=3, nothing should go to fd=4
23 exec 3>&1 4>&2
25 # ensure a sane environment
26 TZ=UTC LC_ALL=C LANG=C
27 export LANG LC_ALL TZ
28 unset CDPATH
30 # pipefail is non-POSIX, but very useful in ksh93/bash
31 ( set -o pipefail 2>/dev/null ) && set -o pipefail
33 SED=${SED-sed}
35 # Unlike other test frameworks, we are unforgiving and bail immediately
36 # on any failures. We do this because we're lazy about error handling
37 # and also because we believe anything broken should not be allowed to
38 # propagate throughout the rest of the test
39 set -e
40 set -u
42 # name of our test
43 T=${0##*/}
45 t_expect_nr=-1
46 t_nr=0
47 t_current=
48 t_complete=false
50 # list of files to remove unconditionally on exit
51 T_RM_LIST=
53 # list of files to remove only on successful exit
54 T_OK_RM_LIST=
56 # emit output to stdout, it'll be parsed by the TAP consumer
57 # so it must be TAP-compliant output
58 t_echo () {
59 echo >&3 "$@"
62 # emits non-parsed information to stdout, it will be prefixed with a '#'
63 # to not throw off TAP consumers
64 t_info () {
65 t_echo '#' "$@"
68 # exit with an error and print a diagnostic
69 die () {
70 echo >&2 "$@"
71 exit 1
74 # our at_exit handler, it'll fire for all exits except SIGKILL (unavoidable)
75 t_at_exit () {
76 code=$?
77 set +e
78 if test $code -eq 0
79 then
80 $t_complete || {
81 t_info "t_done not called"
82 code=1
84 elif test -n "$t_current"
85 then
86 t_echo "not ok $t_nr - $t_current"
88 if test $t_expect_nr -ne -1
89 then
90 test $t_expect_nr -eq $t_nr || {
91 t_info "planned $t_expect_nr tests but ran $t_nr"
92 test $code -ne 0 || code=1
95 $t_complete || {
96 t_info "unexpected test failure"
97 test $code -ne 0 || code=1
99 rm -f $T_RM_LIST
100 test $code -eq 0 && rm -f $T_OK_RM_LIST
101 set +x
102 exec >&3 2>&4
103 t_close_fds
104 exit $code
107 # close test-specific extra file descriptors
108 t_close_fds () {
109 exec 3>&- 4>&-
112 # call this at the start of your test to specify the number of tests
113 # you plan to run
114 t_plan () {
115 test "$1" -ge 1 || die "must plan at least one test"
116 test $t_expect_nr -eq -1 || die "tried to plan twice in one test"
117 t_expect_nr=$1
118 shift
119 t_echo 1..$t_expect_nr "#" "$@"
120 trap t_at_exit EXIT
123 _t_checkup () {
124 test $t_expect_nr -le 0 && die "no tests planned"
125 test -n "$t_current" && t_echo "ok $t_nr - $t_current"
126 true
129 # finalizes any previously test and starts a new one
130 t_begin () {
131 _t_checkup
132 t_nr=$(( $t_nr + 1 ))
133 t_current="$1"
135 # just in case somebody wanted to cheat us:
136 set -e
139 # finalizes the current test without starting a new one
140 t_end () {
141 _t_checkup
142 t_current=
145 # run this to signify the end of your test
146 t_done () {
147 _t_checkup
148 t_current=
149 t_complete=true
150 test $t_expect_nr -eq $t_nr || exit 1
151 exit 0
154 # create and assign named-pipes to variable _names_ passed to this function
155 t_fifos () {
156 for _id in "$@"
158 _name=$_id
159 _tmp=$(mktemp -t $T.$$.$_id.XXXXXXXX)
160 eval "$_id=$_tmp"
161 rm -f $_tmp
162 mkfifo $_tmp
163 T_RM_LIST="$T_RM_LIST $_tmp"
164 done
167 t_verbose=false t_trace=false
169 while test "$#" -ne 0
171 arg="$1"
172 shift
173 case $arg in
174 -v|--verbose) t_verbose=true ;;
175 --trace) t_trace=true t_verbose=true ;;
176 *) die "Unknown option: $arg" ;;
177 esac
178 done
180 # we always only setup stdout, nothing should end up in the "real" stderr
181 if $t_verbose
182 then
183 if test x"$(which mktemp 2>/dev/null)" = x
184 then
185 die "mktemp(1) not available for --verbose"
187 t_fifos t_stdout t_stderr
190 # use a subshell so seds are not waitable
191 $SED -e 's/^/#: /' < $t_stdout &
192 $SED -e 's/^/#! /' < $t_stderr &
194 wait
195 exec > $t_stdout 2> $t_stderr
196 else
197 exec > /dev/null 2> /dev/null
200 $t_trace && set -x
201 true