Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

493
vendor/objc2/tests/declare_class.rs vendored Normal file
View File

@@ -0,0 +1,493 @@
#![deny(deprecated, unreachable_code)]
use core::ptr::{self, NonNull};
use objc2::mutability::Immutable;
use objc2::rc::Retained;
use objc2::runtime::NSObject;
use objc2::{declare_class, extern_methods, sel, ClassType, DeclaredClass};
// Test that adding the `deprecated` attribute does not mean that warnings
// when using the method internally are output.
declare_class!(
struct DeclareClassDepreactedMethod;
unsafe impl ClassType for DeclareClassDepreactedMethod {
type Super = NSObject;
type Mutability = Immutable;
const NAME: &'static str = "DeclareClassDepreactedMethod";
}
impl DeclaredClass for DeclareClassDepreactedMethod {}
#[deprecated]
unsafe impl DeclareClassDepreactedMethod {
#[method(deprecatedOnImpl)]
fn deprecated_on_impl() {}
}
unsafe impl DeclareClassDepreactedMethod {
#[deprecated]
#[method(deprecatedOnMethod)]
fn deprecated_on_method() {}
}
);
#[test]
fn test_deprecated() {
let _cls = DeclareClassDepreactedMethod::class();
}
// Test that `cfg` works properly.
//
// We use `debug_assertions` here because it's something that we know our CI
// already tests.
declare_class!(
struct DeclareClassCfg;
unsafe impl ClassType for DeclareClassCfg {
type Super = NSObject;
type Mutability = Immutable;
const NAME: &'static str = "DeclareClassCfg";
}
impl DeclaredClass for DeclareClassCfg {}
unsafe impl DeclareClassCfg {
#[cfg(debug_assertions)]
#[method(changesOnCfg1)]
fn _changes_on_cfg1() -> i32 {
1
}
#[cfg(not(debug_assertions))]
#[method(changesOnCfg1)]
fn _changes_on_cfg1() -> i32 {
2
}
#[cfg(debug_assertions)]
#[method(onlyWhenEnabled1)]
fn _only_when_enabled1(&self) {}
#[cfg(not(debug_assertions))]
#[method(onlyWhenDisabled1)]
fn _only_when_disabled1(&self) {}
}
#[cfg(debug_assertions)]
unsafe impl DeclareClassCfg {
#[method(changesOnCfg2)]
fn _changes_on_cfg2(&self) -> i32 {
1
}
#[method(onlyWhenEnabled2)]
fn _only_when_enabled2() {}
}
#[cfg(not(debug_assertions))]
unsafe impl DeclareClassCfg {
#[method(changesOnCfg2)]
fn _changes_on_cfg2(&self) -> i32 {
2
}
#[method(onlyWhenDisabled2)]
fn _only_when_disabled2() {}
}
#[cfg(debug_assertions)]
unsafe impl DeclareClassCfg {
#[cfg(not(debug_assertions))]
#[method(never)]
fn _never(&self) {}
#[cfg(not(debug_assertions))]
#[method(never)]
fn _never_class() {}
}
);
extern_methods!(
unsafe impl DeclareClassCfg {
#[method_id(new)]
fn new() -> Retained<Self>;
}
unsafe impl DeclareClassCfg {
#[method(changesOnCfg1)]
fn changes_on_cfg1() -> i32;
#[method(changesOnCfg2)]
fn changes_on_cfg2(&self) -> i32;
#[cfg(debug_assertions)]
#[method(onlyWhenEnabled1)]
fn only_when_enabled1(&self);
#[cfg(not(debug_assertions))]
#[method(onlyWhenDisabled1)]
fn only_when_disabled1(&self);
}
#[cfg(debug_assertions)]
unsafe impl DeclareClassCfg {
#[method(onlyWhenEnabled2)]
fn only_when_enabled2();
}
#[cfg(not(debug_assertions))]
unsafe impl DeclareClassCfg {
#[method(onlyWhenDisabled2)]
fn only_when_disabled2();
}
);
#[test]
fn test_method_that_changes_based_on_cfg() {
let expected = if cfg!(debug_assertions) { 1 } else { 2 };
let actual = DeclareClassCfg::changes_on_cfg1();
assert_eq!(expected, actual, "changes_on_cfg1");
let actual = DeclareClassCfg::new().changes_on_cfg2();
assert_eq!(expected, actual, "changes_on_cfg2");
}
#[test]
fn test_method_that_is_only_available_based_on_cfg() {
let cls = DeclareClassCfg::class();
let metacls = cls.metaclass();
let obj = DeclareClassCfg::new();
#[cfg(debug_assertions)]
{
assert!(!cls.responds_to(sel!(onlyWhenDisabled1)));
assert!(!metacls.responds_to(sel!(onlyWhenDisabled2)));
obj.only_when_enabled1();
DeclareClassCfg::only_when_enabled2();
}
#[cfg(not(debug_assertions))]
{
assert!(!cls.responds_to(sel!(onlyWhenEnabled1)));
assert!(!metacls.responds_to(sel!(onlyWhenEnabled2)));
obj.only_when_disabled1();
DeclareClassCfg::only_when_disabled2();
}
}
#[test]
fn test_method_that_is_never_available() {
let cls = DeclareClassCfg::class();
let metacls = cls.metaclass();
assert!(!cls.responds_to(sel!(never)));
assert!(!metacls.responds_to(sel!(never)));
}
declare_class!(
struct TestMultipleColonSelector;
unsafe impl ClassType for TestMultipleColonSelector {
type Super = NSObject;
type Mutability = Immutable;
const NAME: &'static str = "TestMultipleColonSelector";
}
impl DeclaredClass for TestMultipleColonSelector {}
unsafe impl TestMultipleColonSelector {
#[method(test::arg3:)]
fn _test_class(arg1: i32, arg2: i32, arg3: i32) -> i32 {
arg1 + arg2 + arg3
}
#[method(test::arg3:)]
fn _test_instance(&self, arg1: i32, arg2: i32, arg3: i32) -> i32 {
arg1 * arg2 * arg3
}
#[method(test::error:)]
fn _test_error(&self, _arg1: i32, _arg2: i32, _arg3: *mut *mut NSObject) -> bool {
true
}
#[method_id(test:::withObject:)]
fn _test_object(
&self,
_arg1: i32,
_arg2: i32,
_arg3: i32,
_obj: *const Self,
) -> Option<Retained<Self>> {
None
}
}
);
extern_methods!(
unsafe impl TestMultipleColonSelector {
#[method_id(new)]
fn new() -> Retained<Self>;
#[method(test::arg3:)]
fn test_class(arg1: i32, arg2: i32, arg3: i32) -> i32;
#[method(test::arg3:)]
fn test_instance(&self, arg1: i32, arg2: i32, arg3: i32) -> i32;
#[method(test::error:_)]
fn test_error(&self, arg1: i32, arg2: i32) -> Result<(), Retained<NSObject>>;
#[method_id(test:::withObject:)]
fn test_object(
&self,
arg1: i32,
arg2: i32,
arg3: i32,
obj: *const Self,
) -> Option<Retained<Self>>;
}
);
#[test]
fn test_multiple_colon_selector() {
assert_eq!(TestMultipleColonSelector::test_class(2, 3, 4), 9);
let obj = TestMultipleColonSelector::new();
assert_eq!(obj.test_instance(1, 2, 3), 6);
assert!(obj.test_error(1, 2).is_ok());
assert!(obj.test_object(1, 2, 3, ptr::null()).is_none());
}
declare_class!(
struct DeclareClassAllTheBool;
unsafe impl ClassType for DeclareClassAllTheBool {
type Super = NSObject;
type Mutability = Immutable;
const NAME: &'static str = "DeclareClassAllTheBool";
}
impl DeclaredClass for DeclareClassAllTheBool {}
unsafe impl DeclareClassAllTheBool {
#[method(returnsBool)]
fn returns_bool() -> bool {
true
}
#[method(returnsBoolInstance)]
fn returns_bool_instance(&self) -> bool {
true
}
#[method(takesBool:andMut:andUnderscore:)]
fn takes_bool(a: bool, mut b: bool, _: bool) -> bool {
if b {
b = a;
}
b
}
#[method(takesBoolInstance:andMut:andUnderscore:)]
fn takes_bool_instance(&self, a: bool, mut b: bool, _: bool) -> bool {
if b {
b = a;
}
b
}
#[method(takesReturnsBool:)]
fn takes_returns_bool(b: bool) -> bool {
b
}
#[method(takesReturnsBoolInstance:)]
fn takes_returns_bool_instance(&self, b: bool) -> bool {
b
}
#[method_id(idTakesBool:)]
fn id_takes_bool(_b: bool) -> Option<Retained<Self>> {
None
}
#[method_id(idTakesBoolInstance:)]
fn id_takes_bool_instance(&self, _b: bool) -> Option<Retained<Self>> {
None
}
}
);
#[test]
fn test_all_the_bool() {
let _ = DeclareClassAllTheBool::class();
}
declare_class!(
struct DeclareClassUnreachable;
unsafe impl ClassType for DeclareClassUnreachable {
type Super = NSObject;
type Mutability = Immutable;
const NAME: &'static str = "DeclareClassUnreachable";
}
impl DeclaredClass for DeclareClassUnreachable {}
// Ensure none of these warn
unsafe impl DeclareClassUnreachable {
#[method(unreachable)]
fn unreachable(&self) -> bool {
unreachable!()
}
#[method(unreachableClass)]
fn unreachable_class() -> bool {
unreachable!()
}
#[method(unreachableVoid)]
fn unreachable_void(&self) {
unreachable!()
}
#[method(unreachableClassVoid)]
fn unreachable_class_void() {
unreachable!()
}
#[method_id(unreachableId)]
fn unreachable_id(&self) -> Retained<Self> {
unreachable!()
}
#[method_id(unreachableClassId)]
fn unreachable_class_id() -> Retained<Self> {
unreachable!()
}
}
);
#[test]
fn test_unreachable() {
let _ = DeclareClassUnreachable::class();
}
declare_class!(
#[derive(Debug)]
struct OutParam;
unsafe impl ClassType for OutParam {
type Super = NSObject;
type Mutability = Immutable;
const NAME: &'static str = "OutParam";
}
impl DeclaredClass for OutParam {}
unsafe impl OutParam {
#[method(unsupported1:)]
fn _unsupported1(_param: &mut Retained<Self>) {}
#[method(unsupported2:)]
fn _unsupported2(_param: Option<&mut Retained<Self>>) {}
#[method(unsupported3:)]
fn _unsupported3(_param: &mut Option<Retained<Self>>) {}
#[method(unsupported4:)]
fn _unsupported4(_param: Option<&mut Option<Retained<Self>>>) {}
}
);
extern_methods!(
unsafe impl OutParam {
#[method_id(new)]
fn new() -> Retained<Self>;
#[method(unsupported1:)]
fn unsupported1(_param: &mut Retained<Self>);
#[method(unsupported2:)]
fn unsupported2(_param: Option<&mut Retained<Self>>);
#[method(unsupported3:)]
fn unsupported3(_param: &mut Option<Retained<Self>>);
#[method(unsupported4:)]
fn unsupported4(_param: Option<&mut Option<Retained<Self>>>);
}
);
#[test]
#[should_panic = "`&mut Retained<_>` is not supported in `declare_class!` yet"]
#[cfg_attr(
not(all(target_pointer_width = "64", not(feature = "catch-all"))),
ignore = "unwinds through FFI boundary"
)]
fn out_param1() {
let mut param = OutParam::new();
OutParam::unsupported1(&mut param);
}
#[test]
#[should_panic = "`Option<&mut Retained<_>>` is not supported in `declare_class!` yet"]
#[cfg_attr(
not(all(target_pointer_width = "64", not(feature = "catch-all"))),
ignore = "unwinds through FFI boundary"
)]
fn out_param2() {
OutParam::unsupported2(None);
}
#[test]
#[should_panic = "`&mut Option<Retained<_>>` is not supported in `declare_class!` yet"]
#[cfg_attr(
not(all(target_pointer_width = "64", not(feature = "catch-all"))),
ignore = "unwinds through FFI boundary"
)]
fn out_param3() {
let mut param = Some(OutParam::new());
OutParam::unsupported3(&mut param);
}
#[test]
#[should_panic = "`Option<&mut Option<Retained<_>>>` is not supported in `declare_class!` yet"]
#[cfg_attr(
not(all(target_pointer_width = "64", not(feature = "catch-all"))),
ignore = "unwinds through FFI boundary"
)]
fn out_param4() {
OutParam::unsupported4(None);
}
#[test]
fn test_pointer_receiver_allowed() {
declare_class!(
#[derive(Debug)]
struct PointerReceiver;
unsafe impl ClassType for PointerReceiver {
type Super = NSObject;
type Mutability = Immutable;
const NAME: &'static str = "PointerReceiver";
}
impl DeclaredClass for PointerReceiver {}
unsafe impl PointerReceiver {
#[method(constPtr)]
fn const_ptr(_this: *const Self) {}
#[method(mutPtr)]
fn mut_ptr(_this: *mut Self) {}
#[method(nonnullPtr)]
fn nonnull_ptr(_this: NonNull<Self>) {}
}
);
let _ = PointerReceiver::class();
}

