| use super::{const_io_error, Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage}; |
| use crate::assert_matches::assert_matches; |
| use crate::error; |
| use crate::fmt; |
| use crate::mem::size_of; |
| use crate::sys::decode_error_kind; |
| use crate::sys::os::error_string; |
| |
| #[test] |
| fn test_size() { |
| assert!(size_of::<Error>() <= size_of::<[usize; 2]>()); |
| } |
| |
| #[test] |
| fn test_debug_error() { |
| let code = 6; |
| let msg = error_string(code); |
| let kind = decode_error_kind(code); |
| let err = Error { |
| repr: Repr::new_custom(Box::new(Custom { |
| kind: ErrorKind::InvalidInput, |
| error: Box::new(Error { repr: super::Repr::new_os(code) }), |
| })), |
| }; |
| let expected = format!( |
| "Custom {{ \ |
| kind: InvalidInput, \ |
| error: Os {{ \ |
| code: {:?}, \ |
| kind: {:?}, \ |
| message: {:?} \ |
| }} \ |
| }}", |
| code, kind, msg |
| ); |
| assert_eq!(format!("{err:?}"), expected); |
| } |
| |
| #[test] |
| fn test_downcasting() { |
| #[derive(Debug)] |
| struct TestError; |
| |
| impl fmt::Display for TestError { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| f.write_str("asdf") |
| } |
| } |
| |
| impl error::Error for TestError {} |
| |
| // we have to call all of these UFCS style right now since method |
| // resolution won't implicitly drop the Send+Sync bounds |
| let mut err = Error::new(ErrorKind::Other, TestError); |
| assert!(err.get_ref().unwrap().is::<TestError>()); |
| assert_eq!("asdf", err.get_ref().unwrap().to_string()); |
| assert!(err.get_mut().unwrap().is::<TestError>()); |
| let extracted = err.into_inner().unwrap(); |
| extracted.downcast::<TestError>().unwrap(); |
| } |
| |
| #[test] |
| fn test_const() { |
| const E: Error = const_io_error!(ErrorKind::NotFound, "hello"); |
| |
| assert_eq!(E.kind(), ErrorKind::NotFound); |
| assert_eq!(E.to_string(), "hello"); |
| assert!(format!("{E:?}").contains("\"hello\"")); |
| assert!(format!("{E:?}").contains("NotFound")); |
| } |
| |
| #[test] |
| fn test_os_packing() { |
| for code in -20i32..20i32 { |
| let e = Error::from_raw_os_error(code); |
| assert_eq!(e.raw_os_error(), Some(code)); |
| assert_matches!( |
| e.repr.data(), |
| ErrorData::Os(c) if c == code, |
| ); |
| } |
| } |
| |
| #[test] |
| fn test_errorkind_packing() { |
| assert_eq!(Error::from(ErrorKind::NotFound).kind(), ErrorKind::NotFound); |
| assert_eq!(Error::from(ErrorKind::PermissionDenied).kind(), ErrorKind::PermissionDenied); |
| assert_eq!(Error::from(ErrorKind::Uncategorized).kind(), ErrorKind::Uncategorized); |
| // Check that the innards look like what we want. |
| assert_matches!( |
| Error::from(ErrorKind::OutOfMemory).repr.data(), |
| ErrorData::Simple(ErrorKind::OutOfMemory), |
| ); |
| } |
| |
| #[test] |
| fn test_simple_message_packing() { |
| use super::{ErrorKind::*, SimpleMessage}; |
| macro_rules! check_simple_msg { |
| ($err:expr, $kind:ident, $msg:literal) => {{ |
| let e = &$err; |
| // Check that the public api is right. |
| assert_eq!(e.kind(), $kind); |
| assert!(format!("{e:?}").contains($msg)); |
| // and we got what we expected |
| assert_matches!( |
| e.repr.data(), |
| ErrorData::SimpleMessage(SimpleMessage { kind: $kind, message: $msg }) |
| ); |
| }}; |
| } |
| |
| let not_static = const_io_error!(Uncategorized, "not a constant!"); |
| check_simple_msg!(not_static, Uncategorized, "not a constant!"); |
| |
| const CONST: Error = const_io_error!(NotFound, "definitely a constant!"); |
| check_simple_msg!(CONST, NotFound, "definitely a constant!"); |
| |
| static STATIC: Error = const_io_error!(BrokenPipe, "a constant, sort of!"); |
| check_simple_msg!(STATIC, BrokenPipe, "a constant, sort of!"); |
| } |
| |
| #[derive(Debug, PartialEq)] |
| struct Bojji(bool); |
| impl error::Error for Bojji {} |
| impl fmt::Display for Bojji { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| write!(f, "ah! {:?}", self) |
| } |
| } |
| |
| #[test] |
| fn test_custom_error_packing() { |
| use super::Custom; |
| let test = Error::new(ErrorKind::Uncategorized, Bojji(true)); |
| assert_matches!( |
| test.repr.data(), |
| ErrorData::Custom(Custom { |
| kind: ErrorKind::Uncategorized, |
| error, |
| }) if error.downcast_ref::<Bojji>().as_deref() == Some(&Bojji(true)), |
| ); |
| } |
| |
| #[derive(Debug)] |
| struct E; |
| |
| impl fmt::Display for E { |
| fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| Ok(()) |
| } |
| } |
| |
| impl error::Error for E {} |
| |
| #[test] |
| fn test_std_io_error_downcast() { |
| // Case 1: custom error, downcast succeeds |
| let io_error = Error::new(ErrorKind::Other, Bojji(true)); |
| let e: Box<Bojji> = io_error.downcast().unwrap(); |
| assert!(e.0); |
| |
| // Case 2: custom error, downcast fails |
| let io_error = Error::new(ErrorKind::Other, Bojji(true)); |
| let io_error = io_error.downcast::<E>().unwrap_err(); |
| |
| // ensures that the custom error is intact |
| assert_eq!(ErrorKind::Other, io_error.kind()); |
| let e: Box<Bojji> = io_error.downcast().unwrap(); |
| assert!(e.0); |
| |
| // Case 3: os error |
| let errno = 20; |
| let io_error = Error::from_raw_os_error(errno); |
| let io_error = io_error.downcast::<E>().unwrap_err(); |
| |
| assert_eq!(errno, io_error.raw_os_error().unwrap()); |
| |
| // Case 4: simple |
| let kind = ErrorKind::OutOfMemory; |
| let io_error: Error = kind.into(); |
| let io_error = io_error.downcast::<E>().unwrap_err(); |
| |
| assert_eq!(kind, io_error.kind()); |
| |
| // Case 5: simple message |
| const SIMPLE_MESSAGE: SimpleMessage = |
| SimpleMessage { kind: ErrorKind::Other, message: "simple message error test" }; |
| let io_error = Error::from_static_message(&SIMPLE_MESSAGE); |
| let io_error = io_error.downcast::<E>().unwrap_err(); |
| |
| assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind()); |
| assert_eq!(SIMPLE_MESSAGE.message, &*format!("{io_error}")); |
| } |