test(net/udp): 主机单测补 UDP-25 两条 socket 并存的隔离(#73)
前 24 组全都只用一条 UDP socket,漏掉的正是队列化真正要保的那半句承诺: **等待是一条 socket 自己的事**——串行的是命令,不是等待者。 UDP-25 用 13 条断言钉住两条并存 socket 的四件事:fdB 来包不得结算也不得通知 fdA 的 parked 收包(连它的调用缓冲都不许被写一个字节);fdA 自己的包到达时按 自己那一包的表头结算并恰好通知一次;fdB parked 时 fdA 自己 recvfrom 照常拿到 数据;关停在等的 fdB 必须当场结算、槽位与出参槽都不残留。 写这组时踩到假件的一个真坑,值得记下来:假件的 get_recv_len 是"每次读消耗一格、 最后一格永远重复"的脚本队列,本组前面给同一 fd 留过旧长度,不清就会拿**旧长度配 新内容**(现象是 UDP-25j 读出 12 字节而不是 7 字节),所以每条用例前用 ch395f_fake_reset_recv_len(fd) 归零再宣布——这是测法问题,不是被测代码的问题。 全套 667/667 绿(改造前 470,本次 +13);net_udp.c 仍 237/239 行 14/14 函数, net_socket.c 966/969 行 59/59 函数,模块级覆盖率无回退。
This commit is contained in:
@@ -1456,6 +1456,105 @@ static void tc_udp24_random_soak(void)
|
||||
reset_all();
|
||||
}
|
||||
|
||||
/* ================= UDP-25 两条 UDP socket 并存:挂起不串门 ================= */
|
||||
|
||||
/* 前面 1~24 组全都只用一条 UDP socket,唯独漏了队列化真正要保的东西:
|
||||
* **等待是一条 socket 自己的事**。两条 socket 里一条 parked 时,
|
||||
* 另一条来包不得惊动它,另一条自己收发也不得被它拖住——
|
||||
* 这是"串行执行、并行等待"这半句承诺的唯一形状。 */
|
||||
static void tc_udp25_two_socket_park_isolation(void)
|
||||
{
|
||||
char buf_a[32];
|
||||
char buf_b[32];
|
||||
char buf_c[32];
|
||||
struct net_sockaddr_in sa;
|
||||
net_msg_t msg;
|
||||
int fd_a;
|
||||
int fd_b;
|
||||
int notifies_before;
|
||||
int reads_before;
|
||||
int alen;
|
||||
int rc;
|
||||
char nm[200];
|
||||
|
||||
bring_up_netctx();
|
||||
fd_a = udp_bind(123);
|
||||
fd_b = udp_bind(456);
|
||||
CHECK(fd_a >= 0 && fd_b >= 0 && fd_a != fd_b,
|
||||
"UDP-25a two bound UDP sockets own different slots (per-fd state, not a module-wide flag)");
|
||||
if (fd_a < 0 || fd_b < 0 || fd_a == fd_b) {
|
||||
reset_all();
|
||||
return;
|
||||
}
|
||||
|
||||
/* fdA 的收包登记成挂起:此刻两条 socket 的芯片缓冲都是空的 */
|
||||
(void)memset(buf_a, 0x5A, (int)sizeof(buf_a));
|
||||
ch395f_fake_push_recv_len((uint8_t)fd_a, 0U);
|
||||
ch395f_fake_push_recv_len((uint8_t)fd_b, 0U);
|
||||
udp_arm_and_enqueue(fd_a, buf_a, (int)sizeof(buf_a), 0U, &msg);
|
||||
net_process_messages();
|
||||
CHECK(net_udp_pending(fd_a) == 1 && net_udp_pending(fd_b) == 0,
|
||||
"UDP-25b only the socket someone actually waited on holds a pending slot");
|
||||
|
||||
/* fdB 来一个包:fdA 既不能被结算,也不能被通知,缓冲区一个字节都不能动 */
|
||||
notifies_before = g_ns_notify_calls;
|
||||
reads_before = g_chip.read_calls;
|
||||
s_net_socks[fd_b].readable = 1U;
|
||||
ch395f_fake_push_recv_len((uint8_t)fd_b, 16U);
|
||||
net_process_messages();
|
||||
CHECK(net_udp_pending(fd_a) == 1,
|
||||
"UDP-25c another socket's datagram does not settle my parked receive");
|
||||
CHECK(g_ns_notify_calls == notifies_before,
|
||||
"UDP-25d and does not wake the waiter (a stray notify turns the next real packet into a stale -2)");
|
||||
CHECK(buf_a[0] == (char)0x5A,
|
||||
"UDP-25e the parked caller's buffer is untouched by the other socket's traffic");
|
||||
|
||||
/* 现在轮到 fdA 自己的包到:必须就地结算,源地址来自它自己那一包的头部。
|
||||
* 长度先把该 fd 的脚本队列清空再给一条:假件的长度是"每次读消耗一格、最后一格
|
||||
* 永远重复",本组前面给 fdA 留过 0/16 之类的旧值,不清就会拿旧长度配新内容。 */
|
||||
udp_stage_server_datagram("TIME12345678", 12, 51999U, "192.168.31.9");
|
||||
ch395f_fake_reset_recv_len((uint8_t)fd_a);
|
||||
ch395f_fake_push_recv_len((uint8_t)fd_a, (uint16_t)(8 + 12));
|
||||
s_net_socks[fd_a].readable = 1U;
|
||||
net_process_messages();
|
||||
CHECK(net_udp_pending(fd_a) == 0 && net_udp_armed(fd_a) == 0,
|
||||
"UDP-25f the socket's own datagram settles its receive and frees both slots");
|
||||
CHECK(g_ns_notify_calls == notifies_before + 1 &&
|
||||
g_ns_notify_last_to == (TaskHandle_t)&g_ns_task_caller &&
|
||||
g_ns_notify_last_val == (uint32_t)12,
|
||||
"UDP-25g exactly one notify to the waiter, carrying the byte count");
|
||||
CHECK(memcmp(buf_a, "TIME12345678", 12) == 0,
|
||||
"UDP-25h the delivered bytes are this socket's own payload (header stripped, nothing off by eight)");
|
||||
|
||||
/* 先把 fdB 的芯片长度重新宣布成"空"(清掉上一段留的 16),否则它当场读走而不是挂起 */
|
||||
ch395f_fake_reset_recv_len((uint8_t)fd_b);
|
||||
ch395f_fake_push_recv_len((uint8_t)fd_b, 0U);
|
||||
udp_arm_and_enqueue(fd_b, buf_b, (int)sizeof(buf_b), 0U, &msg);
|
||||
net_process_messages();
|
||||
CHECK(net_udp_pending(fd_b) == 1, "UDP-25i the second socket is now the parked one");
|
||||
udp_stage_server_datagram("FD-A-OK", 7, 52000U, "192.168.31.8");
|
||||
ch395f_fake_reset_recv_len((uint8_t)fd_a);
|
||||
ch395f_fake_push_recv_len((uint8_t)fd_a, (uint16_t)(8 + 7));
|
||||
s_net_socks[fd_a].readable = 1U;
|
||||
alen = (int)sizeof(sa);
|
||||
(void)memset(&sa, 0, sizeof(sa));
|
||||
rc = net_recvfrom(fd_a, buf_c, (int)sizeof(buf_c), NET_MSG_DONTWAIT,
|
||||
(struct net_sockaddr *)&sa, &alen);
|
||||
(void)snprintf(nm, sizeof(nm),
|
||||
"UDP-25j a parked socket never blocks another socket's own recvfrom (rc=%d want 7, parked still waiting=%d want 1)",
|
||||
rc, net_udp_pending(fd_b));
|
||||
CHECK(rc == 7 && net_udp_pending(fd_b) == 1, nm);
|
||||
CHECK(net_ntohs(sa.sin_port) == 52000U,
|
||||
"UDP-25k and the source address still comes from the packet it actually read");
|
||||
CHECK(g_chip.read_calls > reads_before,
|
||||
"UDP-25l the chip really was read for fdA (the group is not passing by doing nothing)");
|
||||
|
||||
(void)net_close_locked(&s_net_socks[fd_b]);
|
||||
CHECK(net_udp_pending(fd_b) == 0 && net_udp_armed(fd_b) == 0,
|
||||
"UDP-25m closing the parked socket settles it; nothing may outlive the fd");
|
||||
reset_all();
|
||||
}
|
||||
|
||||
/* ===================== 本分片入口 ===================== */
|
||||
|
||||
static void ns_run_s7(void)
|
||||
@@ -1484,4 +1583,5 @@ static void ns_run_s7(void)
|
||||
tc_udp22_set_peer_direct_ctx();
|
||||
tc_udp23_recvfrom_direct_ctx();
|
||||
tc_udp24_random_soak();
|
||||
tc_udp25_two_socket_park_isolation();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user