View File

@@ -0,0 +1,72 @@
//! To remind myself that `Self` needs to work in methods in `declare_class!`,
//! and hence whenever we name any of the types involved in this, we need to
//! do it in a context where `Self` works.
use objc2::rc::{Allocated, Retained};
use objc2::runtime::NSObject;
use objc2::{declare_class, mutability, ClassType, DeclaredClass};
trait GetSameType {
type SameType: ?Sized;
}
impl<T: ?Sized> GetSameType for T {
type SameType = T;
}
trait GetId {
type IdType;
}
impl<T> GetId for T {
type IdType = Retained<T>;
}
macro_rules! get_self {
() => {
Self
};
}
declare_class!(
struct MyTestObject;
unsafe impl ClassType for MyTestObject {
type Super = NSObject;
type Mutability = mutability::Mutable;
const NAME: &'static str = "MyTestObject";
}
impl DeclaredClass for MyTestObject {}
unsafe impl MyTestObject {
#[method_id(initWith:)]
fn init(
_this: Allocated<<Self as GetSameType>::SameType>,
_param: <*const Self as GetSameType>::SameType,
) -> Retained<<Self as GetSameType>::SameType> {
unimplemented!()
}
#[method(isEqual:)]
fn is_equal(&self, _other: &Self) -> bool {
unimplemented!()
}
#[method_id(test4)]
#[allow(unused_parens)]
fn test4(_this: &<(Self) as GetSameType>::SameType) -> Retained<get_self!()> {
unimplemented!()
}
#[method_id(test5)]
fn test5(&self) -> <Self as GetId>::IdType {
unimplemented!()
}
}
);
#[test]
fn create_class() {
let _ = MyTestObject::class();
}

