#![cfg(feature = "inventory")]
mod common;
use common::{HasLogger, LogDatabase, Logger};
use expect_test::expect;
use salsa::{Setter, Storage};
#[salsa::tracked]
struct Output<'db> {
value: u32,
}
#[salsa::input]
struct InputValue {
value: u32,
}
#[salsa::tracked]
fn read_value<'db>(db: &'db dyn Db, output: Output<'db>) -> u32 {
output.value(db)
}
#[salsa::tracked]
fn query_a(db: &dyn Db, input: InputValue) -> u32 {
let val = query_b(db, input);
let output = Output::new(db, val);
let read = read_value(db, output);
assert_eq!(read, val);
query_d(db);
if val > 2 { val } else { val + input.value(db) }
}
#[salsa::tracked(cycle_initial=cycle_initial)]
fn query_b(db: &dyn Db, input: InputValue) -> u32 {
query_a(db, input)
}
fn cycle_initial(_db: &dyn Db, _id: salsa::Id, _input: InputValue) -> u32 {
0
}
#[salsa::tracked]
fn query_c(db: &dyn Db, input: InputValue) -> u32 {
input.value(db)
}
#[salsa::tracked]
fn query_d(db: &dyn Db) -> u32 {
db.get_input().map(|input| input.value(db)).unwrap_or(0)
}
trait HasOptionInput {
fn get_input(&self) -> Option<InputValue>;
fn set_input(&mut self, input: InputValue);
}
#[salsa::db]
trait Db: HasOptionInput + salsa::Database {}
#[salsa::db]
#[derive(Clone)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
input: Option<InputValue>,
}
impl HasLogger for Database {
fn logger(&self) -> &Logger {
&self.logger
}
}
impl Default for Database {
fn default() -> Self {
let logger = Logger::default();
Self {
storage: Storage::new(Some(Box::new({
let logger = logger.clone();
move |event| match event.kind {
salsa::EventKind::WillExecute { .. }
| salsa::EventKind::DidValidateMemoizedValue { .. } => {
logger.push_log(format!("salsa_event({:?})", event.kind));
}
salsa::EventKind::WillCheckCancellation => {}
_ => {
logger.push_log(format!("salsa_event({:?})", event.kind));
}
}
}))),
logger,
input: Default::default(),
}
}
}
impl HasOptionInput for Database {
fn get_input(&self) -> Option<InputValue> {
self.input
}
fn set_input(&mut self, input: InputValue) {
self.input.replace(input);
}
}
#[salsa::db]
impl salsa::Database for Database {}
#[salsa::db]
impl Db for Database {}
#[test_log::test]
fn single_revision() {
let db = Database::default();
let input = InputValue::new(&db, 1);
assert_eq!(query_b(&db, input), 3);
}
#[test_log::test]
fn revalidate_no_changes() {
let mut db = Database::default();
let ab_input = InputValue::new(&db, 1);
let c_input = InputValue::new(&db, 10);
assert_eq!(query_c(&db, c_input), 10);
assert_eq!(query_b(&db, ab_input), 3);
db.assert_logs_len(16);
c_input.set_value(&mut db).to(20);
assert_eq!(query_b(&db, ab_input), 3);
db.assert_logs(expect![[r#"
[
"salsa_event(DidSetCancellationFlag)",
"salsa_event(DidValidateMemoizedValue { database_key: read_value(Id(400)) })",
"salsa_event(DidValidateInternedValue { key: query_d::interned_arguments(Id(800)), revision: R2 })",
"salsa_event(DidValidateMemoizedValue { database_key: query_d(Id(800)) })",
"salsa_event(DidValidateMemoizedValue { database_key: read_value(Id(401)) })",
"salsa_event(DidValidateMemoizedValue { database_key: read_value(Id(402)) })",
"salsa_event(DidValidateMemoizedValue { database_key: read_value(Id(403)) })",
"salsa_event(DidValidateMemoizedValue { database_key: query_b(Id(0)) })",
]"#]]);
}
#[test_log::test]
fn revalidate_with_change_after_output_read() {
let mut db = Database::default();
let ab_input = InputValue::new(&db, 1);
let d_input = InputValue::new(&db, 10);
db.set_input(d_input);
assert_eq!(query_b(&db, ab_input), 3);
db.assert_logs_len(15);
d_input.set_value(&mut db).to(20);
assert_eq!(query_b(&db, ab_input), 3);
db.assert_logs(expect![[r#"
[
"salsa_event(DidSetCancellationFlag)",
"salsa_event(DidValidateMemoizedValue { database_key: read_value(Id(400)) })",
"salsa_event(DidValidateInternedValue { key: query_d::interned_arguments(Id(800)), revision: R2 })",
"salsa_event(WillExecute { database_key: query_d(Id(800)) })",
"salsa_event(WillExecute { database_key: query_b(Id(0)) })",
"salsa_event(WillExecute { database_key: query_a(Id(0)) })",
"salsa_event(WillDiscardStaleOutput { execute_key: query_a(Id(0)), output_key: Output(Id(401)) })",
"salsa_event(DidDiscard { key: Output(Id(401)) })",
"salsa_event(DidDiscard { key: read_value(Id(401)) })",
"salsa_event(WillDiscardStaleOutput { execute_key: query_a(Id(0)), output_key: Output(Id(402)) })",
"salsa_event(DidDiscard { key: Output(Id(402)) })",
"salsa_event(DidDiscard { key: read_value(Id(402)) })",
"salsa_event(WillDiscardStaleOutput { execute_key: query_a(Id(0)), output_key: Output(Id(403)) })",
"salsa_event(DidDiscard { key: Output(Id(403)) })",
"salsa_event(DidDiscard { key: read_value(Id(403)) })",
"salsa_event(WillIterateCycle { database_key: query_b(Id(0)), iteration_count: IterationCount(1) })",
"salsa_event(WillExecute { database_key: query_a(Id(0)) })",
"salsa_event(WillExecute { database_key: read_value(Id(401g1)) })",
"salsa_event(WillIterateCycle { database_key: query_b(Id(0)), iteration_count: IterationCount(2) })",
"salsa_event(WillExecute { database_key: query_a(Id(0)) })",
"salsa_event(WillExecute { database_key: read_value(Id(402g1)) })",
"salsa_event(WillIterateCycle { database_key: query_b(Id(0)), iteration_count: IterationCount(3) })",
"salsa_event(WillExecute { database_key: query_a(Id(0)) })",
"salsa_event(WillExecute { database_key: read_value(Id(403g1)) })",
"salsa_event(DidFinalizeCycle { database_key: query_b(Id(0)), iteration_count: IterationCount(3) })",
]"#]]);
}