launcher a42171a1→ddfe616e; 宿主 test_media ALL PASS + qemu 端到端(META注册→PLAYCTL下发闭环) 出入: 卡片以媒体会话为权威非通知; PLAYCTL 走会话fd(断连防御留后续)
170 lines
6.7 KiB
C
170 lines
6.7 KiB
C
/* test_media.c — 宿主单测:meow_media 媒体会话(阶段D附录B)。
|
||
*
|
||
* 验证:
|
||
* 1) META 上报 → 注册会话,current()=最近上报者;
|
||
* 2) 最近活跃切换(两个源,后上报者胜);
|
||
* 3) has_source()(state=0 停止 → 0);
|
||
* 4) PLAYCTL 下发字节格式(socketpair 对端可读回 MSG_PLAYCTL 帧,cmd/seek 正确);
|
||
* 5) meow_media_playctl 无源 → -1;
|
||
* 6) clear(app) 清会话 → current()=NULL。
|
||
* 只依赖 meow_media.c + meow_ipc.h(纯 C),宿主 gcc 直编。
|
||
*/
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <stdint.h>
|
||
#include <unistd.h>
|
||
#include <sys/socket.h>
|
||
#include "meow_media.h"
|
||
#include "meow_ipc.h"
|
||
|
||
static int g_fails = 0;
|
||
#define CHECK(cond, msg) do { \
|
||
if (cond) printf("PASS: %s\n", msg); \
|
||
else { printf("FAIL: %s\n", msg); g_fails++; } } while (0)
|
||
|
||
/* ops.send:把帧写到 fd(socketpair 一端)。 */
|
||
static size_t test_send(int fd, uint16_t type, uint32_t req_id,
|
||
const void *payload, uint32_t len)
|
||
{
|
||
uint8_t hdr[MEOW_IPC_HDR_LEN];
|
||
uint32_t magic = MEOW_IPC_MAGIC;
|
||
uint16_t ver = MEOW_IPC_VERSION;
|
||
memcpy(hdr + 0, &magic, 4);
|
||
memcpy(hdr + 4, &ver, 2);
|
||
memcpy(hdr + 6, &type, 2);
|
||
memcpy(hdr + 8, &req_id, 4);
|
||
memcpy(hdr + 12, &len, 4);
|
||
size_t off = 0;
|
||
while (off < sizeof hdr) {
|
||
ssize_t w = write(fd, hdr + off, sizeof hdr - off);
|
||
if (w <= 0) return off;
|
||
off += (size_t)w;
|
||
}
|
||
if (len && payload) {
|
||
size_t p = 0;
|
||
while (p < len) {
|
||
ssize_t w = write(fd, (const uint8_t *)payload + p, len - p);
|
||
if (w <= 0) break;
|
||
p += (size_t)w;
|
||
}
|
||
off += p;
|
||
}
|
||
return off;
|
||
}
|
||
|
||
static void fill_meta(m_meta_t *m, const char *app, uint32_t state, uint32_t pos, uint32_t dur,
|
||
const char *title, const char *artist)
|
||
{
|
||
memset(m, 0, sizeof *m);
|
||
snprintf(m->app_id, sizeof m->app_id, "%s", app);
|
||
m->state = state; m->pos_ms = pos; m->dur_ms = dur;
|
||
snprintf(m->title, sizeof m->title, "%s", title);
|
||
snprintf(m->artist, sizeof m->artist, "%s", artist);
|
||
}
|
||
|
||
/* 从 fd 读一帧(头+负载),校验 magic/ver,返回负载类型;不足返回 -1。 */
|
||
static int read_frame(int fd, uint16_t *type, uint32_t *req_id, uint8_t *rbuf, uint32_t rcap)
|
||
{
|
||
uint8_t rhdr[MEOW_IPC_HDR_LEN];
|
||
size_t off = 0;
|
||
while (off < sizeof rhdr) {
|
||
ssize_t r = read(fd, rhdr + off, sizeof rhdr - off);
|
||
if (r <= 0) return -1;
|
||
off += (size_t)r;
|
||
}
|
||
uint32_t magic, len; uint16_t ver;
|
||
memcpy(&magic, rhdr, 4); memcpy(&ver, rhdr + 4, 2); memcpy(type, rhdr + 6, 2);
|
||
memcpy(req_id, rhdr + 8, 4); memcpy(&len, rhdr + 12, 4);
|
||
if (magic != MEOW_IPC_MAGIC || ver != MEOW_IPC_VERSION) return -1;
|
||
if (len > rcap) len = rcap;
|
||
off = 0;
|
||
while (off < len) {
|
||
ssize_t r = read(fd, rbuf + off, len - off);
|
||
if (r <= 0) return -1;
|
||
off += (size_t)r;
|
||
}
|
||
return (int)len;
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
meow_media_ops ops;
|
||
ops.send = test_send;
|
||
ops.log = NULL; /* 静默 */
|
||
meow_media_init(&ops);
|
||
|
||
int sv[2];
|
||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0) { perror("socketpair"); return 1; }
|
||
/* sv[0]=会话 fd(launcher 下发端);sv[1]=测试读回端(模拟 App)。 */
|
||
|
||
/* 1) 单源注册 */
|
||
m_meta_t m;
|
||
fill_meta(&m, "com.ncm", MEOW_MEDIA_PLAYING, 120000, 240000, "Song A", "Artist A");
|
||
CHECK(meow_media_meta(&m, sv[0]) == 0, "meta registers source");
|
||
const meow_media_session_t *cur = meow_media_current();
|
||
CHECK(cur && !strcmp(cur->app_id, "com.ncm"), "current() == com.ncm");
|
||
CHECK(cur && cur->state == MEOW_MEDIA_PLAYING, "state == PLAYING");
|
||
CHECK(meow_media_has_source() == 1, "has_source == 1 (playing)");
|
||
|
||
/* 2) 多源最近活跃切换 */
|
||
fill_meta(&m, "com.player2", MEOW_MEDIA_PLAYING, 0, 180000, "Song B", "Artist B");
|
||
CHECK(meow_media_meta(&m, sv[0]) == 0, "meta registers second source");
|
||
cur = meow_media_current();
|
||
CHECK(cur && !strcmp(cur->app_id, "com.player2"), "current() == com.player2 (newest)");
|
||
fill_meta(&m, "com.ncm", MEOW_MEDIA_PLAYING, 30000, 240000, "Song A", "Artist A");
|
||
meow_media_meta(&m, sv[0]);
|
||
cur = meow_media_current();
|
||
CHECK(cur && !strcmp(cur->app_id, "com.ncm"), "current() == com.ncm after re-report (newest)");
|
||
|
||
/* 3) state=0 停止 → has_source=0(但会话仍在,current() 仍指向它) */
|
||
fill_meta(&m, "com.ncm", MEOW_MEDIA_STOPPED, 0, 0, "Song A", "Artist A");
|
||
meow_media_meta(&m, sv[0]);
|
||
cur = meow_media_current();
|
||
CHECK(cur && !strcmp(cur->app_id, "com.ncm"), "current() still == com.ncm (stopped)");
|
||
CHECK(meow_media_has_source() == 0, "has_source == 0 (stopped)");
|
||
|
||
/* 恢复播放,准备 PLAYCTL 下发验证 */
|
||
fill_meta(&m, "com.ncm", MEOW_MEDIA_PLAYING, 1000, 240000, "Song A", "Artist A");
|
||
meow_media_meta(&m, sv[0]);
|
||
|
||
/* 4) PLAYCTL 下发:cmd=1(播放),读回端应收到 MSG_PLAYCTL/第11号 req */
|
||
CHECK(meow_media_playctl(1, 0) == 0, "playctl sends (cmd=play)");
|
||
uint8_t rbuf[512];
|
||
uint16_t type; uint32_t req;
|
||
int n = read_frame(sv[1], &type, &req, rbuf, sizeof rbuf);
|
||
CHECK(n == (int)sizeof(m_playctl_t), "downlink frame payload == m_playctl_t size");
|
||
CHECK(type == MSG_PLAYCTL, "downlink type == MSG_PLAYCTL");
|
||
m_playctl_t c;
|
||
memcpy(&c, rbuf, sizeof c);
|
||
CHECK(c.cmd == 1 && c.seek_ms == 0, "downlink cmd=1 seek=0");
|
||
|
||
/* 5) seek:cmd=5 seek=12345 */
|
||
CHECK(meow_media_playctl(5, 12345) == 0, "playctl sends (cmd=seek)");
|
||
n = read_frame(sv[1], &type, &req, rbuf, sizeof rbuf);
|
||
memcpy(&c, rbuf, sizeof c);
|
||
CHECK(type == MSG_PLAYCTL && c.cmd == 5 && c.seek_ms == 12345, "downlink seek cmd=5 seek=12345");
|
||
|
||
/* playctl_to 目标 app(另一会话) */
|
||
CHECK(meow_media_playctl_to("com.player2", 3, 0) == 0, "playctl_to player2 sends");
|
||
n = read_frame(sv[1], &type, &req, rbuf, sizeof rbuf);
|
||
memcpy(&c, rbuf, sizeof c);
|
||
CHECK(type == MSG_PLAYCTL && c.cmd == 3, "downlink to player2 cmd=3");
|
||
CHECK(meow_media_playctl_to("no.such.app", 1, 0) == -1, "playctl_to unknown app == -1");
|
||
|
||
/* 6) 无源:清全部 → playctl -1 / has_source 0 */
|
||
meow_media_clear(NULL);
|
||
CHECK(meow_media_current() == NULL, "current() == NULL after clear all");
|
||
CHECK(meow_media_playctl(1, 0) == -1, "playctl no source == -1");
|
||
CHECK(meow_media_has_source() == 0, "has_source == 0 after clear");
|
||
|
||
close(sv[0]); close(sv[1]);
|
||
|
||
/* 7) 空 app_id 拒绝 */
|
||
fill_meta(&m, "", MEOW_MEDIA_PLAYING, 0, 0, "x", "y");
|
||
CHECK(meow_media_meta(&m, sv[0]) == -1, "meta empty app_id == -1");
|
||
|
||
printf("\n%s (%d fails)\n", g_fails ? "FAILED" : "ALL PASS", g_fails);
|
||
return g_fails ? 1 : 0;
|
||
}
|