282 lines
11 KiB
C
282 lines
11 KiB
C
/*
|
||
* test_notify_ipc.c — 宿主测试:任务 #5 SDK↔IPC 桥(meow_notify_ipc.{h,c})。
|
||
*
|
||
* 用本地 AF_UNIX stub socket(回环)验证:
|
||
* T1 枚举↔字符串映射:CAT_PROGRESS→"progress"、CAT_MEDIA→"media" + media=1;
|
||
* T2 timeout 映射:-1(ONGOING)→expire=0xFFFFFFFF + ongoing=1;0(NEVER)→expire=0;
|
||
* >0→该毫秒 + ongoing=0;
|
||
* T3 回读 MSG_NOTIFY_ACK 拿 launcher 分配的 nid;replaces_id 原样透传;progress 钳位;
|
||
* T4 无 socket 降级:connect 失败 → meow_notify_ipc_send 返回 0,不崩;
|
||
* T5 dismiss:meow_notify_ipc_dismiss(nid) 发出 MSG_NOTIFY_DISMISS 帧(负载=nid)。
|
||
*
|
||
* 编译(宿主 gcc,不依赖 meow_ui.a 其余 TU):
|
||
* gcc -o test_notify_ipc test_notify_ipc.c meow_notify_ipc.c \
|
||
* -I. -I../meow_launcherd -lpthread
|
||
* 运行:./test_notify_ipc (全部 PASS 退出码 0,任一 FAIL 退出码 1)
|
||
*/
|
||
#include "meow_notify_ipc.h"
|
||
#include "meow_ui.h"
|
||
#include "meow_ipc.h" /* 帧头 / m_notify_t / m_ack_t / MSG_*(同源校验) */
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
#include <sys/socket.h>
|
||
#include <sys/un.h>
|
||
#include <pthread.h>
|
||
|
||
/* ---- 捕获:stub 服务端解析到的帧内容(mutex 保护;客户端返回后读取) ---- */
|
||
typedef struct {
|
||
uint32_t type; /* 收到的帧类型 */
|
||
m_notify_t n; /* MSG_NOTIFY 负载 */
|
||
uint32_t dismiss_id; /* MSG_NOTIFY_DISMISS 负载 */
|
||
uint32_t req_id;
|
||
uint32_t nid_to_return; /* ACK 中回填的 status */
|
||
int got; /* 已收到一帧 */
|
||
} cap_t;
|
||
static cap_t g_cap;
|
||
static pthread_mutex_t g_cap_mu = PTHREAD_MUTEX_INITIALIZER;
|
||
|
||
static void cap_store(uint32_t type, uint32_t req_id, const uint8_t *pay, uint32_t len,
|
||
uint32_t nid_to_return)
|
||
{
|
||
pthread_mutex_lock(&g_cap_mu);
|
||
memset(&g_cap, 0, sizeof g_cap);
|
||
g_cap.type = type;
|
||
g_cap.req_id = req_id;
|
||
g_cap.nid_to_return = nid_to_return;
|
||
if (type == MSG_NOTIFY && len >= sizeof(m_notify_t)) memcpy(&g_cap.n, pay, sizeof(m_notify_t));
|
||
if (type == MSG_NOTIFY_DISMISS && len >= 4) memcpy(&g_cap.dismiss_id, pay, 4);
|
||
g_cap.got = 1;
|
||
pthread_mutex_unlock(&g_cap_mu);
|
||
}
|
||
|
||
/* ---- stub 服务端线程:accept 一个连接 → 读一帧 → 校验由主线程做,这里只收 + 回 ACK ---- */
|
||
static int g_listen_fd = -1;
|
||
static void *server_thread(void *arg)
|
||
{
|
||
(void)arg;
|
||
int c = accept(g_listen_fd, NULL, NULL);
|
||
if (c < 0) return NULL;
|
||
/* 读 16B 头 */
|
||
uint8_t hdr[MEOW_IPC_HDR_LEN];
|
||
uint32_t off = 0;
|
||
while (off < sizeof hdr) {
|
||
ssize_t r = read(c, hdr + off, sizeof hdr - off);
|
||
if (r <= 0) { close(c); return NULL; }
|
||
off += (size_t)r;
|
||
}
|
||
uint32_t magic, len;
|
||
uint16_t ver, type;
|
||
memcpy(&magic, hdr, 4);
|
||
memcpy(&ver, hdr + 4, 2);
|
||
memcpy(&type, hdr + 6, 2);
|
||
memcpy(&len, hdr + 12, 4);
|
||
uint32_t req_id; memcpy(&req_id, hdr + 8, 4);
|
||
if (magic != MEOW_IPC_MAGIC || ver != MEOW_IPC_VERSION) { close(c); return NULL; }
|
||
if (len > MEOW_IPC_MAX) { close(c); return NULL; }
|
||
uint8_t pay[MEOW_IPC_MAX];
|
||
off = 0;
|
||
while (off < len) {
|
||
ssize_t r = read(c, pay + off, len - off);
|
||
if (r <= 0) { close(c); return NULL; }
|
||
off += (size_t)r;
|
||
}
|
||
cap_store(type, req_id, pay, len, (uint32_t)strtoul(getenv("TEST_NID"), NULL, 0));
|
||
|
||
/* MSG_NOTIFY → 回 m_ack_t(code=OK, status=nid_to_return);
|
||
* MSG_NOTIFY_DISMISS → 回 m_ack_t(code=OK, status=0,同 launcher 行为)。 */
|
||
if (type == MSG_NOTIFY || type == MSG_NOTIFY_DISMISS) {
|
||
m_ack_t a;
|
||
memset(&a, 0, sizeof a);
|
||
a.code = MEOW_OK;
|
||
a.status = (type == MSG_NOTIFY) ? (uint32_t)strtoul(getenv("TEST_NID"), NULL, 0) : 0;
|
||
uint8_t rhdr[MEOW_IPC_HDR_LEN];
|
||
uint32_t rmagic = MEOW_IPC_MAGIC;
|
||
uint16_t rver = MEOW_IPC_VERSION, rtype = (uint16_t)(type == MSG_NOTIFY ? MSG_NOTIFY_ACK : MSG_ACK);
|
||
uint32_t rlen = sizeof a;
|
||
memcpy(rhdr + 0, &rmagic, 4);
|
||
memcpy(rhdr + 4, &rver, 2);
|
||
memcpy(rhdr + 6, &rtype, 2);
|
||
memcpy(rhdr + 8, &req_id, 4);
|
||
memcpy(rhdr + 12, &rlen, 4);
|
||
off = 0;
|
||
while (off < sizeof rhdr) { ssize_t w = write(c, rhdr + off, sizeof rhdr - off); if (w <= 0) break; off += (size_t)w; }
|
||
off = 0;
|
||
while (off < rlen) { ssize_t w = write(c, (uint8_t*)&a + off, rlen - off); if (w <= 0) break; off += (size_t)w; }
|
||
}
|
||
close(c);
|
||
return NULL;
|
||
}
|
||
|
||
static int failures = 0;
|
||
|
||
static void check(const char *name, int cond)
|
||
{
|
||
printf("%s %s\n", cond ? "PASS" : "FAIL", name);
|
||
if (!cond) failures++;
|
||
}
|
||
|
||
/* 建 listening socket,返回 fd;路径写入 sock_path。 */
|
||
static int arb_listen(char *sock_path, size_t slen)
|
||
{
|
||
static int count = 0;
|
||
snprintf(sock_path, slen, "/tmp/meow_notify_ipc_%d_%d.sock", (int)getpid(), count++);
|
||
unlink(sock_path);
|
||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||
if (fd < 0) { perror("socket"); return -1; }
|
||
struct sockaddr_un a;
|
||
memset(&a, 0, sizeof a);
|
||
a.sun_family = AF_UNIX;
|
||
strncpy(a.sun_path, sock_path, sizeof a.sun_path - 1);
|
||
if (bind(fd, (struct sockaddr *)&a, sizeof a) < 0) { perror("bind"); close(fd); return -1; }
|
||
if (listen(fd, 1) < 0) { perror("listen"); close(fd); return -1; }
|
||
return fd;
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
setenv("MEOW_LOG_APP", "testapp", 1); /* 固定 app_id,供 stub 校验 */
|
||
|
||
/* ---------- T1/T2/T3:映射 + ACK 回 nid ---------- */
|
||
{
|
||
char sock[108];
|
||
g_listen_fd = arb_listen(sock, sizeof sock);
|
||
if (g_listen_fd < 0) { fprintf(stderr, "cannot create listener\n"); return 2; }
|
||
setenv("MEOW_IPC_SOCK", sock, 1);
|
||
setenv("TEST_NID", "0x1234", 1);
|
||
pthread_t th;
|
||
pthread_create(&th, NULL, server_thread, NULL);
|
||
|
||
meow_notify nt;
|
||
memset(&nt, 0, sizeof nt);
|
||
nt.cat = MEOW_NOTIFY_CAT_PROGRESS;
|
||
nt.urgency = MEOW_NOTIFY_URG_NORMAL;
|
||
nt.title = "下载中";
|
||
nt.body = "70%";
|
||
nt.timeout_ms = 5000; /* >0 → expire=5000, ongoing=0 */
|
||
nt.progress = 150; /* 钳位 → 100 */
|
||
uint32_t nid = meow_notify_ipc_send(&nt, 0);
|
||
pthread_join(th, NULL);
|
||
close(g_listen_fd);
|
||
|
||
check("T3 send 返回 launcher nid(0x1234)", nid == 0x1234);
|
||
pthread_mutex_lock(&g_cap_mu);
|
||
int got = g_cap.got;
|
||
const m_notify_t *cn = &g_cap.n;
|
||
pthread_mutex_unlock(&g_cap_mu);
|
||
check("T3 stub 收到 MSG_NOTIFY 帧", got && g_cap.type == MSG_NOTIFY);
|
||
check("T1 CAT_PROGRESS→category=progress", !strcmp(cn->category, "progress"));
|
||
check("T2 timeout>0→expire_timeout=5000", cn->expire_timeout == 5000);
|
||
check("T2 timeout>0→ongoing=0", cn->ongoing == 0);
|
||
check("T1 media=0(非媒体)", cn->media == 0);
|
||
check("T1 progress 钳位 150→100", cn->progress == 100);
|
||
check("T2 replaces_id 透传=0", cn->replaces_id == 0);
|
||
check("T1 app_id=testapp", !strcmp(cn->app_id, "testapp"));
|
||
check("T1 summary=下载中", !strcmp(cn->summary, "下载中"));
|
||
}
|
||
|
||
/* ---------- T1/T2:媒体类型 + ONGOING(-1) ---------- */
|
||
{
|
||
char sock[108];
|
||
g_listen_fd = arb_listen(sock, sizeof sock);
|
||
setenv("MEOW_IPC_SOCK", sock, 1);
|
||
setenv("TEST_NID", "0x9999", 1);
|
||
pthread_t th;
|
||
pthread_create(&th, NULL, server_thread, NULL);
|
||
|
||
meow_notify nt;
|
||
memset(&nt, 0, sizeof nt);
|
||
nt.cat = MEOW_NOTIFY_CAT_MEDIA;
|
||
nt.urgency = MEOW_NOTIFY_URG_CRITICAL;
|
||
nt.title = "Now Playing";
|
||
nt.timeout_ms = MEOW_NOTIFY_TIMEOUT_ONGOING; /* -1 */
|
||
uint32_t nid = meow_notify_ipc_send(&nt, 0);
|
||
pthread_join(th, NULL);
|
||
close(g_listen_fd);
|
||
|
||
check("T3 send(media/ongoing) 返回 nid(0x9999)", nid == 0x9999);
|
||
check("T1 CAT_MEDIA→category=media", !strcmp(g_cap.n.category, "media"));
|
||
check("T1 CAT_MEDIA→media=1", g_cap.n.media == 1);
|
||
check("T2 ONGOING(-1)→expire=0xFFFFFFFF", g_cap.n.expire_timeout == 0xFFFFFFFFu);
|
||
check("T2 ONGOING(-1)→ongoing=1", g_cap.n.ongoing == 1);
|
||
}
|
||
|
||
/* ---------- T2:NEVER(0) 超时 ---------- */
|
||
{
|
||
char sock[108];
|
||
g_listen_fd = arb_listen(sock, sizeof sock);
|
||
setenv("MEOW_IPC_SOCK", sock, 1);
|
||
setenv("TEST_NID", "0x1", 1);
|
||
pthread_t th;
|
||
pthread_create(&th, NULL, server_thread, NULL);
|
||
meow_notify nt;
|
||
memset(&nt, 0, sizeof nt);
|
||
nt.cat = MEOW_NOTIFY_CAT_GENERAL;
|
||
nt.title = "x";
|
||
nt.timeout_ms = MEOW_NOTIFY_TIMEOUT_NEVER; /* 0 */
|
||
(void)meow_notify_ipc_send(&nt, 0);
|
||
pthread_join(th, NULL);
|
||
close(g_listen_fd);
|
||
check("T2 NEVER(0)→expire=0 & ongoing=0",
|
||
g_cap.n.expire_timeout == 0 && g_cap.n.ongoing == 0);
|
||
check("T1 CAT_GENERAL→category=general", !strcmp(g_cap.n.category, "general"));
|
||
}
|
||
|
||
/* ---------- T3:replaces_id 透传 ---------- */
|
||
{
|
||
char sock[108];
|
||
g_listen_fd = arb_listen(sock, sizeof sock);
|
||
setenv("MEOW_IPC_SOCK", sock, 1);
|
||
setenv("TEST_NID", "0xBEEF", 1);
|
||
pthread_t th;
|
||
pthread_create(&th, NULL, server_thread, NULL);
|
||
meow_notify nt;
|
||
memset(&nt, 0, sizeof nt);
|
||
nt.cat = MEOW_NOTIFY_CAT_MESSAGE;
|
||
nt.title = "repl";
|
||
nt.timeout_ms = 0;
|
||
(void)meow_notify_ipc_send(&nt, 0x42u);
|
||
pthread_join(th, NULL);
|
||
close(g_listen_fd);
|
||
check("T3 replaces_id 透传=0x42", g_cap.n.replaces_id == 0x42u);
|
||
}
|
||
|
||
/* ---------- T4:无 socket 降级不崩 ---------- */
|
||
{
|
||
/* 指向不存在路径 */
|
||
char dead[108];
|
||
snprintf(dead, sizeof dead, "/tmp/meow_notify_ipc_dead_%d.sock", (int)getpid());
|
||
unlink(dead);
|
||
setenv("MEOW_IPC_SOCK", dead, 1);
|
||
meow_notify nt;
|
||
memset(&nt, 0, sizeof nt);
|
||
nt.cat = MEOW_NOTIFY_CAT_EMAIL;
|
||
nt.title = "no-socket";
|
||
nt.timeout_ms = 0;
|
||
uint32_t nid = meow_notify_ipc_send(&nt, 0);
|
||
check("T4 无 socket→返回 0(降级不崩)", nid == 0);
|
||
meow_notify_ipc_dismiss(0x1234); /* 无 socket 静默,不崩 */
|
||
check("T4 dismiss 无 socket 静默不崩", 1);
|
||
}
|
||
|
||
/* ---------- T5:dismiss 发 MSG_NOTIFY_DISMISS ---------- */
|
||
{
|
||
char sock[108];
|
||
g_listen_fd = arb_listen(sock, sizeof sock);
|
||
setenv("MEOW_IPC_SOCK", sock, 1);
|
||
pthread_t th;
|
||
pthread_create(&th, NULL, server_thread, NULL);
|
||
meow_notify_ipc_dismiss(0xABCDu);
|
||
pthread_join(th, NULL);
|
||
close(g_listen_fd);
|
||
check("T5 stub 收到 MSG_NOTIFY_DISMISS 帧", g_cap.got && g_cap.type == MSG_NOTIFY_DISMISS);
|
||
check("T5 dismiss 负载=nid=0xABCD", g_cap.dismiss_id == 0xABCDu);
|
||
}
|
||
|
||
printf("\n%s (%d failure%s)\n", failures ? "RESULT: FAIL" : "RESULT: PASS",
|
||
failures, failures == 1 ? "" : "s");
|
||
return failures ? 1 : 0;
|
||
}
|