From 0a7328760ba260dcb7338d64c22c4200bc8773ee Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 6 Sep 2023 19:52:21 +0200 Subject: [PATCH 1/3] Slightly improve NSMutableSet and NSMutableDictionary testing --- crates/icrate/tests/mutable_dictionary.rs | 41 ++++++++++++++++++++--- crates/icrate/tests/mutable_set.rs | 14 ++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/crates/icrate/tests/mutable_dictionary.rs b/crates/icrate/tests/mutable_dictionary.rs index d7fca34ff..83ed4ba67 100644 --- a/crates/icrate/tests/mutable_dictionary.rs +++ b/crates/icrate/tests/mutable_dictionary.rs @@ -67,16 +67,47 @@ fn test_insert() { } #[test] -fn test_insert_retain_release() { +fn test_insert_key_copies() { let mut dict = NSMutableDictionary::new(); - dict.insert(NSNumber::new_i32(1), __RcTestObject::new()); + let key1 = __RcTestObject::new(); let mut expected = __ThreadTestData::current(); - let old = dict.insert(NSNumber::new_i32(1), __RcTestObject::new()); + let _ = dict.insert(key1, NSNumber::new_i32(1)); + // Create copy + expected.copy += 1; expected.alloc += 1; expected.init += 1; - expected.retain += 2; - expected.release += 2; + + // Release passed-in key + expected.release += 1; + expected.dealloc += 1; + expected.assert_current(); + + dict.removeAllObjects(); + // Release key + expected.release += 1; + expected.dealloc += 1; + expected.assert_current(); +} + +#[test] +fn test_insert_value_retain_release() { + let mut dict = NSMutableDictionary::new(); + dict.insert(NSNumber::new_i32(1), __RcTestObject::new()); + let to_insert = __RcTestObject::new(); + let mut expected = __ThreadTestData::current(); + + let old = dict.insert(NSNumber::new_i32(1), to_insert); + // Grab old value + expected.retain += 1; + + // Dictionary takes new value and overwrites the old one + expected.retain += 1; + expected.release += 1; + + // Release passed-in `Id` + expected.release += 1; + expected.assert_current(); drop(old); diff --git a/crates/icrate/tests/mutable_set.rs b/crates/icrate/tests/mutable_set.rs index a94b94b37..ba47c3310 100644 --- a/crates/icrate/tests/mutable_set.rs +++ b/crates/icrate/tests/mutable_set.rs @@ -1,6 +1,7 @@ #![cfg(feature = "Foundation_NSMutableSet")] #![cfg(feature = "Foundation_NSString")] use objc2::rc::{__RcTestObject, __ThreadTestData}; +use objc2::ClassType; use icrate::Foundation::{self, ns_string, NSMutableSet, NSSet, NSString}; @@ -79,17 +80,30 @@ fn test_insert_retain_release() { let mut set = NSMutableSet::new(); let obj1 = __RcTestObject::new(); let obj2 = __RcTestObject::new(); + let obj2_copy = obj2.retain(); let mut expected = __ThreadTestData::current(); set.insert(obj1); + // Retain to store in set expected.retain += 1; + // Release passed in object expected.release += 1; expected.assert_current(); assert_eq!(set.len(), 1); assert_eq!(set.get_any(), set.get_any()); set.insert(obj2); + // Retain to store in set expected.retain += 1; + // Release passed in object + expected.release += 1; + expected.assert_current(); + assert_eq!(set.len(), 2); + + set.insert(obj2_copy); + // No retain, since the object is already in the set + expected.retain += 0; + // Release passed in object expected.release += 1; expected.assert_current(); assert_eq!(set.len(), 2); From 391134ac53ea8ea688d58849fa34ac3979d09cce Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 7 Sep 2023 04:19:15 +0200 Subject: [PATCH 2/3] Improve our testing of core functionality on NSObjectProtocol --- crates/objc2/src/declare/mod.rs | 60 ++++++++++++++++++++++++++-- crates/objc2/src/runtime/nsobject.rs | 11 +++++ 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/crates/objc2/src/declare/mod.rs b/crates/objc2/src/declare/mod.rs index 2dd45eb60..c3024cceb 100644 --- a/crates/objc2/src/declare/mod.rs +++ b/crates/objc2/src/declare/mod.rs @@ -760,12 +760,15 @@ impl Drop for ProtocolBuilder { #[cfg(test)] mod tests { + use core::hash::Hasher; + use std::collections::hash_map::DefaultHasher; + use std::hash::Hash; + use super::*; use crate::mutability::Immutable; use crate::rc::Id; - use crate::runtime::{NSObject, NSZone, __NSCopying as NSCopying}; - use crate::test_utils; - use crate::{declare_class, msg_send, ClassType, ProtocolType}; + use crate::runtime::{NSObject, NSObjectProtocol, NSZone, __NSCopying as NSCopying}; + use crate::{declare_class, extern_methods, msg_send, test_utils, ClassType, ProtocolType}; #[test] fn test_alignment() { @@ -1157,4 +1160,55 @@ mod tests { let _ = GenericDeclareClass::<()>::class(); } + + #[test] + fn test_inherited_nsobject_methods_work() { + declare_class!( + #[derive(Debug, PartialEq, Eq, Hash)] + struct Custom; + + unsafe impl ClassType for Custom { + type Super = NSObject; + type Mutability = Immutable; + const NAME: &'static str = "TestInheritedNSObjectMethodsWork"; + } + ); + + extern_methods!( + unsafe impl Custom { + #[method_id(new)] + fn new() -> Id; + } + ); + + let obj1 = Custom::new(); + let obj2 = Custom::new(); + + // isEqual: + assert_eq!(obj1, obj1); + assert_ne!(obj1, obj2); + + // description + let expected = + format!("Custom {{ __superclass: ManuallyDrop {{ value: }} }}"); + assert_eq!(format!("{obj1:?}"), expected); + + // hash + let mut hashstate1 = DefaultHasher::new(); + let mut hashstate2 = DefaultHasher::new(); + + obj1.hash(&mut hashstate1); + obj1.hash(&mut hashstate2); + + assert_eq!(hashstate1.finish(), hashstate2.finish()); + + let mut hashstate2 = DefaultHasher::new(); + obj2.hash(&mut hashstate2); + assert_ne!(hashstate1.finish(), hashstate2.finish()); + + // isKindOfClass: + assert!(obj1.is_kind_of::()); + assert!(obj1.is_kind_of::()); + assert!(obj1.is_kind_of::()); + } } diff --git a/crates/objc2/src/runtime/nsobject.rs b/crates/objc2/src/runtime/nsobject.rs index 4027ae4da..549c330b0 100644 --- a/crates/objc2/src/runtime/nsobject.rs +++ b/crates/objc2/src/runtime/nsobject.rs @@ -357,4 +357,15 @@ mod tests { assert!(obj.is_kind_of::()); assert!(obj.is_kind_of::<__RcTestObject>()); } + + #[test] + fn test_retain_same() { + let obj1 = NSObject::new(); + let ptr1 = Id::as_ptr(&obj1); + + let obj2 = obj1.clone(); + let ptr2 = Id::as_ptr(&obj2); + + assert_eq!(ptr1, ptr2); + } } From 70300502aaa5e8cce80dddfd43217333387d04c9 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 7 Sep 2023 04:19:35 +0200 Subject: [PATCH 3/3] Add additional exception test --- crates/objc2/src/exception.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/crates/objc2/src/exception.rs b/crates/objc2/src/exception.rs index d3e399f91..7db1b9392 100644 --- a/crates/objc2/src/exception.rs +++ b/crates/objc2/src/exception.rs @@ -298,8 +298,10 @@ pub unsafe fn catch( mod tests { use alloc::format; use alloc::string::ToString; + use core::panic::AssertUnwindSafe; use super::*; + use crate::msg_send_id; use crate::runtime::NSObject; #[test] @@ -332,6 +334,27 @@ mod tests { assert!(result.unwrap_err().is_none()); } + #[test] + #[cfg_attr( + feature = "catch-all", + ignore = "Panics inside `catch` when catch-all is enabled" + )] + fn test_catch_unknown_selector() { + let obj = AssertUnwindSafe(NSObject::new()); + let ptr = Id::as_ptr(&obj); + let result = unsafe { + catch(|| { + let _: Id = msg_send_id![&*obj, copy]; + }) + }; + let err = result.unwrap_err().unwrap(); + + assert_eq!( + format!("{err}"), + format!("-[NSObject copyWithZone:]: unrecognized selector sent to instance {ptr:?}"), + ); + } + #[test] fn test_throw_catch_object() { let obj = NSObject::new();