forked from workshopper/workshopper-exercise
-
Notifications
You must be signed in to change notification settings - Fork 1
/
execute.js
103 lines (76 loc) · 3.24 KB
/
execute.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
const spawn = require('child_process').spawn
, path = require('path')
, fs = require('fs')
, after = require('after')
, xtend = require('xtend')
function execute (exercise, opts) {
if (!opts) opts = {}
exercise.addSetup(setup)
exercise.addProcessor(processor)
// override if you want to mess with stdout
exercise.getStdout = function (type, child) {
// type == 'submission' || 'solution'
return child.stdout
}
exercise.getSolutionFiles = function (callback) {
var translated = path.join(this.dir, './solution_' + this.lang)
fs.exists(translated, function (exists) {
var solutionDir = exists ? translated : path.join(this.dir, './solution')
fs.readdir(solutionDir, function (err, list) {
if (err)
return callback(err)
list = list
.filter(function (f) { return (/\.js$/).test(f) })
.map(function (f) { return path.join(solutionDir, f)})
callback(null, list)
})
}.bind(this))
}
return exercise
function setup (mode, callback) {
this.submission = this.args[0] // first arg obviously
// default args, override if you want to pass special args to the
// solution and/or submission, override this.setup to do this
this.submissionArgs = Array.prototype.slice.call(1, this.args)
this.solutionArgs = Array.prototype.slice.call(1, this.args)
// edit/override if you want to alter the child process environment
this.env = xtend(process.env)
// set this.solution if your solution is elsewhere
if (!this.solution) {
var localisedSolutionPath = path.join(this.dir, './solution_' + this.lang + '/solution.js');
this.solution = fs.existsSync(localisedSolutionPath) ? localisedSolutionPath : path.join(this.dir, './solution/solution.js')
}
process.nextTick(callback)
}
function kill () {
;[ this.submissionChild, this.solutionChild ].forEach(function (child) {
if (child && typeof child.kill == 'function')
child.kill()
})
setTimeout(function () {
this.emit('executeEnd')
}.bind(this), 10)
}
function processor (mode, callback) {
var ended = after(mode == 'verify' ? 2 : 1, kill.bind(this))
// backwards compat for workshops that use older custom setup functions
if (!this.solutionCommand) this.solutionCommand = [ this.solution ].concat(this.solutionArgs)
if (!this.submissionCommand) this.submissionCommand = [ this.submission ].concat(this.submissionArgs)
this.submissionChild = spawn(opts.exec || process.execPath, this.submissionCommand, this.env)
this.submissionStdout = this.getStdout('submission', this.submissionChild)
setImmediate(function () { // give other processors a chance to overwrite stdout
this.submissionStdout.on('end', ended)
}.bind(this))
if (mode == 'verify') {
this.solutionChild = spawn(opts.exec || process.execPath, this.solutionCommand, this.env)
this.solutionStdout = this.getStdout('solution', this.solutionChild)
setImmediate(function () { // give other processors a chance to overwrite stdout
this.solutionStdout.on('end', ended)
}.bind(this))
}
process.nextTick(function () {
callback(null, true)
})
}
}
module.exports = execute