Merge pull request #2348 from storytold/test/fontdb-race-coverage

Fix concurrent font integration test file race
This commit is contained in:
Dylan M. Taylor
2026-11-13 11:19:49 -05:00
committed by GitHub
2 changed files with 34 additions and 5 deletions
+22 -3
View File
@@ -2,15 +2,21 @@
//! `--no-system-fonts` or `FIGCRAFT_NO_SYSTEM_FONTS=1` opts out.
use std::process::Command;
use std::sync::atomic::{AtomicU64, Ordering};
use serde_json::{Value, json};
/// The result of one `figcraft-cli run` step (script file `name`); `opt_out` sets the environment
/// variable.
fn run_one(name: &str, step: Value, args: &[&str], opt_out: bool) -> Value {
static NEXT_SCRIPT: AtomicU64 = AtomicU64::new(0);
let d = std::env::temp_dir().join(format!("figcraft-cli-fonts-{}", std::process::id()));
std::fs::create_dir_all(&d).unwrap();
let script = d.join(format!("{name}.json"));
// Tests in this binary run concurrently. `sketch_imports_keep_to_no_system_fonts` also calls
// `families`, so a shared `families.json` could be truncated while another child reads it;
// an empty script is valid and makes the CLI exit successfully without printing result JSON.
let sequence = NEXT_SCRIPT.fetch_add(1, Ordering::Relaxed);
let script = d.join(format!("{name}-{sequence}.json"));
std::fs::write(&script, json!([step]).to_string()).unwrap();
let mut cmd = Command::new(env!("CARGO_BIN_EXE_figcraft-cli"));
cmd.arg("run").arg(&script).args(args);
@@ -20,8 +26,21 @@ fn run_one(name: &str, step: Value, args: &[&str], opt_out: bool) -> Value {
cmd.env_remove("FIGCRAFT_NO_SYSTEM_FONTS");
}
let out = cmd.output().unwrap();
assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
let line: Value = serde_json::from_slice(&out.stdout).unwrap();
assert!(
out.status.success(),
"{name}: CLI status {:?}; stdout={:?}; stderr={}",
out.status.code(),
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr),
);
let line: Value = serde_json::from_slice(&out.stdout).unwrap_or_else(|error| {
panic!(
"{name} {:?}: CLI returned success without JSON: {error}; stdout={:?}; stderr={}",
args,
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr),
)
});
line["result"].clone()
}
+12 -2
View File
@@ -376,9 +376,19 @@ fn font_bytes_load_into_the_db() {
let families = load_font_bytes(f.bytes.to_vec());
assert_eq!(families.len(), 1);
assert!(families.iter().all(|f| has_family(f)));
let faces = db().read().unwrap().face_count();
// The global face count can grow here when another test finishes the concurrent system
// font scan. Count explicitly loaded faces from this family so the idempotency assertion
// stays isolated from unrelated installed fonts.
let loaded_faces = || {
let db = db().read().unwrap();
families
.iter()
.map(|family| db.faces.iter().filter(|face| face.loaded > 0 && face.family.eq_ignore_ascii_case(family)).count())
.collect::<Vec<_>>()
};
let faces = loaded_faces();
assert_eq!(load_font_bytes(f.bytes.to_vec()), families);
assert_eq!(db().read().unwrap().face_count(), faces);
assert_eq!(loaded_faces(), faces);
}
}