@@ -4,16 +4,18 @@ import (
4
4
"bytes"
5
5
"errors"
6
6
"fmt"
7
- "github.com/chermehdi/egor/config"
8
- "github.com/chermehdi/skimo/skimo"
9
- "github.com/fatih/color"
10
- "github.com/jedib0t/go-pretty/table"
11
7
"io/ioutil"
12
8
"os"
13
9
"os/exec"
14
10
"path"
15
11
"path/filepath"
16
12
"strings"
13
+ "time"
14
+
15
+ "github.com/chermehdi/egor/config"
16
+ "github.com/chermehdi/skimo/skimo"
17
+ "github.com/fatih/color"
18
+ "github.com/jedib0t/go-pretty/table"
17
19
18
20
"github.com/urfave/cli/v2"
19
21
)
@@ -23,17 +25,27 @@ var (
23
25
SK int8 = 1
24
26
RE int8 = 2
25
27
WA int8 = 3
28
+ TL int8 = 4
29
+ )
30
+
31
+ var (
32
+ OK int8 = 0 // OK execution status
33
+ TO int8 = 1 // Timed out status
26
34
)
27
35
28
36
var (
29
37
green = color .New (color .FgGreen ).SprintfFunc ()
30
38
red = color .New (color .FgRed ).SprintfFunc ()
31
39
magenta = color .New (color .FgMagenta ).SprintfFunc ()
32
40
yellow = color .New (color .FgYellow ).SprintfFunc ()
41
+ blue = color .New (color .FgBlue ).SprintfFunc ()
33
42
)
34
43
35
44
const WorkDir = "work"
36
45
46
+ // Time out delta in miliseconds
47
+ const TimeOutDelta float64 = 25.0
48
+
37
49
// Checks the output of a given testcase against it's expected output
38
50
type Checker interface {
39
51
// Execute the check (got, expected) and returns
@@ -59,6 +71,7 @@ type CaseDescription struct {
59
71
OutputFile string
60
72
WorkFile string
61
73
CustomCase bool
74
+ TimeLimit float64
62
75
}
63
76
64
77
func getWorkFile (fileName string ) string {
@@ -68,14 +81,15 @@ func getWorkFile(fileName string) string {
68
81
}
69
82
70
83
// Creates a new CaseDescription from a pair of input and output IoFiles
71
- func NewCaseDescription (input , output config.IoFile ) * CaseDescription {
84
+ func NewCaseDescription (input , output config.IoFile , timeLimit float64 ) * CaseDescription {
72
85
base , file := filepath .Split (input .Path )
73
86
workFilePath := path .Join (base , getWorkFile (file ))
74
87
return & CaseDescription {
75
88
InputFile : input .Path ,
76
89
OutputFile : output .Path ,
77
90
WorkFile : workFilePath ,
78
91
CustomCase : input .Custom ,
92
+ TimeLimit : timeLimit ,
79
93
}
80
94
}
81
95
@@ -85,6 +99,7 @@ type CaseStatus struct {
85
99
Status int8
86
100
CheckerError error
87
101
Stderr string
102
+ Duration time.Duration
88
103
}
89
104
90
105
// Implementation must be able to prepare the working environment to compile and execute testscases,
@@ -144,6 +159,8 @@ func getDisplayStatus(status int8) string {
144
159
return yellow ("SK" )
145
160
case WA :
146
161
return red ("WA" )
162
+ case TL :
163
+ return blue ("TL" )
147
164
}
148
165
return "Unknown"
149
166
}
@@ -158,7 +175,7 @@ func getStderrDisplay(stderr string) string {
158
175
func (c * ConsoleJudgeReport ) Display () {
159
176
t := table .NewWriter ()
160
177
t .SetOutputMirror (os .Stdout )
161
- t .AppendHeader (table.Row {"#" , "Test Name" , "Status" , "Custom" , "Additional infos" , "Stderr" })
178
+ t .AppendHeader (table.Row {"#" , "Test Name" , "Status" , "Custom" , "Additional infos" , "Stderr" , "Execution Time" })
162
179
for i , stat := range c .Stats {
163
180
output := "None"
164
181
if stat .CheckerError != nil {
@@ -171,6 +188,7 @@ func (c *ConsoleJudgeReport) Display() {
171
188
c .Descs [i ].CustomCase ,
172
189
output ,
173
190
getStderrDisplay (stat .Stderr ),
191
+ stat .Duration ,
174
192
})
175
193
}
176
194
t .SetStyle (table .StyleLight )
@@ -181,6 +199,26 @@ func newJudgeReport() JudgeReport {
181
199
return & ConsoleJudgeReport {Stats : []CaseStatus {}}
182
200
}
183
201
202
+ // Utility function to execute a given command and insure to stop it after a timeOut (in miliseconds).
203
+ // The function returns the status of the execution, the duration of the exeuction, and an error (if any).
204
+ func timedExecution (cmd * exec.Cmd , timeOut float64 ) (int8 , time.Duration , error ) {
205
+ cmd .Start ()
206
+ start := time .Now ()
207
+ done := make (chan error )
208
+ go func () { done <- cmd .Wait () }()
209
+
210
+ timeout := time .After (time .Duration (timeOut ) * time .Millisecond )
211
+ select {
212
+ case <- timeout :
213
+ elapsed := time .Since (start )
214
+ cmd .Process .Kill ()
215
+ return TO , elapsed , nil
216
+ case err := <- done :
217
+ elapsed := time .Since (start )
218
+ return OK , elapsed , err
219
+ }
220
+ }
221
+
184
222
// Utility function to execute the given command that is associated with the given judge
185
223
// the method returns the case status and the error (if any)
186
224
func execute (judge Judge , desc CaseDescription , command string , args ... string ) (CaseStatus , error ) {
@@ -208,11 +246,23 @@ func execute(judge Judge, desc CaseDescription, command string, args ...string)
208
246
cmd .Stdin = inputFile
209
247
cmd .Stdout = outputFile
210
248
cmd .Stderr = & stderrBuffer
211
- if err = cmd .Run (); err != nil {
249
+
250
+ status , duration , err := timedExecution (cmd , desc .TimeLimit + TimeOutDelta )
251
+ if status == TO {
252
+ return CaseStatus {
253
+ Status : TL ,
254
+ CheckerError : nil ,
255
+ Stderr : stderrBuffer .String (),
256
+ Duration : duration ,
257
+ }, nil
258
+ }
259
+
260
+ if err != nil {
212
261
return CaseStatus {
213
262
Status : RE ,
214
263
CheckerError : nil ,
215
264
Stderr : stderrBuffer .String (),
265
+ Duration : duration ,
216
266
}, err
217
267
}
218
268
@@ -221,6 +271,7 @@ func execute(judge Judge, desc CaseDescription, command string, args ...string)
221
271
return CaseStatus {
222
272
Status : RE ,
223
273
CheckerError : nil ,
274
+ Duration : duration ,
224
275
}, err
225
276
}
226
277
output , err := ioutil .ReadFile (desc .WorkFile )
@@ -229,6 +280,7 @@ func execute(judge Judge, desc CaseDescription, command string, args ...string)
229
280
return CaseStatus {
230
281
Status : RE ,
231
282
CheckerError : nil ,
283
+ Duration : duration ,
232
284
}, err
233
285
}
234
286
err = judge .Checker ().Check (string (output ), string (expectedOutput ))
@@ -237,12 +289,14 @@ func execute(judge Judge, desc CaseDescription, command string, args ...string)
237
289
Status : WA ,
238
290
CheckerError : err ,
239
291
Stderr : stderrBuffer .String (),
292
+ Duration : duration ,
240
293
}, err
241
294
}
242
295
return CaseStatus {
243
296
Status : AC ,
244
297
CheckerError : nil ,
245
298
Stderr : stderrBuffer .String (),
299
+ Duration : duration ,
246
300
}, err
247
301
}
248
302
@@ -459,7 +513,7 @@ func RunAction(_ *cli.Context) error {
459
513
460
514
for i , input := range egorMeta .Inputs {
461
515
output := egorMeta .Outputs [i ]
462
- caseDescription := NewCaseDescription (input , output )
516
+ caseDescription := NewCaseDescription (input , output , egorMeta . TimeLimit )
463
517
status := judge .RunTestCase (* caseDescription )
464
518
report .Add (status , * caseDescription )
465
519
}
0 commit comments