Skip to content

Commit 8337731

Browse files
rickeylevBlaze Rules Copybara
authored andcommitted
feat: add contains_exactly_predicates, contains_at_least_predicates, 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
1 parent b73038c commit 8337731

3 files changed

Lines changed: 110 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@
44

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

7+
### Added
8+
* `matching.any()` and `matching.all()` for composing `Matcher` objects using
9+
"or" and "and" semantics, respectively
10+
* Predicate-variants for RunfilesSubject methods:
11+
`RunfilesSubject.contains_exactly_predicates()`,
12+
`RunfilesSubject.contains_at_least_predicates()`
13+
* `RunfilesSubject.paths()`, which returns a `CollectionSubject` of the
14+
runfiles paths.
15+
716
## 0.6.0 - 2024-02-15
817

918
[0.6.0]: https://github.com/bazelbuild/rules_testing/releases/tag/v0.6.0

lib/private/runfiles_subject.bzl

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,14 @@ def _runfiles_subject_new(runfiles, meta, kind = None):
6262
actual = runfiles,
6363
contains = lambda *a, **k: _runfiles_subject_contains(self, *a, **k),
6464
contains_at_least = lambda *a, **k: _runfiles_subject_contains_at_least(self, *a, **k),
65+
contains_at_least_predicates = lambda *a, **k: _runfiles_subject_contains_at_least_predicates(self, *a, **k),
6566
contains_exactly = lambda *a, **k: _runfiles_subject_contains_exactly(self, *a, **k),
67+
contains_exactly_predicates = lambda *a, **k: _runfiles_subject_contains_exactly_predicates(self, *a, **k),
6668
contains_none_of = lambda *a, **k: _runfiles_subject_contains_none_of(self, *a, **k),
6769
contains_predicate = lambda *a, **k: _runfiles_subject_contains_predicate(self, *a, **k),
6870
not_contains = lambda *a, **k: _runfiles_subject_not_contains(self, *a, **k),
6971
not_contains_predicate = lambda *a, **k: _runfiles_subject_not_contains_predicate(self, *a, **k),
72+
paths = lambda *a, **k: _runfiles_subject_paths(self, *a, **k),
7073
# keep sorted end
7174
)
7275
return public
@@ -117,6 +120,26 @@ def _runfiles_subject_contains_at_least(self, paths):
117120
container_name = "{}runfiles".format(self.kind + " " if self.kind else ""),
118121
).contains_at_least(paths)
119122

