use super::*;
#[test]
fn parts_copy_only_the_imports_they_reference() {
let src = "use std::fmt::Display;\nuse std::collections::HashMap;\n\npub fn alpha(d: &dyn Display) {\n let _ = d;\n}\n\npub fn bravo() -> i32 {\n 1\n}\n";
let exploded = explode(src).unwrap();
let out = split_mod(&exploded, 10_000, "mod");
let alpha = out
.files
.iter()
.find(|f| f.contents.contains("fn alpha"))
.unwrap();
let bravo = out
.files
.iter()
.find(|f| f.contents.contains("fn bravo"))
.unwrap();
assert!(
alpha.contents.contains("use std::fmt::Display;"),
"alpha references Display:\n{}",
alpha.contents
);
assert!(
!alpha.contents.contains("HashMap"),
"alpha never names HashMap -> import dropped:\n{}",
alpha.contents
);
assert!(
!bravo.contents.contains("use std::fmt::Display;"),
"bravo references nothing from the header:\n{}",
bravo.contents
);
assert!(!bravo.contents.contains("HashMap"));
}
#[test]
fn globs_and_anonymous_trait_imports_are_always_copied() {
let src = "use std::fmt::Write as _;\nuse std::prelude::v1::*;\n\npub fn writes(s: &mut String) {\n let _ = write!(s, \"x\");\n}\n\npub fn other() -> i32 {\n 2\n}\n";
let exploded = explode(src).unwrap();
let out = split_mod(&exploded, 10_000, "mod");
for f in &out.files {
if f.path == "mod.rs" {
continue;
}
assert!(
f.contents.contains("use std::fmt::Write as _;"),
"{} must keep the `as _` trait import:\n{}",
f.path,
f.contents
);
assert!(
f.contents.contains("use std::prelude::v1::*;"),
"{} must keep the glob import:\n{}",
f.path,
f.contents
);
}
}
#[test]
fn item_less_root_keeps_no_imports() {
let src =
"use std::collections::HashMap;\n\npub fn solo(m: HashMap<u8, u8>) {\n let _ = m;\n}\n";
let exploded = explode(src).unwrap();
let out = split_mod(&exploded, 10_000, "mod");
let root = out.files.iter().find(|f| f.path == "mod.rs").unwrap();
assert!(
!root.contents.contains("use std::collections::HashMap;"),
"item-less root drops imports it doesn't use:\n{}",
root.contents
);
let part = out.files.iter().find(|f| f.path != "mod.rs").unwrap();
assert!(
part.contents.contains("use std::collections::HashMap;"),
"the part that uses HashMap keeps it"
);
}
#[test]
fn root_keeps_imports_an_extracted_mod_body_references() {
let src = "use std::collections::HashMap;\n\n\
pub fn alpha() -> i32 {\n 1\n}\n\n\
#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n \
fn smoke() {\n let _m: HashMap<u8, u8> = HashMap::new();\n \
assert_eq!(alpha(), 1);\n }\n}\n";
let exploded = explode(src).unwrap();
let out = split_bin(&exploded, 10_000, "lib");
let root = out.files.iter().find(|f| f.path == "lib.rs").unwrap();
assert!(
root.contents.contains("use std::collections::HashMap;"),
"root must keep imports the extracted body names:\n{}",
root.contents
);
}
#[test]
fn bin_root_keeps_only_imports_main_references() {
let src = "use std::process::exit;\nuse std::collections::HashMap;\n\nfn worker() -> HashMap<u8, u8> {\n HashMap::new()\n}\n\nfn main() {\n let _ = worker();\n exit(0);\n}\n";
let exploded = explode(src).unwrap();
let out = split_bin(&exploded, 10_000, "main");
let root = out.files.iter().find(|f| f.path == "main.rs").unwrap();
assert!(
root.contents.contains("use std::process::exit;"),
"main references exit:\n{}",
root.contents
);
assert!(
!root.contents.contains("HashMap"),
"main never names HashMap (worker does) -> dropped from root:\n{}",
root.contents
);
}
#[test]
fn nested_module_part_imports_gain_one_super_level() {
let src = r#"use super::sibling::Thing;
use super::{alpha, beta::Gamma};
use self::local::X;
use super::*;
use crate::top::Absolute;
use std::fmt::Display;
pub fn one(t: Thing, g: Gamma, x: X, d: &dyn Display) -> i32 {
let _: Option<Absolute> = None;
let _ = (t, g, x, d);
alpha()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn smoke() {
let _ = Thing;
}
}
"#;
let exploded = explode(src).unwrap();
let out = split_mod(&exploded, 10_000, "foo");
let part = out.files.iter().find(|f| f.path == "foo/one.rs").unwrap();
for expected in [
"use super::super::sibling::Thing;",
"use super::super::{alpha, beta::Gamma};",
"use super::local::X;",
"use super::super::*;",
"use crate::top::Absolute;",
"use std::fmt::Display;",
] {
assert!(
part.contents.contains(expected),
"part must carry `{expected}`:\n{}",
part.contents
);
}
assert_eq!(
part.contents
.lines()
.filter(|l| l.trim() == "use super::*;")
.count(),
1,
"exactly one tool-added sibling glob:\n{}",
part.contents
);
let root = out.files.iter().find(|f| f.path == "foo.rs").unwrap();
assert!(
root.contents.contains("use super::sibling::Thing;"),
"root header stays byte-unchanged:\n{}",
root.contents
);
assert!(
!root.contents.contains("super::super"),
"the root did not move — nothing to re-anchor:\n{}",
root.contents
);
}
#[test]
fn crate_root_part_imports_rewrite_self_to_crate() {
let src = "use self::helpers::Helper;\nuse std::fmt::Display;\n\n\
pub fn api(h: Helper, d: &dyn Display) {\n let _ = (h, d);\n}\n";
let exploded = explode(src).unwrap();
let out = split_bin(&exploded, 10_000, "lib");
let part = out.files.iter().find(|f| f.path != "lib.rs").unwrap();
assert!(
part.contents.contains("use crate::helpers::Helper;"),
"`self::` re-anchors to `crate::` below a crate root:\n{}",
part.contents
);
assert!(part.contents.contains("use std::fmt::Display;"));
}
#[test]
fn nested_split_group_headers_gain_one_super_level() {
let mut inner = String::from(
" use super::outer::Thing;\n use self::inner::Y;\n\n\
\x20 fn helper() -> i32 {\n 1\n }\n",
);
for i in 0..30 {
inner.push_str(&format!(
"\n #[test]\n fn t{i}() {{\n \
let _: Option<Thing> = None;\n let _: Option<Y> = None;\n \
assert_eq!(helper(), 1);\n }}\n"
));
}
let src = format!(
"pub fn helper() -> i32 {{\n 1\n}}\n\nfn main() {{}}\n\n\
mod tests {{\n{inner}}}\n"
);
let exploded = explode(&src).unwrap();
let out = split_bin(&exploded, 60, "main");
let group = out.files.iter().find(|f| f.path == "tests/g00.rs").unwrap();
assert!(
group.contents.contains("use super::super::outer::Thing;"),
"group header `super::` gains a level:\n{}",
group.contents
);
assert!(
group.contents.contains("use super::inner::Y;"),
"group header `self::` becomes `super::`:\n{}",
group.contents
);
}
#[test]
fn extracted_inline_mod_keeps_its_own_super_paths() {
let src = "pub fn helper() -> i32 {\n 1\n}\n\n\
mod tests {\n use super::helper;\n\n #[test]\n \
fn smoke() {\n assert_eq!(helper(), 1);\n }\n}\n";
let exploded = explode(src).unwrap();
let out = split_mod(&exploded, 10_000, "foo");
let tests = out.files.iter().find(|f| f.path == "foo/tests.rs").unwrap();
assert!(
tests.contents.contains("use super::helper;"),
"an extracted mod keeps its own module level:\n{}",
tests.contents
);
assert!(
!tests.contents.contains("super::super"),
"the extracted body did not change module level:\n{}",
tests.contents
);
}