Skip to content

Commit a2e42dc

Browse files
committed
test(rustfix): Using snapbox for snapshot testing
`.json` files will have pretty printed json when updated
1 parent ba411d6 commit a2e42dc

File tree

1 file changed

+49
-106
lines changed

1 file changed

+49
-106
lines changed

crates/rustfix/tests/parse_and_replace.rs

+49-106
Original file line numberDiff line numberDiff line change
@@ -7,44 +7,30 @@
77
//! compares the result with the corresponding `.fixed.rs` file. If they don't
88
//! match, then the test fails.
99
//!
10-
//! There are several debugging environment variables for this test that you can set:
10+
//! The files ending in `.nightly.rs` will run only on the nightly toolchain
1111
//!
12-
//! - `RUST_LOG=parse_and_replace=debug`: Print debug information.
13-
//! - `RUSTFIX_TEST_BLESS=test-name.rs`: When given the name of a test, this
14-
//! will overwrite the `.json` and `.fixed.rs` files with the expected
15-
//! values. This can be used when adding a new test.
16-
//! - `RUSTFIX_TEST_RECORD_JSON=1`: Records the JSON output to
17-
//! `*.recorded.json` files. You can then move that to `.json` or whatever
18-
//! you need.
19-
//! - `RUSTFIX_TEST_RECORD_FIXED_RUST=1`: Records the fixed result to
20-
//! `*.recorded.rs` files. You can then move that to `.rs` or whatever you
21-
//! need.
12+
//! To override snapshots, run `SNAPSHOTS=overwrite cargo test`.
13+
//! See [`snapbox::assert::Action`] for different actions.
2214
2315
#![allow(clippy::disallowed_methods, clippy::print_stdout, clippy::print_stderr)]
2416

2517
use anyhow::{anyhow, Context, Error};
2618
use rustfix::apply_suggestions;
19+
use serde_json::Value;
20+
use snapbox::data::DataFormat;
21+
use snapbox::{Assert, Data};
2722
use std::collections::HashSet;
2823
use std::env;
2924
use std::ffi::OsString;
3025
use std::fs;
3126
use std::path::Path;
3227
use std::process::{Command, Output};
3328
use tempfile::tempdir;
34-
use tracing::info;
3529

3630
mod fixmode {
3731
pub const EVERYTHING: &str = "yolo";
3832
}
3933

40-
mod settings {
41-
// can be set as env var to debug
42-
pub const CHECK_JSON: &str = "RUSTFIX_TEST_CHECK_JSON";
43-
pub const RECORD_JSON: &str = "RUSTFIX_TEST_RECORD_JSON";
44-
pub const RECORD_FIXED_RUST: &str = "RUSTFIX_TEST_RECORD_FIXED_RUST";
45-
pub const BLESS: &str = "RUSTFIX_TEST_BLESS";
46-
}
47-
4834
static mut VERSION: (u32, bool) = (0, false);
4935

5036
// Temporarily copy from `cargo_test_macro::version`.
@@ -108,53 +94,19 @@ fn compiles_without_errors(file: &Path) -> Result<(), Error> {
10894

10995
match res.status.code() {
11096
Some(0) => Ok(()),
111-
_ => {
112-
info!(
113-
"file {:?} failed to compile:\n{}",
114-
file,
115-
String::from_utf8(res.stderr)?
116-
);
117-
Err(anyhow!(
118-
"failed with status {:?} (`env RUST_LOG=parse_and_replace=info` for more info)",
119-
res.status.code(),
120-
))
121-
}
122-
}
123-
}
124-
125-
fn diff(expected: &str, actual: &str) -> String {
126-
use similar::{ChangeTag, TextDiff};
127-
use std::fmt::Write;
128-
129-
let mut res = String::new();
130-
let diff = TextDiff::from_lines(expected.trim(), actual.trim());
131-
132-
let mut different = false;
133-
for op in diff.ops() {
134-
for change in diff.iter_changes(op) {
135-
let prefix = match change.tag() {
136-
ChangeTag::Equal => continue,
137-
ChangeTag::Insert => "+",
138-
ChangeTag::Delete => "-",
139-
};
140-
if !different {
141-
writeln!(&mut res, "differences found (+ == actual, - == expected):").unwrap();
142-
different = true;
143-
}
144-
write!(&mut res, "{} {}", prefix, change.value()).unwrap();
145-
}
146-
}
147-
if different {
148-
write!(&mut res, "").unwrap();
97+
_ => Err(anyhow!(
98+
"file {:?} failed compile with status {:?}:\n {}",
99+
file,
100+
res.status.code(),
101+
String::from_utf8(res.stderr)?
102+
)),
149103
}
150-
151-
res
152104
}
153105

