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