use std::{collections::BTreeSet, fs, process::Command};
use rust_split::{SplitOutput, explode, split_bin, split_mod};
use syn::visit::Visit;
fn write_output(out: &SplitOutput, dir: &std::path::Path) {
let mut paths = BTreeSet::new();
for file in &out.files {
assert!(paths.insert(&file.path), "duplicate output: {}", file.path);
let path = dir.join(&file.path);
fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(path, &file.contents).unwrap();
}
}
fn compile(dir: &std::path::Path, root: &str, enabled: bool) {
let mut cmd = Command::new("rustc");
cmd.current_dir(dir)
.args(["--edition=2024", "--crate-type=lib", root, "-o", "out.rlib"]);
if enabled {
cmd.args(["--cfg", "branch_a"]);
}
let output = cmd.output().unwrap();
assert!(
output.status.success(),
"{}",
String::from_utf8_lossy(&output.stderr)
);
}
struct BracedConditionalModules;
impl<'ast> Visit<'ast> for BracedConditionalModules {
fn visit_item_mod(&mut self, item: &'ast syn::ItemMod) {
if item
.attrs
.iter()
.any(|attr| attr.path().is_ident("cfg") || attr.path().is_ident("cfg_attr"))
{
assert!(
item.content.is_some(),
"conditional module {} lost its braces",
item.ident
);
}
syn::visit::visit_item_mod(self, item);
}
}
fn large_function() -> String {
format!(
"/// Keep this declaration intact.\n#[inline(never)]\npub fn oversized() -> Buffer<u8> {{\n{} Buffer::new()\n}}\n",
" // indivisible body\n".repeat(35)
)
}
#[test]
fn oversized_leaf_survives_at_its_original_scope_in_both_topologies() {
let leaf = large_function();
let source = format!("use std::vec::Vec as Buffer;\n{leaf}\npub fn small() {{}}\n");
let exploded = explode(&source).unwrap();
for out in [
split_bin(&exploded, 25, "lib"),
split_mod(&exploded, 25, "foo"),
] {
assert!(!out.still_oversized.is_empty());
assert!(
out.files[0].contents.contains(&leaf),
"oversized declaration was lost"
);
assert!(
out.files[0]
.contents
.contains("use std::vec::Vec as Buffer;")
);
let temp = tempfile::tempdir().unwrap();
write_output(&out, temp.path());
if out.files[0].path == "foo.rs" {
fs::write(temp.path().join("lib.rs"), "pub mod foo;\n").unwrap();
}
compile(temp.path(), "lib.rs", false);
}
}
#[test]
fn oversized_nested_leaf_keeps_its_imports_and_preamble() {
let leaf = large_function();
let source = format!(
"pub mod outer {{\n#![deny(unsafe_code)]\nuse std::vec::Vec as Buffer;\n{leaf}\npub fn small() {{}}\n}}\n"
);
let out = split_bin(&explode(&source).unwrap(), 25, "lib");
let root = out.files.iter().find(|f| f.path == "outer/mod.rs").unwrap();
assert!(
root.contents.contains(&leaf),
"nested oversized declaration was lost"
);
assert!(root.contents.contains("use std::vec::Vec as Buffer;"));
assert!(root.contents.starts_with("#![deny(unsafe_code)]"));
let temp = tempfile::tempdir().unwrap();
write_output(&out, temp.path());
compile(temp.path(), "lib.rs", false);
}
#[test]
fn oversized_main_survives() {
let source = format!("fn main() {{\n{} }}\n", " // large main\n".repeat(30));
let out = split_bin(&explode(&source).unwrap(), 20, "main");
assert!(out.files[0].contents.contains(&source));
}
#[test]
fn conditional_module_bodies_survive_with_explicit_boundaries() {
let gated = r#"
/// First platform implementation.
#[cfg(branch_a)]
pub mod platform_rename {
pub fn rename() -> u8 { 1 }
}
// Fallback implementation.
#[cfg_attr(all(), cfg(not(branch_a)))]
pub mod platform_rename {
pub fn rename() -> u8 { 2 }
}
"#;
for nested in [false, true] {
let source = if nested {
format!(
"pub mod outer {{\nuse std::vec::Vec as Buffer;\n{gated}\n{}\n}}",
large_function()
)
} else {
gated.to_owned()
};
let out = split_bin(&explode(&source).unwrap(), 25, "lib");
let combined: String = out.files.iter().map(|f| f.contents.as_str()).collect();
assert!(
combined.contains(gated),
"gated declarations must remain intact"
);
for file in &out.files {
BracedConditionalModules.visit_file(&syn::parse_file(&file.contents).unwrap());
}
let temp = tempfile::tempdir().unwrap();
write_output(&out, temp.path());
compile(temp.path(), "lib.rs", false);
compile(temp.path(), "lib.rs", true);
}
}
#[test]
fn cli_reports_unmet_budget_after_writing_preserved_output() {
let temp = tempfile::tempdir().unwrap();
let source = temp.path().join("lib.rs");
fs::write(&source, large_function()).unwrap();
let out = temp.path().join("output");
let result = Command::new(env!("CARGO_BIN_EXE_rust-split"))
.arg("split")
.arg(&source)
.args(["--max-loc", "20", "--out"])
.arg(&out)
.output()
.unwrap();
assert!(
!result.status.success(),
"an unmet budget must not report success"
);
assert!(
fs::read_to_string(out.join("lib.rs"))
.unwrap()
.contains(&large_function())
);
assert!(String::from_utf8_lossy(&result.stderr).contains("preserved"));
}
#[test]
fn cli_reports_root_overhead_exceeding_budget() {
let temp = tempfile::tempdir().unwrap();
let source = temp.path().join("lib.rs");
fs::write(
&source,
(0..15)
.map(|n| format!("pub fn f{n}() {{}}\n"))
.collect::<String>(),
)
.unwrap();
let result = Command::new(env!("CARGO_BIN_EXE_rust-split"))
.arg("split")
.arg(&source)
.args(["--max-loc", "15", "--out"])
.arg(temp.path().join("output"))
.output()
.unwrap();
assert!(!result.status.success(), "root wiring exceeds budget");
}
#[test]
fn cli_succeeds_when_all_output_fits() {
let temp = tempfile::tempdir().unwrap();
let source = temp.path().join("lib.rs");
fs::write(&source, "pub fn small() {}\n").unwrap();
let result = Command::new(env!("CARGO_BIN_EXE_rust-split"))
.arg("split")
.arg(&source)
.args(["--max-loc", "50", "--out"])
.arg(temp.path().join("output"))
.output()
.unwrap();
assert!(
result.status.success(),
"{}",
String::from_utf8_lossy(&result.stderr)
);
}