Skip to content

Commit b80056b

Browse files
committed
test(apple): keep screenshot recovery proofs within lint limits
1 parent 5481297 commit b80056b

5 files changed

Lines changed: 59 additions & 54 deletions

File tree

‎packages/platform-apple/src/core/__tests__/screenshot-boot-state-recovery.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ async function probeRecovery(
124124
},
125125
}).then(
126126
() => undefined,
127-
(caught: unknown) => caught,
127+
(error: unknown) => error,
128128
);
129129
return {
130130
error,
@@ -179,7 +179,7 @@ test('the re-capture that fails again reports its own failure and adds no second
179179
test.each(['Booted', 'Booting', 'Creating', 'Shutting Down', 'Living'] as const)(
180180
'a simulator listed as %s is not turned off, so nothing boots or re-captures',
181181
async (state) => {
182-
const device = simulator(`sim-recover-${state.toLowerCase().replace(/[^a-z]+/g, '-')}`);
182+
const device = simulator(`sim-recover-${state.toLowerCase().replaceAll(/[^a-z]+/g, '-')}`);
183183
const probe = await probeRecovery(device, { state: () => listingFor(device.id, state) });
184184

185185
assert.equal(probe.stateReads.length, 1, 'exactly one state observation');
@@ -330,7 +330,7 @@ test('a mask-bearing capture that observed a shutdown still refuses the runner f
330330
},
331331
}).then(
332332
() => undefined,
333-
(caught: unknown) => caught,
333+
(error: unknown) => error,
334334
);
335335
return { error, stateReads: executor.calls().filter(isSimctlListDevices) };
336336
},
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { test } from 'vitest';
2+
import assert from 'node:assert/strict';
3+
import { AppError } from '@agent-device/kernel/errors';
4+
import { shouldRetryIosSimulatorScreenshot } from '../screenshot.ts';
5+
import { SIMCTL_SCREENSHOT_TIMEOUT_REASON } from '../tool-provider.ts';
6+
7+
test('shouldRetryIosSimulatorScreenshot retries the timeout reason the adapter stamps', () => {
8+
// The reason the local simctl adapter stamps from the tool's protocol header; see
9+
// screenshot-simctl-protocol.test.ts for the producer that mints it.
10+
const error = new AppError('COMMAND_FAILED', 'xcrun exited with code 60', {
11+
stderr: 'Timeout waiting for screen surfaces',
12+
exitCode: 60,
13+
reason: SIMCTL_SCREENSHOT_TIMEOUT_REASON,
14+
});
15+
assert.equal(shouldRetryIosSimulatorScreenshot(error), true);
16+
});
17+
18+
test('shouldRetryIosSimulatorScreenshot retries a command host-kit killed at its deadline', () => {
19+
const error = new AppError('COMMAND_FAILED', 'xcrun timed out after 20000ms', {
20+
args: ['simctl', 'io', 'sim-1', 'screenshot', '/tmp/out.png'],
21+
timeoutMs: 20_000,
22+
});
23+
assert.equal(shouldRetryIosSimulatorScreenshot(error), true);
24+
});
25+
26+
test('shouldRetryIosSimulatorScreenshot ignores unrelated screenshot failures', () => {
27+
const error = new AppError('COMMAND_FAILED', 'Failed to capture iOS screenshot', {
28+
stderr: 'No such file or directory',
29+
exitCode: 2,
30+
});
31+
assert.equal(shouldRetryIosSimulatorScreenshot(error), false);
32+
});
33+
34+
test('the same screenshot timeout wording without the typed reason is not retryable', () => {
35+
// Identical wording and argv to the two positives above, minus the typed evidence: the retry
36+
// decision must not be reachable from the tool's prose or from this module's own argv.
37+
const worded = new AppError('COMMAND_FAILED', 'Detected file type from extension: PNG', {
38+
stderr: 'Timeout waiting for screen surfaces',
39+
exitCode: 60,
40+
});
41+
const wordedAndSpelled = new AppError('COMMAND_FAILED', 'xcrun timed out after 20000ms', {
42+
args: ['simctl', 'io', 'sim-1', 'screenshot', '/tmp/out.png'],
43+
stderr: 'Timeout waiting for screen surfaces',
44+
exitCode: 60,
45+
});
46+
assert.equal(shouldRetryIosSimulatorScreenshot(worded), false);
47+
assert.equal(shouldRetryIosSimulatorScreenshot(wordedAndSpelled), false);
48+
});

‎packages/platform-apple/src/core/__tests__/screenshot-simctl-protocol.test.ts‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ test('the adapter reads the protocol header for a set-scoped screenshot call', a
154154
return await runXcrun(buildSimctlArgsForDevice(scopedDevice, SCREENSHOT_ARGS), {
155155
timeoutMs: 20_000,
156156
});
157-
} catch (caught) {
158-
return { error: caught };
157+
} catch (error) {
158+
return { error };
159159
}
160160
},
161161
),
@@ -268,7 +268,7 @@ test('the adapter leaves a caller-supplied command executor unclassified', async
268268
async () =>
269269
await runScreenshot().then(
270270
() => null,
271-
(caught: unknown) => caught,
271+
(error: unknown) => error,
272272
),
273273
),
274274
);
@@ -300,7 +300,7 @@ test('the adapter leaves a caller-supplied simctl runner unclassified', async ()
300300
async () =>
301301
await runScreenshot().then(
302302
() => null,
303-
(caught: unknown) => caught,
303+
(error: unknown) => error,
304304
),
305305
),
306306
);
@@ -328,7 +328,7 @@ test('the adapter does not parse a simctl subcommand it did not scope as a scree
328328
timeoutMs: 20_000,
329329
}).then(
330330
(result) => ({ result }),
331-
(caught: unknown) => ({ error: caught }),
331+
(error: unknown) => ({ error }),
332332
),
333333
),
334334
);
@@ -352,8 +352,8 @@ test('the adapter leaves an allowFailure screenshot call untouched', async () =>
352352
async (executor) => {
353353
try {
354354
return { result: await runScreenshot({ allowFailure: true }), calls: executor.calls() };
355-
} catch (caught) {
356-
return { error: caught };
355+
} catch (error) {
356+
return { error };
357357
}
358358
},
359359
),