View File

@@ -0,0 +1,49 @@
use core::mem::ManuallyDrop;
use objc2::msg_send;
use objc2::rc::{autoreleasepool, Retained};
use objc2::runtime::{NSObject, NSObjectProtocol};
fn create_obj() -> Retained<NSObject> {
let obj = ManuallyDrop::new(NSObject::new());
unsafe {
let obj: *mut NSObject = msg_send![&*obj, autorelease];
// All code between the `msg_send!` and the `retain_autoreleased` must
// be able to be optimized away for this to work.
Retained::retain_autoreleased(obj).unwrap()
}
}
#[test]
fn test_retain_autoreleased() {
autoreleasepool(|_| {
// Run once to allow DYLD to resolve the symbol stubs.
// Required for making `retain_autoreleased` work on x86_64.
let _data = create_obj();
// When compiled in release mode / with optimizations enabled,
// subsequent usage of `retain_autoreleased` will succeed in retaining
// the autoreleased value!
#[allow(clippy::if_same_then_else)]
let expected = if cfg!(feature = "gnustep-1-7") {
1
} else if cfg!(all(target_arch = "arm", panic = "unwind")) {
// 32-bit ARM unwinding interferes with the optimization
2
} else if cfg!(any(debug_assertions, feature = "catch-all")) {
2
} else {
1
};
let data = create_obj();
assert_eq!(data.retainCount(), expected);
let data = create_obj();
assert_eq!(data.retainCount(), expected);
// Here we manually clean up the autorelease, so it will always be 1.
let data = autoreleasepool(|_| create_obj());
assert_eq!(data.retainCount(), 1);
});
}

