Add lightweight snapshot testing for bootstrap tests
This commit is contained in:
parent
983fe4f265
commit
6feb9b792c
2 changed files with 107 additions and 10 deletions
|
|
@ -1231,3 +1231,81 @@ fn any_debug() {
|
|||
// Downcasting to the underlying type should succeed.
|
||||
assert_eq!(x.downcast_ref::<MyStruct>(), Some(&MyStruct { x: 7 }));
|
||||
}
|
||||
|
||||
/// The staging tests use insta for snapshot testing.
|
||||
/// See bootstrap's README on how to bless the snapshots.
|
||||
mod staging {
|
||||
use crate::core::builder::tests::{
|
||||
TEST_TRIPLE_1, configure, configure_with_args, render_steps, run_build,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn build_compiler_stage_1() {
|
||||
let mut cache = run_build(
|
||||
&["compiler".into()],
|
||||
configure_with_args(&["build", "--stage", "1"], &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]),
|
||||
);
|
||||
let steps = cache.into_executed_steps();
|
||||
insta::assert_snapshot!(render_steps(&steps), @r"
|
||||
[build] rustc 0 <target1> -> std 0 <target1>
|
||||
[build] llvm <target1>
|
||||
[build] rustc 0 <target1> -> rustc 1 <target1>
|
||||
[build] rustc 0 <target1> -> rustc 1 <target1>
|
||||
");
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the executed bootstrap steps for usage in snapshot tests with insta.
|
||||
/// Only renders certain important steps.
|
||||
/// Each value in `steps` should be a tuple of (Step, step output).
|
||||
fn render_steps(steps: &[(Box<dyn Any>, Box<dyn Any>)]) -> String {
|
||||
steps
|
||||
.iter()
|
||||
.filter_map(|(step, output)| {
|
||||
// FIXME: implement an optional method on Step to produce metadata for test, instead
|
||||
// of this downcasting
|
||||
if let Some((rustc, output)) = downcast_step::<compile::Rustc>(step, output) {
|
||||
Some(format!(
|
||||
"[build] {} -> {}",
|
||||
render_compiler(rustc.build_compiler),
|
||||
// FIXME: return the correct stage from the `Rustc` step, now it behaves weirdly
|
||||
render_compiler(Compiler::new(rustc.build_compiler.stage + 1, rustc.target)),
|
||||
))
|
||||
} else if let Some((std, output)) = downcast_step::<compile::Std>(step, output) {
|
||||
Some(format!(
|
||||
"[build] {} -> std {} <{}>",
|
||||
render_compiler(std.compiler),
|
||||
std.compiler.stage,
|
||||
std.target
|
||||
))
|
||||
} else if let Some((llvm, output)) = downcast_step::<llvm::Llvm>(step, output) {
|
||||
Some(format!("[build] llvm <{}>", llvm.target))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(|line| {
|
||||
line.replace(TEST_TRIPLE_1, "target1")
|
||||
.replace(TEST_TRIPLE_2, "target2")
|
||||
.replace(TEST_TRIPLE_3, "target3")
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
fn downcast_step<'a, S: Step>(
|
||||
step: &'a Box<dyn Any>,
|
||||
output: &'a Box<dyn Any>,
|
||||
) -> Option<(&'a S, &'a S::Output)> {
|
||||
let Some(step) = step.downcast_ref::<S>() else {
|
||||
return None;
|
||||
};
|
||||
let Some(output) = output.downcast_ref::<S::Output>() else {
|
||||
return None;
|
||||
};
|
||||
Some((step, output))
|
||||
}
|
||||
|
||||
fn render_compiler(compiler: Compiler) -> String {
|
||||
format!("rustc {} <{}>", compiler.stage, compiler.host)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use std::borrow::Borrow;
|
|||
use std::cell::RefCell;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
|
|
@ -208,25 +209,30 @@ pub static INTERNER: LazyLock<Interner> = LazyLock::new(Interner::default);
|
|||
/// any type in its output. It is a write-once cache; values are never evicted,
|
||||
/// which means that references to the value can safely be returned from the
|
||||
/// `get()` method.
|
||||
#[derive(Debug)]
|
||||
pub struct Cache(
|
||||
RefCell<
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Cache {
|
||||
cache: RefCell<
|
||||
HashMap<
|
||||
TypeId,
|
||||
Box<dyn Any>, // actually a HashMap<Step, Interned<Step::Output>>
|
||||
>,
|
||||
>,
|
||||
);
|
||||
#[cfg(test)]
|
||||
/// Contains steps in the same order in which they were executed
|
||||
/// Useful for tests
|
||||
/// Tuples (step, step output)
|
||||
executed_steps: RefCell<Vec<(Box<dyn Any>, Box<dyn Any>)>>,
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
/// Creates a new empty cache.
|
||||
pub fn new() -> Cache {
|
||||
Cache(RefCell::new(HashMap::new()))
|
||||
Cache::default()
|
||||
}
|
||||
|
||||
/// Stores the result of a computation step in the cache.
|
||||
pub fn put<S: Step>(&self, step: S, value: S::Output) {
|
||||
let mut cache = self.0.borrow_mut();
|
||||
let mut cache = self.cache.borrow_mut();
|
||||
let type_id = TypeId::of::<S>();
|
||||
let stepcache = cache
|
||||
.entry(type_id)
|
||||
|
|
@ -234,12 +240,20 @@ impl Cache {
|
|||
.downcast_mut::<HashMap<S, S::Output>>()
|
||||
.expect("invalid type mapped");
|
||||
assert!(!stepcache.contains_key(&step), "processing {step:?} a second time");
|
||||
|
||||
#[cfg(test)]
|
||||
{
|
||||
let step: Box<dyn Any> = Box::new(step.clone());
|
||||
let output: Box<dyn Any> = Box::new(value.clone());
|
||||
self.executed_steps.borrow_mut().push((step, output));
|
||||
}
|
||||
|
||||
stepcache.insert(step, value);
|
||||
}
|
||||
|
||||
/// Retrieves a cached result for the given step, if available.
|
||||
pub fn get<S: Step>(&self, step: &S) -> Option<S::Output> {
|
||||
let mut cache = self.0.borrow_mut();
|
||||
let mut cache = self.cache.borrow_mut();
|
||||
let type_id = TypeId::of::<S>();
|
||||
let stepcache = cache
|
||||
.entry(type_id)
|
||||
|
|
@ -252,8 +266,8 @@ impl Cache {
|
|||
|
||||
#[cfg(test)]
|
||||
impl Cache {
|
||||
pub fn all<S: Ord + Clone + Step>(&mut self) -> Vec<(S, S::Output)> {
|
||||
let cache = self.0.get_mut();
|
||||
pub fn all<S: Ord + Step>(&mut self) -> Vec<(S, S::Output)> {
|
||||
let cache = self.cache.get_mut();
|
||||
let type_id = TypeId::of::<S>();
|
||||
let mut v = cache
|
||||
.remove(&type_id)
|
||||
|
|
@ -265,7 +279,12 @@ impl Cache {
|
|||
}
|
||||
|
||||
pub fn contains<S: Step>(&self) -> bool {
|
||||
self.0.borrow().contains_key(&TypeId::of::<S>())
|
||||
self.cache.borrow().contains_key(&TypeId::of::<S>())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn into_executed_steps(mut self) -> Vec<(Box<dyn Any>, Box<dyn Any>)> {
|
||||
mem::take(&mut self.executed_steps.borrow_mut())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue