Files
vectorcraft/crates/engine/tests/removed_font.rs
T
dotCooCoo ba2531bc3f Tests: engine tests remove the temp folders they make
vectorcraft_testkit::temp_dir returns a bare path, so every engine test
that wrote into one left the folder in the system temporary folder.

- testkit: TempDir, a test's own folder made through temp_dir (same
  private creation and ownership checks) under a tag no other TempDir in
  the process uses, removed with its contents when dropped, also when
  the test panics.
- Engine unit and integration tests hold a TempDir for the whole test
  instead of calling temp_dir: graph, fileio, compare, journal, library,
  shortcut set, spelling, store, web export, cancel, containers, build,
  color settings, command sweep, font folder, removed font, model-based
  and the font search fuzz.
- The DCS and swatch library tests' own helpers and the PDF editing
  tests' shared vc-pdfedit-<pid> folder use a TempDir per test too.
- The font and link file search, store, color settings and font folder
  helpers no longer delete the private folder temp_dir checked and make
  it again with default permissions.
2026-11-13 09:33:36 -08:00

74 lines
3.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! A font removed while the app runs: once the fonts are scanned again (`text.rescanFonts`), its
//! family is no longer listed, and type set in it, which had loaded it, is laid out in the fallback
//! font and listed as missing, until the font is installed again. One test in its own process, so
//! the process-wide font database and font folders start fresh, as when the app starts.
// Integration tests: unwrapping and panicking on failure is fine here, unlike in shipped code (AGENTS.md › Robustness).
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use serde_json::{Value, json};
use vectorcraft_doc::{NodeId, NodeKind};
use vectorcraft_engine::Session;
use vectorcraft_text::{FALLBACK_FAMILY, FontDb};
const FAMILY: &str = "Gonefont Sans";
/// `text.fonts`' status for [`FAMILY`] and the family that stands in for it.
fn status(s: &mut Session) -> (String, String) {
let listed = s.execute("text.fonts", &json!({})).unwrap();
let row = listed.as_array().unwrap().iter().find(|f| f["family"] == FAMILY).cloned().unwrap();
(row["status"].as_str().unwrap().into(), row["resolved"]["family"].as_str().unwrap().into())
}
/// Whether `text.fontList` lists [`FAMILY`].
fn listed(s: &mut Session) -> bool {
let r = s.execute("text.fontList", &json!({})).unwrap();
r["families"].as_array().unwrap().iter().any(|f| f == FAMILY)
}
/// The families of the faces the type object `id` is laid out in.
fn laid_out_in(s: &Session, id: u64) -> Vec<String> {
let st = s.doc().unwrap();
let Some(NodeKind::Text(t)) = st.doc.node(NodeId(id)).map(|n| &n.kind) else { panic!("no type object {id}") };
let db = FontDb::global();
let mut families: Vec<String> = vectorcraft_text::layout(db, t).glyphs.iter().map(|g| db.face_by_id(g.font_id).unwrap().family.clone()).collect();
families.dedup();
families
}
#[test]
fn type_in_a_removed_font_falls_back_after_a_rescan() {
let root = vectorcraft_testkit::TempDir::new("removed-font");
vectorcraft_text::set_user_font_dirs(vec![root.clone()]);
let file = root.join("Gonefont.otf");
std::fs::write(&file, vectorcraft_testkit::fonts::renamed(FAMILY)).unwrap();
let mut s = Session::new();
s.execute("file.new", &json!({"width": 300, "height": 200})).unwrap();
let id = s.execute("text.create", &json!({"x": 10, "y": 40, "text": "Gone", "font": FAMILY})).unwrap()["id"].as_u64().unwrap();
assert_eq!(status(&mut s), ("exact".into(), FAMILY.into()));
assert!(listed(&mut s));
assert_eq!(laid_out_in(&s, id), [FAMILY]);
// Removed: after a rescan the type is laid out in the fallback font, without editing the document.
std::fs::remove_file(&file).unwrap();
s.doc_mut().unwrap().mark_saved();
let revision = s.doc().unwrap().revision;
let r: Value = s.execute("text.rescanFonts", &json!({})).unwrap();
assert!(r["faces"].is_u64(), "{r}");
assert_eq!(status(&mut s), ("missing".into(), FALLBACK_FAMILY.into()));
assert!(!listed(&mut s));
assert!(s.execute("text.fontList", &json!({"family": FAMILY})).is_err());
assert_eq!(laid_out_in(&s, id), [FALLBACK_FAMILY]);
let missing = s.execute("text.missingFonts", &json!({})).unwrap();
assert_eq!(missing["fonts"][0]["family"], FAMILY, "{missing}");
let st = s.doc().unwrap();
assert!(st.revision > revision && !st.is_dirty());
// Installed again: the type is set in it again.
std::fs::write(&file, vectorcraft_testkit::fonts::renamed(FAMILY)).unwrap();
s.execute("text.rescanFonts", &json!({})).unwrap();
assert_eq!(status(&mut s), ("exact".into(), FAMILY.into()));
assert!(listed(&mut s));
assert_eq!(laid_out_in(&s, id), [FAMILY]);
}