Add support for a simple event loop with proxy to the testing backend

Moved from the spawn_local test, this allows for re-use in the
show_strongref integration test.
This commit is contained in:
Simon Hausmann 2023-08-25 15:32:51 +02:00 committed by Simon Hausmann
parent de58d5e83c
commit a98e07417e
4 changed files with 64 additions and 64 deletions

View file

@ -167,7 +167,6 @@ log = { version = "0.4.17", optional = true }
[dev-dependencies]
slint-build = { path = "../build" }
i-slint-backend-testing = { path = "../../../internal/backends/testing" }
i-slint-backend-winit = { path = "../../../internal/backends/winit", features = ["wayland", "x11", "renderer-software"] }
serde_json = "1.0.96"
serde = { version = "1.0.163", features = ["derive"] }

View file

@ -3,11 +3,9 @@
use ::slint::slint;
// Sorry, can't test with rust test harness and multiple threads.
#[cfg(not(any(target_arch = "wasm32", target_os = "macos", target_os = "ios")))]
#[test]
fn show_maintains_strong_reference() {
slint::platform::set_platform(Box::new(i_slint_backend_winit::Backend::new())).unwrap();
i_slint_backend_testing::init_with_event_loop();
slint!(export component TestWindow inherits Window {
callback root-clicked();
@ -21,10 +19,11 @@ fn show_maintains_strong_reference() {
let window_weak = window.as_weak();
let window_weak_2 = window_weak.clone();
slint::Timer::single_shot(std::time::Duration::from_millis(20), move || {
slint::invoke_from_event_loop(move || {
window_weak_2.upgrade().unwrap().hide().unwrap();
slint::quit_event_loop().unwrap();
});
})
.unwrap();
window.show().unwrap();
drop(window);

View file

@ -1,63 +1,6 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
mod fake_backend {
enum Event {
Quit,
Event(Box<dyn FnOnce() + Send>),
}
#[derive(Clone)]
struct Queue(
std::sync::Arc<std::sync::Mutex<std::collections::VecDeque<Event>>>,
std::thread::Thread,
);
pub struct FakeBackend {
queue: Queue,
}
impl Default for FakeBackend {
fn default() -> Self {
Self { queue: Queue(Default::default(), std::thread::current()) }
}
}
impl slint::platform::Platform for FakeBackend {
fn create_window_adapter(
&self,
) -> Result<std::rc::Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> {
unimplemented!()
}
fn run_event_loop(&self) -> Result<(), slint::PlatformError> {
loop {
let e = self.queue.0.lock().unwrap().pop_front();
match e {
Some(Event::Quit) => break Ok(()),
Some(Event::Event(e)) => e(),
None => std::thread::park(),
}
}
}
fn new_event_loop_proxy(&self) -> Option<Box<dyn slint::platform::EventLoopProxy>> {
Some(Box::new(self.queue.clone()))
}
}
impl slint::platform::EventLoopProxy for Queue {
fn quit_event_loop(&self) -> Result<(), slint::EventLoopError> {
self.0.lock().unwrap().push_back(Event::Quit);
self.1.unpark();
Ok(())
}
fn invoke_from_event_loop(
&self,
event: Box<dyn FnOnce() + Send>,
) -> Result<(), slint::EventLoopError> {
self.0.lock().unwrap().push_back(Event::Event(event));
self.1.unpark();
Ok(())
}
}
}
/// Code from https://doc.rust-lang.org/std/task/trait.Wake.html#examples
mod executor {
use std::future::Future;
@ -96,7 +39,7 @@ mod executor {
#[test]
fn main() {
slint::platform::set_platform(Box::new(fake_backend::FakeBackend::default())).unwrap();
i_slint_backend_testing::init_with_event_loop();
slint::invoke_from_event_loop(|| {
let handle = slint::spawn_local(async { String::from("Hello") }).unwrap();

View file

@ -19,6 +19,7 @@ use std::sync::Mutex;
#[derive(Default)]
pub struct TestingBackend {
clipboard: Mutex<Option<String>>,
queue: Option<Queue>,
}
impl i_slint_core::platform::Platform for TestingBackend {
@ -51,6 +52,28 @@ impl i_slint_core::platform::Platform for TestingBackend {
None
}
}
fn run_event_loop(&self) -> Result<(), PlatformError> {
let queue = match self.queue.as_ref() {
Some(queue) => queue.clone(),
None => return Err(PlatformError::NoEventLoopProvider),
};
loop {
let e = queue.0.lock().unwrap().pop_front();
match e {
Some(Event::Quit) => break Ok(()),
Some(Event::Event(e)) => e(),
None => std::thread::park(),
}
}
}
fn new_event_loop_proxy(&self) -> Option<Box<dyn i_slint_core::platform::EventLoopProxy>> {
self.queue
.as_ref()
.map(|q| Box::new(q.clone()) as Box<dyn i_slint_core::platform::EventLoopProxy>)
}
}
pub struct TestingWindow {
@ -162,6 +185,33 @@ impl RendererSealed for TestingWindow {
}
}
enum Event {
Quit,
Event(Box<dyn FnOnce() + Send>),
}
#[derive(Clone)]
struct Queue(
std::sync::Arc<std::sync::Mutex<std::collections::VecDeque<Event>>>,
std::thread::Thread,
);
impl i_slint_core::platform::EventLoopProxy for Queue {
fn quit_event_loop(&self) -> Result<(), i_slint_core::api::EventLoopError> {
self.0.lock().unwrap().push_back(Event::Quit);
self.1.unpark();
Ok(())
}
fn invoke_from_event_loop(
&self,
event: Box<dyn FnOnce() + Send>,
) -> Result<(), i_slint_core::api::EventLoopError> {
self.0.lock().unwrap().push_back(Event::Event(event));
self.1.unpark();
Ok(())
}
}
/// Initialize the testing backend.
/// Must be called before any call that would otherwise initialize the rendering backend.
/// Calling it when the rendering backend is already initialized will have no effects
@ -170,6 +220,15 @@ pub fn init() {
.expect("platform already initialized");
}
/// Initialize the testing backend with support for simple event loop.
/// This function can only be called once per process, so make sure to use integration
/// tests with one `#[test]` function.
pub fn init_with_event_loop() {
let mut backend = TestingBackend::default();
backend.queue = Some(Queue(Default::default(), std::thread::current()));
i_slint_core::platform::set_platform(Box::new(backend)).expect("platform already initialized");
}
/// This module contains functions useful for unit tests
mod for_unit_test {
use i_slint_core::api::ComponentHandle;