0241f793b2175e6aed4a2d8100e6e989c402419f
7
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
79b796fd58 |
fix(m161-r1): 崩溃/静默失败不再冒充「启动进程失败」;补三处假绿的守卫
复审 P1-1(用户可见假文案):`LaunchEnd` 在 `failure == None`(跑起来之后崩的)时
临时造了 `Stage::Spawn + format!("exit {:?}")`,于是磁贴写「启动失败 · 启动游戏进程」、
详情页漏出 `exit Some(1)`、静默失败丢掉「exit 0 却没起来」,而同一时刻 Toast 说「崩溃」。
- 新增 `LaunchFailureCause{Stage|Crashed|SilentFailure}`(dto/launch_failure.rs),
`LaunchFailureCause::of(outcome, failure, exit_code)` 是唯一组装点;`InstanceRuntime::Failed{cause}`。
`LaunchFailureStage` 保持「只装装配阶段」(文档那句「崩溃是结局不是阶段」现在成立)。
- 界面:`InstancesFailed`(框架)+ `InstancesCrashed` + `InstancesSilentFailed`;
删掉 `InstancesFailedStage`。退出码按数字出词,不再有 Debug 文本。
- 用例:presenter 断言**枚举变体**(三种形状逐条)、app 断言**具体句子**
(「不许出现 Some(」「进程起来了不许说启动游戏进程」「静默失败要点出没起来」)。
mock 夹具与 launch_state_tests 的期望值同步;launch-error 三张图重拍。
复审 P2-1(`convert::failure_stage` 零覆盖):新增 `convert/stage_tests.rs` 5 条(三档 +
「既没回滚也没宣称干净」+ 非致命无阶段 + `failed_report_with` ⇒ NotStarted)。
变异实测:改成恒 `RolledBack` ⇒ 3 条红。
复审 P2-2(矮窗口用例的算式三个数全错、`body_height` 零测试):用例改成直接调
`frame::body_height`,chrome 从 `Ui::metrics` + `WidthClass::gutter` 现取,并把门槛与真实
chrome 绑死(chrome+MIN ⇒ Split、-1px ⇒ ScrollWhole);新增 `frame` 自己的 `body_height` 用例。
变异实测:`2.0 * gutter` → `gutter` ⇒ 2 条红。
复审 P2-3(源指纹闸门 / `Refused` 零覆盖):新增两条 core 用例——闸门(原地留着 prefs.json
+ 预先写好的同源收据 ⇒ Nothing 且收据未被重写,反证:源变了允许重放)、`Refused`
(`$schema` 1.7.0 ⇒ ReadOnly ⇒ 不动旧文件、不写收据、理由含「不可写」)。
变异实测:删闸门 ⇒ 1 条红;`can_save` 守卫改 `if false` ⇒ 1 条红。
顺手收:
- CLI `pack import` 那句「已整包回滚」也按三档出词(`RollbackStage` + `rollback_line`),
加 2 条单测。CLI 不依赖 presenter,判定暂与 presenter 各一份 → 已 file finding 建议下沉 core。
- 设置页 `status_text()` 抽出 applying 档,用例断言「正在安装…」且非错误色。
金表:新 key 加在 `golden.rs`(81 行),未动 `golden/tail.rs`(211 行),避免与 M162 的拆分冲突。
|
||
|
|
03741ecd2d |
fix(m161): 收更新键/按钮/矮窗口/启动错误/导入结果五笔债
① update.auto_check / update.channel 登记进 core setting schema(1.6.0)
- 新增 setting/keys/update.rs(两键 + UPDATE_CHANNELS,只允许 stable);
SettingSchema 加 with_all 供域键表批量登记。
- 新增 setting/update_prefs.rs:cache/update/prefs.json → launcher.json 的一次性迁移。
四条硬约束:源指纹闸门(幂等)、不丢 false、新值胜出、旧文件改名 .migrated 后不再被读;
主配置不可写时不动旧文件。收据 config/update-prefs-migration.json。
- presenter Runtime 建会话时迁一次并把结局留在 migration();prefs 字段换成
auto_check + 每次派发从主配置读 channel(不再硬编码 Channel::Stable)。
保留旧字段 auto_check 的真实来源(原有测试 cases 会断言)。
- CLI config get/set/dump 先跑同一次迁移(幂等),输出里给 migration 段与收据路径。
② presenter/dto/events.rs 的「本次会话不再弹提示条」注释改成真实语义
(记的是版本号:同版本压住、更高版本照弹、手动检查清掉)。
③ 检查/下载进行中按钮禁用:pages/update/busy.rs 的 busy/can_check/can_apply;
设置页两颗按钮改画禁用(不是消失),app 层 on_update_action 再挡一道
(忙碌时 CheckNow/Apply 不派发);新增两条用例(含「忙完恢回可点」)。
④ 内容库低高度:content_layout::MIN_BODY_HEIGHT=460 + BodyMode(按固定件+三整行算出来,
用例守着算式);frame::body_height 从 Ui::metrics 算正文可用高,pages::Model 带 window_height。
≥460 列表自己滚;<460 主列整列一个滚动条、列表按自然高度铺开。
⑤ 启动错误文案本地化:presenter 删掉 launch_summary / 「启动失败:」前缀 / 五条中文阶段前缀,
改发 LaunchFailureView{stage,detail};LaunchFinished 改带 exit_code/uptime_ms/failure,
Toast 由 app 层按 i18n key 现求(与 install_cancelled 同构)。InstancesFailed 拆成
InstancesFailedStage(列表单行)+ LaunchStageFailed(详情/Toast 全文)。
⑥ 导入结果块:ImportReportView 加 failure_stage(NotStarted/RolledBack/RollbackIncomplete,
由 core 的 rollback 机器位推出);标题、图标、底色三处分档,预检失败不再说「已回滚」。
顺带:六处注释/文档漂移(update.rs 的分工表与偏好落点、pack 的「已整包回滚」、
build_request 的 Errors、settings/update 的按钮禁用说明等),
以及为守 400 行把 update/cli-config/core-keys/presenter-dto/presenter-launch/i18n-golden
里贴线或越线的文件按模块拆开(纯搬移,行为不变)。
|
||
|
|
d568cc6f37 |
fix(m160-r1): 预检失败不再静默空转 + 写侧条目数闸 + 判据归一 + 冲突页对齐
复审 reviewer-modpackpick-r1(p2-1items / fix-then-merge)的返工。 P2-1 预检失败被吞掉(错误呈现回归) - host::import::spawn_scan 在 `scan.error.is_some()` 时**再发一条 PackEvent::Report** (`convert::failed_report_of` 把 core 的 ImportError 含 hint 逐字段搬过去),于是页面回到 既有的「致命 —— 已回滚」结果块,不再落回入口态。 - convert 里把失败报告的组装收成一处(`failed_report` / `failed_report_of` → `failed_report_with`)。 - 证据:`a_failed_scan_still_reaches_the_screen_as_a_report` 断言**事件序列** `["scan-with-error", "report-failed"]` + 报告里 code/message 与预检同源(MultiMC `formatVersion: 2`,detect 认得、read 才失败);`a_clean_scan_does_not_emit_a_report` 是对照。 变异验证:删掉那条 Report 发送 ⇒ 该用例红。 P3-1 写侧补条目数闸 - `WriteLimits` 增加 `entries`(生产口径 = `PACK_MAX_ENTRIES`);`preflight` 在**读盘之前** 按「清单 1 条 + 覆盖文件 N 条」判 `count > limit`。 - 证据:`the_write_side_refuses_a_pack_over_the_unified_entry_count`(20001 条声明只放 1 个 真文件 ⇒ 报 `pack_limits` 而不是 `pack_io`,即闸在读盘前)、 `the_entry_count_gate_is_the_same_number_as_the_read_side`(两侧同一个数)。 变异验证:注释掉 `guard_entries` 调用 ⇒ 前一条红。 P3-2 「内容相同」判据归一 - 删掉 `scan::judge` 里与 `merge::decide` 重复的 `disk.sha1 == pack_sha1` 早退,只留 `decide().reason?`;两处文档互相指路(`merge.rs` 注明这是唯一判据)。 - 变异验证:把 `decide` 的相同分支改成"冲突" ⇒ **8 条**红(含 4 条 scan 用例与 merge 单测)。 P3-3/P3-4 口径与注释 - `TogglePick` 注释改成与代码一致(保留项留在表里,靠 `decisions()` 过滤)。 - `unknown_remote` 的计数与文案统一:判不了的两种来路(补全前没路径 / 有路径但缺 sha1)都算, 注记载明两者;新增 `a_remote_entry_with_a_path_but_no_sha1_is_counted_as_unjudgeable`。 视觉(tower 看图给的三条) - 冲突行两种取值都改成**同几何的 pill**(保留=描边、覆盖=tonal),各自右对齐进同宽度的槽 ⇒ 右边缘同一条线;路径列吃 `Fill` 把槽推到行右缘。 - 列表与动作行之间加 8dp 呼吸;分页行的 Previous/Next 用 `Space::Fill` 顶到右端, 与内容列对齐(不再是右侧一大片留白)。 - 重新出图:C:/Temp/m160-shots/pick-light.png / pick-dark.png。 顺手 - `format/write.rs` 的单测拆到 `format/write/tests.rs`(纯搬移,守 400 行),并把里面两个 自己拼 `%TEMP%` 路径的用例改用 `TempRoot`(断言红了也不留垃圾)。 - `import_decisions.rs` 的夹具拆到 `tests/decisions_support.rs`(纯搬移,守 400 行)。 证据:fmt 干净、clippy -D warnings 干净、`cargo test --workspace --all-targets -j 4` = 2512 passed / 0 failed / **4 ignored**(51 个 test binary);跑前/跑后 `%TEMP%` 的 `ull-pack-*` 计数 1922→1922、`ull-pack-*-zip64-*` 0→0、`ull-import-*` 1360→1360(增量 0), df 106G→106G 余量不变。 |
||
|
|
4defc41e8d |
feat(m160): 整合包逐文件覆盖/保留决策 + 导入前冲突清单 + 限额口径统一
主任务(逐文件覆盖/保留)
- core: 新增 import/scan.rs —— 只读预检产出「冲突清单」(目标已存在且 SHA-1 不同才进),
逐条给路径/来源/两侧体积与哈希;用户删过的、内容相同的、被叠层遮蔽的一律不打扰。
- core: 新增 import/decision.rs —— ConflictDecisions(整批档 + 逐项表,逐项优先),
缺省档恒为 KeepUser;ImportRequest.conflict_policy 改由它承载,CLI 调用点不变。
- core: phase.rs 的 settle 按 path 取决策;非冲突路径不受决策影响。
- presenter: ImportScanView / ImportConflictView / ConflictDecisionsView(全部保留 /
全部覆盖 / 逐项{default,items})+ PackCommand::Scan / PackEvent::Scan;host 新增只读
spawn_scan,且新实例路径不再提前建目录(target_of 只算路径,prepare_target 才建)。
- app-iced: 导入前冲突清单页(分页 40 条/页、三入口、计数摘要、确认/取消、两行式行布局);
预检干净时直接往下导,不打扰用户。出图见 C:/Temp/m160-shots/{pick-light,pick-dark}.png。
- i18n: 新增 18 条 en/zh 文案 + 更新「两者都留」那条口径说明。
限额口径统一
- pack/archive/limits.rs 成为唯一来源:PACK_MAX_ENTRIES/UNCOMPRESSED_BYTES/ENTRY_BYTES
(20k / 512 MiB / 64 MiB);读侧 PackLimits::default 与写侧 WriteLimits::production 同源。
- 写侧不再直接用 zip::ZIP64_BYTES_THR;新增单条目闸,于是「能导出的包一定能导入」。
不等式在 pack/tests/limits.rs 里用 const 断言钉死。
测试临时物泄漏(塔台追加项)
- addon/temp.rs:TempRoot —— Drop 即删(panic 展开同样删),Deref/AsRef 到落点路径,
under()/at() 支持"子树落点、删父根"。形状照 core::update::tests::support::TempDir。
- addon::pack 与 addon::tests 两族全部改用 TempRoot(含 fixtures::Fixture 持守卫、
multimc/fabric_instance 等返回守卫);4 GiB 稀疏文件的语义一字未改,只改"谁负责删"。
- 自证:pack/tests/mod.rs 三条用例(drop 删 / panic 也删 / 子树删父根)。
测试
- import_decisions.rs(11 例):预检清单、不打扰、逐项决策后的最终文件集、基线记包内哈希。
- roundtrip_three.rs(2 例):叠层 + 冲突夹具 import→export→import 三轮文件集逐字节一致,
以及「选保留就按设计分叉」的反向证据。
- pack/tests/limits.rs(5 例):单条/总量边界、读侧用同一批常量、写出即可读回。
证据:cargo fmt --check 干净;cargo clippy --workspace --all-targets -D warnings 干净;
cargo test --workspace --all-targets -j 4 → 2507 passed / 0 failed(51 个 test binary)。
|
||
|
|
134b0a2aa4 |
fix(packui): M113 r2 复审整改 —— RAII 在跑句柄、per-host 代次、注释与代码对齐
复审 r2(p2-3items / fix-then-merge)逐条整改。**P2-6 与 P3-3/4/5 按塔台指示不动**,只登记。 ## 必修 1:`PACK_RUNNING` 在早退路径不清零(跨功能泄漏) 改前:`fresh_cancel()` 置 bool,`super::finished()` 在 `block_on(import_pack(..))` **之后**才清; 探测失败 / 目标目录不可用两条 `return` 都在它之前 ⇒ bool 永久留在「在跑」。 后果:之后**任何**一次 `CancelInstall`(包括下载页的「取消安装」)①不再发 `InstallCancelled`、 ②还给内容库发 `PackEvent::Cancelled`(一句「导入还在后台跑」的假话)。 改后:`InFlight` **RAII 句柄**(`Drop` 减一)取代手工 `finished()`;计数用「每个 host 一格」的 `in_flight: usize` 而不是 bool(连点两次 Import 各自进出)。 `InFlight::begin(&env)` 在**派发点**创建、随线程移动到出口 ⇒ 正常收尾、两条早退、`?`、panic 展开**任何**退出路径都会减一,以后再加早退分支也不用记得补一行。 ## 必修 2:`dir_name` 注释与实测不一致 改前:注释与报告写「`Better MC` → `Better-MC`(不加哈希)」,而 `faithful` 只认 `[A-Za-z0-9_-]`, 空格不算 ⇒ 实际 `Better-MC-c7c9c149`;测试只钉了无空格形态。 改后:**选「空格算 faithful」这边**,注释、代码、测试三处对齐 —— 名字只含 ASCII 字母数字、 空格、`-`、`_` 时保持原样(空格折成 `-`);折过**别的**字符(中文、`.`、括号…)才补 `-<hash8>`。 测试钉住四例:`Better-MC` → 原样、`Better MC` → `Better-MC`(不加哈希)、`pack` → `pack`、 `Survival 1.20.1` → `Survival-1-20-1-<hash>`(折过 `.` 必须补)。 ## 必修 3:两处注释与代码相反 - `host/job.rs` 的 `HostTask::Pack`:原写「代次与 `HostTask::Install` 共用同一计数器」,改为 「pack 自己的一条(`PACK_EPOCH`),不与 `install_epoch` 共用」并写明为什么(P2-7 的教训)。 - `host/addon/pack/import.rs` 的 `target_of`:`<slug>` → `<dir_name>`,并注明中文名不会被折成同一个 `pack`。 ## 必修 4:两条 P3 - `04` 图里「Back」出现两次:有预检结果时字段区那行只留「预检」,`Back` 只由预览块给一个 (没有预检时才在字段区给 `Back`)。 - 导入侧 `rejected` 列表补 `bounded`(与 missing / conflicts 同一口径)。 ## 必修 5:app 级用例(P2-1 的护栏) 新增 `a_failed_import_releases_the_in_flight_flag_and_keeps_install_cancel_intact`: `CoreHost::headless` + 真派 `PackCommand::Import`(包不存在 ⇒ 探测早退)→ 泵到 `Probe` → 断言 `in_flight == 0` → 再派 `CancelInstall` → 断言**仍会**发 `InstallCancelled` 且**不发** `PackEvent::Cancelled`。 ## P3-6(裸静态量):按塔台要求正面回答并已改掉 不再用裸静态量:`PACK_EPOCH` / `PACK_RUNNING` / `PACK_CANCEL` 换成**按 host 键控**的一张表 (键 = `LauncherLayout::home()`),理由是实测撞到过串扰 —— `host/cancel_tests` 派一次 `CancelInstall` 就 bump 了全局代次,我这条在飞的导入事件被按代次丢掉(新用例因此红过)。 为什么不挪进 `CoreHost`:那要给 `host/mod.rs` 加字段(不在 scope)。这张表等价,`host/mod.rs` 一旦进 scope,搬过去只是把表删掉 —— 报告里也这么写了。 新增用例 `pack_epoch_is_per_host_and_is_its_own_counter` 钉住「另一个 host 的代次不受影响」。 ## 顺手的两条 - `pack/tests.rs` 加完用例后 429 行超线 ⇒ 夹具(临时目录 / 写文件 / 泵 host)拆到 `pack/tests/harness.rs`,`tests.rs` 回到 3xx 行。 - clippy `-D warnings` 抓到 `== false`、两处多余的 `&`、一个未用的再导出,都改掉了。 门禁:`cargo fmt --all --check` 通过;clippy 三 crate `--all-targets -- -D warnings` EXIT=0; `cargo test -p unlucky-app-iced --lib` **507 passed**;`cargo test -p unlucky-presenter --lib` **185 passed**(新增的 app 级用例在内,且是在**全量并行**下跑的);改动文件全部 ≤400 行。 |
||
|
|
99538e45b9 |
fix(packui): M113 r1 复审整改 —— 2 处「界面说假话」+ 7 条必修 P2
复审 r1(p1-2items / fix-then-merge)逐条整改。**P2-6(core 的本地化契约)按塔台指示本轮不动**,
已登记为独立 mission 的债。
## P1
1. **「Save to」是死输入框**:`ExportRequestView` 的唯一构造点硬编码 `target: None`,用户敲的路径
从不进请求。现在构造收成纯函数 `packui::model::export::request_of`(预检与确认共用同一份),
`target` 由字段去空白后给出(空 ⇒ `None`,交给 host 给缺省)。
2. **取消导入的 toast 撒谎**:下载页那条说「安装已取消、实例未创建」,而导入会在后台跑完并
**登记实例**。现在 `cancel_pack()` 回报「刚才有没有导入在跑」,`dispatch` 据此改发
`PackEvent::Cancelled`(不再发 `InstallCancelled`),界面与提示改成
「导入会在后台继续跑完 —— 本次结果作废;已写下的文件会留下」,并把这句话**留在屏上**
(`ImportState.cancelled` + 面板位)。
## P2 必修(7 条)
3. **`Dismiss` 误伤下载页安装**(跨功能污染,最高优先):pack 原来共用 `CoreHost::install_epoch`,
关一次导入面板会把下载页正在跑的安装静默丢掉。现在 pack 有自己的
`PACK_EPOCH`(模块级 `AtomicU64`),`handle` 的 pack 分支只比它,`Dismiss`/取消只动它。
4. **中文实例名塌成 `pack`**:新增 `convert::dir_name` —— 名字全 ASCII 安全字符时保持原样
(`Better MC` → `Better-MC`),一旦折过东西(中文/空格/符号)就补 `-<hash8>`(FNV-1a),
保证两个中文名得到不同目录。
5. **失败回滚后空目录挡住重试**:`target_of` 对「已存在但空」的目录**复用**(非空才拒),
并在失败回滚后清掉自己建的空目录;`failed_report` 的 `target` 语义改正
(以前把包路径塞进「目标实例目录」)。
6. **新实例流程下「以包为准」必然失败**:`can_overwrite` 追加 `page.instance.is_some()` ——
新建实例那条路上不再显示一个必然失败的按钮。
7. **阶段词打架且文档说修好了**:`progress.rs` 的模块文档改成与屏幕一致(粗/细两层 + 已知重叠),
并写清彻底解决要靠「安装面板提成共享组件」那条 finding。
8. **导入侧标题计数与行数对不上**:被拒 component 有了自己的标题(`The client does not support
these: {count}`);冲突标题改成中性的 `Conflicts: {count}`(逐条自带成因与处置)。
9. **预检「没进包」列表没有有界滚动**:13 条是真实形态。列表改成有界滚动(110px),
并且**把动作按钮提到摘要下面**(`Write the pack` / `Back`)—— 明细再长也顶不掉按钮。
10. **导入成功后不刷新实例列表**:`handle` 的 pack 分支在收到 `Report` 时调 `refresh_instances()`
(与 `InstallDone` 同一条),新实例不用等下一次无关的 `RefreshAll`。
## 证据
- 新增测试(都跑真管线/真函数):`the_save_to_field_really_reaches_the_request`、
`pack_epoch_is_its_own_counter`、`chinese_instance_names_get_distinct_directories`、
`a_failed_new_instance_directory_can_be_reused_for_retry`、
`overwrite_is_offered_only_when_importing_into_an_existing_instance`、
`cancelled_state_is_visible_and_says_the_import_keeps_running`。
- 出图重出 12 张(6 态 × 浅深),新增 `06-cancelled-*`;`04-export-*` 用 13 条 skipped 的真实形态。
门禁:`cargo fmt --all --check` 通过;`cargo clippy -p unlucky-app-iced -p unlucky-presenter
-p unlucky-i18n --all-targets -- -D warnings` EXIT=0;`cargo test -p unlucky-app-iced --lib`
507 passed;`cargo test -p unlucky-presenter --lib` 184 passed;`cargo test -p unlucky-i18n --lib`
24 passed;全部改动文件 ≤400 行。
|
||
|
|
5f09b8113a |
feat(packui): M113 phase 2 —— 导入导出真正接线(三个共享枚举各只多一个变体)
搬到 M109 rebase 后的新 main(26e764e)之后按**新 main 的形状**写一次,不写两遍。
## 接线
- `dto/pack/event.rs`(新):`PackCommand` / `PackEvent` / `PackState::apply`。
整条流程在三个最挤的共享枚举里各只占**一个**变体(`AppCommand::Pack` / `CoreEvent::Pack` /
`HostTask::Pack{event, epoch}`),子枚举承载细节 —— rebase 时不用替别人定序。
- `events.rs` / `job.rs` / `handle.rs` / `dispatch.rs` / `state.rs`:变体与分支,全部带
`// M113: import/export` 锚点。`state.rs` 只多 1 个字段 + 1 条 apply 分支(399 行)。
- `host/addon/**`(新目录,父文件只加 `mod pack;` + 再导出):
- `pack.rs`:`CoreHost::pack` 落点 + 取消标记 + 实例目录/快照/格式/缺省路径解析
- `pack/import.rs`:探测 → 目标目录 → `import_pack` → 报告(默认 KeepUser,显式才 Overwrite)
- `pack/export.rs`:预检(`scan_instance`,**不写盘**)与写盘(`export_instance`)
- `pack/convert.rs`:core 报告 → DTO,逐字段照搬(三态/五阶段/四处置/三成因/十一原因)
- `pack/diagnose.rs`:探测诊断「像 X 缺 Y」(按标记文件的有无,不看文件名)
- `app/update/content.rs`:导入 / 以包为准 / 预检 / 确认写盘 / 关面板真的派指令;
预检时先记下那份请求,确认时**原样发回**(防「预检 A、写 B」)。
- `pages/content/model.rs`:读 `state.pack`(不再是默认空态)。
- 面板接口按新形状改:`install_panel_model(&InstallProgressView, catalog) -> Option<Download>`
(`InstallView` 不再从 `pages/download` 导出)。
## 取消(如实)
`CancelInstall` 现在同时置导入的取消标记 + 代次 +1:面板立刻收回、旧事件按代次丢弃。
但 core 的 `import_pack` 没有取消令牌、进度回调也拿不到控制流 ⇒ **后台那次导入会跑完**。
所以「取消」的真实含义是「不再显示 + 结果作废」,已在模块文档与报告里写清,并单开 finding。
取消标记用 `Arc<AtomicBool>` 而不是裸静态量:裸静态量会在并行测试间泄漏取消状态(实测到过)。
## 图看出来的三处(塔台要求)
1. `no download source (CurseForge file API not wired)` → `no download source available yet`,
API 名只留在 DTO 的 detail 与日志里;顺带把 `sha512` / `projectID/fileID` 也改成用户话。
2. 右栏 `Installing modpacks is not available yet` → `Installing modpacks from the library into
an instance is not available yet`(那句在 i18n 词表里,不在 M109 页面代码里,所以我自己改)。
3. 结果面板接线后可以关:`Dismiss` / `AbortExport` 发 `PackCommand::Dismiss` → host 回
`PackEvent::Cleared` → 回到入口/浏览态,不再常驻。
## 证据(三条都是真跑,不是假数据)
- `bad_packs_get_a_named_diagnosis`:把文本改名成 .mrpack → 有具名诊断(不是静默、不 panic);
目录形态有 overrides/ 无 manifest.json → 说得出「缺哪个标记」。
- `a_real_pack_runs_through_the_pipeline_end_to_end`:造一个真 MultiMC 实例目录当包,
走 `import::run` 真路径,断言事件流里有进度 + 结果、格式=MutliMC、报告有写盘文件、文件真落地、
代次原样带回。
- `preview_counts_match_the_written_report`:同一实例上跑预检与 `export_instance`,
inline / skipped / remote **三项计数逐项相等**,且逐条路径的归类一致。
门禁:cargo check ✓ / clippy -D warnings(三 crate)EXIT=0 /
cargo test(app-iced 504 + 6 守卫,i18n 24,presenter 181)全绿 / rustfmt --check ✓ /
所有改动文件 ≤400 行(`state.rs` 399、`addon.rs` 106,pack 子目录最大 338)。
|