test(host): P2 覆盖率批次,5 套主机单测与共享假 FatFs

P2 五个文件全部进分母(三名队友并行,写范围互斥、零越界):
  net_socket 420 条 / 881-883=99.8%、52-52 函数(S1~S6 分片)
  sys_log_mcu 75 条 / 310-313=99.0%、12-12
  console_log 104 条 / 188-189=99.5%、15-15
  cfg_file_store_mcu 34 条 / 97-97=100%、10-10
  soe_store 26 条 / 78-79=98.7%、4-4
合计 659 条断言,P2 本体 1554-1561=99.6%、93-93 函数。

账:套件 28→33 全绿;files_tested 32→37;未触及 112→107(.c 34→29,
恒等式 29 = 0(P1剩) + 0(P2) + 29(P3) 咬合,§9 终点判据达成);分母
4684→6245 插桩行;line 85.8%→89.3%;func 90.1%→92.2%(这批 5 个文件
全都以 ≥98.7% 进分母,故未摊薄)。--update-baseline 前后逐文件比对:
下降 0 个文件。

基建:新建 test/host/common/fatfs_fake.{h,c}(以真实 ff.h 为声明源、只
定义三文件实际调用的 9 个 f_*,缺哪个链接期响;卷忙/短写/FR_DENIED
可注入;短名违规分档);扩展 ch395f_fake 为纯新增(g_fx/g_fxchip 另开
一份世界 + ext_reset 挂进既有复位,既有三套面一字不动),并按真头修正
两个错值常量(SOCKET_OPEN 0x01→0x05、TCP_ESTABLISHED 0x05→0x04),
另在 net_socket runner 加真头/假件同名常量文本核对闸(负例验证过)。
存量回归:net_listener 34、net_events 34、net_pending 43、net_select 22、
nand_port 149、lftpd_io 33、vol_lock 33、soe 32、cfg_file 151 全绿。

编号:补 TC-HOST-{NETSOCK,SYSLOG,CFGFST,SOESTORE,CONLOG} 五族(22→27,
先补族再引号);net_socket 模块码由自取的两字母 NS 正名为 NETSOCK;
id-check 增量拦截无台账外新账,可判定比例 83.27%(基线 82.6%)。

文档:覆盖率口径 §9 新增「P2 落地」(含两套分母口径差、10 条现状表、
12 条可复用手法与并发红线),§8 第 1 条改「清单已吃穿」,补变更记录。
本提交未改任何生产代码(App/Drivers/Src/Lib/MDK-ARM 零触碰)。

10 条现状已逐条登记在 §9(含 sys_log_mcu.c:509~537 卷忙整批丢日志、
net_socket.c 同文件两套 IP 字节序、UDP 两 API 绕过 netTask),已获指示
全部修复,另开一轮(含测试字面量统一纯 ASCII)。

