Files
rea/tests/boundary/cli/adbWindowDisplays.test.ts
morluto 7086768a27 fix(adb): preserve display-scoped window focus declarations
Retain every focus declaration with its display header and identity, including explicit absence, unknown identities and duplicates. Derive singular summaries only for one unambiguous context; preserve scope and ambiguity through named CLI/MCP contracts.

Validation: original public CLI replay before/after, four failing baseline parser controls, 23 focused checks plus 11 overlapping provider/parser controls, converted MCP schemas, check:fast and docs:check. Bounded completed synthetic CLI warm reply tokens 282/513, elapsed 0.391/0.403s and CPU 0.622/0.622s; no efficiency claim. No live Android, vendor or large-output qualification. Refs #4660.
2026-10-17 13:40:09 +08:00

102 lines
4.2 KiB
TypeScript

import { readFile, writeFile } from "node:fs/promises";
import { dirname, join } from "node:path";
import { expect, onTestFinished } from "vitest";
import { AdbProvider } from "../../../src/adb/AdbProvider.js";
import { parseEvidence } from "../../../src/domain/evidence.js";
import { createApplicationMcpHarness } from "../../fixtures/applicationMcpHarness.js";
import { advertisedMcpToolSchemas } from "../../fixtures/advertisedMcpTool.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
const inactive =
" Display: mDisplayId=3 rootTasks=0\n mCurrentFocus=null\n mFocusedApp=null\n";
const active =
" Display: mDisplayId=0 rootTasks=12\n mCurrentFocus=Window{active}\n mFocusedApp=ActivityRecord{active}\n";
for (const { serial, capture, ids } of [
{ serial: "inactive-first", capture: inactive + active, ids: ["3", "0"] },
{ serial: "active-first", capture: active + inactive, ids: ["0", "3"] },
{ serial: "active-only", capture: active, ids: ["0"] },
]) {
cliTest.skipIf(process.platform === "win32")(
`retains display-scoped focus through CLI and MCP: ${serial}`,
async ({ cli }) => {
const root = await createTestTempDirectory("rea-adb-display-focus-");
const binary = join(root, "fake-adb.mjs");
await writeFile(
binary,
`#!/usr/bin/env node
import { appendFileSync } from 'node:fs';
const args = process.argv.slice(2);
if (args.length === 1 && args[0] === 'version') {
console.log('Android Debug Bridge version 1.0.41\\nVersion 34.0.5-fixture');
} else if (args[0] === '-s' && args[1] === ${JSON.stringify(serial)} && args[2] === 'shell' && args[3] === 'dumpsys' && args[4] === 'window') {
appendFileSync(process.argv[1] + '.calls', args[5] + '\\n');
process.stdout.write(args[5] === 'windows' ? 'no focus declarations\\n' : ${JSON.stringify(capture)});
} else process.exitCode = 9;
`,
{ mode: 0o700 },
);
const environment = {
REA_ADB_PATH: binary,
HOME: root,
TMPDIR: root,
PATH: `${dirname(process.execPath)}:/usr/bin:/bin`,
};
const output = await cli.run({
arguments: ["inspect-adb-window", serial, "--json"],
environment,
});
expect(output.exitCode).toBe(0);
expect(output.stderr).toBe("");
const harness = await createApplicationMcpHarness({
serverOptions: { adbDeviceAnalysis: new AdbProvider({ environment }) },
});
onTestFinished(() => harness.close());
const schemas = await advertisedMcpToolSchemas(
harness.client,
"inspect_adb_window",
);
const response = await harness.client.callTool({
name: schemas.name,
arguments: { serial },
});
expect(response.isError).not.toBe(true);
expect(
schemas.ajv.validate(schemas.outputSchema, response.structuredContent),
).toBe(true);
for (const raw of [output.json, response.structuredContent]) {
const evidence = parseEvidence(raw);
expect(evidence.normalized_result).toMatchObject({
serial,
source_section: "displays",
focus_summary: ids.length === 1 ? "single_context" : "ambiguous",
focused_window: ids.length === 1 ? "Window{active}" : null,
focused_app: ids.length === 1 ? "ActivityRecord{active}" : null,
observed_lines: capture.trimEnd().split("\n"),
focus_contexts: ids.map((id) => ({
scope: "display",
display_id: id,
declarations: [
{
field: "mCurrentFocus",
value: id === "0" ? "Window{active}" : null,
},
{
field: "mFocusedApp",
value: id === "0" ? "ActivityRecord{active}" : null,
},
],
})),
});
if (ids.length > 1)
expect(evidence.limitations).toContainEqual(
expect.stringContaining("prevent a singular focus summary"),
);
}
expect(
(await readFile(binary + ".calls", "utf8")).trim().split("\n"),
).toEqual(["windows", "displays", "windows", "displays"]);
},
);
}