| #!/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. | |
| # 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 "build --experimental_build_event_upload_strategy=local" | |
| #### SETUP ############################################################# | |
| set -e | |
| function set_up() { | |
| setup_skylib_support | |
| mkdir -p pkg | |
| touch pkg/somesourcefile | |
| cat > pkg/true.sh <<'EOF' | |
| #!/bin/sh | |
| [ -n "${XML_OUTPUT_FILE}" ] && touch "${XML_OUTPUT_FILE}" | |
| exit 0 | |
| EOF | |
| chmod 755 pkg/true.sh | |
| cat > pkg/false.sh <<'EOF' | |
| #!/bin/sh | |
| [ -n "${XML_OUTPUT_FILE}" ] && touch "${XML_OUTPUT_FILE}" | |
| exit 1 | |
| EOF | |
| chmod 755 pkg/false.sh | |
| cat > pkg/slowtest.sh <<'EOF' | |
| #!/bin/sh | |
| sleep 1 | |
| exit 0 | |
| EOF | |
| chmod 755 pkg/slowtest.sh | |
| touch pkg/sourcefileA pkg/sourcefileB pkg/sourcefileC | |
| cat > pkg/BUILD <<'EOF' | |
| exports_files(["somesourcefile"]) | |
| sh_test( | |
| name = "true", | |
| srcs = ["true.sh"], | |
| size = "small", | |
| ) | |
| sh_test( | |
| name = "slow", | |
| srcs = ["slowtest.sh"], | |
| ) | |
| test_suite( | |
| name = "suite", | |
| tests = ["true"], | |
| ) | |
| sh_test( | |
| name = "flaky", | |
| srcs = ["false.sh"], | |
| flaky = True, | |
| ) | |
| genrule( | |
| name = "fails_to_build", | |
| outs = ["fails_to_build.txt"], | |
| cmd = "echo This build will fail && false", | |
| executable = 1, | |
| ) | |
| sh_test( | |
| name = "test_that_fails_to_build", | |
| srcs = [":fails_to_build"], | |
| ) | |
| genrule( | |
| name = "output_files_and_tags", | |
| outs = ["out1.txt"], | |
| cmd = "echo foo > \"$@\"", | |
| tags = ["tag1", "tag2"] | |
| ) | |
| action_listener( | |
| name = "listener", | |
| mnemonics = ["Genrule"], | |
| extra_actions = [":extra"], | |
| visibility = ["//visibility:public"], | |
| ) | |
| extra_action( | |
| name = "extra", | |
| cmd = "echo Hello World", | |
| ) | |
| filegroup( | |
| name = "innergroup", | |
| srcs = ["sourcefileA", "sourcefileB"], | |
| ) | |
| filegroup( | |
| name = "outergroup", | |
| srcs = ["sourcefileC", ":innergroup"], | |
| ) | |
| genrule( | |
| name = "not_a_test", | |
| outs = ["not_a_test.txt"], | |
| cmd = "touch $@", | |
| ) | |
| EOF | |
| cat > simpleaspect.bzl <<'EOF' | |
| def _simple_aspect_impl(target, ctx): | |
| for orig_out in ctx.rule.attr.outs: | |
| aspect_out = ctx.actions.declare_file(orig_out.name + ".aspect") | |
| ctx.actions.write( | |
| output=aspect_out, | |
| content = "Hello from aspect") | |
| return struct(output_groups={ | |
| "aspect-out" : depset([aspect_out]) }) | |
| simple_aspect = aspect(implementation=_simple_aspect_impl) | |
| EOF | |
| cat > failingaspect.bzl <<'EOF' | |
| def _failing_aspect_impl(target, ctx): | |
| for orig_out in ctx.rule.attr.outs.to_list(): | |
| aspect_out = ctx.actions.declare_file(orig_out.name + ".aspect") | |
| ctx.actions.run_shell( | |
| inputs = [], | |
| outputs = [aspect_out], | |
| command = "false", | |
| ) | |
| return struct(output_groups={ | |
| "aspect-out" : depset([aspect_out]) }) | |
| failing_aspect = aspect(implementation=_failing_aspect_impl) | |
| EOF | |
| cat > semifailingaspect.bzl <<'EOF' | |
| def _semifailing_aspect_impl(target, ctx): | |
| if not ctx.rule.attr.outs: | |
| return struct(output_groups = {}) | |
| bad_outputs = list() | |
| good_outputs = list() | |
| for out in ctx.rule.attr.outs: | |
| if out.name[0] == "f": | |
| aspect_out = ctx.actions.declare_file(out.name + ".aspect.bad") | |
| bad_outputs.append(aspect_out) | |
| cmd = "false" | |
| else: | |
| aspect_out = ctx.actions.declare_file(out.name + ".aspect.good") | |
| good_outputs.append(aspect_out) | |
| cmd = "echo %s > %s" % (out.name, aspect_out.path) | |
| ctx.actions.run_shell( | |
| inputs = [], | |
| outputs = [aspect_out], | |
| command = cmd, | |
| ) | |
| return [OutputGroupInfo(**{ | |
| "bad-aspect-out": depset(bad_outputs), | |
| "good-aspect-out": depset(good_outputs), | |
| })] | |
| semifailing_aspect = aspect(implementation = _semifailing_aspect_impl) | |
| EOF | |
| mkdir -p semifailingpkg/ | |
| cat > semifailingpkg/BUILD <<'EOF' | |
| genrule( | |
| name = "semifail", | |
| outs = ["out1.txt", "out2.txt", "failingout1.txt"], | |
| cmd = "for f in $(OUTS); do echo foo > $(RULEDIR)/$$f; done" | |
| ) | |
| EOF | |
| touch BUILD | |
| cat > sample_workspace_status <<'EOF' | |
| #!/bin/sh | |
| echo SAMPLE_WORKSPACE_STATUS workspace_status_value | |
| EOF | |
| chmod 755 sample_workspace_status | |
| mkdir -p visibility/hidden | |
| cat > visibility/hidden/BUILD <<'EOF' | |
| genrule( | |
| name = "hello", | |
| outs = ["hello.txt"], | |
| cmd = "echo Hello World > $@", | |
| ) | |
| EOF | |
| cat > visibility/BUILD <<'EOF' | |
| genrule( | |
| name = "cannotsee", | |
| outs = ["cannotsee.txt"], | |
| srcs = ["//visibility/hidden:hello"], | |
| cmd = "cp $< $@", | |
| ) | |
| genrule( | |
| name = "indirect", | |
| outs = ["indirect.txt"], | |
| srcs = [":cannotsee"], | |
| cmd = "cp $< $@", | |
| ) | |
| genrule( | |
| name = "cannotsee2", | |
| outs = ["cannotsee2.txt"], | |
| srcs = ["//visibility/hidden:hello"], | |
| cmd = "cp $< $@", | |
| ) | |
| genrule( | |
| name = "indirect2", | |
| outs = ["indirect2.txt"], | |
| srcs = [":cannotsee2.txt"], | |
| cmd = "cp $< $@", | |
| ) | |
| EOF | |
| mkdir -p failingtool | |
| cat > failingtool/BUILD <<'EOF' | |
| genrule( | |
| name = "tool", | |
| outs = ["tool.sh"], | |
| cmd = "false", | |
| ) | |
| genrule( | |
| name = "usestool", | |
| outs = ["out.txt"], | |
| tools = [":tool"], | |
| cmd = "$(location :tool) > $@", | |
| ) | |
| EOF | |
| mkdir -p alias/actual | |
| cat > alias/actual/BUILD <<'EOF' | |
| genrule( | |
| name = "it", | |
| outs = ["it.txt"], | |
| cmd = "touch $@", | |
| visibility = ["//:__subpackages__"], | |
| ) | |
| EOF | |
| cat > alias/BUILD <<'EOF' | |
| alias( | |
| name = "it", | |
| actual = "//alias/actual:it", | |
| ) | |
| EOF | |
| mkdir -p chain | |
| cat > chain/BUILD <<'EOF' | |
| genrule( | |
| name = "entry0", | |
| outs = ["entry0.txt"], | |
| cmd = "echo Hello0; touch $@", | |
| ) | |
| EOF | |
| for i in `seq 1 10` | |
| do | |
| cat >> chain/BUILD <<EOF | |
| genrule( | |
| name = "entry$i", | |
| outs = ["entry$i.txt"], | |
| srcs = [ "entry$(( $i - 1)).txt" ], | |
| cmd = "echo Hello$i; cp \$< \$@", | |
| ) | |
| EOF | |
| done | |
| mkdir -p outputgroups | |
| cat > outputgroups/rules.bzl <<EOF | |
| def _my_rule_impl(ctx): | |
| group_kwargs = {} | |
| for name, exit in (("foo", 0), ("bar", 0), ("baz", 1), ("skip", 0)): | |
| outfile = ctx.actions.declare_file(ctx.label.name + "-" + name + ".out") | |
| ctx.actions.run_shell( | |
| outputs = [outfile], | |
| command = "printf %s > %s && exit %d" % (name, outfile.path, exit), | |
| ) | |
| group_kwargs[name + "_outputs"] = depset([outfile]) | |
| for name, exit, suffix in ( | |
| ("foo", 1, ".fail.out"), ("bar", 0, ".ok.out"), ("bar", 0, ".ok.out2"), | |
| ("bar", 0, ".ok.out3"), ("bar", 0, ".ok.out4"), ("bar", 0, ".ok.out5")): | |
| outfile = ctx.actions.declare_file(ctx.label.name + "-" + name + suffix) | |
| ctx.actions.run_shell( | |
| outputs = [outfile], | |
| command = "printf %s > %s && exit %d" % (name, outfile.path, exit), | |
| ) | |
| group_kwargs[name + "_outputs"] = depset( | |
| [outfile], transitive=[group_kwargs[name + "_outputs"]]) | |
| return [OutputGroupInfo(**group_kwargs)] | |
| my_rule = rule(implementation = _my_rule_impl, attrs = { | |
| "outs": attr.output_list(), | |
| }) | |
| EOF | |
| cat > outputgroups/BUILD <<EOF | |
| load("//outputgroups:rules.bzl", "my_rule") | |
| my_rule(name = "my_lib", outs=[]) | |
| EOF | |
| } | |
| #### TESTS ############################################################# | |
| function test_basic() { | |
| # Basic properties of the event stream | |
| # - a completed target explicitly requested should be reported | |
| # - after success the stream should close naturally, without any | |
| # reports about aborted events | |
| # - the command line is reported in structured and unstructured form | |
| # - the target_kind is reported | |
| # - for single-configuration builds, there is precisely one configuration | |
| # event reported; also make variables are shown | |
| bazel test -k --build_event_text_file=$TEST_log --tool_tag=MyFancyTool pkg:true \ | |
| || fail "bazel test failed" | |
| expect_log 'pkg:true' | |
| # Command line | |
| expect_log_once 'args: "test"' | |
| expect_log_once 'args: "--build_event_text_file=' | |
| expect_log_once 'args: "-k"' | |
| expect_log_once 'args: "--tool_tag=MyFancyTool"' | |
| expect_log_once 'args: "pkg:true"' | |
| # Options parsed. Since cmd_line lines are a substring of the equivalent | |
| # explicit_cmd_line lines, we expect 2 instances for these. | |
| expect_log_n 'cmd_line: "--tool_tag=MyFancyTool"' 2 | |
| expect_log_n 'cmd_line: "--keep_going"' 2 | |
| expect_log_once 'explicit_cmd_line: "--keep_going"' | |
| expect_log_once 'explicit_cmd_line: "--tool_tag=MyFancyTool"' | |
| expect_log_once 'tool_tag: "MyFancyTool"' | |
| # Structured command line. Expect the explicit flags to appear twice, | |
| # in the canonical and original command lines. We did not pass a tool | |
| # command line, but still expect an empty report. | |
| expect_log 'command_line_label: "original"' | |
| expect_log 'command_line_label: "canonical"' | |
| expect_log 'command_line_label: "tool"' | |
| expect_log_n 'combined_form: "-k"' 2 | |
| expect_log_n 'option_name: "keep_going"' 2 | |
| expect_log 'option_value: "1"' # too vague to count. | |
| expect_log_n 'combined_form: "--tool_tag=MyFancyTool"' 2 | |
| expect_log_n 'option_name: "tool_tag"' 2 | |
| expect_log_n 'option_value: "MyFancyTool"' 2 | |
| expect_log_n "combined_form: \"--build_event_text_file=${TEST_log}\"" 2 | |
| expect_log_n 'option_name: "build_event_text_file"' 2 | |
| expect_log_n "option_value: \"${TEST_log}\"" 2 | |
| expect_log_n 'chunk: "test"' 2 | |
| expect_log_n 'chunk: "pkg:true"' 2 | |
| # Build Finished | |
| expect_log 'build_finished' | |
| expect_log 'SUCCESS' | |
| expect_log 'finish_time' | |
| expect_log_once 'last_message: true' | |
| expect_not_log 'aborted' | |
| expect_log_once '^build_tool_logs' | |
| # Target kind for the sh_test | |
| expect_log 'target_kind:.*sh' | |
| # Test size should be reported | |
| expect_log 'test_size: SMALL' | |
| # Configuration reported with make variables | |
| expect_log_once '^configuration ' | |
| expect_log 'key: "TARGET_CPU"' | |
| } | |
| function test_target_information_early() { | |
| # Verify that certain information is present in the log as part of | |
| # the TargetConfigured event (verifying that it comes at least before | |
| # the first TargetCompleted event, which is fine, if we only ask for | |
| # a single target). | |
| bazel test --build_event_text_file=$TEST_log pkg:true \ | |
| || fail "bazel test failed" | |
| expect_log '^completed' | |
| ed $TEST_log <<'EOF' | |
| 1 | |
| /^completed/+1,$d | |
| a | |
| ...[cut here] | |
| . | |
| w | |
| q | |
| EOF | |
| expect_log 'target_kind:.*sh' | |
| expect_log 'test_size: SMALL' | |
| bazel build --verbose_failures --build_event_text_file=$TEST_log \ | |
| pkg:output_files_and_tags || fail "bazel build failed" | |
| expect_log '^completed' | |
| ed $TEST_log <<'EOF' | |
| 1 | |
| /^completed/+1,$d | |
| a | |
| ...[cut here] | |
| . | |
| w | |
| q | |
| EOF | |
| expect_log 'tag1' | |
| expect_log 'tag2' | |
| } | |
| function test_workspace_status() { | |
| bazel test --build_event_text_file=$TEST_log \ | |
| --workspace_status_command=sample_workspace_status pkg:true \ | |
| || fail "bazel test failed" | |
| expect_log_once '^workspace_status' | |
| expect_log 'key.*SAMPLE_WORKSPACE_STATUS' | |
| expect_log 'value.*workspace_status_value' | |
| } | |
| function test_suite() { | |
| # ...same true when running a test suite containing that test | |
| bazel test --build_event_text_file=$TEST_log pkg:suite \ | |
| || fail "bazel test failed" | |
| expect_log 'pkg:true' | |
| expect_not_log 'aborted' | |
| expect_log 'last_message: true' | |
| expect_log_once '^build_tool_logs' | |
| } | |
| function test_test_summary() { | |
| # Requesting a test, we expect | |
| # - precisely one test summary (for the single test we run) | |
| # - that is properly chained (no additional progress events) | |
| # - the correct overall status being reported | |
| bazel test --build_event_text_file=$TEST_log pkg:true \ | |
| || fail "bazel test failed" | |
| expect_log_once '^test_summary ' | |
| expect_not_log 'aborted' | |
| expect_log 'status.*PASSED' | |
| expect_not_log 'status.*FAILED' | |
| expect_not_log 'status.*FLAKY' | |
| } | |
| function test_test_inidivual_results() { | |
| # Requesting a test, we expect | |
| # - precisely one test summary (for the single test we run) | |
| # - that is properly chained (no additional progress events) | |
| bazel test --build_event_text_file=$TEST_log \ | |
| --runs_per_test=2 pkg:true \ | |
| || fail "bazel test failed" | |
| expect_log '^test_result' | |
| expect_log 'run.*1' | |
| expect_log 'status.*PASSED' | |
| expect_log_once '^test_summary ' | |
| expect_not_log 'aborted' | |
| expect_log 'last_message: true' | |
| expect_log_once '^build_tool_logs' | |
| } | |
| function test_test_attempts() { | |
| # Run a failing test declared as flaky. | |
| # We expect to see 3 attempts to happen, and also find the 3 xml files | |
| # mentioned in the stream. | |
| # Moreover, as the test consistently fails, we expect the overall status | |
| # to be reported as failure. | |
| (bazel test --build_event_text_file=$TEST_log pkg:flaky \ | |
| && fail "test failure expected" ) || true | |
| expect_log 'attempt.*1$' | |
| expect_log 'attempt.*2$' | |
| expect_log 'attempt.*3$' | |
| expect_log_once '^test_summary ' | |
| expect_log 'status.*FAILED' | |
| expect_not_log 'status.*PASSED' | |
| expect_not_log 'status.*FLAKY' | |
| expect_not_log 'aborted' | |
| expect_log '^test_result' | |
| expect_log 'test_action_output' | |
| expect_log 'flaky/.*_1.xml' | |
| expect_log 'flaky/.*_2.xml' | |
| expect_log 'flaky/.*test.xml' | |
| expect_log 'name:.*test.log' | |
| expect_log 'name:.*test.xml' | |
| expect_log 'name:.*TESTS_FAILED' | |
| expect_not_log 'name:.*SUCCESS' | |
| } | |
| function test_test_runtime() { | |
| bazel test --build_event_text_file=$TEST_log pkg:slow \ | |
| || fail "bazel test failed" | |
| expect_log 'pkg:slow' | |
| expect_log '^test_result' | |
| expect_log 'test_attempt_duration_millis.*[1-9]' | |
| expect_log 'build_finished' | |
| expect_log 'SUCCESS' | |
| expect_log 'finish_time' | |
| expect_not_log 'aborted' | |
| } | |
| function test_test_start_times() { | |
| # Verify that the start time of a test is reported, regardless whether | |
| # it was cached or not. | |
| bazel clean --expunge | |
| bazel test --build_event_text_file=$TEST_log pkg:true \ | |
| || fail "bazel test failed" | |
| expect_log 'test_attempt_start_millis_epoch.*[1-9]' | |
| expect_not_log 'cached_locally' | |
| bazel test --build_event_text_file=$TEST_log pkg:true \ | |
| || fail "bazel test failed" | |
| expect_log 'test_attempt_start_millis_epoch.*[1-9]' | |
| expect_log 'cached_locally.*true' | |
| } | |
| function test_test_attempts_multi_runs() { | |
| # Sanity check on individual test attempts. Even in more complicated | |
| # situations, with some test rerun and some not, all events are properly | |
| # announced by the test actions (and not chained into the progress events). | |
| ( bazel test --build_event_text_file=$TEST_log \ | |
| --runs_per_test=2 pkg:true pkg:flaky \ | |
| && fail "test failure expected" ) || true | |
| expect_log 'run.*1' | |
| expect_log 'attempt.*2' | |
| expect_not_log 'aborted' | |
| } | |
| function test_test_attempts_multi_runs_flake_detection() { | |
| # Sanity check on individual test attempts. Even in more complicated | |
| # situations, with some test rerun and some not, all events are properly | |
| # announced by the test actions (and not chained into the progress events). | |
| ( bazel test --build_event_text_file=$TEST_log \ | |
| --runs_per_test=2 --runs_per_test_detects_flakes pkg:true pkg:flaky \ | |
| && fail "test failure expected" ) || true | |
| expect_log 'run.*1' | |
| expect_log 'attempt.*2' | |
| expect_not_log 'aborted' | |
| } | |
| function test_cached_test_results() { | |
| # Verify that both, clean and cached test results are reported correctly, | |
| # including the appropriate reference to log files. | |
| bazel clean --expunge | |
| bazel test --build_event_text_file=$TEST_log pkg:true \ | |
| || fail "Clean testing pkg:true failed" | |
| expect_log '^test_result' | |
| expect_log 'name:.*test.log' | |
| expect_log 'name:.*test.xml' | |
| expect_not_log 'cached_locally' | |
| expect_not_log 'aborted' | |
| bazel test --build_event_text_file=$TEST_log pkg:true \ | |
| || fail "Second testing of pkg:true failed" | |
| expect_log '^test_result' | |
| expect_log 'name:.*test.log' | |
| expect_log 'name:.*test.xml' | |
| expect_log 'cached_locally' | |
| expect_not_log 'aborted' | |
| } | |
| function test_target_complete() { | |
| bazel build --verbose_failures --build_event_text_file=$TEST_log \ | |
| pkg:output_files_and_tags || fail "bazel build failed" | |
| expect_log 'output_group' | |
| expect_log 'out1.txt' | |
| expect_log 'tag1' | |
| expect_log 'tag2' | |
| } | |
| function test_test_target_complete() { | |
| bazel test --build_event_text_file="${TEST_log}" pkg:true \ | |
| || tail "expected success" | |
| expect_log_once '^completed' | |
| cp "${TEST_log}" complete_event | |
| ed complete_event <<'EOF' | |
| 1 | |
| /^complete | |
| 1,.-1d | |
| /^} | |
| +1,$d | |
| w | |
| q | |
| EOF | |
| grep -q 'output_group' complete_event \ | |
| || fail "expected reference to output in complete event" | |
| expect_log 'name: *"pkg/true.sh"' | |
| } | |
| function test_extra_action() { | |
| # verify that normal successful actions are not reported, but extra actions | |
| # are | |
| bazel build --build_event_text_file=$TEST_log \ | |
| pkg:output_files_and_tags || fail "bazel build failed" | |
| expect_not_log '^action' | |
| bazel build --build_event_text_file=$TEST_log \ | |
| --experimental_action_listener=pkg:listener \ | |
| pkg:output_files_and_tags || fail "bazel build with listener failed" | |
| expect_log '^action' | |
| expect_log 'last_message: true' | |
| expect_log_once '^build_tool_logs' | |
| } | |
| function test_action_ids() { | |
| bazel build --build_event_text_file=$TEST_log \ | |
| --experimental_action_listener=pkg:listener \ | |
| pkg:output_files_and_tags || fail "bazel build with listener failed" | |
| expect_log '^action' | |
| # Action ids should contain label and configuration if those exist. | |
| # Assumes action_completed id is 6 lines long | |
| for id_line in $(grep -n 'action_completed {' $TEST_log | cut -f 1 -d :) | |
| do | |
| sed -n "$id_line,$((id_line+6))p" $TEST_log > "$TEST_TMPDIR/event_id.txt" | |
| assert_contains '.*primary_output: .*' "$TEST_TMPDIR/event_id.txt" | |
| assert_contains '.*label: .*' "$TEST_TMPDIR/event_id.txt" | |
| assert_contains '.*configuration.*' "$TEST_TMPDIR/event_id.txt" | |
| assert_contains '.*id: .*' "$TEST_TMPDIR/event_id.txt" | |
| done | |
| } | |
| function test_bep_output_groups() { | |
| # In outputgroups/rules.bzl, the `my_rule` definition defines four output | |
| # groups with different (successful/failed) action counts: | |
| # 1. foo_outputs (1 successful/1 failed) | |
| # 2. bar_outputs (1/0) | |
| # 3. baz_outputs (0/1) | |
| # 4. skip_outputs (1/0) | |
| # | |
| # We request the first three output groups and expect only bar_outputs to | |
| # appear in BEP, because all actions contributing to bar_outputs succeeded. | |
| bazel build //outputgroups:my_lib \ | |
| --keep_going\ | |
| --build_event_text_file=bep_output \ | |
| --build_event_json_file="$TEST_log" \ | |
| --build_event_max_named_set_of_file_entries=1 \ | |
| --output_groups=foo_outputs,bar_outputs,baz_outputs \ | |
| && fail "expected failure" || true | |
| for name in bar; do | |
| expect_log "\"name\":\"${name}_outputs\"" | |
| expect_log "\"name\":\"outputgroups/my_lib-${name}.out\"" | |
| done | |
| # Verify that nested NamedSetOfFiles structure is preserved in BEP. | |
| expect_log "namedSet.*\"1\".*bar.ok.*fileSets.*\"2\"" | |
| expect_log "namedSet.*\"2\".*bar.ok.*fileSets.*\"3\"" | |
| expect_log "namedSet.*\"3\".*bar.ok.*fileSets.*\"4\"" | |
| for name in foo baz skip; do | |
| expect_not_log "\"name\":\"${name}_outputs\"" | |
| expect_not_log "\"name\":\"outputgroups/my_lib-${name}.out\"" | |
| done | |
| } | |
| function test_aspect_artifacts() { | |
| bazel build --build_event_text_file=$TEST_log \ | |
| --aspects=simpleaspect.bzl%simple_aspect \ | |
| --output_groups=aspect-out \ | |
| pkg:output_files_and_tags || fail "bazel build failed" | |
| expect_log 'aspect.*simple_aspect' | |
| expect_log 'name.*aspect-out' | |
| expect_log 'name.*out1.txt.aspect' | |
| expect_not_log 'aborted' | |
| expect_log_n '^configured' 2 | |
| expect_log 'last_message: true' | |
| expect_log_once '^build_tool_logs' | |
| } | |
| function test_failing_aspect() { | |
| bazel build --build_event_text_file=$TEST_log \ | |
| --aspects=failingaspect.bzl%failing_aspect \ | |
| --output_groups=aspect-out \ | |
| pkg:output_files_and_tags && fail "expected failure" || true | |
| expect_log 'aspect.*failing_aspect' | |
| expect_log '^finished' | |
| expect_log 'last_message: true' | |
| expect_log_once '^build_tool_logs' | |
| } | |
| function test_failing_aspect_bep_output_groups() { | |
| # In outputgroups/rules.bzl, the `my_rule` definition defines four output | |
| # groups with different (successful/failed) action counts: | |
| # 1. foo_outputs (1 successful/1 failed) | |
| # 2. bar_outputs (1/0) | |
| # 3. baz_outputs (0/1) | |
| # 4. skip_outputs (1/0) | |
| # | |
| # We request the first two output groups and expect only bar_outputs to | |
| # appear in BEP, because all actions contributing to bar_outputs succeeded. | |
| # | |
| # Similarly, in semifailingaspect.bzl, the `semifailing_aspect` definition | |
| # defines two output groups: good-aspect-out and bad-aspect-out. We only | |
| # expect to see good-aspect-out in BEP because its actions all succeeded. | |
| bazel build //semifailingpkg:semifail //outputgroups:my_lib \ | |
| --keep_going \ | |
| --build_event_text_file=bep_output \ | |
| --build_event_json_file="$TEST_log" \ | |
| --build_event_max_named_set_of_file_entries=1 \ | |
| --aspects=semifailingaspect.bzl%semifailing_aspect \ | |
| --output_groups=foo_outputs,bar_outputs,good-aspect-out,bad-aspect-out \ | |
| && fail "expected failure" || true | |
| for name in bar; do | |
| expect_log "\"name\":\"${name}_outputs\"" | |
| expect_log "\"name\":\"outputgroups/my_lib-${name}.out\"" | |
| done | |
| for name in foo baz skip; do | |
| expect_not_log "\"name\":\"${name}_outputs\"" | |
| expect_not_log "\"name\":\"outputgroups/my_lib-${name}.out\"" | |
| done | |
| expect_log "\"name\":\"good-aspect-out\"" | |
| expect_not_log "\"name\":\"bad-aspect-out\"" | |
| } | |
| function test_build_only() { | |
| # When building but not testing a test, there won't be a test summary | |
| # (as nothing was tested), so it should not be announced. | |
| # Still, no event should only be chained in by progress events. | |
| bazel build --build_event_text_file=$TEST_log pkg:true \ | |
| || fail "bazel build failed" | |
| expect_not_log 'aborted' | |
| expect_not_log 'test_summary ' | |
| # Build Finished | |
| expect_log 'build_finished' | |
| expect_log 'finish_time' | |
| expect_log 'SUCCESS' | |
| expect_log 'last_message: true' | |
| expect_log_once '^build_tool_logs' | |
| } | |
| function test_command_whitelisting() { | |
| # We expect the "help" command to not generate a build-event stream, | |
| # but the "build" command to do. | |
| bazel shutdown | |
| rm -f bep.txt | |
| bazel help --build_event_text_file=bep.txt || fail "bazel help failed" | |
| ( [ -f bep.txt ] && fail "bazel help generated a build-event file" ) || : | |
| bazel version --build_event_text_file=bep.txt || fail "bazel help failed" | |
| ( [ -f bep.txt ] && fail "bazel version generated a build-event file" ) || : | |
| bazel build --build_event_text_file=bep.txt //pkg:true \ | |
| || fail "bazel build failed" | |
| [ -f bep.txt ] || fail "build did not generate requested build-event file" | |
| bazel shutdown | |
| } | |
| function test_multiple_transports() { | |
| # Verifies usage of multiple build event transports at the same time | |
| outdir=$(mktemp -d ${TEST_TMPDIR}/bazel.XXXXXXXX) | |
| bazel test \ | |
| --build_event_text_file=${outdir}/test_multiple_transports.txt \ | |
| --build_event_binary_file=${outdir}/test_multiple_transports.bin \ | |
| --build_event_json_file=${outdir}/test_multiple_transports.json \ | |
| pkg:suite || fail "bazel test failed" | |
| [ -f ${outdir}/test_multiple_transports.txt ] || fail "Missing expected file test_multiple_transports.txt" | |
| [ -f ${outdir}/test_multiple_transports.bin ] || fail "Missing expected file test_multiple_transports.bin" | |
| [ -f ${outdir}/test_multiple_transports.json ] || fail "Missing expected file test_multiple_transports.bin" | |
| } | |
| function test_basic_json() { | |
| # Verify that the json transport writes json files | |
| bazel test --build_event_json_file=$TEST_log pkg:true \ | |
| || fail "bazel test failed" | |
| # check for some typical fragments that would be encoded differently in the | |
| # proto text format. | |
| expect_log '"started"' | |
| expect_log '"id"' | |
| expect_log '"children" *: *\[' | |
| expect_log '"overallSuccess": *true' | |
| } | |
| function test_root_cause_early() { | |
| (bazel build --build_event_text_file=$TEST_log \ | |
| pkg:fails_to_build && fail "build failure expected") || true | |
| # We expect precisely one action being reported (the failed one) and | |
| # precisely on report on a completed target; moreover, the action has | |
| # to be reported first. | |
| expect_log_once '^action' | |
| expect_log 'type: "Genrule"' | |
| expect_log_once '^completed' | |
| expect_not_log 'success: true' | |
| local naction=`grep -n '^action' $TEST_log | cut -f 1 -d :` | |
| local ncomplete=`grep -n '^completed' $TEST_log | cut -f 1 -d :` | |
| [ $naction -lt $ncomplete ] \ | |
| || fail "failed action not before completed target" | |
| expect_log 'last_message: true' | |
| expect_log_once '^build_tool_logs' | |
| } | |
| function test_action_conf() { | |
| # Verify that the expected configurations for actions are reported. | |
| # The example contains a configuration transition (from building for | |
| # target to building for host). As the action fails, we expect the | |
| # configuration of the action to be reported as well. | |
| (bazel build --build_event_text_file=$TEST_log \ | |
| -k failingtool/... && fail "build failure expected") || true | |
| count=`grep '^configuration' "${TEST_log}" | wc -l` | |
| [ "${count}" -eq 2 ] || fail "Expected 2 configurations, found $count." | |
| } | |
| function test_loading_failure() { | |
| # Verify that if loading fails, this is properly reported as the | |
| # reason for the target expansion event not resulting in targets | |
| # being expanded. | |
| (bazel build --build_event_text_file=$TEST_log \ | |
| //does/not/exist && fail "build failure expected") || true | |
| expect_log_once 'aborted' | |
| expect_log_once 'reason: LOADING_FAILURE' | |
| expect_log 'description.*BUILD file not found' | |
| expect_not_log 'expanded' | |
| expect_log 'last_message: true' | |
| expect_log_once '^build_tool_logs' | |
| } | |
| function test_visibility_failure() { | |
| bazel shutdown | |
| (bazel build --build_event_text_file=$TEST_log \ | |
| //visibility:cannotsee && fail "build failure expected") || true | |
| expect_log 'reason: ANALYSIS_FAILURE' | |
| expect_log '^aborted' | |
| # The same should hold true, if the server has already analyzed the target | |
| (bazel build --build_event_text_file=$TEST_log \ | |
| //visibility:cannotsee && fail "build failure expected") || true | |
| expect_log 'reason: ANALYSIS_FAILURE' | |
| expect_log '^aborted' | |
| expect_log 'last_message: true' | |
| expect_log_once '^build_tool_logs' | |
| } | |
| function test_visibility_indirect() { | |
| # verify that an indirect visibility error is reported, including the | |
| # target that violates visibility constraints. | |
| bazel shutdown | |
| (bazel build --build_event_text_file=$TEST_log \ | |
| //visibility:indirect && fail "build failure expected") || true | |
| expect_log 'reason: ANALYSIS_FAILURE' | |
| expect_log '^aborted' | |
| expect_log '//visibility:cannotsee' | |
| # There should be precisely one events with target_completed as event id type | |
| (echo 'g/^id/+1p'; echo 'q') | ed "${TEST_log}" 2>&1 | tail -n +2 > event_id_types | |
| [ `grep target_completed event_id_types | wc -l` -eq 1 ] \ | |
| || fail "not precisely one target_completed event id" | |
| } | |
| function test_independent_visibility_failures() { | |
| bazel shutdown | |
| (bazel build -k --build_event_text_file=$TEST_log \ | |
| //visibility:indirect //visibility:indirect2 \ | |
| && fail "build failure expected") || true | |
| (echo 'g/^aborted/.,+2p'; echo 'q') | ed "${TEST_log}" 2>&1 | tail -n +2 \ | |
| > aborted_events | |
| [ `grep '^aborted' aborted_events | wc -l` \ | |
| -eq `grep ANALYSIS_FAILURE aborted_events | wc -l` ] \ | |
| || fail "events should only be aborted due to analysis failure" | |
| } | |
| function test_loading_failure_keep_going() { | |
| (bazel build --build_event_text_file=$TEST_log \ | |
| -k //does/not/exist && fail "build failure expected") || true | |
| expect_log_once 'aborted' | |
| expect_log_once 'reason: LOADING_FAILURE' | |
| # We don't expect an expanded message in this case, since all patterns failed. | |
| expect_log 'description.*BUILD file not found' | |
| expect_log 'last_message: true' | |
| expect_log_once '^build_tool_logs' | |
| } | |
| function test_loading_failure_keep_going_two_targets() { | |
| (bazel build --build_event_text_file=$TEST_log \ | |
| -k //does/not/exist //pkg:somesourcefile && fail "build failure expected") || true | |
| expect_log_once 'aborted' | |
| expect_log_once 'reason: LOADING_FAILURE' | |
| expect_log_once '^expanded' | |
| expect_log 'description.*BUILD file not found' | |
| expect_log 'last_message: true' | |
| expect_log_once '^build_tool_logs' | |
| } | |
| # TODO(aehlig): readd, once we stop reporting the important artifacts | |
| # for every target completion | |
| # | |
| # function test_artifact_dedup() { | |
| # bazel build --build_event_text_file=$TEST_log \ | |
| # pkg:innergroup pkg:outergroup \ | |
| # || fail "bazel build failed" | |
| # expect_log_once "name.*sourcefileA" | |
| # expect_log_once "name.*sourcefileB" | |
| # expect_log_once "name.*sourcefileC" | |
| # expect_not_log 'aborted' | |
| # } | |
| function test_stdout_stderr_reported() { | |
| # Verify that bazel's stdout/stderr is included in the build event stream. | |
| # Make sure we generate enough output on stderr | |
| bazel clean --expunge | |
| bazel test --build_event_text_file=$TEST_log --curses=no \ | |
| pkg:slow 2>stderr.log || fail "slowtest failed" | |
| # Take a line that is likely not the output of an action (possibly reported | |
| # independently in the stream) and still characteristic enough to not occur | |
| # in the stream by accident. Taking the first line mentioning the test name | |
| # is likely some form of progress report. | |
| sample_line=`cat stderr.log | grep 'slow' | head -n 1 | tr '[]\r' '....'` | |
| echo "Sample regexp of stderr: ${sample_line}" | |
| expect_log "stderr.*${sample_line}" | |
| } | |
| function test_unbuffered_stdout_stderr() { | |
| # Verify that the option --bes_outerr_buffer_size ensures that messages are | |
| # flushed out to the BEP immediately | |
| bazel clean --expunge | |
| bazel build --build_event_text_file="${TEST_log}" \ | |
| --bes_outerr_buffer_size=1 chain:entry10 | |
| progress_count=$(grep '^progress' "${TEST_log}" | wc -l ) | |
| # As we requested no buffereing, each action output has to be reported | |
| # immediately, creating an individual progress event. | |
| [ "${progress_count}" -gt 10 ] || fail "expected at least 10 progress events" | |
| } | |
| function test_srcfiles() { | |
| # Even if the build target is a source file, the stream should be correctly | |
| # and bazel shouldn't crash. | |
| bazel build --build_event_text_file=$TEST_log \ | |
| pkg:somesourcefile || fail "build failed" | |
| expect_log 'SUCCESS' | |
| expect_log_n '^configuration' 2 | |
| expect_not_log 'aborted' | |
| expect_log 'last_message: true' | |
| expect_log_once '^build_tool_logs' | |
| } | |
| function test_test_fails_to_build() { | |
| (bazel test --build_event_text_file=$TEST_log \ | |
| pkg:test_that_fails_to_build && fail "test failure expected") || true | |
| expect_not_log '^test_summary' | |
| expect_log 'last_message: true' | |
| expect_log 'BUILD_FAILURE' | |
| expect_log 'last_message: true' | |
| expect_log 'command_line:.*This build will fail' | |
| expect_log_once '^build_tool_logs' | |
| } | |
| function test_no_tests_found() { | |
| (bazel test --build_event_text_file=$TEST_log \ | |
| pkg:not_a_test && fail "failure expected") || true | |
| expect_not_log '^test_summary' | |
| expect_log 'last_message: true' | |
| expect_log 'NO_TESTS_FOUND' | |
| expect_log 'last_message: true' | |
| expect_log_once '^build_tool_logs' | |
| } | |
| function test_no_tests_found_build_failure() { | |
| (bazel test -k --build_event_text_file=$TEST_log \ | |
| pkg:not_a_test pkg:fails_to_build && fail "failure expected") || true | |
| expect_not_log '^test_summary' | |
| expect_log 'last_message: true' | |
| expect_log 'yet testing was requested' | |
| expect_log 'BUILD_FAILURE' | |
| expect_log 'last_message: true' | |
| expect_log_once '^build_tool_logs' | |
| } | |
| function test_alias() { | |
| bazel build --build_event_text_file=$TEST_log alias/... \ | |
| || fail "build failed" | |
| # If alias:it would be reported as the underlying alias/actual:it, then | |
| # there would be no event for alias:it. So we can check the correct reporting | |
| # by checking for aborted events. | |
| expect_not_log 'aborted' | |
| (echo 'g/^completed/?label?p'; echo 'q') | ed "${TEST_log}" 2>&1 | tail -n +2 > completed_labels | |
| cat completed_labels | |
| grep -q '//alias:it' completed_labels || fail "//alias:it not completed" | |
| grep -q '//alias/actual:it' completed_labels \ | |
| || fail "//alias/actual:it not completed" | |
| [ `cat completed_labels | wc -l` -eq 2 ] \ | |
| || fail "more than two targets completed" | |
| rm -f completed_labels | |
| bazel build --build_event_text_file=$TEST_log alias:it \ | |
| || fail "build failed" | |
| expect_log 'label: "//alias:it"' | |
| expect_not_log 'label: "//alias/actual' | |
| } | |
| function test_circular_dep() { | |
| touch test.sh | |
| chmod u+x test.sh | |
| cat > BUILD <<'EOF' | |
| sh_test( | |
| name = "circular", | |
| srcs = ["test.sh"], | |
| deps = ["circular"], | |
| ) | |
| EOF | |
| (bazel build --build_event_text_file="${TEST_log}" :circular \ | |
| && fail "Expected failure") || : | |
| expect_log_once 'last_message: true' | |
| expect_log 'name: "PARSING_FAILURE"' | |
| (bazel test --build_event_text_file="${TEST_log}" :circular \ | |
| && fail "Expected failure") || : | |
| expect_log_once 'last_message: true' | |
| expect_log 'name: "PARSING_FAILURE"' | |
| } | |
| function test_missing_file() { | |
| cat > BUILD <<'EOF' | |
| filegroup( | |
| name = "badfilegroup", | |
| srcs = ["doesnotexist"], | |
| ) | |
| EOF | |
| (bazel build --build_event_text_file="${TEST_log}" :badfilegroup \ | |
| && fail "Expected failure") || : | |
| # There should be precisely one event with target_completed as event id type | |
| (echo 'g/^id/+1p'; echo 'q') | ed "${TEST_log}" 2>&1 | tail -n +2 > event_id_types | |
| [ `grep target_completed event_id_types | wc -l` -eq 1 ] \ | |
| || fail "not precisely one target_completed event id" | |
| # Moreover, we expect precisely one event identified by an unconfigured label | |
| [ `grep unconfigured_label event_id_types | wc -l` -eq 1 ] \ | |
| || fail "not precisely one unconfigured_label event id" | |
| (bazel build --build_event_text_file="${TEST_log}" :badfilegroup :doesnotexist \ | |
| && fail "Expected failure") || : | |
| # There should be precisely two events with target_completed as event id type | |
| (echo 'g/^id/+1p'; echo 'q') | ed "${TEST_log}" 2>&1 | tail -n +2 > event_id_types | |
| [ `grep target_completed event_id_types | wc -l` -eq 2 ] \ | |
| || fail "not precisely one target_completed event id" | |
| # Moreover, we expect precisely one event identified by an unconfigured label | |
| [ `grep unconfigured_label event_id_types | wc -l` -eq 1 ] \ | |
| || fail "not precisely one unconfigured_label event id" | |
| } | |
| function test_tool_command_line() { | |
| bazel build --experimental_tool_command_line="foo bar" --build_event_text_file=$TEST_log \ | |
| || fail "build failed" | |
| # Sanity check the arglist | |
| expect_log_once 'args: "build"' | |
| expect_log_once 'args: "--experimental_tool_command_line=' | |
| # Structured command line. Expect the explicit flags to appear twice, | |
| # in the canonical and original command lines | |
| expect_log 'command_line_label: "original"' | |
| expect_log 'command_line_label: "canonical"' | |
| expect_log 'command_line_label: "tool"' | |
| # Expect the actual tool command line flag to appear twice, because of the two | |
| # bazel command lines that are reported | |
| expect_log_n 'combined_form: "--experimental_tool_command_line=' 2 | |
| expect_log_n 'option_name: "experimental_tool_command_line"' 2 | |
| expect_log_n 'option_value: "foo bar"' 2 | |
| # Check the contents of the tool command line | |
| expect_log_once 'chunk: "foo bar"' | |
| } | |
| function test_noanalyze() { | |
| bazel build --noanalyze --build_event_text_file="${TEST_log}" pkg:true \ | |
| || fail "build failed" | |
| expect_log_once '^aborted' | |
| expect_log 'reason: NO_ANALYZE' | |
| expect_log 'last_message: true' | |
| expect_log_once '^build_tool_logs' | |
| } | |
| function test_nobuild() { | |
| bazel build --nobuild --build_event_text_file="${TEST_log}" pkg:true \ | |
| || fail "build failed" | |
| expect_log_once '^aborted' | |
| expect_log 'reason: NO_BUILD' | |
| } | |
| function test_server_pid() { | |
| bazel build --test_output=all --build_event_text_file=bep.txt \ | |
| || fail "Build failed but should have succeeded" | |
| cat bep.txt | grep server_pid >> "$TEST_log" | |
| expect_log_once "server_pid:.*$(bazel info server_pid)$" | |
| rm bep.txt | |
| } | |
| function test_bep_report_only_important_artifacts() { | |
| bazel build --test_output=all --build_event_text_file=bep.txt \ | |
| //pkg:true || fail "Build failed but should have succeeded" | |
| cat bep.txt >> "$TEST_log" | |
| expect_not_log "_hidden_top_level_INTERNAL_" | |
| rm bep.txt | |
| } | |
| function test_starlark_flags() { | |
| cat >> build_setting.bzl <<EOF | |
| def _build_setting_impl(ctx): | |
| return [] | |
| int_setting = rule( | |
| implementation = _build_setting_impl, | |
| build_setting = config.int(flag=True) | |
| ) | |
| EOF | |
| cat >> BUILD <<EOF | |
| load('//:build_setting.bzl', 'int_setting') | |
| int_setting(name = 'my_int_setting', | |
| build_setting_default = 42, | |
| ) | |
| EOF | |
| bazel build --build_event_text_file=bep.txt \ | |
| --//:my_int_setting=666 \ | |
| //pkg:true || fail "Build failed but should have succeeded" | |
| cat bep.txt >> "$TEST_log" | |
| rm bep.txt | |
| expect_log 'option_name: "//:my_int_setting"' | |
| expect_log 'option_value: "666"' | |
| } | |
| run_suite "Integration tests for the build event stream" |