test(host): 断言字面量统一纯 ASCII,30 文件 1535 行

用户裁定「统一」。口径就是 AGENTS 铁律:字符串字面量必须纯 ASCII
(无 BOM 文件里奇数个汉字会吞掉结束引号,偶数才侥幸通过),中文只写注释。
四名队友按目录分四组并行、写范围互斥:net_socket 418 行(6 文件)、
console_log/sys_log_mcu/soe_store/cfg_file_store_mcu 268 行(4 文件)、
11 个存量套件 404 行 + adc_input 89 行、iec104 与 lftpd 与 NET 四套等 356 行
(9 文件)。runner 的 .py 文档串不在口径内(Python 源不经 ARMCC 代码页)。

两套口径必须分清:粗口径(按行切引号)普查 = 34 文件 / 1,552 行;严格口径
(状态机剥掉块/行注释与字符常量后再取字符串字面量)= 30 文件 / 1,535 行。
差值那 4 个文件的命中全在注释里(ns_peers.c、net_socket/stub/ns_rtos.h、
common/ch395f_fake.h、common/fatfs_fake.c)——按口径一处都不该改,改了等于
把中文从注释里搬走、正好违背口径。粗口径绝不能当交付判据,否则要么白改
注释、要么留下「这行为什么没改」的悬案。

Lead 独立终检(不采信自报)五条硬判据,结果 30 文件漂移 0:① 每文件 TC 号
集合与 HEAD 逐字相同 ② 断言宏调用点数不变 ③ 行数与 HEAD 完全相等(专抓
整文件回写把尾部含 main() 吃掉的截断,第 1 轮真发生过两次)④ 剥注释后代码区
非 ASCII 字符 = 0 ⑤ 字节卫生(BOM 与 HEAD 一致、无 CR、无 U+FFFD、单尾换行)。
另用「逐行把字面量整体替换成空串后与 HEAD 必须逐行相等」机械证明只改了字面量;
各套 --module 覆盖率与断言数逐字不变(net_socket 887/889·53/53、console_log
199/200·16/16、sys_log_mcu 324/326·13/13、adc_input 103/103、sys_clock 100/100)
→ 生产码零改动,基线不需重登。

闸拦下一处真实回归(我的验收器没抓到):ns_s6_edges.c 翻译时把一个上一轮
已整改成平实描述的编号又写回了 CHECK 标签。原因很清楚:「只改字面量」并不
等于「只改无害内容」,标签里完全可以带进未登记族的编号形态;而 Lead 的自动
验收器只比对了 TC 形 id 集合,未覆盖 L/E/N/S/TRAP 等族。教训:批量改标签的
守卫必须加一条「新串不得出现任何已登记前缀族之外的编号形态」,且结构不变式
只覆盖自己想到的维度时不能替代真闸。
Lead 自查另清掉 126 处写坏的反斜杠(JS→Python 转义层数翻倍,把 markdown
反引号写成反斜杠+反引号),恰好发生在记录这条教训的那一节里。

文档:docs/测试说明/主机单测覆盖率口径.md §9 新增「第 2 轮」小节(两套口径对照
表、五条终检判据、六条手法含「转换说明符序列必须逐位相同」取代「禁止裸 %」、
三条协作纪律含先改板再派工与临时文件带组前缀、后续项 3 条)+ 变更记录一行。

后续项(本轮不做):把剥注释词法扫描器收进 scripts 当门禁(需用户点头并先定
test/ 是否纳入 coding-standard);iec104 的简写编号归族(牵 id_registry,
不能顺手做);替换器加「编号形态白名单」校验。

