test_util/
lib.rs

1// Copyright 2020 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
5//! This crate defines a collection of useful utilities for testing rust code.
6
7use std::sync::Mutex;
8
9/// Asserts that the first argument is strictly less than the second.
10#[macro_export]
11macro_rules! assert_lt {
12    ($x:expr, $y:expr) => {
13        assert!(
14            $x < $y,
15            "assertion `{} < {}` failed; actual: {:?} is not less than {:?}",
16            stringify!($x),
17            stringify!($y),
18            $x,
19            $y
20        );
21    };
22}
23
24/// Asserts that the first argument is less than or equal to the second.
25#[macro_export]
26macro_rules! assert_leq {
27    ($x:expr, $y:expr) => {
28        assert!(
29            $x <= $y,
30            "assertion `{} <= {}` failed; actual: {:?} is not less than or equal to {:?}",
31            stringify!($x),
32            stringify!($y),
33            $x,
34            $y
35        );
36    };
37}
38
39/// Asserts that the first argument is strictly greater than the second.
40#[macro_export]
41macro_rules! assert_gt {
42    ($x:expr, $y:expr) => {
43        assert!(
44            $x > $y,
45            "assertion `{} > {}` failed; actual: {:?} is not greater than {:?}",
46            stringify!($x),
47            stringify!($y),
48            $x,
49            $y
50        );
51    };
52}
53
54/// Asserts that the first argument is greater than or equal to the second.
55#[macro_export]
56macro_rules! assert_geq {
57    ($x:expr, $y:expr) => {
58        assert!(
59            $x >= $y,
60            "assertion `{} >= {}` failed; actual: {:?} is not greater than or equal to {:?}",
61            stringify!($x),
62            stringify!($y),
63            $x,
64            $y
65        );
66    };
67}
68
69/// Asserts that `x` and `y` are within `delta` of one another.
70///
71/// `x` and `y` must be of a common type that supports both subtraction and negation. (Note that it
72/// would be natural to define this macro using `abs()`, but when attempting to do so, the compiler
73/// fails to apply its inference rule for under-constrained types. See
74/// [https://github.com/rust-lang/reference/issues/104].)
75#[macro_export]
76macro_rules! assert_near {
77    ($x: expr, $y: expr, $delta: expr) => {
78        let difference = $x - $y;
79        assert!(
80            (-$delta..=$delta).contains(&difference),
81            "assertion `{} is near {} (within delta {})` failed; actual: |{:?} - {:?}| > {:?}",
82            stringify!($x),
83            stringify!($y),
84            stringify!($delta),
85            $x,
86            $y,
87            $delta
88        );
89    };
90}
91
92/// A mutually exclusive counter that is not shareable, but can be defined statically for the
93/// duration of a test. This simplifies the implementation of a simple test-global counter,
94/// avoiding the complexity of alternatives like std::sync::atomic objects that are typically
95/// wrapped in Arc()s, cloned, and require non-intuitive memory management options.
96///
97/// # Example
98///
99/// ```
100///    use test_util::Counter;
101///    use lazy_static::lazy_static;
102///
103///    #[test]
104///    async fn my_test() {
105///        lazy_static! {
106///            static ref CALL_COUNT: Counter = Counter::new(0);
107///        }
108///
109///        let handler = || {
110///            // some async callback
111///            // ...
112///            CALL_COUNT.inc();
113///        };
114///        handler();
115///        // ...
116///        CALL_COUNT.inc();
117///
118///        assert_eq!(CALL_COUNT.get(), 2);
119///    }
120/// ```
121///
122/// *Important:* Since inc() and get() obtain separate Mutex lock()s to access the underlying
123/// counter value, it is very possible that a separate thread, if also mutating the same counter,
124/// may increment the value between the first thread's calls to inc() and get(), in which case,
125/// the two threads could get() the same value (the result after both calls to inc()). If get()
126/// is used to, for example, print the values after each call to inc(), the resulting values might
127/// include duplicate intermediate counter values, with some numbers skipped, but the final value
128/// after all threads complete will be the exact number of all calls to inc() (offset by the
129/// initial value).
130///
131/// To provide slightly more consistent results, inc() returns the new value after incrementing
132/// the counter, obtaining the value while the lock is held. This way, each incremental value will
133/// be returned to the calling threads; *however* the threads could still receive the values out of
134/// order.
135///
136/// Consider, thread 1 calls inc() starting at count 0. A value of 1 is returned, but before thread
137/// 1 receives the new value, it might be interrupted by thread 2. Thread 2 increments the counter
138/// from 1 to 2, return the new value 2, and (let's say, for example) prints the value "2". Thread 1
139/// then resumes, and prints "1".
140///
141/// Specifically, the Counter guarantees that each invocation of inc() will return a value that is
142/// 1 greater than the previous value returned by inc() (or 1 greater than the `initial` value, if
143/// it is the first invocation). Call get() after completing all invocations of inc() to get the
144/// total number of times inc() was called (offset by the initial value).
145pub struct Counter {
146    count: Mutex<usize>,
147}
148
149impl Counter {
150    /// Initializes a new counter to the given value.
151    pub fn new(initial: usize) -> Self {
152        Counter { count: Mutex::new(initial) }
153    }
154
155    /// Increments the counter by one and returns the new value.
156    pub fn inc(&self) -> usize {
157        let mut count = self.count.lock().unwrap();
158        *count += 1;
159        *count
160    }
161
162    /// Returns the current value of the counter.
163    pub fn get(&self) -> usize {
164        *self.count.lock().unwrap()
165    }
166}
167
168impl std::fmt::Debug for Counter {
169    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170        f.debug_struct("Counter").field("count", &self.get()).finish()
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177    use lazy_static::lazy_static;
178    use std::collections::BTreeSet;
179    use std::sync::mpsc;
180    use std::sync::mpsc::{Receiver, Sender};
181    use std::thread;
182
183    #[derive(Debug, PartialEq, PartialOrd)]
184    struct NotDisplay {
185        x: i32,
186    }
187
188    impl core::ops::Sub for NotDisplay {
189        type Output = Self;
190
191        fn sub(self, other: Self) -> Self {
192            NotDisplay { x: self.x - other.x }
193        }
194    }
195
196    impl core::ops::Neg for NotDisplay {
197        type Output = Self;
198
199        fn neg(self) -> Self {
200            NotDisplay { x: -self.x }
201        }
202    }
203
204    #[test]
205    fn test_assert_lt_passes() {
206        assert_lt!(1, 2);
207        assert_lt!(1u8, 2u8);
208        assert_lt!(1u16, 2u16);
209        assert_lt!(1u32, 2u32);
210        assert_lt!(1u64, 2u64);
211        assert_lt!(-1, 3);
212        assert_lt!(-1i8, 3i8);
213        assert_lt!(-1i16, 3i16);
214        assert_lt!(-1i32, 3i32);
215        assert_lt!(-1i64, 3i64);
216        assert_lt!(-2.0, 7.0);
217        assert_lt!(-2.0f32, 7.0f32);
218        assert_lt!(-2.0f64, 7.0f64);
219        assert_lt!('a', 'b');
220        assert_lt!(NotDisplay { x: 1 }, NotDisplay { x: 2 });
221    }
222
223    #[test]
224    #[should_panic(expected = "assertion `a < b` failed; actual: 2 is not less than 2")]
225    fn test_assert_lt_fails_a_equals_b() {
226        let a = 2;
227        let b = 2;
228        assert_lt!(a, b);
229    }
230
231    #[test]
232    #[should_panic(expected = "assertion `a < b` failed; actual: 5 is not less than 2")]
233    fn test_assert_lt_fails_a_greater_than_b() {
234        let a = 5;
235        let b = 2;
236        assert_lt!(a, b);
237    }
238
239    #[test]
240    fn test_assert_leq_passes() {
241        assert_leq!(1, 2);
242        assert_leq!(2, 2);
243        assert_leq!(-2.0, 3.0);
244        assert_leq!(3.0, 3.0);
245        assert_leq!('a', 'b');
246        assert_leq!('b', 'b');
247        assert_leq!(NotDisplay { x: 1 }, NotDisplay { x: 2 });
248        assert_leq!(NotDisplay { x: 2 }, NotDisplay { x: 2 });
249    }
250
251    #[test]
252    #[should_panic(
253        expected = "assertion `a <= b` failed; actual: 3 is not less than or equal to 2"
254    )]
255    fn test_assert_leq_fails() {
256        let a = 3;
257        let b = 2;
258        assert_leq!(a, b);
259    }
260
261    #[test]
262    fn test_assert_gt_passes() {
263        assert_gt!(2, 1);
264        assert_gt!(2u8, 1u8);
265        assert_gt!(2u16, 1u16);
266        assert_gt!(2u32, 1u32);
267        assert_gt!(2u64, 1u64);
268        assert_gt!(3, -1);
269        assert_gt!(3i8, -1i8);
270        assert_gt!(3i16, -1i16);
271        assert_gt!(3i32, -1i32);
272        assert_gt!(3i64, -1i64);
273        assert_gt!(7.0, -2.0);
274        assert_gt!(7.0f32, -2.0f32);
275        assert_gt!(7.0f64, -2.0f64);
276        assert_gt!('b', 'a');
277        assert_gt!(NotDisplay { x: 2 }, NotDisplay { x: 1 });
278    }
279
280    #[test]
281    #[should_panic(expected = "assertion `a > b` failed; actual: 2 is not greater than 2")]
282    fn test_assert_gt_fails_a_equals_b() {
283        let a = 2;
284        let b = 2;
285        assert_gt!(a, b);
286    }
287
288    #[test]
289    #[should_panic(expected = "assertion `a > b` failed; actual: -1 is not greater than 2")]
290    fn test_assert_gt_fails_a_less_than_b() {
291        let a = -1;
292        let b = 2;
293        assert_gt!(a, b);
294    }
295
296    #[test]
297    fn test_assert_geq_passes() {
298        assert_geq!(2, 1);
299        assert_geq!(2, 2);
300        assert_geq!(3.0, -2.0);
301        assert_geq!(3.0, 3.0);
302        assert_geq!('b', 'a');
303        assert_geq!('b', 'b');
304        assert_geq!(NotDisplay { x: 2 }, NotDisplay { x: 1 });
305        assert_geq!(NotDisplay { x: 2 }, NotDisplay { x: 2 });
306    }
307
308    #[test]
309    #[should_panic(
310        expected = "assertion `a >= b` failed; actual: 2 is not greater than or equal to 3"
311    )]
312    fn test_assert_geq_fails() {
313        let a = 2;
314        let b = 3;
315        assert_geq!(a, b);
316    }
317
318    #[test]
319    fn test_assert_near_passes() {
320        // Test both possible orderings and equality with literals.
321        assert_near!(1.0001, 1.0, 0.01);
322        assert_near!(1.0, 1.0001, 0.01);
323        assert_near!(1.0, 1.0, 0.0);
324
325        // Ensure the macro operates on all other expected input types.
326        assert_near!(1.0001f32, 1.0f32, 0.01f32);
327        assert_near!(1.0001f64, 1.0f64, 0.01f64);
328        assert_near!(7, 5, 2);
329        assert_near!(7i8, 5i8, 2i8);
330        assert_near!(7i16, 5i16, 2i16);
331        assert_near!(7i32, 5i32, 2i32);
332        assert_near!(7i64, 5i64, 2i64);
333
334        assert_near!(NotDisplay { x: 7 }, NotDisplay { x: 5 }, NotDisplay { x: 2 });
335    }
336
337    #[test]
338    #[should_panic]
339    fn test_assert_near_fails() {
340        assert_near!(1.00001, 1.0, 1e-8);
341    }
342
343    // Test error message with integers so display is predictable.
344    #[test]
345    #[should_panic(
346        expected = "assertion `a is near b (within delta d)` failed; actual: |3 - 5| > 1"
347    )]
348    fn test_assert_near_fails_with_message() {
349        let a = 3;
350        let b = 5;
351        let d = 1;
352        assert_near!(a, b, d);
353    }
354
355    #[test]
356    fn test_inc() {
357        lazy_static! {
358            static ref CALL_COUNT: Counter = Counter::new(0);
359        }
360
361        CALL_COUNT.inc();
362
363        assert_eq!(CALL_COUNT.get(), 1);
364    }
365
366    #[test]
367    fn test_incs_from_10() {
368        lazy_static! {
369            static ref CALL_COUNT: Counter = Counter::new(10);
370        }
371
372        CALL_COUNT.inc();
373        CALL_COUNT.inc();
374        CALL_COUNT.inc();
375
376        assert_eq!(CALL_COUNT.get(), 13);
377    }
378
379    #[test]
380    fn async_counts() {
381        lazy_static! {
382            static ref CALL_COUNT: Counter = Counter::new(0);
383        }
384
385        let (tx, rx): (Sender<usize>, Receiver<usize>) = mpsc::channel();
386        let mut children = Vec::new();
387
388        static NTHREADS: usize = 10;
389
390        for _ in 0..NTHREADS {
391            let thread_tx = tx.clone();
392            let child = thread::spawn(move || {
393                let new_value = CALL_COUNT.inc();
394                thread_tx.send(new_value).unwrap();
395                println!("Sent: {} (OK if out of order)", new_value);
396            });
397
398            children.push(child);
399        }
400
401        let mut ordered_ids: BTreeSet<usize> = BTreeSet::new();
402        for _ in 0..NTHREADS {
403            let received_id = rx.recv().unwrap();
404            println!("Received: {} (OK if in yet a different order)", received_id);
405            ordered_ids.insert(received_id);
406        }
407
408        // Wait for the threads to complete any remaining work
409        for child in children {
410            child.join().expect("child thread panicked");
411        }
412
413        // All threads should have incremented the count by 1 each.
414        assert_eq!(CALL_COUNT.get(), NTHREADS);
415
416        // All contiguous incremental values should have been received, though possibly not in
417        // order. The BTreeSet will return them in order so the complete set can be verified.
418        let mut expected_id: usize = 1;
419        for id in ordered_ids.iter() {
420            assert_eq!(*id, expected_id);
421            expected_id += 1;
422        }
423    }
424
425    #[test]
426    fn debug_format_counter() {
427        let counter = Counter::new(0);
428        assert_eq!(format!("{:?}", counter), "Counter { count: 0 }");
429    }
430}