use serde::{
Serialize, Serializer,
de::IgnoredAny,
ser::{SerializeMap, SerializeTuple},
};
use zbus::{
Error, MaxDepthExceeded, Variant,
wire::{
Array, Dict, LE, Signature, Structure,
serialized::{Context, Data},
to_bytes, to_bytes_for_signature,
},
};
#[test]
fn recursion_limits() {
let ctxt = Context::new(LE, 0);
let mut value = Variant::from(0u8);
for _ in 0..64 {
value = Variant::Variant(Box::new(value));
}
assert!(matches!(
to_bytes(ctxt, &value),
Err(Error::MaxDepthExceeded(MaxDepthExceeded::Container))
));
let vec = vec![vec![vec![vec![vec![vec![vec![vec![vec![vec![vec![
vec![vec![vec![vec![vec![vec![vec![vec![vec![vec![vec![
vec![vec![vec![vec![vec![vec![vec![vec![vec![vec![vec![
0u8,
]]]]]]]]]]],
]]]]]]]]]]],
]]]]]]]]]]];
assert!(matches!(
to_bytes(ctxt, &vec),
Err(Error::MaxDepthExceeded(MaxDepthExceeded::Array))
));
let tuple = ((((((((((((((((((
(((((((((((((((0u8,),),),),),),),),),),),),),),),
),),),),),),),),),),),),),),),),),);
assert!(matches!(
to_bytes(ctxt, &tuple),
Err(Error::MaxDepthExceeded(MaxDepthExceeded::Structure))
));
let mut value = Variant::from(0u8);
for _ in 0..32 {
value = Variant::Variant(Box::new(value));
}
let tuple_array =
(
((((((((((((((((vec![vec![vec![vec![vec![vec![vec![vec![vec![
vec![vec![vec![vec![vec![vec![vec![value]]]]]]],
]]]]]]]]],),),),),),),),),),),),),),),),),
);
assert!(matches!(
to_bytes(ctxt, &tuple_array),
Err(Error::MaxDepthExceeded(MaxDepthExceeded::Container))
));
}
#[test]
fn nesting_per_signature() {
let ctxt = Context::new(LE, 0);
let array = in_arrays(in_variants(in_arrays(Variant::U8(7), 17), 1), 16);
let array = Array::try_from(array).unwrap();
assert_eq!(array.signature(), "aaaaaaaaaaaaaaaav");
let encoded = to_bytes(ctxt, &array).unwrap();
let (decoded, len) = encoded
.deserialize_for_dynamic_signature::<_, Array<'_>>(array.signature())
.unwrap();
assert_eq!(len, encoded.len());
assert_eq!(decoded, array);
let structure = in_structures(in_variants(in_structures(Variant::U8(7), 17), 1), 16);
let structure = Structure::try_from(structure).unwrap();
let signature = format!("{}v{}", "(".repeat(16), ")".repeat(16));
assert_eq!(structure.signature(), signature.as_str());
let encoded = to_bytes(ctxt, &structure).unwrap();
let (decoded, len) = encoded
.deserialize_for_dynamic_signature::<_, Structure<'_>>(structure.signature())
.unwrap();
assert_eq!(len, encoded.len());
assert_eq!(decoded, structure);
}
#[test]
fn dict_entries() {
let ctxt = Context::new(LE, 0);
for (dicts, fits) in [(21, true), (22, false)] {
let mut value = Variant::U8(7);
for _ in 0..dicts {
value = in_dicts(in_variants(value, 1), 1);
}
let dict = Dict::try_from(value).unwrap();
assert_eq!(dict.signature(), "a{sv}");
let result = dict_to_bytes(ctxt, &dict);
let case = format!("{dicts} dicts");
if fits {
assert_deserializes_back(result, dict.signature(), &case);
} else {
assert_too_deep(result, &case);
}
}
}
#[test]
fn variants_hold_any_value_of_their_type() {
let ctxt = Context::new(LE, 0);
let cases = [
("ay", Variant::from(vec![7u8]), 63),
("empty as", Variant::from(Vec::<&str>::new()), 63),
("as", Variant::from(vec!["7"]), 63),
("ab", Variant::from(vec![true]), 63),
("empty ab", Variant::from(Vec::<bool>::new()), 63),
("empty aas", Variant::from(Vec::<Vec<&str>>::new()), 62),
("empty a(yy)", Variant::from(Vec::<(u8, u8)>::new()), 62),
];
for (name, array, max) in cases {
let variant = in_variants(array.clone(), max - 1);
let case = format!("{name} in {max} variants");
let encoded = to_bytes(ctxt, &variant).unwrap_or_else(|e| panic!("{case}: {e}"));
let (decoded, len) = encoded.deserialize::<Variant<'_>>().unwrap();
assert_eq!(len, encoded.len(), "{case}");
assert_eq!(decoded, variant, "{case}");
let variant = in_variants(array, max);
let case = format!("{name} in {} variants", max + 1);
assert_too_deep(to_bytes(ctxt, &variant), &case);
}
}
#[test]
fn arrays_outside_variants() {
let ctxt = Context::new(LE, 0);
let cases = [
("ay", Variant::from(vec![7u8]), true),
("empty ay", Variant::from(Vec::<u8>::new()), true),
("ab", Variant::from(vec![true]), true),
("empty ab", Variant::from(Vec::<bool>::new()), false),
("empty as", Variant::from(Vec::<&str>::new()), false),
("as", Variant::from(vec!["7"]), true),
];
for (name, array, elements_count) in cases {
for structures in [1, 2] {
let dict = in_dicts(in_structures(array.clone(), structures), 31);
let dict = Dict::try_from(dict).unwrap();
let result = dict_to_bytes(ctxt, &dict);
let case = format!("{name} in {structures} structures");
if structures == 2 && elements_count {
assert_too_deep(result, &case);
} else {
assert_deserializes_back(result, dict.signature(), &case);
}
}
}
}
#[test]
fn bytes() {
let ctxt = Context::new(LE, 0);
for structures in [1, 2] {
let signature = format!(
"{}{}ay{}{}",
"a{s".repeat(31),
"(".repeat(structures),
")".repeat(structures),
"}".repeat(31),
);
let signature = Signature::try_from(signature.as_str()).unwrap();
let value = EmptyBytes {
dicts: 31,
structures,
};
let result = to_bytes_for_signature(ctxt, &signature, &value);
let case = format!("bytes in {structures} structures");
if structures == 2 {
assert_too_deep(result, &case);
} else {
assert_deserializes_back(result, &signature, &case);
}
}
}
fn in_arrays(value: Variant<'static>, n: usize) -> Variant<'static> {
(0..n).fold(value, |element, _| {
let mut array = Array::new(element.value_signature());
array.append(element).unwrap();
Variant::Array(array)
})
}
fn in_structures(value: Variant<'static>, n: usize) -> Variant<'static> {
(0..n).fold(value, |field, _| {
let structure = Structure::builder().append_field(field).build().unwrap();
Variant::Structure(structure)
})
}
fn in_dicts(value: Variant<'static>, n: usize) -> Variant<'static> {
(0..n).fold(value, |value, _| {
let mut dict = Dict::new(&Signature::Str, value.value_signature());
dict.append(Variant::from("k"), value).unwrap();
Variant::Dict(dict)
})
}
fn in_variants(value: Variant<'static>, n: usize) -> Variant<'static> {
(0..n).fold(value, |value, _| Variant::Variant(Box::new(value)))
}
fn dict_to_bytes(ctxt: Context, dict: &Dict<'_, '_>) -> zbus::Result<Data<'static, 'static>> {
to_bytes_for_signature(ctxt, dict.signature(), dict)
}
fn assert_deserializes_back(
result: zbus::Result<Data<'static, 'static>>,
signature: &Signature,
case: &str,
) {
let encoded = result.unwrap_or_else(|e| panic!("{case}: {e}"));
let (_, len) = encoded
.deserialize_for_signature::<_, IgnoredAny>(signature)
.unwrap_or_else(|e| panic!("{case}: {e}"));
assert_eq!(len, encoded.len(), "{case}");
}
fn assert_too_deep(result: zbus::Result<Data<'static, 'static>>, case: &str) {
let result = result.map(|encoded| encoded.len());
assert!(
matches!(
result,
Err(Error::MaxDepthExceeded(MaxDepthExceeded::Container))
),
"{case}: {result:?}"
);
}
struct EmptyBytes {
dicts: usize,
structures: usize,
}
impl Serialize for EmptyBytes {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match (self.dicts, self.structures) {
(0, 0) => serializer.serialize_bytes(&[]),
(0, structures) => {
let mut tuple = serializer.serialize_tuple(1)?;
tuple.serialize_element(&Self {
dicts: 0,
structures: structures - 1,
})?;
tuple.end()
}
(dicts, structures) => {
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry(
"k",
&Self {
dicts: dicts - 1,
structures,
},
)?;
map.end()
}
}
}
}