1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
|
#!/bin/bash
# Copyright 2015 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# shift stderr to stdout.
exec 2>&1
no_echo=
if [[ "$1" = "--no_echo" ]]; then
# Don't print anything to stdout in this special case.
# Currently needed for persistent test runner.
no_echo="true"
shift
else
echo 'exec ${PAGER:-/usr/bin/less} "$0" || exit 1'
echo "Executing tests from ${TEST_TARGET}"
fi
function is_absolute {
[[ "$1" = /* ]] || [[ "$1" =~ ^[a-zA-Z]:[/\\].* ]]
}
# The original execution root. Usually this script changes directory into the
# runfiles directory, so using $PWD is not a reliable way to find the execution
# root.
EXEC_ROOT="$PWD"
# Bazel sets some environment vars to relative paths to improve caching and
# support remote execution, where the absolute path may not be known to Bazel.
# Convert them to absolute paths here before running the actual test.
is_absolute "$TEST_PREMATURE_EXIT_FILE" ||
TEST_PREMATURE_EXIT_FILE="$PWD/$TEST_PREMATURE_EXIT_FILE"
is_absolute "$TEST_WARNINGS_OUTPUT_FILE" ||
TEST_WARNINGS_OUTPUT_FILE="$PWD/$TEST_WARNINGS_OUTPUT_FILE"
is_absolute "$TEST_LOGSPLITTER_OUTPUT_FILE" ||
TEST_LOGSPLITTER_OUTPUT_FILE="$PWD/$TEST_LOGSPLITTER_OUTPUT_FILE"
is_absolute "$TEST_INFRASTRUCTURE_FAILURE_FILE" ||
TEST_INFRASTRUCTURE_FAILURE_FILE="$PWD/$TEST_INFRASTRUCTURE_FAILURE_FILE"
is_absolute "$TEST_UNUSED_RUNFILES_LOG_FILE" ||
TEST_UNUSED_RUNFILES_LOG_FILE="$PWD/$TEST_UNUSED_RUNFILES_LOG_FILE"
is_absolute "$TEST_UNDECLARED_OUTPUTS_DIR" ||
TEST_UNDECLARED_OUTPUTS_DIR="$PWD/$TEST_UNDECLARED_OUTPUTS_DIR"
is_absolute "$TEST_UNDECLARED_OUTPUTS_MANIFEST" ||
TEST_UNDECLARED_OUTPUTS_MANIFEST="$PWD/$TEST_UNDECLARED_OUTPUTS_MANIFEST"
is_absolute "$TEST_UNDECLARED_OUTPUTS_ZIP" ||
TEST_UNDECLARED_OUTPUTS_ZIP="$PWD/$TEST_UNDECLARED_OUTPUTS_ZIP"
is_absolute "$TEST_UNDECLARED_OUTPUTS_ANNOTATIONS" ||
TEST_UNDECLARED_OUTPUTS_ANNOTATIONS="$PWD/$TEST_UNDECLARED_OUTPUTS_ANNOTATIONS"
is_absolute "$TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR" ||
TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR="$PWD/$TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR"
is_absolute "$TEST_SRCDIR" || TEST_SRCDIR="$PWD/$TEST_SRCDIR"
is_absolute "$TEST_TMPDIR" || TEST_TMPDIR="$PWD/$TEST_TMPDIR"
is_absolute "$HOME" || HOME="$TEST_TMPDIR"
is_absolute "$XML_OUTPUT_FILE" || XML_OUTPUT_FILE="$PWD/$XML_OUTPUT_FILE"
# Set USER to the current user, unless passed by Bazel via --test_env.
if [[ -z "$USER" ]]; then
export USER=$(whoami)
fi
# The test shard status file is only set for sharded tests.
if [[ -n "$TEST_SHARD_STATUS_FILE" ]]; then
is_absolute "$TEST_SHARD_STATUS_FILE" || TEST_SHARD_STATUS_FILE="$PWD/$TEST_SHARD_STATUS_FILE"
mkdir -p "$(dirname "$TEST_SHARD_STATUS_FILE")"
fi
is_absolute "$RUNFILES_DIR" || RUNFILES_DIR="$PWD/$RUNFILES_DIR"
# TODO(ulfjack): Standardize on RUNFILES_DIR and remove the {JAVA,PYTHON}_RUNFILES vars.
is_absolute "$JAVA_RUNFILES" || JAVA_RUNFILES="$PWD/$JAVA_RUNFILES"
is_absolute "$PYTHON_RUNFILES" || PYTHON_RUNFILES="$PWD/$PYTHON_RUNFILES"
# Create directories for undeclared outputs and their annotations
mkdir -p "$(dirname "$XML_OUTPUT_FILE")" \
"$TEST_UNDECLARED_OUTPUTS_DIR" \
"$TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR"
# Create the test temp directory, which may not exist on the remote host when
# doing a remote build.
mkdir -p "$TEST_TMPDIR"
# Unexport environment variables related to undeclared test outputs that are
# only supposed to be used in this script.
export -n TEST_UNDECLARED_OUTPUTS_MANIFEST
export -n TEST_UNDECLARED_OUTPUTS_ZIP
export -n TEST_UNDECLARED_OUTPUTS_ANNOTATIONS
# Tell googletest about Bazel sharding.
if [[ -n "${TEST_TOTAL_SHARDS+x}" ]] && ((TEST_TOTAL_SHARDS != 0)); then
export GTEST_SHARD_INDEX="${TEST_SHARD_INDEX}"
export GTEST_TOTAL_SHARDS="${TEST_TOTAL_SHARDS}"
fi
export GTEST_TMP_DIR="${TEST_TMPDIR}"
# TODO(ulfjack): Update Gunit to accept XML_OUTPUT_FILE and drop this env
# variable.
GUNIT_OUTPUT="xml:${XML_OUTPUT_FILE}"
RUNFILES_MANIFEST_FILE="${TEST_SRCDIR}/MANIFEST"
function rlocation() {
if is_absolute "$1" ; then
# If the file path is already fully specified, simply return it.
echo "$1"
elif [[ -e "$TEST_SRCDIR/$1" ]]; then
# If the file exists in the $TEST_SRCDIR then just use it.
echo "$TEST_SRCDIR/$1"
elif [[ -e "$RUNFILES_MANIFEST_FILE" ]]; then
# If a runfiles manifest file exists then use it.
echo "$(grep "^$1 " "$RUNFILES_MANIFEST_FILE" | sed 's/[^ ]* //')"
fi
}
export -f rlocation
export -f is_absolute
# If RUNFILES_MANIFEST_ONLY is set to 1 and the manifest file does exist,
# then test programs should use manifest file to find runfiles.
if [[ "${RUNFILES_MANIFEST_ONLY:-}" == "1" && -e "${RUNFILES_MANIFEST_FILE:-}" ]]; then
export RUNFILES_MANIFEST_FILE
export RUNFILES_MANIFEST_ONLY
fi
DIR="$TEST_SRCDIR"
if [ ! -z "$TEST_WORKSPACE" ]; then
DIR="$DIR"/"$TEST_WORKSPACE"
fi
[[ -n "$RUNTEST_PRESERVE_CWD" ]] && DIR="$PWD"
# normal commands are run in the exec-root where they have access to
# the entire source tree. By chdir'ing to the runfiles root, tests only
# have direct access to their declared dependencies.
if [ -z "$COVERAGE_DIR" ]; then
cd "$DIR" || { echo "Could not chdir $DIR"; exit 1; }
fi
# This header marks where --test_output=streamed will start being printed.
if [[ -z "$no_echo" ]]; then
echo "-----------------------------------------------------------------------------"
fi
# Unused if EXPERIMENTAL_SPLIT_XML_GENERATION is set.
function encode_stream {
# See generate-xml.sh for documentation.
LC_ALL=C sed -E \
-e 's/.*/& /g' \
-e 's/(('\
"$(echo -e '[\x9\x20-\x7f]')|"\
"$(echo -e '[\xc0-\xdf][\x80-\xbf]')|"\
"$(echo -e '[\xe0-\xec][\x80-\xbf][\x80-\xbf]')|"\
"$(echo -e '[\xed][\x80-\x9f][\x80-\xbf]')|"\
"$(echo -e '[\xee-\xef][\x80-\xbf][\x80-\xbf]')|"\
"$(echo -e '[\xf0][\x80-\x8f][\x80-\xbf][\x80-\xbf]')"\
')*)./\1?/g' \
-e 's/(.*)\?/\1/g' \
-e 's|]]>|]]>]]<![CDATA[>|g'
}
function encode_output_file {
if [ -f "$1" ]; then
cat "$1" | encode_stream
fi
}
# Unused if EXPERIMENTAL_SPLIT_XML_GENERATION is set.
# Keep this in sync with generate-xml.sh!
function write_xml_output_file {
local duration=$(expr $(date +%s) - $start)
local errors=0
local error_msg=
local signal="${1-}"
local test_name=
if [ -n "${XML_OUTPUT_FILE-}" -a ! -f "${XML_OUTPUT_FILE-}" ]; then
# Create a default XML output file if the test runner hasn't generated it
if [ -n "${signal}" ]; then
errors=1
if [ "${signal}" = "SIGTERM" ]; then
error_msg="<error message=\"Timed out\"></error>"
else
error_msg="<error message=\"Terminated by signal ${signal}\"></error>"
fi
elif (( $exitCode != 0 )); then
errors=1
error_msg="<error message=\"exited with error code $exitCode\"></error>"
fi
test_name="${TEST_BINARY#./}"
# Ensure that test shards have unique names in the xml output.
if [[ -n "${TEST_TOTAL_SHARDS+x}" ]] && ((TEST_TOTAL_SHARDS != 0)); then
((shard_num=TEST_SHARD_INDEX+1))
test_name="${test_name}"_shard_"$shard_num"/"$TEST_TOTAL_SHARDS"
fi
cat <<EOF >${XML_OUTPUT_FILE}
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite name="$test_name" tests="1" failures="0" errors="${errors}">
<testcase name="$test_name" status="run" duration="${duration}" time="${duration}">${error_msg}</testcase>
<system-out>Generated test.log (if the file is not UTF-8, then this may be unreadable):
<![CDATA[$(encode_output_file "${XML_OUTPUT_FILE}.log")]]>
</system-out>
</testsuite>
</testsuites>
EOF
fi
rm -f "${XML_OUTPUT_FILE}.log"
}
# The path of this command-line is usually relative to the exec-root,
# but when using --run_under it can be a "/bin/bash -c" command-line.
# If the test is at the top of the tree, we have to add '.' to $PATH,
PATH=".:$PATH"
if [ -z "$COVERAGE_DIR" ]; then
EXE="${1#./}"
shift
else
EXE="${2#./}"
fi
if is_absolute "$EXE"; then
TEST_PATH="$EXE"
else
TEST_PATH="$(rlocation $TEST_WORKSPACE/$EXE)"
fi
# TODO(jsharpe): Use --test_env=TEST_SHORT_EXEC_PATH=true to activate this code
# path to workaround a bug with long executable paths when executing remote
# tests on Windows.
if [ ! -z "$TEST_SHORT_EXEC_PATH" ]; then
QUALIFIER=0
BASE="${EXEC_ROOT}/t${QUALIFIER}"
while [[ -e "${BASE}" || -e "${BASE}.exe" || -e "${BASE}.zip" ]]; do
((QUALIFIER++))
BASE="${EXEC_ROOT}/t${QUALIFIER}"
done
# Note for the commands below: "ln -s" is equivalent to "cp" on Windows.
# Needs to be in the same directory for sh_test. Ignore the error when it
# doesn't exist.
ln -s "${TEST_PATH%.*}" "${BASE}" 2>/dev/null
# Needs to be in the same directory for py_test. Ignore the error when it
# doesn't exist.
ln -s "${TEST_PATH%.*}.zip" "${BASE}.zip" 2>/dev/null
# Needed for all tests.
ln -s "${TEST_PATH}" "${BASE}.exe"
TEST_PATH="${BASE}.exe"
fi
# Helper to kill a process and its entire group.
function kill_group {
local signal="${1-}"
local pid="${2-}"
kill -$signal -$pid &> /dev/null
}
childPid=""
function signal_children {
local signal="${1-}"
if [ "${signal}" = "SIGTERM" ] && [ -z "$no_echo" ]; then
echo "-- Test timed out at $(date +"%F %T %Z") --"
fi
if [ ! -z "$childPid" ]; then
# For consistency with historical bazel behaviour, send signal to all child
# processes, not just the first one. We use the process group for this
# purpose.
kill_group $signal $childPid
fi
}
exitCode=0
signals="$(trap -l | sed -E 's/[0-9]+\)//g')"
if [[ "${EXPERIMENTAL_SPLIT_XML_GENERATION}" == "1" ]]; then
for signal in $signals; do
# SIGCHLD is expected when a subprocess dies
[ "${signal}" = "SIGCHLD" ] && continue
trap "signal_children ${signal}" ${signal}
done
else
for signal in $signals; do
# SIGCHLD is expected when a subprocess dies
[ "${signal}" = "SIGCHLD" ] && continue
trap "write_xml_output_file ${signal}; signal_children ${signal}" ${signal}
done
fi
start=$(date +%s)
# We have a challenge here: we want to forward signals to our child processes,
# but we also want them to send themselves signals like SIGINT. Catching signals
# ourselves requires use of background processes, trap, and wait. But normally
# background processes are themselves unable to receive signals like SIGINT,
# since those signals are intended for interactive processes - the only way for
# them to get SIGINT in bash is for us to run them in the foreground.
# To achieve this, we have to use `set -m` to enable Job Control in bash. This
# has the effect of putting the child processes and any children of their own
# into their own process groups, which are then able to receive SIGINT, etc,
# without our shell interfering. Of course, this has the new complication that
# anyone trying to SIGKILL *us* by group (as we know bazel's legacy process
# wrapper does) will only kill this process and not the children below it. Any
# reasonable sandboxing uses at least a process namespace, but we don't have the
# luxury of assuming one, so our children could be left behind in that
# eventuality. So, what we do is spawn a *second* background process that
# watches for us to be killed, and then chain-kills the test's process group.
# Aren't processes fun?
set -m
if [[ "${EXPERIMENTAL_SPLIT_XML_GENERATION}" == "1" ]]; then
if [ -z "$COVERAGE_DIR" ]; then
("${TEST_PATH}" "$@" 2>&1) <&0 &
else
("$1" "$TEST_PATH" "${@:3}" 2>&1) <&0 &
fi
else
if [ -z "$COVERAGE_DIR" ]; then
("${TEST_PATH}" "$@" 2> >(tee -a "${XML_OUTPUT_FILE}.log" >&2) 1> >(tee -a "${XML_OUTPUT_FILE}.log") 2>&1) <&0 &
else
("$1" "$TEST_PATH" "${@:3}" 2> >(tee -a "${XML_OUTPUT_FILE}.log" >&2) 1> >(tee -a "${XML_OUTPUT_FILE}.log") 2>&1) <&0 &
fi
fi
childPid=$!
# Cleanup helper
( if ! (ps -p $$ &> /dev/null || [ "`pgrep -a -g $$ 2> /dev/null`" != "" ] ); then
# `ps` is known to be unrunnable in the darwin sandbox-exec environment due
# to being a set-uid root program. pgrep exists in most environments, but not
# universally. In the event that we find ourselves running in an environment
# where *neither* exists, we have no reliable way to check if our parent is
# still alive - so simply disable this cleanup routine entirely.
exit 0
fi
while ps -p $$ &> /dev/null || [ "`pgrep -a -g $$ 2> /dev/null`" != "" ]; do
sleep 10
done
# Parent process not found - we've been abandoned! Clean up test processes.
kill_group SIGKILL $childPid
) &
cleanupPid=$!
set +m
wait $childPid
# If interrupted by a signal, use the signal as the exit code. But allow
# the child to actually finish from the signal we sent _it_ via signal_child.
# (Waiting on a stopped process is a no-op).
# Only once - if we receive multiple signals (of any sort), give up.
exitCode=$?
wait $childPid
# By this point, we have everything we're willing to wait for. Tidy up our own
# processes and move on.
kill_group SIGKILL $childPid
kill_group SIGKILL $cleanupPid &> /dev/null
wait $cleanupPid
for signal in $signals; do
trap - ${signal}
done
if [[ "${EXPERIMENTAL_SPLIT_XML_GENERATION}" != "1" ]]; then
# This call to write_xml_output_file does nothing if a a test.xml already
# exists, e.g., because we received SIGTERM and the trap handler created it.
write_xml_output_file
fi
# Add all of the files from the undeclared outputs directory to the manifest.
if [[ -n "$TEST_UNDECLARED_OUTPUTS_DIR" && -n "$TEST_UNDECLARED_OUTPUTS_MANIFEST" ]]; then
undeclared_outputs="$(find -L "$TEST_UNDECLARED_OUTPUTS_DIR" -type f | sort)"
# Only write the manifest if there are any undeclared outputs.
if [[ ! -z "$undeclared_outputs" ]]; then
# For each file, write a tab-separated line with name (relative to
# TEST_UNDECLARED_OUTPUTS_DIR), size, and mime type to the manifest. e.g.
# foo.txt 9 text/plain
while read -r undeclared_output; do
rel_path="${undeclared_output#$TEST_UNDECLARED_OUTPUTS_DIR/}"
# stat has different flags for different systems. -c is supported by GNU,
# and -f by BSD (and thus OSX). Try both.
file_size="$(stat -f%z "$undeclared_output" 2>/dev/null || stat -c%s "$undeclared_output" 2>/dev/null || echo "Could not stat $undeclared_output")"
file_type="$(file -L -b --mime-type "$undeclared_output")"
printf "$rel_path\t$file_size\t$file_type\n"
done <<< "$undeclared_outputs" \
> "$TEST_UNDECLARED_OUTPUTS_MANIFEST"
if [[ ! -s "$TEST_UNDECLARED_OUTPUTS_MANIFEST" ]]; then
rm "$TEST_UNDECLARED_OUTPUTS_MANIFEST"
fi
fi
fi
# Add all of the custom manifest entries to the annotation file.
if [[ -n "$TEST_UNDECLARED_OUTPUTS_ANNOTATIONS" && \
-n "$TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR" && \
-d "$TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR" ]]; then
(
shopt -s failglob
cat "$TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR"/*.part > "$TEST_UNDECLARED_OUTPUTS_ANNOTATIONS"
) 2> /dev/null
fi
# Zip up undeclared outputs.
if [[ -n "$TEST_UNDECLARED_OUTPUTS_ZIP" ]] && cd "$TEST_UNDECLARED_OUTPUTS_DIR"; then
shopt -s dotglob
if [[ "$(echo *)" != "*" ]]; then
# If * found nothing, echo printed the literal *.
# Otherwise echo printed the top-level files and directories.
# Pass files to zip with *, so paths with spaces aren't broken up.
zip -qr "$TEST_UNDECLARED_OUTPUTS_ZIP" -- * 2>/dev/null || \
echo >&2 "Could not create \"$TEST_UNDECLARED_OUTPUTS_ZIP\": zip not found or failed"
fi
fi
exit $exitCode
|