Skip to content

Commit 6118e7f

Browse files
committed
Add non_send option to mry macro
1 parent de8ea50 commit 6118e7f

File tree

11 files changed

+515
-73
lines changed

11 files changed

+515
-73
lines changed

mry/src/lib.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,42 @@ pub use rule::ArgMatcher::Any;
1818
pub use mockable::*;
1919

2020
#[cfg(feature = "send_wrapper")]
21-
pub use send_wrapper;
21+
pub mod send_wrapper {
22+
use std::ops::{Deref, DerefMut};
23+
24+
#[derive(Debug, Clone)]
25+
pub struct SendWrapper<T>(send_wrapper::SendWrapper<T>);
26+
27+
impl<T> SendWrapper<T> {
28+
pub fn new(value: T) -> Self {
29+
Self(send_wrapper::SendWrapper::new(value))
30+
}
31+
32+
pub fn take(self) -> T {
33+
self.0.take()
34+
}
35+
}
36+
37+
impl<T> PartialEq for SendWrapper<T>
38+
where
39+
T: PartialEq,
40+
{
41+
fn eq(&self, other: &Self) -> bool {
42+
*self.0 == *other.0
43+
}
44+
}
45+
46+
impl<T> Deref for SendWrapper<T> {
47+
type Target = T;
48+
49+
fn deref(&self) -> &Self::Target {
50+
&self.0
51+
}
52+
}
53+
54+
impl<T> DerefMut for SendWrapper<T> {
55+
fn deref_mut(&mut self) -> &mut Self::Target {
56+
&mut self.0
57+
}
58+
}
59+
}

mry/src/rule/matcher.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,20 @@ where
123123
}
124124
}
125125

126+
impl<O: PartialEq + MockableArg, I, const N: usize> From<[I; N]> for ArgMatcher<Vec<O>>
127+
where
128+
I: Into<ArgMatcher<O>> + Clone,
129+
{
130+
fn from(value: [I; N]) -> Self {
131+
<&[I]>::into(&value[..])
132+
}
133+
}
134+
126135
#[cfg(feature = "send_wrapper")]
127-
impl<T: PartialEq + 'static> From<T> for ArgMatcher<send_wrapper::SendWrapper<T>> {
136+
impl<T: PartialEq + 'static> From<T> for ArgMatcher<crate::send_wrapper::SendWrapper<T>> {
128137
fn from(value: T) -> Self {
129-
let value = send_wrapper::SendWrapper::new(value);
130-
ArgMatcher::Fn(Box::new(move |input| **input == *value))
138+
let value = crate::send_wrapper::SendWrapper::new(value);
139+
ArgMatcher::Fn(Box::new(move |input| *input == value))
131140
}
132141
}
133142

@@ -166,4 +175,17 @@ mod tests {
166175
assert!(!matcher.matches(&(1, 2)));
167176
assert!(!matcher.matches(&(1, 1)));
168177
}
178+
179+
#[test]
180+
fn matcher_vec_of_send_wrapper() {
181+
let matcher: ArgMatcher<Vec<crate::send_wrapper::SendWrapper<u8>>> = [1u8, 2u8].into();
182+
assert!(matcher.matches(&vec![
183+
crate::send_wrapper::SendWrapper::new(1),
184+
crate::send_wrapper::SendWrapper::new(2),
185+
]));
186+
assert!(!matcher.matches(&vec![
187+
crate::send_wrapper::SendWrapper::new(1),
188+
crate::send_wrapper::SendWrapper::new(3),
189+
]));
190+
}
169191
}

