Use the owning DEX/JVM and Ark/JavaScript component lists for runtime summaries. Remove the independent filename checks and Harmony path copy. Preserve native format checks and framework path conventions. Adapt @eco-null's proposal and regressions from PR #3627. Cover all eight supported source extensions and unrelated content, plus the Android inverse mismatch discovered during consumer review: .dex/.class files had no bytecode components but produced runtime hints. Validation: 44 focused checks, build, check:fast and docs:check pass. Six Harmony assertions and one Android built CLI assertion fail before this fix. 36 actual built CLI cost controls preserve four ordinary complete replies and projection IDs byte for byte. No heavy profiling, target or real deep-provider execution; npm release unverified. Fixes #3350 Fixes #4782
64 lines
2.6 KiB
TypeScript
64 lines
2.6 KiB
TypeScript
import { writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
import { TextReader, Uint8ArrayWriter, ZipWriter } from "@zip.js/zip.js";
|
|
import { expect } from "vitest";
|
|
|
|
import { androidApplicationProjectionResultSchema } from "../../../src/domain/android/androidApplication.js";
|
|
import { parseEvidence } from "../../../src/domain/evidence.js";
|
|
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { cliTest } from "../../support/cli/cliFixture.js";
|
|
|
|
cliTest(
|
|
"Android runtime hints follow classified bytecode through CLI and MCP",
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory("rea-android-runtime-hints-");
|
|
const environment = { HOME: root, USERPROFILE: root };
|
|
const { call } = await connectLocalToolsMcp(environment);
|
|
for (const prefix of ["", "named"]) {
|
|
const writer = new ZipWriter(new Uint8ArrayWriter());
|
|
await writer.add("AndroidManifest.xml", new TextReader("fixture"));
|
|
for (const extension of ["dex", "class"])
|
|
await writer.add(
|
|
`assets/${prefix}.${extension}`,
|
|
new TextReader("ordinary text"),
|
|
);
|
|
const path = join(root, `${prefix || "dot-names"}.apk`);
|
|
await writeFile(path, await writer.close());
|
|
const inspected = await cli.run({
|
|
arguments: ["inspect-artifact", path, "--json"],
|
|
environment,
|
|
});
|
|
expect(inspected.exitCode, inspected.stderr).toBe(0);
|
|
const source = parseEvidence(inspected.json);
|
|
const input = { inventory_evidence: [source] };
|
|
const projected = await cli.run({
|
|
arguments: [
|
|
"project-android-application-graph",
|
|
JSON.stringify(input),
|
|
"--json",
|
|
],
|
|
environment,
|
|
});
|
|
expect(projected.exitCode, projected.stderr).toBe(0);
|
|
const evidence = parseEvidence(projected.json);
|
|
const graph = androidApplicationProjectionResultSchema.parse(
|
|
evidence.normalized_result,
|
|
);
|
|
expect(graph.components.dex.map(({ path }) => path)).toEqual(
|
|
prefix ? ["assets/named.dex"] : [],
|
|
);
|
|
expect(graph.components.jvm_classes.map(({ path }) => path)).toEqual(
|
|
prefix ? ["assets/named.class"] : [],
|
|
);
|
|
expect(graph.runtime_families).toEqual(
|
|
prefix ? ["dalvik-art", "java-kotlin"] : [],
|
|
);
|
|
const mcp = await call("project_android_application_graph", input);
|
|
expect(mcp.isError).not.toBe(true);
|
|
expect(parseEvidence(mcp.structuredContent)).toEqual(evidence);
|
|
}
|
|
},
|
|
);
|