common_testing/
assert.rs

1use crate::setup;
2use pretty_assertions::{assert_eq, assert_ne};
3use std::borrow::Cow;
4use std::fmt::Debug;
5use std::io::Cursor;
6use std::ops::Deref;
7
8/// Assert that they share the same AsRef somewhere.
9///
10/// # Example
11///
12/// ```
13/// use common_testing::assert;
14///
15/// #[test]
16/// fn test_1() {
17///   let my_string = "abc";
18///
19///   // When there is more than one AsRef possible, say which one.
20///   assert::ref_equal::<str>(&my_string, &"abc");
21///   assert::ref_equal::<str>(&my_string.to_string(), &"abc".to_string());
22///
23///   // When there is only one AsRef possible, the types are inferred.
24///   assert::ref_equal(&my_string, &b"abc");
25/// }
26/// ```
27#[track_caller]
28pub fn ref_equal<A>(a: &(impl AsRef<A> + Debug), b: &(impl AsRef<A> + Debug))
29where
30  A: Debug + PartialEq + ?Sized,
31{
32  assert_eq!(a.as_ref(), b.as_ref());
33}
34
35/// A trait that can be `R`, `Result<R>`, `Option<R>`, `Result<Option<R>>`, or
36/// `Option<Result<R>>` and can unwrap to return R.
37pub trait Unwrappable<T, R> {
38  type Output: Debug + PartialEq<R>;
39  fn unwrap_into(self) -> Self::Output;
40}
41
42impl<T: Debug + PartialEq + PartialEq<R>, R: Debug + PartialEq<T>> Unwrappable<T, R> for T {
43  type Output = T;
44  fn unwrap_into(self) -> Self::Output {
45    self
46  }
47}
48
49impl<T: Debug + PartialEq + PartialEq<R>, R: Debug + PartialEq<T>> Unwrappable<T, R> for Option<T> {
50  type Output = T;
51  fn unwrap_into(self) -> Self::Output {
52    self.unwrap() // Panics if None, which is suitable for a test failure
53  }
54}
55
56impl<T: Debug + PartialEq + PartialEq<R>, R: Debug + PartialEq<T>, E: std::fmt::Debug> Unwrappable<T, R>
57  for std::result::Result<T, E>
58{
59  type Output = T;
60  fn unwrap_into(self) -> Self::Output {
61    self.unwrap() // Panics if Err, suitable for indicating a test failure
62  }
63}
64
65impl<T: Debug + PartialEq + PartialEq<R>, R: Debug + PartialEq<T>, E: std::fmt::Debug> Unwrappable<T, R>
66  for std::result::Result<Option<T>, E>
67{
68  type Output = T;
69  fn unwrap_into(self) -> Self::Output {
70    self.unwrap().unwrap() // Double unwrap, panics on Err or None
71  }
72}
73
74impl<T: Debug + PartialEq + PartialEq<R>, R: Debug + PartialEq<T>, E: std::fmt::Debug> Unwrappable<T, R>
75  for Option<std::result::Result<T, E>>
76{
77  type Output = T;
78  fn unwrap_into(self) -> Self::Output {
79    self.unwrap().unwrap() // Double unwrap, panics on Err or None
80  }
81}
82
83/// Asserts two values are equal using PartialEq, allowing for different
84/// types to be compared. Automatically unwraps Result and Option, failing
85/// the test if the value is Err or None.  This is useful for removing
86/// boilerplate `.unwrap().unwrap()` calls in tests.
87///
88/// Error message will show the values that were compared using
89/// `pretty_assertions` crate.
90///
91/// # Example
92///
93/// ```
94/// use common_testing::assert;
95///
96/// #[test]
97/// fn test_1() {
98///   let result = "abc";
99///   equal(result, "abc");
100///   equal(&result, &"abc");
101///   equal(5, 5);
102///   equal(&5, &5);
103///   equal(Result::Ok(5), 5);
104///   equal(Option::Some(5), 5);
105///   equal(Result::Ok(Option::Some(5)), 5);
106///   equal(Option::Some(Result::Ok(5)), 5);
107/// }
108/// ```
109#[track_caller]
110pub fn equal<E, T, R>(a: E, b: R)
111where
112  E: Debug + Unwrappable<T, R>,
113  E::Output: Debug + PartialEq<R>,
114  R: Debug + PartialEq + PartialEq<T>,
115{
116  let c = a.unwrap_into();
117  assert_eq!(c, b, "Expected {:?} to equal {:?}.", c, b);
118}
119
120/// Asserts two values are not equal using PartialEq, allowing for
121/// different types to be compared. Automatically unwraps Result and Option,
122/// failing the test if the value is Err or None.  This is useful for removing
123/// boilerplate `.unwrap().unwrap()` calls in tests.
124///
125/// Error message will show the values that were compared using
126/// `pretty_assertions` crate.
127///
128/// # Example
129///
130/// ```
131/// use common_testing::assert;
132///
133/// #[test]
134/// fn test_1() {
135///   let result = "abc";
136///   not_equal(result, "def");
137///   not_equal(result.as_bytes(), b"bcd");
138/// }
139#[track_caller]
140pub fn not_equal<A, T, B>(a: A, b: B)
141where
142  A: Debug + Unwrappable<T, B>,
143  B: Debug + PartialEq<A::Output>,
144{
145  let c = a.unwrap_into();
146  assert_ne!(c, b, "Expected {:?} to not equal {:?}.", c, b);
147}
148
149/// More specific than assert::equal, must be for AsRef<[u8]>. On failure,
150/// the output message will show the hex values of the bytes for easier
151/// debugging of longer byte arrays.
152///
153/// # Example
154///
155/// ```
156/// use common_testing::assert;
157///
158/// #[test]
159/// fn test_1() {
160///  let result = vec![0x01, 0x0E, 0xF3];
161///  assert::equal_bytes(&result, &[0x01, 0x0E, 0xF3]);
162/// }
163#[track_caller]
164pub fn equal_bytes<R, E>(a: &R, b: &E)
165where
166  R: AsRef<[u8]> + ?Sized,
167  E: AsRef<[u8]> + ?Sized,
168{
169  assert_eq!(
170    a.as_ref(),
171    b.as_ref(),
172    "Expected {:02x?} to equal {:02x?}.",
173    a.as_ref(),
174    b.as_ref()
175  );
176}
177
178/// Asserts that the value is equal to the contents of the file. Works
179/// for anything that implements AsRef<[u8]>. This is useful for testing
180/// against large fixtures. The file is read into memory and compared
181/// against the value.
182///
183/// The file is not read until the assertion is run, preventing side
184/// effects from reading the file during test setup or teardown, or from
185/// affecting assertions earlier in the test.
186///
187/// # Example
188///
189/// ```
190/// use common_testing::assert;
191///
192/// #[test]
193/// fn test_1() {
194///  let result1 = "some file contents";
195///  assert::equal_file_contents(&result, "./fixtures/test_file1");
196///
197///  // Works for anything that implements AsRef<[u8]>
198///  let result2 = vec![0x01, 0x0E, 0xF3];
199///  assert::equal_file_contents(&result, "./fixtures/test_file2");
200/// }
201/// ```
202///
203#[track_caller]
204pub fn equal_file_contents<R>(a: &R, path: &str)
205where
206  R: AsRef<[u8]> + ?Sized,
207{
208  let expected = setup::get_file_contents(path).unwrap();
209  ref_equal(&a.as_ref(), &expected);
210}
211
212/// More specific than assert::equal, must be for AsRef<[u8]>.
213///
214/// # Example
215///
216/// ```
217/// use common_testing::assert;
218///
219/// #[test]
220/// fn test_1() {
221///  let result = vec![0x01, 0x0E, 0xF3];
222///  assert::equal_hex_bytes(&result, "010EF3");
223///  // or
224///  assert::equal_hex_bytes(&result, "010ef3");
225/// }
226/// ```
227#[track_caller]
228pub fn equal_hex_bytes<R>(a: &R, b: &str)
229where
230  R: AsRef<[u8]> + ?Sized,
231{
232  let value = hex::encode(a.as_ref());
233  assert_eq!(value, b, "Expected {} to equal {}.", value, b);
234}
235
236/// Assert that the value is some.
237///
238/// # Example
239///
240/// ```
241/// use common_testing::assert;
242///
243/// #[test]
244/// fn test_1() {
245///  let result = Some("abc");
246///  assert::some(&result);
247/// }
248/// ```
249#[track_caller]
250pub fn some<T>(a: &Option<T>)
251where
252  T: Debug,
253{
254  assert!(a.is_some(), "Expected to be some: {:?}", a);
255}
256
257/// Asserts that the value is some and returns the value.
258///
259/// # Example
260///
261/// ```
262/// use common_testing::assert;
263///
264/// #[test]
265/// fn test_1() {
266///  let result = Some("abc");
267///  let some = assert::some_into(result);
268///  assert::equal(some, "abc");
269/// }
270/// ```
271#[track_caller]
272pub fn some_into<T>(a: Option<T>) -> T
273where
274  T: Debug,
275{
276  assert!(a.is_some(), "Expected to be some: {:?}", a);
277  a.unwrap()
278}
279
280/// Assert that the value is none.
281///
282/// # Example
283///
284/// ```
285/// use common_testing::assert;
286///
287/// #[test]
288/// fn test_1() {
289///  let result = None::<&str>;
290///  assert::none(&result);
291/// }
292/// ```
293#[track_caller]
294pub fn none<T>(a: &Option<T>)
295where
296  T: Debug,
297{
298  assert!(a.is_none(), "Expected to be none: {:?}", a);
299}
300
301/// Assert that the value is ok.
302///
303/// # Example
304///
305/// ```
306/// use common_testing::assert;
307///
308/// #[test]
309/// fn test_1() {
310///  let result = Ok("abc");
311///  assert::ok(&result);
312/// }
313/// ```
314#[track_caller]
315pub fn ok<T, E>(a: &Result<T, E>)
316where
317  T: Debug,
318  E: Debug,
319{
320  assert!(a.is_ok(), "Expected to be ok: {:?}", a);
321}
322
323/// Asserts that the value is ok and returns the value.
324///
325/// # Example
326///
327/// ```
328/// use common_testing::assert;
329///
330/// #[test]
331/// fn test_1() {
332///  let result = Ok("abc");
333///  let ok = assert::ok_into(a);
334///  assert::equal(ok, "abc");
335/// }
336///
337#[track_caller]
338pub fn ok_into<T, E>(a: Result<T, E>) -> T
339where
340  T: Debug,
341  E: Debug,
342{
343  assert!(a.is_ok(), "Expected to be ok: {:?}", a);
344  a.unwrap()
345}
346
347/// Assert that the value is err.
348///
349/// # Example
350///
351/// ```
352/// use common_testing::assert;
353///
354/// #[test]
355/// fn test_1() {
356///  let result = Err("abc");
357///  assert::err(&result);
358/// }
359/// ```
360#[track_caller]
361pub fn err<T, E>(a: &Result<T, E>)
362where
363  T: Debug,
364  E: Debug,
365{
366  assert!(a.is_err(), "Expected to be err: {:?}", a);
367}
368
369/// Asserts that the value is err and returns the value.
370///
371/// # Example
372///
373/// ```
374/// use common_testing::assert;
375///
376/// #[test]
377/// fn test_1() {
378///  let result = Err("abc");
379///  let err = assert::err_into(a);
380///  assert::equal(err, "abc");
381/// }
382#[track_caller]
383pub fn err_into<T, E>(a: Result<T, E>) -> E
384where
385  T: Debug,
386  E: Debug,
387{
388  assert!(a.is_err(), "Expected to be err: {:?}", a);
389  a.unwrap_err()
390}
391
392/// Asserts that the value is default.
393///
394/// # Example
395///
396/// ```
397/// use common_testing::assert;
398///
399/// #[test]
400/// fn test_1() {
401///  let a = 0;
402///  assert::default(&a);
403/// }
404/// ```
405#[track_caller]
406pub fn default<R>(a: &R)
407where
408  R: Default + Debug + PartialEq + ?Sized,
409{
410  assert_eq!(a, &R::default());
411}
412
413/// Asserts that the value implements Cow and is borrowed.
414/// This is useful for testing that a Cow is not cloned.
415///
416/// # Example
417///
418/// ```
419/// use std::borrow::Cow;
420/// use common_testing::assert;
421///
422/// #[test]
423/// fn test_1() {
424///  let a = Cow::Borrowed("abc");
425///  assert::borrowed(&a);
426/// }
427/// ```
428#[allow(clippy::ptr_arg)]
429#[track_caller]
430pub fn borrowed<R>(a: &Cow<'_, R>)
431where
432  R: Debug + PartialEq + ToOwned + ?Sized,
433{
434  assert!(
435    match a {
436      Cow::Borrowed(_) => true,
437      Cow::Owned(_) => false,
438    },
439    "Expected {:?} to be borrowed",
440    a.deref(),
441  );
442}
443
444/// Asserts that the value implements Cow and is owned.
445/// This is useful for testing that a Cow is cloned.
446///
447/// # Example
448///
449/// ```
450/// use std::borrow::Cow;
451/// use common_testing::assert;
452///
453/// #[test]
454/// fn test_1() {
455///  let a = Cow::Owned("abc".to_string());
456///  assert::owned(&a);
457/// }
458/// ```
459#[allow(clippy::ptr_arg)]
460#[track_caller]
461pub fn owned<R>(a: &Cow<'_, R>)
462where
463  R: Debug + PartialEq + ToOwned + ?Sized,
464{
465  assert!(
466    match a {
467      Cow::Borrowed(_) => false,
468      Cow::Owned(_) => true,
469    },
470    "Expected {:?} to be owned",
471    a.deref(),
472  );
473}
474
475/// Asserts cursor position has reached the end. This is useful for testing
476/// that a cursor has been completely consumed.
477///
478/// # Example
479///
480/// ```
481/// use std::io::Cursor;
482/// use common_testing::assert;
483///
484/// #[test]
485/// fn test_1() {
486///  let cursor = Cursor::new("abc");
487///  assert::cursor_completely_consumed(&cursor);
488/// }
489/// ```
490#[track_caller]
491pub fn cursor_completely_consumed<T>(cursor: &Cursor<T>)
492where
493  T: AsRef<[u8]>,
494{
495  assert_eq!(
496    cursor.position(),
497    cursor.get_ref().as_ref().len() as u64,
498    "Cursor was not completely consumed"
499  );
500}
501
502#[cfg(test)]
503mod tests {
504  use super::*;
505  use std::io::Result;
506
507  #[test]
508  fn test_ref_equal() {
509    let my_string = "abc";
510
511    // When there is more than one AsRef possible, say which one.
512    ref_equal::<str>(&my_string, &"abc");
513    ref_equal::<str>(&my_string.to_string(), &"abc".to_string());
514
515    // When there is only one AsRef possible, the types are inferred.
516    ref_equal(&my_string, &b"abc");
517  }
518
519  #[test]
520  fn test_equal() {
521    let result = "abc";
522    equal(result, "abc");
523    equal(&result, &"abc");
524    pretty_assertions::assert_eq!("abc".to_string(), "abc");
525    equal("abc".to_string(), "abc");
526    equal(5, 5);
527    equal(&5, &5);
528    equal(b"abc".as_slice(), b"abc".to_vec());
529    equal(Option::Some(Result::Ok(b"abc".to_vec())), b"abc".to_vec());
530    equal(Option::Some(Result::Ok(b"abc".as_slice())), b"abc".to_vec());
531    equal(Result::Ok(Option::Some(b"abc".to_vec())), b"abc".to_vec());
532    equal(Result::Ok(Option::Some(b"abc".as_slice())), b"abc".to_vec());
533    equal(Result::Ok(b"abc".to_vec()), b"abc".to_vec());
534    equal(Result::Ok(b"abc".to_vec()), b"abc".as_slice());
535    equal(Option::Some(b"abc".to_vec()), b"abc".to_vec());
536    equal(Option::Some(b"abc".to_vec()), b"abc".as_slice());
537    equal(Result::Ok(5), 5);
538    equal(Option::Some(5), 5);
539    equal(Result::Ok(Option::Some(5)), 5);
540    equal(Option::Some(Result::Ok(5)), 5);
541  }
542
543  #[test]
544  fn test_not_equal() {
545    let result = "abc";
546    not_equal(result, "def");
547    not_equal(result.as_bytes(), b"bcd");
548    pretty_assertions::assert_ne!("abc".to_string(), "def");
549    not_equal("abc".to_string(), "def");
550    not_equal(5, 4);
551    not_equal(&5, &4);
552    not_equal(Option::Some(Result::Ok(b"abc".to_vec())), b"def".to_vec());
553    not_equal(Option::Some(Result::Ok(b"abc".as_slice())), b"def".to_vec());
554    not_equal(Result::Ok(Option::Some(b"abc".to_vec())), b"def".to_vec());
555    not_equal(Result::Ok(Option::Some(b"abc".as_slice())), b"def".to_vec());
556    not_equal(Result::Ok(b"abc".to_vec()), b"def".to_vec());
557    not_equal(Result::Ok(b"abc".to_vec()), b"def".as_slice());
558    not_equal(Option::Some(b"abc".to_vec()), b"def".to_vec());
559    not_equal(Option::Some(b"abc".to_vec()), b"def".as_slice());
560    not_equal(Result::Ok(5), 4);
561    not_equal(Option::Some(5), 4);
562    not_equal(Result::Ok(Option::Some(5)), 4);
563    not_equal(Option::Some(Result::Ok(5)), 4);
564  }
565
566  #[test]
567  fn test_equal_bytes() {
568    let result = vec![0x01, 0x0E, 0xF3];
569    equal_bytes(&result, &[0x01, 0x0E, 0xF3]);
570  }
571
572  #[test]
573  fn test_equal_file_contents() {
574    let result1 = "some file content\n";
575    equal_file_contents(&result1, "./fixtures/test.txt");
576  }
577
578  #[test]
579  fn test_equal_hex_bytes() {
580    let result = vec![0x01, 0x0E, 0xF3];
581    equal_hex_bytes(&result, "010ef3");
582  }
583
584  #[test]
585  fn test_some() {
586    let result = Some("abc");
587    some(&result);
588  }
589
590  #[test]
591  fn test_some_into() {
592    let result = Some("abc");
593    let some = some_into(result);
594    equal(some, "abc");
595  }
596
597  #[test]
598  fn test_none() {
599    let result = None::<&str>;
600    none(&result);
601  }
602
603  #[test]
604  fn test_ok() {
605    let result = Result::Ok("abc");
606    ok(&result);
607  }
608
609  #[test]
610  fn test_ok_into() {
611    let result = Result::Ok("abc");
612    let ok = ok_into(result);
613    equal(ok, "abc");
614  }
615}