mry/tests/integration/complex_clone.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ fn takes_slice_str_original_type_ok() {
8888
fn takes_slice_str_original_type_ok_array() {
8989
let mut target = mry::new!(Struct {});
9090
let mock = target
91-
.mock_takes_slice_str(&["first arg", "second arg"])
91+
.mock_takes_slice_str(["first arg", "second arg"])
9292
.returns(1);
9393

9494
assert_eq!(target.takes_slice_str(&["first arg", "second arg"]), 1);

mry/tests/integration/raw_pointer.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@ impl Test {
2222
}
2323
data
2424
}
25+
26+
fn use_ptr_array(&self, ptr: [*mut String; 3], data: u8) -> u8 {
27+
unsafe {
28+
println!("{} {}", *ptr[0], data);
29+
}
30+
data
31+
}
32+
33+
fn use_slice_of_ptr(&self, ptr: &[*mut String], data: u8) -> u8 {
34+
unsafe {
35+
println!("{} {}", *ptr[0], data);
36+
}
37+
data
38+
}
2539
}
2640

2741
#[test]
@@ -113,3 +127,31 @@ fn test_raw_pointer_return_with() {
113127
let _ = Box::from_raw(result_ptr);
114128
}
115129
}
130+
131+
#[test]
132+
fn test_use_ptr_array() {
133+
let mut test = Test {
134+
mry: Default::default(),
135+
};
136+
let ptr1 = Box::into_raw(Box::new(String::from("Hello, world!")));
137+
let ptr2 = Box::into_raw(Box::new(String::from("Hello, world!")));
138+
let ptr3 = Box::into_raw(Box::new(String::from("Hello, world!")));
139+
let ptr_array = [ptr1, ptr2, ptr3];
140+
test.mock_use_ptr_array(ptr_array, 1)
141+
.returns_with(|vec: Vec<SendWrapper<*mut String>>, _| vec.len() as u8);
142+
assert_eq!(test.use_ptr_array(ptr_array, 1), 3);
143+
}
144+
145+
#[test]
146+
fn test_use_slice_of_ptr() {
147+
let mut test = Test {
148+
mry: Default::default(),
149+
};
150+
let ptr1 = Box::into_raw(Box::new(String::from("Hello, world!")));
151+
let ptr2 = Box::into_raw(Box::new(String::from("Hello, world!")));
152+
let ptr3 = Box::into_raw(Box::new(String::from("Hello, world!")));
153+
let ptr_slice = &[ptr1, ptr2, ptr3] as &[*mut String];
154+
test.mock_use_slice_of_ptr(ptr_slice, 1)
155+
.returns_with(|vec: Vec<SendWrapper<*mut String>>, _| vec.len() as u8);
156+
assert_eq!(test.use_slice_of_ptr(ptr_slice, 1), 3);
157+
}

