use async_lock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::{
collections::{hash_map::Entry, HashMap},
convert::TryInto,
fmt::Write,
marker::PhantomData,
ops::{Deref, DerefMut},
sync::Arc,
};
use static_assertions::assert_impl_all;
use zbus_names::InterfaceName;
use zvariant::{ObjectPath, OwnedObjectPath};
use crate::{
fdo,
fdo::{Introspectable, Peer, Properties},
Connection, DispatchResult, Error, Interface, Message, MessageType, Result, SignalContext,
WeakConnection,
};
pub struct InterfaceDeref<'d, I> {
iface: RwLockReadGuard<'d, dyn Interface>,
phantom: PhantomData<I>,
}
impl<I> Deref for InterfaceDeref<'_, I>
where
I: Interface,
{
type Target = I;
fn deref(&self) -> &I {
self.iface.downcast_ref::<I>().unwrap()
}
}
pub struct InterfaceDerefMut<'d, I> {
iface: RwLockWriteGuard<'d, dyn Interface>,
phantom: PhantomData<I>,
}
impl<I> Deref for InterfaceDerefMut<'_, I>
where
I: Interface,
{
type Target = I;
fn deref(&self) -> &I {
self.iface.downcast_ref::<I>().unwrap()
}
}
impl<I> DerefMut for InterfaceDerefMut<'_, I>
where
I: Interface,
{
fn deref_mut(&mut self) -> &mut Self::Target {
self.iface.downcast_mut::<I>().unwrap()
}
}
pub struct InterfaceRef<I> {
ctxt: SignalContext<'static>,
lock: Arc<RwLock<dyn Interface>>,
phantom: PhantomData<I>,
}
impl<I> InterfaceRef<I>
where
I: 'static,
{
pub async fn get(&self) -> InterfaceDeref<'_, I> {
let iface = self.lock.read().await;
iface
.downcast_ref::<I>()
.expect("Unexpected interface type");
InterfaceDeref {
iface,
phantom: PhantomData,
}
}
pub async fn get_mut(&self) -> InterfaceDerefMut<'_, I> {
let mut iface = self.lock.write().await;
iface
.downcast_ref::<I>()
.expect("Unexpected interface type");
iface
.downcast_mut::<I>()
.expect("Unexpected interface type");
InterfaceDerefMut {
iface,
phantom: PhantomData,
}
}
pub fn signal_context(&self) -> &SignalContext<'static> {
&self.ctxt
}
}
#[derive(Default, derivative::Derivative)]
#[derivative(Debug)]
pub(crate) struct Node {
path: OwnedObjectPath,
children: HashMap<String, Node>,
#[derivative(Debug = "ignore")]
interfaces: HashMap<InterfaceName<'static>, Arc<RwLock<dyn Interface>>>,
}
impl Node {
pub(crate) fn new(path: OwnedObjectPath) -> Self {
let mut node = Self {
path,
..Default::default()
};
node.at(Peer::name(), Peer);
node.at(Introspectable::name(), Introspectable);
node.at(Properties::name(), Properties);
node
}
pub(crate) fn get_child(&self, path: &ObjectPath<'_>) -> Option<&Node> {
let mut node = self;
for i in path.split('/').skip(1) {
if i.is_empty() {
continue;
}
match node.children.get(i) {
Some(n) => node = n,
None => return None,
}
}
Some(node)
}
fn get_child_mut(&mut self, path: &ObjectPath<'_>, create: bool) -> Option<&mut Node> {
let mut node = self;
let mut node_path = String::new();
for i in path.split('/').skip(1) {
if i.is_empty() {
continue;
}
write!(&mut node_path, "/{}", i).unwrap();
match node.children.entry(i.into()) {
Entry::Vacant(e) => {
if create {
let path = node_path.as_str().try_into().expect("Invalid Object Path");
node = e.insert(Node::new(path));
} else {
return None;
}
}
Entry::Occupied(e) => node = e.into_mut(),
}
}
Some(node)
}
pub(crate) fn interface_lock(
&self,
interface_name: InterfaceName<'_>,
) -> Option<Arc<RwLock<dyn Interface>>> {
self.interfaces.get(&interface_name).cloned()
}
fn remove_interface(&mut self, interface_name: InterfaceName<'static>) -> bool {
self.interfaces.remove(&interface_name).is_some()
}
fn is_empty(&self) -> bool {
!self
.interfaces
.keys()
.any(|k| *k != Peer::name() && *k != Introspectable::name() && *k != Properties::name())
}
fn remove_node(&mut self, node: &str) -> bool {
self.children.remove(node).is_some()
}
fn at<I>(&mut self, name: InterfaceName<'static>, iface: I) -> bool
where
I: Interface,
{
match self.interfaces.entry(name) {
Entry::Vacant(e) => e.insert(Arc::new(RwLock::new(iface))),
Entry::Occupied(_) => return false,
};
true
}
fn at_ready(
&mut self,
name: InterfaceName<'static>,
iface: Arc<RwLock<dyn Interface>>,
) -> bool {
match self.interfaces.entry(name) {
Entry::Vacant(e) => e.insert(iface),
Entry::Occupied(_) => return false,
};
true
}
#[async_recursion::async_recursion]
async fn introspect_to_writer<W: Write + Send>(&self, writer: &mut W, level: usize) {
if level == 0 {
writeln!(
writer,
r#"
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>"#
)
.unwrap();
}
for iface in self.interfaces.values() {
iface.read().await.introspect_to_writer(writer, level + 2);
}
for (path, node) in &self.children {
let level = level + 2;
writeln!(
writer,
"{:indent$}<node name=\"{}\">",
"",
path,
indent = level
)
.unwrap();
node.introspect_to_writer(writer, level).await;
writeln!(writer, "{:indent$}</node>", "", indent = level).unwrap();
}
if level == 0 {
writeln!(writer, "</node>").unwrap();
}
}
pub(crate) async fn introspect(&self) -> String {
let mut xml = String::with_capacity(1024);
self.introspect_to_writer(&mut xml, 0).await;
xml
}
}
#[derive(Debug)]
pub struct ObjectServer {
conn: WeakConnection,
root: RwLock<Node>,
}
assert_impl_all!(ObjectServer: Send, Sync, Unpin);
impl ObjectServer {
pub(crate) fn new(conn: &Connection) -> Self {
Self {
conn: conn.into(),
root: RwLock::new(Node::new("/".try_into().expect("zvariant bug"))),
}
}
pub(crate) fn root(&self) -> &RwLock<Node> {
&self.root
}
pub async fn at<'p, P, I>(&self, path: P, iface: I) -> Result<bool>
where
I: Interface,
P: TryInto<ObjectPath<'p>>,
P::Error: Into<Error>,
{
let path = path.try_into().map_err(Into::into)?;
Ok(self
.root
.write()
.await
.get_child_mut(&path, true)
.unwrap()
.at_ready(I::name(), Arc::new(RwLock::new(iface))))
}
pub(crate) async fn at_ready<'node, P>(
&'node self,
path: P,
name: InterfaceName<'static>,
iface: Arc<RwLock<dyn Interface + 'static>>,
) -> Result<bool>
where
P: TryInto<ObjectPath<'static>>,
P::Error: Into<Error>,
{
let path = path.try_into().map_err(Into::into)?;
Ok(self
.root()
.write()
.await
.get_child_mut(&path, true)
.unwrap()
.at_ready(name, iface))
}
pub async fn remove<'p, I, P>(&self, path: P) -> Result<bool>
where
I: Interface,
P: TryInto<ObjectPath<'p>>,
P::Error: Into<Error>,
{
let path = path.try_into().map_err(Into::into)?;
let mut root = self.root.write().await;
let node = root
.get_child_mut(&path, false)
.ok_or(Error::InterfaceNotFound)?;
if !node.remove_interface(I::name()) {
return Err(Error::InterfaceNotFound);
}
if node.is_empty() {
let mut path_parts = path.rsplit('/').filter(|i| !i.is_empty());
let last_part = path_parts.next().unwrap();
let ppath = ObjectPath::from_string_unchecked(
path_parts.fold(String::new(), |a, p| format!("/{}{}", p, a)),
);
root.get_child_mut(&ppath, false)
.unwrap()
.remove_node(last_part);
return Ok(true);
}
Ok(false)
}
pub async fn interface<'p, P, I>(&self, path: P) -> Result<InterfaceRef<I>>
where
I: Interface,
P: TryInto<ObjectPath<'p>>,
P::Error: Into<Error>,
{
let path = path.try_into().map_err(Into::into)?;
let root = self.root().read().await;
let node = root.get_child(&path).ok_or(Error::InterfaceNotFound)?;
let lock = node
.interface_lock(I::name())
.ok_or(Error::InterfaceNotFound)?
.clone();
lock.read()
.await
.downcast_ref::<I>()
.ok_or(Error::InterfaceNotFound)?;
let conn = self.connection();
let ctxt = SignalContext::new(&conn, path).unwrap().into_owned();
Ok(InterfaceRef {
ctxt,
lock,
phantom: PhantomData,
})
}
async fn dispatch_method_call_try(
&self,
connection: &Connection,
msg: &Message,
) -> fdo::Result<Result<()>> {
let path = msg
.path()
.ok_or_else(|| fdo::Error::Failed("Missing object path".into()))?;
let iface = msg
.interface()
.ok_or_else(|| fdo::Error::Failed("Missing interface".into()))?;
let member = msg
.member()
.ok_or_else(|| fdo::Error::Failed("Missing member".into()))?;
let iface = {
let root = self.root.read().await;
let node = root
.get_child(&path)
.ok_or_else(|| fdo::Error::UnknownObject(format!("Unknown object '{}'", path)))?;
node.interface_lock(iface.as_ref()).ok_or_else(|| {
fdo::Error::UnknownInterface(format!("Unknown interface '{}'", iface))
})?
};
let read_lock = iface.read().await;
match read_lock.call(self, connection, msg, member.as_ref()) {
DispatchResult::NotFound => {
return Err(fdo::Error::UnknownMethod(format!(
"Unknown method '{}'",
member
)));
}
DispatchResult::Async(f) => {
return Ok(f.await);
}
DispatchResult::RequiresMut => {}
}
drop(read_lock);
let mut write_lock = iface.write().await;
match write_lock.call_mut(self, connection, msg, member.as_ref()) {
DispatchResult::NotFound => {}
DispatchResult::RequiresMut => {}
DispatchResult::Async(f) => {
return Ok(f.await);
}
}
drop(write_lock);
Err(fdo::Error::UnknownMethod(format!(
"Unknown method '{}'",
member
)))
}
async fn dispatch_method_call(&self, connection: &Connection, msg: &Message) -> Result<()> {
match self.dispatch_method_call_try(connection, msg).await {
Err(e) => {
let hdr = msg.header()?;
connection.reply_dbus_error(&hdr, e).await?;
Ok(())
}
Ok(r) => r,
}
}
pub(crate) async fn dispatch_message(&self, msg: &Message) -> Result<bool> {
match msg.message_type() {
MessageType::MethodCall => {
let conn = self.connection();
self.dispatch_method_call(&conn, msg).await?;
Ok(true)
}
_ => Ok(false),
}
}
pub(crate) fn connection(&self) -> Connection {
self.conn
.upgrade()
.expect("ObjectServer can't exist w/o an associated Connection")
}
}
impl From<crate::blocking::ObjectServer> for ObjectServer {
fn from(server: crate::blocking::ObjectServer) -> Self {
server.into_inner()
}
}
#[cfg(test)]
#[allow(clippy::blacklisted_name)]
mod tests {
#[cfg(all(unix, feature = "async-io"))]
use std::os::unix::net::UnixStream;
use std::{collections::HashMap, convert::TryInto};
#[cfg(all(unix, not(feature = "async-io")))]
use tokio::net::UnixStream;
use crate::utils::block_on;
use async_channel::{bounded, Sender};
use event_listener::Event;
use futures_util::StreamExt;
use ntest::timeout;
use serde::{Deserialize, Serialize};
use test_log::test;
use zbus::DBusError;
use zvariant::{DeserializeDict, OwnedValue, SerializeDict, Type, Value};
use crate::{
dbus_interface, dbus_proxy, CacheProperties, Connection, ConnectionBuilder, InterfaceRef,
MessageHeader, MessageType, ObjectServer, SignalContext,
};
#[derive(Deserialize, Serialize, Type)]
pub struct ArgStructTest {
foo: i32,
bar: String,
}
#[derive(DeserializeDict, SerializeDict, Type, Debug, Value, OwnedValue, PartialEq)]
#[zvariant(signature = "dict")]
pub struct IP4Adress {
prefix: u32,
address: String,
}
#[dbus_proxy(gen_blocking = false)]
trait MyIface {
fn ping(&self) -> zbus::Result<u32>;
fn quit(&self) -> zbus::Result<()>;
fn test_header(&self) -> zbus::Result<()>;
fn test_error(&self) -> zbus::Result<()>;
fn test_single_struct_arg(&self, arg: ArgStructTest) -> zbus::Result<()>;
fn test_single_struct_ret(&self) -> zbus::Result<ArgStructTest>;
fn test_multi_ret(&self) -> zbus::Result<(i32, String)>;
fn test_hashmap_return(&self) -> zbus::Result<HashMap<String, String>>;
fn create_obj(&self, key: &str) -> zbus::Result<()>;
fn destroy_obj(&self, key: &str) -> zbus::Result<()>;
#[dbus_proxy(property)]
fn count(&self) -> zbus::Result<u32>;
#[dbus_proxy(property)]
fn set_count(&self, count: u32) -> zbus::Result<()>;
#[dbus_proxy(property)]
fn hash_map(&self) -> zbus::Result<HashMap<String, String>>;
#[dbus_proxy(property)]
fn address_data(&self) -> zbus::Result<IP4Adress>;
#[dbus_proxy(property)]
fn address_data2(&self) -> zbus::Result<IP4Adress>;
}
#[derive(Debug, Clone)]
enum NextAction {
Quit,
CreateObj(String),
DestroyObj(String),
}
struct MyIfaceImpl {
next_tx: Sender<NextAction>,
count: u32,
}
impl MyIfaceImpl {
fn new(next_tx: Sender<NextAction>) -> Self {
Self { next_tx, count: 0 }
}
}
#[derive(Debug, DBusError)]
#[dbus_error(prefix = "org.freedesktop.MyIface.Error")]
enum MyIfaceError {
SomethingWentWrong(String),
#[dbus_error(zbus_error)]
ZBus(zbus::Error),
}
#[dbus_interface(interface = "org.freedesktop.MyIface")]
impl MyIfaceImpl {
async fn ping(&mut self, #[zbus(signal_context)] ctxt: SignalContext<'_>) -> u32 {
self.count += 1;
if self.count % 3 == 0 {
MyIfaceImpl::alert_count(&ctxt, self.count)
.await
.expect("Failed to emit signal");
}
self.count
}
async fn quit(&self) {
self.next_tx.send(NextAction::Quit).await.unwrap();
}
fn test_header(&self, #[zbus(header)] header: MessageHeader<'_>) {
assert_eq!(header.message_type().unwrap(), MessageType::MethodCall);
assert_eq!(header.member().unwrap().unwrap(), "TestHeader");
}
fn test_error(&self) -> zbus::fdo::Result<()> {
Err(zbus::fdo::Error::Failed("error raised".to_string()))
}
fn test_custom_error(&self) -> Result<(), MyIfaceError> {
Err(MyIfaceError::SomethingWentWrong("oops".to_string()))
}
fn test_single_struct_arg(
&self,
arg: ArgStructTest,
#[zbus(header)] header: MessageHeader<'_>,
) -> zbus::fdo::Result<()> {
assert_eq!(header.signature()?.unwrap(), "(is)");
assert_eq!(arg.foo, 1);
assert_eq!(arg.bar, "TestString");
Ok(())
}
fn test_single_struct_ret(&self) -> zbus::fdo::Result<ArgStructTest> {
Ok(ArgStructTest {
foo: 42,
bar: String::from("Meaning of life"),
})
}
#[dbus_interface(out_args("foo", "bar"))]
fn test_multi_ret(&self) -> zbus::fdo::Result<(i32, String)> {
Ok((42, String::from("Meaning of life")))
}
async fn test_hashmap_return(&self) -> zbus::fdo::Result<HashMap<String, String>> {
let mut map = HashMap::new();
map.insert("hi".into(), "hello".into());
map.insert("bye".into(), "now".into());
Ok(map)
}
async fn create_obj(&self, key: String) {
self.next_tx.send(NextAction::CreateObj(key)).await.unwrap();
}
async fn create_obj_inside(
&self,
#[zbus(object_server)] object_server: &ObjectServer,
key: String,
) {
object_server
.at(
format!("/zbus/test/{}", key),
MyIfaceImpl::new(self.next_tx.clone()),
)
.await
.unwrap();
}
async fn destroy_obj(&self, key: String) {
self.next_tx
.send(NextAction::DestroyObj(key))
.await
.unwrap();
}
#[dbus_interface(property)]
fn set_count(&mut self, val: u32) -> zbus::fdo::Result<()> {
if val == 42 {
return Err(zbus::fdo::Error::InvalidArgs("Tsss tsss!".to_string()));
}
self.count = val;
Ok(())
}
#[dbus_interface(property)]
fn count(&self) -> u32 {
self.count
}
#[dbus_interface(property)]
async fn hash_map(&self) -> HashMap<String, String> {
self.test_hashmap_return().await.unwrap()
}
#[dbus_interface(property)]
fn address_data(&self) -> IP4Adress {
IP4Adress {
address: "127.0.0.1".to_string(),
prefix: 1234,
}
}
#[dbus_interface(property)]
fn address_data2(&self) -> HashMap<String, OwnedValue> {
let mut map = HashMap::new();
map.insert("address".into(), Value::from("127.0.0.1").into());
map.insert("prefix".into(), 1234u32.into());
map
}
#[dbus_interface(signal)]
async fn alert_count(ctxt: &SignalContext<'_>, val: u32) -> zbus::Result<()>;
}
fn check_hash_map(map: HashMap<String, String>) {
assert_eq!(map["hi"], "hello");
assert_eq!(map["bye"], "now");
}
fn check_ipv4_address(address: IP4Adress) {
assert_eq!(
address,
IP4Adress {
address: "127.0.0.1".to_string(),
prefix: 1234,
}
);
}
async fn my_iface_test(conn: Connection, event: Event) -> zbus::Result<u32> {
let proxy = MyIfaceProxy::builder(&conn)
.destination("org.freedesktop.MyService")?
.path("/org/freedesktop/MyService")?
.cache_properties(CacheProperties::No)
.build()
.await?;
let props_proxy = zbus::fdo::PropertiesProxy::builder(&conn)
.destination("org.freedesktop.MyService")?
.path("/org/freedesktop/MyService")?
.build()
.await?;
let mut props_changed_stream = props_proxy.receive_properties_changed().await?;
event.notify(1);
match props_changed_stream.next().await {
Some(changed) => {
assert_eq!(
*changed.args()?.changed_properties().keys().next().unwrap(),
"Count"
);
}
None => panic!(""),
};
proxy.ping().await?;
assert_eq!(proxy.count().await?, 1);
assert_eq!(proxy.cached_count()?, None);
proxy.test_header().await?;
proxy
.test_single_struct_arg(ArgStructTest {
foo: 1,
bar: "TestString".into(),
})
.await?;
check_hash_map(proxy.test_hashmap_return().await?);
check_hash_map(proxy.hash_map().await?);
check_ipv4_address(proxy.address_data().await?);
check_ipv4_address(proxy.address_data2().await?);
#[cfg(feature = "xml")]
{
let xml = proxy.introspect().await?;
let node = crate::xml::Node::from_reader(xml.as_bytes())?;
let ifaces = node.interfaces();
let iface = ifaces
.iter()
.find(|i| i.name() == "org.freedesktop.MyIface")
.unwrap();
let methods = iface.methods();
for method in methods {
if method.name() != "TestSingleStructRet" && method.name() != "TestMultiRet" {
continue;
}
let args = method.args();
let mut out_args = args.iter().filter(|a| a.direction().unwrap() == "out");
if method.name() == "TestSingleStructRet" {
assert_eq!(args.len(), 1);
assert_eq!(out_args.next().unwrap().ty(), "(is)");
assert!(out_args.next().is_none());
} else {
assert_eq!(args.len(), 2);
let foo = out_args.find(|a| a.name() == Some("foo")).unwrap();
assert_eq!(foo.ty(), "i");
let bar = out_args.find(|a| a.name() == Some("bar")).unwrap();
assert_eq!(bar.ty(), "s");
}
}
}
let _ = proxy.test_single_struct_ret().await?.foo;
let _ = proxy.test_multi_ret().await?.1;
let val = proxy.ping().await?;
proxy.create_obj("MyObj").await?;
assert!(proxy.call_method("CreateObj", &()).await.is_err());
let my_obj_proxy = MyIfaceProxy::builder(&conn)
.destination("org.freedesktop.MyService")?
.path("/zbus/test/MyObj")?
.build()
.await?;
my_obj_proxy.receive_count_changed().await;
assert_eq!(my_obj_proxy.cached_count()?, None);
assert_eq!(my_obj_proxy.count().await?, 0);
assert_eq!(my_obj_proxy.cached_count()?, Some(0));
assert_eq!(
my_obj_proxy.cached_property_raw("Count").as_deref(),
Some(&Value::from(0u32))
);
my_obj_proxy.ping().await?;
proxy.destroy_obj("MyObj").await?;
assert!(my_obj_proxy.introspect().await.is_err());
assert!(my_obj_proxy.ping().await.is_err());
proxy
.call_method("CreateObjInside", &("CreatedInside"))
.await?;
let created_inside_proxy = MyIfaceProxy::builder(&conn)
.destination("org.freedesktop.MyService")?
.path("/zbus/test/CreatedInside")?
.build()
.await?;
created_inside_proxy.ping().await?;
proxy.destroy_obj("CreatedInside").await?;
proxy.quit().await?;
Ok(val)
}
#[test]
#[timeout(15000)]
fn basic_iface() {
block_on(basic_iface_(false));
}
#[cfg(unix)]
#[test]
#[timeout(15000)]
fn basic_iface_unix_p2p() {
block_on(basic_iface_(true));
}
async fn basic_iface_(p2p: bool) {
let event = event_listener::Event::new();
let guid = zbus::Guid::generate();
let (service_conn_builder, client_conn_builder) = if p2p {
#[cfg(unix)]
{
let (p0, p1) = UnixStream::pair().unwrap();
(
ConnectionBuilder::unix_stream(p0).server(&guid).p2p(),
ConnectionBuilder::unix_stream(p1).p2p(),
)
}
#[cfg(windows)]
{
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let p1 = std::net::TcpStream::connect(addr).unwrap();
let p0 = listener.incoming().next().unwrap().unwrap();
(
ConnectionBuilder::tcp_stream(p0).server(&guid).p2p(),
ConnectionBuilder::tcp_stream(p1).p2p(),
)
}
} else {
let service_conn_builder = ConnectionBuilder::session()
.unwrap()
.name("org.freedesktop.MyService")
.unwrap()
.name("org.freedesktop.MyService.foo")
.unwrap()
.name("org.freedesktop.MyService.bar")
.unwrap();
let client_conn_builder = ConnectionBuilder::session().unwrap();
(service_conn_builder, client_conn_builder)
};
let (next_tx, next_rx) = bounded(64);
let iface = MyIfaceImpl::new(next_tx.clone());
let service_conn_builder = service_conn_builder
.serve_at("/org/freedesktop/MyService", iface)
.unwrap();
let (service_conn, client_conn) =
futures_util::try_join!(service_conn_builder.build(), client_conn_builder.build(),)
.unwrap();
let listen = event.listen();
let child = async_std::task::spawn(my_iface_test(client_conn, event));
listen.await;
let iface: InterfaceRef<MyIfaceImpl> = service_conn
.object_server()
.interface("/org/freedesktop/MyService")
.await
.unwrap();
iface
.get()
.await
.count_changed(iface.signal_context())
.await
.unwrap();
loop {
MyIfaceImpl::alert_count(iface.signal_context(), 51)
.await
.unwrap();
match next_rx.recv().await.unwrap() {
NextAction::Quit => break,
NextAction::CreateObj(key) => {
let path = format!("/zbus/test/{}", key);
service_conn
.object_server()
.at(path, MyIfaceImpl::new(next_tx.clone()))
.await
.unwrap();
}
NextAction::DestroyObj(key) => {
let path = format!("/zbus/test/{}", key);
service_conn
.object_server()
.remove::<MyIfaceImpl, _>(path)
.await
.unwrap();
}
}
}
let val = child.await.unwrap();
assert_eq!(val, 2);
if p2p {
return;
}
assert_eq!(
service_conn.release_name("org.freedesktop.MyService").await,
Ok(true)
);
assert_eq!(
service_conn
.release_name("org.freedesktop.MyService.foo")
.await,
Ok(true)
);
assert_eq!(
service_conn
.release_name("org.freedesktop.MyService.bar")
.await,
Ok(true)
);
let proxy = zbus::fdo::DBusProxy::new(&service_conn).await.unwrap();
assert_eq!(
proxy
.name_has_owner("org.freedesktop.MyService".try_into().unwrap())
.await,
Ok(false)
);
assert_eq!(
proxy
.name_has_owner("org.freedesktop.MyService.foo".try_into().unwrap())
.await,
Ok(false)
);
assert_eq!(
proxy
.name_has_owner("org.freedesktop.MyService.bar".try_into().unwrap())
.await,
Ok(false)
);
}
}