blob: ab90fd91fd9cac19955b9e585ff9069e576aafce [file] [log] [blame]
# Copyright 2023 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.
"""# Expect"""
load(":action_subject.bzl", "ActionSubject")
load(":bool_subject.bzl", "BoolSubject")
load(":collection_subject.bzl", "CollectionSubject")
load(":depset_file_subject.bzl", "DepsetFileSubject")
load(":dict_subject.bzl", "DictSubject")
load(":expect_meta.bzl", "ExpectMeta")
load(":file_subject.bzl", "FileSubject")
load(":int_subject.bzl", "IntSubject")
load(":str_subject.bzl", "StrSubject")
load(":struct_subject.bzl", "StructSubject")
load(":target_subject.bzl", "TargetSubject")
def _expect_new_from_env(env):
"""Wrapper around `env`.
This is the entry point to the Truth-style assertions. Example usage:
expect = expect(env)
expect.that_action(action).contains_at_least_args(...)
The passed in `env` object allows optional attributes to be set to
customize behavior. Usually this is helpful for testing. See `_fake_env()`
in truth_tests.bzl for examples.
* `fail`: callable that takes a failure message. If present, it
will be called instead of the regular `Expect.add_failure` logic.
* `get_provider`: callable that takes 2 positional args (target and
provider) and returns the found provider or fails.
* `has_provider`: callable that takes 2 positional args (a [`Target`] and
a [`provider`]) and returns [`bool`] (`True` if present, `False` otherwise) or fails.
Args:
env: unittest env struct, or some approximation. There are several
attributes that override regular behavior; see above doc.
Returns:
[`Expect`] object
"""
return _expect_new(env, None)
def _expect_new(env, meta):
"""Creates a new Expect object.
Internal; only other `Expect` methods should be calling this.
Args:
env: unittest env struct or some approximation.
meta: ([`ExpectMeta`]) metadata about call chain and state.
Returns:
[`Expect`] object
"""
meta = meta or ExpectMeta.new(env)
# buildifier: disable=uninitialized
public = struct(
# keep sorted start
meta = meta,
that_action = lambda *a, **k: _expect_that_action(self, *a, **k),
that_bool = lambda *a, **k: _expect_that_bool(self, *a, **k),
that_collection = lambda *a, **k: _expect_that_collection(self, *a, **k),
that_depset_of_files = lambda *a, **k: _expect_that_depset_of_files(self, *a, **k),
that_dict = lambda *a, **k: _expect_that_dict(self, *a, **k),
that_file = lambda *a, **k: _expect_that_file(self, *a, **k),
that_int = lambda *a, **k: _expect_that_int(self, *a, **k),
that_str = lambda *a, **k: _expect_that_str(self, *a, **k),
that_struct = lambda *a, **k: _expect_that_struct(self, *a, **k),
that_target = lambda *a, **k: _expect_that_target(self, *a, **k),
where = lambda *a, **k: _expect_where(self, *a, **k),
# keep sorted end
# Attributes used by Subject classes and internal helpers
)
self = struct(env = env, public = public, meta = meta)
return public
def _expect_that_action(self, action):
"""Creates a subject for asserting Actions.
Args:
self: implicitly added.
action: ([`Action`]) the action to check.
Returns:
[`ActionSubject`] object.
"""
return ActionSubject.new(
action,
self.meta.derive(
expr = "action",
details = ["action: [{}] {}".format(action.mnemonic, action)],
),
)
def _expect_that_bool(self, value, expr = "boolean"):
"""Creates a subject for asserting a boolean.
Args:
self: implicitly added.
value: ([`bool`]) the bool to check.
expr: ([`str`]) the starting "value of" expression to report in errors.
Returns:
[`BoolSubject`] object.
"""
return BoolSubject.new(
value,
meta = self.meta.derive(expr = expr),
)
def _expect_that_collection(self, collection, expr = "collection", **kwargs):
"""Creates a subject for asserting collections.
Args:
self: implicitly added.
collection: The collection (list or depset) to assert.
expr: ([`str`]) the starting "value of" expression to report in errors.
**kwargs: Additional kwargs to pass onto CollectionSubject.new
Returns:
[`CollectionSubject`] object.
"""
return CollectionSubject.new(collection, self.meta.derive(expr), **kwargs)
def _expect_that_depset_of_files(self, depset_files):
"""Creates a subject for asserting a depset of files.
Method: Expect.that_depset_of_files
Args:
self: implicitly added.
depset_files: ([`depset`] of [`File`]) the values to assert on.
Returns:
[`DepsetFileSubject`] object.
"""
return DepsetFileSubject.new(depset_files, self.meta.derive("depset_files"))
def _expect_that_dict(self, mapping, meta = None):
"""Creates a subject for asserting a dict.
Method: Expect.that_dict
Args:
self: implicitly added
mapping: ([`dict`]) the values to assert on
meta: ([`ExpectMeta`]) optional custom call chain information to use instead
Returns:
[`DictSubject`] object.
"""
meta = meta or self.meta.derive("dict")
return DictSubject.new(mapping, meta = meta)
def _expect_that_file(self, file, meta = None):
"""Creates a subject for asserting a file.
Method: Expect.that_file
Args:
self: implicitly added.
file: ([`File`]) the value to assert.
meta: ([`ExpectMeta`]) optional custom call chain information to use instead
Returns:
[`FileSubject`] object.
"""
meta = meta or self.meta.derive("file")
return FileSubject.new(file, meta = meta)
def _expect_that_int(self, value, expr = "integer"):
"""Creates a subject for asserting an `int`.
Method: Expect.that_int
Args:
self: implicitly added.
value: ([`int`]) the value to check against.
expr: ([`str`]) the starting "value of" expression to report in errors.
Returns:
[`IntSubject`] object.
"""
return IntSubject.new(value, self.meta.derive(expr))
def _expect_that_str(self, value):
"""Creates a subject for asserting a `str`.
Args:
self: implicitly added.
value: ([`str`]) the value to check against.
Returns:
[`StrSubject`] object.
"""
return StrSubject.new(value, self.meta.derive("string"))
def _expect_that_struct(self, value):
"""Creates a subject for asserting a `struct`.
Args:
self: implicitly added.
value: ([`struct`]) the value to check against.
Returns:
[`StructSubject`] object.
"""
return StructSubject.new(value, self.meta.derive("string"))
def _expect_that_target(self, target):
"""Creates a subject for asserting a `Target`.
This adds the following parameters to `ExpectMeta.format_str`:
{package}: The target's package, e.g. "foo/bar" from "//foo/bar:baz"
{name}: The target's base name, e.g., "baz" from "//foo/bar:baz"
Args:
self: implicitly added.
target: ([`Target`]) subject target to check against.
Returns:
[`TargetSubject`] object.
"""
return TargetSubject.new(target, self.meta.derive(
expr = "target({})".format(target.label),
details = ["target: {}".format(target.label)],
format_str_kwargs = {
"name": target.label.name,
"package": target.label.package,
},
))
def _expect_where(self, **details):
"""Add additional information about the assertion.
This is useful for attaching information that isn't part of the call
chain or some reason. Example usage:
expect(env).where(platform=ctx.attr.platform).that_str(...)
Would include "platform: {ctx.attr.platform}" in failure messages.
Args:
self: implicitly added.
**details: ([`dict`] of [`str`] to value) Each named arg is added to
the metadata details with the provided string, which is printed as
part of displaying any failures.
Returns:
[`Expect`] object with separate metadata derived from the original self.
"""
meta = self.meta.derive(
details = ["{}: {}".format(k, v) for k, v in details.items()],
)
return _expect_new(env = self.env, meta = meta)
# We use this name so it shows up nice in docs.
# buildifier: disable=name-conventions
Expect = struct(
# keep sorted start
new_from_env = _expect_new_from_env,
new = _expect_new,
that_action = _expect_that_action,
that_bool = _expect_that_bool,
that_collection = _expect_that_collection,
that_depset_of_files = _expect_that_depset_of_files,
that_dict = _expect_that_dict,
that_file = _expect_that_file,
that_int = _expect_that_int,
that_str = _expect_that_str,
that_struct = _expect_that_struct,
that_target = _expect_that_target,
where = _expect_where,
# keep sorted end
)