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_else(|| 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        matches!(self, Predicate::Equal(_, _, _))
267    }
268    pub fn describe<'a>(&self, doc: &'a BoxAllocator) -> DocBuilder<'a> {
269        match self {
270            Predicate::Equal(expected, _, repr) => {
271                doc.text("==").append(doc.space()).append(doc.text(repr(expected))).group()
272            }
273            Predicate::And(left, right) => parens(doc, left.describe(doc))
274                .append(doc.space())
275                .append(doc.text("AND"))
276                .append(doc.space())
277                .append(parens(doc, right.describe(doc)))
278                .group(),
279            Predicate::Or(left, right) => parens(doc, left.describe(doc))
280                .nest(2)
281                .append(doc.space())
282                .append(doc.text("OR"))
283                .append(doc.space())
284                .append(parens(doc, right.describe(doc)).nest(2))
285                .group(),
286            // Succinct override for NOT-Equal cases
287            Predicate::Not(inner) if inner.is_equal() => {
288                if let Predicate::Equal(expected, _, repr) = &**inner {
289                    doc.text("!=").append(doc.space()).append(doc.text(repr(&*expected))).group()
290                } else {
291                    // This branch is guarded by inner.is_equal()
292                    unreachable!()
293                }
294            }
295            Predicate::Not(inner) => {
296                doc.text("NOT").append(doc.space().append(inner.describe(doc)).nest(2)).group()
297            }
298            Predicate::Predicate(_, desc) => doc.as_string(desc).group(),
299            Predicate::Over(over) => over.describe(doc),
300            Predicate::Any(any) => any.describe(doc),
301            Predicate::All(all) => all.describe(doc),
302        }
303    }
304    /// Provide a minimized falsification of the predicate, if possible
305    pub fn falsify<'d>(&self, t: &T, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>> {
306        match self {
307            Predicate::Equal(expected, are_eq, repr) => {
308                if are_eq(expected, t) {
309                    None
310                } else {
311                    Some(
312                        doc.text(repr(t))
313                            .append(doc.space())
314                            .append(doc.text("!="))
315                            .append(doc.space())
316                            .append(doc.text(repr(expected)))
317                            .group(),
318                    )
319                }
320            }
321            Predicate::And(left, right) => match (left.falsify(t, doc), right.falsify(t, doc)) {
322                (Some(l), Some(r)) => Some(
323                    parens(doc, l)
324                        .append(doc.space())
325                        .append(doc.text("AND"))
326                        .append(doc.space())
327                        .append(parens(doc, r))
328                        .group(),
329                ),
330                (Some(l), None) => Some(l),
331                (None, Some(r)) => Some(r),
332                (None, None) => None,
333            },
334            Predicate::Or(left, right) => left
335                .falsify(t, doc)
336                .and_then(|l| right.falsify(t, doc).map(|r| parens(doc, l).append(parens(doc, r)))),
337            Predicate::Not(inner) => match inner.falsify(t, doc) {
338                Some(_) => None,
339                None => match &**inner {
340                    Predicate::Equal(expected, _, repr) => Some(
341                        doc.text(repr(t))
342                            .append(doc.space())
343                            .append(doc.text("=="))
344                            .append(doc.space())
345                            .append(doc.text(repr(&expected))),
346                    ),
347                    _ => Some(
348                        doc.text("NOT")
349                            .append(doc.space().append(inner.describe(doc)).nest(2))
350                            .group(),
351                    ),
352                },
353            },
354            Predicate::Predicate(pred, desc) => {
355                if pred(t) {
356                    None
357                } else {
358                    Some(doc.text("NOT").append(doc.space().append(doc.as_string(desc)).nest(2)))
359                }
360            }
361            Predicate::Over(over) => over.falsify_over(t, doc),
362            Predicate::Any(any) => any.falsify_any(t, doc),
363            Predicate::All(all) => all.falsify_all(t, doc),
364        }
365    }
366    pub fn satisfied<'t>(&self, t: &'t T) -> bool {
367        match self {
368            Predicate::Equal(expected, are_eq, _) => are_eq(t, expected),
369            Predicate::And(left, right) => left.satisfied(t) && right.satisfied(t),
370            Predicate::Or(left, right) => left.satisfied(t) || right.satisfied(t),
371            Predicate::Not(inner) => !inner.satisfied(t),
372            Predicate::Predicate(pred, _) => pred(t),
373            Predicate::Over(over) => over.falsify_over(t, &BoxAllocator).is_none(),
374            Predicate::Any(any) => any.falsify_any(t, &BoxAllocator).is_none(),
375            Predicate::All(all) => all.falsify_all(t, &BoxAllocator).is_none(),
376        }
377    }
378    pub fn assert_satisfied(&self, t: &T) -> Result<(), DebugString> {
379        let doc = BoxAllocator;
380        match self.falsify(t, &doc) {
381            Some(falsification) => {
382                let d = doc
383                    .text("FAILED EXPECTATION")
384                    .append(doc.newline().append(self.describe(&doc)).nest(2))
385                    .append(doc.newline().append(doc.text("BECAUSE")))
386                    .append(doc.newline().append(falsification).nest(2));
387                Err(DebugString(d.1.pretty(FMT_CHAR_WIDTH).to_string()))
388            }
389            None => Ok(()),
390        }
391    }
392    pub fn and(self, rhs: Predicate<T>) -> Predicate<T> {
393        Predicate::And(Box::new(self), Box::new(rhs))
394    }
395    pub fn or(self, rhs: Predicate<T>) -> Predicate<T> {
396        Predicate::Or(Box::new(self), Box::new(rhs))
397    }
398    pub fn not(self) -> Predicate<T> {
399        Predicate::Not(Box::new(self))
400    }
401    /// Construct a simple predicate function
402    pub fn predicate<F, S>(f: F, label: S) -> Predicate<T>
403    where
404        F: for<'t> Fn(&'t T) -> bool + Send + Sync + 'static,
405        S: Into<String>,
406    {
407        Predicate::Predicate(Arc::new(f), label.into())
408    }
409}
410
411/// Convenient implementations that rely on `Debug` to provide output on falsification, and
412/// `PartialEq` to provide equality. If you wish to create predicates for `?Debug` types, use the
413/// `Predicate` or `Equal` constructors directly.
414impl<T: PartialEq + Debug + 'static> Predicate<T> {
415    /// Construct a Predicate that expects two `T`s to be equal
416    pub fn equal(t: T) -> Predicate<T> {
417        Predicate::Equal(Arc::new(t), Arc::new(T::eq), Arc::new(show_debug))
418    }
419    /// Construct a Predicate that expects two `T`s to be non-equal
420    pub fn not_equal(t: T) -> Predicate<T> {
421        Predicate::Equal(Arc::new(t), Arc::new(T::eq), Arc::new(show_debug)).not()
422    }
423}
424
425/// Predicates on iterable types, those convertible into iterators via IntoIterator (e.g. Vec<_>)
426impl<Elem, T> Predicate<T>
427where
428    T: Send + Sync + 'static,
429    for<'a> &'a T: IntoIterator<Item = &'a Elem>,
430    Elem: Debug + Send + Sync + 'static,
431{
432    /// Construct a predicate that all elements of an iterable type match a given predicate
433    /// If the iterable is empty, the predicate will succeed
434    pub fn all(pred: Predicate<Elem>) -> Predicate<T> {
435        Predicate::All(Arc::new(pred))
436    }
437
438    /// Construct a predicate that at least one element of an iterable type match a given predicate
439    /// If the iterable is empty, the predicate will fail
440    pub fn any(pred: Predicate<Elem>) -> Predicate<T> {
441        Predicate::Any(Arc::new(pred))
442    }
443}
444
445/// Predicates on types which are an `Iterator`
446impl<'e, Elem, Iter> Predicate<Iter>
447where
448    Iter: Iterator<Item = &'e Elem> + Clone,
449    Elem: Debug + Send + Sync + 'static,
450{
451    /// Construct a predicate that all elements of an Iterator match a given predicate
452    /// If the Iterator is empty, the predicate will succeed
453    pub fn iter_all(pred: Predicate<Elem>) -> Predicate<Iter> {
454        Predicate::All(Arc::new(OverIterator(pred)))
455    }
456
457    /// Construct a predicate that at least one element of an Iterator match a given predicate
458    /// If the iterator is empty, the predicate will fail
459    pub fn iter_any(pred: Predicate<Elem>) -> Predicate<Iter> {
460        Predicate::Any(Arc::new(OverIterator(pred)))
461    }
462}
463
464impl<U: Send + Sync + 'static> Predicate<U> {
465    /// Lift a predicate over a projection from `T -> &U`. This constructs a Predicate<T> from a
466    /// predicate on some field (or arbitrary projection) of type `U`.
467    ///
468    /// This allows:
469    ///   * Creating a Predicate on a struct from a predicate on a field (or field of a field) of
470    ///     that struct.
471    ///   * Creating a Predicate on a type from a predicate on some value computable from that type
472    ///
473    /// Compared to writing an arbitrary function using the `predicate()` method, an
474    /// `over` projection allows for more minimal falsification and better error reporting in
475    /// failure cases.
476    ///
477    /// Use `over` when your projection function returns a reference. If you need to return a value
478    /// (for example, a temporary whose reference lifetime would not escape the function), use
479    /// `over_value`.
480    pub fn over<F, T, P>(self, project: F, path: P) -> Predicate<T>
481    where
482        F: Fn(&T) -> &U + Send + Sync + 'static,
483        P: Into<String>,
484        T: 'static,
485    {
486        Predicate::Over(Arc::new(OverPred::ByRef(self, Arc::new(project), path.into())))
487    }
488
489    /// Lift a predicate over a projection from `T -> U`. This constructs a Predicate<T> from a
490    /// predicate on some field (or arbitrary projection) of type `U`.
491    ///
492    /// This allows:
493    ///   * Creating a Predicate on a struct from a predicate on a field (or field of a field) of
494    ///     that struct.
495    ///   * Creating a Predicate on a type from a predicate on some value computable from that type
496    ///
497    /// Compared to writing an arbitrary function using the `predicate()` method, an
498    /// `over` projection allows for more minimal falsification and better error reporting in
499    /// failure cases.
500    ///
501    /// Use `over_value` when your projection function needs to return a value. If you can return a
502    /// reference, use `over`.
503    pub fn over_value<F, T, P>(self, project: F, path: P) -> Predicate<T>
504    where
505        F: Fn(&T) -> U + Send + Sync + 'static,
506        P: Into<String>,
507        T: 'static,
508    {
509        Predicate::Over(Arc::new(OverPred::ByValue(self, Arc::new(project), path.into())))
510    }
511}
512
513/// A macro for allowing more ergonomic projection of predicates 'over' some projection function
514/// that focuses on a subset of a data type.
515///
516/// Specify the name of the parent type, the field to focus on, and then pass in the predicate over
517/// that field
518///
519/// usage: over!(Type:field, predicate)
520///
521/// e.g.
522///
523/// ```
524/// let predicate = over!(RemoteDevice:name, P::equal(Some("INCORRECT_NAME".to_string())));
525/// ```
526#[macro_export]
527macro_rules! over {
528    ($type:ty : $selector:tt, $pred:expr) => {
529        $pred.over(|var: &$type| &var.$selector, format!(".{}", stringify!($selector)))
530    };
531}
532
533/// A simple macro to allow idiomatic assertion of predicates, producing well formatted
534/// falsifications if the predicate fails.
535///
536/// usage:
537///
538/// ```
539/// let predicate = correct_name().or(correct_address());
540/// assert_satisfies!(&test_peer(), predicate);
541/// ```
542#[macro_export]
543macro_rules! assert_satisfies {
544    ($subject:expr, $pred:expr) => {
545        if let Err(msg) = $pred.assert_satisfied($subject) {
546            panic!("{}", msg.0)
547        }
548    };
549}