xref: /aosp_15_r20/external/bazelbuild-rules_cc/tests/system_library/unittest.bash (revision eed53cd41c5909d05eedc7ad9720bb158fd93452)
1*eed53cd4SHONG Yifan#!/bin/bash
2*eed53cd4SHONG Yifan#
3*eed53cd4SHONG Yifan# Copyright 2015 The Bazel Authors. All rights reserved.
4*eed53cd4SHONG Yifan#
5*eed53cd4SHONG Yifan# Licensed under the Apache License, Version 2.0 (the "License");
6*eed53cd4SHONG Yifan# you may not use this file except in compliance with the License.
7*eed53cd4SHONG Yifan# You may obtain a copy of the License at
8*eed53cd4SHONG Yifan#
9*eed53cd4SHONG Yifan#    http://www.apache.org/licenses/LICENSE-2.0
10*eed53cd4SHONG Yifan#
11*eed53cd4SHONG Yifan# Unless required by applicable law or agreed to in writing, software
12*eed53cd4SHONG Yifan# distributed under the License is distributed on an "AS IS" BASIS,
13*eed53cd4SHONG Yifan# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*eed53cd4SHONG Yifan# See the License for the specific language governing permissions and
15*eed53cd4SHONG Yifan# limitations under the License.
16*eed53cd4SHONG Yifan#
17*eed53cd4SHONG Yifan# Common utility file for Bazel shell tests
18*eed53cd4SHONG Yifan#
19*eed53cd4SHONG Yifan# unittest.bash: a unit test framework in Bash.
20*eed53cd4SHONG Yifan#
21*eed53cd4SHONG Yifan# A typical test suite looks like so:
22*eed53cd4SHONG Yifan#
23*eed53cd4SHONG Yifan#   ------------------------------------------------------------------------
24*eed53cd4SHONG Yifan#   #!/bin/bash
25*eed53cd4SHONG Yifan#
26*eed53cd4SHONG Yifan#   source path/to/unittest.bash || exit 1
27*eed53cd4SHONG Yifan#
28*eed53cd4SHONG Yifan#   # Test that foo works.
29*eed53cd4SHONG Yifan#   function test_foo() {
30*eed53cd4SHONG Yifan#     foo >$TEST_log || fail "foo failed";
31*eed53cd4SHONG Yifan#     expect_log "blah" "Expected to see 'blah' in output of 'foo'."
32*eed53cd4SHONG Yifan#   }
33*eed53cd4SHONG Yifan#
34*eed53cd4SHONG Yifan#   # Test that bar works.
35*eed53cd4SHONG Yifan#   function test_bar() {
36*eed53cd4SHONG Yifan#     bar 2>$TEST_log || fail "bar failed";
37*eed53cd4SHONG Yifan#     expect_not_log "ERROR" "Unexpected error from 'bar'."
38*eed53cd4SHONG Yifan#     ...
39*eed53cd4SHONG Yifan#     assert_equals $x $y
40*eed53cd4SHONG Yifan#   }
41*eed53cd4SHONG Yifan#
42*eed53cd4SHONG Yifan#   run_suite "Test suite for blah"
43*eed53cd4SHONG Yifan#   ------------------------------------------------------------------------
44*eed53cd4SHONG Yifan#
45*eed53cd4SHONG Yifan# Each test function is considered to pass iff fail() is not called
46*eed53cd4SHONG Yifan# while it is active.  fail() may be called directly, or indirectly
47*eed53cd4SHONG Yifan# via other assertions such as expect_log().  run_suite must be called
48*eed53cd4SHONG Yifan# at the very end.
49*eed53cd4SHONG Yifan#
50*eed53cd4SHONG Yifan# A test function may redefine functions "set_up" and/or "tear_down";
51*eed53cd4SHONG Yifan# these functions are executed before and after each test function,
52*eed53cd4SHONG Yifan# respectively.  Similarly, "cleanup" and "timeout" may be redefined,
53*eed53cd4SHONG Yifan# and these function are called upon exit (of any kind) or a timeout.
54*eed53cd4SHONG Yifan#
55*eed53cd4SHONG Yifan# The user can pass --test_arg to bazel test to select specific tests
56*eed53cd4SHONG Yifan# to run. Specifying --test_arg multiple times allows to select several
57*eed53cd4SHONG Yifan# tests to be run in the given order. Additionally the user may define
58*eed53cd4SHONG Yifan# TESTS=(test_foo test_bar ...) to specify a subset of test functions to
59*eed53cd4SHONG Yifan# execute, for example, a working set during debugging. By default, all
60*eed53cd4SHONG Yifan# functions called test_* will be executed.
61*eed53cd4SHONG Yifan#
62*eed53cd4SHONG Yifan# This file provides utilities for assertions over the output of a
63*eed53cd4SHONG Yifan# command.  The output of the command under test is directed to the
64*eed53cd4SHONG Yifan# file $TEST_log, and then the expect_log* assertions can be used to
65*eed53cd4SHONG Yifan# test for the presence of certain regular expressions in that file.
66*eed53cd4SHONG Yifan#
67*eed53cd4SHONG Yifan# The test framework is responsible for restoring the original working
68*eed53cd4SHONG Yifan# directory before each test.
69*eed53cd4SHONG Yifan#
70*eed53cd4SHONG Yifan# The order in which test functions are run is not defined, so it is
71*eed53cd4SHONG Yifan# important that tests clean up after themselves.
72*eed53cd4SHONG Yifan#
73*eed53cd4SHONG Yifan# Each test will be run in a new subshell.
74*eed53cd4SHONG Yifan#
75*eed53cd4SHONG Yifan# Functions named __* are not intended for use by clients.
76*eed53cd4SHONG Yifan#
77*eed53cd4SHONG Yifan# This framework implements the "test sharding protocol".
78*eed53cd4SHONG Yifan#
79*eed53cd4SHONG Yifan
80*eed53cd4SHONG Yifan[ -n "$BASH_VERSION" ] ||
81*eed53cd4SHONG Yifan  { echo "unittest.bash only works with bash!" >&2; exit 1; }
82*eed53cd4SHONG Yifan
83*eed53cd4SHONG YifanDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
84*eed53cd4SHONG Yifan
85*eed53cd4SHONG Yifan#### Configuration variables (may be overridden by testenv.sh or the suite):
86*eed53cd4SHONG Yifan
87*eed53cd4SHONG Yifan# This function may be called by testenv.sh or a test suite to enable errexit
88*eed53cd4SHONG Yifan# in a way that enables us to print pretty stack traces when something fails.
89*eed53cd4SHONG Yifanfunction enable_errexit() {
90*eed53cd4SHONG Yifan  set -o errtrace
91*eed53cd4SHONG Yifan  set -eu
92*eed53cd4SHONG Yifan  trap __test_terminated_err ERR
93*eed53cd4SHONG Yifan}
94*eed53cd4SHONG Yifan
95*eed53cd4SHONG Yifanfunction disable_errexit() {
96*eed53cd4SHONG Yifan  set +o errtrace
97*eed53cd4SHONG Yifan  set +eu
98*eed53cd4SHONG Yifan  trap - ERR
99*eed53cd4SHONG Yifan}
100*eed53cd4SHONG Yifan
101*eed53cd4SHONG Yifan#### Set up the test environment, branched from the old shell/testenv.sh
102*eed53cd4SHONG Yifan
103*eed53cd4SHONG Yifan# Enable errexit with pretty stack traces.
104*eed53cd4SHONG Yifanenable_errexit
105*eed53cd4SHONG Yifan
106*eed53cd4SHONG Yifan# Print message in "$1" then exit with status "$2"
107*eed53cd4SHONG Yifandie () {
108*eed53cd4SHONG Yifan    # second argument is optional, defaulting to 1
109*eed53cd4SHONG Yifan    local status_code=${2:-1}
110*eed53cd4SHONG Yifan    # Stop capturing stdout/stderr, and dump captured output
111*eed53cd4SHONG Yifan    if [ "$CAPTURED_STD_ERR" -ne 0 -o "$CAPTURED_STD_OUT" -ne 0 ]; then
112*eed53cd4SHONG Yifan        restore_outputs
113*eed53cd4SHONG Yifan        if [ "$CAPTURED_STD_OUT" -ne 0 ]; then
114*eed53cd4SHONG Yifan            cat "${TEST_TMPDIR}/captured.out"
115*eed53cd4SHONG Yifan            CAPTURED_STD_OUT=0
116*eed53cd4SHONG Yifan        fi
117*eed53cd4SHONG Yifan        if [ "$CAPTURED_STD_ERR" -ne 0 ]; then
118*eed53cd4SHONG Yifan            cat "${TEST_TMPDIR}/captured.err" 1>&2
119*eed53cd4SHONG Yifan            CAPTURED_STD_ERR=0
120*eed53cd4SHONG Yifan        fi
121*eed53cd4SHONG Yifan    fi
122*eed53cd4SHONG Yifan
123*eed53cd4SHONG Yifan    if [ -n "${1-}" ] ; then
124*eed53cd4SHONG Yifan        echo "$1" 1>&2
125*eed53cd4SHONG Yifan    fi
126*eed53cd4SHONG Yifan    if [ -n "${BASH-}" ]; then
127*eed53cd4SHONG Yifan      local caller_n=0
128*eed53cd4SHONG Yifan      while [ $caller_n -lt 4 ] && caller_out=$(caller $caller_n 2>/dev/null); do
129*eed53cd4SHONG Yifan        test $caller_n -eq 0 && echo "CALLER stack (max 4):"
130*eed53cd4SHONG Yifan        echo "  $caller_out"
131*eed53cd4SHONG Yifan        let caller_n=caller_n+1
132*eed53cd4SHONG Yifan      done 1>&2
133*eed53cd4SHONG Yifan    fi
134*eed53cd4SHONG Yifan    if [ x"$status_code" != x -a x"$status_code" != x"0" ]; then
135*eed53cd4SHONG Yifan        exit "$status_code"
136*eed53cd4SHONG Yifan    else
137*eed53cd4SHONG Yifan        exit 1
138*eed53cd4SHONG Yifan    fi
139*eed53cd4SHONG Yifan}
140*eed53cd4SHONG Yifan
141*eed53cd4SHONG Yifan# Print message in "$1" then record that a non-fatal error occurred in ERROR_COUNT
142*eed53cd4SHONG YifanERROR_COUNT="${ERROR_COUNT:-0}"
143*eed53cd4SHONG Yifanerror () {
144*eed53cd4SHONG Yifan    if [ -n "$1" ] ; then
145*eed53cd4SHONG Yifan        echo "$1" 1>&2
146*eed53cd4SHONG Yifan    fi
147*eed53cd4SHONG Yifan    ERROR_COUNT=$(($ERROR_COUNT + 1))
148*eed53cd4SHONG Yifan}
149*eed53cd4SHONG Yifan
150*eed53cd4SHONG Yifan# Die if "$1" != "$2", print $3 as death reason
151*eed53cd4SHONG Yifancheck_eq () {
152*eed53cd4SHONG Yifan    [ "$1" = "$2" ] || die "Check failed: '$1' == '$2' ${3:+ ($3)}"
153*eed53cd4SHONG Yifan}
154*eed53cd4SHONG Yifan
155*eed53cd4SHONG Yifan# Die if "$1" == "$2", print $3 as death reason
156*eed53cd4SHONG Yifancheck_ne () {
157*eed53cd4SHONG Yifan    [ "$1" != "$2" ] || die "Check failed: '$1' != '$2' ${3:+ ($3)}"
158*eed53cd4SHONG Yifan}
159*eed53cd4SHONG Yifan
160*eed53cd4SHONG Yifan# The structure of the following if statements is such that if '[' fails
161*eed53cd4SHONG Yifan# (e.g., a non-number was passed in) then the check will fail.
162*eed53cd4SHONG Yifan
163*eed53cd4SHONG Yifan# Die if "$1" > "$2", print $3 as death reason
164*eed53cd4SHONG Yifancheck_le () {
165*eed53cd4SHONG Yifan  [ "$1" -gt "$2" ] || die "Check failed: '$1' <= '$2' ${3:+ ($3)}"
166*eed53cd4SHONG Yifan}
167*eed53cd4SHONG Yifan
168*eed53cd4SHONG Yifan# Die if "$1" >= "$2", print $3 as death reason
169*eed53cd4SHONG Yifancheck_lt () {
170*eed53cd4SHONG Yifan    [ "$1" -lt "$2" ] || die "Check failed: '$1' < '$2' ${3:+ ($3)}"
171*eed53cd4SHONG Yifan}
172*eed53cd4SHONG Yifan
173*eed53cd4SHONG Yifan# Die if "$1" < "$2", print $3 as death reason
174*eed53cd4SHONG Yifancheck_ge () {
175*eed53cd4SHONG Yifan    [ "$1" -ge "$2" ] || die "Check failed: '$1' >= '$2' ${3:+ ($3)}"
176*eed53cd4SHONG Yifan}
177*eed53cd4SHONG Yifan
178*eed53cd4SHONG Yifan# Die if "$1" <= "$2", print $3 as death reason
179*eed53cd4SHONG Yifancheck_gt () {
180*eed53cd4SHONG Yifan    [ "$1" -gt "$2" ] || die "Check failed: '$1' > '$2' ${3:+ ($3)}"
181*eed53cd4SHONG Yifan}
182*eed53cd4SHONG Yifan
183*eed53cd4SHONG Yifan# Die if $2 !~ $1; print $3 as death reason
184*eed53cd4SHONG Yifancheck_match ()
185*eed53cd4SHONG Yifan{
186*eed53cd4SHONG Yifan  expr match "$2" "$1" >/dev/null || \
187*eed53cd4SHONG Yifan    die "Check failed: '$2' does not match regex '$1' ${3:+ ($3)}"
188*eed53cd4SHONG Yifan}
189*eed53cd4SHONG Yifan
190*eed53cd4SHONG Yifan# Run command "$1" at exit. Like "trap" but multiple atexits don't
191*eed53cd4SHONG Yifan# overwrite each other. Will break if someone does call trap
192*eed53cd4SHONG Yifan# directly. So, don't do that.
193*eed53cd4SHONG YifanATEXIT="${ATEXIT-}"
194*eed53cd4SHONG Yifanatexit () {
195*eed53cd4SHONG Yifan    if [ -z "$ATEXIT" ]; then
196*eed53cd4SHONG Yifan        ATEXIT="$1"
197*eed53cd4SHONG Yifan    else
198*eed53cd4SHONG Yifan        ATEXIT="$1 ; $ATEXIT"
199*eed53cd4SHONG Yifan    fi
200*eed53cd4SHONG Yifan    trap "$ATEXIT" EXIT
201*eed53cd4SHONG Yifan}
202*eed53cd4SHONG Yifan
203*eed53cd4SHONG Yifan## TEST_TMPDIR
204*eed53cd4SHONG Yifanif [ -z "${TEST_TMPDIR:-}" ]; then
205*eed53cd4SHONG Yifan  export TEST_TMPDIR="$(mktemp -d ${TMPDIR:-/tmp}/bazel-test.XXXXXXXX)"
206*eed53cd4SHONG Yifanfi
207*eed53cd4SHONG Yifanif [ ! -e "${TEST_TMPDIR}" ]; then
208*eed53cd4SHONG Yifan  mkdir -p -m 0700 "${TEST_TMPDIR}"
209*eed53cd4SHONG Yifan  # Clean TEST_TMPDIR on exit
210*eed53cd4SHONG Yifan  atexit "rm -fr ${TEST_TMPDIR}"
211*eed53cd4SHONG Yifanfi
212*eed53cd4SHONG Yifan
213*eed53cd4SHONG Yifan# Functions to compare the actual output of a test to the expected
214*eed53cd4SHONG Yifan# (golden) output.
215*eed53cd4SHONG Yifan#
216*eed53cd4SHONG Yifan# Usage:
217*eed53cd4SHONG Yifan#   capture_test_stdout
218*eed53cd4SHONG Yifan#   ... do something ...
219*eed53cd4SHONG Yifan#   diff_test_stdout "$TEST_SRCDIR/path/to/golden.out"
220*eed53cd4SHONG Yifan
221*eed53cd4SHONG Yifan# Redirect a file descriptor to a file.
222*eed53cd4SHONG YifanCAPTURED_STD_OUT="${CAPTURED_STD_OUT:-0}"
223*eed53cd4SHONG YifanCAPTURED_STD_ERR="${CAPTURED_STD_ERR:-0}"
224*eed53cd4SHONG Yifan
225*eed53cd4SHONG Yifancapture_test_stdout () {
226*eed53cd4SHONG Yifan    exec 3>&1 # Save stdout as fd 3
227*eed53cd4SHONG Yifan    exec 4>"${TEST_TMPDIR}/captured.out"
228*eed53cd4SHONG Yifan    exec 1>&4
229*eed53cd4SHONG Yifan    CAPTURED_STD_OUT=1
230*eed53cd4SHONG Yifan}
231*eed53cd4SHONG Yifan
232*eed53cd4SHONG Yifancapture_test_stderr () {
233*eed53cd4SHONG Yifan    exec 6>&2 # Save stderr as fd 6
234*eed53cd4SHONG Yifan    exec 7>"${TEST_TMPDIR}/captured.err"
235*eed53cd4SHONG Yifan    exec 2>&7
236*eed53cd4SHONG Yifan    CAPTURED_STD_ERR=1
237*eed53cd4SHONG Yifan}
238*eed53cd4SHONG Yifan
239*eed53cd4SHONG Yifan# Force XML_OUTPUT_FILE to an existing path
240*eed53cd4SHONG Yifanif [ -z "${XML_OUTPUT_FILE:-}" ]; then
241*eed53cd4SHONG Yifan  XML_OUTPUT_FILE=${TEST_TMPDIR}/ouput.xml
242*eed53cd4SHONG Yifanfi
243*eed53cd4SHONG Yifan
244*eed53cd4SHONG Yifan#### Global variables:
245*eed53cd4SHONG Yifan
246*eed53cd4SHONG YifanTEST_name=""                    # The name of the current test.
247*eed53cd4SHONG Yifan
248*eed53cd4SHONG YifanTEST_log=$TEST_TMPDIR/log       # The log file over which the
249*eed53cd4SHONG Yifan                                # expect_log* assertions work.  Must
250*eed53cd4SHONG Yifan                                # be absolute to be robust against
251*eed53cd4SHONG Yifan                                # tests invoking 'cd'!
252*eed53cd4SHONG Yifan
253*eed53cd4SHONG YifanTEST_passed="true"              # The result of the current test;
254*eed53cd4SHONG Yifan                                # failed assertions cause this to
255*eed53cd4SHONG Yifan                                # become false.
256*eed53cd4SHONG Yifan
257*eed53cd4SHONG Yifan# These variables may be overridden by the test suite:
258*eed53cd4SHONG Yifan
259*eed53cd4SHONG YifanTESTS=()                        # A subset or "working set" of test
260*eed53cd4SHONG Yifan                                # functions that should be run.  By
261*eed53cd4SHONG Yifan                                # default, all tests called test_* are
262*eed53cd4SHONG Yifan                                # run.
263*eed53cd4SHONG Yifanif [ $# -gt 0 ]; then
264*eed53cd4SHONG Yifan  # Legacy behavior is to ignore missing regexp, but with errexit
265*eed53cd4SHONG Yifan  # the following line fails without || true.
266*eed53cd4SHONG Yifan  # TODO(dmarting): maybe we should revisit the way of selecting
267*eed53cd4SHONG Yifan  # test with that framework (use Bazel's environment variable instead).
268*eed53cd4SHONG Yifan  TESTS=($(for i in $@; do echo $i; done | grep ^test_ || true))
269*eed53cd4SHONG Yifan  if (( ${#TESTS[@]} == 0 )); then
270*eed53cd4SHONG Yifan    echo "WARNING: Arguments do not specifies tests!" >&2
271*eed53cd4SHONG Yifan  fi
272*eed53cd4SHONG Yifanfi
273*eed53cd4SHONG Yifan
274*eed53cd4SHONG YifanTEST_verbose="true"             # Whether or not to be verbose.  A
275*eed53cd4SHONG Yifan                                # command; "true" or "false" are
276*eed53cd4SHONG Yifan                                # acceptable.  The default is: true.
277*eed53cd4SHONG Yifan
278*eed53cd4SHONG YifanTEST_script="$(pwd)/$0"         # Full path to test script
279*eed53cd4SHONG Yifan
280*eed53cd4SHONG Yifan#### Internal functions
281*eed53cd4SHONG Yifan
282*eed53cd4SHONG Yifanfunction __show_log() {
283*eed53cd4SHONG Yifan    echo "-- Test log: -----------------------------------------------------------"
284*eed53cd4SHONG Yifan    [[ -e $TEST_log ]] && cat $TEST_log || echo "(Log file did not exist.)"
285*eed53cd4SHONG Yifan    echo "------------------------------------------------------------------------"
286*eed53cd4SHONG Yifan}
287*eed53cd4SHONG Yifan
288*eed53cd4SHONG Yifan# Usage: __pad <title> <pad-char>
289*eed53cd4SHONG Yifan# Print $title padded to 80 columns with $pad_char.
290*eed53cd4SHONG Yifanfunction __pad() {
291*eed53cd4SHONG Yifan    local title=$1
292*eed53cd4SHONG Yifan    local pad=$2
293*eed53cd4SHONG Yifan    {
294*eed53cd4SHONG Yifan        echo -n "$pad$pad $title "
295*eed53cd4SHONG Yifan        printf "%80s" " " | tr ' ' "$pad"
296*eed53cd4SHONG Yifan    } | head -c 80
297*eed53cd4SHONG Yifan    echo
298*eed53cd4SHONG Yifan}
299*eed53cd4SHONG Yifan
300*eed53cd4SHONG Yifan#### Exported functions
301*eed53cd4SHONG Yifan
302*eed53cd4SHONG Yifan# Usage: init_test ...
303*eed53cd4SHONG Yifan# Deprecated.  Has no effect.
304*eed53cd4SHONG Yifanfunction init_test() {
305*eed53cd4SHONG Yifan    :
306*eed53cd4SHONG Yifan}
307*eed53cd4SHONG Yifan
308*eed53cd4SHONG Yifan
309*eed53cd4SHONG Yifan# Usage: set_up
310*eed53cd4SHONG Yifan# Called before every test function.  May be redefined by the test suite.
311*eed53cd4SHONG Yifanfunction set_up() {
312*eed53cd4SHONG Yifan    :
313*eed53cd4SHONG Yifan}
314*eed53cd4SHONG Yifan
315*eed53cd4SHONG Yifan# Usage: tear_down
316*eed53cd4SHONG Yifan# Called after every test function.  May be redefined by the test suite.
317*eed53cd4SHONG Yifanfunction tear_down() {
318*eed53cd4SHONG Yifan    :
319*eed53cd4SHONG Yifan}
320*eed53cd4SHONG Yifan
321*eed53cd4SHONG Yifan# Usage: cleanup
322*eed53cd4SHONG Yifan# Called upon eventual exit of the test suite.  May be redefined by
323*eed53cd4SHONG Yifan# the test suite.
324*eed53cd4SHONG Yifanfunction cleanup() {
325*eed53cd4SHONG Yifan    :
326*eed53cd4SHONG Yifan}
327*eed53cd4SHONG Yifan
328*eed53cd4SHONG Yifan# Usage: timeout
329*eed53cd4SHONG Yifan# Called upon early exit from a test due to timeout.
330*eed53cd4SHONG Yifanfunction timeout() {
331*eed53cd4SHONG Yifan    :
332*eed53cd4SHONG Yifan}
333*eed53cd4SHONG Yifan
334*eed53cd4SHONG Yifan# Usage: fail <message> [<message> ...]
335*eed53cd4SHONG Yifan# Print failure message with context information, and mark the test as
336*eed53cd4SHONG Yifan# a failure.  The context includes a stacktrace including the longest sequence
337*eed53cd4SHONG Yifan# of calls outside this module.  (We exclude the top and bottom portions of
338*eed53cd4SHONG Yifan# the stack because they just add noise.)  Also prints the contents of
339*eed53cd4SHONG Yifan# $TEST_log.
340*eed53cd4SHONG Yifanfunction fail() {
341*eed53cd4SHONG Yifan    __show_log >&2
342*eed53cd4SHONG Yifan    echo "$TEST_name FAILED:" "$@" "." >&2
343*eed53cd4SHONG Yifan    echo "$@" >$TEST_TMPDIR/__fail
344*eed53cd4SHONG Yifan    TEST_passed="false"
345*eed53cd4SHONG Yifan    __show_stack
346*eed53cd4SHONG Yifan    # Cleanup as we are leaving the subshell now
347*eed53cd4SHONG Yifan    tear_down
348*eed53cd4SHONG Yifan    exit 1
349*eed53cd4SHONG Yifan}
350*eed53cd4SHONG Yifan
351*eed53cd4SHONG Yifan# Usage: warn <message>
352*eed53cd4SHONG Yifan# Print a test warning with context information.
353*eed53cd4SHONG Yifan# The context includes a stacktrace including the longest sequence
354*eed53cd4SHONG Yifan# of calls outside this module.  (We exclude the top and bottom portions of
355*eed53cd4SHONG Yifan# the stack because they just add noise.)
356*eed53cd4SHONG Yifanfunction warn() {
357*eed53cd4SHONG Yifan    __show_log >&2
358*eed53cd4SHONG Yifan    echo "$TEST_name WARNING: $1." >&2
359*eed53cd4SHONG Yifan    __show_stack
360*eed53cd4SHONG Yifan
361*eed53cd4SHONG Yifan    if [ -n "${TEST_WARNINGS_OUTPUT_FILE:-}" ]; then
362*eed53cd4SHONG Yifan      echo "$TEST_name WARNING: $1." >> "$TEST_WARNINGS_OUTPUT_FILE"
363*eed53cd4SHONG Yifan    fi
364*eed53cd4SHONG Yifan}
365*eed53cd4SHONG Yifan
366*eed53cd4SHONG Yifan# Usage: show_stack
367*eed53cd4SHONG Yifan# Prints the portion of the stack that does not belong to this module,
368*eed53cd4SHONG Yifan# i.e. the user's code that called a failing assertion.  Stack may not
369*eed53cd4SHONG Yifan# be available if Bash is reading commands from stdin; an error is
370*eed53cd4SHONG Yifan# printed in that case.
371*eed53cd4SHONG Yifan__show_stack() {
372*eed53cd4SHONG Yifan    local i=0
373*eed53cd4SHONG Yifan    local trace_found=0
374*eed53cd4SHONG Yifan
375*eed53cd4SHONG Yifan    # Skip over active calls within this module:
376*eed53cd4SHONG Yifan    while (( i < ${#FUNCNAME[@]} )) && [[ ${BASH_SOURCE[i]:-} == ${BASH_SOURCE[0]} ]]; do
377*eed53cd4SHONG Yifan       (( ++i ))
378*eed53cd4SHONG Yifan    done
379*eed53cd4SHONG Yifan
380*eed53cd4SHONG Yifan    # Show all calls until the next one within this module (typically run_suite):
381*eed53cd4SHONG Yifan    while (( i < ${#FUNCNAME[@]} )) && [[ ${BASH_SOURCE[i]:-} != ${BASH_SOURCE[0]} ]]; do
382*eed53cd4SHONG Yifan        # Read online docs for BASH_LINENO to understand the strange offset.
383*eed53cd4SHONG Yifan        # Undefined can occur in the BASH_SOURCE stack apparently when one exits from a subshell
384*eed53cd4SHONG Yifan        echo "${BASH_SOURCE[i]:-"Unknown"}:${BASH_LINENO[i - 1]:-"Unknown"}: in call to ${FUNCNAME[i]:-"Unknown"}" >&2
385*eed53cd4SHONG Yifan        (( ++i ))
386*eed53cd4SHONG Yifan        trace_found=1
387*eed53cd4SHONG Yifan    done
388*eed53cd4SHONG Yifan
389*eed53cd4SHONG Yifan    [ $trace_found = 1 ] || echo "[Stack trace not available]" >&2
390*eed53cd4SHONG Yifan}
391*eed53cd4SHONG Yifan
392*eed53cd4SHONG Yifan# Usage: expect_log <regexp> [error-message]
393*eed53cd4SHONG Yifan# Asserts that $TEST_log matches regexp.  Prints the contents of
394*eed53cd4SHONG Yifan# $TEST_log and the specified (optional) error message otherwise, and
395*eed53cd4SHONG Yifan# returns non-zero.
396*eed53cd4SHONG Yifanfunction expect_log() {
397*eed53cd4SHONG Yifan    local pattern=$1
398*eed53cd4SHONG Yifan    local message=${2:-Expected regexp "$pattern" not found}
399*eed53cd4SHONG Yifan    grep -sq -- "$pattern" $TEST_log && return 0
400*eed53cd4SHONG Yifan
401*eed53cd4SHONG Yifan    fail "$message"
402*eed53cd4SHONG Yifan    return 1
403*eed53cd4SHONG Yifan}
404*eed53cd4SHONG Yifan
405*eed53cd4SHONG Yifan# Usage: expect_log_warn <regexp> [error-message]
406*eed53cd4SHONG Yifan# Warns if $TEST_log does not match regexp.  Prints the contents of
407*eed53cd4SHONG Yifan# $TEST_log and the specified (optional) error message on mismatch.
408*eed53cd4SHONG Yifanfunction expect_log_warn() {
409*eed53cd4SHONG Yifan    local pattern=$1
410*eed53cd4SHONG Yifan    local message=${2:-Expected regexp "$pattern" not found}
411*eed53cd4SHONG Yifan    grep -sq -- "$pattern" $TEST_log && return 0
412*eed53cd4SHONG Yifan
413*eed53cd4SHONG Yifan    warn "$message"
414*eed53cd4SHONG Yifan    return 1
415*eed53cd4SHONG Yifan}
416*eed53cd4SHONG Yifan
417*eed53cd4SHONG Yifan# Usage: expect_log_once <regexp> [error-message]
418*eed53cd4SHONG Yifan# Asserts that $TEST_log contains one line matching <regexp>.
419*eed53cd4SHONG Yifan# Prints the contents of $TEST_log and the specified (optional)
420*eed53cd4SHONG Yifan# error message otherwise, and returns non-zero.
421*eed53cd4SHONG Yifanfunction expect_log_once() {
422*eed53cd4SHONG Yifan    local pattern=$1
423*eed53cd4SHONG Yifan    local message=${2:-Expected regexp "$pattern" not found exactly once}
424*eed53cd4SHONG Yifan    expect_log_n "$pattern" 1 "$message"
425*eed53cd4SHONG Yifan}
426*eed53cd4SHONG Yifan
427*eed53cd4SHONG Yifan# Usage: expect_log_n <regexp> <count> [error-message]
428*eed53cd4SHONG Yifan# Asserts that $TEST_log contains <count> lines matching <regexp>.
429*eed53cd4SHONG Yifan# Prints the contents of $TEST_log and the specified (optional)
430*eed53cd4SHONG Yifan# error message otherwise, and returns non-zero.
431*eed53cd4SHONG Yifanfunction expect_log_n() {
432*eed53cd4SHONG Yifan    local pattern=$1
433*eed53cd4SHONG Yifan    local expectednum=${2:-1}
434*eed53cd4SHONG Yifan    local message=${3:-Expected regexp "$pattern" not found exactly $expectednum times}
435*eed53cd4SHONG Yifan    local count=$(grep -sc -- "$pattern" $TEST_log)
436*eed53cd4SHONG Yifan    [[ $count = $expectednum ]] && return 0
437*eed53cd4SHONG Yifan    fail "$message"
438*eed53cd4SHONG Yifan    return 1
439*eed53cd4SHONG Yifan}
440*eed53cd4SHONG Yifan
441*eed53cd4SHONG Yifan# Usage: expect_not_log <regexp> [error-message]
442*eed53cd4SHONG Yifan# Asserts that $TEST_log does not match regexp.  Prints the contents
443*eed53cd4SHONG Yifan# of $TEST_log and the specified (optional) error message otherwise, and
444*eed53cd4SHONG Yifan# returns non-zero.
445*eed53cd4SHONG Yifanfunction expect_not_log() {
446*eed53cd4SHONG Yifan    local pattern=$1
447*eed53cd4SHONG Yifan    local message=${2:-Unexpected regexp "$pattern" found}
448*eed53cd4SHONG Yifan    grep -sq -- "$pattern" $TEST_log || return 0
449*eed53cd4SHONG Yifan
450*eed53cd4SHONG Yifan    fail "$message"
451*eed53cd4SHONG Yifan    return 1
452*eed53cd4SHONG Yifan}
453*eed53cd4SHONG Yifan
454*eed53cd4SHONG Yifan# Usage: expect_log_with_timeout <regexp> <timeout> [error-message]
455*eed53cd4SHONG Yifan# Waits for the given regexp in the $TEST_log for up to timeout seconds.
456*eed53cd4SHONG Yifan# Prints the contents of $TEST_log and the specified (optional)
457*eed53cd4SHONG Yifan# error message otherwise, and returns non-zero.
458*eed53cd4SHONG Yifanfunction expect_log_with_timeout() {
459*eed53cd4SHONG Yifan    local pattern=$1
460*eed53cd4SHONG Yifan    local timeout=$2
461*eed53cd4SHONG Yifan    local message=${3:-Regexp "$pattern" not found in "$timeout" seconds}
462*eed53cd4SHONG Yifan    local count=0
463*eed53cd4SHONG Yifan    while [ $count -lt $timeout ]; do
464*eed53cd4SHONG Yifan      grep -sq -- "$pattern" $TEST_log && return 0
465*eed53cd4SHONG Yifan      let count=count+1
466*eed53cd4SHONG Yifan      sleep 1
467*eed53cd4SHONG Yifan    done
468*eed53cd4SHONG Yifan
469*eed53cd4SHONG Yifan    grep -sq -- "$pattern" $TEST_log && return 0
470*eed53cd4SHONG Yifan    fail "$message"
471*eed53cd4SHONG Yifan    return 1
472*eed53cd4SHONG Yifan}
473*eed53cd4SHONG Yifan
474*eed53cd4SHONG Yifan# Usage: expect_cmd_with_timeout <expected> <cmd> [timeout]
475*eed53cd4SHONG Yifan# Repeats the command once a second for up to timeout seconds (10s by default),
476*eed53cd4SHONG Yifan# until the output matches the expected value. Fails and returns 1 if
477*eed53cd4SHONG Yifan# the command does not return the expected value in the end.
478*eed53cd4SHONG Yifanfunction expect_cmd_with_timeout() {
479*eed53cd4SHONG Yifan    local expected="$1"
480*eed53cd4SHONG Yifan    local cmd="$2"
481*eed53cd4SHONG Yifan    local timeout=${3:-10}
482*eed53cd4SHONG Yifan    local count=0
483*eed53cd4SHONG Yifan    while [ $count -lt $timeout ]; do
484*eed53cd4SHONG Yifan      local actual="$($cmd)"
485*eed53cd4SHONG Yifan      [ "$expected" = "$actual" ] && return 0
486*eed53cd4SHONG Yifan      let count=count+1
487*eed53cd4SHONG Yifan      sleep 1
488*eed53cd4SHONG Yifan    done
489*eed53cd4SHONG Yifan
490*eed53cd4SHONG Yifan    [ "$expected" = "$actual" ] && return 0
491*eed53cd4SHONG Yifan    fail "Expected '$expected' within ${timeout}s, was '$actual'"
492*eed53cd4SHONG Yifan    return 1
493*eed53cd4SHONG Yifan}
494*eed53cd4SHONG Yifan
495*eed53cd4SHONG Yifan# Usage: assert_one_of <expected_list>... <actual>
496*eed53cd4SHONG Yifan# Asserts that actual is one of the items in expected_list
497*eed53cd4SHONG Yifan# Example: assert_one_of ( "foo", "bar", "baz" ) actualval
498*eed53cd4SHONG Yifanfunction assert_one_of() {
499*eed53cd4SHONG Yifan    local args=("$@")
500*eed53cd4SHONG Yifan    local last_arg_index=$((${#args[@]} - 1))
501*eed53cd4SHONG Yifan    local actual=${args[last_arg_index]}
502*eed53cd4SHONG Yifan    unset args[last_arg_index]
503*eed53cd4SHONG Yifan    for expected_item in "${args[@]}"; do
504*eed53cd4SHONG Yifan      [ "$expected_item" = "$actual" ] && return 0
505*eed53cd4SHONG Yifan    done;
506*eed53cd4SHONG Yifan
507*eed53cd4SHONG Yifan    fail "Expected one of '${args[@]}', was '$actual'"
508*eed53cd4SHONG Yifan    return 1
509*eed53cd4SHONG Yifan}
510*eed53cd4SHONG Yifan
511*eed53cd4SHONG Yifan# Usage: assert_equals <expected> <actual>
512*eed53cd4SHONG Yifan# Asserts [ expected = actual ].
513*eed53cd4SHONG Yifanfunction assert_equals() {
514*eed53cd4SHONG Yifan    local expected=$1 actual=$2
515*eed53cd4SHONG Yifan    [ "$expected" = "$actual" ] && return 0
516*eed53cd4SHONG Yifan
517*eed53cd4SHONG Yifan    fail "Expected '$expected', was '$actual'"
518*eed53cd4SHONG Yifan    return 1
519*eed53cd4SHONG Yifan}
520*eed53cd4SHONG Yifan
521*eed53cd4SHONG Yifan# Usage: assert_not_equals <unexpected> <actual>
522*eed53cd4SHONG Yifan# Asserts [ unexpected != actual ].
523*eed53cd4SHONG Yifanfunction assert_not_equals() {
524*eed53cd4SHONG Yifan    local unexpected=$1 actual=$2
525*eed53cd4SHONG Yifan    [ "$unexpected" != "$actual" ] && return 0;
526*eed53cd4SHONG Yifan
527*eed53cd4SHONG Yifan    fail "Expected not '$unexpected', was '$actual'"
528*eed53cd4SHONG Yifan    return 1
529*eed53cd4SHONG Yifan}
530*eed53cd4SHONG Yifan
531*eed53cd4SHONG Yifan# Usage: assert_contains <regexp> <file> [error-message]
532*eed53cd4SHONG Yifan# Asserts that file matches regexp.  Prints the contents of
533*eed53cd4SHONG Yifan# file and the specified (optional) error message otherwise, and
534*eed53cd4SHONG Yifan# returns non-zero.
535*eed53cd4SHONG Yifanfunction assert_contains() {
536*eed53cd4SHONG Yifan    local pattern=$1
537*eed53cd4SHONG Yifan    local file=$2
538*eed53cd4SHONG Yifan    local message=${3:-Expected regexp "$pattern" not found in "$file"}
539*eed53cd4SHONG Yifan    grep -sq -- "$pattern" "$file" && return 0
540*eed53cd4SHONG Yifan
541*eed53cd4SHONG Yifan    cat "$file" >&2
542*eed53cd4SHONG Yifan    fail "$message"
543*eed53cd4SHONG Yifan    return 1
544*eed53cd4SHONG Yifan}
545*eed53cd4SHONG Yifan
546*eed53cd4SHONG Yifan# Usage: assert_not_contains <regexp> <file> [error-message]
547*eed53cd4SHONG Yifan# Asserts that file does not match regexp.  Prints the contents of
548*eed53cd4SHONG Yifan# file and the specified (optional) error message otherwise, and
549*eed53cd4SHONG Yifan# returns non-zero.
550*eed53cd4SHONG Yifanfunction assert_not_contains() {
551*eed53cd4SHONG Yifan    local pattern=$1
552*eed53cd4SHONG Yifan    local file=$2
553*eed53cd4SHONG Yifan    local message=${3:-Expected regexp "$pattern" found in "$file"}
554*eed53cd4SHONG Yifan    grep -sq -- "$pattern" "$file" || return 0
555*eed53cd4SHONG Yifan
556*eed53cd4SHONG Yifan    cat "$file" >&2
557*eed53cd4SHONG Yifan    fail "$message"
558*eed53cd4SHONG Yifan    return 1
559*eed53cd4SHONG Yifan}
560*eed53cd4SHONG Yifan
561*eed53cd4SHONG Yifan# Updates the global variables TESTS if
562*eed53cd4SHONG Yifan# sharding is enabled, i.e. ($TEST_TOTAL_SHARDS > 0).
563*eed53cd4SHONG Yifanfunction __update_shards() {
564*eed53cd4SHONG Yifan    [ -z "${TEST_TOTAL_SHARDS-}" ] && return 0
565*eed53cd4SHONG Yifan
566*eed53cd4SHONG Yifan    [ "$TEST_TOTAL_SHARDS" -gt 0 ] ||
567*eed53cd4SHONG Yifan      { echo "Invalid total shards $TEST_TOTAL_SHARDS" >&2; exit 1; }
568*eed53cd4SHONG Yifan
569*eed53cd4SHONG Yifan    [ "$TEST_SHARD_INDEX" -lt 0 -o "$TEST_SHARD_INDEX" -ge  "$TEST_TOTAL_SHARDS" ] &&
570*eed53cd4SHONG Yifan      { echo "Invalid shard $shard_index" >&2; exit 1; }
571*eed53cd4SHONG Yifan
572*eed53cd4SHONG Yifan    TESTS=$(for test in "${TESTS[@]}"; do echo "$test"; done |
573*eed53cd4SHONG Yifan      awk "NR % $TEST_TOTAL_SHARDS == $TEST_SHARD_INDEX")
574*eed53cd4SHONG Yifan
575*eed53cd4SHONG Yifan    [ -z "${TEST_SHARD_STATUS_FILE-}" ] || touch "$TEST_SHARD_STATUS_FILE"
576*eed53cd4SHONG Yifan}
577*eed53cd4SHONG Yifan
578*eed53cd4SHONG Yifan# Usage: __test_terminated <signal-number>
579*eed53cd4SHONG Yifan# Handler that is called when the test terminated unexpectedly
580*eed53cd4SHONG Yifanfunction __test_terminated() {
581*eed53cd4SHONG Yifan    __show_log >&2
582*eed53cd4SHONG Yifan    echo "$TEST_name FAILED: terminated by signal $1." >&2
583*eed53cd4SHONG Yifan    TEST_passed="false"
584*eed53cd4SHONG Yifan    __show_stack
585*eed53cd4SHONG Yifan    timeout
586*eed53cd4SHONG Yifan    exit 1
587*eed53cd4SHONG Yifan}
588*eed53cd4SHONG Yifan
589*eed53cd4SHONG Yifan# Usage: __test_terminated_err
590*eed53cd4SHONG Yifan# Handler that is called when the test terminated unexpectedly due to "errexit".
591*eed53cd4SHONG Yifanfunction __test_terminated_err() {
592*eed53cd4SHONG Yifan    # When a subshell exits due to signal ERR, its parent shell also exits,
593*eed53cd4SHONG Yifan    # thus the signal handler is called recursively and we print out the
594*eed53cd4SHONG Yifan    # error message and stack trace multiple times. We're only interested
595*eed53cd4SHONG Yifan    # in the first one though, as it contains the most information, so ignore
596*eed53cd4SHONG Yifan    # all following.
597*eed53cd4SHONG Yifan    if [[ -f $TEST_TMPDIR/__err_handled ]]; then
598*eed53cd4SHONG Yifan      exit 1
599*eed53cd4SHONG Yifan    fi
600*eed53cd4SHONG Yifan    __show_log >&2
601*eed53cd4SHONG Yifan    if [[ ! -z "$TEST_name" ]]; then
602*eed53cd4SHONG Yifan      echo -n "$TEST_name "
603*eed53cd4SHONG Yifan    fi
604*eed53cd4SHONG Yifan    echo "FAILED: terminated because this command returned a non-zero status:" >&2
605*eed53cd4SHONG Yifan    touch $TEST_TMPDIR/__err_handled
606*eed53cd4SHONG Yifan    TEST_passed="false"
607*eed53cd4SHONG Yifan    __show_stack
608*eed53cd4SHONG Yifan    # If $TEST_name is still empty, the test suite failed before we even started
609*eed53cd4SHONG Yifan    # to run tests, so we shouldn't call tear_down.
610*eed53cd4SHONG Yifan    if [[ ! -z "$TEST_name" ]]; then
611*eed53cd4SHONG Yifan      tear_down
612*eed53cd4SHONG Yifan    fi
613*eed53cd4SHONG Yifan    exit 1
614*eed53cd4SHONG Yifan}
615*eed53cd4SHONG Yifan
616*eed53cd4SHONG Yifan# Usage: __trap_with_arg <handler> <signals ...>
617*eed53cd4SHONG Yifan# Helper to install a trap handler for several signals preserving the signal
618*eed53cd4SHONG Yifan# number, so that the signal number is available to the trap handler.
619*eed53cd4SHONG Yifanfunction __trap_with_arg() {
620*eed53cd4SHONG Yifan    func="$1" ; shift
621*eed53cd4SHONG Yifan    for sig ; do
622*eed53cd4SHONG Yifan        trap "$func $sig" "$sig"
623*eed53cd4SHONG Yifan    done
624*eed53cd4SHONG Yifan}
625*eed53cd4SHONG Yifan
626*eed53cd4SHONG Yifan# Usage: <node> <block>
627*eed53cd4SHONG Yifan# Adds the block to the given node in the report file. Quotes in the in
628*eed53cd4SHONG Yifan# arguments need to be escaped.
629*eed53cd4SHONG Yifanfunction __log_to_test_report() {
630*eed53cd4SHONG Yifan    local node="$1"
631*eed53cd4SHONG Yifan    local block="$2"
632*eed53cd4SHONG Yifan    if [[ ! -e "$XML_OUTPUT_FILE" ]]; then
633*eed53cd4SHONG Yifan        local xml_header='<?xml version="1.0" encoding="UTF-8"?>'
634*eed53cd4SHONG Yifan        echo "$xml_header<testsuites></testsuites>" > $XML_OUTPUT_FILE
635*eed53cd4SHONG Yifan    fi
636*eed53cd4SHONG Yifan
637*eed53cd4SHONG Yifan    # replace match on node with block and match
638*eed53cd4SHONG Yifan    # replacement expression only needs escaping for quotes
639*eed53cd4SHONG Yifan    perl -e "\
640*eed53cd4SHONG Yifan\$input = @ARGV[0]; \
641*eed53cd4SHONG Yifan\$/=undef; \
642*eed53cd4SHONG Yifanopen FILE, '+<$XML_OUTPUT_FILE'; \
643*eed53cd4SHONG Yifan\$content = <FILE>; \
644*eed53cd4SHONG Yifanif (\$content =~ /($node.*)\$/) { \
645*eed53cd4SHONG Yifan  seek FILE, 0, 0; \
646*eed53cd4SHONG Yifan  print FILE \$\` . \$input . \$1; \
647*eed53cd4SHONG Yifan}; \
648*eed53cd4SHONG Yifanclose FILE" "$block"
649*eed53cd4SHONG Yifan}
650*eed53cd4SHONG Yifan
651*eed53cd4SHONG Yifan# Usage: <total> <passed>
652*eed53cd4SHONG Yifan# Adds the test summaries to the xml nodes.
653*eed53cd4SHONG Yifanfunction __finish_test_report() {
654*eed53cd4SHONG Yifan    local total=$1
655*eed53cd4SHONG Yifan    local passed=$2
656*eed53cd4SHONG Yifan    local failed=$((total - passed))
657*eed53cd4SHONG Yifan
658*eed53cd4SHONG Yifan    cat $XML_OUTPUT_FILE | \
659*eed53cd4SHONG Yifan      sed \
660*eed53cd4SHONG Yifan        "s/<testsuites>/<testsuites tests=\"$total\" failures=\"0\" errors=\"$failed\">/" | \
661*eed53cd4SHONG Yifan      sed \
662*eed53cd4SHONG Yifan        "s/<testsuite>/<testsuite tests=\"$total\" failures=\"0\" errors=\"$failed\">/" \
663*eed53cd4SHONG Yifan         > $XML_OUTPUT_FILE.bak
664*eed53cd4SHONG Yifan
665*eed53cd4SHONG Yifan    rm -f $XML_OUTPUT_FILE
666*eed53cd4SHONG Yifan    mv $XML_OUTPUT_FILE.bak $XML_OUTPUT_FILE
667*eed53cd4SHONG Yifan}
668*eed53cd4SHONG Yifan
669*eed53cd4SHONG Yifan# Multi-platform timestamp function
670*eed53cd4SHONG YifanUNAME=$(uname -s | tr 'A-Z' 'a-z')
671*eed53cd4SHONG Yifanif [ "$UNAME" = "linux" ] || [[ "$UNAME" =~ msys_nt* ]]; then
672*eed53cd4SHONG Yifan    function timestamp() {
673*eed53cd4SHONG Yifan      echo $(($(date +%s%N)/1000000))
674*eed53cd4SHONG Yifan    }
675*eed53cd4SHONG Yifanelse
676*eed53cd4SHONG Yifan    function timestamp() {
677*eed53cd4SHONG Yifan      # OS X and FreeBSD do not have %N so python is the best we can do
678*eed53cd4SHONG Yifan      python -c 'import time; print int(round(time.time() * 1000))'
679*eed53cd4SHONG Yifan    }
680*eed53cd4SHONG Yifanfi
681*eed53cd4SHONG Yifan
682*eed53cd4SHONG Yifanfunction get_run_time() {
683*eed53cd4SHONG Yifan  local ts_start=$1
684*eed53cd4SHONG Yifan  local ts_end=$2
685*eed53cd4SHONG Yifan  run_time_ms=$((${ts_end}-${ts_start}))
686*eed53cd4SHONG Yifan  echo $(($run_time_ms/1000)).${run_time_ms: -3}
687*eed53cd4SHONG Yifan}
688*eed53cd4SHONG Yifan
689*eed53cd4SHONG Yifan# Usage: run_tests <suite-comment>
690*eed53cd4SHONG Yifan# Must be called from the end of the user's test suite.
691*eed53cd4SHONG Yifan# Calls exit with zero on success, non-zero otherwise.
692*eed53cd4SHONG Yifanfunction run_suite() {
693*eed53cd4SHONG Yifan    echo >&2
694*eed53cd4SHONG Yifan    echo "$1" >&2
695*eed53cd4SHONG Yifan    echo >&2
696*eed53cd4SHONG Yifan
697*eed53cd4SHONG Yifan    __log_to_test_report "<\/testsuites>" "<testsuite></testsuite>"
698*eed53cd4SHONG Yifan
699*eed53cd4SHONG Yifan    local total=0
700*eed53cd4SHONG Yifan    local passed=0
701*eed53cd4SHONG Yifan
702*eed53cd4SHONG Yifan    atexit "cleanup"
703*eed53cd4SHONG Yifan
704*eed53cd4SHONG Yifan    # If the user didn't specify an explicit list of tests (e.g. a
705*eed53cd4SHONG Yifan    # working set), use them all.
706*eed53cd4SHONG Yifan    if [ ${#TESTS[@]} = 0 ]; then
707*eed53cd4SHONG Yifan      TESTS=$(declare -F | awk '{print $3}' | grep ^test_)
708*eed53cd4SHONG Yifan    elif [ -n "${TEST_WARNINGS_OUTPUT_FILE:-}" ]; then
709*eed53cd4SHONG Yifan      if grep -q "TESTS=" "$TEST_script" ; then
710*eed53cd4SHONG Yifan        echo "TESTS variable overridden in Bazel sh_test. Please remove before submitting" \
711*eed53cd4SHONG Yifan          >> "$TEST_WARNINGS_OUTPUT_FILE"
712*eed53cd4SHONG Yifan      fi
713*eed53cd4SHONG Yifan    fi
714*eed53cd4SHONG Yifan
715*eed53cd4SHONG Yifan    __update_shards
716*eed53cd4SHONG Yifan
717*eed53cd4SHONG Yifan    for TEST_name in ${TESTS[@]}; do
718*eed53cd4SHONG Yifan      >$TEST_log # Reset the log.
719*eed53cd4SHONG Yifan      TEST_passed="true"
720*eed53cd4SHONG Yifan
721*eed53cd4SHONG Yifan      total=$(($total + 1))
722*eed53cd4SHONG Yifan      if [[ "$TEST_verbose" == "true" ]]; then
723*eed53cd4SHONG Yifan          __pad $TEST_name '*' >&2
724*eed53cd4SHONG Yifan      fi
725*eed53cd4SHONG Yifan
726*eed53cd4SHONG Yifan      local run_time="0.0"
727*eed53cd4SHONG Yifan      rm -f $TEST_TMPDIR/{__ts_start,__ts_end}
728*eed53cd4SHONG Yifan
729*eed53cd4SHONG Yifan      if [ "$(type -t $TEST_name)" = function ]; then
730*eed53cd4SHONG Yifan        # Save exit handlers eventually set.
731*eed53cd4SHONG Yifan        local SAVED_ATEXIT="$ATEXIT";
732*eed53cd4SHONG Yifan        ATEXIT=
733*eed53cd4SHONG Yifan
734*eed53cd4SHONG Yifan        # Run test in a subshell.
735*eed53cd4SHONG Yifan        rm -f $TEST_TMPDIR/__err_handled
736*eed53cd4SHONG Yifan        __trap_with_arg __test_terminated INT KILL PIPE TERM ABRT FPE ILL QUIT SEGV
737*eed53cd4SHONG Yifan        (
738*eed53cd4SHONG Yifan          timestamp >$TEST_TMPDIR/__ts_start
739*eed53cd4SHONG Yifan          set_up
740*eed53cd4SHONG Yifan          eval $TEST_name
741*eed53cd4SHONG Yifan          tear_down
742*eed53cd4SHONG Yifan          timestamp >$TEST_TMPDIR/__ts_end
743*eed53cd4SHONG Yifan          test $TEST_passed == "true"
744*eed53cd4SHONG Yifan        ) 2>&1 | tee $TEST_TMPDIR/__log
745*eed53cd4SHONG Yifan        # Note that tee will prevent the control flow continuing if the test
746*eed53cd4SHONG Yifan        # spawned any processes which are still running and have not closed
747*eed53cd4SHONG Yifan        # their stdout.
748*eed53cd4SHONG Yifan
749*eed53cd4SHONG Yifan        test_subshell_status=${PIPESTATUS[0]}
750*eed53cd4SHONG Yifan        if [ "$test_subshell_status" != 0 ]; then
751*eed53cd4SHONG Yifan          TEST_passed="false"
752*eed53cd4SHONG Yifan          # Ensure that an end time is recorded in case the test subshell
753*eed53cd4SHONG Yifan          # terminated prematurely.
754*eed53cd4SHONG Yifan          [ -f $TEST_TMPDIR/__ts_end ] || timestamp >$TEST_TMPDIR/__ts_end
755*eed53cd4SHONG Yifan        fi
756*eed53cd4SHONG Yifan
757*eed53cd4SHONG Yifan        # Calculate run time for the testcase.
758*eed53cd4SHONG Yifan        local ts_start=$(cat $TEST_TMPDIR/__ts_start)
759*eed53cd4SHONG Yifan        local ts_end=$(cat $TEST_TMPDIR/__ts_end)
760*eed53cd4SHONG Yifan        run_time=$(get_run_time $ts_start $ts_end)
761*eed53cd4SHONG Yifan
762*eed53cd4SHONG Yifan        # Eventually restore exit handlers.
763*eed53cd4SHONG Yifan        if [ -n "$SAVED_ATEXIT" ]; then
764*eed53cd4SHONG Yifan          ATEXIT="$SAVED_ATEXIT"
765*eed53cd4SHONG Yifan          trap "$ATEXIT" EXIT
766*eed53cd4SHONG Yifan        fi
767*eed53cd4SHONG Yifan      else # Bad test explicitly specified in $TESTS.
768*eed53cd4SHONG Yifan        fail "Not a function: '$TEST_name'"
769*eed53cd4SHONG Yifan      fi
770*eed53cd4SHONG Yifan
771*eed53cd4SHONG Yifan      local testcase_tag=""
772*eed53cd4SHONG Yifan
773*eed53cd4SHONG Yifan      if [[ "$TEST_passed" == "true" ]]; then
774*eed53cd4SHONG Yifan        if [[ "$TEST_verbose" == "true" ]]; then
775*eed53cd4SHONG Yifan          echo "PASSED: $TEST_name" >&2
776*eed53cd4SHONG Yifan        fi
777*eed53cd4SHONG Yifan        passed=$(($passed + 1))
778*eed53cd4SHONG Yifan        testcase_tag="<testcase name=\"$TEST_name\" status=\"run\" time=\"$run_time\" classname=\"\"></testcase>"
779*eed53cd4SHONG Yifan      else
780*eed53cd4SHONG Yifan        echo "FAILED: $TEST_name" >&2
781*eed53cd4SHONG Yifan        # end marker in CDATA cannot be escaped, we need to split the CDATA sections
782*eed53cd4SHONG Yifan        log=$(cat $TEST_TMPDIR/__log | sed 's/]]>/]]>]]&gt;<![CDATA[/g')
783*eed53cd4SHONG Yifan        fail_msg=$(cat $TEST_TMPDIR/__fail 2> /dev/null || echo "No failure message")
784*eed53cd4SHONG Yifan        testcase_tag="<testcase name=\"$TEST_name\" status=\"run\" time=\"$run_time\" classname=\"\"><error message=\"$fail_msg\"><![CDATA[$log]]></error></testcase>"
785*eed53cd4SHONG Yifan      fi
786*eed53cd4SHONG Yifan
787*eed53cd4SHONG Yifan      if [[ "$TEST_verbose" == "true" ]]; then
788*eed53cd4SHONG Yifan          echo >&2
789*eed53cd4SHONG Yifan      fi
790*eed53cd4SHONG Yifan      __log_to_test_report "<\/testsuite>" "$testcase_tag"
791*eed53cd4SHONG Yifan    done
792*eed53cd4SHONG Yifan
793*eed53cd4SHONG Yifan    __finish_test_report $total $passed
794*eed53cd4SHONG Yifan    __pad "$passed / $total tests passed." '*' >&2
795*eed53cd4SHONG Yifan    [ $total = $passed ] || {
796*eed53cd4SHONG Yifan      __pad "There were errors." '*'
797*eed53cd4SHONG Yifan      exit 1
798*eed53cd4SHONG Yifan    } >&2
799*eed53cd4SHONG Yifan
800*eed53cd4SHONG Yifan    exit 0
801*eed53cd4SHONG Yifan}
802