ctdb-tests: Avoid use of non-portable getopt in run_tests.sh
[vlendec/samba-autobuild/.git] / ctdb / tests / run_tests.sh
1 #!/usr/bin/env bash
2
3 usage() {
4     cat <<EOF
5 Usage: $0 [OPTIONS] [TESTS]
6
7 Options:
8   -A            Use "cat -A" to print test output (only some tests)
9   -c            Run integration tests on a cluster
10   -C            Clean up - kill daemons and remove TEST_VAR_DIR when done
11   -d            Print descriptions of tests instead of filenames (dodgy!)
12   -D            Show diff between failed/expected test output (some tests only)
13   -e            Exit on the first test failure
14   -H            No headers - for running single test with other wrapper
15   -N            Don't print summary of tests results after running all tests
16   -q            Quiet - don't show tests being run (hint: use with -s)
17   -S <lib>      Use socket wrapper library <lib> for local integration tests
18   -v            Verbose - print test output for non-failures (only some tests)
19   -V <dir>      Use <dir> as TEST_VAR_DIR
20   -x            Trace this script with the -x option
21   -X            Trace certain scripts run by tests using -x (only some tests)
22 EOF
23     exit 1
24 }
25
26 # Print a message and exit.
27 die ()
28 {
29     echo "$1" >&2 ; exit ${2:-1}
30 }
31
32 ######################################################################
33
34 with_summary=true
35 with_desc=false
36 quiet=false
37 exit_on_fail=false
38 no_header=false
39
40 export TEST_VERBOSE=false
41 export TEST_COMMAND_TRACE=false
42 export TEST_CAT_RESULTS_OPTS=""
43 export TEST_DIFF_RESULTS=false
44 export TEST_LOCAL_DAEMONS
45 [ -n "$TEST_LOCAL_DAEMONS" ] || TEST_LOCAL_DAEMONS=3
46 export TEST_VAR_DIR=""
47 export TEST_CLEANUP=false
48 export TEST_TIMEOUT=3600
49 export TEST_SOCKET_WRAPPER_SO_PATH=""
50
51 while getopts "AcCdDehHNqS:T:vV:xX?" opt ; do
52         case "$opt" in
53         A) TEST_CAT_RESULTS_OPTS="-A" ;;
54         c) TEST_LOCAL_DAEMONS="" ;;
55         C) TEST_CLEANUP=true ;;
56         d) with_desc=true ;;  # 4th line of output is description
57         D) TEST_DIFF_RESULTS=true ;;
58         e) exit_on_fail=true ;;
59         H) no_header=true ;;
60         N) with_summary=false ;;
61         q) quiet=true ;;
62         S) TEST_SOCKET_WRAPPER_SO_PATH="$OPTARG" ;;
63         T) TEST_TIMEOUT="$OPTARG" ;;
64         v) TEST_VERBOSE=true ;;
65         V) TEST_VAR_DIR="$OPTARG" ;;
66         x) set -x ;;
67         X) TEST_COMMAND_TRACE=true ;;
68         \?|h) usage ;;
69         esac
70 done
71 shift $((OPTIND - 1))
72
73 case $(basename "$0") in
74     *run_cluster_tests*)
75         # Running on a cluster...  same as -c
76         TEST_LOCAL_DAEMONS=""
77         ;;
78 esac
79
80 if $quiet ; then
81     show_progress() { cat >/dev/null ; }
82 else
83     show_progress() { cat ; }
84 fi
85
86 ######################################################################
87
88 ctdb_test_begin ()
89 {
90     local name="$1"
91
92     teststarttime=$(date '+%s')
93     testduration=0
94
95     echo "--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--"
96     echo "Running test $name ($(date '+%T'))"
97     echo "--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--"
98 }
99
100 ctdb_test_end ()
101 {
102     local name="$1" ; shift
103     local status="$1" ; shift
104     # "$@" is command-line
105
106     local interp="SKIPPED"
107     local statstr=" (reason $*)"
108     if [ -n "$status" ] ; then
109         if [ $status -eq 0 ] ; then
110             interp="PASSED"
111             statstr=""
112             echo "ALL OK: $*"
113         elif [ $status -eq 124 ] ; then
114             interp="TIMEOUT"
115             statstr=" (status $status)"
116         else
117             interp="FAILED"
118             statstr=" (status $status)"
119         fi
120     fi
121
122     testduration=$(($(date +%s)-$teststarttime))
123
124     echo "=========================================================================="
125     echo "TEST ${interp}: ${name}${statstr} (duration: ${testduration}s)"
126     echo "=========================================================================="
127
128 }
129
130 ctdb_test_run ()
131 {
132     local name="$1" ; shift
133
134     [ -n "$1" ] || set -- "$name"
135
136     $no_header || ctdb_test_begin "$name"
137
138     local status=0
139     if [ -x "$1" ] ; then
140             timeout $TEST_TIMEOUT "$@" || status=$?
141     else
142             echo "TEST IS NOT EXECUTABLE"
143             status=1
144     fi
145
146     $no_header || ctdb_test_end "$name" "$status" "$*"
147
148     return $status
149 }
150
151 ######################################################################
152
153 tests_total=0
154 tests_passed=0
155 tests_failed=0
156 summary=""
157
158 if ! which mktemp >/dev/null 2>&1 ; then
159     # Not perfect, but it will do...
160     mktemp ()
161     {
162         local dir=false
163         if [ "$1" = "-d" ] ; then
164             dir=true
165         fi
166         local t="${TMPDIR:-/tmp}/tmp.$$.$RANDOM"
167         (
168             umask 077
169             if $dir ; then
170                 mkdir "$t"
171             else
172                 >"$t"
173             fi
174         )
175         echo "$t"
176     }
177 fi
178
179 tf=$(mktemp) || die "mktemp failed for tf - is TMPDIR missing?"
180 sf=$(mktemp) || die "mktemp failed for sf - is TMPDIR missing?"
181
182 set -o pipefail
183
184 run_one_test ()
185 {
186     local f="$1"
187
188     tests_total=$(($tests_total + 1))
189
190     ctdb_test_run "$f" | tee "$tf" | show_progress
191     status=$?
192     if [ $status -eq 0 ] ; then
193         tests_passed=$(($tests_passed + 1))
194     else
195         tests_failed=$(($tests_failed + 1))
196     fi
197     if $with_summary ; then
198         local t
199         if [ $status -eq 0 ] ; then
200             t=" PASSED "
201         else
202             t="*FAILED*"
203         fi
204         if $with_desc ; then
205             desc=$(tail -n +4 $tf | head -n 1)
206             f="$desc"
207         fi
208         echo "$t $f" >>"$sf"
209     fi
210 }
211
212 find_and_run_one_test ()
213 {
214     local t="$1"
215     local dir="$2"
216
217     local f="${dir}${dir:+/}${t}"
218
219     if [ -d "$f" ] ; then
220         local i
221         for i in $(ls "${f%/}/"*".sh" 2>/dev/null) ; do
222             run_one_test "$i"
223             if $exit_on_fail && [ $status -ne 0 ] ; then
224                 break
225             fi
226         done
227         # No tests found?  Not a tests directory!  Not found...
228         [ -n "$status" ] || status=127
229     elif [ -f "$f" ] ; then
230         run_one_test "$f"
231     else
232         status=127
233     fi
234 }
235
236 export CTDB_TEST_MODE="yes"
237
238 # Following 2 lines may be modified by installation script
239 export CTDB_TESTS_ARE_INSTALLED=false
240 export CTDB_TEST_DIR=$(dirname "$0")
241
242 if [ -z "$TEST_VAR_DIR" ] ; then
243     if $CTDB_TESTS_ARE_INSTALLED ; then
244         TEST_VAR_DIR=$(mktemp -d)
245     else
246         TEST_VAR_DIR="${CTDB_TEST_DIR}/var"
247     fi
248 fi
249 mkdir -p "$TEST_VAR_DIR"
250
251 # Must be absolute
252 TEST_VAR_DIR=$(cd "$TEST_VAR_DIR"; echo "$PWD")
253 echo "TEST_VAR_DIR=$TEST_VAR_DIR"
254
255 export TEST_SCRIPTS_DIR="${CTDB_TEST_DIR}/scripts"
256
257 unit_tests="
258         cunit
259         eventd
260         eventscripts
261         onnode
262         shellcheck
263         takeover
264         takeover_helper
265         tool
266 "
267
268 # If no tests specified then run some defaults
269 if [ -z "$1" ] ; then
270         if [ -n "$TEST_LOCAL_DAEMONS" ] ; then
271                 set -- UNIT simple
272         else
273                 set -- simple complex
274     fi
275 fi
276
277 do_cleanup ()
278 {
279     if $TEST_CLEANUP ; then
280         echo "Removing TEST_VAR_DIR=$TEST_VAR_DIR"
281         rm -rf "$TEST_VAR_DIR"
282     else
283         echo "Not cleaning up TEST_VAR_DIR=$TEST_VAR_DIR"
284     fi
285 }
286
287 cleanup_handler ()
288 {
289     if $TEST_CLEANUP ; then
290         if [ -n "$TEST_LOCAL_DAEMONS" -a "$f" = "simple" ] ; then
291             echo "***** shutting down daemons *****"
292             find_and_run_one_test simple/99_daemons_shutdown.sh "$tests_dir"
293         fi
294     fi
295     do_cleanup
296 }
297
298 trap cleanup_handler SIGINT SIGTERM
299
300 declare -a tests
301 i=0
302 for f ; do
303         if [ "$f" = "UNIT" ] ; then
304                 for t in $unit_tests ; do
305                         tests[i++]="$t"
306                 done
307         else
308                 tests[i++]="$f"
309         fi
310 done
311
312 for f in "${tests[@]}" ; do
313     find_and_run_one_test "$f"
314
315     if [ $status -eq 127 ] ; then
316         # Find the the top-level tests directory
317         tests_dir=$(dirname $(cd $TEST_SCRIPTS_DIR; echo $PWD))
318         # Strip off current directory from beginning, if there, just
319         # to make paths more friendly.
320         tests_dir=${tests_dir#$PWD/}
321         find_and_run_one_test "$f" "$tests_dir"
322     fi
323
324     if [ $status -eq 127 ] ; then
325             die "test \"$f\" is not recognised"
326     fi
327
328     if $exit_on_fail && [ $status -ne 0 ] ; then
329             break
330     fi
331 done
332
333 rm -f "$tf"
334
335 if $with_summary ; then
336     echo
337     cat "$sf"
338     echo
339     echo "${tests_passed}/${tests_total} tests passed"
340 fi
341
342 rm -f "$sf"
343
344 echo
345
346 do_cleanup
347
348 if $no_header || $exit_on_fail ; then
349     exit $status
350 elif [ $tests_failed -gt 0 ] ; then
351     exit 1
352 else
353     exit 0
354 fi