/* test_settings_reader_persist.c — 阅读方向持久化宿主测试(M5 评审 P1 修复)。 * * 被测:logic/reader.c 的 meow_config("reader.dir") 持久化。 * 「新进程语义」用 reader.c 的测试钩子 mr_reader_test_reset()(-DMR_READER_TEST_HOOKS) * 模拟:清掉静态恢复标记,配置存根保留——等价于重启后首次 get 从配置恢复。 * * 覆盖: * T1 无配置默认 RTL,且 get 不写配置; * T2 set LTR 落盘 + 内存立即生效;T3 reset 后从配置恢复 LTR; * T4 set RTL 落盘 + reset 恢复 RTL; * T5 坏值("sideways")忽略 → 默认 RTL,且 get 不回写坏值; * T6 非法方向(99)忽略且不落盘; * T7 坏值后 set 合法方向会覆盖配置; * T8 配置只读一次(首次 get 后配置再变不影响本次内存态 = 快路径); * T9 写失败:切换仍生效(内存)、只打一条 WARN、配置未变。 * * 编译(tuku/ 下,SDK 仅需公共头): * gcc -Wall -Wextra -O1 -DMR_READER_TEST_HOOKS -I. -I"$SDK" \ * test/test_settings_reader_persist.c logic/reader.c test/settings_reader_stub.c \ * -o test/test_settings_reader_persist && ./test/test_settings_reader_persist */ #include "logic/reader.h" #include "test/settings_reader_stub.h" #include #include /* reader.c 在 -DMR_READER_TEST_HOOKS 下导出(正式构建无此符号,故不写进 reader.h, * 避免生产头暴露测试入口)。 */ extern void mr_reader_test_reset(void); static int g_pass, g_fail; #define CHECK(cond, ...) do { \ if (cond) { g_pass++; } \ else { g_fail++; printf("FAIL %s:%d ", __FILE__, __LINE__); printf(__VA_ARGS__); printf("\n"); } \ } while (0) static int cfg_is(const char *want) { char buf[32] = ""; if (!mr_test_cfg_get("reader.dir", buf, (int)sizeof buf)) return 0; return strcmp(buf, want) == 0; } static int cfg_has(const char *key) { char buf[32]; return mr_test_cfg_get(key, buf, (int)sizeof buf); } static void test_default_and_ltr(void) { /* T1 无配置 → 默认 RTL;get 是只读的。 */ mr_test_cfg_clear(); mr_reader_test_reset(); CHECK(mr_reader_get_direction() == MR_DIR_RTL, "T1 default RTL with no config"); CHECK(!cfg_has("reader.dir"), "T1 get does not write config"); /* T2 set LTR:内存立即生效 + 落盘 "ltr"。 */ mr_reader_set_direction(MR_DIR_LTR); CHECK(mr_reader_get_direction() == MR_DIR_LTR, "T2 memory updated immediately"); CHECK(cfg_is("ltr"), "T2 set LTR persists reader.dir=ltr"); /* T3 模拟重启 → 从配置恢复 LTR。 */ mr_reader_test_reset(); CHECK(mr_reader_get_direction() == MR_DIR_LTR, "T3 restore LTR after restart"); /* T4 反向:set RTL 落盘并恢复。 */ mr_reader_set_direction(MR_DIR_RTL); CHECK(cfg_is("rtl"), "T4 set RTL persists reader.dir=rtl"); mr_reader_test_reset(); CHECK(mr_reader_get_direction() == MR_DIR_RTL, "T4 restore RTL after restart"); } static void test_bad_values(void) { /* T5 坏值忽略 → 默认 RTL,且 get 不改写坏值。 */ mr_test_cfg_clear(); mr_test_cfg_put("reader.dir", "sideways"); mr_reader_test_reset(); CHECK(mr_reader_get_direction() == MR_DIR_RTL, "T5 bad value ignored -> default RTL"); CHECK(cfg_is("sideways"), "T5 get leaves bad value untouched"); /* T6 非法方向:忽略且不落盘。 */ mr_test_cfg_clear(); mr_reader_test_reset(); mr_reader_set_direction(99); CHECK(mr_reader_get_direction() == MR_DIR_RTL, "T6 invalid dir ignored (memory)"); CHECK(!cfg_has("reader.dir"), "T6 invalid dir writes nothing"); /* T7 坏值后 set 合法方向 → 覆盖配置并在重启后生效。 */ mr_test_cfg_clear(); mr_test_cfg_put("reader.dir", "bogus"); mr_reader_test_reset(); mr_reader_set_direction(MR_DIR_LTR); CHECK(cfg_is("ltr"), "T7 valid set overwrites bad config"); mr_reader_test_reset(); CHECK(mr_reader_get_direction() == MR_DIR_LTR, "T7 restored after overwrite"); } static void test_read_once_and_failure(void) { /* T8 配置只读一次:首次 get 之后配置变化不影响内存(快路径)。 */ mr_test_cfg_clear(); mr_reader_test_reset(); CHECK(mr_reader_get_direction() == MR_DIR_RTL, "T8 first get with no config"); mr_test_cfg_put("reader.dir", "ltr"); CHECK(mr_reader_get_direction() == MR_DIR_RTL, "T8 config read only once (fast path)"); /* T9 写失败:切换仍生效、WARN 一条、配置不变。 */ mr_test_cfg_clear(); mr_reader_test_reset(); mr_test_log_clear(); mr_test_cfg_fail_writes(1); mr_reader_set_direction(MR_DIR_LTR); CHECK(mr_reader_get_direction() == MR_DIR_LTR, "T9 switch still applies on write failure"); CHECK(mr_test_log_warns() == 1, "T9 one WARN on write failure (got %d)", mr_test_log_warns()); CHECK(!cfg_has("reader.dir"), "T9 config unchanged on failure"); mr_test_cfg_fail_writes(0); mr_reader_set_direction(MR_DIR_RTL); CHECK(cfg_is("rtl") && mr_test_log_warns() == 1, "T9 recovery writes rtl, no extra WARN"); } int main(void) { test_default_and_ltr(); test_bad_values(); test_read_once_and_failure(); printf("%s: %d passed, %d failed\n", g_fail ? "FAIL" : "PASS", g_pass, g_fail); return g_fail ? 1 : 0; }