Description:
[grader] [MERGED] Merged new-arch-branch changes 74:105 into the trunk
git-svn-id: http://theory.cpe.ku.ac.th/grader/judge/trunk/scripts@106 6386c4cd-e34a-4fa8-8920-d93eb39b512e
Commit status:
[Not Reviewed]
References:
Diff options:
Comments:
0 Commit comments
0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
r23:197d5085d55a - - 98 files changed: 1441 inserted, 380 deleted
@@ -0,0 +1,54 | |||
|
1 | + # | |
|
2 | + # A runner drives the engine into various tasks. | |
|
3 | + # | |
|
4 | + | |
|
5 | + module Grader | |
|
6 | + | |
|
7 | + class Runner | |
|
8 | + | |
|
9 | + def initialize(engine, grader_process=nil) | |
|
10 | + @engine = engine | |
|
11 | + @grader_process = grader_process | |
|
12 | + end | |
|
13 | + | |
|
14 | + def grade_oldest_task | |
|
15 | + task = Task.get_inqueue_and_change_status(Task::STATUS_GRADING) | |
|
16 | + if task!=nil | |
|
17 | + @grader_process.report_active(task) if @grader_process!=nil | |
|
18 | + | |
|
19 | + submission = Submission.find(task.submission_id) | |
|
20 | + @engine.grade(submission) | |
|
21 | + task.status_complete! | |
|
22 | + end | |
|
23 | + return task | |
|
24 | + end | |
|
25 | + | |
|
26 | + def grade_problem(problem) | |
|
27 | + users = User.find(:all) | |
|
28 | + users.each do |u| | |
|
29 | + puts "user: #{u.login}" | |
|
30 | + last_sub = Submission.find(:first, | |
|
31 | + :conditions => "user_id = #{u.id} and " + | |
|
32 | + "problem_id = #{prob.id}", | |
|
33 | + :order => 'submitted_at DESC') | |
|
34 | + if last_sub!=nil | |
|
35 | + @engine.grade(last_sub) | |
|
36 | + end | |
|
37 | + end | |
|
38 | + end | |
|
39 | + | |
|
40 | + def grade_oldest_test_request | |
|
41 | + test_request = TestRequest.get_inqueue_and_change_status(Task::STATUS_GRADING) | |
|
42 | + if test_request!=nil | |
|
43 | + @grader_process.report_active(test_request) if @grader_process!=nil | |
|
44 | + | |
|
45 | + @engine.grade(test_request) | |
|
46 | + test_request.status_complete! | |
|
47 | + end | |
|
48 | + return test_request | |
|
49 | + end | |
|
50 | + | |
|
51 | + end | |
|
52 | + | |
|
53 | + end | |
|
54 | + |
@@ -0,0 +1,95 | |||
|
1 | + module Grader | |
|
2 | + | |
|
3 | + class SubmissionRoomMaker | |
|
4 | + def initialize | |
|
5 | + @config = Grader::Configuration.get_instance | |
|
6 | + end | |
|
7 | + | |
|
8 | + def produce_grading_room(submission) | |
|
9 | + user = submission.user | |
|
10 | + problem = submission.problem | |
|
11 | + grading_room = "#{@config.user_result_dir}/" + | |
|
12 | + "#{user.login}/#{problem.name}/#{submission.id}" | |
|
13 | + | |
|
14 | + FileUtils.mkdir_p(grading_room) | |
|
15 | + grading_room | |
|
16 | + end | |
|
17 | + | |
|
18 | + def find_problem_home(submission) | |
|
19 | + problem = submission.problem | |
|
20 | + "#{@config.problems_dir}/#{problem.name}" | |
|
21 | + end | |
|
22 | + | |
|
23 | + def save_source(submission,source_name) | |
|
24 | + dir = self.produce_grading_room(submission) | |
|
25 | + f = File.open("#{dir}/#{source_name}","w") | |
|
26 | + f.write(submission.source) | |
|
27 | + f.close | |
|
28 | + end | |
|
29 | + | |
|
30 | + def clean_up(submission) | |
|
31 | + end | |
|
32 | + end | |
|
33 | + | |
|
34 | + class SubmissionReporter | |
|
35 | + def initialize | |
|
36 | + @config = Grader::Configuration.get_instance | |
|
37 | + end | |
|
38 | + | |
|
39 | + def report(sub,test_result_dir) | |
|
40 | + save_result(sub,read_result(test_result_dir)) | |
|
41 | + end | |
|
42 | + | |
|
43 | + def report_error(sub,msg) | |
|
44 | + save_result(sub,{:points => 0, | |
|
45 | + :comment => "Grading error: #{msg}" }) | |
|
46 | + end | |
|
47 | + | |
|
48 | + protected | |
|
49 | + def read_result(test_result_dir) | |
|
50 | + cmp_msg_fname = "#{test_result_dir}/compiler_message" | |
|
51 | + cmp_file = File.open(cmp_msg_fname) | |
|
52 | + cmp_msg = cmp_file.read | |
|
53 | + cmp_file.close | |
|
54 | + | |
|
55 | + result_fname = "#{test_result_dir}/result" | |
|
56 | + comment_fname = "#{test_result_dir}/comment" | |
|
57 | + if FileTest.exist?(result_fname) | |
|
58 | + result_file = File.open(result_fname) | |
|
59 | + result = result_file.readline.to_i | |
|
60 | + result_file.close | |
|
61 | + | |
|
62 | + comment_file = File.open(comment_fname) | |
|
63 | + comment = comment_file.readline.chomp | |
|
64 | + comment_file.close | |
|
65 | + | |
|
66 | + return {:points => result, | |
|
67 | + :comment => comment, | |
|
68 | + :cmp_msg => cmp_msg} | |
|
69 | + else | |
|
70 | + return {:points => 0, | |
|
71 | + :comment => 'compile error', | |
|
72 | + :cmp_msg => cmp_msg} | |
|
73 | + end | |
|
74 | + end | |
|
75 | + | |
|
76 | + def save_result(submission,result) | |
|
77 | + problem = submission.problem | |
|
78 | + submission.graded_at = Time.now | |
|
79 | + points = result[:points] | |
|
80 | + submission.points = points | |
|
81 | + comment = @config.report_comment(result[:comment]) | |
|
82 | + if problem == nil | |
|
83 | + submission.grader_comment = 'PASSED: ' + comment + '(problem is nil)' | |
|
84 | + elsif points == problem.full_score | |
|
85 | + submission.grader_comment = 'PASSED: ' + comment | |
|
86 | + else | |
|
87 | + submission.grader_comment = 'FAILED: ' + comment | |
|
88 | + end | |
|
89 | + submission.compiler_message = result[:cmp_msg] or '' | |
|
90 | + submission.save | |
|
91 | + end | |
|
92 | + | |
|
93 | + end | |
|
94 | + | |
|
95 | + end |
@@ -0,0 +1,190 | |||
|
1 | + # | |
|
2 | + # This part contains various test_request helpers for interfacing | |
|
3 | + # with Grader::Engine. There are TestRequestRoomMaker and | |
|
4 | + # TestRequestReporter. | |
|
5 | + | |
|
6 | + module Grader | |
|
7 | + | |
|
8 | + # | |
|
9 | + # A TestRequestRoomMaker is a helper object for Engine | |
|
10 | + # - finds grading room: in user_result_dir/(user)/test_request/ ... | |
|
11 | + # - prepare problem configuration for grading --- basically it copy | |
|
12 | + # all config files, and copy user's input into the testcase | |
|
13 | + # directory. First, it finds the template from problem template | |
|
14 | + # directory; if it can't find a template, it'll use the template | |
|
15 | + # from default template. | |
|
16 | + class TestRequestRoomMaker | |
|
17 | + def initialize | |
|
18 | + @config = Grader::Configuration.get_instance | |
|
19 | + end | |
|
20 | + | |
|
21 | + def produce_grading_room(test_request) | |
|
22 | + grading_room = grading_room_dir(test_request) | |
|
23 | + FileUtils.mkdir_p(grading_room) | |
|
24 | + grading_room | |
|
25 | + end | |
|
26 | + | |
|
27 | + def find_problem_home(test_request) | |
|
28 | + problem_name = test_request.problem_name | |
|
29 | + | |
|
30 | + template_dir = "#{@config.test_request_problem_templates_dir}/" + problem_name | |
|
31 | + | |
|
32 | + raise "Test Request: error template not found" if !File.exists?(template_dir) | |
|
33 | + | |
|
34 | + problem_home = problem_home_dir(test_request) | |
|
35 | + FileUtils.mkdir_p(problem_home) | |
|
36 | + | |
|
37 | + copy_problem_template(template_dir,problem_home) | |
|
38 | + link_input_file(test_request,problem_home) | |
|
39 | + | |
|
40 | + problem_home | |
|
41 | + end | |
|
42 | + | |
|
43 | + def save_source(test_request,source_name) | |
|
44 | + dir = self.produce_grading_room(test_request) | |
|
45 | + submission = test_request.submission | |
|
46 | + f = File.open("#{dir}/#{source_name}","w") | |
|
47 | + f.write(submission.source) | |
|
48 | + f.close | |
|
49 | + end | |
|
50 | + | |
|
51 | + def clean_up(test_request) | |
|
52 | + problem_home = problem_home_dir(test_request) | |
|
53 | + remove_data_files(problem_home) | |
|
54 | + end | |
|
55 | + | |
|
56 | + protected | |
|
57 | + def grading_room_dir(test_request) | |
|
58 | + problem_name = test_request.problem_name | |
|
59 | + user = test_request.user | |
|
60 | + "#{@config.user_result_dir}" + | |
|
61 | + "/#{user.login}/test_request" + | |
|
62 | + "/#{problem_name}/#{test_request.id}" | |
|
63 | + end | |
|
64 | + | |
|
65 | + def problem_home_dir(test_request) | |
|
66 | + problem_name = test_request.problem_name | |
|
67 | + user = test_request.user | |
|
68 | + "#{@config.user_result_dir}" + | |
|
69 | + "/#{user.login}/test_request/#{problem_name}" | |
|
70 | + end | |
|
71 | + | |
|
72 | + def copy_problem_template(template_dir,problem_home) | |
|
73 | + cmd = "cp -R #{template_dir}/* #{problem_home}" | |
|
74 | + system_and_raise_when_fail(cmd,"Test Request: cannot copy problem template") | |
|
75 | + end | |
|
76 | + | |
|
77 | + def link_input_file(test_request,problem_home) | |
|
78 | + cmd = "ln -s #{test_request.input_file_name} #{problem_home}/test_cases/1/input-1.txt" | |
|
79 | + system_and_raise_when_fail(cmd,"Test Request: cannot link input file") | |
|
80 | + end | |
|
81 | + | |
|
82 | + def remove_data_files(problem_home) | |
|
83 | + if File.exists?("#{problem_home}/test_cases/1/input-1.txt") | |
|
84 | + cmd = "rm #{problem_home}/test_cases/1/*" | |
|
85 | + system_and_raise_when_fail(cmd,"Test Request: cannot remove data files") | |
|
86 | + end | |
|
87 | + end | |
|
88 | + | |
|
89 | + def system_and_raise_when_fail(cmd,msg) | |
|
90 | + if !system(cmd) | |
|
91 | + raise msg | |
|
92 | + end | |
|
93 | + end | |
|
94 | + | |
|
95 | + end | |
|
96 | + | |
|
97 | + class TestRequestReporter | |
|
98 | + def initialize | |
|
99 | + @config = Grader::Configuration.get_instance | |
|
100 | + end | |
|
101 | + | |
|
102 | + def report(test_request,test_result_dir) | |
|
103 | + save_result(test_request,read_result(test_result_dir)) | |
|
104 | + end | |
|
105 | + | |
|
106 | + def report_error(test_request, msg) | |
|
107 | + save_result(test_request, {:running_stat => "#{msg}"}) | |
|
108 | + end | |
|
109 | + | |
|
110 | + protected | |
|
111 | + def read_result(test_result_dir) | |
|
112 | + # TODO: | |
|
113 | + cmp_msg_fname = "#{test_result_dir}/compiler_message" | |
|
114 | + cmp_file = File.open(cmp_msg_fname) | |
|
115 | + cmp_msg = cmp_file.read | |
|
116 | + cmp_file.close | |
|
117 | + | |
|
118 | + result_file_name = "#{test_result_dir}/1/result" | |
|
119 | + | |
|
120 | + if File.exists?(result_file_name) | |
|
121 | + output_file_name = "#{test_result_dir}/1/output.txt" | |
|
122 | + results = File.open("#{test_result_dir}/1/result").readlines | |
|
123 | + stat = format_running_stat(results) | |
|
124 | + | |
|
125 | + return { | |
|
126 | + :output_file_name => output_file_name, | |
|
127 | + :running_stat => stat, | |
|
128 | + :comment => "", | |
|
129 | + :cmp_msg => cmp_msg} | |
|
130 | + else | |
|
131 | + return { | |
|
132 | + :running_stat => "", | |
|
133 | + :comment => "Compilation error", | |
|
134 | + :cmp_msg => cmp_msg} | |
|
135 | + end | |
|
136 | + end | |
|
137 | + | |
|
138 | + def format_running_stat(results) | |
|
139 | + running_time_line = results[-1] | |
|
140 | + | |
|
141 | + run_stat = "" | |
|
142 | + if !(/[Cc]orrect/.match(results[0])) | |
|
143 | + run_stat = results[0].chomp | |
|
144 | + end | |
|
145 | + | |
|
146 | + if res = /r(.*)u(.*)s/.match(running_time_line) | |
|
147 | + seconds = (res[1].to_f + res[2].to_f) | |
|
148 | + time_stat = "Time used: #{seconds} sec." | |
|
149 | + else | |
|
150 | + time_stat = "Time used: n/a sec." | |
|
151 | + end | |
|
152 | + return "#{run_stat}#{time_stat}" | |
|
153 | + end | |
|
154 | + | |
|
155 | + def save_result(test_request,result) | |
|
156 | + if result[:output_file_name]!=nil | |
|
157 | + test_request.output_file_name = link_output_file(test_request, | |
|
158 | + result[:output_file_name]) | |
|
159 | + end | |
|
160 | + test_request.graded_at = Time.now | |
|
161 | + test_request.compiler_message = (result[:cmp_msg] or '') | |
|
162 | + test_request.grader_comment = (result[:comment] or '') | |
|
163 | + test_request.running_stat = (result[:running_stat] or '') | |
|
164 | + test_request.save | |
|
165 | + end | |
|
166 | + | |
|
167 | + protected | |
|
168 | + def link_output_file(test_request, fname) | |
|
169 | + target_file_name = random_output_file_name(test_request.user, | |
|
170 | + test_request.problem) | |
|
171 | + FileUtils.mkdir_p(File.dirname(target_file_name)) | |
|
172 | + cmd = "ln -s #{fname} #{target_file_name}" | |
|
173 | + if !system(cmd) | |
|
174 | + raise "TestRequestReporter: cannot move output file" | |
|
175 | + end | |
|
176 | + return target_file_name | |
|
177 | + end | |
|
178 | + | |
|
179 | + def random_output_file_name(user,problem) | |
|
180 | + problem_name = TestRequest.name_of(problem) | |
|
181 | + begin | |
|
182 | + tmpname = "#{@config.test_request_output_base_dir}" + | |
|
183 | + "/#{user.login}/#{problem_name}/#{rand(10000)}" | |
|
184 | + end while File.exists?(tmpname) | |
|
185 | + tmpname | |
|
186 | + end | |
|
187 | + | |
|
188 | + end | |
|
189 | + | |
|
190 | + end |
@@ -0,0 +1,13 | |||
|
1 | + problem do | |
|
2 | + num_tests <%= num_testcases %> | |
|
3 | + full_score <%= num_testcases*10 %> | |
|
4 | + time_limit_each <%= options[:time_limit] %> | |
|
5 | + mem_limit_each <%= options[:mem_limit] %> | |
|
6 | + score_each 10 | |
|
7 | + | |
|
8 | + <% 1.upto(num_testcases) do |i| %> | |
|
9 | + run <%= i %> do | |
|
10 | + tests <%= i %> | |
|
11 | + end | |
|
12 | + <% end %> | |
|
13 | + end |
new file 100644 |
@@ -0,0 +1,47 | |||
|
1 | + #!/usr/bin/ruby | |
|
2 | + | |
|
3 | + problem_home = ENV['PROBLEM_HOME'] | |
|
4 | + require "#{problem_home}/script/test_dsl.rb" | |
|
5 | + | |
|
6 | + if ARGV.length < 2 | |
|
7 | + puts "Usage: check <language> <test-number> [<output-file>]" | |
|
8 | + exit(0) | |
|
9 | + end | |
|
10 | + | |
|
11 | + language = ARGV[0] | |
|
12 | + test_num = ARGV[1].to_i | |
|
13 | + if ARGV.length >= 3 | |
|
14 | + output_file_name = ARGV[2] | |
|
15 | + else | |
|
16 | + output_file_name = "output.txt" | |
|
17 | + end | |
|
18 | + | |
|
19 | + load "#{problem_home}/test_cases/all_tests.cfg" | |
|
20 | + problem = Problem.get_instance | |
|
21 | + | |
|
22 | + output_file = File.new(output_file_name, "r") | |
|
23 | + answer_file = File.new("#{problem_home}/test_cases/#{test_num}/answer-#{test_num}.txt") | |
|
24 | + result_file = File.new("check_result", "w") | |
|
25 | + | |
|
26 | + output_file_content = output_file.read | |
|
27 | + answer_file_content = answer_file.read | |
|
28 | + | |
|
29 | + report_correct = lambda { | |
|
30 | + result_file.write "Correct\n" | |
|
31 | + result_file.write problem.get_score(test_num) | |
|
32 | + result_file.write "\n" | |
|
33 | + result_file.close | |
|
34 | + exit(0) | |
|
35 | + } | |
|
36 | + | |
|
37 | + report_wrong = lambda { | |
|
38 | + result_file.write "Incorrect\n" | |
|
39 | + result_file.write "0\n" | |
|
40 | + result_file.close | |
|
41 | + exit(0) | |
|
42 | + } | |
|
43 | + | |
|
44 | + ################## | |
|
45 | + # Your code here # | |
|
46 | + ################## | |
|
47 | + report_correct.call |
@@ -0,0 +1,12 | |||
|
1 | + problem do | |
|
2 | + num_tests 1 | |
|
3 | + full_score 10 | |
|
4 | + time_limit_each <%= options[:time_limit] %> | |
|
5 | + mem_limit_each <%= options[:mem_limit] %> | |
|
6 | + score_each 10 | |
|
7 | + | |
|
8 | + run 1 do | |
|
9 | + tests 1 | |
|
10 | + end | |
|
11 | + | |
|
12 | + end |
@@ -0,0 +1,10 | |||
|
1 | + #include <stdio.h> | |
|
2 | + | |
|
3 | + int main() | |
|
4 | + { | |
|
5 | + int a,b; | |
|
6 | + scanf("%d %d",&a,&b); | |
|
7 | + printf("%d\n",a+b); | |
|
8 | + return 10; | |
|
9 | + } | |
|
10 | + |
@@ -0,0 +1,19 | |||
|
1 | + #include <stdio.h> | |
|
2 | + #include <stdlib.h> | |
|
3 | + | |
|
4 | + int main() | |
|
5 | + { | |
|
6 | + int a,b; | |
|
7 | + char *huge_array; | |
|
8 | + | |
|
9 | + scanf("%d %d",&a,&b); | |
|
10 | + | |
|
11 | + huge_array = (char *)malloc(5000000); | |
|
12 | + if(huge_array==NULL) | |
|
13 | + printf("NO!"); | |
|
14 | + else | |
|
15 | + printf("%d\n",a+b); | |
|
16 | + | |
|
17 | + return 0; | |
|
18 | + } | |
|
19 | + |
@@ -0,0 +1,12 | |||
|
1 | + #include <stdio.h> | |
|
2 | + | |
|
3 | + int big_array[2000000]; | |
|
4 | + | |
|
5 | + int main() | |
|
6 | + { | |
|
7 | + int a,b; | |
|
8 | + scanf("%d %d",&a,&b); | |
|
9 | + printf("%d\n",a+b); | |
|
10 | + return 0; | |
|
11 | + } | |
|
12 | + |
@@ -0,0 +1,59 | |||
|
1 | + #!/usr/bin/ruby | |
|
2 | + | |
|
3 | + problem_home = ENV['PROBLEM_HOME'] | |
|
4 | + require "#{problem_home}/script/test_dsl.rb" | |
|
5 | + | |
|
6 | + if ARGV.length < 2 | |
|
7 | + puts "Usage: check <language> <test-number> [<output-file>]" | |
|
8 | + exit(0) | |
|
9 | + end | |
|
10 | + | |
|
11 | + language = ARGV[0] | |
|
12 | + test_num = ARGV[1].to_i | |
|
13 | + if ARGV.length >= 3 | |
|
14 | + output_file_name = ARGV[2] | |
|
15 | + else | |
|
16 | + output_file_name = "output.txt" | |
|
17 | + end | |
|
18 | + | |
|
19 | + load "#{problem_home}/test_cases/all_tests.cfg" | |
|
20 | + problem = Problem.get_instance | |
|
21 | + | |
|
22 | + output_file = File.new(output_file_name, "r") | |
|
23 | + answer_file = File.new("#{problem_home}/test_cases/#{test_num}/answer-#{test_num}.txt") | |
|
24 | + result_file = File.new("check_result", "w") | |
|
25 | + | |
|
26 | + output_file_content = output_file.read | |
|
27 | + answer_file_content = answer_file.read | |
|
28 | + | |
|
29 | + report_correct = lambda { | |
|
30 | + result_file.write "Correct\n" | |
|
31 | + result_file.write problem.get_score(test_num) | |
|
32 | + result_file.write "\n" | |
|
33 | + result_file.close | |
|
34 | + exit(0) | |
|
35 | + } | |
|
36 | + | |
|
37 | + report_wrong = lambda { | |
|
38 | + result_file.write "Incorrect\n" | |
|
39 | + result_file.write "0\n" | |
|
40 | + result_file.close | |
|
41 | + exit(0) | |
|
42 | + } | |
|
43 | + | |
|
44 | + ################## | |
|
45 | + # Your code here # | |
|
46 | + ################## | |
|
47 | + num_pattern = /^[0-9]*/ | |
|
48 | + if (output_file_content =~ num_pattern) == nil | |
|
49 | + report_wrong.call | |
|
50 | + end | |
|
51 | + | |
|
52 | + output_i = output_file_content.to_i | |
|
53 | + answer_i = answer_file_content.to_i | |
|
54 | + | |
|
55 | + if output_i == answer_i | |
|
56 | + report_correct.call | |
|
57 | + else | |
|
58 | + report_wrong.call | |
|
59 | + end |
@@ -0,0 +1,20 | |||
|
1 | + problem do | |
|
2 | + num_tests 2 | |
|
3 | + full_score 20 | |
|
4 | + time_limit_each 1 | |
|
5 | + mem_limit_each 5 | |
|
6 | + score_each 10 | |
|
7 | + | |
|
8 | + run 1 do | |
|
9 | + tests 1 | |
|
10 | + end | |
|
11 | + | |
|
12 | + test 2 do | |
|
13 | + mem_limit 10 | |
|
14 | + end | |
|
15 | + | |
|
16 | + run 2 do | |
|
17 | + tests 2 | |
|
18 | + end | |
|
19 | + | |
|
20 | + end |
@@ -0,0 +1,59 | |||
|
1 | + #!/usr/bin/ruby | |
|
2 | + | |
|
3 | + problem_home = ENV['PROBLEM_HOME'] | |
|
4 | + require "#{problem_home}/script/test_dsl.rb" | |
|
5 | + | |
|
6 | + if ARGV.length < 2 | |
|
7 | + puts "Usage: check <language> <test-number> [<output-file>]" | |
|
8 | + exit(0) | |
|
9 | + end | |
|
10 | + | |
|
11 | + language = ARGV[0] | |
|
12 | + test_num = ARGV[1].to_i | |
|
13 | + if ARGV.length >= 3 | |
|
14 | + output_file_name = ARGV[2] | |
|
15 | + else | |
|
16 | + output_file_name = "output.txt" | |
|
17 | + end | |
|
18 | + | |
|
19 | + load "#{problem_home}/test_cases/all_tests.cfg" | |
|
20 | + problem = Problem.get_instance | |
|
21 | + | |
|
22 | + output_file = File.new(output_file_name, "r") | |
|
23 | + answer_file = File.new("#{problem_home}/test_cases/#{test_num}/answer-#{test_num}.txt") | |
|
24 | + result_file = File.new("check_result", "w") | |
|
25 | + | |
|
26 | + output_file_content = output_file.read | |
|
27 | + answer_file_content = answer_file.read | |
|
28 | + | |
|
29 | + report_correct = lambda { | |
|
30 | + result_file.write "Correct\n" | |
|
31 | + result_file.write problem.get_score(test_num) | |
|
32 | + result_file.write "\n" | |
|
33 | + result_file.close | |
|
34 | + exit(0) | |
|
35 | + } | |
|
36 | + | |
|
37 | + report_wrong = lambda { | |
|
38 | + result_file.write "Incorrect\n" | |
|
39 | + result_file.write "0\n" | |
|
40 | + result_file.close | |
|
41 | + exit(0) | |
|
42 | + } | |
|
43 | + | |
|
44 | + ################## | |
|
45 | + # Your code here # | |
|
46 | + ################## | |
|
47 | + num_pattern = /^[0-9]*/ | |
|
48 | + if (output_file_content =~ num_pattern) == nil | |
|
49 | + report_wrong.call | |
|
50 | + end | |
|
51 | + | |
|
52 | + output_i = output_file_content.to_i | |
|
53 | + answer_i = answer_file_content.to_i | |
|
54 | + | |
|
55 | + if output_i == answer_i | |
|
56 | + report_correct.call | |
|
57 | + else | |
|
58 | + report_wrong.call | |
|
59 | + end |
@@ -0,0 +1,1 | |||
|
1 | + 2 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + 1 1 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + -256 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + -128 -128 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + 32 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + 20 12 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + 4 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + 1 3 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + mem_limit 32 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + 64 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + 32 32 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + -2 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + -1 -1 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + -32 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + -16 -16 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + 0 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + 0 0 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + -1 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + 0 -1 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + mem_limit 64 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + 256 No newline at end of file |
@@ -0,0 +1,1 | |||
|
1 | + 128 128 No newline at end of file |
@@ -0,0 +1,39 | |||
|
1 | + problem do | |
|
2 | + num_tests 10 | |
|
3 | + full_score 135 | |
|
4 | + time_limit_each 1 | |
|
5 | + mem_limit_each 11 | |
|
6 | + score_each 10 | |
|
7 | + | |
|
8 | + run 1 do | |
|
9 | + tests 1, 2 | |
|
10 | + scores 10, 20 | |
|
11 | + time_limits 1, 2 | |
|
12 | + mem_limits 5, 6 | |
|
13 | + end | |
|
14 | + | |
|
15 | + run 2 do | |
|
16 | + tests 3, 4, 5, 6, 7 | |
|
17 | + score_each 10 | |
|
18 | + time_limit_each 3 | |
|
19 | + mem_limit_each 3 | |
|
20 | + end | |
|
21 | + | |
|
22 | + run 3 do | |
|
23 | + tests 8, 9, 10 | |
|
24 | + end | |
|
25 | + | |
|
26 | + test 8 do | |
|
27 | + score 30 | |
|
28 | + time_limit 3 | |
|
29 | + mem_limit 10 | |
|
30 | + end | |
|
31 | + | |
|
32 | + test 9 do | |
|
33 | + score 15 | |
|
34 | + end | |
|
35 | + | |
|
36 | + test 10 do | |
|
37 | + time_limit 1 | |
|
38 | + end | |
|
39 | + end |
@@ -0,0 +1,59 | |||
|
1 | + #!/usr/bin/ruby | |
|
2 | + | |
|
3 | + problem_home = ENV['PROBLEM_HOME'] | |
|
4 | + require "#{problem_home}/script/test_dsl.rb" | |
|
5 | + | |
|
6 | + if ARGV.length < 2 | |
|
7 | + puts "Usage: check <language> <test-number> [<output-file>]" | |
|
8 | + exit(0) | |
|
9 | + end | |
|
10 | + | |
|
11 | + language = ARGV[0] | |
|
12 | + test_num = ARGV[1].to_i | |
|
13 | + if ARGV.length >= 3 | |
|
14 | + output_file_name = ARGV[2] | |
|
15 | + else | |
|
16 | + output_file_name = "output.txt" | |
|
17 | + end | |
|
18 | + | |
|
19 | + load "#{problem_home}/test_cases/all_tests.cfg" | |
|
20 | + problem = Problem.get_instance | |
|
21 | + | |
|
22 | + output_file = File.new(output_file_name, "r") | |
|
23 | + answer_file = File.new("#{problem_home}/test_cases/#{test_num}/answer-#{test_num}.txt") | |
|
24 | + result_file = File.new("check_result", "w") | |
|
25 | + | |
|
26 | + output_file_content = output_file.read | |
|
27 | + answer_file_content = answer_file.read | |
|
28 | + | |
|
29 | + report_correct = lambda { | |
|
30 | + result_file.write "Correct\n" | |
|
31 | + result_file.write problem.get_score(test_num) | |
|
32 | + result_file.write "\n" | |
|
33 | + result_file.close | |
|
34 | + exit(0) | |
|
35 | + } | |
|
36 | + | |
|
37 | + report_wrong = lambda { | |
|
38 | + result_file.write "Incorrect\n" | |
|
39 | + result_file.write "0\n" | |
|
40 | + result_file.close | |
|
41 | + exit(0) | |
|
42 | + } | |
|
43 | + | |
|
44 | + ################## | |
|
45 | + # Your code here # | |
|
46 | + ################## | |
|
47 | + num_pattern = /^[0-9]*/ | |
|
48 | + if (output_file_content =~ num_pattern) == nil | |
|
49 | + report_wrong.call | |
|
50 | + end | |
|
51 | + | |
|
52 | + output_i = output_file_content.to_i | |
|
53 | + answer_i = answer_file_content.to_i | |
|
54 | + | |
|
55 | + if output_i == answer_i | |
|
56 | + report_correct.call | |
|
57 | + else | |
|
58 | + report_wrong.call | |
|
59 | + end |
@@ -0,0 +1,20 | |||
|
1 | + problem do | |
|
2 | + num_tests 2 | |
|
3 | + full_score 20 | |
|
4 | + time_limit_each 1 | |
|
5 | + mem_limit_each 16 | |
|
6 | + score_each 10 | |
|
7 | + | |
|
8 | + run 1 do | |
|
9 | + tests 1 | |
|
10 | + end | |
|
11 | + | |
|
12 | + test 2 do | |
|
13 | + time_limit 2 | |
|
14 | + end | |
|
15 | + | |
|
16 | + run 2 do | |
|
17 | + tests 2 | |
|
18 | + end | |
|
19 | + | |
|
20 | + end |
@@ -0,0 +1,47 | |||
|
1 | + #!/usr/bin/ruby | |
|
2 | + | |
|
3 | + problem_home = ENV['PROBLEM_HOME'] | |
|
4 | + require "#{problem_home}/script/test_dsl.rb" | |
|
5 | + | |
|
6 | + if ARGV.length < 2 | |
|
7 | + puts "Usage: check <language> <test-number> [<output-file>]" | |
|
8 | + exit(0) | |
|
9 | + end | |
|
10 | + | |
|
11 | + language = ARGV[0] | |
|
12 | + test_num = ARGV[1].to_i | |
|
13 | + if ARGV.length >= 3 | |
|
14 | + output_file_name = ARGV[2] | |
|
15 | + else | |
|
16 | + output_file_name = "output.txt" | |
|
17 | + end | |
|
18 | + | |
|
19 | + load "#{problem_home}/test_cases/all_tests.cfg" | |
|
20 | + problem = Problem.get_instance | |
|
21 | + | |
|
22 | + output_file = File.new(output_file_name, "r") | |
|
23 | + answer_file = File.new("#{problem_home}/test_cases/#{test_num}/answer-#{test_num}.txt") | |
|
24 | + result_file = File.new("check_result", "w") | |
|
25 | + | |
|
26 | + output_file_content = output_file.read | |
|
27 | + answer_file_content = answer_file.read | |
|
28 | + | |
|
29 | + report_correct = lambda { | |
|
30 | + result_file.write "Correct\n" | |
|
31 | + result_file.write problem.get_score(test_num) | |
|
32 | + result_file.write "\n" | |
|
33 | + result_file.close | |
|
34 | + exit(0) | |
|
35 | + } | |
|
36 | + | |
|
37 | + report_wrong = lambda { | |
|
38 | + result_file.write "Incorrect\n" | |
|
39 | + result_file.write "0\n" | |
|
40 | + result_file.close | |
|
41 | + exit(0) | |
|
42 | + } | |
|
43 | + | |
|
44 | + ################## | |
|
45 | + # Your code here # | |
|
46 | + ################## | |
|
47 | + report_correct.call |
new file 100644 |
@@ -0,0 +1,11 | |||
|
1 | + problem do | |
|
2 | + num_tests 1 | |
|
3 | + full_score 10 | |
|
4 | + time_limit_each 1 | |
|
5 | + mem_limit_each 16 | |
|
6 | + score_each 10 | |
|
7 | + | |
|
8 | + run 1 do | |
|
9 | + tests 1 | |
|
10 | + end | |
|
11 | + end |
@@ -0,0 +1,246 | |||
|
1 | + require File.join(File.dirname(__FILE__),'spec_helper') | |
|
2 | + require File.join(File.dirname(__FILE__),'engine_spec_helper') | |
|
3 | + | |
|
4 | + describe "A grader engine, when grading submissions" do | |
|
5 | + | |
|
6 | + include GraderEngineHelperMethods | |
|
7 | + | |
|
8 | + before(:each) do | |
|
9 | + @config = Grader::Configuration.get_instance | |
|
10 | + | |
|
11 | + # this test is from Pong | |
|
12 | + @problem_test_normal = stub(Problem, | |
|
13 | + :id => 1, :name => 'test_normal', | |
|
14 | + :full_score => 135) | |
|
15 | + @user_user1 = stub(User, | |
|
16 | + :id => 1, :login => 'user1') | |
|
17 | + | |
|
18 | + @engine = Grader::Engine.new | |
|
19 | + init_sandbox | |
|
20 | + end | |
|
21 | + | |
|
22 | + it "should grade normal submission" do | |
|
23 | + grader_should(:grade => "test1_correct.c", | |
|
24 | + :on => @problem_test_normal, | |
|
25 | + :and_report => { | |
|
26 | + :score => 135, | |
|
27 | + :comment => /^PASSED/}) | |
|
28 | + end | |
|
29 | + | |
|
30 | + | |
|
31 | + it "should produce error message when submission cannot compile" do | |
|
32 | + grader_should(:grade => "test1_compile_error.c", | |
|
33 | + :on => @problem_test_normal, | |
|
34 | + :and_report => { | |
|
35 | + :score => 0, | |
|
36 | + :comment => 'FAILED: compile error', | |
|
37 | + :compiler_message => /[Ee]rror/}) | |
|
38 | + end | |
|
39 | + | |
|
40 | + it "should produce timeout error when submission runs forever" do | |
|
41 | + @problem_test_timeout = stub(Problem, | |
|
42 | + :id => 1, :name => 'test_timeout', | |
|
43 | + :full_score => 10) | |
|
44 | + grader_should(:grade => "test2_timeout.c", | |
|
45 | + :on => @problem_test_timeout, | |
|
46 | + :and_report => { | |
|
47 | + :score => 0, | |
|
48 | + :comment => 'FAILED: TT'}) | |
|
49 | + end | |
|
50 | + | |
|
51 | + it "should produce timeout error correctly when submission runs slower than expected in less than a second" do | |
|
52 | + @problem_test_timeout = stub(Problem, | |
|
53 | + :id => 1, :name => 'test_timeout', | |
|
54 | + :full_score => 20) | |
|
55 | + grader_should(:grade => "test2_1-5sec.c", | |
|
56 | + :on => @problem_test_timeout, | |
|
57 | + :and_report => { | |
|
58 | + :score => 10, | |
|
59 | + :comment => 'FAILED: TP'}) | |
|
60 | + end | |
|
61 | + | |
|
62 | + it "should produce runtime error when submission uses too much static memory" do | |
|
63 | + @problem_test_memory = stub(Problem, | |
|
64 | + :id => 1, :name => 'test_memory', | |
|
65 | + :full_score => 20) | |
|
66 | + grader_should(:grade => "add_too_much_memory_static.c", | |
|
67 | + :on => @problem_test_memory, | |
|
68 | + :and_report => { | |
|
69 | + :score => 10, | |
|
70 | + :comment => /FAILED: [^P]P/}) | |
|
71 | + end | |
|
72 | + | |
|
73 | + it "should not allow submission to allocate too much dynamic memory" do | |
|
74 | + @problem_test_memory = stub(Problem, | |
|
75 | + :id => 1, :name => 'test_memory', | |
|
76 | + :full_score => 20) | |
|
77 | + grader_should(:grade => "add_too_much_memory_dynamic.c", | |
|
78 | + :on => @problem_test_memory, | |
|
79 | + :and_report => { | |
|
80 | + :score => 10, | |
|
81 | + :comment => /FAILED: [^P]P/}) | |
|
82 | + end | |
|
83 | + | |
|
84 | + it "should fail submission with non-zero exit status" do | |
|
85 | + violated("has not been implemented") | |
|
86 | + end | |
|
87 | + | |
|
88 | + def grader_should(args) | |
|
89 | + @user1 = stub(User, | |
|
90 | + :id => 1, :login => 'user1') | |
|
91 | + submission = | |
|
92 | + create_submission_from_file(1, @user1, args[:on], args[:grade]) | |
|
93 | + submission.should_receive(:graded_at=) | |
|
94 | + | |
|
95 | + expected_score = args[:and_report][:score] | |
|
96 | + expected_comment = args[:and_report][:comment] | |
|
97 | + if args[:and_report][:compiler_message]!=nil | |
|
98 | + expected_compiler_message = args[:and_report][:compiler_message] | |
|
99 | + else | |
|
100 | + expected_compiler_message = '' | |
|
101 | + end | |
|
102 | + | |
|
103 | + submission.should_receive(:points=).with(expected_score) | |
|
104 | + submission.should_receive(:grader_comment=).with(expected_comment) | |
|
105 | + submission.should_receive(:compiler_message=).with(expected_compiler_message) | |
|
106 | + submission.should_receive(:save) | |
|
107 | + | |
|
108 | + @engine.grade(submission) | |
|
109 | + end | |
|
110 | + | |
|
111 | + protected | |
|
112 | + | |
|
113 | + def create_normal_submission_mock_from_file(source_fname) | |
|
114 | + create_submission_from_file(1, @user_user1, @problem_test_normal, source_fname) | |
|
115 | + end | |
|
116 | + | |
|
117 | + end | |
|
118 | + | |
|
119 | + describe "A grader engine, when grading test requests" do | |
|
120 | + | |
|
121 | + include GraderEngineHelperMethods | |
|
122 | + | |
|
123 | + before(:each) do | |
|
124 | + @config = Grader::Configuration.get_instance | |
|
125 | + @engine = Grader::Engine.new(Grader::TestRequestRoomMaker.new, | |
|
126 | + Grader::TestRequestReporter.new) | |
|
127 | + init_sandbox | |
|
128 | + end | |
|
129 | + | |
|
130 | + it "should report error if there is no problem template" do | |
|
131 | + problem = stub(Problem, | |
|
132 | + :id => 1, :name => 'nothing') | |
|
133 | + grader_should(:grade => 'test1_correct.c', | |
|
134 | + :on => problem, | |
|
135 | + :with => 'in1.txt', | |
|
136 | + :and_report => { | |
|
137 | + :graded_at= => nil, | |
|
138 | + :compiler_message= => '', | |
|
139 | + :grader_comment= => '', | |
|
140 | + :running_stat= => /template not found/, | |
|
141 | + :save => nil}) | |
|
142 | + end | |
|
143 | + | |
|
144 | + it "should run test request and produce output file" do | |
|
145 | + problem = stub(Problem, | |
|
146 | + :id => 1, :name => 'test_normal') | |
|
147 | + grader_should(:grade => 'test1_correct.c', | |
|
148 | + :on => problem, | |
|
149 | + :with => 'in1.txt', | |
|
150 | + :and_report => { | |
|
151 | + :graded_at= => nil, | |
|
152 | + :compiler_message= => '', | |
|
153 | + :grader_comment= => '', | |
|
154 | + :running_stat= => /0.0 sec./, | |
|
155 | + :output_file_name= => lambda { |fname| | |
|
156 | + File.exists?(fname).should be_true | |
|
157 | + }, | |
|
158 | + :save => nil}) | |
|
159 | + end | |
|
160 | + | |
|
161 | + it "should clean up problem directory after running test request" do | |
|
162 | + problem = stub(Problem, | |
|
163 | + :id => 1, :name => 'test_normal') | |
|
164 | + grader_should(:grade => 'test1_correct.c', | |
|
165 | + :on => problem, | |
|
166 | + :with => 'in1.txt', | |
|
167 | + :and_report => { | |
|
168 | + :graded_at= => nil, | |
|
169 | + :compiler_message= => '', | |
|
170 | + :grader_comment= => '', | |
|
171 | + :running_stat= => nil, | |
|
172 | + :output_file_name= => nil, | |
|
173 | + :save => nil}) | |
|
174 | + File.exists?(@config.user_result_dir + "/test_request/test_normal/test_cases/1/input-1.txt").should be_false | |
|
175 | + end | |
|
176 | + | |
|
177 | + it "should compile test request with error and report compilation error" do | |
|
178 | + problem = stub(Problem, | |
|
179 | + :id => 1, :name => 'test_normal') | |
|
180 | + grader_should(:grade => 'test1_compile_error.c', | |
|
181 | + :on => problem, | |
|
182 | + :with => 'in1.txt', | |
|
183 | + :and_report => { | |
|
184 | + :graded_at= => nil, | |
|
185 | + :compiler_message= => /.+/, | |
|
186 | + :grader_comment= => /[Cc]ompil.*error/, | |
|
187 | + :running_stat= => '', | |
|
188 | + :save => nil}) | |
|
189 | + end | |
|
190 | + | |
|
191 | + it "should report exit status" do | |
|
192 | + problem = stub(Problem, | |
|
193 | + :id => 1, :name => 'test_normal') | |
|
194 | + grader_should(:grade => 'add_nonzero_exit_status.c', | |
|
195 | + :on => problem, | |
|
196 | + :with => 'in1.txt', | |
|
197 | + :and_report => { | |
|
198 | + :graded_at= => nil, | |
|
199 | + :compiler_message= => '', | |
|
200 | + :grader_comment= => '', | |
|
201 | + :running_stat= => /[Ee]xit.*status.*10.*0.0 sec./, | |
|
202 | + :output_file_name= => lambda { |fname| | |
|
203 | + File.exists?(fname).should be_true | |
|
204 | + }, | |
|
205 | + :save => nil}) | |
|
206 | + end | |
|
207 | + | |
|
208 | + protected | |
|
209 | + def grader_should(args) | |
|
210 | + @user1 = stub(User, | |
|
211 | + :id => 1, :login => 'user1') | |
|
212 | + | |
|
213 | + problem = args[:on] | |
|
214 | + input_file = @config.test_request_input_base_dir + "/" + args[:with] | |
|
215 | + | |
|
216 | + submission = | |
|
217 | + create_submission_from_file(1, @user1, args[:on], args[:grade]) | |
|
218 | + | |
|
219 | + test_request = stub(TestRequest, | |
|
220 | + :id => 1, | |
|
221 | + :user => @user1, | |
|
222 | + :problem => problem, | |
|
223 | + :submission => submission, | |
|
224 | + :input_file_name => input_file, | |
|
225 | + :language => submission.language, | |
|
226 | + :problem_name => problem.name) | |
|
227 | + | |
|
228 | + expectations = args[:and_report] | |
|
229 | + | |
|
230 | + expectations.each do |key,val| | |
|
231 | + if val==nil | |
|
232 | + test_request.should_receive(key) | |
|
233 | + elsif val.class == Proc | |
|
234 | + test_request.should_receive(key) { |fname| | |
|
235 | + val.call(fname) | |
|
236 | + } | |
|
237 | + else | |
|
238 | + test_request.should_receive(key).with(val) | |
|
239 | + end | |
|
240 | + end | |
|
241 | + | |
|
242 | + @engine.grade(test_request) | |
|
243 | + end | |
|
244 | + | |
|
245 | + end | |
|
246 | + |
@@ -0,0 +1,30 | |||
|
1 | + module GraderEngineHelperMethods | |
|
2 | + | |
|
3 | + def clear_sandbox | |
|
4 | + config = Grader::Configuration.get_instance | |
|
5 | + clear_cmd = "rm -rf #{config.test_sandbox_dir}/*" | |
|
6 | + system(clear_cmd) | |
|
7 | + end | |
|
8 | + | |
|
9 | + def init_sandbox | |
|
10 | + config = Grader::Configuration.get_instance | |
|
11 | + clear_sandbox | |
|
12 | + Dir.mkdir config.user_result_dir | |
|
13 | + cp_cmd = "cp -R #{config.test_data_dir}/ev #{config.test_sandbox_dir}" | |
|
14 | + system(cp_cmd) | |
|
15 | + end | |
|
16 | + | |
|
17 | + def create_submission_from_file(id, user, problem, | |
|
18 | + source_fname, language=nil) | |
|
19 | + | |
|
20 | + language = stub(Language, :name => 'c', :ext => 'c') if language==nil | |
|
21 | + | |
|
22 | + config = Grader::Configuration.get_instance | |
|
23 | + source = File.open(config.test_data_dir + "/" + source_fname).read | |
|
24 | + stub(Submission, | |
|
25 | + :id => id, :user => user, :problem => problem, | |
|
26 | + :source => source, :language => language) | |
|
27 | + end | |
|
28 | + | |
|
29 | + end | |
|
30 | + |
@@ -0,0 +1,65 | |||
|
1 | + require File.join(File.dirname(__FILE__),'spec_helper') | |
|
2 | + require File.join(File.dirname(__FILE__),'engine_spec_helper') | |
|
3 | + | |
|
4 | + describe "A grader runner, when grade task" do | |
|
5 | + | |
|
6 | + include GraderEngineHelperMethods | |
|
7 | + | |
|
8 | + before(:each) do | |
|
9 | + @config = Grader::Configuration.get_instance | |
|
10 | + @problem_test_normal = stub(Problem, | |
|
11 | + :id => 1, :name => 'test_normal', | |
|
12 | + :full_score => 135) | |
|
13 | + @user_user1 = stub(User, | |
|
14 | + :id => 1, :login => 'user1') | |
|
15 | + | |
|
16 | + @engine = Grader::Engine.new | |
|
17 | + @runner = Grader::Runner.new(@engine) | |
|
18 | + init_sandbox | |
|
19 | + end | |
|
20 | + | |
|
21 | + it "should just return nil when there is no submission" do | |
|
22 | + Task.should_receive(:get_inqueue_and_change_status).and_return(nil) | |
|
23 | + @runner.grade_oldest_task.should be_nil | |
|
24 | + end | |
|
25 | + | |
|
26 | + it "should grade oldest task in queue" do | |
|
27 | + submission = create_normal_submission_mock_from_file("test1_correct.c") | |
|
28 | + | |
|
29 | + submission.should_receive(:graded_at=) | |
|
30 | + submission.should_receive(:points=).with(135) | |
|
31 | + submission.should_receive(:grader_comment=).with(/^PASSED/) | |
|
32 | + submission.should_receive(:compiler_message=).with('') | |
|
33 | + submission.should_receive(:save) | |
|
34 | + | |
|
35 | + # mock task | |
|
36 | + task = stub(Task,:id => 1, :submission_id => submission.id) | |
|
37 | + Task.should_receive(:get_inqueue_and_change_status).and_return(task) | |
|
38 | + task.should_receive(:status_complete!) | |
|
39 | + | |
|
40 | + # mock Submission | |
|
41 | + Submission.should_receive(:find). | |
|
42 | + with(task.submission_id). | |
|
43 | + and_return(submission) | |
|
44 | + | |
|
45 | + @runner.grade_oldest_task | |
|
46 | + end | |
|
47 | + | |
|
48 | + # to be converted | |
|
49 | + def test_grade_oldest_task_with_grader_process | |
|
50 | + grader_process = stub | |
|
51 | + grader_process.expects(:report_active) | |
|
52 | + | |
|
53 | + @runner = Grader::Runner.new(@engine,grader_process) | |
|
54 | + | |
|
55 | + test_grade_oldest_task | |
|
56 | + end | |
|
57 | + | |
|
58 | + protected | |
|
59 | + | |
|
60 | + def create_normal_submission_mock_from_file(source_fname) | |
|
61 | + create_submission_from_file(1, @user_user1, @problem_test_normal, source_fname) | |
|
62 | + end | |
|
63 | + | |
|
64 | + end | |
|
65 | + |
@@ -0,0 +1,25 | |||
|
1 | + | |
|
2 | + # This test helper loads the grader's environment and rails environment | |
|
3 | + | |
|
4 | + GRADER_ENV = 'test' | |
|
5 | + require File.join(File.dirname(__FILE__),'../config/environment') | |
|
6 | + | |
|
7 | + | |
|
8 | + # this shall be removed soon | |
|
9 | + RAILS_ENV = Grader::Configuration.get_instance.rails_env | |
|
10 | + require RAILS_ROOT + '/config/environment' | |
|
11 | + | |
|
12 | + # make sure not to access real database! | |
|
13 | + # taken from http://blog.jayfields.com/2006/06/ruby-on-rails-unit-tests.html | |
|
14 | + | |
|
15 | + class UnitTest | |
|
16 | + def self.TestCase | |
|
17 | + class << ActiveRecord::Base | |
|
18 | + def connection | |
|
19 | + raise 'You cannot access the database from a unit test' | |
|
20 | + # raise InvalidActionError, 'You cannot access the database from a unit test', caller | |
|
21 | + end | |
|
22 | + end | |
|
23 | + Test::Unit::TestCase | |
|
24 | + end | |
|
25 | + end |
@@ -1,20 +1,20 | |||
|
1 | - | |
|
2 | - | |
|
1 | + # | |
|
2 | + # See documentation in lib/configuration.rb | |
|
3 | + # | |
|
3 | 4 | Grader::Initializer.run do |config| |
|
4 | 5 | |
|
5 |
- config.problems_dir = |
|
|
6 |
- config.user_result_dir = |
|
|
6 | + config.problems_dir = GRADER_ROOT + "/../ev" | |
|
7 | + config.user_result_dir = GRADER_ROOT + "/../result" | |
|
7 | 8 | |
|
8 | 9 | config.talkative = true |
|
10 | + config.logging = true | |
|
11 | + config.log_dir = GRADER_ROOT + "/../log" | |
|
9 | 12 | |
|
10 | 13 | config.report_grader = true |
|
11 | 14 |
|
|
12 | - config.report_comment = lambda do |comment| | |
|
13 | - if comment.chomp =~ /^P+$/ # all P's | |
|
14 | - 'passed' | |
|
15 | - else | |
|
16 | - 'failed' | |
|
15 | + config.test_request_input_base_dir = RAILS_ROOT + "/data/test_request/input" | |
|
16 | + config.test_request_output_base_dir = RAILS_ROOT + "/data/test_request/output" | |
|
17 | + config.test_request_problem_templates_dir = config.problems_dir + "/test_request" | |
|
18 | + | |
|
19 | + config.comment_report_style = :short | |
|
17 | 20 |
|
|
18 | - end | |
|
19 | - | |
|
20 | - end |
@@ -1,16 +1,19 | |||
|
1 | - | |
|
2 | - | |
|
1 | + # | |
|
2 | + # See documentation in lib/configuration.rb | |
|
3 | + # | |
|
3 | 4 | Grader::Initializer.run do |config| |
|
4 | - | |
|
5 | - config.problems_dir = "/home/jittat/grader/ev" | |
|
6 | - config.user_result_dir = "/home/jittat/grader/result" | |
|
5 | + config.problems_dir = GRADER_ROOT + "/../ev" | |
|
6 | + config.user_result_dir = GRADER_ROOT + "/../result" | |
|
7 | 7 | |
|
8 | 8 | config.talkative = true |
|
9 | + config.logging = true | |
|
10 | + config.log_dir = GRADER_ROOT + "/../log" | |
|
9 | 11 | |
|
10 | 12 | config.report_grader = true |
|
11 | 13 | |
|
12 | - config.report_comment = lambda do |comment| | |
|
13 | - comment.chomp | |
|
14 | + config.test_request_input_base_dir = RAILS_ROOT + "/data/test_request/input" | |
|
15 | + config.test_request_output_base_dir = RAILS_ROOT + "/data/test_request/output" | |
|
16 | + config.test_request_problem_templates_dir = config.problems_dir + "/test_request" | |
|
17 | + | |
|
18 | + config.comment_report_style = :full | |
|
14 | 19 |
|
|
15 | - | |
|
16 | - end |
@@ -1,25 +1,29 | |||
|
1 | - | |
|
2 | - | |
|
1 | + # | |
|
2 | + # See documentation in lib/configuration.rb | |
|
3 | + # | |
|
3 | 4 | Grader::Initializer.run do |config| |
|
5 | + config.problems_dir = GRADER_ROOT + "/test/sandbox/ev" | |
|
6 | + config.user_result_dir = GRADER_ROOT + "/test/sandbox/result" | |
|
4 | 7 | |
|
5 | - config.problems_dir = "/home/jittat/grader/scripts/test/sandbox/ev" | |
|
6 | - config.user_result_dir = "/home/jittat/grader/scripts/test/sandbox/result" | |
|
7 | - | |
|
8 | - config.talkative = true | |
|
8 | + config.talkative = false | |
|
9 | 9 | |
|
10 | 10 | config.report_grader = false |
|
11 | 11 | |
|
12 | 12 | config.rails_env = 'test' |
|
13 | 13 | |
|
14 | - config.report_comment = lambda do |comment| | |
|
15 | - comment.chomp | |
|
16 | - end | |
|
14 | + config.comment_report_style = :full | |
|
17 | 15 | |
|
16 | + config.test_request_input_base_dir = GRADER_ROOT + "/test/data/test_request/input" | |
|
17 | + config.test_request_output_base_dir = GRADER_ROOT + "/test/sandbox/test_request/output" | |
|
18 | + config.test_request_problem_templates_dir = GRADER_ROOT + "/test/data/test_request/problems" | |
|
19 | + | |
|
20 | + # | |
|
21 | + # These options are for testing | |
|
22 | + # | |
|
18 | 23 | class << config |
|
19 | 24 | attr_accessor :test_data_dir, :test_sandbox_dir |
|
20 | 25 | end |
|
21 | 26 | |
|
22 |
- config.test_data_dir = " |
|
|
23 |
- config.test_sandbox_dir = " |
|
|
24 | - | |
|
27 | + config.test_data_dir = GRADER_ROOT + "/test/data" | |
|
28 | + config.test_sandbox_dir = GRADER_ROOT + "/test/sandbox" | |
|
25 | 29 | end |
@@ -1,11 +1,10 | |||
|
1 | - | |
|
2 | 1 | # Rails app directory |
|
3 | 2 | RAILS_ROOT = "/home/jittat/web_grader" |
|
4 | 3 | |
|
5 | 4 | GRADER_ROOT = "/home/jittat/grader/scripts" |
|
6 | 5 | |
|
6 | + # This load all required codes | |
|
7 | 7 | require File.join(File.dirname(__FILE__),'../lib/boot') |
|
8 | 8 | |
|
9 | 9 | # load the required environment file |
|
10 | 10 | require File.dirname(__FILE__) + "/env_#{GRADER_ENV}.rb" |
|
11 | - |
@@ -16,16 +16,46 | |||
|
16 | 16 | Grader::Configuration.get_instance |
|
17 | 17 | end |
|
18 | 18 | |
|
19 | - def talk(str) | |
|
19 | + def log_file_name | |
|
20 | + config.log_dir + | |
|
21 | + "/#{GRADER_ENV}_#{config.grader_mode}.#{Process.pid}" | |
|
22 | + end | |
|
23 | + | |
|
24 | + def log(str) | |
|
20 | 25 | if config.talkative |
|
21 | 26 | puts str |
|
22 | 27 | end |
|
28 | + if config.logging | |
|
29 | + fp = File.open(log_file_name,"a") | |
|
30 | + fp.puts("GRADER: #{Time.new.strftime("%H:%M")} #{str}") | |
|
31 | + fp.close | |
|
32 | + end | |
|
33 | + end | |
|
34 | + | |
|
35 | + def display_manual | |
|
36 | + puts <<USAGE | |
|
37 | + Grader. | |
|
38 | + using: (1) grader | |
|
39 | + (2) grader environment [mode] | |
|
40 | + (3) grader stop | |
|
41 | + (4) grader --help | |
|
42 | + (1) call grader with environment = 'exam', mode = 'queue' | |
|
43 | + (2) possible modes are: 'queue', 'prob', 'test_request' | |
|
44 | + (3) create stop-file to stop running grader in queue mode | |
|
45 | + (4) You are here. | |
|
46 | + USAGE | |
|
23 | 47 | end |
|
24 | 48 | |
|
25 | 49 | ######################################### |
|
26 | 50 | # main program |
|
27 | 51 | ######################################### |
|
28 | 52 | |
|
53 | + # with --help | |
|
54 | + if (ARGV.length==1) and (/help/.match(ARGV[0])) | |
|
55 | + display_manual | |
|
56 | + exit(0) | |
|
57 | + end | |
|
58 | + | |
|
29 | 59 | # reading environment and options |
|
30 | 60 | if (ARGV.length >= 1) and (ARGV[0]=='stop') |
|
31 | 61 | stop_grader |
@@ -52,13 +82,19 | |||
|
52 | 82 | puts "environment: #{GRADER_ENV}" |
|
53 | 83 | require File.join(File.dirname(__FILE__),'config/environment') |
|
54 | 84 | |
|
85 | + # add grader_mode to config | |
|
86 | + # this is needed because method log needs it. TODO: clean this up | |
|
87 | + class << config | |
|
88 | + attr_accessor :grader_mode | |
|
89 | + end | |
|
90 | + config.grader_mode = grader_mode | |
|
91 | + | |
|
55 | 92 | #reading rails environment |
|
56 |
- |
|
|
93 | + log 'Reading rails environment' | |
|
57 | 94 | |
|
58 | 95 | RAILS_ENV = config.rails_env |
|
59 | 96 | require RAILS_ROOT + '/config/environment' |
|
60 | 97 | |
|
61 | - | |
|
62 | 98 | #register grader process |
|
63 | 99 | if config.report_grader |
|
64 | 100 | grader_proc = GraderProcess.register(config.grader_hostname, |
@@ -68,27 +104,45 | |||
|
68 | 104 | grader_proc = nil |
|
69 | 105 | end |
|
70 | 106 | |
|
71 | - # create judge engine | |
|
72 | - engine = Grader::Engine.new(grader_proc) | |
|
107 | + #set loggin environment | |
|
108 | + ENV['GRADER_LOGGING'] = log_file_name | |
|
109 | + | |
|
110 | + # | |
|
111 | + # MAIN LOOP | |
|
112 | + # | |
|
73 | 113 | |
|
74 | 114 | case grader_mode |
|
75 | - when "queue" | |
|
76 | - talk 'Grader queue' | |
|
115 | + when "queue", "test_request" | |
|
116 | + log "Grader: #{grader_mode}" | |
|
117 | + if grader_mode=="queue" | |
|
118 | + engine = Grader::Engine.new | |
|
119 | + else | |
|
120 | + engine = Grader::Engine.new(Grader::TestRequestRoomMaker.new, | |
|
121 | + Grader::TestRequestReporter.new) | |
|
122 | + end | |
|
123 | + | |
|
124 | + runner = Grader::Runner.new(engine, grader_proc) | |
|
77 | 125 | while true |
|
78 | 126 | |
|
79 | 127 | if check_stopfile # created by calling grader stop |
|
80 | 128 | clear_stopfile |
|
81 | - puts "stopped" | |
|
129 | + log "stopped (with stop file)" | |
|
82 | 130 | break |
|
83 | 131 | end |
|
84 | 132 | |
|
85 | - task = engine.grade_oldest_task | |
|
133 | + if grader_mode=="queue" | |
|
134 | + task = runner.grade_oldest_task | |
|
135 | + else | |
|
136 | + task = runner.grade_oldest_test_request | |
|
137 | + end | |
|
86 | 138 | if task==nil |
|
87 |
- sleep( |
|
|
139 | + sleep(1) | |
|
88 | 140 | end |
|
89 | 141 | end |
|
90 | 142 | |
|
91 | 143 | when "prob" |
|
144 | + engine = Grader::Engine.new | |
|
145 | + runner = Grader::Runner.new(engine, grader_proc) | |
|
92 | 146 | |
|
93 | 147 | grader_proc.report_active if grader_proc!=nil |
|
94 | 148 | |
@@ -96,9 +150,12 | |||
|
96 | 150 | if prob==nil |
|
97 | 151 | puts "cannot find problem: #{ARGV[2]}" |
|
98 | 152 | else |
|
99 |
- |
|
|
153 | + runner.grade_problem(prob) | |
|
100 | 154 | end |
|
101 | 155 | |
|
156 | + else | |
|
157 | + display_manual | |
|
158 | + exit(0) | |
|
102 | 159 | end |
|
103 | 160 | |
|
104 | 161 | # report inactive |
@@ -5,6 +5,7 | |||
|
5 | 5 | # * copy testdata in the old format and create standard testcase config file |
|
6 | 6 | |
|
7 | 7 | require 'erb' |
|
8 | + require 'fileutils' | |
|
8 | 9 | |
|
9 | 10 | def input_filename(dir,i) |
|
10 | 11 | "#{dir}/input-#{i}.txt" |
@@ -34,15 +35,18 | |||
|
34 | 35 | end |
|
35 | 36 | end |
|
36 | 37 | |
|
37 |
- |
|
|
38 | + SCRIPT_DIR = File.dirname(__FILE__) | |
|
38 | 39 | |
|
39 | 40 | # print usage |
|
40 | 41 | if ARGV.length < 3 |
|
41 | - puts "using: import_problem prob_name importing_testcase_dir num_of_testcase [options] | |
|
42 | + puts <<USAGE | |
|
43 | + using: import_problem prob_name importing_testcase_dir num_of_testcase [options] | |
|
42 | 44 | * creates a directory for a problem in the current directory, |
|
43 | 45 | * copy testdata in the old format and create standard testcase config file |
|
46 | + * creates a test_request template in the current directory + '/test_request' | |
|
44 | 47 | * options: -t time-limit (in seconds) |
|
45 |
- -m memory-limit (in megabytes) |
|
|
48 | + -m memory-limit (in megabytes) | |
|
49 | + USAGE | |
|
46 | 50 | exit(127) |
|
47 | 51 | end |
|
48 | 52 | |
@@ -57,6 +61,7 | |||
|
57 | 61 | # start working |
|
58 | 62 | puts "creating directories" |
|
59 | 63 | |
|
64 | + | |
|
60 | 65 | system("mkdir #{problem}") |
|
61 | 66 | system("mkdir #{problem}/script") |
|
62 | 67 | system("mkdir #{problem}/test_cases") |
@@ -73,11 +78,26 | |||
|
73 | 78 | # generating all_tests.cfg |
|
74 | 79 | puts "generating testcase config file" |
|
75 | 80 | |
|
76 |
- template = File.open( |
|
|
81 | + template = File.open(SCRIPT_DIR + "/templates/all_tests.cfg.erb").read | |
|
77 | 82 | all_test_cfg = ERB.new(template) |
|
78 | 83 | |
|
79 | 84 | cfg_file = File.open("#{problem}/test_cases/all_tests.cfg","w") |
|
80 | 85 | cfg_file.puts all_test_cfg.result |
|
81 | 86 | cfg_file.close |
|
82 | 87 | |
|
88 | + # generating test_request directory | |
|
89 | + puts "generating test_request template" | |
|
90 | + FileUtils.mkdir_p("test_request/#{problem}/script") | |
|
91 | + FileUtils.mkdir_p("test_request/#{problem}/test_cases/1") | |
|
92 | + | |
|
93 | + template = File.open(SCRIPT_DIR + "/templates/test_request_all_tests.cfg.erb").read | |
|
94 | + test_request_all_test_cfg = ERB.new(template) | |
|
95 | + | |
|
96 | + cfg_file = File.open("test_request/#{problem}/test_cases/all_tests.cfg","w") | |
|
97 | + cfg_file.puts test_request_all_test_cfg.result | |
|
98 | + cfg_file.close | |
|
99 | + | |
|
100 | + system("cp #{SCRIPT_DIR}/templates/check_empty test_request/#{problem}/script/check") | |
|
101 | + system("cp #{SCRIPT_DIR}/templates/answer-1.txt test_request/#{problem}/test_cases/1") | |
|
102 | + | |
|
83 | 103 | puts "done" |
@@ -1,5 +1,10 | |||
|
1 | 1 | |
|
2 | 2 | require File.join(File.dirname(__FILE__), 'configuration') |
|
3 | 3 | require File.join(File.dirname(__FILE__), 'initializer') |
|
4 | + | |
|
5 | + require File.join(File.dirname(__FILE__), 'submission_helper') | |
|
6 | + require File.join(File.dirname(__FILE__), 'test_request_helper') | |
|
7 | + | |
|
4 | 8 | require File.join(File.dirname(__FILE__), 'engine') |
|
9 | + require File.join(File.dirname(__FILE__), 'runner') | |
|
5 | 10 |
@@ -1,17 +1,60 | |||
|
1 | - | |
|
2 | 1 |
|
|
3 | 2 | |
|
3 | + # This singleton class holds basic configurations for grader. When | |
|
4 | + # running in each mode, grader uses resources from different | |
|
5 | + # directories and outputs differently. Usually the attributes name | |
|
6 | + # are descriptive; below we explain more on each attributes. | |
|
4 | 7 | class Configuration |
|
8 | + # Rails' environment: "development", "production" | |
|
9 | + attr_accessor :rails_env | |
|
5 | 10 | |
|
6 | - private_class_method :new | |
|
7 | - | |
|
11 | + # Grader looks for problem [prob] in problem_dir/[prob], and store | |
|
12 | + # execution results for submission [x] of user [u] in directory | |
|
13 | + # user_result_dir/[u]/[x] | |
|
8 | 14 | attr_accessor :problems_dir |
|
9 | 15 | attr_accessor :user_result_dir |
|
10 | - attr_accessor :talkative | |
|
16 | + | |
|
17 | + # If report_grader=true, the grader would add a row in model | |
|
18 | + # GraderProcess. It would report itself with grader_hostname and | |
|
19 | + # process id. | |
|
11 | 20 | attr_accessor :report_grader |
|
12 | 21 | attr_accessor :grader_hostname |
|
13 | - attr_accessor :report_comment | |
|
14 | - attr_accessor :rails_env | |
|
22 | + | |
|
23 | + # If talkative=true, grader would report status to console. If | |
|
24 | + # logging=true, grader would report status to a log file located | |
|
25 | + # in log_dir, in a file name mode.options.pid. TODO: defined | |
|
26 | + # log file naming. | |
|
27 | + attr_accessor :talkative | |
|
28 | + attr_accessor :logging | |
|
29 | + attr_accessor :log_dir | |
|
30 | + | |
|
31 | + # These are directories related to the test interface. | |
|
32 | + attr_accessor :test_request_input_base_dir | |
|
33 | + attr_accessor :test_request_output_base_dir | |
|
34 | + attr_accessor :test_request_problem_templates_dir | |
|
35 | + | |
|
36 | + # Comment received from the grading script will be filtered | |
|
37 | + # through Configuration#report_comment. How this method behave | |
|
38 | + # depends on this option; right now only two formats, :short and | |
|
39 | + # :long | |
|
40 | + attr_accessor :comment_report_style | |
|
41 | + | |
|
42 | + def report_comment(comment) | |
|
43 | + case comment_report_style | |
|
44 | + when :short | |
|
45 | + if comment.chomp =~ /^P+$/ # all P's | |
|
46 | + 'passed' | |
|
47 | + else | |
|
48 | + 'failed' | |
|
49 | + end | |
|
50 | + | |
|
51 | + when :full | |
|
52 | + comment.chomp | |
|
53 | + end | |
|
54 | + end | |
|
55 | + | |
|
56 | + # Codes for singleton | |
|
57 | + private_class_method :new | |
|
15 | 58 | |
|
16 | 59 | @@instance = nil |
|
17 | 60 | |
@@ -25,12 +68,13 | |||
|
25 | 68 | private |
|
26 | 69 | def initialize |
|
27 | 70 | @talkative = false |
|
71 | + @log_file = nil | |
|
28 | 72 | @report_grader = false |
|
29 | 73 | @grader_hostname = `hostname`.chomp |
|
30 | 74 | |
|
31 | 75 | @rails_env = 'development' |
|
32 | 76 | |
|
33 | - @report_comment = lambda { |comment| comment } | |
|
77 | + @comment_report_style = :full | |
|
34 | 78 | end |
|
35 | 79 | |
|
36 | 80 | end |
@@ -1,77 +1,75 | |||
|
1 | + require 'fileutils' | |
|
1 | 2 | |
|
2 | 3 | module Grader |
|
3 | 4 | |
|
5 | + # | |
|
6 | + # A grader engine grades a submission, against anything: a test | |
|
7 | + # data, or a user submitted test data. It uses two helpers objects: | |
|
8 | + # room_maker and reporter. | |
|
9 | + # | |
|
4 | 10 | class Engine |
|
5 | 11 | |
|
6 | - def initialize(grader_process=nil) | |
|
12 | + attr_writer :room_maker | |
|
13 | + attr_writer :reporter | |
|
14 | + | |
|
15 | + def initialize(room_maker=nil, reporter=nil) | |
|
7 | 16 | @config = Grader::Configuration.get_instance |
|
8 | - @grader_process = grader_process | |
|
17 | + | |
|
18 | + @room_maker = room_maker || Grader::SubmissionRoomMaker.new | |
|
19 | + @reporter = reporter || Grader::SubmissionReporter.new | |
|
9 | 20 | end |
|
10 | 21 | |
|
11 | - def grade(sub) | |
|
22 | + # takes a submission, asks room_maker to produce grading directories, | |
|
23 | + # calls grader scripts, and asks reporter to save the result | |
|
24 | + def grade(submission) | |
|
12 | 25 | current_dir = `pwd`.chomp |
|
13 | 26 | |
|
14 |
- |
|
|
15 | - user = sub.user | |
|
16 | - problem = sub.problem | |
|
27 | + user = submission.user | |
|
28 | + problem = submission.problem | |
|
17 | 29 | |
|
18 | 30 | # TODO: will have to create real exception for this |
|
19 |
- |
|
|
31 | + if user==nil or problem == nil | |
|
32 | + @reporter.report_error(submission,"Grading error: problem with submission") | |
|
33 | + #raise "engine: user or problem is nil" | |
|
34 | + end | |
|
20 | 35 | |
|
21 | - language = sub.language.name | |
|
22 | - lang_ext = sub.language.ext | |
|
36 | + language = submission.language.name | |
|
37 | + lang_ext = submission.language.ext | |
|
23 | 38 | # FIX THIS |
|
24 | 39 | talk 'some hack on language' |
|
25 | 40 | if language == 'cpp' |
|
26 | 41 | language = 'c++' |
|
27 | 42 | end |
|
28 | 43 | |
|
29 | - user_dir = "#{@config.user_result_dir}/#{user.login}" | |
|
30 | - problem_out_dir = "#{user_dir}/#{problem.name}" | |
|
31 | - submission_out_dir = "#{user_dir}/#{problem.name}/#{submission_id}" | |
|
44 | + # COMMENT: should it be only source.ext? | |
|
45 | + if problem!=nil | |
|
46 | + source_name = "#{problem.name}.#{lang_ext}" | |
|
47 | + else | |
|
48 | + source_name = "source.#{lang_ext}" | |
|
49 | + end | |
|
32 | 50 | |
|
33 | - mkdir_if_does_not_exist(user_dir) | |
|
34 | - mkdir_if_does_not_exist(problem_out_dir) | |
|
35 | - mkdir_if_does_not_exist(submission_out_dir) | |
|
51 | + begin | |
|
52 | + grading_dir = @room_maker.produce_grading_room(submission) | |
|
53 | + @room_maker.save_source(submission,source_name) | |
|
54 | + problem_home = @room_maker.find_problem_home(submission) | |
|
36 | 55 | |
|
37 | - problem_home = "#{@config.problems_dir}/#{problem.name}" | |
|
38 | - source_name = "#{problem.name}.#{lang_ext}" | |
|
39 | - | |
|
40 | - save_source(sub,submission_out_dir,source_name) | |
|
56 | + # puts "GRADING DIR: #{grading_dir}" | |
|
57 | + # puts "PROBLEM DIR: #{problem_home}" | |
|
41 | 58 | |
|
42 | 59 | copy_log = copy_script(problem_home) |
|
43 | 60 | |
|
44 |
- call_judge(problem_home,language, |
|
|
45 | - save_result(sub,read_result("#{submission_out_dir}/test-result")) | |
|
61 | + call_judge(problem_home,language,grading_dir,source_name) | |
|
62 | + | |
|
63 | + @reporter.report(submission,"#{grading_dir}/test-result") | |
|
46 | 64 | |
|
47 | 65 | clear_script(copy_log,problem_home) |
|
48 | 66 | |
|
49 | - Dir.chdir(current_dir) | |
|
50 | - end | |
|
51 | - | |
|
52 | - def grade_oldest_task | |
|
53 | - task = Task.get_inqueue_and_change_status(Task::STATUS_GRADING) | |
|
54 | - if task!=nil | |
|
55 | - @grader_process.report_active(task) if @grader_process!=nil | |
|
67 | + rescue RuntimeError => msg | |
|
68 | + @reporter.report_error(submission,"Grading error: #{msg}") | |
|
56 | 69 | |
|
57 | - submission = Submission.find(task.submission_id) | |
|
58 |
- |
|
|
59 | - task.status_complete! | |
|
60 | - end | |
|
61 | - return task | |
|
62 | - end | |
|
63 | - | |
|
64 | - def grade_problem(problem) | |
|
65 | - users = User.find(:all) | |
|
66 | - users.each do |u| | |
|
67 | - puts "user: #{u.login}" | |
|
68 | - last_sub = Submission.find(:first, | |
|
69 | - :conditions => "user_id = #{u.id} and " + | |
|
70 | - "problem_id = #{prob.id}", | |
|
71 | - :order => 'submitted_at DESC') | |
|
72 | - if last_sub!=nil | |
|
73 | - grade(last_sub) | |
|
74 | - end | |
|
70 | + ensure | |
|
71 | + @room_maker.clean_up(submission) | |
|
72 | + Dir.chdir(current_dir) # this is really important | |
|
75 | 73 | end |
|
76 | 74 | end |
|
77 | 75 | |
@@ -83,66 +81,16 | |||
|
83 | 81 | end |
|
84 | 82 | end |
|
85 | 83 | |
|
86 | - def save_source(submission,dir,fname) | |
|
87 | - f = File.open("#{dir}/#{fname}","w") | |
|
88 | - f.write(submission.source) | |
|
89 | - f.close | |
|
90 | - end | |
|
91 | - | |
|
92 | - def call_judge(problem_home,language,submission_out_dir,fname) | |
|
84 | + def call_judge(problem_home,language,grading_dir,fname) | |
|
93 | 85 | ENV['PROBLEM_HOME'] = problem_home |
|
94 | 86 | |
|
95 |
- talk |
|
|
96 |
- Dir.chdir |
|
|
87 | + talk grading_dir | |
|
88 | + Dir.chdir grading_dir | |
|
97 | 89 | cmd = "#{problem_home}/script/judge #{language} #{fname}" |
|
98 | 90 | talk "CMD: #{cmd}" |
|
99 | 91 | system(cmd) |
|
100 | 92 | end |
|
101 | 93 | |
|
102 | - def read_result(test_result_dir) | |
|
103 | - cmp_msg_fname = "#{test_result_dir}/compiler_message" | |
|
104 | - cmp_file = File.open(cmp_msg_fname) | |
|
105 | - cmp_msg = cmp_file.read | |
|
106 | - cmp_file.close | |
|
107 | - | |
|
108 | - result_fname = "#{test_result_dir}/result" | |
|
109 | - comment_fname = "#{test_result_dir}/comment" | |
|
110 | - if FileTest.exist?(result_fname) | |
|
111 | - result_file = File.open(result_fname) | |
|
112 | - result = result_file.readline.to_i | |
|
113 | - result_file.close | |
|
114 | - | |
|
115 | - comment_file = File.open(comment_fname) | |
|
116 | - comment = comment_file.readline.chomp | |
|
117 | - comment_file.close | |
|
118 | - | |
|
119 | - return {:points => result, | |
|
120 | - :comment => comment, | |
|
121 | - :cmp_msg => cmp_msg} | |
|
122 | - else | |
|
123 | - return {:points => 0, | |
|
124 | - :comment => 'compile error', | |
|
125 | - :cmp_msg => cmp_msg} | |
|
126 | - end | |
|
127 | - end | |
|
128 | - | |
|
129 | - def save_result(submission,result) | |
|
130 | - problem = submission.problem | |
|
131 | - submission.graded_at = Time.now | |
|
132 | - points = result[:points] | |
|
133 | - submission.points = points | |
|
134 | - comment = @config.report_comment.call(result[:comment]) | |
|
135 | - if problem == nil | |
|
136 | - submission.grader_comment = 'PASSED: ' + comment + '(problem is nil)' | |
|
137 | - elsif points == problem.full_score | |
|
138 | - submission.grader_comment = 'PASSED: ' + comment | |
|
139 | - else | |
|
140 | - submission.grader_comment = 'FAILED: ' + comment | |
|
141 | - end | |
|
142 | - submission.compiler_message = result[:cmp_msg] | |
|
143 | - submission.save | |
|
144 | - end | |
|
145 | - | |
|
146 | 94 | def get_std_script_dir |
|
147 | 95 | GRADER_ROOT + '/std-script' |
|
148 | 96 | end |
@@ -20,7 +20,7 | |||
|
20 | 20 | elsif comment =~ /[Tt]ime/ |
|
21 | 21 | 'T' |
|
22 | 22 | else |
|
23 | - '?' | |
|
23 | + 'x' # these are run time errors | |
|
24 | 24 | end |
|
25 | 25 | end |
|
26 | 26 |
@@ -12,6 +12,18 | |||
|
12 | 12 | end |
|
13 | 13 | end |
|
14 | 14 | |
|
15 | + def extract_time(t) | |
|
16 | + if (result=/^(.*)r(.*)u(.*)s/.match(t)) | |
|
17 | + {:real => result[1], :user => result[2], :sys => result[3]} | |
|
18 | + else | |
|
19 | + raise "Error reading running time" | |
|
20 | + end | |
|
21 | + end | |
|
22 | + | |
|
23 | + def compile_box(source,bin) | |
|
24 | + system("gcc #{source} -o #{bin}") | |
|
25 | + end | |
|
26 | + | |
|
15 | 27 | if ARGV.length < 2 || ARGV.length > 3 |
|
16 | 28 | puts "Usage: run <language> <test-num> [<program-name>]" |
|
17 | 29 | exit(127) |
@@ -55,8 +67,17 | |||
|
55 | 67 | # Copy the input file. |
|
56 | 68 | #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .` |
|
57 | 69 | |
|
70 | + time_output_format = "%Er%Uu%Ss" | |
|
71 | + | |
|
72 | + # check if box is there, if not, compile it! | |
|
73 | + if !File.exists?("#{problem_home}/script/box") | |
|
74 | + log "WARNING: Compiling box: to increase efficiency, it should be compile manually" | |
|
75 | + compile_box("#{problem_home}/script/box.c", | |
|
76 | + "#{problem_home}/script/box") | |
|
77 | + end | |
|
78 | + | |
|
58 | 79 | # Run the program. |
|
59 |
- run_command = "/usr/bin/time -f \" |
|
|
80 | + run_command = "/usr/bin/time -f \"#{time_output_format}\" 2>run_result #{problem_home}/script/box -a 2 -f -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name}" | |
|
60 | 81 | log "Running test #{test_num}..." |
|
61 | 82 | log run_command |
|
62 | 83 | log |
@@ -71,6 +92,7 | |||
|
71 | 92 | run_result = run_result_file.readlines |
|
72 | 93 | run_result_file.close |
|
73 | 94 | time_elapsed = run_result[run_result.length-1] |
|
95 | + running_time = extract_time(time_elapsed) | |
|
74 | 96 | |
|
75 | 97 | report = lambda{ |status, points, comment| |
|
76 | 98 | result_file.write status.strip |
@@ -99,6 +121,11 | |||
|
99 | 121 | report.call(run_result[0], 0, "No comment.\n") |
|
100 | 122 | end |
|
101 | 123 | |
|
124 | + if running_time[:user].to_f + running_time[:sys].to_f > time_limit | |
|
125 | + log "Time limit exceeded." | |
|
126 | + report.call("Time limit exceeded", 0, "No comment.\n") | |
|
127 | + end | |
|
128 | + | |
|
102 | 129 | # Run 'check' to evaluate the output. |
|
103 | 130 | #puts "There was no runtime error. Proceed to checking the output." |
|
104 | 131 | check_command = "#{problem_home}/script/check #{language} #{test_num}" |
@@ -1,4 +1,5 | |||
|
1 | 1 | #include <stdio.h> |
|
2 | + #include <stdlib.h> | |
|
2 | 3 | |
|
3 | 4 | int main() |
|
4 | 5 | { |
@@ -15,5 +16,6 | |||
|
15 | 16 | b+=c; |
|
16 | 17 | } |
|
17 | 18 | } |
|
19 | + exit(0); | |
|
18 | 20 | } |
|
19 | 21 |
@@ -76,14 +76,14 | |||
|
76 | 76 | end |
|
77 | 77 | |
|
78 | 78 | def test_timeout_submission_running_one_and_a_half_second |
|
79 |
- @problem_test2 = stub(:id => 1, :name => 'test2', :full_score => |
|
|
79 | + @problem_test2 = stub(:id => 1, :name => 'test2', :full_score => 20) | |
|
80 | 80 | @user_user1 = stub(:id => 1, :login => 'user1') |
|
81 | 81 | |
|
82 | 82 | submission = create_submission_from_file(1, @user_user1, @problem_test2, |
|
83 | 83 | "test2_1-5sec.c") |
|
84 | 84 | |
|
85 | 85 | submission.expects(:graded_at=) |
|
86 | - submission.expects(:points=).with(0) | |
|
86 | + submission.expects(:points=).with(10) | |
|
87 | 87 | submission.expects(:grader_comment=).with do |value| |
|
88 | 88 | /^FAILED: TP$/.match value |
|
89 | 89 | end |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
deleted file |
You need to be logged in to leave comments.
Login now