use super::*;
#[test]
fn function_name_renames_symbol() {
let loc = SourceLocation::default();
let func: syn::ItemFn = syn::parse_quote!(
pub fn rust_init() {
unimplemented!()
}
);
let reg =
crate::test_util::reg_from_items(declare_referenced([(syn::Item::Fn(func), loc.clone())]))
.expect("index items");
let cb = CbindgenBuilder::new()
.source_module(syn::parse_quote!(zenoh_flat))
.function(syn::parse_quote!(rust_init))
.base_name("z_init");
let src = write(cb, reg, "fnname");
let compact: String = src.split_whitespace().collect();
assert!(compact.contains("extern\"C\"fnz_init("), "{src}");
assert!(compact.contains("zenoh_flat::rust_init("), "{src}");
}
#[test]
fn error_after_ptr_struct_panics() {
assert!(catch(|| {
let _ = CbindgenBuilder::new()
.opaque_ptr(syn::parse_quote!(ZKeyExpr))
.error();
}));
}
#[test]
fn panic_after_data_struct_panics() {
assert!(catch(|| {
let _ = CbindgenBuilder::new()
.data_struct(syn::parse_quote!(Error))
.panic();
}));
}
#[test]
fn name_with_no_declaration_panics() {
assert!(catch(|| {
let _ = CbindgenBuilder::new()
.source_module(syn::parse_quote!(zenoh_flat))
.base_name("x");
}));
}
#[test]
fn function_and_ignore_function_conflict_panics() {
assert!(catch(|| {
let _ = CbindgenBuilder::new()
.function(syn::parse_quote!(z_open))
.ignore_function(syn::parse_quote!(z_open));
}));
}
#[test]
fn data_struct_and_ignore_type_conflict_panics() {
assert!(catch(|| {
let _ = CbindgenBuilder::new()
.data_struct(syn::parse_quote!(Error))
.ignore_type(syn::parse_quote!(Error));
}));
}
#[test]
fn free_memory_function_required() {
let loc = SourceLocation::default();
let func: syn::ItemFn = syn::parse_quote!(
pub fn z_describe(key: String) -> Result<String, 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))
.data_struct(syn::parse_quote!(Error))
.base_name("z_error")
.error()
.function(syn::parse_quote!(z_describe));
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("nofree.rs")));
}));
assert!(
result.is_err(),
"expected a build error when string memory is produced without a free fn"
);
}
#[test]
fn manglers_generate_all_names() {
let loc = SourceLocation::default();
let func: syn::ItemFn = syn::parse_quote!(
pub fn z_sub(
key_expr: ZKeyExpr,
callback: impl Fn(ZSample) + Send + Sync + 'static,
on_close: impl Fn() + Send + Sync + 'static,
) -> Result<ZSubscriber, 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")
.mangle_rust_type(|short| {
let s = short.strip_prefix('Z').unwrap_or(short);
match s {
"KeyExpr" => "keyexpr".to_string(),
other => snake_case(other),
}
})
.mangle_type_name(|base| format!("z_{base}_t"))
.mangle_destructor(|base| format!("z_{base}_drop"))
.mangle_callback(|bases| {
if bases.is_empty() {
"z_closure_drop_t".to_string()
} else {
format!("z_closure_{}_t", bases.join("_"))
}
})
.mangle_function(|n| {
if n.starts_with("z_") {
n.to_string()
} else {
format!("z_{n}")
}
})
.opaque_ptr(syn::parse_quote!(ZKeyExpr))
.opaque_ptr(syn::parse_quote!(ZSample))
.opaque_ptr(syn::parse_quote!(ZSubscriber))
.data_struct(syn::parse_quote!(Error))
.error()
.callback(syn::parse_quote!(impl Fn(ZSample) + Send + Sync + 'static))
.callback(syn::parse_quote!(impl Fn() + Send + Sync + 'static))
.function(syn::parse_quote!(z_sub));
let src = write(cbindgen, registry, "manglers");
let compact: String = src.split_whitespace().collect();
assert!(compact.contains("structz_keyexpr_t"), "{src}");
assert!(compact.contains("structz_sample_t"), "{src}");
assert!(compact.contains("structz_subscriber_t"), "{src}");
assert!(compact.contains("structz_error_t"), "{src}");
// Destructor mangler.
assert!(
compact.contains("fnz_keyexpr_drop(this_:*mutz_keyexpr_t"),
"{src}"
);
assert!(
compact.contains("fnz_sample_drop(this_:*mutz_sample_t"),
"{src}"
);
// Callback mangler (arg base + zero-arg).
assert!(compact.contains("structz_closure_sample_t"), "{src}");
assert!(compact.contains("structz_closure_drop_t"), "{src}");
// Callback `call` takes the owned handle wire produced via the manglers.
assert!(
compact.contains("fn(*mutz_sample_t,*mut::core::ffi::c_void)"),
"{src}"
);
// Function mangler leaves the already-`z_`-prefixed symbol unchanged.
assert!(compact.contains("extern\"C\"fnz_sub("), "{src}");
assert!(compact.contains("->*mutz_subscriber_t"), "{src}");
}
#[test]
fn qualified_signature_spelling_matches_bare_opaque_ptr() {
let loc = SourceLocation {
crate_name: Some("zenoh-flat".to_string()),
..Default::default()
};
let func: syn::ItemFn = syn::parse_quote!(
pub fn z_keyexpr_len(k: &zenoh_flat::ZKeyExpr) -> i64 {
unimplemented!()
}
);
let reg =
crate::test_util::reg_from_items(declare_referenced([(syn::Item::Fn(func), loc.clone())]))
.expect("index items");
let cb = CbindgenBuilder::new()
.source_module(syn::parse_quote!(zenoh_flat))
.opaque_ptr(syn::parse_quote!(ZKeyExpr))
.function(syn::parse_quote!(z_keyexpr_len))
.panic();
let src = write(cb, reg, "q95");
let compact: String = src.split_whitespace().collect();
assert!(compact.contains("extern\"C\"fnz_keyexpr_len("), "{src}");
assert!(compact.contains("zenoh_flat::z_keyexpr_len("), "{src}");
}
#[test]
fn enum_mirror_preserves_the_source_discriminant_domain() {
let loc = SourceLocation::default();
let e: syn::ItemEnum = syn::parse_quote!(
pub enum Wide {
FromConst = SOME_CONST,
Huge = 18446744073709551615,
Implicit,
}
);
let f: syn::ItemFn = syn::parse_quote!(
pub fn wide_get() -> Wide {
unimplemented!()
}
);
let registry = crate::test_util::reg_from_items(declare_referenced([
(syn::Item::Enum(e), loc.clone()),
(syn::Item::Fn(f), loc.clone()),
]))
.expect("index items");
let cbindgen = CbindgenBuilder::new()
.source_module(syn::parse_quote!(myflat))
.mangle_type_name(|base| format!("{base}_t"))
.enum_type(syn::parse_quote!(Wide))
.function(syn::parse_quote!(wide_get));
let src = write(cbindgen, registry, "enum_domain");
let compact: String = src.split_whitespace().collect();
assert!(compact.contains("FromConst=SOME_CONST"), "{src}");
assert!(compact.contains("Huge=18446744073709551615"), "{src}");
assert!(compact.contains("Implicit,"), "{src}");
}