use std::collections::BTreeSet;
use super::*;
#[test]
fn include_paths_in_moved_bodies_are_rebased_one_directory_deeper() {
let src = r#"pub fn doc() -> &'static str {
include_str!("../docs/a.md")
}
pub fn blob() -> &'static [u8] {
include_bytes!("fixtures/b.bin")
}
pub fn built() -> &'static str {
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/x"))
}
pub fn rooted() -> &'static str {
include_str!("/etc/x")
}
"#;
let exploded = explode(src).unwrap();
let out = split_mod(&exploded, 10_000, "foo");
let moved: String = out
.files
.iter()
.filter(|f| f.path != "foo.rs")
.map(|f| f.contents.as_str())
.collect();
assert!(
moved.contains(r#"include_str!("../../docs/a.md")"#),
"a relative include! path gains one `../`:\n{moved}"
);
assert!(
moved.contains(r#"include_bytes!("../fixtures/b.bin")"#),
"a bare relative path is prefixed too:\n{moved}"
);
assert!(
moved.contains(r#"include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/x"))"#),
"a non-literal argument is byte-unchanged:\n{moved}"
);
assert!(
moved.contains(r#"include_str!("/etc/x")"#),
"an absolute path is byte-unchanged:\n{moved}"
);
}
#[test]
fn include_paths_are_untouched_when_the_file_stays_in_its_directory() {
let src = "fn worker() -> &'static str {\n include_str!(\"../docs/w.md\")\n}\n\n\
fn main() {\n let _ = include_str!(\"../docs/m.md\");\n \
let _ = worker();\n}\n";
let exploded = explode(src).unwrap();
let out = split_bin(&exploded, 10_000, "main");
for f in &out.files {
assert!(
!f.contents.contains("../../docs/"),
"{} stayed in the source directory:\n{}",
f.path,
f.contents
);
}
let root = out.files.iter().find(|f| f.path == "main.rs").unwrap();
assert!(root.contents.contains(r#"include_str!("../docs/m.md")"#));
}
#[test]
fn review_body_paths_preserve_opaque_macro_and_attribute_tokens() {
let src = r#"#[metadata(super::Thing)]
pub fn probe() -> super::Thing {
macro_rules! data { () => { self::Thing }; }
let _ = stringify!(super::Thing);
let _ = stringify! { self::Thing };
let _ = custom!(super::Thing, (self::Thing), stringify!(super::Thing));
let _ = super::factory!(self::Thing);
<super::Thing as self::Trait>::make()
}
"#;
let expected = src
.replace("-> super::Thing", "-> super::super::Thing")
.replace("super::factory!", "super::super::factory!")
.replace(
"<super::Thing as self::Trait>",
"<super::super::Thing as super::Trait>",
);
assert_eq!(rebase_body_paths(src, Rebase::Nested), expected);
assert_eq!(rebase_body_paths(src, Rebase::Keep), src);
}
#[test]
fn review_standard_expression_macro_arguments_rebase_paths() {
let src = r#"fn probe() {
assert_eq!(super::Thing::value(), self::value(), "{}", super::message());
let _ = format!("{}", super::Thing::value());
let _ = custom!(super::Thing, self::Thing);
let _ = custom::assert_eq!(super::Thing, self::Thing);
let _ = stringify!(super::Thing);
}
"#;
let expected = src
.replace("super::Thing::value()", "super::super::Thing::value()")
.replace("self::value()", "super::value()")
.replace("super::message()", "super::super::message()");
assert_eq!(rebase_body_paths(src, Rebase::Nested), expected);
}
#[test]
fn review_renamed_use_anchors_rebase_in_headers_and_bodies() {
for (source, nested, root) in [
(
"super as parent",
"super::super as parent",
"super as parent",
),
("self as current", "super as current", "crate as current"),
(
"{super as parent, self as current, super::{self as p, Thing}}",
"{super::super as parent, super as current, super::super::{self as p, Thing}}",
"{super as parent, crate as current, super::{self as p, Thing}}",
),
("crate as root", "crate as root", "crate as root"),
] {
for (rebase, expected) in [
(Rebase::Nested, nested),
(Rebase::CrateRoot, root),
(Rebase::Keep, source),
] {
let import = format!("// keep this comment\n#[allow(unused_imports)]\nuse {source};\n");
let expected = import.replace(source, expected);
assert_eq!(rebase_import(&import, rebase, &BTreeSet::new()), expected);
assert_eq!(
rebase_body_paths(&format!("fn probe() {{\n{import}}}"), rebase),
format!("fn probe() {{\n{expected}}}"),
);
}
}
}
#[test]
fn review_include_literals_accept_one_optional_trailing_comma() {
for name in PATH_MACROS {
for literal in [r#""docs/file""#, r##"r#"docs/file"#"##] {
let src = format!("fn probe() {{ {name}!({literal} /* keep */,); }}");
assert_eq!(
rebase_includes(&src, 2, "foo/part.rs"),
src.replace("docs/file", "../../docs/file"),
);
for suffix in [",,", ", other", " + other"] {
let src = format!("fn probe() {{ {name}!({literal}{suffix}); }}");
assert_eq!(rebase_includes(&src, 1, "foo/part.rs"), src);
}
}
}
}
#[test]
fn review_include_absolute_windows_paths_are_host_independent() {
for name in PATH_MACROS {
for literal in [
r#""C:\\docs\\file""#,
r#""z:/docs/file""#,
r#"r"C:\docs\file""#,
r#""\\\\server\\share\\file""#,
r#"r"\\server\share\file""#,
r#"r"\\?\C:\docs\file""#,
r#""//server/share/file""#,
r#""/docs/file""#,
] {
let src = format!("fn probe() {{ {name}!({literal}); }}");
assert_eq!(rebase_includes(&src, 1, "foo/part.rs"), src);
}
let src = format!("fn probe() {{ {name}!(\"C:relative\"); }}");
assert_eq!(
rebase_includes(&src, 1, "foo/part.rs"),
src.replace("C:relative", "../C:relative"),
);
}
}
#[test]
fn moved_body_paths_gain_one_super_level() {
let src = r#"pub fn probe() -> i32 {
use super::alpha::Beta;
let _ = Beta;
let _ = super::filter::parse("x");
let _ = super::super::grandparent::thing();
let _ = self::local::helper();
let _: super::sibling::Kind = super::sibling::make();
let _ = super::sibling::Record {
meta: super::meta::build("req"),
};
crate::top::value()
}
"#;
let exploded = explode(src).unwrap();
let out = split_mod(&exploded, 10_000, "foo");
let part = out.files.iter().find(|f| f.path == "foo/probe.rs").unwrap();
for expected in [
"use super::super::alpha::Beta;",
r#"super::super::filter::parse("x")"#,
"super::super::super::grandparent::thing()",
"super::local::helper()",
"let _: super::super::sibling::Kind = super::super::sibling::make();",
"meta: super::super::meta::build(\"req\"),",
"crate::top::value()",
] {
assert!(
part.contents.contains(expected),
"moved body must carry `{expected}`:\n{}",
part.contents
);
}
}
#[test]
fn moved_body_leaves_self_receivers_and_use_groups_alone() {
let src = r#"pub struct Widget {
pub n: i32,
}
impl Widget {
pub fn get(&self) -> i32 {
use std::collections::{self, HashMap};
let _: Option<HashMap<u8, u8>> = None;
let _ = collections::BTreeMap::<u8, u8>::new();
let _ = Self::make();
self.n
}
pub fn make() -> Self {
Self { n: 1 }
}
}
pub(super) fn gated() -> i32 {
1
}
"#;
let exploded = explode(src).unwrap();
let out = split_mod(&exploded, 10_000, "foo");
let moved: String = out
.files
.iter()
.filter(|f| f.path != "foo.rs")
.map(|f| f.contents.as_str())
.collect();
for expected in [
"use std::collections::{self, HashMap};",
"Self::make()",
"self.n",
"pub(super) fn gated",
] {
assert!(
moved.contains(expected),
"`{expected}` is not a module anchor and must not move:\n{moved}"
);
}
assert!(
!moved.contains("pub(super::super)"),
"a `pub(super)` restriction must never be mangled:\n{moved}"
);
}
#[test]
fn moved_body_paths_below_a_crate_root_rewrite_self_to_crate() {
let src = "pub fn entry() -> i32 {\n let _ = self::helpers::setup();\n \
crate::other::value()\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("crate::helpers::setup()"),
"`self::` below a crate root is `crate::`:\n{}",
part.contents
);
assert!(part.contents.contains("crate::other::value()"));
}
#[test]
fn explode_never_rewrites_include_paths() {
let src = "pub fn doc() -> &'static str {\n include_str!(\"../docs/a.md\")\n}\n";
let exploded = explode(src).unwrap();
let joined: String = exploded.chunks.iter().map(|c| c.text.as_str()).collect();
assert_eq!(joined, src, "explode must stay lossless");
}