Skip to content

Commit de7fcd0

Browse files
committed
Auto merge of #744 - ecstatic-morse:snap-test, r=jackh726
Allow tests to be updated automatically Uses the `expect-test` crate (thanks for the recommendation `@lnicola!)` to implement snapshot testing. `expect-test` is a bit more flexible than `insta`, so it seems like a good fit. This gives nicer diffs as well, since we don't strip whitespace prior to the comparison. Setting `UPDATE_EXPECT=1` before running `cargo test` will update all tests automatically. ## Blockers - [x] Omit empty lists from `Solution`'s `Display` impl. * `chalk` currently compares prefixes to make things less noisy, but that seems error prone anyways. * Harder than it seems at first, since you need to unintern to check whether a list is empty. - [x] Handle recursive/SLG comparisons better. * Many tests use `yields`, which compares the results of the two solvers against a known output. Unfortunately, the test macro will duplicate the `expect` invocation, so if neither output matches the expected one, `expect-test` will overwrite it *twice*, almost assuredly corrupting the test file. * There's many ways to fix this. ~~I've hacked around it here by adding a method to `Expect` (which is not yet upstream).~~ Most robust is to collect the various engine results first and only compare them against the output if they agree. *This is implemented now*. - [x] rust-analyzer/expect-test#20 ## Nice to haves - [x] Don't use raw strings with a hash unconditionally when blessing (rust-analyzer/expect-test#28). - [x] Shorter alias for `expect![[`? It's currently hard-coded into the `expect-test` crate (rust-analyzer/expect-test#26). - [ ] Remove newline escapes from existing tests.
2 parents f47846d + 1627b6e commit de7fcd0

39 files changed

+1014
-1021
lines changed

Cargo.lock

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,6 @@ chalk-integration = { version = "0.77.0-dev.0", path = "chalk-integration" }
3434
[dev-dependencies]
3535
# used for program_writer test errors
3636
diff = "0.1"
37+
expect-test = "1.2.1"
3738
pretty_assertions = "0.6.1"
3839
regex = "1"

chalk-ir/src/debug.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
//! Debug impls for types.
22
3-
use std::fmt::{Debug, Display, Error, Formatter};
3+
use std::fmt::{self, Debug, Display, Error, Formatter};
44

55
use super::*;
66

7+
/// Wrapper to allow forwarding to `Display::fmt`, `Debug::fmt`, etc.
8+
pub struct Fmt<F>(pub F)
9+
where
10+
F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result;
11+
12+
impl<F> fmt::Display for Fmt<F>
13+
where
14+
F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
15+
{
16+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17+
(self.0)(f)
18+
}
19+
}
20+
721
impl<I: Interner> Debug for TraitId<I> {
822
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> {
923
I::debug_trait_id(*self, fmt).unwrap_or_else(|| write!(fmt, "TraitId({:?})", self.0))
@@ -959,14 +973,27 @@ impl<I: Interner> Debug for Constraint<I> {
959973
}
960974

961975
impl<I: Interner> Display for ConstrainedSubst<I> {
976+
#[rustfmt::skip]
962977
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
963978
let ConstrainedSubst { subst, constraints } = self;
964979

965-
write!(
966-
f,
967-
"substitution {}, lifetime constraints {:?}",
968-
subst, constraints,
969-
)
980+
let mut first = true;
981+
982+
let subst = format!("{}", Fmt(|f| Display::fmt(subst, f)));
983+
if subst != "[]" {
984+
write!(f, "substitution {}", subst)?;
985+
first = false;
986+
}
987+
988+
let constraints = format!("{}", Fmt(|f| Debug::fmt(constraints, f)));
989+
if constraints != "[]" {
990+
if !first { write!(f, ", ")?; }
991+
write!(f, "lifetime constraints {}", constraints)?;
992+
first = false;
993+
}
994+
995+
let _ = first;
996+
Ok(())
970997
}
971998
}
972999

chalk-solve/src/solve.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,20 @@ pub struct SolutionDisplay<'a, I: Interner> {
159159
}
160160

