Skip to content

Commit 18312cb

Browse files
committed
fix: shut down iOS Harness agent gracefully
1 parent 92d1720 commit 18312cb

3 files changed

Lines changed: 177 additions & 1 deletion

File tree

bun.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@
126126
}
127127
},
128128
"patchedDependencies": {
129-
"react-native@0.85.3": "patches/react-native@0.85.3.patch"
129+
"react-native@0.85.3": "patches/react-native@0.85.3.patch",
130+
"@react-native-harness/platform-apple@1.4.0-rc.1": "patches/@react-native-harness%2Fplatform-apple@1.4.0-rc.1.patch"
130131
},
131132
"version": "5.0.11"
132133
}
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
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

Comments
 (0)