use super::*;
#[test]
fn result_error_not_declared_is_build_error() {
let loc = SourceLocation::default();
let func: syn::ItemFn = syn::parse_quote!(
pub fn z_keyexpr_try_from(s: String) -> Result<ZKeyExpr, Error> {
unimplemented!()
}
);
let registry = crate::test_util::reg_from_items(declare_referenced([
(syn::Item::Fn(func), loc.clone()),
(syn::Item::Struct(error_struct()), loc.clone()),
]))
.expect("index items");
let cbindgen = CbindgenBuilder::new()
.source_module(syn::parse_quote!(zenoh_flat))
.free_memory_function("z_free")
.opaque_ptr(syn::parse_quote!(ZKeyExpr))
.base_name("z_keyexpr")
.data_struct(syn::parse_quote!(Error))
.base_name("z_error")
.function(syn::parse_quote!(z_keyexpr_try_from));
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _ = cbindgen
.build_with(registry)
.and_then(|gen| gen.write_rust(std::env::temp_dir().join("nope.rs")));
}));
assert!(
result.is_err(),
"expected a build error for undeclared error type"
);
}
#[test]
fn fallible_input_without_result_needs_panic() {
let loc = SourceLocation::default();
let func: syn::ItemFn = syn::parse_quote!(
pub fn z_log(s: String) {
unimplemented!()
}
);
let reg1 = crate::test_util::reg_from_items(declare_referenced([(
syn::Item::Fn(func.clone()),
loc.clone(),
)]))
.expect("index items");
let cb1 = CbindgenBuilder::new()
.source_module(syn::parse_quote!(zenoh_flat))
.function(syn::parse_quote!(z_log));
let err = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _ = cb1
.build_with(reg1)
.and_then(|gen| gen.write_rust(std::env::temp_dir().join("nope2.rs")));
}));
assert!(err.is_err(), "expected a build error without .panic()");
let reg2 =
crate::test_util::reg_from_items(declare_referenced([(syn::Item::Fn(func), loc.clone())]))
.expect("index items");
let cb2 = CbindgenBuilder::new()
.source_module(syn::parse_quote!(zenoh_flat))
.function(syn::parse_quote!(z_log))
.panic();
let src = write(cb2, reg2, "panicfn");
let compact: String = src.split_whitespace().collect();
assert!(compact.contains("extern\"C\"fnz_log"), "{src}");
assert!(compact.contains("panic!("), "{src}");
}
#[test]
fn error_out_param_is_null_guarded() {
let loc = SourceLocation::default();
let ptr_fn: syn::ItemFn = syn::parse_quote!(
pub fn z_keyexpr_try_from(s: String) -> Result<ZKeyExpr, Error> {
unimplemented!()
}
);
let unit_fn: syn::ItemFn = syn::parse_quote!(
pub fn z_unit_op(s: String) -> Result<(), Error> {
unimplemented!()
}
);
let registry = crate::test_util::reg_from_items(declare_referenced([
(syn::Item::Fn(ptr_fn), loc.clone()),
(syn::Item::Fn(unit_fn), loc.clone()),
(syn::Item::Struct(error_struct()), loc.clone()),
]))
.expect("index items");
let cbindgen = CbindgenBuilder::new()
.source_module(syn::parse_quote!(zenoh_flat))
.free_memory_function("z_free")
.opaque_ptr(syn::parse_quote!(ZKeyExpr))
.base_name("z_keyexpr")
.data_struct(syn::parse_quote!(Error))
.base_name("z_error")
.error()
.function(syn::parse_quote!(z_keyexpr_try_from))
.function(syn::parse_quote!(z_unit_op));
let src = write(cbindgen, registry, "err_null_guard");
let compact: String = src.split_whitespace().collect();
assert!(
compact.contains("=>{if!e.is_null(){*e=__cbg_out_Error(__err);}::core::ptr::null_mut()}"),
"{src}"
);
assert!(
compact.contains("=>{if!e.is_null(){*e=__cbg_out_Error(__err);}false}"),
"{src}"
);
assert!(
compact
.matches("if!e.is_null(){*e=__cbg_out_Error(")
.count()
>= 4,
"expected ≥4 guarded `*e =` writes (2 input-decode + 2 Err arms):\n{src}"
);
let mut search = compact.as_str();
while let Some(pos) = search.find("*e=") {
let before = &search[..pos];
assert!(
before.ends_with("if!e.is_null(){"),
"unguarded `*e =` found before offset {pos}:\n{src}"
);
search = &search[pos + 3..];
}
}