mirror of
https://github.com/software-mansion/argent.git
synced 2026-09-14 19:27:14 +08:00
d34ac458e4
## Problem
`profiler-stack-query` and `profiler-combined-report` reject Android
devices with:
> Tool '…' is not supported on android emulator (no android support
declared)
even though **both tools already contain complete, working Android
implementations**:
- `profiler-stack-query` has `executeAndroid` (queries the Perfetto
`.pftrace` via `runAndroidStackQuery`) and an `api.platform ===
"android"` dispatch branch.
- `profiler-combined-report` has an `api.platform === "android"` branch
using `loadAndroidCombinedData` and `buildPerfettoAnchor`.
The only thing gating Android was the `capability` declaration: `{
apple: { simulator: true, device: true } }` with no `android` block.
`assertSupported` (`packages/tool-server/src/utils/capability.ts`)
throws `UnsupportedOperationError` for any platform without a declared
block — so it rejected the device *before* the Android code path could
run.
This was a broken workflow: the sibling `native-profiler-analyze` (which
**does** declare Android support) and `profiler-load`'s `load_native`
output both tell Android users to drill down with `profiler-stack-query`
— which then refused.
## Fix
Add `android: { emulator: true, device: true, unknown: true }` to the
`capability` of both tools, matching `native-profiler-analyze`. The
existing `apple` block is unchanged. No code in the handlers needed to
change — the Android paths were already there.
### `leak_stacks` on Android
`leak_stacks` mode is iOS-only. `executeAndroid` already guards it: it
returns a clean guidance message (`"Memory leak detection is not yet
supported on Android. … adb shell am dumpheap"`) *before* touching the
trace, so enabling the capability does not let `leak_stacks` reach any
crashing code path. A test pins this behavior.
## Tests
New
`packages/tool-server/test/android-perfetto/profiler-query-android-capability.test.ts`:
- both tools are supported on Android emulator / device / unknown,
- both tools are still supported on Apple simulator + device,
- `profiler-stack-query` `leak_stacks` on Android returns the iOS-only
guidance message without querying the trace.
## Verification
- `npm test -w @argent/tool-server` — 131 files, 1158 tests passed.
- `npm test --workspaces` — all package suites green.
- `npm run build` (`tsc --build`) — clean.
- `prettier --write` on changed files — no changes.
64 lines
3.1 KiB
TypeScript
64 lines
3.1 KiB
TypeScript
import { describe, it, expect, vi } from "vitest";
|
|
import { assertSupported } from "../../src/utils/capability";
|
|
import type { DeviceInfo } from "@argent/registry";
|
|
|
|
// runAndroidStackQuery is stubbed so the leak_stacks guard test never touches a
|
|
// real .pftrace. It should never be called for leak_stacks (the guard returns
|
|
// early), so we make it throw to prove the early return.
|
|
vi.mock("../../src/utils/android-profiler/pipeline/index", async (importActual) => ({
|
|
...(await importActual<typeof import("../../src/utils/android-profiler/pipeline/index")>()),
|
|
runAndroidStackQuery: vi.fn(async () => {
|
|
throw new Error("runAndroidStackQuery should not be reached for leak_stacks on Android");
|
|
}),
|
|
}));
|
|
|
|
import { profilerStackQueryTool } from "../../src/tools/profiler/query/profiler-stack-query";
|
|
import { profilerCombinedReportTool } from "../../src/tools/profiler/combined/profiler-combined-report";
|
|
import type { NativeProfilerSessionApi } from "../../src/blueprints/native-profiler-session";
|
|
|
|
const iosSim: DeviceInfo = { id: "ios-sim", platform: "ios", kind: "simulator" };
|
|
const iosDevice: DeviceInfo = { id: "ios-dev", platform: "ios", kind: "device" };
|
|
const androidEmu: DeviceInfo = { id: "emulator-5554", platform: "android", kind: "emulator" };
|
|
const androidDevice: DeviceInfo = { id: "android-dev", platform: "android", kind: "device" };
|
|
const androidUnknown: DeviceInfo = { id: "android-unknown", platform: "android", kind: "unknown" };
|
|
|
|
describe("profiler-stack-query / profiler-combined-report Android capability", () => {
|
|
// Both tools ship complete Android implementations (executeAndroid /
|
|
// loadAndroidCombinedData). These tests pin the capability so the Android
|
|
// path can actually be reached by the dispatcher.
|
|
for (const tool of [profilerStackQueryTool, profilerCombinedReportTool]) {
|
|
describe(tool.id, () => {
|
|
it("is supported on Android emulator / device / unknown", () => {
|
|
expect(() => assertSupported(tool.id, tool.capability, androidEmu)).not.toThrow();
|
|
expect(() => assertSupported(tool.id, tool.capability, androidDevice)).not.toThrow();
|
|
expect(() => assertSupported(tool.id, tool.capability, androidUnknown)).not.toThrow();
|
|
});
|
|
|
|
it("is still supported on Apple simulator + device", () => {
|
|
expect(() => assertSupported(tool.id, tool.capability, iosSim)).not.toThrow();
|
|
expect(() => assertSupported(tool.id, tool.capability, iosDevice)).not.toThrow();
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
describe("profiler-stack-query leak_stacks on Android", () => {
|
|
it("returns the iOS-only guidance message without querying the trace", async () => {
|
|
const api: Partial<NativeProfilerSessionApi> = {
|
|
platform: "android",
|
|
traceFile: "/fake.pftrace",
|
|
exportedFiles: { pftrace: "/fake.pftrace" },
|
|
appProcess: "com.example.app",
|
|
};
|
|
|
|
const out = await profilerStackQueryTool.execute({ session: api } as never, {
|
|
device_id: androidEmu.id,
|
|
mode: "leak_stacks",
|
|
top_n: 15,
|
|
});
|
|
|
|
expect(out).toContain("Memory leak detection is not yet supported on Android");
|
|
expect(out).toContain("adb shell am dumpheap");
|
|
});
|
|
});
|