xref: /aosp_15_r20/external/bazelbuild-rules_testing/lib/private/expect.bzl (revision d605057434dcabba796c020773aab68d9790ff9f)
1*d6050574SRomain Jobredeaux# Copyright 2023 The Bazel Authors. All rights reserved.
2*d6050574SRomain Jobredeaux#
3*d6050574SRomain Jobredeaux# Licensed under the Apache License, Version 2.0 (the "License");
4*d6050574SRomain Jobredeaux# you may not use this file except in compliance with the License.
5*d6050574SRomain Jobredeaux# You may obtain a copy of the License at
6*d6050574SRomain Jobredeaux#
7*d6050574SRomain Jobredeaux#     http://www.apache.org/licenses/LICENSE-2.0
8*d6050574SRomain Jobredeaux#
9*d6050574SRomain Jobredeaux# Unless required by applicable law or agreed to in writing, software
10*d6050574SRomain Jobredeaux# distributed under the License is distributed on an "AS IS" BASIS,
11*d6050574SRomain Jobredeaux# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*d6050574SRomain Jobredeaux# See the License for the specific language governing permissions and
13*d6050574SRomain Jobredeaux# limitations under the License.
14*d6050574SRomain Jobredeaux
15*d6050574SRomain Jobredeaux"""# Expect"""
16*d6050574SRomain Jobredeaux
17*d6050574SRomain Jobredeauxload(":action_subject.bzl", "ActionSubject")
18*d6050574SRomain Jobredeauxload(":bool_subject.bzl", "BoolSubject")
19*d6050574SRomain Jobredeauxload(":collection_subject.bzl", "CollectionSubject")
20*d6050574SRomain Jobredeauxload(":depset_file_subject.bzl", "DepsetFileSubject")
21*d6050574SRomain Jobredeauxload(":dict_subject.bzl", "DictSubject")
22*d6050574SRomain Jobredeauxload(":expect_meta.bzl", "ExpectMeta")
23*d6050574SRomain Jobredeauxload(":file_subject.bzl", "FileSubject")
24*d6050574SRomain Jobredeauxload(":int_subject.bzl", "IntSubject")
25*d6050574SRomain Jobredeauxload(":str_subject.bzl", "StrSubject")
26*d6050574SRomain Jobredeauxload(":struct_subject.bzl", "StructSubject")
27*d6050574SRomain Jobredeauxload(":target_subject.bzl", "TargetSubject")
28*d6050574SRomain Jobredeaux
29*d6050574SRomain Jobredeauxdef _expect_new_from_env(env):
30*d6050574SRomain Jobredeaux    """Wrapper around `env`.
31*d6050574SRomain Jobredeaux
32*d6050574SRomain Jobredeaux    This is the entry point to the Truth-style assertions. Example usage:
33*d6050574SRomain Jobredeaux        expect = expect(env)
34*d6050574SRomain Jobredeaux        expect.that_action(action).contains_at_least_args(...)
35*d6050574SRomain Jobredeaux
36*d6050574SRomain Jobredeaux    The passed in `env` object allows optional attributes to be set to
37*d6050574SRomain Jobredeaux    customize behavior. Usually this is helpful for testing. See `_fake_env()`
38*d6050574SRomain Jobredeaux    in truth_tests.bzl for examples.
39*d6050574SRomain Jobredeaux      * `fail`: callable that takes a failure message. If present, it
40*d6050574SRomain Jobredeaux        will be called instead of the regular `Expect.add_failure` logic.
41*d6050574SRomain Jobredeaux      * `get_provider`: callable that takes 2 positional args (target and
42*d6050574SRomain Jobredeaux        provider) and returns the found provider or fails.
43*d6050574SRomain Jobredeaux      * `has_provider`: callable that takes 2 positional args (a [`Target`] and
44*d6050574SRomain Jobredeaux        a [`provider`]) and returns [`bool`] (`True` if present, `False` otherwise) or fails.
45*d6050574SRomain Jobredeaux
46*d6050574SRomain Jobredeaux    Args:
47*d6050574SRomain Jobredeaux        env: unittest env struct, or some approximation. There are several
48*d6050574SRomain Jobredeaux            attributes that override regular behavior; see above doc.
49*d6050574SRomain Jobredeaux
50*d6050574SRomain Jobredeaux    Returns:
51*d6050574SRomain Jobredeaux        [`Expect`] object
52*d6050574SRomain Jobredeaux    """
53*d6050574SRomain Jobredeaux    return _expect_new(env, None)
54*d6050574SRomain Jobredeaux
55*d6050574SRomain Jobredeauxdef _expect_new(env, meta):
56*d6050574SRomain Jobredeaux    """Creates a new Expect object.
57*d6050574SRomain Jobredeaux
58*d6050574SRomain Jobredeaux    Internal; only other `Expect` methods should be calling this.
59*d6050574SRomain Jobredeaux
60*d6050574SRomain Jobredeaux    Args:
61*d6050574SRomain Jobredeaux        env: unittest env struct or some approximation.
62*d6050574SRomain Jobredeaux        meta: ([`ExpectMeta`]) metadata about call chain and state.
63*d6050574SRomain Jobredeaux
64*d6050574SRomain Jobredeaux    Returns:
65*d6050574SRomain Jobredeaux        [`Expect`] object
66*d6050574SRomain Jobredeaux    """
67*d6050574SRomain Jobredeaux
68*d6050574SRomain Jobredeaux    meta = meta or ExpectMeta.new(env)
69*d6050574SRomain Jobredeaux
70*d6050574SRomain Jobredeaux    # buildifier: disable=uninitialized
71*d6050574SRomain Jobredeaux    public = struct(
72*d6050574SRomain Jobredeaux        # keep sorted start
73*d6050574SRomain Jobredeaux        meta = meta,
74*d6050574SRomain Jobredeaux        that_action = lambda *a, **k: _expect_that_action(self, *a, **k),
75*d6050574SRomain Jobredeaux        that_bool = lambda *a, **k: _expect_that_bool(self, *a, **k),
76*d6050574SRomain Jobredeaux        that_collection = lambda *a, **k: _expect_that_collection(self, *a, **k),
77*d6050574SRomain Jobredeaux        that_depset_of_files = lambda *a, **k: _expect_that_depset_of_files(self, *a, **k),
78*d6050574SRomain Jobredeaux        that_dict = lambda *a, **k: _expect_that_dict(self, *a, **k),
79*d6050574SRomain Jobredeaux        that_file = lambda *a, **k: _expect_that_file(self, *a, **k),
80*d6050574SRomain Jobredeaux        that_int = lambda *a, **k: _expect_that_int(self, *a, **k),
81*d6050574SRomain Jobredeaux        that_str = lambda *a, **k: _expect_that_str(self, *a, **k),
82*d6050574SRomain Jobredeaux        that_struct = lambda *a, **k: _expect_that_struct(self, *a, **k),
83*d6050574SRomain Jobredeaux        that_target = lambda *a, **k: _expect_that_target(self, *a, **k),
84*d6050574SRomain Jobredeaux        where = lambda *a, **k: _expect_where(self, *a, **k),
85*d6050574SRomain Jobredeaux        # keep sorted end
86*d6050574SRomain Jobredeaux        # Attributes used by Subject classes and internal helpers
87*d6050574SRomain Jobredeaux    )
88*d6050574SRomain Jobredeaux    self = struct(env = env, public = public, meta = meta)
89*d6050574SRomain Jobredeaux    return public
90*d6050574SRomain Jobredeaux
91*d6050574SRomain Jobredeauxdef _expect_that_action(self, action):
92*d6050574SRomain Jobredeaux    """Creates a subject for asserting Actions.
93*d6050574SRomain Jobredeaux
94*d6050574SRomain Jobredeaux    Args:
95*d6050574SRomain Jobredeaux        self: implicitly added.
96*d6050574SRomain Jobredeaux        action: ([`Action`]) the action to check.
97*d6050574SRomain Jobredeaux
98*d6050574SRomain Jobredeaux    Returns:
99*d6050574SRomain Jobredeaux        [`ActionSubject`] object.
100*d6050574SRomain Jobredeaux    """
101*d6050574SRomain Jobredeaux    return ActionSubject.new(
102*d6050574SRomain Jobredeaux        action,
103*d6050574SRomain Jobredeaux        self.meta.derive(
104*d6050574SRomain Jobredeaux            expr = "action",
105*d6050574SRomain Jobredeaux            details = ["action: [{}] {}".format(action.mnemonic, action)],
106*d6050574SRomain Jobredeaux        ),
107*d6050574SRomain Jobredeaux    )
108*d6050574SRomain Jobredeaux
109*d6050574SRomain Jobredeauxdef _expect_that_bool(self, value, expr = "boolean"):
110*d6050574SRomain Jobredeaux    """Creates a subject for asserting a boolean.
111*d6050574SRomain Jobredeaux
112*d6050574SRomain Jobredeaux    Args:
113*d6050574SRomain Jobredeaux        self: implicitly added.
114*d6050574SRomain Jobredeaux        value: ([`bool`]) the bool to check.
115*d6050574SRomain Jobredeaux        expr: ([`str`]) the starting "value of" expression to report in errors.
116*d6050574SRomain Jobredeaux
117*d6050574SRomain Jobredeaux    Returns:
118*d6050574SRomain Jobredeaux        [`BoolSubject`] object.
119*d6050574SRomain Jobredeaux    """
120*d6050574SRomain Jobredeaux    return BoolSubject.new(
121*d6050574SRomain Jobredeaux        value,
122*d6050574SRomain Jobredeaux        meta = self.meta.derive(expr = expr),
123*d6050574SRomain Jobredeaux    )
124*d6050574SRomain Jobredeaux
125*d6050574SRomain Jobredeauxdef _expect_that_collection(self, collection, expr = "collection", **kwargs):
126*d6050574SRomain Jobredeaux    """Creates a subject for asserting collections.
127*d6050574SRomain Jobredeaux
128*d6050574SRomain Jobredeaux    Args:
129*d6050574SRomain Jobredeaux        self: implicitly added.
130*d6050574SRomain Jobredeaux        collection: The collection (list or depset) to assert.
131*d6050574SRomain Jobredeaux        expr: ([`str`]) the starting "value of" expression to report in errors.
132*d6050574SRomain Jobredeaux        **kwargs: Additional kwargs to pass onto CollectionSubject.new
133*d6050574SRomain Jobredeaux
134*d6050574SRomain Jobredeaux    Returns:
135*d6050574SRomain Jobredeaux        [`CollectionSubject`] object.
136*d6050574SRomain Jobredeaux    """
137*d6050574SRomain Jobredeaux    return CollectionSubject.new(collection, self.meta.derive(expr), **kwargs)
138*d6050574SRomain Jobredeaux
139*d6050574SRomain Jobredeauxdef _expect_that_depset_of_files(self, depset_files):
140*d6050574SRomain Jobredeaux    """Creates a subject for asserting a depset of files.
141*d6050574SRomain Jobredeaux
142*d6050574SRomain Jobredeaux    Method: Expect.that_depset_of_files
143*d6050574SRomain Jobredeaux
144*d6050574SRomain Jobredeaux    Args:
145*d6050574SRomain Jobredeaux        self: implicitly added.
146*d6050574SRomain Jobredeaux        depset_files: ([`depset`] of [`File`]) the values to assert on.
147*d6050574SRomain Jobredeaux
148*d6050574SRomain Jobredeaux    Returns:
149*d6050574SRomain Jobredeaux        [`DepsetFileSubject`] object.
150*d6050574SRomain Jobredeaux    """
151*d6050574SRomain Jobredeaux    return DepsetFileSubject.new(depset_files, self.meta.derive("depset_files"))
152*d6050574SRomain Jobredeaux
153*d6050574SRomain Jobredeauxdef _expect_that_dict(self, mapping, meta = None):
154*d6050574SRomain Jobredeaux    """Creates a subject for asserting a dict.
155*d6050574SRomain Jobredeaux
156*d6050574SRomain Jobredeaux    Method: Expect.that_dict
157*d6050574SRomain Jobredeaux
158*d6050574SRomain Jobredeaux    Args:
159*d6050574SRomain Jobredeaux        self: implicitly added
160*d6050574SRomain Jobredeaux        mapping: ([`dict`]) the values to assert on
161*d6050574SRomain Jobredeaux        meta: ([`ExpectMeta`]) optional custom call chain information to use instead
162*d6050574SRomain Jobredeaux
163*d6050574SRomain Jobredeaux    Returns:
164*d6050574SRomain Jobredeaux        [`DictSubject`] object.
165*d6050574SRomain Jobredeaux    """
166*d6050574SRomain Jobredeaux    meta = meta or self.meta.derive("dict")
167*d6050574SRomain Jobredeaux    return DictSubject.new(mapping, meta = meta)
168*d6050574SRomain Jobredeaux
169*d6050574SRomain Jobredeauxdef _expect_that_file(self, file, meta = None):
170*d6050574SRomain Jobredeaux    """Creates a subject for asserting a file.
171*d6050574SRomain Jobredeaux
172*d6050574SRomain Jobredeaux    Method: Expect.that_file
173*d6050574SRomain Jobredeaux
174*d6050574SRomain Jobredeaux    Args:
175*d6050574SRomain Jobredeaux        self: implicitly added.
176*d6050574SRomain Jobredeaux        file: ([`File`]) the value to assert.
177*d6050574SRomain Jobredeaux        meta: ([`ExpectMeta`]) optional custom call chain information to use instead
178*d6050574SRomain Jobredeaux
179*d6050574SRomain Jobredeaux    Returns:
180*d6050574SRomain Jobredeaux        [`FileSubject`] object.
181*d6050574SRomain Jobredeaux    """
182*d6050574SRomain Jobredeaux    meta = meta or self.meta.derive("file")
183*d6050574SRomain Jobredeaux    return FileSubject.new(file, meta = meta)
184*d6050574SRomain Jobredeaux
185*d6050574SRomain Jobredeauxdef _expect_that_int(self, value, expr = "integer"):
186*d6050574SRomain Jobredeaux    """Creates a subject for asserting an `int`.
187*d6050574SRomain Jobredeaux
188*d6050574SRomain Jobredeaux    Method: Expect.that_int
189*d6050574SRomain Jobredeaux
190*d6050574SRomain Jobredeaux    Args:
191*d6050574SRomain Jobredeaux        self: implicitly added.
192*d6050574SRomain Jobredeaux        value: ([`int`]) the value to check against.
193*d6050574SRomain Jobredeaux        expr: ([`str`]) the starting "value of" expression to report in errors.
194*d6050574SRomain Jobredeaux
195*d6050574SRomain Jobredeaux    Returns:
196*d6050574SRomain Jobredeaux        [`IntSubject`] object.
197*d6050574SRomain Jobredeaux    """
198*d6050574SRomain Jobredeaux    return IntSubject.new(value, self.meta.derive(expr))
199*d6050574SRomain Jobredeaux
200*d6050574SRomain Jobredeauxdef _expect_that_str(self, value):
201*d6050574SRomain Jobredeaux    """Creates a subject for asserting a `str`.
202*d6050574SRomain Jobredeaux
203*d6050574SRomain Jobredeaux    Args:
204*d6050574SRomain Jobredeaux        self: implicitly added.
205*d6050574SRomain Jobredeaux        value: ([`str`]) the value to check against.
206*d6050574SRomain Jobredeaux
207*d6050574SRomain Jobredeaux    Returns:
208*d6050574SRomain Jobredeaux        [`StrSubject`] object.
209*d6050574SRomain Jobredeaux    """
210*d6050574SRomain Jobredeaux    return StrSubject.new(value, self.meta.derive("string"))
211*d6050574SRomain Jobredeaux
212*d6050574SRomain Jobredeauxdef _expect_that_struct(self, value):
213*d6050574SRomain Jobredeaux    """Creates a subject for asserting a `struct`.
214*d6050574SRomain Jobredeaux
215*d6050574SRomain Jobredeaux    Args:
216*d6050574SRomain Jobredeaux        self: implicitly added.
217*d6050574SRomain Jobredeaux        value: ([`struct`]) the value to check against.
218*d6050574SRomain Jobredeaux
219*d6050574SRomain Jobredeaux    Returns:
220*d6050574SRomain Jobredeaux        [`StructSubject`] object.
221*d6050574SRomain Jobredeaux    """
222*d6050574SRomain Jobredeaux    return StructSubject.new(value, self.meta.derive("string"))
223*d6050574SRomain Jobredeaux
224*d6050574SRomain Jobredeauxdef _expect_that_target(self, target):
225*d6050574SRomain Jobredeaux    """Creates a subject for asserting a `Target`.
226*d6050574SRomain Jobredeaux
227*d6050574SRomain Jobredeaux    This adds the following parameters to `ExpectMeta.format_str`:
228*d6050574SRomain Jobredeaux      {package}: The target's package, e.g. "foo/bar" from "//foo/bar:baz"
229*d6050574SRomain Jobredeaux      {name}: The target's base name, e.g., "baz" from "//foo/bar:baz"
230*d6050574SRomain Jobredeaux
231*d6050574SRomain Jobredeaux    Args:
232*d6050574SRomain Jobredeaux        self: implicitly added.
233*d6050574SRomain Jobredeaux        target: ([`Target`]) subject target to check against.
234*d6050574SRomain Jobredeaux
235*d6050574SRomain Jobredeaux    Returns:
236*d6050574SRomain Jobredeaux        [`TargetSubject`] object.
237*d6050574SRomain Jobredeaux    """
238*d6050574SRomain Jobredeaux    return TargetSubject.new(target, self.meta.derive(
239*d6050574SRomain Jobredeaux        expr = "target({})".format(target.label),
240*d6050574SRomain Jobredeaux        details = ["target: {}".format(target.label)],
241*d6050574SRomain Jobredeaux        format_str_kwargs = {
242*d6050574SRomain Jobredeaux            "name": target.label.name,
243*d6050574SRomain Jobredeaux            "package": target.label.package,
244*d6050574SRomain Jobredeaux        },
245*d6050574SRomain Jobredeaux    ))
246*d6050574SRomain Jobredeaux
247*d6050574SRomain Jobredeauxdef _expect_where(self, **details):
248*d6050574SRomain Jobredeaux    """Add additional information about the assertion.
249*d6050574SRomain Jobredeaux
250*d6050574SRomain Jobredeaux    This is useful for attaching information that isn't part of the call
251*d6050574SRomain Jobredeaux    chain or some reason. Example usage:
252*d6050574SRomain Jobredeaux
253*d6050574SRomain Jobredeaux        expect(env).where(platform=ctx.attr.platform).that_str(...)
254*d6050574SRomain Jobredeaux
255*d6050574SRomain Jobredeaux    Would include "platform: {ctx.attr.platform}" in failure messages.
256*d6050574SRomain Jobredeaux
257*d6050574SRomain Jobredeaux    Args:
258*d6050574SRomain Jobredeaux        self: implicitly added.
259*d6050574SRomain Jobredeaux        **details: ([`dict`] of [`str`] to value) Each named arg is added to
260*d6050574SRomain Jobredeaux            the metadata details with the provided string, which is printed as
261*d6050574SRomain Jobredeaux            part of displaying any failures.
262*d6050574SRomain Jobredeaux
263*d6050574SRomain Jobredeaux    Returns:
264*d6050574SRomain Jobredeaux        [`Expect`] object with separate metadata derived from the original self.
265*d6050574SRomain Jobredeaux    """
266*d6050574SRomain Jobredeaux    meta = self.meta.derive(
267*d6050574SRomain Jobredeaux        details = ["{}: {}".format(k, v) for k, v in details.items()],
268*d6050574SRomain Jobredeaux    )
269*d6050574SRomain Jobredeaux    return _expect_new(env = self.env, meta = meta)
270*d6050574SRomain Jobredeaux
271*d6050574SRomain Jobredeaux# We use this name so it shows up nice in docs.
272*d6050574SRomain Jobredeaux# buildifier: disable=name-conventions
273*d6050574SRomain JobredeauxExpect = struct(
274*d6050574SRomain Jobredeaux    # keep sorted start
275*d6050574SRomain Jobredeaux    new_from_env = _expect_new_from_env,
276*d6050574SRomain Jobredeaux    new = _expect_new,
277*d6050574SRomain Jobredeaux    that_action = _expect_that_action,
278*d6050574SRomain Jobredeaux    that_bool = _expect_that_bool,
279*d6050574SRomain Jobredeaux    that_collection = _expect_that_collection,
280*d6050574SRomain Jobredeaux    that_depset_of_files = _expect_that_depset_of_files,
281*d6050574SRomain Jobredeaux    that_dict = _expect_that_dict,
282*d6050574SRomain Jobredeaux    that_file = _expect_that_file,
283*d6050574SRomain Jobredeaux    that_int = _expect_that_int,
284*d6050574SRomain Jobredeaux    that_str = _expect_that_str,
285*d6050574SRomain Jobredeaux    that_struct = _expect_that_struct,
286*d6050574SRomain Jobredeaux    that_target = _expect_that_target,
287*d6050574SRomain Jobredeaux    where = _expect_where,
288*d6050574SRomain Jobredeaux    # keep sorted end
289*d6050574SRomain Jobredeaux)
290