Skip to content

Commit 9e5d1d5

Browse files
committed
Add timeouts
1 parent 90b3e9c commit 9e5d1d5

File tree

11 files changed

+446
-124
lines changed

11 files changed

+446
-124
lines changed

dist/grader/builder/Builder.d.ts

Lines changed: 7 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/grader/builder/GradleBuilder.d.ts

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/grader/types.d.ts

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js

Lines changed: 174 additions & 54 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package-lock.json

Lines changed: 12 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"csv-parse": "^5.6.0",
5050
"glob": "^11.0.1",
5151
"node-fetch": "^3.3.2",
52+
"tinyspawn": "^1.5.0",
5253
"ts-interface-checker": "^1.0.2"
5354
},
5455
"devDependencies": {

src/grader/Grader.ts

Lines changed: 122 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import GradleBuilder from './builder/GradleBuilder.js'
88
import Logger from './Logger.js'
99
import {
1010
AutograderTestFeedback,
11+
DEFAULT_TIMEOUTS,
1112
GradedPart,
1213
GradedUnit,
1314
isMutationTestUnit,
@@ -297,13 +298,19 @@ class Grader {
297298
console.log('Resetting to run instructor tests on student submission')
298299
await this.resetSolutionFiles()
299300
await this.copyStudentFiles('files')
301+
const gradedParts = this.config.gradedParts || []
300302

301303
try {
302304
console.log(
303305
'Building project with student submission and running instructor tests'
304306
)
305-
await this.builder.buildClean()
307+
console.log('Really seems to be in a try/catch')
308+
await this.builder.buildClean({
309+
timeoutSeconds:
310+
this.config.build.timeouts_seconds?.build || DEFAULT_TIMEOUTS.build
311+
})
306312
} catch (err) {
313+
console.log('CIDebug: Build failed')
307314
const msg = err instanceof Error ? err.message : 'Unknown error'
308315
this.logger.log(
309316
'visible',
@@ -354,8 +361,58 @@ class Grader {
354361
artifacts: []
355362
}
356363
}
357-
358-
const testResults = await this.builder.test()
364+
let testResults: TestResult[] = []
365+
try {
366+
testResults = await this.builder.test({
367+
timeoutSeconds:
368+
this.config.build.timeouts_seconds?.instructor_tests ||
369+
DEFAULT_TIMEOUTS.instructor_tests
370+
})
371+
} catch (err) {
372+
this.logger.log(
373+
'visible',
374+
`An error occurred while running instructor tests. Please fix the above errors and resubmit for grading. Here is the error message: ${err}`
375+
)
376+
const allTests: AutograderTestFeedback[] = gradedParts
377+
.filter((part) => !part.hide_until_released)
378+
.map((part) =>
379+
part.gradedUnits.map((gradedUnit) => {
380+
if (isRegularTestUnit(gradedUnit)) {
381+
return {
382+
name: gradedUnit.name,
383+
output:
384+
'Build failed, test not run. Please see overall output for more details.',
385+
output_format: 'text' as OutputFormat,
386+
score: 0,
387+
part: part.name,
388+
max_score: gradedUnit.points
389+
}
390+
} else if (isMutationTestUnit(gradedUnit)) {
391+
return {
392+
name: gradedUnit.name,
393+
output:
394+
'Build failed, test not run. Please see overall output for more details.',
395+
output_format: 'text' as OutputFormat,
396+
score: 0,
397+
part: part.name,
398+
max_score: gradedUnit.breakPoints[0].pointsToAward
399+
}
400+
} else {
401+
throw new Error(
402+
`Unknown unit type in grading config: ${JSON.stringify(gradedUnit)}`
403+
)
404+
}
405+
})
406+
)
407+
.flat()
408+
return {
409+
lint: lintResult,
410+
output: this.logger.getEachOutput(),
411+
tests: allTests,
412+
score: 0,
413+
artifacts: []
414+
}
415+
}
359416
let mutantResults: MutantResult[] | undefined
360417
let mutantFailureAdvice: string | undefined
361418
let studentTestResults: TestResult[] | undefined
@@ -370,7 +427,10 @@ class Grader {
370427
await this.copyStudentFiles('testFiles')
371428
console.log('Building solution and running student tests')
372429
try {
373-
await this.builder.buildClean()
430+
await this.builder.buildClean({
431+
timeoutSeconds:
432+
this.config.build.timeouts_seconds?.build || DEFAULT_TIMEOUTS.build
433+
})
374434
} catch (err) {
375435
const msg = err instanceof Error ? err.message : 'Unknown error'
376436
mutantFailureAdvice =
@@ -381,44 +441,84 @@ class Grader {
381441
)
382442
this.logger.log('visible', msg)
383443
}
384-
studentTestResults = await this.builder.test()
385-
if (studentTestResults.some((result) => result.status === 'fail')) {
444+
try {
445+
studentTestResults = await this.builder.test({
446+
timeoutSeconds:
447+
this.config.build.timeouts_seconds?.student_tests ||
448+
DEFAULT_TIMEOUTS.student_tests
449+
})
450+
} catch (err) {
451+
const msg = err instanceof Error ? err.message : 'Unknown error'
452+
this.logger.log(
453+
'visible',
454+
'Error running student tests on instructor solution:'
455+
)
456+
this.logger.log('visible', msg)
457+
}
458+
if (
459+
!studentTestResults ||
460+
studentTestResults.some((result) => result.status === 'fail')
461+
) {
386462
this.logger.log(
387463
'visible',
388464
"Some of your tests failed when run against the instructor's solution. Your tests will not be graded for this submission. Please fix them before resubmitting. "
389465
)
390466
mutantFailureAdvice =
391467
"**Error**: Some of your tests failed when run against the instructor's solution. Your tests will not be graded for this submission. Please fix them before resubmitting.\n\n\nHere are your failing test results:\n\n\n"
392468
this.logger.log('visible', 'Here are your failing test results:')
393-
for (const result of studentTestResults) {
394-
if (result.status === 'fail') {
395-
mutantFailureAdvice += `\n❌ ${result.name}\n`
396-
mutantFailureAdvice += '```\n' + result.output + '\n```'
397-
this.logger.log('visible', `${result.name}: ${result.status}`)
398-
this.logger.log('visible', result.output)
469+
if (studentTestResults) {
470+
for (const result of studentTestResults) {
471+
if (result.status === 'fail') {
472+
mutantFailureAdvice += `\n❌ ${result.name}\n`
473+
mutantFailureAdvice += '```\n' + result.output + '\n```'
474+
this.logger.log('visible', `${result.name}: ${result.status}`)
475+
this.logger.log('visible', result.output)
476+
}
399477
}
400478
}
401479
mutantFailureAdvice +=
402480
'\n\nPlease fix the above errors and resubmit for grading.'
403481
} else {
404482
console.log('Running student tests against buggy solutions')
405-
mutantResults = await this.builder.mutationTest()
483+
try {
484+
mutantResults = await this.builder.mutationTest({
485+
timeoutSeconds:
486+
this.config.build.timeouts_seconds?.mutants ||
487+
DEFAULT_TIMEOUTS.mutants
488+
})
489+
} catch (err) {
490+
const msg = err instanceof Error ? err.message : 'Unknown error'
491+
this.logger.log('visible', 'Error running mutation tests: ' + msg)
492+
}
406493
}
407494
}
495+
let studentTestAdvice: string | undefined
408496
if (
409497
(this.config.build.student_tests?.student_impl?.report_branch_coverage ||
410498
this.config.build.student_tests?.student_impl?.run_tests) &&
411499
this.config.submissionFiles.testFiles.length > 0
412500
) {
413501
console.log('Running student tests against student implementation')
414-
await this.resetSolutionFiles()
415-
await this.copyStudentFiles('testFiles')
416-
await this.copyStudentFiles('files')
417-
await this.builder.buildClean()
418-
studentTestResults = await this.builder.test()
502+
try {
503+
await this.resetSolutionFiles()
504+
await this.copyStudentFiles('testFiles')
505+
await this.copyStudentFiles('files')
506+
await this.builder.buildClean({
507+
timeoutSeconds:
508+
this.config.build.timeouts_seconds?.build || DEFAULT_TIMEOUTS.build
509+
})
510+
studentTestResults = await this.builder.test({
511+
timeoutSeconds:
512+
this.config.build.timeouts_seconds?.student_tests ||
513+
DEFAULT_TIMEOUTS.student_tests
514+
})
515+
} catch (err) {
516+
const msg = err instanceof Error ? err.message : 'Unknown error'
517+
studentTestAdvice = 'Your tests failed to compile. ' + msg
518+
this.logger.log('visible', msg)
519+
}
419520
}
420521
console.log('Wrapping up')
421-
const gradedParts = this.config.gradedParts || []
422522
const testFeedbacks = gradedParts
423523
.map((part) =>
424524
this.gradePart(part, testResults, mutantResults, mutantFailureAdvice)
@@ -502,6 +602,9 @@ class Grader {
502602
const totalTestCount = studentTestResults?.length
503603
let studentTestOutput =
504604
'Please refer to your assignment instructions for the specifications of how (if at all) your tests will be graded. These results are purely informational:\n\n'
605+
if (studentTestAdvice) {
606+
studentTestOutput += studentTestAdvice
607+
}
505608
studentTestOutput += `**Student-written tests passed: ${passingTestCount} / ${totalTestCount}**\n`
506609
if (studentTestResults && studentTestResults.length > 0) {
507610
for (const result of studentTestResults) {

0 commit comments

Comments
 (0)