/* test_netmgr_ipc.c — M6.0 宿主桩测试:设置页 netmgr IPC 客户端 / WiFi 新网络录入逻辑。 * * 假 socket 帧:本测试起一个 AF_UNIX 服务器线程,按 Meow IPC 帧(16B 头 + 负载)应答 * MSG_HELLO→ACK、MSG_NET_WIFI_CTL/BT_CTL→*_RESULT(netmgrd 同构),让 launcher 侧 * netmgr_ipc.c(netmgr_wifi / nm_wifi_* 系列 / netmgr_ok)与 wifi_page.c(wifi_rows)跑真实数据。 * * 覆盖(打桩帧): * ① 扫描列表渲染:netmgrd 返回 scan_results → nm_wifi_scan / wifi_rows 解析出 SSID/mac/sec, * 加密网络 sec=WPA-PSK、开放网络 sec=Open; * ② 连接成功/失败两态:connect 子命令 MEOW_OK / MEOW_ERR_NODEV → nm_wifi_connect 返回值+错误文本; * ③ 降级占位:MEOW_NET_SOCK 指向不存在路径 → netmgr_ok()==0、netmgr_wifi=NETMGR_UNREACH、 * nm_wifi_scan 回退直连(桩返回 0 不崩),且 wifi_rows 渲染「网络管理服务未运行(直连模式)」占位。 * * 编译(链 netmgr_ipc.o;net.c 回退函数/UI 用桩替代,同 test_media.c 惯例): * gcc -I../sdk -I. -Wall -Wextra -c netmgr_ipc.c -o netmgr_ipc.test.o * gcc -I../sdk -I. -Wall -Wextra test_netmgr_ipc.c netmgr_ipc.test.o -lpthread -o test_netmgr_ipc */ #include #include #include #include #include #include #include #include #include #include #include "meow_launcher_internal.h" /* meow_net_t / S_NET_MAX / nm_* / sub_page_t etc. */ #include "wifi_page.c" /* 把静态 wifi_ctx_t/wifi_rows 平移进本 TU(沿用既有模式) */ /* ---------- net.c 直连回退路径桩(netmgrd 不可达时走这里;宿主无 wpa_cli/bluetoothctl) ---------- */ int wlan_connected(void) { return 0; } int net_wifi_saved(meow_net_t *out, int max) { (void)out; (void)max; return 0; } int net_wifi_scan(meow_net_t *out, int max) { (void)out; (void)max; return 0; } void net_wifi_ssid(char *buf, int len) { (void)len; buf[0] = 0; } int net_wifi_info(char (*info)[48], int max) { (void)info; (void)max; return 0; } int hci_powered(void) { return 0; } int net_bt_paired(meow_net_t *out, int max) { (void)out; (void)max; return 0; } void s_net_toggle_wifi(int on) { (void)on; } void s_net_toggle_bt(int on) { (void)on; } void net_wifi_scan_trigger(void) {} char *popen_read_tmo(int tmo_ms, const char *cmd) { (void)tmo_ms; (void)cmd; return strdup("0\n"); } /* 模拟 wpa_cli add_network 返回 id=0 */ /* ---------- launcher/UI/IME 桩(wifi_page.c 链接所需;测试不触碰这些路径) ---------- */ static unsigned long long g_test_ms = 0; int meow_settings_get_bool(const char *k, int *v) { (void)k; if (v) *v = 1; return 1; } int meow_settings_set_bool(const char *k, int v) { (void)k; (void)v; return 0; } int meow_settings_save(void) { return 0; } void paint_sb_right(void) {} void logmsg(const char *fmt, ...) { (void)fmt; } unsigned long long now_ms(void) { return ++g_test_ms; } void bt_role_refresh(int force) { (void)force; } meow_sys_state g_sys; /* extern 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; } int meow_ui_text_input(meow_ime_ctx *ctx) { (void)ctx; return 0; } /* 取消:不进 IME 路径 */ /* ---------- 假 netmgrd 服务器 ---------- */ typedef struct { int lfd; int fail_connect; /* 1=connect 返回 MEOW_ERR_NODEV */ const char *scan_text; const char *networks_text; const char *status_text; const char *bt_status_text; const char *bt_paired_text; } fake_net_t; static int rw_all(int fd, uint8_t *b, size_t n) { 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 wr_all(int fd, const uint8_t *b, size_t n) { size_t o = 0; while (o < n) { ssize_t r = write(fd, b + o, n - o); if (r <= 0) return -1; o += (size_t)r; } return 0; } static void fk_send(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, rlen = len; uint16_t ver = MEOW_IPC_VERSION, t = type; memcpy(hdr + 0, &magic, 4); memcpy(hdr + 4, &ver, 2); memcpy(hdr + 6, &t, 2); memcpy(hdr + 8, &req, 4); memcpy(hdr + 12, &rlen, 4); if (wr_all(fd, hdr, sizeof hdr) < 0) return; if (len && pl) wr_all(fd, pl, len); } static void fk_result(int fd, uint16_t type, uint32_t req, uint32_t code, const char *txt) { m_net_cmd_result_t r; memset(&r, 0, sizeof r); r.code = code; size_t pl = txt ? strlen(txt) : 0; if (pl > sizeof r.text - 1) pl = sizeof r.text - 1; r.text_len = (uint16_t)pl; memcpy(r.text, txt, pl); r.text[pl] = 0; fk_send(fd, type, req, &r, (uint32_t)(8 + pl + 1)); } static void fk_handle(int c, fake_net_t *f) { for (;;) { uint8_t hdr[MEOW_IPC_HDR_LEN]; if (rw_all(c, hdr, sizeof hdr) < 0) return; uint32_t magic, rlen, req; uint16_t ver, type; memcpy(&magic, hdr, 4); memcpy(&ver, hdr + 4, 2); memcpy(&type, hdr + 6, 2); memcpy(&req, hdr + 8, 4); memcpy(&rlen, hdr + 12, 4); if (magic != MEOW_IPC_MAGIC || ver != MEOW_IPC_VERSION) return; uint8_t pl[MEOW_IPC_MAX]; if (rlen) { if (rlen > sizeof pl) rlen = sizeof pl; if (rw_all(c, pl, rlen) < 0) return; } if (type == MSG_HELLO) { m_ack_t a; memset(&a, 0, sizeof a); a.code = MEOW_OK; fk_send(c, MSG_ACK, req, &a, sizeof a); continue; } m_net_cmd_t q; memset(&q, 0, sizeof q); if (rlen > sizeof q) rlen = sizeof q; memcpy(&q, pl, rlen); if (type == MSG_NET_WIFI_CTL) { if (!strcmp(q.cmd, "scan")) fk_result(c, MSG_NET_WIFI_RESULT, req, MEOW_OK, f->scan_text); else if (!strcmp(q.cmd, "networks")) fk_result(c, MSG_NET_WIFI_RESULT, req, MEOW_OK, f->networks_text); else if (!strcmp(q.cmd, "status")) fk_result(c, MSG_NET_WIFI_RESULT, req, MEOW_OK, f->status_text); else if (!strcmp(q.cmd, "connect")) { char buf[200]; if (f->fail_connect) fk_result(c, MSG_NET_WIFI_RESULT, req, MEOW_ERR_NODEV, "connect: add_network 失败 (wpa_cli 不可用?)"); else { snprintf(buf, sizeof buf, "connect: ssid=%s network_id=5 (已 enable+select)", q.arg); fk_result(c, MSG_NET_WIFI_RESULT, req, MEOW_OK, buf); } } else if (!strcmp(q.cmd, "disconnect")) fk_result(c, MSG_NET_WIFI_RESULT, req, MEOW_OK, "wifi disconnect: 已下发"); else if (!strcmp(q.cmd, "on") || !strcmp(q.cmd, "off")) fk_result(c, MSG_NET_WIFI_RESULT, req, MEOW_OK, "wifi on/off: rfkill wlan done"); else fk_result(c, MSG_NET_WIFI_RESULT, req, MEOW_ERR_INVAL, "wifi: 未知子命令"); continue; } if (type == MSG_NET_BT_CTL) { if (!strcmp(q.cmd, "status")) fk_result(c, MSG_NET_BT_RESULT, req, MEOW_OK, f->bt_status_text); else if (!strcmp(q.cmd, "paired")) fk_result(c, MSG_NET_BT_RESULT, req, MEOW_OK, f->bt_paired_text); else if (!strcmp(q.cmd, "on") || !strcmp(q.cmd, "off")) fk_result(c, MSG_NET_BT_RESULT, req, MEOW_OK, "bt on/off: hci0 done"); else fk_result(c, MSG_NET_BT_RESULT, req, MEOW_ERR_INVAL, "bt: 未知子命令"); continue; } return; /* 未知帧:断开 */ } } static void *fk_server(void *arg) { fake_net_t *f = arg; for (;;) { int c = accept(f->lfd, NULL, NULL); if (c < 0) return NULL; fk_handle(c, f); close(c); } } static int start_server(fake_net_t *f, const char *path) { f->lfd = socket(AF_UNIX, SOCK_STREAM, 0); if (f->lfd < 0) return -1; struct sockaddr_un a; memset(&a, 0, sizeof a); a.sun_family = AF_UNIX; strncpy(a.sun_path, path, sizeof a.sun_path - 1); unlink(path); if (bind(f->lfd, (struct sockaddr *)&a, sizeof a) < 0) return -2; if (listen(f->lfd, 5) < 0) return -3; pthread_t t; return pthread_create(&t, NULL, fk_server, f) == 0 ? 0 : -4; } static int row_has_label(const sub_row_t *rows, int n, const char *needle) { for (int i = 0; i < n; i++) if (rows[i].label && strstr(rows[i].label, needle)) return 1; return 0; } int main(void) { char sock[110]; snprintf(sock, sizeof sock, "/tmp/meow_net_test_%d.sock", (int)getpid()); fake_net_t f; memset(&f, 0, sizeof f); f.scan_text = "bssid / frequency / signal level / flags / ssid\n" "00:11:22:33:44:01 2412 -45 [WPA-PSK-CCMP][ESS] Home2G\n" "00:11:22:33:44:02 2417 -60 [WPA2-PSK][ESS] Office\n" "00:11:22:33:44:03 2442 -70 [ESS] FreeWiFi\n"; f.networks_text = "network id / ssid / bssid / flags\n" "0\tHome2G\t00:11:22:33:44:01\t[WPA-PSK-CCMP]\n"; f.status_text = "wifi state=COMPLETED connected=1 ssid=Home2G ip=192.168.1.5 rssi=-45"; f.bt_status_text = "bt available=1 powered=1 address=00:1A:7D:DA:71:13 paired=2"; f.bt_paired_text = "Device 00:1A:7D:DA:71:13 Headphones\nDevice 00:1A:7D:DA:71:14 Speaker\n"; if (start_server(&f, sock) != 0) { fprintf(stderr, "fake server start fail\n"); return 1; } setenv("MEOW_NET_SOCK", sock, 1); /* ---- ① 扫描列表渲染:nm_wifi_scan 解析(IPsec/开放安全类型) ---- */ meow_net_t scan[S_NET_MAX]; int sn = nm_wifi_scan(scan, S_NET_MAX); assert(sn >= 2 && "scan: 至少解析 2 条(BSSID 去重后)"); assert(!strcmp(scan[0].name, "Home2G") && "scan[0].name=Home2G"); assert(!strcmp(scan[0].sec, "WPA-PSK-CCMP") && "scan[0].sec 加密"); assert(!strcmp(scan[1].sec, "WPA2-PSK") && "scan[1].sec 加密"); /* FreeWiFi 无 WPA → Open */ int open_idx = -1; for (int i = 0; i < sn; i++) if (!strcmp(scan[i].name, "FreeWiFi")) open_idx = i; assert(open_idx >= 0 && "scan 含 FreeWiFi"); assert(!strcmp(scan[open_idx].sec, "Open") && "开放网络 sec=Open"); printf("[A] nm_wifi_scan: %d 条, Home2G/WPA-PSK-CCMP, FreeWiFi/Open PASS\n", sn); /* ---- wifi_rows:附近网络分组渲染(含 SSID/sec 摘要行) ---- */ { wifi_ctx_t c; memset(&c, 0, sizeof c); sub_row_t rows[SUB_ROWS_MAX]; int rn = wifi_rows(&c, rows, SUB_ROWS_MAX); assert(row_has_label(rows, rn, "附近网络") && "rows 有「附近网络」分组"); assert(row_has_label(rows, rn, "Home2G") && "rows 渲染扫描 SSID Home2G"); assert(row_has_label(rows, rn, "FreeWiFi") && "rows 渲染开放网络 FreeWiFi"); assert(row_has_label(rows, rn, "已保存网络") && "rows 有「已保存网络」分组"); assert(!row_has_label(rows, rn, "网络管理服务未运行") && "netmgrd 在 → 无降级占位"); printf("[B] wifi_rows 渲染: 附近/已保存/扫描SSID 均现, 无降级占位 PASS\n"); } /* ---- ② 连接成功/失败两态 ---- */ char err[192]; err[0] = 0; f.fail_connect = 0; int rc = nm_wifi_connect("Home2G", "pass1234", err, sizeof err); assert(rc == MEOW_OK && "connect 成功返回 MEOW_OK"); assert(strstr(err, "network_id=5") && "connect 成功带回 network_id"); printf("[C] connect 成功: rc=%d err=%s PASS\n", rc, err); f.fail_connect = 1; err[0] = 0; rc = nm_wifi_connect("Home2G", "bad", err, sizeof err); assert(rc == MEOW_ERR_NODEV && "connect 失败返回 MEOW_ERR_NODEV"); assert(strstr(err, "add_network") && "connect 失败带回原因"); printf("[D] connect 失败: rc=%d err=%s PASS\n", rc, err); /* ---- ③ 降级占位:MEOW_NET_SOCK 指向不存在路径 ---- * 先发一次 netmgr_wifi(内部强制重连,失败 → 置可达缓存=0),再断言 netmgr_ok()。 */ setenv("MEOW_NET_SOCK", "/tmp/meow_net_nonexistent_zz.sock", 1); assert(netmgr_wifi("status", NULL, err, sizeof err) == NETMGR_UNREACH && "netmgr_wifi=NETMGR_UNREACH"); assert(netmgr_ok() == 0 && "netmgr_ok()==0(服务不可达)"); err[0] = 0; int dr = nm_wifi_connect("Home2G", "x", err, sizeof err); assert(dr == MEOW_OK && "不可达→回退直连(桩返回成功,保功能)"); int nr = nm_wifi_scan(scan, S_NET_MAX); assert(nr == 0 && "不可达→回退直连扫描(桩返回 0,不崩)"); { wifi_ctx_t c; memset(&c, 0, sizeof c); sub_row_t rows[SUB_ROWS_MAX]; int rn = wifi_rows(&c, rows, SUB_ROWS_MAX); assert(row_has_label(rows, rn, "网络管理服务未运行(直连模式)") && "降级占位渲染"); printf("[E] 降级: netmgr_ok=0, UNREACH, 回退不崩, 占位渲染 PASS\n"); } printf("ALL PASS\n"); unlink(sock); return 0; }