feat(ui+记录): 整定写路径与四类页落地,五类记录写入方补齐
UI 面:ui_data 增加深度 4 参数写队列(请求 168 B、队列 .bss 672 B)与状态机 OK/ERR_RANGE/ERR_LOCKED/ERR_BUSY,ui_data_feed 出队后按注入 ops 走「闭锁 → 整批 预检 → 逐项 prot_set_set_sealed」,只提交改动位图;par 帧尾追加 cal_block (口径 = UA/UN/IA 三路 ADC_CAL_F_VALID,与 protect_task 判据同构,不用 rt.eng 反推,V0.1 下 eng 恒 0)、UI_DATA_VER 升到 2。参数/定值/控制字/软压板四叶绑定 内容页(UI_PAGE_MAP_COUNT 12→16,复用四个查看模块,零新增 .c),编辑态按 is_editable 分派,LOCK(写不进去)与「未标定」(仍可改值)两源两通道两词。 记录面:syslog_request_clear/state/apply 三段式删除 API(UI 只发请求,落地在 defaultTask 的 drain 尾部且必须后置,卷锁一律 try,幂等只认 FR_NO_FILE); 事故/告警/启动/操作/遥控五类各有一个真实写入点,记录类型 = 日志 ID 本身 (一类一个 /log 严格 8.3 文件;64B 定长体没有类型字段位,塞字段会砸掉既有读者)。 为守 protectTask 872 B 静态最坏链另拆 syslog_post_quiet()(无 DBG 的投递核, 丢弃留痕挪到 drain 侧差值补发),syslog_post 对外语义逐字不变。 为什么两道合成一笔而不是各一笔(实测,不是省事):simulator/main.c 同时消费 两道新增 API(整定 pump 与 logtest 清除用例),按道切文件会让中间提交编不过; RAM 受管快照与体积同码凭证描述的是合并后的 .map,拆开必落在「台账与 .map 不同 码」这条红线上。pre-commit 跑钩子前会 stash 未 staged 部分,这类耦合没法绕。 实测:@build 0 错误 0 警告;Code 228,260 / RO 24,960 / RW 1,440 / ZI 135,976 B; RW_IRAM1 79,992 B = 61.0%、RW_CCM 87.9%(阈值 92%);九个任务静态最坏链全 PASS, protectTask 872 B / 57% 改前后逐字节不变(记录投递没垫栈)。主机单测 ui_data 82→ 164、sys_log_mcu 106→137、prot_elm 47→60、iec104 141→154、iec104_serv 28→34, 既有断言零删改;48 个钩子 --all-files 全绿;编号门禁可判定 86.34%(基线 86.0%)、 不可判定 1,153 处低于上限 1,166;doc-links 无法判定 0;UI 黄金 18 页扩到 27 页 连跑两次逐字节一致;字库 diff 空。 顺手修掉三处既有缺陷(都是本轮第一个消费者才暴露):sim_syslog.c 的 s_paths 行宽 16 而 "sim_selftest.log" 正好 16 字符没有 NUL 位(stat/remove 读越过数组尾,此前 靠后面 .rodata 碰巧为 0);ui_w_setlist 二态行 ENTER 不回绕(控制字与软压板出厂值 全在最后一项,按了像没反应而叠层里已是越界值);生成物 ui_menu_tab.h 与 gen_menu.py 模板漂移(上一笔改了模板没重跑生成器),现随生成器输出对齐。 未收口(不算完成):保存后重启值保持与主站 C_RD 读回两条只能上板,台架窗口执行; LOCK 屏显与越界反显的页面侧在模拟器造不出来(模拟器不跑定值自检、无 NAND), 已由生成侧单测与代码注释点名差距;同类页数与钩子数陈述残留另立待办。
This commit is contained in:
@@ -41,6 +41,25 @@
|
||||
* PE_COUNT == 表长);元件名不超名列宽、非元件/越界回占位
|
||||
* TC-UIDATA-014 注入 reader 后的两行渲染:时标行/内容行分工、单条读盘缓存
|
||||
* (同记录两行只读一次)、IV 时标抑制、读失败退化与不绑空态
|
||||
* ── 以下 6 条 = 定值写路径(道F task-1,docs/任务规划/UI补齐.md §2 第 1 行)──
|
||||
* TC-UIDATA-015 队列深度 4:四格都进得去、第五格满返 -1 且状态转 ERR_BUSY;
|
||||
* FIFO 顺序(最后投的一条最后落定)、空队列 drain 返 0、
|
||||
* drain(NULL) 返 -1 且不动队列、投递成功即转 WAIT
|
||||
* TC-UIDATA-016 请求表示法:NULL/带非法位 mask 返 -2 且状态与队列都不动;
|
||||
* mask=0 空请求返 0 且直接落 OK 不占槽;请求尺寸与队列 .bss
|
||||
* 预算钉死(168 B/格 × 4 格)——加深或加宽必须先过这条
|
||||
* TC-UIDATA-017 正常往返:请求 → WAIT → drain → OK → 新值可读;**只提交 mask
|
||||
* 内的 id**(未提交的槽一位都不动,这是"从显示值回提全表会
|
||||
* 顺手改掉返回系数"那条坑的机器防线)
|
||||
* TC-UIDATA-018 越界不改值:整批预检任一项越界 → ERR_RANGE 且**一字不写**
|
||||
* (含同批里合法的那几项)+ err_mask 指认越界项 + 下一次成功
|
||||
* 落定后 err_mask 自动清零
|
||||
* TC-UIDATA-019 闭锁拒写:locked 置位 → ERR_LOCKED、丢弃本条(不排队等解锁)、
|
||||
* 值区一字不动;解闭锁后重新提交可成功
|
||||
* TC-UIDATA-020 apply 中途失败(预检与执行不一致的防御分支):如实 ERR_RANGE,
|
||||
* 已生效的前几项不回滚(prot_set 无回滚接口,谎称整批未写更坏)
|
||||
* TC-UIDATA-021 v2 帧扩展:UI_DATA_VER == 2、既有字段偏移逐字节见证(只允许尾
|
||||
* 追加)、cal_block 两种态(未标定/已标定)整帧往返
|
||||
*
|
||||
* 通过:全部 TC PASS(main 返回 0)。
|
||||
* 入口:int main(void),返回 0=PASS / 1=FAIL(由 run 脚本判读)。
|
||||
@@ -49,6 +68,7 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h> /* offsetof:v2 帧布局见证(只许尾追加) */
|
||||
#include <string.h>
|
||||
|
||||
/* 生产源文件原样纳入(纯内存叶子,零平台依赖,直接编) */
|
||||
@@ -521,6 +541,351 @@ static void tc_soe_render(void)
|
||||
ui_w_soe_bind(NULL);
|
||||
}
|
||||
|
||||
/* ====== TC-UIDATA-015~020:定值写路径(队列 + 状态机 + 注入执行侧) ====== */
|
||||
|
||||
/*
|
||||
* 假执行侧(替掉 App/algo/prot_set):一张 40 项值区 + 一张上下限表。
|
||||
* ⚠ 本单测不链 App 侧任何 .c(prot_set 依赖 Flash/校验和编排,且写路径的**契约**
|
||||
* 本来就只是"三个函数的返回值"),所以测的是 ui_data.c 里那份真实的队列 +
|
||||
* 状态机代码:越界不改值、闭锁拒写、只提交改动 id 三条判据都落在生产代码上,
|
||||
* 不是测试里的副本。真实 App 实现的等价性由 ui_data_feed.c 的三个包函数
|
||||
* (feed_param_locked/check/apply)在代码审查与台架侧保证。
|
||||
*/
|
||||
#define FAKE_LO_ALL (-100000) /* 默认宽松下限:单条 TC 里再逐行收紧 */
|
||||
#define FAKE_HI_ALL (100000)
|
||||
|
||||
static int32_t s_fake_val[UI_DATA_SET_N];
|
||||
static int32_t s_fake_lo[UI_DATA_SET_N];
|
||||
static int32_t s_fake_hi[UI_DATA_SET_N];
|
||||
static uint8_t s_fake_locked;
|
||||
static uint8_t s_fake_poison; /* 0xFF = 不投毒;否则该 id 的 apply 必失败 */
|
||||
static uint32_t s_fake_apply_calls;
|
||||
|
||||
static uint8_t fake_locked(void)
|
||||
{
|
||||
return s_fake_locked;
|
||||
}
|
||||
|
||||
static int fake_check(uint8_t a_id, int32_t a_q10)
|
||||
{
|
||||
if (a_id >= (uint8_t)UI_DATA_SET_N) {
|
||||
return -1;
|
||||
}
|
||||
if ((a_q10 < s_fake_lo[a_id]) || (a_q10 > s_fake_hi[a_id])) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fake_apply(uint8_t a_id, int32_t a_q10)
|
||||
{
|
||||
if (fake_check(a_id, a_q10) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if ((s_fake_poison != 0xFFu) && (a_id == s_fake_poison)) {
|
||||
return -1; /* 投毒:模拟"预检过了但执行侧仍拒"(理论不可达的防御分支) */
|
||||
}
|
||||
s_fake_val[a_id] = a_q10;
|
||||
s_fake_apply_calls++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const ui_data_param_ops_t s_fake_ops = { fake_locked, fake_check, fake_apply };
|
||||
|
||||
/* 每个 TC 开头调一次:值区填可辨识的旧值 + 快照与队列复位 */
|
||||
static void fake_reset(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0u; i < (uint8_t)UI_DATA_SET_N; i++) {
|
||||
s_fake_val[i] = (int32_t)i * 1000;
|
||||
s_fake_lo[i] = FAKE_LO_ALL;
|
||||
s_fake_hi[i] = FAKE_HI_ALL;
|
||||
}
|
||||
s_fake_locked = 0u;
|
||||
s_fake_poison = 0xFFu;
|
||||
s_fake_apply_calls = 0u;
|
||||
ui_data_init();
|
||||
}
|
||||
|
||||
/* 投一条只改若干项的请求;未列出的 id 不进 mask(= 本次不提交) */
|
||||
static int fake_submit(const uint8_t *a_ids, const int32_t *a_vals, uint8_t a_n)
|
||||
{
|
||||
ui_data_param_req_t req;
|
||||
uint8_t i;
|
||||
|
||||
(void)memset(&req, 0, sizeof(req));
|
||||
for (i = 0u; i < a_n; i++) {
|
||||
req.mask |= ((uint64_t)1u << a_ids[i]);
|
||||
req.q10[a_ids[i]] = a_vals[i];
|
||||
}
|
||||
return ui_data_param_request(&req);
|
||||
}
|
||||
|
||||
static int32_t fake_val(uint8_t a_id)
|
||||
{
|
||||
return s_fake_val[a_id];
|
||||
}
|
||||
|
||||
static void tc_param_queue(void)
|
||||
{
|
||||
ui_data_param_req_t req;
|
||||
uint8_t k;
|
||||
int accepted = 1;
|
||||
int was_wait = 1;
|
||||
|
||||
fake_reset();
|
||||
CHECK(ui_data_param_state() == UI_DATA_PARAM_ST_IDLE,
|
||||
"TC-UIDATA-015a state IDLE after init (never submitted)");
|
||||
CHECK(ui_data_param_drain(&s_fake_ops) == 0, "TC-UIDATA-015b drain on empty queue == 0");
|
||||
CHECK(ui_data_param_state() == UI_DATA_PARAM_ST_IDLE,
|
||||
"TC-UIDATA-015c empty drain leaves state alone (not WAIT)");
|
||||
CHECK(ui_data_param_err_mask() == 0u, "TC-UIDATA-015d err_mask clean at idle");
|
||||
|
||||
/* 四格全进得去;每投一条状态就该是 WAIT */
|
||||
(void)memset(&req, 0, sizeof(req));
|
||||
req.mask = 1u; /* 只改 id0 */
|
||||
for (k = 0u; k < UI_DATA_PARAM_QD; k++) {
|
||||
req.q10[0] = (int32_t)(1000 + (int32_t)k);
|
||||
if (ui_data_param_request(&req) != 0) {
|
||||
accepted = 0;
|
||||
}
|
||||
if (ui_data_param_state() != UI_DATA_PARAM_ST_WAIT) {
|
||||
was_wait = 0;
|
||||
}
|
||||
}
|
||||
CHECK(UI_DATA_PARAM_QD == 4u, "TC-UIDATA-015e queue depth is 4 (contract witness)");
|
||||
CHECK(accepted == 1, "TC-UIDATA-015f four requests all accepted");
|
||||
CHECK(was_wait == 1, "TC-UIDATA-015g state WAIT while queued");
|
||||
|
||||
/* 第五格:满 → 返 -1 + 状态 ERR_BUSY(在队四条一条不掉) */
|
||||
req.q10[0] = 9999;
|
||||
CHECK(ui_data_param_request(&req) == -1, "TC-UIDATA-015h fifth request rejected (-1)");
|
||||
CHECK(ui_data_param_state() == UI_DATA_PARAM_ST_ERR_BUSY,
|
||||
"TC-UIDATA-015i rejected request surfaces ERR_BUSY");
|
||||
|
||||
/* FIFO:前三条落定后仍是 WAIT,第四条才落终态;值 = 最后投的那条(1003) */
|
||||
CHECK(ui_data_param_drain(&s_fake_ops) == 1, "TC-UIDATA-015j drain processes one");
|
||||
CHECK(ui_data_param_state() == UI_DATA_PARAM_ST_WAIT,
|
||||
"TC-UIDATA-015k still WAIT with 3 requests left in queue");
|
||||
(void)ui_data_param_drain(&s_fake_ops);
|
||||
(void)ui_data_param_drain(&s_fake_ops);
|
||||
CHECK(ui_data_param_state() == UI_DATA_PARAM_ST_WAIT,
|
||||
"TC-UIDATA-015l WAIT until the last queued request settles");
|
||||
CHECK(ui_data_param_drain(&s_fake_ops) == 1, "TC-UIDATA-015m fourth request drained");
|
||||
CHECK(ui_data_param_state() == UI_DATA_PARAM_ST_OK, "TC-UIDATA-015n terminal OK when drained");
|
||||
CHECK((fake_val(0) == 1003) && (s_fake_apply_calls == 4u),
|
||||
"TC-UIDATA-015o FIFO: last queued value lands, all four applied");
|
||||
CHECK(ui_data_param_drain(&s_fake_ops) == 0, "TC-UIDATA-015p queue empty again");
|
||||
|
||||
/* ops 无效:不动队列也不动状态 */
|
||||
fake_reset();
|
||||
CHECK(fake_submit(NULL, NULL, 0u) == 0, "TC-UIDATA-015q empty request accepted (see 016)");
|
||||
CHECK(ui_data_param_drain(NULL) == -1, "TC-UIDATA-015r drain(NULL) == -1");
|
||||
CHECK(ui_data_param_state() == UI_DATA_PARAM_ST_OK,
|
||||
"TC-UIDATA-015s failed drain does not rewrite state");
|
||||
CHECK(ui_data_param_drain(&s_fake_ops) == 0, "TC-UIDATA-015t failed drain does not consume");
|
||||
}
|
||||
|
||||
static void tc_param_request_shape(void)
|
||||
{
|
||||
ui_data_param_req_t req;
|
||||
|
||||
fake_reset();
|
||||
CHECK(ui_data_param_request(NULL) == -2, "TC-UIDATA-016a request(NULL) == -2");
|
||||
CHECK(ui_data_param_state() == UI_DATA_PARAM_ST_IDLE,
|
||||
"TC-UIDATA-016b invalid request leaves state IDLE (page must not show ERR for a bug)");
|
||||
|
||||
(void)memset(&req, 0, sizeof(req));
|
||||
req.mask = ((uint64_t)1u << 63u); /* 第 63 项不存在 */
|
||||
CHECK(ui_data_param_request(&req) == -2, "TC-UIDATA-016c mask with bit63 == -2");
|
||||
req.mask = ((uint64_t)1u << (UI_DATA_SET_N - 1u)); /* 第 39 项 = 最后一项,合法 */
|
||||
CHECK(ui_data_param_request(&req) == 0, "TC-UIDATA-016d last legal bit accepted");
|
||||
CHECK(ui_data_param_drain(&s_fake_ops) == 1, "TC-UIDATA-016e drained last-bit request");
|
||||
CHECK(s_fake_apply_calls == 1u, "TC-UIDATA-016f item 39 really reached the executor");
|
||||
|
||||
/* 空请求:不占槽、直接 OK("没改东西却按了保存"是正常操作) */
|
||||
fake_reset();
|
||||
req.mask = 0u;
|
||||
CHECK(ui_data_param_request(&req) == 0, "TC-UIDATA-016g empty request returns 0");
|
||||
CHECK(ui_data_param_state() == UI_DATA_PARAM_ST_OK, "TC-UIDATA-016h empty request settles OK");
|
||||
CHECK(ui_data_param_drain(&s_fake_ops) == 0, "TC-UIDATA-016i empty request consumed no slot");
|
||||
|
||||
/* 内存预算护栏:请求是 .bss 里的定长槽,168 B/格 × 4 格 = 672 B
|
||||
* (台账 docs/内存管理/RAM使用分析与优化方案.md;扩深度/加宽必须先过这条) */
|
||||
CHECK(sizeof(ui_data_param_req_t) == 168u, "TC-UIDATA-016j request is 168 bytes");
|
||||
CHECK(sizeof(s_pq) == (672u), "TC-UIDATA-016k queue .bss is 4 x 168 = 672 bytes");
|
||||
CHECK(sizeof(s_pq) <= 768u, "TC-UIDATA-016l queue .bss within budget (<=768B)");
|
||||
CHECK((uint32_t)UI_DATA_SET_N <= 64u, "TC-UIDATA-016m item count fits the 64-bit mask");
|
||||
}
|
||||
|
||||
static void tc_param_roundtrip(void)
|
||||
{
|
||||
const uint8_t ids[3] = { 2u, 3u, 7u };
|
||||
const int32_t vals[3] = { 12345, 60000, 7 };
|
||||
|
||||
fake_reset();
|
||||
CHECK(fake_submit(ids, vals, 3u) == 0, "TC-UIDATA-017a batch of three submitted");
|
||||
CHECK(ui_data_param_state() == UI_DATA_PARAM_ST_WAIT, "TC-UIDATA-017b WAIT before drain");
|
||||
CHECK(fake_val(2) == 2000, "TC-UIDATA-017c old value still in effect before drain");
|
||||
CHECK(ui_data_param_drain(&s_fake_ops) == 1, "TC-UIDATA-017d drained one request");
|
||||
CHECK(ui_data_param_state() == UI_DATA_PARAM_ST_OK, "TC-UIDATA-017e OK after drain");
|
||||
CHECK((fake_val(2) == 12345) && (fake_val(3) == 60000) && (fake_val(7) == 7),
|
||||
"TC-UIDATA-017f new values readable after roundtrip");
|
||||
/* 只提交改动项:mask 外的槽一位都不动(Q10 往返精度坑的机器防线) */
|
||||
CHECK((fake_val(0) == 0) && (fake_val(1) == 1000) && (fake_val(4) == 4000)
|
||||
&& (fake_val(6) == 6000) && (fake_val(8) == 8000)
|
||||
&& (fake_val(UI_DATA_SET_N - 1u) == (int32_t)(UI_DATA_SET_N - 1u) * 1000),
|
||||
"TC-UIDATA-017g untouched items are byte-identical (mask-only commit)");
|
||||
CHECK(s_fake_apply_calls == 3u, "TC-UIDATA-017h exactly the masked items were applied");
|
||||
|
||||
/* 回环的后半段:App 落值后重发参数帧 → 页面按新 seq 读到新值 */
|
||||
{
|
||||
ui_data_par_t frame;
|
||||
ui_data_par_t got;
|
||||
uint8_t i;
|
||||
|
||||
(void)memset(&frame, 0, sizeof(frame));
|
||||
frame.valid = 1u;
|
||||
frame.checksum = 0x1234u;
|
||||
frame.cal_block = 0u;
|
||||
for (i = 0u; i < (uint8_t)UI_DATA_SET_N; i++) {
|
||||
frame.q10[i] = s_fake_val[i];
|
||||
}
|
||||
CHECK(ui_data_publish_par(&frame) == 0, "TC-UIDATA-017i par frame republished");
|
||||
(void)memset(&got, 0, sizeof(got));
|
||||
CHECK((ui_data_par(&got) == 0) && (got.q10[2] == 12345) && (got.q10[7] == 7),
|
||||
"TC-UIDATA-017j page reads back the committed values");
|
||||
}
|
||||
}
|
||||
|
||||
static void tc_param_range_atomic(void)
|
||||
{
|
||||
/* 非 const:本 TC 复用同一组数组跑三种越界组合(改了再投) */
|
||||
uint8_t ids[2] = { 2u, 5u };
|
||||
int32_t vals[2] = { 12345, 99999 };
|
||||
|
||||
fake_reset();
|
||||
s_fake_hi[5] = 100; /* 第 5 项上限 100 → 99999 越界;第 2 项本身合法 */
|
||||
CHECK(fake_submit(ids, vals, 2u) == 0, "TC-UIDATA-018a out-of-range batch accepted for queue");
|
||||
CHECK(ui_data_param_drain(&s_fake_ops) == 1, "TC-UIDATA-018b drained");
|
||||
CHECK(ui_data_param_state() == UI_DATA_PARAM_ST_ERR_RANGE, "TC-UIDATA-018c state ERR_RANGE");
|
||||
CHECK(ui_data_param_err_mask() == ((uint64_t)1u << 5u),
|
||||
"TC-UIDATA-018d err_mask points at the offending item");
|
||||
CHECK((fake_val(2) == 2000) && (fake_val(5) == 5000),
|
||||
"TC-UIDATA-018e all-or-nothing: the legal item in the same batch NOT written");
|
||||
CHECK(s_fake_apply_calls == 0u, "TC-UIDATA-018f precheck blocks every apply call");
|
||||
|
||||
/* 多项同时越界:err_mask 全给(页面逐行反显,不是只报第一行) */
|
||||
fake_reset();
|
||||
s_fake_lo[6] = 500;
|
||||
s_fake_hi[5] = 100;
|
||||
ids[0] = 5u;
|
||||
ids[1] = 6u;
|
||||
vals[0] = 99999; /* 超上限 */
|
||||
vals[1] = 60; /* 低于下限 */
|
||||
CHECK(fake_submit(ids, vals, 2u) == 0, "TC-UIDATA-018g two-item bad batch queued");
|
||||
(void)ui_data_param_drain(&s_fake_ops);
|
||||
CHECK(ui_data_param_err_mask() == (((uint64_t)1u << 5u) | ((uint64_t)1u << 6u)),
|
||||
"TC-UIDATA-018h err_mask lists every offending item (both bounds)");
|
||||
|
||||
/* 下一次成功落定 → err_mask 自动清零(否则页面把上次越界显示成这次) */
|
||||
ids[0] = 2u;
|
||||
vals[0] = 12345;
|
||||
CHECK(fake_submit(ids, vals, 1u) == 0, "TC-UIDATA-018i good request queued");
|
||||
(void)ui_data_param_drain(&s_fake_ops);
|
||||
CHECK((ui_data_param_state() == UI_DATA_PARAM_ST_OK) && (ui_data_param_err_mask() == 0u),
|
||||
"TC-UIDATA-018j err_mask cleared on next successful commit");
|
||||
}
|
||||
|
||||
static void tc_param_locked(void)
|
||||
{
|
||||
const uint8_t ids[1] = { 2u };
|
||||
const int32_t vals[1] = { 12345 };
|
||||
|
||||
fake_reset();
|
||||
s_fake_locked = 1u;
|
||||
CHECK(fake_submit(ids, vals, 1u) == 0, "TC-UIDATA-019a request queued while locked");
|
||||
CHECK(ui_data_param_drain(&s_fake_ops) == 1, "TC-UIDATA-019b drained under lock");
|
||||
CHECK(ui_data_param_state() == UI_DATA_PARAM_ST_ERR_LOCKED, "TC-UIDATA-019c state ERR_LOCKED");
|
||||
CHECK((fake_val(2) == 2000) && (s_fake_apply_calls == 0u),
|
||||
"TC-UIDATA-019d locked commit writes nothing (no apply call)");
|
||||
CHECK(ui_data_param_err_mask() == 0u, "TC-UIDATA-019e lock failure is not a range failure");
|
||||
CHECK(ui_data_param_drain(&s_fake_ops) == 0,
|
||||
"TC-UIDATA-019f locked request is dropped, not queued waiting for unlock");
|
||||
|
||||
/* 解闭锁后重新提交 → OK(闭锁丢弃的是那一条,不是永久拉黑写路径) */
|
||||
s_fake_locked = 0u;
|
||||
CHECK(fake_submit(ids, vals, 1u) == 0, "TC-UIDATA-019g resubmit after unlock accepted");
|
||||
(void)ui_data_param_drain(&s_fake_ops);
|
||||
CHECK((ui_data_param_state() == UI_DATA_PARAM_ST_OK) && (fake_val(2) == 12345),
|
||||
"TC-UIDATA-019h write path healthy again after unlock");
|
||||
}
|
||||
|
||||
static void tc_param_apply_fail(void)
|
||||
{
|
||||
const uint8_t ids[2] = { 3u, 7u };
|
||||
const int32_t vals[2] = { 33333, 77777 };
|
||||
|
||||
fake_reset();
|
||||
s_fake_poison = 7u; /* 预检全过、执行侧仍拒第 7 项(两真源不一致的防御分支) */
|
||||
CHECK(fake_submit(ids, vals, 2u) == 0, "TC-UIDATA-020a request with poisoned item queued");
|
||||
CHECK(ui_data_param_drain(&s_fake_ops) == 1, "TC-UIDATA-020b drained");
|
||||
CHECK(ui_data_param_state() == UI_DATA_PARAM_ST_ERR_RANGE,
|
||||
"TC-UIDATA-020c apply failure reported as ERR_RANGE (not silent OK)");
|
||||
CHECK(ui_data_param_err_mask() == ((uint64_t)1u << 7u), "TC-UIDATA-020d failing item named");
|
||||
CHECK(fake_val(3) == 33333, "TC-UIDATA-020e earlier item already applied is NOT rolled back");
|
||||
CHECK(fake_val(7) == 7000, "TC-UIDATA-020f failing item itself unchanged");
|
||||
}
|
||||
|
||||
static void tc_param_frame_v2(void)
|
||||
{
|
||||
ui_data_par_t src;
|
||||
ui_data_par_t got;
|
||||
|
||||
/* 版本号是页面与写侧唯一对齐凭据;v2 = 尾追加 cal_block */
|
||||
CHECK(UI_DATA_VER == 2u, "TC-UIDATA-021a contract version is 2");
|
||||
/* v1 布局逐字节见证:字段只允许尾追加,任何插入/重排都会让下面四条红
|
||||
* (页面按名字取字段,错位是静默的——只有把偏移钉死才照得出来) */
|
||||
CHECK(offsetof(ui_data_par_t, seq) == 0u, "TC-UIDATA-021b seq offset unchanged");
|
||||
CHECK(offsetof(ui_data_par_t, bad_mask) == 16u, "TC-UIDATA-021c bad_mask offset unchanged");
|
||||
CHECK(offsetof(ui_data_par_t, q10) == 24u, "TC-UIDATA-021d q10 offset unchanged");
|
||||
CHECK(offsetof(ui_data_par_t, cal_block) == 184u,
|
||||
"TC-UIDATA-021e cal_block is the appended tail field (offset 184)");
|
||||
CHECK(sizeof(ui_data_par_t) == 192u, "TC-UIDATA-021f par frame is 192 B (+8 for v2)");
|
||||
/* 写侧组装缓冲是 rt/par/info 三帧共用的 union(App/app/ui_data_feed.c 的 s_scr):
|
||||
* rt 恒大于 par,所以给 par 追加字段**不会**连带抬升那块 .bss——加字段之前
|
||||
* 先确认这条,否则每次尾追加都悄悄多啃一份 RAM(台账只记总量,看不出是谁长的) */
|
||||
CHECK(sizeof(ui_data_rt_t) == 208u, "TC-UIDATA-021g rt frame is 208 B (union stays put)");
|
||||
CHECK(sizeof(ui_data_par_t) < sizeof(ui_data_rt_t),
|
||||
"TC-UIDATA-021h par still smaller than rt (shared scratch unchanged)");
|
||||
|
||||
/* 闭锁态:1 = 未标定(写侧按 UA/UN/IA 三路 VALID 现算,本层只存真值) */
|
||||
ui_data_init();
|
||||
(void)memset(&src, 0, sizeof(src));
|
||||
src.valid = 1u;
|
||||
src.locked = 0u;
|
||||
src.cal_block = 1u;
|
||||
CHECK(ui_data_publish_par(&src) == 0, "TC-UIDATA-021g publish blocked frame");
|
||||
(void)memset(&got, 0, sizeof(got));
|
||||
CHECK((ui_data_par(&got) == 0) && (got.cal_block == 1u) && (got.locked == 0u),
|
||||
"TC-UIDATA-021h cal_block=1 roundtrip, distinct from locked");
|
||||
|
||||
/* 解卡态:写侧填 0 后覆盖发布(同一份存储不会粘住旧值) */
|
||||
src.cal_block = 0u;
|
||||
(void)ui_data_publish_par(&src);
|
||||
(void)memset(&got, 0xAA, sizeof(got));
|
||||
CHECK((ui_data_par(&got) == 0) && (got.cal_block == 0u),
|
||||
"TC-UIDATA-021i cal_block=0 roundtrip (no stale block flag)");
|
||||
|
||||
/* 写侧默认值纪律:调用方清零后不填本字段 = 0(未闭锁),首帧不会假报徽标 */
|
||||
(void)memset(&src, 0, sizeof(src));
|
||||
src.valid = 1u;
|
||||
(void)ui_data_publish_par(&src);
|
||||
(void)ui_data_par(&got);
|
||||
CHECK(got.cal_block == 0u, "TC-UIDATA-021j zero-initialized frame means not blocked");
|
||||
}
|
||||
|
||||
/* ===================== main ===================== */
|
||||
|
||||
int main(void)
|
||||
@@ -551,6 +916,20 @@ int main(void)
|
||||
tc_soe_elms();
|
||||
printf("-- soe render --\n");
|
||||
tc_soe_render();
|
||||
printf("-- param write path: queue --\n");
|
||||
tc_param_queue();
|
||||
printf("-- param write path: request shape --\n");
|
||||
tc_param_request_shape();
|
||||
printf("-- param write path: roundtrip --\n");
|
||||
tc_param_roundtrip();
|
||||
printf("-- param write path: range atomic --\n");
|
||||
tc_param_range_atomic();
|
||||
printf("-- param write path: locked --\n");
|
||||
tc_param_locked();
|
||||
printf("-- param write path: apply failure --\n");
|
||||
tc_param_apply_fail();
|
||||
printf("-- param frame v2 (cal_block) --\n");
|
||||
tc_param_frame_v2();
|
||||
|
||||
printf("\n=== UI Data Snapshot Tests: %d/%d PASSED (failed=%d) ===\n",
|
||||
s_passed, s_total, s_failed);
|
||||
|
||||
Reference in New Issue
Block a user