use crate::common::SupervisedChild;
use anyhow::Context;
use std::path::{Path, PathBuf};
use std::process::Stdio;
use std::time::{Duration, Instant};
use zbus::zvariant::ObjectPath;
pub fn dbus_daemon(kind: &str, tmpdir: &Path) -> SupervisedChild {
let config_path = tmpdir.join(format!("{}-dbus.xml", kind));
let sock_path = tmpdir.join(format!("{}.sock", kind));
let dbus_path = format!("unix:path={}", sock_path.display());
std::fs::write(
&config_path,
format!(
r#"
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<type>{}</type>
<keep_umask/>
<listen>{}</listen>
<policy context="default">
<allow send_destination="*" eavesdrop="true"/>
<allow eavesdrop="true"/>
<allow own="*"/>
</policy>
</busconfig>
"#,
kind, dbus_path
),
)
.expect("failed to write dbus config");
std::env::set_var(
format!("DBUS_{}_BUS_ADDRESS", kind.to_uppercase()),
dbus_path,
);
let child = std::process::Command::new("dbus-daemon")
.arg(format!("--config-file={}", config_path.to_str().unwrap()))
.stdout(Stdio::null())
.stdin(Stdio::null())
.stderr(Stdio::null())
.spawn()
.expect("failed to launch dbus-daemon");
let start = Instant::now();
while !sock_path.exists() {
if start.elapsed() > Duration::from_secs(5) {
panic!("dbus-daemon failed to launch");
}
std::thread::sleep(Duration::from_millis(50));
}
SupervisedChild::new("dbus-daemon", child)
}
struct AccountsFixture {
num_users: Option<u32>,
}
struct UserFixture {
name: String,
username: String,
icon_file: String,
}
#[zbus::interface(name = "org.freedesktop.Accounts")]
impl AccountsFixture {
async fn list_cached_users(&self) -> Vec<ObjectPath<'_>> {
let mut users = vec![
ObjectPath::from_static_str_unchecked("/org/freedesktop/Accounts/phoshi"),
ObjectPath::from_static_str_unchecked("/org/freedesktop/Accounts/agx"),
ObjectPath::from_static_str_unchecked("/org/freedesktop/Accounts/sam"),
];
if let Some(num_users) = self.num_users {
users.truncate(num_users as _);
}
users
}
}
impl UserFixture {
fn new(name: &str, username: &str, icon_file: &str) -> Self {
Self {
name: name.into(),
username: username.into(),
icon_file: PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/")
.join(icon_file)
.display()
.to_string(),
}
}
}
#[zbus::interface(name = "org.freedesktop.Accounts.User")]
impl UserFixture {
#[zbus(property)]
async fn real_name(&self) -> &str {
&self.name
}
#[zbus(property)]
async fn user_name(&self) -> &str {
&self.username
}
#[zbus(property)]
async fn icon_file(&self) -> &str {
&self.icon_file
}
}
pub async fn run_accounts_fixture(
connection: zbus::Connection,
num_users: Option<u32>,
) -> anyhow::Result<()> {
connection
.object_server()
.at("/org/freedesktop/Accounts", AccountsFixture { num_users })
.await
.context("failed to serve org.freedesktop.Accounts")?;
connection
.object_server()
.at(
"/org/freedesktop/Accounts/agx",
UserFixture::new("Guido", "agx", "guido.png"),
)
.await
.context("failed to serve org.freedesktop.Accounts.User")?;
connection
.object_server()
.at(
"/org/freedesktop/Accounts/phoshi",
UserFixture::new("Phoshi", "phoshi", "phoshi.png"),
)
.await
.context("failed to serve org.freedesktop.Accounts.User")?;
connection
.object_server()
.at(
"/org/freedesktop/Accounts/sam",
UserFixture::new("Sam", "samcday", "samcday.jpeg"),
)
.await
.context("failed to serve org.freedesktop.Accounts.User")?;
connection
.request_name("org.freedesktop.Accounts")
.await
.context("failed to request name")?;
Ok(())
}