Files
STM32F4-Base/test/host/run_par/run_par_test.c
T
wandering1 8c72698666 feat(运行参数): 卡10 run_par真源收编+104段0x8020~0x8058读写映射(道D M6-①)
新增 App/algo/run_par.c/h(「数据可信」卡 10,lead 四裁落地):
- [run_par] cfg 段:遥测死区 9 键 int32 毫单位(db_ua…db_freq),工厂默认 =
  卡 6 额定 0.5% 档(500mV×4 / 5mA×4 / 500mHz);注册走 adc_cal 同款内核前
  契约(app_main_init),不挂回调 = 引擎副本即生效值;键数 9≤16 不拆段
- 真源收编(裁③):pts 层 s_yc_db 运行时副本删除,COV 每拍实时读 run_par
  (协议→算法向下依赖,同 meas 先例;无投递无缓存 = 顺序/幂等平凡成立);
  iec104_pts_yc_set_deadband()/deadband() 保留为转发壳,壳内保留"成功清
  半截确认计数"语义并由 TC-IEC104-027 追加断言钉住(裁⑤)
- 104 地址映射(裁①):0x8030~0x8038 死区 9 点 / 0x8040~0x8047 k 覆盖 /
  0x8048~0x804F b 覆盖(逐项地址本仓自定——复现规划 §5.4 权威表不在仓,
  送检点表到位后替换 run_par.c 顶部常量段一处;已在 IEC104 §3.4 表头挂账);
  路由顺序 = run_par 表先、未命中落 prot_set——0x8027~0x802A 属 prot_set
  告警段永不覆盖,TC-IEC104-029 双向往返钉死;空隙地址自然 COT14/PN
- 通道系数覆盖 = adc_cal_set 代理(裁②a):合法带/越带 [ERR] 留痕全复用,
  run_par 不存第二份系数(防双真源);单侧写 RMW 用另一侧当前生效值;
  回显 = 生效值视图(文件坏值被哨兵回退 ROM 时主站看到的就是下游吃到的);
  adc_cal 配套:+adc_cal_b_codes() 读取口(回显/RMW 用)+ set() 授权写者
  注记扩至 iec104Task 直通路径(单字原子契约同「数据可信」§4.4)
- 持久化(裁②b):新事件位 SYS_EVT_RUN_PAR_SAVE(1<<8);iec104Task 主循环
  peek 投递 → defaultTask run_par_store_save() 一条事件一趟 cfg_file_save
  整档外科(覆盖 [run_par] 9 键 + 联动 [adc_cal] 两段);失败脏保留 = 天然
  重试(B7 prot_set 同款)。C_SE 现行无"暂停/测试态"门控——文档现行如此
  不造新语义,送检若要求接线点已写进 §3.4 注(裁④)

测试:新建 test/host/run_par(TC-RUNPAR-001~008,67 断言,真实 cfg 引擎 +
真实 adc_cal + 内存虚拟盘同 adc_cal 套件先例;[run_par] 注册契约/落盘往返/
外改文件即副本生效/两段式不提交不置脏/合法带拒+RMW+生效值回显/init 幂等
重注册留痕);iec104 套扩真链 TU(cfg_file+adc_cal+run_par 非桩,141 断言:
+TC-IEC104-029 路由 + 壳清确认计数 3 断言);meas 套静态更名 s_snap*→
s_ms_snap*(与 cfg_file.c s_snap 同 TU 相撞,D1 同款改名法,226 断言不变);
iec104_serv 套补 run_par_dirty_peek 假件。id_registry 建族 TC-HOST-RUNPAR
(游标 008)+ TC-HOST-IEC104 游标 028→029;新钩子 run-par-unit-test +
iec104 钩子触发面随真链扩;uvprojx +run_par.c(合法入库)+ SIM_EXEMPT 登记
(模拟器不编 104 协议,已核实 CMake 面)。

文档:IEC104 §3.4 运行参数段表(含送检替换账/门控未来条件/代理声明)、§7、
§8 第 4 条、变更记录;网络配置说明新增 §3.5 [run_par] 键表;复现规划 §5.4
补运行参数段落实进度。**遗留(交 lead)**:数据可信 §4.4 "引擎 RAM 副本"
一节当时的措辞对象含 pts 侧副本,s_yc_db 删除后该句过时(裁③b 点名,§4.4
在写域外未动)。

