Skip to content

Commit

Permalink
Test reference counting in NSValue, NSArray and NSMutableArray
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Jul 31, 2022
1 parent 6582c3a commit f1f12ec
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 145 deletions.
43 changes: 18 additions & 25 deletions objc2/src/__macro_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,6 @@ mod tests {
use crate::rc::{Allocated, Owned, RcTestObject, Shared, ThreadTestData};
use crate::runtime::Object;
use crate::{class, msg_send_id};
use crate::{Encoding, RefEncode};

#[test]
fn test_macro_alloc() {
Expand All @@ -273,30 +272,15 @@ mod tests {
}

#[test]
#[cfg_attr(
all(feature = "gnustep-1-7", feature = "verify_message"),
ignore = "NSZone's encoding is quite complex on GNUStep"
)]
fn test_alloc_with_zone() {
#[repr(C)]
struct _NSZone {
_inner: [u8; 0],
}

unsafe impl RefEncode for _NSZone {
const ENCODING_REF: Encoding<'static> =
Encoding::Pointer(&Encoding::Struct("_NSZone", &[]));
}

let expected = ThreadTestData::current();
use crate::foundation::NSZone;
let mut expected = ThreadTestData::current();
let cls = RcTestObject::class();

let zone: *const _NSZone = ptr::null();
let zone: *const NSZone = ptr::null();
let _obj: Id<Allocated<RcTestObject>, Owned> =
unsafe { msg_send_id![cls, allocWithZone: zone].unwrap() };
// `+[NSObject alloc]` delegates to `+[NSObject allocWithZone:]`, but
// `RcTestObject` only catches `alloc`.
// expected.alloc += 1;
expected.alloc += 1;
expected.assert_current();
}

Expand Down Expand Up @@ -339,9 +323,18 @@ mod tests {
expected.init += 1;
expected.assert_current();

// TODO:
// let copy: Id<RcTestObject, Shared> = unsafe { msg_send_id![&obj, copy].unwrap() };
// let mutable_copy: Id<RcTestObject, Shared> = unsafe { msg_send_id![&obj, mutableCopy].unwrap() };
let _copy: Id<RcTestObject, Shared> = unsafe { msg_send_id![&obj, copy].unwrap() };
expected.copy += 1;
expected.alloc += 1;
expected.init += 1;
expected.assert_current();

let _mutable_copy: Id<RcTestObject, Shared> =
unsafe { msg_send_id![&obj, mutableCopy].unwrap() };
expected.mutable_copy += 1;
expected.alloc += 1;
expected.init += 1;
expected.assert_current();

let _self: Id<RcTestObject, Shared> = unsafe { msg_send_id![&obj, self].unwrap() };
expected.retain += 1;
Expand All @@ -351,8 +344,8 @@ mod tests {
unsafe { msg_send_id![&obj, description] };
expected.assert_current();
});
expected.release += 3;
expected.dealloc += 2;
expected.release += 5;
expected.dealloc += 4;
expected.assert_current();
}

