use std::collections::HashMap;
use std::path::{Path, PathBuf};
use badness::incremental::{IncrementalDatabase, QueryKind, QueryLogEntry, SourceFile};
use badness::project::{IncludeKind, Project, ProjectMember, project_graph};
fn count_by_kind(entries: &[QueryLogEntry]) -> HashMap<QueryKind, usize> {
let mut counts = HashMap::new();
for entry in entries {
*counts.entry(entry.kind).or_insert(0) += 1;
}
counts
}
fn project_main_part<'db>(
db: &'db IncrementalDatabase,
main: SourceFile,
part: SourceFile,
) -> Project<'db> {
let mut members = vec![
ProjectMember {
file: main,
path: PathBuf::from("/proj/main.tex"),
},
ProjectMember {
file: part,
path: PathBuf::from("/proj/part.tex"),
},
];
members.sort_by(|a, b| a.path.cmp(&b.path));
Project::new(db, members)
}
fn main_part(main_text: &str, part_text: &str) -> (IncrementalDatabase, SourceFile, SourceFile) {
let mut db = IncrementalDatabase::default();
let main = db.upsert_file(Path::new("/proj/main.tex"), main_text.to_string());
let part = db.upsert_file(Path::new("/proj/part.tex"), part_text.to_string());
(db, main, part)
}
#[test]
fn graph_resolves_an_input_edge() {
let (db, main, part) = main_part("\\input{part}\n", "hello\n");
let graph = project_graph(&db, project_main_part(&db, main, part));
let out = graph.outgoing(Path::new("/proj/main.tex"));
assert_eq!(out.len(), 1);
assert_eq!(out[0].to, PathBuf::from("/proj/part.tex"));
assert_eq!(out[0].kind, IncludeKind::Input);
assert_eq!(
graph.included_by(Path::new("/proj/part.tex")),
&[PathBuf::from("/proj/main.tex")]
);
assert!(graph.unresolved().is_empty());
}
#[test]
fn body_edit_does_not_rebuild_graph() {
let (mut db, main, part) = main_part("\\input{part}\n", "hello\n");
let _ = project_graph(&db, project_main_part(&db, main, part));
db.clear_query_log();
db.set_file_text(part, "hello world\n");
let _ = project_graph(&db, project_main_part(&db, main, part));
let counts = count_by_kind(&db.query_log());
assert_eq!(counts.get(&QueryKind::IncludeEdges), Some(&1));
assert_eq!(
counts.get(&QueryKind::ProjectGraph),
None,
"project graph must not rebuild on a body edit"
);
}
#[test]
fn edge_change_rebuilds_graph() {
let (mut db, main, part) = main_part("\\input{part}\n", "hello\n");
let _ = project_graph(&db, project_main_part(&db, main, part));
db.clear_query_log();
db.set_file_text(main, "\\input{part}\n\\input{extra}\n");
let graph = project_graph(&db, project_main_part(&db, main, part));
let counts = count_by_kind(&db.query_log());
assert_eq!(
counts.get(&QueryKind::ProjectGraph),
Some(&1),
"project graph must rebuild when an edge changes"
);
assert_eq!(graph.unresolved().len(), 1);
assert_eq!(graph.unresolved()[0].from, PathBuf::from("/proj/main.tex"));
}
#[test]
fn reinterning_same_membership_reuses_graph_memo() {
let (db, main, part) = main_part("\\input{part}\n", "hello\n");
let project = project_main_part(&db, main, part);
let _ = project_graph(&db, project);
db.clear_query_log();
let project2 = project_main_part(&db, main, part);
assert!(
project == project2,
"same membership should re-intern to the same id"
);
let _ = project_graph(&db, project2);
assert_eq!(
count_by_kind(&db.query_log()).get(&QueryKind::ProjectGraph),
None,
"an unchanged membership must not rebuild the graph"
);
}