/* test_meow_cachegov.c — 宿主测试:SDK meow_cachegov(A:DONTNEED L0/L1 + refault 回退)。 * * 背景(OOM_PAGECACHE_GOVERNANCE §2/§4,③混合):A 的 DONTNEED/read_pos 热路径随 SDK 库; * 本测试用宿主桩: * - 真实 posix_fadvise(文件数据完整性校验:drop 后文件内容不变、drop_finished 可再读); * - meow_log 桩捕获 "[cachegov] L1 drop off=.. len=.." 断言丢弃范围([0, read_pos-keep_hot)); * - meow_cachegov_test_set_time_ms() 注入时钟驱动 refault 滚窗回退(放大 keep_hot→降L0→关A); * - MEOW_CACHEGOV_C_FILE 指向临时文件写入 fake C 状态,断言 download_paused/prefetch_paused。 * * 编译(patch_v2/sdk 目录下): * gcc -Os -Wall -Wextra -I. -Iinclude -I../meow_launcherd -c meow_cachegov.c -o /tmp/cg.o * gcc -Os -Wall -Wextra -I. -Iinclude -I../meow_launcherd test_meow_cachegov.c /tmp/cg.o -lpthread -o test_meow_cachegov * 运行:./test_meow_cachegov (exit 0=PASS / 1=FAIL) */ #define _GNU_SOURCE #include "meow_cachegov.h" #include #include #include #include #include #include #include /* ---- meow_log 桩:捕获最近一条 [cachegov] 行,供断言 ---- */ static char g_last_log[512]; void meow_log(int lvl, const char *fmt, ...) { (void)lvl; va_list ap; va_start(ap, fmt); vsnprintf(g_last_log, sizeof g_last_log, fmt, ap); va_end(ap); printf(" [cachegov.log] %s\n", g_last_log); } static int log_has(const char *needle) { return strstr(g_last_log, needle) != NULL; } static int g_pass = 0, g_fail = 0; static void check(const char *name, int cond) { if (cond) { g_pass++; printf("PASS %s\n", name); } else { g_fail++; printf("FAIL %s\n", name); } } /* ---- read_pos 桩:由测试驱动当前读指针 ---- */ static uint64_t g_pos; static uint64_t fake_read_pos(void *ctx) { (void)ctx; return g_pos; } /* 建一个 size 字节的临时文件并填充确定性内容。 */ static int make_file(const char *path, size_t size) { int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) return -1; for (size_t off = 0; off < size; off += 4096) { char tmp[4096]; memset(tmp, (int)(off / 4096) & 0xFF, sizeof tmp); size_t n = (size - off) < sizeof tmp ? (size - off) : sizeof tmp; if (write(fd, tmp, n) != (ssize_t)n) { close(fd); return -1; } } close(fd); return 0; } /* 校验文件内容仍为确定性模式(drop 后数据完整)。 */ static int verify_file(const char *path, size_t size) { int fd = open(path, O_RDONLY); if (fd < 0) return 0; char tmp[4096]; size_t off = 0; int ok = 1; while (off < size) { size_t n = (size - off) < sizeof tmp ? (size - off) : sizeof tmp; ssize_t r = read(fd, tmp, n); if (r != (ssize_t)n) { ok = 0; break; } char expect[4096]; memset(expect, (int)(off / 4096) & 0xFF, n); if (memcmp(tmp, expect, n) != 0) { ok = 0; break; } off += (size_t)n; } close(fd); return ok; } int main(void) { char path[320], cfile[360]; snprintf(path, sizeof path, "/tmp/meow_cg_%ld.bin", (long)getpid()); snprintf(cfile, sizeof cfile, "/tmp/meow_cg_%ld.state", (long)getpid()); setenv("MEOW_CACHEGOV_C_FILE", cfile, 1); unlink(path); unlink(cfile); const size_t size = 1024u * 1024u; /* 1MB 测试文件 */ check("make_file", make_file(path, size) == 0); /* 1) cfg:默认 + set/get 往返 */ struct meow_cachegov_cfg c = {0}; meow_cachegov_get_cfg(&c); check("cfg default enabled", c.enabled == 1); check("cfg default keep_hot=256KB", c.keep_hot_bytes == 256u * 1024u); struct meow_cachegov_cfg c2 = c; c2.keep_hot_bytes = 128u * 1024u; c2.drop_mode = 1; c2.refault_backoff = 2; check("set_cfg", meow_cachegov_set_cfg(&c2) == 0); meow_cachegov_get_cfg(&c); check("get_cfg roundtrip", c.keep_hot_bytes == 128u * 1024u && c.drop_mode == 1 && c.refault_backoff == 2); /* 2) L1 drop:read_pos=512KB、keep_hot=128KB → drop [0, 384KB) off=0 len=384KB */ check("register", meow_cachegov_register(path, size, fake_read_pos, NULL) == 0); g_pos = 512u * 1024u; meow_cachegov_tick(); check("L1 drop off=0 len=384KB", log_has("L1 drop") && strstr(g_last_log, "off=0 len=393216")); check("L1 range bounded by read_pos-keep_hot (no L2)", !strstr(g_last_log, "off=393216") && strstr(g_last_log, "len=393216")); /* 3) L1 推进:read_pos=800KB → drop [384KB, 800-128KB=672KB) off=384KB len=288KB */ g_pos = 800u * 1024u; meow_cachegov_tick(); check("L1 drop off=384KB len=288KB", log_has("L1 drop") && strstr(g_last_log, "off=393216") && strstr(g_last_log, "len=294912")); /* 4) refault 回退档1(放大 keep_hot 翻倍):3 次触发 2 次 → 0→1(每升降一档需间隔 ≥10s */ meow_cachegov_test_set_time_ms(0); meow_cachegov_note_refault(); meow_cachegov_note_refault(); meow_cachegov_tick(); check("backoff -> 1 (refault*2)", meow_cachegov_backoff_level() == 1); check("file intact after backoff1", verify_file(path, size)); /* 档1 且 keep_hot=128KB*2=256KB:read_pos=1000KB → drop 上界=744KB(已丢到 672KB) */ g_pos = 1000u * 1024u; meow_cachegov_tick(); check("backoff1 L1 keep_hot enlarged (no L2)", meow_cachegov_backoff_level() == 1); /* 5) 再 2 次(间隔 10s)→ 档2(L1 关,只 L0)→ download_paused=1 */ meow_cachegov_test_set_time_ms(11000); meow_cachegov_note_refault(); meow_cachegov_note_refault(); meow_cachegov_tick(); check("backoff -> 2 (L1 off)", meow_cachegov_backoff_level() == 2); g_pos = 900u * 1024u; meow_cachegov_tick(); /* L1 off:应无新的 L1 drop 日志 */ check("level2 no L1 drop (only L0)", !log_has("L1 drop")); check("download_paused @backoff>=2", meow_cachegov_download_paused() == 1); /* 6) 再 2 次(间隔 10s)→ 档3(A 全关)→ prefetch_paused=1 + drop_finished 不再丢页 */ meow_cachegov_test_set_time_ms(22000); meow_cachegov_note_refault(); meow_cachegov_note_refault(); meow_cachegov_tick(); check("backoff -> 3 (A off)", meow_cachegov_backoff_level() == 3); check("prefetch_paused @backoff>=3", meow_cachegov_prefetch_paused() == 1); meow_cachegov_test_set_time_ms(23000); meow_cachegov_drop_finished(path); check("drop_finished skip @A-off", log_has("drop_finished skip")); check("file intact after A-off", verify_file(path, size)); /* 7) C 侧降载:write fake C state dl_pause=1;滚窗干净逐级降回 0 后 download_paused 仍 =1 */ { FILE *f = fopen(cfile, "w"); check("write C state", f != NULL); if (f) { fputs("cached_kb=35000\ndirty_kb=1000\ndl_pause=1\nprefetch_pause=0\n", f); fclose(f); } } meow_cachegov_test_set_time_ms(33000); meow_cachegov_tick(); /* 3→2 */ meow_cachegov_test_set_time_ms(44000); meow_cachegov_tick(); /* 2→1 */ meow_cachegov_test_set_time_ms(55000); meow_cachegov_tick(); /* 1→0 */ check("backoff de-escalated to 0", meow_cachegov_backoff_level() == 0); check("download_paused via C file", meow_cachegov_download_paused() == 1); /* 8) L0 drop_finished(A 正常态):整文件 DONTNEED + 数据完整 + 注销条目 */ unlink(cfile); meow_cachegov_register(path, size, fake_read_pos, NULL); meow_cachegov_test_set_time_ms(66000); meow_cachegov_drop_finished(path); check("drop_finished L0 log", log_has("drop_finished L0")); check("file intact after L0", verify_file(path, size)); check("unregister removes entry", meow_cachegov_unregister(path) != 0); /* 已注销 → 找不到 */ /* 9) disable 时 drop/tick 均 no-op:数据仍完整 */ c2.enabled = 0; meow_cachegov_set_cfg(&c2); meow_cachegov_register(path, size, fake_read_pos, NULL); g_pos = 512u * 1024u; meow_cachegov_tick(); meow_cachegov_drop_finished(path); check("file intact when disabled", verify_file(path, size)); unlink(path); unlink(cfile); printf("----\n%d passed, %d failed\n", g_pass, g_fail); return g_fail ? 1 : 0; }