Files

255 lines
9.7 KiB
C
Raw Permalink 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.
/* test_notif_tf.c — 宿主单测:通知#6 TF 转储(CSV 转义 / 10MB 滚动截断 / init 清板载+内存表)。
*
* 直接 #include "meow_notif.c"(同 TU 平移其 static 常量/函数),stub 掉 meow_settings_get_int
* (配额读——返回负=键缺失即用默认,涵盖 CSV/截断/init 逻辑)。宿主 gcc 编译运行即可,不进固件。
*
* 运行:gcc -D_GNU_SOURCE -o test_notif_tf test_notif_tf.c && ./test_notif_tf
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
/* stub:meow_notif.c 的 read_quota() 读 notify.* 配额。<0 => 键缺失=> 用默认值 */
int meow_settings_get_int(const char *key, int *out)
{
(void)key; (void)out;
return -2; /* MEOW_ERR_NOENT */
}
#include "meow_notif.c" /* 把全部实现(含 statics)收进本 TU */
static int g_fail = 0;
#define CHECK(cond, msg) do { \
if (!(cond)) { fprintf(stderr, "FAIL: %s (%s:%d)\n", msg, __FILE__, __LINE__); g_fail++; } \
else fprintf(stderr, "ok: %s\n", msg); \
} while (0)
static char *read_all(const char *path, long *out_len)
{
FILE *f = fopen(path, "rb");
if (!f) return NULL;
fseek(f, 0, SEEK_END);
long n = ftell(f);
fseek(f, 0, SEEK_SET);
char *s = malloc((size_t)n + 1);
if (!s) { fclose(f); return NULL; }
size_t got = fread(s, 1, (size_t)n, f);
s[got] = 0;
fclose(f);
if (out_len) *out_len = n;
return s;
}
static void make_notif(meow_notif_t *n, uint32_t id, const char *sum, const char *body)
{
memset(n, 0, sizeof *n);
n->id = id;
n->created_ms = 1234567890123ULL;
strcpy(n->app_id, "com.app");
strcpy(n->category, "general");
strcpy(n->summary, sum);
strcpy(n->body, body);
strcpy(n->target_activity, "act");
n->urgency = 0; /* 0=LOW(缺省值) */
n->progress = -1; /* 无进度 */
n->ongoing = 1;
}
/* 回读指定 summary 的活跃通知(meow_notif_list 回调;按 g_find_key 匹配) */
static meow_notif_t g_got; static int g_got_found = 0; static char g_find_key[64];
static int find_cb(const meow_notif_t *n, void *ctx)
{
(void)ctx;
if (n && strcmp(n->summary, g_find_key) == 0) { g_got = *n; g_got_found = 1; return 1; }
return 0;
}
static meow_notif_t find_notif(const char *summary)
{
g_got_found = 0; memset(&g_got, 0, sizeof g_got);
snprintf(g_find_key, sizeof g_find_key, "%s", summary);
meow_notif_list(NULL, find_cb, NULL);
return g_got_found ? g_got : (meow_notif_t){0};
}
/* 图标回退 stub:com.app → "app_icon_name";其他 app → 无 */
static int stub_icon_cb(const char *app_id, char *out, size_t outsz)
{
if (app_id && !strcmp(app_id, "com.app")) { snprintf(out, outsz, "app_icon_name"); return 1; }
return 0;
}
/* dump_cb 类型是 void,而 meow_notif_dump_tf 返回 int——包一层(同 launcher notif_dump_tf_placeholder) */
static void dump_tf_wrapper(const meow_notif_t *n){ (void)meow_notif_dump_tf(n); }
/* --- 1) CSV 转义 + 去换行 --- */
static void test_csv_escape(const char *path)
{
meow_notif_set_tf_path(path);
meow_notif_set_tf_max(1024 * 1024); /* 放大上限避免此测触发截断 */
meow_notif_t n;
/* summary: 逗号 + 双引号 + 换行;body: CRLF + tab */
make_notif(&n, 42, "Hel\"lo, world\nline2", "Body\r\nwith\ttab");
n.media = 1; /* 让 media 列=1 */
CHECK(meow_notif_dump_tf(&n) == 0, "dump_tf returns 0");
char *s = read_all(path, NULL);
CHECK(s != NULL, "file exists after dump");
const char *expect =
"1234567890123,42,\"com.app\",\"\",\"general\",0,-1,\"Hel\"\"lo, world line2\","
"\"Body with\ttab\",\"act\",1,1,0\n"; /* app_icon="" urgency=0 progress=-1 ongoing=1 media=1 dismissed=0 */
CHECK(s && strcmp(s, expect) == 0, "CSV line matches expected (escape+newline-strip+new cols)");
if (s) { fprintf(stderr, " got: %s", s); fprintf(stderr, " exp: %s", expect); }
free(s);
}
/* --- 2) 10MB 滚动截断(假文件:把上限压到很小模拟)--- */
static void test_trim(const char *path)
{
meow_notif_set_tf_path(path);
meow_notif_set_tf_max(256); /* 用 256B 模拟 10MB 上限 */
/* 先写 4 条不同标记,让文件超过 256B */
meow_notif_t n;
make_notif(&n, 1, "lineAAAA", "");
meow_notif_dump_tf(&n);
make_notif(&n, 2, "lineBBBB", "");
meow_notif_dump_tf(&n);
make_notif(&n, 3, "lineCCCC", "");
meow_notif_dump_tf(&n);
make_notif(&n, 4, "lineDDDD", "");
meow_notif_dump_tf(&n);
long len = 0;
char *s = read_all(path, &len);
CHECK(s != NULL && len <= 256, "after roll, file size <= limit (10MB analog)");
CHECK(s && strstr(s, "lineDDDD") != NULL, "newest row retained after roll");
CHECK(s && strstr(s, "lineAAAA") == NULL, "oldest row rolled away");
if (s) fprintf(stderr, " rolled content (%ldB):\n%s", len, s);
/* 追加一条仍 0 成功 */
make_notif(&n, 5, "lineEEEE", "");
CHECK(meow_notif_dump_tf(&n) == 0, "append after roll ok");
free(s);
}
/* --- 3) init 清板载 + 清内存表,且 post 正常 --- */
static void test_init_clear(const char *path)
{
meow_notif_set_tf_path(path);
meow_notif_set_tf_max(1024 * 1024);
/* 先塞点板载内容 */
meow_notif_t n;
make_notif(&n, 99, "junk", "");
meow_notif_dump_tf(&n);
/* init:应清板载文件 + 清内存表 */
CHECK(meow_notif_init() == NOTIF_OK, "meow_notif_init ok");
long len = -1;
char *s = read_all(path, &len);
CHECK(s != NULL && len == 0, "board log cleared at init (dir kept)");
CHECK(meow_notif_count(NULL) == 0, "memory table cleared at init");
/* init 后 post 一条,应活跃 + 可转储 */
make_notif(&n, 0, "fresh", "");
uint32_t id = 0;
CHECK(meow_notif_post(&n, &id) == NOTIF_OK, "post after init ok");
CHECK(id != 0 && meow_notif_count(NULL) == 1, "one active notif after post");
free(s);
}
/* --- 4) icon 回退:app_icon 空 → 按 app_id 用该 app 默认图标;非空 → 保留 --- */
static void test_icon_fallback(const char *path)
{
meow_notif_set_icon_cb(stub_icon_cb);
meow_notif_before_poweroff(); /* 清内存表,回到干净态(dump_cb=NULL 不落盘) */
meow_notif_set_tf_path(path);
meow_notif_set_tf_max(1024 * 1024);
/* 空 app_icon → 回退 app 图标 */
meow_notif_t n;
make_notif(&n, 0, "icon-test", "");
uint32_t id = 0;
CHECK(meow_notif_post(&n, &id) == NOTIF_OK, "icon-fallback post ok");
meow_notif_t got = find_notif("icon-test");
CHECK(got.id != 0 && !strcmp(got.app_icon, "app_icon_name"), "empty app_icon → app icon fallback");
/* 非空 app_icon → 独立图标(不回退) */
make_notif(&n, 0, "icon-custom", "");
n.app_icon[0] = 0; strcpy(n.app_icon, "custom/icon.png");
CHECK(meow_notif_post(&n, &id) == NOTIF_OK, "icon-custom post ok");
got = find_notif("icon-custom");
CHECK(got.id != 0 && !strcmp(got.app_icon, "custom/icon.png"), "non-empty app_icon kept (per-notif icon)");
}
/* --- 5) progress 实时(仅内存)vs 里程碑(100% 落盘 + 释放) --- */
static void test_progress_live(const char *path)
{
meow_notif_set_icon_cb(NULL);
meow_notif_set_dump_cb(NULL);
meow_notif_before_poweroff(); /* 清内存表,回到干净态 */
meow_notif_set_tf_path(path);
meow_notif_set_tf_max(1024 * 1024);
meow_notif_set_dump_cb(dump_tf_wrapper); /* 归档即写 CSV;验证落盘快照 */
meow_notif_t n;
make_notif(&n, 0, "live", "");
n.ongoing = 0; /* 非 ONGOING,便于验证 100% 释放 */
n.progress = 0;
uint32_t id = 0;
CHECK(meow_notif_post(&n, &id) == NOTIF_OK, "progress post ok");
CHECK(meow_notif_count(NULL) == 1, "one active after post");
/* 实时 <100%:仅内存,不落盘 */
CHECK(meow_notif_update_progress(id, 70) == NOTIF_OK, "update_progress(70) ok");
CHECK(meow_notif_count(NULL) == 1, "still active after live update (not dumped)");
long len = 0; char *s = read_all(path, &len);
CHECK((s == NULL || len == 0), "no CSV written on live update (<100%)");
free(s);
/* 里程碑 100%:立即归档落盘 + 释放非 ONGOING 槽 */
CHECK(meow_notif_update_progress(id, 100) == NOTIF_OK, "update_progress(100) ok");
CHECK(meow_notif_count(NULL) == 0, "slot released after 100% milestone");
s = read_all(path, &len);
CHECK(s != NULL && len > 0, "CSV written on 100% milestone");
CHECK(s && strstr(s, ",100,\"live\"") != NULL, "milestone CSV has progress=100 title=live");
if (s) fprintf(stderr, " milestone row: %s", s);
free(s);
/* 已释放 id → NOENT */
CHECK(meow_notif_update_progress(id, 50) == NOTIF_ERR_NOENT, "update on released id -> NOENT");
meow_notif_set_dump_cb(NULL); /* 复位 */
}
int main(void)
{
char tmp[] = "/tmp/meow_notif_tf_test_XXXXXX";
char *dir = mkdtemp(tmp);
if (!dir) { perror("mkdtemp"); return 2; }
char p1[512], p2[512], p3[512], p4[512], p5[512];
snprintf(p1, sizeof p1, "%s/csv.log", dir);
snprintf(p2, sizeof p2, "%s/trim.log", dir);
snprintf(p3, sizeof p3, "%s/init.log", dir);
snprintf(p4, sizeof p4, "%s/icon.log", dir);
snprintf(p5, sizeof p5, "%s/prog.log", dir);
CHECK(p1[0] != 0, "tmpdir ready");
test_csv_escape(p1);
test_trim(p2);
test_init_clear(p3);
test_icon_fallback(p4);
test_progress_live(p5);
/* 清理:删除日志文件 + 目录(保底) */
unlink(p1); unlink(p2); unlink(p3); unlink(p4); unlink(p5); rmdir(dir);
fprintf(stderr, "\n%s (failures=%d)\n", g_fail ? "== TEST FAILED ==" : "== TEST PASSED ==", g_fail);
return g_fail ? 1 : 0;
}