Backport official GLib iterator safety fix for Linux desktop
This commit is contained in:
1 parent
43c43ebdc9
commit
8ef874c609
133 files changed
+55342
-2
No files matched your search
@@ -0,0 +1,8 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
#[test]
|
||||
fn check_gir_file() {
|
||||
let res = gir_format_check::check_gir_file("Gir.toml");
|
||||
println!("{res}");
|
||||
assert_eq!(res.nb_errors, 0);
|
||||
}
|
||||
+635
@@ -0,0 +1,635 @@
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use glib::{Downgrade, Object};
|
||||
|
||||
#[test]
|
||||
fn derive_downgrade() {
|
||||
#[derive(Downgrade)]
|
||||
pub struct NewType(Object);
|
||||
|
||||
#[derive(Downgrade)]
|
||||
pub struct Struct {
|
||||
o1: Object,
|
||||
o2: std::rc::Rc<u32>,
|
||||
}
|
||||
|
||||
#[derive(Downgrade)]
|
||||
pub enum Enum {
|
||||
None,
|
||||
Pair { x: Object, y: Object },
|
||||
Unit(),
|
||||
SingleUnnamed(Object),
|
||||
MultipleUnnamed(Object, Object, Object),
|
||||
}
|
||||
|
||||
#[derive(Downgrade)]
|
||||
pub struct TypedWrapper<T>(Object, PhantomData<T>);
|
||||
|
||||
#[derive(Downgrade)]
|
||||
pub enum TypedEnum<T> {
|
||||
This(Object, PhantomData<T>),
|
||||
That(Object, PhantomData<T>),
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use glib::*;
|
||||
|
||||
#[derive(Default)]
|
||||
struct Counters {
|
||||
criticals: usize,
|
||||
warnings: usize,
|
||||
messages: usize,
|
||||
infos: usize,
|
||||
debugs: usize,
|
||||
}
|
||||
|
||||
fn assert_counts(
|
||||
count: &Arc<Mutex<Counters>>,
|
||||
criticals: usize,
|
||||
warnings: usize,
|
||||
messages: usize,
|
||||
infos: usize,
|
||||
debugs: usize,
|
||||
) {
|
||||
let count = count.lock().expect("failed to lock 1");
|
||||
assert_eq!(count.criticals, criticals);
|
||||
assert_eq!(count.warnings, warnings);
|
||||
assert_eq!(count.messages, messages);
|
||||
assert_eq!(count.infos, infos);
|
||||
assert_eq!(count.debugs, debugs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_log_handlers() {
|
||||
// We set the fatal level explicitly in case it's set outside of the test.
|
||||
log_set_fatal_mask(Some("domain"), LogLevels::LEVEL_ERROR);
|
||||
|
||||
//
|
||||
// log_set_default_handler check part
|
||||
//
|
||||
let count = Arc::new(Mutex::new(Counters::default()));
|
||||
log_set_default_handler(clone!(@weak count => move |_, level, _| {
|
||||
match level {
|
||||
LogLevel::Critical => { count.lock().expect("failed to lock 3").criticals += 1; }
|
||||
LogLevel::Warning => { count.lock().expect("failed to lock 4").warnings += 1; }
|
||||
LogLevel::Message => { count.lock().expect("failed to lock 5").messages += 1; }
|
||||
LogLevel::Info => { count.lock().expect("failed to lock 6").infos += 1; }
|
||||
LogLevel::Debug => { count.lock().expect("failed to lock 7").debugs += 1; }
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}));
|
||||
assert_counts(&count, 0, 0, 0, 0, 0);
|
||||
g_warning!(Some("domain"), "hello");
|
||||
assert_counts(&count, 0, 1, 0, 0, 0);
|
||||
g_warning!(Some("domain"), "hello");
|
||||
g_critical!(Some("domain"), "hello");
|
||||
g_warning!(Some("domain"), "hello");
|
||||
g_message!(Some("domain"), "hello");
|
||||
g_info!(Some("domain"), "hello");
|
||||
g_debug!(Some("domain"), "hello");
|
||||
g_info!(Some("domain"), "hello");
|
||||
assert_counts(&count, 1, 3, 1, 2, 1);
|
||||
|
||||
// We now unset our callback and check if it has really been unset.
|
||||
log_unset_default_handler();
|
||||
g_info!(Some("domain"), "hello");
|
||||
g_debug!(Some("domain"), "hello");
|
||||
assert_counts(&count, 1, 3, 1, 2, 1);
|
||||
|
||||
//
|
||||
// log_set_handler check part
|
||||
//
|
||||
let count = Arc::new(Mutex::new(Counters::default()));
|
||||
// We set the handler for both warning and debug.
|
||||
let handler_id = log_set_handler(
|
||||
Some("domain"),
|
||||
LogLevels::LEVEL_WARNING | LogLevels::LEVEL_DEBUG,
|
||||
false,
|
||||
false,
|
||||
clone!(@weak count => move |_, level, _| {
|
||||
match level {
|
||||
LogLevel::Warning => { count.lock().expect("failed to lock 4").warnings += 1; }
|
||||
LogLevel::Debug => { count.lock().expect("failed to lock 7").debugs += 1; }
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}),
|
||||
);
|
||||
assert_counts(&count, 0, 0, 0, 0, 0);
|
||||
g_warning!(Some("domain"), "hello");
|
||||
assert_counts(&count, 0, 1, 0, 0, 0);
|
||||
g_critical!(Some("domain"), "hello");
|
||||
g_message!(Some("domain"), "hello");
|
||||
g_info!(Some("domain"), "hello");
|
||||
assert_counts(&count, 0, 1, 0, 0, 0);
|
||||
g_debug!(Some("domain"), "hello");
|
||||
assert_counts(&count, 0, 1, 0, 0, 1);
|
||||
// We check that only "domain" messages are calling our callback.
|
||||
g_debug!(Some("not-domain"), "hello");
|
||||
g_warning!(Some("not-domain"), "hello");
|
||||
assert_counts(&count, 0, 1, 0, 0, 1);
|
||||
|
||||
log_remove_handler(Some("domain"), handler_id);
|
||||
g_critical!(Some("domain"), "hello");
|
||||
g_message!(Some("domain"), "hello");
|
||||
g_info!(Some("domain"), "hello");
|
||||
g_debug!(Some("domain"), "hello");
|
||||
g_warning!(Some("domain"), "hello");
|
||||
assert_counts(&count, 0, 1, 0, 0, 1);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use glib::*;
|
||||
|
||||
// Funny thing: we can't put those two tests in two different functions, otherwise they might
|
||||
// conflict with the results of the other one (or it would be mandatory to run the tests on only
|
||||
// one thread).
|
||||
#[test]
|
||||
fn check_print_handler() {
|
||||
//
|
||||
// g_print check part
|
||||
//
|
||||
let count = Arc::new(Mutex::new(0));
|
||||
set_print_handler(clone!(@weak count => move |_| {
|
||||
// we don't care about the message in here!
|
||||
*count.lock().expect("failed to lock 1") += 1;
|
||||
}));
|
||||
g_print!("test");
|
||||
assert_eq!(*count.lock().expect("failed to lock 2"), 1);
|
||||
g_printerr!("one");
|
||||
assert_eq!(*count.lock().expect("failed to lock 3"), 1);
|
||||
g_print!("another");
|
||||
assert_eq!(*count.lock().expect("failed to lock 4"), 2);
|
||||
unset_print_handler();
|
||||
g_print!("tadam");
|
||||
assert_eq!(*count.lock().expect("failed to lock 5"), 2);
|
||||
g_printerr!("toudoum");
|
||||
assert_eq!(*count.lock().expect("failed to lock 6"), 2);
|
||||
|
||||
//
|
||||
// g_printerr check part
|
||||
//
|
||||
let count = Arc::new(Mutex::new(0));
|
||||
set_printerr_handler(clone!(@weak count => move |_| {
|
||||
// we don't care about the message in here!
|
||||
*count.lock().expect("failed to lock a") += 1;
|
||||
}));
|
||||
g_printerr!("test");
|
||||
assert_eq!(*count.lock().expect("failed to lock b"), 1);
|
||||
g_print!("one");
|
||||
assert_eq!(*count.lock().expect("failed to lock c"), 1);
|
||||
g_printerr!("another");
|
||||
assert_eq!(*count.lock().expect("failed to lock d"), 2);
|
||||
unset_printerr_handler();
|
||||
g_printerr!("tadam");
|
||||
assert_eq!(*count.lock().expect("failed to lock e"), 2);
|
||||
g_print!("toudoum");
|
||||
assert_eq!(*count.lock().expect("failed to lock f"), 2);
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
#![allow(clippy::unnecessary_to_owned)]
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
#[test]
|
||||
fn structured_log() {
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use glib::*;
|
||||
|
||||
let log = Arc::new(Mutex::new(Vec::new()));
|
||||
{
|
||||
let log = log.clone();
|
||||
// can only be called once per test file
|
||||
log_set_writer_func(move |level, fields| {
|
||||
let fields = fields
|
||||
.iter()
|
||||
.map(|f| {
|
||||
let value = if let Some(data) = f.user_data() {
|
||||
assert!(f.value_str().is_none());
|
||||
format!("USERDATA: {data}")
|
||||
} else {
|
||||
f.value_str().unwrap().to_owned()
|
||||
};
|
||||
(f.key().to_owned(), value)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
log.lock().unwrap().push((level, fields));
|
||||
LogWriterOutput::Handled
|
||||
});
|
||||
}
|
||||
log_structured!(
|
||||
"test",
|
||||
LogLevel::Message,
|
||||
{
|
||||
"MY_META" => "abc";
|
||||
"MESSAGE" => "normal with meta";
|
||||
"MY_META2" => "def";
|
||||
}
|
||||
);
|
||||
|
||||
log_structured!(
|
||||
None,
|
||||
LogLevel::Message,
|
||||
{
|
||||
"MY_META" => "abc";
|
||||
"MESSAGE" => "formatted with meta: {} {}", 123, 456.0;
|
||||
"MY_META2" => "def{}", "ghi";
|
||||
"EMPTY" => b"";
|
||||
GString::from("MY_META3") => b"bstring".to_owned();
|
||||
}
|
||||
);
|
||||
log_structured_array(
|
||||
LogLevel::Warning,
|
||||
&[
|
||||
LogField::new(
|
||||
gstr!("MESSAGE_ID"),
|
||||
"1e45a69523d3460680e2721d3072408f".as_bytes(),
|
||||
),
|
||||
LogField::new(gstr!("PRIORITY"), "4".as_bytes()),
|
||||
LogField::new(gstr!("MESSAGE"), "from array".as_bytes()),
|
||||
LogField::new_user_data(gstr!("SOMEDATA"), 12345),
|
||||
],
|
||||
);
|
||||
let dict = VariantDict::new(None);
|
||||
dict.insert_value(
|
||||
"MESSAGE_ID",
|
||||
&"9e093d0fac2f4d50838a649796ab154b".to_variant(),
|
||||
);
|
||||
dict.insert_value("RELEASE", &"true".to_variant());
|
||||
dict.insert_value("MY_BYTES", &"123".as_bytes().to_variant());
|
||||
dict.insert_value("MESSAGE", &"from variant".to_variant());
|
||||
log_variant(Some("test"), LogLevel::Debug, &dict.end());
|
||||
|
||||
let log = std::mem::take(&mut *log.lock().unwrap());
|
||||
let log = log
|
||||
.iter()
|
||||
.map(|(l, v)| {
|
||||
(
|
||||
*l,
|
||||
v.iter()
|
||||
.map(|(k, v)| (k.as_str(), v.as_str()))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let path = if cfg!(windows) {
|
||||
"glib\\tests\\structured_log.rs"
|
||||
} else {
|
||||
"glib/tests/structured_log.rs"
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
log[0],
|
||||
(
|
||||
LogLevel::Message,
|
||||
vec![
|
||||
("PRIORITY", "5" as &str),
|
||||
("CODE_FILE", path as &str),
|
||||
("CODE_LINE", "31" as &str),
|
||||
("CODE_FUNC", "structured_log::structured_log" as &str),
|
||||
("MY_META", "abc"),
|
||||
("MESSAGE", "normal with meta"),
|
||||
("MY_META2", "def"),
|
||||
("GLIB_DOMAIN", "test"),
|
||||
]
|
||||
),
|
||||
);
|
||||
assert_eq!(
|
||||
log[1],
|
||||
(
|
||||
LogLevel::Message,
|
||||
vec![
|
||||
("PRIORITY", "5" as &str),
|
||||
("CODE_FILE", path as &str),
|
||||
("CODE_LINE", "41" as &str),
|
||||
("CODE_FUNC", "structured_log::structured_log" as &str),
|
||||
("MY_META", "abc"),
|
||||
("MESSAGE", "formatted with meta: 123 456"),
|
||||
("MY_META2", "defghi"),
|
||||
("EMPTY", ""),
|
||||
("MY_META3", "bstring"),
|
||||
]
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
log[2],
|
||||
(
|
||||
LogLevel::Warning,
|
||||
vec![
|
||||
("MESSAGE_ID", "1e45a69523d3460680e2721d3072408f" as &str),
|
||||
("PRIORITY", "4"),
|
||||
("MESSAGE", "from array"),
|
||||
("SOMEDATA", "USERDATA: 12345"),
|
||||
]
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
log[3],
|
||||
(
|
||||
LogLevel::Debug,
|
||||
vec![
|
||||
("PRIORITY", "7" as &str),
|
||||
("GLIB_DOMAIN", "test"),
|
||||
("MESSAGE_ID", "9e093d0fac2f4d50838a649796ab154b"),
|
||||
("MY_BYTES", "123"),
|
||||
("RELEASE", "true"),
|
||||
("MESSAGE", "from variant"),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#[test]
|
||||
pub fn test() {
|
||||
let t = trybuild2::TestCases::new();
|
||||
|
||||
t.pass("tests/subclass_compiletest/01-auto-send-sync.rs");
|
||||
t.compile_fail("tests/subclass_compiletest/02-no-auto-send-sync.rs");
|
||||
t.compile_fail("tests/subclass_compiletest/03-object-no-auto-send-sync.rs");
|
||||
t.pass("tests/subclass_compiletest/04-auto-send-sync-with-send-sync-parent.rs");
|
||||
t.compile_fail("tests/subclass_compiletest/05-no-auto-send-sync-with-non-send-sync-parent.rs");
|
||||
t.compile_fail(
|
||||
"tests/subclass_compiletest/06-no-auto-send-sync-with-non-send-sync-ffi-parent.rs",
|
||||
);
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
mod imp {
|
||||
use glib::subclass::prelude::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TestObject {
|
||||
s: String,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for TestObject {
|
||||
const NAME: &'static str = "TestObject";
|
||||
type Type = super::TestObject;
|
||||
}
|
||||
|
||||
impl ObjectImpl for TestObject {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct TestObject(ObjectSubclass<imp::TestObject>);
|
||||
}
|
||||
|
||||
impl Default for TestObject {
|
||||
fn default() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn check<T: Send + Sync>(_obj: &T) {}
|
||||
|
||||
let obj = TestObject::default();
|
||||
check(&obj);
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
mod imp {
|
||||
use glib::subclass::prelude::*;
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TestObject {
|
||||
s: RefCell<String>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for TestObject {
|
||||
const NAME: &'static str = "TestObject";
|
||||
type Type = super::TestObject;
|
||||
}
|
||||
|
||||
impl ObjectImpl for TestObject {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct TestObject(ObjectSubclass<imp::TestObject>);
|
||||
}
|
||||
|
||||
impl Default for TestObject {
|
||||
fn default() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn check<T: Send>(_obj: &T) {}
|
||||
|
||||
let obj = TestObject::default();
|
||||
check(&obj);
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
error[E0277]: `RefCell<std::string::String>` cannot be shared between threads safely
|
||||
--> tests/subclass_compiletest/02-no-auto-send-sync.rs:33:11
|
||||
|
|
||||
33 | check(&obj);
|
||||
| ----- ^^^^ `RefCell<std::string::String>` cannot be shared between threads safely
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `imp::TestObject`, the trait `Sync` is not implemented for `RefCell<std::string::String>`
|
||||
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
|
||||
note: required because it appears within the type `TestObject`
|
||||
--> tests/subclass_compiletest/02-no-auto-send-sync.rs:6:16
|
||||
|
|
||||
6 | pub struct TestObject {
|
||||
| ^^^^^^^^^^
|
||||
= note: required for `TypedObjectRef<imp::TestObject, ()>` to implement `Send`
|
||||
note: required because it appears within the type `TestObject`
|
||||
--> tests/subclass_compiletest/02-no-auto-send-sync.rs:20:16
|
||||
|
|
||||
20 | pub struct TestObject(ObjectSubclass<imp::TestObject>);
|
||||
| ^^^^^^^^^^
|
||||
note: required by a bound in `main::check`
|
||||
--> tests/subclass_compiletest/02-no-auto-send-sync.rs:30:17
|
||||
|
|
||||
30 | fn check<T: Send>(_obj: &T) {}
|
||||
| ^^^^ required by this bound in `check`
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
fn main() {
|
||||
fn check<T: Send + Sync>(_obj: &T) {}
|
||||
|
||||
let obj = glib::Object::new::<glib::Object>();
|
||||
check(&obj);
|
||||
}
|
||||
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
error[E0277]: `*mut c_void` cannot be sent between threads safely
|
||||
--> tests/subclass_compiletest/03-object-no-auto-send-sync.rs:5:11
|
||||
|
|
||||
5 | check(&obj);
|
||||
| ----- ^^^^ `*mut c_void` cannot be sent between threads safely
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `Send` is not implemented for `*mut c_void`
|
||||
= note: required for `TypedObjectRef<*mut c_void, ()>` to implement `Send`
|
||||
note: required because it appears within the type `Object`
|
||||
--> src/object.rs
|
||||
|
|
||||
| pub Object, *mut std::os::raw::c_void, (), gobject_ffi::GObject, @ffi_class gobject_ffi::GObjectClass, @type_ gobject_ffi::g_object_g...
|
||||
| ^^^^^^
|
||||
note: required by a bound in `main::check`
|
||||
--> tests/subclass_compiletest/03-object-no-auto-send-sync.rs:2:17
|
||||
|
|
||||
2 | fn check<T: Send + Sync>(_obj: &T) {}
|
||||
| ^^^^ required by this bound in `check`
|
||||
|
||||
error[E0277]: `*mut c_void` cannot be shared between threads safely
|
||||
--> tests/subclass_compiletest/03-object-no-auto-send-sync.rs:5:11
|
||||
|
|
||||
5 | check(&obj);
|
||||
| ----- ^^^^ `*mut c_void` cannot be shared between threads safely
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `Sync` is not implemented for `*mut c_void`
|
||||
= note: required for `TypedObjectRef<*mut c_void, ()>` to implement `Send`
|
||||
note: required because it appears within the type `Object`
|
||||
--> src/object.rs
|
||||
|
|
||||
| pub Object, *mut std::os::raw::c_void, (), gobject_ffi::GObject, @ffi_class gobject_ffi::GObjectClass, @type_ gobject_ffi::g_object_g...
|
||||
| ^^^^^^
|
||||
note: required by a bound in `main::check`
|
||||
--> tests/subclass_compiletest/03-object-no-auto-send-sync.rs:2:17
|
||||
|
|
||||
2 | fn check<T: Send + Sync>(_obj: &T) {}
|
||||
| ^^^^ required by this bound in `check`
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
mod imp_parent {
|
||||
use glib::subclass::prelude::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TestParent {
|
||||
s: String,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for TestParent {
|
||||
const NAME: &'static str = "TestParent";
|
||||
type Type = super::TestParent;
|
||||
}
|
||||
|
||||
impl ObjectImpl for TestParent {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct TestParent(ObjectSubclass<imp_parent::TestParent>);
|
||||
}
|
||||
|
||||
pub trait TestParentImpl: glib::subclass::prelude::ObjectImpl + Send + Sync {}
|
||||
|
||||
unsafe impl<T: TestParentImpl> glib::subclass::prelude::IsSubclassable<T> for TestParent {}
|
||||
|
||||
impl Default for TestParent {
|
||||
fn default() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
}
|
||||
|
||||
mod imp_object {
|
||||
use glib::subclass::prelude::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TestObject {
|
||||
s: String,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for TestObject {
|
||||
const NAME: &'static str = "TestObject";
|
||||
type Type = super::TestObject;
|
||||
type ParentType = super::TestParent;
|
||||
}
|
||||
|
||||
impl ObjectImpl for TestObject {}
|
||||
impl super::TestParentImpl for TestObject {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct TestObject(ObjectSubclass<imp_object::TestObject>) @extends TestParent;
|
||||
}
|
||||
|
||||
impl Default for TestObject {
|
||||
fn default() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn check<T: Send + Sync>(_obj: &T) {}
|
||||
|
||||
let obj = TestParent::default();
|
||||
check(&obj);
|
||||
|
||||
let obj = TestObject::default();
|
||||
check(&obj);
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
mod imp_parent {
|
||||
use glib::subclass::prelude::*;
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TestParent {
|
||||
s: RefCell<String>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for TestParent {
|
||||
const NAME: &'static str = "TestParent";
|
||||
type Type = super::TestParent;
|
||||
}
|
||||
|
||||
impl ObjectImpl for TestParent {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct TestParent(ObjectSubclass<imp_parent::TestParent>);
|
||||
}
|
||||
|
||||
pub trait TestParentImpl: glib::subclass::prelude::ObjectImpl {}
|
||||
|
||||
unsafe impl<T: TestParentImpl> glib::subclass::prelude::IsSubclassable<T> for TestParent {}
|
||||
|
||||
impl Default for TestParent {
|
||||
fn default() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
}
|
||||
|
||||
mod imp_object {
|
||||
use glib::subclass::prelude::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TestObject {
|
||||
s: String,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for TestObject {
|
||||
const NAME: &'static str = "TestObject";
|
||||
type Type = super::TestObject;
|
||||
type ParentType = super::TestParent;
|
||||
}
|
||||
|
||||
impl ObjectImpl for TestObject {}
|
||||
impl super::TestParentImpl for TestObject {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct TestObject(ObjectSubclass<imp_object::TestObject>) @extends TestParent;
|
||||
}
|
||||
|
||||
impl Default for TestObject {
|
||||
fn default() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn check<T: Send + Sync>(_obj: &T) {}
|
||||
|
||||
let obj = TestObject::default();
|
||||
check(&obj);
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
error[E0277]: `RefCell<std::string::String>` cannot be shared between threads safely
|
||||
--> tests/subclass_compiletest/05-no-auto-send-sync-with-non-send-sync-parent.rs:66:11
|
||||
|
|
||||
66 | check(&obj);
|
||||
| ----- ^^^^ `RefCell<std::string::String>` cannot be shared between threads safely
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: within `imp_parent::TestParent`, the trait `Sync` is not implemented for `RefCell<std::string::String>`
|
||||
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
|
||||
note: required because it appears within the type `TestParent`
|
||||
--> tests/subclass_compiletest/05-no-auto-send-sync-with-non-send-sync-parent.rs:6:16
|
||||
|
|
||||
6 | pub struct TestParent {
|
||||
| ^^^^^^^^^^
|
||||
= note: required for `TypedObjectRef<imp_parent::TestParent, ()>` to implement `Send`
|
||||
note: required because it appears within the type `TestParent`
|
||||
--> tests/subclass_compiletest/05-no-auto-send-sync-with-non-send-sync-parent.rs:20:16
|
||||
|
|
||||
20 | pub struct TestParent(ObjectSubclass<imp_parent::TestParent>);
|
||||
| ^^^^^^^^^^
|
||||
= note: required for `TypedObjectRef<imp_object::TestObject, TestParent>` to implement `Send`
|
||||
note: required because it appears within the type `TestObject`
|
||||
--> tests/subclass_compiletest/05-no-auto-send-sync-with-non-send-sync-parent.rs:53:16
|
||||
|
|
||||
53 | pub struct TestObject(ObjectSubclass<imp_object::TestObject>) @extends TestParent;
|
||||
| ^^^^^^^^^^
|
||||
note: required by a bound in `main::check`
|
||||
--> tests/subclass_compiletest/05-no-auto-send-sync-with-non-send-sync-parent.rs:63:17
|
||||
|
|
||||
63 | fn check<T: Send + Sync>(_obj: &T) {}
|
||||
| ^^^^ required by this bound in `check`
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
glib::wrapper! {
|
||||
#[doc(alias = "GInitiallyUnowned")]
|
||||
pub struct InitiallyUnowned(Object<glib::gobject_ffi::GInitiallyUnowned, glib::gobject_ffi::GInitiallyUnownedClass>);
|
||||
|
||||
match fn {
|
||||
type_ => || glib::gobject_ffi::g_initially_unowned_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InitiallyUnownedImpl: glib::subclass::prelude::ObjectImpl {}
|
||||
|
||||
unsafe impl<T: InitiallyUnownedImpl> glib::subclass::prelude::IsSubclassable<T>
|
||||
for InitiallyUnowned
|
||||
{
|
||||
}
|
||||
|
||||
mod imp_object {
|
||||
use glib::subclass::prelude::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TestObject {
|
||||
s: String,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for TestObject {
|
||||
const NAME: &'static str = "TestObject";
|
||||
type Type = super::TestObject;
|
||||
type ParentType = super::InitiallyUnowned;
|
||||
}
|
||||
|
||||
impl ObjectImpl for TestObject {}
|
||||
impl super::InitiallyUnownedImpl for TestObject {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct TestObject(ObjectSubclass<imp_object::TestObject>) @extends InitiallyUnowned;
|
||||
}
|
||||
|
||||
impl Default for TestObject {
|
||||
fn default() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn check<T: Send + Sync>(_obj: &T) {}
|
||||
|
||||
let obj = TestObject::default();
|
||||
check(&obj);
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
error[E0277]: `*mut c_void` cannot be sent between threads safely
|
||||
--> tests/subclass_compiletest/06-no-auto-send-sync-with-non-send-sync-ffi-parent.rs:50:11
|
||||
|
|
||||
50 | check(&obj);
|
||||
| ----- ^^^^ `*mut c_void` cannot be sent between threads safely
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `Send` is not implemented for `*mut c_void`
|
||||
= note: required for `TypedObjectRef<*mut c_void, ()>` to implement `Send`
|
||||
note: required because it appears within the type `InitiallyUnowned`
|
||||
--> tests/subclass_compiletest/06-no-auto-send-sync-with-non-send-sync-ffi-parent.rs:3:16
|
||||
|
|
||||
3 | pub struct InitiallyUnowned(Object<glib::gobject_ffi::GInitiallyUnowned, glib::gobject_ffi::GInitiallyUnownedClass>);
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
= note: required for `TypedObjectRef<imp_object::TestObject, InitiallyUnowned>` to implement `Send`
|
||||
note: required because it appears within the type `TestObject`
|
||||
--> tests/subclass_compiletest/06-no-auto-send-sync-with-non-send-sync-ffi-parent.rs:37:16
|
||||
|
|
||||
37 | pub struct TestObject(ObjectSubclass<imp_object::TestObject>) @extends InitiallyUnowned;
|
||||
| ^^^^^^^^^^
|
||||
note: required by a bound in `main::check`
|
||||
--> tests/subclass_compiletest/06-no-auto-send-sync-with-non-send-sync-ffi-parent.rs:47:17
|
||||
|
|
||||
47 | fn check<T: Send + Sync>(_obj: &T) {}
|
||||
| ^^^^ required by this bound in `check`
|
||||
|
||||
error[E0277]: `*mut c_void` cannot be shared between threads safely
|
||||
--> tests/subclass_compiletest/06-no-auto-send-sync-with-non-send-sync-ffi-parent.rs:50:11
|
||||
|
|
||||
50 | check(&obj);
|
||||
| ----- ^^^^ `*mut c_void` cannot be shared between threads safely
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `Sync` is not implemented for `*mut c_void`
|
||||
= note: required for `TypedObjectRef<*mut c_void, ()>` to implement `Send`
|
||||
note: required because it appears within the type `InitiallyUnowned`
|
||||
--> tests/subclass_compiletest/06-no-auto-send-sync-with-non-send-sync-ffi-parent.rs:3:16
|
||||
|
|
||||
3 | pub struct InitiallyUnowned(Object<glib::gobject_ffi::GInitiallyUnowned, glib::gobject_ffi::GInitiallyUnownedClass>);
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
= note: required for `TypedObjectRef<imp_object::TestObject, InitiallyUnowned>` to implement `Send`
|
||||
note: required because it appears within the type `TestObject`
|
||||
--> tests/subclass_compiletest/06-no-auto-send-sync-with-non-send-sync-ffi-parent.rs:37:16
|
||||
|
|
||||
37 | pub struct TestObject(ObjectSubclass<imp_object::TestObject>) @extends InitiallyUnowned;
|
||||
| ^^^^^^^^^^
|
||||
note: required by a bound in `main::check`
|
||||
--> tests/subclass_compiletest/06-no-auto-send-sync-with-non-send-sync-ffi-parent.rs:47:17
|
||||
|
|
||||
47 | fn check<T: Send + Sync>(_obj: &T) {}
|
||||
| ^^^^ required by this bound in `check`
|
||||
@@ -0,0 +1,67 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
use glib::*;
|
||||
|
||||
// FIXME all .get::<i32>() should be replaced with .get(); the compiler is totally able to infer the type itself.
|
||||
// But somehow without some tests are failing on Windows because the type inference doesn't work or something.
|
||||
|
||||
// Test that `ToValue` (and conversely, `FromValue`) uphold the promised invariants
|
||||
#[test]
|
||||
pub fn to_value_invariants() {
|
||||
// Inverse
|
||||
assert_eq!(0i32, 0i32.to_value().get::<i32>().unwrap());
|
||||
assert_eq!(0i32, 0i32.to_value().get::<i32>().unwrap());
|
||||
|
||||
// Idempotence
|
||||
assert_eq!(
|
||||
&0i32.to_value().type_(),
|
||||
&0i32.to_value().to_value().type_()
|
||||
);
|
||||
assert_eq!(0i32, 0i32.to_value().to_value().get::<i32>().unwrap());
|
||||
assert_eq!(
|
||||
0i32,
|
||||
0i32.to_value()
|
||||
.get::<Value>()
|
||||
.unwrap()
|
||||
.get::<i32>()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
0i32,
|
||||
0i32.to_value()
|
||||
.get::<Value>()
|
||||
.unwrap()
|
||||
.get::<i32>()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
// Test that `ToValue` and `FromValue` handle nexted boxed values correctly (as per the documentation)
|
||||
#[test]
|
||||
pub fn to_value_boxed() {
|
||||
let x = 0i32.to_value();
|
||||
let boxed = BoxedValue(x);
|
||||
assert_eq!(
|
||||
0i32,
|
||||
boxed
|
||||
.to_value()
|
||||
.to_value()
|
||||
.get::<BoxedValue>()
|
||||
.unwrap()
|
||||
.deref()
|
||||
.get::<i32>()
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
0i32,
|
||||
boxed
|
||||
.to_value()
|
||||
.get::<Value>()
|
||||
.unwrap()
|
||||
.get::<BoxedValue>()
|
||||
.unwrap()
|
||||
.deref()
|
||||
.get::<i32>()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user