‎packages/platform-apple/src/core/__tests__/screenshot-simctl-retry.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ async function captureThroughLocalAdapter(
109109
await withScriptedCommands(script, async (executor) => {
110110
const error = await captureSimulatorScreenshotWithRetry(SIMULATOR, outPath, undefined).then(
111111
() => undefined,
112-
(caught: unknown) => caught,
112+
(error: unknown) => error,
113113
);
114114
const calls = executor.calls();
115115
return {

‎packages/platform-apple/src/core/__tests__/screenshot.test.ts‎

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -113,49 +113,6 @@ async function waitForFileText(filePath: string, attempts = 20): Promise<string>
113113
throw lastError;
114114
}
115115

116-
test('shouldRetryIosSimulatorScreenshot retries the timeout reason the adapter stamps', () => {
117-
// The reason the local simctl adapter stamps from the tool's protocol header; see
118-
// screenshot-simctl-protocol.test.ts for the producer that mints it.
119-
const error = new AppError('COMMAND_FAILED', 'xcrun exited with code 60', {
120-
stderr: 'Timeout waiting for screen surfaces',
121-
exitCode: 60,
122-
reason: SIMCTL_SCREENSHOT_TIMEOUT_REASON,
123-
});
124-
assert.equal(shouldRetryIosSimulatorScreenshot(error), true);
125-
});
126-
127-
test('shouldRetryIosSimulatorScreenshot retries a command host-kit killed at its deadline', () => {
128-
const error = new AppError('COMMAND_FAILED', 'xcrun timed out after 20000ms', {
129-
args: ['simctl', 'io', 'sim-1', 'screenshot', '/tmp/out.png'],
130-
timeoutMs: 20_000,
131-
});
132-
assert.equal(shouldRetryIosSimulatorScreenshot(error), true);
133-
});
134-
135-
test('shouldRetryIosSimulatorScreenshot ignores unrelated screenshot failures', () => {
136-
const error = new AppError('COMMAND_FAILED', 'Failed to capture iOS screenshot', {
137-
stderr: 'No such file or directory',
138-
exitCode: 2,
139-
});
140-
assert.equal(shouldRetryIosSimulatorScreenshot(error), false);
141-
});
142-
143-
test('the same screenshot timeout wording without the typed reason is not retryable', () => {
144-
// Identical wording and argv to the two positives above, minus the typed evidence: the retry
145-
// decision must not be reachable from the tool's prose or from this module's own argv.
146-
const worded = new AppError('COMMAND_FAILED', 'Detected file type from extension: PNG', {
147-
stderr: 'Timeout waiting for screen surfaces',
148-
exitCode: 60,
149-
});
150-
const wordedAndSpelled = new AppError('COMMAND_FAILED', 'xcrun timed out after 20000ms', {
151-
args: ['simctl', 'io', 'sim-1', 'screenshot', '/tmp/out.png'],
152-
stderr: 'Timeout waiting for screen surfaces',
153-
exitCode: 60,
154-
});
155-
assert.equal(shouldRetryIosSimulatorScreenshot(worded), false);
156-
assert.equal(shouldRetryIosSimulatorScreenshot(wordedAndSpelled), false);
157-
});
158-
159116
test('captureSimulatorScreenshotWithFallback falls back to runner after retry exhaustion', async () => {
160117
const tmpDir = await mkdtempForTest('agent-device-ios-runner-fallback-');
161118
let ensureBootedCalls = 0;

0 commit comments

Comments
 (0)