Skip to content

Commit 8768bb4

Browse files
committed
fix: tune iOS runner response retention
1 parent 3f65124 commit 8768bb4

1 file changed

Lines changed: 141 additions & 3 deletions

File tree

ios-runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift

Lines changed: 141 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Foundation
2+
import XCTest
23

34
enum RunnerCommandLifecycleState: String {
45
case notAccepted
@@ -50,7 +51,7 @@ final class RunnerCommandJournal {
5051
command: command,
5152
state: response.ok ? .completed : .failed,
5253
responseOk: response.ok,
53-
responseJson: encodeResponseJson(response),
54+
responseJson: encodeResponseJson(command: command, response: response),
5455
error: response.error
5556
)
5657
}
@@ -131,10 +132,147 @@ final class RunnerCommandJournal {
131132
return trimmed.isEmpty ? nil : trimmed
132133
}
133134

134-
private func encodeResponseJson(_ response: Response) -> String? {
135-
guard response.data?.nodes == nil else { return nil }
135+
private func encodeResponseJson(command: Command, response: Response) -> String? {
136+
guard shouldRetainResponseJson(command: command) else { return nil }
136137
guard let data = try? JSONEncoder().encode(response) else { return nil }
137138
guard data.count <= maxResponseJsonBytes else { return nil }
138139
return String(data: data, encoding: .utf8)
139140
}
141+
142+
private func shouldRetainResponseJson(command: Command) -> Bool {
143+
switch command.command {
144+
case .snapshot, .screenshot:
145+
return false
146+
default:
147+
return true
148+
}
149+
}
150+
}
151+
152+
extension RunnerTests {
153+
func testCommandJournalRetentionPolicy() throws {
154+
let journal = RunnerCommandJournal()
155+
156+
let uptime = runnerJournalCommand("uptime", id: "small-scalar")
157+
journal.accept(command: uptime)
158+
journal.finish(
159+
command: uptime,
160+
response: Response(ok: true, data: DataPayload(currentUptimeMs: 12.5))
161+
)
162+
163+
let scalarStatus = journal.status(commandId: "small-scalar")
164+
XCTAssertEqual(scalarStatus.lifecycleState, RunnerCommandLifecycleState.completed.rawValue)
165+
XCTAssertEqual(scalarStatus.lifecycleResponseOk, true)
166+
XCTAssertNotNil(scalarStatus.lifecycleResponseJson)
167+
let scalarResponse = try decodeRunnerJournalResponse(scalarStatus.lifecycleResponseJson)
168+
XCTAssertEqual(scalarResponse.data?.currentUptimeMs, 12.5)
169+
170+
let querySelector = runnerJournalCommand("querySelector", id: "small-object")
171+
journal.accept(command: querySelector)
172+
journal.finish(
173+
command: querySelector,
174+
response: Response(ok: true, data: DataPayload(found: true, nodes: [runnerJournalNode()]))
175+
)
176+
177+
let objectStatus = journal.status(commandId: "small-object")
178+
XCTAssertNotNil(objectStatus.lifecycleResponseJson)
179+
let objectResponse = try decodeRunnerJournalResponse(objectStatus.lifecycleResponseJson)
180+
XCTAssertEqual(objectResponse.data?.found, true)
181+
XCTAssertEqual(objectResponse.data?.nodes?.count, 1)
182+
183+
let snapshot = runnerJournalCommand("snapshot", id: "snapshot-tree")
184+
journal.accept(command: snapshot)
185+
journal.finish(
186+
command: snapshot,
187+
response: Response(ok: true, data: DataPayload(nodes: [runnerJournalNode()], truncated: false))
188+
)
189+
190+
let snapshotStatus = journal.status(commandId: "snapshot-tree")
191+
XCTAssertEqual(snapshotStatus.lifecycleState, RunnerCommandLifecycleState.completed.rawValue)
192+
XCTAssertEqual(snapshotStatus.lifecycleResponseOk, true)
193+
XCTAssertNil(snapshotStatus.lifecycleResponseJson)
194+
195+
let screenshot = runnerJournalCommand("screenshot", id: "screenshot-artifact")
196+
journal.accept(command: screenshot)
197+
journal.finish(
198+
command: screenshot,
199+
response: Response(ok: true, data: DataPayload(message: "tmp/screenshot-1.png"))
200+
)
201+
202+
let screenshotStatus = journal.status(commandId: "screenshot-artifact")
203+
XCTAssertEqual(screenshotStatus.lifecycleState, RunnerCommandLifecycleState.completed.rawValue)
204+
XCTAssertEqual(screenshotStatus.lifecycleResponseOk, true)
205+
XCTAssertNil(screenshotStatus.lifecycleResponseJson)
206+
207+
let largeRead = runnerJournalCommand("readText", id: "large-read")
208+
journal.accept(command: largeRead)
209+
journal.finish(
210+
command: largeRead,
211+
response: Response(ok: true, data: DataPayload(text: String(repeating: "x", count: 17 * 1024)))
212+
)
213+
214+
let largeReadStatus = journal.status(commandId: "large-read")
215+
XCTAssertEqual(largeReadStatus.lifecycleState, RunnerCommandLifecycleState.completed.rawValue)
216+
XCTAssertEqual(largeReadStatus.lifecycleResponseOk, true)
217+
XCTAssertNil(largeReadStatus.lifecycleResponseJson)
218+
}
219+
220+
func testCommandJournalKeepsErrorMetadataWhenResponseJsonIsDropped() {
221+
let journal = RunnerCommandJournal()
222+
let snapshot = runnerJournalCommand("snapshot", id: "snapshot-error")
223+
let hint = "Try a smaller read such as snapshot -s <visible label or id> -d 8."
224+
225+
journal.accept(command: snapshot)
226+
journal.finish(
227+
command: snapshot,
228+
response: Response(
229+
ok: false,
230+
error: ErrorPayload(
231+
code: "IOS_AX_SNAPSHOT_FAILED",
232+
message: "iOS XCTest snapshot failed while serializing the accessibility tree.",
233+
hint: hint
234+
)
235+
)
236+
)
237+
238+
let status = journal.status(commandId: "snapshot-error")
239+
XCTAssertEqual(status.lifecycleState, RunnerCommandLifecycleState.failed.rawValue)
240+
XCTAssertEqual(status.lifecycleResponseOk, false)
241+
XCTAssertNil(status.lifecycleResponseJson)
242+
XCTAssertEqual(status.lifecycleErrorCode, "IOS_AX_SNAPSHOT_FAILED")
243+
XCTAssertEqual(
244+
status.lifecycleErrorMessage,
245+
"iOS XCTest snapshot failed while serializing the accessibility tree."
246+
)
247+
XCTAssertEqual(status.lifecycleErrorHint, hint)
248+
}
249+
250+
private func runnerJournalCommand(_ command: String, id: String) -> Command {
251+
let json = #"{"command":"\#(command)","commandId":"\#(id)"}"#
252+
return try! JSONDecoder().decode(Command.self, from: Data(json.utf8))
253+
}
254+
255+
private func runnerJournalNode() -> SnapshotNode {
256+
SnapshotNode(
257+
index: 0,
258+
type: "button",
259+
label: "Continue",
260+
identifier: "continue",
261+
value: nil,
262+
rect: SnapshotRect(x: 10, y: 20, width: 100, height: 44),
263+
enabled: true,
264+
focused: nil,
265+
selected: nil,
266+
hittable: true,
267+
depth: 0,
268+
parentIndex: nil,
269+
hiddenContentAbove: nil,
270+
hiddenContentBelow: nil
271+
)
272+
}
273+
274+
private func decodeRunnerJournalResponse(_ responseJson: String?) throws -> Response {
275+
let responseJson = try XCTUnwrap(responseJson)
276+
return try JSONDecoder().decode(Response.self, from: Data(responseJson.utf8))
277+
}
140278
}

0 commit comments

Comments
 (0)