7efa58d9a1
分两段(用户裁定 2026-11-02):先做地址迁移,再把同类字面量收敛成
单一真源,以后再改地址只动一处。
一、IP 迁移 192.168.1.100 → 192.168.31.100(gw 同步 → .31.1)
- 工厂默认:Drivers/BSP/NETCFG/net_cfg.c 的 NET_CFG_DEF_IP/_GW
- 注释/文档示例、主机与板级测试、台架 py 脚本、两份 sim_config.ini
- 字节序教学点同步换算 hex:0x6401A8C0 → 0x641FA8C0(内存
C0 A8 1F 64),旧版示例引文与历史 Trap 记录按冻结纪律不动
- UI 黄金基准:网口设置 4 态 + shot_info 共 5 张 .bin 由
check_content_pages.py --update 重刷(渲染值 IP 变了,属预期变更)
- 保留判定:客户点表引用(IEC104/继电保护规划)、芯片手册摘录、
日期冻结的变更记录行、已退役台架档案 MDK-ARM/retracted_bench_*
二、单一真源收口(A+B+C+D,用户批准全做)
- 新建 test/host/bench_defaults.py:台架 py 默认值唯一定义点
(DEVICE_IP/DEVICE_GW/PC_IP/NET_BCAST),22 个脚本改为引用;
MDK-ARM 两探针与 scripts/board_ui_nav.py 经 sys.path shim 引入
- test/board/net_test_task.h 新增 TEST_PC_PEER_IP,板测三处
PHASE*_REMOTE_IP 改引该宏(保留 -D 覆盖);Phase1 断言改
memcmp 数组比较,数组各只出现一次
- simulator/tests/check_content_pages.py 从 net_cfg.c 正则解析
NET_CFG_DEF_* 推导期望值(解析失败即报错、不静默兜底),
脚本内台架 IP 字面量清零
- 装饰性注释去具体值(net_cfg.h/net_socket.h/net_socket.c/
net_socket_sim.c);字节序教学 hex 对刻意保留为唯一写值点
- 刻意不动:主机单测夹具 IP(逐文件钉住的任意值是正确测试
实践)、文档正文 IP(那是文档内容本身)
顺带:scripts/gen_task_table.py 规范串清掉「,B5」——编号清退提交
72b4b094 改了受管区没重跑生成器,task-table 门禁因此一直红
验证:产品 @build 0/0;CH395F 套件开关构建 0/0 后还原产品态;
6 套主机单测 rc=0;content_pages(含宏推导路径)/菜单 8 态/
推流位比对全 PASS;bom-drift 99 无漂移;doc-links/coding-standard/
cppcheck 绿;py_compile 23 文件 + --help 冒烟(含 shim 绝对路径)
550 lines
22 KiB
C
550 lines
22 KiB
C
/*
|
||
* 模块名称:网络配置(NETCFG)主机侧单元测试
|
||
* 模块功能:在 PC 上离线验证 Drivers/BSP/NETCFG/net_cfg.c 的**编排与状态机**:
|
||
* 出厂默认有效性、注册幂等与注册内容、load 失败仍返回 0 的"RAM 恒有效"
|
||
* 契约、跨任务保存请求交接(IDLE/WAIT/OK/FAIL)、写盘期间新请求不丢、
|
||
* 写盘失败不得触发网络重建、MAC 三态优先级、IPv4 定长格式化。
|
||
*
|
||
* 被测对象:真实源文件 ../../../Drivers/BSP/NETCFG/net_cfg.c(#include 原样纳入,
|
||
* 保证测的是生产代码而非副本)。桩只有三处,都写在下面:
|
||
* - stub/dbg_log.h:日志宏 → 计数(本目录);
|
||
* - 配置文件引擎:用**真实头** App/cfg/cfg_file.h(接口契约原汁,引擎
|
||
* 一改签名本测试立刻编译期报错),被调的三个函数
|
||
* cfg_file_register / cfg_file_load / cfg_file_save 由本文件给出
|
||
* **可控实现**(同 test/host/nand_port/ 的路子);
|
||
* - ftp_cfg_register:只计数不实现——[ftp] 段自身的解析/默认值/默认
|
||
* 口令判定归 test/host/ftp_cfg/ 模块,两边不重复也不互相顶包。
|
||
* (不能把 ftp_cfg.c 也 include 进来:两个 .c 都有 static s_cfg,
|
||
* 同一个 TU 里直接重定义。)
|
||
* ⚠ 引擎的解析/写盘/沉降契约已由 test/host/cfg_file/(103 条)覆盖,
|
||
* 本模块刻意不重测引擎,只测 net_cfg.c 这一层的编排与交接。
|
||
*
|
||
* 为什么要主机侧单测(而不是只靠台架):
|
||
* 1. 保存链是**跨任务交接**(uiTask 投递 net_cfg_save_request → defaultTask 执行
|
||
* net_cfg_save_run → 成功才 net_cfg_request_apply → netTask 按网络代次重建监听)。
|
||
* "写盘期间 UI 又按了一次 ENTER"与"写盘失败却去重建网络"这两条,真机上要靠
|
||
* 手速 + 故障注入才碰得到;错了就是"改了 IP 不生效"或"保存失败网络被无端重建"
|
||
* 这类现场极难定位的问题——主机侧用假引擎的写盘钩子直接构造时序。
|
||
* 2. MAC 三态优先级(存储非零 > 芯片回写影子 > 全零)决定 UI 显示值和下发给
|
||
* CH395F 的 MAC;写错会**静默用错 MAC**(同网段 MAC 冲突,症状是偶发丢包)。
|
||
* 3. net_cfg_fmt_ipv4 的**定长 15 字符(每段 %03u 补零)**是 UI 编辑光标按固定偏移定位
|
||
* 字节的前提;哪天被"优化"成不补零,光标就错位——纯字符串行为,一次钉死。
|
||
*
|
||
* 覆盖点:
|
||
* —— 出厂默认(load 之前也必须可用,TC-NETCFG-001..004)——
|
||
* TC-NETCFG-001 未 load 时 ip = 工厂默认 192.168.31.100
|
||
* TC-NETCFG-002 未 load 时 mask = 255.255.255.0
|
||
* TC-NETCFG-003 未 load 时 gw = 192.168.31.1
|
||
* TC-NETCFG-004 未 load 时 mac = 全零(哨兵 = 不覆盖芯片内置 MAC)
|
||
* —— 注册与 load(TC-NETCFG-005..011)——
|
||
* TC-NETCFG-005 首次 load 注册 [network] 段恰好一次(且 load 返回 0)
|
||
* TC-NETCFG-006 首次 load 一并注册 [ftp] 段(FTP 鉴权:所有受管段先注册、后 load)
|
||
* TC-NETCFG-007 重复 load 不重复注册(幂等;否则引擎按"段名重复"拒登 → 静默失配)
|
||
* TC-NETCFG-008 注册描述符内容:段名/尺寸/键数/默认值指针/变更回调/数据指针 = RAM 副本
|
||
* TC-NETCFG-009 load 成功:返回 0 且留一条 INFO、不误报 ERROR
|
||
* TC-NETCFG-010 **引擎报 -1 时 net_cfg_load 仍返回 0**(RAM 恒有效,不带崩网络启动)
|
||
* TC-NETCFG-011 load 失败必须留 ERROR(不许静默回落默认值)
|
||
* —— RAM 副本读写(TC-NETCFG-012..013)——
|
||
* TC-NETCFG-012 net_cfg_set 写入被 net_cfg_get 读回
|
||
* TC-NETCFG-013 net_cfg_set(NULL) 不改写(UI 侧误传不得毁掉配置)
|
||
* —— 直接保存(TC-NETCFG-014..015)——
|
||
* TC-NETCFG-014 引擎写盘成功 → net_cfg_save 返回 0
|
||
* TC-NETCFG-015 引擎写盘失败 → net_cfg_save 返回 -1 并留痕(错误码没被吞成 0)
|
||
* —— 跨任务保存交接状态机(TC-NETCFG-020..033,UI 保存行显示的就是它)——
|
||
* TC-NETCFG-020 初始态 = IDLE
|
||
* TC-NETCFG-021 save_request 后置 WAIT
|
||
* TC-NETCFG-022 save_request 经已注册回调唤醒属主任务(计数 +1)
|
||
* TC-NETCFG-023 未注册回调时 save_request 不崩、请求照样挂起
|
||
* TC-NETCFG-024a 有请求时 save_run 确实写盘一次(024 的前置)
|
||
* TC-NETCFG-024 无请求时空醒 → 返回 0 且**不写盘**(2s 周期轮询不刷 Flash)
|
||
* TC-NETCFG-025 save_run 成功后 pending 清零
|
||
* TC-NETCFG-026 save_run 成功后状态 = OK
|
||
* TC-NETCFG-027 save_run 成功后触发一次"立即应用"
|
||
* TC-NETCFG-028 **写盘进行中**状态仍是 WAIT(写盘前先清 pending,靠 busy 撑住;
|
||
* 否则这段窗口会退回上一轮的 OK/IDLE,用户看不到反馈只会再按一次)
|
||
* TC-NETCFG-029a **写盘期间投递的新请求不丢**:本轮结束后仍为 WAIT
|
||
* TC-NETCFG-029b 下一轮真的再写一次(不是只把状态挂在那里)
|
||
* TC-NETCFG-030 写盘失败 → 状态 = FAIL
|
||
* TC-NETCFG-031 **写盘失败不得触发"立即应用"**(内存值还没落盘,不能当已生效)
|
||
* TC-NETCFG-032 未注册应用回调时 request_apply 是空操作(模拟器就是这条路)
|
||
* TC-NETCFG-033a 应用回调注册后可被触发
|
||
* TC-NETCFG-033b 传 NULL 注销后不再被触发
|
||
* —— MAC 三态优先级(TC-NETCFG-040..045)——
|
||
* TC-NETCFG-040 存储 MAC 非零 → 用存储值(即使芯片已回写影子)
|
||
* TC-NETCFG-041 存储全零 + 芯片已回写 → 用影子值
|
||
* TC-NETCFG-042 存储全零 + 未回写 → 全零
|
||
* TC-NETCFG-043 set_active_mac(NULL) 不得把影子置为有效
|
||
* TC-NETCFG-044 set_active_mac 正常回写后影子生效
|
||
* TC-NETCFG-045 get_mac(NULL) 安全返回
|
||
* —— IPv4 定长格式化(TC-NETCFG-050..056,UI 光标定位依赖)——
|
||
* TC-NETCFG-050 常规地址渲染为定长 15 字符 "192.168.001.100"
|
||
* TC-NETCFG-051 全零地址补零为 "000.000.000.000"(仍是 15 字符,偏移不变)
|
||
* TC-NETCFG-052 255 边界 "255.255.255.255"
|
||
* TC-NETCFG-053 p_ip 为 NULL:不写缓冲
|
||
* TC-NETCFG-054 p_buf 为 NULL:不崩
|
||
* TC-NETCFG-055 len 为 0:不写缓冲
|
||
* TC-NETCFG-056 len 不足:截断但**必须 NUL 结尾**且不越界(UI 缓冲防线)
|
||
*
|
||
* 用例顺序有依赖(模块静态状态无法整体复位):出厂默认组必须最先跑(任何 load 之前);
|
||
* IDLE 组在任何 save_request 之前;需要干净初态处显式复位白盒量并注明原因。
|
||
*
|
||
* 通过:全部 TC PASS(main 返回 0)。
|
||
* 入口:int main(void),返回 0=PASS / 1=FAIL(由 run 脚本判读)。
|
||
*
|
||
* 创建日期:2026-10-23
|
||
*/
|
||
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
/* 真实引擎头(接口原汁)+ 被测生产源(原样纳入);dbg_log.h 命中本目录 stub */
|
||
#include "cfg_file.h"
|
||
#include "net_cfg.h"
|
||
|
||
#include "net_cfg.c"
|
||
|
||
/* ===================== 断言框架 ===================== */
|
||
|
||
static int s_total;
|
||
static int s_passed;
|
||
static int s_failed;
|
||
|
||
#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)
|
||
|
||
/* ===================== dbg_log 桩的计数定义 ===================== */
|
||
|
||
int g_netcfg_info_count;
|
||
int g_netcfg_error_count;
|
||
int g_netcfg_debug_count;
|
||
|
||
static void logs_clear(void)
|
||
{
|
||
g_netcfg_info_count = 0;
|
||
g_netcfg_error_count = 0;
|
||
g_netcfg_debug_count = 0;
|
||
}
|
||
|
||
/* ===================== 假引擎(可控实现;原型来自真实 cfg_file.h) ========== */
|
||
|
||
/* 注册描述符在 net_cfg.c 里是栈上临时量,必须**按值留档**后才可比对 */
|
||
typedef struct {
|
||
char name[CFG_FILE_MAX_NAME_LEN + 1u];
|
||
const void *p_data;
|
||
const void *p_defaults;
|
||
size_t data_size;
|
||
uint8_t key_count;
|
||
void (*p_cb)(void);
|
||
} fake_reg_t;
|
||
|
||
static fake_reg_t g_regs[CFG_FILE_MAX_SECTIONS];
|
||
static int g_reg_n;
|
||
static int g_ftp_reg_calls; /* ftp_cfg_register 被 net_cfg_load 调了几次 */
|
||
static int g_load_calls;
|
||
static int g_save_calls;
|
||
static int g_apply_calls;
|
||
static int g_req_cb_calls;
|
||
static int g_load_rc; /* 注入:cfg_file_load 返回值 */
|
||
static int g_save_rc; /* 注入:cfg_file_save 返回值 */
|
||
static int g_state_during_save = -1;/* 写盘钩子里读到的交接状态 */
|
||
static int g_requeue_during_save; /* 1 = 写盘中再投递一次请求 */
|
||
static void (*g_save_hook)(void); /* 写盘时刻回调(构造真机难复现的时序) */
|
||
|
||
static void fake_copy_name(char *dst, const char *src, size_t cap)
|
||
{
|
||
size_t i;
|
||
|
||
for (i = 0u; (i + 1u < cap) && (src[i] != '\0'); i++) {
|
||
dst[i] = src[i];
|
||
}
|
||
dst[i] = '\0';
|
||
}
|
||
|
||
int cfg_file_register(const cfg_section_reg_t *p_reg)
|
||
{
|
||
fake_reg_t *rec;
|
||
|
||
if ((p_reg == NULL) || (p_reg->name == NULL)) {
|
||
return -1;
|
||
}
|
||
if ((size_t)g_reg_n >= (size_t)CFG_FILE_MAX_SECTIONS) {
|
||
return -1;
|
||
}
|
||
rec = &g_regs[g_reg_n];
|
||
fake_copy_name(rec->name, p_reg->name, sizeof(rec->name));
|
||
rec->p_data = p_reg->p_data;
|
||
rec->p_defaults = p_reg->p_defaults;
|
||
rec->data_size = p_reg->data_size;
|
||
rec->key_count = p_reg->key_count;
|
||
rec->p_cb = p_reg->p_cb;
|
||
g_reg_n++;
|
||
return 0;
|
||
}
|
||
|
||
int cfg_file_load(void)
|
||
{
|
||
g_load_calls++;
|
||
return g_load_rc;
|
||
}
|
||
|
||
int cfg_file_save(void)
|
||
{
|
||
g_save_calls++;
|
||
if (g_save_hook != NULL) {
|
||
g_save_hook();
|
||
}
|
||
return g_save_rc;
|
||
}
|
||
|
||
/* [ftp] 段归 test/host/ftp_cfg/ 模块测;这里只验证"net_cfg_load 确实带上了它" */
|
||
void ftp_cfg_register(void)
|
||
{
|
||
g_ftp_reg_calls++;
|
||
}
|
||
|
||
static void fake_reset(void)
|
||
{
|
||
g_reg_n = 0;
|
||
g_ftp_reg_calls = 0;
|
||
g_load_calls = 0;
|
||
g_save_calls = 0;
|
||
g_apply_calls = 0;
|
||
g_req_cb_calls = 0;
|
||
g_load_rc = 0;
|
||
g_save_rc = 0;
|
||
g_state_during_save = -1;
|
||
g_requeue_during_save = 0;
|
||
g_save_hook = NULL;
|
||
logs_clear();
|
||
}
|
||
|
||
static int mac_eq(const uint8_t *p_a, const uint8_t *p_b)
|
||
{
|
||
return memcmp(p_a, p_b, 6u) == 0;
|
||
}
|
||
|
||
static int bytes_eq(const uint8_t *p_a, const uint8_t *p_b, size_t n)
|
||
{
|
||
return memcmp(p_a, p_b, n) == 0;
|
||
}
|
||
|
||
/* ===================== 回调 ===================== */
|
||
|
||
static void apply_cb(void)
|
||
{
|
||
g_apply_calls++;
|
||
}
|
||
|
||
static void req_cb(void)
|
||
{
|
||
g_req_cb_calls++;
|
||
}
|
||
|
||
/* 写盘时刻钩子:记录当时的交接状态,并按开关模拟"写盘期间 UI 又按了一次 ENTER" */
|
||
static void save_hook_probe(void)
|
||
{
|
||
g_state_during_save = net_cfg_save_state();
|
||
if (g_requeue_during_save != 0) {
|
||
(void)net_cfg_save_request();
|
||
}
|
||
}
|
||
|
||
/* ===================== 出厂默认(必须在任何 load 之前跑) ===================== */
|
||
|
||
static void tc_defaults(void)
|
||
{
|
||
const net_cfg_t *p = net_cfg_get();
|
||
static const uint8_t def_ip[4] = { 192u, 168u, 31u, 100u };
|
||
static const uint8_t def_mask[4] = { 255u, 255u, 255u, 0u };
|
||
static const uint8_t def_gw[4] = { 192u, 168u, 31u, 1u };
|
||
static const uint8_t zero[6] = { 0u, 0u, 0u, 0u, 0u, 0u };
|
||
|
||
printf("-- TC-NETCFG-001..004 defaults before load --\n");
|
||
CHECK(bytes_eq(p->ip, def_ip, 4u), "TC-NETCFG-001 ip = 192.168.31.100");
|
||
CHECK(bytes_eq(p->mask, def_mask, 4u), "TC-NETCFG-002 mask = 255.255.255.0");
|
||
CHECK(bytes_eq(p->gw, def_gw, 4u), "TC-NETCFG-003 gw = 192.168.31.1");
|
||
CHECK(mac_eq(p->mac, zero), "TC-NETCFG-004 mac = all zero (chip built-in sentinel)");
|
||
}
|
||
|
||
/* ===================== 注册与 load 契约 ===================== */
|
||
|
||
static void tc_register_load(void)
|
||
{
|
||
int rc;
|
||
|
||
printf("-- TC-NETCFG-005..011 register + load --\n");
|
||
fake_reset();
|
||
rc = net_cfg_load();
|
||
CHECK((rc == 0) && (g_reg_n == 1) && (strcmp(g_regs[0].name, "network") == 0),
|
||
"TC-NETCFG-005 first load registers [network] exactly once");
|
||
CHECK((g_ftp_reg_calls == 1) && (g_load_calls == 1),
|
||
"TC-NETCFG-006 also registers [ftp], registered before load");
|
||
|
||
rc = net_cfg_load();
|
||
CHECK((rc == 0) && (g_reg_n == 1) && (g_ftp_reg_calls == 2),
|
||
"TC-NETCFG-007 repeated load does not re-register [network]");
|
||
|
||
CHECK((strcmp(g_regs[0].name, "network") == 0) &&
|
||
(g_regs[0].data_size == sizeof(net_cfg_t)) &&
|
||
(g_regs[0].key_count == 4u) &&
|
||
(g_regs[0].p_defaults != NULL) &&
|
||
(g_regs[0].p_cb != NULL) &&
|
||
(g_regs[0].p_data == (const void *)&s_cfg),
|
||
"TC-NETCFG-008 registration content: size/key count/defaults/callback/data pointer = RAM copy");
|
||
|
||
fake_reset();
|
||
rc = net_cfg_load();
|
||
CHECK((rc == 0) && (g_netcfg_info_count == 1) && (g_netcfg_error_count == 0),
|
||
"TC-NETCFG-009 load success: returns 0 + one INFO line");
|
||
|
||
fake_reset();
|
||
g_load_rc = -1;
|
||
rc = net_cfg_load();
|
||
CHECK(rc == 0, "TC-NETCFG-010 engine -1 still returns 0 (RAM copy is always valid, by contract)");
|
||
CHECK(g_netcfg_error_count == 1, "TC-NETCFG-011 load failure must leave an ERROR line");
|
||
}
|
||
|
||
/* ===================== RAM 副本读写 ===================== */
|
||
|
||
static void tc_set_get(void)
|
||
{
|
||
net_cfg_t c;
|
||
static const uint8_t ip[4] = { 10u, 0u, 0u, 7u };
|
||
static const uint8_t mac[6] = { 0x02u, 0x11u, 0x22u, 0x33u, 0x44u, 0x55u };
|
||
|
||
printf("-- TC-NETCFG-012..013 set/get --\n");
|
||
c = *net_cfg_get();
|
||
memcpy(c.ip, ip, 4u);
|
||
memcpy(c.mac, mac, 6u);
|
||
net_cfg_set(&c);
|
||
CHECK(bytes_eq(net_cfg_get()->ip, ip, 4u) &&
|
||
mac_eq(net_cfg_get()->mac, mac), "TC-NETCFG-012 set is read back by get");
|
||
|
||
net_cfg_set(NULL);
|
||
CHECK(bytes_eq(net_cfg_get()->ip, ip, 4u), "TC-NETCFG-013 set(NULL) rewrites nothing");
|
||
}
|
||
|
||
/* ===================== 直接保存 ===================== */
|
||
|
||
static void tc_save_direct(void)
|
||
{
|
||
int rc;
|
||
|
||
printf("-- TC-NETCFG-014..015 save --\n");
|
||
fake_reset();
|
||
rc = net_cfg_save();
|
||
CHECK((rc == 0) && (g_save_calls == 1), "TC-NETCFG-014 write success returns 0");
|
||
|
||
fake_reset();
|
||
g_save_rc = -1;
|
||
rc = net_cfg_save();
|
||
CHECK((rc == -1) && (g_netcfg_error_count == 1),
|
||
"TC-NETCFG-015 write failure returns -1 and leaves a trace");
|
||
}
|
||
|
||
/* ===================== 跨任务保存交接状态机 ===================== */
|
||
|
||
static void tc_save_handoff(void)
|
||
{
|
||
int rc;
|
||
|
||
printf("-- TC-NETCFG-020..033 save handoff state machine --\n");
|
||
fake_reset();
|
||
net_cfg_set_apply_cb(NULL);
|
||
net_cfg_set_save_req_cb(NULL);
|
||
s_save_pending = 0u; /* 白盒复位:上一组的 save() 不碰 pending/result,此处 */
|
||
s_save_result = 0u; /* 只是防止将来有人调整后漏出脏态 */
|
||
CHECK(net_cfg_save_state() == NET_CFG_SAVE_ST_IDLE, "TC-NETCFG-020 initial state IDLE");
|
||
|
||
CHECK((net_cfg_save_request() == 0) &&
|
||
(net_cfg_save_state() == NET_CFG_SAVE_ST_WAIT), "TC-NETCFG-021 WAIT after posting");
|
||
|
||
net_cfg_set_save_req_cb(req_cb);
|
||
g_req_cb_calls = 0;
|
||
(void)net_cfg_save_request();
|
||
CHECK(g_req_cb_calls == 1, "TC-NETCFG-022 posting wakes the owner task through the callback");
|
||
|
||
net_cfg_set_save_req_cb(NULL);
|
||
(void)net_cfg_save_request();
|
||
CHECK((g_req_cb_calls == 1) &&
|
||
(net_cfg_save_state() == NET_CFG_SAVE_ST_WAIT),
|
||
"TC-NETCFG-023 no callback registered -> no-op, request still pending");
|
||
|
||
g_save_rc = 0;
|
||
g_save_calls = 0;
|
||
rc = net_cfg_save_run();
|
||
CHECK((rc == 0) && (g_save_calls == 1), "TC-NETCFG-024a with a pending request it really writes once");
|
||
g_save_calls = 0;
|
||
rc = net_cfg_save_run();
|
||
CHECK((rc == 0) && (g_save_calls == 0),
|
||
"TC-NETCFG-024 no request: empty wake does not write (2 s polling never flushes Flash)");
|
||
|
||
(void)net_cfg_save_request();
|
||
net_cfg_set_apply_cb(apply_cb);
|
||
g_apply_calls = 0;
|
||
g_save_rc = 0;
|
||
rc = net_cfg_save_run();
|
||
CHECK((rc == 0) && (s_save_pending == 0u), "TC-NETCFG-025 pending cleared after success");
|
||
CHECK((net_cfg_save_state() == NET_CFG_SAVE_ST_OK) && (s_save_busy == 0u),
|
||
"TC-NETCFG-026 state OK after success and the write flag is cleared");
|
||
CHECK(g_apply_calls == 1, "TC-NETCFG-027 success triggers one immediate apply");
|
||
|
||
(void)net_cfg_save_request();
|
||
g_save_hook = save_hook_probe;
|
||
g_state_during_save = -1;
|
||
g_apply_calls = 0;
|
||
g_save_rc = 0;
|
||
rc = net_cfg_save_run();
|
||
CHECK((rc == 0) && (g_state_during_save == (int)NET_CFG_SAVE_ST_WAIT),
|
||
"TC-NETCFG-028 state stays WAIT while the write is in progress");
|
||
|
||
/* 写盘期间再投递一次:本轮不得把它当已处理,下轮必须真的再写 */
|
||
g_requeue_during_save = 1;
|
||
(void)net_cfg_save_request();
|
||
g_save_calls = 0;
|
||
rc = net_cfg_save_run();
|
||
CHECK((rc == 0) && (net_cfg_save_state() == NET_CFG_SAVE_ST_WAIT),
|
||
"TC-NETCFG-029a a new request during the write is not lost");
|
||
g_requeue_during_save = 0;
|
||
g_save_hook = NULL;
|
||
g_save_calls = 0;
|
||
rc = net_cfg_save_run();
|
||
CHECK((rc == 0) && (g_save_calls == 1), "TC-NETCFG-029b the next round really writes once more");
|
||
|
||
/* 写盘失败:置 FAIL 且**不得**触发应用(内存值没落盘,网络不能重建) */
|
||
s_save_pending = 0u;
|
||
s_save_result = 0u;
|
||
g_apply_calls = 0;
|
||
(void)net_cfg_save_request();
|
||
g_save_rc = -1;
|
||
rc = net_cfg_save_run();
|
||
CHECK((rc == -1) && (net_cfg_save_state() == NET_CFG_SAVE_ST_FAIL) &&
|
||
(s_save_busy == 0u),
|
||
"TC-NETCFG-030 failure sets FAIL and clears the write flag (else UI sticks in WAIT forever)");
|
||
CHECK(g_apply_calls == 0, "TC-NETCFG-031 write failure must not trigger an immediate apply");
|
||
|
||
net_cfg_set_apply_cb(NULL);
|
||
g_apply_calls = 0;
|
||
net_cfg_request_apply();
|
||
CHECK(g_apply_calls == 0, "TC-NETCFG-032 apply is a no-op while no callback is registered");
|
||
|
||
net_cfg_set_apply_cb(apply_cb);
|
||
net_cfg_request_apply();
|
||
CHECK(g_apply_calls == 1, "TC-NETCFG-033a after registering it can be triggered");
|
||
net_cfg_set_apply_cb(NULL);
|
||
net_cfg_request_apply();
|
||
CHECK(g_apply_calls == 1, "TC-NETCFG-033b after unregistering it is no longer triggered");
|
||
}
|
||
|
||
/* ===================== MAC 三态优先级 ===================== */
|
||
|
||
static void tc_mac_priority(void)
|
||
{
|
||
net_cfg_t c;
|
||
uint8_t out[6];
|
||
static const uint8_t stored[6] = { 0xAAu, 0xBBu, 0xCCu, 0x11u, 0x22u, 0x33u };
|
||
static const uint8_t shadow[6] = { 0x02u, 0x00u, 0x00u, 0x01u, 0x02u, 0x03u };
|
||
static const uint8_t zero[6] = { 0u, 0u, 0u, 0u, 0u, 0u };
|
||
|
||
printf("-- TC-NETCFG-040..045 mac display priority --\n");
|
||
|
||
c = *net_cfg_get();
|
||
memcpy(c.mac, stored, 6u);
|
||
net_cfg_set(&c);
|
||
net_cfg_set_active_mac(shadow);
|
||
memset(out, 0xFF, sizeof(out));
|
||
net_cfg_get_mac(out);
|
||
CHECK(mac_eq(out, stored), "TC-NETCFG-040 stored non-zero takes precedence over the chip shadow");
|
||
|
||
c = *net_cfg_get();
|
||
memcpy(c.mac, zero, 6u);
|
||
net_cfg_set(&c);
|
||
memset(out, 0xFF, sizeof(out));
|
||
net_cfg_get_mac(out);
|
||
CHECK(mac_eq(out, shadow), "TC-NETCFG-041 stored all zero -> use the chip shadow write-back");
|
||
|
||
s_active_valid = 0u; /* 白盒:回到"芯片还没回写"的上电初态 */
|
||
memset(out, 0xFF, sizeof(out));
|
||
net_cfg_get_mac(out);
|
||
CHECK(mac_eq(out, zero), "TC-NETCFG-042 neither available -> all zero");
|
||
|
||
net_cfg_set_active_mac(NULL);
|
||
CHECK(s_active_valid == 0u, "TC-NETCFG-043 NULL write-back must not mark the shadow valid");
|
||
|
||
net_cfg_set_active_mac(shadow);
|
||
memset(out, 0xFF, sizeof(out));
|
||
net_cfg_get_mac(out);
|
||
CHECK(mac_eq(out, shadow), "TC-NETCFG-044 shadow becomes active after a normal write-back");
|
||
|
||
net_cfg_get_mac(NULL);
|
||
CHECK(1, "TC-NETCFG-045 get_mac(NULL) returns safely");
|
||
}
|
||
|
||
/* ===================== IPv4 定长格式化 ===================== */
|
||
|
||
static void tc_fmt_ipv4(void)
|
||
{
|
||
static const uint8_t ip1[4] = { 192u, 168u, 1u, 100u };
|
||
static const uint8_t ip0[4] = { 0u, 0u, 0u, 0u };
|
||
static const uint8_t ipm[4] = { 255u, 255u, 255u, 255u };
|
||
char buf[24];
|
||
char guard[24];
|
||
|
||
printf("-- TC-NETCFG-050..056 fixed-width ipv4 format --\n");
|
||
|
||
memset(buf, 0x7A, sizeof(buf));
|
||
net_cfg_fmt_ipv4(ip1, buf, sizeof(buf));
|
||
/* 定长 = 4 段 × 3 位 + 3 个点 = 15(曾有误记为 11 的注释,已同日更正)
|
||
* 钉死这个长度是 UI 光标偏移(段 i 起点 4i、点号 3/7/11)的前提 */
|
||
CHECK((strcmp(buf, "192.168.001.100") == 0) && (strlen(buf) == 15u),
|
||
"TC-NETCFG-050 fixed 15 chars with zero padding in every segment");
|
||
|
||
memset(buf, 0x7A, sizeof(buf));
|
||
net_cfg_fmt_ipv4(ip0, buf, sizeof(buf));
|
||
CHECK(strcmp(buf, "000.000.000.000") == 0, "TC-NETCFG-051 all zero still fills 15 chars");
|
||
|
||
memset(buf, 0x7A, sizeof(buf));
|
||
net_cfg_fmt_ipv4(ipm, buf, sizeof(buf));
|
||
CHECK(strcmp(buf, "255.255.255.255") == 0, "TC-NETCFG-052 255 boundary");
|
||
|
||
memset(guard, 0x7A, sizeof(guard));
|
||
memcpy(buf, guard, sizeof(buf));
|
||
net_cfg_fmt_ipv4(NULL, buf, sizeof(buf));
|
||
CHECK(memcmp(buf, guard, sizeof(buf)) == 0, "TC-NETCFG-053 p_ip NULL does not touch the buffer");
|
||
|
||
net_cfg_fmt_ipv4(ip1, NULL, 16u);
|
||
CHECK(1, "TC-NETCFG-054 p_buf NULL does not crash");
|
||
|
||
memset(guard, 0x7A, sizeof(guard));
|
||
memcpy(buf, guard, sizeof(buf));
|
||
net_cfg_fmt_ipv4(ip1, buf, 0u);
|
||
CHECK(memcmp(buf, guard, sizeof(buf)) == 0, "TC-NETCFG-055 len 0 does not touch the buffer");
|
||
|
||
memset(buf, 0x7A, sizeof(buf));
|
||
net_cfg_fmt_ipv4(ip1, buf, 5u);
|
||
CHECK((strlen(buf) == 4u) && (buf[4] == '\0') && (buf[5] == (char)0x7A),
|
||
"TC-NETCFG-056 short len: truncate but must stay NUL-terminated and in bounds");
|
||
}
|
||
|
||
/* ===================== 入口 ===================== */
|
||
|
||
int main(void)
|
||
{
|
||
printf("=== NET Config Tests ===\n");
|
||
|
||
tc_defaults(); /* 必须最先:任何 load 之前 */
|
||
tc_register_load();
|
||
tc_set_get();
|
||
tc_save_direct();
|
||
tc_save_handoff();
|
||
tc_mac_priority();
|
||
tc_fmt_ipv4();
|
||
|
||
printf("\n=== NET Config Tests: %d/%d PASSED (failed=%d) ===\n",
|
||
s_passed, s_total, s_failed);
|
||
return (s_failed == 0) ? 0 : 1;
|
||
}
|