Skip to content

Commit 3371864

Browse files
committed
Add data gatherer
1 parent 7855332 commit 3371864

File tree

2 files changed

+233
-39
lines changed

2 files changed

+233
-39
lines changed
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
// © 2025 Cristian Felipe Patiño Rojas. Created on 28/5/25.
2+
3+
import XCTest
4+
import Core
5+
6+
class DataGatheringTests: XCTestCase {
7+
final class IterationRecorder {
8+
private var date: Date?
9+
private var code: String?
10+
var iterations: [DataGatheringTests.Iteration] = []
11+
12+
func recordGeneratedCode(_ code: String, at date: Date) {
13+
self.code = code
14+
self.date = date
15+
}
16+
17+
func recordOutput(_ output: Runner.ProcessOutput) {
18+
guard let date = date, let code = code else {
19+
print("⚠️ Skipped iteration: incomplete data")
20+
return
21+
}
22+
iterations.append(
23+
.init(
24+
startDate: date,
25+
generatedCode: code,
26+
exitCode: output.exitCode,
27+
stdErr: output.stderr,
28+
stdOut: output.stdout
29+
)
30+
)
31+
self.code = nil
32+
self.date = nil
33+
}
34+
}
35+
36+
struct GatheredData: Equatable {
37+
let testId: String
38+
let modelName: String
39+
let executions: [Execution]
40+
}
41+
42+
struct Execution: Equatable {
43+
let startDate: Date
44+
let iterations: [Iteration]
45+
}
46+
47+
struct Iteration: Equatable {
48+
let startDate: Date
49+
let generatedCode: String
50+
let exitCode: Int
51+
let stdErr: String?
52+
let stdOut: String?
53+
}
54+
55+
struct Specs {
56+
let id: String
57+
let content: String
58+
}
59+
60+
class DataGatherer {
61+
let client: Client
62+
let runner: Runner
63+
let currentDate: () -> Date
64+
65+
init(client: Client, runner: Runner, currentDate: @escaping () -> Date = Date.init) {
66+
self.client = client
67+
self.runner = runner
68+
self.currentDate = currentDate
69+
}
70+
71+
func gatherData(systemPrompt: String, specs: Specs, executionCount: Int, iterationCount: Int) async throws -> GatheredData {
72+
73+
class ClientSpy: Client {
74+
let client: Client
75+
76+
init(client: Client, onResponse: @escaping (String) -> Void) {
77+
self.client = client
78+
self.onResponse = onResponse
79+
}
80+
var model: String {client.model}
81+
let onResponse: (String) -> Void
82+
func send(messages: [Message]) async throws -> String {
83+
let response = try await client.send(messages: messages)
84+
onResponse(response)
85+
return response
86+
}
87+
}
88+
89+
class RunnerSpy: Runner {
90+
let runner: Runner
91+
92+
init(runner: Runner, onProcessOutput: @escaping (ProcessOutput) -> Void) {
93+
self.runner = runner
94+
self.onProcessOutput = onProcessOutput
95+
}
96+
let onProcessOutput: (ProcessOutput) -> Void
97+
func run(_ code: String) throws -> ProcessOutput {
98+
let output = try runner.run(code)
99+
onProcessOutput(output)
100+
return output
101+
}
102+
}
103+
104+
var executions = [Execution]()
105+
106+
let currentDate = currentDate
107+
for _ in (1...executionCount) {
108+
let initialTimestamp = currentDate()
109+
let recorder = IterationRecorder()
110+
let clientSpy = ClientSpy(client: client) { [weak recorder] in
111+
recorder?.recordGeneratedCode($0, at: currentDate())
112+
}
113+
let runnerSpy = RunnerSpy(runner: runner) { [weak recorder] in
114+
recorder?.recordOutput($0)
115+
}
116+
let coordinator = Coordinator(client: clientSpy, runner: runnerSpy)
117+
let _ = try await coordinator.generateCode(
118+
systemPrompt: systemPrompt,
119+
specs: specs.content,
120+
maxIterationCount: iterationCount
121+
)
122+
executions.append(
123+
Execution(
124+
startDate: initialTimestamp,
125+
iterations: recorder.iterations
126+
)
127+
)
128+
}
129+
130+
return GatheredData(testId: specs.id, modelName: client.model, executions: executions)
131+
}
132+
}
133+
134+
func test() async throws {
135+
struct ClientStub: Client {
136+
let model: String
137+
let result: String
138+
func send(messages: [Message]) async throws -> String {
139+
result
140+
}
141+
}
142+
143+
struct RunnerStub: Runner {
144+
let result: ProcessOutput
145+
func run(_ code: String) throws -> ProcessOutput {
146+
return result
147+
}
148+
}
149+
150+
let executionCount = 5
151+
let iterationCount = 5
152+
let timestamp = Date()
153+
let client = ClientStub(model: "fake model", result: "any generated code")
154+
let runner = RunnerStub(result: (stdout: "", stderr: "any stderr", exitCode: 1))
155+
let sut = DataGatherer(client: client, runner: runner, currentDate: { timestamp })
156+
let specs = Specs(
157+
id: "adder_spec",
158+
content: """
159+
func test_adder() {
160+
let sut = Adder(1,3)
161+
assert(sut.result == 4)
162+
}
163+
test_adder()
164+
"""
165+
)
166+
167+
let data = try await sut.gatherData(
168+
systemPrompt: "any system prompt",
169+
specs: specs,
170+
executionCount: executionCount,
171+
iterationCount: iterationCount
172+
)
173+
174+
let iteration = Iteration(
175+
startDate: timestamp,
176+
generatedCode: "any generated code",
177+
exitCode: 1,
178+
stdErr: "any stderr",
179+
stdOut: ""
180+
)
181+
182+
let iterations = Array(repeating: iteration, count: iterationCount)
183+
let execution = Execution(startDate: timestamp, iterations: iterations)
184+
let executions = Array(repeating: execution, count: executionCount)
185+
186+
let expectedData = GatheredData(
187+
testId: "adder_spec",
188+
modelName: "fake model",
189+
executions: executions
190+
)
191+
192+
XCTAssertEqual(data, expectedData)
193+
}
194+
}
Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,39 @@
1-
// © 2025 Cristian Felipe Patiño Rojas. Created on 26/5/25.
2-
3-
import Core
4-
5-
extension CoordinatorTests {
6-
func test_generateAndSaveCode_deliversErrorOnClientError() async throws {
7-
let client = ClientStub(result: .failure(anyError()))
8-
let coordinatior = makeSUT(client: client)
9-
10-
await XCTAssertThrowsErrorAsync(
11-
try await coordinatior.generateAndSaveCode(
12-
systemPrompt: anySystemPrompt(),
13-
specsFileURL: anyURL(),
14-
outputFileURL: anyURL()
15-
)
16-
)
17-
}
18-
19-
func test_generateAndSaveCode_deliversNoErrorOnClientSuccess() async throws {
20-
let client = ClientStub(result: .success("any genereted code"))
21-
let sut = makeSUT(client: client)
22-
await XCTAssertNoThrowAsync(
23-
try await sut.generateAndSaveCode(
24-
systemPrompt: anySystemPrompt(),
25-
specsFileURL: anyURL(),
26-
outputFileURL: anyURL()
27-
)
28-
)
29-
}
30-
31-
private func makeSUT(client: Client) -> Coordinator {
32-
Coordinator(
33-
reader: FileReaderDummy(),
34-
client: client,
35-
runner: RunnerDummy(),
36-
persistor: PersistorDummy()
37-
)
38-
}
39-
}
1+
//// © 2025 Cristian Felipe Patiño Rojas. Created on 26/5/25.
2+
//
3+
//import Core
4+
//
5+
//extension CoordinatorTests {
6+
// func test_generateAndSaveCode_deliversErrorOnClientError() async throws {
7+
// let client = ClientStub(result: .failure(anyError()))
8+
// let coordinatior = makeSUT(client: client)
9+
//
10+
// await XCTAssertThrowsErrorAsync(
11+
// try await coordinatior.generateAndSaveCode(
12+
// systemPrompt: anySystemPrompt(),
13+
// specsFileURL: anyURL(),
14+
// outputFileURL: anyURL()
15+
// )
16+
// )
17+
// }
18+
//
19+
// func test_generateAndSaveCode_deliversNoErrorOnClientSuccess() async throws {
20+
// let client = ClientStub(result: .success("any genereted code"))
21+
// let sut = makeSUT(client: client)
22+
// await XCTAssertNoThrowAsync(
23+
// try await sut.generateAndSaveCode(
24+
// systemPrompt: anySystemPrompt(),
25+
// specsFileURL: anyURL(),
26+
// outputFileURL: anyURL()
27+
// )
28+
// )
29+
// }
30+
//
31+
// private func makeSUT(client: Client) -> Coordinator {
32+
// Coordinator(
33+
// reader: FileReaderDummy(),
34+
// client: client,
35+
// runner: RunnerDummy(),
36+
// persistor: PersistorDummy()
37+
// )
38+
// }
39+
//}

0 commit comments

Comments
 (0)