Skip to content

Commit

Permalink
Test(block): Extend tests for with_encoding
Browse files Browse the repository at this point in the history
Signed-off-by: Paul Mabileau <paul.mabileau@harfanglab.fr>
  • Loading branch information
PaulDance committed Jul 19, 2024
1 parent 975ac51 commit c2d2088
Showing 1 changed file with 253 additions and 1 deletion.
254 changes: 253 additions & 1 deletion crates/tests/src/block.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use core::cell::RefCell;
use std::ffi::CStr;
use std::thread_local;

use alloc::string::ToString;
use block2::{global_block, Block, RcBlock, StackBlock};
use block2::{global_block, Block, ManualBlockEncoding, RcBlock, StackBlock};
use objc2::encode::{Encode, Encoding};
use objc2::rc::Retained;
use objc2::runtime::{AnyObject, Bool, NSObject};
Expand Down Expand Up @@ -35,6 +36,36 @@ unsafe impl Encode for LargeStruct {

type Add12 = Block<dyn Fn(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32) -> i32>;

struct VoidToVoid;
unsafe impl ManualBlockEncoding for VoidToVoid {
type Arguments = ();
type Return = ();
#[cfg(target_pointer_width = "64")]
const ENCODING_CSTR: &'static CStr = c"v8@?0";
#[cfg(target_pointer_width = "32")]
const ENCODING_CSTR: &'static CStr = c"v4@?0";
}

struct VoidToInt;
unsafe impl ManualBlockEncoding for VoidToInt {
type Arguments = ();
type Return = i32;
#[cfg(target_pointer_width = "64")]
const ENCODING_CSTR: &'static CStr = c"i8@?0";
#[cfg(target_pointer_width = "32")]
const ENCODING_CSTR: &'static CStr = c"i4@?0";
}

struct IntToInt;
unsafe impl ManualBlockEncoding for IntToInt {
type Arguments = (i32,);
type Return = i32;
#[cfg(target_pointer_width = "64")]
const ENCODING_CSTR: &'static CStr = c"i12@?0i8";
#[cfg(target_pointer_width = "32")]
const ENCODING_CSTR: &'static CStr = c"i8@?0i4";
}

extern "C" {
/// Returns a pointer to a global block that returns 7.
fn get_int_block() -> *mut Block<dyn Fn() -> i32>;
Expand Down Expand Up @@ -108,6 +139,14 @@ fn test_int_block() {
);
invoke_assert(&StackBlock::new(|| 10), 10);
invoke_assert(&RcBlock::new(|| 6), 6);
invoke_assert(
unsafe { &StackBlock::with_encoding::<VoidToInt>(|| 10) },
10,
);
invoke_assert(
unsafe { &RcBlock::with_encoding::<_, _, _, VoidToInt>(|| 6) },
6,
);
invoke_assert(&GLOBAL_BLOCK, 42);
}

Expand All @@ -132,6 +171,14 @@ fn test_add_block() {
);
invoke_assert(&StackBlock::new(|a: i32| a + 6), 11);
invoke_assert(&RcBlock::new(|a: i32| a + 6), 11);
invoke_assert(
unsafe { &StackBlock::with_encoding::<IntToInt>(|a: i32| a + 6) },
11,
);
invoke_assert(
unsafe { &RcBlock::with_encoding::<_, _, _, IntToInt>(|a: i32| a + 6) },
11,
);
invoke_assert(&GLOBAL_BLOCK, 47);
}

Expand All @@ -149,6 +196,16 @@ fn test_add_12() {
);
}

struct Enc;
unsafe impl ManualBlockEncoding for Enc {
type Arguments = (i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32);
type Return = i32;
#[cfg(target_pointer_width = "64")]
const ENCODING_CSTR: &'static CStr = c"i56@?0i8i12i16i20i24i28i32i36i40i44i48i52";
#[cfg(target_pointer_width = "32")]
const ENCODING_CSTR: &'static CStr = c"i52@?0i4i8i12i16i20i24i28i32i36i40i44i48";
}

global_block! {
static GLOBAL_BLOCK = |
a1: i32, a2: i32, a3: i32, a4: i32,
Expand All @@ -166,6 +223,11 @@ fn test_add_12() {
};
invoke_assert(&StackBlock::new(closure), 78);
invoke_assert(&RcBlock::new(closure), 78);
invoke_assert(unsafe { &StackBlock::with_encoding::<Enc>(closure) }, 78);
invoke_assert(
unsafe { &RcBlock::with_encoding::<_, _, _, Enc>(closure) },
78,
);
invoke_assert(&GLOBAL_BLOCK, 120);
}

Expand All @@ -192,6 +254,16 @@ fn test_large_struct_block() {
};
}

struct Enc;
unsafe impl ManualBlockEncoding for Enc {
type Arguments = (LargeStruct,);
type Return = LargeStruct;
#[cfg(target_pointer_width = "64")]
const ENCODING_CSTR: &'static CStr = c"{LargeStruct=f[100C]}112@?0{LargeStruct=f[100C]}8";
#[cfg(target_pointer_width = "32")]
const ENCODING_CSTR: &'static CStr = c"{LargeStruct=f[100C]}108@?0{LargeStruct=f[100C]}4";
}

let data = LargeStruct::get();
let mut new_data = data;
new_data.mutate();
Expand All @@ -206,6 +278,16 @@ fn test_large_struct_block() {
assert_eq!(unsafe { invoke_large_struct_block(&block, data) }, new_data);
let block = block.copy();
assert_eq!(unsafe { invoke_large_struct_block(&block, data) }, new_data);

let block = unsafe {
StackBlock::with_encoding::<Enc>(|mut x: LargeStruct| {
x.mutate();
x
})
};
assert_eq!(unsafe { invoke_large_struct_block(&block, data) }, new_data);
let block = block.copy();
assert_eq!(unsafe { invoke_large_struct_block(&block, data) }, new_data);
}

