Parse window focus into declaration records, normalize explicit null references once, and preserve original named lines in Evidence. Keep fallback based on declaration presence and retain unknown focus when both sections omit fields. Validation: 86 focused tests including public CLI/MCP with advertised schemas, plus docs checks and five conformance tests. Fake ADB only; no physical device verified. Refs #4608
115 lines
4.1 KiB
TypeScript
115 lines
4.1 KiB
TypeScript
import { readFile, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { expect, onTestFinished } from "vitest";
|
|
|
|
import { AdbProvider } from "../../../src/adb/AdbProvider.js";
|
|
import { toolContract } from "../../../src/contracts/toolContracts.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";
|
|
|
|
for (const { serial, section, focused, missing } of [
|
|
{ serial: "null-device", section: "windows", focused: false, missing: false },
|
|
{
|
|
serial: "fallback-null-device",
|
|
section: "displays",
|
|
focused: false,
|
|
missing: false,
|
|
},
|
|
{
|
|
serial: "focused-device",
|
|
section: "windows",
|
|
focused: true,
|
|
missing: false,
|
|
},
|
|
{
|
|
serial: "missing-device",
|
|
section: "displays",
|
|
focused: false,
|
|
missing: true,
|
|
},
|
|
]) {
|
|
cliTest.skipIf(process.platform === "win32")(
|
|
`preserves focus presence through CLI and MCP: ${serial}`,
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory("rea-adb-window-focus-");
|
|
const binary = join(root, "adb");
|
|
await writeFile(
|
|
binary,
|
|
`#!/bin/sh
|
|
if [ "$1" = version ]; then
|
|
printf 'Android Debug Bridge version 1.0.41\\nVersion 34.0.5-fixture\\nInstalled as %s\\n' "$0"
|
|
exit 0
|
|
fi
|
|
if [ "$1" = -s ] && [ "$3" = shell ] && [ "$4" = dumpsys ] && [ "$5" = window ]; then
|
|
printf '%s\\n' "$6" >> "$0.calls"
|
|
if [ "$2" = missing-device ]; then printf 'no focus declarations\\n'; exit 0; fi
|
|
if [ "$2" = fallback-null-device ] && [ "$6" = windows ]; then printf 'no focus declarations\\n'; exit 0; fi
|
|
if [ "$2" = focused-device ]; then
|
|
printf ' mCurrentFocus=Window{current}\\n mFocusedApp=ActivityRecord{current}\\n'
|
|
else
|
|
printf ' mCurrentFocus=null\\n mFocusedApp=null\\n'
|
|
fi
|
|
exit 0
|
|
fi
|
|
exit 1
|
|
`,
|
|
{ mode: 0o755 },
|
|
);
|
|
const output = await cli.run({
|
|
arguments: ["inspect-adb-window", serial, "--json"],
|
|
environment: { REA_ADB_PATH: binary },
|
|
});
|
|
expect(output.exitCode).toBe(0);
|
|
const provider = new AdbProvider({
|
|
environment: { REA_ADB_PATH: binary },
|
|
});
|
|
const harness = await createApplicationMcpHarness({
|
|
serverOptions: { adbDeviceAnalysis: provider },
|
|
});
|
|
onTestFinished(() => harness.close());
|
|
const schemas = await advertisedMcpToolSchemas(
|
|
harness.client,
|
|
"inspect_adb_window",
|
|
);
|
|
const args = { serial };
|
|
expect(schemas.ajv.validate(schemas.inputSchema, args)).toBe(true);
|
|
const response = await harness.client.callTool({
|
|
name: schemas.name,
|
|
arguments: args,
|
|
});
|
|
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(
|
|
toolContract("inspect_adb_window").outputSchema.parse(raw),
|
|
);
|
|
expect(evidence.normalized_result).toMatchObject({
|
|
focused_window: focused ? "Window{current}" : null,
|
|
focused_app: focused ? "ActivityRecord{current}" : null,
|
|
observed_lines: missing
|
|
? []
|
|
: [
|
|
` mCurrentFocus=${focused ? "Window{current}" : "null"}`,
|
|
` mFocusedApp=${focused ? "ActivityRecord{current}" : "null"}`,
|
|
],
|
|
});
|
|
expect(evidence.limitations).toContainEqual(
|
|
missing
|
|
? expect.stringContaining("focus is unknown, not absent")
|
|
: `Focus declarations were read from \`dumpsys window ${section}\``,
|
|
);
|
|
}
|
|
const sections =
|
|
section === "windows" ? ["windows"] : ["windows", "displays"];
|
|
expect(
|
|
(await readFile(`${binary}.calls`, "utf8")).trim().split("\n"),
|
|
).toEqual([...sections, ...sections]);
|
|
},
|
|
);
|
|
}
|