1use pretty::{BoxAllocator, DocAllocator};
6use std::fmt::{self, Debug, Display, Formatter};
7use std::sync::Arc;
8
9pub mod asynchronous;
11pub mod host_driver;
13pub mod peer;
15#[cfg(test)]
17pub mod test;
18
19#[derive(Clone, PartialEq)]
26pub struct DebugString(String);
27
28const 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
37type 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
44fn show_debug<T: Debug>(t: &T) -> String {
46 format!("{:?}", t)
47}
48
49type Comparison<T> = Arc<dyn Fn(&T, &T) -> bool + Send + Sync + 'static>;
51type Show<T> = Arc<dyn Fn(&T) -> String + Send + Sync + 'static>;
53
54pub 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 Over(Arc<dyn IsOver<T> + Send + Sync>),
67 Any(Arc<dyn IsAny<T> + Send + Sync + 'static>),
72 All(Arc<dyn IsAll<T> + Send + Sync + 'static>),
77}
78
79impl<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
106const 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
135pub 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 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
162pub 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
184struct 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 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
228enum 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
236pub 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 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 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 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 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
408impl<T: PartialEq + Debug + 'static> Predicate<T> {
412 pub fn equal(t: T) -> Predicate<T> {
414 Predicate::Equal(Arc::new(t), Arc::new(T::eq), Arc::new(show_debug))
415 }
416 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
422impl<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 pub fn all(pred: Predicate<Elem>) -> Predicate<T> {
432 Predicate::All(Arc::new(pred))
433 }
434
435 pub fn any(pred: Predicate<Elem>) -> Predicate<T> {
438 Predicate::Any(Arc::new(pred))
439 }
440}
441
442impl<'e, Elem, Iter> Predicate<Iter>
444where
445 Iter: Iterator<Item = &'e Elem> + Clone,
446 Elem: Debug + Send + Sync + 'static,
447{
448 pub fn iter_all(pred: Predicate<Elem>) -> Predicate<Iter> {
451 Predicate::All(Arc::new(OverIterator(pred)))
452 }
453
454 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 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 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#[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#[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}