use splitrs::file_analyzer::FileAnalyzer;
use std::collections::HashSet;
fn analyze_with_split(code: &str, max_impl_lines: usize) -> (syn::File, FileAnalyzer) {
let file = syn::parse_file(code).expect("test fixture must parse as Rust");
let mut analyzer = FileAnalyzer::new(true, max_impl_lines);
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()
}
fn padded_method(name: &str, body: &str) -> String {
format!(
r#"
pub fn {name}(&self) -> i32 {{
{body}
let _a1 = 1;
let _a2 = 2;
let _a3 = 3;
let _a4 = 4;
let _a5 = 5;
let _a6 = 6;
let _a7 = 7;
let _a8 = 8;
let _a9 = 9;
let _a10 = 10;
let _a11 = 11;
let _a12 = 12;
let _a13 = 13;
let _a14 = 14;
let _a15 = 15;
let _a16 = 16;
let _a17 = 17;
let _a18 = 18;
let _a19 = 19;
42
}}
"#,
name = name,
body = body,
)
}
#[test]
fn test_serialization_methods_get_semantic_name() {
let m1 = padded_method("serialize_json", "let _x = 0;");
let m2 = padded_method("deserialize_json", "let _x = 1;");
let code = format!(
r#"
pub struct Payload {{
data: Vec<u8>,
}}
impl Payload {{
{m1}
{m2}
}}
"#
);
let (_, analyzer) = analyze_with_split(&code, 30);
let names = module_names(&analyzer, 500);
assert!(
names.iter().any(|n| n.contains("serialization")),
"expected a module containing 'serialization' in name; got: {:?}",
names
);
assert!(
!names.iter().any(|n| n == "payload_impl"),
"should not produce generic 'payload_impl' when semantic name is available; got: {:?}",
names
);
}
#[test]
fn test_builder_methods_get_semantic_name() {
let m1 = padded_method("with_value", "let _x = 0;");
let m2 = padded_method("with_name", "let _x = 1;");
let code = format!(
r#"
pub struct Config {{
value: i32,
name: String,
}}
impl Config {{
{m1}
{m2}
}}
"#
);
let (_, analyzer) = analyze_with_split(&code, 30);
let names = module_names(&analyzer, 500);
assert!(
names.iter().any(|n| n.contains("builders")),
"expected a module containing 'builders' in name; got: {:?}",
names
);
}
#[test]
fn test_collision_falls_back_to_numeric_suffix() {
let m1 = padded_method("serialize_json", "let _x = 0;");
let m2 = padded_method("deserialize_json", "let _x = 1;");
let m3 = padded_method("serialize_xml", "let _x = 2;");
let m4 = padded_method("deserialize_xml", "let _x = 3;");
let code = format!(
r#"
pub struct Document {{
content: String,
}}
impl Document {{
{m1}
{m2}
}}
impl Document {{
{m3}
{m4}
}}
"#
);
let (_, analyzer) = analyze_with_split(&code, 10);
let names = module_names(&analyzer, 500);
let ser_count = names.iter().filter(|n| n.contains("serialization")).count();
assert!(
ser_count >= 2,
"expected at least 2 serialization-named modules for two separate impl blocks; got {:?}",
names
);
assert!(
names
.iter()
.any(|n| n.ends_with("_2") && n.contains("serialization")),
"expected a collision-deduplicated `..._2` module; got: {:?}",
names
);
}
#[test]
fn test_unstructured_methods_preserve_legacy_naming() {
let m1 = padded_method("foo_bar", "let _x = 0;");
let m2 = padded_method("baz_qux", "let _x = 1;");
let code = format!(
r#"
pub struct Widget {{
data: u32,
}}
impl Widget {{
{m1}
{m2}
}}
"#
);
let (_, analyzer) = analyze_with_split(&code, 30);
let names = module_names(&analyzer, 500);
assert!(
names
.iter()
.any(|n| n.contains("_impl") || n.contains("_group")),
"expected legacy '_impl' or '_group' style name for unstructured methods; got: {:?}",
names
);
assert!(
!names
.iter()
.any(|n| n.contains("serialization") || n.contains("builders")),
"should not produce semantic name for unstructured methods; got: {:?}",
names
);
}