123+
def _runfiles_subject_contains_at_least_predicates(self, matchers):
124+
"""Assert that the runfiles contains at least all of the provided matchers.
125+
126+
The runfile paths must match all the matchers. It can contain extra elements.
127+
The multiplicity of matchers is respected. Checking that the relative order
128+
of matches is the same as the passed-in matchers order can done by calling
129+
`in_order()`.
130+
131+
Args:
132+
self: implicitly added.
133+
matchers: ([`list`] of [`Matcher`]) (see `matchers` struct). They are
134+
passed string paths.
135+
"""
136+
return CollectionSubject.new(
137+
self.actual_paths,
138+
meta = self.meta,
139+
element_plural_name = "paths",
140+
container_name = "{}runfiles".format(self.kind + " " if self.kind else ""),
141+
).contains_at_least_predicates(matchers)
142+
120143
def _runfiles_subject_contains_predicate(self, matcher):
121144
"""Asserts that `matcher` matches at least one value.
122145
@@ -173,6 +196,27 @@ def _runfiles_subject_contains_exactly(self, paths):
173196
meta = self.meta,
174197
)
175198

199+
def _runfiles_subject_contains_exactly_predicates(self, expected):
200+
"""Asserts the runfiles contains exactly the given matchers.
201+
202+
See `CollectionSubject.contains_exactly_predicates` for details on
203+
behavior.
204+
205+
Args:
206+
self: implicitly added.
207+
expected: ([`list`] of [`Matcher`]) that must match. They are passed
208+
string paths.
209+
210+
Returns:
211+
[`Ordered`] (see `_ordered_incorrectly_new`).
212+
"""
213+
return CollectionSubject.new(
214+
self.actual_paths,
215+
meta = self.meta,
216+
element_plural_name = "paths",
217+
container_name = "{}runfiles".format(self.kind + " " if self.kind else ""),
218+
).contains_exactly_predicates(expected)
219+
176220
def _runfiles_subject_contains_none_of(self, paths, require_workspace_prefix = True):
177221
"""Asserts the runfiles contain none of `paths`.
178222
@@ -244,6 +288,22 @@ def _runfiles_subject_not_contains_predicate(self, matcher):
244288
"""
245289
check_not_contains_predicate(self.actual_paths, matcher, meta = self.meta)
246290

291+
def _runfiles_subject_paths(self):
292+
"""Returns a `CollectionSubject` of the computed runfile path strings.
293+
294+
Args:
295+
self: implicitly added
296+
297+
Returns:
298+
[`CollectionSubject`] of the runfile path strings.
299+
"""
300+
return CollectionSubject.new(
301+
self.actual_paths,
302+
meta = self.meta.derive("paths()"),
303+
element_plural_name = "paths",
304+
container_name = "{}runfiles".format(self.kind + " " if self.kind else ""),
305+
)
306+
247307
def _runfiles_subject_check_workspace_prefix(self, path):
248308
if not path.startswith(self.meta.ctx.workspace_name + "/"):
249309
fail("Rejecting path lacking workspace prefix: this often indicates " +
@@ -257,8 +317,10 @@ RunfilesSubject = struct(
257317
new = _runfiles_subject_new,
258318
contains = _runfiles_subject_contains,
259319
contains_at_least = _runfiles_subject_contains_at_least,
320+
contains_at_least_predicates = _runfiles_subject_contains_at_least_predicates,
260321
contains_predicate = _runfiles_subject_contains_predicate,
261322
contains_exactly = _runfiles_subject_contains_exactly,
323+
contains_exactly_predicates = _runfiles_subject_contains_exactly_predicates,
262324
contains_none_of = _runfiles_subject_contains_none_of,
263325
not_contains = _runfiles_subject_not_contains,
264326
not_contains_predicate = _runfiles_subject_not_contains_predicate,

tests/truth_tests.bzl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,6 +1264,25 @@ def _runfiles_subject_test(env, target):
12641264
msg = "check contains_exactly fails",
12651265
)
12661266

1267+
subject.contains_exactly_predicates([
1268+
matching.str_matches("runfile1.txt"),
1269+
matching.str_matches("helper.txt"),
1270+
])
1271+
_assert_no_failures(fake_env, env = env)
1272+
subject.contains_exactly_predicates([
1273+
matching.str_matches("runfile1.txt"),
1274+
matching.str_matches("does-not-match"),
1275+
])
1276+
_assert_failure(
1277+
fake_env,
1278+
[
1279+
"1 missing",
1280+
"<matches 'does-not-match'>",
1281+
],
1282+
env = env,
1283+
msg = "check contains_exactly_predicates",
1284+
)
1285+
12671286
subject.contains_at_least([
12681287
"{workspace}/{package}/default_runfile1.txt",
12691288
])
@@ -1282,6 +1301,26 @@ def _runfiles_subject_test(env, target):
12821301
msg = "check contains_at_least fails",
12831302
)
12841303

1304+
subject.contains_at_least_predicates([
1305+
matching.str_matches("runfile1.txt"),
1306+
])
1307+
_assert_no_failures(fake_env, env = env)
1308+
subject.contains_at_least_predicates([
1309+
matching.str_matches("does-not-match"),
1310+
])
1311+
_assert_failure(
1312+
fake_env,
1313+
[
1314+
"1 expected paths missing",
1315+
"<matches 'does-not-match'>",
1316+
],
1317+
env = env,
1318+
msg = "check contains_at_least_predicates",
1319+
)
1320+
1321+
subject.paths().contains_predicate(matching.str_matches("runfile1.txt"))
1322+
_assert_no_failures(fake_env, env = env)
1323+
12851324
_end(env, fake_env)
12861325

12871326
_suite.append(runfiles_subject_test)

0 commit comments

Comments
 (0)