xref: /aosp_15_r20/external/bazelbuild-rules_testing/lib/private/check_util.bzl (revision d605057434dcabba796c020773aab68d9790ff9f)
1*d6050574SRomain Jobredeaux"""Helper functions to perform checks."""
2*d6050574SRomain Jobredeaux
3*d6050574SRomain Jobredeauxload("@bazel_skylib//lib:types.bzl", "types")
4*d6050574SRomain Jobredeauxload(":compare_util.bzl", "MatchResult", "compare_contains_exactly_predicates")
5*d6050574SRomain Jobredeauxload(":failure_messages.bzl", "format_failure_unexpected_values")
6*d6050574SRomain Jobredeauxload(":matching.bzl", "matching")
7*d6050574SRomain Jobredeauxload(":ordered.bzl", "IN_ORDER", "OrderedIncorrectly")
8*d6050574SRomain Jobredeauxload(":truth_common.bzl", "enumerate_list_as_lines", "maybe_sorted", "to_list")
9*d6050574SRomain Jobredeaux
10*d6050574SRomain Jobredeauxdef check_contains_exactly(
11*d6050574SRomain Jobredeaux        *,
12*d6050574SRomain Jobredeaux        expect_contains,
13*d6050574SRomain Jobredeaux        actual_container,
14*d6050574SRomain Jobredeaux        format_actual,
15*d6050574SRomain Jobredeaux        format_expected,
16*d6050574SRomain Jobredeaux        format_missing,
17*d6050574SRomain Jobredeaux        format_unexpected,
18*d6050574SRomain Jobredeaux        format_out_of_order,
19*d6050574SRomain Jobredeaux        meta):
20*d6050574SRomain Jobredeaux    """Check that a collection contains exactly the given values and no more.
21*d6050574SRomain Jobredeaux
22*d6050574SRomain Jobredeaux    This checks that the collection contains exactly the given values. Extra
23*d6050574SRomain Jobredeaux    values are not allowed. Multiplicity of the expected values is respected.
24*d6050574SRomain Jobredeaux    Ordering is not checked; call `in_order()` to also check the order
25*d6050574SRomain Jobredeaux    of the actual values matches the order of the expected values.
26*d6050574SRomain Jobredeaux
27*d6050574SRomain Jobredeaux    Args:
28*d6050574SRomain Jobredeaux        expect_contains: the values that must exist (and no more).
29*d6050574SRomain Jobredeaux        actual_container: the values to check within.
30*d6050574SRomain Jobredeaux        format_actual: (callable) accepts no args and returns [`str`] (the
31*d6050574SRomain Jobredeaux            description of the actual values).
32*d6050574SRomain Jobredeaux        format_expected: (callable) accepts no args and returns [`str`] (
33*d6050574SRomain Jobredeaux            description of the expected values).
34*d6050574SRomain Jobredeaux        format_missing: (callable) accepts 1 position arg (list of values from
35*d6050574SRomain Jobredeaux            `expect_contains` that were missing), and returns [`str`] (description of
36*d6050574SRomain Jobredeaux            the missing values).
37*d6050574SRomain Jobredeaux        format_unexpected: (callable) accepts 1 positional arg (list of values from
38*d6050574SRomain Jobredeaux           `actual_container` that weren't expected), and returns [`str`] (description of
39*d6050574SRomain Jobredeaux           the unexpected values).
40*d6050574SRomain Jobredeaux        format_out_of_order: (callable) accepts 1 arg (a list of "MatchResult"
41*d6050574SRomain Jobredeaux            structs, see above) and returns a string (the problem message
42*d6050574SRomain Jobredeaux            reported on failure). The order of match results is the expected
43*d6050574SRomain Jobredeaux            order.
44*d6050574SRomain Jobredeaux        meta: ([`ExpectMeta`]) to record failures.
45*d6050574SRomain Jobredeaux
46*d6050574SRomain Jobredeaux    Returns:
47*d6050574SRomain Jobredeaux        [`Ordered`] object.
48*d6050574SRomain Jobredeaux    """
49*d6050574SRomain Jobredeaux    result = compare_contains_exactly_predicates(
50*d6050574SRomain Jobredeaux        expect_contains = [
51*d6050574SRomain Jobredeaux            matching.equals_wrapper(raw_expected)
52*d6050574SRomain Jobredeaux            for raw_expected in expect_contains
53*d6050574SRomain Jobredeaux        ],
54*d6050574SRomain Jobredeaux        actual_container = actual_container,
55*d6050574SRomain Jobredeaux    )
56*d6050574SRomain Jobredeaux    if not result.contains_exactly:
57*d6050574SRomain Jobredeaux        problems = []
58*d6050574SRomain Jobredeaux        if result.missing:
59*d6050574SRomain Jobredeaux            problems.append(format_missing([m.desc for m in result.missing]))
60*d6050574SRomain Jobredeaux        if result.unexpected:
61*d6050574SRomain Jobredeaux            problems.append(format_unexpected(result.unexpected))
62*d6050574SRomain Jobredeaux        problems.append(format_expected())
63*d6050574SRomain Jobredeaux
64*d6050574SRomain Jobredeaux        meta.add_failure("\n".join(problems), format_actual())
65*d6050574SRomain Jobredeaux
66*d6050574SRomain Jobredeaux        # We already recorded an error, so just pretend order is correct to
67*d6050574SRomain Jobredeaux        # avoid spamming another error.
68*d6050574SRomain Jobredeaux        return IN_ORDER
69*d6050574SRomain Jobredeaux    elif result.is_in_order:
70*d6050574SRomain Jobredeaux        return IN_ORDER
71*d6050574SRomain Jobredeaux    else:
72*d6050574SRomain Jobredeaux        return OrderedIncorrectly.new(
73*d6050574SRomain Jobredeaux            format_problem = lambda: format_out_of_order(result.matches),
74*d6050574SRomain Jobredeaux            format_actual = format_actual,
75*d6050574SRomain Jobredeaux            meta = meta,
76*d6050574SRomain Jobredeaux        )
77*d6050574SRomain Jobredeaux
78*d6050574SRomain Jobredeauxdef check_contains_exactly_predicates(
79*d6050574SRomain Jobredeaux        *,
80*d6050574SRomain Jobredeaux        expect_contains,
81*d6050574SRomain Jobredeaux        actual_container,
82*d6050574SRomain Jobredeaux        format_actual,
83*d6050574SRomain Jobredeaux        format_expected,
84*d6050574SRomain Jobredeaux        format_missing,
85*d6050574SRomain Jobredeaux        format_unexpected,
86*d6050574SRomain Jobredeaux        format_out_of_order,
87*d6050574SRomain Jobredeaux        meta):
88*d6050574SRomain Jobredeaux    """Check that a collection contains values matching the given predicates and no more.
89*d6050574SRomain Jobredeaux
90*d6050574SRomain Jobredeaux    todo doc to describe behavior
91*d6050574SRomain Jobredeaux    This checks that the collection contains values that match the given exactly the given values.
92*d6050574SRomain Jobredeaux    Extra values that do not match a predicate are not allowed. Multiplicity of
93*d6050574SRomain Jobredeaux    the expected predicates is respected. Ordering is not checked; call
94*d6050574SRomain Jobredeaux    `in_order()` to also check the order of the actual values matches the order
95*d6050574SRomain Jobredeaux    of the expected predicates.
96*d6050574SRomain Jobredeaux
97*d6050574SRomain Jobredeaux    Args:
98*d6050574SRomain Jobredeaux        expect_contains: the predicates that must match (and no more).
99*d6050574SRomain Jobredeaux        actual_container: the values to check within.
100*d6050574SRomain Jobredeaux        format_actual: (callable) accepts no args and returns [`str`] (the
101*d6050574SRomain Jobredeaux            description of the actual values).
102*d6050574SRomain Jobredeaux        format_expected: (callable) accepts no args and returns [`str`] (
103*d6050574SRomain Jobredeaux            description of the expected values).
104*d6050574SRomain Jobredeaux        format_missing: (callable) accepts 1 position arg (list of values from
105*d6050574SRomain Jobredeaux            `expect_contains` that were missing), and returns [`str`] (description of
106*d6050574SRomain Jobredeaux            the missing values).
107*d6050574SRomain Jobredeaux        format_unexpected: (callable) accepts 1 positional arg (list of values from
108*d6050574SRomain Jobredeaux           `actual_container` that weren't expected), and returns [`str`] (description of
109*d6050574SRomain Jobredeaux           the unexpected values).
110*d6050574SRomain Jobredeaux        format_out_of_order: (callable) accepts 1 arg (a list of "MatchResult"
111*d6050574SRomain Jobredeaux            structs, see above) and returns a string (the problem message
112*d6050574SRomain Jobredeaux            reported on failure). The order of match results is the expected
113*d6050574SRomain Jobredeaux            order.
114*d6050574SRomain Jobredeaux        meta: ([`ExpectMeta`]) to record failures.
115*d6050574SRomain Jobredeaux
116*d6050574SRomain Jobredeaux    Returns:
117*d6050574SRomain Jobredeaux        [`Ordered`] object.
118*d6050574SRomain Jobredeaux    """
119*d6050574SRomain Jobredeaux    result = compare_contains_exactly_predicates(
120*d6050574SRomain Jobredeaux        expect_contains = expect_contains,
121*d6050574SRomain Jobredeaux        actual_container = actual_container,
122*d6050574SRomain Jobredeaux    )
123*d6050574SRomain Jobredeaux    if not result.contains_exactly:
124*d6050574SRomain Jobredeaux        problems = []
125*d6050574SRomain Jobredeaux        if result.missing:
126*d6050574SRomain Jobredeaux            problems.append(format_missing(result.missing))
127*d6050574SRomain Jobredeaux        if result.unexpected:
128*d6050574SRomain Jobredeaux            problems.append(format_unexpected(result.unexpected))
129*d6050574SRomain Jobredeaux        problems.append(format_expected())
130*d6050574SRomain Jobredeaux
131*d6050574SRomain Jobredeaux        meta.add_failure("\n".join(problems), format_actual())
132*d6050574SRomain Jobredeaux
133*d6050574SRomain Jobredeaux        # We already recorded an error, so just pretend order is correct to
134*d6050574SRomain Jobredeaux        # avoid spamming another error.
135*d6050574SRomain Jobredeaux        return IN_ORDER
136*d6050574SRomain Jobredeaux    elif result.is_in_order:
137*d6050574SRomain Jobredeaux        return IN_ORDER
138*d6050574SRomain Jobredeaux    else:
139*d6050574SRomain Jobredeaux        return OrderedIncorrectly.new(
140*d6050574SRomain Jobredeaux            format_problem = lambda: format_out_of_order(result.matches),
141*d6050574SRomain Jobredeaux            format_actual = format_actual,
142*d6050574SRomain Jobredeaux            meta = meta,
143*d6050574SRomain Jobredeaux        )
144*d6050574SRomain Jobredeaux
145*d6050574SRomain Jobredeauxdef check_contains_predicate(collection, matcher, *, format_problem, format_actual, meta):
146*d6050574SRomain Jobredeaux    """Check that `matcher` matches any value in `collection`, and record an error if not.
147*d6050574SRomain Jobredeaux
148*d6050574SRomain Jobredeaux    Args:
149*d6050574SRomain Jobredeaux        collection: ([`collection`]) the collection whose values are compared against.
150*d6050574SRomain Jobredeaux        matcher: ([`Matcher`]) that must match.
151*d6050574SRomain Jobredeaux        format_problem: ([`str`] |  callable) If a string, then the problem message
152*d6050574SRomain Jobredeaux            to use when failing. If a callable, a no-arg callable that returns
153*d6050574SRomain Jobredeaux            the problem string; see `_format_problem_*` for existing helpers.
154*d6050574SRomain Jobredeaux        format_actual: ([`str`] |  callable) If a string, then the actual message
155*d6050574SRomain Jobredeaux            to use when failing. If a callable, a no-arg callable that returns
156*d6050574SRomain Jobredeaux            the actual string; see `_format_actual_*` for existing helpers.
157*d6050574SRomain Jobredeaux        meta: ([`ExpectMeta`]) to record failures
158*d6050574SRomain Jobredeaux    """
159*d6050574SRomain Jobredeaux    for value in collection:
160*d6050574SRomain Jobredeaux        if matcher.match(value):
161*d6050574SRomain Jobredeaux            return
162*d6050574SRomain Jobredeaux    meta.add_failure(
163*d6050574SRomain Jobredeaux        format_problem if types.is_string(format_problem) else format_problem(),
164*d6050574SRomain Jobredeaux        format_actual if types.is_string(format_actual) else format_actual(),
165*d6050574SRomain Jobredeaux    )
166*d6050574SRomain Jobredeaux
167*d6050574SRomain Jobredeauxdef check_contains_at_least_predicates(
168*d6050574SRomain Jobredeaux        collection,
169*d6050574SRomain Jobredeaux        matchers,
170*d6050574SRomain Jobredeaux        *,
171*d6050574SRomain Jobredeaux        format_missing,
172*d6050574SRomain Jobredeaux        format_out_of_order,
173*d6050574SRomain Jobredeaux        format_actual,
174*d6050574SRomain Jobredeaux        meta):
175*d6050574SRomain Jobredeaux    """Check that the collection is a subset of the predicates.
176*d6050574SRomain Jobredeaux
177*d6050574SRomain Jobredeaux    The collection must match all the predicates. It can contain extra elements.
178*d6050574SRomain Jobredeaux    The multiplicity of matchers is respected. Checking that the relative order
179*d6050574SRomain Jobredeaux    of matches is the same as the passed-in matchers order can done by calling
180*d6050574SRomain Jobredeaux    `in_order()`.
181*d6050574SRomain Jobredeaux
182*d6050574SRomain Jobredeaux    Args:
183*d6050574SRomain Jobredeaux        collection: [`collection`] of values to check within.
184*d6050574SRomain Jobredeaux        matchers: [`collection`] of [`Matcher`] objects to match (see `matchers` struct)
185*d6050574SRomain Jobredeaux        format_missing: (callable) accepts 1 positional arg (a list of the
186*d6050574SRomain Jobredeaux            `matchers` that did not match) and returns a string (the problem
187*d6050574SRomain Jobredeaux            message reported on failure).
188*d6050574SRomain Jobredeaux        format_out_of_order: (callable) accepts 1 arg (a list of `MatchResult`s)
189*d6050574SRomain Jobredeaux            and returns a string (the problem message reported on failure). The
190*d6050574SRomain Jobredeaux            order of match results is the expected order.
191*d6050574SRomain Jobredeaux        format_actual: callable: accepts no args and returns a string (the
192*d6050574SRomain Jobredeaux            text describing the actual value reported on failure).
193*d6050574SRomain Jobredeaux        meta: ([`ExpectMeta`]) used for reporting errors.
194*d6050574SRomain Jobredeaux
195*d6050574SRomain Jobredeaux    Returns:
196*d6050574SRomain Jobredeaux        [`Ordered`] object to allow checking the order of matches.
197*d6050574SRomain Jobredeaux    """
198*d6050574SRomain Jobredeaux
199*d6050574SRomain Jobredeaux    # We'll later update this list in-place with results. We keep the order
200*d6050574SRomain Jobredeaux    # so that, on failure, the formatters receive the expected order of matches.
201*d6050574SRomain Jobredeaux    matches = [None for _ in matchers]
202*d6050574SRomain Jobredeaux
203*d6050574SRomain Jobredeaux    # A list of (original position, matcher) tuples. This allows
204*d6050574SRomain Jobredeaux    # mapping a matcher back to its original order and respecting
205*d6050574SRomain Jobredeaux    # the multiplicity of matchers.
206*d6050574SRomain Jobredeaux    remaining_matchers = enumerate(matchers)
207*d6050574SRomain Jobredeaux    ordered = True
208*d6050574SRomain Jobredeaux    for absolute_pos, value in enumerate(collection):
209*d6050574SRomain Jobredeaux        if not remaining_matchers:
210*d6050574SRomain Jobredeaux            break
211*d6050574SRomain Jobredeaux        found_i = -1
212*d6050574SRomain Jobredeaux        for cur_i, (_, matcher) in enumerate(remaining_matchers):
213*d6050574SRomain Jobredeaux            if matcher.match(value):
214*d6050574SRomain Jobredeaux                found_i = cur_i
215*d6050574SRomain Jobredeaux                break
216*d6050574SRomain Jobredeaux        if found_i > -1:
217*d6050574SRomain Jobredeaux            ordered = ordered and (found_i == 0)
218*d6050574SRomain Jobredeaux            orig_matcher_pos, matcher = remaining_matchers.pop(found_i)
219*d6050574SRomain Jobredeaux            matches[orig_matcher_pos] = MatchResult.new(
220*d6050574SRomain Jobredeaux                matched_value = value,
221*d6050574SRomain Jobredeaux                found_at = absolute_pos,
222*d6050574SRomain Jobredeaux                matcher = matcher,
223*d6050574SRomain Jobredeaux            )
224*d6050574SRomain Jobredeaux
225*d6050574SRomain Jobredeaux    if remaining_matchers:
226*d6050574SRomain Jobredeaux        meta.add_failure(
227*d6050574SRomain Jobredeaux            format_missing([v[1] for v in remaining_matchers]),
228*d6050574SRomain Jobredeaux            format_actual if types.is_string(format_actual) else format_actual(),
229*d6050574SRomain Jobredeaux        )
230*d6050574SRomain Jobredeaux
231*d6050574SRomain Jobredeaux        # We've added a failure, so no need to spam another error message, so
232*d6050574SRomain Jobredeaux        # just pretend things are in order.
233*d6050574SRomain Jobredeaux        return IN_ORDER
234*d6050574SRomain Jobredeaux    elif ordered:
235*d6050574SRomain Jobredeaux        return IN_ORDER
236*d6050574SRomain Jobredeaux    else:
237*d6050574SRomain Jobredeaux        return OrderedIncorrectly.new(
238*d6050574SRomain Jobredeaux            format_problem = lambda: format_out_of_order(matches),
239*d6050574SRomain Jobredeaux            format_actual = format_actual,
240*d6050574SRomain Jobredeaux            meta = meta,
241*d6050574SRomain Jobredeaux        )
242*d6050574SRomain Jobredeaux
243*d6050574SRomain Jobredeauxdef check_contains_none_of(*, collection, none_of, meta, sort = True):
244*d6050574SRomain Jobredeaux    """Check that a collection does not have any of the `none_of` values.
245*d6050574SRomain Jobredeaux
246*d6050574SRomain Jobredeaux    Args:
247*d6050574SRomain Jobredeaux        collection: ([`collection`]) the values to check within.
248*d6050574SRomain Jobredeaux        none_of: the values that should not exist.
249*d6050574SRomain Jobredeaux        meta: ([`ExpectMeta`]) to record failures.
250*d6050574SRomain Jobredeaux        sort: ([`bool`]) If true, sort the values for display.
251*d6050574SRomain Jobredeaux    """
252*d6050574SRomain Jobredeaux    unexpected = []
253*d6050574SRomain Jobredeaux    for value in none_of:
254*d6050574SRomain Jobredeaux        if value in collection:
255*d6050574SRomain Jobredeaux            unexpected.append(value)
256*d6050574SRomain Jobredeaux    if not unexpected:
257*d6050574SRomain Jobredeaux        return
258*d6050574SRomain Jobredeaux
259*d6050574SRomain Jobredeaux    unexpected = maybe_sorted(unexpected, sort)
260*d6050574SRomain Jobredeaux    problem, actual = format_failure_unexpected_values(
261*d6050574SRomain Jobredeaux        none_of = "\n" + enumerate_list_as_lines(unexpected, prefix = "  "),
262*d6050574SRomain Jobredeaux        unexpected = unexpected,
263*d6050574SRomain Jobredeaux        actual = collection,
264*d6050574SRomain Jobredeaux        sort = sort,
265*d6050574SRomain Jobredeaux    )
266*d6050574SRomain Jobredeaux    meta.add_failure(problem, actual)
267*d6050574SRomain Jobredeaux
268*d6050574SRomain Jobredeauxdef check_not_contains_predicate(collection, matcher, *, meta, sort = True):
269*d6050574SRomain Jobredeaux    """Check that `matcher` matches no values in `collection`.
270*d6050574SRomain Jobredeaux
271*d6050574SRomain Jobredeaux    Args:
272*d6050574SRomain Jobredeaux        collection: ([`collection`]) the collection whose values are compared against.
273*d6050574SRomain Jobredeaux        matcher: ([`Matcher`]) that must not match.
274*d6050574SRomain Jobredeaux        meta: ([`ExpectMeta`]) to record failures
275*d6050574SRomain Jobredeaux        sort: ([`bool`]) If `True`, the collection will be sorted for display.
276*d6050574SRomain Jobredeaux    """
277*d6050574SRomain Jobredeaux    matches = maybe_sorted([v for v in collection if matcher.match(v)], sort)
278*d6050574SRomain Jobredeaux    if not matches:
279*d6050574SRomain Jobredeaux        return
280*d6050574SRomain Jobredeaux    problem, actual = format_failure_unexpected_values(
281*d6050574SRomain Jobredeaux        none_of = matcher.desc,
282*d6050574SRomain Jobredeaux        unexpected = matches,
283*d6050574SRomain Jobredeaux        actual = collection,
284*d6050574SRomain Jobredeaux        sort = sort,
285*d6050574SRomain Jobredeaux    )
286*d6050574SRomain Jobredeaux    meta.add_failure(problem, actual)
287*d6050574SRomain Jobredeaux
288*d6050574SRomain Jobredeauxdef common_subject_is_in(self, any_of):
289*d6050574SRomain Jobredeaux    """Generic implementation of `Subject.is_in`
290*d6050574SRomain Jobredeaux
291*d6050574SRomain Jobredeaux    Args:
292*d6050574SRomain Jobredeaux        self: The subject object. It must provide `actual` and `meta`
293*d6050574SRomain Jobredeaux            attributes.
294*d6050574SRomain Jobredeaux        any_of: [`collection`] of values.
295*d6050574SRomain Jobredeaux    """
296*d6050574SRomain Jobredeaux    return _check_is_in(self.actual, to_list(any_of), self.meta)
297*d6050574SRomain Jobredeaux
298*d6050574SRomain Jobredeauxdef _check_is_in(actual, any_of, meta):
299*d6050574SRomain Jobredeaux    """Check that `actual` is one of the values in `any_of`.
300*d6050574SRomain Jobredeaux
301*d6050574SRomain Jobredeaux    Args:
302*d6050574SRomain Jobredeaux        actual: value to check for in `any_of`
303*d6050574SRomain Jobredeaux        any_of: [`collection`] of values to check within.
304*d6050574SRomain Jobredeaux        meta: ([`ExpectMeta`]) to record failures
305*d6050574SRomain Jobredeaux    """
306*d6050574SRomain Jobredeaux    if actual in any_of:
307*d6050574SRomain Jobredeaux        return
308*d6050574SRomain Jobredeaux    meta.add_failure(
309*d6050574SRomain Jobredeaux        "expected any of:\n{}".format(
310*d6050574SRomain Jobredeaux            enumerate_list_as_lines(any_of, prefix = "  "),
311*d6050574SRomain Jobredeaux        ),
312*d6050574SRomain Jobredeaux        "actual: {}".format(actual),
313*d6050574SRomain Jobredeaux    )
314*d6050574SRomain Jobredeaux
315*d6050574SRomain Jobredeauxdef check_not_equals(*, unexpected, actual, meta):
316*d6050574SRomain Jobredeaux    """Check that the values are the same type and not equal (according to !=).
317*d6050574SRomain Jobredeaux
318*d6050574SRomain Jobredeaux    NOTE: This requires the same type for both values. This is to prevent
319*d6050574SRomain Jobredeaux    mistakes where different data types (usually) can never be equal.
320*d6050574SRomain Jobredeaux
321*d6050574SRomain Jobredeaux    Args:
322*d6050574SRomain Jobredeaux        unexpected: (object) the value that actual cannot equal
323*d6050574SRomain Jobredeaux        actual: (object) the observed value
324*d6050574SRomain Jobredeaux        meta: ([`ExpectMeta`]) to record failures
325*d6050574SRomain Jobredeaux    """
326*d6050574SRomain Jobredeaux    same_type = type(actual) == type(unexpected)
327*d6050574SRomain Jobredeaux    equal = not (actual != unexpected)  # Use != to preserve semantics
328*d6050574SRomain Jobredeaux    if same_type and not equal:
329*d6050574SRomain Jobredeaux        return
330*d6050574SRomain Jobredeaux    if not same_type:
331*d6050574SRomain Jobredeaux        meta.add_failure(
332*d6050574SRomain Jobredeaux            "expected not to be: {} (type: {})".format(unexpected, type(unexpected)),
333*d6050574SRomain Jobredeaux            "actual: {} (type: {})".format(actual, type(actual)),
334*d6050574SRomain Jobredeaux        )
335*d6050574SRomain Jobredeaux    else:
336*d6050574SRomain Jobredeaux        meta.add_failure(
337*d6050574SRomain Jobredeaux            "expected not to be: {}".format(unexpected),
338*d6050574SRomain Jobredeaux            "actual: {}".format(actual),
339*d6050574SRomain Jobredeaux        )
340