fix(m168): 复审 r3 四项 —— 说准 panic 档、给两条修复补守卫、时限算术补洞
复审 r3(p2-4items / fix-then-merge)四项 + 顺手五条:
1. **P2-1 `catch_unwind` 在发布档是死代码,三处文档说了不成立的话**:
`Cargo.toml` 的 `[profile.release]` 是 `panic = "abort"`(只有 `fast` 是 `unwind`),发布走
`--release`。分两半写清楚:①**闸前移仍安全**——release 档 panic 是进程退出,不存在永久停在
`Downloading`;`dev`/`test`/`fast` 档 `catch_unwind` 真会写 `Failed`;②但「下载失败不崩」在
panic 这一支上对发布版**是反的**:注册/`load_font` panic ⇒ 启动器整体 abort,不是降级。
`install.rs::plan_with_offline` 的文档(新增两档对照表)、`worker.rs` 模块文档与 `run` 注释全部
按这个说法重写,不再用 `catch_unwind` 给「不崩」背书。
2. **P2-2 「启动不发起下载」这条修复没有守卫**:拆出纯函数
`fonts::resolve_with_faces(setting, faces)`(`resolve_with` 只是 `system_faces()` 的包装),
新增 `fonts::tests::resolving_fonts_never_starts_a_download`:给一张只有拉丁族的脸表
(一定判 `NeedsDownload`),断言调用后 `install::status()` **不是** `Downloading`。
变异(实跑):把 `install::after_probe(cjk_report.strategy)` 加回 `resolve_with_faces` ⇒
`求值字体**不许**发起下载(状态从 Unknown 变成了 Downloading { bytes: 17772300 })`,红。
3. **P2-3 时限算术有洞 + `is_last` 没守卫**:改 `ATTEMPT_DEADLINE=60 s` / `TOTAL_DEADLINE=420 s`
(原来 120/360 时,第一个候选吃掉 120 s 后最后一个只剩 240 s ⇒ 门槛 72.3 KiB/s,r1 实测
67 KiB/s 需要 259 s,仍然必失败)。文档按新数值重算成一张表:第一个候选瞬间失败 ⇒ 420 s ⇒
42.3 KiB/s;爬满 60 s 被切 ⇒ 360 s ⇒ 49.4 KiB/s,两者都低于实测最慢的 67 KiB/s。
守卫:把同解的旧用例换成 **单候选 + attempt 300 ms / total 5 s**——有 `is_last` 时跑到总时限
(≈5 s),没有时 300 ms 就切,两种实现可分辨。变异(实跑):删掉 `is_last` ⇒
`the_last_candidate_gets_the_whole_remaining_budget` 红在
`最后一个候选该拿整个剩余预算(≈5 s)…452.6035ms`。
4. **P2-4(b)(c)**:①`install::tests` 里三处判据用例改用 `plan_with_offline(..., false)`,把进程级
`OFFLINE` 从判据里摘出去(否则同进程里 `headless(screenshot_options())` 会把它置 true 且从不
还原 ⇒ 结果依赖执行顺序);②`fonts.rs` 注释改成真实机制——出图 harness 不经
`App::with_presenter`、压根没有 `set_offline`,它零流量靠的是「`resolve_with` 不触发」。
顺手:①`sha256_mismatch…` 的 doc 改成「两层校验互备,本用例抓的是两层同时拆」(原来说
「拆任意一层就红」是假的);②`font.rs` 文档「把 90 s 压成 300 ms」→ 60 s/420 s;③`install.rs` 与
`catalog/font_install.rs` 的「≈17 MB」→ 16.9;④`worker::start` 的裸 `thread::spawn` 换成
`Builder::spawn`,起不来就落 `Failed`(发布档 panic = abort,不能让它 panic);线程体抽成 `run()`。
验证(`CARGO_TARGET_DIR=C:/Temp/ull-target/wt-168`,取退出码、不截断):
- `cargo test -p unlucky-app-iced --lib` → **688 passed / 0 failed / 2 ignored / exit 0**
- `cargo test -p unlucky-core --all-targets` → **16 个 target / 1189 passed / 0 failed / exit 0**
- `cargo test -p unlucky-i18n` → 31 passed / 0 failed / exit 0
- `cargo clippy -p unlucky-i18n -p unlucky-core -p unlucky-app-iced --all-targets -- -D warnings` → exit 0
- `cargo fmt --all -- --check` 干净;全仓无文件 >400;本轮改动文件最大 378(`app-iced/src/fonts.rs`)
This commit is contained in:
1 parent
b9009c0e61
commit
460da9330d
9 files changed
+231
-103
No files matched your search
@@ -186,16 +186,30 @@ fn register_termux_fonts() {
|
||||
}
|
||||
|
||||
/// 同 [`resolve`],但把配置里 `ui.language` 的显式值喂给中文策略(D28 的第一级)。
|
||||
///
|
||||
/// 只做「取真实字体库 + 交给 [`resolve_with_faces`]」两件事:真正的求值在后者,于是
|
||||
/// **「启动这一拍不发起下载」这件事有测试可打**(复审 r3 P2-2:`system_faces()` 不可注入,
|
||||
/// 上一轮的修复没有任何用例守着,把 `after_probe` 加回来整套测试仍绿)。
|
||||
pub fn resolve_with(setting: Option<&str>) -> Resolved {
|
||||
register_termux_fonts();
|
||||
resolve_with_faces(setting, system_faces().as_deref())
|
||||
}
|
||||
|
||||
/// [`resolve_with`] 的**纯**求值:给定「某族装没装」的字面表,算出 [`Resolved`]。
|
||||
///
|
||||
/// `faces = None` 表示字体库读不出来(→ 中文策略 `Unprobeable`,不是「缺字体」)。
|
||||
///
|
||||
/// **这里不发起下载**(复审 r2 P2-2 ① / r3 P2-2):启动那一拍(`boot.rs` / `pages/harness/app.rs`)
|
||||
/// 拿到的 `setting` 是 `None`,只按系统 locale 判——zh-locale + 配置 `en_us` 的机器若在这里触发,
|
||||
/// 就会白下 17 MiB,而随后 `App` 读到配置又把状态翻成「当前界面语言不需要中文字体」。
|
||||
/// 唯一的触发点是 [`cjk_for`](`ui/lang.rs::log_cjk`,那一拍已经知道**生效语言**)。
|
||||
///
|
||||
/// 这条不变量由 `fonts::tests::resolving_fonts_never_starts_a_download` 守着。
|
||||
pub fn resolve_with_faces(setting: Option<&str>, faces: Option<&[Face]>) -> Resolved {
|
||||
let platform = Platform::current();
|
||||
let faces = system_faces();
|
||||
|
||||
let is_installed = |family: &str| -> bool {
|
||||
faces
|
||||
.as_ref()
|
||||
.is_none_or(|faces: &Vec<Face>| faces.iter().any(|face| face.family == family))
|
||||
faces.is_none_or(|faces: &[Face]| faces.iter().any(|face| face.family == family))
|
||||
};
|
||||
|
||||
let families = [
|
||||
@@ -219,15 +233,11 @@ pub fn resolve_with(setting: Option<&str>) -> Resolved {
|
||||
// 字重:**优先用系统里这个族真实提供的档位**(令牌表的 `WeightCoverage` 是「这个族通常有哪几档」,
|
||||
// 装机版可能是静态子集:Win Server / 精简版只有经典 Segoe UI,没有 500)。查不到才回落到令牌纯函数。
|
||||
let weights = [Weight::Regular, Weight::Medium, Weight::Bold]
|
||||
.map(|weight| weight_report(platform, families[0].family, weight, faces.as_deref()));
|
||||
.map(|weight| weight_report(platform, families[0].family, weight, faces));
|
||||
|
||||
// D28 后半:语言 + 中文环境的中文字体策略。`faces` 的 `None` 在这里是「字体库读不出来」,
|
||||
// 适配层会把它翻成 `Unprobeable` 而不是「缺字体」(否则会误触发一次 CDN 下载)。
|
||||
let cjk_report = cjk::probe_with(setting, faces.as_deref());
|
||||
// **这里不发起下载**(复审 r2 P2-2 ①):启动那一拍(`boot.rs` / `pages/harness/app.rs`)拿到的
|
||||
// `setting` 是 `None`,只按系统 locale 判——zh-locale + 配置 `en_us` 的机器会在这里就下 17 MiB,
|
||||
// 而随后 `App` 读到配置、把状态翻成「当前界面语言不需要中文字体」,线程却还在下。
|
||||
// 真正的触发点只有一个:[`cjk_for`](`ui/lang.rs::log_cjk`,那一拍已经知道**生效语言**)。
|
||||
let cjk_report = cjk::probe_with(setting, faces);
|
||||
|
||||
Resolved {
|
||||
platform,
|
||||
@@ -247,8 +257,12 @@ pub fn resolve_with(setting: Option<&str>) -> Resolved {
|
||||
/// **为什么触发只留在这里**(复审 r2 P2-2 ①):这一拍的 `setting` 与 `LangChoice` 同源
|
||||
/// (`ULL_LANG` > 配置 > 系统 locale),也就是「界面实际用哪种语言」的那一份判据。
|
||||
/// 放在 [`resolve`] 里就会用「系统 locale」代表「用户选择」,zh-locale + 配置 `en_us` 的机器
|
||||
/// 会白下一次 17 MiB。出图通道(`--screenshot`)由 `install::set_offline(true)` 兜住,
|
||||
/// 仍然一次都不发。
|
||||
/// 会白下一次 17 MiB。
|
||||
///
|
||||
/// 两条通道各自靠什么做到「一次都不发」:**出图 harness**(`pages/harness/app.rs`)不经
|
||||
/// `App::with_presenter`,它零流量靠的是「[`resolve_with`] 根本不触发」;**`--screenshot`**
|
||||
/// 走真 `App`,除了同一条不变量,还有 `install::set_offline(true)` 这道闸。别把前者的功劳
|
||||
/// 记到 `set_offline` 上(复审 r3 P2-4 ②)。
|
||||
pub fn cjk_for(setting: Option<&str>) -> CjkReport {
|
||||
let report = cjk::probe_with(setting, system_faces().as_deref());
|
||||
install::after_probe(report.strategy);
|
||||
@@ -256,8 +270,11 @@ pub fn cjk_for(setting: Option<&str>) -> CjkReport {
|
||||
}
|
||||
|
||||
/// 一条系统字面(族名 + 字重)。
|
||||
///
|
||||
/// `pub` 是因为 [`resolve_with_faces`] 把它作为参数暴露出去(那是「启动不发起下载」的唯一
|
||||
/// 可测入口,复审 r3 P2-2)——`private_interfaces` 会拦「pub 函数收私有类型」。
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct Face {
|
||||
pub struct Face {
|
||||
family: String,
|
||||
weight: u16,
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user