-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Added better filesystem layout testing harness #15874
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Haven't look at the change, but thanks a lot for improving the test infrastructure! |
4f31868
to
c939a79
Compare
9a42834
to
edef1aa
Compare
r? @weihanglo rustbot has assigned @weihanglo. Use |
I updated the implementation to fix the flaws of the initial design. The build-dir tests now pass on all of the platforms tested in CI. |
let actual_layout = LayoutTree::from_path(&self).unwrap(); | ||
|
||
let expected_layout = LayoutTree::parse(expected.as_ref()); | ||
|
||
if !actual_layout.matches_snapshot(&expected_layout) { | ||
let actual_snapshot = actual_layout.to_string(); | ||
let expected_snapshot = expected_layout.to_string(); | ||
|
||
compare::assert_e2e().eq(actual_snapshot, expected_snapshot); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
LayoutTree
struct handles comparison logic (as opposed to using snapbox directly)
This avoids the issues initial implementation (ordering and platform specific files, see full details below)
If the comparison fails, 2 human readable tree's are generated and snapbox is used to display the diffs.
Note: These diffs are in-memory thus, do not support
SNAPSHOTS=overwrite
. This is arguably good as often the overwrite would be incorrect as the snapshots are platform specific.Snapshots would support conditional files based on the target os / platform env
- Files/directories may be suffixed with
[target_platform=<target-platform>]
- Multiple platforms may be specified separated by comma
Our general aim should be to support snapshotting wherever possible.
Also, tracking of platform-specific details is rough for contributors
- Likely won't discover until the PR is posted
- Likely don't have access to a machine, so must do a slow iteration by pushing and seeing CI's result
Unsure f there is something better but felt this was at least worth specifically discussing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I totally agree with you.
One idea I have to only check for files in the snapshot and ignore other files. Meaning if a new file was is created a test would not fail. So tests would be able to ignore many of the platform specific files that are not relevant to the test.
But this comes with the trade off of potentially not including a file that is important.
I think I lean toward the current implementation that explicitly matches the file tree structure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two alternative ideas:
- Have a function that enumerates files in a directory as relative-path-per-line
- a snapshot assert can then be run against that with the expected results having a
...
inside it and.unordered()
called on it - a parameter could take a glob to ignore
- a snapshot assert can then be run against that with the expected results having a
- Have a low level function that accepts a glob to ignore and enumerates files as a tree. A higher level assert function could assume a specific policy for globs and compare against a snapshot
The problem comes in with
[target_platform=windows-msvc]
│ ├── foo[EXE]
[target_platform=macos,linux,windows-gnu]
│ ├── foo-[HASH][EXE]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current tests handle this by using globs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with the existing tests doing format!("build-dir/debug/deps/foo*{EXE_SUFFIX}")
and if we did foo[..][EXE]
is that it over matches. On Unix-like systems, this could match any file and not just the binary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we limp along for now with foo[..][EXE]
as #15947 would unblock removing -Cextra-filename
from more places, see #15947 (comment)
As for why msrv doesn't include the hash, see
cargo/src/cargo/core/compiler/build_runner/compilation_files.rs
Lines 835 to 865 in e0e1a6e
// No metadata in these cases: | |
// | |
// - dylibs: | |
// - if any dylib names are encoded in executables, so they can't be renamed. | |
// - TODO: Maybe use `-install-name` on macOS or `-soname` on other UNIX systems | |
// to specify the dylib name to be used by the linker instead of the filename. | |
// - Windows MSVC executables: The path to the PDB is embedded in the | |
// executable, and we don't want the PDB path to include the hash in it. | |
// - wasm32-unknown-emscripten executables: When using emscripten, the path to the | |
// .wasm file is embedded in the .js file, so we don't want the hash in there. | |
// | |
// This is only done for local packages, as we don't expect to export | |
// dependencies. | |
// | |
// The __CARGO_DEFAULT_LIB_METADATA env var is used to override this to | |
// force metadata in the hash. This is only used for building libstd. For | |
// example, if libstd is placed in a common location, we don't want a file | |
// named /usr/lib/libstd.so which could conflict with other rustc | |
// installs. In addition it prevents accidentally loading a libstd of a | |
// different compiler at runtime. | |
// See https://github.com/rust-lang/cargo/issues/3005 | |
let short_name = bcx.target_data.short_name(&unit.kind); | |
if (unit.target.is_dylib() | |
|| unit.target.is_cdylib() | |
|| (unit.target.is_executable() && short_name == "wasm32-unknown-emscripten") | |
|| (unit.target.is_executable() && short_name.contains("msvc"))) | |
&& unit.pkg.package_id().source_id().is_path() | |
&& bcx.gctx.get_env("__CARGO_DEFAULT_LIB_METADATA").is_err() | |
{ | |
return false; | |
} |
├── examples | ||
│ ├── foo.d [target_platform=windows-msvc] | ||
│ ├── foo.pdb [target_platform=windows-msvc] | ||
│ ├── foo[EXE] [target_platform=windows-msvc] | ||
│ ├── foo-[HASH].d [target_platform=macos,linux,windows-gnu] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like some of the benefit of tree
output is lost with the conditionals breaking up the output
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it was not very bad until I realized windows-msvc
does not include hashes in many places that linux, macos, and winddows-gnu does. I felt this was still better than duplicating the tree with #[cfg(target_os = "linux")]
.
This comment has been minimized.
This comment has been minimized.
edef1aa
to
b9f9cbb
Compare
I rebased to resolve the conflict with #15910 I'm waiting to see what we decide to do on this PR before writing tests for the build-dir layout changes. If we feel this is not a meaningful improvement, I am happy to close this and continue with the existing approach in |
What does this PR try to resolve?
The goal is to make filesystem layout tests easier to understand and review.
The
build-dir
test's have a helper fns that have imperative checks. While this works, its not easy to see what its validating at a glance.My thought is to reuse the snapbox infrastructure we already have for the cargo ui tests.
The prototype in this PR generates a file tree (like the unix tree command) and uses snapbox to compare against a snapshot. This makes the layout tests much easier to read and failures are much more obvious what what went wrong.
Implementation Details
LayoutTree
struct handles comparison logic (as opposed to using snapbox directly)SNAPSHOTS=overwrite
. This is arguably good as often the overwrite would be incorrect as the snapshots are platform specific.[target_platform=<target-platform>]
initial implementation issues
However there are some problems with the current implementation that limit the usefulness of it.
dSYM
on macos└── foo-[HASH].dSYM [cfg(target_os = "macos")]
└
,├
,│
) to handle optional files.target/<profile>/build/pkg-[HASH]
directories. The hash changes the order of the directories when generating the tree making snapshots inconsistent.[HASH]
, but this does not help with the order.Fun fact: These changes were written and tested at Rust Forge 2025 in New Zealand. 🇳🇿 🥳