mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-01-22 21:05:11 +00:00
Support on-demand inputs
This adds initial support for on-demand inputs by allowing new inputs to be created with only a shared reference to the database. This allows creating new inputs during a revision and therefore from inside tracked functions.
This commit is contained in:
parent
f8f6dbd349
commit
5b8464c4f9
29 changed files with 108 additions and 107 deletions
|
@ -33,8 +33,8 @@ mod parser;
|
||||||
mod type_check;
|
mod type_check;
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let mut db = db::Database::default();
|
let db = db::Database::default();
|
||||||
let source_program = SourceProgram::new(&mut db, String::new());
|
let source_program = SourceProgram::new(&db, String::new());
|
||||||
compile::compile(&db, source_program);
|
compile::compile(&db, source_program);
|
||||||
let diagnostics = compile::compile::accumulated::<Diagnostics>(&db, source_program);
|
let diagnostics = compile::compile::accumulated::<Diagnostics>(&db, source_program);
|
||||||
eprintln!("{diagnostics:?}");
|
eprintln!("{diagnostics:?}");
|
||||||
|
|
|
@ -355,10 +355,10 @@ fn parse_string(source_text: &str) -> String {
|
||||||
use salsa::debug::DebugWithDb;
|
use salsa::debug::DebugWithDb;
|
||||||
|
|
||||||
// Create the database
|
// Create the database
|
||||||
let mut db = crate::db::Database::default();
|
let db = crate::db::Database::default();
|
||||||
|
|
||||||
// Create the source program
|
// Create the source program
|
||||||
let source_program = SourceProgram::new(&mut db, source_text.to_string());
|
let source_program = SourceProgram::new(&db, source_text.to_string());
|
||||||
|
|
||||||
// Invoke the parser
|
// Invoke the parser
|
||||||
let statements = parse_statements(&db, source_program);
|
let statements = parse_statements(&db, source_program);
|
||||||
|
|
|
@ -97,7 +97,7 @@ fn check_string(
|
||||||
let mut db = Database::default().enable_logging();
|
let mut db = Database::default().enable_logging();
|
||||||
|
|
||||||
// Create the source program
|
// Create the source program
|
||||||
let source_program = SourceProgram::new(&mut db, source_text.to_string());
|
let source_program = SourceProgram::new(&db, source_text.to_string());
|
||||||
|
|
||||||
// Invoke the parser
|
// Invoke the parser
|
||||||
let program = parse_statements(&db, source_program);
|
let program = parse_statements(&db, source_program);
|
||||||
|
|
|
@ -130,10 +130,10 @@ impl InputStruct {
|
||||||
|
|
||||||
let constructor: syn::ImplItemMethod = if singleton {
|
let constructor: syn::ImplItemMethod = if singleton {
|
||||||
parse_quote! {
|
parse_quote! {
|
||||||
pub fn #constructor_name(__db: &mut #db_dyn_ty, #(#field_names: #field_tys,)*) -> Self
|
pub fn #constructor_name(__db: &#db_dyn_ty, #(#field_names: #field_tys,)*) -> Self
|
||||||
{
|
{
|
||||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar_mut(__db);
|
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
|
||||||
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient_mut(__jar);
|
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient(__jar);
|
||||||
let __id = __ingredients.#input_index.new_singleton_input(__runtime);
|
let __id = __ingredients.#input_index.new_singleton_input(__runtime);
|
||||||
#(
|
#(
|
||||||
__ingredients.#field_indices.store(__runtime, __id, #field_names, salsa::Durability::LOW);
|
__ingredients.#field_indices.store(__runtime, __id, #field_names, salsa::Durability::LOW);
|
||||||
|
@ -143,10 +143,10 @@ impl InputStruct {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
parse_quote! {
|
parse_quote! {
|
||||||
pub fn #constructor_name(__db: &mut #db_dyn_ty, #(#field_names: #field_tys,)*) -> Self
|
pub fn #constructor_name(__db: &#db_dyn_ty, #(#field_names: #field_tys,)*) -> Self
|
||||||
{
|
{
|
||||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar_mut(__db);
|
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
|
||||||
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient_mut(__jar);
|
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient(__jar);
|
||||||
let __id = __ingredients.#input_index.new_input(__runtime);
|
let __id = __ingredients.#input_index.new_input(__runtime);
|
||||||
#(
|
#(
|
||||||
__ingredients.#field_indices.store(__runtime, __id, #field_names, salsa::Durability::LOW);
|
__ingredients.#field_indices.store(__runtime, __id, #field_names, salsa::Durability::LOW);
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use std::fmt;
|
use std::{
|
||||||
|
fmt,
|
||||||
|
sync::atomic::{AtomicU32, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cycle::CycleRecoveryStrategy,
|
cycle::CycleRecoveryStrategy,
|
||||||
|
@ -16,7 +19,7 @@ where
|
||||||
Id: InputId,
|
Id: InputId,
|
||||||
{
|
{
|
||||||
ingredient_index: IngredientIndex,
|
ingredient_index: IngredientIndex,
|
||||||
counter: u32,
|
counter: AtomicU32,
|
||||||
debug_name: &'static str,
|
debug_name: &'static str,
|
||||||
_phantom: std::marker::PhantomData<Id>,
|
_phantom: std::marker::PhantomData<Id>,
|
||||||
}
|
}
|
||||||
|
@ -41,24 +44,20 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_input(&mut self, _runtime: &mut Runtime) -> Id {
|
pub fn new_input(&self, _runtime: &Runtime) -> Id {
|
||||||
let next_id = self.counter;
|
let next_id = self.counter.fetch_add(1, Ordering::Relaxed);
|
||||||
self.counter += 1;
|
|
||||||
Id::from_id(crate::Id::from_u32(next_id))
|
Id::from_id(crate::Id::from_u32(next_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_singleton_input(&mut self, _runtime: &mut Runtime) -> Id {
|
pub fn new_singleton_input(&self, _runtime: &Runtime) -> Id {
|
||||||
if self.counter >= 1 {
|
// There's only one singleton so record that we've created it
|
||||||
// already exists
|
// and return the only id.
|
||||||
Id::from_id(crate::Id::from_u32(self.counter - 1))
|
self.counter.store(1, Ordering::Relaxed);
|
||||||
} else {
|
Id::from_id(crate::Id::from_u32(0))
|
||||||
self.new_input(_runtime)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_singleton_input(&self, _runtime: &Runtime) -> Option<Id> {
|
pub fn get_singleton_input(&self, _runtime: &Runtime) -> Option<Id> {
|
||||||
(self.counter > 0)
|
(self.counter.load(Ordering::Relaxed) > 0).then(|| Id::from_id(crate::Id::from_u32(0)))
|
||||||
.then(|| Id::from_id(crate::Id::from_id(crate::Id::from_u32(self.counter - 1))))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::key::DependencyIndex;
|
||||||
use crate::runtime::local_state::QueryOrigin;
|
use crate::runtime::local_state::QueryOrigin;
|
||||||
use crate::runtime::StampedValue;
|
use crate::runtime::StampedValue;
|
||||||
use crate::{AsId, DatabaseKeyIndex, Durability, Id, IngredientIndex, Revision, Runtime};
|
use crate::{AsId, DatabaseKeyIndex, Durability, Id, IngredientIndex, Revision, Runtime};
|
||||||
use rustc_hash::FxHashMap;
|
use dashmap::DashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ use std::hash::Hash;
|
||||||
/// This makes the implementation considerably simpler.
|
/// This makes the implementation considerably simpler.
|
||||||
pub struct InputFieldIngredient<K, F> {
|
pub struct InputFieldIngredient<K, F> {
|
||||||
index: IngredientIndex,
|
index: IngredientIndex,
|
||||||
map: FxHashMap<K, StampedValue<F>>,
|
map: DashMap<K, StampedValue<F>>,
|
||||||
debug_name: &'static str,
|
debug_name: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,13 +31,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn store(
|
pub fn store(&self, runtime: &Runtime, key: K, value: F, durability: Durability) -> Option<F> {
|
||||||
&mut self,
|
|
||||||
runtime: &mut Runtime,
|
|
||||||
key: K,
|
|
||||||
value: F,
|
|
||||||
durability: Durability,
|
|
||||||
) -> Option<F> {
|
|
||||||
let revision = runtime.current_revision();
|
let revision = runtime.current_revision();
|
||||||
let stamped_value = StampedValue {
|
let stamped_value = StampedValue {
|
||||||
value,
|
value,
|
||||||
|
@ -52,12 +46,12 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetch(&self, runtime: &Runtime, key: K) -> &F {
|
pub fn fetch<'db>(&'db self, runtime: &'db Runtime, key: K) -> &F {
|
||||||
let StampedValue {
|
let StampedValue {
|
||||||
value,
|
value,
|
||||||
durability,
|
durability,
|
||||||
changed_at,
|
changed_at,
|
||||||
} = self.map.get(&key).unwrap();
|
} = &*self.map.get(&key).unwrap();
|
||||||
|
|
||||||
runtime.report_tracked_read(
|
runtime.report_tracked_read(
|
||||||
self.database_key_index(key).into(),
|
self.database_key_index(key).into(),
|
||||||
|
@ -65,7 +59,9 @@ where
|
||||||
*changed_at,
|
*changed_at,
|
||||||
);
|
);
|
||||||
|
|
||||||
value
|
// SAFETY:
|
||||||
|
// * Values are only removed or altered when we have `&mut self`
|
||||||
|
unsafe { transmute_lifetime(self, value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn database_key_index(&self, key: K) -> DatabaseKeyIndex {
|
fn database_key_index(&self, key: K) -> DatabaseKeyIndex {
|
||||||
|
@ -76,6 +72,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns `u` but with the lifetime of `t`.
|
||||||
|
//
|
||||||
|
// Safe if you know that data at `u` will remain shared
|
||||||
|
// until the reference `t` expires.
|
||||||
|
unsafe fn transmute_lifetime<'t, 'u, T, U>(_t: &'t T, u: &'u U) -> &'t U {
|
||||||
|
std::mem::transmute(u)
|
||||||
|
}
|
||||||
|
|
||||||
impl<DB: ?Sized, K, F> Ingredient<DB> for InputFieldIngredient<K, F>
|
impl<DB: ?Sized, K, F> Ingredient<DB> for InputFieldIngredient<K, F>
|
||||||
where
|
where
|
||||||
K: AsId,
|
K: AsId,
|
||||||
|
|
|
@ -65,8 +65,8 @@ impl HasLogger for Database {
|
||||||
fn test1() {
|
fn test1() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
let l0 = List::new(&mut db, 1, None);
|
let l0 = List::new(&db, 1, None);
|
||||||
let l1 = List::new(&mut db, 10, Some(l0));
|
let l1 = List::new(&db, 10, Some(l0));
|
||||||
|
|
||||||
compute(&db, l1);
|
compute(&db, l1);
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
|
|
|
@ -69,8 +69,8 @@ impl HasLogger for Database {
|
||||||
fn test1() {
|
fn test1() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
let l1 = List::new(&mut db, 1, None);
|
let l1 = List::new(&db, 1, None);
|
||||||
let l2 = List::new(&mut db, 2, Some(l1));
|
let l2 = List::new(&db, 2, Some(l1));
|
||||||
|
|
||||||
assert_eq!(compute(&db, l2), 2);
|
assert_eq!(compute(&db, l2), 2);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
|
|
|
@ -64,8 +64,8 @@ impl HasLogger for Database {
|
||||||
fn test1() {
|
fn test1() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
let l1 = List::new(&mut db, 1, None);
|
let l1 = List::new(&db, 1, None);
|
||||||
let l2 = List::new(&mut db, 2, Some(l1));
|
let l2 = List::new(&db, 2, Some(l1));
|
||||||
|
|
||||||
assert_eq!(compute(&db, l2), 2);
|
assert_eq!(compute(&db, l2), 2);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
|
|
|
@ -85,7 +85,7 @@ fn accumulate_once() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
// Just call accumulate on a base input to see what happens.
|
// Just call accumulate on a base input to see what happens.
|
||||||
let input = MyInput::new(&mut db, 2, 3);
|
let input = MyInput::new(&db, 2, 3);
|
||||||
let logs = push_logs::accumulated::<Logs>(&db, input);
|
let logs = push_logs::accumulated::<Logs>(&db, input);
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
[
|
[
|
||||||
|
@ -109,7 +109,7 @@ fn change_a_and_reaccumulate() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
// Accumulate logs for `a = 2` and `b = 3`
|
// Accumulate logs for `a = 2` and `b = 3`
|
||||||
let input = MyInput::new(&mut db, 2, 3);
|
let input = MyInput::new(&db, 2, 3);
|
||||||
let logs = push_logs::accumulated::<Logs>(&db, input);
|
let logs = push_logs::accumulated::<Logs>(&db, input);
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
[
|
[
|
||||||
|
@ -148,7 +148,7 @@ fn get_a_logs_after_changing_b() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
// Invoke `push_a_logs` with `a = 2` and `b = 3` (but `b` doesn't matter)
|
// Invoke `push_a_logs` with `a = 2` and `b = 3` (but `b` doesn't matter)
|
||||||
let input = MyInput::new(&mut db, 2, 3);
|
let input = MyInput::new(&db, 2, 3);
|
||||||
let logs = push_a_logs::accumulated::<Logs>(&db, input);
|
let logs = push_a_logs::accumulated::<Logs>(&db, input);
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
[
|
[
|
||||||
|
|
|
@ -183,7 +183,7 @@ fn extract_cycle(f: impl FnOnce() + UnwindSafe) -> salsa::Cycle {
|
||||||
#[test]
|
#[test]
|
||||||
fn cycle_memoized() {
|
fn cycle_memoized() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
let input = MyInput::new(&mut db);
|
let input = MyInput::new(&db);
|
||||||
let cycle = extract_cycle(|| memoized_a(&db, input));
|
let cycle = extract_cycle(|| memoized_a(&db, input));
|
||||||
let expected = expect![[r#"
|
let expected = expect![[r#"
|
||||||
[
|
[
|
||||||
|
@ -197,7 +197,7 @@ fn cycle_memoized() {
|
||||||
#[test]
|
#[test]
|
||||||
fn cycle_volatile() {
|
fn cycle_volatile() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
let input = MyInput::new(&mut db);
|
let input = MyInput::new(&db);
|
||||||
let cycle = extract_cycle(|| volatile_a(&db, input));
|
let cycle = extract_cycle(|| volatile_a(&db, input));
|
||||||
let expected = expect![[r#"
|
let expected = expect![[r#"
|
||||||
[
|
[
|
||||||
|
@ -215,7 +215,7 @@ fn expect_cycle() {
|
||||||
// +-----+
|
// +-----+
|
||||||
|
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
|
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
|
||||||
assert!(cycle_a(&db, abc).is_err());
|
assert!(cycle_a(&db, abc).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +225,7 @@ fn inner_cycle() {
|
||||||
// ^ |
|
// ^ |
|
||||||
// +-----+
|
// +-----+
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::A, CycleQuery::B);
|
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::A, CycleQuery::B);
|
||||||
let err = cycle_c(&db, abc);
|
let err = cycle_c(&db, abc);
|
||||||
assert!(err.is_err());
|
assert!(err.is_err());
|
||||||
let expected = expect![[r#"
|
let expected = expect![[r#"
|
||||||
|
@ -243,7 +243,7 @@ fn cycle_revalidate() {
|
||||||
// ^ |
|
// ^ |
|
||||||
// +-----+
|
// +-----+
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
|
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
|
||||||
assert!(cycle_a(&db, abc).is_err());
|
assert!(cycle_a(&db, abc).is_err());
|
||||||
abc.set_b(&mut db).to(CycleQuery::A); // same value as default
|
abc.set_b(&mut db).to(CycleQuery::A); // same value as default
|
||||||
assert!(cycle_a(&db, abc).is_err());
|
assert!(cycle_a(&db, abc).is_err());
|
||||||
|
@ -255,7 +255,7 @@ fn cycle_recovery_unchanged_twice() {
|
||||||
// ^ |
|
// ^ |
|
||||||
// +-----+
|
// +-----+
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
|
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
|
||||||
assert!(cycle_a(&db, abc).is_err());
|
assert!(cycle_a(&db, abc).is_err());
|
||||||
|
|
||||||
abc.set_c(&mut db).to(CycleQuery::A); // force new revision
|
abc.set_c(&mut db).to(CycleQuery::A); // force new revision
|
||||||
|
@ -267,7 +267,7 @@ fn cycle_appears() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
// A --> B
|
// A --> B
|
||||||
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::None, CycleQuery::None);
|
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::None, CycleQuery::None);
|
||||||
assert!(cycle_a(&db, abc).is_ok());
|
assert!(cycle_a(&db, abc).is_ok());
|
||||||
|
|
||||||
// A --> B
|
// A --> B
|
||||||
|
@ -284,7 +284,7 @@ fn cycle_disappears() {
|
||||||
// A --> B
|
// A --> B
|
||||||
// ^ |
|
// ^ |
|
||||||
// +-----+
|
// +-----+
|
||||||
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
|
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
|
||||||
assert!(cycle_a(&db, abc).is_err());
|
assert!(cycle_a(&db, abc).is_err());
|
||||||
|
|
||||||
// A --> B
|
// A --> B
|
||||||
|
@ -334,7 +334,7 @@ fn cycle_mixed_1() {
|
||||||
// A --> B <-- C
|
// A --> B <-- C
|
||||||
// | ^
|
// | ^
|
||||||
// +-----+
|
// +-----+
|
||||||
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::C, CycleQuery::B);
|
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::C, CycleQuery::B);
|
||||||
|
|
||||||
let expected = expect![[r#"
|
let expected = expect![[r#"
|
||||||
[
|
[
|
||||||
|
@ -354,7 +354,7 @@ fn cycle_mixed_2() {
|
||||||
// A --> B --> C
|
// A --> B --> C
|
||||||
// ^ |
|
// ^ |
|
||||||
// +-----------+
|
// +-----------+
|
||||||
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::C, CycleQuery::A);
|
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::C, CycleQuery::A);
|
||||||
let expected = expect![[r#"
|
let expected = expect![[r#"
|
||||||
[
|
[
|
||||||
"cycle_a(0)",
|
"cycle_a(0)",
|
||||||
|
@ -374,7 +374,7 @@ fn cycle_deterministic_order() {
|
||||||
// A --> B
|
// A --> B
|
||||||
// ^ |
|
// ^ |
|
||||||
// +-----+
|
// +-----+
|
||||||
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
|
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::A, CycleQuery::None);
|
||||||
(db, abc)
|
(db, abc)
|
||||||
};
|
};
|
||||||
let (db, abc) = f();
|
let (db, abc) = f();
|
||||||
|
@ -411,7 +411,7 @@ fn cycle_multiple() {
|
||||||
//
|
//
|
||||||
// Here, conceptually, B encounters a cycle with A and then
|
// Here, conceptually, B encounters a cycle with A and then
|
||||||
// recovers.
|
// recovers.
|
||||||
let abc = ABC::new(&mut db, CycleQuery::B, CycleQuery::AthenC, CycleQuery::A);
|
let abc = ABC::new(&db, CycleQuery::B, CycleQuery::AthenC, CycleQuery::A);
|
||||||
|
|
||||||
let c = cycle_c(&db, abc);
|
let c = cycle_c(&db, abc);
|
||||||
let b = cycle_b(&db, abc);
|
let b = cycle_b(&db, abc);
|
||||||
|
@ -446,7 +446,7 @@ fn cycle_recovery_set_but_not_participating() {
|
||||||
// A --> C -+
|
// A --> C -+
|
||||||
// ^ |
|
// ^ |
|
||||||
// +--+
|
// +--+
|
||||||
let abc = ABC::new(&mut db, CycleQuery::C, CycleQuery::None, CycleQuery::C);
|
let abc = ABC::new(&db, CycleQuery::C, CycleQuery::None, CycleQuery::C);
|
||||||
|
|
||||||
// Here we expect C to panic and A not to recover:
|
// Here we expect C to panic and A not to recover:
|
||||||
let r = extract_cycle(|| drop(cycle_a(&db, abc)));
|
let r = extract_cycle(|| drop(cycle_a(&db, abc)));
|
||||||
|
|
|
@ -36,13 +36,13 @@ impl Db for Database {}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn input() {
|
fn input() {
|
||||||
let mut db = Database::default();
|
let db = Database::default();
|
||||||
|
|
||||||
let input = MyInput::new(&mut db, 22);
|
let input = MyInput::new(&db, 22);
|
||||||
let not_salsa = NotSalsa {
|
let not_salsa = NotSalsa {
|
||||||
field: "it's salsa time".to_string(),
|
field: "it's salsa time".to_string(),
|
||||||
};
|
};
|
||||||
let complex_struct = ComplexStruct::new(&mut db, input, not_salsa);
|
let complex_struct = ComplexStruct::new(&db, input, not_salsa);
|
||||||
|
|
||||||
// default debug only includes identity fields
|
// default debug only includes identity fields
|
||||||
let actual = format!("{:?}", complex_struct.debug(&db));
|
let actual = format!("{:?}", complex_struct.debug(&db));
|
||||||
|
|
|
@ -91,7 +91,7 @@ fn basic() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
// Creates 3 tracked structs
|
// Creates 3 tracked structs
|
||||||
let input = MyInput::new(&mut db, 3);
|
let input = MyInput::new(&db, 3);
|
||||||
assert_eq!(final_result(&db, input), 2 * 2 + 2);
|
assert_eq!(final_result(&db, input), 2 * 2 + 2);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
[
|
[
|
||||||
|
|
|
@ -84,7 +84,7 @@ fn basic() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
// Creates 3 tracked structs
|
// Creates 3 tracked structs
|
||||||
let input = MyInput::new(&mut db, 3);
|
let input = MyInput::new(&db, 3);
|
||||||
assert_eq!(final_result(&db, input), 2 * 2 + 2);
|
assert_eq!(final_result(&db, input), 2 * 2 + 2);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
[
|
[
|
||||||
|
|
|
@ -74,7 +74,7 @@ fn execute() {
|
||||||
// intermediate results:
|
// intermediate results:
|
||||||
// x = (22 + 1) / 2 = 11
|
// x = (22 + 1) / 2 = 11
|
||||||
// y = 22 / 2 = 11
|
// y = 22 / 2 = 11
|
||||||
let input = MyInput::new(&mut db, 22);
|
let input = MyInput::new(&db, 22);
|
||||||
assert_eq!(final_result_depends_on_x(&db, input), 22);
|
assert_eq!(final_result_depends_on_x(&db, input), 22);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
[
|
[
|
||||||
|
|
|
@ -53,7 +53,7 @@ fn execute() {
|
||||||
// result_depends_on_y = y - 1
|
// result_depends_on_y = y - 1
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
let input = MyInput::new(&mut db, 22, 33);
|
let input = MyInput::new(&db, 22, 33);
|
||||||
assert_eq!(result_depends_on_x(&db, input), 23);
|
assert_eq!(result_depends_on_x(&db, input), 23);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
[
|
[
|
||||||
|
|
|
@ -54,7 +54,7 @@ impl HasLogger for Database {
|
||||||
fn execute() {
|
fn execute() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
let input = MyInput::new(&mut db, 22);
|
let input = MyInput::new(&db, 22);
|
||||||
assert_eq!(final_result(&db, input), 22);
|
assert_eq!(final_result(&db, input), 22);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
[
|
[
|
||||||
|
@ -85,7 +85,7 @@ fn execute() {
|
||||||
fn red_herring() {
|
fn red_herring() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
let input = MyInput::new(&mut db, 22);
|
let input = MyInput::new(&db, 22);
|
||||||
assert_eq!(final_result(&db, input), 22);
|
assert_eq!(final_result(&db, input), 22);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
[
|
[
|
||||||
|
@ -96,7 +96,7 @@ fn red_herring() {
|
||||||
// Create a distinct input and mutate it.
|
// Create a distinct input and mutate it.
|
||||||
// This will trigger a new revision in the database
|
// This will trigger a new revision in the database
|
||||||
// but shouldn't actually invalidate our existing ones.
|
// but shouldn't actually invalidate our existing ones.
|
||||||
let input2 = MyInput::new(&mut db, 44);
|
let input2 = MyInput::new(&db, 44);
|
||||||
input2.set_field(&mut db).to(66);
|
input2.set_field(&mut db).to(66);
|
||||||
|
|
||||||
// Re-run the query on the original input. Nothing re-executes!
|
// Re-run the query on the original input. Nothing re-executes!
|
||||||
|
|
|
@ -82,28 +82,26 @@ fn load_n_potatoes() -> usize {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lru_works() {
|
fn lru_works() {
|
||||||
let mut db = DatabaseImpl::default();
|
let db = DatabaseImpl::default();
|
||||||
assert_eq!(load_n_potatoes(), 0);
|
assert_eq!(load_n_potatoes(), 0);
|
||||||
|
|
||||||
for i in 0..128u32 {
|
for i in 0..128u32 {
|
||||||
let input = MyInput::new(&mut db, i);
|
let input = MyInput::new(&db, i);
|
||||||
let p = get_hot_potato(&db, input);
|
let p = get_hot_potato(&db, input);
|
||||||
assert_eq!(p.0, i)
|
assert_eq!(p.0, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new input to change the revision, and trigger the GC
|
// Create a new input to change the revision, and trigger the GC
|
||||||
MyInput::new(&mut db, 0);
|
MyInput::new(&db, 0);
|
||||||
assert_eq!(load_n_potatoes(), 32);
|
assert_eq!(load_n_potatoes(), 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lru_doesnt_break_volatile_queries() {
|
fn lru_doesnt_break_volatile_queries() {
|
||||||
let mut db = DatabaseImpl::default();
|
let db = DatabaseImpl::default();
|
||||||
|
|
||||||
// Create all inputs first, so that there are no revision changes among calls to `get_volatile`
|
// Create all inputs first, so that there are no revision changes among calls to `get_volatile`
|
||||||
let inputs: Vec<MyInput> = (0..128usize)
|
let inputs: Vec<MyInput> = (0..128usize).map(|i| MyInput::new(&db, i as u32)).collect();
|
||||||
.map(|i| MyInput::new(&mut db, i as u32))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Here, we check that we execute each volatile query at most once, despite
|
// Here, we check that we execute each volatile query at most once, despite
|
||||||
// LRU. That does mean that we have more values in DB than the LRU capacity,
|
// LRU. That does mean that we have more values in DB than the LRU capacity,
|
||||||
|
@ -118,10 +116,10 @@ fn lru_doesnt_break_volatile_queries() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lru_can_be_changed_at_runtime() {
|
fn lru_can_be_changed_at_runtime() {
|
||||||
let mut db = DatabaseImpl::default();
|
let db = DatabaseImpl::default();
|
||||||
assert_eq!(load_n_potatoes(), 0);
|
assert_eq!(load_n_potatoes(), 0);
|
||||||
|
|
||||||
let inputs: Vec<(u32, MyInput)> = (0..128).map(|i| (i, MyInput::new(&mut db, i))).collect();
|
let inputs: Vec<(u32, MyInput)> = (0..128).map(|i| (i, MyInput::new(&db, i))).collect();
|
||||||
|
|
||||||
for &(i, input) in inputs.iter() {
|
for &(i, input) in inputs.iter() {
|
||||||
let p = get_hot_potato(&db, input);
|
let p = get_hot_potato(&db, input);
|
||||||
|
@ -129,7 +127,7 @@ fn lru_can_be_changed_at_runtime() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new input to change the revision, and trigger the GC
|
// Create a new input to change the revision, and trigger the GC
|
||||||
MyInput::new(&mut db, 0);
|
MyInput::new(&db, 0);
|
||||||
assert_eq!(load_n_potatoes(), 32);
|
assert_eq!(load_n_potatoes(), 32);
|
||||||
|
|
||||||
get_hot_potato::set_lru_capacity(&db, 64);
|
get_hot_potato::set_lru_capacity(&db, 64);
|
||||||
|
@ -140,7 +138,7 @@ fn lru_can_be_changed_at_runtime() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new input to change the revision, and trigger the GC
|
// Create a new input to change the revision, and trigger the GC
|
||||||
MyInput::new(&mut db, 0);
|
MyInput::new(&db, 0);
|
||||||
assert_eq!(load_n_potatoes(), 64);
|
assert_eq!(load_n_potatoes(), 64);
|
||||||
|
|
||||||
// Special case: setting capacity to zero disables LRU
|
// Special case: setting capacity to zero disables LRU
|
||||||
|
@ -152,7 +150,7 @@ fn lru_can_be_changed_at_runtime() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new input to change the revision, and trigger the GC
|
// Create a new input to change the revision, and trigger the GC
|
||||||
MyInput::new(&mut db, 0);
|
MyInput::new(&db, 0);
|
||||||
assert_eq!(load_n_potatoes(), 128);
|
assert_eq!(load_n_potatoes(), 128);
|
||||||
|
|
||||||
drop(db);
|
drop(db);
|
||||||
|
@ -167,7 +165,7 @@ fn lru_keeps_dependency_info() {
|
||||||
// Invoke `get_hot_potato2` 33 times. This will (in turn) invoke
|
// Invoke `get_hot_potato2` 33 times. This will (in turn) invoke
|
||||||
// `get_hot_potato`, which will trigger LRU after 32 executions.
|
// `get_hot_potato`, which will trigger LRU after 32 executions.
|
||||||
let inputs: Vec<MyInput> = (0..(capacity + 1))
|
let inputs: Vec<MyInput> = (0..(capacity + 1))
|
||||||
.map(|i| MyInput::new(&mut db, i as u32))
|
.map(|i| MyInput::new(&db, i as u32))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for (i, input) in inputs.iter().enumerate() {
|
for (i, input) in inputs.iter().enumerate() {
|
||||||
|
|
|
@ -36,7 +36,7 @@ impl HasLogger for Database {
|
||||||
fn execute() {
|
fn execute() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
let input = MyInput::new(&mut db, "Hello".to_string());
|
let input = MyInput::new(&db, "Hello".to_string());
|
||||||
|
|
||||||
// Overwrite field with an empty String
|
// Overwrite field with an empty String
|
||||||
// and store the old value in my_string
|
// and store the old value in my_string
|
||||||
|
|
|
@ -92,10 +92,10 @@ fn recover_b2(db: &dyn Db, _cycle: &salsa::Cycle, key: MyInput) -> i32 {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute() {
|
fn execute() {
|
||||||
let mut db = Database::default();
|
let db = Database::default();
|
||||||
db.knobs().signal_on_will_block.set(3);
|
db.knobs().signal_on_will_block.set(3);
|
||||||
|
|
||||||
let input = MyInput::new(&mut db, 1);
|
let input = MyInput::new(&db, 1);
|
||||||
|
|
||||||
let thread_a = std::thread::spawn({
|
let thread_a = std::thread::spawn({
|
||||||
let db = db.snapshot();
|
let db = db.snapshot();
|
||||||
|
|
|
@ -87,10 +87,10 @@ fn recover_b3(db: &dyn Db, _cycle: &salsa::Cycle, key: MyInput) -> i32 {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute() {
|
fn execute() {
|
||||||
let mut db = Database::default();
|
let db = Database::default();
|
||||||
db.knobs().signal_on_will_block.set(3);
|
db.knobs().signal_on_will_block.set(3);
|
||||||
|
|
||||||
let input = MyInput::new(&mut db, 1);
|
let input = MyInput::new(&db, 1);
|
||||||
|
|
||||||
let thread_a = std::thread::spawn({
|
let thread_a = std::thread::spawn({
|
||||||
let db = db.snapshot();
|
let db = db.snapshot();
|
||||||
|
|
|
@ -43,10 +43,10 @@ pub(crate) fn b(db: &dyn Db, input: MyInput) -> i32 {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute() {
|
fn execute() {
|
||||||
let mut db = Database::default();
|
let db = Database::default();
|
||||||
db.knobs().signal_on_will_block.set(3);
|
db.knobs().signal_on_will_block.set(3);
|
||||||
|
|
||||||
let input = MyInput::new(&mut db, -1);
|
let input = MyInput::new(&db, -1);
|
||||||
|
|
||||||
let thread_a = std::thread::spawn({
|
let thread_a = std::thread::spawn({
|
||||||
let db = db.snapshot();
|
let db = db.snapshot();
|
||||||
|
|
|
@ -76,10 +76,10 @@ pub(crate) fn b2(db: &dyn Db, input: MyInput) -> i32 {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute() {
|
fn execute() {
|
||||||
let mut db = Database::default();
|
let db = Database::default();
|
||||||
db.knobs().signal_on_will_block.set(3);
|
db.knobs().signal_on_will_block.set(3);
|
||||||
|
|
||||||
let input = MyInput::new(&mut db, 1);
|
let input = MyInput::new(&db, 1);
|
||||||
|
|
||||||
let thread_a = std::thread::spawn({
|
let thread_a = std::thread::spawn({
|
||||||
let db = db.snapshot();
|
let db = db.snapshot();
|
||||||
|
|
|
@ -56,6 +56,6 @@ impl Db for Database {}
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn execute_when_specified() {
|
fn execute_when_specified() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
let input = MyInput::new(&mut db, 22);
|
let input = MyInput::new(&db, 22);
|
||||||
let tracked = tracked_fn(&db, input);
|
let tracked = tracked_fn(&db, input);
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ impl HasLogger for Database {
|
||||||
fn test_run_0() {
|
fn test_run_0() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
let input = MyInput::new(&mut db, 0);
|
let input = MyInput::new(&db, 0);
|
||||||
assert_eq!(final_result(&db, input), 100);
|
assert_eq!(final_result(&db, input), 100);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
[
|
[
|
||||||
|
@ -110,7 +110,7 @@ fn test_run_0() {
|
||||||
fn test_run_5() {
|
fn test_run_5() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
let input = MyInput::new(&mut db, 5);
|
let input = MyInput::new(&db, 5);
|
||||||
assert_eq!(final_result(&db, input), 100);
|
assert_eq!(final_result(&db, input), 100);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
[
|
[
|
||||||
|
@ -131,7 +131,7 @@ fn test_run_5() {
|
||||||
fn test_run_10() {
|
fn test_run_10() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
let input = MyInput::new(&mut db, 10);
|
let input = MyInput::new(&db, 10);
|
||||||
assert_eq!(final_result(&db, input), 100);
|
assert_eq!(final_result(&db, input), 100);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
[
|
[
|
||||||
|
@ -155,7 +155,7 @@ fn test_run_10() {
|
||||||
fn test_run_20() {
|
fn test_run_20() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
let input = MyInput::new(&mut db, 20);
|
let input = MyInput::new(&db, 20);
|
||||||
assert_eq!(final_result(&db, input), 200);
|
assert_eq!(final_result(&db, input), 200);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
[
|
[
|
||||||
|
@ -183,7 +183,7 @@ fn test_run_0_then_5_then_20() {
|
||||||
//
|
//
|
||||||
// * `create_tracked` specifies `10` for `maybe_specified`
|
// * `create_tracked` specifies `10` for `maybe_specified`
|
||||||
// * final resuilt of `100` is derived by executing `read_maybe_specified`
|
// * final resuilt of `100` is derived by executing `read_maybe_specified`
|
||||||
let input = MyInput::new(&mut db, 0);
|
let input = MyInput::new(&db, 0);
|
||||||
assert_eq!(final_result(&db, input), 100);
|
assert_eq!(final_result(&db, input), 100);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
[
|
[
|
||||||
|
@ -254,7 +254,7 @@ fn test_run_0_then_5_then_10_then_20() {
|
||||||
//
|
//
|
||||||
// * `create_tracked` specifies `10` for `maybe_specified`
|
// * `create_tracked` specifies `10` for `maybe_specified`
|
||||||
// * final resuilt of `100` is derived by executing `read_maybe_specified`
|
// * final resuilt of `100` is derived by executing `read_maybe_specified`
|
||||||
let input = MyInput::new(&mut db, 0);
|
let input = MyInput::new(&db, 0);
|
||||||
assert_eq!(final_result(&db, input), 100);
|
assert_eq!(final_result(&db, input), 100);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
[
|
[
|
||||||
|
@ -342,7 +342,7 @@ fn test_run_0_then_5_then_10_then_20() {
|
||||||
fn test_run_5_then_20() {
|
fn test_run_5_then_20() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
let input = MyInput::new(&mut db, 5);
|
let input = MyInput::new(&db, 5);
|
||||||
assert_eq!(final_result(&db, input), 100);
|
assert_eq!(final_result(&db, input), 100);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
[
|
[
|
||||||
|
|
|
@ -30,6 +30,6 @@ fn execute() {
|
||||||
impl Db for Database {}
|
impl Db for Database {}
|
||||||
|
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
let input = MyInput::new(&mut db, 22);
|
let input = MyInput::new(&db, 22);
|
||||||
assert_eq!(tracked_fn(&db, input), 44);
|
assert_eq!(tracked_fn(&db, input), 44);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ impl Db for Database {}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn execute() {
|
fn execute() {
|
||||||
let mut db = Database::default();
|
let db = Database::default();
|
||||||
let input = MyInput::new(&mut db, 22);
|
let input = MyInput::new(&db, 22);
|
||||||
assert_eq!(tracked_fn(&db, input).field(&db), 44);
|
assert_eq!(tracked_fn(&db, input).field(&db), 44);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ impl Db for Database {}
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_when_specified() {
|
fn execute_when_specified() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
let input = MyInput::new(&mut db, 22);
|
let input = MyInput::new(&db, 22);
|
||||||
let tracked = tracked_fn(&db, input);
|
let tracked = tracked_fn(&db, input);
|
||||||
assert_eq!(tracked.field(&db), 44);
|
assert_eq!(tracked.field(&db), 44);
|
||||||
assert_eq!(tracked_fn_extra(&db, tracked), 2222);
|
assert_eq!(tracked_fn_extra(&db, tracked), 2222);
|
||||||
|
@ -53,7 +53,7 @@ fn execute_when_specified() {
|
||||||
#[test]
|
#[test]
|
||||||
fn execute_when_not_specified() {
|
fn execute_when_not_specified() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
let input = MyInput::new(&mut db, 0);
|
let input = MyInput::new(&db, 0);
|
||||||
let tracked = tracked_fn(&db, input);
|
let tracked = tracked_fn(&db, input);
|
||||||
assert_eq!(tracked.field(&db), 0);
|
assert_eq!(tracked.field(&db), 0);
|
||||||
assert_eq!(tracked_fn_extra(&db, tracked), 0);
|
assert_eq!(tracked_fn_extra(&db, tracked), 0);
|
||||||
|
|
|
@ -55,7 +55,7 @@ impl HasLogger for Database {
|
||||||
fn one_entity() {
|
fn one_entity() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
let input = MyInput::new(&mut db, 22);
|
let input = MyInput::new(&db, 22);
|
||||||
assert_eq!(final_result(&db, input), 22);
|
assert_eq!(final_result(&db, input), 22);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
[
|
[
|
||||||
|
@ -86,7 +86,7 @@ fn one_entity() {
|
||||||
fn red_herring() {
|
fn red_herring() {
|
||||||
let mut db = Database::default();
|
let mut db = Database::default();
|
||||||
|
|
||||||
let input = MyInput::new(&mut db, 22);
|
let input = MyInput::new(&db, 22);
|
||||||
assert_eq!(final_result(&db, input), 22);
|
assert_eq!(final_result(&db, input), 22);
|
||||||
db.assert_logs(expect![[r#"
|
db.assert_logs(expect![[r#"
|
||||||
[
|
[
|
||||||
|
@ -97,7 +97,7 @@ fn red_herring() {
|
||||||
// Create a distinct input and mutate it.
|
// Create a distinct input and mutate it.
|
||||||
// This will trigger a new revision in the database
|
// This will trigger a new revision in the database
|
||||||
// but shouldn't actually invalidate our existing ones.
|
// but shouldn't actually invalidate our existing ones.
|
||||||
let input2 = MyInput::new(&mut db, 44);
|
let input2 = MyInput::new(&db, 44);
|
||||||
input2.set_field(&mut db).to(66);
|
input2.set_field(&mut db).to(66);
|
||||||
|
|
||||||
// Re-run the query on the original input. Nothing re-executes!
|
// Re-run the query on the original input. Nothing re-executes!
|
||||||
|
|
Loading…
Reference in a new issue