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:
@@ -9,8 +9,13 @@
|
||||
|
||||
#include "ch395f_fake.h"
|
||||
|
||||
/* 时基:假复位机的最短沉降窗要用(与真实 main.h 同名同签名,各测试 TU 自带实现) */
|
||||
uint32_t HAL_GetTick(void);
|
||||
|
||||
ch395f_fake_world_t g_fake;
|
||||
ch395f_fake_obs_t g_chip;
|
||||
ch395f_fake_ext_t g_fx;
|
||||
ch395f_fake_ext_obs_t g_fxchip;
|
||||
|
||||
static void fake_log(char c)
|
||||
{
|
||||
@@ -35,6 +40,33 @@ void ch395f_fake_reset(void)
|
||||
g_fake.open_rc[i] = CH395F_ERR_SUCCESS;
|
||||
g_fake.listen_rc[i] = CH395F_ERR_SUCCESS;
|
||||
}
|
||||
/* 扩展区(net_socket 批次新增):同一次复位里一并清零,避免场景间残留 */
|
||||
ch395f_fake_ext_reset();
|
||||
}
|
||||
|
||||
/* 扩展面的复位:默认"芯片在位、复位/协议栈初始化成功、版本够新、复位机全 IDLE" */
|
||||
void ch395f_fake_ext_reset(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
(void)memset(&g_fx, 0, sizeof(g_fx));
|
||||
(void)memset(&g_fxchip, 0, sizeof(g_fxchip));
|
||||
g_fx.exist_rc = (int)CH395F_STATUS_OK;
|
||||
g_fx.reset_rc = (int)CH395F_STATUS_OK;
|
||||
g_fx.init_rc = (int)CH395F_STATUS_OK;
|
||||
g_fx.version = 0x46U; /* 台架实测芯片版本(版本门控的默认通过值)*/
|
||||
g_fx.phy_status = 0x08U; /* PHY 100M_FULL(非 DISCONN,glob_event_feed 判 link up)*/
|
||||
for (i = 0; i < CH395F_FAKE_SOCK_MAX; i++) {
|
||||
g_fx.connect_rc[i] = CH395F_ERR_SUCCESS;
|
||||
g_fx.rst_state[i] = (uint8_t)CH395F_FAKE_RST_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void ch395f_fake_push_glob(uint16_t v)
|
||||
{
|
||||
if (g_fx.glob_n < CH395F_FAKE_GLOB_QUEUE) {
|
||||
g_fx.glob_q[g_fx.glob_n++] = v;
|
||||
}
|
||||
}
|
||||
|
||||
void ch395f_fake_push_recv_len(uint8_t sock, uint16_t v)
|
||||
@@ -94,7 +126,9 @@ void ch395f_get_socket_status(uint8_t sock, uint8_t *status)
|
||||
}
|
||||
}
|
||||
|
||||
/* 整体读即清:返回后该 socket 的挂起位图清零(真实芯片没有按位清除手段) */
|
||||
/* 整体读即清:返回后该 socket 的挂起位图清零(真实芯片没有按位清除手段)。
|
||||
* 唯一新增:清完立刻回填**电平型**掩码 sint_sticky(默认全 0 → 与既有行为逐字相同,
|
||||
* net_listener / net_events / net_pending 三套不受影响)。 */
|
||||
uint8_t ch395f_get_sock_int_status(uint8_t sock)
|
||||
{
|
||||
uint8_t v = 0U;
|
||||
@@ -104,11 +138,37 @@ uint8_t ch395f_get_sock_int_status(uint8_t sock)
|
||||
fake_log('i');
|
||||
if (sock < CH395F_FAKE_SOCK_MAX) {
|
||||
v = g_fake.int_status[sock];
|
||||
g_fake.int_status[sock] = 0U;
|
||||
g_fake.int_status[sock] = g_fx.sint_sticky[sock];
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
void ch395f_fake_clear_log(void)
|
||||
{
|
||||
(void)memset(g_chip.log, 0, sizeof(g_chip.log));
|
||||
g_chip.log_n = 0;
|
||||
}
|
||||
|
||||
int ch395f_fake_log_find(char c1)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < g_chip.log_n; i++) {
|
||||
if (g_chip.log[i] == c1) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ch395f_fake_set_sint_sticky(uint8_t sock, uint8_t mask)
|
||||
{
|
||||
if (sock < CH395F_FAKE_SOCK_MAX) {
|
||||
g_fx.sint_sticky[sock] = mask;
|
||||
g_fake.int_status[sock] |= mask;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t ch395f_get_recv_len(uint8_t sock)
|
||||
{
|
||||
uint16_t v;
|
||||
@@ -215,3 +275,344 @@ uint16_t ch395f_link_change_count(void)
|
||||
{
|
||||
return g_fake.link_change;
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* 扩展区实现(net_socket 主机单测批次;纯新增,上面既有实现一字未动)
|
||||
* 签名逐字抄自 Drivers/BSP/CH395F/ch395f.h。
|
||||
* ========================================================================= */
|
||||
|
||||
ch395f_status_t ch395f_check_exist(void)
|
||||
{
|
||||
g_fxchip.exist_calls++;
|
||||
fake_log('E');
|
||||
return (ch395f_status_t)g_fx.exist_rc;
|
||||
}
|
||||
|
||||
ch395f_status_t ch395f_reset(void)
|
||||
{
|
||||
g_fxchip.reset_calls++;
|
||||
fake_log('Z');
|
||||
return (ch395f_status_t)g_fx.reset_rc;
|
||||
}
|
||||
|
||||
ch395f_status_t ch395f_init(void)
|
||||
{
|
||||
g_fxchip.init_calls++;
|
||||
fake_log('n');
|
||||
return (ch395f_status_t)g_fx.init_rc;
|
||||
}
|
||||
|
||||
uint8_t ch395f_get_version(void)
|
||||
{
|
||||
return g_fx.version;
|
||||
}
|
||||
|
||||
void ch395f_set_fun_para(uint32_t para)
|
||||
{
|
||||
g_fxchip.set_fun_para_calls++;
|
||||
g_fx.fun_para = para;
|
||||
fake_log('F');
|
||||
}
|
||||
|
||||
void ch395f_set_tcp_mss(uint16_t mss)
|
||||
{
|
||||
g_fxchip.set_mss_calls++;
|
||||
g_fx.mss = mss;
|
||||
fake_log('Q');
|
||||
}
|
||||
|
||||
void ch395f_set_retrans_count(uint8_t cnt)
|
||||
{
|
||||
g_fxchip.set_retrans_calls++;
|
||||
g_fx.retrans_cnt = cnt;
|
||||
}
|
||||
|
||||
void ch395f_set_retrans_period(uint16_t period)
|
||||
{
|
||||
g_fx.retrans_period = period;
|
||||
}
|
||||
|
||||
void ch395f_set_keepalive_idle(uint32_t time)
|
||||
{
|
||||
g_fxchip.set_ka_calls++;
|
||||
g_fx.ka_idle = time;
|
||||
}
|
||||
|
||||
void ch395f_set_keepalive_intvl(uint32_t time)
|
||||
{
|
||||
g_fx.ka_intvl = time;
|
||||
}
|
||||
|
||||
void ch395f_set_keepalive_cnt(uint8_t cnt)
|
||||
{
|
||||
g_fx.ka_cnt = cnt;
|
||||
}
|
||||
|
||||
void ch395f_set_dhcp(uint8_t enable)
|
||||
{
|
||||
g_fxchip.set_dhcp_calls++;
|
||||
g_fx.dhcp_enable = enable;
|
||||
fake_log('D');
|
||||
}
|
||||
|
||||
void ch395f_ping_enable(uint8_t enable)
|
||||
{
|
||||
g_fxchip.ping_calls++;
|
||||
g_fx.ping_enable = enable;
|
||||
}
|
||||
|
||||
void ch395f_set_ip_addr(uint8_t *p_ip)
|
||||
{
|
||||
g_fxchip.set_ip_calls++;
|
||||
fake_log('I');
|
||||
if (p_ip != NULL) {
|
||||
(void)memcpy(g_fx.ip, p_ip, 4U);
|
||||
}
|
||||
}
|
||||
|
||||
void ch395f_set_gwip_addr(uint8_t *p_gwip)
|
||||
{
|
||||
g_fxchip.set_gw_calls++;
|
||||
fake_log('N');
|
||||
if (p_gwip != NULL) {
|
||||
(void)memcpy(g_fx.gw, p_gwip, 4U);
|
||||
}
|
||||
}
|
||||
|
||||
void ch395f_set_mask_addr(uint8_t *p_mask)
|
||||
{
|
||||
g_fxchip.set_mask_calls++;
|
||||
fake_log('K');
|
||||
if (p_mask != NULL) {
|
||||
(void)memcpy(g_fx.mask, p_mask, 4U);
|
||||
}
|
||||
}
|
||||
|
||||
void ch395f_set_mac_addr(uint8_t *p_mac)
|
||||
{
|
||||
g_fxchip.set_mac_calls++;
|
||||
fake_log('M');
|
||||
if (p_mac != NULL) {
|
||||
(void)memcpy(g_fx.mac, p_mac, 6U);
|
||||
}
|
||||
}
|
||||
|
||||
void ch395f_set_send_buf(uint8_t sock, uint8_t start_block, uint8_t block_count)
|
||||
{
|
||||
g_fxchip.set_send_buf_calls++;
|
||||
fake_log('b');
|
||||
if (sock < CH395F_FAKE_SOCK_MAX) {
|
||||
g_fx.send_blk_start[sock] = start_block;
|
||||
g_fx.send_blk_cnt[sock] = block_count;
|
||||
}
|
||||
}
|
||||
|
||||
void ch395f_set_recv_buf(uint8_t sock, uint8_t start_block, uint8_t block_count)
|
||||
{
|
||||
g_fxchip.set_recv_buf_calls++;
|
||||
fake_log('u');
|
||||
if (sock < CH395F_FAKE_SOCK_MAX) {
|
||||
g_fx.recv_blk_start[sock] = start_block;
|
||||
g_fx.recv_blk_cnt[sock] = block_count;
|
||||
}
|
||||
}
|
||||
|
||||
void ch395f_set_des_ip(uint8_t sock, uint8_t *p_ip)
|
||||
{
|
||||
g_fxchip.set_des_ip_calls++;
|
||||
fake_log('e');
|
||||
if ((sock < CH395F_FAKE_SOCK_MAX) && (p_ip != NULL)) {
|
||||
(void)memcpy(g_fx.des_ip[sock], p_ip, 4U);
|
||||
}
|
||||
}
|
||||
|
||||
void ch395f_set_des_port(uint8_t sock, uint16_t port)
|
||||
{
|
||||
g_fxchip.set_des_port_calls++;
|
||||
fake_log('q');
|
||||
if (sock < CH395F_FAKE_SOCK_MAX) {
|
||||
g_fx.des_port[sock] = port;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ch395f_tcp_connect(uint8_t sock)
|
||||
{
|
||||
g_fxchip.connect_calls++;
|
||||
g_chip.last_sock = sock;
|
||||
fake_log('T');
|
||||
return (sock < CH395F_FAKE_SOCK_MAX) ? g_fx.connect_rc[sock] : 0x01U;
|
||||
}
|
||||
|
||||
uint8_t ch395f_get_phy_status(void)
|
||||
{
|
||||
fake_log('g');
|
||||
return g_fx.phy_status;
|
||||
}
|
||||
|
||||
/* 全局中断状态(0x19,2 字节):**整体读即清**,且含全部 Socket 位——
|
||||
* 假件按调用序号返回预置值(耗尽后回落最后一个),并单独计数(B3 唯一读者=net_poll) */
|
||||
uint16_t ch395f_get_glob_int_status_all(void)
|
||||
{
|
||||
uint16_t v = 0U;
|
||||
|
||||
g_fxchip.glob_calls++;
|
||||
fake_log('G');
|
||||
if (g_fx.glob_n == 0) {
|
||||
v = 0U;
|
||||
} else if (g_fx.glob_i < g_fx.glob_n) {
|
||||
v = g_fx.glob_q[g_fx.glob_i++];
|
||||
} else {
|
||||
v = g_fx.glob_q[g_fx.glob_n - 1];
|
||||
}
|
||||
g_fxchip.glob_bits = (int)v;
|
||||
return v;
|
||||
}
|
||||
|
||||
/* 与真实驱动同判据:只有 PHY_CHANGE 位才回读 PHY;链路电平变了才计一次变更 */
|
||||
void ch395f_glob_event_feed(uint16_t glob_bits)
|
||||
{
|
||||
uint8_t up;
|
||||
|
||||
if ((glob_bits & CH395F_GINT_STAT_PHY_CHANGE) == 0U) {
|
||||
return;
|
||||
}
|
||||
up = (ch395f_get_phy_status() != CH395F_PHY_DISCONN) ? 1U : 0U;
|
||||
if (up != g_fake.link_up) {
|
||||
g_fake.link_up = up;
|
||||
g_fake.link_change++;
|
||||
}
|
||||
}
|
||||
|
||||
/* 只读本 Socket 的 SINT(读即清),永不碰全局寄存器——与真实护栏原语同语义 */
|
||||
uint8_t ch395f_sock_events(uint8_t sn)
|
||||
{
|
||||
g_fxchip.sock_events_calls++;
|
||||
return ch395f_get_sock_int_status(sn);
|
||||
}
|
||||
|
||||
/* —— sock_reset 复位机(契约等价:四步 + 最短沉降窗 + 二段 CLOSE 补发)——
|
||||
* 与驱动一致的**可断言点**:begin 重复启动必须被拒;GRACEFUL 满 100ms、
|
||||
* QUARANTINE 满 400ms 才继续;quiescent 要连续 2 轮无事件;byte0 回 00H 才算完成;
|
||||
* 芯片 byte0=05H 且 TCP=00H 时补发第二段 CLOSE。
|
||||
* 不复制驱动的超时数值细节(那是驱动自己的单测面),本假件不自造失败:
|
||||
* 想测超时由测试把 cached_open 一直留成 OPEN,由上层 net_sock_reset_sync 的 600ms 收口。 */
|
||||
int ch395f_sock_reset_begin(uint8_t sn, ch395f_reset_mode_t mode)
|
||||
{
|
||||
g_fxchip.rst_begin_calls++;
|
||||
fake_log('X');
|
||||
if (sn >= CH395F_FAKE_SOCK_MAX) {
|
||||
return -1;
|
||||
}
|
||||
if (g_fx.rst_state[sn] != (uint8_t)CH395F_FAKE_RST_IDLE) {
|
||||
g_fxchip.rst_begin_reject++;
|
||||
return -1;
|
||||
}
|
||||
g_fx.rst_state[sn] = (uint8_t)CH395F_FAKE_RST_DRAINED;
|
||||
g_fx.rst_mode[sn] = (mode == CH395F_RESET_QUARANTINE) ? 1U : 0U;
|
||||
g_fx.rst_t0[sn] = HAL_GetTick();
|
||||
g_fx.rst_quiet[sn] = 0U;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ch395f_sock_reset_poll(uint8_t sn)
|
||||
{
|
||||
uint32_t now;
|
||||
uint32_t el;
|
||||
uint32_t need;
|
||||
|
||||
g_fxchip.rst_poll_calls++;
|
||||
fake_log('x');
|
||||
if (sn >= CH395F_FAKE_SOCK_MAX) {
|
||||
return -1;
|
||||
}
|
||||
if (g_fx.rst_state[sn] == (uint8_t)CH395F_FAKE_RST_IDLE) {
|
||||
return 0; /* 未启动/已完成:幂等 */
|
||||
}
|
||||
now = HAL_GetTick();
|
||||
el = now - g_fx.rst_t0[sn];
|
||||
|
||||
switch (g_fx.rst_state[sn]) {
|
||||
case CH395F_FAKE_RST_DRAINED:
|
||||
/* 第 1 步:读空 RX;第 2 步:CLOSE */
|
||||
if (ch395f_get_recv_len(sn) != 0U) {
|
||||
uint8_t scratch[8];
|
||||
g_fxchip.rst_rx_discard_calls++;
|
||||
ch395f_read_recv_buf(sn, scratch, 8U);
|
||||
}
|
||||
g_fxchip.rst_close_calls++;
|
||||
(void)ch395f_close_socket(sn);
|
||||
if (g_fx.rst_keep_open[sn] == 0U) {
|
||||
g_fake.cached_open[sn] = CH395F_SOCKET_CLOSED;
|
||||
g_fake.cached_tcp[sn] = CH395F_TCP_CLOSED;
|
||||
}
|
||||
g_fx.rst_state[sn] = (uint8_t)CH395F_FAKE_RST_MINWAIT;
|
||||
g_fx.rst_t0[sn] = now;
|
||||
break;
|
||||
case CH395F_FAKE_RST_MINWAIT:
|
||||
need = (g_fx.rst_mode[sn] != 0U) ? 400U : 100U;
|
||||
if (el >= need) {
|
||||
g_fx.rst_state[sn] = (uint8_t)CH395F_FAKE_RST_QUIET;
|
||||
g_fx.rst_t0[sn] = now;
|
||||
g_fx.rst_quiet[sn] = 0U;
|
||||
}
|
||||
break;
|
||||
case CH395F_FAKE_RST_QUIET:
|
||||
if (ch395f_sock_events(sn) == 0U) {
|
||||
g_fx.rst_quiet[sn]++;
|
||||
if (g_fx.rst_quiet[sn] >= CH395F_FAKE_RST_QUIESCENT) {
|
||||
g_fx.rst_state[sn] = (uint8_t)CH395F_FAKE_RST_WAITCLS;
|
||||
g_fx.rst_t0[sn] = now;
|
||||
}
|
||||
} else {
|
||||
g_fx.rst_quiet[sn] = 0U;
|
||||
}
|
||||
break;
|
||||
case CH395F_FAKE_RST_WAITCLS:
|
||||
if (ch395f_cmd_done(sn, CH395F_DONE_CLOSE) == 1) {
|
||||
g_fx.rst_state[sn] = (uint8_t)CH395F_FAKE_RST_IDLE;
|
||||
g_fxchip.rst_done[sn]++;
|
||||
return 0;
|
||||
}
|
||||
/* 二段 CLOSE 补发(LISTEN 态一次 CLOSE 只拆监听,byte0 恒 05H) */
|
||||
if ((g_fake.cached_open[sn] == CH395F_SOCKET_OPEN) &&
|
||||
(g_fake.cached_tcp[sn] == CH395F_TCP_CLOSED) &&
|
||||
(g_fx.rst_quiet[sn] != 0xFFU)) {
|
||||
g_fx.rst_quiet[sn] = 0xFFU;
|
||||
g_fxchip.rst_close_calls++;
|
||||
(void)ch395f_close_socket(sn);
|
||||
if (g_fx.rst_keep_open[sn] == 0U) {
|
||||
g_fake.cached_open[sn] = CH395F_SOCKET_CLOSED;
|
||||
g_fake.cached_tcp[sn] = CH395F_TCP_CLOSED;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_fx.rst_state[sn] = (uint8_t)CH395F_FAKE_RST_IDLE;
|
||||
g_fxchip.rst_fail[sn]++;
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ch395f_cmd_done(uint8_t sn, ch395f_done_what_t what)
|
||||
{
|
||||
ch395f_sock_state_t st;
|
||||
|
||||
g_fxchip.cmd_done_calls++;
|
||||
g_fxchip.last_cmd_done_what = (uint8_t)what;
|
||||
fake_log('z');
|
||||
if (sn >= CH395F_FAKE_SOCK_MAX) {
|
||||
return -1;
|
||||
}
|
||||
st = ch395f_get_sock_state(sn);
|
||||
switch (what) {
|
||||
case CH395F_DONE_OPEN:
|
||||
return (st.open == CH395F_SOCKET_OPEN) ? 1 : 0;
|
||||
case CH395F_DONE_LISTEN:
|
||||
return (st.open == CH395F_SOCKET_OPEN && st.tcp_state == CH395F_TCP_LISTEN) ? 1 : 0;
|
||||
case CH395F_DONE_CLOSE:
|
||||
return (st.open == CH395F_SOCKET_CLOSED) ? 1 : 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,10 +44,10 @@
|
||||
#define CH395F_ERR_SUCCESS 0x00U
|
||||
#define CH395F_PROTO_TYPE_TCP 0x03U
|
||||
#define CH395F_SOCKET_CLOSED 0x00U
|
||||
#define CH395F_SOCKET_OPEN 0x01U
|
||||
#define CH395F_SOCKET_OPEN 0x05U /* 真头 ch395f.h:220 = 0x05U(05H SOCKET_OPEN,docs/芯片手册/CH395F.md:527)*/
|
||||
#define CH395F_TCP_CLOSED 0x00U
|
||||
#define CH395F_TCP_LISTEN 0x01U
|
||||
#define CH395F_TCP_ESTABLISHED 0x05U
|
||||
#define CH395F_TCP_ESTABLISHED 0x04U /* 真头 ch395f.h:229 = 0x04U(04H TCP_ESTABLISHED,docs/芯片手册/CH395F.md:539)*/
|
||||
|
||||
/* 发送/接收缓冲尺寸宏链:**与真实 ch395f.h 同构转抄**(2026-10-23 逐条核对)。
|
||||
* net_pending.h 里的 NET_SEND_CHUNK_MAX 由这两个宏推导,是"一次最多往芯片写多少"
|
||||
@@ -66,6 +66,68 @@
|
||||
#define CH395F_FAKE_SOCK_MAX 8 /* 与 NET_MAX_SOCKETS 同量级,测试自用 */
|
||||
#define CH395F_FAKE_QUEUE_LEN 24
|
||||
#define CH395F_FAKE_LOG_LEN 96
|
||||
#define CH395F_FAKE_GLOB_QUEUE 24
|
||||
|
||||
/* ===== 扩展区(2026-10-24,net_socket 主机单测批次;**纯新增**,上面既有宏/字段/函数
|
||||
* 一字未动,net_listener / net_events / net_pending 三套的语义不受影响)=====
|
||||
*
|
||||
* 追加的是 net_socket.c 这一层会调、而前三个模块不调的芯片原语(初始化下发类 +
|
||||
* 全局中断 + sock_reset 复位机 + cmd_done 判据)。签名/常量同样**逐字抄自真实头**
|
||||
* Drivers/BSP/CH395F/ch395f.h,改真头即编译期/断言期暴露漂移。
|
||||
*
|
||||
* 必须复刻的第三条芯片怪癖(前两套测不到、net_socket.c 却是主路):
|
||||
* 3. GET_GLOB_INT_STATUS_ALL(0x19,2 字节)**整体读即清**,且它是全局寄存器
|
||||
* (含全部 Socket 事件位)。AGENTS 模块约定 B3:唯一读者只能是 net_poll。
|
||||
* 假件按调用序号返回预置值,并单独计数,让"别处偷偷读全局"成为可断言的事。
|
||||
* 4. sock_reset 复位机(E4 定案四步:读空 RX → CLOSE → 排空事件(quiescent) →
|
||||
* 等 byte0=00H,含 LISTEN 态二段 CLOSE 补发)。这里做**契约等价**实现:
|
||||
* 阶段推进与最短等待(GRACEFUL 100ms / QUARANTINE 400ms)照抄驱动,
|
||||
* 但不复制驱动的超时数值细节(那属于驱动自己的单测面)。
|
||||
*/
|
||||
#define CH395F_ERR_BUSY 0x10U /* 真头 ch395f.h:170 */
|
||||
#define CH395F_ERR_MEM 0x11U /* 真头 ch395f.h:171 */
|
||||
#define CH395F_ERR_TIMEOUT 0x13U /* 真头 ch395f.h:173 */
|
||||
#define CH395F_ERR_VAL 0x19U /* 真头 ch395f.h:179 */
|
||||
#define CH395F_ERR_USE 0x1BU /* 真头 ch395f.h:181(open 已被占用)*/
|
||||
#define CH395F_PROTO_TYPE_UDP 0x02U /* 真头 ch395f.h:213 */
|
||||
#define CH395F_FUN_PARA_SOCKET_CLOSE (1UL << 3) /* 真头 ch395f.h:205 */
|
||||
#define CH395F_TCP_MSS 1024U /* 真头 ch395f.h:124 */
|
||||
#define CH395F_PHY_DISCONN 0x01U /* 真头 ch395f.h:191 */
|
||||
#define CH395F_GINT_STAT_PHY_CHANGE 0x04U /* 真头 ch395f.h:245 */
|
||||
#define CH395F_SOCK_STATE_NONE 0xFFU /* 真头 ch395f.h:762 */
|
||||
|
||||
#define CH395F_SINT_STAT_SENBUF_FREE 0x01U /* 真头 ch395f.h:263 */
|
||||
#define CH395F_SINT_STAT_SEND_OK 0x02U /* 真头 ch395f.h:264 */
|
||||
#define CH395F_SINT_STAT_RECV_OK 0x04U /* 真头 ch395f.h:265 */
|
||||
#define CH395F_SINT_STAT_CONNECT 0x08U /* 真头 ch395f.h:266 */
|
||||
#define CH395F_SINT_STAT_DISCONNECT 0x10U /* 真头 ch395f.h:267 */
|
||||
#define CH395F_SINT_STAT_SOCK_TIMEOUT 0x40U /* 真头 ch395f.h:269 */
|
||||
|
||||
typedef enum {
|
||||
CH395F_STATUS_OK = 0, /* 真头 ch395f.h:275 */
|
||||
CH395F_STATUS_ERROR = -1,
|
||||
CH395F_STATUS_TIMEOUT = -2,
|
||||
CH395F_STATUS_NOT_DETECTED = -3
|
||||
} ch395f_status_t;
|
||||
|
||||
typedef enum {
|
||||
CH395F_RESET_GRACEFUL = 0, /* 真头 :803:最短等待 100ms */
|
||||
CH395F_RESET_QUARANTINE /* 真头 :804:最短等待 400ms */
|
||||
} ch395f_reset_mode_t;
|
||||
|
||||
typedef enum {
|
||||
CH395F_DONE_OPEN = 0, /* 真头 :826:byte0 变 05H */
|
||||
CH395F_DONE_LISTEN, /* 真头 :827:byte0=05H 且 TCP=01H */
|
||||
CH395F_DONE_CLOSE /* 真头 :828:byte0=00H */
|
||||
} ch395f_done_what_t;
|
||||
|
||||
/* 复位机阶段(与驱动同名状态的**契约等价**简化,不代表驱动内部实现) */
|
||||
#define CH395F_FAKE_RST_IDLE 0
|
||||
#define CH395F_FAKE_RST_DRAINED 1 /* 已读空 RX 并发过 CLOSE */
|
||||
#define CH395F_FAKE_RST_MINWAIT 2 /* 等最短沉降窗(100/400ms) */
|
||||
#define CH395F_FAKE_RST_QUIET 3 /* 排空本 Socket 事件(连续 2 轮无位) */
|
||||
#define CH395F_FAKE_RST_WAITCLS 4 /* 等 byte0=00H(含二段 CLOSE 补发) */
|
||||
#define CH395F_FAKE_RST_QUIESCENT 2U
|
||||
|
||||
typedef struct {
|
||||
uint8_t open;
|
||||
@@ -111,7 +173,13 @@ typedef struct {
|
||||
/* 调用顺序日志(短名首字母):
|
||||
* 'c'=close 'i'=int_status(读即清) 's'=get_socket_status 'r'=read_recv_buf
|
||||
* 'R'=get_recv_len 'o'=open_socket 'l'=tcp_listen 'p'=set_sour_port
|
||||
* 'P'=set_proto_type */
|
||||
* 'P'=set_proto_type 'w'=write_send_buf
|
||||
* 扩展区新增(net_socket 批次):'G'=读全局中断 'v'=sock_events(读即清)
|
||||
* 'X'=sock_reset_begin 'x'=sock_reset_poll 'z'=cmd_done 'T'=tcp_connect
|
||||
* 'I'=set_ip_addr 'N'=set_gwip 'K'=set_mask 'M'=set_mac 'D'=set_dhcp
|
||||
* 'F'=set_fun_para 'Q'=set_tcp_mss 'b'=set_send_buf 'u'=set_recv_buf
|
||||
* 'e'=set_des_ip 'q'=set_des_port 'g'=get_phy_status 'E'=check_exist
|
||||
* 'Z'=reset 'n'=init */
|
||||
char log[CH395F_FAKE_LOG_LEN];
|
||||
int log_n;
|
||||
} ch395f_fake_obs_t;
|
||||
@@ -143,4 +211,133 @@ void ch395f_set_keepalive_enable(uint8_t sock, uint8_t enable);
|
||||
uint8_t ch395f_link_up_cached(void);
|
||||
uint16_t ch395f_link_change_count(void);
|
||||
|
||||
/* ===== 扩展区第二部分:新增状态世界 + 新增假芯片原型 =====
|
||||
* ⚠ 刻意**不往既有 ch395f_fake_world_t / ch395f_fake_obs_t 里加字段**,而是另开一份
|
||||
* ch395f_fake_ext_t / ch395f_fake_ext_obs_t:既有三套单测的复位与断言面一字不动,
|
||||
* 新增状态由 ch395f_fake_reset() 调 ch395f_fake_ext_reset() 一并清零(场景间不残留)。
|
||||
*/
|
||||
typedef struct {
|
||||
/* —— 初始化/参数下发面(net_init 主路)—— */
|
||||
int exist_rc; /* ch395f_check_exist 返回(ch395f_status_t)*/
|
||||
int reset_rc; /* ch395f_reset 返回 */
|
||||
int init_rc; /* ch395f_init 返回 */
|
||||
uint8_t version; /* ch395f_get_version(版本门控 0x46 的用点位)*/
|
||||
uint8_t dhcp_enable; /* ch395f_set_dhcp */
|
||||
uint8_t ping_enable; /* ch395f_ping_enable */
|
||||
uint8_t ip[4]; /* ch395f_set_ip_addr */
|
||||
uint8_t gw[4]; /* ch395f_set_gwip_addr */
|
||||
uint8_t mask[4]; /* ch395f_set_mask_addr */
|
||||
uint8_t mac[6]; /* ch395f_set_mac_addr */
|
||||
uint32_t fun_para; /* ch395f_set_fun_para 最近一次实发值 */
|
||||
uint16_t mss; /* ch395f_set_tcp_mss */
|
||||
uint8_t retrans_cnt;
|
||||
uint16_t retrans_period;
|
||||
uint32_t ka_idle;
|
||||
uint32_t ka_intvl;
|
||||
uint8_t ka_cnt;
|
||||
uint8_t send_blk_start[CH395F_FAKE_SOCK_MAX]; /* ch395f_set_send_buf */
|
||||
uint8_t send_blk_cnt[CH395F_FAKE_SOCK_MAX];
|
||||
uint8_t recv_blk_start[CH395F_FAKE_SOCK_MAX]; /* ch395f_set_recv_buf */
|
||||
uint8_t recv_blk_cnt[CH395F_FAKE_SOCK_MAX];
|
||||
uint8_t des_ip[CH395F_FAKE_SOCK_MAX][4]; /* ch395f_set_des_ip */
|
||||
uint16_t des_port[CH395F_FAKE_SOCK_MAX]; /* ch395f_set_des_port */
|
||||
uint8_t connect_rc[CH395F_FAKE_SOCK_MAX]; /* ch395f_tcp_connect 返回 */
|
||||
/* —— 全局中断(0x19,整体读即清;唯一读者应为 net_poll)—— */
|
||||
uint16_t glob_q[CH395F_FAKE_GLOB_QUEUE];
|
||||
int glob_n;
|
||||
int glob_i;
|
||||
uint8_t phy_status; /* ch395f_get_phy_status(喂 glob_event_feed)*/
|
||||
/* —— sock_reset 复位机(契约等价,见文件头注释 4)—— */
|
||||
uint8_t rst_state[CH395F_FAKE_SOCK_MAX];
|
||||
uint8_t rst_mode[CH395F_FAKE_SOCK_MAX];
|
||||
uint32_t rst_t0[CH395F_FAKE_SOCK_MAX];
|
||||
uint8_t rst_quiet[CH395F_FAKE_SOCK_MAX]; /* 连续无事件轮数 */
|
||||
/* 1=复位机**不**替芯片把 cached_open 落回 CLOSED(造"永远关不掉"的芯片形态,
|
||||
* 用来量上层的复位超时收口)。默认 0 → 与既有行为一致。 */
|
||||
uint8_t rst_keep_open[CH395F_FAKE_SOCK_MAX];
|
||||
/* SINT 的**电平型**来源(可脚本化):读即清之后立刻把这个掩码放回 int_status。
|
||||
* 默认 0 → 与既有假件行为逐字相同(读一次就没了)。真实芯片的 RECV_OK /
|
||||
* SENBUF_FREE 本就是"条件位":只要条件还在(RX 仍非空 / 发送缓冲仍空),下一轮
|
||||
* 就还会再报一次。net_socket.c 的多块发送要的就是这个稳态,没有它就只能
|
||||
* 造"写完一块就再也等不到空闲"的假死形态。 */
|
||||
uint8_t sint_sticky[CH395F_FAKE_SOCK_MAX];
|
||||
} ch395f_fake_ext_t;
|
||||
|
||||
typedef struct {
|
||||
int exist_calls;
|
||||
int reset_calls;
|
||||
int init_calls;
|
||||
int set_ip_calls;
|
||||
int set_gw_calls;
|
||||
int set_mask_calls;
|
||||
int set_mac_calls;
|
||||
int set_dhcp_calls;
|
||||
int ping_calls;
|
||||
int set_fun_para_calls;
|
||||
int set_mss_calls;
|
||||
int set_retrans_calls;
|
||||
int set_ka_calls;
|
||||
int set_send_buf_calls;
|
||||
int set_recv_buf_calls;
|
||||
int set_des_ip_calls;
|
||||
int set_des_port_calls;
|
||||
int connect_calls;
|
||||
int glob_calls; /* 读全局中断次数(B3:除 net_poll 外应为 0)*/
|
||||
int glob_bits; /* 最近一次读到的 16 位全局位图 */
|
||||
int sock_events_calls; /* ch395f_sock_events(只碰本 Socket 的护栏原语)*/
|
||||
int rst_begin_calls;
|
||||
int rst_begin_reject; /* begin 被拒次数(复位机已在运行)*/
|
||||
int rst_poll_calls;
|
||||
int rst_done[CH395F_FAKE_SOCK_MAX]; /* 复位机完成计数 */
|
||||
int rst_fail[CH395F_FAKE_SOCK_MAX]; /* 复位机失败计数 */
|
||||
int cmd_done_calls;
|
||||
uint8_t last_cmd_done_what;
|
||||
int rst_close_calls; /* 复位机内部发出的 CLOSE 命令(含二段补发)*/
|
||||
int rst_rx_discard_calls; /* 复位机 RX 排空事务次数 */
|
||||
} ch395f_fake_ext_obs_t;
|
||||
|
||||
extern ch395f_fake_ext_t g_fx;
|
||||
extern ch395f_fake_ext_obs_t g_fxchip;
|
||||
|
||||
/* 由 ch395f_fake_reset() 自动调用;单独暴露是为了让测试能只清扩展面 */
|
||||
void ch395f_fake_ext_reset(void);
|
||||
void ch395f_fake_push_glob(uint16_t v); /* 预置全局中断返回序列 */
|
||||
void ch395f_fake_set_sint_sticky(uint8_t sock, uint8_t mask);
|
||||
/* 顺序断言辅助:清空调用日志 / 找某个短名第一次出现的位置(-1=没调过)。
|
||||
* 刻意提供"清零日志",否则一次长轮询(如 sock_reset 的几十轮 'x')会把 96 格
|
||||
* 环形外的顺序信息挤掉,让后续场景的顺序断言失真。 */
|
||||
void ch395f_fake_clear_log(void);
|
||||
int ch395f_fake_log_find(char c);
|
||||
|
||||
/* —— 新增假芯片实现(签名与真实 ch395f.h 一字不改)—— */
|
||||
ch395f_status_t ch395f_check_exist(void);
|
||||
ch395f_status_t ch395f_reset(void);
|
||||
ch395f_status_t ch395f_init(void);
|
||||
uint8_t ch395f_get_version(void);
|
||||
void ch395f_set_fun_para(uint32_t para);
|
||||
void ch395f_set_tcp_mss(uint16_t mss);
|
||||
void ch395f_set_retrans_count(uint8_t cnt);
|
||||
void ch395f_set_retrans_period(uint16_t period);
|
||||
void ch395f_set_keepalive_idle(uint32_t time);
|
||||
void ch395f_set_keepalive_intvl(uint32_t time);
|
||||
void ch395f_set_keepalive_cnt(uint8_t cnt);
|
||||
void ch395f_set_dhcp(uint8_t enable);
|
||||
void ch395f_ping_enable(uint8_t enable);
|
||||
void ch395f_set_ip_addr(uint8_t *p_ip);
|
||||
void ch395f_set_gwip_addr(uint8_t *p_gwip);
|
||||
void ch395f_set_mask_addr(uint8_t *p_mask);
|
||||
void ch395f_set_mac_addr(uint8_t *p_mac);
|
||||
void ch395f_set_send_buf(uint8_t sock, uint8_t start_block, uint8_t block_count);
|
||||
void ch395f_set_recv_buf(uint8_t sock, uint8_t start_block, uint8_t block_count);
|
||||
void ch395f_set_des_ip(uint8_t sock, uint8_t *p_ip);
|
||||
void ch395f_set_des_port(uint8_t sock, uint16_t port);
|
||||
uint8_t ch395f_tcp_connect(uint8_t sock);
|
||||
uint8_t ch395f_get_phy_status(void);
|
||||
uint16_t ch395f_get_glob_int_status_all(void);
|
||||
void ch395f_glob_event_feed(uint16_t glob_bits);
|
||||
uint8_t ch395f_sock_events(uint8_t sn);
|
||||
int ch395f_sock_reset_begin(uint8_t sn, ch395f_reset_mode_t mode);
|
||||
int ch395f_sock_reset_poll(uint8_t sn);
|
||||
int ch395f_cmd_done(uint8_t sn, ch395f_done_what_t what);
|
||||
|
||||
#endif /* CH395F_FAKE_H */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 模块名称:假 FatFs 内存卷(存储域主机侧单测共享件)
|
||||
* 模块功能:给 App/log 与 App/cfg 的主机侧单测一个可脚本化的假文件系统——
|
||||
* 类型与常量取自**真实 Lib/FatFs/ff.h**(FRESULT / FIL / FILINFO / FA_*
|
||||
* 一字不改),本件在 fatfs_fake.c 里给出九个 f_* 原型的 RAM 实现:
|
||||
* f_open / f_read / f_write / f_lseek / f_close / f_stat / f_unlink /
|
||||
* f_mkdir / f_rename。
|
||||
*
|
||||
* 仅供 test/host/** 使用:不参与 MCU 构建,也不参与 simulator 构建。
|
||||
*
|
||||
* 为什么共享一份(照 test/host/common/ch395f_fake.{h,c} 的先例):
|
||||
* sys_log_mcu / soe_store / cfg_file_store_mcu 三块的成败**全部**由
|
||||
* 「这次 f_open 拿到什么码、f_write 写回多少字节」决定。三份假件各写一遍,
|
||||
* 漂移 = 「假件与被测代码同时错而无人报错」。签名与真实头同源 → 真实 API 一改
|
||||
* 就编译期报错(同 test/host/nand_port 的「真头 + 假 diskio」路子)。
|
||||
*
|
||||
* —— 必须复刻的三条本仓实证语义(docs/架构设计/STM32F4-Base存储架构说明.md §10.1)——
|
||||
* 1) FA_OPEN_APPEND(0x30) **不含 FA_WRITE(0x02)**:本移植 FA_OPEN_APPEND =
|
||||
* FA_OPEN_ALWAYS(0x10) | FA_SEEKEND(0x20),而 FA_SEEKEND 只定义在 ff.c:74、
|
||||
* ff.h 里没有。所以只给 FA_OPEN_APPEND 打开的文件**能打开、能创建**,但
|
||||
* f_write 恒返 FR_DENIED(7)——2026-09-20 台架「/log 写失败 x5/开机」的真根因
|
||||
* (§10.1 第 11 条)。假件按真实 ff.c 一样把 mode 原值存进 fp->flag 再判
|
||||
* flag & FA_WRITE,不做任何「便利放行」。
|
||||
* 2) f_write 簇分配失败 = **FR_OK + 短写**(无错误码,判失败只能校验写回字节数):
|
||||
* 真实 ff.c:4102 的 if (clst == 0) break; → 循环带着已写计数退出 → FR_OK。
|
||||
* 假件把「每文件固定后备簇池」当介质:写到装不下就返回 FR_OK + 截断字节数;
|
||||
* 另给定点注入,让「恰好短一字节」这种真机要撞坏块才有的形态可复现。
|
||||
* 3) FF_USE_LFN=0 → **严格 8.3 短名**:主名 <=8、扩展 <=3、非法字符集
|
||||
* * + ; < = > [ ] | 与 0x7F(ff.c create_name 非 LFN 分支逐字模拟)。
|
||||
* 「.fschk」的真实死法 = 开头那个点把游标直接推进扩展域、随后 5 个字符撑爆
|
||||
* 扩展域上限 3 → FR_INVALID_NAME(6);路径里任何位置出现 ':' 被 get_ldnumber
|
||||
* 当盘符前缀吃下 → FR_INVALID_DRIVE(11)。两者都是**纯 RAM 里的名字判定、
|
||||
* 不碰盘**(§10.1 第 10 条,含「测试用例须同时接受这两个码」的口径)。
|
||||
*
|
||||
* —— 其余与真实 ff.c 对齐的路径/错误码细则 ——
|
||||
* - 前导分隔符被剥掉、恒从卷根开始(FF_FS_RPATH=0,见 ff.c follow_path);
|
||||
* - 中间目录段不存在 → FR_NO_PATH;末段不存在且未给创建位 → FR_NO_FILE;
|
||||
* - 对目录本身 f_open(无创建位)→ FR_NO_FILE;
|
||||
* - FA_CREATE_NEW 撞上已存在 → FR_EXIST;FA_CREATE_ALWAYS → 截零 + 刷新时间戳;
|
||||
* - 路径剥完分隔符为空 → FR_INVALID_NAME(ff.c create_name 的 i==0 分支);
|
||||
* - f_stat:父目录在而对象不在 → FR_NO_FILE;父目录也不在 → FR_NO_PATH;
|
||||
* - f_unlink 不存在 → FR_NO_FILE;f_mkdir 已存在 → FR_EXIST;
|
||||
* - **f_rename 撞已存在的新名 → FR_EXIST、不覆盖**(ff.c「new name already in
|
||||
* use」,2026-09-22 台架 E-01 暴露的那条:缺了它,一切「目标存在的覆盖写」
|
||||
* 必然失败,而模拟器端 POSIX rename 的覆盖语义照不出来)。不复刻本条 =
|
||||
* cfg_store_write_atomic 里「先 unlink 再 rename」那一步永远测不出必要性;
|
||||
* - f_lseek 越过文件尾:只读句柄**钳到 objsize 且不报错**(ff.c 正常寻址分支),
|
||||
* 可写句柄才扩展文件;
|
||||
* - f_open 失败即作废句柄(ff.c「fp->obj.fs = 0」)→ 后续 f_write 拿
|
||||
* FR_INVALID_OBJECT,不会拿着脏句柄继续写;
|
||||
* - f_close 成功即释放句柄,并把打开期间的变更落成新的 fdate/ftime
|
||||
* (真实 ff.c 在 close 里用 get_fattime 回写目录项)。
|
||||
*
|
||||
* —— 脚本化模型(沿用本仓既有假件风格:nand_port 的 RAM 假芯片 / lftpd_io 的假堆)——
|
||||
* - 注入:按路径 + op 预置「接下来 N 次返回某码 / 只写回 n 字节 / 只读回 n 字节」;
|
||||
* path 传 NULL = 不限路径;
|
||||
* - 观测:逐 op 调用计数 + 最近一次入参(路径、mode、btw/bw、btr/br、lseek 偏移),
|
||||
* 让「这轮压根不该碰盘」与「打开模式必须带写位」成为可断言的事;
|
||||
* - 卷:条目直读直写(ff_fake_mkfile / ff_fake_mkfile_size / ff_fake_fetch)用来
|
||||
* 铺前置状态——「run.log 已经有 1024 条记录」不可能靠真写 65536 字节铺出来;
|
||||
* - 假时钟:ff_fake_set_dostime 控制 fdate/ftime,FAT 分钟粒度导致
|
||||
* 「同分钟内同长度重写不可检出」这件事只有它能测(cfg_store_stat 的 mtime 令牌);
|
||||
* - ff_fake_reset() 一次全清(卷 + 注入 + 观测 + 时钟)。
|
||||
*
|
||||
* 有意**不提供** f_mount / f_mkfs / f_opendir / f_readdir:三个被测模块一次都不该
|
||||
* 调它们,真调了就是链接期报错——比运行时断言更硬,也不会静默通过。
|
||||
*
|
||||
* 创建日期:2026-10-24(P2 主机单测覆盖率批次,task-2)
|
||||
*/
|
||||
|
||||
#ifndef FATFS_FAKE_H
|
||||
#define FATFS_FAKE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* 真实头在 Lib/FatFs/;本文件不重定义任何 FatFs 类型或宏 */
|
||||
#include "ff.h"
|
||||
|
||||
#define FF_FAKE_MAX_ENT 16u /* 卷内条目上限(文件 + 目录) */
|
||||
#define FF_FAKE_PATH_MAX 24u /* 归一化后路径上限("/log/selftest.log" = 17) */
|
||||
/* 每文件的后备簇池:必须**大于生产侧单文件容量上限**(run.log / soe.log
|
||||
* = 1024 条 x 64 B = 65536 B,见 sys_log.h:SYSLOG_RUN_MAX 与 soe_store.h:
|
||||
* SOE_STORE_MAX),否则测"容量满 unlink 重建"时追加位落在池外,f_write 恒
|
||||
* 返回 0 字节短写,轮转场景根本跑不到。写满仍然如实返回 FR_OK + 短写
|
||||
*(真实 ff.c 簇分配失败的同一形态)。16 条目 x 66 KB ≈ 1 MB 静态,主机侧无压力。 */
|
||||
#define FF_FAKE_CLUSTER 67584u
|
||||
#define FF_FAKE_INJ_MAX 8u /* 注入槽位数 */
|
||||
#define FF_FAKE_GUARD 50000 /* 跑飞保险:单个 f_* 调用数超此值直接 exit(2) */
|
||||
|
||||
/* 注入与计数的操作编号(fatfs_fake.c 内部同一套) */
|
||||
enum {
|
||||
FF_FAKE_OP_OPEN = 0,
|
||||
FF_FAKE_OP_READ,
|
||||
FF_FAKE_OP_WRITE,
|
||||
FF_FAKE_OP_LSEEK,
|
||||
FF_FAKE_OP_CLOSE,
|
||||
FF_FAKE_OP_STAT,
|
||||
FF_FAKE_OP_UNLINK,
|
||||
FF_FAKE_OP_MKDIR,
|
||||
FF_FAKE_OP_RENAME,
|
||||
FF_FAKE_OP_N
|
||||
};
|
||||
|
||||
/* 注入动作 */
|
||||
enum {
|
||||
FF_FAKE_ACT_RC = 0, /* 直接返回指定 FRESULT,不做任何数据搬运 */
|
||||
FF_FAKE_ACT_WSHORT, /* 仅 f_write:FR_OK 但只写回 n 字节 */
|
||||
FF_FAKE_ACT_RSHORT /* 仅 f_read :FR_OK 但只读回 n 字节 */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int calls[FF_FAKE_OP_N]; /* 逐 op 调用次数(含失败的调用) */
|
||||
BYTE last_open_mode; /* 最近一次 f_open 的 mode 原值(断言 FA_WRITE 位) */
|
||||
char last_open_path[FF_FAKE_PATH_MAX];
|
||||
char last_unlink_path[FF_FAKE_PATH_MAX];
|
||||
char last_mkdir_path[FF_FAKE_PATH_MAX];
|
||||
char last_rename_old[FF_FAKE_PATH_MAX];
|
||||
char last_rename_new[FF_FAKE_PATH_MAX];
|
||||
UINT last_write_btw; /* 请求写的字节数 */
|
||||
UINT last_write_bw; /* 实际写回的字节数(短写的断言面) */
|
||||
UINT last_read_btr;
|
||||
UINT last_read_br;
|
||||
FSIZE_t last_lseek_ofs;
|
||||
int last_open_rc; /* 最近一次 f_open 的返回码 */
|
||||
int last_write_rc; /* 最近一次 f_write 的返回码 */
|
||||
} ff_fake_obs_t;
|
||||
|
||||
extern ff_fake_obs_t g_ff_obs;
|
||||
|
||||
/* —— 世界控制 —— */
|
||||
void ff_fake_reset(void); /* 卷 + 注入 + 观测 + 时钟一次全清 */
|
||||
void ff_fake_set_dostime(uint16_t fdate, uint16_t ftime); /* 假 get_fattime() 的两个域 */
|
||||
|
||||
/* —— 故障注入(times = 生效次数;path = NULL 表示不限路径)—— */
|
||||
void ff_fake_inject_rc(const char *path, int op, FRESULT rc, int times);
|
||||
void ff_fake_inject_write_short(const char *path, UINT n, int times);
|
||||
void ff_fake_inject_read_short(const char *path, UINT n, int times);
|
||||
|
||||
/* —— 卷内容直读直写(绕开打开语义,用来铺前置状态)—— */
|
||||
int ff_fake_exists(const char *path); /* 0 无 / 1 文件 / 2 目录 */
|
||||
uint32_t ff_fake_size(const char *path); /* 无此文件返回 0xFFFFFFFF */
|
||||
uint32_t ff_fake_time_token(const char *path); /* (fdate<<16)|ftime,同 cfg_store_stat */
|
||||
uint32_t ff_fake_fetch(const char *path, uint8_t *p_buf, uint32_t off, uint32_t cap);
|
||||
int ff_fake_mkfile(const char *path, const uint8_t *p_data, uint32_t len);
|
||||
int ff_fake_mkfile_size(const char *path, uint32_t size); /* 只给逻辑长度(造容量满) */
|
||||
int ff_fake_mkdir_direct(const char *path); /* 无条件建目录 */
|
||||
int ff_fake_count_entries(void); /* 卷内条目数(含目录) */
|
||||
|
||||
#endif /* FATFS_FAKE_H */
|
||||
Reference in New Issue
Block a user