Skip to content

Commit 6feb9b7

Browse files
committed
Add lightweight snapshot testing for bootstrap tests
1 parent 983fe4f commit 6feb9b7

File tree

2 files changed

+107
-10
lines changed

2 files changed

+107
-10
lines changed

src/bootstrap/src/core/builder/tests.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,3 +1231,81 @@ fn any_debug() {
12311231
// Downcasting to the underlying type should succeed.
12321232
assert_eq!(x.downcast_ref::<MyStruct>(), Some(&MyStruct { x: 7 }));
12331233
}
1234+
1235+
/// The staging tests use insta for snapshot testing.
1236+
/// See bootstrap's README on how to bless the snapshots.
1237+
mod staging {
1238+
use crate::core::builder::tests::{
1239+
TEST_TRIPLE_1, configure, configure_with_args, render_steps, run_build,
1240+
};
1241+
1242+
#[test]
1243+
fn build_compiler_stage_1() {
1244+
let mut cache = run_build(
1245+
&["compiler".into()],
1246+
configure_with_args(&["build", "--stage", "1"], &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]),
1247+
);
1248+
let steps = cache.into_executed_steps();
1249+
insta::assert_snapshot!(render_steps(&steps), @r"
1250+
[build] rustc 0 <target1> -> std 0 <target1>
1251+
[build] llvm <target1>
1252+
[build] rustc 0 <target1> -> rustc 1 <target1>
1253+
[build] rustc 0 <target1> -> rustc 1 <target1>
1254+
");
1255+
}
1256+
}
1257+
1258+
/// Renders the executed bootstrap steps for usage in snapshot tests with insta.
1259+
/// Only renders certain important steps.
1260+
/// Each value in `steps` should be a tuple of (Step, step output).
1261+
fn render_steps(steps: &[(Box<dyn Any>, Box<dyn Any>)]) -> String {
1262+
steps
1263+
.iter()
1264+
.filter_map(|(step, output)| {
1265+
// FIXME: implement an optional method on Step to produce metadata for test, instead
1266+
// of this downcasting
1267+
if let Some((rustc, output)) = downcast_step::<compile::Rustc>(step, output) {
1268+
Some(format!(
1269+
"[build] {} -> {}",
1270+
render_compiler(rustc.build_compiler),
1271+
// FIXME: return the correct stage from the `Rustc` step, now it behaves weirdly
1272+
render_compiler(Compiler::new(rustc.build_compiler.stage + 1, rustc.target)),
1273+
))
1274+
} else if let Some((std, output)) = downcast_step::<compile::Std>(step, output) {
1275+
Some(format!(
1276+
"[build] {} -> std {} <{}>",
1277+
render_compiler(std.compiler),
1278+
std.compiler.stage,
1279+
std.target
1280+
))
1281+
} else if let Some((llvm, output)) = downcast_step::<llvm::Llvm>(step, output) {
1282+
Some(format!("[build] llvm <{}>", llvm.target))
1283+
} else {
1284+
None
1285+
}
1286+
})
1287+
.map(|line| {
1288+
line.replace(TEST_TRIPLE_1, "target1")
1289+
.replace(TEST_TRIPLE_2, "target2")
1290+
.replace(TEST_TRIPLE_3, "target3")
1291+
})
1292+
.collect::<Vec<_>>()
1293+
.join("\n")
1294+
}
1295+
1296+
fn downcast_step<'a, S: Step>(
1297+
step: &'a Box<dyn Any>,
1298+
output: &'a Box<dyn Any>,
1299+
) -> Option<(&'a S, &'a S::Output)> {
1300+
let Some(step) = step.downcast_ref::<S>() else {
1301+
return None;
1302+
};
1303+
let Some(output) = output.downcast_ref::<S::Output>() else {
1304+
return None;
1305+
};
1306+
Some((step, output))
1307+
}
1308+
1309+
fn render_compiler(compiler: Compiler) -> String {
1310+
format!("rustc {} <{}>", compiler.stage, compiler.host)
1311+
}