mry_macros/src/attrs.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
use darling::{ast::NestedMeta, FromMeta};
2+
use syn::{visit::Visit, Meta};
3+
4+
#[derive(FromMeta, Default)]
5+
pub(crate) struct MryAttr {
6+
pub debug: darling::util::Flag,
7+
pub not_send: Option<NotSend>,
8+
}
9+
10+
pub(crate) struct NotSend(pub Vec<syn::Path>);
11+
12+
impl FromMeta for NotSend {
13+
fn from_list(list: &[NestedMeta]) -> darling::Result<Self> {
14+
list.iter()
15+
.map(|meta| match meta {
16+
NestedMeta::Meta(Meta::Path(path)) => Ok(path.clone()),
17+
_ => Err(darling::Error::custom(
18+
"expected a list of types like not_send(T, U)",
19+
)),
20+
})
21+
.collect::<Result<Vec<_>, _>>()
22+
.map(NotSend)
23+
}
24+
}
25+
26+
impl MryAttr {
27+
pub fn test_non_send(&self, ty: &syn::Type) -> bool {
28+
let Some(not_send) = &self.not_send else {
29+
return false;
30+
};
31+
struct Visitor<'a> {
32+
not_send: &'a NotSend,
33+
found: bool,
34+
}
35+
impl Visit<'_> for Visitor<'_> {
36+
fn visit_path(&mut self, path: &syn::Path) {
37+
if self.found {
38+
return;
39+
}
40+
if self.not_send.0.iter().any(|p| p == path) {
41+
self.found = true;
42+
}
43+
}
44+
}
45+
let mut visitor = Visitor {
46+
not_send,
47+
found: false,
48+
};
49+
visitor.visit_type(ty);
50+
visitor.found
51+
}
52+
}
53+
54+
#[cfg(test)]
55+
mod tests {
56+
use syn::parse_quote;
57+
58+
use super::*;
59+
60+
#[test]
61+
fn test_debug() {
62+
let attr = MryAttr::from_list(
63+
&NestedMeta::parse_meta_list(parse_quote! {
64+
debug
65+
})
66+
.unwrap(),
67+
)
68+
.unwrap();
69+
assert!(attr.debug.is_present());
70+
}
71+
72+
#[test]
73+
fn test_not_send() {
74+
let attr = MryAttr::from_list(
75+
&NestedMeta::parse_meta_list(parse_quote! {
76+
debug,not_send(T, U)
77+
})
78+
.unwrap(),
79+
)
80+
.unwrap();
81+
assert!(attr.debug.is_present());
82+
assert!(matches!(attr.not_send, Some(NotSend(_))));
83+
let lists = attr.not_send.as_ref().unwrap().0.clone();
84+
assert_eq!(lists.len(), 2);
85+
assert_eq!(lists[0], parse_quote!(T));
86+
assert_eq!(lists[1], parse_quote!(U));
87+
}
88+
}

