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