@@ -42,7 +42,7 @@ import (
42
42
43
43
var (
44
44
configPathFlag = flag .String ("config" , "" , "path to the benchmark config file" )
45
- outPathFlag = flag .String ("out" , "./results" , "path to the output folder" )
45
+ outPathFlag = flag .String ("out" , "./results/${now} " , "path to the output folder" )
46
46
toolsFlag = internal .StringsFlag ("tool" , nil , "filter tool to be tested (can be provided multiple times)" )
47
47
testsFlag = internal .StringsFlag ("tests" , nil , "filter test to run (can be provided multiple times)" )
48
48
)
@@ -61,26 +61,31 @@ func mainE() error {
61
61
return fmt .Errorf ("config path is required" )
62
62
}
63
63
64
+ if outPathFlag == nil || strings .TrimSpace (* outPathFlag ) == "" {
65
+ return fmt .Errorf ("output path is required" )
66
+ }
67
+
68
+ * outPathFlag = strings .ReplaceAll (* outPathFlag , "${now}" , time .Now ().Format ("20060102150405" ))
69
+
64
70
// Create output directory if it does not exist.
65
71
err := os .MkdirAll (* outPathFlag , 0o755 )
66
72
if err != nil {
67
73
return fmt .Errorf ("failed to create output directory: %w" , err )
68
74
}
69
75
70
- now := time .Now ()
71
- infoReader , errorReader , closeLog , err := prepareLogger (now )
76
+ infoReader , errorReader , closeLog , err := prepareLogger ()
72
77
if err != nil {
73
78
return fmt .Errorf ("failed to prepare logger: %w" , err )
74
79
}
75
80
defer closeLog ()
76
81
77
- _ , err = tea .NewProgram (newMainModel (infoReader , errorReader , now )).Run ()
82
+ _ , err = tea .NewProgram (newMainModel (infoReader , errorReader )).Run ()
78
83
return err //nolint:wrapcheck // Wrapping this error wouldn't add any value.
79
84
}
80
85
81
- func prepareLogger (now time. Time ) (io.Reader , io.Reader , func (), error ) {
86
+ func prepareLogger () (io.Reader , io.Reader , func (), error ) {
82
87
// Create log file.
83
- logPath := filepath .Join (* outPathFlag , fmt . Sprintf ( "%s_benchi .log", now . Format ( "20060102150405" )) )
88
+ logPath := filepath .Join (* outPathFlag , "benchi .log" )
84
89
logFile , err := os .Create (logPath )
85
90
if err != nil {
86
91
return nil , nil , nil , fmt .Errorf ("failed to create log file: %w" , err )
@@ -122,7 +127,6 @@ type mainModel struct {
122
127
// These fields are initialized in Init
123
128
config config.Config
124
129
resultsDir string
125
- startedAt time.Time
126
130
dockerClient client.APIClient
127
131
128
132
tests []testModel
@@ -136,17 +140,18 @@ type mainModel struct {
136
140
type mainModelMsgInitDone struct {
137
141
config config.Config
138
142
resultsDir string
139
- startedAt time.Time
140
143
dockerClient client.APIClient
141
144
142
145
testRunners benchi.TestRunners
146
+
147
+ err error
143
148
}
144
149
145
150
type mainModelMsgNextTest struct {
146
151
testIndex int
147
152
}
148
153
149
- func newMainModel (infoReader , errorReader io.Reader , now time. Time ) mainModel {
154
+ func newMainModel (infoReader , errorReader io.Reader ) mainModel {
150
155
ctx , ctxCancel := context .WithCancel (context .Background ())
151
156
cleanupCtx , cleanupCtxCancel := context .WithCancel (context .Background ())
152
157
return mainModel {
@@ -155,8 +160,6 @@ func newMainModel(infoReader, errorReader io.Reader, now time.Time) mainModel {
155
160
cleanupCtx : cleanupCtx ,
156
161
cleanupCtxCancel : cleanupCtxCancel ,
157
162
158
- startedAt : now ,
159
-
160
163
infoLogModel : internal .NewLogModel (infoReader , 10 ),
161
164
errorLogModel : internal .NewLogModel (errorReader , 0 ),
162
165
}
@@ -182,52 +185,51 @@ func (mainModel) parseConfig(path string) (config.Config, error) {
182
185
183
186
func (m mainModel ) initCmd () tea.Cmd {
184
187
return func () tea.Msg {
185
- now := time . Now ()
188
+ var msg mainModelMsgInitDone
186
189
187
190
// Resolve absolute paths.
188
191
resultsDir , configPath , err := m .initPaths ()
189
192
if err != nil {
190
- return fmt .Errorf ("failed to resolve paths: %w" , err )
193
+ msg .err = fmt .Errorf ("failed to resolve paths: %w" , err )
194
+ return msg
191
195
}
196
+ msg .resultsDir = resultsDir
192
197
193
198
// Parse config.
194
- cfg , err : = m .parseConfig (configPath )
199
+ msg . config , err = m .parseConfig (configPath )
195
200
if err != nil {
196
- return fmt .Errorf ("failed to parse config: %w" , err )
201
+ msg .err = fmt .Errorf ("failed to parse config: %w" , err )
202
+ return msg
197
203
}
198
204
slog .Info ("Parsed config" , "path" , configPath )
199
205
200
206
// Change working directory to config path, all relative paths are relative
201
207
// to the config file.
202
208
err = os .Chdir (filepath .Dir (configPath ))
203
209
if err != nil {
204
- return fmt .Errorf ("could not change working directory: %w" , err )
210
+ msg .err = fmt .Errorf ("could not change working directory: %w" , err )
211
+ return msg
205
212
}
206
213
207
214
// Create docker client and initialize network.
208
- dockerClient , err : = m .initDocker ()
215
+ msg . dockerClient , err = m .initDocker ()
209
216
if err != nil {
210
- return fmt .Errorf ("failed to initialize docker: %w" , err )
217
+ msg .err = fmt .Errorf ("failed to initialize docker: %w" , err )
218
+ return msg
211
219
}
212
220
213
- testRunners , err := benchi .BuildTestRunners (cfg , benchi.TestRunnerOptions {
214
- ResultsDir : resultsDir ,
215
- StartedAt : now ,
221
+ msg .testRunners , err = benchi .BuildTestRunners (msg .config , benchi.TestRunnerOptions {
222
+ ResultsDir : msg .resultsDir ,
216
223
FilterTests : * testsFlag ,
217
224
FilterTools : * toolsFlag ,
218
- DockerClient : dockerClient ,
225
+ DockerClient : msg . dockerClient ,
219
226
})
220
227
if err != nil {
221
- return fmt .Errorf ("failed to build test runners: %w" , err )
228
+ msg .err = fmt .Errorf ("failed to build test runners: %w" , err )
229
+ return msg
222
230
}
223
231
224
- return mainModelMsgInitDone {
225
- config : cfg ,
226
- resultsDir : resultsDir ,
227
- startedAt : now ,
228
- dockerClient : dockerClient ,
229
- testRunners : testRunners ,
230
- }
232
+ return msg
231
233
}
232
234
}
233
235
@@ -273,14 +275,16 @@ func (mainModel) runTestCmd(index int) tea.Cmd {
273
275
274
276
func (m mainModel ) quitCmd () tea.Cmd {
275
277
return func () tea.Msg {
276
- slog .Info ("Removing docker network" , "network" , benchi .NetworkName )
277
- err := dockerutil .RemoveNetwork (m .cleanupCtx , m .dockerClient , benchi .NetworkName )
278
- if err != nil {
279
- slog .Error ("Failed to remove network" , "network" , benchi .NetworkName , "error" , err )
280
- }
281
- err = m .dockerClient .Close ()
282
- if err != nil {
283
- slog .Error ("Failed to close docker client" , "error" , err )
278
+ if m .dockerClient != nil {
279
+ slog .Info ("Removing docker network" , "network" , benchi .NetworkName )
280
+ err := dockerutil .RemoveNetwork (m .cleanupCtx , m .dockerClient , benchi .NetworkName )
281
+ if err != nil {
282
+ slog .Error ("Failed to remove network" , "network" , benchi .NetworkName , "error" , err )
283
+ }
284
+ err = m .dockerClient .Close ()
285
+ if err != nil {
286
+ slog .Error ("Failed to close docker client" , "error" , err )
287
+ }
284
288
}
285
289
return tea .Quit ()
286
290
}
@@ -295,7 +299,6 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
295
299
case mainModelMsgInitDone :
296
300
m .config = msg .config
297
301
m .resultsDir = msg .resultsDir
298
- m .startedAt = msg .startedAt
299
302
m .dockerClient = msg .dockerClient
300
303
301
304
tests := make ([]testModel , len (msg .testRunners ))
@@ -307,8 +310,13 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
307
310
tests [i ] = test
308
311
}
309
312
m .tests = tests
310
- m .initialized = true
311
313
314
+ if msg .err != nil {
315
+ slog .Error ("Error initializing" , "error" , msg .err )
316
+ return m , m .quitCmd ()
317
+ }
318
+
319
+ m .initialized = true
312
320
return m , m .runTestCmd (0 )
313
321
case mainModelMsgNextTest :
314
322
if msg .testIndex >= len (m .tests ) {
0 commit comments