#! [cfg(all(target_pointer_width = "64" , not(feature = "catch-all" )))]
#! [allow(dead_code)]
//! Test that our use of #[track_caller] is making the correct line number
//! show up.
use std::panic;
use std::process::abort;
use std::ptr;
use std::sync::Mutex;
use objc2::encode::Encode;
use objc2::rc::{self , Allocated, Retained};
use objc2::runtime::{self , NSObject};
use objc2::{class, define_class, msg_send, AnyThread, ClassType};
#[ path = "../src/rc/test_object.rs" ]
#[ allow(dead_code)]
mod test_object;
use self ::test_object::RcTestObject;
static EXPECTED_MESSAGE: Mutex<String> = Mutex::new(String::new());
static EXPECTED_LINE: Mutex<u32> = Mutex::new(0 );
struct PanicChecker(());
impl PanicChecker {
fn new() -> Self {
panic::set_hook(Box ::new(|info| {
let expected_message = EXPECTED_MESSAGE.lock().unwrap();
let expected_line = EXPECTED_LINE.lock().unwrap();
let payload = info.payload();
let message = if let Some(payload) = payload.downcast_ref::<&'static str>() {
payload.to_string()
} else if let Some(payload) = payload.downcast_ref::<String>() {
payload.clone()
} else {
format!("could not extract message: {payload:?}" )
};
let location = info.location().expect("location" );
if !message.contains(&*expected_message) {
eprintln!("expected {expected_message:?}, got: {message:?}" );
abort();
}
if location.file() != file!() {
eprintln!("expected file {:?}, got: {:?}" , file!(), location.file());
abort();
}
if location.line() != *expected_line {
eprintln!("expected line {expected_line}, got: {}" , location.line());
abort();
}
}));
Self (())
}
fn assert_panics(&self , message: &str, line: u32, f: impl FnOnce()) {
*EXPECTED_MESSAGE.lock().unwrap() = message.to_string();
*EXPECTED_LINE.lock().unwrap() = line;
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
f();
}));
assert!(res.is_err());
*EXPECTED_MESSAGE.lock().unwrap() = "unknown" .to_string();
*EXPECTED_LINE.lock().unwrap() = 0 ;
}
}
impl Drop for PanicChecker {
fn drop(&mut self ) {
let _ = panic::take_hook();
}
}
#[ test]
#[ cfg_attr(panic = "abort" , ignore = "requires `catch_unwind`" )]
fn test_track_caller() {
let checker = PanicChecker::new();
if cfg!(debug_assertions) {
test_nil(&checker);
if cfg!(not(feature = "disable-encoding-assertions" )) {
test_verify(&checker);
test_error_methods(&checker);
}
}
test_retained_unwrap(&checker);
if cfg!(feature = "catch-all" ) {
test_catch_all(&checker);
}
test_unwind(&checker);
#[ cfg(not(feature = "unstable-static-class" ))]
test_unknown_class(&checker);
}
fn test_nil(checker: &PanicChecker) {
let nil: *mut NSObject = ptr::null_mut();
let msg = "messsaging description to nil" ;
checker.assert_panics(msg, line!() + 1 , || {
let _: *mut NSObject = unsafe { msg_send![nil, description] };
});
checker.assert_panics(msg, line!() + 1 , || {
let _: *mut NSObject = unsafe { msg_send![super (nil, NSObject::class()), description] };
});
checker.assert_panics(msg, line!() + 1 , || {
let _: Option<Retained<NSObject>> = unsafe { msg_send![nil, description] };
});
}
fn test_verify(checker: &PanicChecker) {
let obj = NSObject::new();
let msg = "invalid message send to -[NSObject description]: expected return to have type code '@', but found 'v'" ;
checker.assert_panics(msg, line!() + 1 , || {
let _: () = unsafe { msg_send![&obj, description] };
});
let msg = format!("invalid message send to -[NSObject hash]: expected return to have type code '{}', but found '@'" , usize::ENCODING);
checker.assert_panics(&msg, line!() + 1 , || {
let _: Option<Retained<NSObject>> = unsafe { msg_send![&obj, hash] };
});
}
fn test_error_methods(checker: &PanicChecker) {
let nil: *mut NSObject = ptr::null_mut();
let msg = "messsaging someSelectorWithError: to nil" ;
checker.assert_panics(msg, line!() + 1 , || {
let _: Result<(), Retained<NSObject>> = unsafe { msg_send![nil, someSelectorWithError: _] };
});
checker.assert_panics(msg, line!() + 2 , || {
let _: Result<(), Retained<NSObject>> =
unsafe { msg_send![super (nil, NSObject::class()), someSelectorWithError: _] };
});
checker.assert_panics(msg, line!() + 2 , || {
let _: Result<Retained<NSObject>, Retained<NSObject>> =
unsafe { msg_send![nil, someSelectorWithError: _] };
});
let msg = "invalid message send to -[NSObject someSelectorWithError:]: method not found" ;
checker.assert_panics(msg, line!() + 3 , || {
let obj = RcTestObject::new();
let _: Result<(), Retained<NSObject>> =
unsafe { msg_send![super (&obj), someSelectorWithError: _] };
});
}
fn test_retained_unwrap(checker: &PanicChecker) {
let cls = RcTestObject::class();
let obj = RcTestObject::new();
let msg = "failed creating new instance using +[__RcTestObject newReturningNull]" ;
checker.assert_panics(msg, line!() + 1 , || {
let _obj: Retained<RcTestObject> = unsafe { msg_send![cls, newReturningNull] };
});
let msg = if cfg!(debug_assertions) {
"messsaging init to nil"
} else {
"failed allocating object"
};
checker.assert_panics(msg, line!() + 2 , || {
let obj: Allocated<RcTestObject> = unsafe { msg_send![cls, allocReturningNull] };
let _obj: Retained<RcTestObject> = unsafe { msg_send![obj, init] };
});
let msg = "failed initializing object with -initReturningNull" ;
checker.assert_panics(msg, line!() + 2 , || {
let _obj: Retained<RcTestObject> =
unsafe { msg_send![RcTestObject::alloc(), initReturningNull] };
});
let msg = "failed copying object" ;
checker.assert_panics(msg, line!() + 1 , || {
let _obj: Retained<RcTestObject> = unsafe { msg_send![&obj, copyReturningNull] };
});
let msg = "unexpected NULL returned from -[__RcTestObject methodReturningNull]" ;
checker.assert_panics(msg, line!() + 1 , || {
let _obj: Retained<RcTestObject> = unsafe { msg_send![&obj, methodReturningNull] };
});
}
fn test_catch_all(checker: &PanicChecker) {
let obj: Retained<NSObject> = unsafe { msg_send![class!(NSArray), new] };
let msg = "NSRangeException" ;
checker.assert_panics(msg, line!() + 1 , || {
let _: *mut NSObject = unsafe { msg_send![&obj, objectAtIndex: 0 usize] };
});
let msg = "NSRangeException" ;
checker.assert_panics(msg, line!() + 1 , || {
let _: Retained<NSObject> = unsafe { msg_send![&obj, objectAtIndex: 0 usize] };
});
}
define_class!(
#[ unsafe (super (NSObject))]
struct PanickingClass;
impl PanickingClass {
#[ unsafe (method(panic))]
fn _panic() -> *mut Self {
panic!("panic in PanickingClass" )
}
}
);
fn test_unwind(checker: &PanicChecker) {
let msg = "panic in PanickingClass" ;
let line = line!() - 7 ;
checker.assert_panics(msg, line, || {
let _: *mut NSObject = unsafe { msg_send![PanickingClass::class(), panic] };
});
checker.assert_panics(msg, line, || {
let _: Retained<NSObject> = unsafe { msg_send![PanickingClass::class(), panic] };
});
}
#[ cfg(not(feature = "unstable-static-class" ))]
fn test_unknown_class(checker: &PanicChecker) {
let msg = "class NonExistentClass could not be found" ;
checker.assert_panics(msg, line!() + 1 , || {
let _ = class!(NonExistentClass);
});
}
Messung V0.5 in Prozent C=83 H=86 G=83
¤ Dauer der Verarbeitung: 0.4 Sekunden
¤
*© Formatika GbR, Deutschland