View File

@@ -0,0 +1,86 @@
use objc2::rc::Retained;
use objc2::runtime::{NSObject, NSObjectProtocol};
use objc2::{
declare_class, extern_methods, extern_protocol, mutability, ClassType, DeclaredClass,
ProtocolType,
};
extern_protocol!(
#[allow(clippy::missing_safety_doc)]
unsafe trait Proto: NSObjectProtocol {
#[method(myMethod:)]
fn protocol_method(mtm: MainThreadMarker, arg: i32) -> i32;
#[method_id(myMethodId:)]
fn protocol_method_id(mtm: MainThreadMarker, arg: &Self) -> Retained<Self>;
}
unsafe impl ProtocolType for dyn Proto {
const NAME: &'static str = "MainThreadMarkerTestProtocol";
}
);
declare_class!(
#[derive(PartialEq, Eq, Hash, Debug)]
struct Cls;
unsafe impl ClassType for Cls {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
const NAME: &'static str = "MainThreadMarkerTest";
}
impl DeclaredClass for Cls {}
unsafe impl Proto for Cls {
#[method(myMethod:)]
fn _my_mainthreadonly_method(arg: i32) -> i32 {
arg + 1
}
#[method_id(myMethodId:)]
fn _my_mainthreadonly_method_id(arg: &Self) -> Retained<Self> {
unsafe { Retained::retain(arg as *const Self as *mut Self).unwrap() }
}
}
);
unsafe impl NSObjectProtocol for Cls {}
// The macro does a textual match; but when users actually use
// `objc2_foundation::MainThreadMarker` to ensure soundness, they will not
// do this!
#[derive(Clone, Copy)]
struct MainThreadMarker {
_some_field: u32,
}
extern_methods!(
unsafe impl Cls {
#[method_id(new)]
fn new(mtm: MainThreadMarker) -> Retained<Self>;
#[method(myMethod:)]
fn method(mtm: MainThreadMarker, arg: i32, mtm2: MainThreadMarker) -> i32;
#[method_id(myMethodId:)]
fn method_id(mtm: MainThreadMarker, arg: &Self, mtm2: MainThreadMarker) -> Retained<Self>;
}
);
#[test]
fn call() {
let mtm = MainThreadMarker { _some_field: 0 };
let obj1 = Cls::new(mtm);
let res = Cls::method(mtm, 2, mtm);
assert_eq!(res, 3);
let res = Cls::protocol_method(mtm, 3);
assert_eq!(res, 4);
let obj2 = Cls::method_id(mtm, &obj1, mtm);
assert_eq!(obj1, obj2);
let obj2 = Cls::protocol_method_id(mtm, &obj1);
assert_eq!(obj1, obj2);
}

