Files
MeowOS-SDK/sdk/test_ui_audio_host.c
T
AskaEth 432875d4e6 sdk: 宿主测试头注释按实测修订 + select T1 探针修正
- test_meow_ui_select: T1 选中高亮探针移到容器卡内(x=12),
  适配 M6.37 起的 listview 默认容器模式;6/6 PASS
- 各 test_*.c 头注释补 -Iinclude、meow_ui_nav.c 及精简源列表最小补集
  (theme/widgets/notif_gesture/overlay/suppress/sdk_fixes/pinyin/ime_fix)
- test_sb_statusbar 头注释给出可链接命令(--allow-multiple-definition)
- notify_post/ui_audio_host/p1p2p3 标注需主仓组件,公开仓跳过
- docs/APP_DEV_GUIDE.md: 去掉叠字与过强表述,改为 34/38 实测口径
2026-11-04 20:26:35 +08:00

160 lines
6.1 KiB
C
Raw 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.
#define _GNU_SOURCE
/* test_ui_audio_host.c — meow_ui_audio SDK 客户端宿主测试。
*
* 验证:
* - meow_audio_open/write/query/pause/resume/close 全链(走 audiod FILE sink)
* - on_preempt 回调:第二客户端(raw)抢先 → 本 SDK 客户端收到 PREEMPTED,write 返回负值
*
* 启动被测试 audiod(宿主二进制,FILE sink)。
* **需主仓组件,公开仓跳过**:运行需 ../meow_audiod/meow_audiod_host(未随本仓发布)。
* 编译:cc -I. -Iinclude -Os -Wall -Wextra test_ui_audio_host.c meow_ui_audio.c -lpthread -lm -o test_ui_audio_host
*/
#include "meow_ui.h"
#include "meow_ipc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <math.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#define TSOCK "/tmp/meow_audio_ui.sock"
#define TWAV "/tmp/meow_audio_ui.wav"
static int g_pass = 0, g_fail = 0;
static void check(int c, const char *n) { if (c) { printf(" PASS %s\n", n); g_pass++; } else { printf(" FAIL %s\n", n); g_fail++; } }
static int g_preempted = 0;
static char g_by[64];
static void preempt_cb(const char *by_app, void *ctx)
{
(void)ctx;
g_preempted = 1;
snprintf(g_by, sizeof g_by, "%s", by_app ? by_app : "?");
}
/* 小工具:raw 客户端连接 */
static int raw_connect(void)
{
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) return -1;
struct sockaddr_un a;
memset(&a, 0, sizeof a);
a.sun_family = AF_UNIX;
snprintf(a.sun_path, sizeof a.sun_path, "%s", TSOCK);
if (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) { close(fd); return -1; }
return fd;
}
static int w_all(int fd, const void *p, size_t n)
{
const uint8_t *b = p; size_t o = 0;
while (o < n) { ssize_t r = write(fd, b + o, n - o); if (r < 0) { if (errno == EINTR) continue; return -1; } o += (size_t)r; }
return 0;
}
static int r_all(int fd, void *p, size_t n)
{
uint8_t *b = p; size_t o = 0;
while (o < n) { ssize_t r = read(fd, b + o, n - o); if (r <= 0) return -1; o += (size_t)r; }
return 0;
}
static int sf(int fd, uint16_t type, uint32_t req, const void *pl, uint32_t len)
{
uint8_t hdr[MEOW_IPC_HDR_LEN];
uint32_t magic = MEOW_IPC_MAGIC; uint16_t ver = MEOW_IPC_VERSION;
memcpy(hdr, &magic, 4); memcpy(hdr + 4, &ver, 2); memcpy(hdr + 6, &type, 2); memcpy(hdr + 8, &req, 4); memcpy(hdr + 12, &len, 4);
if (w_all(fd, hdr, sizeof hdr)) return -1;
if (len && pl && w_all(fd, pl, len)) return -1;
return 0;
}
static int rf(int fd, uint16_t *type, uint8_t *rbuf, uint32_t rcap)
{
uint8_t hdr[MEOW_IPC_HDR_LEN];
if (r_all(fd, hdr, sizeof hdr)) return -1;
uint32_t magic, len; uint16_t ver;
memcpy(&magic, hdr, 4); memcpy(&ver, hdr + 4, 2); memcpy(type, hdr + 6, 2); memcpy(&len, hdr + 12, 4);
if (magic != MEOW_IPC_MAGIC || ver != MEOW_IPC_VERSION) return -1;
if (len > rcap) len = rcap;
if (len && r_all(fd, rbuf, len)) return -1;
return (int)len;
}
static int raw_open(int fd, const char *app, int rate)
{
m_audio_open_t o; memset(&o, 0, sizeof o);
snprintf(o.app_id, sizeof o.app_id, "%s", app);
o.rate = (uint32_t)rate; o.ch = 2; o.fmt = AUDIO_FMT_S16;
if (sf(fd, MSG_AUDIO_OPEN, 9, &o, sizeof o)) return -1;
uint8_t rbuf[64]; uint16_t rt;
if (rf(fd, &rt, rbuf, sizeof rbuf) < 0 || rt != MSG_AUDIO_OPEN_ACK) return -1;
m_audio_open_ack_t a; memcpy(&a, rbuf, sizeof a);
return (a.err == MEOW_OK) ? (int)a.stream_id : -1;
}
static int g_audiod_pid = -1;
static void stop_audiod(void) { if (g_audiod_pid > 0) { kill(g_audiod_pid, SIGTERM); waitpid(g_audiod_pid, NULL, 0); g_audiod_pid = -1; } }
int main(void)
{
unlink(TSOCK); unlink(TWAV);
setenv("MEOW_AUDIO_SOCK", TSOCK, 1); /* SDK 客户端连接测试 audiod 的 socket */
g_audiod_pid = fork();
if (g_audiod_pid == 0) {
setenv("MEOW_AUDIO_SINK", "file", 1);
setenv("MEOW_AUDIO_OUT", TWAV, 1);
setenv("MEOW_AUDIO_SOCK", TSOCK, 1);
setenv("MEOW_AUDIO_FORCE", "1", 1);
execl("../meow_audiod/meow_audiod_host", "meow_audiod_host", (char *)0); /* SDK 测试运行于 sdk/ 目录 */
_exit(127);
}
usleep(300000);
printf("=== meow_ui_audio SDK host test ===\n");
/* 注册回调(读线程懒启动) */
meow_audio_on_preempt(preempt_cb, NULL);
/* 无 audiod 场景:断开后 open 应返回 -ENOENT(先测一次) */
int sid = meow_audio_open(44100, 2, AUDIO_FMT_S16);
check(sid > 0, "open (audiod present)");
/* 写正弦(stereo:L/R 同值交织) */
static int16_t pcm[4096];
for (int i = 0; i < 2048; i++) {
int16_t s = (int16_t)(sin(2.0 * M_PI * 440 * i / 44100) * 0.3 * 32767);
pcm[i * 2] = s; pcm[i * 2 + 1] = s;
}
int wr = meow_audio_write(pcm, 2048);
check(wr == 2048, "write 2048 frames");
meow_audio_state st;
check(meow_audio_query(&st) == 0 && st.active == 1, "query active");
printf(" state: active=%d app=%s rate=%u written=%llu\n", st.active, st.app_id, (unsigned)st.rate, (unsigned long long)st.written_frames);
check(meow_audio_pause(sid, 1) == 0, "pause");
meow_audio_query(&st);
check(st.paused == 1, "query paused");
check(meow_audio_pause(sid, 0) == 0, "resume");
/* 第二客户端(raw)抢先 → 本 SDK 收 PREEMPTED,write 返回负值 */
int rfd = raw_connect();
check(rfd >= 0, "raw connect");
int rsid = raw_open(rfd, "raw-app", 96000);
check(rsid > 0, "raw open (preempt)");
usleep(200000);
check(g_preempted == 1, "on_preempt fired");
printf(" preempted by %s\n", g_by);
check(meow_audio_write(pcm, 2048) < 0, "write after preempt -> <0 (dead)");
check(meow_audio_query(&st) == 0, "query still works");
/* close 旧 sid(已死,应无副作用) */
check(meow_audio_close(sid) == 0, "close dead sid");
check(meow_audio_pause(sid, 1) < 0 || meow_audio_pause(sid, 1) == -MEOW_ERR_NOENT, "pause dead sid -> error");
close(rfd);
stop_audiod();
printf("=== SUMMARY: pass=%d fail=%d ===\n", g_pass, g_fail);
return g_fail ? 1 : 0;
}