Files
wandering1 04997e48aa refactor(编号): test/host 与 App/Drivers 注释去测试层级号(73 站点 / 64 文件)
同族第二道:代码注释面。41 个 run_unit_test.py 文件头与引用行、common harness 与两份
README、4 个 *_test.c 文件头、App/** 14 处(含 BOM 文件 App/cfg/cfg_file.c)、
GD5F nand_ftl.c 1 处,共 73 站点 / 64 文件;措辞取「主机侧纯逻辑单元测试运行器
(纯标准库)」避免同行「主机侧」重复。

零行为改动(机器证据,不是口头):
- 17 个 .c/.h 剥掉注释后与 HEAD **逐字节相同**;
- 42 个 .py 走 ast.dump 比较、仅归一 docstring(真实字符串字面量参与比较,故旗标或
  输出文本被动过必然报红)→ 全部相等;behaviour_changed=0。
- py_compile 42/42;抽跑 watchdog 47/47、freq 46/46(动手前先取基线,改后同数);
  CR 字节 0、EF BF BD 0、BOM 与 HEAD 逐一一致、check_bom_drift rc=0。

刻意未动:6 处字符串字面量(FTP 应答 "215 UNIX Type: L8"、lftpd_test.c SYST 断言、
console_log_test.c 的 "L1\r\n"、phasor_test.c 的 "L2-in ring" 文案与 printf 标题)——
改了就是改测试输出/行为;UI 设计令牌层 L0(ui_tokens.h、ui_content_page.c)与
test/host/README.md 的算法层号是**另外的族**。

两条 SyntaxWarning(ftp_cfg / net_cfg 文件头 \S 转义)**HEAD 同样存在**,已对 HEAD 副本
复现证明非本次引入,未顺手改。
2026-10-28 17:52:55 +08:00

214 lines
8.3 KiB
C
Raw Permalink 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.
/*
* 模块名称:SOE 记录编解码主机侧纯逻辑单元测试(B9)
* 模块功能:离线验证 App/log/soe_rec.c——64B 定长记录构建/解析往返、
* 校验和硬门槛(撕裂/半写拒绝)、版本门槛、CP56Time2a 时标
* 编码正确性(与 iec104_asdu.c 的 CP56 解码交叉验证:同一算法族
* 两个实现互证)、IV 语义、钳位、保留区清零。
*
* 被测对象:真实源文件(#include 原样纳入):
* soe_rec.c(B9 记录编解码)
* iec104_asdu.c(交叉验证用 CP56 解码器,纯逻辑)
*
* 覆盖点:
* TC-SOE-001 构建/解析字段往返(type/elm/flags/seq/epoch/value)
* TC-SOE-002 校验和硬门槛:单字节翻转 → -3;版本篡改 → -2
* TC-SOE-003 CP56 编码正确性:已知 epoch → 期望字节序列;
* 与 iec104_cp56_to_epoch_ms 往返互证(含闰日 2024-02-29)
* TC-SOE-004 IV 语义:flags IV → CP56 byte2 bit7 置位 + epoch 字段 0
* TC-SOE-005 钳位:epoch < 2000 → 2000-01-01;epoch > 2127 上界 → 2127-12-31
* TC-SOE-006 结构不变量:size=64、保留区 [23..62] 恒 0、sum=sum8([0..62])
* TC-SOE-007 序号域:0 / 0xFFFFFFFF 边界往返(uint32 回绕语义)
*
* 通过:全部 TC PASS(main 返回 0)。
* 创建日期:2026-10-12(B9)
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
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 "soe_rec.c"
#include "iec104_asdu.c"
/* ---------------- TC-SOE-001 字段往返 ---------------- */
static void tc_roundtrip(void)
{
uint8_t rec[SOE_REC_SIZE];
soe_rec_t out;
int rc;
soe_rec_build(rec, (uint8_t)SOE_T_ELM_ASSERT, 2u, 0u, 12345u,
1760234567u, 0xDEADBEEFu);
rc = soe_rec_parse(rec, &out);
CHECK(rc == 0, "TC-SOE-001 parse ok");
CHECK(out.ver == SOE_REC_VER, "TC-SOE-001 ver");
CHECK(out.type == (uint8_t)SOE_T_ELM_ASSERT, "TC-SOE-001 type");
CHECK(out.elm_id == 2u, "TC-SOE-001 elm_id");
CHECK(out.flags == 0u, "TC-SOE-001 flags");
CHECK(out.seq == 12345u, "TC-SOE-001 seq");
CHECK(out.epoch_s == 1760234567u, "TC-SOE-001 epoch");
CHECK(out.value == 0xDEADBEEFu, "TC-SOE-001 value");
CHECK(out.cp56[6] == (uint8_t)((2025u - 2000u) & 0x7Fu), "TC-SOE-001 cp56 year=2025");
}
/* ---------------- TC-SOE-002 校验和/版本硬门槛 ---------------- */
static void tc_gates(void)
{
uint8_t rec[SOE_REC_SIZE];
soe_rec_t out;
uint32_t i;
int bad = 0;
soe_rec_build(rec, (uint8_t)SOE_T_ELM_RETURN, 0u, 0u, 7u, 1760234567u, 3u);
/* 每个数据字节单独翻转:解析必须全部拒绝(sum 覆盖 [0..62]) */
for (i = 0u; i < SOE_OFF_SUM; i++) {
uint8_t save = rec[i];
rec[i] ^= 0x01u;
if (soe_rec_parse(rec, &out) == 0) {
bad = 1;
}
rec[i] = save;
}
CHECK(bad == 0, "TC-SOE-002 any byte flip rejected");
CHECK(soe_rec_parse(rec, &out) == 0, "TC-SOE-002 intact parses ok");
rec[SOE_OFF_VER] = (uint8_t)(SOE_REC_VER + 1u); /* 版本篡改:sum 仍按新
* 字节算会自洽,但版本
* 门槛先拒 */
CHECK(soe_rec_parse(rec, &out) == -2, "TC-SOE-002 bad version -> -2");
CHECK(soe_rec_parse(NULL, &out) == -1, "TC-SOE-002 null args -> -1");
}
/* ---------------- TC-SOE-003 CP56 正确性 + 交叉往返 ---------------- */
static void tc_cp56(void)
{
uint8_t rec[SOE_REC_SIZE];
soe_rec_t out;
uint64_t back;
int rc;
/* 2026-10-12 12:34:56 UTC = 1791808496(手算:2026-10-12 = epoch 1791806400
*(当日 00:00 UTC)+ 12*3600+34*60+56 = 45296 → 1791851696? 以解码器为准,
* 这里用"往返互证 + 显式字段"双保险,不依赖手算) */
soe_rec_build(rec, (uint8_t)SOE_T_ELM_ASSERT, 0u, 0u, 1u, 1791808496u, 0u);
(void)soe_rec_parse(rec, &out);
/* 显式字段:12:34:56 → ms 域 = 56*1000(秒域时基如实编码,亚秒 0) */
CHECK(((uint32_t)out.cp56[0] | ((uint32_t)out.cp56[1] << 8)) == 56000u,
"TC-SOE-003 ms field = sec*1000 (56000)");
rc = iec104_cp56_to_epoch_ms(out.cp56, &back);
CHECK(rc == 0, "TC-SOE-003 cross decode ok");
CHECK(back == 1791808496000ULL, "TC-SOE-003 cross roundtrip exact");
/* 闰日 2024-02-29 00:00:00 UTC = 1709164800 */
soe_rec_build(rec, (uint8_t)SOE_T_SAMPLE_BLOCK, 0u, 0u, 2u, 1709164800u, 0u);
(void)soe_rec_parse(rec, &out);
CHECK(out.cp56[4] == 29u && out.cp56[5] == 2u && out.cp56[6] == 24u,
"TC-SOE-003 leap day fields");
rc = iec104_cp56_to_epoch_ms(out.cp56, &back);
CHECK((rc == 0) && (back == 1709164800000ULL), "TC-SOE-003 leap roundtrip");
/* 2000-01-01 00:00:00 边界 */
soe_rec_build(rec, (uint8_t)SOE_T_PLATE_IN, 0u, 0u, 3u, 946684800u, 0u);
(void)soe_rec_parse(rec, &out);
rc = iec104_cp56_to_epoch_ms(out.cp56, &back);
CHECK((rc == 0) && (back == 946684800000ULL), "TC-SOE-003 year-2000 boundary");
}
/* ---------------- TC-SOE-004 IV 语义 ---------------- */
static void tc_iv(void)
{
uint8_t rec[SOE_REC_SIZE];
soe_rec_t out;
uint64_t back;
soe_rec_build(rec, (uint8_t)SOE_T_ELM_ASSERT, 1u, SOE_FLG_IV, 9u, 0u, 1u);
(void)soe_rec_parse(rec, &out);
CHECK((out.cp56[2] & 0x80u) != 0u, "TC-SOE-004 cp56 IV bit set");
CHECK(out.epoch_s == 0u, "TC-SOE-004 epoch field zeroed");
CHECK(iec104_cp56_to_epoch_ms(out.cp56, &back) == -2,
"TC-SOE-004 cross decoder rejects IV");
}
/* ---------------- TC-SOE-005 钳位 ---------------- */
static void tc_clamp(void)
{
uint8_t rec[SOE_REC_SIZE];
soe_rec_t out;
uint64_t back;
soe_rec_build(rec, (uint8_t)SOE_T_ELM_ASSERT, 0u, 0u, 4u, 100u, 0u);
(void)soe_rec_parse(rec, &out);
CHECK((out.cp56[4] == 1u) && (out.cp56[5] == 1u) && (out.cp56[6] == 0u),
"TC-SOE-005 clamp low -> 2000-01-01");
(void)iec104_cp56_to_epoch_ms(out.cp56, &back);
CHECK(back == 946684800000ULL, "TC-SOE-005 clamp low exact");
/* epoch_s 为 uint32 → 最大 2106-02-07(0xFFFFFFFF),恒在 CP56 域内:
* 上界钳位是防御性死分支(epoch 域扩展时生效),此处断言精确往返 */
soe_rec_build(rec, (uint8_t)SOE_T_ELM_ASSERT, 0u, 0u, 5u, 0xFFFFFFFFu, 0u);
(void)soe_rec_parse(rec, &out);
CHECK(out.cp56[6] == 106u, "TC-SOE-005 uint32 max -> year 2106 (no clamp needed)");
(void)iec104_cp56_to_epoch_ms(out.cp56, &back);
CHECK(back == 4294967295000ULL, "TC-SOE-005 uint32 max exact roundtrip");
}
/* ---------------- TC-SOE-006 结构不变量 ---------------- */
static void tc_layout(void)
{
uint8_t rec[SOE_REC_SIZE];
uint32_t i;
uint8_t s = 0u;
int zero_ok = 1;
CHECK(SOE_REC_SIZE == 64u, "TC-SOE-006 record size 64");
soe_rec_build(rec, (uint8_t)SOE_T_SAMPLE_RESUME, SOE_ELM_NONE, 0u,
0u, 946684800u, 0u);
for (i = 23u; i <= 62u; i++) {
if (rec[i] != 0u) {
zero_ok = 0;
}
}
CHECK(zero_ok, "TC-SOE-006 reserved [23..62] zero");
for (i = 0u; i < SOE_OFF_SUM; i++) {
s = (uint8_t)(s + rec[i]);
}
CHECK(rec[SOE_OFF_SUM] == s, "TC-SOE-006 sum8 covers [0..62]");
CHECK(rec[SOE_OFF_ELM] == SOE_ELM_NONE, "TC-SOE-006 elm none");
}
/* ---------------- TC-SOE-007 序号边界 ---------------- */
static void tc_seq_edges(void)
{
uint8_t rec[SOE_REC_SIZE];
soe_rec_t out;
soe_rec_build(rec, (uint8_t)SOE_T_ELM_ASSERT, 0u, 0u, 0u, 946684800u, 0u);
CHECK((soe_rec_parse(rec, &out) == 0) && (out.seq == 0u), "TC-SOE-007 seq 0");
soe_rec_build(rec, (uint8_t)SOE_T_ELM_ASSERT, 0u, 0u, 0xFFFFFFFFu,
946684800u, 0u);
CHECK((soe_rec_parse(rec, &out) == 0) && (out.seq == 0xFFFFFFFFu),
"TC-SOE-007 seq 0xFFFFFFFF");
}
int main(void)
{
printf("=== SOE Record Codec Host Tests ===\n");
tc_roundtrip();
tc_gates();
tc_cp56();
tc_iv();
tc_clamp();
tc_layout();
tc_seq_edges();
printf("=== SOE Tests: %d/%d PASSED (failed=%d) ===\n",
s_passed, s_total, s_failed);
return (s_failed == 0) ? 0 : 1;
}