fix(reconnect): 上一次会话的关闭码污染下一次会话(v0.2.1)+ 重连链路的真实 WebSocket 集成测试

ReconnectPolicyTest(11 项)验的是**纯函数**「给定关闭码应该怎么退避」,而 ⑦
「连接频繁重连」的缺陷往往出在**调用点**。新增 PoseWebSocketReconnectTest(8 项):
用 MockWebServer.withWebSocketUpgrade 在 JVM 上起**真实** WebSocket 服务端
(能按 §3.2 回 welcome、按 §3.6 回 pong、按指定关闭码关连接),无需设备即可覆盖
「该重连的」与「不该重连的」两条分支。Robolectric 只用来提供 android.os.Build 桩。

它抓到的真实缺陷(本版修复):
  上一次会话的关闭码会污染下一次会话,导致重新开始推流后失去自动重连能力。
  复现:第一次连接被对端以 1001 关闭(按 §7 不重连,正确)→ 用户再点「开始推流」
  → 这一次是网络层失败(服务端还没起来),按 §7 **应当**重连 → 但 connect() 只重置了
  reconnectCounter 与 lastPongAtMs,**没有清 lastCloseCode** → scheduleReconnect()
  仍按 1001 判定 → 直接 stopped,**再也不会自动重连**。
  测试失败信息(修复前):
    ✗ 上一次 1001 关闭后重新连接,遇到网络失败仍应按策略重连
      AssertionError: 当前 state=STOPPED,lastCloseCode=1001,attempts=0

修复(PoseWebSocketClient.kt):
  * connect():清掉会话级关闭状态(lastCloseCode=0、lastCloseReason="");
  * onFailure():记录 recordClose(0, …)(契约 §7 中 0=未收到关闭帧 → 应重连),
    顺带让界面「最近一次断开原因」显示本次失败而不是旧值。

用例清单:握手→streaming 与 welcome 保活参数解析、按协商间隔发 ping 收 pong、
1011 按退避重连且等待落在策略 ±20% 区间、成功后计数清零、1001/1002 不重连、
主动断开不重连、上一次 1001 后再连遇网络失败仍应重连(本次回归锁)、
服务端未启动时 1.5s 内 ≤3 次尝试(退避生效)。

实测:./gradlew :app:testDebugUnitTest → 6 个测试类 / 64 项用例 / 0 失败
(原 56 项);build/verify.sh 全绿(签名、APK 版本元数据 0.2.1/201、native 库体检、
ABI、R8、dex)。版本号 0.2.0/200 → 0.2.1/201。

踩坑:第一版 MockWebServer 夹具的 101 响应没有回显 Sec-WebSocket-Protocol
(RFC 6455 要求),客户端记录的 negotiatedProtocol 为空导致断言失败 —— 那是
**夹具**不合规而非客户端问题,已在夹具里补上。写「真实对端」夹具时要按 RFC/契约
把服务端做对,否则会把夹具缺陷误判成产品缺陷。
This commit is contained in:
2026-09-17 05:44:03 +08:00
parent 95429e8ce3
commit 6dfd90f7ca
7 changed files with 462 additions and 16 deletions
+15 -2
View File
@@ -1,5 +1,5 @@
================================================================
OpenDance Studio -- Android 辅助 App v0.2.0 使用说明
OpenDance Studio -- Android 辅助 App v0.2.1 使用说明
================================================================
手机端姿态采集器:用摄像头做端侧姿态推理,把 33 个骨骼关键点通过
@@ -125,6 +125,19 @@ WebSocket 推送给 Windows 客户端,由后者驱动 VRM 角色并实时评
App 运行页 「人体识别」为「已识别」,出现骨架叠加(黄点好、红点置信度低)
Windows 端 显示已连接并收到关键点,VRM 模型随动作实时动起来
6.5) v0.2.1 修订
1. 重连链路的真实 WebSocket 集成测试(T-AND-016): 用 MockWebServer 的
withWebSocketUpgrade 在 JVM 上起真实 WebSocket 服务端,覆盖「该重连的」
(异常关闭码 1011 / 服务端未启动)与「不该重连的」(1000/1001/1002 /
用户主动断开)两条分支,并断言重连等待取自策略、落在 ±20% 区间、
成功后计数清零。无需设备。
2. 修一处真实缺陷: 上一次会话的关闭码会污染下一次会话 —— 第一次被 1001 关闭后
用户重新开始推流,若这一次是网络层失败,客户端仍按 1001 判定「不重连」,
于是再也不会自动重连,只能反复手点。现在 connect() 会清掉会话级关闭状态,
网络失败按「未收到关闭帧(0)」记录并按 §7 重连。
3. 单元测试 64 项(原 56 项)。
7) 自检清单
./gradlew testDebugUnitTest
@@ -144,7 +157,7 @@ WebSocket 推送给 Windows 客户端,由后者驱动 VRM 角色并实时评
依赖闭包(DT_NEEDED,非系统库必须同 ABI 自带)、页对齐(16 KB 兼容)。
0) 发布包级验收(对**发布包**而不是本地构建;文档 §7.5)
bash build/accept-release.sh dist-release/opendance-android-v0.2.0.zip
bash build/accept-release.sh dist-release/opendance-android-v0.2.1.zip
预期末行「发布包级验收全部通过」。它会校验 sha256、解压、核对文件清单、
读包内 APK 的 AndroidManifest 版本元数据、核对 ABI 与 MediaPipe native 库、
模型资源、dex 里的 PoseLandmarker、验签,以及包内契约快照与仓库是否一致。