|
| 1 | +diff --git a/dist/xctest-agent-client.d.ts b/dist/xctest-agent-client.d.ts |
| 2 | +index 6f896672f61f8cdd00249b00a0a6501b4a0a0288..0ef8d608c7ec91fc01b62483f8f77240e07b48ae 100644 |
| 3 | +--- a/dist/xctest-agent-client.d.ts |
| 4 | ++++ b/dist/xctest-agent-client.d.ts |
| 5 | +@@ -11,6 +11,7 @@ export type XCTestAgentClient = { |
| 6 | + dispose: () => Promise<void>; |
| 7 | + getPermissionsConfig: () => Promise<XCTestAgentPermissionsConfiguration>; |
| 8 | + health: () => Promise<XCTestAgentHealthResponse>; |
| 9 | ++ shutdown: () => Promise<void>; |
| 10 | + }; |
| 11 | + export declare const createXCTestAgentClient: (transport: XCTestAgentTransport) => XCTestAgentClient; |
| 12 | + export {}; |
| 13 | +diff --git a/dist/xctest-agent-client.js b/dist/xctest-agent-client.js |
| 14 | +index 4a70bba5e197673eba02c5210171c24207511e02..bef996567ed11723f33254cfb9d841fae94b55d5 100644 |
| 15 | +--- a/dist/xctest-agent-client.js |
| 16 | ++++ b/dist/xctest-agent-client.js |
| 17 | +@@ -29,6 +29,12 @@ export const createXCTestAgentClient = (transport) => { |
| 18 | + }); |
| 19 | + return response.permissions; |
| 20 | + }, |
| 21 | ++ shutdown: async () => { |
| 22 | ++ await requestJson({ |
| 23 | ++ method: 'POST', |
| 24 | ++ path: '/shutdown', |
| 25 | ++ }); |
| 26 | ++ }, |
| 27 | + dispose: async () => { |
| 28 | + await transport.dispose(); |
| 29 | + }, |
| 30 | +diff --git a/dist/xctest-agent.js b/dist/xctest-agent.js |
| 31 | +index 7ee0ac916a3708e98953ead0a80711bd2c7b2822..de0f0ae36fef4199cf614e5725e30599b038f83f 100644 |
| 32 | +--- a/dist/xctest-agent.js |
| 33 | ++++ b/dist/xctest-agent.js |
| 34 | +@@ -670,7 +670,21 @@ export const createXCTestAgentController = (options) => { |
| 35 | + agentClient = null; |
| 36 | + processTask = null; |
| 37 | + xctestAgentLogger.info('Stopping XCTest agent session for %s target', target.kind); |
| 38 | ++ if (currentClient) { |
| 39 | ++ try { |
| 40 | ++ await currentClient.shutdown(); |
| 41 | ++ } |
| 42 | ++ catch (error) { |
| 43 | ++ xctestAgentLogger.warn('Failed to gracefully shut down XCTest agent for %s: %s', target.kind, getErrorMessage(error)); |
| 44 | ++ } |
| 45 | ++ } |
| 46 | + await currentClient?.dispose(); |
| 47 | ++ if (await waitForShutdown({ |
| 48 | ++ processTask: currentProcessTask, |
| 49 | ++ shutdownTimeoutMs, |
| 50 | ++ })) { |
| 51 | ++ return; |
| 52 | ++ } |
| 53 | + await stopProcess({ |
| 54 | + process: currentProcess, |
| 55 | + processTask: currentProcessTask, |
| 56 | +diff --git a/src/xctest-agent-client.ts b/src/xctest-agent-client.ts |
| 57 | +index 93f28b5c66ab907c4645ea01431d593d205a7f28..763c8e40243d026c30720b4123d79e1aa17e51d5 100644 |
| 58 | +--- a/src/xctest-agent-client.ts |
| 59 | ++++ b/src/xctest-agent-client.ts |
| 60 | +@@ -23,6 +23,7 @@ export type XCTestAgentClient = { |
| 61 | + dispose: () => Promise<void>; |
| 62 | + getPermissionsConfig: () => Promise<XCTestAgentPermissionsConfiguration>; |
| 63 | + health: () => Promise<XCTestAgentHealthResponse>; |
| 64 | ++ shutdown: () => Promise<void>; |
| 65 | + }; |
| 66 | + |
| 67 | + export const createXCTestAgentClient = ( |
| 68 | +@@ -67,6 +68,12 @@ export const createXCTestAgentClient = ( |
| 69 | + |
| 70 | + return response.permissions; |
| 71 | + }, |
| 72 | ++ shutdown: async () => { |
| 73 | ++ await requestJson({ |
| 74 | ++ method: 'POST', |
| 75 | ++ path: '/shutdown', |
| 76 | ++ }); |
| 77 | ++ }, |
| 78 | + dispose: async () => { |
| 79 | + await transport.dispose(); |
| 80 | + }, |
| 81 | +diff --git a/src/xctest-agent.ts b/src/xctest-agent.ts |
| 82 | +index 00e3034e46cb789a9e3690593b08da63d4ebb1ce..82b9655c60acd8f8fca3745729c8be0c4312dcf9 100644 |
| 83 | +--- a/src/xctest-agent.ts |
| 84 | ++++ b/src/xctest-agent.ts |
| 85 | +@@ -1113,7 +1113,28 @@ export const createXCTestAgentController = (options: { |
| 86 | + target.kind |
| 87 | + ); |
| 88 | + |
| 89 | ++ if (currentClient) { |
| 90 | ++ try { |
| 91 | ++ await currentClient.shutdown(); |
| 92 | ++ } catch (error) { |
| 93 | ++ xctestAgentLogger.warn( |
| 94 | ++ 'Failed to gracefully shut down XCTest agent for %s: %s', |
| 95 | ++ target.kind, |
| 96 | ++ getErrorMessage(error) |
| 97 | ++ ); |
| 98 | ++ } |
| 99 | ++ } |
| 100 | ++ |
| 101 | + await currentClient?.dispose(); |
| 102 | ++ if ( |
| 103 | ++ await waitForShutdown({ |
| 104 | ++ processTask: currentProcessTask, |
| 105 | ++ shutdownTimeoutMs, |
| 106 | ++ }) |
| 107 | ++ ) { |
| 108 | ++ return; |
| 109 | ++ } |
| 110 | ++ |
| 111 | + await stopProcess({ |
| 112 | + process: currentProcess, |
| 113 | + processTask: currentProcessTask, |
| 114 | +diff --git a/xctest-agent/HarnessXCTestAgentUITests/HarnessXCTestAgentUITests.swift b/xctest-agent/HarnessXCTestAgentUITests/HarnessXCTestAgentUITests.swift |
| 115 | +index 31abf4b787a602d53fadf30794b27fe287aa0bd2..d608f8cc928ef813f0b66064bc73032ecf30af5c 100644 |
| 116 | +--- a/xctest-agent/HarnessXCTestAgentUITests/HarnessXCTestAgentUITests.swift |
| 117 | ++++ b/xctest-agent/HarnessXCTestAgentUITests/HarnessXCTestAgentUITests.swift |
| 118 | +@@ -4,6 +4,7 @@ import Network |
| 119 | + final class HarnessXCTestAgentState { |
| 120 | + private let lock = NSLock() |
| 121 | + private var _permissions: PermissionPromptConfiguration |
| 122 | ++ private var _shouldStop = false |
| 123 | + |
| 124 | + init(permissions: PermissionPromptConfiguration) { |
| 125 | + _permissions = permissions |
| 126 | +@@ -20,6 +21,18 @@ final class HarnessXCTestAgentState { |
| 127 | + _permissions = permissions |
| 128 | + lock.unlock() |
| 129 | + } |
| 130 | ++ |
| 131 | ++ var shouldStop: Bool { |
| 132 | ++ lock.lock() |
| 133 | ++ defer { lock.unlock() } |
| 134 | ++ return _shouldStop |
| 135 | ++ } |
| 136 | ++ |
| 137 | ++ func requestStop() { |
| 138 | ++ lock.lock() |
| 139 | ++ _shouldStop = true |
| 140 | ++ lock.unlock() |
| 141 | ++ } |
| 142 | + } |
| 143 | + |
| 144 | + private struct XCTestAgentHealthResponse: Codable { |
| 145 | +@@ -31,6 +44,10 @@ private struct XCTestAgentPermissionsResponse: Codable { |
| 146 | + let permissions: PermissionPromptConfiguration |
| 147 | + } |
| 148 | + |
| 149 | ++private struct XCTestAgentShutdownResponse: Codable { |
| 150 | ++ let status: String |
| 151 | ++} |
| 152 | ++ |
| 153 | + |
| 154 | + private struct XCTestAgentRequest { |
| 155 | + let body: Data |
| 156 | +@@ -250,6 +267,9 @@ final class HarnessXCTestAgentUITests: XCTestCase { |
| 157 | + return jsonResponse(XCTestAgentPermissionsResponse(permissions: state.permissions)) |
| 158 | + case ("GET", "/permissions"): |
| 159 | + return jsonResponse(XCTestAgentPermissionsResponse(permissions: state.permissions)) |
| 160 | ++ case ("POST", "/shutdown"): |
| 161 | ++ state.requestStop() |
| 162 | ++ return jsonResponse(XCTestAgentShutdownResponse(status: "stopping")) |
| 163 | + default: |
| 164 | + return XCTestAgentResponse(body: Data("{\"error\":\"not found\"}".utf8), statusCode: 404) |
| 165 | + } |
| 166 | +@@ -301,7 +321,7 @@ final class HarnessXCTestAgentUITests: XCTestCase { |
| 167 | + |
| 168 | + let sessionDeadline = Date().addingTimeInterval(Constants.defaultSessionDuration) |
| 169 | + |
| 170 | +- while Date() < sessionDeadline { |
| 171 | ++ while Date() < sessionDeadline && !state.shouldStop { |
| 172 | + observeTargetApplication() |
| 173 | + |
| 174 | + for capability in capabilities { |
0 commit comments