@@ -30,7 +30,8 @@ module KGrader | |||
def tests | |||
@tests ||= @config['grade'].map do |it| | |||
{ :name => it.keys.first, :max => it.values.first } | |||
script = File.join @root, it.keys.first + ".rb" | |||
{ :name => it.keys.first, :script => script, :max => it.values.first } | |||
end | |||
end | |||
@@ -36,6 +36,7 @@ module KGrader::Backend | |||
def commit(repo, message, paths = nil) | |||
# TODO | |||
# run 'commit', '-m', message, *paths.map { |fn| File.join repo, fn } | |||
end | |||
def commit_date(repo) | |||
@@ -3,6 +3,7 @@ module KGrader | |||
def initialize(root) | |||
@root = root | |||
@salt = nil | |||
end | |||
def reset | |||
@@ -11,6 +12,7 @@ module KGrader | |||
def init | |||
FileUtils.mkdir_p @root | |||
@salt = rand 100000000 | |||
end | |||
def stage(source, target) | |||
@@ -18,15 +20,43 @@ module KGrader | |||
end | |||
def exec(command, logpath) | |||
pid = Process.fork do | |||
fp = File.open(logpath, 'w+') | |||
Dir.chdir @root | |||
# TODO: rlimit in exec, umask? | |||
Process.exec command, :in => :close, :out => fp, :err => fp, | |||
:close_others => true | |||
end | |||
pid = execute command, logpath | |||
Process.waitpid pid, 0 | |||
$?.exited? && $?.exitstatus == 0 | |||
end | |||
def run_test(script, logpath) | |||
grade_rd, grade_wr = IO.pipe | |||
cmt_rd, cmt_wr = IO.pipe | |||
command = ['ruby', '-r', '../lib/kgrader/runtime.rb', script, @salt] | |||
pid = execute command, logpath do |options| | |||
[grade_rd, cmt_rd].each &:close | |||
options[3] = grade_wr | |||
options[4] = cmt_wr | |||
end | |||
[grade_wr, cmt_wr].each &:close | |||
Process.waitpid pid, 0 | |||
cmt_rd.read.split("\n").each { |cmt| yield cmt } if block_given? | |||
grade = grade_rd.read.strip.to_i | |||
[grade_rd, cmt_rd].each &:close | |||
grade | |||
end | |||
private | |||
def execute(command, logpath) | |||
Process.fork do | |||
fp = File.open(logpath, 'a') | |||
Dir.chdir @root | |||
options = { | |||
:in => :close, :out => fp, :err => fp, :close_others => true, | |||
:rlimit_nproc => 32 | |||
} | |||
yield options if block_given? | |||
Process.exec *command, options | |||
end | |||
end | |||
end | |||
end |
@@ -60,10 +60,9 @@ module KGrader | |||
def commit | |||
if status == :graded && File.exists?(pendingfile) | |||
target = File.join(repo, @assignment.report) | |||
message = @assignment.commit_message @student | |||
FileUtils.cp gradefile, target | |||
@course.backend.commit repo, message, target | |||
FileUtils.cp gradefile, File.join(repo, @assignment.report) | |||
@course.backend.commit repo, message, @assignment.report | |||
FileUtils.rm pendingfile | |||
end | |||
end | |||
@@ -113,16 +112,12 @@ module KGrader | |||
@failure = false | |||
@comments = [] | |||
@summary = nil | |||
@tests = [] | |||
@tests = @assignment.tests.clone.each { |test| test[:score] = 0 } | |||
self.status = :ungraded | |||
FileUtils.rm_f [buildlog, testlog] | |||
@fs.jail.reset | |||
@fs.jail.init | |||
@assignment.tests.each do |test| | |||
@tests.push({ :name => test[:name], :max => test[:max], :score => 0 }) | |||
end | |||
end | |||
def stage | |||
@@ -142,8 +137,10 @@ module KGrader | |||
def test | |||
return if @failure | |||
@assignment.tests.each do |test| | |||
# TODO: execute script in jail and update @test/@comments; out testlog | |||
@tests.each do |test| | |||
test[:score] = @fs.jail.run_test test[:script], testlog do |comment| | |||
@comments.push "#{test[:name]}: #{comment}" | |||
end | |||
end | |||
end | |||
@@ -199,7 +196,7 @@ module KGrader | |||
end | |||
def generate_summary | |||
tests = @tests.each do |test| | |||
tests = @tests.map do |test| | |||
"#{test[:score].to_s.rjust get_span(test[:max])}/#{test[:max]}" | |||
end.join ', ' | |||
"#{format_points score, max_score}: #{tests}" | |||