Files
STM32F4-Base/test/host/vol_lock/vol_lock_test.c
T
wandering1 bda7ab2b50 refactor(编号): 16 个测试模块码单词化 + 门禁识别面连带放宽
批 2(用户授权改名 + 「新名要一眼看懂」裁定)
- 16 个有歧义模块码单词化,共 1,146 处随 scripts/rename_ids.py 改写:
  AI→ADCIN、AH→ADCHEALTH、PS→PROTSET、PE→PROTELEM、PH→PHASOR、
  TS→TIMESYNC、VL→VOLLOCK、NP→NANDPORT、CFG→CONFIG、CLK→SYSCLK、
  WD→WATCHDOG、STO→STORAGE、SH→SHA256、FA→FTPAUTH、FR→FREQ、104→IEC104
- 不动 NET/GD5F/RTC/SOE/UI/ADC/LOG/RS485/TOOL(已是单词、芯片名或业界通用词,
  改了纯属 churn)
- 别名按全局登记:这批字面量已完全限定(TC-<码>-<nnn>),不存在裸形多义
- 映射表存档 scripts/rename_maps/batch2.json 作审计轨迹;§6 模块码表同步

连带修门禁(关键教训)
- 模块码涨到 9 位(ADCHEALTH/WATCHDOG)后,候选正则与族正则的 {0,4} 上界
  会让改名后的号**整批从门禁眼里消失**:实测编号字面量从 8271 掉到 7315、
  悬空假降 490 处。已放宽到 {0,9} 并写进 §6 注记
- 结论:改名批次必须连带复核识别面,只改数据不改判据 = 静默放行;
  这次是靠「可判定比例跌破基线即失败」那条线拦下来的

现状与遗留
- 校验 1 未登记前缀 0 处;可判定比例 82.71%(基线 82.6%,只涨不跌成立)
- 遗留:7 个模块的「单元测试说明.md」仍缺(PROTSET 116、NANDPORT 109、
  PROTELEM 65、FREQ 48、FTPAUTH 45、SHA256 25、SOE 14 处悬空)——改名不解决
  缺文档,处置方式待议
- 遗留:T-2/T-3/T-4/T-6 历史任务号悬空(别名表未收全,属本工具批次漏项)
- 未改任何 C/H 可编译语义(仅注释与宏名内的编号),故不需 @build
2026-10-21 09:53:28 +08:00

