use crate::adapters::analyzers::architecture::matcher::find_item_kind_matches;
use crate::adapters::analyzers::architecture::{MatchLocation, ViolationKind};
fn parse(src: &str) -> syn::File {
syn::parse_str(src).expect("test fixture must parse")
}
fn find(src: &str, kinds: &[&str]) -> Vec<MatchLocation> {
let ast = parse(src);
let owned: Vec<String> = kinds.iter().map(|k| (*k).to_string()).collect();
find_item_kind_matches("fixture.rs", &ast, &owned)
}
fn kinds(hits: &[MatchLocation]) -> Vec<&'static str> {
hits.iter()
.filter_map(|h| match &h.kind {
ViolationKind::ItemKind { kind, .. } => Some(*kind),
_ => None,
})
.collect()
}
fn names(hits: &[MatchLocation]) -> Vec<String> {
hits.iter()
.filter_map(|h| match &h.kind {
ViolationKind::ItemKind { name, .. } => Some(name.clone()),
_ => None,
})
.collect()
}
#[test]
fn clean_file_no_matches() {
let src = r#"
pub fn sync_function() {}
pub struct Foo;
impl Foo { pub fn bar(&self) {} }
"#;
assert!(find(src, &["async_fn", "unsafe_fn", "unsafe_impl"]).is_empty());
}
#[test]
fn unrequested_kind_never_matches() {
let src = "pub async fn foo() {}";
assert!(find(src, &["unsafe_fn"]).is_empty());
}
#[test]
fn async_fn_top_level() {
let src = "pub async fn foo() {}";
let hits = find(src, &["async_fn"]);
assert_eq!(hits.len(), 1);
assert_eq!(kinds(&hits), vec!["async_fn"]);
assert_eq!(names(&hits), vec!["foo"]);
}
#[test]
fn async_fn_inside_impl() {
let src = r#"
pub struct S;
impl S { pub async fn work(&self) {} }
"#;
let hits = find(src, &["async_fn"]);
assert_eq!(hits.len(), 1);
assert_eq!(names(&hits), vec!["work"]);
}
#[test]
fn async_fn_inside_nested_mod() {
let src = r#"
pub mod inner { pub async fn foo() {} }
"#;
let hits = find(src, &["async_fn"]);
assert_eq!(hits.len(), 1);
}
#[test]
fn unsafe_fn_top_level() {
let src = "pub unsafe fn bar() {}";
let hits = find(src, &["unsafe_fn"]);
assert_eq!(kinds(&hits), vec!["unsafe_fn"]);
assert_eq!(names(&hits), vec!["bar"]);
}
#[test]
fn unsafe_fn_inside_impl() {
let src = r#"
pub struct S;
impl S { pub unsafe fn dangerous(&self) {} }
"#;
let hits = find(src, &["unsafe_fn"]);
assert_eq!(hits.len(), 1);
}
#[test]
fn unsafe_impl_matched() {
let src = r#"
pub struct S;
unsafe impl Send for S {}
"#;
let hits = find(src, &["unsafe_impl"]);
assert_eq!(kinds(&hits), vec!["unsafe_impl"]);
}
#[test]
fn regular_impl_not_matched() {
let src = r#"
pub struct S;
impl Default for S { fn default() -> Self { S } }
"#;
assert!(find(src, &["unsafe_impl"]).is_empty());
}
#[test]
fn static_mut_matched() {
let src = "pub static mut COUNTER: usize = 0;";
let hits = find(src, &["static_mut"]);
assert_eq!(kinds(&hits), vec!["static_mut"]);
assert_eq!(names(&hits), vec!["COUNTER"]);
}
#[test]
fn immutable_static_not_matched() {
let src = "pub static COUNTER: usize = 0;";
assert!(find(src, &["static_mut"]).is_empty());
}
#[test]
fn extern_c_block_matched() {
let src = r#"
extern "C" {
pub fn printf(fmt: *const u8, ...) -> i32;
}
"#;
let hits = find(src, &["extern_c_block"]);
assert_eq!(kinds(&hits), vec!["extern_c_block"]);
}
#[test]
fn extern_rust_block_not_matched() {
let src = r#"
extern "Rust" { fn foo(); }
"#;
let hits = find(src, &["extern_c_block"]);
assert_eq!(
hits.len(),
1,
"any extern block counts as extern_c_block today"
);
}
#[test]
fn inline_cfg_test_mod_matched() {
let src = r#"
#[cfg(test)]
mod tests { fn t() {} }
"#;
let hits = find(src, &["inline_cfg_test_module"]);
assert_eq!(kinds(&hits), vec!["inline_cfg_test_module"]);
assert_eq!(names(&hits), vec!["tests"]);
}
#[test]
fn cfg_test_mod_declaration_without_body_not_matched() {
let src = r#"
#[cfg(test)]
mod tests;
"#;
assert!(find(src, &["inline_cfg_test_module"]).is_empty());
}
#[test]
fn mod_without_cfg_test_not_matched() {
let src = "pub mod utils { fn helper() {} }";
assert!(find(src, &["inline_cfg_test_module"]).is_empty());
}
#[test]
fn top_level_cfg_test_fn_matched() {
let src = r#"
#[cfg(test)]
fn helper_fixture() {}
"#;
let hits = find(src, &["top_level_cfg_test_item"]);
assert_eq!(kinds(&hits), vec!["top_level_cfg_test_item"]);
assert_eq!(names(&hits), vec!["helper_fixture"]);
}
#[test]
fn top_level_cfg_test_const_matched() {
let src = r#"
#[cfg(test)]
const FIXTURE: usize = 10;
"#;
let hits = find(src, &["top_level_cfg_test_item"]);
assert_eq!(kinds(&hits), vec!["top_level_cfg_test_item"]);
}
#[test]
fn top_level_cfg_test_impl_matched() {
let src = r#"
pub struct S;
#[cfg(test)]
impl S { fn only_in_test(&self) {} }
"#;
let hits = find(src, &["top_level_cfg_test_item"]);
assert_eq!(kinds(&hits), vec!["top_level_cfg_test_item"]);
}
#[test]
fn top_level_cfg_test_ignores_mod_tests() {
let src = r#"
#[cfg(test)]
mod tests { fn t() {} }
"#;
assert!(find(src, &["top_level_cfg_test_item"]).is_empty());
}
#[test]
fn nested_cfg_test_fn_inside_mod_not_top_level() {
let src = r#"
pub mod inner {
#[cfg(test)]
fn fixture() {}
}
"#;
assert!(find(src, &["top_level_cfg_test_item"]).is_empty());
}
#[test]
fn multiple_kinds_each_checked() {
let src = r#"
pub async fn a() {}
pub unsafe fn b() {}
pub static mut C: i32 = 0;
"#;
let hits = find(src, &["async_fn", "unsafe_fn", "static_mut"]);
assert_eq!(hits.len(), 3);
let ks: std::collections::HashSet<&str> = kinds(&hits).into_iter().collect();
assert!(ks.contains("async_fn"));
assert!(ks.contains("unsafe_fn"));
assert!(ks.contains("static_mut"));
}
#[test]
fn unknown_kind_silently_ignored() {
let src = "pub async fn foo() {}";
let hits = find(src, &["bogus_kind", "async_fn"]);
assert_eq!(hits.len(), 1);
}