#[test]
Expand All @@ -219,6 +301,17 @@ fn test_block_copy() {
assert_eq!(unsafe { invoke_int_block(&copied) }, expected_len);
}

#[test]
fn test_block_copy_with_encoding() {
let s = "Hello!".to_string();
let expected_len = s.len() as i32;
let block = unsafe { StackBlock::with_encoding::<VoidToInt>(move || s.len() as i32) };
assert_eq!(unsafe { invoke_int_block(&block) }, expected_len);

let copied = block.copy();
assert_eq!(unsafe { invoke_int_block(&copied) }, expected_len);
}

#[test]
fn test_block_stack_move() {
fn make_block() -> StackBlock<'static, (), i32, impl Fn() -> i32> {
Expand All @@ -230,6 +323,17 @@ fn test_block_stack_move() {
assert_eq!(unsafe { invoke_int_block(&block) }, 7);
}

#[test]
fn test_block_stack_move_with_encoding() {
fn make_block() -> StackBlock<'static, (), i32, impl Fn() -> i32> {
let x = 7;
unsafe { StackBlock::with_encoding::<VoidToInt>(move || x) }
}

let block = make_block();
assert_eq!(unsafe { invoke_int_block(&block) }, 7);
}

#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
struct Count {
new: usize,
Expand Down Expand Up @@ -313,6 +417,34 @@ fn stack_new_clone_drop() {
expected.assert_current();
}

#[test]
fn stack_new_clone_drop_with_encoding() {
let mut expected = Count::current();

let counter = CloneDropTracker::new();
expected.new += 1;
expected.assert_current();

let block = unsafe {
StackBlock::with_encoding::<VoidToVoid>(move || {
let _ = &counter;
})
};
expected.assert_current();

let clone = block.clone();
expected.clone += 1;
expected.assert_current();

drop(clone);
expected.drop += 1;
expected.assert_current();

drop(block);
expected.drop += 1;
expected.assert_current();
}

#[test]
fn rc_new_clone_drop() {
let mut expected = Count::current();
Expand All @@ -337,6 +469,32 @@ fn rc_new_clone_drop() {
expected.assert_current();
}

#[test]
fn rc_new_clone_drop_with_encoding() {
let mut expected = Count::current();

let counter = CloneDropTracker::new();
expected.new += 1;
expected.assert_current();

let block = unsafe {
RcBlock::with_encoding::<_, _, _, VoidToVoid>(move || {
let _ = &counter;
})
};
expected.assert_current();

let clone = block.clone();
expected.assert_current();

drop(clone);
expected.assert_current();

drop(block);
expected.drop += 1;
expected.assert_current();
}

#[test]
fn stack_to_rc() {
let mut expected = Count::current();
Expand Down Expand Up @@ -377,6 +535,48 @@ fn stack_to_rc() {
expected.assert_current();
}

#[test]
fn stack_to_rc_with_encoding() {
let mut expected = Count::current();

let counter = CloneDropTracker::new();
expected.new += 1;
expected.assert_current();

let stack = unsafe {
StackBlock::with_encoding::<VoidToVoid>(move || {
let _ = &counter;
})
};
expected.assert_current();

let rc1 = stack.copy();
expected.clone += 1;
expected.assert_current();

let rc2 = stack.copy();
expected.clone += 1;
expected.assert_current();

let clone2 = rc2.clone();
expected.assert_current();

drop(rc2);
expected.assert_current();

drop(stack);
expected.drop += 1;
expected.assert_current();

drop(rc1);
expected.drop += 1;
expected.assert_current();

drop(clone2);
expected.drop += 1;
expected.assert_current();
}

#[test]
fn retain_release_rc_block() {
let mut expected = Count::current();
Expand All @@ -402,6 +602,33 @@ fn retain_release_rc_block() {
expected.assert_current();
}

#[test]
fn retain_release_rc_block_with_encoding() {
let mut expected = Count::current();

let counter = CloneDropTracker::new();
expected.new += 1;
expected.assert_current();

let block = unsafe {
RcBlock::with_encoding::<_, _, _, VoidToVoid>(move || {
let _ = &counter;
})
};
expected.assert_current();

let ptr = &*block as *const Block<_> as *mut AnyObject;
let obj = unsafe { Retained::retain(ptr) }.unwrap();
expected.assert_current();

drop(block);
expected.assert_current();

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

/// Retaining/releasing stack blocks is kinda weird and unsupported.
///
/// As an example, the reference count is not increased for stack blocks on
Expand Down Expand Up @@ -457,3 +684,28 @@ fn capture_id() {

assert!(rc_block.call(()).is_false());
}

#[test]
fn capture_id_with_encoding() {
struct Enc;
unsafe impl ManualBlockEncoding for Enc {
type Arguments = ();
type Return = Bool;

#[cfg(target_os = "macos", target_arch = "x86_64")]
const ENCODING_CSTR: &'static CStr = c"c8@?0";
#[cfg(all(target_os = "linux", target_pointer_width = "64"))]
const ENCODING_CSTR: &'static CStr = c"C8@?0";
#[cfg(all(target_os = "linux", target_pointer_width = "32"))]
const ENCODING_CSTR: &'static CStr = c"C4@?0";
}
let stack_block = {
let obj1 = NSObject::new();
let obj2 = NSObject::new();
unsafe { StackBlock::with_encoding::<Enc>(move || Bool::new(obj1 == obj2)) }
};

let rc_block = stack_block.copy();

assert!(rc_block.call(()).is_false());
}

0 comments on commit c2d2088

Please sign in to comment.