- Added new test document that documents conducted tests.

- Add test for job engine
- Fixed a pointer on cleanup
This commit is contained in:
Johannes Leupolz 2025-11-22 21:34:05 +00:00
parent 8950288b09
commit 636604b586
5 changed files with 84 additions and 12 deletions

14
docs/TESTS.md Normal file
View file

@ -0,0 +1,14 @@
# Tests
## Manual end-to-end tests
| vuinputd | host | input type | app that creates device | app that reads device | working | Notes |
| -------------- | ---------- |---------- | ---------- |---------- |---------- |---------- |
| 0.2.0 | Ubuntu 24.04 amd64 | virtual keyboard | Sunshine (via moonlight-qt 6.1.0 on macos) | labwc via libinput | :white_check_mark: | (1) |
| 0.2.0 | Ubuntu 24.04 amd64| virtual mouse | Sunshine (via moonlight-qt 6.1.0 on macos) | labwc via libinput | :white_check_mark:) | (1) |
| 0.2.0 | Ubuntu 24.04 amd64 | virtual keyboard | Steam (via Remote Play from Mac) | Return to Monkey Island | :white_check_mark: | (2) |
| 0.2.0 | Ubuntu 24.04 amd64 | virtual gamepad | Steam (via Remote Play from Mac) | Return to Monkey Island | :x: | (2) |
(1) works also for programs running on the wayland desktop
(2) Steam is a 32-bit application on linux

View file

@ -11,7 +11,7 @@ pub struct ClosureJob {
desc: String,
execute_after_cancellation: bool,
target: JobTarget,
task_creator: Box<dyn Fn(JobTarget) -> Pin<Box<dyn Future<Output = ()>>> + Send + 'static>,
task_creator: Box<dyn Fn(&ClosureJob) -> Pin<Box<dyn Future<Output = ()>>> + Send + 'static>,
}
impl ClosureJob {
@ -20,7 +20,7 @@ impl ClosureJob {
target: JobTarget,
execute_after_cancellation: bool,
f: Box<
dyn Fn(JobTarget) -> Pin<Box<dyn Future<Output = ()>>> // closure returns any future
dyn Fn(&ClosureJob) -> Pin<Box<dyn Future<Output = ()>>> // closure returns any future
+ Send // the closure itself can be sent across threads
+ 'static,
>,
@ -46,8 +46,7 @@ impl Job for ClosureJob {
fn create_task(self: &ClosureJob) -> Pin<Box<dyn Future<Output = ()>>> {
let creator = &self.task_creator;
let target = self.job_target();
let task = creator(target);
let task = creator(self);
task
}
@ -66,7 +65,8 @@ pub fn example() {
"Host maintenance",
JobTarget::Host,
false,
Box::new(|target| {
Box::new(|job: &ClosureJob| {
let target = job.target.clone();
Box::pin(async move {
println!("Running host job on {:?}", target);
})

View file

@ -33,3 +33,6 @@
pub mod closure_job;
pub mod job;
#[cfg(test)]
mod tests;

View file

@ -0,0 +1,55 @@
use crate::jobs::closure_job::ClosureJob;
use crate::jobs::job::{Dispatcher, Job, JobTarget};
use super::*;
use futures::executor::LocalPool;
use futures::task::LocalSpawnExt;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
/// Simple shared integer counter
fn shared_counter() -> Arc<Mutex<i32>> {
Arc::new(Mutex::new(0))
}
//
// 1. Ordering test
//
#[test]
fn test_job_ordering() {
let mut dispatcher = Dispatcher::new();
let c = shared_counter();
let c1 = c.clone();
dispatcher.dispatch(Box::new(ClosureJob::new(
"set to 5",
JobTarget::Host,
false,
Box::new(move |_job| {
let c1 = c1.clone();
Box::pin(async move {
*c1.lock().unwrap() = 5;
})
}),
)));
// job 2: increment to 6
let c2 = c.clone();
dispatcher.dispatch(Box::new(ClosureJob::new(
"increment to 6",
JobTarget::Host,
false,
Box::new(move |_job| {
let c2 = c2.clone();
Box::pin(async move {
*c2.lock().unwrap() += 1;
})
}),
)));
dispatcher.wait_until_finished();
assert_eq!(*c.lock().unwrap(), 6);
}

View file

@ -742,15 +742,15 @@ fn check_permissions() -> Result<(), std::io::Error> {
fn main() -> std::io::Result<()> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug")).init();
check_permissions().unwrap();
check_permissions().expect("failed to read the capabilities of the vuinputd process");;
let args: Vec<String> = std::env::args().collect();
VUINPUT_STATE.set(RwLock::new(HashMap::new())).unwrap();
VUINPUT_COUNTER.set(AtomicU64::new(3)).unwrap();
JOB_DISPATCHER.set(Mutex::new(Dispatcher::new())).unwrap();
VUINPUTD_NAMESPACES.set(get_namespace(Pid::SelfPid)).unwrap();
DEDUP_LAST_ERROR.set(Mutex::new(None)).unwrap();
VUINPUT_STATE.set(RwLock::new(HashMap::new())).expect("failed to initialize global state");
VUINPUT_COUNTER.set(AtomicU64::new(3)).expect("failed to initialize the counter that provides the values of the CUSE file handles"); // 3, because 1 and 2 are usually STDOUT and STDERR
JOB_DISPATCHER.set(Mutex::new(Dispatcher::new())).expect("failed to initialize the job dispatcher");
VUINPUTD_NAMESPACES.set(get_namespace(Pid::SelfPid)).expect("failed to retrieve the namespaces of the vuinputd process");
DEDUP_LAST_ERROR.set(Mutex::new(None)).expect("failed to initialize the log deduplication state");
JOB_DISPATCHER.get().unwrap().lock().unwrap().dispatch(Box::new(MonitorBackgroundLoop::new()));
info!("Starting vuinputd");
@ -799,7 +799,7 @@ fn main() -> std::io::Result<()> {
);
let _reclaim_arg_program_name = CString::from_raw(parg_program_name);
let _reclaim_arg_foreground = CString::from_raw(parg_foreground);
let _reclaim_arg_foreground = CString::from_raw(parg_singlethreaded);
let _reclaim_arg_singlethreaded = CString::from_raw(parg_singlethreaded);
}
info!("Stopping vuinputd");
JOB_DISPATCHER.get().unwrap().lock().unwrap().close();