181
vendor/objc2/tests/no_prelude.rs vendored Normal file
View File

@@ -0,0 +1,181 @@
//! Tests macros in a hostile environment (`#![no_implicit_prelude]` and all
//! functions, modules, traits, and types replaced with custom bogus user
//! replacements).
//!
//! Heavy inspiration for this file taken from `objrs`:
//! https://gitlab.com/objrs/objrs/-/blob/b4f6598696b3fa622e6fddce7aff281770b0a8c2/test/src/no_prelude.rs
#![no_implicit_prelude]
#![allow(dead_code, non_camel_case_types)]
extern crate objc2 as new_objc2;
use new_objc2::{ClassType, DeclaredClass, ProtocolType};
mod core {}
mod std {}
mod libc {}
mod objc2 {}
enum BogusType {}
type u8 = BogusType;
type u16 = BogusType;
type u32 = BogusType;
type u64 = BogusType;
type u128 = BogusType;
type usize = BogusType;
type i8 = BogusType;
type i16 = BogusType;
type i32 = BogusType;
type i64 = BogusType;
type i128 = BogusType;
type isize = BogusType;
type bool = BogusType;
type char = BogusType;
type str = BogusType;
type f32 = BogusType;
type f64 = BogusType;
type Option = BogusType;
type Some = BogusType;
type None = BogusType;
type Result = BogusType;
type Ok = BogusType;
type Err = BogusType;
type Box = BogusType;
type String = BogusType;
type Vec = BogusType;
type drop = BogusType;
type Copy = BogusType;
type Send = BogusType;
type Sized = BogusType;
type Sync = BogusType;
type Drop = BogusType;
type Fn = BogusType;
type FnMut = BogusType;
type FnOnce = BogusType;
type ToOwned = BogusType;
type Clone = BogusType;
type PartialEq = BogusType;
type PartialOrd = BogusType;
type Eq = BogusType;
type Ord = BogusType;
type AsRef = BogusType;
type AsMut = BogusType;
type Into = BogusType;
type From = BogusType;
type Default = BogusType;
type Hash = BogusType;
type Debug = BogusType;
type Iterator = BogusType;
type Extend = BogusType;
type IntoIterator = BogusType;
type DoubleEndedIterator = BogusType;
type ExactSizeIterator = BogusType;
type SliceConcatExt = BogusType;
type ToString = BogusType;
type PhantomData = BogusType;
// Test begin below this line
struct MyCustomIvars {
ivars: i32,
}
new_objc2::declare_class!(
struct CustomObject;
unsafe impl ClassType for CustomObject {
type Super = new_objc2::runtime::NSObject;
type Mutability = new_objc2::mutability::Immutable;
const NAME: &'static str = "CustomObject";
}
impl DeclaredClass for CustomObject {
type Ivars = MyCustomIvars;
}
unsafe impl CustomObject {
#[method(a)]
fn _a() {}
#[method_id(b)]
fn _b() -> new_objc2::rc::Retained<CustomObject> {
::core::unimplemented!()
}
}
);
// Ensure that extern_methods! works without the ClassType trait in scope
mod test_extern_methods {
use super::{new_objc2, CustomObject};
new_objc2::extern_methods!(
unsafe impl CustomObject {
#[method(a)]
fn a();
#[method(b)]
fn b(&self);
}
);
}
new_objc2::extern_class!(
struct NSObject2;
unsafe impl ClassType for NSObject2 {
type Super = new_objc2::runtime::NSObject;
type Mutability = new_objc2::mutability::Immutable;
const NAME: &'static str = "NSObject";
}
);
new_objc2::extern_protocol!(
#[allow(clippy::missing_safety_doc)]
unsafe trait CustomProtocol {
#[method(c)]
fn c(&self);
}
unsafe impl ProtocolType for dyn CustomProtocol {}
);
#[test]
fn test_selector() {
let _sel = new_objc2::sel!(abc);
let _sel = new_objc2::sel!(abc:def:);
}
#[test]
fn test_class() {
let _class = new_objc2::class!(NSObject);
}
fn test_msg_send(obj: &CustomObject) {
let superclass = obj.class().superclass().unwrap();
let _: () = unsafe { new_objc2::msg_send![obj, a] };
let _: () = unsafe { new_objc2::msg_send![obj, a: obj, b: obj] };
let _: () = unsafe { new_objc2::msg_send![super(obj), a] };
let _: () = unsafe { new_objc2::msg_send![super(obj), a: obj, b: obj] };
let _: () = unsafe { new_objc2::msg_send![super(obj, superclass), a] };
let _: () = unsafe { new_objc2::msg_send![super(obj, superclass), a: obj, b: obj] };
}
fn test_msg_send_id(obj: &new_objc2::runtime::AnyObject) {
let _: new_objc2::rc::Retained<new_objc2::runtime::AnyObject> =
unsafe { new_objc2::msg_send_id![obj, a] };
let _: new_objc2::__macro_helpers::Option<
new_objc2::rc::Retained<new_objc2::runtime::AnyObject>,
> = unsafe { new_objc2::msg_send_id![obj, a] };
let _: new_objc2::rc::Retained<new_objc2::runtime::AnyObject> =
unsafe { new_objc2::msg_send_id![obj, a: obj, b: obj] };
}

