Files
STM32F4-Base/test/host/net_socket/ns_peers.c
T
wandering1 794906cdc8 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,台账回填留待主树合并后。
2026-10-24 17:54:02 +08:00

380 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称:NET 层兄弟模块桩(net_socket 主机侧单测专用)
* 模块功能:net_socket.c 依赖的三个兄弟模块(net_listener / net_pending / net_events)
* 在这里**按边界契约打桩**,让本套单测只测 net_socket.c 自己那部分决策。
*
* ⚠ 仅供 test/host/net_socket/ 使用,不参与 MCU 构建。
*
* 为什么打桩而不是 #include 真实源(而不是"越真越好"):
* 1. 那三个模块**已各有自己的主机单测**(net_listener 34 条、net_events 34 条、
* net_pending 43 条),本套再跑一遍不增加任何对 net_socket.c 的判别力,
* 只会把"谁的红"变成糊账;
* 2. 本套真正要钉的是**边界契约**——net_socket.c 在什么条件下把什么 reason 登记给
* 监听层、什么条件下把挂起请求登记给 pending 层、以及"登记了就不能再自己发回执"。
* 桩把这些变成可断言的记录,比让真实 tick 在背后默默跑更有判别力;
* 3. 原型一律取**真实头**(net_listener.h / net_pending.h / net_events.h),
* 兄弟模块改签名 → 本套编译期就红。
*
* 桩的取值纪律(不自己发明语义,全部按真实头的函数说明):
* - net_pending_register / net_send_pending_register 返回 NET_PENDING_REGISTERED,
* 与真实实现一致(登记成功=哨兵,供分派跳过即时通知);可注入 -1 测登记被拒;
* - net_pending_tick 支持"下一轮把脚本里的结果当作数据到达,通知登记时的 caller",
* 复刻真实 tick 的"数据到达即通知"契约;
* - 其余全为纯记录(次数 + 最近一次入参),供"不该登记/必须登记"的断言。
*
* 创建日期:2026-10-24
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "net_listener.h"
#include "net_pending.h"
#include "net_events.h"
#include "net_socket.h"
#include "ns_peers.h"
#include "ns_rtos.h"
ns_peers_obs_t g_peers;
/* 桩内部"完成/取消"要发通知:转给假内核,这样测试能断言通知次数与通知值 */
void ns_peers_note_notify(TaskHandle_t to, uint32_t v)
{
ns_rtos_post_notify(to, v);
}
/* 挂起登记簿(只记一条,够本套断言用) */
static int s_pend_active[NET_MAX_SOCKETS];
static void *s_pend_buf[NET_MAX_SOCKETS];
static int s_pend_len[NET_MAX_SOCKETS];
static TaskHandle_t s_pend_caller[NET_MAX_SOCKETS];
static int s_snd_active[NET_MAX_SOCKETS];
static TaskHandle_t s_snd_caller[NET_MAX_SOCKETS];
/* tick 完成脚本:fd -> 结果值(一次生效) */
static int s_pend_done_armed[NET_MAX_SOCKETS];
static int s_pend_done_val[NET_MAX_SOCKETS];
static int s_snd_done_armed[NET_MAX_SOCKETS];
static int s_snd_done_val[NET_MAX_SOCKETS];
void ns_peers_reset(void)
{
(void)memset(&g_peers, 0, sizeof(g_peers));
(void)memset(s_pend_active, 0, sizeof(s_pend_active));
(void)memset(s_pend_buf, 0, sizeof(s_pend_buf));
(void)memset(s_pend_len, 0, sizeof(s_pend_len));
(void)memset(s_pend_caller, 0, sizeof(s_pend_caller));
(void)memset(s_snd_active, 0, sizeof(s_snd_active));
(void)memset(s_snd_caller, 0, sizeof(s_snd_caller));
(void)memset(s_pend_done_armed, 0, sizeof(s_pend_done_armed));
(void)memset(s_pend_done_val, 0, sizeof(s_pend_done_val));
(void)memset(s_snd_done_armed, 0, sizeof(s_snd_done_armed));
(void)memset(s_snd_done_val, 0, sizeof(s_snd_done_val));
g_peers.pend_register_rc = NET_PENDING_REGISTERED;
g_peers.send_register_rc = NET_PENDING_REGISTERED;
}
void ns_peers_arm_pending_done(int sockfd, int result)
{
if ((sockfd >= 0) && (sockfd < NET_MAX_SOCKETS)) {
s_pend_done_armed[sockfd] = 1;
s_pend_done_val[sockfd] = result;
}
}
void ns_peers_arm_send_pending_done(int sockfd, int result)
{
if ((sockfd >= 0) && (sockfd < NET_MAX_SOCKETS)) {
s_snd_done_armed[sockfd] = 1;
s_snd_done_val[sockfd] = result;
}
}
/* ================= net_listener.h ================= */
void net_listener_request(int sockfd, net_listener_req_t reason)
{
g_peers.listener_req_calls++;
g_peers.last_req_fd = sockfd;
g_peers.last_req_reason = (int)reason;
if ((sockfd >= 0) && (sockfd < NET_MAX_SOCKETS)) {
g_peers.req_armed[sockfd] = 1;
g_peers.req_reason[sockfd] = (int)reason;
}
}
void net_listener_clear(int sockfd)
{
g_peers.listener_clear_calls++;
g_peers.last_clear_fd = sockfd;
if ((sockfd >= 0) && (sockfd < NET_MAX_SOCKETS)) {
g_peers.req_armed[sockfd] = 0;
}
}
void net_listener_tick(void)
{
g_peers.listener_tick_calls++;
}
void net_listener_settle(int sockfd, net_listener_settle_t level)
{
g_peers.listener_settle_calls++;
g_peers.last_settle_fd = sockfd;
g_peers.last_settle_level = (int)level;
}
/* ================= net_pending.h(recv 侧) ================= */
void net_pending_capture_owner(void)
{
g_peers.pending_capture_calls++;
}
int net_pending_register(int sockfd, void *buf, int len, uint32_t to_ms,
TaskHandle_t caller)
{
g_peers.pending_reg_calls++;
g_peers.last_pend_fd = sockfd;
g_peers.last_pend_to = to_ms;
g_peers.last_pend_caller = caller;
if (g_peers.pend_register_rc != NET_PENDING_REGISTERED) {
return g_peers.pend_register_rc; /* 测"登记被拒"分支 */
}
if ((sockfd >= 0) && (sockfd < NET_MAX_SOCKETS)) {
s_pend_active[sockfd] = 1;
s_pend_buf[sockfd] = buf;
s_pend_len[sockfd] = len;
s_pend_caller[sockfd] = caller;
g_peers.pend_active_now[sockfd] = 1;
}
return NET_PENDING_REGISTERED;
}
void net_pending_tick(net_sock_t *p_socks)
{
int i;
g_peers.pending_tick_calls++;
(void)p_socks;
/* 复刻真实 tick 的主用例:数据到达 → 按登记时的 caller 通知 → 清槽 */
for (i = 0; i < NET_MAX_SOCKETS; i++) {
if ((s_pend_active[i] != 0) && (s_pend_done_armed[i] != 0)) {
s_pend_done_armed[i] = 0;
s_pend_active[i] = 0;
g_peers.pend_active_now[i] = 0;
g_peers.pending_notified++;
ns_peers_note_notify(s_pend_caller[i], (uint32_t)s_pend_done_val[i]);
}
}
}
void net_pending_clear(int sockfd)
{
g_peers.pending_clear_calls++;
g_peers.last_pend_clear_fd = sockfd;
if ((sockfd >= 0) && (sockfd < NET_MAX_SOCKETS)) {
s_pend_active[sockfd] = 0;
g_peers.pend_active_now[sockfd] = 0;
}
}
void net_pending_cancel(int sockfd, int err)
{
g_peers.pending_cancel_calls++;
g_peers.last_pend_cancel_err = err;
if ((sockfd >= 0) && (sockfd < NET_MAX_SOCKETS)) {
s_pend_active[sockfd] = 0;
g_peers.pend_active_now[sockfd] = 0;
}
}
void net_pending_cancel_all(int err)
{
int i;
g_peers.pending_cancel_all_calls++;
g_peers.last_pend_cancel_err = err;
for (i = 0; i < NET_MAX_SOCKETS; i++) {
if (s_pend_active[i] != 0) {
g_peers.pending_notified++;
ns_peers_note_notify(s_pend_caller[i], (uint32_t)err);
}
s_pend_active[i] = 0;
g_peers.pend_active_now[i] = 0;
}
}
void net_pending_settle_close(int sockfd, net_sock_t *p_sock)
{
g_peers.pending_settle_calls++;
g_peers.last_pend_fd = sockfd;
(void)p_sock;
if ((sockfd >= 0) && (sockfd < NET_MAX_SOCKETS) && (s_pend_active[sockfd] != 0)) {
/* 真实实现:有数据先送达,无数据按 CONNRESET 取消——两者都**必通知** */
s_pend_active[sockfd] = 0;
g_peers.pend_active_now[sockfd] = 0;
g_peers.pending_notified++;
ns_peers_note_notify(s_pend_caller[sockfd], (uint32_t)NET_ERR_CONNRESET);
}
}
int net_pending_active(int sockfd)
{
if ((sockfd < 0) || (sockfd >= NET_MAX_SOCKETS)) {
return 0;
}
return s_pend_active[sockfd];
}
/* ================= net_pending.h(send 侧) ================= */
int net_send_pending_register(int sockfd, const void *buf, int len, int sent,
TaskHandle_t caller)
{
g_peers.send_reg_calls++;
g_peers.last_snd_fd = sockfd;
g_peers.last_snd_sent = sent;
g_peers.last_snd_len = len;
(void)buf;
if (g_peers.send_register_rc != NET_PENDING_REGISTERED) {
return g_peers.send_register_rc;
}
/* 真实实现的硬前置:sent >= len 视为参数非法拒收(末块边界) */
if (sent >= len) {
return -1;
}
if ((sockfd >= 0) && (sockfd < NET_MAX_SOCKETS)) {
s_snd_active[sockfd] = 1;
s_snd_caller[sockfd] = caller;
}
return NET_PENDING_REGISTERED;
}
void net_send_pending_tick(net_sock_t *p_socks)
{
int i;
g_peers.send_tick_calls++;
(void)p_socks;
for (i = 0; i < NET_MAX_SOCKETS; i++) {
if ((s_snd_active[i] != 0) && (s_snd_done_armed[i] != 0)) {
s_snd_done_armed[i] = 0;
s_snd_active[i] = 0;
g_peers.send_notified++;
ns_peers_note_notify(s_snd_caller[i], (uint32_t)s_snd_done_val[i]);
}
}
}
void net_send_pending_clear(int sockfd)
{
g_peers.send_clear_calls++;
if ((sockfd >= 0) && (sockfd < NET_MAX_SOCKETS)) {
s_snd_active[sockfd] = 0;
}
}
void net_send_pending_cancel(int sockfd, int err)
{
g_peers.send_cancel_calls++;
g_peers.last_snd_cancel_err = err;
if ((sockfd >= 0) && (sockfd < NET_MAX_SOCKETS) && (s_snd_active[sockfd] != 0)) {
s_snd_active[sockfd] = 0;
g_peers.send_notified++;
ns_peers_note_notify(s_snd_caller[sockfd], (uint32_t)err);
}
}
void net_send_pending_cancel_all(int err)
{
int i;
g_peers.send_cancel_all_calls++;
g_peers.last_snd_cancel_err = err;
for (i = 0; i < NET_MAX_SOCKETS; i++) {
if (s_snd_active[i] != 0) {
g_peers.send_notified++;
ns_peers_note_notify(s_snd_caller[i], (uint32_t)err);
}
s_snd_active[i] = 0;
}
}
int net_send_pending_active(int sockfd)
{
if ((sockfd < 0) || (sockfd >= NET_MAX_SOCKETS)) {
return 0;
}
return s_snd_active[sockfd];
}
/* ================= net_events.h(事件面与对账) ================= */
/* 事件处理桩:按真实头注释的效果改控制块(CONNECT→ESTABLISHED + 记对端等),
* 让 net_poll 的"中断 → 状态机"分派可断言;同时计数。 */
void handle_connect_event(net_sock_t *p_sock)
{
g_peers.evt_connect_calls++;
if (p_sock != NULL) {
p_sock->state = NET_SOCK_STATE_ESTABLISHED;
p_sock->readable = 0U;
p_sock->released_abnormal = 0U;
p_sock->session_dead = 0U;
}
}
void handle_disconnect_event(net_sock_t *p_sock)
{
g_peers.evt_disconnect_calls++;
if (p_sock != NULL) {
p_sock->last_peer_port = p_sock->remote_port;
p_sock->state = NET_SOCK_STATE_CLOSED;
p_sock->session_dead = 1U;
}
}
void handle_recv_event(net_sock_t *p_sock)
{
g_peers.evt_recv_calls++;
if (p_sock != NULL) {
p_sock->readable = 1U;
}
}
void handle_send_event(net_sock_t *p_sock)
{
g_peers.evt_send_calls++;
if (p_sock != NULL) {
p_sock->send_ready = 1U;
}
}
void handle_timeout_event(net_sock_t *p_sock)
{
g_peers.evt_timeout_calls++;
if (p_sock != NULL) {
p_sock->last_peer_port = p_sock->remote_port;
p_sock->state = NET_SOCK_STATE_CLOSED;
p_sock->released_abnormal = 1U;
p_sock->session_dead = 1U;
}
}
void net_reconcile_tick(void)
{
g_peers.reconcile_calls++;
}
void net_lost_connect_probe(net_sock_t *p_sock)
{
g_peers.lost_probe_calls++;
g_peers.last_probe_state = (p_sock != NULL) ? (int)p_sock->state : -1;
if ((p_sock != NULL) && (g_peers.lost_probe_hit != 0)) {
/* 脚本:探针命中 → 直接转 ESTABLISHED(复刻补 accept 的净效果) */
p_sock->state = NET_SOCK_STATE_ESTABLISHED;
p_sock->lost_conn_seen = 0U;
}
}