Skip to content

Commit

Permalink
feat: add contains_exactly_predicates, contains_at_least_predicates, …
Browse files Browse the repository at this point in the history
…paths to RunfilesSubject

This exposes predicate-variants of the contains functions, which make it easier
to assert more complex conditions of runfiles. Also expose the paths directly
as a CollectionSubject, which makes it easier to use other CollectionSubject
methods on it (e.g. CollectionSubject.transform).

Along the way...
* Add missing changelog entry for matching.any/all

PiperOrigin-RevId: 685598653
  • Loading branch information
rickeylev authored and Blaze Rules Copybara committed Oct 14, 2024
1 parent b73038c commit 8337731
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 0 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@

[0.X.0]: https://github.com/bazelbuild/rules_testing/releases/tag/v0.X.0

### Added
* `matching.any()` and `matching.all()` for composing `Matcher` objects using
"or" and "and" semantics, respectively
* Predicate-variants for RunfilesSubject methods:
`RunfilesSubject.contains_exactly_predicates()`,
`RunfilesSubject.contains_at_least_predicates()`
* `RunfilesSubject.paths()`, which returns a `CollectionSubject` of the
runfiles paths.

## 0.6.0 - 2024-02-15

[0.6.0]: https://github.com/bazelbuild/rules_testing/releases/tag/v0.6.0
Expand Down
62 changes: 62 additions & 0 deletions lib/private/runfiles_subject.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,14 @@ def _runfiles_subject_new(runfiles, meta, kind = None):
actual = runfiles,
contains = lambda *a, **k: _runfiles_subject_contains(self, *a, **k),
contains_at_least = lambda *a, **k: _runfiles_subject_contains_at_least(self, *a, **k),
contains_at_least_predicates = lambda *a, **k: _runfiles_subject_contains_at_least_predicates(self, *a, **k),
contains_exactly = lambda *a, **k: _runfiles_subject_contains_exactly(self, *a, **k),
contains_exactly_predicates = lambda *a, **k: _runfiles_subject_contains_exactly_predicates(self, *a, **k),
contains_none_of = lambda *a, **k: _runfiles_subject_contains_none_of(self, *a, **k),
contains_predicate = lambda *a, **k: _runfiles_subject_contains_predicate(self, *a, **k),
not_contains = lambda *a, **k: _runfiles_subject_not_contains(self, *a, **k),
not_contains_predicate = lambda *a, **k: _runfiles_subject_not_contains_predicate(self, *a, **k),
paths = lambda *a, **k: _runfiles_subject_paths(self, *a, **k),
# keep sorted end
)
return public
Expand Down Expand Up @@ -117,6 +120,26 @@ def _runfiles_subject_contains_at_least(self, paths):
container_name = "{}runfiles".format(self.kind + " " if self.kind else ""),
).contains_at_least(paths)

def _runfiles_subject_contains_at_least_predicates(self, matchers):
"""Assert that the runfiles contains at least all of the provided matchers.
The runfile paths must match all the matchers. It can contain extra elements.
The multiplicity of matchers is respected. Checking that the relative order
of matches is the same as the passed-in matchers order can done by calling
`in_order()`.
Args:
self: implicitly added.
matchers: ([`list`] of [`Matcher`]) (see `matchers` struct). They are
passed string paths.
"""
return CollectionSubject.new(
self.actual_paths,
meta = self.meta,
element_plural_name = "paths",
container_name = "{}runfiles".format(self.kind + " " if self.kind else ""),
).contains_at_least_predicates(matchers)

def _runfiles_subject_contains_predicate(self, matcher):
"""Asserts that `matcher` matches at least one value.
Expand Down Expand Up @@ -173,6 +196,27 @@ def _runfiles_subject_contains_exactly(self, paths):
meta = self.meta,
)

def _runfiles_subject_contains_exactly_predicates(self, expected):
"""Asserts the runfiles contains exactly the given matchers.
See `CollectionSubject.contains_exactly_predicates` for details on
behavior.
Args:
self: implicitly added.
expected: ([`list`] of [`Matcher`]) that must match. They are passed
string paths.
Returns:
[`Ordered`] (see `_ordered_incorrectly_new`).
"""
return CollectionSubject.new(
self.actual_paths,
meta = self.meta,
element_plural_name = "paths",
container_name = "{}runfiles".format(self.kind + " " if self.kind else ""),
).contains_exactly_predicates(expected)

def _runfiles_subject_contains_none_of(self, paths, require_workspace_prefix = True):
"""Asserts the runfiles contain none of `paths`.
Expand Down Expand Up @@ -244,6 +288,22 @@ def _runfiles_subject_not_contains_predicate(self, matcher):
"""
check_not_contains_predicate(self.actual_paths, matcher, meta = self.meta)

def _runfiles_subject_paths(self):
"""Returns a `CollectionSubject` of the computed runfile path strings.
Args:
self: implicitly added
Returns:
[`CollectionSubject`] of the runfile path strings.
"""
return CollectionSubject.new(
self.actual_paths,
meta = self.meta.derive("paths()"),
element_plural_name = "paths",
container_name = "{}runfiles".format(self.kind + " " if self.kind else ""),
)

def _runfiles_subject_check_workspace_prefix(self, path):
if not path.startswith(self.meta.ctx.workspace_name + "/"):
fail("Rejecting path lacking workspace prefix: this often indicates " +
Expand All @@ -257,8 +317,10 @@ RunfilesSubject = struct(
new = _runfiles_subject_new,
contains = _runfiles_subject_contains,
contains_at_least = _runfiles_subject_contains_at_least,
contains_at_least_predicates = _runfiles_subject_contains_at_least_predicates,
contains_predicate = _runfiles_subject_contains_predicate,
contains_exactly = _runfiles_subject_contains_exactly,
contains_exactly_predicates = _runfiles_subject_contains_exactly_predicates,
contains_none_of = _runfiles_subject_contains_none_of,
not_contains = _runfiles_subject_not_contains,
not_contains_predicate = _runfiles_subject_not_contains_predicate,
Expand Down
39 changes: 39 additions & 0 deletions tests/truth_tests.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -1264,6 +1264,25 @@ def _runfiles_subject_test(env, target):
msg = "check contains_exactly fails",
)

subject.contains_exactly_predicates([
matching.str_matches("runfile1.txt"),
matching.str_matches("helper.txt"),
])
_assert_no_failures(fake_env, env = env)
subject.contains_exactly_predicates([
matching.str_matches("runfile1.txt"),
matching.str_matches("does-not-match"),
])
_assert_failure(
fake_env,
[
"1 missing",
"<matches 'does-not-match'>",
],
env = env,
msg = "check contains_exactly_predicates",
)

subject.contains_at_least([
"{workspace}/{package}/default_runfile1.txt",
])
Expand All @@ -1282,6 +1301,26 @@ def _runfiles_subject_test(env, target):
msg = "check contains_at_least fails",
)

subject.contains_at_least_predicates([
matching.str_matches("runfile1.txt"),
])
_assert_no_failures(fake_env, env = env)
subject.contains_at_least_predicates([
matching.str_matches("does-not-match"),
])
_assert_failure(
fake_env,
[
"1 expected paths missing",
"<matches 'does-not-match'>",
],
env = env,
msg = "check contains_at_least_predicates",
)

subject.paths().contains_predicate(matching.str_matches("runfile1.txt"))
_assert_no_failures(fake_env, env = env)

_end(env, fake_env)

_suite.append(runfiles_subject_test)
Expand Down

0 comments on commit 8337731

Please sign in to comment.