Skip to content

Commit 951f73b

Browse files
mehdi.cheracher@gmail.comchermehdi
authored andcommitted
wip
1 parent 7925ed5 commit 951f73b

File tree

2 files changed

+240
-3
lines changed

2 files changed

+240
-3
lines changed

commands/parse_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func getDummyTaskJson() string {
2323
"languages" : {
2424
"java" : {
2525
"mainClass" : "Main",
26-
"taskClass" : "CMinimizeTheInteger"
26+
"taskClass" : "CMinimizeTheInteger"
2727
}
2828
},
2929
"input" : {

commands/run.go

+239-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,28 @@
11
package commands
22

33
import (
4+
"bufio"
5+
"bytes"
46
"errors"
57
"fmt"
8+
"github.com/chermehdi/egor/config"
9+
"io/ioutil"
10+
"os"
11+
"os/exec"
12+
"path"
13+
"path/filepath"
614
"strings"
715

816
"github.com/urfave/cli/v2"
917
)
1018

19+
var (
20+
AC int8 = 0
21+
SK int8 = 1
22+
RE int8 = 2
23+
WA int8 = 3
24+
)
25+
1126
// Checks the output of a given testcase against it's expected output
1227
type Checker interface {
1328
// Execute the check (got, expected) and returns
@@ -31,12 +46,34 @@ func (c *DiffChecker) Check(got, expected string) error {
3146
type CaseDescription struct {
3247
InputFile string
3348
OutputFile string
49+
WorkFile string
3450
CustomCase bool
3551
}
3652

53+
func getWorkFile(fileName string) string {
54+
fileNameParts := strings.Split(fileName, ".")
55+
fileNameNoExtension := fileNameParts[0]
56+
return fmt.Sprintf("%s-ex.out", fileNameNoExtension)
57+
}
58+
59+
// Creates a new CaseDescription from a pair of input and output IoFiles
60+
func NewCaseDescription(input, output config.IoFile) *CaseDescription {
61+
base, file := filepath.Split(input.Path)
62+
workFilePath := path.Join(base, getWorkFile(file))
63+
return &CaseDescription{
64+
InputFile: input.Path,
65+
OutputFile: output.Path,
66+
WorkFile: workFilePath,
67+
CustomCase: input.Custom,
68+
}
69+
}
70+
3771
// Report the execution status for a given testcase.
38-
// Type also stores the compiler output, and the checker response
72+
// Type stores also checker response
3973
type CaseStatus struct {
74+
Status int8
75+
CheckerError error
76+
Stderr string
4077
}
4178

4279
// Implementation must be able to prepare the working environement to compile and execute testscases,
@@ -55,10 +92,210 @@ type Judge interface {
5592
Cleanup() error
5693
}
5794

58-
func RunAction(context *cli.Context) error {
95+
// This represents the result of running the testcases of a given task
96+
type JudgeReport interface {
97+
Add(status CaseStatus)
98+
99+
Display() string
100+
}
101+
102+
type ConsoleJudgeReport struct {
103+
Stats []CaseStatus
104+
}
105+
106+
func (c *ConsoleJudgeReport) Add(status CaseStatus) {
107+
c.Stats = append(c.Stats, status)
108+
}
109+
110+
func (c *ConsoleJudgeReport) Display() string {
111+
panic("implement me")
112+
}
113+
114+
func newJudgeReport() JudgeReport {
115+
return &ConsoleJudgeReport{Stats: []CaseStatus{}}
116+
}
117+
118+
//
119+
// Java judge
120+
//
121+
type JavaJudge struct {
122+
Meta config.EgorMeta
123+
CurrentWorkDir string
124+
Checker Checker
125+
}
126+
127+
func (judge *JavaJudge) Setup() error {
128+
currentDir, err := os.Getwd()
129+
if err != nil {
130+
return err
131+
}
132+
workDirPath := path.Join(currentDir, "work")
133+
if err = os.Mkdir(workDirPath, 777); err != nil {
134+
return err
135+
}
136+
//TODO(chermehdi): make the executables path configurable #14
137+
138+
// Compilation for Java
139+
cmd := exec.Command("javac", judge.Meta.TaskFile, "-d", workDirPath)
140+
if err = cmd.Run(); err != nil {
141+
return err
142+
}
143+
judge.CurrentWorkDir = workDirPath
144+
return nil
145+
}
146+
147+
func (judge *JavaJudge) RunTestCase(desc CaseDescription) CaseStatus {
148+
// We suppose that all java executables will be called Main
149+
execFilePath := path.Join(judge.CurrentWorkDir, "Main")
150+
cmd := exec.Command("java", execFilePath)
151+
cmd.Dir = judge.CurrentWorkDir
152+
inputFile, err := os.Open(desc.InputFile)
153+
if err != nil {
154+
return CaseStatus{
155+
Status: RE,
156+
CheckerError: nil,
157+
}
158+
}
159+
defer inputFile.Close()
160+
161+
outputFile, err := os.OpenFile(desc.WorkFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
162+
if err != nil {
163+
return CaseStatus{
164+
Status: RE,
165+
CheckerError: nil,
166+
}
167+
}
168+
defer outputFile.Close()
169+
170+
var stderrBuffer bytes.Buffer
171+
cmd.Stdin = inputFile
172+
cmd.Stdout = bufio.NewWriter(outputFile)
173+
cmd.Stderr = &stderrBuffer
174+
if err = cmd.Run(); err != nil {
175+
return CaseStatus{
176+
Status: RE,
177+
CheckerError: nil,
178+
Stderr: stderrBuffer.String(),
179+
}
180+
181+
}
182+
183+
expectedOutput, err := ioutil.ReadFile(desc.OutputFile)
184+
if err != nil {
185+
return CaseStatus{
186+
Status: RE,
187+
CheckerError: nil,
188+
}
189+
}
190+
output, err := ioutil.ReadFile(desc.WorkFile)
191+
if err != nil {
192+
return CaseStatus{
193+
Status: RE,
194+
CheckerError: nil,
195+
}
196+
}
197+
err = judge.Checker.Check(string(output), string(expectedOutput))
198+
if err != nil {
199+
return CaseStatus{
200+
Status: WA,
201+
CheckerError: err,
202+
Stderr: stderrBuffer.String(),
203+
}
204+
}
205+
return CaseStatus{
206+
Status: AC,
207+
CheckerError: err,
208+
Stderr: stderrBuffer.String(),
209+
}
210+
}
211+
212+
func (judge *JavaJudge) Cleanup() error {
213+
return os.RemoveAll(judge.CurrentWorkDir)
214+
}
215+
216+
//
217+
// C / Cpp Judge
218+
//
219+
type CppJudge struct {
220+
Meta config.EgorMeta
221+
}
222+
223+
func (judge *CppJudge) Setup() error {
224+
return nil
225+
}
226+
227+
func (judge *CppJudge) RunTestCase(description CaseDescription) CaseStatus {
228+
return CaseStatus{}
229+
}
230+
func (judge *CppJudge) Cleanup() error {
59231
return nil
60232
}
61233

234+
//
235+
// Python Judge
236+
//
237+
type PythonJudge struct {
238+
Meta config.EgorMeta
239+
}
240+
241+
func (judge *PythonJudge) Setup() error {
242+
panic("implement me")
243+
}
244+
245+
func (judge *PythonJudge) RunTestCase(CaseDescription) CaseStatus {
246+
panic("implement me")
247+
}
248+
249+
func (judge *PythonJudge) Cleanup() error {
250+
panic("implement me")
251+
}
252+
253+
// Creates and returns a Judge implementation corresponding to the given language
254+
func NewJudgeFor(meta config.EgorMeta) (Judge, error) {
255+
switch meta.TaskLang {
256+
case "java":
257+
return &JavaJudge{Meta: meta}, nil
258+
case "cpp":
259+
case "c":
260+
return &CppJudge{Meta: meta}, nil
261+
case "python":
262+
return &PythonJudge{Meta: meta}, nil
263+
}
264+
return nil, errors.New(fmt.Sprintf("Cannot find judge for the given lang %s", meta.TaskLang))
265+
}
266+
267+
func RunAction(_ *cli.Context) error {
268+
configuration, err := config.LoadDefaultConfiguration()
269+
if err != nil {
270+
return err
271+
}
272+
cwd, err := os.Getwd()
273+
if err != nil {
274+
return err
275+
}
276+
egorMetaFile := path.Join(cwd, configuration.ConfigFileName)
277+
egorMeta, err := config.LoadMetaFromPath(egorMetaFile)
278+
if err != nil {
279+
return err
280+
}
281+
282+
judge, err := NewJudgeFor(egorMeta)
283+
if err != nil {
284+
return err
285+
}
286+
if err := judge.Setup(); err != nil {
287+
return err
288+
}
289+
defer judge.Cleanup()
290+
report := newJudgeReport()
291+
for i, input := range egorMeta.Inputs {
292+
output := egorMeta.Outputs[i]
293+
caseDescription := NewCaseDescription(input, output)
294+
status := judge.RunTestCase(*caseDescription)
295+
report.Add(status)
296+
}
297+
}
298+
62299
var TestCommand = cli.Command{
63300
Name: "test",
64301
Aliases: []string{"r"},

0 commit comments

Comments
 (0)