WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit ffe76c8

Browse files
committed
added tango compatibility and test
1 parent 69a4ad6 commit ffe76c8

File tree

2 files changed

+241
-4
lines changed

2 files changed

+241
-4
lines changed

src/service/jobs/job_service.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,24 @@ type JobService struct {
2626
}
2727

2828
func NewJobService(db *gorm.DB, bc *models.BroadcastChannel, dockerService *docker.DkService, labService *labs.LabService) *JobService {
29-
return &JobService{
29+
srv := &JobService{
3030
db: db,
3131
broadcastCh: bc,
3232
dockerSrv: dockerService,
3333
queue: NewJobQueue(uint(com.ConcurrentJobs.GetUint64()), db, dockerService),
3434
labSrv: labService,
3535
}
36+
srv.cleanupOrphanJobs()
37+
return srv
3638
}
3739

3840
func NewJobServiceWithDeps(dockerService *docker.DkService, labService *labs.LabService) *JobService {
3941
db, bc := com.InitDB()
4042
srv := NewJobService(db, bc, dockerService, labService)
41-
srv.cleanupOrphanJobs()
4243
return srv
4344
}
4445

45-
func (job *JobService) NewJobFromRPC(newJob *models.Job, jobFiles []*v1.FileUpload, dockerfile *v1.FileUpload) (string, error) {
46+
func (job *JobService) NewJobFromRPC(newJob *models.Job, jobFiles []*v1.FileUpload, dockerfile *v1.FileUpload, isTangoCompatible ...bool) (string, error) {
4647
jobId, err := uuid.NewUUID()
4748
if err != nil {
4849
log.Error().Err(err).Msg("failed to generate job ID")
@@ -84,6 +85,11 @@ func (job *JobService) NewJobFromRPC(newJob *models.Job, jobFiles []*v1.FileUplo
8485
newJob.LabData.VerifyJobLimits()
8586
jog(newJob.JobCtx).Debug().Any("limits", newJob.LabData.JobLimits).Msg("job limits")
8687

88+
if len(isTangoCompatible) > 0 && isTangoCompatible[0] {
89+
// todo maybe let entry command be compatible
90+
newJob.LabData.JobEntryCmd = createTangoEntryCommand(nil)
91+
}
92+
8793
res := job.db.Create(newJob)
8894
if res.Error != nil {
8995
jog(ctx).Error().Err(res.Error).Msg("Failed to save job to db")
@@ -325,7 +331,7 @@ func (job *JobService) getJobFromDB(jobUuid string) (*models.Job, error) {
325331
// for example machine running leviathan shutdown unexpectedly or leviathan had an unrecoverable error
326332
func (job *JobService) cleanupOrphanJobs() {
327333
var orphanJobs []*models.Job
328-
res := job.db.
334+
res := job.db.Preload("labs").
329335
Where("status = ?", string(models.Queued)).
330336
Or("status = ?", string(models.Running)).
331337
Or("status = ?", string(models.Preparing)).
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
package jobs
2+
3+
import (
4+
v1 "github.com/makeopensource/leviathan/generated/types/v1"
5+
"github.com/makeopensource/leviathan/models"
6+
"os"
7+
"path/filepath"
8+
"testing"
9+
"time"
10+
)
11+
12+
var (
13+
basePath = "../../../example/tango"
14+
tangoDockerFile = basePath + "/tango-Dockerfile"
15+
autolab0 = basePath + "/tango0"
16+
autolab1 = basePath + "/tango1"
17+
autolab2 = basePath + "/tango2"
18+
autolab3 = basePath + "/tango3"
19+
autolab4 = basePath + "/tango4"
20+
)
21+
22+
type testMap = map[string]testCase
23+
24+
var (
25+
tangoTestCases = map[string]testMap{
26+
"tango0": {
27+
"correct": {
28+
studentFile: autolab0 + "/handin.py",
29+
expectedOutput: `{"scores": {"q1": 10, "q2": 10, "q3": 10}}`,
30+
correctStatus: models.Complete,
31+
},
32+
"cheating1": testCase{
33+
studentFile: autolab0 + "/handin_cheating1.py",
34+
expectedOutput: `{"scores": {"q1": 0, "q2": 0, "q3": 0}}`,
35+
correctStatus: models.Complete,
36+
},
37+
"cheating2": testCase{
38+
studentFile: autolab0 + "/handin_cheating2.py",
39+
expectedOutput: `Maximum timeout reached for job, job ran for 10s`,
40+
correctStatus: models.Failed,
41+
},
42+
"incorrect1": testCase{
43+
studentFile: autolab0 + "/handin_incorrect1.py",
44+
expectedOutput: `{"scores": {"q1": 0, "q2": 0, "q3": 0}}`,
45+
correctStatus: models.Complete,
46+
},
47+
"incorrect2": testCase{
48+
studentFile: autolab0 + "/handin_incorrect2.py",
49+
expectedOutput: `unable to parse log output`,
50+
correctStatus: models.Failed,
51+
},
52+
},
53+
"tango1": {
54+
"correct": {
55+
studentFile: autolab1 + "/handin.py",
56+
expectedOutput: `{"scores": {"q1": 10, "q2": 10, "q3": 10}}`,
57+
correctStatus: models.Complete,
58+
},
59+
"incorrect1": testCase{
60+
studentFile: autolab1 + "/handin_incorrect1.py",
61+
expectedOutput: `{"scores": {"q1": 10, "q2": 0, "q3": 0}}`,
62+
correctStatus: models.Complete,
63+
},
64+
"incorrect2": testCase{
65+
studentFile: autolab1 + "/handin_incorrect2.py",
66+
expectedOutput: `{"scores": {"q1": 9, "q2": 3, "q3": 3}}`,
67+
correctStatus: models.Complete,
68+
},
69+
"incorrect3": testCase{
70+
studentFile: autolab1 + "/handin_incorrect3.py",
71+
expectedOutput: `{"scores": {"q1": 1, "q2": 0, "q3": 0}}`,
72+
correctStatus: models.Complete,
73+
},
74+
"incorrect4": testCase{
75+
studentFile: autolab1 + "/handin_incorrect4.py",
76+
expectedOutput: `{"scores": {"q1": 0, "q2": 0, "q3": 0}}`,
77+
correctStatus: models.Complete,
78+
},
79+
},
80+
"tango3": {
81+
"correct": {
82+
studentFile: autolab3 + "/handin.json",
83+
expectedOutput: `{"scores": {"q1": 10, "q2": 10, "q3": 99}}`,
84+
correctStatus: models.Complete,
85+
},
86+
},
87+
"tango4": {
88+
"correct": {
89+
studentFile: autolab4 + "/handin.py",
90+
expectedOutput: `{"scores": {"q1": 10}}`,
91+
correctStatus: models.Complete,
92+
},
93+
"incorrect": {
94+
studentFile: autolab4 + "/handin_incorrect.py",
95+
expectedOutput: `{"scores": {"q1": 0}}`,
96+
correctStatus: models.Complete,
97+
},
98+
},
99+
}
100+
tangoTestFuncs = map[string]func(*testing.T){
101+
"autolab0": TestTango0,
102+
}
103+
)
104+
105+
func TestTango0(t *testing.T) {
106+
setupTest()
107+
108+
folderName := "tango0"
109+
cases := tangoTestCases[folderName]
110+
111+
for name, test := range cases {
112+
t.Run(name, func(t *testing.T) {
113+
t.Parallel()
114+
jobid := setupJobProcessTango(t, basePath+"/"+folderName, test.studentFile, 10*time.Second)
115+
testJob(t, jobid, test.expectedOutput, test.correctStatus)
116+
})
117+
}
118+
}
119+
120+
func TestTango1(t *testing.T) {
121+
setupTest()
122+
123+
folderName := "tango1"
124+
cases := tangoTestCases[folderName]
125+
126+
for name, test := range cases {
127+
t.Run(name, func(t *testing.T) {
128+
t.Parallel()
129+
jobid := setupJobProcessTango(t, basePath+"/"+folderName, test.studentFile, 10*time.Second)
130+
testJob(t, jobid, test.expectedOutput, test.correctStatus)
131+
})
132+
}
133+
}
134+
135+
func TestTango3(t *testing.T) {
136+
setupTest()
137+
138+
folderName := "tango3"
139+
cases := tangoTestCases[folderName]
140+
141+
for name, test := range cases {
142+
t.Run(name, func(t *testing.T) {
143+
t.Parallel()
144+
jobid := setupJobProcessTango(t, basePath+"/"+folderName, test.studentFile, 10*time.Second)
145+
testJob(t, jobid, test.expectedOutput, test.correctStatus)
146+
})
147+
}
148+
}
149+
150+
func TestTango4(t *testing.T) {
151+
setupTest()
152+
153+
folderName := "tango4"
154+
cases := tangoTestCases[folderName]
155+
156+
for name, test := range cases {
157+
t.Run(name, func(t *testing.T) {
158+
t.Parallel()
159+
jobid := setupJobProcessTango(t, basePath+"/"+folderName, test.studentFile, 10*time.Second)
160+
testJob(t, jobid, test.expectedOutput, test.correctStatus)
161+
})
162+
}
163+
}
164+
165+
func setupJobProcessTango(t *testing.T, testFolder, studentCodePath string, timeout time.Duration) string {
166+
167+
tarPath := testFolder + "/autograde.tar"
168+
graderTar, err := os.ReadFile(tarPath)
169+
if err != nil {
170+
t.Fatal(err)
171+
}
172+
tangoMakeFilePath := testFolder + "/Makefile"
173+
tangoMakeFilePathBytes, err := os.ReadFile(tangoMakeFilePath)
174+
if err != nil {
175+
t.Fatal(err)
176+
}
177+
178+
studentFileName := filepath.Base(studentCodePath)
179+
if filepath.Ext(studentFileName) == ".json" {
180+
studentFileName = "handin.json"
181+
} else {
182+
studentFileName = "handin.py"
183+
}
184+
185+
studentBytes, err := os.ReadFile(studentCodePath)
186+
if err != nil {
187+
t.Fatal(err)
188+
}
189+
190+
dockerBytes, err := os.ReadFile(tangoDockerFile)
191+
if err != nil {
192+
t.Fatal(err)
193+
}
194+
195+
newLab := models.Lab{
196+
JobTimeout: timeout,
197+
//JobEntryCmd: "ls -la ;while true; do sleep 1; done",
198+
ImageTag: "tango-test",
199+
}
200+
201+
newJob := &models.Job{LabData: &newLab}
202+
203+
jobId, err := jobTestService.NewJobFromRPC(
204+
newJob,
205+
[]*v1.FileUpload{
206+
{
207+
Filename: filepath.Base(tarPath),
208+
Content: graderTar,
209+
},
210+
{
211+
Filename: filepath.Base(tangoMakeFilePath),
212+
Content: tangoMakeFilePathBytes,
213+
},
214+
{
215+
Filename: studentFileName,
216+
Content: studentBytes,
217+
},
218+
},
219+
&v1.FileUpload{
220+
Filename: filepath.Base(tangoDockerFile),
221+
Content: dockerBytes,
222+
},
223+
true,
224+
)
225+
226+
if err != nil {
227+
t.Fatal("unable to create job", err)
228+
}
229+
230+
return jobId
231+
}

0 commit comments

Comments
 (0)