Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/test_netmgr_ipc.c
T
AskaEth b20e3c43e9 fix(launcher): M47 设置页蓝牙/WiFi 开关只认真实态 — 三态查询 + 待确认收敛 + 未知态呈现
用户原话:「开关都存在显示问题 我都不能判断它是不是真的开启还是关闭(包括 WiFi 页也是)」。
根因不是「没读到硬件态」,而是「读到/没读到都被拿去当用户意图的装饰」:
- bt_page.c:103 开关初值来自 settings 布尔;:112-114 下发后立刻 g_sys.bt_on = on(显示意图);
  :129 c->bt_on = nm_bt_powered() 赋值后全文无人引用(M45 也独立发现)。
- wifi_page.c:40 读不到 settings 时默认 1(显示已开)。
- netmgrd 的 bt/wifi 查询本身把「问不到」塌缩成 0(nm_bt_powered/nm_wifi_connected)。

本单只解「状态可信」,不碰扫描/配对/音频(另有 M45/M46 单)。

1) 真实态三态(-1 未知 / 0 关 / 1 开)
- net.c 新增 net_bt_power_probe()(hciconfig hci0 UP/DOWN;无输出=未知)与
  net_wifi_power_probe()(rfkill wlan soft/hard 汇总,回退 wlan0 IFF_UP;读不到=未知)。
- netmgr_ipc.c 新增 nm_bt_power_state()(daemon bt status 的 available/powered 权威,不可达
  回退直连探测)与 nm_wifi_power_state()(netmgrd 的 wifi status 只有 wpa_state、不报射频态,
  故取本地 rfkill——daemon 的 wifi on/off 改的就是同一份内核状态)。
- 两个子页的 top_sw/rows 只读这两个函数;settings 布尔降级为「意图持久化」,不再决定外观。
- 「读不到默认 1」删除:未知就是未知。

2) 点击 → 待确认 → 真实态定稿
- 纯函数 meow_toggle_{display,pending,rollback,error}(bt_page.c,无 I/O/UI):
  只有 probe 确认才算收敛;probe 未知继续等,绝不因 last_real 恰好等于意图就宣布成功。
- 页面:落盘意图 → 下发 → 250ms 轮询(BT 上限 4s、WiFi 上限 5s;hciconfig 0.3~2s、
  wifi_up.sh 起 supplicant/DHCP 更慢,故不用 20ms 级忙轮询)→ 真实态定稿;失败/超时把
  settings 回滚到已确认真实态 + s_dd_modal 可见报错,不静默、不乐观。
- subpage.c:切换前先画「正在开启/关闭…(等待设备确认)」条(待确认态与稳定态可区分);
  切换后回读 top_sw 定稿(取消原「点了就翻面」),live tick 也回读,外部改动驻留期间可见。

3) 第三态呈现(未知 ≠ 已关)
- SDK meow_switch 只有两态:未知时开关停「关」并在左侧画灰色「?」;行内另有
  「蓝牙状态未知 / WiFi 开关状态未知」文字说明。选「?」是因为它不占用开关控件本身、
  且「关+?」不会被读成「已关」。

4) 顺带修 net.c 三条开关失真(塔追加的 M45 盘点,同一病根)
- s_net_toggle_bt() 补 rfkill soft(与 netmgrd 的 bt on/off 同一语义;开:先 unblock 再 up;
  关:先 down 再 block)。此前只发 hciconfig → 「BT 页关过再从头开」会被 rfkill 拦下。
- s_net_sync_from_sys() WiFi 判据从 wlan_connected()(已关联)改为射频态;BT 用三态,
  读不到不写(旧写法把「网卡 up 未关联」写成 0,把未知写成已关)。
- rfkill 同类多节点(本机 2 wlan + 2 bluetooth)按「任一 block 即 block」读、全部写入。

测试(`bash patch_v2/meow_launcherd/build_launcher.sh test`,新增 test 入口)
- test_bt_page.c:P4 三态与未知占位、P5 收敛四例(达成/下发失败/超时/未知)、P6 收敛矩阵
  12 组(真实开/关 × 下发成功/失败/超时 × probe 未知),全 PASS(64 条断言)。
- test_netmgr_ipc.c:新增 nm_bt_power_state 解析(powered=1/0、available=0、缺字段→-1)、
  nm_wifi_power_state 透传、不可达回退=未知、wifi_rows 未知态占位;并修掉两处既有测试缺陷
  (行内 UI 桩缺失导致本文件此前根本链不过;直连连接桩统一返回 "0\n" 导致等满 10s 必失败)。
- 交叉编译 0 warning(-Wall -Wextra,逐文件单独编 + 整链);launcherd 899388 B
  md5 1c88661a9410ac1a4139304362281cdd,同 commit 同步 scripts/build/fw_content.conf 钉值。

真机(10.58.215.50,仅只读自检,未替换 /usr/bin/meow_launcherd、未刷机)
- 二进制放 /usr/data/mnt/sd_0/MeowOS/tmp/ 跑 --sysdump:exit 0,输出 wifi_on=1 / bt_seen=1
  bt_on=0,与设备现状一致(rfkill 全 0 + wlan0 up + hci0 DOWN + netmgrd powered=0)。
- 卡上 du -s /usr/data/mnt/sd_0/Music 前后恒 199164544;临时文件已删;settings 未改。
- launcherd 在 squashfs 里 → 本改动需刷机生效。

已知不在本单:输出目标只写 settings(audiod 仅启动读一次)→ M45 finding / M46;
netmgrd 侧四处失真(未知当关、up 与 unblock 顺序、rfkill 只写首节点、wifi status 无射频字段)
已报 finding;设置主页/通知中心的开关显示仍是 settings(另立单)。
2026-11-11 17:16:28 +08:00