gate: pre-commit run --all-files → 39 hooks, 38 Passed; only mem-numbers
Failed(本工作树从未 @build,无 .map 事实源,属分支期已知陷阱);
mem-budget Passed;test-coverage 与 id-check Passed。RAM/Flash 增量 = 0
(纯测试批次)。按仓内分支约定走 --no-verify,台账回填留待主树合并后。
This commit is contained in:
2026-10-24 17:54:02 +08:00
parent 4ac869058d
commit 794906cdc8
46 changed files with 10821 additions and 39 deletions
+613
View File
@@ -0,0 +1,613 @@
/*
* 分片 3:跨上下文消息面(net_process_messages 全分派 + 结果经任务通知值回传)
* 前置:net_socket_test.c 已纳入生产源与假内核/假芯片/兄弟模块桩(同一 TU)。
*
* 这一片是整套里最值钱的部分,钉的是 AGENTS 模块约定的两条硬契约:
* A.「所有 net_* socket 操作经 netMsgQueue 在 netTask 串行执行」
* B.「net_* 公开 API 的错误码经任务通知值回传:net_msg_t 是按值拷入队列的,
* netTask 改的是自己那份副本,调用方永远看不到 → 错误码搭 xTaskNotify 通知值回来;
* 勿给 net_msg_t 加回传字段(加了只会让调用方读到栈垃圾)」
* 断言形态因此刻意是:先看**通知值本身**(32 位整体即结果,负值=精确错误码),
* 再看调用方在自己上下文恢复出的 errno——而不是去读队列里的 msg.result
* (那正是旧实现的错觉,也是 B 条明文禁止的方向)。
*
* 创建日期:2026-10-24
*/
/* close 场景的增量快照基准(前面场景也会 clear,必须按增量断言) */
static int s_close_base_clear;
static int s_close_base_settle;
/* 造一条已建立的 TCP 会话(多数消息场景的前置) */
static int mk_established(int type)
{
int fd = net_socket(NET_AF_INET, type);
net_sock_t *p;
if (fd < 0) {
return fd;
}
p = &s_net_socks[fd];
p->state = NET_SOCK_STATE_ESTABLISHED;
p->send_ready = 1U;
p->readable = 0U;
p->local_port = 2404U;
p->remote_port = 51999U;
p->remote_ip = 0x0B01A8C0U;
return fd;
}
/* TC-NETSOCK-200 未知消息类型:枚举闭合外的值也必须给出**本条消息自身**的确定结果
* (不许把上一条的回执残留当成这次的结果) */
static void tc_200_unknown_type(void)
{
net_msg_t msg;
bring_up();
(void)memset(&msg, 0, sizeof(msg));
msg.type = (net_msg_type_t)999;
msg.sockfd = 0;
msg.caller = (TaskHandle_t)&g_ns_task_caller;
msg.result = 12345; /* 脏值:分派必须覆写它 */
CHECK(ns_rtos_q_put_raw(&msg) == 0, "TC-NETSOCK-200a 原始消息入队");
CHECK(g_ns_notify_calls == 0, "TC-NETSOCK-200b 入队本身不产生通知");
net_process_messages(); /* 以 netTask 身份跑一轮 */
CHECK(g_ns_notify_calls == 1, "TC-NETSOCK-200c 未知 type 仍必发回执(否则调用方永久悬挂)");
CHECK(g_ns_notify_last_val == (uint32_t)NET_ERR,
"TC-NETSOCK-200d 未知 type 的通知值 = 本条的精确错误码 NET_ERR(不是上一条残留)");
CHECK(ns_rtos_q_count() == 0, "TC-NETSOCK-200e 本轮队列被排空(非阻塞 while 语义)");
reset_all();
}
/* TC-NETSOCK-201 队列为空:四个自愈 tick 必跑,且不产生任何通知 */
static void tc_201_ticks_run_before_dispatch(void)
{
bring_up();
net_process_messages();
CHECK(g_peers.listener_tick_calls == 1 && g_peers.pending_tick_calls == 1 &&
g_peers.send_tick_calls == 1 && g_peers.reconcile_calls == 1,
"TC-NETSOCK-201a 空队列也要跑满四个自愈 tick(D10/pending×2/对账)");
CHECK(g_ns_notify_calls == 0 && g_ns_get_calls == 1,
"TC-NETSOCK-201b 空队列:取一次即退,不发通知");
reset_all();
}
/* TC-NETSOCK-202 队列句柄未绑定:直接返回,连 tick 都不该跑 */
static void tc_202_no_queue_bound(void)
{
reset_all();
g_ns_pump = net_process_messages;
net_process_messages();
CHECK(g_peers.listener_tick_calls == 0 && g_ns_get_calls == 0,
"TC-NETSOCK-202 未绑定队列就调 net_process_messages → 立即返回(不碰芯片也不跑 tick)");
reset_all();
}
/* TC-NETSOCK-210 SEND 快路径:端到端跑通"入队 → 分派 → 写芯片 → 通知值=字节数" */
static void tc_210_send_end_to_end(void)
{
char buf[8];
int fd;
int rc;
bring_up();
fd = mk_established(NET_SOCK_STREAM);
(void)memset(buf, 'A', sizeof(buf));
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
rc = net_send(fd, buf, (int)sizeof(buf), 0);
CHECK(rc == (int)sizeof(buf), "TC-NETSOCK-210a net_send 返回实际写入字节数");
CHECK(g_chip.write_calls == 1 && g_chip.last_read_len == (uint16_t)sizeof(buf),
"TC-NETSOCK-210b 一次写满(健康链路不拆块、不进挂起)");
CHECK(s_net_socks[fd].send_bytes == sizeof(buf), "TC-NETSOCK-210c 发送字节统计累计");
CHECK(g_ns_notify_calls == 1 && g_ns_notify_last_val == (uint32_t)sizeof(buf),
"TC-NETSOCK-210d 结果经通知值回传(32 位整体即结果)");
CHECK(g_ns_notify_last_to == (TaskHandle_t)&g_ns_task_caller,
"TC-NETSOCK-210e 通知发给**登记的那个 caller**,不是广播");
CHECK(g_ns_block_waits == 1,
"TC-NETSOCK-210f 零等待没拾到 → 走阻塞分支(两段式拾取的第二段)");
reset_all();
}
/* TC-NETSOCK-211 大帧分块:超过 NET_SEND_CHUNK_MAX 必须按硬件发送缓冲上界切块
* (TRAP-NET-16:单次写 >1KB 会写穿 1KB 发送 FIFO) */
static void tc_211_send_chunking(void)
{
static char buf[3000];
int fd;
int rc;
bring_up();
fd = mk_established(NET_SOCK_STREAM);
/* 造"每写完一块芯片就报 SENBUF_FREE"的健康链路:让 net_poll 把 send_ready 置回来 */
g_ns_int_pin_set = 0;
ch395f_fake_push_glob((uint16_t)(1U << (fd + 4)));
/* SENBUF_FREE 是**条件位**:发送缓冲空着就还会再报。用假件的电平型掩码而不是
* 一次性位图,否则第二块起就再也等不到空闲 → 变成测假件的清零时机。 */
ch395f_fake_set_sint_sticky((uint8_t)fd, CH395F_SINT_STAT_SENBUF_FREE);
rc = net_send(fd, buf, 2500, 0);
CHECK(rc == 2500, "TC-NETSOCK-211a 2500 字节全部交付");
CHECK(g_chip.write_calls == 3, "TC-NETSOCK-211b 按 1024 上界切成 3 块(1024/1024/452)");
CHECK(g_chip.last_read_len == 452U, "TC-NETSOCK-211c 末块只写剩余量");
CHECK(NET_SEND_CHUNK_MAX == 1024,
"TC-NETSOCK-211d 分块粒度取芯片发送缓冲(不是假件自定的数)");
reset_all();
}
/* TC-NETSOCK-212 SEND 挂起登记:等发送缓冲超预算 → 登记挂起并**跳过即时回执** */
static void tc_212_send_pending_defers_notify(void)
{
static char buf[64];
net_msg_t msg;
int fd;
bring_up();
fd = mk_established(NET_SOCK_STREAM);
s_net_socks[fd].send_ready = 0U; /* 芯片一直不腾缓冲 */
(void)memset(&msg, 0, sizeof(msg));
msg.type = NET_MSG_SEND;
msg.sockfd = fd;
msg.buf = buf;
msg.len = (int)sizeof(buf);
msg.caller = (TaskHandle_t)&g_ns_task_caller;
CHECK(ns_rtos_q_put_raw(&msg) == 0, "TC-NETSOCK-212a SEND 消息入队");
net_process_messages();
CHECK(g_peers.send_reg_calls == 1,
"TC-NETSOCK-212b 超自旋预算后登记挂起 send(而不是继续占着 netTask 忙等)");
CHECK(g_peers.last_snd_fd == fd && g_peers.last_snd_len == (int)sizeof(buf) &&
g_peers.last_snd_sent == 0,
"TC-NETSOCK-212c 登记的进度=已写芯片字节数(此处 0),总长与 caller 一并带走");
CHECK(g_ns_notify_calls == 0,
"TC-NETSOCK-212d 登记成功**绝不发即时回执**(否则「登记成功」被当成本次结果)");
CHECK(g_ns_notify_last_to == NULL,
"TC-NETSOCK-212e 一次通知都没发生(0x70000000 哨兵值也没可能漏进通知值)");
/* 完成时刻:由挂起 send 的 tick 结束(本套由桩脚本模拟"缓冲腾出来了") */
ns_peers_arm_send_pending_done(fd, 64);
net_process_messages();
CHECK(g_ns_notify_calls == 1 && g_ns_notify_last_val == 64U,
"TC-NETSOCK-212f 挂起完成后才发回执,通知值=实际写出字节数");
reset_all();
}
/* TC-NETSOCK-213 SEND 失败:未建立连接 → 通知值带**精确**错误码,不是笼统 -1 */
static void tc_213_send_error_code_roundtrip(void)
{
char buf[4];
net_msg_t msg;
int fd;
int rc;
bring_up();
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM); /* CREATED,未连接 */
(void)memset(&msg, 0, sizeof(msg));
msg.type = NET_MSG_SEND;
msg.sockfd = fd;
msg.buf = buf;
msg.len = (int)sizeof(buf);
msg.caller = (TaskHandle_t)&g_ns_task_caller;
(void)ns_rtos_q_put_raw(&msg);
net_process_messages();
CHECK(g_ns_notify_last_val == (uint32_t)NET_ERR_NOTCONN,
"TC-NETSOCK-213a 未连接的 SEND:通知值=NET_ERR_NOTCONN(精确码回传,供调用方分支)");
/* 端到端:公开 net_send 的 BADF 形态(fd 越界由**分派侧**再判一次) */
reset_all();
bring_up();
(void)memset(&msg, 0, sizeof(msg));
msg.type = NET_MSG_SEND;
msg.sockfd = 3; /* 合法范围内但未分配 */
msg.buf = buf;
msg.len = 4;
msg.caller = (TaskHandle_t)&g_ns_task_caller;
(void)ns_rtos_q_put_raw(&msg);
net_process_messages();
CHECK(g_ns_notify_last_val == (uint32_t)NET_ERR_BADF,
"TC-NETSOCK-213b 范围内但未分配的 fd:net_get_sock 判空 → BADF 搭通知值回来");
/* 调用方上下文恢复 errno:拿到的必须是"本任务的"精确码 */
reset_all();
bring_up();
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
rc = net_send(6, buf, 4, 0); /* 6 未分配 → 分派侧 BADF → 通知值 -2 */
CHECK(rc == -1, "TC-NETSOCK-213c 对外保持 BSD 语义:失败一律 -1");
CHECK(net_get_errno() == NET_ERR_BADF,
"TC-NETSOCK-213d 调用方在自己的错误码槽里拿到精确码(E-03 + 通知值回传合力)");
reset_all();
}
/* TC-NETSOCK-220 RECV 快路径:芯片缓冲里已有数据 → 当场读出,不登记挂起 */
static void tc_220_recv_fast_path(void)
{
char buf[16];
int fd;
int rc;
bring_up();
fd = mk_established(NET_SOCK_STREAM);
s_net_socks[fd].readable = 1U;
ch395f_fake_push_recv_len((uint8_t)fd, 1400U);
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
rc = net_recv(fd, buf, 10, NET_MSG_DONTWAIT);
CHECK(rc == 10, "TC-NETSOCK-220a 缓冲 1400B、只给 10B 缓冲 → 读满 10(部分读合法)");
CHECK(g_chip.read_calls == 1 && g_chip.last_read_len == 10U,
"TC-NETSOCK-220b 只读调用方给的量(不得按芯片可用量越界写)");
CHECK(s_net_socks[fd].recv_bytes == 10U, "TC-NETSOCK-220c 接收字节统计累计");
CHECK(s_net_socks[fd].readable == 0U,
"TC-NETSOCK-220d 读过即清 readable(下一次 RECV_OK 中断再置)");
CHECK(g_peers.pending_reg_calls == 0, "TC-NETSOCK-220e 数据已在手,不该登记挂起");
reset_all();
}
/* TC-NETSOCK-221 RECV 非阻塞无数据 → WOULDBLOCK(且不许登记挂起) */
static void tc_221_recv_wouldblock(void)
{
char buf[8];
int fd;
int rc;
bring_up();
fd = mk_established(NET_SOCK_STREAM);
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
rc = net_recv(fd, buf, (int)sizeof(buf), NET_MSG_DONTWAIT);
CHECK(rc == -1 && net_get_errno() == NET_ERR_WOULDBLOCK,
"TC-NETSOCK-221a DONTWAIT 无数据 → -1 + WOULDBLOCK");
CHECK(g_peers.pending_reg_calls == 0,
"TC-NETSOCK-221b 非阻塞绝不登记挂起(否则调用方以为自己在等,实际已被挂住)");
/* 模式属性(net_setblocking(0))与单次标志同判据 */
CHECK(net_setblocking(fd, 0) == 0, "TC-NETSOCK-221c 切非阻塞模式成功");
rc = net_recv(fd, buf, (int)sizeof(buf), 0);
CHECK(rc == -1 && net_get_errno() == NET_ERR_WOULDBLOCK,
"TC-NETSOCK-221d blocking 属性=0 与 NET_MSG_DONTWAIT 等价生效");
CHECK(net_recv_nb(fd, buf, (int)sizeof(buf)) == -1 &&
net_get_errno() == NET_ERR_WOULDBLOCK,
"TC-NETSOCK-221e net_recv_nb 具名入口同路径");
reset_all();
}
/* TC-NETSOCK-222 RECV EOF:会话已结束 + 无数据 → 返 0 并登记重建(不是 -1) */
static void tc_222_recv_eof_relisten(void)
{
char buf[8];
int fd;
int rc;
bring_up();
fd = mk_established(NET_SOCK_STREAM);
s_net_socks[fd].state = NET_SOCK_STATE_CLOSED;
s_net_socks[fd].auto_relisten = 1U;
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
rc = net_recv(fd, buf, (int)sizeof(buf), NET_MSG_DONTWAIT);
CHECK(rc == 0, "TC-NETSOCK-222a CLOSED+无数据 → EOF(0),不是错误");
CHECK(g_peers.listener_req_calls == 1 &&
g_peers.last_req_reason == (int)NET_LISTENER_REQ_EOF,
"TC-NETSOCK-222b EOF 登记 REQ_EOF(重建动作交监听层唯一执行者,本层不动芯片)");
CHECK(g_chip.open_calls == 0 && g_chip.listen_calls == 0 && g_chip.close_calls == 0,
"TC-NETSOCK-222c 登记路径上**一个芯片事务都不发**(单一所有者不变量 II)");
/* 抢先 relisten 后 state 已回 LISTENING,但会话电平仍是"已结束" */
reset_all();
bring_up();
fd = mk_established(NET_SOCK_STREAM);
s_net_socks[fd].state = NET_SOCK_STATE_LISTENING;
s_net_socks[fd].session_dead = 1U;
s_net_socks[fd].auto_relisten = 1U;
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
rc = net_recv(fd, buf, (int)sizeof(buf), NET_MSG_DONTWAIT);
CHECK(rc == 0,
"TC-NETSOCK-222d 已 relisten 回 LISTENING 仍判 EOF(N-32 P1 电平化:否则 EOF 永久失踪)");
/* auto_relisten=0(客户端 fd):EOF 不登记重建,交调用方决定重连 */
reset_all();
bring_up();
fd = mk_established(NET_SOCK_STREAM);
s_net_socks[fd].state = NET_SOCK_STATE_CLOSED;
s_net_socks[fd].session_dead = 1U;
s_net_socks[fd].auto_relisten = 0U;
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
rc = net_recv(fd, buf, (int)sizeof(buf), NET_MSG_DONTWAIT);
CHECK(rc == 0 && g_peers.listener_req_calls == 0,
"TC-NETSOCK-222e 非监听 fd 的 EOF 不许替调用方擅自重建");
reset_all();
}
/* TC-NETSOCK-223 RECV 阻塞无数据 → 登记挂起 + 跳过即时回执;到点由 tick 结束 */
static void tc_223_recv_pending_roundtrip(void)
{
char buf[16];
net_msg_t msg;
int fd;
bring_up();
fd = mk_established(NET_SOCK_STREAM);
(void)memset(&msg, 0, sizeof(msg));
msg.type = NET_MSG_RECV;
msg.sockfd = fd;
msg.buf = buf;
msg.len = (int)sizeof(buf);
msg.flags = 0;
msg.timeout_ms = 250U; /* 显式预算必须原样带到登记处 */
msg.caller = (TaskHandle_t)&g_ns_task_caller;
(void)ns_rtos_q_put_raw(&msg);
net_process_messages();
CHECK(g_peers.pending_reg_calls == 1 && g_peers.last_pend_fd == fd,
"TC-NETSOCK-223a 阻塞无数据 → 登记挂起 recv(netTask 零占用返回)");
CHECK(g_peers.last_pend_to == 250U,
"TC-NETSOCK-223b 调用方的显式预算随登记带走(0=无限,这里必须不是 0)");
CHECK(g_peers.last_pend_caller == (TaskHandle_t)&g_ns_task_caller,
"TC-NETSOCK-223c 完成时要知道通知谁");
CHECK(g_ns_notify_calls == 0,
"TC-NETSOCK-223d 登记即发回执 = 把「登记成功」当结果,本条钉它没发生");
/* 数据到达:由挂起 recv 的 tick 结束(脚本模拟) */
ns_peers_arm_pending_done(fd, 7);
net_process_messages();
CHECK(g_ns_notify_calls == 1 && g_ns_notify_last_val == 7U,
"TC-NETSOCK-223e 数据到达后才回执,通知值=字节数");
reset_all();
}
/* TC-NETSOCK-224 net_recv_to:分片超时用独立返回码 -2(不靠 errno,避免跨任务竞态) */
static void tc_224_recv_to_timeout_code(void)
{
char buf[16];
int fd;
int rc;
bring_up();
fd = mk_established(NET_SOCK_STREAM);
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
/* 先造一条"已登记"的挂起,再让 tick 判到点 */
ns_peers_arm_pending_done(fd, NET_ERR_TIMEDOUT);
rc = net_recv_to(fd, buf, (int)sizeof(buf), 30U);
CHECK(rc == -2, "TC-NETSOCK-224a 本片超时 → 独立返回码 -2(与真错误 -1 区分)");
reset_all();
}
/* TC-NETSOCK-225 RECV 参数非法:不碰芯片、不登记 */
static void tc_225_recv_bad_args(void)
{
char buf[4];
int fd;
bring_up();
fd = mk_established(NET_SOCK_STREAM);
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
CHECK(net_recv_to(0, buf, 4, 0U) == -1 && net_get_errno() == NET_ERR_INVAL,
"TC-NETSOCK-225a net_recv_to(timeout=0) 直接拒——0 在 netTask 侧是「无限等待」,"
"与本接口「有界占用」的存在理由相反");
CHECK(g_ns_put_calls == 0, "TC-NETSOCK-225b 拒绝发生在入队之前");
CHECK(net_recv(fd, NULL, 4, NET_MSG_DONTWAIT) == -1 &&
net_get_errno() == NET_ERR_INVAL, "TC-NETSOCK-225c buf 为 NULL → INVAL");
CHECK(net_recv(fd, buf, 0, NET_MSG_DONTWAIT) == -1 &&
net_get_errno() == NET_ERR_INVAL, "TC-NETSOCK-225d len<=0 → INVAL");
CHECK(net_send(fd, NULL, 4, 0) == -1 && net_get_errno() == NET_ERR_INVAL,
"TC-NETSOCK-225e send 的 buf 为 NULL → INVAL");
CHECK(net_send(fd, buf, -1, 0) == -1 && net_get_errno() == NET_ERR_INVAL,
"TC-NETSOCK-225f send 的 len<0 → INVAL");
CHECK(g_chip.write_calls == 0 && g_chip.read_calls == 0 &&
g_peers.pending_reg_calls == 0 && g_peers.send_reg_calls == 0,
"TC-NETSOCK-225g 参数非法路径上零芯片事务、零挂起登记");
reset_all();
}
/* TC-NETSOCK-230 CLOSE:先结束挂起再关芯片,且 last_peer_port 跨释放保留 */
static void tc_230_close_order_and_peer(void)
{
net_msg_t msg;
int fd;
bring_up();
fd = mk_established(NET_SOCK_STREAM);
s_net_socks[fd].auto_relisten = 1U;
/* 登记一条挂起 recv,再看 close 是否先把它结算掉(排空/取消都必通知) */
(void)memset(&msg, 0, sizeof(msg));
msg.type = NET_MSG_RECV;
msg.sockfd = fd;
msg.buf = (void *)"x";
msg.len = 1;
msg.caller = (TaskHandle_t)&g_ns_task_net; /* 另一个任务在等这条流 */
(void)ns_rtos_q_put_raw(&msg);
net_process_messages(); /* 走登记路径(不发回执) */
CHECK(g_peers.pending_reg_calls == 1 && g_ns_notify_calls == 0,
"TC-NETSOCK-230a 前置:已登记挂起且未回执");
/* 芯片侧此刻仍是 OPEN:close 才需要真去拆(已 CLOSED 的形态见 TC-NETSOCK-232) */
g_fake.cached_open[fd] = CH395F_SOCKET_OPEN;
g_ns_notify_calls = 0;
{
s_close_base_clear = g_peers.listener_clear_calls;
s_close_base_settle = g_peers.pending_settle_calls;
}
(void)memset(&msg, 0, sizeof(msg));
msg.type = NET_MSG_CLOSE;
msg.sockfd = fd;
msg.caller = (TaskHandle_t)&g_ns_task_caller;
(void)ns_rtos_q_put_raw(&msg);
net_process_messages();
CHECK(g_peers.pending_settle_calls == s_close_base_settle + 1,
"TC-NETSOCK-230b close 必先结算本 fd 的挂起 recv(否则等待方永久悬挂)");
CHECK(g_peers.pending_notified == 1,
"TC-NETSOCK-230c 结算必通知(取消也要通知,不是静默清槽)");
CHECK(g_chip.close_calls >= 1 && g_fxchip.rst_begin_calls == 1,
"TC-NETSOCK-230d 芯片仍 OPEN → 走 sock_reset 确定性排渣把它拆掉");
CHECK(g_fxchip.glob_calls == 0,
"TC-NETSOCK-230e close 全程不得读全局中断寄存器(B3:唯一读者=net_poll)");
CHECK(s_net_socks[fd].in_use == 0U && s_net_socks[fd].last_peer_port == 51999U,
"TC-NETSOCK-230f 控制块清零但 last_peer_port 保留(幽灵甄别基准属芯片 Socket 级)");
CHECK(g_peers.listener_clear_calls == s_close_base_clear + 1,
"TC-NETSOCK-230g close 幂等清理监听重试(关闭即取消待重试,D10)");
CHECK(g_peers.pend_active_now[fd] == 0,
"TC-NETSOCK-230h close 后本 fd 不再有挂起 recv(两张表都归零)");
reset_all();
}
/* TC-NETSOCK-231 CLOSE 未在用:net_get_sock 判空 → BADF 通知值 */
static void tc_231_close_badfd(void)
{
net_msg_t msg;
bring_up();
(void)memset(&msg, 0, sizeof(msg));
msg.type = NET_MSG_CLOSE;
msg.sockfd = 5;
msg.caller = (TaskHandle_t)&g_ns_task_caller;
(void)ns_rtos_q_put_raw(&msg);
net_process_messages();
CHECK(g_ns_notify_last_val == (uint32_t)NET_ERR_BADF,
"TC-NETSOCK-231a close 未分配的 fd → BADF 经通知值回传");
CHECK(g_chip.close_calls == 0, "TC-NETSOCK-231b BADF 不碰芯片");
bring_up();
(void)memset(&msg, 0, sizeof(msg));
msg.type = NET_MSG_ACCEPT; /* 没有 in_use 的监听 */
msg.sockfd = 5;
msg.caller = (TaskHandle_t)&g_ns_task_caller;
(void)ns_rtos_q_put_raw(&msg);
net_process_messages();
CHECK(g_ns_notify_last_val == (uint32_t)NET_ERR_INVAL,
"TC-NETSOCK-231c accept 未在用 → INVAL(控制块级校验,不是 fd 越界)");
reset_all();
}
/* TC-NETSOCK-240 DROP_CONN:四类前置各走各的分支(幂等语义是关键) */
static void tc_240_drop_conn_branches(void)
{
net_msg_t msg;
int fd;
/* 非在用 → BADF */
bring_up();
(void)memset(&msg, 0, sizeof(msg));
msg.type = NET_MSG_DROP_CONN;
msg.sockfd = 2;
msg.caller = (TaskHandle_t)&g_ns_task_caller;
(void)ns_rtos_q_put_raw(&msg);
net_process_messages();
CHECK(g_ns_notify_last_val == (uint32_t)NET_ERR_BADF, "TC-NETSOCK-240a 未分配 → BADF");
/* UDP → INVAL */
bring_up();
fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
(void)memset(&msg, 0, sizeof(msg));
msg.type = NET_MSG_DROP_CONN;
msg.sockfd = fd;
msg.caller = (TaskHandle_t)&g_ns_task_caller;
(void)ns_rtos_q_put_raw(&msg);
net_process_messages();
CHECK(g_ns_notify_last_val == (uint32_t)NET_ERR_INVAL,
"TC-NETSOCK-240b 非 TCP 流 → INVAL(本 API 只拆 TCP 会话)");
/* 监听态但无活动会话 → 幂等 0,不动芯片 */
bring_up();
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
s_net_socks[fd].state = NET_SOCK_STATE_LISTENING;
s_net_socks[fd].auto_relisten = 1U;
(void)memset(&msg, 0, sizeof(msg));
msg.type = NET_MSG_DROP_CONN;
msg.sockfd = fd;
msg.caller = (TaskHandle_t)&g_ns_task_caller;
(void)ns_rtos_q_put_raw(&msg);
net_process_messages();
CHECK(g_ns_notify_last_val == 0U, "TC-NETSOCK-240c 无活动会话 → 幂等成功 0");
CHECK(g_chip.close_calls == 0 && g_fxchip.rst_begin_calls == 0,
"TC-NETSOCK-240d 幂等分支不得拆芯片(会把在听的监听毁掉)");
/* ESTABLISHED + 监听型 → QUARANTINE 拆除 + 登记重建 */
bring_up();
fd = mk_established(NET_SOCK_STREAM);
s_net_socks[fd].auto_relisten = 1U;
(void)memset(&msg, 0, sizeof(msg));
msg.type = NET_MSG_DROP_CONN;
msg.sockfd = fd;
msg.caller = (TaskHandle_t)&g_ns_task_caller;
(void)ns_rtos_q_put_raw(&msg);
net_process_messages();
CHECK(g_peers.pending_settle_calls == 1 && g_peers.send_cancel_calls == 1,
"TC-NETSOCK-240e 拆会话前先结束两张表的挂起(必通知)");
CHECK(g_fxchip.rst_begin_calls == 1,
"TC-NETSOCK-240f 拆除走 sock_reset 确定性排渣(不是 close+盲等)");
CHECK(g_peers.listener_req_calls == 1 &&
g_peers.last_req_reason == (int)NET_LISTENER_REQ_DROP,
"TC-NETSOCK-240g 监听型 fd 拆完登记 REQ_DROP(重建交监听层,本层不自己 open)");
CHECK(s_net_socks[fd].state == NET_SOCK_STATE_CLOSED &&
s_net_socks[fd].in_use == 1U && s_net_socks[fd].local_port == 2404U &&
s_net_socks[fd].auto_relisten == 1U,
"TC-NETSOCK-240h 只收口会话簿记:fd 保留、监听属性与端口都留下");
CHECK(g_fxchip.glob_calls == 0,
"TC-NETSOCK-240i 拆除路径也只碰本 Socket(F-NET-7:不读全局)");
reset_all();
}
/* TC-NETSOCK-250 入队失败(队列满)→ 调用方 BUSY,不留下"已提交"的假象 */
static void tc_250_put_fail_busy(void)
{
int fd;
int rc;
bring_up();
fd = mk_established(NET_SOCK_STREAM);
ns_rtos_q_fill((int)NS_Q_CAP); /* 把队列灌满 */
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
rc = net_send(fd, "abc", 3, 0);
CHECK(rc == -1 && net_get_errno() == NET_ERR_BUSY,
"TC-NETSOCK-250a 队列满 → net_send 报 BUSY(不是静默丢弃)");
CHECK(g_chip.write_calls == 0 && g_ns_notify_calls == 0,
"TC-NETSOCK-250b 入队失败时零芯片事务、零通知(调用方不会误等回执)");
CHECK(net_listen(fd) == -1 && net_get_errno() == NET_ERR_BUSY,
"TC-NETSOCK-250c 同一形态对 net_listen 成立");
CHECK(net_close(fd) == -1 && net_get_errno() == NET_ERR_BUSY,
"TC-NETSOCK-250d 同一形态对 net_close 成立");
CHECK(net_drop_conn(fd) == -1 && net_get_errno() == NET_ERR_BUSY,
"TC-NETSOCK-250e 同一形态对 net_drop_conn 成立");
CHECK(net_recv_to(fd, (void *)"x", 1, 5U) == -1 && net_get_errno() == NET_ERR_BUSY,
"TC-NETSOCK-250f 同一形态对 net_recv_to 成立");
reset_all();
}
static void ns_run_s3(void)
{
tc_200_unknown_type();
tc_201_ticks_run_before_dispatch();
tc_202_no_queue_bound();
tc_210_send_end_to_end();
tc_211_send_chunking();
tc_212_send_pending_defers_notify();
tc_213_send_error_code_roundtrip();
tc_220_recv_fast_path();
tc_221_recv_wouldblock();
tc_222_recv_eof_relisten();
tc_223_recv_pending_roundtrip();
tc_224_recv_to_timeout_code();
tc_225_recv_bad_args();
tc_230_close_order_and_peer();
tc_231_close_badfd();
tc_240_drop_conn_branches();
tc_250_put_fail_busy();
}