mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
8af2660332
* refactor(android): extract adb executor and IME cluster into packages/platform-android (#2041) Implementation moves behind an injected adb host port (R13-clean); shared vocabulary moves to contracts (android-touch-plan, android-helper-artifacts) and kernel (keyed-lock); root keeps thin re-export shims plus the composition wiring that binds the port. session.ts/session-observability.ts no longer import platform transport types (opaque unknown, R62 pattern). * refactor(android): trim shims to consumed surface, break helper-install cycle, add gate coverage Fallow-clean: shim re-exports carry only names root still consumes; helper-package-install imports the package subpath directly (no cycle) and reuses the contracts decision types; transitional R13 table gets planted-red tests; ADR-0019 records the transitional exception; eager-closure rows added for the new entry surfaces. * review: restore maxBuffer on spawn options, scope the transitional test allowance Adversarial review round 1: AndroidAdbSpawnOptions keeps maxBuffer for spawn- signature width parity with the pre-move ExecBackgroundOptions; the R13 test allowance narrows from any test file to the cluster's own src/platforms/android __tests__ directory, with planted-red coverage for a foreign test file; ADR text records the scoped allowance. * review: keep an unbound adb host port loud in listAndroidAdbSerialsQuick * style: format policy test * refactor(android): split the extracted adb and IME modules into focused owners Review: the moved adb-executor.ts (652 lines) and ime-lifecycle.ts (487 lines) carried three concerns each across the package boundary. The entry subpaths are now thin surfaces over focused modules — adb: transport vocabulary, failure classification+enrichment, provider normalization, provider scope/routing, port-reverse ownership, pull/install transfers; IME: ownership state, on-device settings record, activation transaction, restore+orphan recovery — every module under 250 lines, each with colocated tests over an in-memory host-port stub and a shared fake IME device. R31's fence invariant repoints to ime-activation.ts; eager-closure pins move to the split shape. * rebase: reconcile the transitional android table with the #2050 mechanics-facet model The R13 declaration check now composes both enumerated subpath sets on the exact-list model #2050 introduced; the policy test fixture declares android's transitional subpaths the same way it declares apple's runner facet.
72 lines
2.3 KiB
TypeScript
72 lines
2.3 KiB
TypeScript
import { expect, test } from 'vitest';
|
|
import { bindAndroidAdbHostStub } from './adb-host.fixtures.ts';
|
|
import { installAndroidAdbPackage, pullAndroidAdbFile } from './adb-transfer.ts';
|
|
import type { AndroidAdbExecutorResult, AndroidAdbProvider } from './adb-transport.ts';
|
|
|
|
const ok = (): AndroidAdbExecutorResult => ({ exitCode: 0, stdout: '', stderr: '' });
|
|
|
|
test('semantic provider methods win over the exec fallback and escape the override scope', async () => {
|
|
let escaped = 0;
|
|
bindAndroidAdbHostStub({
|
|
withoutAdbCommandExecutorOverride: async (fn) => {
|
|
escaped += 1;
|
|
return await fn();
|
|
},
|
|
});
|
|
const pulls: string[][] = [];
|
|
const installs: string[] = [];
|
|
const provider: AndroidAdbProvider = {
|
|
exec: async () => {
|
|
throw new Error('exec fallback must not run when semantic methods exist');
|
|
},
|
|
pull: async (remotePath, localPath) => {
|
|
pulls.push([remotePath, localPath]);
|
|
return ok();
|
|
},
|
|
install: async (apkPath) => {
|
|
installs.push(apkPath);
|
|
return ok();
|
|
},
|
|
};
|
|
|
|
await pullAndroidAdbFile('/device/a.apk', '/tmp/a.apk', { provider });
|
|
await installAndroidAdbPackage('/tmp/a.apk', { provider, replace: true });
|
|
|
|
expect(pulls).toEqual([['/device/a.apk', '/tmp/a.apk']]);
|
|
expect(installs).toEqual(['/tmp/a.apk']);
|
|
expect(escaped).toBe(2);
|
|
});
|
|
|
|
test('the exec fallback lowers semantic install options into adb flags', async () => {
|
|
bindAndroidAdbHostStub();
|
|
const calls: string[][] = [];
|
|
const provider: AndroidAdbProvider = {
|
|
exec: async (args) => {
|
|
calls.push(args);
|
|
return ok();
|
|
},
|
|
};
|
|
|
|
await installAndroidAdbPackage('/tmp/a.apk', {
|
|
provider,
|
|
replace: true,
|
|
allowTestPackages: true,
|
|
allowDowngrade: true,
|
|
grantPermissions: true,
|
|
});
|
|
await pullAndroidAdbFile('/device/a.apk', '/tmp/a.apk', { provider });
|
|
|
|
expect(calls).toEqual([
|
|
['install', '-r', '-t', '-d', '-g', '/tmp/a.apk'],
|
|
['pull', '/device/a.apk', '/tmp/a.apk'],
|
|
]);
|
|
});
|
|
|
|
test('transfers without any provider or scope fail closed', async () => {
|
|
bindAndroidAdbHostStub();
|
|
await expect(pullAndroidAdbFile('/device/a.apk', '/tmp/a.apk')).rejects.toThrow(
|
|
/requires an adb provider/,
|
|
);
|
|
await expect(installAndroidAdbPackage('/tmp/a.apk')).rejects.toThrow(/requires an adb provider/);
|
|
});
|