use prebindgen_registry::RegistryBuilder;
use super::*;
fn resolve_result(
tag: &str,
registry: RegistryBuilder<KotlinMeta>,
jni: JniGenBuilder,
) -> Result<(), String> {
match jni.build_with(registry) {
Ok(gen) => {
let dir = unique_test_dir(tag);
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
gen.write_rust(dir.join("gen.rs"))
.expect("valid binding writes");
Ok(())
}
Err(e) => Err(e.to_string()),
}
}
fn one_fn(src: &str) -> RegistryBuilder<KotlinMeta> {
let f: syn::ItemFn = syn::parse_str(src).unwrap();
crate::test_util::reg_from_items(declare_referenced(vec![(syn::Item::Fn(f), myflat_loc())]))
.expect("index")
}
#[test]
fn invalid_name_override_is_error() {
let registry = one_fn("pub fn z_do_thing() -> i64 { 0 }");
let jni = JniGenBuilder::new()
.set_package_prefix("io.test.jni")
.package(crate::package!("thing").fun(prebindgen_registry::fun!(z_do_thing).name("when")));
let err = resolve_result("jni_sym_name", registry, jni).expect_err("invalid .name()");
assert!(err.contains("`when`"), "{err}");
assert!(err.contains("not a valid Kotlin identifier"), "{err}");
}
#[test]
fn invalid_hook_output_is_error() {
let registry = one_fn("pub fn z_do_thing() -> i64 { 0 }");
let jni = JniGenBuilder::new()
.set_package_prefix("io.test.jni")
.set_fun_name_mangle(|_pkg, _name| "1bad".to_string())
.package(crate::package!("thing").fun(prebindgen_registry::fun!(z_do_thing)));
let err = resolve_result("jni_sym_hook", registry, jni).expect_err("invalid hook output");
assert!(err.contains("`1bad`"), "{err}");
assert!(err.contains("not a valid Kotlin identifier"), "{err}");
}
#[test]
fn valid_default_names_pass() {
let registry = one_fn("pub fn z_do_thing() -> i64 { 0 }");
let jni = JniGenBuilder::new()
.set_package_prefix("io.test.jni")
.package(crate::package!("thing").fun(prebindgen_registry::fun!(z_do_thing)));
resolve_result("jni_sym_ok", registry, jni).expect("valid names must pass");
}
#[test]
fn duplicate_native_symbol_is_error() {
let items: Vec<(syn::Item, prebindgen::SourceLocation)> = vec![
(
syn::Item::Fn(syn::parse_str("pub fn z_alpha() -> i64 { 0 }").unwrap()),
myflat_loc(),
),
(
syn::Item::Fn(syn::parse_str("pub fn z_beta() -> i64 { 0 }").unwrap()),
myflat_loc(),
),
];
let registry = crate::test_util::reg_from_items(declare_referenced(items)).expect("index");
let jni = JniGenBuilder::new()
.set_package_prefix("io.test.jni")
.set_method_name_mangle(|_pkg, _class, _name| "collide".to_string())
.package(
crate::package!("thing")
.fun(prebindgen_registry::fun!(z_alpha))
.fun(prebindgen_registry::fun!(z_beta)),
);
let err = resolve_result("jni_sym_dupnative", registry, jni).expect_err("duplicate symbol");
assert!(err.contains("duplicate native symbol"), "{err}");
assert!(err.contains("z_alpha") && err.contains("z_beta"), "{err}");
}
#[test]
fn keyword_struct_field_is_sanitized_not_error() {
let items: Vec<(syn::Item, prebindgen::SourceLocation)> = vec![
(
syn::Item::Struct(syn::parse_quote!(
pub struct Payload {
pub object: i64,
pub value: f64,
}
)),
myflat_loc(),
),
(
syn::Item::Fn(syn::parse_str("pub fn make() -> Payload { todo!() }").unwrap()),
myflat_loc(),
),
];
let registry = crate::test_util::reg_from_items(declare_referenced(items)).expect("index");
let jni = JniGenBuilder::new()
.set_package_prefix("io.test.jni")
.package(
crate::package!("thing")
.class(crate::data_class!(Payload))
.fun(prebindgen_registry::fun!(make)),
);
let dir = unique_test_dir("jni_sym_field");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let gen = jni.build_with(registry).expect("resolve");
gen.write_rust(dir.join("gen.rs"))
.expect("keyword field sanitized, not an error");
let paths = gen.write_kotlin(&dir.join("kotlin")).expect("write_kotlin");
let all: String = paths
.iter()
.filter_map(|p| std::fs::read_to_string(p).ok())
.collect();
let ac: String = all.split_whitespace().collect();
assert!(
ac.contains("valobject_:Long") || ac.contains("object_:Long"),
"{all}"
);
assert!(
!ac.contains("valobject:Long"),
"unsanitized keyword leaked:\n{all}"
);
}
#[test]
fn class_interface_collision_is_error() {
let registry = one_fn("pub fn z_thing_new() -> ZThing { unimplemented!() }");
let jni = JniGenBuilder::new()
.set_package_prefix("io.test.jni")
.set_interface_name_mangle(|_pkg, n| n.to_string()) .package(
crate::package!("thing")
.class(crate::ptr_class!(ZThing).interface())
.fun(prebindgen_registry::fun!(z_thing_new)),
);
let err = resolve_result("jni_sym_iface", registry, jni).expect_err("iface==class collision");
assert!(
err.contains("duplicate top-level Kotlin name `ZThing`"),
"{err}"
);
}
#[test]
fn same_name_same_signature_functions_collide() {
let items: Vec<(syn::Item, prebindgen::SourceLocation)> = vec![
(
syn::Item::Fn(syn::parse_str("pub fn z_alpha(x: i64) -> i64 { x }").unwrap()),
myflat_loc(),
),
(
syn::Item::Fn(syn::parse_str("pub fn z_beta(y: i64) -> i64 { y }").unwrap()),
myflat_loc(),
),
];
let registry = crate::test_util::reg_from_items(declare_referenced(items)).expect("index");
let jni = JniGenBuilder::new()
.set_package_prefix("io.test.jni")
.package(
crate::package!("thing")
.fun(prebindgen_registry::fun!(z_alpha).name("combine"))
.fun(prebindgen_registry::fun!(z_beta).name("combine")),
);
let err = resolve_result("jni_ov_collide", registry, jni).expect_err("overload clash");
assert!(err.contains("conflicting Kotlin overload"), "{err}");
assert!(err.contains("combine"), "{err}");
}
#[test]
fn same_name_distinct_signature_functions_allowed() {
let items: Vec<(syn::Item, prebindgen::SourceLocation)> = vec![
(
syn::Item::Fn(syn::parse_str("pub fn z_alpha(x: i64) -> i64 { x }").unwrap()),
myflat_loc(),
),
(
syn::Item::Fn(syn::parse_str("pub fn z_beta(y: bool) -> i64 { 0 }").unwrap()),
myflat_loc(),
),
];
let registry = crate::test_util::reg_from_items(declare_referenced(items)).expect("index");
let jni = JniGenBuilder::new()
.set_package_prefix("io.test.jni")
.package(
crate::package!("thing")
.fun(prebindgen_registry::fun!(z_alpha).name("combine"))
.fun(prebindgen_registry::fun!(z_beta).name("combine")),
);
resolve_result("jni_ov_ok", registry, jni).expect("distinct signatures are valid overloads");
}
#[test]
fn method_and_factory_same_name_do_not_collide() {
let items: Vec<(syn::Item, prebindgen::SourceLocation)> = vec![
(
syn::Item::Fn(syn::parse_str("pub fn thing_size(this_: &Thing) -> i64 { 0 }").unwrap()),
myflat_loc(),
),
(
syn::Item::Fn(syn::parse_str("pub fn thing_make() -> Thing { todo!() }").unwrap()),
myflat_loc(),
),
];
let registry = crate::test_util::reg_from_items(declare_referenced(items)).expect("index");
let jni = JniGenBuilder::new()
.set_package_prefix("io.test.jni")
.package(
crate::package!("thing").class(
crate::ptr_class!(Thing)
.method(prebindgen_registry::fun!(thing_size).name("of"))
.constructor(prebindgen_registry::fun!(thing_make).name("of")),
),
);
resolve_result("jni_ov_scopes", registry, jni).expect("method vs factory don't collide");
}