Files
STM32F4-Base/test/host/sys_log_mcu/run_unit_test.py
T
wandering1 0f7b5a87fb chore(编号): 存量编号全量转语义短语,门禁识别面与语料同批重打
登记簿、文档与代码注释里的存量字母加数字编号(N-x / S-x / T-x / E-x / A-x / 卡号)
一律改写为语义短语,净删 2,028 处引用;删除基线与待确认、并行分道方案、排期详述、
采集保护收尾 4 篇(内容已并入 docs/任务规划/README 与登记簿)。

门禁机器与三张账同批重打:check_ids.py 新增变更记录行豁免与第二根护栏
undecidable_total_ceiling;check_test_traceability.py 只加 stdout/stderr UTF-8
reconfigure(TC_RE 未动);check_cubemx_survival.py 的 --write 固定 newline=LF;
id_registry / id_aliases(+19 条 retired-to-phrase)/ id_known_backlog 重打。

test/ 56 个文件同步 TC 标签语义化,其中 18 个 TU 改了 CHECK / printf 里的标签文字,
是本批唯一改变构建产物的文件集合。

为什么合成一笔、不拆四笔(实测撞闸,非偏好):pre-commit 跑钩子前会 stash 未 staged
部分,拆开后中间提交必然是「新规则 + 旧语料」——实跑可判定 85.15% < 基线 86.00%、
不可判定 1,453 > 上限 1,166,编号门禁直接拦下。编号识别面与它描述的语料只能同码移动。

实跑基线(工作区全量):754 文件 / 8,230 处编号字面量 / 可判定 7,079 = 86.01%
(基线 86.0%,容差 0.1pp)/ 不可判定绝对数 1,151(上限 1,166)/ 校验 1-6 错误级 0 /
告警 = 悬空 2(App/algo/freq.c 的 S2,属 §4.4 同号不同义)+ 孤儿 50。

基线从 84.0 抬到 86.0 的理由见 docs/门禁说明/id-check.md §7.6:本批真删了 2,028 处
引用,可判定面随真实内容上升,不是把分母做小。同批新增排除 docs/硬件平台/ 语料,使
识别面少 264 处——芯片手册位号与网表料号不属编号空间(registry 有 excluded_paths_note)。

⚠ 余量只剩 0.11pp(约 10 处)与绝对数 15 处:把「可判定的引用」改成散文会立刻撞第二
根护栏。另遗留一项:id_aliases 里 retired 的 N / S / T / A 别名没有 scope(U1~U5 有),
今后有人新写一个 A-4,校验 3 不会响,只能靠人,建议下批补 scope。

固件不变证据:115 个 .c/.h 剥注释并归一字符串后与 HEAD 逐字节全等,残留代码差异 0;
App / Src / Drivers 的字符串字面量零改动。@build 0 错误 0 警告;40/40 主机套件、2,889
断言全通过;覆盖率 line 89.2% / func 92.6%;pre-commit 48 钩子 --all-files 全绿;
check_doc_links --all 通过(176 文档,章节引用无法判定 0);逐字节卫生核过:14 个带
BOM 文件与 HEAD 一致、0 个 CR、0 个 U+FFFD,字符串字面量纯 ASCII。

.cubemx_survival_baseline.json 的 watchdog-module 哈希 56bf27 变 2bb051 属合法:
Src/freertos.c 的 USER CODE 区内只有注释文字随编号改写,代码未变。

同批带上体积同码凭证与 RAM 台账行(@build 之后盖戳):RW_IRAM1 79,008 B / 60.3%,
RW_CCM 57,376 B / 87.9%,阈值 92%。
2026-10-26 12:28:14 +08:00

