[go: up one dir, main page]

ntest/
lib.rs

1//! The ntest lib enhances the rust test framework with some useful functions.
2
3// Reexport procedural macros
4extern crate ntest_test_cases;
5extern crate ntest_timeout;
6
7#[doc(inline)]
8pub use ntest_test_cases::test_case;
9
10#[doc(inline)]
11pub use ntest_timeout::timeout;
12
13use std::sync::mpsc;
14use std::thread;
15use std::time::Duration;
16
17// Reexport traits
18mod traits;
19#[doc(inline)]
20pub use crate::traits::MaxDifference;
21
22#[doc(hidden)]
23/// Timeout helper for proc macro timeout
24pub fn execute_with_timeout<T: Send>(
25    code: &'static (dyn Fn() -> T + Sync + 'static),
26    timeout_ms: u64,
27) -> Option<T> {
28    let (sender, receiver) = mpsc::channel();
29    thread::spawn(move || if let Ok(()) = sender.send(code()) {});
30    match receiver.recv_timeout(Duration::from_millis(timeout_ms)) {
31        Ok(t) => Some(t),
32        Err(_) => None,
33    }
34}
35
36#[doc(hidden)]
37/// Difference helper for proc macro about equal
38pub fn about_eq<T: MaxDifference>(a: T, b: T, eps: f64) -> bool {
39    a.max_diff(b) < eps
40}
41
42/// Compare floating point values or vectors of floating points wether they are approximately equal.
43/// The default value for epsilon is `1.0e-6`.
44///
45/// # Examples
46///
47/// Compare two floating point values which are about equal:
48/// ```
49/// # use ntest::assert_about_eq;
50/// # fn main() {
51/// assert_about_eq!(42.00000001f32, 42.0f32);
52/// # }
53/// ```
54///
55/// Explicitly set an epsilon value. This test should fail:
56/// ``` should_panic
57/// # use ntest::assert_about_eq;
58/// # fn main() {
59/// assert_about_eq!(42.001f32, 42.0f32, 1.0e-4);
60/// # }
61/// ```
62///
63/// Compare two vectors or arrays of floats which are about equal:
64/// ```
65/// # use ntest::assert_about_eq;
66/// # fn main() {
67/// assert_about_eq!(vec![1.100000001, 2.1], vec![1.1, 2.1], 0.001f64);
68/// # }
69/// ```
70///
71/// Arrays can be compared to a length of up to `32`. See the [MaxDifference](trait.MaxDifference.html) implementation for more details:
72/// ```
73/// # use ntest::assert_about_eq;
74/// # fn main() {
75///# // Test double usage
76///# assert_about_eq!([1.100000001, 2.1], [1.1, 2.1], 0.001f64);
77/// assert_about_eq!([1.100000001, 2.1], [1.1, 2.1], 0.001f64);
78/// # }
79/// ```
80#[macro_export]
81macro_rules! assert_about_eq {
82    ($a:expr, $b:expr, $eps:expr) => {
83        let eps = $eps;
84        assert!(
85            $crate::about_eq($a, $b, eps),
86            "assertion failed: `(left !== right)` \
87             (left: `{:?}`, right: `{:?}`, epsilon: `{:?}`)",
88            $a,
89            $b,
90            eps
91        );
92    };
93    ($a:expr, $b:expr,$eps:expr,) => {
94        assert_about_eq!($a, $b, $eps);
95    };
96    ($a:expr, $b:expr) => {
97        assert_about_eq!($a, $b, 1.0e-6);
98    };
99    ($a:expr, $b:expr,) => {
100        assert_about_eq!($a, $b, 1.0e-6);
101    };
102}
103
104/// Expects a true expression. Otherwise panics.
105///
106/// Is an alias for the [assert! macro](https://doc.rust-lang.org/std/macro.assert.html).
107///
108/// # Examples
109///
110/// This call won't panic.
111/// ```rust
112/// # use ntest::assert_true;
113/// # fn main() {
114/// assert_true!(true);
115/// # }
116///```
117///
118/// This call will panic.
119/// ```should_panic
120/// # use ntest::assert_true;
121/// # fn main() {
122/// assert_true!(false);
123/// # }
124/// ```
125#[macro_export]
126macro_rules! assert_true {
127    ($x:expr) => {
128        if !$x {
129            panic!("assertion failed: Expected 'true', but was 'false'");
130        }
131    };
132    ($x:expr,) => {
133        assert_true!($x);
134    };
135}
136
137/// Expects a false expression. Otherwise panics.
138///
139/// # Examples
140///
141/// This call won't panic.
142/// ```rust
143/// # use ntest::assert_false;
144/// # fn main() {
145/// assert_false!(false);
146/// # }
147/// ```
148///
149/// This call will panic.
150/// ```should_panic
151/// # use ntest::assert_false;
152/// # fn main() {
153/// assert_false!(true);
154/// # }
155/// ```
156#[macro_export]
157macro_rules! assert_false {
158    ($x:expr) => {{
159        if $x {
160            panic!("assertion failed: Expected 'false', but was 'true'");
161        }
162    }};
163    ($x:expr,) => {{
164        assert_false!($x);
165    }};
166}
167
168/// A panic in Rust is not always implemented via unwinding, but can be implemented by aborting the
169/// process as well. This function only catches unwinding panics, not those that abort the process.
170/// See the catch unwind [documentation](https://doc.rust-lang.org/std/panic/fn.catch_unwind.html)
171/// for more information.
172///
173/// # Examples
174///
175/// This call won't panic.
176/// ```rust
177/// # use ntest::assert_panics;
178/// # fn main() {
179/// // Other panics can happen before this call.
180/// assert_panics!({panic!("I am panicing")});
181/// # }
182/// ```
183///
184/// This call will panic.
185/// ```should_panic
186/// # use ntest::assert_panics;
187/// # fn main() {
188/// assert_panics!({println!("I am not panicing")});
189/// # }
190/// ```
191#[macro_export]
192macro_rules! assert_panics {
193    ($x:block) => {{
194        let result = std::panic::catch_unwind(|| $x);
195        if !result.is_err() {
196            panic!("assertion failed: code in block did not panic");
197        }
198    }};
199    ($x:block,) => {{
200        assert_panics!($x);
201    }};
202}
203
204#[cfg(test)]
205mod tests {
206    use super::*;
207    #[test]
208    fn assert_true() {
209        assert_true!(true);
210    }
211    #[test]
212    #[should_panic]
213    fn assert_true_fails() {
214        assert_true!(false);
215    }
216    #[test]
217    fn assert_true_trailing_comma() {
218        assert_true!(true,);
219    }
220    #[test]
221    fn assert_false() {
222        assert_false!(false);
223    }
224    #[test]
225    #[should_panic]
226    fn assert_false_fails() {
227        assert_false!(true);
228    }
229    #[test]
230    fn assert_false_trailing_comma() {
231        assert_false!(false,);
232    }
233    #[test]
234    fn assert_panics() {
235        assert_panics!({ panic!("I am panicing!") },);
236    }
237    #[test]
238    #[should_panic]
239    fn assert_panics_fails() {
240        assert_panics!({ println!("I am not panicing!") },);
241    }
242    #[test]
243    fn assert_panics_trailing_comma() {
244        assert_panics!({ panic!("I am panicing!") },);
245    }
246
247    #[test]
248    fn vector() {
249        assert_about_eq!(vec![1.1, 2.1], vec![1.1, 2.1]);
250    }
251
252    #[test]
253    #[should_panic]
254    fn vector_fails() {
255        assert_about_eq!(vec![1.2, 2.1], vec![1.1, 2.1]);
256    }
257
258    #[test]
259    fn vector_trailing_comma() {
260        assert_about_eq!(vec![1.2, 2.1], vec![1.2, 2.1],);
261    }
262
263    #[test]
264    fn vector_trailing_comma_with_epsilon() {
265        assert_about_eq!(vec![1.100000001, 2.1], vec![1.1, 2.1], 0.001f64,);
266    }
267
268    #[test]
269    fn it_should_not_panic_if_values_are_approx_equal() {
270        assert_about_eq!(64f32.sqrt(), 8f32);
271    }
272
273    #[test]
274    fn about_equal_f32() {
275        assert_about_eq!(3f32, 3f32, 1f64);
276    }
277
278    #[test]
279    fn about_equal_f64() {
280        assert_about_eq!(3f64, 3f64);
281    }
282
283    #[test]
284    fn compare_with_epsilon() {
285        assert_about_eq!(42f64, 43f64, 2f64);
286    }
287
288    #[test]
289    #[should_panic]
290    fn fail_with_epsilon() {
291        assert_about_eq!(3f64, 4f64, 1e-8f64);
292    }
293}