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