mry_macros/src/create_behaviors.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub fn create() -> TokenStream {
2121
#[doc(hidden)]
2222
pub struct #behavior_name<I, O>(Box<dyn FnMut(I) -> O + Send + 'static>);
2323
#[doc(hidden)]
24-
pub struct #behavior_name_send_wrapper<I, O>(Box<dyn FnMut(I) -> send_wrapper::SendWrapper<O> + Send + 'static>);
24+
pub struct #behavior_name_send_wrapper<I, O>(Box<dyn FnMut(I) -> crate::send_wrapper::SendWrapper<O> + Send + 'static>);
2525

2626
impl<Fn, O, #(#types),*> From<Fn> for #behavior_name<(#(#types,)*), O>
2727
where
@@ -37,7 +37,7 @@ pub fn create() -> TokenStream {
3737
Fn: FnMut(#(#types),*) -> O + Send + 'static,
3838
{
3939
fn from(mut function: Fn) -> Self {
40-
#behavior_name_send_wrapper(Box::new(move |(#(#args,)*)| send_wrapper::SendWrapper::new(function(#(#args),*))))
40+
#behavior_name_send_wrapper(Box::new(move |(#(#args,)*)| crate::send_wrapper::SendWrapper::new(function(#(#args),*))))
4141
}
4242
}
4343

@@ -50,8 +50,8 @@ pub fn create() -> TokenStream {
5050
}
5151
}
5252

53-
impl<I: Clone, O> Into<Behavior<I, send_wrapper::SendWrapper<O>>> for #behavior_name_send_wrapper<I, O> {
54-
fn into(self) -> Behavior<I, send_wrapper::SendWrapper<O>> {
53+
impl<I: Clone, O> Into<Behavior<I, crate::send_wrapper::SendWrapper<O>>> for #behavior_name_send_wrapper<I, O> {
54+
fn into(self) -> Behavior<I, crate::send_wrapper::SendWrapper<O>> {
5555
Behavior::Function {
5656
clone: Clone::clone,
5757
call: self.0,

mry_macros/src/item_fn.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ use proc_macro2::TokenStream;
22
use quote::{quote, ToTokens};
33
use syn::ItemFn;
44

5-
use crate::method;
5+
use crate::{attrs::MryAttr, method};
66

7-
pub(crate) fn transform(input: ItemFn) -> TokenStream {
7+
pub(crate) fn transform(mry_attr: &MryAttr, input: ItemFn) -> TokenStream {
88
let (original, mock) = method::transform(
9+
mry_attr,
910
quote![mry::get_static_mocks()],
1011
Default::default(),
1112
"",
@@ -39,7 +40,7 @@ mod test {
3940
.unwrap();
4041

4142
assert_eq!(
42-
transform(input).to_string(),
43+
transform(&MryAttr::default(), input).to_string(),
4344
quote! {
4445
fn meow(count: usize) -> String {
4546
#[cfg(debug_assertions)]
@@ -77,7 +78,7 @@ mod test {
7778
.unwrap();
7879

7980
assert_eq!(
80-
transform(input).to_string(),
81+
transform(&MryAttr::default(), input).to_string(),
8182
quote! {
8283
fn _meow(count: usize) -> String {
8384
#[cfg(debug_assertions)]

mry_macros/src/item_impl.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::method;
1+
use crate::{method, MryAttr};
22
use proc_macro2::TokenStream;
33
use quote::{quote, ToTokens};
44
use syn::visit::Visit;
@@ -49,7 +49,7 @@ impl VisitMut for QualifiesAssociatedTypes {
4949
}
5050
}
5151

52-
pub(crate) fn transform(mut input: ItemImpl) -> TokenStream {
52+
pub(crate) fn transform(mry_attr: &MryAttr, mut input: ItemImpl) -> TokenStream {
5353
if let Some((_, path, _)) = input.trait_.clone() {
5454
let ty = path.clone();
5555
let associated_types: Vec<_> = input
@@ -129,6 +129,7 @@ pub(crate) fn transform(mut input: ItemImpl) -> TokenStream {
129129
if let ImplItem::Fn(method) = item {
130130
if let Some(FnArg::Receiver(_)) = method.sig.inputs.first() {
131131
method::transform(
132+
mry_attr,
132133
quote![self.mry.mocks()],
133134
quote![#qualified_type::],
134135
&(type_name.clone() + "::"),
@@ -146,6 +147,7 @@ pub(crate) fn transform(mut input: ItemImpl) -> TokenStream {
146147
)
147148
} else {
148149
method::transform(
150+
mry_attr,
149151
quote![mry::get_static_mocks()],
150152
quote![#qualified_type::],
151153
&(type_name.clone() + "::"),
@@ -206,7 +208,7 @@ mod test {
206208
.unwrap();
207209

208210
assert_eq!(
209-
transform(input).to_string(),
211+
transform(&MryAttr::default(), input).to_string(),
210212
quote! {
211213
impl Cat {
212214
#[meow]
@@ -250,7 +252,7 @@ mod test {
250252
.unwrap();
251253

252254
assert_eq!(
253-
transform(input).to_string(),
255+
transform(&MryAttr::default(), input).to_string(),
254256
quote! {
255257
impl<'a, A: Clone> Cat<'a, A> {
256258
fn meow<'a, B>(&'a self, count: usize) -> B {
@@ -292,7 +294,7 @@ mod test {
292294
.unwrap();
293295

294296
assert_eq!(
295-
transform(input).to_string(),
297+
transform(&MryAttr::default(), input).to_string(),
296298
quote! {
297299
impl<A: Clone> Animal<A> for Cat {
298300
fn name(&self) -> String {
@@ -335,7 +337,7 @@ mod test {
335337
.unwrap();
336338

337339
assert_eq!(
338-
transform(input).to_string(),
340+
transform(&MryAttr::default(), input).to_string(),
339341
quote! {
340342
impl Iterator for Cat {
341343
type Item = String;
@@ -378,7 +380,7 @@ mod test {
378380
.unwrap();
379381

380382
assert_eq!(
381-
transform(input).to_string(),
383+
transform(&MryAttr::default(), input).to_string(),
382384
quote! {
383385
impl Cat {
384386
fn meow(count: usize) -> String {

0 commit comments

Comments
 (0)