# HG changeset patch # User Jittat Fakcharoenphol # Date 2010-05-02 07:42:47 # Node ID 20e4101950cb75008bebf53cc2197d88e9c9bb7b # Parent cc3e1102f8c619da2c25ab79e6a5b649a4e01e91 # Parent 57e6d251108ec10f52cd54d2206daa8bb5309d52 Merge branch 'master' of gitorious.org:cafe-grader/cafe-grader-judge-scripts into windows diff --git a/grader b/grader --- a/grader +++ b/grader @@ -50,7 +50,7 @@ (3) grader stop [all|pids-list] (4) grader --help (1) call grader with environment = 'exam', mode = 'queue' -(2) possible modes are: 'queue', 'prob', 'test_request' +(2) possible modes are: 'queue', 'test_request', 'prob', 'sub', 'contest', and 'autonew' (3) create stop-file to stop running grader in queue mode (4) You are here. USAGE @@ -108,13 +108,13 @@ end options[:dry_run] = (ARGV.delete('--dry') != nil) - if options[:dry_run] and (not ['prob','contest'].include? options[:mode]) + if options[:dry_run] and (not ['prob','contest','autonew'].include? options[:mode]) puts "Dry run currently works only for 'prob' or 'contest' modes." exit(0) end options[:report] = (ARGV.delete('--report') != nil) - if options[:report] and (not ['prob','contest'].include? options[:mode]) + if options[:report] and (not ['prob','contest','autonew'].include? options[:mode]) puts "Report currently works only for 'prob' or 'contest' modes." exit(0) end @@ -129,7 +129,12 @@ @users = {} end - def save(user, problem, grading_result) + def after_save_hook(submission, grading_result) + end + + def save(submission, grading_result) + user = submission.user + problem = submission.problem if not @problems.has_key? problem.id @problems[problem.id] = problem end @@ -137,6 +142,8 @@ @users[user.id] = user end @results[[user.id, problem.id]] = grading_result + + after_save_hook(submission, grading_result) end def print_report_by_user @@ -164,6 +171,200 @@ end end +def grader_general_loop(engine, grader_proc, options) + runner = Grader::Runner.new(engine, grader_proc) + while true + + if check_stopfile # created by calling grader stop + clear_stopfile + log "stopped (with stop file)" + break + end + + task = yield(runner) + + if task==nil + sleep(1) + end + end +end + +def grader_queue_loop(grader_proc, options) + log "Grader: queue" + engine = Grader::Engine.new + grader_general_loop(engine, grader_proc, options) do |runner| + runner.grade_oldest_task + end +end + +def grader_test_request_loop(grader_proc, options) + log "Grader: test_request" + engine = Grader::Engine.new(:room_maker => Grader::TestRequestRoomMaker.new, + :reporter => Grader::TestRequestReporter.new) + grader_general_loop(engine, grader_proc, options) do |runner| + runner.grade_oldest_test_request + end +end + +def grader_autonew_loop(grader_proc, options) + log "Grader: autonew" + + if options[:report] + result_collector = ResultCollector.new + else + result_collector = nil + end + + if options[:dry_run] + puts "Running in dry mode" + end + + prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run], + :result_collector => result_collector) + + engine = Grader::Engine.new(:reporter => prob_reporter) + runner = Grader::Runner.new(engine, grader_proc) + + grader_proc.report_active if grader_proc!=nil + + latest_submitted_at = nil + graded_submission_ids = {} + + while true + + if check_stopfile # created by calling grader stop + clear_stopfile + log "stopped (with stop file)" + break + end + + if latest_submitted_at==nil + submissions = Submission.all + else + submissions = Submission.all(:conditions => ["submitted_at >= :latest", + {:latest => latest_submitted_at}]) + end + + graded_any = false + + if submissions.length != 0 + submissions.each do |submission| + if ! graded_submission_ids[submission.id] + runner.grade_submission(submission) + graded_submission_ids[submission.id] = true + if (!latest_submitted_at or + latest_submitted_at < submission.submitted_at) + latest_submitted_at = submission.submitted_at + end + puts "graded: #{submission.id}" + puts "latest: #{latest_submitted_at}" + graded_any = true + end + end + end + + if ! graded_any + sleep(1) + end + end +end + +def grader_grade_problems(grader_proc, options) + if options[:report] + result_collector = ResultCollector.new + else + result_collector = nil + end + + if options[:dry_run] + puts "Running in dry mode" + end + + prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run], + :result_collector => result_collector) + engine = Grader::Engine.new(:reporter => prob_reporter) + runner = Grader::Runner.new(engine, grader_proc) + + grader_proc.report_active if grader_proc!=nil + + ARGV.each do |prob_name| + prob = Problem.find_by_name(prob_name) + if prob==nil + puts "cannot find problem: #{prob_name}" + else + runner.grade_problem(prob) + end + end + + if options[:report] + result_collector.print_report_by_user + end +end + +def grader_grade_contests(grader_proc, options) + # always use dry run when grading during contest + dry_run = options[:dry_run] = true + + contest_name = ARGV.shift + + contest = Contest.find_by_name(contest_name) + if contest==nil + puts "cannot find contest: #{contest_name}" + exit(0) + end + + if options[:report] + result_collector = ResultCollector.new + else + result_collector = nil + end + + if options[:dry_run] + puts "Running in dry mode" + end + + prob_reporter = Grader::SubmissionReporter.new(:dry_run => dry_run, + :result_collector => result_collector) + engine = Grader::Engine.new(:reporter => prob_reporter) + runner = Grader::Runner.new(engine, grader_proc) + + grader_proc.report_active if grader_proc!=nil + + contest.problems.each do |problem| + puts "Grading: #{problem.name}" + runner.grade_problem(problem, + :user_conditions => lambda do |u| + u.contest_finished? and + u.contest_ids.include?(contest.id) + end) + end + + if options[:report] + result_collector.print_report_by_user + end +end + +def grader_grade_submissions(grader_proc, options) + engine = Grader::Engine.new + runner = Grader::Runner.new(engine, grader_proc) + + grader_proc.report_active if grader_proc!=nil + + ARGV.each do |sub_id| + puts "Grading #{sub_id}" + begin + submission = Submission.find(sub_id.to_i) + rescue ActiveRecord::RecordNotFound + puts "Record not found" + submission = nil + end + + if submission!=nil + runner.grade_submission(submission) + end + end +end + ######################################### # main program ######################################### @@ -214,128 +415,23 @@ # case grader_mode -when "queue", "test_request" - log "Grader: #{grader_mode}" - if grader_mode=="queue" - engine = Grader::Engine.new - else - engine = Grader::Engine.new(:room_maker => Grader::TestRequestRoomMaker.new, - :reporter => Grader::TestRequestReporter.new) - end +when "queue" + grader_queue_loop(grader_proc, options) - runner = Grader::Runner.new(engine, grader_proc) - while true - - if check_stopfile # created by calling grader stop - clear_stopfile - log "stopped (with stop file)" - break - end - - if grader_mode=="queue" - task = runner.grade_oldest_task - else - task = runner.grade_oldest_test_request - end - if task==nil - sleep(1) - end - end - +when "test_request" + grader_test_request_loop(grader_proc, options) + when "prob" - if options[:report] - result_collector = ResultCollector.new - else - result_collector = nil - end - - if options[:dry_run] - puts "Running in dry mode" - end - - prob_reporter = Grader::SubmissionReporter.new(:dry_run => dry_run, - :result_collector => result_collector) - engine = Grader::Engine.new(:reporter => prob_reporter) - runner = Grader::Runner.new(engine, grader_proc) - - grader_proc.report_active if grader_proc!=nil - - ARGV.each do |prob_name| - prob = Problem.find_by_name(prob_name) - if prob==nil - puts "cannot find problem: #{prob_name}" - else - runner.grade_problem(prob) - end - end - - if options[:report] - result_collector.print_report_by_user - end + grader_grade_problems(grader_proc, options) when "contest" - # always use dry run when grading during contest - contest_name = ARGV.shift - - dry_run = options[:dry_run] = true - - contest = Contest.find_by_name(contest_name) - if contest==nil - puts "cannot find contest: #{contest_name}" - exit(0) - end - - if options[:report] - result_collector = ResultCollector.new - else - result_collector = nil - end - - if options[:dry_run] - puts "Running in dry mode" - end - - prob_reporter = Grader::SubmissionReporter.new(:dry_run => dry_run, - :result_collector => result_collector) - engine = Grader::Engine.new(:reporter => prob_reporter) - runner = Grader::Runner.new(engine, grader_proc) - - grader_proc.report_active if grader_proc!=nil - - contest.problems.each do |problem| - puts "Grading: #{problem.name}" - runner.grade_problem(problem, - :user_conditions => lambda do |u| - u.contest_finished? and - u.contest_ids.include?(contest.id) - end) - end - - if options[:report] - result_collector.print_report_by_user - end + grader_grade_contests(grader_proc, options) when "sub" - engine = Grader::Engine.new - runner = Grader::Runner.new(engine, grader_proc) - - grader_proc.report_active if grader_proc!=nil + grader_grade_submissions(grader_proc, options) - ARGV.each do |sub_id| - puts "Grading #{sub_id}" - begin - submission = Submission.find(sub_id.to_i) - rescue ActiveRecord::RecordNotFound - puts "Record not found" - submission = nil - end - - if submission!=nil - runner.grade_submission(submission) - end - end - - +when "autonew" + grader_autonew_loop(grader_proc, options) else display_manual diff --git a/lib/engine.rb b/lib/engine.rb --- a/lib/engine.rb +++ b/lib/engine.rb @@ -51,8 +51,8 @@ lang_ext = 'c' end - # FIX THIS - talk 'some hack on language' + # This is needed because older version of std-scripts/compile + # only look for c++. if language == 'cpp' language = 'c++' end diff --git a/lib/submission_helper.rb b/lib/submission_helper.rb --- a/lib/submission_helper.rb +++ b/lib/submission_helper.rb @@ -42,8 +42,7 @@ def report(sub,test_result_dir) result = read_result(test_result_dir) if @result_collector - @result_collector.save(sub.user, - sub.problem, + @result_collector.save(sub, result) end save_result(sub,result) diff --git a/std-script/compile b/std-script/compile --- a/std-script/compile +++ b/std-script/compile @@ -77,6 +77,10 @@ exit(127) end +if params[:prog_lang]=='cpp': + params[:prog_lang] = 'c++' +end + # Compile. case params[:prog_lang] diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,3 @@ +sandbox +coverage + diff --git a/test/data/test1_correct_cc.cc b/test/data/test1_correct_cc.cc new file mode 100644 --- /dev/null +++ b/test/data/test1_correct_cc.cc @@ -0,0 +1,13 @@ +/* +LANG: C++ +*/ +#include + +int main() +{ + int a,b,r; + r = scanf("%d %d",&a,&b); + printf("%d\n",a+b); + return 0; +} + diff --git a/test/engine_spec.rb b/test/engine_spec.rb --- a/test/engine_spec.rb +++ b/test/engine_spec.rb @@ -28,6 +28,17 @@ end + it "should grade normal submission in C++" do + cpp_lang = stub(Language, :name => 'cpp', :ext => 'cpp') + + grader_should(:grade => "test1_correct_cc.cc", + :language => cpp_lang, + :on => @problem_test_normal, + :and_report => { + :score => 135, + :comment => /^(\[|P|\])+/}) + end + it "should produce error message when submission cannot compile" do grader_should(:grade => "test1_compile_error.c", :on => @problem_test_normal, @@ -122,8 +133,9 @@ def grader_should(args) @user1 = stub(User, :id => 1, :login => 'user1') + submission = - create_submission_from_file(1, @user1, args[:on], args[:grade]) + create_submission_from_file(1, @user1, args[:on], args[:grade], args[:language]) submission.should_receive(:graded_at=) expected_score = args[:and_report][:score] diff --git a/test/rakefile b/test/rakefile new file mode 100644 --- /dev/null +++ b/test/rakefile @@ -0,0 +1,14 @@ +require 'rake' +require 'spec/rake/spectask' + +desc "Run all examples" +Spec::Rake::SpecTask.new('spec') do |t| + t.spec_files = FileList['*spec.rb'] +end + +desc "Run all examples with RCov" +Spec::Rake::SpecTask.new('spec_with_rcov') do |t| + t.spec_files = FileList['*spec.rb'] + t.rcov = true + t.rcov_opts = ['--exclude', '.*_spec\.rb'] +end