use text_document::{DocumentBackend, TextDocument};
static SERIAL: std::sync::Mutex<()> = std::sync::Mutex::new(());
fn serial() -> std::sync::MutexGuard<'static, ()> {
SERIAL
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
#[cfg(target_os = "linux")]
fn process_thread_count() -> usize {
std::fs::read_to_string("/proc/self/status")
.expect("/proc/self/status")
.lines()
.find_map(|l| l.strip_prefix("Threads:"))
.and_then(|v| v.trim().parse().ok())
.expect("Threads: line")
}
#[cfg(target_os = "linux")]
#[test]
fn a_backend_costs_one_thread_where_standalone_documents_cost_one_each() {
const N: usize = 16;
let _serial = serial();
let before_standalone = process_thread_count();
let standalone: Vec<TextDocument> = (0..N).map(|_| TextDocument::new()).collect();
let standalone_cost = process_thread_count().saturating_sub(before_standalone);
let backend = DocumentBackend::new();
let before_shared = process_thread_count();
let shared: Vec<TextDocument> = (0..N).map(|_| TextDocument::new_in(&backend)).collect();
for (i, doc) in shared.iter().enumerate() {
doc.set_plain_text(&format!("Scene {i}: she called his name into the trees."))
.expect("set_plain_text");
}
let shared_cost = process_thread_count().saturating_sub(before_shared);
assert_eq!(standalone.len(), N);
assert_eq!(shared.len(), N);
assert!(
shared_cost <= 1,
"{N} documents in one backend started {shared_cost} thread(s). The backend \
exists precisely so a document does not pay a thread of its own; something \
reintroduced EventHubClient::start per document."
);
assert!(
standalone_cost > shared_cost,
"the comparison is the point: {N} standalone documents cost {standalone_cost} \
thread(s) and {N} shared ones cost {shared_cost}"
);
}
#[test]
fn documents_in_one_backend_keep_separate_text() {
let _serial = serial();
let backend = DocumentBackend::new();
let a = TextDocument::new_in(&backend);
let b = TextDocument::new_in(&backend);
a.set_plain_text("The ferry left before the light did.")
.expect("set a");
b.set_plain_text("She counted the bells and lost count twice.")
.expect("set b");
assert_eq!(
a.to_plain_text().expect("a"),
"The ferry left before the light did."
);
assert_eq!(
b.to_plain_text().expect("b"),
"She counted the bells and lost count twice."
);
}
#[test]
fn undo_in_one_document_leaves_its_sibling_alone() {
let _serial = serial();
let backend = DocumentBackend::new();
let a = TextDocument::new_in(&backend);
let b = TextDocument::new_in(&backend);
a.set_plain_text("First.").expect("set a");
b.set_plain_text("Second.").expect("set b");
let b_before = b.to_plain_text().expect("b before");
a.undo().expect("undo a");
assert_eq!(
b.to_plain_text().expect("b after"),
b_before,
"undoing in one document must not touch a sibling sharing the backend"
);
}
#[test]
fn dropping_one_document_leaves_the_backend_running() {
let _serial = serial();
let backend = DocumentBackend::new();
let doomed = TextDocument::new_in(&backend);
let survivor = TextDocument::new_in(&backend);
doomed.set_plain_text("Gone in a moment.").expect("set");
drop(doomed);
survivor
.set_plain_text("Still here, and still edited.")
.expect("the survivor must still take an edit");
assert_eq!(
survivor.to_plain_text().expect("survivor"),
"Still here, and still edited."
);
}
#[test]
fn a_document_reports_whether_it_shares_a_backend() {
let backend = DocumentBackend::new();
let shared = TextDocument::new_in(&backend);
let alone = TextDocument::new();
assert!(shared.shares_a_backend());
assert!(!alone.shares_a_backend());
}