154106
fn test_rustfix_with_file<P: AsRef<Path>>(file: P, mode: &str) {
155107
let file: &Path = file.as_ref();
156108
let json_file = file.with_extension("json");
157-
let fixed_file = file.with_extension("fixed.rs");
109+
let expected_fixed_file = file.with_extension("fixed.rs");
158110

159111
let filter_suggestions = if mode == fixmode::EVERYTHING {
160112
rustfix::Filter::Everything
@@ -163,60 +115,51 @@ fn test_rustfix_with_file<P: AsRef<Path>>(file: P, mode: &str) {
163115
};
164116

165117
let code = fs::read_to_string(file).unwrap();
166-
let errors = compile_and_get_json_errors(file)
167-
.with_context(|| format!("could not compile {}", file.display())).unwrap();
168-
let suggestions =
169-
rustfix::get_suggestions_from_json(&errors, &HashSet::new(), filter_suggestions)
170-
.context("could not load suggestions").unwrap();
171118

172-
if std::env::var(settings::RECORD_JSON).is_ok() {
173-
fs::write(file.with_extension("recorded.json"), &errors).unwrap();
174-
}
119+
let json = compile_and_get_json_errors(file)
120+
.with_context(|| format!("could not compile {}", file.display()))
121+
.unwrap();
175122

176-
if std::env::var(settings::CHECK_JSON).is_ok() {
177-
let expected_json = fs::read_to_string(&json_file)
178-
.with_context(|| format!("could not load json fixtures for {}", file.display())).unwrap();
179-
let expected_suggestions =
180-
rustfix::get_suggestions_from_json(&expected_json, &HashSet::new(), filter_suggestions)
181-
.context("could not load expected suggestions").unwrap();
182-
183-
assert!(
184-
expected_suggestions == suggestions,
185-
"got unexpected suggestions from clippy:\n{}",
186-
diff(
187-
&format!("{:?}", expected_suggestions),
188-
&format!("{:?}", suggestions)
189-
)
190-
);
191-
}
123+
let suggestions =
124+
rustfix::get_suggestions_from_json(&json, &HashSet::new(), filter_suggestions)
125+
.context("could not load suggestions")
126+
.unwrap();
192127

193128
let fixed = apply_suggestions(&code, &suggestions)
194-
.with_context(|| format!("could not apply suggestions to {}", file.display())).unwrap()
129+
.with_context(|| format!("could not apply suggestions to {}", file.display()))
130+
.unwrap()
195131
.replace('\r', "");
196132

197-
if std::env::var(settings::RECORD_FIXED_RUST).is_ok() {
198-
fs::write(file.with_extension("recorded.rs"), &fixed).unwrap();
199-
}
200-
201-
if let Some(bless_name) = std::env::var_os(settings::BLESS) {
202-
if bless_name == file.file_name().unwrap() {
203-
std::fs::write(&json_file, &errors).unwrap();
204-
std::fs::write(&fixed_file, &fixed).unwrap();
205-
}
206-
}
207-
208-
let expected_fixed = fs::read_to_string(&fixed_file)
209-
.with_context(|| format!("could read fixed file for {}", file.display())).unwrap()
210-
.replace('\r', "");
211-
assert!(
212-
fixed.trim() == expected_fixed.trim(),
213-
"file {} doesn't look fixed:\n{}",
214-
file.display(),
215-
diff(fixed.trim(), expected_fixed.trim())
133+
let assert = Assert::new().action_env(snapbox::assert::DEFAULT_ACTION_ENV);
134+
let (actual_fix, expected_fix) = assert.normalize(
135+
Data::text(&fixed),
136+
Data::read_from(expected_fixed_file.as_path(), Some(DataFormat::Text)),
216137
);
217138

218-
compiles_without_errors(&fixed_file).unwrap();
139+
if actual_fix != expected_fix {
140+
let fixed_assert = assert.try_eq(Some(&"Current Fix"), actual_fix, expected_fix);
141+
assert!(fixed_assert.is_ok(), "{}", fixed_assert.err().unwrap());
142+
143+
let expected_json = Data::read_from(json_file.as_path(), Some(DataFormat::Text));
144+
145+
let pretty_json = json
146+
.split("\n")
147+
.filter(|j| !j.is_empty())
148+
.map(|j| {
149+
serde_json::to_string_pretty(&serde_json::from_str::<Value>(j).unwrap()).unwrap()
150+
})
151+
.collect::<Vec<String>>()
152+
.join("\n");
153+
154+
let json_assert = assert.try_eq(
155+
Some(&"Compiler Error"),
156+
Data::text(pretty_json),
157+
expected_json,
158+
);
159+
assert!(json_assert.is_ok(), "{}", json_assert.err().unwrap());
160+
}
219161

162+
compiles_without_errors(&expected_fixed_file).unwrap();
220163
}
221164

222165
macro_rules! run_test {

0 commit comments

Comments
 (0)