Skip to content

Commit 4c3ea0b

Browse files
committed
Create a failure test rule
When testing for analysis-phase-failures in rules this rule provides otherwise necessary boilerplate that simply allows to test for correct Error messages
1 parent ee67264 commit 4c3ea0b

File tree

7 files changed

+380
-0
lines changed

7 files changed

+380
-0
lines changed

docs/BUILD

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ stardoc(
1717
input = "//rules:analysis_test.bzl",
1818
)
1919

20+
stardoc(
21+
name = "analysis_failure_test_docs",
22+
out = "analysis_failure_test_doc_gen.md",
23+
input = "//rules:analysis_failure_test.bzl",
24+
deps = ["//rules:analysis_failure_test"],
25+
)
26+
2027
stardoc(
2128
name = "collections_docs",
2229
out = "collections_doc_gen.md",

docs/analysis_failure_test_doc.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<!-- Generated with Stardoc: http://skydoc.bazel.build -->
2+
3+
A test verifying that another target fails to analyse as part of a `bazel test`
4+
5+
This analysistest is mostly aimed at rule authors that want to assert certain error conditions.
6+
If the target under test does not fail the analysis phase, the test will evaluate to FAILED.
7+
If the given error_message is not contained in the otherwise printed ERROR message, the test evaluates to FAILED.
8+
If the given error_message is contained in the otherwise printed ERROR message, the test evaluates to PASSED.
9+
10+
NOTE:
11+
Adding the `manual` tag to the target-under-test is recommended.
12+
It prevents analysis failure of that target if `bazel test //...` is used.
13+
14+
Typical usage:
15+
```
16+
load("@bazel_skylib//rules:analysis_failure_test.bzl", "analysis_failure_test")
17+
18+
rule_with_analysis_failure(
19+
name = "unit",
20+
tags = ["manual"],
21+
)
22+
23+
24+
analysis_failure_test(
25+
name = "analysis_fails_with_error",
26+
target_under_test = ":unit",
27+
error_message = _EXPECTED_ERROR_MESSAGE,
28+
)
29+
```
30+
31+
Args:
32+
target_under_test: The target that is expected to cause an anlysis failure
33+
error_message: The asserted error message in the (normally printed) ERROR.
34+
35+
<a id="#analysis_failure_test"></a>
36+
37+
## analysis_failure_test
38+
39+
<pre>
40+
analysis_failure_test(<a href="#analysis_failure_test-name">name</a>, <a href="#analysis_failure_test-error_message">error_message</a>, <a href="#analysis_failure_test-target_under_test">target_under_test</a>)
41+
</pre>
42+
43+
44+
45+
**ATTRIBUTES**
46+
47+
48+
| Name | Description | Type | Mandatory | Default |
49+
| :------------- | :------------- | :------------- | :------------- | :------------- |
50+
| <a id="analysis_failure_test-name"></a>name | A unique name for this target. | <a href="https://bazel.build/docs/build-ref.html#name">Name</a> | required | |
51+
| <a id="analysis_failure_test-error_message"></a>error_message | The test asserts that the given string is contained in the error message of the target under test. | String | required | |
52+
| <a id="analysis_failure_test-target_under_test"></a>target_under_test | - | <a href="https://bazel.build/docs/build-ref.html#labels">Label</a> | required | |
53+
54+

rules/BUILD

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ bzl_library(
99
srcs = ["analysis_test.bzl"],
1010
)
1111

12+
bzl_library(
13+
name = "analysis_failure_test",
14+
srcs = ["analysis_failure_test.bzl"],
15+
deps = ["//lib:unittest"],
16+
)
17+
1218
bzl_library(
1319
name = "build_test",
1420
srcs = ["build_test.bzl"],

rules/analysis_failure_test.bzl

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Copyright 2021 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""A test verifying that another target fails to analyse as part of a `bazel test`
16+
17+
This analysistest is mostly aimed at rule authors that want to assert certain error conditions.
18+
If the target under test does not fail the analysis phase, the test will evaluate to FAILED.
19+
If the given error_message is not contained in the otherwise printed ERROR message, the test evaluates to FAILED.
20+
If the given error_message is contained in the otherwise printed ERROR message, the test evaluates to PASSED.
21+
22+
NOTE:
23+
Adding the `manual` tag to the target-under-test is recommended.
24+
It prevents analysis failure of that target if `bazel test //...` is used.
25+
26+
Typical usage:
27+
```
28+
load("@bazel_skylib//rules:analysis_failure_test.bzl", "analysis_failure_test")
29+
30+
rule_with_analysis_failure(
31+
name = "unit",
32+
tags = ["manual"],
33+
)
34+
35+
36+
analysis_failure_test(
37+
name = "analysis_fails_with_error",
38+
target_under_test = ":unit",
39+
error_message = _EXPECTED_ERROR_MESSAGE,
40+
)
41+
```
42+
43+
Args:
44+
target_under_test: The target that is expected to cause an anlysis failure
45+
error_message: The asserted error message in the (normally printed) ERROR."""
46+
47+
load("//lib:unittest.bzl", "analysistest", "asserts")
48+
49+
def _analysis_failure_test_impl(ctx):
50+
"""Implementation function for analysis_failure_test. """
51+
env = analysistest.begin(ctx)
52+
asserts.expect_failure(env, expected_failure_msg = ctx.attr.error_message)
53+
return analysistest.end(env)
54+
55+
analysis_failure_test = analysistest.make(
56+
_analysis_failure_test_impl,
57+
expect_failure = True,
58+
attrs = {
59+
"error_message": attr.string(
60+
mandatory = True,
61+
doc = "The test asserts that the given string is contained in the error message of the target under test.",
62+
),
63+
},
64+
)

tests/BUILD

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
load("//:bzl_library.bzl", "bzl_library")
2+
load(":analysis_failure_test_tests.bzl", "analysis_failure_test_test_suite")
23
load(":build_test_tests.bzl", "build_test_test_suite")
34
load(":collections_tests.bzl", "collections_test_suite")
45
load(":dicts_tests.bzl", "dicts_test_suite")
@@ -43,6 +44,8 @@ unittest_passing_tests_suite()
4344

4445
versions_test_suite()
4546

47+
analysis_failure_test_test_suite()
48+
4649
bzl_library(
4750
name = "unittest_tests_bzl",
4851
srcs = ["unittest_tests.bzl"],
@@ -78,6 +81,19 @@ sh_test(
7881
tags = ["local"],
7982
)
8083

84+
sh_test(
85+
name = "analysis_failure_test_e2e_test",
86+
srcs = ["analysis_failure_test_test.sh"],
87+
data = [
88+
":unittest.bash",
89+
"//lib:unittest",
90+
"//rules:analysis_failure_test.bzl",
91+
"//toolchains/unittest:test_deps",
92+
"@bazel_tools//tools/bash/runfiles",
93+
],
94+
tags = ["local"],
95+
)
96+
8197
sh_test(
8298
name = "common_settings_e2e_test",
8399
srcs = ["common_settings_test.sh"],
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
#!/bin/bash
2+
3+
# Copyright 2021 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+
# End to end tests for analysis_failure_test.bzl.
18+
#
19+
# End to end tests of analysis_failure_test.bzl cover verification that
20+
# analysis_failure_test tests succeed when their underlying test targets fail analysis with
21+
# a given error message.
22+
23+
# --- begin runfiles.bash initialization ---
24+
set -euo pipefail
25+
if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
26+
if [[ -f "$0.runfiles_manifest" ]]; then
27+
export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
28+
elif [[ -f "$0.runfiles/MANIFEST" ]]; then
29+
export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
30+
elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
31+
export RUNFILES_DIR="$0.runfiles"
32+
fi
33+
fi
34+
if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
35+
source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
36+
elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
37+
source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \
38+
"$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
39+
else
40+
echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
41+
exit 1
42+
fi
43+
# --- end runfiles.bash initialization ---
44+
45+
source "$(rlocation bazel_skylib/tests/unittest.bash)" \
46+
|| { echo "Could not source bazel_skylib/tests/unittest.bash" >&2; exit 1; }
47+
48+
function create_pkg() {
49+
local -r pkg="$1"
50+
mkdir -p "$pkg"
51+
cd "$pkg"
52+
53+
cat > WORKSPACE <<EOF
54+
workspace(name = 'bazel_skylib')
55+
56+
load("//lib:unittest.bzl", "register_unittest_toolchains")
57+
58+
register_unittest_toolchains()
59+
EOF
60+
61+
mkdir -p rules
62+
cat > rules/BUILD <<EOF
63+
exports_files(["*.bzl"])
64+
EOF
65+
ln -sf "$(rlocation bazel_skylib/rules/analysis_failure_test.bzl)" rules/analysis_failure_test.bzl
66+
67+
mkdir -p lib
68+
cat > lib/BUILD <<EOF
69+
exports_files(["*.bzl"])
70+
EOF
71+
ln -sf "$(rlocation bazel_skylib/lib/unittest.bzl)" lib/unittest.bzl
72+
ln -sf "$(rlocation bazel_skylib/lib/types.bzl)" lib/types.bzl
73+
ln -sf "$(rlocation bazel_skylib/lib/partial.bzl)" lib/partial.bzl
74+
ln -sf "$(rlocation bazel_skylib/lib/new_sets.bzl)" lib/new_sets.bzl
75+
ln -sf "$(rlocation bazel_skylib/lib/dicts.bzl)" lib/dicts.bzl
76+
77+
mkdir -p toolchains/unittest
78+
ln -sf "$(rlocation bazel_skylib/toolchains/unittest/BUILD)" toolchains/unittest/BUILD
79+
80+
mkdir -p fakerules
81+
cat > fakerules/rules.bzl <<EOF
82+
load("//rules:analysis_failure_test.bzl", "analysis_failure_test")
83+
84+
def _fake_rule_impl(ctx):
85+
fail("This rule fails at analysis phase")
86+
87+
fake_rule = rule(
88+
implementation = _fake_rule_impl,
89+
)
90+
91+
def _fake_depending_rule_impl(ctx):
92+
return []
93+
94+
fake_depending_rule = rule(
95+
implementation = _fake_depending_rule_impl,
96+
attrs = {"deps" : attr.label_list()},
97+
)
98+
EOF
99+
100+
cat > fakerules/BUILD <<EOF
101+
exports_files(["*.bzl"])
102+
EOF
103+
104+
mkdir -p testdir
105+
cat > testdir/BUILD <<EOF
106+
load("//rules:analysis_failure_test.bzl", "analysis_failure_test")
107+
load("//fakerules:rules.bzl", "fake_rule", "fake_depending_rule")
108+
109+
fake_rule(name = "target_fails")
110+
111+
fake_depending_rule(
112+
name = "dep_fails",
113+
deps = [":target_fails"],
114+
)
115+
116+
fake_depending_rule(
117+
name = "rule_that_does_not_fail",
118+
deps = [],
119+
)
120+
121+
analysis_failure_test(
122+
name = "direct_target_fails",
123+
target_under_test = ":target_fails",
124+
error_message = "This rule fails at analysis phase",
125+
)
126+
127+
analysis_failure_test(
128+
name = "transitive_target_fails",
129+
target_under_test = ":dep_fails",
130+
error_message = "This rule fails at analysis phase",
131+
)
132+
133+
analysis_failure_test(
134+
name = "fails_with_wrong_error_message",
135+
target_under_test = ":dep_fails",
136+
error_message = "This is the wrong error message",
137+
)
138+
139+
analysis_failure_test(
140+
name = "fails_with_target_that_does_not_fail",
141+
target_under_test = ":rule_that_does_not_fail",
142+
error_message = "This rule fails at analysis phase",
143+
)
144+
EOF
145+
}
146+
147+
148+
function test_direct_target_succeeds() {
149+
local -r pkg="${FUNCNAME[0]}"
150+
create_pkg "$pkg"
151+
152+
bazel test testdir:direct_target_fails >"$TEST_log" 2>&1 || fail "Expected test to pass"
153+
154+
expect_log "PASSED"
155+
}
156+
157+
function test_transitive_target_succeeds() {
158+
local -r pkg="${FUNCNAME[0]}"
159+
create_pkg "$pkg"
160+
161+
bazel test testdir:transitive_target_fails >"$TEST_log" 2>&1 || fail "Expected test to pass"
162+
163+
expect_log "PASSED"
164+
}
165+
166+
function test_with_wrong_error_message_fails() {
167+
local -r pkg="${FUNCNAME[0]}"
168+
create_pkg "$pkg"
169+
170+
bazel test testdir:fails_with_wrong_error_message --test_output=all --verbose_failures \
171+
>"$TEST_log" 2>&1 && fail "Expected test to fail" || true
172+
173+
expect_log "Expected errors to contain 'This is the wrong error message' but did not. Actual errors:"
174+
}
175+
176+
function test_with_rule_that_does_not_fail_fails() {
177+
local -r pkg="${FUNCNAME[0]}"
178+
create_pkg "$pkg"
179+
180+
bazel test testdir:fails_with_target_that_does_not_fail --test_output=all --verbose_failures \
181+
>"$TEST_log" 2>&1 && fail "Expected test to fail" || true
182+
183+
expect_log "Expected failure of target_under_test, but found success"
184+
}
185+
186+
187+
cd "$TEST_TMPDIR"
188+
run_suite "analysis_failure_test test suite"

0 commit comments

Comments
 (0)