use futures_util::{
future::{select, Either},
stream::StreamExt,
};
use std::{convert::TryInto, future::ready};
use zbus::{block_on, fdo, CacheProperties, SignalContext};
use zbus_macros::{dbus_interface, dbus_proxy, DBusError};
#[test]
fn test_proxy() {
#[dbus_proxy(
interface = "org.freedesktop.zbus_macros.ProxyParam",
default_service = "org.freedesktop.zbus_macros",
default_path = "/org/freedesktop/zbus_macros/test"
)]
trait ProxyParam {
#[dbus_proxy(object = "Test")]
fn some_method<T>(&self, test: &T);
}
#[dbus_proxy(
assume_defaults = false,
interface = "org.freedesktop.zbus_macros.Test",
default_service = "org.freedesktop.zbus_macros"
)]
trait Test {
fn a_test(&self, val: &str) -> zbus::Result<u32>;
fn some_method<T>(&self, object_path: &T) -> zbus::Result<()>;
#[dbus_proxy(name = "CheckRENAMING")]
fn check_renaming(&self) -> zbus::Result<Vec<u8>>;
#[dbus_proxy(property)]
fn property(&self) -> fdo::Result<Vec<String>>;
#[dbus_proxy(property(emits_changed_signal = "const"))]
fn a_const_property(&self) -> fdo::Result<Vec<String>>;
#[dbus_proxy(property(emits_changed_signal = "false"))]
fn a_live_property(&self) -> fdo::Result<Vec<String>>;
#[dbus_proxy(property)]
fn set_property(&self, val: u16) -> fdo::Result<()>;
#[dbus_proxy(signal)]
fn a_signal<T>(&self, arg: u8, other: T) -> fdo::Result<()>
where
T: AsRef<str>;
}
block_on(async move {
let connection = zbus::Connection::session().await.unwrap();
let proxy = TestProxy::builder(&connection)
.path("/org/freedesktop/zbus_macros/test")
.unwrap()
.cache_properties(CacheProperties::No)
.build()
.await
.unwrap();
fdo::DBusProxy::builder(&connection)
.build()
.await
.unwrap()
.request_name(
"org.freedesktop.zbus_macros".try_into().unwrap(),
fdo::RequestNameFlags::DoNotQueue.into(),
)
.await
.unwrap();
let mut stream = proxy.receive_a_signal().await.unwrap();
let left_future = async move {
// These calls will never happen so just testing the build mostly.
let signal = stream.next().await.unwrap();
let args = signal.args::<&str>().unwrap();
assert_eq!(*args.arg(), 0u8);
assert_eq!(*args.other(), "whatever");
};
futures_util::pin_mut!(left_future);
let right_future = async {
ready(()).await;
};
futures_util::pin_mut!(right_future);
if let Either::Left((_, _)) = select(left_future, right_future).await {
panic!("Shouldn't be receiving our dummy signal: `ASignal`");
}
});
}
#[test]
fn test_derive_error() {
#[derive(Debug, DBusError)]
#[dbus_error(prefix = "org.freedesktop.zbus")]
enum Test {
#[dbus_error(zbus_error)]
ZBus(zbus::Error),
SomeExcuse,
#[dbus_error(name = "I.Am.Sorry.Dave")]
IAmSorryDave(String),
LetItBe {
desc: String,
},
}
}
#[test]
fn test_interface() {
use serde::{Deserialize, Serialize};
use zbus::{
zvariant::{Type, Value},
Interface,
};
struct Test<T> {
something: String,
generic: T,
}
#[derive(Serialize, Deserialize, Type, Value)]
struct MyCustomPropertyType(u32);
#[dbus_interface(name = "org.freedesktop.zbus.Test")]
impl<T: 'static> Test<T>
where
T: serde::ser::Serialize + zbus::zvariant::Type + Send + Sync,
{
fn no_arg(&self) {
unimplemented!()
}
#[allow(unused_assignments)]
fn str_u32(&self, mut val: &str) -> zbus::fdo::Result<u32> {
let res = val
.parse()
.map_err(|e| zbus::fdo::Error::Failed(format!("Invalid val: {e}")));
val = "test mut";
res
}
fn many_output(&self) -> zbus::fdo::Result<(&T, String)> {
Ok((&self.generic, self.something.clone()))
}
fn pair_output(&self) -> zbus::fdo::Result<((u32, String),)> {
unimplemented!()
}
#[dbus_interface(property)]
fn my_custom_property(&self) -> MyCustomPropertyType {
unimplemented!()
}
#[dbus_interface(property)]
fn set_my_custom_property(&self, mut _value: MyCustomPropertyType) {
_value = MyCustomPropertyType(42);
}
#[dbus_interface(name = "CheckVEC")]
fn check_vec(&self) -> Vec<u8> {
unimplemented!()
}
#[dbus_interface(property)]
fn my_prop(&self) -> u16 {
unimplemented!()
}
#[dbus_interface(property)]
fn set_my_prop(&mut self, _val: u16) {
unimplemented!()
}
#[dbus_interface(signal)]
async fn signal(ctxt: &SignalContext<'_>, arg: u8, other: &str) -> zbus::Result<()>;
}
const EXPECTED_XML: &str = r#"<interface name="org.freedesktop.zbus.Test">
<!--
Testing `no_arg` documentation is reflected in XML.
-->
<method name="NoArg">
</method>
<method name="StrU32">
<arg name="val" type="s" direction="in"/>
<arg type="u" direction="out"/>
</method>
<method name="ManyOutput">
<arg type="u" direction="out"/>
<arg type="s" direction="out"/>
</method>
<method name="PairOutput">
<arg type="(us)" direction="out"/>
</method>
<method name="CheckVEC">
<arg type="ay" direction="out"/>
</method>
<!--
Emit a signal.
-->
<signal name="Signal">
<arg name="arg" type="y"/>
<arg name="other" type="s"/>
</signal>
<property name="MyCustomProperty" type="u" access="readwrite"/>
<!--
Testing my_prop documentation is reflected in XML.
And that too.
-->
<property name="MyProp" type="q" access="readwrite"/>
</interface>
"#;
let t = Test {
something: String::from("somewhere"),
generic: 42u32,
};
let mut xml = String::new();
t.introspect_to_writer(&mut xml, 0);
assert_eq!(xml, EXPECTED_XML);
assert_eq!(Test::<u32>::name(), "org.freedesktop.zbus.Test");
if false {
block_on(async {
let c = zbus::Connection::session().await.unwrap();
let s = c.object_server();
let m =
zbus::Message::method(None::<()>, None::<()>, "/", None::<()>, "StrU32", &(42,))
.unwrap();
let _ = t.call(&s, &c, &m, "StrU32".try_into().unwrap());
let ctxt = SignalContext::new(&c, "/does/not/matter").unwrap();
block_on(Test::<u32>::signal(&ctxt, 23, "ergo sum")).unwrap();
});
}
}
mod signal_from_message {
use super::*;
use std::sync::Arc;
use zbus::MessageBuilder;
#[dbus_proxy(
interface = "org.freedesktop.zbus_macros.Test",
default_service = "org.freedesktop.zbus_macros",
default_path = "/org/freedesktop/zbus_macros/test"
)]
trait Test {
#[dbus_proxy(signal)]
fn signal_u8(&self, arg: u8) -> fdo::Result<()>;
#[dbus_proxy(signal)]
fn signal_string(&self, arg: String) -> fdo::Result<()>;
}
#[test]
fn signal_u8() {
let message = Arc::new(
MessageBuilder::signal(
"/org/freedesktop/zbus_macros/test",
"org.freedesktop.zbus_macros.Test",
"SignalU8",
)
.expect("Failed to create signal message builder")
.build(&(1u8,))
.expect("Failed to build signal message"),
);
assert!(
SignalU8::from_message(message.clone()).is_some(),
"Message is a SignalU8"
);
assert!(
SignalString::from_message(message).is_none(),
"Message is not a SignalString"
);
}
#[test]
fn signal_string() {
let message = Arc::new(
MessageBuilder::signal(
"/org/freedesktop/zbus_macros/test",
"org.freedesktop.zbus_macros.Test",
"SignalString",
)
.expect("Failed to create signal message builder")
.build(&(String::from("test"),))
.expect("Failed to build signal message"),
);
assert!(
SignalString::from_message(message.clone()).is_some(),
"Message is a SignalString"
);
assert!(
SignalU8::from_message(message).is_none(),
"Message is not a SignalU8"
);
}
#[test]
fn wrong_data() {
let message = Arc::new(
MessageBuilder::signal(
"/org/freedesktop/zbus_macros/test",
"org.freedesktop.zbus_macros.Test",
"SignalU8",
)
.expect("Failed to create signal message builder")
.build(&(String::from("test"),))
.expect("Failed to build signal message"),
);
let signal = SignalU8::from_message(message).expect("Message is a SignalU8");
signal
.args()
.expect_err("Message does not have correct data");
}
}