use splitrs::file_analyzer::FileAnalyzer;
use std::collections::HashSet;
fn parse(code: &str) -> syn::File {
syn::parse_file(code).expect("test fixture must parse as Rust")
}
fn analyze(code: &str) -> (syn::File, FileAnalyzer) {
let file = parse(code);
let mut analyzer = FileAnalyzer::new(false, 500);
analyzer.analyze(&file);
(file, analyzer)
}
fn module_names(analyzer: &FileAnalyzer, max_lines: usize) -> HashSet<String> {
analyzer
.group_by_module(max_lines)
.into_iter()
.map(|m| m.name)
.collect()
}
#[test]
fn test_const_static_routed_to_constants_module() {
let code = r#"
pub const MAX_SIZE: usize = 1024;
pub static GLOBAL_FLAG: bool = false;
pub const BUFFER_LEN: usize = 4096;
"#;
let (_, analyzer) = analyze(code);
let names = module_names(&analyzer, 500);
assert!(
names.contains("constants"),
"expected 'constants' module for const/static items; got: {:?}",
names
);
assert!(
!names.contains("functions"),
"should NOT route const/static to 'functions'; got: {:?}",
names
);
}
#[test]
fn test_macro_rules_routed_to_macros_module() {
let code = r#"
#[macro_export]
macro_rules! my_vec {
($($x:expr),*) => {
{
let mut v = Vec::new();
$(v.push($x);)*
v
}
};
}
macro_rules! assert_close {
($a:expr, $b:expr, $tol:expr) => {
assert!(($a - $b).abs() < $tol, "{} vs {} tolerance {}", $a, $b, $tol);
};
}
"#;
let (_, analyzer) = analyze(code);
let names = module_names(&analyzer, 500);
assert!(
names.contains("macros"),
"expected 'macros' module for macro_rules! items; got: {:?}",
names
);
assert!(
!names.contains("functions"),
"should NOT route macro_rules! to 'functions'; got: {:?}",
names
);
}
#[test]
fn test_type_alias_routed_to_type_aliases_module() {
let code = r#"
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
pub type ByteSlice<'a> = &'a [u8];
pub type Callback = fn(i32) -> i32;
"#;
let (_, analyzer) = analyze(code);
let names = module_names(&analyzer, 500);
assert!(
names.contains("type_aliases"),
"expected 'type_aliases' module for type alias items; got: {:?}",
names
);
assert!(
!names.contains("functions"),
"should NOT route type aliases to 'functions'; got: {:?}",
names
);
}
#[test]
fn test_mixed_items_partitioned_correctly() {
let code = r#"
pub const VERSION: &str = "1.0.0";
pub type Offset = usize;
macro_rules! log_info {
($msg:expr) => { println!("[INFO] {}", $msg); };
}
pub fn helper(x: i32) -> i32 {
x + 1
}
"#;
let (_, analyzer) = analyze(code);
let names = module_names(&analyzer, 500);
assert!(
names.contains("constants"),
"expected 'constants' for const items; got: {:?}",
names
);
assert!(
names.contains("type_aliases"),
"expected 'type_aliases' for type alias; got: {:?}",
names
);
assert!(
names.contains("macros"),
"expected 'macros' for macro_rules!; got: {:?}",
names
);
assert!(
names.contains("functions"),
"expected 'functions' for fn items; got: {:?}",
names
);
}
#[test]
fn test_empty_buckets_skip_module_emission() {
let code = r#"
pub fn alpha(x: i32) -> i32 { x }
pub fn beta(x: i32) -> i32 { x + 1 }
"#;
let (_, analyzer) = analyze(code);
let names = module_names(&analyzer, 500);
assert!(
!names.contains("constants"),
"should not emit empty 'constants' module; got: {:?}",
names
);
assert!(
!names.contains("macros"),
"should not emit empty 'macros' module; got: {:?}",
names
);
assert!(
!names.contains("type_aliases"),
"should not emit empty 'type_aliases' module; got: {:?}",
names
);
assert!(
names.contains("functions"),
"should emit 'functions' for fn items; got: {:?}",
names
);
}