b4441f0e1e
假 CH395F 的第二批消费者。新增 test/host/net_pending/,net_pending.c 覆盖 169/194 = 87.1%。共享假芯片补了**缓冲尺寸宏链的同构转抄**(RAM_BLOCK 512 / SEND_BLOCKS 2 → SEND_BUF 1024;RECV_BLOCKS 4 → RECV_BUF 2048 → SPI_DMA_BUF 2052 → SPI_DMA_MAX_PAYLOAD 2048),于是 net_pending.h 里的 NET_SEND_CHUNK_MAX 是**真值 1024**,分块断言测的是芯片上界而不是假件自己发明的数字。 本模块的核心契约是跨上下文回传:AGENTS 模块约定写着 net_* 的错误码经**任务通知值** 回传(net_msg_t 按值拷入队列,netTask 改的是自己那份副本,调用方永远看不到;也勿给 net_msg_t 加回传字段,否则调用方读到栈垃圾)。台架读不到通知值,所以把 xTaskNotify 做成可观测假件(记目标句柄与通知值)之后,这条契约才第一次被真正验证。 钉住的契约: - 注册闸门:越界 fd 报 BADF;同 fd 已有挂起报 BUSY;caller 为空报 BUSY;**属主任务 自己发起被拒**(自己等自己等于死锁 netTask);to_ms=0 记为无限等(不置 deadline)。 - 到达与截断:读量钳在调用方缓冲内(绝不越界写别人的 buf);readable 但芯片没数据时 只清标志继续等,不误交 0 字节。 - 收尾四条分支:还有残留先把最后一包读走再收尾(**不丢尾包**);干净结束报 0 并按 auto_relisten 请求重开监听(不自动重开的不被偷偷要求重建);released_abnormal 报 CONNRESET 而不是 0(调用方要能分辨好坏收尾);session_dead 与 CLOSED 同路。 - settle_close:没挂起时直接返回不打扰芯片;有残留交还字节数;无残留报 CONNRESET。 - 挂起发送:参数校验(buf 空 / len<=0 / sent<0 / sent>=len 报 INVAL);单次写入钳在 NET_SEND_CHUNK_MAX;**deadline 每写一轮就续期**(还在推进就不该被 1000ms 掐死, FTP 上传大文件最容易撞这条);停滞不前进才报 TIMEDOUT;槽位消失或会话塌掉都**交还 已发字节数**(不是错误码,调用方据此算进度);cancel 留痕且幂等;**clear 不发通知** (与 recv 侧同一条纪律,不给调用方假答复)。 自查:42/43 首跑,唯一红的是**我的测试构造缺陷**——无限等那条没先把槽位置成在用, 被 in_use==0 兜底分支提前收口,测不到时间轴;补 mk_sock 后 43/43。reset_all 从上一 批的教训直接带过来(连被测模块的 static 挂起表与属主句柄一起复位),这次没再红。 覆盖率与台账:套件 23 → 24 全绿;files_tested 27 → 28;未触及 .c 39 → 38;分母 3,572 → 3,766 行;line=3297/3766(87.5%)、func=255/282(90.4%)。恒等式 38 = 4(P1 剩) + 5(P2) + 29(P3) 仍咬合。编号补建 TC-HOST-NETPEND(游标 040), 族数 17 → 18;注册表仍只做增量插入。 自查门禁:test-cppcheck Passed;check_ids --strict 通过;doc-links 35 处(= 存量); 12 个新增/改动文件 BOM/CR/U+FFFD/行尾逐项净。零生产代码改动,未跑 @build。 --all-files 仍只有 mem-numbers / mem-budget(.map 台账漂移,等 Lead 合并后统一回填) 与 ui-golden(ref/*.bin 不入库)三条失败,与本分支改动无关。
468 lines
20 KiB
C
468 lines
20 KiB
C
/*
|
||
* 模块名称:NET 层挂起 recv / 挂起 send(net_pending)主机侧单元测试
|
||
* 模块功能:在 PC 上离线验证 Drivers/BSP/NET/net_pending.c 的两条挂起队列:注册闸门
|
||
* (越界/重复/执行任务自己发起)、**错误码经任务通知值回传**这条跨上下文
|
||
* 契约、收数据到达与截断、超时、会话塌掉的四种收尾分支(不丢尾包 / 干净
|
||
* 结束报 0 / 异常释放报 CONNRESET / 槽位消失报 CONNRESET)、settle_close 的
|
||
* 收尾语义,以及发送侧的分块上限、停滞判定、deadline 续期与取消语义。
|
||
*
|
||
* 被测对象:真实源文件 ../../../Drivers/BSP/NET/net_pending.c(#include 原样纳入)。
|
||
* net_pending.h / net_types.h / net_config.h / net_listener.h 全用**真实头**
|
||
* (TaskHandle_t 入参、NET_PENDING_REGISTERED、NET_SEND_TIMEOUT_MS、
|
||
* net_listener_req_t 枚举一改就编译期报错)。假件:
|
||
* - 假 CH395F 芯片(test/host/common/ch395f_fake.h/.c,含与真实头同构转抄
|
||
* 的缓冲尺寸宏链,NET_SEND_CHUNK_MAX 因此是真值 1024 而非假件自造);
|
||
* - **可观测的 xTaskNotify**(记目标句柄与通知值)与固定句柄的
|
||
* xTaskGetCurrentTaskHandle;
|
||
* - 记录型 net_listener_request;可控时基;可控 socket 表数组(tick 入参);
|
||
* 可控"芯片此刻还有多少字节可读";dbg_log 计数;net_set_errno 记录。
|
||
*
|
||
* 为什么要主机侧单测(而不是只靠台架):
|
||
* 1. AGENTS 模块约定写着:net_* 公开 API 的错误码经**任务通知值**回传,因为 net_msg_t
|
||
* 是**按值拷入队列**的,netTask 改的是自己那份副本,调用方永远看不到;也**勿给
|
||
* net_msg_t 加回传字段**(加了只会让调用方读到栈垃圾)。这条契约在真机上表现为
|
||
* "偶发拿到 0 或正数却不知道为什么",日志无痕;在 PC 上必须把 xTaskNotify 做成
|
||
* 可观测的才谈得上验证——台架根本读不到通知值。
|
||
* 2. 挂起 recv 的收尾有**四条**分支(还有尾包 / 干净结束报 0 / 异常释放报 CONNRESET /
|
||
* 槽位没了报 CONNRESET),差一条就是"对端已断而我方任务还挂着"或"最后一包数据
|
||
* 被丢掉"。真机要凑齐"关闭瞬间芯片接收区还剩字节"这种窗口极难。
|
||
* 3. 发送侧分块上限 = min(芯片发送缓冲, SPI DMA 上界):一次写太多会踩芯片 FIFO,
|
||
* 表现为"大包发送偶发数据错位";而 deadline 每写一轮就续期,写错就会把**长文件
|
||
* 在传输中途掐死**(FTP 上传大文件最容易撞)。两者都要能精确数着字节测。
|
||
*
|
||
* 覆盖点:TC-NETPEND-001..006 注册闸门;007..018 到达/截断/超时/四条收尾/取消与清理;
|
||
* 019..021 settle_close;030..040 挂起发送(参数校验、分块、续期、停滞、取消)。
|
||
*
|
||
* 通过:全部 TC PASS(main 返回 0)。
|
||
* 入口:int main(void),返回 0=PASS / 1=FAIL(由 run 脚本判读)。
|
||
*
|
||
* 创建日期:2026-10-23
|
||
*/
|
||
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
#include "net_pending.h"
|
||
#include "net_listener.h"
|
||
#include "net_socket.h"
|
||
#include "ch395f.h"
|
||
|
||
#include "ch395f_fake.c"
|
||
#include "net_pending.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)
|
||
|
||
int g_npd_info_count;
|
||
int g_npd_error_count;
|
||
int g_npd_debug_count;
|
||
|
||
/* ============ 可观测的任务通知(本模块最重要的一条契约靠它才测得到) ============ */
|
||
|
||
static int g_owner_obj;
|
||
static int g_caller_obj;
|
||
static int g_notify_n;
|
||
static void *g_notify_target[16];
|
||
static uint32_t g_notify_value[16];
|
||
|
||
TaskHandle_t xTaskGetCurrentTaskHandle(void)
|
||
{
|
||
return (TaskHandle_t)&g_owner_obj;
|
||
}
|
||
|
||
BaseType_t xTaskNotify(TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction)
|
||
{
|
||
(void)eAction;
|
||
if (g_notify_n < (int)(sizeof(g_notify_target) / sizeof(g_notify_target[0]))) {
|
||
g_notify_target[g_notify_n] = (void *)xTaskToNotify;
|
||
g_notify_value[g_notify_n] = ulValue;
|
||
}
|
||
g_notify_n++;
|
||
return 1;
|
||
}
|
||
|
||
/* ============ 记录型监听请求 ============ */
|
||
|
||
static int g_lis_req_n;
|
||
static int g_lis_req_sock;
|
||
static int g_lis_req_reason;
|
||
|
||
void net_listener_request(int sockfd, net_listener_req_t reason)
|
||
{
|
||
g_lis_req_n++;
|
||
g_lis_req_sock = sockfd;
|
||
g_lis_req_reason = (int)reason;
|
||
}
|
||
|
||
void net_listener_settle(int sockfd, net_listener_settle_t level) { (void)sockfd; (void)level; }
|
||
void net_listener_clear(int sockfd) { (void)sockfd; }
|
||
void net_listener_tick(void) { }
|
||
|
||
/* ============ 可控时基与 errno ============ */
|
||
|
||
static uint32_t g_now;
|
||
static int g_errno_last;
|
||
static int g_errno_calls;
|
||
|
||
uint32_t HAL_GetTick(void) { return g_now; }
|
||
|
||
int net_set_errno(int err)
|
||
{
|
||
g_errno_last = err;
|
||
g_errno_calls++;
|
||
return err;
|
||
}
|
||
|
||
/* ============ 可控 socket 表(tick 的入参) ============ */
|
||
|
||
static net_sock_t g_socks[NET_MAX_SOCKETS];
|
||
static uint8_t g_rbuf[64];
|
||
static uint8_t g_wbuf[4096];
|
||
|
||
static void mk_sock(int fd, net_sock_state_t st)
|
||
{
|
||
g_socks[fd].in_use = 1u;
|
||
g_socks[fd].state = st;
|
||
g_socks[fd].type = NET_SOCK_STREAM;
|
||
g_socks[fd].auto_relisten = 1u;
|
||
g_socks[fd].local_port = 2404u;
|
||
}
|
||
|
||
static void reset_all(void)
|
||
{
|
||
int i;
|
||
|
||
(void)memset(g_socks, 0, sizeof(g_socks));
|
||
(void)memset(g_rbuf, 0, sizeof(g_rbuf));
|
||
g_now = 1000u;
|
||
g_errno_last = 0;
|
||
g_errno_calls = 0;
|
||
g_notify_n = 0;
|
||
g_lis_req_n = 0;
|
||
g_lis_req_sock = -1;
|
||
g_lis_req_reason = -1;
|
||
g_npd_info_count = 0;
|
||
g_npd_error_count = 0;
|
||
g_npd_debug_count = 0;
|
||
ch395f_fake_reset();
|
||
/* 被测模块自己的 static 必须复位:两张挂起表 + 属主句柄
|
||
(net_listener 批次在这里红过 9 条,同样的坑不踩第二遍)。 */
|
||
for (i = 0; i < NET_MAX_SOCKETS; i++) {
|
||
s_net_pending[i].active = 0u;
|
||
s_net_send[i].active = 0u;
|
||
}
|
||
s_owner_task = (TaskHandle_t)&g_owner_obj;
|
||
}
|
||
|
||
static void tc_recv_register(void)
|
||
{
|
||
int rc;
|
||
|
||
printf("-- TC-NETPEND-001..006 recv register --\n");
|
||
reset_all();
|
||
rc = net_pending_register(-1, g_rbuf, 10, 100u, (TaskHandle_t)&g_caller_obj);
|
||
CHECK((rc == -1) && (g_errno_last == NET_ERR_BADF), "TC-NETPEND-001 下越界 fd 报 BADF");
|
||
rc = net_pending_register(NET_MAX_SOCKETS, g_rbuf, 10, 100u, (TaskHandle_t)&g_caller_obj);
|
||
CHECK((rc == -1) && (g_errno_last == NET_ERR_BADF), "TC-NETPEND-001b 上越界同样拦下");
|
||
|
||
reset_all();
|
||
rc = net_pending_register(0, g_rbuf, 10, 100u, (TaskHandle_t)&g_caller_obj);
|
||
CHECK(rc == NET_PENDING_REGISTERED, "TC-NETPEND-005 注册成功回 0x70000000 标记");
|
||
rc = net_pending_register(0, g_rbuf, 10, 100u, (TaskHandle_t)&g_caller_obj);
|
||
CHECK((rc == -1) && (g_errno_last == NET_ERR_BUSY),
|
||
"TC-NETPEND-002 同 fd 已有挂起就拒绝(一条 socket 只挂一个 recv)");
|
||
|
||
reset_all();
|
||
rc = net_pending_register(0, g_rbuf, 10, 100u, NULL);
|
||
CHECK((rc == -1) && (g_errno_last == NET_ERR_BUSY),
|
||
"TC-NETPEND-003 caller 为空拒绝(没有回传目标就别挂起)");
|
||
rc = net_pending_register(0, g_rbuf, 10, 100u, (TaskHandle_t)&g_owner_obj);
|
||
CHECK((rc == -1) && (g_errno_last == NET_ERR_BUSY),
|
||
"TC-NETPEND-004 属主任务自己发起被拒(自己等自己 = 死锁 netTask)");
|
||
|
||
reset_all();
|
||
/* 必须先把槽位置成"在用且已建立":否则 tick 第一条兜底(in_use == 0 →
|
||
* CONNRESET)就会把挂起收掉,测的就不是时间轴了。 */
|
||
mk_sock(0, NET_SOCK_STATE_ESTABLISHED);
|
||
(void)net_pending_register(0, g_rbuf, 10, 0u, (TaskHandle_t)&g_caller_obj);
|
||
CHECK((s_net_pending[0].infinite == 1u) && (s_net_pending[0].deadline == 0u),
|
||
"TC-NETPEND-006 to_ms=0 记为无限等(不置 deadline)");
|
||
g_now = 999999u;
|
||
net_pending_tick(g_socks);
|
||
CHECK((s_net_pending[0].active == 1u) && (g_notify_n == 0),
|
||
"TC-NETPEND-006b 无限等不被时间掐掉(也不会偷偷发通知)");
|
||
}
|
||
|
||
static void tc_recv_paths(void)
|
||
{
|
||
printf("-- TC-NETPEND-007..018 recv completion --\n");
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_ESTABLISHED);
|
||
g_socks[0].readable = 1u;
|
||
(void)net_pending_register(0, g_rbuf, 10, 500u, (TaskHandle_t)&g_caller_obj);
|
||
ch395f_fake_push_recv_len(0, 5u);
|
||
net_pending_tick(g_socks);
|
||
CHECK((g_notify_n == 1) && (g_notify_value[0] == 5u) &&
|
||
(g_notify_target[0] == (void *)&g_caller_obj) &&
|
||
(g_socks[0].recv_bytes == 5u) && (g_socks[0].readable == 0u) &&
|
||
(s_net_pending[0].active == 0u),
|
||
"TC-NETPEND-007 数据到达:字节数经通知值回传给发起任务");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_ESTABLISHED);
|
||
g_socks[0].readable = 1u;
|
||
(void)net_pending_register(0, g_rbuf, 10, 500u, (TaskHandle_t)&g_caller_obj);
|
||
ch395f_fake_push_recv_len(0, 100u);
|
||
net_pending_tick(g_socks);
|
||
CHECK((g_notify_value[0] == 10u) && (g_chip.last_read_len == 10u),
|
||
"TC-NETPEND-008 读量钳在调用方缓冲内(绝不越界写别人的 buf)");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_ESTABLISHED);
|
||
g_socks[0].readable = 1u;
|
||
(void)net_pending_register(0, g_rbuf, 10, 500u, (TaskHandle_t)&g_caller_obj);
|
||
net_pending_tick(g_socks);
|
||
CHECK((g_notify_n == 0) && (g_socks[0].readable == 0u) &&
|
||
(s_net_pending[0].active == 1u),
|
||
"TC-NETPEND-009 空 readable:清标志继续等,不误交 0 字节");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_ESTABLISHED);
|
||
(void)net_pending_register(0, g_rbuf, 10, 100u, (TaskHandle_t)&g_caller_obj);
|
||
g_now = 1100u;
|
||
net_pending_tick(g_socks);
|
||
CHECK((g_notify_value[0] == (uint32_t)NET_ERR_TIMEDOUT) &&
|
||
(g_errno_last == NET_ERR_TIMEDOUT) && (s_net_pending[0].active == 0u),
|
||
"TC-NETPEND-010 超时:错误码同时进通知值与 errno(跨上下文拿得到)");
|
||
|
||
reset_all();
|
||
(void)net_pending_register(0, g_rbuf, 10, 500u, (TaskHandle_t)&g_caller_obj);
|
||
g_socks[0].in_use = 0u;
|
||
net_pending_tick(g_socks);
|
||
CHECK(g_notify_value[0] == (uint32_t)NET_ERR_CONNRESET,
|
||
"TC-NETPEND-011 槽位消失报 CONNRESET(不能让任务熬到超时才发现)");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_CLOSED);
|
||
(void)net_pending_register(0, g_rbuf, 20, 500u, (TaskHandle_t)&g_caller_obj);
|
||
ch395f_fake_push_recv_len(0, 12u);
|
||
net_pending_tick(g_socks);
|
||
CHECK((g_notify_value[0] == 12u) && (g_socks[0].recv_bytes == 12u) &&
|
||
(g_lis_req_n == 0),
|
||
"TC-NETPEND-012 关闭但有残留:先把最后一包读走再收尾(不丢尾包)");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_CLOSED);
|
||
(void)net_pending_register(0, g_rbuf, 20, 500u, (TaskHandle_t)&g_caller_obj);
|
||
net_pending_tick(g_socks);
|
||
CHECK((g_notify_value[0] == 0u) && (g_lis_req_n == 1) && (g_lis_req_sock == 0) &&
|
||
(g_lis_req_reason == (int)NET_LISTENER_REQ_PENDING),
|
||
"TC-NETPEND-013 干净结束:报 0 并请求重开监听(单连接 socket 必须回 LISTEN)");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_CLOSED);
|
||
g_socks[0].auto_relisten = 0u;
|
||
(void)net_pending_register(0, g_rbuf, 20, 500u, (TaskHandle_t)&g_caller_obj);
|
||
net_pending_tick(g_socks);
|
||
CHECK((g_notify_value[0] == 0u) && (g_lis_req_n == 0),
|
||
"TC-NETPEND-013b 不自动重开的 socket 不会被偷偷要求重建监听");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_CLOSED);
|
||
g_socks[0].released_abnormal = 1u;
|
||
(void)net_pending_register(0, g_rbuf, 20, 500u, (TaskHandle_t)&g_caller_obj);
|
||
net_pending_tick(g_socks);
|
||
CHECK(g_notify_value[0] == (uint32_t)NET_ERR_CONNRESET,
|
||
"TC-NETPEND-014 异常释放报 CONNRESET 而不是 0(调用方要能分辨好坏收尾)");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_ESTABLISHED);
|
||
g_socks[0].session_dead = 1u;
|
||
(void)net_pending_register(0, g_rbuf, 20, 500u, (TaskHandle_t)&g_caller_obj);
|
||
net_pending_tick(g_socks);
|
||
CHECK((g_notify_n == 1) && (g_lis_req_n == 1),
|
||
"TC-NETPEND-015 session_dead 与 CLOSED 同路(状态滞后时仍能收口)");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_ESTABLISHED);
|
||
(void)net_pending_register(0, g_rbuf, 10, 500u, (TaskHandle_t)&g_caller_obj);
|
||
net_pending_cancel(0, NET_ERR_CONNRESET);
|
||
CHECK((g_notify_value[0] == (uint32_t)NET_ERR_CONNRESET) &&
|
||
(g_errno_last == NET_ERR_CONNRESET) && (s_net_pending[0].active == 0u),
|
||
"TC-NETPEND-016 取消:错误码经通知值送达并解除挂起");
|
||
net_pending_cancel(0, NET_ERR_CONNRESET);
|
||
CHECK(g_notify_n == 1, "TC-NETPEND-016b 重复取消幂等(不给同一任务发两次通知)");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_ESTABLISHED);
|
||
mk_sock(2, NET_SOCK_STATE_ESTABLISHED);
|
||
(void)net_pending_register(0, g_rbuf, 10, 500u, (TaskHandle_t)&g_caller_obj);
|
||
(void)net_pending_register(2, g_rbuf, 10, 500u, (TaskHandle_t)&g_caller_obj);
|
||
net_pending_cancel_all(NET_ERR_CONNRESET);
|
||
CHECK((g_notify_n == 2) && (s_net_pending[0].active == 0u) &&
|
||
(s_net_pending[2].active == 0u),
|
||
"TC-NETPEND-017 cancel_all 只通知挂着的那两个(网络整体复位时用)");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_ESTABLISHED);
|
||
(void)net_pending_register(0, g_rbuf, 10, 500u, (TaskHandle_t)&g_caller_obj);
|
||
net_pending_clear(0);
|
||
CHECK((s_net_pending[0].active == 0u) && (g_notify_n == 0),
|
||
"TC-NETPEND-018 clear 只解除挂起、不发通知(不给调用方假答复)");
|
||
}
|
||
|
||
static void tc_settle_close(void)
|
||
{
|
||
int before;
|
||
|
||
printf("-- TC-NETPEND-019..021 settle_close --\n");
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_CLOSED);
|
||
before = g_chip.read_calls + g_chip.get_status_calls;
|
||
net_pending_settle_close(0, &g_socks[0]);
|
||
CHECK((g_chip.read_calls + g_chip.get_status_calls) == before,
|
||
"TC-NETPEND-019 没挂起时直接返回(不打扰芯片)");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_CLOSED);
|
||
(void)net_pending_register(0, g_rbuf, 20, 500u, (TaskHandle_t)&g_caller_obj);
|
||
ch395f_fake_push_recv_len(0, 7u);
|
||
net_pending_settle_close(0, &g_socks[0]);
|
||
CHECK((g_notify_value[0] == 7u) && (g_socks[0].recv_bytes == 7u) &&
|
||
(g_socks[0].readable == 0u),
|
||
"TC-NETPEND-020 收尾时把剩余字节读走并交还字节数(收口不丢数据)");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_CLOSED);
|
||
(void)net_pending_register(0, g_rbuf, 20, 500u, (TaskHandle_t)&g_caller_obj);
|
||
net_pending_settle_close(0, &g_socks[0]);
|
||
CHECK(g_notify_value[0] == (uint32_t)NET_ERR_CONNRESET,
|
||
"TC-NETPEND-021 无残留则报 CONNRESET(关闭收尾不该被当成读到 0 字节)");
|
||
}
|
||
|
||
static void tc_send_paths(void)
|
||
{
|
||
int rc;
|
||
|
||
printf("-- TC-NETPEND-030..040 send pending --\n");
|
||
reset_all();
|
||
rc = net_send_pending_register(-1, g_wbuf, 100, 0, (TaskHandle_t)&g_caller_obj);
|
||
CHECK((rc == -1) && (g_errno_last == NET_ERR_BADF), "TC-NETPEND-030 越界 fd 报 BADF");
|
||
rc = net_send_pending_register(0, NULL, 100, 0, (TaskHandle_t)&g_caller_obj);
|
||
CHECK(g_errno_last == NET_ERR_INVAL, "TC-NETPEND-030b buf 为空报 INVAL");
|
||
rc = net_send_pending_register(0, g_wbuf, 0, 0, (TaskHandle_t)&g_caller_obj);
|
||
CHECK(g_errno_last == NET_ERR_INVAL, "TC-NETPEND-030c len 非正报 INVAL");
|
||
rc = net_send_pending_register(0, g_wbuf, 100, -1, (TaskHandle_t)&g_caller_obj);
|
||
CHECK(g_errno_last == NET_ERR_INVAL, "TC-NETPEND-030d sent 为负报 INVAL");
|
||
rc = net_send_pending_register(0, g_wbuf, 100, 100, (TaskHandle_t)&g_caller_obj);
|
||
CHECK((g_errno_last == NET_ERR_INVAL) && (s_net_send[0].active == 0u),
|
||
"TC-NETPEND-030e sent>=len 报 INVAL(已发完的不必挂)");
|
||
rc = net_send_pending_register(0, g_wbuf, 100, 50, NULL);
|
||
CHECK(g_errno_last == NET_ERR_BUSY, "TC-NETPEND-031 caller 为空报 BUSY");
|
||
rc = net_send_pending_register(0, g_wbuf, 100, 50, (TaskHandle_t)&g_owner_obj);
|
||
CHECK(g_errno_last == NET_ERR_BUSY, "TC-NETPEND-031b 属主自己发报 BUSY");
|
||
rc = net_send_pending_register(0, g_wbuf, 100, 50, (TaskHandle_t)&g_caller_obj);
|
||
CHECK(rc == NET_PENDING_REGISTERED, "TC-NETPEND-032 挂起发送注册成功");
|
||
rc = net_send_pending_register(0, g_wbuf, 100, 50, (TaskHandle_t)&g_caller_obj);
|
||
CHECK(g_errno_last == NET_ERR_BUSY, "TC-NETPEND-031c 同 fd 只允许一条挂起发送");
|
||
|
||
reset_all();
|
||
(void)net_send_pending_register(0, g_wbuf, 3000, 700, (TaskHandle_t)&g_caller_obj);
|
||
g_socks[0].in_use = 0u;
|
||
net_send_pending_tick(g_socks);
|
||
CHECK((g_notify_n == 1) && (g_notify_value[0] == 700u),
|
||
"TC-NETPEND-033 槽位消失回**已发字节数**(不是错误码,调用方据此算进度)");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_CLOSED);
|
||
(void)net_send_pending_register(0, g_wbuf, 3000, 700, (TaskHandle_t)&g_caller_obj);
|
||
net_send_pending_tick(g_socks);
|
||
CHECK((g_npd_error_count == 1) && (g_errno_last == NET_ERR_CONNRESET) &&
|
||
(g_notify_value[0] == 700u),
|
||
"TC-NETPEND-034 会话塌了:留 ERROR + 报 CONNRESET,但仍把已发字节交回去");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_ESTABLISHED);
|
||
g_socks[0].send_ready = 1u;
|
||
(void)net_send_pending_register(0, g_wbuf, 3000, 0, (TaskHandle_t)&g_caller_obj);
|
||
net_send_pending_tick(g_socks);
|
||
CHECK((g_chip.write_calls == 1) &&
|
||
(g_chip.last_read_len == (uint16_t)NET_SEND_CHUNK_MAX) &&
|
||
(g_socks[0].send_bytes == (uint32_t)NET_SEND_CHUNK_MAX) &&
|
||
(g_socks[0].send_ready == 0u) && (s_net_send[0].active == 1u),
|
||
"TC-NETPEND-035 单次写入钳在 NET_SEND_CHUNK_MAX(芯片发送缓冲上界)");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_ESTABLISHED);
|
||
(void)net_send_pending_register(0, g_wbuf, 3000, 0, (TaskHandle_t)&g_caller_obj);
|
||
g_socks[0].send_ready = 1u;
|
||
net_send_pending_tick(g_socks);
|
||
g_socks[0].send_ready = 1u;
|
||
net_send_pending_tick(g_socks);
|
||
g_socks[0].send_ready = 1u;
|
||
net_send_pending_tick(g_socks);
|
||
CHECK((g_notify_value[0] == 3000u) && (s_net_send[0].active == 0u) &&
|
||
(g_chip.write_calls == 3),
|
||
"TC-NETPEND-036 分三块发完 3000 字节后交还总长度");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_ESTABLISHED);
|
||
(void)net_send_pending_register(0, g_wbuf, 3000, 0, (TaskHandle_t)&g_caller_obj);
|
||
g_now = 1900u;
|
||
g_socks[0].send_ready = 1u;
|
||
net_send_pending_tick(g_socks);
|
||
g_now = 2800u;
|
||
g_socks[0].send_ready = 1u;
|
||
net_send_pending_tick(g_socks);
|
||
CHECK((g_npd_error_count == 0) && (s_net_send[0].active == 1u) &&
|
||
(g_chip.write_calls == 2),
|
||
"TC-NETPEND-038 每写一轮续期 deadline:还在推进就不该被 1000ms 掐死");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_ESTABLISHED);
|
||
(void)net_send_pending_register(0, g_wbuf, 3000, 0, (TaskHandle_t)&g_caller_obj);
|
||
g_now = 2000u;
|
||
net_send_pending_tick(g_socks);
|
||
CHECK((g_npd_error_count == 1) && (g_errno_last == NET_ERR_TIMEDOUT) &&
|
||
(g_notify_value[0] == 0u),
|
||
"TC-NETPEND-037 停滞超时报 TIMEDOUT(对端不收时不能永久占着发送通道)");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_ESTABLISHED);
|
||
(void)net_send_pending_register(0, g_wbuf, 100, 40, (TaskHandle_t)&g_caller_obj);
|
||
net_send_pending_cancel(0, NET_ERR_CONNRESET);
|
||
CHECK((g_npd_error_count == 1) && (g_errno_last == NET_ERR_CONNRESET) &&
|
||
(g_notify_value[0] == 40u) && (s_net_send[0].active == 0u),
|
||
"TC-NETPEND-039 取消挂起发送:留痕 + 交还已发字节");
|
||
net_send_pending_cancel(0, NET_ERR_CONNRESET);
|
||
CHECK(g_notify_n == 1, "TC-NETPEND-039b 重复取消幂等");
|
||
|
||
reset_all();
|
||
mk_sock(0, NET_SOCK_STATE_ESTABLISHED);
|
||
(void)net_send_pending_register(0, g_wbuf, 100, 40, (TaskHandle_t)&g_caller_obj);
|
||
net_send_pending_clear(0);
|
||
CHECK((s_net_send[0].active == 0u) && (g_notify_n == 0),
|
||
"TC-NETPEND-040 clear 不发通知(与 recv 侧同一条纪律)");
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
printf("=== NET Pending Tests ===\n");
|
||
|
||
tc_recv_register();
|
||
tc_recv_paths();
|
||
tc_settle_close();
|
||
tc_send_paths();
|
||
|
||
printf("\n=== NET Pending Tests: %d/%d PASSED (failed=%d) ===\n",
|
||
s_passed, s_total, s_failed);
|
||
return (s_failed == 0) ? 0 : 1;
|
||
}
|