@@ -30,7 +30,8 @@ module KGrader | |||||
def tests | def tests | ||||
@tests ||= @config['grade'].map do |it| | @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 | ||||
end | end | ||||
@@ -36,6 +36,7 @@ module KGrader::Backend | |||||
def commit(repo, message, paths = nil) | def commit(repo, message, paths = nil) | ||||
# TODO | # TODO | ||||
# run 'commit', '-m', message, *paths.map { |fn| File.join repo, fn } | |||||
end | end | ||||
def commit_date(repo) | def commit_date(repo) | ||||
@@ -3,6 +3,7 @@ module KGrader | |||||
def initialize(root) | def initialize(root) | ||||
@root = root | @root = root | ||||
@salt = nil | |||||
end | end | ||||
def reset | def reset | ||||
@@ -11,6 +12,7 @@ module KGrader | |||||
def init | def init | ||||
FileUtils.mkdir_p @root | FileUtils.mkdir_p @root | ||||
@salt = rand 100000000 | |||||
end | end | ||||
def stage(source, target) | def stage(source, target) | ||||
@@ -18,15 +20,43 @@ module KGrader | |||||
end | end | ||||
def exec(command, logpath) | 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 | Process.waitpid pid, 0 | ||||
$?.exited? && $?.exitstatus == 0 | $?.exited? && $?.exitstatus == 0 | ||||
end | 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 | ||||
end | end |
@@ -60,10 +60,9 @@ module KGrader | |||||
def commit | def commit | ||||
if status == :graded && File.exists?(pendingfile) | if status == :graded && File.exists?(pendingfile) | ||||
target = File.join(repo, @assignment.report) | |||||
message = @assignment.commit_message @student | 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 | FileUtils.rm pendingfile | ||||
end | end | ||||
end | end | ||||
@@ -113,16 +112,12 @@ module KGrader | |||||
@failure = false | @failure = false | ||||
@comments = [] | @comments = [] | ||||
@summary = nil | @summary = nil | ||||
@tests = [] | |||||
@tests = @assignment.tests.clone.each { |test| test[:score] = 0 } | |||||
self.status = :ungraded | self.status = :ungraded | ||||
FileUtils.rm_f [buildlog, testlog] | FileUtils.rm_f [buildlog, testlog] | ||||
@fs.jail.reset | @fs.jail.reset | ||||
@fs.jail.init | @fs.jail.init | ||||
@assignment.tests.each do |test| | |||||
@tests.push({ :name => test[:name], :max => test[:max], :score => 0 }) | |||||
end | |||||
end | end | ||||
def stage | def stage | ||||
@@ -142,8 +137,10 @@ module KGrader | |||||
def test | def test | ||||
return if @failure | 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 | ||||
end | end | ||||
@@ -199,7 +196,7 @@ module KGrader | |||||
end | end | ||||
def generate_summary | def generate_summary | ||||
tests = @tests.each do |test| | |||||
tests = @tests.map do |test| | |||||
"#{test[:score].to_s.rjust get_span(test[:max])}/#{test[:max]}" | "#{test[:score].to_s.rjust get_span(test[:max])}/#{test[:max]}" | ||||
end.join ', ' | end.join ', ' | ||||
"#{format_points score, max_score}: #{tests}" | "#{format_points score, max_score}: #{tests}" | ||||