test(m170): 改对复审点出的两处证据陈述 + 收紧用例前提(零行为改动)
r1 复审 p2-2items(守卫本身判 clean,四条变异自跑确认有牙)。这一轮**只动注释与测试 前提,产品行为零变化**:`format/write.rs` 一字未改,`probe.rs` 保持上一提交的收窄结果。 p2-1 复审核正的事实:`one_file_over_the_threshold_...` 的 `kind()` **不区分闸的位置**。 它的源是**可读的** 8 KiB 真文件,闸被挪到 `File::open` 之后时写循环第二道闸 `Written::spend`(每块之后 `guard_with`)照样返回 `Limits`——所以它在「闸挪位置」的 变异下仍然绿(我这轮重跑变异 A 也复现:其余 4 条全绿)。原来那句「源文件能读时这里 拿到的是 pack_io」是错的,改成「可读源命中第二道闸」并把顺序的举证责任明确交给 `the_gate_runs_before_any_content_is_opened`。 p2-2 目录长度的实测数字之前钉错了:我早先那条探针用的是**短名**(`f0`..`f39`,7 条 才到 4096、40 条仍 4096),而用例里填的是 `filler-NN`。这轮按用例真实形状重测三组 (`std::fs::metadata`,同一台机器同一 `%TEMP%`): 短名 `f0`…`f39`:空 0 | 3 条 0 | 7 条 4096 | 20/32/39/40 条 4096 `filler-NN`: 空 0 | 3 条 4096 | 7 条 4096 | 20 条 8192 | 32 条 8192 | 39 条 12288 | 40 条 16384 两次独立跑同形状结果一致。所以「第几个条目起非零」没有平台无关答案(NTFS 目录索引按 4 KiB 一档分配,跟条目名长度与条数都有关),三处注释改成区间 + 一张实测表,并注明 「只钉 32 个 `filler-NN` 这个形状(本机 8192)」,不再硬凑一个好看的数字。 顺带做掉复审那条「不计入 p2 但建议收紧」:前提从 `declared > 0` 提到 **`limit > 清单 JSON 长度`**(`limit = declared.saturating_sub(1)`,`json_len` 由 `CURSEFORGE.render(&manifest)` 现算,本用例实测 220 字节)。否则某个对 32 个条目报得很 小的文件系统会让用例因「清单自己越线」而假绿。同时把 `declared - 1` 换成 `saturating_sub`,让长度为 0 时走到清楚的 assert 文案而不是整数下溢 panic。 把 `DIR_LENGTH_FILLERS` 临时改成 0 验证过这条 assert 是活的:红在这句上 (`声明长度 0 − 1 = 0 … 必须大于清单 JSON 自身长度(220)`)。 变异复跑(本机 Windows/NTFS,跑完已还原 write.rs): - A:`preflight` 挪到 `File::open(&source)` 之后 → 新用例**红**(left Io / right Limits), 其余 4 条仍绿(含上面那条可读源用例,正好印证 p2-1 的更正); - C:A 之上只留 `expect_err` / `!target.exists()` / `leftovers` → **5 条全绿**, 结构断言确属同义反复。 门禁:`cargo test -p unlucky-core --lib` 613 passed / 0 failed / 0 ignored; `cargo fmt --all --check` 干净;`cargo clippy -p unlucky-core --all-targets -- -D warnings` 干净;zip64.rs 345 行(<400)、probe.rs 45 行。
This commit is contained in:
1 parent
72767067f3
commit
add7f2e672
1 file changed
+46
-18
@@ -29,10 +29,11 @@
|
||||
//! # 「声明长度」必须自己造出来(M170)
|
||||
//!
|
||||
//! M166 的 `the_gate_runs_before_any_content_is_opened` 把源做成目录、量 `metadata().len()`,
|
||||
//! 量到 0 就 `return`。而**本机 NTFS 上空目录的长度就是 0**(实测:空目录 0,第 7 个条目起
|
||||
//! 4096;ext4 空目录本来就报 4096)——所以那条"行为证明"在 Windows 上一条断言都没执行,
|
||||
//! 只有 CI 的 ubuntu 那侧真跑。现在改成:先把目录填到长度非零,拿**这个长度减一字节**当
|
||||
//! 总量上限,并且前提不满足时 `assert!` 而不是 `return`——要么真判过东西,要么在这台机器上红。
|
||||
//! 量到 0 就 `return`。而**本机 NTFS 上空目录的长度就是 0**(实测:空目录 0;条目塞进去之后
|
||||
//! 才变成 4–16 KiB 量级,见 [`DIR_LENGTH_FILLERS`] 的注释;ext4 空目录直接报 4096)——所以
|
||||
//! 那条"行为证明"在 Windows 上一条断言都没执行,只有 CI 的 ubuntu 那侧真跑。现在改成:先把
|
||||
//! 目录填到长度**大于清单 JSON 自身**,拿**这个长度减一字节**当总量上限,并且前提不满足时
|
||||
//! `assert!` 而不是 `return`——要么真判过东西,要么在这台机器上红。
|
||||
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
@@ -51,9 +52,20 @@ const SMALL_TOTAL: u64 = 8 * 1024;
|
||||
const SMALL_ENTRY: u64 = 2 * 1024;
|
||||
const SMALL_ENTRIES: usize = 16;
|
||||
|
||||
/// 把目录填充到「`metadata().len()` 非零」要几个条目(本机 NTFS 实测第 7 个起报 4096、
|
||||
/// ext4 空目录就报 4096)。取 32 而不是临界值:不多花一个字节,也不卡在文件系统的条数
|
||||
/// 阈值上(见 `the_gate_runs_before_any_content_is_opened`)。
|
||||
/// 把目录填充到「`metadata().len()` 大到能当判据」用几个条目。
|
||||
///
|
||||
/// **这个数字不是阈值,是区间里挑的一个稳的点**:NTFS 的目录长度是索引分配的字节数,按 4 KiB
|
||||
/// 一档往上加,**跟条目名的长度和条目数都有关**——本机实测(`std::fs::metadata`,同一台机器、
|
||||
/// 同一个 `%TEMP%`):
|
||||
///
|
||||
/// | 条目名 | 空 | 3 条 | 7 条 | 20 条 | 32 条 | 39 条 | 40 条 |
|
||||
/// | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
/// | `f0`…`f39`(短名) | 0 | 0 | 4096 | 4096 | 4096 | 4096 | 4096 |
|
||||
/// | `filler-NN`(本用例的形状) | 0 | 4096 | 4096 | 8192 | **8192** | 12288 | 16384 |
|
||||
///
|
||||
/// 所以"第几个条目起非零"没有平台无关的答案,别在注释里钉死一个漂亮的数字:这里只钉
|
||||
/// **32 个 `filler-NN` 条目**这个形状(本机 8192),其余文件系统(ext4 空目录 4096、tmpfs
|
||||
/// 680 量级)由下面那条 `limit > 清单 JSON 长度` 的 `assert!` 现场把关——不满足就用例红。
|
||||
const DIR_LENGTH_FILLERS: usize = 32;
|
||||
|
||||
/// 上面那三个数组成的限额(`WriteLimits::new` 是 `#[cfg(test)]` 的测试入口)。
|
||||
@@ -136,9 +148,11 @@ fn one_file_over_the_threshold_is_refused_before_a_single_byte_is_read() {
|
||||
assert!(!target.exists(), "失败后目标路径不许存在");
|
||||
// 临时文件也不许留。**但这条不是"一个字节都没读"的证明**(M170 更正):失败分支自己会
|
||||
// `remove_file(&temp)`,所以它在任何失败路径上都成立,等于"确实返回了 Err"的同义反复。
|
||||
// 真有牙的判据是上面的 `kind()`:闸若被挪到读内容之后,源文件能读时这里拿到的是
|
||||
// `pack_io`。顺序本身由 `the_gate_runs_before_any_content_is_opened` 用"读不出来的源"
|
||||
// 正面证明(含反证)。
|
||||
// 上面的 `kind()` 也**不区分闸的位置**:这条用例的源是**可读的** 8 KiB 真文件,闸被挪到
|
||||
// `File::open` 之后时,写循环的第二道闸 `Written::spend`(`write.rs` 每块之后
|
||||
// `guard_with`)照样返回 `Limits`——所以本用例在"闸挪位置"的变异下仍然绿(复审 r1 实测,
|
||||
// 我自己的变异 A 表里"其余 4 条仍绿"就是同一件事)。**顺序只由
|
||||
// `the_gate_runs_before_any_content_is_opened` 用"读不出来的源"证明**(含常驻反证)。
|
||||
assert_eq!(leftovers(&root, &["overrides"]), Vec::<String>::new());
|
||||
}
|
||||
|
||||
@@ -211,7 +225,8 @@ fn the_gate_runs_before_any_content_is_opened() {
|
||||
let root = TempRoot::new("zip64-before-read");
|
||||
let directory = root.join("overrides/mods/not-a-file");
|
||||
std::fs::create_dir_all(&directory).expect("建目录");
|
||||
// 前提①:长度得**自己造**。NTFS 上空目录报 0(本机实测:空 0、第 7 个条目起 4096),
|
||||
// 长度得**自己造**:NTFS 上空目录报 0(本机实测:空 0;塞条目之后按 4 KiB 一档
|
||||
// 往上跳,`filler-NN` 形状 3 条 → 4096、32 条 → 8192,详见 `DIR_LENGTH_FILLERS`),
|
||||
// 直接量空目录会得到 0 —— r2 版本就是在这里 `return` 的,于是 Windows 上一条断言都没跑。
|
||||
for index in 0..DIR_LENGTH_FILLERS {
|
||||
write_file(&directory.join(format!("filler-{index:02}")), b"x");
|
||||
@@ -219,10 +234,26 @@ fn the_gate_runs_before_any_content_is_opened() {
|
||||
let declared = std::fs::metadata(&directory)
|
||||
.expect("stat 目录(这一步必须成功,否则区分不了两道闸)")
|
||||
.len();
|
||||
let manifest = manifest_of(vec![override_file("mods/not-a-file")]);
|
||||
let json_len = CURSEFORGE
|
||||
.render(&manifest)
|
||||
.expect("渲染清单(只是为了量它多长)")
|
||||
.len() as u64;
|
||||
// 上限 = 声明长度减一字节(`guard_with` 是 `>=`,声明尺寸自己就越线)。用
|
||||
// `saturating_sub` 而不是 `-`:目录长度报 0 时要走到下一条 `assert!` 的清楚信息,
|
||||
// 而不是一个整数下溢的 panic。
|
||||
let limit = declared.saturating_sub(1);
|
||||
// 前提①(r1 复审建议的收紧):上限必须**大于清单 JSON 自身长度**。`total` 是
|
||||
// `清单 + 声明尺寸`,若上限比清单还小,清单自己就能把 `total` 顶过线——用例照样绿,
|
||||
// 却悄悄失去对「总量闸漏算了覆盖条目的声明尺寸」那类洞的判别力(复审的变异 E)。
|
||||
// 本用例这份 CurseForge 清单渲染出来实测 **220 字节**(复审用另一份清单量到约 165,差别
|
||||
// 来自清单字段);ext4(4095)、tmpfs(679)、本机 NTFS(8191)都远在它之上。对 32 个
|
||||
// 条目报不到 220 字节的文件系统会让这条用例红——那是对的,它在那里证明不了声明尺寸承重
|
||||
// (把 `DIR_LENGTH_FILLERS` 临时改成 0 就能看到这句 assert 的话:`声明长度 0 − 1 = 0`)。
|
||||
assert!(
|
||||
declared > 0,
|
||||
"前提:目录的声明长度必须非零才有判据可造;{DIR_LENGTH_FILLERS} 个条目之后仍报 0,\
|
||||
说明该文件系统不报目录长度,这条用例在那里无法证明闸的顺序(**不许静默 return**)"
|
||||
limit > json_len,
|
||||
"前提:总量上限(目录声明长度 {declared} − 1 = {limit})必须大于清单 JSON 自身长度\
|
||||
({json_len}),否则拒写可能只是清单越线,声明尺寸没被证明承重(**不许静默 return**)"
|
||||
);
|
||||
|
||||
// 前提②:这条源**确实**读不出来,否则 `pack_limits` / `pack_io` 区分不了闸的位置。
|
||||
@@ -236,16 +267,13 @@ fn the_gate_runs_before_any_content_is_opened() {
|
||||
"前提:目录源必须读不出来,否则拿到 pack_limits 也说明不了闸在内容之前:{consumed:?}"
|
||||
);
|
||||
|
||||
// 总量上限 = 目录的**声明长度减一字节**:声明尺寸自己就越线(`guard_with` 是 `>=`),
|
||||
// 所以拒写的理由是这条覆盖条目的声明尺寸,而不是清单 JSON 顺带越线。
|
||||
// 单条目上限给 `u64::MAX`:不让 `guard_entry` 当替身——如果总量闸漏算了覆盖条目的声明
|
||||
// 尺寸,这里就不会拿到 `pack_limits`,而是一路走到读内容上拿到 `pack_io`(用例变红)。
|
||||
let limit = declared - 1;
|
||||
let target = root.join("out.zip");
|
||||
|
||||
let error = write_with_limits(
|
||||
&CURSEFORGE,
|
||||
&manifest_of(vec![override_file("mods/not-a-file")]),
|
||||
&manifest,
|
||||
PackPayload::from_root(&root),
|
||||
&target,
|
||||
WriteLimits::new(limit, u64::MAX, SMALL_ENTRIES),
|
||||
|
||||
Reference in new issue
Block a user