gate:@build 0/0(Code=219620 RO=22036 RW=1280 ZI=132864);vs 0cbae7d:
Flash +1912B、RAM(RW+ZI) +8B(37B 新静态被链接器并洞吸收);iec104Task 栈
1456B/margin 43%、defaultTask 3032B 不变;id-check/doc-links/cppcheck/
coding-standard 实跑过;--all-files 全量 45 钩子实跑(43 绿 + 仅 mem-budget /
mem-numbers 两红 = 台账受管区 vs .map 漂移,回填属 lead 单写者区,批 1/道D-1
/D2 同口径):gate: only mem-budget/mem-numbers pending lead ledger backfill
BOM/CR/U+FFFD/字面量 ASCII 逐文件自查通过。
2026-10-25 13:15:41 +08:00

394 lines
16 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.
/*
* 模块名称:运行参数段(run_par)主机侧单元测试
* 模块功能:离线钉死「数据可信」卡 10 的三层契约——① [run_par] cfg 段:
* 注册/工厂默认/落盘往返/文件改值生效(真实 cfg 引擎 + 内存虚拟盘,
* 同 adc_cal 套件先例);② 104 地址映射:命中集/空隙/两段式/分档
* 量程守卫;③ k/b 覆盖直通 adc_cal_set(真实标定层):合法带拒绝、
* 单侧 RMW 保另一侧、回显 = 生效值视图。
* 被测对象:真实源文件(#include 纳入):run_par.c + cfg_file.c + adc_cal.c;
* dbg 计数桩(stub/dbg_log.h)+ cfg_store_* 内存虚拟盘 + adc_input_desc
* V0.1 权威映射桩。**路由顺序证据(run_par 表先 / prot_set 后)在
* test/host/iec104 TC-IEC104-029 钉(那边才有全协议栈)**。
*
* 覆盖点(TC-RUNPAR-xxx;属主登记处 = 本目录,族 TC-HOST-RUNPAR):
* TC-RUNPAR-001 init 工厂默认 = 卡 6 额定档 + idx 守卫
* TC-RUNPAR-002 死区写往返 + 分档上限(V/A/Hz)+ NaN/负值拒 + 定点量化
* TC-RUNPAR-003 落盘链:set→脏→store_save 成功清脏 + 文件含 9 键 +
* load 回读往返 + 外部改文件值生效 + 无脏 save 空操作
* TC-RUNPAR-004 104 读映射:0x8030~0x8038 命中 / 空隙与邻段 -1 / k·b 回显
* TC-RUNPAR-005 104 两段式:选择段不提交不置脏,执行段提交置脏
* TC-RUNPAR-006 k/b 直通:合法带接受与拒绝 / 非正 k 拒 / b 单侧 RMW 保 k /
* 非法带值经 load 注入后回显 = 生效值(哨兵视图)
* TC-RUNPAR-007 init 幂等:重复调用复位默认 + 清脏;重注册被引擎拒(留痕)
* TC-RUNPAR-008 引擎副本 = 生效值(无回调):load 原地改副本即时可见
*
* 通过:全部 TC PASS(main 返回 0)。
* 创建日期:2026-10-25(道D 卡 10)
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* 先桩头后生产源码(顺序敏感,同 adc_cal 套件) */
#include "dbg_log.h"
/* ===================== 内存虚拟文件系统(cfg_store_* 强符号) ===================== */
#include "cfg_file_store.h"
#define VFS_CAP 8192u
static uint8_t s_vfs[VFS_CAP];
static size_t s_vfs_len = 0u;
static uint32_t s_vfs_mtime = 100u;
static int s_vfs_exists = 0;
static int s_vfs_writes = 0;
void *cfg_store_alloc(size_t size) { return malloc(size); }
void cfg_store_free(void *p_buf) { free(p_buf); }
uint32_t cfg_store_heap_free(void) { return 0u; }
int cfg_store_read(const char *p_path, uint8_t *p_buf, size_t cap, size_t *p_nread) {
size_t n;
(void)p_path;
if (!s_vfs_exists) {
return -1;
}
n = s_vfs_len;
if (n > cap) {
n = cap;
}
(void)memcpy(p_buf, s_vfs, n);
*p_nread = n;
return 0;
}
int cfg_store_write_atomic(const char *p_path, const uint8_t *p_buf, size_t len) {
(void)p_path;
if (len > VFS_CAP) {
return -1;
}
(void)memcpy(s_vfs, p_buf, len);
s_vfs_len = len;
s_vfs_exists = 1;
s_vfs_mtime++;
s_vfs_writes++;
return 0;
}
int cfg_store_stat(const char *p_path, cfg_file_stat_t *p_st) {
(void)p_path;
if (!s_vfs_exists) {
return -1;
}
p_st->size = (uint32_t)s_vfs_len;
p_st->mtime = s_vfs_mtime;
return 0;
}
int cfg_store_remove(const char *p_path) {
(void)p_path;
s_vfs_exists = 0;
s_vfs_len = 0u;
return 0;
}
const char *cfg_store_config_path(void) { return "/config.ini"; }
const char *cfg_store_tmp_path(void) { return "/config.tmp"; }
/* ---- sys_log 桩 ---- */
#include "sys_log.h"
static int s_syslog_count = 0;
void syslog_post(syslog_id_t id, const char *desc) {
(void)id;
(void)desc;
s_syslog_count++;
}
/* ---- adc_input_desc 桩:V0.1 权威映射(docs/硬件平台/ADC通道映射说明.md) ---- */
#include "adc_input.h"
static const adc_channel_desc_t s_desc_stub[ADC_SEM_COUNT] = {
{ ADC_SRC_TPAFE, 6u }, /* UA = buf[6] */
{ ADC_SRC_STM32, 14u }, /* UB = CH14(PC4) */
{ ADC_SRC_STM32, 3u }, /* UC = CH3 */
{ ADC_SRC_TPAFE, 5u }, /* UN = buf[5] */
{ ADC_SRC_TPAFE, 2u }, /* IA = buf[2] */
{ ADC_SRC_STM32, 6u }, /* IB = CH6 */
{ ADC_SRC_STM32, 1u }, /* IC = CH1 */
{ ADC_SRC_STM32, 2u }, /* IN = CH2 */
{ ADC_SRC_STM32, 5u }, /* TEMP = CH5 */
};
const adc_channel_desc_t *adc_input_desc(adc_semantic_t ch) {
if ((int)ch < 0 || (unsigned)ch >= (unsigned)ADC_SEM_COUNT) {
return NULL;
}
return &s_desc_stub[(unsigned)ch];
}
/* dbg 计数定义(stub/dbg_log.h extern 声明) */
int g_rptest_err_count = 0;
int g_rptest_info_count = 0;
int g_rptest_debug_count = 0;
/* ===================== 测试框架(极简,同 adc_cal 套件) ===================== */
static int s_total = 0, s_passed = 0, s_failed = 0;
#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)
#define ASSERT_NEAR(a, b, eps, name) CHECK(fabsf((float)(a) - (float)(b)) <= (eps), name)
/* ===================== 被测代码 ===================== */
#include "cfg_file.c"
#include "adc_cal.c"
#include "run_par.c"
static void vfs_reset(void) {
s_vfs_len = 0u;
s_vfs_mtime = 100u;
s_vfs_exists = 0;
}
/* 全复位:引擎段复位 + run_par 复位 + 标定复位(重注册被拒 = 预期,清计数) */
static void reset_all_rp(void) {
run_par_init();
adc_cal_init();
g_rptest_err_count = 0;
}
/* ===================== 测试用例 ===================== */
static void tc_defaults(void)
{
uint8_t i;
vfs_reset();
reset_all_rp();
for (i = 0u; i < 4u; i++) {
CHECK(fabsf(run_par_deadband(i) - 0.5f) <= 1e-6f, "TC-RUNPAR-001 voltage ch factory 0.5 V");
}
for (i = 4u; i < 8u; i++) {
CHECK(fabsf(run_par_deadband(i) - 0.005f) <= 1e-9f, "TC-RUNPAR-001 current ch factory 0.005 A");
}
CHECK(fabsf(run_par_deadband(8u) - 0.5f) <= 1e-6f, "TC-RUNPAR-001 freq factory 0.5 Hz");
CHECK(run_par_deadband(9u) == 0.0f, "TC-RUNPAR-001 idx guard returns zero");
CHECK(run_par_dirty_peek() == 0u, "TC-RUNPAR-001 init leaves clean");
}
static void tc_set_range(void)
{
volatile float fnan;
vfs_reset();
reset_all_rp();
CHECK((run_par_deadband_set(0u, 0.7f) == 0)
&& (fabsf(run_par_deadband(0u) - 0.7f) <= 1e-6f),
"TC-RUNPAR-002 write round trip voltage domain");
CHECK((run_par_deadband_set(4u, 0.02f) == 0)
&& (fabsf(run_par_deadband(4u) - 0.02f) <= 1e-6f),
"TC-RUNPAR-002 write round trip current domain");
CHECK(run_par_deadband_set(0u, 50.0f) == 0, "TC-RUNPAR-002 voltage cap endpoint inclusive");
CHECK((run_par_deadband_set(0u, 50.001f) == -1)
&& (fabsf(run_par_deadband(0u) - 50.0f) <= 1e-6f),
"TC-RUNPAR-002 over voltage cap rejected");
CHECK(run_par_deadband_set(4u, 1.001f) == -1, "TC-RUNPAR-002 current cap 1 A enforced");
CHECK(run_par_deadband_set(8u, 5.001f) == -1, "TC-RUNPAR-002 freq cap 5 Hz enforced");
CHECK(run_par_deadband_set(0u, -0.001f) == -1, "TC-RUNPAR-002 negative rejected");
fnan = 0.0f;
fnan = fnan / fnan;
CHECK(run_par_deadband_set(0u, fnan) == -1, "TC-RUNPAR-002 NaN rejected");
CHECK(run_par_deadband_set(9u, 1.0f) == -1, "TC-RUNPAR-002 idx guard rejected");
CHECK((run_par_deadband_set(2u, 0.1234f) == 0)
&& (fabsf(run_par_deadband(2u) - 0.123f) <= 1e-6f),
"TC-RUNPAR-002 milli quantization rounds to nearest");
}
static void tc_persist(void)
{
char probe[24];
vfs_reset();
reset_all_rp();
CHECK(run_par_store_save() == 0, "TC-RUNPAR-003 clean save is no-op ok");
CHECK(run_par_deadband_set(2u, 0.9f) == 0, "TC-RUNPAR-003 seed write ok");
CHECK(run_par_dirty_peek() == 1u, "TC-RUNPAR-003 write marks dirty");
CHECK((run_par_store_save() == 0) && (run_par_dirty_peek() == 0u),
"TC-RUNPAR-003 save clears dirty on success");
(void)memset(probe, 0, sizeof(probe));
(void)memcpy(probe, "db_uc=", 6u);
CHECK((s_vfs_exists == 1) && (strstr((const char *)s_vfs, "db_uc=900") != NULL),
"TC-RUNPAR-003 file carries milli value");
CHECK(strstr((const char *)s_vfs, "db_freq=500") != NULL,
"TC-RUNPAR-003 all nine keys present (freq spot check)");
/* 往返:改内存 → 落盘 → 引擎 load 回读 */
CHECK(run_par_deadband_set(4u, 0.02f) == 0, "TC-RUNPAR-003 second seed");
(void)run_par_store_save();
run_par_deadband_set(4u, 0.5f); /* 覆盖内存态 */
CHECK(cfg_file_load() == 0, "TC-RUNPAR-003 engine reload ok");
CHECK(fabsf(run_par_deadband(4u) - 0.02f) <= 1e-9f,
"TC-RUNPAR-003 reload restores last saved value");
/* set(4,0.5) 是一次未落盘的本地整定 → 脏必须活过 reload(reload 不是
* 清脏事件;只有 save 成功才清) */
CHECK(run_par_dirty_peek() == 1u, "TC-RUNPAR-003 unsaved local edit survives reload");
/* 外部改档(FTP 手改 config.ini 的现场形态):load 后副本即生效值 */
{
static const char s_edited[] =
"[run_par]\ndb_ua=250\ndb_ub=500\ndb_uc=500\ndb_un=500\n"
"db_ia=10\ndb_ib=5\ndb_ic=5\ndb_in=5\ndb_freq=800\n";
(void)memcpy(s_vfs, s_edited, sizeof(s_edited) - 1u);
s_vfs_len = sizeof(s_edited) - 1u;
s_vfs_exists = 1;
s_vfs_mtime++;
}
CHECK(cfg_file_load() == 0, "TC-RUNPAR-003 edited file accepted");
CHECK((fabsf(run_par_deadband(0u) - 0.25f) <= 1e-6f)
&& (fabsf(run_par_deadband(4u) - 0.01f) <= 1e-9f)
&& (fabsf(run_par_deadband(8u) - 0.8f) <= 1e-6f),
"TC-RUNPAR-003 edited values live in copy immediately");
/* 越界文件值:引擎照收(int32 域),104/COV 读到超程值 —— 守卫在**写路径**,
* 文件通路的防线是"人工编辑 + 沉降去抖 + 可再改回";与 adc_cal 合法带不同,
* 死区超程不毁量纲(只是上送变钝),此处钉住"引擎不吞值"即可 */
CHECK(fabsf(run_par_deadband(0u) - 0.25f) <= 1e-6f, "TC-RUNPAR-003 value stable re-read");
}
static void tc_104_read(void)
{
float v;
vfs_reset();
reset_all_rp();
(void)run_par_deadband_set(0u, 0.6f);
CHECK((run_par_104_read(0x8030u, &v) == 0) && (fabsf(v - 0.6f) <= 1e-6f),
"TC-RUNPAR-004 db first slot hit");
CHECK((run_par_104_read(0x8038u, &v) == 0) && (fabsf(v - 0.5f) <= 1e-6f),
"TC-RUNPAR-004 db last slot (freq) hit");
CHECK(run_par_104_read(0x8039u, &v) == -1, "TC-RUNPAR-004 gap address not owned");
CHECK(run_par_104_read(0x8027u, &v) == -1, "TC-RUNPAR-004 prot_set alarm addr untouched");
CHECK(run_par_104_read(0x802Fu, &v) == -1, "TC-RUNPAR-004 below base not claimed");
CHECK((run_par_104_read(0x8040u, &v) == 0) && (fabsf(v - 152.587890625f) <= 1e-4f),
"TC-RUNPAR-004 k echo = adc_cal effective UA");
CHECK((run_par_104_read(0x8047u, &v) == 0) && (fabsf(v - 805.6640625f) <= 1e-4f),
"TC-RUNPAR-004 k echo last channel (IN, STM32 ROM)");
CHECK((run_par_104_read(0x8048u, &v) == 0) && (v == 0.0f),
"TC-RUNPAR-004 b echo factory zero");
CHECK(run_par_104_read(0x8050u, &v) == -1, "TC-RUNPAR-004 above b block not owned");
}
static void tc_104_two_step(void)
{
float v;
vfs_reset();
reset_all_rp();
CHECK((run_par_104_write(0x8031u, 0.8f, 1u) == 0)
&& (fabsf(run_par_deadband(1u) - 0.5f) <= 1e-6f)
&& (run_par_dirty_peek() == 0u),
"TC-RUNPAR-005 select validates without commit or dirty");
CHECK((run_par_104_write(0x8031u, 0.8f, 0u) == 0)
&& (fabsf(run_par_deadband(1u) - 0.8f) <= 1e-6f)
&& (run_par_dirty_peek() == 1u),
"TC-RUNPAR-005 execute commits and marks dirty");
CHECK(run_par_104_write(0x8031u, 60.0f, 1u) == -1, "TC-RUNPAR-005 range reject in select");
CHECK(run_par_104_write(0x8039u, 1.0f, 0u) == -2, "TC-RUNPAR-005 foreign addr passes through");
CHECK(run_par_104_write(0x8027u, 1.0f, 0u) == -2, "TC-RUNPAR-005 alarm addr passes through");
CHECK((run_par_104_read(0x8031u, &v) == 0) && (fabsf(v - 0.8f) <= 1e-6f),
"TC-RUNPAR-005 committed value readable at same addr");
}
static void tc_104_cal_proxy(void)
{
float v;
vfs_reset();
reset_all_rp();
CHECK(run_par_104_write(0x8040u, 300.0f, 0u) == 0, "TC-RUNPAR-006 k in-band accepted");
CHECK((run_par_104_read(0x8040u, &v) == 0) && (fabsf(v - 300.0f) <= 0.01f),
"TC-RUNPAR-006 k echo follows adc_cal_set");
CHECK((run_par_104_write(0x8040u, 400.0f, 0u) == -1)
&& (fabsf(adc_cal_k_uv(ADC_SEM_UA) - 300.0f) <= 0.01f),
"TC-RUNPAR-006 k over band rejected by adc_cal, RAM intact");
CHECK((run_par_104_write(0x8040u, 70.0f, 0u) == -1),
"TC-RUNPAR-006 k under band rejected (0.5x ROM)");
CHECK(run_par_104_write(0x8040u, -1.0f, 0u) == -1, "TC-RUNPAR-006 non-positive k rejected");
CHECK((run_par_104_write(0x8048u, -5.0f, 0u) == 0)
&& (fabsf(adc_cal_b_codes(ADC_SEM_UA) + 5.0f) <= 1e-3f)
&& (fabsf(adc_cal_k_uv(ADC_SEM_UA) - 300.0f) <= 0.01f),
"TC-RUNPAR-006 b single-side RMW keeps k");
CHECK(run_par_104_write(0x8048u, -9000.0f, 0u) == -1, "TC-RUNPAR-006 b over +-8192 rejected");
CHECK((run_par_104_write(0x8040u, 200.0f, 1u) == 0)
&& (fabsf(adc_cal_k_uv(ADC_SEM_UA) - 300.0f) <= 0.01f),
"TC-RUNPAR-006 k select does not touch adc_cal");
/* 生效值视图回显:文件塞越带 k → 哨兵回退 ROM → 104 回显 ROM */
{
static const char s_badk[] =
"[adc_cal]\nk_ua=99011483\nvalid=1\n";
(void)memcpy(s_vfs, s_badk, sizeof(s_badk) - 1u);
s_vfs_len = sizeof(s_badk) - 1u;
s_vfs_exists = 1;
s_vfs_mtime++;
}
CHECK(cfg_file_load() == 0, "TC-RUNPAR-006 poisoned file loaded by engine");
CHECK((run_par_104_read(0x8040u, &v) == 0) && (fabsf(v - 152.587890625f) <= 1e-4f),
"TC-RUNPAR-006 echo = effective (ROM fallback), not raw file value");
reset_all_rp();
CHECK(cfg_file_save() == 0, "TC-RUNPAR-006 reset file rewritten");
}
static void tc_init_idempotent(void)
{
int err_before;
vfs_reset();
reset_all_rp();
(void)run_par_deadband_set(1u, 0.9f);
(void)run_par_104_write(0x8040u, 200.0f, 0u);
err_before = g_rptest_err_count;
run_par_init();
CHECK(fabsf(run_par_deadband(1u) - 0.5f) <= 1e-6f, "TC-RUNPAR-007 re-init restores defaults");
CHECK(run_par_dirty_peek() == 0u, "TC-RUNPAR-007 re-init clears dirty");
CHECK(g_rptest_err_count > err_before, "TC-RUNPAR-007 dup register leaves trace");
CHECK(fabsf(run_par_deadband(0u) - 0.5f) <= 1e-6f, "TC-RUNPAR-007 values intact after dup reg");
}
static void tc_copy_is_effective(void)
{
static const char s_file[] =
"[run_par]\ndb_ua=650\ndb_ub=500\ndb_uc=500\ndb_un=500\n"
"db_ia=5\ndb_ib=5\ndb_ic=5\ndb_in=5\ndb_freq=500\n";
vfs_reset();
reset_all_rp();
(void)memcpy(s_vfs, s_file, sizeof(s_file) - 1u);
s_vfs_len = sizeof(s_file) - 1u;
s_vfs_exists = 1;
s_vfs_mtime++;
CHECK(cfg_file_load() == 0, "TC-RUNPAR-008 load ok");
CHECK(fabsf(run_par_deadband(0u) - 0.65f) <= 1e-6f,
"TC-RUNPAR-008 no cache layer: copy read is effective value");
CHECK(run_par_dirty_peek() == 0u, "TC-RUNPAR-008 load itself is not a local edit");
}
int main(void)
{
printf("=== RunPar Layer Host Tests ===\n");
tc_defaults();
tc_set_range();
tc_persist();
tc_104_read();
tc_104_two_step();
tc_104_cal_proxy();
tc_init_idempotent();
tc_copy_is_effective();
printf("=== RunPar Tests: %d/%d PASSED (failed=%d) ===\n",
s_passed, s_total, s_failed);
return (s_failed == 0) ? 0 : 1;
}