336 lines
17 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_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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <assert.h>
#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) {}
int net_bt_connected(meow_net_t *out, int max) { (void)out; (void)max; return 0; }
int net_bt_scan(meow_net_t *out, int max) { (void)out; (void)max; return 0; }
void net_bt_scan_trigger(void) {}
/* UI 桩:wifi_connecting_banner 走 SDK 绘制函数;宿主无 fb,空实现即可(不验证像素)。 */
const meow_ui_theme_t *s_ui_theme(void) { static meow_ui_theme_t th; return &th; }
int meow_ui_width(void) { return 480; }
void meow_ui_fill_rect(int x, int y, int w, int h, uint32_t color)
{ (void)x; (void)y; (void)w; (void)h; (void)color; }
void meow_ui_text_center(int cx, int y, const char *utf8, int size, uint32_t color)
{ (void)cx; (void)y; (void)utf8; (void)size; (void)color; }
int meow_ui_text_height(int size) { return size; }
void meow_dirty_full(void) {}
void meow_ui_flush(void) {}
char *popen_read_tmo(int tmo_ms, const char *cmd)
{
(void)tmo_ms;
/* 直连回退(不可达分支)会跑 wpa_cli 剧本:list_networks/add_network/status/save_config。
* 桩按子命令给近似真机的应答——旧桩统一返回 "0\n",会让 nm_direct_connect_ex 等满 10s
* 再报 MEOW_ERR_NODEV(该断言因此长期失败;M47 顺手把桩修成可收敛)。 */
if (strstr(cmd, " status")) return strdup("wpa_state=COMPLETED\nip_address=192.168.1.5\nssid=Home2G\n");
if (strstr(cmd, "list_networks")) return strdup("network id / ssid / bssid / flags\n0\tHome2G\t00:11:22:33:44:01\t[WPA-PSK-CCMP]\n");
return strdup("OK\n");
}
/* ---------- M47:net.c 三态直连探测桩(daemon 不可达时 nm_*_power_state 落到这里) ---------- */
int nm_bt_power_state(void); /* netmgr_ipc.c(头文件不在本单改动范围) */
static int g_bt_probe = -1, g_wifi_probe = -1;
int net_bt_power_probe(void) { return g_bt_probe; }
int net_wifi_power_probe(void) { return g_wifi_probe; }
/* M47:开关收敛判定纯函数的实现放在 bt_page.c,矩阵测试在 test_bt_page.c;本测试只验
* nm_*_power_state 的解析/回退与 wifi_rows 的未知态渲染,故这里给可链接的最小桩。 */
int meow_toggle_display(int intent, int last, int ok, int probe, int to)
{ (void)intent; (void)ok; (void)to; return (probe >= 0) ? probe : last; }
int meow_toggle_pending(int intent, int last, int ok, int probe, int to)
{ (void)intent; (void)last; (void)ok; (void)probe; (void)to; return 0; }
int meow_toggle_rollback(int intent, int last, int ok, int probe, int to)
{ (void)intent; (void)last; (void)ok; (void)probe; (void)to; return 0; }
int meow_toggle_error(int intent, int last, int ok, int probe, int to)
{ (void)intent; (void)last; (void)ok; (void)probe; (void)to; return 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];
/* 宿主 /tmp 可能不可写(Android 上 /tmp 属 shell):优先 TMPDIR。 */
snprintf(sock, sizeof sock, "%s/meow_net_test_%d.sock",
getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp", (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);
/* ---- ③ M47:开关真实态三态查询(daemon 权威解析) ---- */
{
f.bt_status_text = "bt available=1 powered=1 address=00:1A:7D:DA:71:13 paired=2 connected=0";
assert(nm_bt_power_state() == 1 && "bt status powered=1 → 1(开)");
f.bt_status_text = "bt available=1 powered=0 address=00:1A:7D:DA:71:13 paired=2 connected=0";
assert(nm_bt_power_state() == 0 && "bt status powered=0 → 0(关)");
f.bt_status_text = "bt available=0 powered=0 address=- paired=-1 connected=-1";
assert(nm_bt_power_state() == 0 && "无控制器(available=0) → 0");
f.bt_status_text = "bt available=1 address=00:1A:7D:DA:71:13 paired=-1 connected=-1";
assert(nm_bt_power_state() == -1 && "缺 powered 字段 → -1(未知,不猜 0)");
g_wifi_probe = -1;
assert(nm_wifi_power_state() == -1 && "wifi 探测失败 → -1(不再默认已开)");
g_wifi_probe = 1;
assert(nm_wifi_power_state() == 1 && "wifi 探测开 → 1");
g_wifi_probe = 0;
assert(nm_wifi_power_state() == 0 && "wifi 探测关 → 0");
printf("[F] nm_bt_power_state/nm_wifi_power_state 三态解析 PASS\n");
}
/* ---- ④ 降级占位: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");
}
/* ---- ⑤ M47:daemon 不可达时真实态回退直连探测;探测失败=未知(不当已关/已开) ---- */
g_bt_probe = -1;
assert(nm_bt_power_state() == -1 && "BT daemon 不可达 + 直连探测失败 → -1");
g_bt_probe = 0;
assert(nm_bt_power_state() == 0 && "BT daemon 不可达 + 直连探测关 → 0");
g_wifi_probe = -1;
assert(nm_wifi_power_state() == -1 && "WiFi 探测失败 → -1");
{
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, "WiFi 开关状态未知") && "未知态 → 行内第三态占位");
printf("[G] 不可达回退三态: BT/WiFi 失败=未知, 行内第三态 PASS\n");
}
printf("ALL PASS\n");
unlink(sock);
return 0;
}