Files
STM32F4-Base/test/host/net_select/run_unit_test.py
T
wandering1 658bc40ccc test(NET): net_select 模块(21 条)与 NET 域首份 ch395f 桩,登记两处死路径
P1 的 NET 四块开了第一块:新增 test/host/net_select/,21 条断言全部 MSVC 编译
通过并 PASS。net_select.c 一个芯片函数都不调,所以它是四块里唯一不需要假芯片的,
先把它拿下,顺带把 NET 域的桩骨架立起来。

钉住的契约:
- 就绪位回写:入参集合里未就绪的 fd 必须被清掉,只留就绪的(POSIX 语义);未请求
  的集合不被写回、可以传 NULL。
- 状态准入:LISTENING 即使 readable 也不算可读(accept 语义不在这层);ESTABLISHED
  + send_ready 才可写;CONNECTING 时 send_ready 不作数。
- 边界:nfds 为 0 / 负 / 超过 NET_MAX_SOCKETS 都返回 -1(不许扫出 socket 表外)。
- 超时算术:tv_usec 是**整除截断**(1999us 当 1ms 而不是 2ms);timeout=NULL 时
  无限等并由对端事件唤醒;poll 的 timeout=0 首轮即退、每轮先清 revents。
用可控 tick(每问一次 +1)+ 可控 osDelay(计数、可指定轮数后造一个就绪 socket)
把这些变成确定性轮数,台架上根本构造不出"没人来连时该怎么退"。

照出两处**死路径**(只登记,未改语义;全库唯一调用点 test/board/net_test_task.c
传的是 NULL 与非零 tv,今天没有受害者):
1. net_select 的 timeout = {0,0} 换算出 timeout_ms = 0,而退出条件写的是
   if ((timeout_ms > 0) && (已用时 >= timeout_ms)) → **超时 0 永不触发退出**,退化成
   无限等;POSIX 在这里必须立刻返回 0。同文件 net_poll_events 写的 timeout >= 0
   是对的,两条并排放着正好是对照。风险面:谁拿它当"非阻塞查一遍",netTask 就
   回不到主循环,而 netTask 在 IWDG required_mask 里。
2. exceptfds 是恒为空的死参数:循环体开头 p_sock == NULL → continue 先跳过,
   check_socket_error 的 NULL 分支从 select 里到不了。按 POSIX 直觉用它收异常的人
   会永远什么都收不到。
两处都用"现状桩"钉成改了会响(用例名里写明不是期望行为)。另修掉我自己写错的一条
用例:给 timeout=NULL 又没有就绪 fd,直接死循环——已在假 osDelay 里加了 500 轮
保险(跑飞就 exit(2)),因为挂死的测试会把 exe 文件名占住、让后续构建报 LNK1104。

桩的边界写死在文件头:net_select.h / net_types.h / net_config.h 用真实头(net_sock_t
字段名与 NET_SOCK_STATE_* 一改即编译期报错);main.h / cmsis_os.h / FreeRTOS.h /
task.h 是本目录桩;新建的 ch395f.h 是**空壳**,只服务不调芯片函数的文件——
net_pending / net_events / net_listener 直接调十余处 ch395f_*,要的是可脚本化返回的
假芯片,不要把这个空壳当成"NET 域桩已就绪"。

覆盖率:套件 21 → 22 全绿;files_tested 25 → 26;未触及 .c 41 → 40;分母 3,332 →
3,446 行;net_select.c 97/114(85.1%)。恒等式更新为 40 = 6(P1 剩) + 5(P2) + 29(P3)。
编号按 B 层口径补建 TC-HOST-NETSEL(游标 019),TC-HOST 族现共 16 族;注册表仍只
做增量插入。

自查:test-cppcheck Passed;check_ids --strict 通过;doc-links = 35 处(= 存量);
BOM/CR/U+FFFD/行尾逐项净。本增量零生产代码改动,未跑 @build。--all-files 仍只有
mem-numbers / mem-budget(.map 台账漂移,等 Lead 合并后统一回填)与 ui-golden
(ref/*.bin 不入库,新 worktree 天生没有黄金参考)三条失败,与本分支无关。
2026-10-23 23:44:48 +08:00

56 lines
2.5 KiB
Python
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.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""NET 层 select/poll(net_select)的主机侧单元测试运行器(L0.5,纯标准库)。
做什么:
用本机 MSVC 编译 test/host/net_select/net_select_test.c(以 #include 纳入生产源
Drivers/BSP/NET/net_select.c),在 PC 上离线验证就绪位回写、socket 状态对可读/
可写的准入、非法 fd 处理、tv_sec/tv_usec 超时换算、poll 的 revents 语义。
工具链与判定委托 test/host/common/unit_test_harness.py。
为什么要主机侧单测(而不是只用台架):
netTask 是串行 socket 栈的唯一执行者,且在 IWDG required_mask 里——这里的等待
逻辑算错,表现是"网络整体停摆 + 喂不上狗复位",而且日志里什么都没有。台架无法
构造"没人来连时该怎么退"(对端不可控);把 HAL_GetTick 与 osDelay 做成可控量后,
"第几轮该退出"能精确复现,包括 1999us 该按 1ms 还是 2ms 这种算术。
桩的边界:net_select.h / net_types.h / net_config.h 用真实头(net_sock_t 字段名与
NET_SOCK_STATE_* 一改就编译期报错);main.h / cmsis_os.h / FreeRTOS.h / task.h /
ch395f.h 是本目录桩——net_select.c 一个芯片函数都不调,所以 ch395f.h 是空壳。
⚠ 那个空壳不代表 NET 域桩已就绪:net_listener / net_pending / net_events 直接调
十多个 ch395f_* 且语义在 socket 状态机里,需要的是能脚本化返回的假芯片。
用法(仓库根目录):
python test/host/net_select/run_unit_test.py
退出码:0=PASS / 1=测试失败 / 2=环境错误。
设计依据:docs/网络/Net层阻塞模型架构设计.md、docs/测试说明/主机单测覆盖率口径.md §9 P1
创建日期:2026-10-23
"""
import os
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(HERE)))
sys.path.insert(0, os.path.join(ROOT, "test", "host", "common"))
from unit_test_harness import TestBuild, run_host_unit_test # noqa: E402
# 顺序敏感:stub 必须在 Drivers/BSP/NET 之前,才能用桩覆盖 net_socket.h 与 ch395f.h;
# net_select.h / net_types.h / net_config.h 仍从真实目录取。
BUILD = TestBuild(
name="NET Select Tests",
src=os.path.join(HERE, "net_select_test.c"),
include_dirs=[
os.path.join(HERE, "stub"),
os.path.join(ROOT, "Drivers", "BSP", "NET"),
HERE,
],
summary_marker="=== NET Select Tests:",
)
if __name__ == "__main__":
sys.exit(run_host_unit_test(BUILD))