-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Hand-rolling mini insta? #3835
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
Comments
I am warming up to this idea, here's the API I came up with: use std::{
env,
path::{Path, PathBuf},
};
#[derive(Debug)]
pub struct Expectation {
pub file: &'static str,
pub line: u32,
pub data: &'static str,
}
impl Expectation {
pub fn assert_eq(&self, actual: &str) {
if self.data != actual {
let path = dbg!(project_root()).join(self.file);
let text = std::fs::read_to_string(&path).unwrap();
let expect_start = self.line as usize;
let expect_len = text
.lines()
.skip(expect_start + 1)
.take_while(|it| !it.contains(r##""#]"##))
.count();
for l in text.lines().skip(expect_start).take(expect_len) {
println!("{}", l)
}
}
}
}
fn project_root() -> PathBuf {
Path::new(
&env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),
)
.ancestors()
.nth(2)
.unwrap()
.to_path_buf()
}
#[macro_export]
macro_rules! expect {
[$(,)?$data:literal] => {
$crate::Expectation {
file: $crate::__rt::file!(), line: $crate::__rt::line!(), data: $data
}
};
}
#[test]
fn foo() {
let expectation = expect![,r#"
64..84 '{ ...1 }; }': ()
70..81 'SS { x: 1 }': S<u32>
78..79 '1': u32
"#];
eprintln!("{:?}", expectation);
}
#[doc(hidden)]
pub mod __rt {
pub use std::{file, line};
} #[test]
fn nested_module_resolution() {
check(
r"
//- /lib.rs
mod n1;
//- /n1.rs
mod n2;
//- /n1/n2.rs
struct X;
",
expect![,r#"
crate
n1: t
crate::n1
n2: t
crate::n1::n2
X: t v
"#],
);
} |
That api wil never mark a test as failed. |
Yeah, there should be assetion failure there as well :) The bigger missing piece is that this doesn't (yet) show a 💡 in the editor which changes the data in-place (one of my main motivations for this). Not sure how to approach that though -- my current plan is to dump expect failures to It would be sweet if I can just print something to stdout which would be applicable in VS Code, but I don't thing that that is possible? |
Can you use a problem matcher to match the test run output? |
I think that's not possible unfortunately :-( |
Wouldn't it be nicer to implement that support for insta, so other users can also profit from it? In general, in this case I don't really understand the motivation to create a replacement; I think it'd be better to improve insta instead. |
So, the super-big picture here is that we are going to have an infinite number of tests eventually, and so something tailored for our use-case would be better economically. If you are going to use a library for Medium level picture is that I think there's a different set of tradoffs we can try, in particular:
These feels sufficiently different from insta that I feel that it makes sense to try this out separately, and then do a synthesis, rather than to try to change insta in-place. |
In particular, I really want this, but I don't see how this can be implemented in a nice way without hard-coding it into rust-analyzer, and, for hard-coding, NIH thing seems easier :) |
That would be useful for other users of |
If the |
Feature flags? Could be useful for other users too. |
Yeah, but |
I think we could do without the diff and colors if we update the snapshots on disk (because you can run |
I think it'd be pretty annoying to have to update snapshots to compare them, especially if the failure happens in CI. |
I'm thinking that |
So, I think I've figured a way to avoid any kind of disk persistence and IO. On test failure, the error message on stdout will contain file name, line number and expected/actual output (the whole lines, including the If When cursor is on the |
hey @matklad, i was hacking on a snapshot testing library yesterday that i think does almost what you're describing. I actually used parts of rust-analyzer to parse original source files and find the assertion macro location. updating multiple assertions in the same file is a bit challenging. global mutex wont' solve it, cause after every update lines in the file shift and the original That's a space i worked in for a while (outside of Rust tho) and would be happy to help hacking on any of that if i can. |
Apply changes in reverse source order. Applying a change can only shift the position of tokens after the change. |
But you need to gather the failing ones before doing that. |
Found another syntax to convince rustfmt to keep raw strings in place: expect![[r#"
63..93 '{ ... }': ()
73..86 'Self { x: 1 }': S<u32>
83..84 '1': u32
"#]] |
This is something we unfortunately have to avoid. There's a lot of things in rust-analyzer I'd love to use rust-analyzer for, but that means that we'll need to setup bootstrapping for development, which doubles compile times. So, we'd have to get by with approximate "parsing". |
unfortunately tests run in parallel in a random order, i don't think there's a way to change that
is there a lightweight parser i can use? the only thing i need to get is the location of that inline snapshot string literal when given line and column of the macro
i can technically just search for the right characters in the string, but that will probably break half of the time :) |
Maybe it wouldn't, as we control the fixtures in RA. |
I think the semi-proper way to do this would be to depend on assert_matches_inline_snapshot!(something, "snapshot it serializes to");
^ ^^^^^^^^^^^^^^^^^^^^^^^^^
| find this
Given this One thing I don't enjoy about insta is that its snapshots and assertions are coupled to the single macro. If I want to abstract over insta, I have to write a macro. For example, #[test]
fn self_in_struct_lit() {
assert_snapshot!(infer(
r#"
//- /main.rs
struct S<T> { x: T }
impl S<u32> {
fn foo() {
Self { x: 1 };
}
}
"#,
), @r###"
49..79 '{ ... }': ()
59..72 'Self { x: 1 }': S<u32>
69..70 '1': u32
"###);
} there isn't convenient way to abstract #[test]
fn self_in_struct_lit() {
check_types(
r#"
//- /main.rs
struct S<T> { x: T }
impl S<u32> {
fn foo() {
Self { x: 1 };
}
}
"#,
expect![[r#"
49..79 '{ ... }': ()
59..72 'Self { x: 1 }': S<u32>
69..70 '1': u32
"#]],
);
} That is, I'll be able to write a function which abstracts over testing routine. Incidentally, this should make extracting the text of snapshot much easier. |
PR is up: #5101 |
So far, we've been using insta for snapshot testing, and it is super useful, but not perfect. Some of the paper cuts I see:
cargo install insta
to fix snapshotsI am thinking that maybe less is more, and we should roll our own snapshot testing thing? I think getting rid of the coloring and using a simpler assert_eq_text style diff would be more convenient. The biggest problem is updating inline snapshots, but I think that shouldn't be too hard to implement.
The text was updated successfully, but these errors were encountered: