salsa/tests/cycle_accumulate.rs
Micha Reiser f7eea9d69b
Use DatabaseKey for interned events (#813)
* Use `DatabaseKey` for interned events

* Tame clippy
2025-04-24 10:38:46 +00:00

324 lines
8.8 KiB
Rust

use std::collections::HashSet;
mod common;
use common::{LogDatabase, LoggerDatabase};
use expect_test::expect;
use salsa::{Accumulator, Setter};
use test_log::test;
#[salsa::input(debug)]
struct File {
name: String,
dependencies: Vec<File>,
issues: Vec<u32>,
}
#[salsa::accumulator]
#[derive(Debug)]
struct Diagnostic(#[allow(dead_code)] String);
#[salsa::tracked(cycle_fn = cycle_fn, cycle_initial = cycle_initial)]
fn check_file(db: &dyn LogDatabase, file: File) -> Vec<u32> {
db.push_log(format!(
"check_file(name = {}, issues = {:?})",
file.name(db),
file.issues(db)
));
let mut collected_issues = HashSet::<u32>::from_iter(file.issues(db).iter().copied());
for dep in file.dependencies(db) {
let issues = check_file(db, dep);
collected_issues.extend(issues);
}
let mut sorted_issues = collected_issues.iter().copied().collect::<Vec<_>>();
sorted_issues.sort();
for issue in &sorted_issues {
Diagnostic(format!("file {}: issue {}", file.name(db), issue)).accumulate(db);
}
sorted_issues
}
fn cycle_initial(_db: &dyn LogDatabase, _file: File) -> Vec<u32> {
vec![]
}
fn cycle_fn(
_db: &dyn LogDatabase,
_value: &[u32],
_count: u32,
_file: File,
) -> salsa::CycleRecoveryAction<Vec<u32>> {
salsa::CycleRecoveryAction::Iterate
}
#[test]
fn accumulate_once() {
let db = LoggerDatabase::default();
let file = File::new(&db, "fn".to_string(), vec![], vec![1]);
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file);
db.assert_logs(expect![[r#"
[
"check_file(name = fn, issues = [1])",
]"#]]);
expect![[r#"
[
Diagnostic(
"file fn: issue 1",
),
]"#]]
.assert_eq(&format!("{diagnostics:#?}"));
}
#[test]
fn accumulate_with_dep() {
let db = LoggerDatabase::default();
let file_a = File::new(&db, "file_a".to_string(), vec![], vec![1]);
let file_b = File::new(&db, "file_b".to_string(), vec![file_a], vec![2]);
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file_b);
db.assert_logs(expect![[r#"
[
"check_file(name = file_b, issues = [2])",
"check_file(name = file_a, issues = [1])",
]"#]]);
expect![[r#"
[
Diagnostic(
"file file_b: issue 1",
),
Diagnostic(
"file file_b: issue 2",
),
Diagnostic(
"file file_a: issue 1",
),
]"#]]
.assert_eq(&format!("{diagnostics:#?}"));
}
#[test]
fn accumulate_with_cycle() {
let mut db = LoggerDatabase::default();
let file_a = File::new(&db, "file_a".to_string(), vec![], vec![1]);
let file_b = File::new(&db, "file_b".to_string(), vec![file_a], vec![2]);
file_a.set_dependencies(&mut db).to(vec![file_b]);
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file_b);
db.assert_logs(expect![[r#"
[
"check_file(name = file_b, issues = [2])",
"check_file(name = file_a, issues = [1])",
"check_file(name = file_b, issues = [2])",
"check_file(name = file_a, issues = [1])",
]"#]]);
expect![[r#"
[
Diagnostic(
"file file_b: issue 1",
),
Diagnostic(
"file file_b: issue 2",
),
Diagnostic(
"file file_a: issue 1",
),
Diagnostic(
"file file_a: issue 2",
),
]"#]]
.assert_eq(&format!("{diagnostics:#?}"));
}
#[test]
fn accumulate_with_cycle_second_revision() {
let mut db = LoggerDatabase::default();
let file_a = File::new(&db, "file_a".to_string(), vec![], vec![1]);
let file_b = File::new(&db, "file_b".to_string(), vec![file_a], vec![2]);
file_a.set_dependencies(&mut db).to(vec![file_b]);
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file_b);
db.assert_logs(expect![[r#"
[
"check_file(name = file_b, issues = [2])",
"check_file(name = file_a, issues = [1])",
"check_file(name = file_b, issues = [2])",
"check_file(name = file_a, issues = [1])",
]"#]]);
expect![[r#"
[
Diagnostic(
"file file_b: issue 1",
),
Diagnostic(
"file file_b: issue 2",
),
Diagnostic(
"file file_a: issue 1",
),
Diagnostic(
"file file_a: issue 2",
),
]"#]]
.assert_eq(&format!("{diagnostics:#?}"));
file_b.set_issues(&mut db).to(vec![2, 3]);
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file_a);
db.assert_logs(expect![[r#"
[
"check_file(name = file_b, issues = [2, 3])",
"check_file(name = file_a, issues = [1])",
"check_file(name = file_a, issues = [1])",
"check_file(name = file_b, issues = [2, 3])",
]"#]]);
expect![[r#"
[
Diagnostic(
"file file_a: issue 1",
),
Diagnostic(
"file file_a: issue 2",
),
Diagnostic(
"file file_a: issue 3",
),
Diagnostic(
"file file_b: issue 1",
),
Diagnostic(
"file file_b: issue 2",
),
Diagnostic(
"file file_b: issue 3",
),
]"#]]
.assert_eq(&format!("{diagnostics:#?}"));
}
#[test]
fn accumulate_add_cycle() {
let mut db = LoggerDatabase::default();
let file_a = File::new(&db, "file_a".to_string(), vec![], vec![1]);
let file_b = File::new(&db, "file_b".to_string(), vec![file_a], vec![2]);
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file_b);
db.assert_logs(expect![[r#"
[
"check_file(name = file_b, issues = [2])",
"check_file(name = file_a, issues = [1])",
]"#]]);
expect![[r#"
[
Diagnostic(
"file file_b: issue 1",
),
Diagnostic(
"file file_b: issue 2",
),
Diagnostic(
"file file_a: issue 1",
),
]"#]]
.assert_eq(&format!("{diagnostics:#?}"));
file_a.set_dependencies(&mut db).to(vec![file_b]);
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file_a);
db.assert_logs(expect![[r#"
[
"check_file(name = file_a, issues = [1])",
"check_file(name = file_b, issues = [2])",
"check_file(name = file_a, issues = [1])",
"check_file(name = file_b, issues = [2])",
]"#]]);
expect![[r#"
[
Diagnostic(
"file file_a: issue 1",
),
Diagnostic(
"file file_a: issue 2",
),
Diagnostic(
"file file_b: issue 1",
),
Diagnostic(
"file file_b: issue 2",
),
]"#]]
.assert_eq(&format!("{diagnostics:#?}"));
}
#[test]
fn accumulate_remove_cycle() {
let mut db = LoggerDatabase::default();
let file_a = File::new(&db, "file_a".to_string(), vec![], vec![1]);
let file_b = File::new(&db, "file_b".to_string(), vec![file_a], vec![2]);
file_a.set_dependencies(&mut db).to(vec![file_b]);
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file_b);
db.assert_logs(expect![[r#"
[
"check_file(name = file_b, issues = [2])",
"check_file(name = file_a, issues = [1])",
"check_file(name = file_b, issues = [2])",
"check_file(name = file_a, issues = [1])",
]"#]]);
expect![[r#"
[
Diagnostic(
"file file_b: issue 1",
),
Diagnostic(
"file file_b: issue 2",
),
Diagnostic(
"file file_a: issue 1",
),
Diagnostic(
"file file_a: issue 2",
),
]"#]]
.assert_eq(&format!("{diagnostics:#?}"));
file_a.set_dependencies(&mut db).to(vec![]);
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file_b);
db.assert_logs(expect![[r#"
[
"check_file(name = file_a, issues = [1])",
"check_file(name = file_b, issues = [2])",
]"#]]);
expect![[r#"
[
Diagnostic(
"file file_b: issue 1",
),
Diagnostic(
"file file_b: issue 2",
),
Diagnostic(
"file file_a: issue 1",
),
]"#]]
.assert_eq(&format!("{diagnostics:#?}"));
}