mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
fix(android): stop a structurally-failed helper session before the one-shot viewport retry
A structured ok=false viewport response leaves the session process alive, and Android permits only one instrumentation owner of UiAutomation - running the one-shot fallback against a still-live helper contends with it and masks the original structured failure. Stop the session first; regression pins that the one-shot retry only executes once the session is gone.
This commit is contained in:
@@ -672,3 +672,48 @@ test('viewport falls back to one-shot instrumentation after a session error', as
|
||||
assert.ok(oneShotArgs?.includes('viewport'));
|
||||
assert.equal(await session.isSessionAlive(), false);
|
||||
});
|
||||
|
||||
test('a structured ok=false viewport response stops the session before the one-shot retry', async () => {
|
||||
const device = makeIsolatedDevice();
|
||||
const session = await startFakeTouchHelperSession(device, (command, requestId) => {
|
||||
if (command.startsWith('viewport')) {
|
||||
return sessionHeaderResponse({
|
||||
agentDeviceProtocol: 'android-snapshot-helper-v1',
|
||||
requestId,
|
||||
ok: 'false',
|
||||
errorType: 'java.lang.IllegalStateException',
|
||||
message: 'Active application interaction viewport is unavailable',
|
||||
});
|
||||
}
|
||||
return sessionHeaderResponse({
|
||||
agentDeviceProtocol: 'android-snapshot-helper-v1',
|
||||
requestId,
|
||||
ok: 'true',
|
||||
});
|
||||
});
|
||||
|
||||
let oneShotArgs: string[] | undefined;
|
||||
const viewportResult = await withAndroidAdbProvider(
|
||||
{
|
||||
exec: currentVersionAdb(async (args) => {
|
||||
// One instrumentation may own UiAutomation: the one-shot retry must only run once the
|
||||
// structurally-failed session has been stopped.
|
||||
assert.equal(await session.isSessionAlive(), false);
|
||||
oneShotArgs = args;
|
||||
return {
|
||||
exitCode: 0,
|
||||
stdout: [
|
||||
resultRecord({ ok: 'true', x: '5', y: '6', width: '300', height: '400' }),
|
||||
'INSTRUMENTATION_CODE: 0',
|
||||
].join('\n'),
|
||||
stderr: '',
|
||||
};
|
||||
}),
|
||||
},
|
||||
{ serial: device.id },
|
||||
async () => await readAndroidTouchHelperViewport(device),
|
||||
);
|
||||
|
||||
assert.deepEqual(viewportResult, { x: 5, y: 6, width: 300, height: 400 });
|
||||
assert.ok(oneShotArgs?.includes('viewport'));
|
||||
});
|
||||
|
||||
@@ -17,6 +17,7 @@ import { ensureAndroidSnapshotHelper } from './snapshot-helper-install.ts';
|
||||
import {
|
||||
getAndroidSnapshotHelperSessionDeviceKey,
|
||||
runAndroidSnapshotHelperSessionTouchCommand,
|
||||
stopAndroidSnapshotHelperSession,
|
||||
} from './snapshot-helper-session.ts';
|
||||
import {
|
||||
ANDROID_SNAPSHOT_HELPER_PROTOCOL,
|
||||
@@ -112,7 +113,10 @@ export async function readAndroidTouchHelperViewport(device: DeviceInfo): Promis
|
||||
});
|
||||
if (sessionHeaders) return readViewportResult(sessionHeaders);
|
||||
} catch (error) {
|
||||
// Viewport reads are idempotent; after the failed session is stopped, retry one-shot below.
|
||||
// Viewport reads are idempotent, so a fresh one-shot run may still answer. The session must
|
||||
// be stopped first: Android permits one instrumentation owner of UiAutomation, and a helper
|
||||
// left alive after a structured failure would contend with the one-shot instrumentation.
|
||||
await stopAndroidSnapshotHelperSession(prepared.deviceKey);
|
||||
emitDiagnostic({
|
||||
level: 'warn',
|
||||
phase: 'android_touch_helper_viewport_session_fallback',
|
||||
|
||||
Reference in New Issue
Block a user