238
vendor/objc2/tests/track_caller.rs vendored Normal file
View File

@@ -0,0 +1,238 @@
#![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, declare_class, msg_send, msg_send_id, mutability, ClassType, DeclaredClass};
#[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]
fn test_track_caller() {
let checker = PanicChecker::new();
#[cfg(debug_assertions)]
{
test_nil(&checker);
test_verify(&checker);
test_error_methods(&checker);
}
test_id_unwrap(&checker);
#[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_id![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_id![&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_id![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_id_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_id![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_id![cls, allocReturningNull] };
let _obj: Retained<RcTestObject> = unsafe { msg_send_id![obj, init] };
});
let msg = "failed initializing object with -initReturningNull";
checker.assert_panics(msg, line!() + 2, || {
let _obj: Retained<RcTestObject> =
unsafe { msg_send_id![RcTestObject::alloc(), initReturningNull] };
});
let msg = "failed copying object";
checker.assert_panics(msg, line!() + 1, || {
let _obj: Retained<RcTestObject> = unsafe { msg_send_id![&obj, copyReturningNull] };
});
let msg = "unexpected NULL returned from -[__RcTestObject methodReturningNull]";
checker.assert_panics(msg, line!() + 1, || {
let _obj: Retained<RcTestObject> = unsafe { msg_send_id![&obj, methodReturningNull] };
});
}
fn test_catch_all(checker: &PanicChecker) {
let obj: Retained<NSObject> = unsafe { msg_send_id![class!(NSArray), new] };
let msg = "NSRangeException";
checker.assert_panics(msg, line!() + 1, || {
let _: *mut NSObject = unsafe { msg_send![&obj, objectAtIndex: 0usize] };
});
let msg = "NSRangeException";
checker.assert_panics(msg, line!() + 1, || {
let _: Retained<NSObject> = unsafe { msg_send_id![&obj, objectAtIndex: 0usize] };
});
}
declare_class!(
struct PanickingClass;
unsafe impl ClassType for PanickingClass {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
const NAME: &'static str = "PanickingClass";
}
impl DeclaredClass for PanickingClass {}
unsafe impl PanickingClass {
#[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_id![PanickingClass::class(), panic] };
});
}
#[cfg(not(feature = "unstable-static-class"))]
fn test_unknown_class(checker: &PanicChecker) {
let msg = "class NonExistantClass could not be found";
checker.assert_panics(msg, line!() + 1, || {
let _ = class!(NonExistantClass);
});
}