src/bootstrap/src/utils/cache.rs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use std::borrow::Borrow;
1717
use std::cell::RefCell;
1818
use std::cmp::Ordering;
1919
use std::collections::HashMap;
20+
use std::fmt::Debug;
2021
use std::hash::{Hash, Hasher};
2122
use std::marker::PhantomData;
2223
use std::ops::Deref;
@@ -208,38 +209,51 @@ pub static INTERNER: LazyLock<Interner> = LazyLock::new(Interner::default);
208209
/// any type in its output. It is a write-once cache; values are never evicted,
209210
/// which means that references to the value can safely be returned from the
210211
/// `get()` method.
211-
#[derive(Debug)]
212-
pub struct Cache(
213-
RefCell<
212+
#[derive(Debug, Default)]
213+
pub struct Cache {
214+
cache: RefCell<
214215
HashMap<
215216
TypeId,
216217
Box<dyn Any>, // actually a HashMap<Step, Interned<Step::Output>>
217218
>,
218219
>,
219-
);
220+
#[cfg(test)]
221+
/// Contains steps in the same order in which they were executed
222+
/// Useful for tests
223+
/// Tuples (step, step output)
224+
executed_steps: RefCell<Vec<(Box<dyn Any>, Box<dyn Any>)>>,
225+
}
220226

221227
impl Cache {
222228
/// Creates a new empty cache.
223229
pub fn new() -> Cache {
224-
Cache(RefCell::new(HashMap::new()))
230+
Cache::default()
225231
}
226232

227233
/// Stores the result of a computation step in the cache.
228234
pub fn put<S: Step>(&self, step: S, value: S::Output) {
229-
let mut cache = self.0.borrow_mut();
235+
let mut cache = self.cache.borrow_mut();
230236
let type_id = TypeId::of::<S>();
231237
let stepcache = cache
232238
.entry(type_id)
233239
.or_insert_with(|| Box::<HashMap<S, S::Output>>::default())
234240
.downcast_mut::<HashMap<S, S::Output>>()
235241
.expect("invalid type mapped");
236242
assert!(!stepcache.contains_key(&step), "processing {step:?} a second time");
243+
244+
#[cfg(test)]
245+
{
246+
let step: Box<dyn Any> = Box::new(step.clone());
247+
let output: Box<dyn Any> = Box::new(value.clone());
248+
self.executed_steps.borrow_mut().push((step, output));
249+
}
250+
237251
stepcache.insert(step, value);
238252
}
239253

240254
/// Retrieves a cached result for the given step, if available.
241255
pub fn get<S: Step>(&self, step: &S) -> Option<S::Output> {
242-
let mut cache = self.0.borrow_mut();
256+
let mut cache = self.cache.borrow_mut();
243257
let type_id = TypeId::of::<S>();
244258
let stepcache = cache
245259
.entry(type_id)
@@ -252,8 +266,8 @@ impl Cache {
252266

253267
#[cfg(test)]
254268
impl Cache {
255-
pub fn all<S: Ord + Clone + Step>(&mut self) -> Vec<(S, S::Output)> {
256-
let cache = self.0.get_mut();
269+
pub fn all<S: Ord + Step>(&mut self) -> Vec<(S, S::Output)> {
270+
let cache = self.cache.get_mut();
257271
let type_id = TypeId::of::<S>();
258272
let mut v = cache
259273
.remove(&type_id)
@@ -265,7 +279,12 @@ impl Cache {
265279
}
266280

267281
pub fn contains<S: Step>(&self) -> bool {
268-
self.0.borrow().contains_key(&TypeId::of::<S>())
282+
self.cache.borrow().contains_key(&TypeId::of::<S>())
283+
}
284+
285+
#[cfg(test)]
286+
pub fn into_executed_steps(mut self) -> Vec<(Box<dyn Any>, Box<dyn Any>)> {
287+
mem::take(&mut self.executed_steps.borrow_mut())
269288
}
270289
}
271290

0 commit comments

Comments
 (0)