diff options
Diffstat (limited to 'tests/testsuite/clean.rs')
-rw-r--r-- | tests/testsuite/clean.rs | 675 |
1 files changed, 675 insertions, 0 deletions
diff --git a/tests/testsuite/clean.rs b/tests/testsuite/clean.rs new file mode 100644 index 0000000..e0885fd --- /dev/null +++ b/tests/testsuite/clean.rs @@ -0,0 +1,675 @@ +//! Tests for the `cargo clean` command. + +use cargo_test_support::registry::Package; +use cargo_test_support::{ + basic_bin_manifest, basic_manifest, git, main_file, project, project_in, rustc_host, +}; +use glob::GlobError; +use std::env; +use std::path::{Path, PathBuf}; + +#[cargo_test] +fn cargo_clean_simple() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + p.cargo("build").run(); + assert!(p.build_dir().is_dir()); + + p.cargo("clean").run(); + assert!(!p.build_dir().is_dir()); +} + +#[cargo_test] +fn different_dir() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .file("src/bar/a.rs", "") + .build(); + + p.cargo("build").run(); + assert!(p.build_dir().is_dir()); + + p.cargo("clean").cwd("src").with_stdout("").run(); + assert!(!p.build_dir().is_dir()); +} + +#[cargo_test] +fn clean_multiple_packages() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.d1] + path = "d1" + [dependencies.d2] + path = "d2" + + [[bin]] + name = "foo" + "#, + ) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .file("d1/Cargo.toml", &basic_bin_manifest("d1")) + .file("d1/src/main.rs", "fn main() { println!(\"d1\"); }") + .file("d2/Cargo.toml", &basic_bin_manifest("d2")) + .file("d2/src/main.rs", "fn main() { println!(\"d2\"); }") + .build(); + + p.cargo("build -p d1 -p d2 -p foo").run(); + + let d1_path = &p + .build_dir() + .join("debug") + .join(format!("d1{}", env::consts::EXE_SUFFIX)); + let d2_path = &p + .build_dir() + .join("debug") + .join(format!("d2{}", env::consts::EXE_SUFFIX)); + + assert!(p.bin("foo").is_file()); + assert!(d1_path.is_file()); + assert!(d2_path.is_file()); + + p.cargo("clean -p d1 -p d2") + .cwd("src") + .with_stdout("") + .run(); + assert!(p.bin("foo").is_file()); + assert!(!d1_path.is_file()); + assert!(!d2_path.is_file()); +} + +#[cargo_test] +fn clean_multiple_packages_in_glob_char_path() { + let p = project_in("[d1]") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + let foo_path = &p.build_dir().join("debug").join("deps"); + + #[cfg(not(target_env = "msvc"))] + let file_glob = "foo-*"; + + #[cfg(target_env = "msvc")] + let file_glob = "foo.pdb"; + + // Assert that build artifacts are produced + p.cargo("build").run(); + assert_ne!(get_build_artifacts(foo_path, file_glob).len(), 0); + + // Assert that build artifacts are destroyed + p.cargo("clean -p foo").run(); + assert_eq!(get_build_artifacts(foo_path, file_glob).len(), 0); +} + +fn get_build_artifacts(path: &PathBuf, file_glob: &str) -> Vec<Result<PathBuf, GlobError>> { + let pattern = path.to_str().expect("expected utf-8 path"); + let pattern = glob::Pattern::escape(pattern); + + let path = PathBuf::from(pattern).join(file_glob); + let path = path.to_str().expect("expected utf-8 path"); + glob::glob(path) + .expect("expected glob to run") + .into_iter() + .collect::<Vec<Result<PathBuf, GlobError>>>() +} + +#[cargo_test] +fn clean_p_only_cleans_specified_package() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = [ + "foo", + "foo_core", + "foo-base", + ] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "//! foo") + .file("foo_core/Cargo.toml", &basic_manifest("foo_core", "0.1.0")) + .file("foo_core/src/lib.rs", "//! foo_core") + .file("foo-base/Cargo.toml", &basic_manifest("foo-base", "0.1.0")) + .file("foo-base/src/lib.rs", "//! foo-base") + .build(); + + let fingerprint_path = &p.build_dir().join("debug").join(".fingerprint"); + + p.cargo("build -p foo -p foo_core -p foo-base").run(); + + let mut fingerprint_names = get_fingerprints_without_hashes(fingerprint_path); + + // Artifacts present for all after building + assert!(fingerprint_names.iter().any(|e| e == "foo")); + let num_foo_core_artifacts = fingerprint_names + .iter() + .filter(|&e| e == "foo_core") + .count(); + assert_ne!(num_foo_core_artifacts, 0); + let num_foo_base_artifacts = fingerprint_names + .iter() + .filter(|&e| e == "foo-base") + .count(); + assert_ne!(num_foo_base_artifacts, 0); + + p.cargo("clean -p foo").run(); + + fingerprint_names = get_fingerprints_without_hashes(fingerprint_path); + + // Cleaning `foo` leaves artifacts for the others + assert!(!fingerprint_names.iter().any(|e| e == "foo")); + assert_eq!( + fingerprint_names + .iter() + .filter(|&e| e == "foo_core") + .count(), + num_foo_core_artifacts, + ); + assert_eq!( + fingerprint_names + .iter() + .filter(|&e| e == "foo-base") + .count(), + num_foo_core_artifacts, + ); +} + +fn get_fingerprints_without_hashes(fingerprint_path: &Path) -> Vec<String> { + std::fs::read_dir(fingerprint_path) + .expect("Build dir should be readable") + .filter_map(|entry| entry.ok()) + .map(|entry| { + let name = entry.file_name(); + let name = name + .into_string() + .expect("fingerprint name should be UTF-8"); + name.rsplit_once('-') + .expect("Name should contain at least one hyphen") + .0 + .to_owned() + }) + .collect() +} + +#[cargo_test] +fn clean_release() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = "a" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("build --release").run(); + + p.cargo("clean -p foo").run(); + p.cargo("build --release").with_stdout("").run(); + + p.cargo("clean -p foo --release").run(); + p.cargo("build --release") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[FINISHED] release [optimized] target(s) in [..] +", + ) + .run(); + + p.cargo("build").run(); + + p.cargo("clean").arg("--release").run(); + assert!(p.build_dir().is_dir()); + assert!(p.build_dir().join("debug").is_dir()); + assert!(!p.build_dir().join("release").is_dir()); +} + +#[cargo_test] +fn clean_doc() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + a = { path = "a" } + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("a/Cargo.toml", &basic_manifest("a", "0.0.1")) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("doc").run(); + + let doc_path = &p.build_dir().join("doc"); + + assert!(doc_path.is_dir()); + + p.cargo("clean --doc").run(); + + assert!(!doc_path.is_dir()); + assert!(p.build_dir().is_dir()); +} + +#[cargo_test] +fn build_script() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file( + "build.rs", + r#" + use std::path::PathBuf; + use std::env; + + fn main() { + let out = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + if env::var("FIRST").is_ok() { + std::fs::File::create(out.join("out")).unwrap(); + } else { + assert!(!out.join("out").exists()); + } + } + "#, + ) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("build").env("FIRST", "1").run(); + p.cargo("clean -p foo").run(); + p.cargo("build -v") + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([..]) +[RUNNING] `rustc [..] build.rs [..]` +[RUNNING] `[..]build-script-build` +[RUNNING] `rustc [..] src/main.rs [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn clean_git() { + let git = git::new("dep", |project| { + project + .file("Cargo.toml", &basic_manifest("dep", "0.5.0")) + .file("src/lib.rs", "") + }); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + dep = {{ git = '{}' }} + "#, + git.url() + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build").run(); + p.cargo("clean -p dep").with_stdout("").run(); + p.cargo("build").run(); +} + +#[cargo_test] +fn registry() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = "0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.1.0").publish(); + + p.cargo("build").run(); + p.cargo("clean -p bar").with_stdout("").run(); + p.cargo("build").run(); +} + +#[cargo_test] +fn clean_verbose() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + + [dependencies] + bar = "0.1" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + Package::new("bar", "0.1.0").publish(); + + p.cargo("build").run(); + let mut expected = String::from( + "\ +[REMOVING] [..]target/debug/.fingerprint/bar[..] +[REMOVING] [..]target/debug/deps/libbar[..].rlib +[REMOVING] [..]target/debug/deps/bar-[..].d +[REMOVING] [..]target/debug/deps/libbar[..].rmeta +", + ); + if cfg!(target_os = "macos") { + // Rust 1.69 has changed so that split-debuginfo=unpacked includes unpacked for rlibs. + for obj in p.glob("target/debug/deps/bar-*.o") { + expected.push_str(&format!("[REMOVING] [..]{}", obj.unwrap().display())); + } + } + p.cargo("clean -p bar --verbose") + .with_stderr_unordered(&expected) + .run(); + p.cargo("build").run(); +} + +#[cargo_test] +fn clean_remove_rlib_rmeta() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build").run(); + assert!(p.target_debug_dir().join("libfoo.rlib").exists()); + let rmeta = p.glob("target/debug/deps/*.rmeta").next().unwrap().unwrap(); + assert!(rmeta.exists()); + p.cargo("clean -p foo").run(); + assert!(!p.target_debug_dir().join("libfoo.rlib").exists()); + assert!(!rmeta.exists()); +} + +#[cargo_test] +fn package_cleans_all_the_things() { + // -p cleans everything + // Use dashes everywhere to make sure dash/underscore stuff is handled. + for crate_type in &["rlib", "dylib", "cdylib", "staticlib", "proc-macro"] { + // Try each crate type individually since the behavior changes when + // they are combined. + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo-bar" + version = "0.1.0" + + [lib] + crate-type = ["{}"] + "#, + crate_type + ), + ) + .file("src/lib.rs", "") + .build(); + p.cargo("build").run(); + p.cargo("clean -p foo-bar").run(); + assert_all_clean(&p.build_dir()); + } + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo-bar" + version = "0.1.0" + edition = "2018" + + [lib] + crate-type = ["rlib", "dylib", "staticlib"] + + [[example]] + name = "foo-ex-rlib" + crate-type = ["rlib"] + test = true + + [[example]] + name = "foo-ex-cdylib" + crate-type = ["cdylib"] + test = true + + [[example]] + name = "foo-ex-bin" + test = true + "#, + ) + .file("src/lib.rs", "") + .file("src/lib/some-main.rs", "fn main() {}") + .file("src/bin/other-main.rs", "fn main() {}") + .file("examples/foo-ex-rlib.rs", "") + .file("examples/foo-ex-cdylib.rs", "") + .file("examples/foo-ex-bin.rs", "fn main() {}") + .file("tests/foo-test.rs", "") + .file("benches/foo-bench.rs", "") + .file("build.rs", "fn main() {}") + .build(); + + p.cargo("build --all-targets") + .env("CARGO_INCREMENTAL", "1") + .run(); + p.cargo("test --all-targets") + .env("CARGO_INCREMENTAL", "1") + .run(); + p.cargo("check --all-targets") + .env("CARGO_INCREMENTAL", "1") + .run(); + p.cargo("clean -p foo-bar").run(); + assert_all_clean(&p.build_dir()); + + // Try some targets. + p.cargo("build --all-targets --target") + .arg(rustc_host()) + .run(); + p.cargo("clean -p foo-bar --target").arg(rustc_host()).run(); + assert_all_clean(&p.build_dir()); +} + +// Ensures that all files for the package have been deleted. +#[track_caller] +fn assert_all_clean(build_dir: &Path) { + let walker = walkdir::WalkDir::new(build_dir).into_iter(); + for entry in walker.filter_entry(|e| { + let path = e.path(); + // This is a known limitation, clean can't differentiate between + // the different build scripts from different packages. + !(path + .file_name() + .unwrap() + .to_str() + .unwrap() + .starts_with("build_script_build") + && path + .parent() + .unwrap() + .file_name() + .unwrap() + .to_str() + .unwrap() + == "incremental") + }) { + let entry = entry.unwrap(); + let path = entry.path(); + if let ".rustc_info.json" | ".cargo-lock" | "CACHEDIR.TAG" = + path.file_name().unwrap().to_str().unwrap() + { + continue; + } + if path.is_symlink() || path.is_file() { + panic!("{:?} was not cleaned", path); + } + } +} + +#[cargo_test] +fn clean_spec_multiple() { + // clean -p foo where foo matches multiple versions + Package::new("bar", "1.0.0").publish(); + Package::new("bar", "2.0.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar1 = {version="1.0", package="bar"} + bar2 = {version="2.0", package="bar"} + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build").run(); + + // Check suggestion for bad pkgid. + p.cargo("clean -p baz") + .with_status(101) + .with_stderr( + "\ +error: package ID specification `baz` did not match any packages + +<tab>Did you mean `bar`? +", + ) + .run(); + + p.cargo("clean -p bar:1.0.0") + .with_stderr( + "warning: version qualifier in `-p bar:1.0.0` is ignored, \ + cleaning all versions of `bar` found", + ) + .run(); + let mut walker = walkdir::WalkDir::new(p.build_dir()) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| { + let n = e.file_name().to_str().unwrap(); + n.starts_with("bar") || n.starts_with("libbar") + }); + if let Some(e) = walker.next() { + panic!("{:?} was not cleaned", e.path()); + } +} + +#[cargo_test] +fn clean_spec_reserved() { + // Clean when a target (like a test) has a reserved name. In this case, + // make sure `clean -p` doesn't delete the reserved directory `build` when + // there is a test named `build`. + Package::new("bar", "1.0.0") + .file("src/lib.rs", "") + .file("build.rs", "fn main() {}") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .file("tests/build.rs", "") + .build(); + + p.cargo("build --all-targets").run(); + assert!(p.target_debug_dir().join("build").is_dir()); + let build_test = p.glob("target/debug/deps/build-*").next().unwrap().unwrap(); + assert!(build_test.exists()); + // Tests are never "uplifted". + assert!(p.glob("target/debug/build-*").next().is_none()); + + p.cargo("clean -p foo").run(); + // Should not delete this. + assert!(p.target_debug_dir().join("build").is_dir()); + + // This should not rebuild bar. + p.cargo("build -v --all-targets") + .with_stderr( + "\ +[FRESH] bar v1.0.0 +[COMPILING] foo v0.1.0 [..] +[RUNNING] `rustc [..] +[RUNNING] `rustc [..] +[RUNNING] `rustc [..] +[FINISHED] [..] +", + ) + .run(); +} |