Skip to main content

fuchsia_bluetooth/
expectation.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use pretty::{BoxAllocator, DocAllocator};
6use std::fmt::{self, Debug, Display, Formatter};
7use std::sync::Arc;
8
9/// Asynchronous extensions to Expectation Predicates
10pub mod asynchronous;
11/// Expectations for the host driver
12pub mod host_driver;
13/// Expectations for remote peers
14pub mod peer;
15/// Tests for the expectation module
16#[cfg(test)]
17pub mod test;
18
19/// A String whose `Debug` implementation pretty-prints. Used when we need to return a string which
20/// we know will be printed through it's 'Debug' implementation, but we want to ensure that it is
21/// not further escaped (for example, because it already is escaped, or because it contains
22/// characters that we do not want to be escaped).
23///
24/// Essentially, formatting a DebugString with '{:?}' formats as if it was '{}'
25#[derive(Clone, PartialEq)]
26pub struct DebugString(String);
27
28// The default formatting width for pretty printing output
29const FMT_CHAR_WIDTH: usize = 100;
30
31impl fmt::Debug for DebugString {
32    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
33        write!(f, "{}", self.0)
34    }
35}
36
37/// Simplified `DocBuilder` alias used in this file
38type DocBuilder<'a> = pretty::DocBuilder<'a, BoxAllocator>;
39
40fn parens<'a>(alloc: &'a BoxAllocator, doc: DocBuilder<'a>) -> DocBuilder<'a> {
41    alloc.text("(").append(doc).append(alloc.text(")"))
42}
43
44/// Functionalized Standard Debug Formatting
45fn show_debug<T: Debug>(t: &T) -> String {
46    format!("{:?}", t)
47}
48
49/// Function to compare two `T`s
50type Comparison<T> = Arc<dyn Fn(&T, &T) -> bool + Send + Sync + 'static>;
51/// Function to show a representation of a given `T`
52type Show<T> = Arc<dyn Fn(&T) -> String + Send + Sync + 'static>;
53
54/// A Boolean predicate on type `T`. Predicate functions are a boolean algebra
55/// just as raw boolean values are; they an be ANDed, ORed, NOTed. This allows
56/// a clear and concise language for declaring test expectations.
57pub enum Predicate<T> {
58    Equal(Arc<T>, Comparison<T>, Show<T>),
59    And(Box<Predicate<T>>, Box<Predicate<T>>),
60    Or(Box<Predicate<T>>, Box<Predicate<T>>),
61    Not(Box<Predicate<T>>),
62    Predicate(Arc<dyn Fn(&T) -> bool + Send + Sync>, String),
63    // Since we can't use an existential:
64    //  for<U> Over(Predicate<U>, Fn(&T) -> &U)
65    // we use the trait `IsOver` to hide the type `U` of the intermediary
66    Over(Arc<dyn IsOver<T> + Send + Sync>),
67    // Since we can't use an existential:
68    //  for<I> Any(Fn(&T) -> I, Predicate<I::Elem>)
69    //  where I::Elem: Debug
70    // we use the trait `IsAny` to hide the type `I` of the iterator
71    Any(Arc<dyn IsAny<T> + Send + Sync + 'static>),
72    // Since we can't use an existential:
73    //  for<I> All(Fn(&T) -> I, Predicate<I::Elem>)
74    //  where I::Elem: Debug
75    // we use the trait `IsAll` to hide the type `I` of the iterator
76    All(Arc<dyn IsAll<T> + Send + Sync + 'static>),
77}
78
79// Bespoke clone implementation. This implementation is equivalent to the one that would be derived
80// by #[derive(Clone)] _except_ for the trait bounds. The derived impl would automatically require
81// `T: Clone`, when actually `Predicate<T>` is `Clone` for _all_ `T`, even those that are not
82// `Clone`.
83impl<T> Clone for Predicate<T> {
84    fn clone(&self) -> Self {
85        match self {
86            Predicate::Equal(t, comp, repr) => {
87                Predicate::Equal(t.clone(), comp.clone(), repr.clone())
88            }
89            Predicate::And(l, r) => Predicate::And(l.clone(), r.clone()),
90            Predicate::Or(l, r) => Predicate::Or(l.clone(), r.clone()),
91            Predicate::Not(x) => Predicate::Not(x.clone()),
92            Predicate::Predicate(p, msg) => Predicate::Predicate(p.clone(), msg.clone()),
93            Predicate::Over(x) => Predicate::Over(x.clone()),
94            Predicate::Any(x) => Predicate::Any(x.clone()),
95            Predicate::All(x) => Predicate::All(x.clone()),
96        }
97    }
98}
99
100impl<T> Debug for Predicate<T> {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        self.describe(&BoxAllocator).1.pretty(FMT_CHAR_WIDTH).fmt(f)
103    }
104}
105
106/// At most how many elements of an iterator to show in a falsification, when falsifying `any` or
107/// `all`
108const MAX_ITER_FALSIFICATIONS: usize = 5;
109
110fn falsify_elem<'p, 't, 'd, T: Debug>(
111    pred: &'p Predicate<T>,
112    index: usize,
113    el: &'t T,
114    doc: &'d BoxAllocator,
115) -> Option<DocBuilder<'d>> {
116    pred.falsify(el, doc).map(move |falsification| {
117        doc.text(format!("[{}]", index))
118            .append(doc.space().append(doc.text(show_debug(el))).nest(2))
119            .append(doc.space().append(doc.text("BECAUSE")))
120            .append(doc.space().append(falsification.group()).nest(2))
121            .append(doc.text(","))
122            .group()
123    })
124}
125
126fn fmt_falsifications<'d>(
127    i: impl Iterator<Item = DocBuilder<'d>>,
128    doc: &'d BoxAllocator,
129) -> Option<DocBuilder<'d>> {
130    i.take(MAX_ITER_FALSIFICATIONS).fold(None, |acc: Option<DocBuilder<'d>>, falsification| {
131        Some(acc.map_or_else(|| doc.nil(), |d| d.append(doc.space())).append(falsification))
132    })
133}
134
135/// Trait representation of a Predicate on members of an existential iterator type
136pub trait IsAny<T> {
137    fn describe<'d>(&self, doc: &'d BoxAllocator) -> DocBuilder<'d>;
138    fn falsify_any<'d>(&self, t: &T, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>>;
139}
140
141impl<T: 'static, Elem: Debug + 'static> IsAny<T> for Predicate<Elem>
142where
143    for<'a> &'a T: IntoIterator<Item = &'a Elem>,
144{
145    fn describe<'d>(&self, doc: &'d BoxAllocator) -> DocBuilder<'d> {
146        doc.text("ANY").append(doc.space().append(self.describe(doc)).nest(2)).group()
147    }
148    fn falsify_any<'d>(&self, t: &T, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>> {
149        if t.into_iter().any(|e| self.satisfied(e)) {
150            None
151        } else {
152            // All are falsifications, so display all (up to MAX_ITER_FALSIFICATIONS)
153            fmt_falsifications(
154                t.into_iter().enumerate().filter_map(|(i, el)| falsify_elem(&self, i, &el, doc)),
155                doc,
156            )
157            .or_else(|| Some(doc.text("<empty>")))
158        }
159    }
160}
161
162/// Trait representation of a Predicate on members of an existential iterator type
163pub trait IsAll<T> {
164    fn describe<'d>(&self, doc: &'d BoxAllocator) -> DocBuilder<'d>;
165    fn falsify_all<'d>(&self, t: &T, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>>;
166}
167
168impl<T: 'static, Elem: Debug + 'static> IsAll<T> for Predicate<Elem>
169where
170    for<'a> &'a T: IntoIterator<Item = &'a Elem>,
171{
172    fn describe<'d>(&self, doc: &'d BoxAllocator) -> DocBuilder<'d> {
173        doc.text("ALL").append(doc.space().append(self.describe(doc)).nest(2)).group()
174    }
175
176    fn falsify_all<'d>(&self, t: &T, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>> {
177        fmt_falsifications(
178            t.into_iter().enumerate().filter_map(|(i, el)| falsify_elem(&self, i, &el, doc)),
179            doc,
180        )
181    }
182}
183
184/// A wrapping newtype to differentiate those types which are iterators themselves, from those that
185/// implement `IntoIterator`.
186struct OverIterator<Elem>(Predicate<Elem>);
187
188impl<'e, Iter, Elem> IsAll<Iter> for OverIterator<Elem>
189where
190    Elem: Debug + 'static,
191    Iter: Iterator<Item = &'e Elem> + Clone,
192{
193    fn describe<'d>(&self, doc: &'d BoxAllocator) -> DocBuilder<'d> {
194        doc.text("ALL").append(doc.space().append(self.0.describe(doc)).nest(2)).group()
195    }
196
197    fn falsify_all<'d>(&self, iter: &Iter, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>> {
198        fmt_falsifications(
199            iter.clone().enumerate().filter_map(|(i, el)| falsify_elem(&self.0, i, &el, doc)),
200            doc,
201        )
202    }
203}
204
205impl<'e, Iter, Elem> IsAny<Iter> for OverIterator<Elem>
206where
207    Elem: Debug + 'static,
208    Iter: Iterator<Item = &'e Elem> + Clone,
209{
210    fn describe<'d>(&self, doc: &'d BoxAllocator) -> DocBuilder<'d> {
211        doc.text("ANY").append(doc.space().append(self.0.describe(doc)).nest(2)).group()
212    }
213
214    fn falsify_any<'d>(&self, iter: &Iter, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>> {
215        if iter.clone().any(|e| self.0.satisfied(e)) {
216            None
217        } else {
218            // All are falsifications, so display all (up to MAX_ITER_FALSIFICATIONS)
219            fmt_falsifications(
220                iter.clone().enumerate().filter_map(|(i, el)| falsify_elem(&self.0, i, &el, doc)),
221                doc,
222            )
223            .or_else(|| Some(doc.text("<empty>")))
224        }
225    }
226}
227
228/// A Predicate lifted over a mapping function from `T` to `U`. Such mappings allow converting a
229/// predicate from one type - say, a struct field - to an upper type - such as a whole struct -
230/// whilst maintaining information about the relationship.
231enum OverPred<T, U> {
232    ByRef(Predicate<U>, Arc<dyn Fn(&T) -> &U + Send + Sync + 'static>, String),
233    ByValue(Predicate<U>, Arc<dyn Fn(&T) -> U + Send + Sync + 'static>, String),
234}
235
236/// Trait representation of OverPred where `U` is existential
237pub trait IsOver<T> {
238    fn describe<'d>(&self, doc: &'d BoxAllocator) -> DocBuilder<'d>;
239    fn falsify_over<'d>(&self, t: &T, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>>;
240}
241
242impl<T, U> IsOver<T> for OverPred<T, U> {
243    fn describe<'a>(&self, doc: &'a BoxAllocator) -> DocBuilder<'a> {
244        let (pred, path) = match self {
245            OverPred::ByRef(pred, _, path) => (pred, path),
246            OverPred::ByValue(pred, _, path) => (pred, path),
247        };
248        doc.as_string(path).append(doc.space()).append(pred.describe(doc)).group()
249    }
250    fn falsify_over<'d>(&self, t: &T, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>> {
251        let (d, path) = match self {
252            OverPred::ByRef(pred, project, path) => (pred.falsify((project)(t), doc), path),
253            OverPred::ByValue(pred, project, path) => (pred.falsify(&(project)(t), doc), path),
254        };
255        d.map(|falsification| {
256            doc.as_string(path).append(doc.space().append(falsification).nest(2)).group()
257        })
258    }
259}
260
261impl<T> Predicate<T> {
262    fn is_equal(&self) -> bool {
263        matches!(self, Predicate::Equal(_, _, _))
264    }
265    pub fn describe<'a>(&self, doc: &'a BoxAllocator) -> DocBuilder<'a> {
266        match self {
267            Predicate::Equal(expected, _, repr) => {
268                doc.text("==").append(doc.space()).append(doc.text(repr(expected))).group()
269            }
270            Predicate::And(left, right) => parens(doc, left.describe(doc))
271                .append(doc.space())
272                .append(doc.text("AND"))
273                .append(doc.space())
274                .append(parens(doc, right.describe(doc)))
275                .group(),
276            Predicate::Or(left, right) => parens(doc, left.describe(doc))
277                .nest(2)
278                .append(doc.space())
279                .append(doc.text("OR"))
280                .append(doc.space())
281                .append(parens(doc, right.describe(doc)).nest(2))
282                .group(),
283            // Succinct override for NOT-Equal cases
284            Predicate::Not(inner) if inner.is_equal() => {
285                if let Predicate::Equal(expected, _, repr) = &**inner {
286                    doc.text("!=").append(doc.space()).append(doc.text(repr(&*expected))).group()
287                } else {
288                    // This branch is guarded by inner.is_equal()
289                    unreachable!()
290                }
291            }
292            Predicate::Not(inner) => {
293                doc.text("NOT").append(doc.space().append(inner.describe(doc)).nest(2)).group()
294            }
295            Predicate::Predicate(_, desc) => doc.as_string(desc).group(),
296            Predicate::Over(over) => over.describe(doc),
297            Predicate::Any(any) => any.describe(doc),
298            Predicate::All(all) => all.describe(doc),
299        }
300    }
301    /// Provide a minimized falsification of the predicate, if possible
302    pub fn falsify<'d>(&self, t: &T, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>> {
303        match self {
304            Predicate::Equal(expected, are_eq, repr) => {
305                if are_eq(expected, t) {
306                    None
307                } else {
308                    Some(
309                        doc.text(repr(t))
310                            .append(doc.space())
311                            .append(doc.text("!="))
312                            .append(doc.space())
313                            .append(doc.text(repr(expected)))
314                            .group(),
315                    )
316                }
317            }
318            Predicate::And(left, right) => match (left.falsify(t, doc), right.falsify(t, doc)) {
319                (Some(l), Some(r)) => Some(
320                    parens(doc, l)
321                        .append(doc.space())
322                        .append(doc.text("AND"))
323                        .append(doc.space())
324                        .append(parens(doc, r))
325                        .group(),
326                ),
327                (Some(l), None) => Some(l),
328                (None, Some(r)) => Some(r),
329                (None, None) => None,
330            },
331            Predicate::Or(left, right) => left
332                .falsify(t, doc)
333                .and_then(|l| right.falsify(t, doc).map(|r| parens(doc, l).append(parens(doc, r)))),
334            Predicate::Not(inner) => match inner.falsify(t, doc) {
335                Some(_) => None,
336                None => match &**inner {
337                    Predicate::Equal(expected, _, repr) => Some(
338                        doc.text(repr(t))
339                            .append(doc.space())
340                            .append(doc.text("=="))
341                            .append(doc.space())
342                            .append(doc.text(repr(&expected))),
343                    ),
344                    _ => Some(
345                        doc.text("NOT")
346                            .append(doc.space().append(inner.describe(doc)).nest(2))
347                            .group(),
348                    ),
349                },
350            },
351            Predicate::Predicate(pred, desc) => {
352                if pred(t) {
353                    None
354                } else {
355                    Some(doc.text("NOT").append(doc.space().append(doc.as_string(desc)).nest(2)))
356                }
357            }
358            Predicate::Over(over) => over.falsify_over(t, doc),
359            Predicate::Any(any) => any.falsify_any(t, doc),
360            Predicate::All(all) => all.falsify_all(t, doc),
361        }
362    }
363    pub fn satisfied<'t>(&self, t: &'t T) -> bool {
364        match self {
365            Predicate::Equal(expected, are_eq, _) => are_eq(t, expected),
366            Predicate::And(left, right) => left.satisfied(t) && right.satisfied(t),
367            Predicate::Or(left, right) => left.satisfied(t) || right.satisfied(t),
368            Predicate::Not(inner) => !inner.satisfied(t),
369            Predicate::Predicate(pred, _) => pred(t),
370            Predicate::Over(over) => over.falsify_over(t, &BoxAllocator).is_none(),
371            Predicate::Any(any) => any.falsify_any(t, &BoxAllocator).is_none(),
372            Predicate::All(all) => all.falsify_all(t, &BoxAllocator).is_none(),
373        }
374    }
375    pub fn assert_satisfied(&self, t: &T) -> Result<(), DebugString> {
376        let doc = BoxAllocator;
377        match self.falsify(t, &doc) {
378            Some(falsification) => {
379                let d = doc
380                    .text("FAILED EXPECTATION")
381                    .append(doc.newline().append(self.describe(&doc)).nest(2))
382                    .append(doc.newline().append(doc.text("BECAUSE")))
383                    .append(doc.newline().append(falsification).nest(2));
384                Err(DebugString(d.1.pretty(FMT_CHAR_WIDTH).to_string()))
385            }
386            None => Ok(()),
387        }
388    }
389    pub fn and(self, rhs: Predicate<T>) -> Predicate<T> {
390        Predicate::And(Box::new(self), Box::new(rhs))
391    }
392    pub fn or(self, rhs: Predicate<T>) -> Predicate<T> {
393        Predicate::Or(Box::new(self), Box::new(rhs))
394    }
395    pub fn not(self) -> Predicate<T> {
396        Predicate::Not(Box::new(self))
397    }
398    /// Construct a simple predicate function
399    pub fn predicate<F, S>(f: F, label: S) -> Predicate<T>
400    where
401        F: for<'t> Fn(&'t T) -> bool + Send + Sync + 'static,
402        S: Into<String>,
403    {
404        Predicate::Predicate(Arc::new(f), label.into())
405    }
406}
407
408/// Convenient implementations that rely on `Debug` to provide output on falsification, and
409/// `PartialEq` to provide equality. If you wish to create predicates for `?Debug` types, use the
410/// `Predicate` or `Equal` constructors directly.
411impl<T: PartialEq + Debug + 'static> Predicate<T> {
412    /// Construct a Predicate that expects two `T`s to be equal
413    pub fn equal(t: T) -> Predicate<T> {
414        Predicate::Equal(Arc::new(t), Arc::new(T::eq), Arc::new(show_debug))
415    }
416    /// Construct a Predicate that expects two `T`s to be non-equal
417    pub fn not_equal(t: T) -> Predicate<T> {
418        Predicate::Equal(Arc::new(t), Arc::new(T::eq), Arc::new(show_debug)).not()
419    }
420}
421
422/// Predicates on iterable types, those convertible into iterators via IntoIterator (e.g. Vec<_>)
423impl<Elem, T> Predicate<T>
424where
425    T: Send + Sync + 'static,
426    for<'a> &'a T: IntoIterator<Item = &'a Elem>,
427    Elem: Debug + Send + Sync + 'static,
428{
429    /// Construct a predicate that all elements of an iterable type match a given predicate
430    /// If the iterable is empty, the predicate will succeed
431    pub fn all(pred: Predicate<Elem>) -> Predicate<T> {
432        Predicate::All(Arc::new(pred))
433    }
434
435    /// Construct a predicate that at least one element of an iterable type match a given predicate
436    /// If the iterable is empty, the predicate will fail
437    pub fn any(pred: Predicate<Elem>) -> Predicate<T> {
438        Predicate::Any(Arc::new(pred))
439    }
440}
441
442/// Predicates on types which are an `Iterator`
443impl<'e, Elem, Iter> Predicate<Iter>
444where
445    Iter: Iterator<Item = &'e Elem> + Clone,
446    Elem: Debug + Send + Sync + 'static,
447{
448    /// Construct a predicate that all elements of an Iterator match a given predicate
449    /// If the Iterator is empty, the predicate will succeed
450    pub fn iter_all(pred: Predicate<Elem>) -> Predicate<Iter> {
451        Predicate::All(Arc::new(OverIterator(pred)))
452    }
453
454    /// Construct a predicate that at least one element of an Iterator match a given predicate
455    /// If the iterator is empty, the predicate will fail
456    pub fn iter_any(pred: Predicate<Elem>) -> Predicate<Iter> {
457        Predicate::Any(Arc::new(OverIterator(pred)))
458    }
459}
460
461impl<U: Send + Sync + 'static> Predicate<U> {
462    /// Lift a predicate over a projection from `T -> &U`. This constructs a Predicate<T> from a
463    /// predicate on some field (or arbitrary projection) of type `U`.
464    ///
465    /// This allows:
466    ///   * Creating a Predicate on a struct from a predicate on a field (or field of a field) of
467    ///     that struct.
468    ///   * Creating a Predicate on a type from a predicate on some value computable from that type
469    ///
470    /// Compared to writing an arbitrary function using the `predicate()` method, an
471    /// `over` projection allows for more minimal falsification and better error reporting in
472    /// failure cases.
473    ///
474    /// Use `over` when your projection function returns a reference. If you need to return a value
475    /// (for example, a temporary whose reference lifetime would not escape the function), use
476    /// `over_value`.
477    pub fn over<F, T, P>(self, project: F, path: P) -> Predicate<T>
478    where
479        F: Fn(&T) -> &U + Send + Sync + 'static,
480        P: Into<String>,
481        T: 'static,
482    {
483        Predicate::Over(Arc::new(OverPred::ByRef(self, Arc::new(project), path.into())))
484    }
485
486    /// Lift a predicate over a projection from `T -> U`. This constructs a Predicate<T> from a
487    /// predicate on some field (or arbitrary projection) of type `U`.
488    ///
489    /// This allows:
490    ///   * Creating a Predicate on a struct from a predicate on a field (or field of a field) of
491    ///     that struct.
492    ///   * Creating a Predicate on a type from a predicate on some value computable from that type
493    ///
494    /// Compared to writing an arbitrary function using the `predicate()` method, an
495    /// `over` projection allows for more minimal falsification and better error reporting in
496    /// failure cases.
497    ///
498    /// Use `over_value` when your projection function needs to return a value. If you can return a
499    /// reference, use `over`.
500    pub fn over_value<F, T, P>(self, project: F, path: P) -> Predicate<T>
501    where
502        F: Fn(&T) -> U + Send + Sync + 'static,
503        P: Into<String>,
504        T: 'static,
505    {
506        Predicate::Over(Arc::new(OverPred::ByValue(self, Arc::new(project), path.into())))
507    }
508}
509
510/// A macro for allowing more ergonomic projection of predicates 'over' some projection function
511/// that focuses on a subset of a data type.
512///
513/// Specify the name of the parent type, the field to focus on, and then pass in the predicate over
514/// that field
515///
516/// usage: over!(Type:field, predicate)
517///
518/// e.g.
519///
520/// ```
521/// let predicate = over!(RemoteDevice:name, P::equal(Some("INCORRECT_NAME".to_string())));
522/// ```
523#[macro_export]
524macro_rules! over {
525    ($type:ty : $selector:tt, $pred:expr) => {
526        $pred.over(|var: &$type| &var.$selector, format!(".{}", stringify!($selector)))
527    };
528}
529
530/// A simple macro to allow idiomatic assertion of predicates, producing well formatted
531/// falsifications if the predicate fails.
532///
533/// usage:
534///
535/// ```
536/// let predicate = correct_name().or(correct_address());
537/// assert_satisfies!(&test_peer(), predicate);
538/// ```
539#[macro_export]
540macro_rules! assert_satisfies {
541    ($subject:expr, $pred:expr) => {
542        if let Err(msg) = $pred.assert_satisfied($subject) {
543            panic!("{}", msg.0)
544        }
545    };
546}