Backport official GLib iterator safety fix for Linux desktop

This commit is contained in:
karl committed 2026-10-13 10:35:50 +02:00
1 parent 43c43ebdc9
commit 8ef874c609
133 files changed
+55342 -2

No files matched your search

+8
View File
@@ -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
View File
@@ -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);
}
+33
View File
@@ -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
View File
@@ -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);
}
+49
View File
@@ -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",
);
}
@@ -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);
}
@@ -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);
}
@@ -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`
@@ -0,0 +1,6 @@
fn main() {
fn check<T: Send + Sync>(_obj: &T) {}
let obj = glib::Object::new::<glib::Object>();
check(&obj);
}
@@ -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`
@@ -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);
}
@@ -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);
}
@@ -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`
@@ -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);
}
@@ -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`
+67
View File
@@ -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()
);
}