mirror of
https://gitlab.steamos.cloud/holo/steamos-manager.git
synced 2025-07-05 14:10:34 -04:00
testing: Add subprocess dbus session interface for test isolation
This commit is contained in:
parent
24223a4827
commit
6e925e91d9
4 changed files with 91 additions and 12 deletions
|
@ -327,7 +327,7 @@ mod test {
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::fs::{create_dir_all, write};
|
use tokio::fs::{create_dir_all, write};
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
use zbus::{Connection, ConnectionBuilder};
|
use zbus::Connection;
|
||||||
|
|
||||||
struct TestHandle {
|
struct TestHandle {
|
||||||
h: testing::TestHandle,
|
h: testing::TestHandle,
|
||||||
|
@ -335,7 +335,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start() -> Result<TestHandle> {
|
async fn start() -> Result<TestHandle> {
|
||||||
let handle = testing::start();
|
let mut handle = testing::start();
|
||||||
create_dir_all(crate::path("/sys/class/dmi/id")).await?;
|
create_dir_all(crate::path("/sys/class/dmi/id")).await?;
|
||||||
write(crate::path("/sys/class/dmi/id/board_vendor"), "Valve\n").await?;
|
write(crate::path("/sys/class/dmi/id/board_vendor"), "Valve\n").await?;
|
||||||
write(crate::path("/sys/class/dmi/id/board_name"), "Jupiter\n").await?;
|
write(crate::path("/sys/class/dmi/id/board_name"), "Jupiter\n").await?;
|
||||||
|
@ -347,7 +347,7 @@ mod test {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let (tx, _rx) = channel::<RootContext>();
|
let (tx, _rx) = channel::<RootContext>();
|
||||||
let connection = ConnectionBuilder::session()?.build().await?;
|
let connection = handle.new_dbus().await?;
|
||||||
let manager = SteamOSManager::new(connection.clone(), tx).await?;
|
let manager = SteamOSManager::new(connection.clone(), tx).await?;
|
||||||
connection
|
connection
|
||||||
.object_server()
|
.object_server()
|
||||||
|
|
|
@ -368,7 +368,7 @@ mod test {
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::fs::read;
|
use tokio::fs::read;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
use zbus::{Connection, ConnectionBuilder, Interface};
|
use zbus::{Connection, Interface};
|
||||||
use zbus_xml::{Method, Node, Property};
|
use zbus_xml::{Method, Node, Property};
|
||||||
|
|
||||||
struct TestHandle {
|
struct TestHandle {
|
||||||
|
@ -377,9 +377,9 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start() -> Result<TestHandle> {
|
async fn start() -> Result<TestHandle> {
|
||||||
let handle = testing::start();
|
let mut handle = testing::start();
|
||||||
let (tx, _rx) = channel::<UserContext>();
|
let (tx, _rx) = channel::<UserContext>();
|
||||||
let connection = ConnectionBuilder::session()?.build().await?;
|
let connection = handle.new_dbus().await?;
|
||||||
let manager = SteamOSManager::new(connection.clone(), &connection, tx).await?;
|
let manager = SteamOSManager::new(connection.clone(), &connection, tx).await?;
|
||||||
connection
|
connection
|
||||||
.object_server()
|
.object_server()
|
||||||
|
@ -485,7 +485,5 @@ mod test {
|
||||||
assert_eq!(local_property.ty(), remote_property.ty());
|
assert_eq!(local_property.ty(), remote_property.ty());
|
||||||
assert_eq!(local_property.access(), remote_property.access());
|
assert_eq!(local_property.access(), remote_property.access());
|
||||||
}
|
}
|
||||||
|
|
||||||
test.connection.close().await.unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,7 +200,7 @@ mod test {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn ftrace_init() {
|
async fn ftrace_init() {
|
||||||
let _h = testing::start();
|
let mut h = testing::start();
|
||||||
|
|
||||||
let tracefs = Ftrace::base();
|
let tracefs = Ftrace::base();
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ mod test {
|
||||||
Mode::S_IRUSR | Mode::S_IWUSR,
|
Mode::S_IRUSR | Mode::S_IWUSR,
|
||||||
)
|
)
|
||||||
.expect("trace_pipe");
|
.expect("trace_pipe");
|
||||||
let dbus = Connection::session().await.expect("dbus");
|
let dbus = h.new_dbus().await.expect("dbus");
|
||||||
let _ftrace = Ftrace::init(dbus).await.expect("ftrace");
|
let _ftrace = Ftrace::init(dbus).await.expect("ftrace");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
use libc::pid_t;
|
||||||
|
use nix::sys::signal;
|
||||||
|
use nix::sys::signal::Signal;
|
||||||
|
use nix::unistd::Pid;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::process::Stdio;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::time::Duration;
|
||||||
use tempfile::{tempdir, TempDir};
|
use tempfile::{tempdir, TempDir};
|
||||||
|
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||||
|
use tokio::process::{Child, Command};
|
||||||
|
use zbus::connection::{Builder, Connection};
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static TEST: RefCell<Option<Rc<Test>>> = RefCell::new(None);
|
static TEST: RefCell<Option<Rc<Test>>> = RefCell::new(None);
|
||||||
|
@ -47,6 +56,7 @@ pub fn start() -> TestHandle {
|
||||||
let test: Rc<Test> = Rc::new(Test {
|
let test: Rc<Test> = Rc::new(Test {
|
||||||
base: tempdir().expect("Couldn't create test directory"),
|
base: tempdir().expect("Couldn't create test directory"),
|
||||||
process_cb: Cell::new(|_, _| Err(anyhow!("No current process_cb"))),
|
process_cb: Cell::new(|_, _| Err(anyhow!("No current process_cb"))),
|
||||||
|
mock_dbus: Cell::new(None),
|
||||||
});
|
});
|
||||||
*lock.borrow_mut() = Some(test.clone());
|
*lock.borrow_mut() = Some(test.clone());
|
||||||
TestHandle { test }
|
TestHandle { test }
|
||||||
|
@ -54,28 +64,99 @@ pub fn start() -> TestHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop() {
|
pub fn stop() {
|
||||||
TEST.with(|lock| *lock.borrow_mut() = None);
|
TEST.with(|lock| {
|
||||||
|
let test = (*lock.borrow_mut()).take();
|
||||||
|
if let Some(test) = test {
|
||||||
|
if let Some(mock_dbus) = test.mock_dbus.take() {
|
||||||
|
let _ = mock_dbus.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current() -> Rc<Test> {
|
pub fn current() -> Rc<Test> {
|
||||||
TEST.with(|lock| lock.borrow().as_ref().unwrap().clone())
|
TEST.with(|lock| lock.borrow().as_ref().unwrap().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct MockDBus {
|
||||||
|
pub connection: Connection,
|
||||||
|
process: Child,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Test {
|
pub struct Test {
|
||||||
base: TempDir,
|
base: TempDir,
|
||||||
pub process_cb: Cell<fn(&str, &[&OsStr]) -> Result<(i32, String)>>,
|
pub process_cb: Cell<fn(&str, &[&OsStr]) -> Result<(i32, String)>>,
|
||||||
|
pub mock_dbus: Cell<Option<MockDBus>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestHandle {
|
pub struct TestHandle {
|
||||||
pub test: Rc<Test>,
|
pub test: Rc<Test>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MockDBus {
|
||||||
|
pub async fn new() -> Result<MockDBus> {
|
||||||
|
let mut process = Command::new("/usr/bin/dbus-daemon")
|
||||||
|
.args(["--session", "--nofork", "--print-address"])
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
let stdout = BufReader::new(
|
||||||
|
process
|
||||||
|
.stdout
|
||||||
|
.take()
|
||||||
|
.ok_or(anyhow!("Couldn't capture stdout"))?,
|
||||||
|
);
|
||||||
|
|
||||||
|
let address = stdout
|
||||||
|
.lines()
|
||||||
|
.next_line()
|
||||||
|
.await?
|
||||||
|
.ok_or(anyhow!("Failed to read address"))?;
|
||||||
|
|
||||||
|
let connection = Builder::address(address.trim_end())?.build().await?;
|
||||||
|
|
||||||
|
Ok(MockDBus {
|
||||||
|
connection,
|
||||||
|
process,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shutdown(mut self) -> Result<()> {
|
||||||
|
let pid = match self.process.id() {
|
||||||
|
Some(id) => id,
|
||||||
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
let pid: pid_t = match pid.try_into() {
|
||||||
|
Ok(pid) => pid,
|
||||||
|
Err(message) => bail!("Unable to get pid_t from command {message}"),
|
||||||
|
};
|
||||||
|
signal::kill(Pid::from_raw(pid), Signal::SIGINT)?;
|
||||||
|
for _ in [0..10] {
|
||||||
|
// Wait for the process to exit synchronously, but not for too long
|
||||||
|
if self.process.try_wait()?.is_some() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::thread::sleep(Duration::from_micros(100));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Test {
|
impl Test {
|
||||||
pub fn path(&self) -> &Path {
|
pub fn path(&self) -> &Path {
|
||||||
self.base.path()
|
self.base.path()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TestHandle {
|
||||||
|
pub async fn new_dbus(&mut self) -> Result<Connection> {
|
||||||
|
let dbus = MockDBus::new().await?;
|
||||||
|
let connection = dbus.connection.clone();
|
||||||
|
self.test.mock_dbus.set(Some(dbus));
|
||||||
|
Ok(connection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Drop for TestHandle {
|
impl Drop for TestHandle {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
stop();
|
stop();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue