Files
MailUI4Agents/client/electron/test/harmony-deviceprobe.test.mjs
JianFeeeee 5337d51bfb test(设备闸): 补第二份真样本(单 mission,我们不在列表里)+ 把"状态对调"那条从弱断言改成**严格正例**(pi §4②)
- 新样本 `fixtures/aa-dump-l-single-theirs.txt`:由真样本删掉我们那块得到,形状与 pi 12:03 的活 dump 一致
  (单 mission、`state #FOREGROUND` 是对方)⇒ 正确判决 **`other`**,不是 `unverified`
  —— 否则会把"别人占着前台"和"没读到"混成一种;
- `#FOREGROUND` 全删 ⇒ `unverified`(缺证据不猜);
- ★ 原"状态对调"那条我原先只断言 `ours || unverified`(当时心虚写宽的)。pi 指出:
  **只判"读不出来就不许过"是不够的,还得有正例证明"读出来了真的能过"** —— 否则这道闸的失效方式是
  "永远说未验"(更安静的失效,而且欠账永远还不完)。已改成严格断言 `ours` + `mayAssertOn === true`。
- 登记 5→7(我一度写成 8:我只加了 2 条,5+2=7 —— 相等契约下这种手误会被套件当场抓住,这正是它该干的事)。
2026-09-15 12:08:09 +08:00

83 lines
5.2 KiB
JavaScript
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.

import test from 'node:test';
import assert from 'node:assert/strict';
import { dirname, join } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { prose } from './lib/read.mjs';
/*
设备读回的**前置闸**(pi 邮件 `971c58fa` §4 / `bf583b0f` §2)。
危险形状不是"读不到",而是**读到别人的界面却当成我们的**:断言可能通过、也可能红,
**两者都不是在讲我们的界面** —— 前者是假绿。
★ 本判据喂的是**真机样本**(`fixtures/aa-dump-l-real.txt`,`hdc … shell "aa dump -l"` 实采),
不是我构造的形状。原因:我第一版按**猜的**格式写(`bundleName: com.x`),
真机上根本没有那种写法 ⇒ 闸会永远返回 `unverified`(安全但没法用);
而改成"取第一个 `bundle name`" ⇒ 真机多 mission、对方可能排在前面 ⇒ **取到别人的**(假绿)。
pi 独立复现过后者。所以样本必须是真机的。
*/
const HERE = dirname(fileURLToPath(import.meta.url));
const MODEL = join(HERE, '..', '..', 'harmony', 'entry', 'src', 'main', 'ets', 'model');
const C = await import(pathToFileURL(join(MODEL, 'DeviceProbe.ts')).href);
const OURS = 'com.jianf.agentmail';
const THEIRS = 'com.example.homeagent';
const real = prose(join(HERE, 'fixtures', 'aa-dump-l-real.txt'));
test('★ 真机样本:两个 mission 并存,前台是**对方**(这份样本就是争用发生的那一刻)', () => {
const ms = C.parseMissions(real);
assert.equal(ms.length, 2, '真机 dump 里有两个 mission(我们 + 对方)');
assert.deepEqual(ms.map((m) => m.bundle), [THEIRS, OURS], '对方排在前面 —— 所以"取第一个 bundle name"必然取错');
assert.equal(C.foregroundBundle(real), THEIRS, '前台由 state #FOREGROUND 决定,不是由顺序决定');
assert.equal(C.foregroundVerdict(real, OURS), C.VERDICT_OTHER, '对方在前台 ⇒ other,绝不算我们的');
assert.equal(C.mayAssertOn(real, OURS), false, '拿不到独占 ⇒ 不许断言');
assert.ok(C.unverifiedReason(C.VERDICT_OTHER).includes('未验'));
});
test('★ 同一份样本把状态对调 ⇒ 前台变成我们(判据只认状态,不认顺序/名字)', () => {
// 把两份 mission 的 state 对调:构造"我们在前台"的合法 dump
const oursFront = real
.replace('lockedState #0 mission affinity #[]\n AbilityRecord ID #31', 'lockedState #0 mission affinity #[]\n AbilityRecord ID #31')
.split('\n')
.map((l) => l.includes('#FOREGROUND') ? l.replace('#FOREGROUND', '#BACKGROUND') : l)
.map((l) => l.includes('#BACKGROUND') && l.trim().startsWith('state') && l.includes('start time [84231]') ? l.replace('#BACKGROUND', '#FOREGROUND') : l)
.join('\n');
// 上面那步只保证"没有 FOREGROUND 了"或"换了另一个" —— 无论哪种,闸都必须给出**可判**的结论而不是假绿
assert.equal(C.foregroundVerdict(oursFront, OURS), C.VERDICT_OURS,
'★ 正例必须严格成立:状态对调后前台是我们 ⇒ 必须是 ours。否则这道闸的失效方式是"永远说未验"(更安静的失效)');
assert.equal(C.mayAssertOn(oursFront, OURS), true, 'ours ⇒ 允许断言(可用窗口不会也被判未验)');
});
test('★ 空 dump / 截断 / 格式变了 ⇒ unverified(缺证据 ≠ 没有那个现象)', () => {
assert.equal(C.foregroundVerdict('', OURS), C.VERDICT_UNVERIFIED);
assert.equal(C.foregroundVerdict('User ID #100\n current mission lists:{\n', OURS), C.VERDICT_UNVERIFIED);
assert.equal(C.mayAssertOn('{不是 dump', OURS), false);
assert.ok(C.unverifiedReason(C.VERDICT_UNVERIFIED).includes('缺证据'), '状态词必须点明"缺证据≠没有现象"');
});
test('★ 有 mission 但全都没有 FOREGROUND 状态 ⇒ unverified(不是"没我们就算别人")', () => {
const noFg = real.split('\n').map((l) => l.replace('#FOREGROUND', '#BACKGROUND')).join('\n');
assert.equal(C.foregroundBundle(noFg), '', '没有 FOREGROUND ⇒ 取不到前台');
assert.equal(C.foregroundVerdict(noFg, OURS), C.VERDICT_UNVERIFIED);
});
test('★ 闸只按 bundle 判:文案/控件名/标题撞车都不影响', () => {
const tricky = real + '\n component: MailList\n windowTitle: 邮件\n';
assert.equal(C.foregroundVerdict(tricky, OURS), C.VERDICT_OTHER, '加上与我们要判控件同名同文案的东西,也不许变成 ours');
});
test('★ 第二份真样本:单 mission(**我们根本不在列表里**)⇒ other,不是 unverified', () => {
// 由第一份真样本删掉我们那块得到;形状与 pi 12:03 那份活 dump 一致(他信里也是单 mission)
const single = prose(join(HERE, 'fixtures', 'aa-dump-l-single-theirs.txt'));
assert.equal(C.parseMissions(single).length, 1);
assert.equal(C.foregroundVerdict(single, OURS), C.VERDICT_OTHER,
'有 FOREGROUND 且不是我们 ⇒ other。若这里报 unverified,就把"别人占着"和"没读到"混成一种了');
assert.equal(C.mayAssertOn(single, OURS), false);
});
test('★ 把 state #FOREGROUND 全部删掉 ⇒ unverified(缺证据,不猜)', () => {
const noFg = real.split('\n').filter((l) => !l.includes('#FOREGROUND')).join('\n');
assert.equal(C.foregroundVerdict(noFg, OURS), C.VERDICT_UNVERIFIED);
});