Skip to content

Commit

Permalink
Enable unittest.suite to accept partial calls of rules
Browse files Browse the repository at this point in the history
This permits using `unittest.suite` with test rules that have nondefault
attributes, while retaining compatibility with current usage.

For instance, this permits setting a `timeout` on each test in a
`unittest.suite`.  Previously, all tests in a `unittest.suite` would
have the default timeout, with no good way to alter this.  This
made it hard to eliminate all the warnings produced from using the
`--test_verbose_timeout_warnings` bazel option.

While timeouts were the motivation, the solution here is not specific
to timeouts. It will permit arbitrary additional arguments to the test
rules in a `unittest.suite`.

Fixes #98
  • Loading branch information
dws committed Sep 24, 2020
1 parent 2a89db4 commit 3cf001b
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 6 deletions.
1 change: 1 addition & 0 deletions lib/BUILD
Expand Up @@ -71,6 +71,7 @@ bzl_library(
srcs = ["unittest.bzl"],
deps = [
":new_sets",
":partial",
":sets",
":types",
],
Expand Down
15 changes: 15 additions & 0 deletions lib/types.bzl
Expand Up @@ -138,6 +138,20 @@ def _is_set(v):
"""
return type(v) == _a_struct_type and hasattr(v, "_values") and _is_dict(v._values)

def _is_partial(v):
"""Returns True if v is a set created by partial.make().
Args:
v: The value whose type should be checked.
Returns:
True if v was created by partial.make(), False otherwise.
"""
return type(v) == _a_struct_type \
and hasattr(v, "function") and _is_function(v.function) \
and hasattr(v, "args") and _is_tuple(v.args) \
and hasattr(v, "kwargs") and _is_dict(v.kwargs)

types = struct(
is_list = _is_list,
is_string = _is_string,
Expand All @@ -149,4 +163,5 @@ types = struct(
is_function = _is_function,
is_depset = _is_depset,
is_set = _is_set,
is_partial = _is_partial,
)
16 changes: 10 additions & 6 deletions lib/unittest.bzl
Expand Up @@ -20,6 +20,7 @@ assertions used to within tests.
"""

load(":new_sets.bzl", new_sets = "sets")
load(":partial.bzl", "partial")
load(":types.bzl", "types")

# The following function should only be called from WORKSPACE files and workspace macros.
Expand Down Expand Up @@ -229,18 +230,18 @@ def _suite(name, *test_rules):
writing a macro in your `.bzl` file to instantiate all targets, and calling
that macro from your BUILD file so you only have to load one symbol.
For the case where your unit tests do not take any (non-default) attributes --
i.e., if your unit tests do not test rules -- you can use this function to
create the targets and wrap them in a single test_suite target. In your
`.bzl` file, write:
You can use this function to create the targets and wrap them in a single
test_suite target. If a test rule requires no arguments, you can simply list
it as an argument. If you wish to supply attributes explicitly, you can do so
using `partial.make()`. For instance, in your `.bzl` file, you could write:
```
def your_test_suite():
unittest.suite(
"your_test_suite",
your_test,
your_other_test,
yet_another_test,
partial.make(yet_another_test, timeout = "short"),
)
```
Expand All @@ -266,7 +267,10 @@ def _suite(name, *test_rules):
test_names = []
for index, test_rule in enumerate(test_rules):
test_name = "%s_test_%d" % (name, index)
test_rule(name = test_name)
if types.is_partial(test_rule):
partial.call(test_rule, name = test_name)
else:
test_rule(name = test_name)
test_names.append(test_name)

native.test_suite(
Expand Down
18 changes: 18 additions & 0 deletions tests/types_tests.bzl
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.
"""Unit tests for types.bzl."""

load("//lib:partial.bzl", "partial")
load("//lib:types.bzl", "types")
load("//lib:unittest.bzl", "asserts", "unittest")
load("//lib:new_sets.bzl", "sets")
Expand Down Expand Up @@ -231,6 +232,22 @@ def _is_set_test(ctx):

is_set_test = unittest.make(_is_set_test)

def _is_partial_test(ctx):
"""Unit test for types.is_partial."""
env = unittest.begin(ctx)

asserts.true(env, types.is_partial(partial.make(is_set_test)))
asserts.true(env, types.is_partial(partial.make(is_set_test, timeout = "short")))
asserts.true(env, types.is_partial(partial.make(is_set_test, timeout = "short", tags = ["foo"])))
asserts.false(env, types.is_partial(None))
asserts.false(env, types.is_partial({}))
asserts.false(env, types.is_partial(struct(foo = 1)))
asserts.false(env, types.is_partial(struct(function = "not really function")))

return unittest.end(env)

is_partial_test = unittest.make(_is_partial_test)

def types_test_suite():
"""Creates the test targets and test suite for types.bzl tests."""
unittest.suite(
Expand All @@ -245,4 +262,5 @@ def types_test_suite():
is_function_test,
is_depset_test,
is_set_test,
is_partial_test,
)
1 change: 1 addition & 0 deletions tests/unittest_test.sh
Expand Up @@ -73,6 +73,7 @@ exports_files(["*.bzl"])
EOF
ln -sf "$(rlocation bazel_skylib/lib/dicts.bzl)" lib/dicts.bzl
ln -sf "$(rlocation bazel_skylib/lib/new_sets.bzl)" lib/new_sets.bzl
ln -sf "$(rlocation bazel_skylib/lib/partial.bzl)" lib/partial.bzl
ln -sf "$(rlocation bazel_skylib/lib/sets.bzl)" lib/sets.bzl
ln -sf "$(rlocation bazel_skylib/lib/types.bzl)" lib/types.bzl
ln -sf "$(rlocation bazel_skylib/lib/unittest.bzl)" lib/unittest.bzl
Expand Down
15 changes: 15 additions & 0 deletions tests/unittest_tests.bzl
Expand Up @@ -14,6 +14,7 @@

"""Unit tests for unittest.bzl."""

load("//lib:partial.bzl", "partial")
load("//lib:unittest.bzl", "analysistest", "asserts", "unittest")

###################################
Expand Down Expand Up @@ -42,6 +43,19 @@ def _basic_passing_test(ctx):

basic_passing_test = unittest.make(_basic_passing_test)

#################################################
####### basic_passing_short_timeout_test ########
#################################################
def _basic_passing_short_timeout_test(ctx):
"""Unit tests for a basic library verification test."""
env = unittest.begin(ctx)

asserts.equals(env, ctx.attr.timeout, "short")

return unittest.end(env)

basic_passing_short_timeout_test = unittest.make(_basic_passing_short_timeout_test)

###################################
####### change_setting_test #######
###################################
Expand Down Expand Up @@ -234,6 +248,7 @@ def unittest_passing_tests_suite():
unittest.suite(
"unittest_tests",
basic_passing_test,
partial.make(basic_passing_short_timeout_test, timeout = "short"),
)

change_setting_test(
Expand Down

0 comments on commit 3cf001b

Please sign in to comment.