Expand Down
79 changes: 63 additions & 16 deletions objc2/src/foundation/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ mod tests {

use super::*;
use crate::foundation::{NSNumber, NSString};
use crate::rc::autoreleasepool;
use crate::rc::{RcTestObject, ThreadTestData};

fn sample_array(len: usize) -> Id<NSArray<NSObject, Owned>, Owned> {
let mut vec = Vec::with_capacity(len);
Expand All @@ -262,10 +262,6 @@ mod tests {
NSArray::from_vec(vec)
}

fn retain_count(obj: &NSObject) -> usize {
unsafe { msg_send![obj, retainCount] }
}

#[test]
fn test_two_empty() {
let _empty_array1 = NSArray::<NSObject, _>::new();
Expand Down Expand Up @@ -317,25 +313,76 @@ mod tests {
}

#[test]
fn test_get_does_not_autorelease() {
let obj: Id<_, Shared> = NSObject::new().into();
fn test_retains_stored() {
let obj = Id::from_owned(RcTestObject::new());
let mut expected = ThreadTestData::current();

let input = [obj.clone(), obj.clone()];
expected.retain += 2;
expected.assert_current();

assert_eq!(retain_count(&obj), 1);
let array = NSArray::from_slice(&input);
expected.retain += 2;
expected.assert_current();

let array = NSArray::from_slice(&[obj.clone()]);
let _obj = array.first().unwrap();
expected.assert_current();

assert_eq!(retain_count(&obj), 2);
drop(array);
expected.release += 2;
expected.assert_current();

autoreleasepool(|_pool| {
let obj2 = array.first().unwrap();
assert_eq!(retain_count(obj2), 2);
});
let array = NSArray::from_vec(Vec::from(input));
expected.retain += 2;
expected.release += 2;
expected.assert_current();

assert_eq!(retain_count(&obj), 2);
let _obj = array.get(0).unwrap();
let _obj = array.get(1).unwrap();
assert!(array.get(2).is_none());
expected.assert_current();

drop(array);
expected.release += 2;
expected.assert_current();

drop(obj);
expected.release += 1;
expected.dealloc += 1;
expected.assert_current();
}

assert_eq!(retain_count(&obj), 1);
#[test]
fn test_nscopying_uses_retain() {
let obj = Id::from_owned(RcTestObject::new());
let array = NSArray::from_slice(&[obj]);
let mut expected = ThreadTestData::current();

let _copy = array.copy();
expected.assert_current();

let _copy = array.mutable_copy();
expected.retain += 1;
expected.assert_current();
}

#[test]
fn test_iter_no_retain() {
let obj = Id::from_owned(RcTestObject::new());
let array = NSArray::from_slice(&[obj]);
let mut expected = ThreadTestData::current();

let iter = array.iter();
expected.retain += if cfg!(feature = "gnustep-1-7") { 0 } else { 1 };
expected.assert_current();

assert_eq!(iter.count(), 1);
expected.autorelease += if cfg!(feature = "gnustep-1-7") { 0 } else { 1 };
expected.assert_current();

let iter = array.iter_fast();
assert_eq!(iter.count(), 1);
expected.assert_current();
}

#[test]
Expand Down
49 changes: 35 additions & 14 deletions objc2/src/foundation/mutable_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,47 +226,68 @@ mod tests {

use super::*;
use crate::foundation::NSString;
use crate::rc::autoreleasepool;
use crate::rc::{autoreleasepool, RcTestObject, ThreadTestData};

#[test]
fn test_adding() {
let mut array = NSMutableArray::new();
let obj = NSObject::new();
array.push(obj);

let obj1 = RcTestObject::new();
let obj2 = RcTestObject::new();
let mut expected = ThreadTestData::current();

array.push(obj1);
expected.retain += 1;
expected.release += 1;
expected.assert_current();
assert_eq!(array.len(), 1);
assert_eq!(array.get(0), array.get(0));

let obj = NSObject::new();
array.insert(0, obj);
array.insert(0, obj2);
expected.retain += 1;
expected.release += 1;
expected.assert_current();
assert_eq!(array.len(), 2);
}

#[test]
fn test_replace() {
let mut array = NSMutableArray::new();
let obj = NSObject::new();
array.push(obj);

let obj = NSObject::new();
let old_obj = array.replace(0, obj);
let obj1 = RcTestObject::new();
let obj2 = RcTestObject::new();
array.push(obj1);
let mut expected = ThreadTestData::current();

let old_obj = array.replace(0, obj2);
expected.retain += 2;
expected.release += 2;
expected.assert_current();
assert_ne!(&*old_obj, array.get(0).unwrap());
}

#[test]
fn test_remove() {
let mut array = NSMutableArray::new();
for _ in 0..4 {
array.push(NSObject::new());
array.push(RcTestObject::new());
}
let mut expected = ThreadTestData::current();

let _ = array.remove(1);
let _obj = array.remove(1);
expected.retain += 1;
expected.release += 1;
expected.assert_current();
assert_eq!(array.len(), 3);

let _ = array.pop();
let _obj = array.pop();
expected.retain += 1;
expected.release += 1;
expected.assert_current();
assert_eq!(array.len(), 2);

array.clear();
expected.release += 2;
expected.dealloc += 2;
expected.assert_current();
assert_eq!(array.len(), 0);
}

Expand Down
24 changes: 23 additions & 1 deletion objc2/src/foundation/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,16 +227,38 @@ impl fmt::Debug for NSValue {
#[cfg(test)]
mod tests {
use alloc::format;
use core::slice;
use core::{ptr, slice};

use super::*;
use crate::rc::{RcTestObject, ThreadTestData};

#[test]
fn basic() {
let val = NSValue::new(13u32);
assert_eq!(unsafe { val.get::<u32>() }, 13);
}

#[test]
fn does_not_retain() {
let obj = RcTestObject::new();
let expected = ThreadTestData::current();

let val = NSValue::new::<*const RcTestObject>(&*obj);
expected.assert_current();

assert!(ptr::eq(unsafe { val.get::<*const RcTestObject>() }, &*obj));
expected.assert_current();

let _clone = val.clone();
expected.assert_current();

let _copy = val.copy();
expected.assert_current();

drop(val);
expected.assert_current();
}

#[test]
fn test_equality() {
let val1 = NSValue::new(123u32);
Expand Down
8 changes: 3 additions & 5 deletions objc2/src/macros/declare_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,8 +516,6 @@ macro_rules! declare_class {
$v fn class() -> &'static $crate::runtime::Class {
use $crate::__macro_helpers::Once;

use $crate::declare::ClassBuilder;
use $crate::runtime::{Class, Protocol};
static REGISTER_CLASS: Once = Once::new();

REGISTER_CLASS.call_once(|| {
Expand All @@ -527,7 +525,7 @@ macro_rules! declare_class {
stringify!($name),
". Perhaps a class with that name already exists?",
);
let mut builder = ClassBuilder::new(stringify!($name), superclass).expect(err_str);
let mut builder = $crate::declare::ClassBuilder::new(stringify!($name), superclass).expect(err_str);

$(
builder.add_ivar::<<$ivar as $crate::declare::IvarType>::Type>(
Expand All @@ -539,7 +537,7 @@ macro_rules! declare_class {
// Implement protocol if any specified
$(
let err_str = concat!("could not find protocol ", stringify!($protocol));
builder.add_protocol(Protocol::get(stringify!($protocol)).expect(err_str));
builder.add_protocol($crate::runtime::Protocol::get(stringify!($protocol)).expect(err_str));
)?

// Implement methods
Expand All @@ -558,7 +556,7 @@ macro_rules! declare_class {
});

// We just registered the class, so it should be available
Class::get(stringify!($name)).unwrap()
$crate::runtime::Class::get(stringify!($name)).unwrap()
}
}

Expand Down
Loading

0 comments on commit f1f12ec

Please sign in to comment.