Skip to content

Commit 5c9bd8d

Browse files
committed
tests: add tuple struct generic and drop coverage
Extend tuple-struct coverage with the semantic cases that are easiest to review independently from the implementation changes. Add runtime coverage for generic and const-generic tuple structs, `Unpin` behaviour with `!Unpin` field types, pinned drop, and fallible partial-init rollback. Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
1 parent c77d3b7 commit 5c9bd8d

1 file changed

Lines changed: 108 additions & 0 deletions

File tree

tests/tuple_struct.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,111 @@ fn tuple_struct_multi_pinned_fields_projection() {
6565
assert_eq!(*tuple.as_ref().get_ref().1.lock(), 20);
6666
assert_eq!(tuple.as_ref().get_ref().2, 30);
6767
}
68+
69+
#[pin_data]
70+
struct GenericTuple<'a, T, const N: usize>(#[pin] CMutex<(&'a T, [u8; N])>, usize);
71+
72+
#[pin_data]
73+
#[allow(dead_code)]
74+
struct UnpinnedMutexTuple<T>(CMutex<T>, usize);
75+
76+
#[pin_data]
77+
struct TupleConst<T, const N: usize>(#[pin] CMutex<[T; N]>, usize);
78+
79+
fn assert_unpin<T: Unpin>() {}
80+
81+
#[test]
82+
fn tuple_struct_generics_are_supported() {
83+
let value = 77u16;
84+
let payload = (&value, [1, 2, 3, 4]);
85+
stack_pin_init!(
86+
let tuple = pin_init!(GenericTuple { 0 <- CMutex::new(payload), 1: 12 })
87+
);
88+
89+
let projected = tuple.as_mut().project();
90+
assert_pinned_mutex(&projected.0);
91+
let locked = projected.0.as_ref().get_ref().lock();
92+
assert_eq!(*locked.0, 77u16);
93+
assert_eq!(locked.1, [1, 2, 3, 4]);
94+
assert_eq!(*projected.1, 12);
95+
}
96+
97+
#[test]
98+
fn tuple_struct_unpin_ignores_unpinned_non_unpin_field() {
99+
assert_unpin::<UnpinnedMutexTuple<usize>>();
100+
}
101+
102+
#[test]
103+
fn tuple_struct_const_generics_support_explicit_arguments() {
104+
stack_pin_init!(let tuple = pin_init!(TupleConst::<u8, 3> { 0 <- CMutex::new([1, 2, 3]), 1: 9 }));
105+
106+
let projected = tuple.as_mut().project();
107+
assert_pinned_mutex(&projected.0);
108+
assert_eq!(*projected.0.as_ref().get_ref().lock(), [1, 2, 3]);
109+
assert_eq!(*projected.1, 9);
110+
}
111+
112+
#[pin_data(PinnedDrop)]
113+
struct DropTuple(#[pin] CMutex<usize>, usize);
114+
115+
static PINNED_DROP_TUPLE_DROPS: core::sync::atomic::AtomicUsize =
116+
core::sync::atomic::AtomicUsize::new(0);
117+
118+
struct DropCounter;
119+
120+
static FALLIBLE_TUPLE_DROPS: core::sync::atomic::AtomicUsize =
121+
core::sync::atomic::AtomicUsize::new(0);
122+
123+
impl Drop for DropCounter {
124+
fn drop(&mut self) {
125+
FALLIBLE_TUPLE_DROPS.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
126+
}
127+
}
128+
129+
fn tuple_failing_init() -> impl PinInit<TupleStruct<DropCounter>, ()> {
130+
// SAFETY: The closure initializes field 0, explicitly rolls it back before returning `Err`,
131+
// and leaves the slot otherwise untouched.
132+
unsafe {
133+
pin_init_from_closure(|slot: *mut TupleStruct<DropCounter>| {
134+
let field0 = core::ptr::addr_of_mut!((*slot).0);
135+
let init0 = CMutex::new(DropCounter);
136+
match init0.__pinned_init(field0) {
137+
Ok(()) => {}
138+
Err(infallible) => match infallible {},
139+
}
140+
core::ptr::drop_in_place(field0);
141+
Err(())
142+
})
143+
}
144+
}
145+
146+
#[pinned_drop]
147+
impl PinnedDrop for DropTuple {
148+
fn drop(self: Pin<&mut Self>) {
149+
let _ = self;
150+
PINNED_DROP_TUPLE_DROPS.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
151+
}
152+
}
153+
154+
#[test]
155+
fn tuple_struct_pinned_drop_delegates_from_drop() {
156+
PINNED_DROP_TUPLE_DROPS.store(0, core::sync::atomic::Ordering::SeqCst);
157+
{
158+
stack_pin_init!(let _tuple = pin_init!(DropTuple { 0 <- CMutex::new(5usize), 1: 1 }));
159+
}
160+
assert_eq!(
161+
PINNED_DROP_TUPLE_DROPS.load(core::sync::atomic::Ordering::SeqCst),
162+
1
163+
);
164+
}
165+
166+
#[test]
167+
fn tuple_struct_fallible_init_drops_initialized_fields() {
168+
FALLIBLE_TUPLE_DROPS.store(0, core::sync::atomic::Ordering::SeqCst);
169+
stack_try_pin_init!(let tuple: TupleStruct<DropCounter> = tuple_failing_init());
170+
assert!(matches!(tuple, Err(())));
171+
assert_eq!(
172+
FALLIBLE_TUPLE_DROPS.load(core::sync::atomic::Ordering::SeqCst),
173+
1
174+
);
175+
}

0 commit comments

Comments
 (0)