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 绝对路径)
531 lines
21 KiB
C
531 lines
21 KiB
C
/*
|
||
* 模块名称:NET 层事件分发与周期对账(net_events)主机侧单元测试
|
||
* 模块功能:在 PC 上离线验证 Drivers/BSP/NET/net_events.c 的五个事件入口与周期对账器:
|
||
* 对端六元组解析、**陈旧中断防误判**(芯片说还在连接就不许拆会话)、疑似
|
||
* 误报延后交给对账器、超时把会话标成异常释放(喂给 net_pending 收尾报
|
||
* CONNRESET 的那条输入)、掉线自愈自接,以及对账器的 100ms 周期闸门、
|
||
* 1000ms 冷却、五种不一致形态的稳定计数(死亡要连观 4 次、重开监听 2 次、
|
||
* 自接 1 次)与 U11 的 CLOSED->LISTENING 重同步。
|
||
*
|
||
* 被测对象:真实源文件 ../../../Drivers/BSP/NET/net_events.c(#include 原样纳入)。
|
||
* net_events.h / net_types.h / net_pending.h / net_listener.h 全用**真实头**
|
||
* (net_event_cb_t 回调签名、NET_EVENT_* / NET_SOCK_STATE_* /
|
||
* net_listener_settle_t / NET_LISTENER_REQ_* 一改即编译期报错)。假件:
|
||
* 共享假 CH395F(本批补齐对端六元组与链路变更计数)、可控 socket 表
|
||
* (net_sock_raw 对未在用槽位也返回项,与真实语义一致)、**可观测的事件
|
||
* 回调**(记 fd / 事件 / arg)、记录型 net_listener_settle/request 与
|
||
* net_pending_cancel / net_send_pending_cancel、可控时基、dbg_log 计数。
|
||
*
|
||
* 为什么要主机侧单测(而不是只靠台架):
|
||
* 1. CH395F 的中断是**整体读即清**且单连接 socket 复用同一个号,"迟到的 DISCONNECT"
|
||
* 与"丢了 CONNECT 的已连接"在真机上都会发生,却无法按需复现。误判的代价直接落在
|
||
* 现场设备上:把还活着的会话拆掉(104 主站看到莫名掉线),或让 socket 永远停在
|
||
* 已连接假象里不再重开。
|
||
* 2. 对账器的**稳定计数**(死亡 4 次、重开监听 2 次、自接 1 次)本质是"别拿一次
|
||
* 采样就把连接折腾死"。真机一次采样 100ms,凑够 4 次要 400ms,且必须一路稳定;
|
||
* 台架上没法按毫秒喂,也看不出为什么"偶尔不重开"。主机侧把时基做成显式量后,
|
||
* 第 3 次不该动、第 4 次该动、之后 1000ms 内不许再动,全部可精确断言。
|
||
* 3. handle_timeout_event 会置 released_abnormal=1,而 net_pending 收尾时正是靠
|
||
* 这个标志把挂起 recv 报成 CONNRESET 而不是 0(见 test/host/net_pending 的
|
||
* TC-NETPEND-014)。两个模块之间这条**跨文件因果**,只有两边都能离线跑时才可测。
|
||
*
|
||
* 覆盖点:TC-NETEV-001..006 CONNECT;010..014 DISCONNECT(含两条陈旧中断防误判);
|
||
* 020/021 RECV 与 SEND;030..032 TIMEOUT;040..042 掉线自愈;050..058 周期对账
|
||
* (周期闸门、链路变更留痕、五种不一致形态、稳定计数、冷却、in_use 兜底)。
|
||
*
|
||
* 通过:全部 TC PASS(main 返回 0)。
|
||
* 入口:int main(void),返回 0=PASS / 1=FAIL(由 run 脚本判读)。
|
||
*
|
||
* 创建日期:2026-10-23
|
||
*/
|
||
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
#include "net_events.h"
|
||
#include "net_listener.h"
|
||
#include "net_pending.h"
|
||
#include "net_socket.h"
|
||
#include "ch395f.h"
|
||
|
||
#include "ch395f_fake.c"
|
||
#include "net_events.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_nev_info_count;
|
||
int g_nev_error_count;
|
||
int g_nev_debug_count;
|
||
|
||
/* ===================== 可观测的事件回调 ===================== */
|
||
|
||
static int g_ev_n;
|
||
static int g_ev_fd[16];
|
||
static int g_ev_kind[16];
|
||
static void *g_ev_arg[16];
|
||
static int g_ev_token;
|
||
|
||
static void ev_cb(int sockfd, net_event_t event, void *arg)
|
||
{
|
||
if (g_ev_n < (int)(sizeof(g_ev_fd) / sizeof(g_ev_fd[0]))) {
|
||
g_ev_fd[g_ev_n] = sockfd;
|
||
g_ev_kind[g_ev_n] = (int)event;
|
||
g_ev_arg[g_ev_n] = arg;
|
||
}
|
||
g_ev_n++;
|
||
}
|
||
|
||
/* ===================== 记录型监听与挂起取消 ===================== */
|
||
|
||
static int g_settle_n;
|
||
static int g_settle_sock;
|
||
static int g_settle_level;
|
||
static int g_req_n;
|
||
static int g_req_sock;
|
||
static int g_req_reason;
|
||
static int g_pcan_n;
|
||
static int g_scan_n;
|
||
static int g_pcan_err;
|
||
|
||
void net_listener_settle(int sockfd, net_listener_settle_t level)
|
||
{
|
||
g_settle_n++;
|
||
g_settle_sock = sockfd;
|
||
g_settle_level = (int)level;
|
||
}
|
||
|
||
void net_listener_request(int sockfd, net_listener_req_t reason)
|
||
{
|
||
g_req_n++;
|
||
g_req_sock = sockfd;
|
||
g_req_reason = (int)reason;
|
||
}
|
||
|
||
void net_listener_clear(int sockfd) { (void)sockfd; }
|
||
void net_listener_tick(void) { }
|
||
|
||
void net_pending_cancel(int sockfd, int err)
|
||
{
|
||
(void)sockfd;
|
||
g_pcan_n++;
|
||
g_pcan_err = err;
|
||
}
|
||
|
||
void net_send_pending_cancel(int sockfd, int err)
|
||
{
|
||
(void)sockfd;
|
||
(void)err;
|
||
g_scan_n++;
|
||
}
|
||
|
||
/* ===================== 可控时基、errno 与 socket 表 ===================== */
|
||
|
||
static uint32_t g_now;
|
||
static int g_errno_last;
|
||
|
||
uint32_t HAL_GetTick(void) { return g_now; }
|
||
|
||
int net_set_errno(int err) { g_errno_last = err; return err; }
|
||
|
||
uint32_t xTaskGetTickCount(void) { return g_now; }
|
||
|
||
static net_sock_t g_tbl[NET_MAX_SOCKETS];
|
||
|
||
net_sock_t *net_sock_raw(int sockfd)
|
||
{
|
||
if ((sockfd < 0) || (sockfd >= NET_MAX_SOCKETS)) {
|
||
return NULL;
|
||
}
|
||
/* 与真实语义一致:对账循环必须能读到 in_use == 0 的槽位,所以这里不给 NULL */
|
||
return &g_tbl[sockfd];
|
||
}
|
||
|
||
net_sock_t *net_get_sock(int sockfd)
|
||
{
|
||
net_sock_t *p = net_sock_raw(sockfd);
|
||
|
||
if ((p == NULL) || (p->in_use == 0u)) {
|
||
return NULL;
|
||
}
|
||
return p;
|
||
}
|
||
|
||
static void reset_all(void)
|
||
{
|
||
int i;
|
||
|
||
(void)memset(g_tbl, 0, sizeof(g_tbl));
|
||
g_now = 1000u;
|
||
g_errno_last = 0;
|
||
g_ev_n = 0;
|
||
g_settle_n = 0;
|
||
g_settle_sock = -1;
|
||
g_settle_level = -1;
|
||
g_req_n = 0;
|
||
g_req_sock = -1;
|
||
g_req_reason = -1;
|
||
g_pcan_n = 0;
|
||
g_scan_n = 0;
|
||
g_pcan_err = 0;
|
||
g_nev_info_count = 0;
|
||
g_nev_error_count = 0;
|
||
g_nev_debug_count = 0;
|
||
ch395f_fake_reset();
|
||
(void)i;
|
||
}
|
||
|
||
/* 一个"带应用回调的在用槽位",ch395_sock 与数组下标同号 */
|
||
static net_sock_t *mk(int fd, net_sock_state_t st, uint16_t local_port)
|
||
{
|
||
net_sock_t *p = &g_tbl[fd];
|
||
|
||
p->in_use = 1u;
|
||
p->ch395_sock = (uint8_t)fd;
|
||
p->type = NET_SOCK_STREAM;
|
||
p->state = st;
|
||
p->auto_relisten = 1u;
|
||
p->local_port = local_port;
|
||
p->event_cb = ev_cb;
|
||
p->event_arg = (void *)&g_ev_token;
|
||
return p;
|
||
}
|
||
|
||
/* ===================== CONNECT ===================== */
|
||
|
||
static void tc_connect(void)
|
||
{
|
||
net_sock_t *p;
|
||
|
||
printf("-- TC-NETEV-001..006 CONNECT --\n");
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_LISTENING, 2404u);
|
||
ch395f_fake_set_peer(0, 192u, 168u, 31u, 50u, 51234u);
|
||
handle_connect_event(p);
|
||
/* remote_ip 是**低字节在前**拼的(remote[0] 落在最低 8 位),与网络序相反:
|
||
* 192.168.31.50 -> 0x321FA8C0。这是既有实现的字节序,钉住它是防止有人"顺手
|
||
* 改成大端"导致所有按 IP 比对/白名单的判断静默错位。 */
|
||
CHECK((p->remote_ip_arr[0] == 192u) && (p->remote_ip_arr[3] == 50u) &&
|
||
(p->remote_ip == ((192u) | (168u << 8) | (31u << 16) | (50u << 24))) &&
|
||
(p->remote_port == 51234u),
|
||
"TC-NETEV-001 peer tuple parsing: ip assembled little-endian into 32 bits, port from bytes 5/6");
|
||
CHECK((p->state == NET_SOCK_STATE_ESTABLISHED) && (p->session_dead == 0u) &&
|
||
(p->lost_conn_seen == 0u) && (p->send_ready == 1u),
|
||
"TC-NETEV-002 a listened socket becomes established after self-accept and is writable at once");
|
||
CHECK((g_chip.keepalive_calls == 1) && (g_chip.last_keepalive == 1u) &&
|
||
(g_chip.int_status_calls == 1),
|
||
"TC-NETEV-003 on connect, enable keepalive and clear the pending bit once (read clears)");
|
||
CHECK((g_ev_n == 1) && (g_ev_kind[0] == (int)NET_EVENT_CONNECTED) &&
|
||
(g_ev_fd[0] == 0) && (g_ev_arg[0] == (void *)&g_ev_token),
|
||
"TC-NETEV-004 the event calls back into the app with fd and the caller arg");
|
||
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_CONNECTING, 0u);
|
||
ch395f_fake_set_peer(0, 10u, 0u, 0u, 1u, 2404u);
|
||
handle_connect_event(p);
|
||
CHECK((p->state == NET_SOCK_STATE_ESTABLISHED) && (p->send_ready == 0u) &&
|
||
(g_chip.keepalive_calls == 1) && (g_ev_n == 1),
|
||
"TC-NETEV-005 the active-connect branch also becomes established but does NOT touch send_ready (differs from self-accept)");
|
||
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_CLOSED, 2404u);
|
||
handle_connect_event(p);
|
||
CHECK((g_ev_n == 0) && (p->state == NET_SOCK_STATE_CLOSED),
|
||
"TC-NETEV-006 a CONNECT interrupt in any other state is ignored (never invent a session)");
|
||
}
|
||
|
||
/* ===================== DISCONNECT ===================== */
|
||
|
||
static void tc_disconnect(void)
|
||
{
|
||
net_sock_t *p;
|
||
|
||
printf("-- TC-NETEV-010..014 DISCONNECT --\n");
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_ESTABLISHED, 2404u);
|
||
g_fake.status_byte0[0] = CH395F_SOCKET_OPEN;
|
||
g_fake.status_byte1[0] = CH395F_TCP_ESTABLISHED;
|
||
handle_disconnect_event(p);
|
||
CHECK((p->state == NET_SOCK_STATE_ESTABLISHED) && (g_ev_n == 0) &&
|
||
(g_nev_error_count == 1),
|
||
"TC-NETEV-010 the chip still says connecting: ignore this stale DISCONNECT (never tear down a live session)");
|
||
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_ESTABLISHED, 2404u);
|
||
g_fake.status_byte0[0] = CH395F_SOCKET_OPEN;
|
||
g_fake.status_byte1[0] = CH395F_TCP_CLOSED;
|
||
ch395f_fake_push_recv_len(0, 9u); /* 还有 9 字节没读走 */
|
||
handle_disconnect_event(p);
|
||
CHECK((p->state == NET_SOCK_STATE_ESTABLISHED) && (p->lost_conn_seen == 1u) &&
|
||
(g_ev_n == 0) && (g_settle_n == 0),
|
||
"TC-NETEV-011 suspected false alarm (dropped before any data): defer to the reconciler instead of tearing down");
|
||
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_ESTABLISHED, 2404u);
|
||
p->remote_port = 51234u;
|
||
g_fake.status_byte0[0] = CH395F_SOCKET_OPEN;
|
||
g_fake.status_byte1[0] = CH395F_TCP_CLOSED;
|
||
handle_disconnect_event(p);
|
||
CHECK((g_settle_n == 1) && (g_settle_level == (int)NET_LISTENER_SETTLE_GRACEFUL) &&
|
||
(p->session_dead == 1u) && (p->released_abnormal == 0u) &&
|
||
(p->last_peer_port == 51234u) &&
|
||
(p->state == NET_SOCK_STATE_CLOSED) &&
|
||
(g_ev_kind[0] == (int)NET_EVENT_DISCONNECTED),
|
||
"TC-NETEV-012 normal disconnect: graceful settle + mark session dead + remember old port + post event");
|
||
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_LISTENING, 2404u);
|
||
g_fake.status_byte0[0] = CH395F_SOCKET_OPEN;
|
||
g_fake.status_byte1[0] = CH395F_TCP_LISTEN;
|
||
handle_disconnect_event(p);
|
||
CHECK((p->state == NET_SOCK_STATE_LISTENING) && (g_ev_n == 0) &&
|
||
(g_nev_info_count == 1) && (g_nev_error_count == 0),
|
||
"TC-NETEV-013 listen still alive: a stale listen DISCONNECT is only logged, not reported as ERROR");
|
||
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_LISTENING, 2404u);
|
||
g_fake.status_byte1[0] = CH395F_TCP_CLOSED; /* 芯片说不在 LISTEN */
|
||
handle_disconnect_event(p);
|
||
CHECK((p->state == NET_SOCK_STATE_LISTENING) && (g_ev_n == 0) && (g_req_n == 0),
|
||
"TC-NETEV-013b even a lost listener is only logged: reopening belongs to the reconciler, the event layer must not overreach");
|
||
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_CONNECTING, 0u);
|
||
handle_disconnect_event(p);
|
||
CHECK((g_nev_error_count == 1) && (g_ev_kind[0] == (int)NET_EVENT_DISCONNECTED),
|
||
"TC-NETEV-014 DISCONNECT in an unusual state: report ERROR but still notify the application");
|
||
}
|
||
|
||
/* ===================== RECV / SEND / TIMEOUT ===================== */
|
||
|
||
static void tc_recv_send_timeout(void)
|
||
{
|
||
net_sock_t *p;
|
||
|
||
printf("-- TC-NETEV-020/021/030..032 --\n");
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_ESTABLISHED, 2404u);
|
||
handle_recv_event(p);
|
||
CHECK((p->readable == 1u) && (g_ev_kind[0] == (int)NET_EVENT_DATA_RECEIVED),
|
||
"TC-NETEV-020 a RECV event sets readable and notifies the application");
|
||
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_ESTABLISHED, 2404u);
|
||
p->send_ready = 1u;
|
||
handle_send_event(p);
|
||
CHECK((g_ev_kind[0] == (int)NET_EVENT_SEND_COMPLETE) && (p->send_ready == 1u),
|
||
"TC-NETEV-021 SEND completion only posts the event; the fields stay owned by the write path");
|
||
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_CONNECTING, 0u);
|
||
handle_timeout_event(p);
|
||
CHECK((p->state == NET_SOCK_STATE_CREATED) && (g_nev_error_count == 1) &&
|
||
(g_ev_kind[0] == (int)NET_EVENT_TIMEOUT),
|
||
"TC-NETEV-030 connect timeout: fall back to CREATED and notify the application");
|
||
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_ESTABLISHED, 2404u);
|
||
p->remote_port = 777u;
|
||
handle_timeout_event(p);
|
||
CHECK((g_settle_level == (int)NET_LISTENER_SETTLE_QUARANTINE) &&
|
||
(p->released_abnormal == 1u) && (p->session_dead == 1u) &&
|
||
(p->last_peer_port == 777u) && (p->state == NET_SOCK_STATE_CLOSED),
|
||
"TC-NETEV-031 session timeout: quarantine-level settle + mark released abnormally (net_pending reports CONNRESET from it)");
|
||
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_LISTENING, 2404u);
|
||
handle_timeout_event(p);
|
||
CHECK((p->state == NET_SOCK_STATE_LISTENING) && (g_nev_error_count == 1) &&
|
||
(g_ev_kind[0] == (int)NET_EVENT_TIMEOUT),
|
||
"TC-NETEV-032 listen timeout is only logged: state unchanged but waiters are still notified");
|
||
}
|
||
|
||
/* ===================== 掉线自愈 ===================== */
|
||
|
||
static void tc_lost_probe(void)
|
||
{
|
||
net_sock_t *p;
|
||
|
||
printf("-- TC-NETEV-040..042 lost-CONNECT probe --\n");
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_CLOSED, 2404u);
|
||
p->lost_conn_seen = 1u;
|
||
g_fake.cached_tcp[0] = CH395F_TCP_CLOSED;
|
||
net_lost_connect_probe(p);
|
||
CHECK((p->lost_conn_seen == 0u) && (g_ev_n == 0),
|
||
"TC-NETEV-040 the chip is not established: only clear the flag, do not create a session on our own");
|
||
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_CLOSED, 2404u);
|
||
p->lost_conn_seen = 1u;
|
||
p->last_peer_port = 51234u;
|
||
g_fake.cached_tcp[0] = CH395F_TCP_ESTABLISHED;
|
||
ch395f_fake_set_peer(0, 10u, 0u, 0u, 9u, 51234u);
|
||
net_lost_connect_probe(p);
|
||
CHECK((p->lost_conn_seen == 0u) && (g_ev_n == 0),
|
||
"TC-NETEV-041 peer port identical to last time: assume we missed CONNECT, do not accept a second time");
|
||
|
||
/* 自愈的适用面是软件还写着 LISTENING、芯片其实已经连上——accept 分支只认
|
||
* LISTENING/CONNECTING。若软件态已被别处改成 CLOSED,本函数打完 INFO 后什么都
|
||
* 不会发生(现状留痕,见第二断言)。 */
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_LISTENING, 2404u);
|
||
p->lost_conn_seen = 1u;
|
||
p->last_peer_port = 1u;
|
||
g_fake.cached_tcp[0] = CH395F_TCP_ESTABLISHED;
|
||
ch395f_fake_set_peer(0, 10u, 0u, 0u, 9u, 55555u);
|
||
net_lost_connect_probe(p);
|
||
CHECK((g_ev_n == 1) && (g_ev_kind[0] == (int)NET_EVENT_CONNECTED) &&
|
||
(p->state == NET_SOCK_STATE_ESTABLISHED) && (p->remote_port == 55555u) &&
|
||
(p->lost_conn_seen == 0u),
|
||
"TC-NETEV-042 self-heal after a lost CONNECT interrupt: make up one accept so the session is not stranded forever");
|
||
|
||
reset_all();
|
||
p = mk(0, NET_SOCK_STATE_CLOSED, 2404u);
|
||
g_fake.cached_tcp[0] = CH395F_TCP_ESTABLISHED;
|
||
ch395f_fake_set_peer(0, 10u, 0u, 0u, 9u, 55555u);
|
||
net_lost_connect_probe(p);
|
||
CHECK((g_ev_n == 0) && (p->state == NET_SOCK_STATE_CLOSED),
|
||
"TC-NETEV-042b current state: self-heal cannot accept once the software side is CLOSED (logged only, no invented session)");
|
||
}
|
||
|
||
/* ===================== 周期对账 ===================== */
|
||
|
||
static void recon_step(uint32_t now)
|
||
{
|
||
g_now = now;
|
||
net_reconcile_tick();
|
||
}
|
||
|
||
static void tc_reconcile(void)
|
||
{
|
||
net_sock_t *p;
|
||
int infos;
|
||
int c;
|
||
|
||
printf("-- TC-NETEV-050..058 reconcile --\n");
|
||
/* 对账器里的 s_last / s_bad[] / s_cool[] 是函数内 static(外部够不着),
|
||
* 所以每个形态**各占一个 fd**、时间统一单调前进,互不串扰。 */
|
||
reset_all();
|
||
infos = g_nev_info_count;
|
||
recon_step(1000u); /* priming:链路计数记基线 */
|
||
CHECK(g_nev_info_count == infos,
|
||
"TC-NETEV-051a the first pass after power-up logs nothing for the link counter (the 0xFFFF sentinel only records the baseline)");
|
||
c = g_chip.cached_calls;
|
||
recon_step(1000u); /* 同一时刻再调一次 */
|
||
CHECK(g_chip.cached_calls == c, "TC-NETEV-050 100ms period gate: do not query the chip twice at the same instant");
|
||
|
||
reset_all();
|
||
p = mk(1, NET_SOCK_STATE_ESTABLISHED, 2404u);
|
||
g_fake.cached_open[1] = CH395F_SOCKET_CLOSED;
|
||
g_fake.cached_tcp[1] = CH395F_TCP_CLOSED;
|
||
infos = g_nev_info_count;
|
||
recon_step(1100u);
|
||
recon_step(1200u);
|
||
recon_step(1300u);
|
||
CHECK((p->state == NET_SOCK_STATE_ESTABLISHED) && (g_ev_n == 0) &&
|
||
(g_nev_info_count == infos),
|
||
"TC-NETEV-052 a dead session needs 4 consecutive observations before acting: the first three change nothing");
|
||
recon_step(1400u);
|
||
CHECK((p->state == NET_SOCK_STATE_CLOSED) && (p->session_dead == 1u) &&
|
||
(g_ev_kind[0] == (int)NET_EVENT_DISCONNECTED),
|
||
"TC-NETEV-052b once the 4th observation is stable, run the disconnect teardown");
|
||
p->state = NET_SOCK_STATE_ESTABLISHED; /* 假装又活了,验证冷却 */
|
||
recon_step(1500u);
|
||
recon_step(1600u);
|
||
recon_step(1700u);
|
||
recon_step(1800u);
|
||
CHECK(g_ev_n == 1, "TC-NETEV-052c no repeat action inside the 1000ms cooldown after acting (even when the stable count is met)");
|
||
|
||
reset_all();
|
||
p = mk(2, NET_SOCK_STATE_ESTABLISHED, 2404u);
|
||
g_fake.cached_open[2] = CH395F_SOCKET_OPEN;
|
||
g_fake.cached_tcp[2] = CH395F_TCP_LISTEN; /* 芯片自己重开监听了,却没发 DISCONNECT */
|
||
recon_step(2900u);
|
||
CHECK((p->state == NET_SOCK_STATE_ESTABLISHED) && (g_pcan_n == 0),
|
||
"TC-NETEV-053 the chip reopened the listener behind our back: needs 2 stable passes, the first one only accumulates");
|
||
recon_step(3000u);
|
||
CHECK((p->state == NET_SOCK_STATE_LISTENING) && (g_pcan_n == 1) &&
|
||
(g_scan_n == 1) && (g_pcan_err == NET_ERR_CONNRESET),
|
||
"TC-NETEV-053b second stable pass: resync to listen and cancel both pending waits (otherwise a task would wait forever)");
|
||
|
||
reset_all();
|
||
p = mk(3, NET_SOCK_STATE_LISTENING, 2404u);
|
||
p->last_peer_port = 1u;
|
||
g_fake.cached_tcp[3] = CH395F_TCP_ESTABLISHED; /* 连上了却没 CONNECT 中断 */
|
||
ch395f_fake_set_peer(3, 172u, 16u, 0u, 5u, 40000u);
|
||
recon_step(4100u);
|
||
CHECK((p->state == NET_SOCK_STATE_ESTABLISHED) && (p->remote_port == 40000u) &&
|
||
(g_ev_kind[0] == (int)NET_EVENT_CONNECTED),
|
||
"TC-NETEV-054 connect completed but the interrupt was lost: a single pass is enough to make up the accept");
|
||
|
||
reset_all();
|
||
p = mk(4, NET_SOCK_STATE_LISTENING, 2404u);
|
||
g_fake.cached_open[4] = CH395F_SOCKET_CLOSED; /* 监听没了 */
|
||
g_fake.cached_tcp[4] = CH395F_TCP_CLOSED;
|
||
recon_step(5200u);
|
||
recon_step(5300u);
|
||
CHECK((g_req_n == 1) && (g_req_sock == 4) &&
|
||
(g_req_reason == (int)NET_LISTENER_REQ_RECON),
|
||
"TC-NETEV-055 listener lost: request a reopen after 2 stable passes (the event layer does not open it itself)");
|
||
|
||
reset_all();
|
||
p = mk(5, NET_SOCK_STATE_CLOSED, 2404u);
|
||
g_fake.cached_open[5] = CH395F_SOCKET_OPEN; /* 芯片还在 LISTEN */
|
||
g_fake.cached_tcp[5] = CH395F_TCP_LISTEN;
|
||
p->remote_port = 999u;
|
||
p->lost_conn_seen = 1u;
|
||
recon_step(6400u);
|
||
CHECK((p->state == NET_SOCK_STATE_LISTENING) && (p->last_peer_port == 999u) &&
|
||
(p->lost_conn_seen == 0u) && (g_req_n == 0),
|
||
"TC-NETEV-056 U11 resync: software CLOSED while the chip is LISTEN -> follow the chip, no redundant reopen");
|
||
|
||
reset_all();
|
||
p = mk(6, NET_SOCK_STATE_CLOSED, 2404u);
|
||
g_fake.cached_open[6] = CH395F_SOCKET_CLOSED;
|
||
recon_step(7500u);
|
||
recon_step(7600u);
|
||
CHECK((g_req_n == 1) && (g_req_sock == 6),
|
||
"TC-NETEV-057 software CLOSED and the chip is not listening either: request a reopen (the listener layer rebuilds it)");
|
||
|
||
reset_all();
|
||
(void)memset(&g_tbl[7], 0, sizeof(g_tbl[7])); /* 槽位不在用 */
|
||
infos = g_nev_info_count;
|
||
recon_step(8700u);
|
||
CHECK(g_nev_info_count == infos, "TC-NETEV-058 unused slots are skipped outright (no action at all)");
|
||
|
||
/* 链路日志:s_link_last 初值 0xFFFF 是哨兵——第一次看到计数只记基线、不打日志
|
||
* (否则每次上电都会冒一条"链路 up"的假事件);此后计数一变就必须留痕。
|
||
* 本文件开头那次 priming 就是"基线"那一趟(TC-NETEV-051a),这里是"变化"那一趟。 */
|
||
reset_all();
|
||
g_fake.link_change = 7u;
|
||
infos = g_nev_info_count;
|
||
recon_step(9800u);
|
||
CHECK(g_nev_info_count == (infos + 1),
|
||
"TC-NETEV-051b any change of the link-change counter is logged (the baseline pass is already covered by 051a)");
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
printf("=== NET Events Tests ===\n");
|
||
|
||
tc_connect();
|
||
tc_disconnect();
|
||
tc_recv_send_timeout();
|
||
tc_lost_probe();
|
||
tc_reconcile();
|
||
|
||
printf("\n=== NET Events Tests: %d/%d PASSED (failed=%d) ===\n",
|
||
s_passed, s_total, s_failed);
|
||
return (s_failed == 0) ? 0 : 1;
|
||
}
|