|  | 
|  | 1 | +//! Tests that `force_invalidation_on_cache_eviction` causes dependent queries | 
|  | 2 | +//! to be recomputed if our memo is missing, even if our result is unchanged. | 
|  | 3 | +
 | 
|  | 4 | +mod common; | 
|  | 5 | +use common::LogDatabase; | 
|  | 6 | +use expect_test::expect; | 
|  | 7 | +use salsa::Setter; | 
|  | 8 | +use test_log::test; | 
|  | 9 | + | 
|  | 10 | +#[salsa::input(debug)] | 
|  | 11 | +struct MyInput { | 
|  | 12 | +    field: u32, | 
|  | 13 | +} | 
|  | 14 | + | 
|  | 15 | +#[salsa::tracked] | 
|  | 16 | +struct MyTracked<'db> { | 
|  | 17 | +    field: u32, | 
|  | 18 | +} | 
|  | 19 | + | 
|  | 20 | +#[salsa::tracked(force_invalidation_on_cache_eviction, lru = 1)] | 
|  | 21 | +fn intermediate_result(db: &dyn LogDatabase, input: MyInput) -> MyTracked<'_> { | 
|  | 22 | +    db.push_log(format!("intermediate_result({input:?})")); | 
|  | 23 | +    MyTracked::new(db, input.field(db) / 2) | 
|  | 24 | +} | 
|  | 25 | + | 
|  | 26 | +#[salsa::tracked] | 
|  | 27 | +fn final_result(db: &dyn LogDatabase, input: MyInput) -> u32 { | 
|  | 28 | +    db.push_log(format!("final_result({input:?})")); | 
|  | 29 | +    intermediate_result(db, input).field(db) * 2 | 
|  | 30 | +} | 
|  | 31 | + | 
|  | 32 | +#[test] | 
|  | 33 | +fn execute() { | 
|  | 34 | +    let mut db = common::LoggerDatabase::default(); | 
|  | 35 | + | 
|  | 36 | +    let input = MyInput::new(&db, 22); | 
|  | 37 | +    assert_eq!(final_result(&db, input), 22); | 
|  | 38 | +    // on first run, both intermediate and final results were computed | 
|  | 39 | +    db.assert_logs(expect![[r#" | 
|  | 40 | +        [ | 
|  | 41 | +            "final_result(MyInput { [salsa id]: Id(0), field: 22 })", | 
|  | 42 | +            "intermediate_result(MyInput { [salsa id]: Id(0), field: 22 })", | 
|  | 43 | +        ]"#]]); | 
|  | 44 | + | 
|  | 45 | +    input.set_field(&mut db).to(23); | 
|  | 46 | +    assert_eq!(final_result(&db, input), 22); | 
|  | 47 | +    // intermediate result was the same, so final result was not recomputed | 
|  | 48 | +    db.assert_logs(expect![[r#" | 
|  | 49 | +        [ | 
|  | 50 | +            "intermediate_result(MyInput { [salsa id]: Id(0), field: 23 })", | 
|  | 51 | +        ]"#]]); | 
|  | 52 | + | 
|  | 53 | +    assert_eq!( | 
|  | 54 | +        intermediate_result(&db, MyInput::new(&db, 10)).field(&db), | 
|  | 55 | +        5 | 
|  | 56 | +    ); | 
|  | 57 | +    input.set_field(&mut db).to(23); | 
|  | 58 | +    // an intermediate result for a different input evicted the original memo | 
|  | 59 | +    // from the cache (because query's lru = 1) | 
|  | 60 | +    db.assert_logs(expect![[r#" | 
|  | 61 | +        [ | 
|  | 62 | +            "intermediate_result(MyInput { [salsa id]: Id(1), field: 10 })", | 
|  | 63 | +        ]"#]]); | 
|  | 64 | + | 
|  | 65 | +    assert_eq!(final_result(&db, input), 22); | 
|  | 66 | +    // now, despite unchanged intermediate result, final result was recomputed | 
|  | 67 | +    // (because intermediate query is `force_invalidation_on_cache_eviction`) | 
|  | 68 | +    db.assert_logs(expect![[r#" | 
|  | 69 | +        [ | 
|  | 70 | +            "final_result(MyInput { [salsa id]: Id(0), field: 23 })", | 
|  | 71 | +            "intermediate_result(MyInput { [salsa id]: Id(0), field: 23 })", | 
|  | 72 | +        ]"#]]); | 
|  | 73 | +} | 
0 commit comments