gate: pre-commit run --all-files -> 39 hooks, 37 Passed; failed = mem-numbers +
mem-budget(受管快照与本树 .map 的漂移,属第 1 轮生产码改动的遗留,非本批
增量)。本批只碰 test/host/**,不进 Keil 编译面也不进模拟器 CMake,**RAM/Flash
增量 = 0**;实测水位 RW_IRAM1 57.4%、RW_CCM 87.9%,均低于 92% 红线。
id-check 0 台账外新账(可判定 84.63% > 基线 82.6%);test-coverage 33/33 全绿;
cppcheck / coding-standard / bsp-err / layer-deps / doc-links 全 Passed。
按仓内分支约定 --no-verify 提交,台账 --write 回填留待主工作树合并后。
This commit is contained in:
2026-10-24 20:57:46 +08:00
parent 5a806d8a26
commit 924ac3d428
31 changed files with 1626 additions and 1542 deletions
+33 -33
View File
@@ -276,11 +276,11 @@ static void tc_fatfs_path(void)
{
printf("-- TC-LFTPIO-001..004 to_fatfs_path --\n");
CHECK((strcmp(to_fatfs_path(NULL), "0:") == 0) &&
(strcmp(to_fatfs_path(""), "0:") == 0), "TC-LFTPIO-001 NULL/空串 -> 0:");
CHECK(strcmp(to_fatfs_path("/"), "0:") == 0, "TC-LFTPIO-002 根 -> 0:");
(strcmp(to_fatfs_path(""), "0:") == 0), "TC-LFTPIO-001 NULL/empty string -> 0:");
CHECK(strcmp(to_fatfs_path("/"), "0:") == 0, "TC-LFTPIO-002 root -> 0:");
CHECK(strcmp(to_fatfs_path("/a/b.txt"), "a/b.txt") == 0,
"TC-LFTPIO-003 去掉前导斜杠");
CHECK(strcmp(to_fatfs_path("a.txt"), "a.txt") == 0, "TC-LFTPIO-004 原样返回");
"TC-LFTPIO-003 the leading slash is stripped");
CHECK(strcmp(to_fatfs_path("a.txt"), "a.txt") == 0, "TC-LFTPIO-004 returned unchanged");
}
/* ===================== 路径规范化 ===================== */
@@ -289,32 +289,32 @@ static void tc_canon(void)
{
printf("-- TC-LFTPIO-010..022 canonicalize --\n");
reset_all();
CHECK(strcmp(canon("/dir", "f.txt"), "/dir/f.txt") == 0, "TC-LFTPIO-010 相对拼接");
CHECK(strcmp(canon("/dir", "/abs.txt"), "/abs.txt") == 0, "TC-LFTPIO-011 绝对忽略 base");
CHECK(strcmp(canon("/a/b", "../c"), "/a/c") == 0, "TC-LFTPIO-012 上跳一级");
CHECK(strcmp(canon("/dir", "f.txt"), "/dir/f.txt") == 0, "TC-LFTPIO-010 relative join");
CHECK(strcmp(canon("/dir", "/abs.txt"), "/abs.txt") == 0, "TC-LFTPIO-011 an absolute name ignores base");
CHECK(strcmp(canon("/a/b", "../c"), "/a/c") == 0, "TC-LFTPIO-012 go up one level");
CHECK(strcmp(canon("/a", "../../x"), "/x") == 0,
"TC-LFTPIO-013 上跳过头仍被钳在根内(安全属性)");
CHECK(strcmp(canon("/a", "."), "/a") == 0, "TC-LFTPIO-014 丢弃 . 段");
CHECK(strcmp(canon("/a", ".."), "/") == 0, "TC-LFTPIO-015 回退到根");
CHECK(strcmp(canon("/a//", "b"), "/a/b") == 0, "TC-LFTPIO-016 段间多余斜杠被折叠");
"TC-LFTPIO-013 over-shooting .. is still clamped inside the root (safety property)");
CHECK(strcmp(canon("/a", "."), "/a") == 0, "TC-LFTPIO-014 a . segment is dropped");
CHECK(strcmp(canon("/a", ".."), "/") == 0, "TC-LFTPIO-015 .. at the top falls back to the root");
CHECK(strcmp(canon("/a//", "b"), "/a/b") == 0, "TC-LFTPIO-016 redundant slashes between segments are collapsed");
CHECK(strcmp(canon("/dir", "//x"), "/x") == 0,
"TC-LFTPIO-016b name 以斜杠开头按**绝对路径**处理(忽略 base,仍钳在根内)");
CHECK(strcmp(canon(NULL, "x"), "/x") == 0, "TC-LFTPIO-017 base NULL 视作空串");
CHECK(strcmp(canon("/a", NULL), "/a") == 0, "TC-LFTPIO-018 name NULL 视作空串");
CHECK(strcmp(canon(NULL, NULL), "/") == 0, "TC-LFTPIO-019 两个 NULL -> /");
CHECK(strcmp(canon("/", ".."), "/") == 0, "TC-LFTPIO-020 根上加 .. 仍是根");
"TC-LFTPIO-016b a name starting with a slash is treated as an absolute path (base ignored, still clamped inside the root)");
CHECK(strcmp(canon(NULL, "x"), "/x") == 0, "TC-LFTPIO-017 a NULL base counts as an empty string");
CHECK(strcmp(canon("/a", NULL), "/a") == 0, "TC-LFTPIO-018 a NULL name counts as an empty string");
CHECK(strcmp(canon(NULL, NULL), "/") == 0, "TC-LFTPIO-019 both NULL -> /");
CHECK(strcmp(canon("/", ".."), "/") == 0, "TC-LFTPIO-020 .. on the root is still the root");
reset_all();
g_fail_at = 1;
CHECK(lftpd_io_canonicalize_path("/a", "b") == NULL,
"TC-LFTPIO-021 首块分配失败返回 NULL");
CHECK(g_alloc_calls == 1, "TC-LFTPIO-021b 失败后不再分配第二块");
"TC-LFTPIO-021 the first allocation failing returns NULL");
CHECK(g_alloc_calls == 1, "TC-LFTPIO-021b no second allocation is attempted after that failure");
reset_all();
g_fail_at = 2;
CHECK(lftpd_io_canonicalize_path("/a", "b") == NULL,
"TC-LFTPIO-022 结果缓冲分配失败返回 NULL");
CHECK(g_free_calls == 1, "TC-LFTPIO-022b 第一块已释放(不泄漏堆)");
"TC-LFTPIO-022 the result buffer allocation failing returns NULL");
CHECK(g_free_calls == 1, "TC-LFTPIO-022b the first block was freed (no heap leak)");
}
/* ===================== 打开文件:守卫 / 卷锁 / 错误通道 ===================== */
@@ -329,24 +329,24 @@ static void tc_open(void)
rc = lftpd_io_open_read("/a/b.txt");
CHECK((rc == 0) && (g_open_mode == FA_READ) &&
(strcmp(g_open_path, "a/b.txt") == 0),
"TC-LFTPIO-030 成功:FA_READ + 映射后的路径");
"TC-LFTPIO-030 success: FA_READ plus the mapped path");
reset_all();
s_file_open = 1;
rc = lftpd_io_open_read("/x");
CHECK((rc == -1) && (g_open_calls == 0) && (g_wait_calls == 0),
"TC-LFTPIO-031 单文件槽守卫:已打开时不碰卷锁与 FatFs");
"TC-LFTPIO-031 single-file slot guard: while a file is open it touches neither the volume lock nor FatFs");
reset_all();
g_open_rc = FR_NO_FILE;
rc = lftpd_io_open_read("/x");
CHECK((rc == -1) && (g_release_calls == 1) && (g_lfio_error_count == 1),
"TC-LFTPIO-032 打开失败:返回 -1 + 留痕 + 释放卷锁");
"TC-LFTPIO-032 open failure: return -1 + log + release the volume lock");
reset_all();
rc = lftpd_io_open_read("/x");
CHECK((rc == 0) && (g_release_calls == 0) && (s_file_open == 1),
"TC-LFTPIO-033 成功后卷锁不释放(传输期独占)");
"TC-LFTPIO-033 the volume lock stays held after success (exclusive for the whole transfer)");
reset_all();
g_wait_seq[0] = 0; /* 第一轮:卷被 defaultTask 占着 */
@@ -354,22 +354,22 @@ static void tc_open(void)
g_wait_seq[2] = 1; /* 第三轮:拿到 */
rc = lftpd_io_open_read("/x");
CHECK((rc == 0) && (g_beat_calls == 2) && (g_wait_calls == 3),
"TC-LFTPIO-034 等锁循环每轮喂看门狗(漏喂 = 整机复位)");
"TC-LFTPIO-034 the lock-wait loop feeds the watchdog every round (missing a feed resets the whole device)");
reset_all();
rc = lftpd_io_open_write("/up.bin");
CHECK((rc == 0) && (g_open_mode == (BYTE)(FA_WRITE | FA_CREATE_ALWAYS)),
"TC-LFTPIO-035 open_write 用覆盖写模式");
"TC-LFTPIO-035 open_write uses overwrite mode");
reset_all();
s_file_open = 1;
rc = lftpd_io_open_write("/up.bin");
CHECK((rc == -1) && (g_open_calls == 0), "TC-LFTPIO-036 open_write 同样有槽守卫");
CHECK((rc == -1) && (g_open_calls == 0), "TC-LFTPIO-036 open_write has the same slot guard");
reset_all();
g_open_rc = FR_DISK_ERR;
rc = lftpd_io_open_write("/up.bin");
CHECK((rc == -1) && (g_release_calls == 1), "TC-LFTPIO-037 open_write 失败也释放卷锁");
CHECK((rc == -1) && (g_release_calls == 1), "TC-LFTPIO-037 an open_write failure also releases the volume lock");
}
/* ===================== trim ===================== */
@@ -386,19 +386,19 @@ static void tc_trim(void)
(void)strcpy(buf, " PASS x");
r = lftpd_string_ltrim(buf);
CHECK((r != buf) && (strcmp(r, "PASS x") == 0) && (buf[0] == ' '),
"TC-LFTSTR-001 行首空白靠返回指针跳过,原缓冲不搬移");
"TC-LFTSTR-001 leading blanks are skipped via the returned pointer, the buffer itself is not moved");
(void)strcpy(buf, "NOOP");
r = lftpd_string_ltrim(buf);
CHECK((r == buf) && (strcmp(buf, "NOOP") == 0), "TC-LFTSTR-002 无前导空白返回同一指针");
CHECK((r == buf) && (strcmp(buf, "NOOP") == 0), "TC-LFTSTR-002 with no leading blanks the same pointer comes back");
(void)strcpy(buf, " USER admin ");
r = lftpd_string_ltrim(buf);
CHECK(strcmp(r, "USER admin ") == 0, "TC-LFTSTR-003 词中间空白保留");
CHECK(strcmp(r, "USER admin ") == 0, "TC-LFTSTR-003 blanks inside the text are kept");
(void)strcpy(buf, " \t\r\n");
r = lftpd_string_ltrim(buf);
CHECK(r[0] == '\0', "TC-LFTSTR-004 全空白 -> 空串");
CHECK(r[0] == '\0', "TC-LFTSTR-004 all blanks -> empty string");
/* 现状桩(不是期望行为):尾部循环从 strlen(p) 起步,那里是 '\0',
* isspace(0)=0 立刻退出 —— **尾部空白/CRLF 实际从未被裁**。今天无害是因为
@@ -411,7 +411,7 @@ static void tc_trim(void)
(void)strcpy(buf, "LIST ");
r = lftpd_string_ltrim(buf);
CHECK(strcmp(r, "LIST ") == 0,
"TC-LFTSTR-005 尾随空格必须保留(ltrim 语义,RFC 959 参数本体)");
"TC-LFTSTR-005 trailing spaces must survive (ltrim semantics, they belong to the RFC 959 argument)");
}
/* ===================== 入口 ===================== */