| @@ -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}" | |||