use super::super::ExtendrBackend;
use super::{make_config, resolved_one};
use crate::core::backend::Backend;
use crate::core::ir::*;
fn gated_function(name: &str, cfg: Option<&str>) -> FunctionDef {
FunctionDef {
name: name.to_string(),
cfg: cfg.map(str::to_string),
..Default::default()
}
}
fn generate_r(api: &ApiSurface, config: &crate::core::config::ResolvedCrateConfig) -> String {
ExtendrBackend
.generate_bindings(api, config)
.expect("extendr generation")
.iter()
.map(|f| format!("// ==== {} ====\n{}", f.path.display(), f.content))
.collect::<Vec<_>>()
.join("\n")
}
#[test]
fn a_function_gated_on_an_enabled_feature_reaches_the_r_surface() {
let api = ApiSurface {
functions: vec![
gated_function("count_tokens", Some(r#"feature = "tokenizer""#)),
gated_function("always_there", None),
],
..Default::default()
};
let out = generate_r(&api, &make_config());
assert!(
out.contains("fn always_there;"),
"ungated function missing, fixture no longer exercises registration:\n{out}"
);
assert!(
out.contains("fn count_tokens;"),
"a function gated on an enabled feature must still be registered in extendr_module!:\n{out}"
);
assert!(
!out.contains(r#"#[cfg(feature = "tokenizer")]"#),
"the gate must be discharged before generation, not copied into the binding crate:\n{out}"
);
}
#[test]
fn a_function_gated_on_a_disabled_feature_is_removed_entirely() {
let config = resolved_one(
r#"
[workspace]
languages = ["r"]
[[crates]]
name = "test-lib"
sources = ["src/lib.rs"]
[crates.r]
package_name = "testlib"
default_features = false
features = ["other"]
"#,
);
let api = ApiSurface {
functions: vec![
gated_function("count_tokens", Some(r#"feature = "tokenizer""#)),
gated_function("always_there", None),
],
..Default::default()
};
let out = generate_r(&api, &config);
assert!(
out.contains("fn always_there;"),
"ungated function missing, fixture no longer exercises registration:\n{out}"
);
assert!(
!out.contains("count_tokens"),
"a function gated on a disabled feature must not be named anywhere in the R surface:\n{out}"
);
}