161161
impl<'a, I: Interner> fmt::Display for SolutionDisplay<'a, I> {
162+
#[rustfmt::skip]
162163
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
163164
let SolutionDisplay { solution, interner } = self;
164165
match solution {
165-
Solution::Unique(constrained) => {
166-
write!(f, "Unique; {}", constrained.display(*interner))
167-
}
166+
// If a `Unique` solution has no associated data, omit the trailing semicolon.
167+
// This makes blessed test output nicer to read.
168+
Solution::Unique(Canonical { binders, value: ConstrainedSubst { subst, constraints } } )
169+
if interner.constraints_data(constraints.interned()).is_empty()
170+
&& interner.substitution_data(subst.interned()).is_empty()
171+
&& interner.canonical_var_kinds_data(binders.interned()).is_empty()
172+
=> write!(f, "Unique"),
173+
174+
Solution::Unique(constrained) => write!(f, "Unique; {}", constrained.display(*interner)),
175+
168176
Solution::Ambig(Guidance::Definite(subst)) => write!(
169177
f,
170178
"Ambiguous; definite substitution {}",

tests/logging_db/util.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ use chalk_solve::ext::*;
1212
use chalk_solve::logging_db::LoggingRustIrDatabase;
1313
use chalk_solve::RustIrDatabase;
1414

15-
use crate::test::{assert_result, TestGoal};
15+
use crate::test::assert_result_str;
16+
17+
type TestGoal = crate::test::TestGoal<&'static str>;
1618

1719
macro_rules! logging_db_output_sufficient {
1820
($($arg:tt)*) => {{
@@ -25,12 +27,16 @@ macro_rules! logging_db_output_sufficient {
2527

2628
pub fn logging_db_output_sufficient(
2729
program_text: &str,
28-
goals: Vec<(&str, SolverChoice, TestGoal)>,
30+
goals: Vec<(&str, Vec<SolverChoice>, TestGoal)>,
2931
) {
3032
println!("program {}", program_text);
3133
assert!(program_text.starts_with("{"));
3234
assert!(program_text.ends_with("}"));
3335

36+
let goals = goals
37+
.iter()
38+
.flat_map(|(a, bs, c)| bs.into_iter().map(move |b| (a, b, c)));
39+
3440
let output_text = {
3541
let db = ChalkDatabase::with(
3642
&program_text[1..program_text.len() - 1],
@@ -39,6 +45,7 @@ pub fn logging_db_output_sufficient(
3945

4046
let program = db.program_ir().unwrap();
4147
let wrapped = LoggingRustIrDatabase::<_, Program, _>::new(program.clone());
48+
4249
chalk_integration::tls::set_current_program(&program, || {
4350
for (goal_text, solver_choice, expected) in goals.clone() {
4451
let mut solver = solver_choice.into_solver();
@@ -59,7 +66,7 @@ pub fn logging_db_output_sufficient(
5966
match expected {
6067
TestGoal::Aggregated(expected) => {
6168
let result = solver.solve(&wrapped, &peeled_goal);
62-
assert_result(result, expected, db.interner());
69+
assert_result_str(result, expected, db.interner());
6370
}
6471
_ => panic!("only aggregated test goals supported for logger goals"),
6572
}
@@ -101,7 +108,7 @@ pub fn logging_db_output_sufficient(
101108
match expected {
102109
TestGoal::Aggregated(expected) => {
103110
let result = solver.solve(&db, &peeled_goal);
104-
assert_result(result, expected, db.interner());
111+
assert_result_str(result, expected, db.interner());
105112
}
106113
_ => panic!("only aggregated test goals supported for logger goals"),
107114
}

tests/test/arrays.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ fn arrays_are_sized() {
1313
[u32; N]: Sized
1414
}
1515
} yields {
16-
"Unique"
16+
expect![["Unique"]]
1717
}
1818

1919
}
@@ -35,7 +35,7 @@ fn arrays_are_copy_if_element_copy() {
3535
[Foo; N]: Copy
3636
}
3737
} yields {
38-
"Unique"
38+
expect![["Unique"]]
3939
}
4040
}
4141
}
@@ -55,7 +55,7 @@ fn arrays_are_not_copy_if_element_not_copy() {
5555
[Foo; N]: Copy
5656
}
5757
} yields {
58-
"No possible solution"
58+
expect![["No possible solution"]]
5959
}
6060
}
6161
}
@@ -76,7 +76,7 @@ fn arrays_are_clone_if_element_clone() {
7676
[Foo; N]: Clone
7777
}
7878
} yields {
79-
"Unique"
79+
expect![["Unique"]]
8080
}
8181
}
8282
}
@@ -96,7 +96,7 @@ fn arrays_are_not_clone_if_element_not_clone() {
9696
[Foo; N]: Clone
9797
}
9898
} yields {
99-
"No possible solution"
99+
expect![["No possible solution"]]
100100
}
101101
}
102102
}
@@ -116,23 +116,23 @@ fn arrays_are_well_formed_if_elem_sized() {
116116
}
117117
}
118118
} yields {
119-
"Unique"
119+
expect![["Unique"]]
120120
}
121121

122122
goal {
123123
forall<const N, T> {
124124
WellFormed([T; N])
125125
}
126126
} yields {
127-
"No possible solution"
127+
expect![["No possible solution"]]
128128
}
129129

130130
goal {
131131
exists<const N, T> {
132132
WellFormed([T; N])
133133
}
134134
} yields {
135-
"Ambiguous; no inference guidance"
135+
expect![["Ambiguous; no inference guidance"]]
136136
}
137137
}
138138
}

0 commit comments

Comments
 (0)