salsa/tests/on_demand_inputs.rs
Niko Matsakis fad97eeb6a remove the DB parameter
This had two unexpected consequences, one unfortunate, one "medium":

* All `salsa::Database` must be `'static`. This falls out from
`Q::DynDb` not having access to any lifetimes, but also the defaulting
rules for `dyn QueryGroup` that make it `dyn QueryGroup + 'static`. We
don't really support generic databases anyway yet so this isn't a big
deal, and we can add workarounds later (ideally via GATs).

* It is now statically impossible to invoke `snapshot` from a query,
and so we don't need to test that it panics. This is because the
signature of `snapshot` returns a `Snapshot<Self>` and that is not
accessible to a `dyn QueryGroup` type. Similarly, invoking
`Runtime::snapshot` directly is not possible becaues it is
crate-private. So I removed the test. This seems ok, but eventually I
would like to expose ways for queries to do parallel
execution (matklad and I had talked about a "speculation" primitive
for enabling that).

* This commit is 99% boilerplate I did with search-and-replace. I also
rolled in a few other changes I might have preferred to factor out,
most notably removing the `GetQueryTable` plumbing trait in favor of
free-methods, but it was awkward to factor them out and get all the
generics right (so much simpler in this version).
2020-07-04 14:17:11 +00:00

106 lines
2.8 KiB
Rust

//! Test that "on-demand" input pattern works.
//!
//! On-demand inputs are inputs computed lazily on the fly. They are simulated
//! via a b query with zero inputs, which uses `add_synthetic_read` to
//! tweak durability and `invalidate` to clear the input.
use std::{cell::Cell, collections::HashMap, rc::Rc};
use salsa::{Database as _, Durability};
#[salsa::query_group(QueryGroupStorage)]
trait QueryGroup: salsa::Database + AsRef<HashMap<u32, u32>> {
fn a(&self, x: u32) -> u32;
fn b(&self, x: u32) -> u32;
fn c(&self, x: u32) -> u32;
}
fn a(db: &dyn QueryGroup, x: u32) -> u32 {
let durability = if x % 2 == 0 {
Durability::LOW
} else {
Durability::HIGH
};
db.salsa_runtime().report_synthetic_read(durability);
let external_state: &HashMap<u32, u32> = db.as_ref();
external_state[&x]
}
fn b(db: &dyn QueryGroup, x: u32) -> u32 {
db.a(x)
}
fn c(db: &dyn QueryGroup, x: u32) -> u32 {
db.b(x)
}
#[salsa::database(QueryGroupStorage)]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
external_state: HashMap<u32, u32>,
on_event: Option<Box<dyn Fn(salsa::Event)>>,
}
impl salsa::Database for Database {
fn salsa_event(&self, event: salsa::Event) {
if let Some(cb) = &self.on_event {
cb(event)
}
}
}
impl AsRef<HashMap<u32, u32>> for Database {
fn as_ref(&self) -> &HashMap<u32, u32> {
&self.external_state
}
}
#[test]
fn on_demand_input_works() {
let mut db = Database::default();
db.external_state.insert(1, 10);
assert_eq!(db.b(1), 10);
assert_eq!(db.a(1), 10);
// We changed external state, but haven't signaled about this yet,
// so we expect to see the old answer
db.external_state.insert(1, 92);
assert_eq!(db.b(1), 10);
assert_eq!(db.a(1), 10);
AQuery.in_db_mut(&mut db).invalidate(&1);
assert_eq!(db.b(1), 92);
assert_eq!(db.a(1), 92);
}
#[test]
fn on_demand_input_durability() {
let mut db = Database::default();
db.external_state.insert(1, 10);
db.external_state.insert(2, 20);
assert_eq!(db.b(1), 10);
assert_eq!(db.b(2), 20);
let validated = Rc::new(Cell::new(0));
db.on_event = Some(Box::new({
let validated = Rc::clone(&validated);
move |event| match event.kind {
salsa::EventKind::DidValidateMemoizedValue { .. } => validated.set(validated.get() + 1),
_ => (),
}
}));
db.salsa_runtime_mut().synthetic_write(Durability::LOW);
validated.set(0);
assert_eq!(db.c(1), 10);
assert_eq!(db.c(2), 20);
assert_eq!(validated.get(), 2);
db.salsa_runtime_mut().synthetic_write(Durability::HIGH);
validated.set(0);
assert_eq!(db.c(1), 10);
assert_eq!(db.c(2), 20);
assert_eq!(validated.get(), 4);
}