extern crate alloc; #[path = "error/backtrace.rs"] mod backtrace; #[path = "error/backtrace_2.rs"] mod backtrace_2; use binrw::Error; #[test] fn custom_err_context() { use binrw::error::ContextExt; #[derive(Debug, Eq, PartialEq)] struct Oops; impl core::fmt::Display for Oops { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Oops") } } let err = Error::Custom { pos: 0, err: Box::new(Oops), } .with_message("nested oops"); assert_eq!(err.custom_err::(), Some(&Oops)); } #[test] fn custom_error_trait() { #[derive(Debug)] struct Oops; impl core::fmt::Display for Oops { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Oops") } } let err = Error::Custom { pos: 0, err: Box::new(Oops), }; match err { Error::Custom { mut err, .. } => { assert!(err.is::()); assert!(!err.is::()); assert!(err.downcast_ref::().is_some()); assert!(err.downcast_ref::().is_none()); assert!(err.downcast_mut::().is_some()); assert!(err.downcast_mut::().is_none()); match err.downcast::() { Ok(_) => panic!("downcast to wrong type"), Err(err) => assert!(err.downcast::().is_ok()), } } _ => unreachable!(), } } #[test] fn display() { let err = format!( "{}", Error::AssertFail { pos: 0x42, message: "Oops".into() } ); assert!(err.contains("0x42")); assert!(err.contains("Oops")); let err = format!( "{}", Error::BadMagic { pos: 0x42, found: Box::new(57005) } ); assert!(err.contains("0x42")); assert!(err.contains("57005")); let err = format!( "{}", Error::Io(binrw::io::Error::new(binrw::io::ErrorKind::Other, "Oops")) ); assert_eq!( err, format!( "{}", binrw::io::Error::new(binrw::io::ErrorKind::Other, "Oops") ) ); #[cfg(feature = "std")] assert!(err.contains("Oops")); #[cfg(not(feature = "std"))] assert!(err.contains("Other")); let err = format!( "{}", Error::Custom { pos: 0x42, err: Box::new("Oops") } ); assert!(err.contains("0x42")); assert!(err.contains("Oops")); let err = format!("{}", Error::NoVariantMatch { pos: 0x42 }); assert!(err.contains("0x42")); let err = format!( "{}", Error::EnumErrors { pos: 0x42, variant_errors: vec![( "BadVariant", Error::AssertFail { pos: 0x84, message: "Oops".into() } )] } ); assert!(err.contains("0x42")); assert!(err.contains("0x84")); assert!(err.contains("BadVariant")); assert!(err.contains("Oops")); } #[test] fn enum_is_eol() { use binrw::{io::Cursor, BinRead}; #[allow(dead_code)] #[derive(BinRead, Debug)] #[br(return_all_errors)] enum Test { A(u32), #[br(assert(self_0 != 0))] B(u16), } assert!(!Test::read_le(&mut Cursor::new(b"\0\0")) .expect_err("accepted bad data") .is_eof()); assert!(Test::read_le(&mut Cursor::new(b"\0")) .expect_err("accepted bad data") .is_eof()); } #[test] fn is_eof() { use binrw::{io::Cursor, BinRead}; #[allow(dead_code)] #[derive(BinRead, Debug)] enum A { A([u8; 2]), B([u8; 1]), } #[derive(BinRead, Debug)] struct Test { _a: A, } assert!(Test::read_le(&mut Cursor::new(b"")) .expect_err("accepted bad data") .is_eof()); } #[test] fn not_custom_error() { let err = Error::AssertFail { pos: 0, message: "Oops".into(), }; assert!(err.custom_err::().is_none()); } #[test] fn no_seek_struct() { use binrw::{ error::BacktraceFrame, io::{Cursor, NoSeek}, BinRead, }; #[derive(BinRead, Debug)] struct Test { #[br(assert(_a == 1))] _a: u32, } let mut data = NoSeek::new(Cursor::new(b"\0\0\0\0")); let error = Test::read_le(&mut data).expect_err("accepted bad data"); match error { Error::Backtrace(bt) => { assert!(matches!(*bt.error, Error::Io(..))); match (&bt.frames[0], &bt.frames[1]) { (BacktraceFrame::Message(m), BacktraceFrame::Custom(e)) => { assert_eq!(m, "rewinding after a failure"); match e.downcast_ref::() { Some(binrw::Error::AssertFail { pos, .. }) => assert_eq!(*pos, 0), _ => panic!("unexpected error"), } } _ => panic!("unexpected error frame layout"), } } _ => panic!("expected backtrace"), } } #[test] fn no_seek_data_enum() { use binrw::{ error::BacktraceFrame, io::{Cursor, NoSeek}, BinRead, }; #[allow(dead_code)] #[derive(BinRead, Debug)] enum Test { #[br(magic(0u8))] A(#[br(assert(self_0 == 1))] u32), #[br(magic(1u8))] B(#[br(assert(self_0 == 2))] u32), } let mut data = NoSeek::new(Cursor::new(b"\0\0\0\0\0")); let error = Test::read_le(&mut data).expect_err("accepted bad data"); match error { Error::Backtrace(bt) => { assert!(matches!(*bt.error, Error::Io(..))); match (&bt.frames[0], &bt.frames[1]) { (BacktraceFrame::Message(m), BacktraceFrame::Custom(e)) => { assert_eq!(m, "rewinding after a failure"); match e.downcast_ref::() { Some(binrw::Error::AssertFail { pos, .. }) => assert_eq!(*pos, 0), e => panic!("unexpected error {:?}", e), } } _ => panic!("unexpected error frame layout"), } } _ => panic!("expected backtrace"), } } #[test] fn no_seek_unit_enum() { use binrw::{ error::BacktraceFrame, io::{Cursor, NoSeek}, BinRead, }; #[derive(BinRead, Debug)] #[br(big, repr = u32)] enum Test { A = 1, B = 2, C = 3, } let mut data = NoSeek::new(Cursor::new(b"\0\0\0\0")); let error = Test::read_le(&mut data).expect_err("accepted bad data"); match error { Error::Backtrace(bt) => { assert!(matches!(*bt.error, Error::Io(..))); match (&bt.frames[0], &bt.frames[1]) { (BacktraceFrame::Message(m), BacktraceFrame::Custom(e)) => { assert_eq!(m, "rewinding after a failure"); match e.downcast_ref::() { Some(binrw::Error::NoVariantMatch { pos }) => assert_eq!(*pos, 0), e => panic!("unexpected error {:?}", e), } } _ => panic!("unexpected error frame layout"), } } _ => panic!("expected backtrace"), } } #[test] fn parse_backtrace_with_empty_comment_lines() { #[derive(binrw::BinRead)] pub struct Test { /// Blank next line has no whitespace… /// …but it is part of the same span, and needs to not crash the /// backtrace formatter _a: u32, } } #[test] fn show_backtrace() { use alloc::borrow::Cow; use binrw::{io::Cursor, BinReaderExt}; let mut x = Cursor::new(b"\x06\0\0\0"); let err = format!( "{}", x.read_le::() .map(|_| ()) .unwrap_err() ); println!("{err}"); assert_eq!( err, if cfg!(feature = "verbose-backtrace") { Cow::Borrowed(if cfg!(nightly) { include_str!("./error/backtrace_verbose_nightly.stderr") } else { include_str!("./error/backtrace_verbose.stderr") }) } else { let bt = include_str!("./error/backtrace.stderr"); if cfg!(feature = "std") { Cow::Borrowed(bt) } else { Cow::Owned(bt.replace("failed to fill whole buffer", "Simple(UnexpectedEof)")) } } ); } #[test] fn show_backtrace_2() { use alloc::borrow::Cow; use binrw::{io::Cursor, BinReaderExt}; let mut x = Cursor::new(b"\x06\0\0\0"); let err = format!( "{}", x.read_le::() .map(|_| ()) .unwrap_err() ); println!("{err}"); assert_eq!( err, if cfg!(feature = "verbose-backtrace") { Cow::Borrowed(if cfg!(nightly) { include_str!("./error/backtrace_2_verbose_nightly.stderr") } else { include_str!("./error/backtrace_2_verbose.stderr") }) } else { let bt = include_str!("./error/backtrace_2.stderr"); if cfg!(feature = "std") { Cow::Borrowed(bt) } else { Cow::Owned(bt.replace("failed to fill whole buffer", "Simple(UnexpectedEof)")) } } ); }