Description
Proposal
Problem statement
Being able to bless a test failure as expected and have the expected value updated (ie snapshot testing) is a big productivity boon. For example, the Cargo team just put in a lot of work to switch thousands of tests to snapshot testing (rust-lang/cargo#14039). Having that expected value inside the source code makes it easier to manage (rather than dealing with thousands of snapshots that may or may not be used anymore) and allows you to more easily inspect what condition is being tested. You can look at the reverse dependencies of snapbox, expect-test, and insta. The first two primarily work through inline snapshots. I can't say how often people use inline snapshots with insta
.
Similarly, it is can be helpful to be able to look up resources relative to the source file, rather than relative to the manifest root (via CARGO_MANIFEST_DIR
). These can include
- Snapshots (Cargo does this, see https://github.com/rust-lang/cargo/blob/286b6d0efa1db67c17c1fbed1385e9829f09c1e6/tests/testsuite/cargo_add/add_multiple/mod.rs#L36)
- Development-time assets (in theory, unsure if people want this)
Today this done by using file!
(which is unspecified) and assuming it is relative to the workspace root (which isn't always true), and somehow discovering the workspace root. The "somehow" is usually a mixture of
- Walking the path ancestors and assuming the next higher manifest is the workspace root (which it isn't always), https://github.com/assert-rs/snapbox/blob/60c7ce379e36be9d53238d1b936967fe6d28ca3e/crates/snapbox/src/macros.rs#L83-L87
- Dropping a
.cargo/config.toml
with an[env]
that is the workspace root (remember, as I said,file!
isn't always relative to workspace root), https://github.com/rust-lang/cargo/blob/286b6d0efa1db67c17c1fbed1385e9829f09c1e6/.cargo/config.toml#L7-L10
The Cargo team considered adding a CARGO_RUSTC_CURRENT_DIR
(rust-lang/cargo#12996) that would solve this problem but there were concerns over this existing purely for file!
and file!
being unspecified, causing us to push people towards relying on unspecified behavior.
Motivating examples or use cases
Normally, this will be abstracted away in a test like:
#[test]
fn test_no_infostring() {
snapbox::assert_data_eq!(
si!(r#"---
[dependencies]
time="0.1.25"
---
fn main() {}
"#),
str![[r#"
[[bin]]
name = "test-"
path = [..]
[dependencies]
time = "0.1.25"
[package]
autobenches = false
autobins = false
autoexamples = false
autolib = false
autotests = false
build = false
edition = "2021"
name = "test-"
[profile.release]
strip = true
[workspace]
"#]]
);
}
This relies on macros like
#[macro_export]
macro_rules! str {
[$data:literal] => { $crate::str![[$data]] };
[[$data:literal]] => {{
let position = $crate::data::Position {
file: $crate::utils::current_rs!(),
line: line!(),
column: column!(),
};
let inline = $crate::data::Inline {
position,
data: $data,
};
inline
}};
[] => { $crate::str![[""]] };
[[]] => { $crate::str![[""]] };
}
or
#[macro_export]
macro_rules! file {
[_] => {{
let path = $crate::data::generate_snapshot_path($crate::fn_path!(), None);
$crate::Data::read_from(&path, None)
}};
[_ : $type:ident] => {{
let format = $crate::data::DataFormat:: $type;
let path = $crate::data::generate_snapshot_path($crate::fn_path!(), Some(format));
$crate::Data::read_from(&path, Some($crate::data::DataFormat:: $type))
}};
[$path:literal] => {{
let mut path = $crate::utils::current_dir!();
path.push($path);
$crate::Data::read_from(&path, None)
}};
[$path:literal : $type:ident] => {{
let mut path = $crate::utils::current_dir!();
path.push($path);
$crate::Data::read_from(&path, Some($crate::data::DataFormat:: $type))
}};
}
Solution sketch
Like file!
provide a file_absolute!
that fills the role of the above $crate::utils::current_rs!()
(for which a $crate::utils::current_dir!()
can also be made).
Likewise, we should probably make the expectations for file!
more explicit
Open questions
- How will this interact with trim-paths? We were thinking warn or error
- How will this interact with the depinfo file? Unclear. If someone moves their directory, they will want it rebuilt. However, for CI caching where snapshots won't be used, they won't want it tracked.
- Should
file!
be "unspecified, for display purposes only" or be expected to be relative to a certain location (barring trim paths)
Alternatives
- Push back on the Cargo team for
CARGO_RUSTC_CURRENT_DIR
(feat: AddCARGO_RUSTC_CURRENT_DIR
(unstable) cargo#12996)
Links and related work
- Environment variable for Cargo Workspace cargo#3946 which mostly centers on this problem
What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
- We think this problem seems worth solving, and the standard library might be the right place to solve it.
- We think that this probably doesn't belong in the standard library.
Second, if there's a concrete solution:
- We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
- We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.