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/]]>/]]>]]><![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