225 lines
9.3 KiB
C
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.
/*
* 模块名称:Volume Lock(卷锁)主机侧单元测试
* 模块功能:在 PC 上对 App/fs/vol_lock.c 做离线验证(3 个公开函数全覆盖)。
*
* 被测对象:真实源文件 ../../../App/fs/vol_lock.c(以 #include 原样纳入,
* 保证测的是生产代码而非副本)。平台依赖由本文件提供的**可注入桩**实现:
* - cmsis_os.h -> osMutexNew / osMutexWait / osMutexRelease
* (可注入 创建失败 / 等待超时 / 获取成功)
*
* 为什么要主机侧单测(而不是只用板端套件):
* 1. 本模块最要紧的语义是 **fail-open**(锁创建失败时 `vol_lock_wait` 必须返回 1
* 且**绝不阻塞**)——这是"defaultTask 喂狗点绝不能因等锁而停摆"的最后一道保险。
* 真机上 `osMutexNew` 失败几乎不可能复现(需堆耗尽),板端套件无法构造。
* 2. 幂等性(重复 `vol_lock_init` 不得重复创建)、等待超时透传、未创建时 release 无害,
* 都是纯状态逻辑,主机侧一次钉死。
* 3. 不占 MCU 的 RAM,毫秒级完成。
*
* 覆盖点:
* TC-VOLLOCK-001 init 首次调用创建互斥量并返回 0
* TC-VOLLOCK-002 init 幂等(重复调用不再创建)
* TC-VOLLOCK-003 init 在 osMutexNew 失败时返回 -1
* TC-VOLLOCK-004 init 失败后可重试成功(s_lock 未被污染)
* TC-VOLLOCK-005 wait 成功后返回 1,且把 timeout 原样透传给 osMutexWait
* TC-VOLLOCK-006 wait 超时(osMutexWait != osOK)返回 0
* TC-VOLLOCK-007 wait 在锁未创建时自动 init,再正常获取
* TC-VOLLOCK-008 ⭐ fail-open:锁未创建且创建失败 -> 返回 1 且**不调用** osMutexWait
* TC-VOLLOCK-009 fail-open 之后 release 无害(s_lock 仍为 NULL,不调用 osMutexRelease)
* TC-VOLLOCK-010 已创建时 wait 不再调用 osMutexNew
* TC-VOLLOCK-011 release 在已持锁时调用 osMutexRelease 一次
* TC-VOLLOCK-012 release 在锁未创建时静默返回(不崩溃、不调用)
* TC-VOLLOCK-013 完整周期 init -> wait -> release -> wait -> release
* TC-VOLLOCK-014 wait(0) 的 timeout 透传为 0(defaultTask 的 try 语义)
*
* 通过:全部 TC PASS(main 返回 0)。
* 入口:int main(void),返回 0=PASS / 1=FAIL(由 run 脚本判读)。
*
* 创建日期:2026-10-10
*/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
/* ===================== 平台桩(先纳入,覆盖真实头) ===================== */
#include "cmsis_os.h"
/* ===================== CMSIS-RTOS 互斥量桩 =====================
* s_new_fail osMutexNew 返回 NULL(注入创建失败)
* s_wait_ok 0 -> osMutexWait 返回 osErrorTimeout(注入超时)
* s_fake_mutex 成功创建时返回的伪句柄(非 NULL 即可)
* s_last_timeout 记录 osMutexWait 收到的 millisec(断言透传)
*/
static int s_new_calls;
static int s_new_fail;
static int s_wait_calls;
static int s_wait_ok;
static int s_release_calls;
static uint32_t s_last_timeout;
static void *s_fake_mutex = (void *)0x1234;
osMutexId_t osMutexNew(const osMutexAttr_t *attr) {
(void)attr;
s_new_calls++;
return s_new_fail ? NULL : (osMutexId_t)s_fake_mutex;
}
osStatus_t osMutexWait(osMutexId_t mutex_id, uint32_t millisec) {
(void)mutex_id;
s_wait_calls++;
s_last_timeout = millisec;
return s_wait_ok ? osOK : osErrorTimeout;
}
osStatus_t osMutexRelease(osMutexId_t mutex_id) {
(void)mutex_id;
s_release_calls++;
return osOK;
}
/* ===================== 断言 ===================== */
static int s_total = 0, s_passed = 0, s_failed = 0;
#define CHECK(cond, name) do { \
s_total++; \
if (cond) { s_passed++; printf(" [PASS] %s\n", name); } \
else { s_failed++; printf(" [FAIL] %s (line %d)\n", name, __LINE__); } \
} while (0)
/* ===================== 被测代码(原样纳入生产源) ===================== */
#include "vol_lock.c"
/* ===================== 工具 ===================== */
/*
* 函数功能:把全部桩与模块静态态复位到"刚上电"初值
* 入口参数:无
* 返 回 值:无
* 限定条件:无
* 函数说明:`s_lock` 是 vol_lock.c 的文件静态量(源文件已被纳入同一编译单元),
* 直接置 NULL 等价于"未初始化过"——这是白盒单测的可接受代价。
*/
static void fakes_reset(void) {
s_new_calls = 0;
s_new_fail = 0;
s_wait_calls = 0;
s_wait_ok = 1;
s_release_calls = 0;
s_last_timeout = 0xFFFFFFFFu;
s_fake_mutex = (void *)0x1234;
s_lock = NULL;
}
/* ===================== TC-VOLLOCK-001..004:vol_lock_init ===================== */
static void tc_init(void) {
fakes_reset();
CHECK(vol_lock_init() == 0, "TC-VOLLOCK-001 init 首次调用返回 0");
CHECK(s_new_calls == 1, "TC-VOLLOCK-001b init 创建互斥量一次(osMutexNew)");
CHECK(s_lock != NULL, "TC-VOLLOCK-001c 模块静态态已持有句柄");
CHECK(vol_lock_init() == 0, "TC-VOLLOCK-002 init 重复调用仍返回 0(幂等)");
CHECK(s_new_calls == 1, "TC-VOLLOCK-002b 幂等:不再创建第二个互斥量");
fakes_reset();
s_new_fail = 1;
CHECK(vol_lock_init() == -1, "TC-VOLLOCK-003 osMutexNew 失败 -> init 返回 -1");
CHECK(s_lock == NULL, "TC-VOLLOCK-003b 失败时不留下半初始化句柄");
s_new_fail = 0;
CHECK(vol_lock_init() == 0, "TC-VOLLOCK-004 失败后重试可成功(返回 0)");
CHECK(s_new_calls == 2, "TC-VOLLOCK-004b 重试确实再次调用 osMutexNew");
}
/* ===================== TC-VOLLOCK-005..010:vol_lock_wait ===================== */
static void tc_wait(void) {
/* --- 已创建:超时值原样透传 --- */
fakes_reset();
(void)vol_lock_init();
CHECK(vol_lock_wait(100U) == 1, "TC-VOLLOCK-005 wait 获取成功返回 1");
CHECK(s_wait_calls == 1, "TC-VOLLOCK-005b osMutexWait 被调用一次");
CHECK(s_last_timeout == 100U, "TC-VOLLOCK-005c timeout 原样透传给 osMutexWait");
fakes_reset();
(void)vol_lock_init();
CHECK(vol_lock_wait(0U) == 1, "TC-VOLLOCK-014 wait(0) 获取成功返回 1(try 语义)");
CHECK(s_last_timeout == 0U, "TC-VOLLOCK-014b wait(0) 的 timeout 透传为 0");
/* --- 超时:osMutexWait != osOK -> 返回 0 --- */
fakes_reset();
(void)vol_lock_init();
s_wait_ok = 0;
CHECK(vol_lock_wait(50U) == 0, "TC-VOLLOCK-006 osMutexWait 非 osOK -> wait 返回 0");
CHECK(s_wait_calls == 1, "TC-VOLLOCK-006b 超时路径确实尝试过等待");
/* --- 锁未创建:自动 init 后正常获取 --- */
fakes_reset();
CHECK(vol_lock_wait(10U) == 1, "TC-VOLLOCK-007 锁未创建时 wait 自动 init 并获取");
CHECK(s_new_calls == 1, "TC-VOLLOCK-007b 自动 init 创建了互斥量");
CHECK(s_wait_calls == 1, "TC-VOLLOCK-007c 随后执行了一次等待");
/* --- ⭐ fail-open:创建失败必须返回 1 且绝不阻塞 --- */
fakes_reset();
s_new_fail = 1;
CHECK(vol_lock_wait(0U) == 1, "TC-VOLLOCK-008 fail-open:锁创建失败仍返回 1(不阻塞喂狗点)");
CHECK(s_wait_calls == 0, "TC-VOLLOCK-008b fail-open 时**不调用** osMutexWait(零阻塞)");
CHECK(s_lock == NULL, "TC-VOLLOCK-008c fail-open 后仍无句柄");
/* --- fail-open 之后 release 无害 --- */
vol_lock_release();
CHECK(s_release_calls == 0, "TC-VOLLOCK-009 fail-open 后 release 不调用 osMutexRelease(判空静默)");
/* --- 已创建时 wait 不再创建 --- */
fakes_reset();
(void)vol_lock_init();
(void)vol_lock_wait(0U);
(void)vol_lock_wait(0U);
CHECK(s_new_calls == 1, "TC-VOLLOCK-010 多次 wait 不重复创建互斥量");
CHECK(s_wait_calls == 2, "TC-VOLLOCK-010b 每次 wait 各等待一次");
}
/* ===================== TC-VOLLOCK-011..013:vol_lock_release ===================== */
static void tc_release(void) {
/* --- 已持锁:释放一次 --- */
fakes_reset();
(void)vol_lock_init();
(void)vol_lock_wait(0U);
vol_lock_release();
CHECK(s_release_calls == 1, "TC-VOLLOCK-011 已持锁时 release 调用 osMutexRelease 一次");
/* --- 锁未创建:静默返回 --- */
fakes_reset();
vol_lock_release();
CHECK(s_release_calls == 0, "TC-VOLLOCK-012 锁未创建时 release 静默返回(不调用、不崩溃)");
/* --- 完整周期 --- */
fakes_reset();
CHECK(vol_lock_init() == 0, "TC-VOLLOCK-013 周期:init -> 0");
CHECK(vol_lock_wait(0U) == 1, "TC-VOLLOCK-013b 周期:wait -> 1");
vol_lock_release();
CHECK(vol_lock_wait(100U) == 1, "TC-VOLLOCK-013c 周期:再 wait -> 1(可重复获取)");
vol_lock_release();
CHECK(s_new_calls == 1, "TC-VOLLOCK-013d 周期:全程只创建一次互斥量");
CHECK(s_release_calls == 2, "TC-VOLLOCK-013e 周期:释放两次");
CHECK(s_last_timeout == 100U, "TC-VOLLOCK-013f 周期:第二次 wait 的 timeout 透传正确");
}
/* ===================== main ===================== */
int main(void) {
printf("=== Volume Lock Tests ===\n");
printf("-- vol_lock_init --\n");
tc_init();
printf("-- vol_lock_wait (incl. fail-open) --\n");
tc_wait();
printf("-- vol_lock_release --\n");
tc_release();
printf("\n=== Volume Lock Tests: %d/%d PASSED (failed=%d) ===\n",
s_passed, s_total, s_failed);
return (s_failed == 0) ? 0 : 1;
}