Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/test_bt_page.c
T

236 lines
11 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.
/* test_bt_page.c — MeowOS launcher 蓝牙子页(bt_page.c)sub_page_t 逻辑宿主自测(非生产代码)。
*
* 通过 #include bt_page.c 把其 FILE-STATIC 的 bt_top_sw/bt_rows/bt_act/bt_detail 平移进本 TU,
* 桩掉 netmgr IPC 客户端(nm_bt_*)+ 设置后端(meow_settings_*)+ UI(s_dd_modal/logmsg 等),
* 直接验证:
* P1 bt_rows 行骨架(plan §六 8 分组:②A2DP 摘要 ③输出目标 ④已连接 ⑤已配对 ⑥附近 ⑦音质 ⑧功耗)
* 与「已连接设备」数据(权威 netmgrd 状态 → mac/name/codec 渲染);
* P2 bt_act 命中 → 正确下发 netmgrd 命令(已连接→disconnect、已配对未连→connect、输出目标
* 切换、编解码器循环);
* P3 附近设备仅列未配对(已配对者归「已配对设备」)。
*
* 编译(在 patch_v2/meow_launcherd 下):
* gcc -I../sdk -I. -Wall -Wextra -fsanitize=undefined test_bt_page.c -o test_bt_page
* 运行:./test_bt_page
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <strings.h>
#include "meow_launcher_internal.h"
#include "bt_page.c" /* bt_rows/bt_act/bt_top_sw/bt_detail 平移到本 TU */
/* ---------- 测试态:netmgrd BT 状态桩(launcher 客户端视角) ---------- */
static meow_net_t g_conn[8], g_paired[8], g_near[8];
static int g_conn_n, g_paired_n, g_near_n;
static bt_ep_t g_eps[8]; static int g_eps_n;
static char g_last_cmd[128]; /* 记录 act 下发命令,如 "disconnect:<mac>" */
static int g_output_bt; /* audio.output_bt 桩值 */
static char g_codec[16] = "auto"; /* bt.codec 桩值 */
int nm_bt_powered(void) { return 1; }
int nm_bt_onoff(int on) { (void)on; return 0; }
int nm_bt_connected(meow_net_t *out, int max) {
if (max > g_conn_n) max = g_conn_n;
memcpy(out, g_conn, (size_t)max * sizeof *out); return max;
}
int nm_bt_paired(meow_net_t *out, int max) {
if (max > g_paired_n) max = g_paired_n;
memcpy(out, g_paired, (size_t)max * sizeof *out); return max;
}
int nm_bt_devices(meow_net_t *out, int max) {
if (max > g_near_n) max = g_near_n;
memcpy(out, g_near, (size_t)max * sizeof *out); return max;
}
int nm_bt_a2dp(bt_ep_t *eps, int max) {
if (max > g_eps_n) max = g_eps_n;
memcpy(eps, g_eps, (size_t)max * sizeof *eps); return max;
}
static void set_cmd(const char *cmd, const char *mac)
{
snprintf(g_last_cmd, sizeof g_last_cmd, "%s:%s", cmd, mac ? mac : "");
}
int nm_bt_connect(const char *mac) { set_cmd("connect", mac); return MEOW_OK; }
int nm_bt_disconnect(const char *mac) { set_cmd("disconnect", mac); return MEOW_OK; }
int nm_bt_pair(const char *mac) { set_cmd("pair", mac); return MEOW_OK; }
int nm_bt_unpair(const char *mac) { set_cmd("unpair", mac); return MEOW_OK; }
void nm_bt_scan_on(void) {}
void nm_bt_scan_off(void) {}
int netmgr_ok(void) { return 1; }
void net_bt_mode_str(char *buf, int len) { snprintf(buf, (size_t)len, "主模式·已连 Pudding"); }
/* ---------- 设置/UI 桩 ---------- */
int meow_settings_get_bool(const char *k, int *v)
{
if (v) *v = 0;
if (!strcmp(k, "network.bluetooth_enabled")) { if (v) *v = 1; return 0; }
if (!strcmp(k, "audio.output_bt")) { if (v) *v = g_output_bt; return 0; }
return -1;
}
int meow_settings_set_bool(const char *k, int v)
{
if (!strcmp(k, "audio.output_bt")) { g_output_bt = v; return 0; }
if (!strcmp(k, "network.bluetooth_enabled")) return 0;
return -1;
}
int meow_settings_get_str(const char *k, char *b, int n)
{
if (!strcmp(k, "bt.codec")) { snprintf(b, (size_t)n, "%s", g_codec); return 0; }
if (b && n > 0) b[0] = 0;
return -1;
}
int meow_settings_set_str(const char *k, const char *v)
{
if (!strcmp(k, "bt.codec")) { snprintf(g_codec, sizeof g_codec, "%s", v); return 0; }
return -1;
}
int meow_settings_save(void) { return 0; }
void paint_sb_right(void) {}
void logmsg(const char *fmt, ...) { (void)fmt; }
void bt_role_refresh(int force) { (void)force; }
meow_sys_state g_sys;
int s_dd_modal(const char *title, const char *item[S_NET_MAX], int n, int cur) { (void)title; (void)item; (void)n; (void)cur; return -1; }
int meow_subpage_run(sub_page_t *p) { (void)p; return 0; }
static int g_fail = 0;
#define CHECK(cond, ...) do { if (cond) { printf(" PASS: " __VA_ARGS__); printf("\n"); } \
else { printf(" FAIL: " __VA_ARGS__); printf("\n"); g_fail++; } } while (0)
/* 在 rows 里找下标:kind==HDR 且 label==lbl;返回该 HDR 的下一行下标起查找 */
static int find_hdr(const sub_row_t *r, int n, const char *lbl)
{
for (int i = 0; i < n; i++) if (r[i].kind == SUB_HDR && r[i].label && !strcmp(r[i].label, lbl)) return i;
return -1;
}
/* 在 [from,n) 找首个 SUB_ACT 且 label 前缀 == pref,返回下标 */
static int find_act(const sub_row_t *r, int n, int from, const char *pref)
{
for (int i = from; i < n; i++)
if (r[i].kind == SUB_ACT && r[i].label && !strncmp(r[i].label, pref, strlen(pref))) return i;
return -1;
}
static void fill_state(void)
{
/* 已连接:Pudding(a2dp 播放中)+ Speaker */
g_conn_n = 2;
snprintf(g_conn[0].mac, sizeof g_conn[0].mac, "33:DE:2E:21:AD:30");
snprintf(g_conn[0].name, sizeof g_conn[0].name, "Pudding");
snprintf(g_conn[1].mac, sizeof g_conn[1].mac, "11:22:33:44:55:66");
snprintf(g_conn[1].name, sizeof g_conn[1].name, "Speaker");
/* 已配对:+ 17OP15(未连) */
g_paired_n = 3;
memcpy(&g_paired[0], &g_conn[0], sizeof g_conn[0]);
memcpy(&g_paired[1], &g_conn[1], sizeof g_conn[1]);
snprintf(g_paired[2].mac, sizeof g_paired[2].mac, "E0:42:6D:79:C5:98");
snprintf(g_paired[2].name, sizeof g_paired[2].name, "17OP15");
/* 附近:Earbuds(未配对 → 附近列出) */
g_near_n = 2;
snprintf(g_near[0].mac, sizeof g_near[0].mac, "AA:BB:CC:DD:EE:FF");
snprintf(g_near[0].name, sizeof g_near[0].name, "Earbuds");
snprintf(g_near[1].mac, sizeof g_near[1].mac, "33:DE:2E:21:AD:30"); /* 已配对 → 附近应过滤 */
snprintf(g_near[1].name, sizeof g_near[1].name, "Pudding");
/* A2DP:Pudding SBC/44100 acquired */
g_eps_n = 1;
snprintf(g_eps[0].mac, sizeof g_eps[0].mac, "33:DE:2E:21:AD:30");
snprintf(g_eps[0].codec, sizeof g_eps[0].codec, "SBC");
snprintf(g_eps[0].rate, sizeof g_eps[0].rate, "44100");
snprintf(g_eps[0].chn, sizeof g_eps[0].chn, "2");
snprintf(g_eps[0].bits, sizeof g_eps[0].bits, "16");
g_eps[0].acquired = 1;
snprintf(g_eps[0].role, sizeof g_eps[0].role, "src");
g_output_bt = 0; /* DAC */
snprintf(g_codec, sizeof g_codec, "auto");
}
int main(void)
{
setbuf(stdout, NULL);
fill_state();
bt_ctx_t c;
memset(&c, 0, sizeof c);
c.full = 1;
c.output_bt = 0;
sub_row_t rows[SUB_ROWS_MAX];
int n = bt_rows(&c, rows, SUB_ROWS_MAX);
printf("== P1 bt_rows 分组骨架 ==\n");
CHECK(find_hdr(rows, n, "输出目标") >= 0, "分组③输出目标存在");
CHECK(find_hdr(rows, n, "已连接设备") > 0, "分组④已连接设备存在");
CHECK(find_hdr(rows, n, "已配对设备") > 0, "分组⑤已配对设备存在");
CHECK(find_hdr(rows, n, "附近设备") > 0, "分组⑥附近设备存在");
CHECK(find_hdr(rows, n, "音质 / 连接管理") > 0, "分组⑦音质/连接管理存在");
CHECK(find_hdr(rows, n, "功耗") > 0, "分组⑧功耗存在");
/* A2DP 摘要(首行 SUB_ROW,主=Pudding,副含 codec/采样率/输出=DAC) */
CHECK(rows[0].kind == SUB_ROW, "行0=A2DP摘要(SUB_ROW)");
CHECK(rows[0].label && !strcmp(rows[0].label, "Pudding"), "摘要主=Pudding (实际=%s)",
rows[0].label ? rows[0].label : "(null)");
CHECK(rows[0].sub && strstr(rows[0].sub, "SBC") && strstr(rows[0].sub, "44100") &&
strstr(rows[0].sub, "输出=DAC"), "摘要副含 codec/采样率/输出=DAC (%s)",
rows[0].sub ? rows[0].sub : "(null)");
printf("== P1 已连接设备(权威状态,含 codec join)==\n");
int hdr = find_hdr(rows, n, "已连接设备");
CHECK(hdr >= 0, "定位已连接 HDR");
int ci = find_act(rows, n, hdr + 1, "Pudding");
CHECK(ci >= 0, "已连接列出 Pudding");
CHECK(ci >= 0 && rows[ci].sub && strstr(rows[ci].sub, "SBC"), "已连接 Pudding sub 含 codec SBC");
CHECK(find_act(rows, n, hdr + 1, "Speaker") >= 0, "已连接列出 Speaker");
printf("== P1 已配对设备(已连者标「已连接」on=1)==\n");
hdr = find_hdr(rows, n, "已配对设备");
CHECK(hdr >= 0, "定位已配对 HDR");
ci = find_act(rows, n, hdr + 1, "Pudding");
CHECK(ci >= 0 && rows[ci].on == 1 && rows[ci].sub && strstr(rows[ci].sub, "已连接"),
"已配对 Pudding 标已连接(on=1)");
ci = find_act(rows, n, hdr + 1, "17OP15");
CHECK(ci >= 0 && rows[ci].on == 0, "已配对 17OP15 未连(on=0)");
printf("== P3 附近设备仅列未配对(已配对者过滤)==\n");
hdr = find_hdr(rows, n, "附近设备");
CHECK(hdr >= 0, "定位附近 HDR");
ci = find_act(rows, n, hdr + 1, "Earbuds");
CHECK(ci >= 0, "附近列出 Earbuds");
ci = find_act(rows, n, hdr + 1, "Pudding");
CHECK(ci < 0, "已配对 Pudding 不在附近列表");
printf("== P2 bt_act 命中 → netmgrd 命令 ==\n");
/* 已连接 Pudding tap → disconnect */
memset(&c, 0, sizeof c); c.full = 1; fill_state();
n = bt_rows(&c, rows, SUB_ROWS_MAX);
hdr = find_hdr(rows, n, "已连接设备");
ci = find_act(rows, n, hdr + 1, "Pudding");
g_last_cmd[0] = 0;
bt_act(&c, ci, rows[ci].act);
CHECK(strcmp(g_last_cmd, "disconnect:33:DE:2E:21:AD:30") == 0, "已连接 tap → disconnect (实际=%s)", g_last_cmd);
/* 已配对 17OP15(未连)tap → connect */
memset(&c, 0, sizeof c); c.full = 1; fill_state();
n = bt_rows(&c, rows, SUB_ROWS_MAX);
hdr = find_hdr(rows, n, "已配对设备");
ci = find_act(rows, n, hdr + 1, "17OP15");
g_last_cmd[0] = 0;
bt_act(&c, ci, rows[ci].act);
CHECK(strcmp(g_last_cmd, "connect:E0:42:6D:79:C5:98") == 0, "已配对未连 tap → connect (实际=%s)", g_last_cmd);
/* 输出目标 toggle → audio.output_bt 翻转为 1;bt_rows 摘要改输出=蓝牙 */
memset(&c, 0, sizeof c); c.full = 1; fill_state();
n = bt_rows(&c, rows, SUB_ROWS_MAX);
hdr = find_hdr(rows, n, "输出目标");
ci = find_act(rows, n, hdr + 1, "输出目标");
bt_act(&c, ci, rows[ci].act);
CHECK(g_output_bt == 1, "输出目标 tap → output_bt=1 (蓝牙)");
n = bt_rows(&c, rows, SUB_ROWS_MAX);
CHECK(rows[0].sub && strstr(rows[0].sub, "输出=蓝牙"), "切蓝牙后 A2DP 摘要=输出=蓝牙");
/* 编解码器循环:auto → SBC */
memset(&c, 0, sizeof c); c.full = 1; fill_state();
n = bt_rows(&c, rows, SUB_ROWS_MAX);
hdr = find_hdr(rows, n, "音质 / 连接管理");
ci = find_act(rows, n, hdr + 1, "编解码器");
bt_act(&c, ci, rows[ci].act);
CHECK(strcmp(g_codec, "SBC") == 0, "编解码器 auto → SBC (实际=%s)", g_codec);
printf(g_fail ? "\nRESULT: %d FAIL(S)\n" : "\nRESULT: ALL PASS\n", g_fail);
return g_fail;
}