Layout tests: initialize the fixed fonts centrally in the layout entry points

layout, layout_para and layout_equation call crate::test_fonts() first in test
builds, so no test can reach font lookup through them before the pinned fonts
are installed. Tests and helpers that look up fonts directly (word::resolve,
FontDb::global, the nbsp fixture) call it themselves. Fixes init_test_fonts()
returning false when an uninitialised test ran first in a shared process.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
vangel76andClaude Sonnet 5.5 committed 2026-11-01 14:46:27 +01:00
1 parent f5038a12e3
commit 7e967d4a3a
8 files changed
+21 -2

No files matched your search

+8 -2
View File
@@ -8,8 +8,12 @@
//! - [`hit`]: point ↔ position, caret geometry, line navigation, selection rectangles.
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
/// Every unit test calls this before any font lookup (including its fixture construction).
/// A process-wide immutable choice keeps font and math caches consistent across test threads.
/// Installs the pinned test fonts as the process-wide font database. The layout entry points
/// (`layout`, `layout_para`, `layout_equation`) call it first in test builds; a test that looks
/// up fonts itself before any layout call (fixtures, `word::resolve`, `FontDb::global`) calls it
/// at its start. It is idempotent and never calls back into layout, so it cannot recurse. The
/// choice is process-wide and immutable, which keeps font and math caches consistent across
/// test threads.
#[cfg(test)]
fn test_fonts() {
assert!(wordcraft_fonts::FontDb::init_test_fonts(), "layout tests must initialize fixed fonts before any global font lookup");
@@ -314,6 +318,8 @@ pub struct DocLayout {
/// Lay out the whole document.
pub fn layout(doc: &Document, cache: &mut LayoutCache, opts: &LayoutOptions) -> DocLayout {
#[cfg(test)]
crate::test_fonts();
let t0 = now_ms();
// Page totals shown in the body (`NUMPAGES`, `SECTIONPAGES`) are known only after
// paginating: lay out again with the totals found until they hold (bounded). Footnotes
+2
View File
@@ -207,6 +207,8 @@ pub fn layout_equation(
props: &MathProps,
counter: &mut u32,
) -> MathLayout {
#[cfg(test)]
crate::test_fonts();
let parsed;
let nodes: &Arg = if math.nodes.is_empty() && !math.linear {
parsed = parse_linear(linear);
+1
View File
@@ -35,6 +35,7 @@ fn lay_in(math: &Math, lin: &str, display: bool, avail: f32, mp: &MathProps) ->
#[test]
fn eq_array_rows_without_ampersand_are_centred() {
crate::test_fonts();
// Word centres a row with no `&` in the array (#2617: a case row "a, n odd" above
// "|a|, &n even"), whether or not the other rows have one.
use wordcraft_doc::math::*;
+2
View File
@@ -553,6 +553,8 @@ fn joined_cluster_starts(p: &Paragraph, clusters: &[Cluster]) -> Vec<usize> {
/// Lay out one paragraph.
pub fn layout_para(p: &Paragraph, env: &ParaEnv) -> ParaLayout {
#[cfg(test)]
crate::test_fonts();
let doc = env.doc;
let rp = paragraph_props(p, env);
let res = Resolver { doc, style: p.props.style.as_deref(), table: env.table.map(|t| &t.chr) };
+3
View File
@@ -1345,6 +1345,7 @@ fn a_missing_roman_font_is_drawn_as_word_draws_it() {
#[test]
fn a_missing_roman_font_with_its_own_substitute_keeps_it() {
crate::test_fonts();
// Times New Roman is roman in every font table. Where it isn't installed it keeps its metric
// clone (Liberation Serif), not Cambria: Word, which always has it, never draws it missing.
let mut d = Document::from_text("Serif");
@@ -1970,6 +1971,7 @@ fn bullet_first_line(label_font: Option<&str>) -> (f32, f32, f32, f32) {
#[test]
fn a_symbol_bullet_sizes_its_line_by_the_symbol_font() {
crate::test_fonts();
// Measured in Word: a Symbol bullet makes its line taller than the text alone (Calibri 11 pt
// at 1.08 spacing: 15.1 pt, not 14.5 pt), the extra ascent added unscaled. Symbol isn't an open
// font; without it the bullet is drawn from another face but its line keeps Symbol's height.
@@ -6396,6 +6398,7 @@ fn graphic_text_in_a_degenerate_rectangle_is_not_drawn() {
#[test]
fn chart_text_the_font_lacks_is_drawn_in_a_fallback_face() {
crate::test_fonts();
// #4720: a Chinese legend in a chart drawn in the body font (which has no Chinese) showed boxes.
use wordcraft_doc::graphic::{Graphic, GraphicItem, GraphicKind, TextAlign};
use wordcraft_doc::props::Rgb;
+1
View File
@@ -314,6 +314,7 @@ fn hostile_bidi_text_never_panics() {
/// A face that has Arabic letters (installed system fonts), or `None` to skip.
fn arabic_face() -> Option<wordcraft_fonts::FaceRef> {
crate::test_fonts();
wordcraft_fonts::FontDb::global().fallback_for('ب', 0).map(|f| wordcraft_fonts::FaceRef::of(&f)).filter(|f| f.covers('پ'))
}
+3
View File
@@ -68,6 +68,7 @@ fn myanmar_without_spaces_wraps_between_syllables() {
/// A face with Myanmar letters (an installed system font), or `None` to skip.
fn myanmar_face() -> Option<wordcraft_fonts::FaceRef> {
crate::test_fonts();
wordcraft_fonts::FontDb::global().fallback_for('\u{1000}', 0).map(|f| wordcraft_fonts::FaceRef::of(&f)).filter(|f| f.has_layout_tables())
}
@@ -119,12 +120,14 @@ const THAI_RUN: &str = "[D] น้ำ ค่ำ ปั้น ฝั่ง ปู
/// Is a font with Thai letters installed? `false` skips the test.
fn thai_font_installed() -> bool {
crate::test_fonts();
wordcraft_fonts::FontDb::global().fallback_for('\u{0E19}', 0).is_some()
}
/// Is a Thai font with Latin letters and digits installed (Tahoma, Leelawadee UI, Thonburi, the
/// TLWG fonts…)? Noto Sans Thai alone has none, so no face covers [`THAI_RUN`]: `false` skips.
fn thai_latin_font_installed() -> bool {
crate::test_fonts();
let face = wordcraft_fonts::word::resolve("TH Sarabun New", false, false).face;
THAI_RUN.chars().filter(|c| !c.is_whitespace()).all(|c| face.covers(c))
}
+1
View File
@@ -6,6 +6,7 @@ use crate::{Document, LayoutCache, LayoutOptions, Placed, layout};
/// Inter with its U+00A0 glyph twice as wide as its space, under the family name "Wnbsp".
/// Built from the bundled font so the test needs no installed font.
fn wide_nbsp_family() -> &'static str {
crate::test_fonts();
const FAMILY: &str = "Wnbsp";
let inter = wordcraft_fonts::word::resolve("Inter", false, false).face;
let mut data = inter.data().to_vec();