From ece3b777bac10ca3e87158e4266bc343c38f7af0 Mon Sep 17 00:00:00 2001 From: Ross Sullivan Date: Sat, 20 Sep 2025 17:03:31 +0900 Subject: [PATCH 1/3] feat: Added assert_file_layout() to CargoPathExt --- Cargo.lock | 2 +- Cargo.toml | 2 +- crates/cargo-test-support/Cargo.toml | 2 +- crates/cargo-test-support/src/compare.rs | 6 ++ crates/cargo-test-support/src/paths.rs | 73 ++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 95fae292ede..8660353e652 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -472,7 +472,7 @@ version = "0.4.7" [[package]] name = "cargo-test-support" -version = "0.8.2" +version = "0.9.0" dependencies = [ "anstream", "anstyle", diff --git a/Cargo.toml b/Cargo.toml index 132360db883..1b5bbb1f93d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ cargo-credential-macos-keychain = { version = "0.4.18", path = "credential/cargo cargo-credential-wincred = { version = "0.4.18", path = "credential/cargo-credential-wincred" } cargo-platform = { path = "crates/cargo-platform", version = "0.3.0" } cargo-test-macro = { version = "0.4.7", path = "crates/cargo-test-macro" } -cargo-test-support = { version = "0.8.2", path = "crates/cargo-test-support" } +cargo-test-support = { version = "0.9.0", path = "crates/cargo-test-support" } cargo-util = { version = "0.2.25", path = "crates/cargo-util" } cargo-util-schemas = { version = "0.10.1", path = "crates/cargo-util-schemas" } cargo_metadata = "0.21.0" diff --git a/crates/cargo-test-support/Cargo.toml b/crates/cargo-test-support/Cargo.toml index 13d086b0809..e9e6406eece 100644 --- a/crates/cargo-test-support/Cargo.toml +++ b/crates/cargo-test-support/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-test-support" -version = "0.8.2" +version = "0.9.0" edition.workspace = true rust-version = "1.90" # MSRV:1 license.workspace = true diff --git a/crates/cargo-test-support/src/compare.rs b/crates/cargo-test-support/src/compare.rs index f1f3e4c2a66..2338d81ce7d 100644 --- a/crates/cargo-test-support/src/compare.rs +++ b/crates/cargo-test-support/src/compare.rs @@ -229,6 +229,12 @@ fn add_regex_redactions(subs: &mut snapbox::Redactions) { .unwrap(); subs.insert("[HASH]", regex!(r"/[a-z0-9\-_]+-(?[0-9a-f]{16})")) .unwrap(); + // Match multi-part hashes like `06/b451d0d6f88b1d` used in directory paths + subs.insert("[HASH]", regex!(r"/(?[a-f0-9]{2}\/[0-9a-f]{14})")) + .unwrap(); + // Match file name hashes like `foo-06b451d0d6f88b1d` + subs.insert("[HASH]", regex!(r"[a-z0-9]+-(?[a-f0-9]{16})")) + .unwrap(); subs.insert( "[AVG_ELAPSED]", regex!(r"(?[0-9]+(\.[0-9]+)?) ns/iter"), diff --git a/crates/cargo-test-support/src/paths.rs b/crates/cargo-test-support/src/paths.rs index cc6119bcc9f..bbff65d856f 100644 --- a/crates/cargo-test-support/src/paths.rs +++ b/crates/cargo-test-support/src/paths.rs @@ -1,6 +1,8 @@ //! Access common paths and manipulate the filesystem use filetime::FileTime; +use itertools::Itertools; +use walkdir::WalkDir; use std::cell::RefCell; use std::env; @@ -12,6 +14,9 @@ use std::sync::Mutex; use std::sync::OnceLock; use std::sync::atomic::{AtomicUsize, Ordering}; +use crate::compare::assert_e2e; +use crate::compare::match_contains; + static CARGO_INTEGRATION_TEST_DIR: &str = "cit"; static GLOBAL_ROOT: OnceLock>> = OnceLock::new(); @@ -152,6 +157,14 @@ pub trait CargoPathExt { fn move_in_time(&self, travel_amount: F) where F: Fn(i64, u32) -> (i64, u32); + + fn assert_file_layout(&self, expected: impl snapbox::IntoData); + + fn assert_file_layout_with_ignored_paths( + &self, + expected: impl snapbox::IntoData, + ignored: &[PathBuf], + ); } impl CargoPathExt for Path { @@ -236,6 +249,37 @@ impl CargoPathExt for Path { }); } } + + #[track_caller] + fn assert_file_layout(&self, expected: impl snapbox::IntoData) { + self.assert_file_layout_with_ignored_paths(expected, &default_ignored_paths()); + } + + #[track_caller] + fn assert_file_layout_with_ignored_paths( + &self, + expected: impl snapbox::IntoData, + ignored_paths: &[PathBuf], + ) { + let assert = assert_e2e(); + let actual = WalkDir::new(self) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| e.file_type().is_file()) + .map(|e| e.path().to_string_lossy().into_owned()) + .filter(|file| { + for ignored in ignored_paths { + let ignored = ignored.to_str().unwrap(); + if match_contains(&ignored, file, &assert.redactions()).is_ok() { + return false; + } + } + return true; + }) + .join("\n"); + + assert.eq(format!("{actual}\n"), expected.unordered()); + } } impl CargoPathExt for PathBuf { @@ -260,6 +304,21 @@ impl CargoPathExt for PathBuf { { self.as_path().move_in_time(travel_amount) } + + #[track_caller] + fn assert_file_layout(&self, expected: impl snapbox::IntoData) { + self.as_path().assert_file_layout(expected); + } + + #[track_caller] + fn assert_file_layout_with_ignored_paths( + &self, + expected: impl snapbox::IntoData, + ignored_paths: &[PathBuf], + ) { + self.as_path() + .assert_file_layout_with_ignored_paths(expected, ignored_paths); + } } fn do_op(path: &Path, desc: &str, mut f: F) @@ -290,6 +349,20 @@ where } } +/// The default paths to ignore when [`CargoPathExt::assert_file_layout`] is called +fn default_ignored_paths() -> Vec { + vec![ + // Ignore MacOS debug symbols as there are many files/directories that would clutter up + // tests few not a lot of benefit. + "[..].dSYM/[..]", + // Ignore Windows debub symbols files (.pdb) + "[..].pdb", + ] + .into_iter() + .map(|s| PathBuf::from(s)) + .collect() +} + /// Get the filename for a library. /// /// `kind` should be one of: From 4dfd32da37d8d94db5acdcf9c006cd3bc826f831 Mon Sep 17 00:00:00 2001 From: Ross Sullivan Date: Fri, 5 Sep 2025 20:51:53 +0900 Subject: [PATCH 2/3] test(build-dir): Migrated build dir tests to snapshot testing --- tests/testsuite/build_dir.rs | 318 +++++++++++++++++++++++++++++++---- 1 file changed, 288 insertions(+), 30 deletions(-) diff --git a/tests/testsuite/build_dir.rs b/tests/testsuite/build_dir.rs index d3b8941a268..2269330f53c 100644 --- a/tests/testsuite/build_dir.rs +++ b/tests/testsuite/build_dir.rs @@ -13,8 +13,7 @@ use std::path::PathBuf; use crate::prelude::*; use cargo_test_support::registry::RegistryBuilder; -use cargo_test_support::{Project, prelude::*}; -use cargo_test_support::{paths, project, str}; +use cargo_test_support::{paths, prelude::*, project, str}; use std::env::consts::{DLL_PREFIX, DLL_SUFFIX, EXE_SUFFIX}; #[cargo_test] @@ -33,20 +32,28 @@ fn binary_with_debug() { p.cargo("build").enable_mac_dsym().run(); - assert_build_dir_layout(p.root().join("build-dir"), "debug"); - assert_artifact_dir_layout(p.root().join("target-dir"), "debug"); - assert_exists_patterns_with_base_dir( - &p.root(), - &[ - // Check the pre-uplifted binary in the build-dir - &format!("build-dir/debug/deps/foo*{EXE_SUFFIX}"), - "build-dir/debug/deps/foo*.d", - // Verify the binary was copied to the target-dir - &format!("target-dir/debug/foo{EXE_SUFFIX}"), - "target-dir/debug/foo.d", - ], - ); assert_not_exists(&p.root().join("target")); + + p.root().join("build-dir").assert_file_layout(str![[r#" +[ROOT]/foo/build-dir/CACHEDIR.TAG +[ROOT]/foo/build-dir/debug/.cargo-lock +[ROOT]/foo/build-dir/debug/deps/foo[..].d +[ROOT]/foo/build-dir/debug/deps/foo[..][EXE] +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/dep-bin-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo.json +[ROOT]/foo/build-dir/.rustc_info.json + +"#]]); + + p.root().join("target-dir").assert_file_layout(str![[r#" +[ROOT]/foo/target-dir/CACHEDIR.TAG +[ROOT]/foo/target-dir/debug/.cargo-lock +[ROOT]/foo/target-dir/debug/foo[EXE] +[ROOT]/foo/target-dir/debug/foo.d + +"#]]); } #[cargo_test] @@ -78,6 +85,26 @@ fn binary_with_release() { "target-dir/release/foo.d", ], ); + p.root().join("build-dir").assert_file_layout(str![[r#" +[ROOT]/foo/build-dir/CACHEDIR.TAG +[ROOT]/foo/build-dir/release/.cargo-lock +[ROOT]/foo/build-dir/release/deps/foo[..].d +[ROOT]/foo/build-dir/release/deps/foo[..][EXE] +[ROOT]/foo/build-dir/release/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/foo/build-dir/release/.fingerprint/foo-[HASH]/dep-bin-foo +[ROOT]/foo/build-dir/release/.fingerprint/foo-[HASH]/bin-foo +[ROOT]/foo/build-dir/release/.fingerprint/foo-[HASH]/bin-foo.json +[ROOT]/foo/build-dir/.rustc_info.json + +"#]]); + + p.root().join("target-dir").assert_file_layout(str![[r#" +[ROOT]/foo/target-dir/CACHEDIR.TAG +[ROOT]/foo/target-dir/release/.cargo-lock +[ROOT]/foo/target-dir/release/foo[EXE] +[ROOT]/foo/target-dir/release/foo.d + +"#]]); } #[cargo_test] @@ -163,6 +190,20 @@ fn should_default_to_target() { assert_build_dir_layout(p.root().join("target"), "debug"); assert_exists(&p.root().join(format!("target/debug/foo{EXE_SUFFIX}"))); + p.root().join("target").assert_file_layout(str![[r#" +[ROOT]/foo/target/CACHEDIR.TAG +[ROOT]/foo/target/debug/.cargo-lock +[ROOT]/foo/target/debug/deps/foo[..].d +[ROOT]/foo/target/debug/deps/foo[..][EXE] +[ROOT]/foo/target/debug/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/foo/target/debug/.fingerprint/foo-[HASH]/dep-bin-foo +[ROOT]/foo/target/debug/.fingerprint/foo-[HASH]/bin-foo +[ROOT]/foo/target/debug/.fingerprint/foo-[HASH]/bin-foo.json +[ROOT]/foo/target/debug/foo[EXE] +[ROOT]/foo/target/debug/foo.d +[ROOT]/foo/target/.rustc_info.json + +"#]]); } #[cargo_test] @@ -178,6 +219,18 @@ fn should_respect_env_var() { assert_build_dir_layout(p.root().join("build-dir"), "debug"); assert_exists(&p.root().join(format!("target/debug/foo{EXE_SUFFIX}"))); + p.root().join("build-dir").assert_file_layout(str![[r#" +[ROOT]/foo/build-dir/CACHEDIR.TAG +[ROOT]/foo/build-dir/debug/.cargo-lock +[ROOT]/foo/build-dir/debug/deps/foo[..].d +[ROOT]/foo/build-dir/debug/deps/foo[..][EXE] +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/dep-bin-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo.json +[ROOT]/foo/build-dir/.rustc_info.json + +"#]]); } #[cargo_test] @@ -249,6 +302,38 @@ fn cargo_tmpdir_should_output_to_build_dir() { assert_build_dir_layout(p.root().join("build-dir"), "debug"); assert_exists(&p.root().join(format!("build-dir/tmp/foo.txt"))); + p.root().join("build-dir").assert_file_layout(str![[r#" +[ROOT]/foo/build-dir/CACHEDIR.TAG +[ROOT]/foo/build-dir/debug/.cargo-lock +[ROOT]/foo/build-dir/debug/deps/foo-[HASH].d +[ROOT]/foo/build-dir/debug/deps/foo-[HASH].d +[ROOT]/foo/build-dir/debug/deps/foo[..].d +[ROOT]/foo/build-dir/debug/deps/foo-[HASH][EXE] +[ROOT]/foo/build-dir/debug/deps/foo-[HASH][EXE] +[ROOT]/foo/build-dir/debug/deps/foo[..][EXE] +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/dep-test-bin-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/test-bin-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/test-bin-foo.json +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/dep-test-integration-test-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/test-integration-test-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/test-integration-test-foo.json +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/dep-bin-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo.json +[ROOT]/foo/build-dir/tmp/foo.txt +[ROOT]/foo/build-dir/.rustc_info.json + +"#]]); + + p.root().join("target-dir").assert_file_layout(str![[r#" +[ROOT]/foo/target-dir/CACHEDIR.TAG +[ROOT]/foo/target-dir/debug/.cargo-lock +[ROOT]/foo/target-dir/debug/foo[EXE] + +"#]]); } #[cargo_test] @@ -280,6 +365,26 @@ fn examples_should_output_to_build_dir_and_uplift_to_target_dir() { "build-dir/debug/examples/foo*.d", ], ); + p.root().join("build-dir").assert_file_layout(str![[r#" +[ROOT]/foo/build-dir/CACHEDIR.TAG +[ROOT]/foo/build-dir/debug/.cargo-lock +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/dep-example-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/example-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/example-foo.json +[ROOT]/foo/build-dir/debug/examples/foo[..].d +[ROOT]/foo/build-dir/debug/examples/foo[..][EXE] +[ROOT]/foo/build-dir/.rustc_info.json + +"#]]); + + p.root().join("target-dir").assert_file_layout(str![[r#" +[ROOT]/foo/target-dir/CACHEDIR.TAG +[ROOT]/foo/target-dir/debug/.cargo-lock +[ROOT]/foo/target-dir/debug/examples/foo[EXE] +[ROOT]/foo/target-dir/debug/examples/foo.d + +"#]]); } #[cargo_test] @@ -307,6 +412,31 @@ fn benches_should_output_to_build_dir() { "build-dir/debug/deps/foo*.d", ], ); + p.root().join("build-dir").assert_file_layout(str![[r#" +[ROOT]/foo/build-dir/CACHEDIR.TAG +[ROOT]/foo/build-dir/debug/.cargo-lock +[ROOT]/foo/build-dir/debug/deps/foo-[HASH].d +[ROOT]/foo/build-dir/debug/deps/foo[..].d +[ROOT]/foo/build-dir/debug/deps/foo-[HASH][EXE] +[ROOT]/foo/build-dir/debug/deps/foo[..][EXE] +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/dep-test-bench-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/test-bench-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/test-bench-foo.json +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/dep-bin-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo.json +[ROOT]/foo/build-dir/.rustc_info.json + +"#]]); + + p.root().join("target-dir").assert_file_layout(str![[r#" +[ROOT]/foo/target-dir/CACHEDIR.TAG +[ROOT]/foo/target-dir/debug/.cargo-lock +[ROOT]/foo/target-dir/debug/foo[EXE] + +"#]]); } #[cargo_test] @@ -353,11 +483,29 @@ fn cargo_package_should_build_in_build_dir_and_output_to_target_dir() { assert_exists(&package_artifact_dir); assert_exists(&package_artifact_dir.join("foo-0.0.1.crate")); assert!(package_artifact_dir.join("foo-0.0.1.crate").is_file()); - - let package_build_dir = p.root().join("build-dir/package"); - assert_exists(&package_build_dir); - assert_exists(&package_build_dir.join("foo-0.0.1")); - assert!(package_build_dir.join("foo-0.0.1").is_dir()); + p.root().join("build-dir").assert_file_layout(str![[r#" +[ROOT]/foo/build-dir/package/foo-0.0.1.crate +[ROOT]/foo/build-dir/package/foo-0.0.1/Cargo.lock +[ROOT]/foo/build-dir/package/foo-0.0.1/Cargo.toml +[ROOT]/foo/build-dir/package/foo-0.0.1/Cargo.toml.orig +[ROOT]/foo/build-dir/package/foo-0.0.1/src/main.rs +[ROOT]/foo/build-dir/.rustc_info.json +[ROOT]/foo/build-dir/debug/.cargo-lock +[ROOT]/foo/build-dir/debug/deps/foo[..].d +[ROOT]/foo/build-dir/debug/deps/foo[..][EXE] +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/dep-bin-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo.json +[ROOT]/foo/build-dir/debug/foo[EXE] +[ROOT]/foo/build-dir/debug/foo.d + +"#]]); + + p.root().join("target-dir").assert_file_layout(str![[r#" +[ROOT]/foo/target-dir/package/foo-0.0.1.crate + +"#]]); } #[cargo_test] @@ -409,6 +557,26 @@ fn cargo_clean_should_clean_the_target_dir_and_build_dir() { p.cargo("build").enable_mac_dsym().run(); assert_build_dir_layout(p.root().join("build-dir"), "debug"); + p.root().join("build-dir").assert_file_layout(str![[r#" +[ROOT]/foo/build-dir/CACHEDIR.TAG +[ROOT]/foo/build-dir/debug/.cargo-lock +[ROOT]/foo/build-dir/debug/deps/foo[..].d +[ROOT]/foo/build-dir/debug/deps/foo[..][EXE] +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/dep-bin-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo.json +[ROOT]/foo/build-dir/.rustc_info.json + +"#]]); + + p.root().join("target-dir").assert_file_layout(str![[r#" +[ROOT]/foo/target-dir/CACHEDIR.TAG +[ROOT]/foo/target-dir/debug/.cargo-lock +[ROOT]/foo/target-dir/debug/foo[EXE] +[ROOT]/foo/target-dir/debug/foo.d + +"#]]); p.cargo("clean").enable_mac_dsym().run(); @@ -531,6 +699,26 @@ fn template_workspace_root() { // Verify the binary was uplifted to the target-dir assert_exists(&p.root().join(&format!("target-dir/debug/foo{EXE_SUFFIX}"))); + p.root().join("build-dir").assert_file_layout(str![[r#" +[ROOT]/foo/build-dir/CACHEDIR.TAG +[ROOT]/foo/build-dir/debug/.cargo-lock +[ROOT]/foo/build-dir/debug/deps/foo[..].d +[ROOT]/foo/build-dir/debug/deps/foo[..][EXE] +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/dep-bin-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo +[ROOT]/foo/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo.json +[ROOT]/foo/build-dir/.rustc_info.json + +"#]]); + + p.root().join("target-dir").assert_file_layout(str![[r#" +[ROOT]/foo/target-dir/CACHEDIR.TAG +[ROOT]/foo/target-dir/debug/.cargo-lock +[ROOT]/foo/target-dir/debug/foo[EXE] +[ROOT]/foo/target-dir/debug/foo.d + +"#]]); } #[cargo_test] @@ -554,6 +742,28 @@ fn template_cargo_cache_home() { // Verify the binary was uplifted to the target-dir assert_exists(&p.root().join(&format!("target-dir/debug/foo{EXE_SUFFIX}"))); + paths::cargo_home() + .join("build-dir") + .assert_file_layout(str![[r#" +[ROOT]/home/.cargo/build-dir/CACHEDIR.TAG +[ROOT]/home/.cargo/build-dir/debug/.cargo-lock +[ROOT]/home/.cargo/build-dir/debug/deps/foo[..].d +[ROOT]/home/.cargo/build-dir/debug/deps/foo[..][EXE] +[ROOT]/home/.cargo/build-dir/debug/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/home/.cargo/build-dir/debug/.fingerprint/foo-[HASH]/dep-bin-foo +[ROOT]/home/.cargo/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo +[ROOT]/home/.cargo/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo.json +[ROOT]/home/.cargo/build-dir/.rustc_info.json + +"#]]); + + p.root().join("target-dir").assert_file_layout(str![[r#" +[ROOT]/foo/target-dir/CACHEDIR.TAG +[ROOT]/foo/target-dir/debug/.cargo-lock +[ROOT]/foo/target-dir/debug/foo[EXE] +[ROOT]/foo/target-dir/debug/foo.d + +"#]]); } #[cargo_test] @@ -587,11 +797,29 @@ fn template_workspace_path_hash() { let hash_dir = parse_workspace_manifest_path_hash(&foo_dir); let build_dir = hash_dir.as_path().join("build-dir"); - assert_build_dir_layout(build_dir, "debug"); - assert_artifact_dir_layout(p.root().join("target-dir"), "debug"); // Verify the binary was uplifted to the target-dir assert_exists(&p.root().join(&format!("target-dir/debug/foo{EXE_SUFFIX}"))); + build_dir.assert_file_layout(str![[r#" +[ROOT]/foo/foo/[HASH]/build-dir/CACHEDIR.TAG +[ROOT]/foo/foo/[HASH]/build-dir/debug/.cargo-lock +[ROOT]/foo/foo/[HASH]/build-dir/debug/deps/foo[..].d +[ROOT]/foo/foo/[HASH]/build-dir/debug/deps/foo[..][EXE] +[ROOT]/foo/foo/[HASH]/build-dir/debug/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/foo/foo/[HASH]/build-dir/debug/.fingerprint/foo-[HASH]/dep-bin-foo +[ROOT]/foo/foo/[HASH]/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo +[ROOT]/foo/foo/[HASH]/build-dir/debug/.fingerprint/foo-[HASH]/bin-foo.json +[ROOT]/foo/foo/[HASH]/build-dir/.rustc_info.json + +"#]]); + + p.root().join("target-dir").assert_file_layout(str![[r#" +[ROOT]/foo/target-dir/CACHEDIR.TAG +[ROOT]/foo/target-dir/debug/.cargo-lock +[ROOT]/foo/target-dir/debug/foo[EXE] +[ROOT]/foo/target-dir/debug/foo.d + +"#]]); } /// Verify that the {workspace-path-hash} does not changes if cargo is run from inside of @@ -633,7 +861,25 @@ fn template_workspace_path_hash_should_handle_symlink() { let foo_dir = p.root().join("foo"); assert_exists(&foo_dir); let original_hash_dir = parse_workspace_manifest_path_hash(&foo_dir); - verify_layouts(&p, &original_hash_dir); + + original_hash_dir.assert_file_layout(str![[r#" +[ROOT]/foo/foo/[HASH]/build-dir/CACHEDIR.TAG +[ROOT]/foo/foo/[HASH]/build-dir/debug/.cargo-lock +[ROOT]/foo/foo/[HASH]/build-dir/debug/deps/foo-[HASH].d +[ROOT]/foo/foo/[HASH]/build-dir/debug/deps/libfoo-[HASH].rmeta +[ROOT]/foo/foo/[HASH]/build-dir/debug/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/foo/foo/[HASH]/build-dir/debug/.fingerprint/foo-[HASH]/dep-lib-foo +[ROOT]/foo/foo/[HASH]/build-dir/debug/.fingerprint/foo-[HASH]/lib-foo +[ROOT]/foo/foo/[HASH]/build-dir/debug/.fingerprint/foo-[HASH]/lib-foo.json +[ROOT]/foo/foo/[HASH]/build-dir/.rustc_info.json + +"#]]); + + p.root().join("target").assert_file_layout(str![[r#" +[ROOT]/foo/target/CACHEDIR.TAG +[ROOT]/foo/target/debug/.cargo-lock + +"#]]); // Create a symlink of the project root. let mut symlinked_dir = p.root().clone(); @@ -650,16 +896,28 @@ fn template_workspace_path_hash_should_handle_symlink() { // Parse and verify the hash created from the symlinked dir assert_exists(&foo_dir); let symlink_hash_dir = parse_workspace_manifest_path_hash(&foo_dir); - verify_layouts(&p, &symlink_hash_dir); + + symlink_hash_dir.assert_file_layout(str![[r#" +[ROOT]/foo/foo/[HASH]/build-dir/CACHEDIR.TAG +[ROOT]/foo/foo/[HASH]/build-dir/debug/.cargo-lock +[ROOT]/foo/foo/[HASH]/build-dir/debug/deps/foo-[HASH].d +[ROOT]/foo/foo/[HASH]/build-dir/debug/deps/libfoo-[HASH].rmeta +[ROOT]/foo/foo/[HASH]/build-dir/debug/.fingerprint/foo-[HASH]/invoked.timestamp +[ROOT]/foo/foo/[HASH]/build-dir/debug/.fingerprint/foo-[HASH]/dep-lib-foo +[ROOT]/foo/foo/[HASH]/build-dir/debug/.fingerprint/foo-[HASH]/lib-foo +[ROOT]/foo/foo/[HASH]/build-dir/debug/.fingerprint/foo-[HASH]/lib-foo.json +[ROOT]/foo/foo/[HASH]/build-dir/.rustc_info.json + +"#]]); + + p.root().join("target").assert_file_layout(str![[r#" +[ROOT]/foo/target/CACHEDIR.TAG +[ROOT]/foo/target/debug/.cargo-lock + +"#]]); // Verify the hash dir created from the symlinked and non-symlinked dirs are the same. assert_eq!(original_hash_dir, symlink_hash_dir); - - fn verify_layouts(p: &Project, build_dir_parent: &PathBuf) { - let build_dir = build_dir_parent.as_path().join("build-dir"); - assert_build_dir_layout(build_dir, "debug"); - assert_artifact_dir_layout(p.root().join("target"), "debug"); - } } #[cargo_test] From df753db3105ea7c20285a5846df09a797baf6419 Mon Sep 17 00:00:00 2001 From: Ross Sullivan Date: Fri, 29 Aug 2025 19:02:42 +1200 Subject: [PATCH 3/3] docs: Added assert_file_layout() to testing docs --- src/doc/contrib/src/tests/writing.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/doc/contrib/src/tests/writing.md b/src/doc/contrib/src/tests/writing.md index 34058506700..a680f892578 100644 --- a/src/doc/contrib/src/tests/writing.md +++ b/src/doc/contrib/src/tests/writing.md @@ -64,6 +64,16 @@ test. - See [`support::compare`] for an explanation of the string pattern matching. Patterns are used to make it easier to match against the expected output. +#### Filesystem layout testing + +Tests often to need to verify Cargo created/removed files. +The `CargoPathExt` trait (implemented by `Path` and `PathBuf`) provides a `assert_file_layout()` to verify the files in a directory (including nested directories). +This takes a snapshot of (unordered) file paths for the given directory and asserts that all files are present and no new files have been created. + +Files vary across operating systems, for example `.pdb` files on Windows. +Some of these platform specific files are ignored from snapshotting by default. +You can use `assert_file_layout_with_ignored_paths()` to change the file patterns that are ignored. + #### Testing Nightly Features If you are testing a Cargo feature that only works on "nightly" Cargo, then