636 lines
19 KiB
Rust
636 lines
19 KiB
Rust
use std::{
|
|
cell::RefCell,
|
|
panic,
|
|
rc::Rc,
|
|
sync::{Arc, Mutex},
|
|
thread,
|
|
};
|
|
|
|
use futures_executor::block_on;
|
|
use glib::clone;
|
|
|
|
struct State {
|
|
count: i32,
|
|
started: bool,
|
|
}
|
|
|
|
impl State {
|
|
fn new() -> Self {
|
|
Self {
|
|
count: 0,
|
|
started: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn clone_and_references() {
|
|
let state = Rc::new(RefCell::new(State::new()));
|
|
let ref_state = &state;
|
|
assert!(!ref_state.borrow().started);
|
|
|
|
let closure = {
|
|
clone!(@weak ref_state => move || {
|
|
ref_state.borrow_mut().started = true;
|
|
})
|
|
};
|
|
|
|
closure();
|
|
assert!(ref_state.borrow().started);
|
|
}
|
|
|
|
#[test]
|
|
fn subfields_renaming() {
|
|
struct Foo {
|
|
v: Rc<usize>,
|
|
}
|
|
|
|
impl Foo {
|
|
fn foo(&self) {
|
|
let state = Rc::new(RefCell::new(State::new()));
|
|
|
|
let closure = clone!(@strong self.v as v, @weak state as hello => move |_| {
|
|
println!("v: {v}");
|
|
hello.borrow_mut().started = true;
|
|
});
|
|
closure(2);
|
|
}
|
|
}
|
|
|
|
Foo { v: Rc::new(0) }.foo();
|
|
}
|
|
|
|
#[test]
|
|
fn renaming() {
|
|
let state = Rc::new(RefCell::new(State::new()));
|
|
assert!(!state.borrow().started);
|
|
|
|
let closure = {
|
|
clone!(@weak state as hello => move || {
|
|
hello.borrow_mut().started = true;
|
|
})
|
|
};
|
|
|
|
closure();
|
|
assert!(state.borrow().started);
|
|
}
|
|
|
|
#[test]
|
|
fn clone_closure() {
|
|
let state = Rc::new(RefCell::new(State::new()));
|
|
assert!(!state.borrow().started);
|
|
|
|
let closure = {
|
|
clone!(@weak state => move || {
|
|
state.borrow_mut().started = true;
|
|
})
|
|
};
|
|
|
|
closure();
|
|
|
|
assert!(state.borrow().started);
|
|
assert_eq!(state.borrow().count, 0);
|
|
|
|
let closure = {
|
|
let state2 = Rc::new(RefCell::new(State::new()));
|
|
assert!(state.borrow().started);
|
|
|
|
clone!(@weak state, @strong state2 => move || {
|
|
state.borrow_mut().count += 1;
|
|
state.borrow_mut().started = true;
|
|
state2.borrow_mut().started = true;
|
|
})
|
|
};
|
|
|
|
closure();
|
|
|
|
assert_eq!(state.borrow().count, 1);
|
|
assert!(state.borrow().started);
|
|
}
|
|
|
|
#[test]
|
|
fn clone_default_value() {
|
|
let closure = {
|
|
let state = Rc::new(RefCell::new(State::new()));
|
|
clone!(@weak state => @default-return 42, move |_| {
|
|
state.borrow_mut().started = true;
|
|
10
|
|
})
|
|
};
|
|
|
|
assert_eq!(42, closure(50));
|
|
}
|
|
|
|
#[test]
|
|
fn clone_panic() {
|
|
let state = Arc::new(Mutex::new(State::new()));
|
|
state.lock().expect("Failed to lock state mutex").count = 20;
|
|
|
|
let closure = {
|
|
let state2 = Arc::new(Mutex::new(State::new()));
|
|
clone!(@weak state2, @strong state => @default-return panic!(), move |_| {
|
|
state.lock().expect("Failed to lock state mutex").count = 21;
|
|
state2.lock().expect("Failed to lock state2 mutex").started = true;
|
|
10
|
|
})
|
|
};
|
|
|
|
let result = panic::catch_unwind(|| {
|
|
closure(50);
|
|
});
|
|
|
|
assert!(result.is_err());
|
|
|
|
assert_eq!(state.lock().expect("Failed to lock state mutex").count, 20);
|
|
}
|
|
|
|
#[test]
|
|
fn clone_import_rename() {
|
|
import_rename::test();
|
|
}
|
|
|
|
mod import_rename {
|
|
use glib::clone as clone_g;
|
|
|
|
#[allow(unused_macros)]
|
|
macro_rules! clone {
|
|
($($anything:tt)*) => {
|
|
|_, _| panic!("The clone! macro doesn't support renaming")
|
|
};
|
|
}
|
|
|
|
#[allow(unused_variables)]
|
|
pub fn test() {
|
|
let n = 2;
|
|
|
|
let closure: Box<dyn Fn(u32, u32)> = Box::new(clone_g!(
|
|
@strong n
|
|
=> move |_, _|
|
|
println!("The clone! macro does support renaming")
|
|
));
|
|
|
|
closure(0, 0);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_clone_macro_self_rename() {
|
|
#[derive(Debug)]
|
|
struct Foo {
|
|
v: u8,
|
|
}
|
|
|
|
impl Foo {
|
|
#[allow(dead_code)]
|
|
fn foo(&self) {
|
|
let closure = clone!(@strong self as this => move |_x| {
|
|
println!("v: {this:?}");
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@strong self as this => move || {
|
|
println!("v: {this:?}");
|
|
});
|
|
let closure = clone!(@strong self as this => move |_x| println!("v: {this:?}"));
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@strong self as this => move || println!("v: {this:?}"));
|
|
|
|
// Fields now!
|
|
let closure = clone!(@strong self.v as v => move |_x| {
|
|
println!("v: {v:?}");
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@strong self.v as v => move || println!("v: {v:?}"));
|
|
|
|
// With @default-panic
|
|
let closure = clone!(@strong self.v as v => @default-panic, move |_x| {
|
|
println!("v: {v:?}");
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@strong self.v as v => @default-panic, move || println!("v: {v:?}"));
|
|
|
|
// With @default-return
|
|
let closure = clone!(@strong self.v as _v => @default-return true, move |_x| {
|
|
false
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@strong self.v as _v => @default-return true, move || false);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_clone_macro_rename() {
|
|
let v = Rc::new(1);
|
|
|
|
let closure = clone!(@weak v as y => @default-panic, move |_x| {
|
|
println!("v: {y}");
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@weak v as y => @default-panic, move || println!("v: {y}"));
|
|
|
|
let closure = clone!(@strong v as y => @default-panic, move |_x| {
|
|
println!("v: {y}");
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@strong v as y => @default-panic, move || println!("v: {y}"));
|
|
|
|
let closure = clone!(@weak v as y => move |_x| {
|
|
println!("v: {y}");
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@weak v as y => move || println!("v: {y}"));
|
|
|
|
let closure = clone!(@strong v as y => move |_x| {
|
|
println!("v: {y}");
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@strong v as y => move || println!("v: {y}"));
|
|
|
|
let closure = clone!(@weak v as _y => @default-return true, move |_x| {
|
|
false
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@weak v as _y => @default-return true, move || false);
|
|
|
|
let closure = clone!(@strong v as _y => @default-return true, move |_x| false);
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@strong v as _y => @default-return true, move || false);
|
|
}
|
|
|
|
#[test]
|
|
fn test_clone_macro_simple() {
|
|
let v = Rc::new(1);
|
|
|
|
let closure = clone!(@weak v => @default-panic, move |_x| {
|
|
println!("v: {v}");
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@weak v => @default-panic, move || println!("v: {v}"));
|
|
|
|
let closure = clone!(@strong v => @default-panic, move |_x| {
|
|
println!("v: {v}");
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@strong v => @default-panic, move || println!("v: {v}"));
|
|
|
|
let closure = clone!(@weak v => move |_x| {
|
|
println!("v: {v}");
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@weak v => move || println!("v: {v}"));
|
|
|
|
let closure = clone!(@strong v => move |_x| {
|
|
println!("v: {v}");
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@strong v => move || println!("v: {v}"));
|
|
|
|
let closure = clone!(@weak v => @default-return true, move |_x| {
|
|
false
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@weak v => @default-return true, move || false);
|
|
|
|
let closure = clone!(@strong v => @default-return true, move |_x| false);
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@strong v => @default-return true, move || false);
|
|
}
|
|
|
|
#[test]
|
|
fn test_clone_macro_double_simple() {
|
|
let v = Rc::new(1);
|
|
let w = Rc::new(2);
|
|
|
|
let closure = clone!(@weak v, @weak w => @default-panic, move |_x| {
|
|
println!("v: {v}, w: {w}");
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@weak v, @weak w => @default-panic, move || println!("v: {v}, w: {w}"));
|
|
|
|
let closure = clone!(@strong v, @strong w => @default-panic, move |_x| {
|
|
println!("v: {v}, w: {w}");
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@strong v, @strong w => @default-panic, move || println!("v: {v}, w: {w}"));
|
|
|
|
let closure = clone!(@weak v, @weak w => move |_x| {
|
|
println!("v: {v}, w: {w}");
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@weak v, @weak w => move || println!("v: {v}, w: {w}"));
|
|
|
|
let closure = clone!(@strong v, @strong w => move |_x| {
|
|
println!("v: {v}, w: {w}");
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@strong v, @strong w => move || println!("v: {v}, w: {w}"));
|
|
|
|
let closure = clone!(@weak v, @weak w => @default-return true, move |_x| {
|
|
false
|
|
});
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@weak v, @weak w => @default-return true, move || false);
|
|
|
|
let closure = clone!(@strong v, @strong w => @default-return true, move |_x| false);
|
|
closure(0i8); // to prevent compiler error for unknown `x` type.
|
|
let _ = clone!(@strong v, @strong w => @default-return true, move || false);
|
|
}
|
|
|
|
#[test]
|
|
fn test_clone_macro_double_rename() {
|
|
let v = Rc::new(1);
|
|
let w = Rc::new(2);
|
|
let done = Rc::new(RefCell::new(0));
|
|
|
|
let closure = clone!(@weak v as x, @weak w => @default-panic, move |z| {
|
|
z + *x + *w
|
|
});
|
|
assert_eq!(closure(1i8), 4i8);
|
|
let closure = clone!(@weak v as x, @weak w => @default-panic, move || 1);
|
|
assert_eq!(closure(), 1);
|
|
|
|
let closure = clone!(@weak v, @weak w as x => @default-panic, move |z| {
|
|
z + *v + *x
|
|
});
|
|
assert_eq!(closure(10i8), 13i8);
|
|
let closure = clone!(@weak v, @weak w as x => @default-panic, move || 2 + *x);
|
|
assert_eq!(closure(), 4);
|
|
|
|
let closure = clone!(@strong v as x, @strong w => @default-panic, move |z| {
|
|
z + *x + *w
|
|
});
|
|
assert_eq!(closure(3i8), 6i8);
|
|
let closure = clone!(@strong v as x, @strong w => @default-panic, move || 4 + *w);
|
|
assert_eq!(closure(), 6);
|
|
|
|
let closure = clone!(@strong v, @strong w as x => @default-panic, move |z| {
|
|
z + *v + *x
|
|
});
|
|
assert_eq!(closure(0i8), 3i8);
|
|
let closure = clone!(@strong v, @strong w as x => @default-panic, move || 5);
|
|
assert_eq!(closure(), 5);
|
|
|
|
let t_done = done.clone();
|
|
let closure = clone!(@weak v as x, @weak w => move |z| {
|
|
*t_done.borrow_mut() = z + *x + *w;
|
|
});
|
|
closure(4i8);
|
|
assert_eq!(*done.borrow(), 7);
|
|
let t_done = done.clone();
|
|
let closure = clone!(@weak v as x, @weak w => move || *t_done.borrow_mut() = *x + *w);
|
|
closure();
|
|
assert_eq!(*done.borrow(), 3);
|
|
|
|
let t_done = done.clone();
|
|
let closure = clone!(@weak v, @weak w as x => move |z| {
|
|
*t_done.borrow_mut() = z + *v + *x;
|
|
});
|
|
closure(8i8);
|
|
assert_eq!(*done.borrow(), 11i8);
|
|
let t_done = done.clone();
|
|
let closure = clone!(@weak v, @weak w as x => move || *t_done.borrow_mut() = *v * *x);
|
|
closure();
|
|
assert_eq!(*done.borrow(), 2);
|
|
|
|
let t_done = done.clone();
|
|
let closure = clone!(@strong v as x, @strong w => move |z| {
|
|
*t_done.borrow_mut() = z + *x + *w;
|
|
});
|
|
closure(9i8);
|
|
assert_eq!(*done.borrow(), 12i8);
|
|
let t_done = done.clone();
|
|
let closure = clone!(@strong v as x, @strong w => move || *t_done.borrow_mut() = *x - *w);
|
|
closure();
|
|
assert_eq!(*done.borrow(), -1);
|
|
|
|
let t_done = done.clone();
|
|
let closure = clone!(@strong v, @strong w as x => move |z| {
|
|
*t_done.borrow_mut() = *v + *x * z;
|
|
});
|
|
closure(2i8);
|
|
assert_eq!(*done.borrow(), 5);
|
|
let t_done = done.clone();
|
|
let closure = clone!(@strong v, @strong w as x => move || *t_done.borrow_mut() = *x - *v);
|
|
closure();
|
|
assert_eq!(*done.borrow(), 1);
|
|
|
|
let closure = clone!(@weak v as _x, @weak w => @default-return true, move |_| {
|
|
false
|
|
});
|
|
assert!(!closure(0u8));
|
|
let closure = clone!(@weak v as _x, @weak w => @default-return true, move || false);
|
|
assert!(!closure());
|
|
|
|
let closure = clone!(@weak v, @weak w as _x => @default-return true, move |_| {
|
|
false
|
|
});
|
|
assert!(!closure("a"));
|
|
let closure = clone!(@weak v, @weak w as _x => @default-return true, move || false);
|
|
assert!(!closure());
|
|
|
|
let closure = clone!(@strong v as _x, @strong w => @default-return true, move |_| {
|
|
false
|
|
});
|
|
assert!(!closure('a'));
|
|
let closure = clone!(@strong v as _x, @strong w => @default-return true, move || false);
|
|
assert!(!closure());
|
|
|
|
let closure = clone!(@strong v, @strong w as _x => @default-return true, move |_| {
|
|
false
|
|
});
|
|
assert!(!closure(12.));
|
|
let closure = clone!(@strong v, @strong w as _x => @default-return true, move || false);
|
|
assert!(!closure());
|
|
}
|
|
|
|
#[test]
|
|
fn test_clone_macro_typed_args() {
|
|
macro_rules! test_closure {
|
|
($kind:tt, panic) => {{
|
|
// We need Arc and Mutex to use them below in the thread.
|
|
let check = Arc::new(Mutex::new(0));
|
|
let v = Arc::new(Mutex::new(1));
|
|
let w = Arc::new(Mutex::new(1));
|
|
|
|
let closure = clone!(@$kind v as x, @$kind w, @weak check => @default-panic, move |arg: i8| {
|
|
*x.lock().unwrap() += arg;
|
|
*w.lock().unwrap() += arg;
|
|
*check.lock().unwrap() += 1;
|
|
});
|
|
closure(1);
|
|
assert_eq!(2, *v.lock().unwrap());
|
|
assert_eq!(2, *w.lock().unwrap());
|
|
assert_eq!(1, *check.lock().unwrap());
|
|
|
|
let closure2 = clone!(@$kind v, @$kind w as x, @weak check => @default-panic, move |arg: i8| {
|
|
*v.lock().unwrap() += arg;
|
|
*x.lock().unwrap() += arg;
|
|
*check.lock().unwrap() += 1;
|
|
});
|
|
closure2(1);
|
|
assert_eq!(3, *v.lock().unwrap());
|
|
assert_eq!(3, *w.lock().unwrap());
|
|
assert_eq!(2, *check.lock().unwrap());
|
|
|
|
#[allow(unused_macro_rules)]
|
|
macro_rules! inner {
|
|
(strong) => {{}};
|
|
(weak) => {{
|
|
std::mem::drop(v);
|
|
std::mem::drop(w);
|
|
|
|
// We use the threads to ensure that the closure panics as expected.
|
|
assert!(thread::spawn(move || {
|
|
closure(1);
|
|
}).join().is_err());
|
|
assert_eq!(2, *check.lock().unwrap());
|
|
assert!(thread::spawn(move || {
|
|
closure2(1);
|
|
}).join().is_err());
|
|
assert_eq!(2, *check.lock().unwrap());
|
|
}}
|
|
}
|
|
|
|
inner!($kind);
|
|
}};
|
|
($kind:tt) => {{
|
|
let check = Rc::new(RefCell::new(0));
|
|
let v = Rc::new(RefCell::new(1));
|
|
let w = Rc::new(RefCell::new(1));
|
|
|
|
let closure = clone!(@$kind v as x, @$kind w, @weak check => move |arg: i8| {
|
|
*x.borrow_mut() += arg;
|
|
*w.borrow_mut() += arg;
|
|
*check.borrow_mut() += 1;
|
|
});
|
|
closure(1);
|
|
assert_eq!(2, *v.borrow());
|
|
assert_eq!(2, *w.borrow());
|
|
assert_eq!(1, *check.borrow());
|
|
|
|
let closure2 = clone!(@$kind v, @$kind w as x, @weak check => move |arg: i8| {
|
|
*v.borrow_mut() += arg;
|
|
*x.borrow_mut() += arg;
|
|
*check.borrow_mut() += 1;
|
|
});
|
|
closure2(1);
|
|
assert_eq!(3, *v.borrow());
|
|
assert_eq!(3, *w.borrow());
|
|
assert_eq!(2, *check.borrow());
|
|
|
|
#[allow(unused_macro_rules)]
|
|
macro_rules! inner {
|
|
(strong) => {{}};
|
|
(weak) => {{
|
|
std::mem::drop(v);
|
|
std::mem::drop(w);
|
|
|
|
closure(1);
|
|
assert_eq!(2, *check.borrow());
|
|
closure2(1);
|
|
assert_eq!(2, *check.borrow());
|
|
}}
|
|
}
|
|
|
|
inner!($kind);
|
|
}};
|
|
}
|
|
|
|
test_closure!(weak, panic);
|
|
test_closure!(strong, panic);
|
|
test_closure!(weak);
|
|
test_closure!(strong);
|
|
|
|
let check = Rc::new(RefCell::new(0));
|
|
let v = Rc::new(RefCell::new(1));
|
|
let w = Rc::new(RefCell::new(1));
|
|
let closure = clone!(@weak v, @weak w as x, @weak check => move |arg: i8, arg2| {
|
|
*v.borrow_mut() = arg;
|
|
*x.borrow_mut() = arg2;
|
|
*check.borrow_mut() += 1;
|
|
});
|
|
closure(0, 9);
|
|
assert_eq!(0, *v.borrow());
|
|
assert_eq!(9, *w.borrow());
|
|
assert_eq!(1, *check.borrow());
|
|
|
|
std::mem::drop(v);
|
|
std::mem::drop(w);
|
|
assert_eq!(1, *check.borrow());
|
|
}
|
|
|
|
#[test]
|
|
#[allow(clippy::bool_assert_comparison)]
|
|
#[allow(clippy::nonminimal_bool)]
|
|
fn test_clone_macro_default_return() {
|
|
macro_rules! test_default {
|
|
($ret:expr, $($closure_body:tt)*) => {{
|
|
let v = Rc::new(1);
|
|
let tmp = clone!(@weak v => @default-return $ret, move || $($closure_body)*);
|
|
assert_eq!(tmp(), $($closure_body)*, "shouldn't use default-return value!");
|
|
::std::mem::drop(v);
|
|
assert_eq!(tmp(), $ret, "should use default-return value!");
|
|
}}
|
|
}
|
|
|
|
#[derive(PartialEq, Debug)]
|
|
struct Foo(i32);
|
|
|
|
test_default!(Foo(0), Foo(1));
|
|
|
|
#[derive(PartialEq, Debug)]
|
|
struct Bar {
|
|
x: i32,
|
|
}
|
|
|
|
test_default!(Bar { x: 0 }, Bar { x: 1 });
|
|
|
|
#[derive(PartialEq, Debug)]
|
|
enum Enum {
|
|
A,
|
|
B(i32),
|
|
C { x: i32 },
|
|
}
|
|
test_default!(Enum::A, Enum::B(0));
|
|
test_default!(Enum::B(0), Enum::A);
|
|
test_default!(Enum::C { x: 0 }, Enum::A);
|
|
test_default!(
|
|
{
|
|
let x = 12;
|
|
x + 2
|
|
},
|
|
19
|
|
);
|
|
// This one is simply to check that we wait for the comma for the default-return value.
|
|
test_default!(Enum::A == Enum::B(0) || false, true);
|
|
}
|
|
|
|
#[test]
|
|
fn test_clone_macro_body() {
|
|
let v = Arc::new(Mutex::new(0));
|
|
|
|
let closure = clone!(@weak v => move || {
|
|
std::thread::spawn(move || {
|
|
let mut lock = v.lock().expect("failed to lock");
|
|
for _ in 1..=10 {
|
|
*lock += 1;
|
|
}
|
|
}).join().expect("thread::spawn failed");
|
|
});
|
|
closure();
|
|
assert_eq!(10, *v.lock().expect("failed to lock"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_clone_macro_async_kinds() {
|
|
let v = Rc::new(RefCell::new(1));
|
|
|
|
// This one is still a rust unstable feature.
|
|
// let _closure = clone!(@weak v => async move || 0);
|
|
let closure = clone!(@weak v => move || async move { *v.borrow_mut() += 1; });
|
|
block_on(closure());
|
|
assert_eq!(*v.borrow(), 2);
|
|
block_on(clone!(@weak v => async move { *v.borrow_mut() += 1; }));
|
|
assert_eq!(*v.borrow(), 3);
|
|
}
|