64
vendor/objc2/tests/use_macros.rs vendored Normal file
View File

@@ -0,0 +1,64 @@
use objc2::mutability::Immutable;
use objc2::runtime::{AnyClass, NSObject};
use objc2::{class, declare_class, msg_send, sel, ClassType, DeclaredClass};
declare_class!(
pub struct MyObject;
unsafe impl ClassType for MyObject {
type Super = NSObject;
type Mutability = Immutable;
const NAME: &'static str = "MyObject";
}
impl DeclaredClass for MyObject {}
);
#[test]
fn use_class_and_msg_send() {
unsafe {
let cls = class!(NSObject);
let obj: *mut NSObject = msg_send![cls, new];
let _hash: usize = msg_send![obj, hash];
let _: () = msg_send![obj, release];
}
}
#[test]
fn use_sel() {
let _sel = sel!(description);
let _sel = sel!(setObject:forKey:);
}
pub fn test_msg_send_comma_handling(obj: &MyObject, superclass: &AnyClass) {
unsafe {
let _: () = msg_send![obj, a];
let _: () = msg_send![obj, a,];
let _: () = msg_send![obj, a: 32i32];
let _: () = msg_send![obj, a: 32i32,];
#[cfg_attr(feature = "unstable-msg-send-always-comma", allow(deprecated))]
let _: () = msg_send![obj, a: 32i32 b: 32i32];
let _: () = msg_send![obj, a: 32i32, b: 32i32];
let _: () = msg_send![obj, a: 32i32, b: 32i32,];
}
unsafe {
let _: () = msg_send![super(obj, superclass), a];
let _: () = msg_send![super(obj, superclass), a,];
let _: () = msg_send![super(obj, superclass), a: 32i32];
let _: () = msg_send![super(obj, superclass), a: 32i32,];
#[cfg_attr(feature = "unstable-msg-send-always-comma", allow(deprecated))]
let _: () = msg_send![super(obj, superclass), a: 32i32 b: 32i32];
let _: () = msg_send![super(obj, superclass), a: 32i32, b: 32i32];
let _: () = msg_send![super(obj, superclass), a: 32i32, b: 32i32,];
}
unsafe {
let _: () = msg_send![super(obj), a];
let _: () = msg_send![super(obj), a,];
let _: () = msg_send![super(obj), a: 32i32];
let _: () = msg_send![super(obj), a: 32i32,];
let _: () = msg_send![super(obj), a: 32i32, b: 32i32];
let _: () = msg_send![super(obj), a: 32i32, b: 32i32,];
}
}