73 lines
4.0 KiB
Python
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.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
r"""运行/自检记录落盘后端(sys_log_mcu)的主机侧单元测试运行器(L0.5,纯标准库)。
做什么:
用本机 MSVC 编译 test/host/sys_log_mcu/sys_log_mcu_test.c(以 #include 纳入**生产源**
App/log/sys_log_mcu.c,并纳入共享假卷 test/host/common/fatfs_fake.c),在 PC 上离线
跑 64B 定长记录格式、单条/批量两条写路径、syslog_drain 的 preboot+队列排空与
"失败回投不丢条目"、日志读写放大收口的 8 条滑动读窗口 + 写者代数失效、挂载幂等与
上电/重启分类、sys_log_evidence 的 wdev.log 取证通道,失败即非 0 退出。
工具链与判定委托 test/host/common/unit_test_harness.py。
为什么要主机侧单测(而不是只用台架):
1. **FA_OPEN_APPEND 不含 FA_WRITE**(存储架构 §10.1 第 11 条):漏写位时 f_open
会成功、文件甚至被创建出来,只有 f_write 恒返 FR_DENIED —— 真机表现是
"/log 写失败 x5/开机",2026-09-20 才定位到根因。假卷按真实 ff.c 判 fp->flag
就能把这条做成必然失败的断言。
2. **f_write 簇分配失败 = FR_OK + 短写**(同一页第 11 条后半):只判 fr != FR_OK
等于把丢记录当写成功;短写注入只有假卷给得出。
3. **挂载幂等**:syslog_mcu_mounted 曾在建 /log 失败后永久 no-op(运行期日志与自检
全部滞留),改成幂等 + 重试后又必须防"每 2s 刷一条同样错误" → 要注入 mkdir
失败才测得到,且必须能数出"到底留痕几次"。
4. **读窗口 try 语义 / 日志读写放大收口的读侧**:窗口"命中即零 I/O"与"写者一落地就失效"是一枚硬币两面,
只做前者会把陈旧读当正确、只做后者会退回逐条 open/seek/close 的旧实现。
桩的边界:ff.h / sys_log.h / soe_store.h / sys_clock.h / vol_lock.h 用**真实头**;
main.h 是桩,只为截断 sys_clock.h -> sd2506.h -> main.h -> stm32f4xx_hal.h;
cmsis_os.h / dbg_log.h 本目录桩。soe_store_drain() 用**计数桩**(本套只断言
"每次 drain 各调一次"这个 B9 挂接点,SOE 落盘本身归 test/host/soe_store/)。
FatFs 由 test/host/common/fatfs_fake.c 给 RAM 实现(**显式纳入,不用目录通配**)。
sys_clock_* / vol_lock_* / osMessageQueue* 强符号桩写在本测试单元里。
用法(仓库根目录):
& '.venv\\Scripts\\python.exe' test/host/sys_log_mcu/run_unit_test.py
退出码:0=PASS / 1=测试失败 / 2=环境错误(找不到编译器 / 编译失败 / 无汇总行)。
设计依据:docs/子系统设计/运行与自检记录设计说明.md §4.4/§4.5/§5.3、
docs/架构设计/STM32F4-Base存储架构说明.md §10.1 第 10/11 条、
docs/测试说明/主机单测覆盖率口径.md §9 P2 第 2 项
创建日期:2026-10-24
"""
import os
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(HERE)))
sys.path.insert(0, os.path.join(ROOT, "test", "host", "common"))
from unit_test_harness import TestBuild, run_host_unit_test # noqa: E402
# 顺序敏感:stub 必须最先(覆盖 cmsis_os.h / dbg_log.h / main.h),其余全用真实头。
# 假卷 fatfs_fake.c 由测试单元 #include 纳入(同 ch395f_fake.c 惯例,显式列文件、不通配)。
BUILD = TestBuild(
name="Sys Log Mcu Tests",
src=os.path.join(HERE, "sys_log_mcu_test.c"),
include_dirs=[
os.path.join(HERE, "stub"),
os.path.join(ROOT, "test", "host", "common"),
os.path.join(ROOT, "App", "log"),
os.path.join(ROOT, "App", "util"),
os.path.join(ROOT, "App", "fs"),
os.path.join(ROOT, "Drivers", "BSP"),
os.path.join(ROOT, "Drivers", "BSP", "SD2506"),
os.path.join(ROOT, "Lib", "FatFs"),
HERE,
],
summary_marker="=== Sys Log Mcu Tests:",
)
if __name__ == "__main__":
sys.exit(run_host_unit_test(BUILD))