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