M1: 桥引擎单测跑通,桥核心覆盖率 87.3%(T-M1-08 完成,R-18 处置)

- 修复 2 个测试用例(漏先触发 flushMessageQueue 注册 _fetchQueue 回调)
- 补 set_default/remove、FakeWebController、send 用例,共 15 用例全通过
- 桥核心覆盖率 87.3%(MessageCodec/Message 100%、BridgeController 83%、
  HandlerRegistry 88%)≥80% 达标;Adapter/BridgeJsLoader 系统耦合由 V1 真机覆盖
- scripts/test.sh:本地单测 + 覆盖率运行脚本(devecocli 无 test,用 DevEco hvigorw
  + 强制 DEVECO_SDK_HOME 指向自带 sdk);ci.sh 集成单测(SKIP_TESTS=1 可跳过)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lanterngamescn
2026-06-25 12:26:23 +08:00
co-authored by Claude Opus 4.8
parent 38307e8d71
commit 159dcf2687
3 changed files with 77 additions and 5 deletions
+42
View File
@@ -67,6 +67,34 @@ export default function bridgeTest() {
r.default()('x', (d) => { resp = d; }); r.default()('x', (d) => { resp = d; });
expect(resp).assertEqual(''); expect(resp).assertEqual('');
}); });
it('set_default_and_remove', 0, () => {
const r = new HandlerRegistry();
let v = '';
r.setDefault((d, cb) => cb('D:' + d));
r.default()('x', (s) => { v = s; });
expect(v).assertEqual('D:x');
r.put('a', (_d, cb) => cb('A'));
expect(r.size()).assertEqual(1);
r.remove('a');
expect(r.has('a')).assertFalse();
});
});
describe('FakeWebController', () => {
it('records_and_clears', 0, () => {
const f = new FakeWebController();
expect(f.lastScript() === undefined).assertTrue();
f.runJavaScript('s1');
f.loadUrl('u1');
expect(f.scripts.length).assertEqual(1);
expect(f.loadedUrls.length).assertEqual(1);
expect(f.lastScript()).assertEqual('s1');
f.dispose();
f.clear();
expect(f.scripts.length).assertEqual(0);
expect(f.lastScript() === undefined).assertTrue();
});
}); });
describe('BridgeController', () => { describe('BridgeController', () => {
@@ -104,6 +132,16 @@ export default function bridgeTest() {
expect(last.includes('getlocationinfo')).assertTrue(); expect(last.includes('getlocationinfo')).assertTrue();
}); });
it('send_no_handlerName', 0, () => {
const fake = new FakeWebController();
const bridge = new BridgeController(fake);
bridge.onPageEnd();
bridge.send('hello');
const last = fake.lastScript() ?? '';
expect(last.includes('_handleMessageFromNative')).assertTrue();
expect(last.includes('hello')).assertTrue();
});
it('startup_queue_flush_after_onPageEnd', 0, () => { it('startup_queue_flush_after_onPageEnd', 0, () => {
const fake = new FakeWebController(); const fake = new FakeWebController();
const bridge = new BridgeController(fake); const bridge = new BridgeController(fake);
@@ -128,6 +166,8 @@ export default function bridgeTest() {
const m = sent.match(/JAVA_CB_[0-9]+_[0-9]+/); const m = sent.match(/JAVA_CB_[0-9]+_[0-9]+/);
expect(m !== null).assertTrue(); expect(m !== null).assertTrue();
const cbId = m![0]; const cbId = m![0];
// 先触发 flushMessageQueue 注册 _fetchQueue 回调,回执经队列机制路由
bridge.onLoadIntercept('yy://__queue__/');
bridge.onLoadIntercept('yy://return/_fetchQueue/' + bridge.onLoadIntercept('yy://return/_fetchQueue/' +
encodeURIComponent('[{"responseId":"' + cbId + '","responseData":"1700"}]')); encodeURIComponent('[{"responseId":"' + cbId + '","responseData":"1700"}]'));
expect(resp).assertEqual('1700'); expect(resp).assertEqual('1700');
@@ -137,6 +177,8 @@ export default function bridgeTest() {
const fake = new FakeWebController(); const fake = new FakeWebController();
const bridge = new BridgeController(fake); const bridge = new BridgeController(fake);
bridge.onPageEnd(); bridge.onPageEnd();
// 先触发 flushMessageQueue 注册 _fetchQueue 回调
bridge.onLoadIntercept('yy://__queue__/');
// 未注册的 handler 名 → 默认兜底,不抛错 // 未注册的 handler 名 → 默认兜底,不抛错
bridge.onLoadIntercept('yy://return/_fetchQueue/' + bridge.onLoadIntercept('yy://return/_fetchQueue/' +
encodeURIComponent('[{"handlerName":"finsh","data":"","callbackId":"cb9"}]')); encodeURIComponent('[{"handlerName":"finsh","data":"","callbackId":"cb9"}]'));
+6 -5
View File
@@ -21,10 +21,11 @@ fi
echo "==> [CI] 多模块构建校验:devecocli build --build-mode ${MODE}" echo "==> [CI] 多模块构建校验:devecocli build --build-mode ${MODE}"
devecocli build --build-mode "${MODE}" devecocli build --build-mode "${MODE}"
# 单元测试:当前 devecocli 无 test 子命令;待 M1 桥引擎单测(T-M1-08, # 单元测试 + 覆盖率(本地单测)。devecocli 无 test 子命令,故经 DevEco hvigorw(见 test.sh)。
# MessageCodec/HandlerRegistry/BridgeController ≥80% 覆盖)落地并确定运行方式 # 需 DevEco 环境;CI runner 无 DevEco 时设 SKIP_TESTS=1 跳过。
# (hypium)后,在此启用,例如: if [ "${SKIP_TESTS:-0}" != "1" ]; then
# echo "==> [CI] 单元测试" echo "==> [CI] 单元测试 + 覆盖率"
# <hypium 本地单测运行命令> bash "${ROOT}/scripts/test.sh"
fi
echo "==> [CI] 校验通过 (${MODE})" echo "==> [CI] 校验通过 (${MODE})"
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# 本地单元测试 + 覆盖率(T-M1-08,R-18 处置)。
#
# devecocli 无 test 子命令,故用 DevEco 自带 hvigorw 跑本地单测(已获授权的例外)。
# 注意:本机环境变量 DEVECO_SDK_HOME 可能指向不含目标 SDK 的位置,这里强制指向
# DevEco Studio 自带 sdk(含 6.1.1(24))。CI 环境需装 DevEco 或用 DEVECO_HOME 覆盖。
#
# 用法:bash scripts/test.sh
# 报告:entry/.test/default/outputs/test/reports/(含 coverageReport.json + html
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
DEVECO="${DEVECO_HOME:-/c/Program Files/Huawei/DevEco Studio}"
export DEVECO_SDK_HOME="${DEVECO_SDK_HOME_OVERRIDE:-${DEVECO}/sdk}"
NODE="${DEVECO}/tools/node/node.exe"
HVIGOR="${DEVECO}/tools/hvigor/bin/hvigorw.js"
echo "==> [test] 本地单测 + 覆盖率 (DEVECO_SDK_HOME=${DEVECO_SDK_HOME})"
"${NODE}" "${HVIGOR}" test \
--mode module \
-p module=entry@default \
-p product=default \
-p buildMode=test \
-p coverage=true \
"$@"
echo "==> [test] 完成。覆盖率报告:entry/.test/default/outputs/test/reports/coverageReport.json"