Files
Unlucky-Launcher/crates/design/src/typography/fonts/tests.rs
T
AskaEth d616d65ea1
CI / runtime smoke (macos arm64, probe) (push) Skipped
Release guard / 发布标记语义守门(push tag → 稳定版) (push) Canceled after 0s
Release / 选槽位(默认两个 Windows) (push) Canceled after 0s
Release / build (${{ matrix.slot }}) (push) Canceled after 0s
Release / GitHub Release (push) Canceled after 0s
CI / fmt (rustfmt --check) (push) Canceled after 0s
CI / clippy (ubuntu-latest) (push) Canceled after 0s
CI / clippy (windows-latest) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
CI / build release (ubuntu-latest) (push) Canceled after 0s
CI / build release (windows-latest) (push) Canceled after 0s
CI / runtime smoke (ubuntu x64, xvfb) (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好一个文件) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Unlucky Launcher v0.2.3
2026-11-08 00:15:58 +00:00

150 lines
5.2 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.
//! 字体栈与字重解析的测试(独立文件是为了守住单文件 ≤400 行的硬约束)。
use super::*;
use crate::typography::Weight;
const PLATFORMS: [Platform; 3] = [Platform::Windows, Platform::MacOs, Platform::Linux];
const FAMILIES: [FontFamily; 3] = [FontFamily::Display, FontFamily::Text, FontFamily::Mono];
#[test]
fn font_stacks_are_well_formed() {
for platform in PLATFORMS {
for family in FAMILIES {
let stack = platform.font_stack(family);
assert!(
!stack.is_empty(),
"{} 的 {family:?} 栈为空",
platform.name()
);
let last = stack[stack.len() - 1].family;
let generic = match family {
FontFamily::Mono => "monospace",
_ => "sans-serif",
};
assert_eq!(
last,
generic,
"{} 的 {family:?} 栈必须以通用族名兜底",
platform.name()
);
for face in stack {
assert!(!face.family.is_empty());
assert!(
!face.family.contains(['/', '\\', '.', ':']),
"字体栈只允许族名,不得出现路径/文件名:{}",
face.family
);
assert!(
face.family.is_ascii(),
"族名应为 ASCII(非 ASCII 名交给系统匹配):{}",
face.family
);
// 每条栈项都必须声明字重覆盖(连 400 都没有的族写进栈没有意义)。
assert!(
face.supports(Weight::Regular),
"{} 连 400 都没有,写它没意义",
face.family
);
}
}
}
}
#[test]
fn display_and_text_stacks_differ_on_windows_and_macos() {
assert_ne!(
Platform::Windows.font_stack(FontFamily::Display)[0].family,
Platform::Windows.font_stack(FontFamily::Text)[0].family
);
assert_ne!(
Platform::MacOs.font_stack(FontFamily::Display)[0].family,
Platform::MacOs.font_stack(FontFamily::Text)[0].family
);
}
#[test]
fn chinese_fallback_is_present_but_not_first() {
// 中文族名必须在栈里,但不能排第一(纯英文环境不加载中文字体,D28)。
for platform in PLATFORMS {
let stack = platform.font_stack(FontFamily::Text);
let cjk = ["Microsoft YaHei UI", "PingFang SC", "Noto Sans CJK SC"];
assert!(
stack.iter().any(|face| cjk.contains(&face.family)),
"{} 的正文栈缺 CJK 兜底",
platform.name()
);
assert!(
!cjk.contains(&stack[0].family),
"{} 的首选字体不该是 CJK 族",
platform.name()
);
}
}
#[test]
fn cjk_weight_coverage_is_registered() {
// 用户点名的坑:中文字重覆盖三平台差别很大。
let win = Platform::Windows.cjk_face();
assert!(win.supports(Weight::Regular) && win.supports(Weight::Bold));
assert!(
!win.supports(Weight::Medium),
"雅黑没有 500——这条断言就是「中文 500 会掉到降级路径」的登记"
);
let mac = Platform::MacOs.cjk_face();
assert!(mac.supports(Weight::Medium));
assert!(
!mac.supports(Weight::Bold),
"苹方到 600 为止,700 要合成/换族"
);
let linux = Platform::Linux.cjk_face();
for weight in [Weight::Regular, Weight::Medium, Weight::Bold] {
assert!(
linux.supports(weight),
"思源黑体/Noto CJK 应覆盖 400/500/700"
);
}
}
#[test]
fn resolve_prefers_native_and_flags_synthetic() {
// Windows 正文 700:Segoe UI Variable 覆盖 → 原生。
let bold = Platform::Windows.resolve_weight(FontFamily::Text, Weight::Bold);
assert_eq!(bold.quality, WeightMatch::Native);
assert_eq!(bold.available, 700);
assert!(!bold.is_synthetic());
// Linux 正文 500:Roboto 可变 → 原生。
assert_eq!(
Platform::Linux
.resolve_weight(FontFamily::Text, Weight::Medium)
.quality,
WeightMatch::Native
);
// 静态族缺档:只有 400 的族请求 700 → nearest 400,差 300 → 判为合成降级。
let only_regular = FontFace::new("Courier New", WeightCoverage::Static(&[400]));
assert_eq!(only_regular.weights.nearest(700), 400);
let synthetic = FontChoice {
family: only_regular.family,
requested: 700,
available: only_regular.weights.nearest(700),
quality: WeightMatch::Synthetic,
};
assert!(synthetic.is_synthetic());
}
#[test]
fn nearest_picks_closest_static_weight() {
let coverage = WeightCoverage::Static(&[300, 400, 600, 700]);
assert_eq!(coverage.nearest(500), 400);
assert_eq!(coverage.nearest(650), 600);
assert_eq!(coverage.nearest(900), 700);
let variable = WeightCoverage::Variable { min: 100, max: 900 };
assert_eq!(variable.nearest(500), 500);
assert_eq!(variable.nearest(50), 100);
}
#[test]
fn current_platform_returns_a_stack() {
let platform = Platform::current();
assert!(!platform.font_stack(FontFamily::Text).is_empty());
}