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, } 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 = 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); }