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