blob: fb0391db890faf8d3e81824f242bdab59ddcc22e [file] [log] [blame]
#!/bin/bash
#
# Copyright 2016 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.
#
# Integration tests for "bazel run"
NO_SIGNAL_OVERRIDE=1
# Load the test setup defined in the parent directory
CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${CURRENT_DIR}/../integration_test_setup.sh" \
|| { echo "integration_test_setup.sh not found!" >&2; exit 1; }
add_to_bazelrc "test --notest_loasd"
#### HELPER FUNCTIONS ##################################################
function write_py_files() {
mkdir -p py || fail "mkdir py failed"
echo "py_binary(name = 'binary', srcs = ['binary.py'])" > py/BUILD
echo "py_test(name = 'test', srcs = ['test.py'])" >> py/BUILD
echo "print 'Hello, Python World!'" >py/py.py
chmod +x py/py.py
ln -sf py.py py/binary.py
ln -sf py.py py/test.py
}
function write_cc_source_files() {
mkdir -p cc
cat > cc/kitty.cc <<EOF
#include <stdio.h>
int main(void) {
FILE* f;
char buf[256];
f = fopen("cc/hello_kitty.txt", "r");
if (f == NULL) {
f = fopen("cc/pussycat.txt", "r");
}
if (f == NULL) {
return 1;
}
fgets(buf, 255, f);
fclose(f);
printf("%s", buf);
return 0;
}
EOF
cat > cc/BUILD <<EOF
cc_binary(name='kitty',
srcs=['kitty.cc'],
data=glob(['*.txt']))
EOF
}
#### TESTS #############################################################
function test_run_py_binary() {
write_py_files
bazel run //py:binary >& $TEST_log || fail "Expected success"
expect_log_once 'Hello, Python World'
}
function test_run_py_test() {
write_py_files
bazel run //py:test >& $TEST_log || fail "Expected success"
expect_log_once 'Hello, Python World'
}
function test_runfiles_present_cc_binary() {
write_cc_source_files
cat > cc/hello_kitty.txt <<EOF
Hello, kitty.
EOF
bazel run --nobuild_runfile_links //cc:kitty > output \
|| fail "${PRODUCT_NAME} run failed."
assert_contains "Hello, kitty" output || fail "Output is not OK."
bazel run --nobuild_runfile_links //cc:kitty > output2 \
|| fail "Second ${PRODUCT_NAME} run failed."
assert_contains "Hello, kitty" output2 || fail "Output is not OK."
}
function test_runfiles_updated_correctly_with_nobuild_runfile_links {
write_cc_source_files
cat > cc/hello_kitty.txt <<EOF
Hello, kitty.
EOF
bazel run --nobuild_runfile_links //cc:kitty > output \
|| fail "${PRODUCT_NAME} run failed."
assert_contains "Hello, kitty" output || fail "Output is not OK."
rm cc/hello_kitty.txt
cat > cc/pussycat.txt <<EOF
A pussycat.
EOF
bazel run --nobuild_runfile_links //cc:kitty > output \
|| fail "${PRODUCT_NAME} run failed."
assert_contains "pussycat" output || fail "Output is not OK."
}
function test_run_with_no_build_runfile_manifests {
write_cc_source_files
bazel run --nobuild_runfile_manifests //cc:kitty >& $TEST_log && fail "should have failed"
expect_log_once "--nobuild_runfile_manifests is incompatible with the \"run\" command"
}
function test_script_file_generation {
mkdir -p fubar || fail "mkdir fubar failed"
echo 'sh_binary(name = "fubar", srcs = ["fubar.sh"])' > fubar/BUILD
echo 'for t in "$@"; do echo "arg: $t"; done' > fubar/fubar.sh
chmod +x fubar/fubar.sh
bazel run --script_path=$(pwd)/fubar/output.sh //fubar \
|| fail "${PRODUCT_NAME} run failed (--script_path)."
grep "fubar \"\$\@\"" ./fubar/output.sh \
|| fail "${PRODUCT_NAME} run --script_path output was incorrect."
$(pwd)/fubar/output.sh a "b c" d > ./fubar/fubar.output \
|| fail "Generated script exited with an error."
grep "arg: b c" ./fubar/fubar.output \
|| fail "Generated script did not handle arguments correctly."
}
function test_consistent_command_line_encoding {
# todo(aehlig): reenable: https://github.com/bazelbuild/bazel/issues/1775
return 0
# TODO(bazel-team): fix bazel to have consistent encoding, also on darwin;
# see https://github.com/bazelbuild/bazel/issues/1766
[ "$PLATFORM" != "darwin" ] || warn "test disabled on darwin, see Github issue 1766"
[ "$PLATFORM" != "darwin" ] || return 0
# äöüÄÖÜß in UTF8
local arg=$(echo -e '\xC3\xA4\xC3\xB6\xC3\xBC\xC3\x84\xC3\x96\xC3\x9C\xC3\x9F')
mkdir -p foo || fail "mkdir foo failed"
echo 'sh_binary(name = "foo", srcs = ["foo.sh"])' > foo/BUILD
echo 'sh_test(name = "foo_test", srcs = ["foo.sh"])' >> foo/BUILD
echo 'test "$1" = "'"$arg"'"' > foo/foo.sh
chmod +x foo/foo.sh
bazel run //foo -- "$arg" > output \
|| fail "${PRODUCT_NAME} run failed."
bazel test //foo:foo_test --test_arg="$arg" \
|| fail "${PRODUCT_NAME} test failed"
bazel --batch run //foo -- "$arg" > output \
|| fail "${PRODUCT_NAME} run failed (--batch)."
bazel --batch test //foo:foo_test --test_arg="$arg" \
|| fail "${PRODUCT_NAME} test failed (--batch)"
}
function test_interrupt_kills_child() {
mkdir -p foo || fail "mkdir foo failed"
pipe_file="${TEST_TMPDIR}/sleep-minute-pipe"
rm -f "$pipe_file"
mkfifo "$pipe_file" || fail "make pipe failed"
echo 'sh_binary(name = "sleep-minute", srcs = ["sleep-minute.sh"])' > foo/BUILD
echo -e "#!/bin/sh\n"'echo $$ >'"${pipe_file}\n"'sleep 60' > foo/sleep-minute.sh
chmod +x foo/sleep-minute.sh
# Note that if bazel info is not executed before the actual bazel run, this script would have to
# be run in "monitor mode" (with the command set -m) for bazel or the server to receive SIGINT.
local serverpid=$(bazel info server_pid)
if [ -z $serverpid ]; then
fail "Couldn't get ${PRODUCT_NAME} server PID"
fi
(bazel run //foo:sleep-minute || true) &
local sleeppid
read sleeppid <"$pipe_file"
if [ -z $sleeppid ]; then
fail "${PRODUCT_NAME} run did not invoke shell script"
fi
kill -SIGINT $serverpid
# This test is a bit flaky, so we wait a bit more when the process still runs
# after 0.25s.
for i in 0.25 0.5 1 2; do
sleep $i
kill -0 $sleeppid 2> /dev/null || return 0
done
fail "Shell script still running after SIGINT sent to server"
}
# Tests bazel run with --color=no on a failed build does not produce color.
function test_no_color_on_failed_run() {
mkdir -p x || fail "mkdir failed"
echo "cc_binary(name = 'x', srcs = ['x.cc'])" > x/BUILD
cat > x/x.cc <<EOF
int main(int, char**) {
// Missing semicolon
return 0
}
EOF
bazel run //x:x &>$TEST_log --color=no && fail "expected failure"
cat $TEST_log
# Verify that the failure is a build failure.
expect_log "expected ';'"
# Hack to make up for grep -P not being supported.
grep $(echo -e '\x1b') $TEST_log && fail "Expected colorless output"
true
}
function test_no_ansi_stripping_in_stdout_or_stderr() {
mkdir -p x || fail "mkdir failed"
echo "cc_binary(name = 'x', srcs = ['x.cc'])" > x/BUILD
cat > x/x.cc <<EOF
#include <unistd.h>
int main(int, char**) {
const char out[] = {'<', 0x1B, '[', 'a', ',', 0x1B, '[', '1', '>', 0x0A};
const char err[] = {'<', 0x1B, '[', 'b', ',', 0x1B, '[', '2', '>', 0x0A};
write(1, out, 10);
write(2, err, 10);
return 0;
}
EOF
out1color=$(mktemp x/XXXXXX)
out1nocolor=$(mktemp x/XXXXXX)
out2=$(mktemp x/XXXXXX)
err1raw_color=$(mktemp x/XXXXXX)
err1raw_nocolor=$(mktemp x/XXXXXX)
err1color=$(mktemp x/XXXXXX)
err1nocolor=$(mktemp x/XXXXXX)
err2=$(mktemp x/XXXXXX)
# TODO(katre): Figure out why progress rate limiting is required for this on darwin.
add_to_bazelrc common --show_progress_rate_limit=0.03
bazel run //x:x --color=yes >$out1color 2>$err1raw_color || fail "expected success"
bazel run //x:x --color=no >$out1nocolor 2>$err1raw_nocolor || fail "expected success"
echo >> $err1raw_color
echo >> $err1raw_nocolor
${PRODUCT_NAME}-bin/x/x >$out2 2>$err2
echo >> $err2
# Extract the binary's stderr from the raw stderr, which also contains bazel's
# stderr; if present, remove a trailing ^[[0m (reset terminal to defaults).
bazel_stderr_line_count_color=$(cat $err1raw_color \
| grep -n "Running command line: .*/x/x" \
| awk -F ':' '{print $1}')
start=$(($bazel_stderr_line_count_color+1))
tail -n +$start $err1raw_color | sed -e 's/.\[0m$//' >$err1color
bazel_stderr_line_count_nocolor=$(cat $err1raw_nocolor \
| grep -n "Running command line: .*/x/x" \
| awk -F ':' '{print $1}')
start=$(($bazel_stderr_line_count_nocolor+1))
tail -n +$start $err1raw_nocolor >$err1nocolor
diff $out1color $out2 >&$TEST_log || fail "stdout with --color=yes differs"
diff $out1nocolor $out2 >&$TEST_log || fail "stdout with --color=no differs"
diff $err1color $err2 >&$TEST_log || fail "stderr with --color=yes differs"
diff $err1nocolor $err2 >&$TEST_log || fail "stderr with --color=no differs"
rm -rf x
}
# Test for $(location) in args list of sh_binary
function test_location_in_args() {
mkdir -p some/testing
cat > some/testing/BUILD <<'EOF'
genrule(
name = "generated",
cmd = "echo 2 > $@",
outs = ["generated.txt"],
)
sh_binary(
name = "testing",
srcs = ["test.sh"],
data = ["data", ":generated"],
args = ["$(location :data)", "$(location :generated)"],
)
EOF
cat > some/testing/test.sh <<'EOF'
#!/usr/bin/env bash
set -ex
echo "Got $@"
i=1
for arg in $@; do
[[ $((i++)) = $(cat $arg) ]]
done
EOF
chmod +x some/testing/test.sh
echo "1" >some/testing/data
# Arguments are only provided through bazel run, we cannot test it
# with bazel-bin/some/testing/testing
bazel run //some/testing >$TEST_log || fail "Expected success"
expect_log "Got .*some/testing/data.*some/testing/generated.txt"
}
function test_run_for_alias() {
mkdir -p a
cat > a/BUILD <<EOF
sh_binary(name='a', srcs=['a.sh'])
alias(name='b', actual='a')
EOF
cat > a/a.sh <<EOF
#!/bin/sh
echo "Dancing with wolves"
exit 0
EOF
chmod +x a/a.sh
bazel run //a:b >"$TEST_log" || fail "Expected success"
expect_log "Dancing with wolves"
}
function test_run_for_custom_executable() {
mkdir -p a
cat > a/x.bzl <<EOF
def _impl(ctx):
f = ctx.actions.declare_file("x.sh")
ctx.actions.write(f,
"#!/bin/sh\n"
+ "if [ -z \$1 ]; then\\n"
+ " echo Run Forest run\\n"
+ "else\\n"
+ " echo Run Forest run > \$1\\n"
+ "fi",
is_executable=True)
return [DefaultInfo(executable=f)]
my_rule = rule(_impl, executable = True)
def _tool_impl(ctx):
f = ctx.actions.declare_file("output")
ctx.actions.run(executable = ctx.executable.tool,
inputs = [],
outputs = [f],
arguments = [f.path]
)
return DefaultInfo(files = depset([f]))
my_tool_rule = rule(_tool_impl, attrs = { 'tool' : attr.label(executable = True, cfg = "host") })
EOF
cat > a/BUILD <<EOF
load(":x.bzl", "my_rule", "my_tool_rule")
my_rule(name = "zzz")
my_tool_rule(name = "kkk", tool = ":zzz")
EOF
bazel run //a:zzz > "$TEST_log" || fail "Expected success"
expect_log "Run Forest run"
bazel build //a:kkk > "$TEST_log" || fail "Expected success"
grep "Run Forest run" bazel-bin/a/output || fail "Output file wrong"
}
run_suite "'${PRODUCT_NAME} run' integration tests"