From 75a183b26c0526928301e85d2f5de951e64e6252 Mon Sep 17 00:00:00 2001 From: Szymon Chmal Date: Tue, 21 Jul 2026 11:01:04 +0200 Subject: [PATCH 1/2] fix(platform-ios): clear graceful shutdown timer --- .../src/__tests__/xctest-agent.test.ts | 24 +++++++ packages/platform-ios/src/xctest-agent.ts | 64 +++++++++---------- 2 files changed, 53 insertions(+), 35 deletions(-) diff --git a/packages/platform-ios/src/__tests__/xctest-agent.test.ts b/packages/platform-ios/src/__tests__/xctest-agent.test.ts index f38057a1..9c54fd38 100644 --- a/packages/platform-ios/src/__tests__/xctest-agent.test.ts +++ b/packages/platform-ios/src/__tests__/xctest-agent.test.ts @@ -514,6 +514,30 @@ describe('xctest-agent orchestration', () => { expect(mocks.kill).not.toHaveBeenCalled(); }); + it('clears the shutdown timer after graceful shutdown', async () => { + vi.useFakeTimers(); + + try { + const controller = createXCTestAgentController({ + port: 49154, + shutdownTimeoutMs: 30_000, + target: { + kind: 'simulator', + id: 'sim-timeout', + }, + }); + + await controller.ensureStarted(); + await controller.dispose(); + + expect(mocks.shutdown).toHaveBeenCalledTimes(1); + expect(mocks.kill).not.toHaveBeenCalled(); + expect(vi.getTimerCount()).toBe(0); + } finally { + vi.useRealTimers(); + } + }); + it('kills the agent process when graceful shutdown times out', async () => { mocks.shutdown.mockResolvedValue(undefined); diff --git a/packages/platform-ios/src/xctest-agent.ts b/packages/platform-ios/src/xctest-agent.ts index e967da19..b0344e33 100644 --- a/packages/platform-ios/src/xctest-agent.ts +++ b/packages/platform-ios/src/xctest-agent.ts @@ -762,51 +762,45 @@ const waitForAgentReady = async (options: { ); }; -const waitForShutdown = async (options: { - processTask: Promise | null; - shutdownTimeoutMs: number; -}): Promise => { - if (!options.processTask) { - return true; - } - - const timedOut = Symbol('timedOut'); - const result = await Promise.race([ - options.processTask.then(() => undefined), - delay(options.shutdownTimeoutMs).then(() => timedOut), - ]); - - return result !== timedOut; -}; - const waitForGracefulShutdown = async (options: { client: ReturnType; processTask: Promise | null; shutdownTimeoutMs: number; }): Promise<{ didStop: boolean; requestError: unknown | null }> => { let requestError: unknown | null = null; + let shutdownTimer: ReturnType | null = null; const timedOut = Symbol('timedOut'); + const timeoutTask = new Promise((resolve) => { + shutdownTimer = setTimeout( + () => resolve(timedOut), + options.shutdownTimeoutMs + ); + }); - const result = await Promise.race([ - (async () => { - try { - await options.client.shutdown(); - } catch (error) { - requestError = error; - } + try { + const result = await Promise.race([ + (async () => { + try { + await options.client.shutdown(); + } catch (error) { + requestError = error; + } - return await waitForShutdown({ - processTask: options.processTask, - shutdownTimeoutMs: options.shutdownTimeoutMs, - }); - })(), - delay(options.shutdownTimeoutMs).then(() => timedOut), - ]); + await options.processTask; + return true; + })(), + timeoutTask, + ]); - return { - didStop: result !== timedOut && result === true, - requestError, - }; + return { + didStop: result !== timedOut, + requestError, + }; + } finally { + if (shutdownTimer !== null) { + clearTimeout(shutdownTimer); + } + } }; const waitForChildProcessExit = async (subprocess: Subprocess) => { From f44261bd29a37f01a17cfce67b4b49a676b7ece6 Mon Sep 17 00:00:00 2001 From: Szymon Chmal Date: Tue, 21 Jul 2026 11:05:04 +0200 Subject: [PATCH 2/2] chore: add iOS shutdown version plan --- .nx/version-plans/version-plan-1784624669908.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .nx/version-plans/version-plan-1784624669908.md diff --git a/.nx/version-plans/version-plan-1784624669908.md b/.nx/version-plans/version-plan-1784624669908.md new file mode 100644 index 00000000..b9c40c5b --- /dev/null +++ b/.nx/version-plans/version-plan-1784624669908.md @@ -0,0 +1,5 @@ +--- +__default__: patch +--- + +iOS Harness runs now end cleanly after XCTest-agent shutdown instead of lingering for the shutdown timeout.