Skip to content

Commit 63423da

Browse files
authored
fix: harden iOS simulator automation stability (#340)
* fix: harden iOS simulator automation stability * refactor: simplify automation hardening follow-ups
1 parent 3d736eb commit 63423da

7 files changed

Lines changed: 448 additions & 178 deletions

File tree

src/daemon/__tests__/request-router-screenshot.test.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,80 @@ test('screenshot resolves relative positional path against request cwd', async (
111111
expect(recordedAction?.positionals).toEqual([path.join(callerCwd, 'evidence/test.png')]);
112112
});
113113

114+
test('router serializes concurrent commands for the same device across sessions', async () => {
115+
const sessionStore = makeStore();
116+
sessionStore.set('session-a', makeSession('session-a'));
117+
sessionStore.set('session-b', makeSession('session-b'));
118+
119+
const order: string[] = [];
120+
let active = 0;
121+
let maxActive = 0;
122+
const gates: Array<() => void> = [];
123+
124+
mockDispatch.mockImplementation(async (_device, command) => {
125+
order.push(`start-${command}`);
126+
active += 1;
127+
maxActive = Math.max(maxActive, active);
128+
await new Promise<void>((resolve) => {
129+
gates.push(() => {
130+
active -= 1;
131+
order.push(`end-${command}`);
132+
resolve();
133+
});
134+
});
135+
return {};
136+
});
137+
138+
const handler = createRequestHandler({
139+
logPath: path.join(os.tmpdir(), 'daemon.log'),
140+
token: 'test-token',
141+
sessionStore,
142+
leaseRegistry: new LeaseRegistry(),
143+
trackDownloadableArtifact: () => 'artifact-id',
144+
});
145+
146+
const screenshotRequest = handler({
147+
token: 'test-token',
148+
session: 'session-a',
149+
command: 'screenshot',
150+
positionals: ['/tmp/first.png'],
151+
meta: { requestId: 'req-lock-1' },
152+
});
153+
154+
await vi.waitFor(() => {
155+
expect(order).toEqual(['start-screenshot']);
156+
});
157+
158+
const scrollRequest = handler({
159+
token: 'test-token',
160+
session: 'session-b',
161+
command: 'scroll',
162+
positionals: ['down'],
163+
meta: { requestId: 'req-lock-2' },
164+
});
165+
166+
await new Promise((resolve) => setTimeout(resolve, 20));
167+
expect(order).toEqual(['start-screenshot']);
168+
169+
gates.shift()?.();
170+
171+
await vi.waitFor(() => {
172+
expect(order).toEqual(['start-screenshot', 'end-screenshot', 'start-scroll']);
173+
});
174+
175+
gates.shift()?.();
176+
177+
const [screenshotResponse, scrollResponse] = await Promise.all([
178+
screenshotRequest,
179+
scrollRequest,
180+
]);
181+
182+
expect(screenshotResponse.ok).toBe(true);
183+
expect(scrollResponse.ok).toBe(true);
184+
expect(maxActive).toBe(1);
185+
expect(order).toEqual(['start-screenshot', 'end-screenshot', 'start-scroll', 'end-scroll']);
186+
});
187+
114188
test('screenshot forwards macOS session surface to dispatch', async () => {
115189
const sessionStore = makeStore();
116190
sessionStore.set('default', makeMacOsMenubarSession('default'));

0 commit comments

Comments
 (0)