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: 1454 inserted, 393 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 | - |
|
1 | + # |
|
2 | - |
|
2 | + # See documentation in lib/configuration.rb |
|
|
3 | + # | ||
|
3 | Grader::Initializer.run do |config| |
|
4 | Grader::Initializer.run do |config| |
|
4 |
|
5 | ||
|
5 |
- config.problems_dir = |
|
6 | + config.problems_dir = GRADER_ROOT + "/../ev" |
|
6 |
- config.user_result_dir = |
|
7 | + config.user_result_dir = GRADER_ROOT + "/../result" |
|
7 |
|
8 | ||
|
8 | config.talkative = true |
|
9 | config.talkative = true |
|
|
10 | + config.logging = true | ||
|
|
11 | + config.log_dir = GRADER_ROOT + "/../log" | ||
|
9 |
|
12 | ||
|
10 | config.report_grader = true |
|
13 | config.report_grader = true |
|
11 |
- |
|
14 | + |
|
12 | - config.report_comment = lambda do |comment| |
|
15 | + config.test_request_input_base_dir = RAILS_ROOT + "/data/test_request/input" |
|
13 | - if comment.chomp =~ /^P+$/ # all P's |
|
16 | + config.test_request_output_base_dir = RAILS_ROOT + "/data/test_request/output" |
|
14 | - 'passed' |
|
17 | + config.test_request_problem_templates_dir = config.problems_dir + "/test_request" |
|
15 | - else |
|
18 | + |
|
16 | - 'failed' |
|
19 | + config.comment_report_style = :short |
|
17 | - end |
|
||
|
18 | - end |
|
||
|
19 | - |
|
||
|
20 | end |
|
20 | end |
@@ -1,16 +1,19 | |||||
|
1 | - |
|
1 | + # |
|
2 | - |
|
2 | + # See documentation in lib/configuration.rb |
|
|
3 | + # | ||
|
3 | Grader::Initializer.run do |config| |
|
4 | Grader::Initializer.run do |config| |
|
4 | - |
|
5 | + config.problems_dir = GRADER_ROOT + "/../ev" |
|
5 | - config.problems_dir = "/home/jittat/grader/ev" |
|
6 | + config.user_result_dir = GRADER_ROOT + "/../result" |
|
6 | - config.user_result_dir = "/home/jittat/grader/result" |
|
||
|
7 |
|
7 | ||
|
8 | config.talkative = true |
|
8 | config.talkative = true |
|
|
9 | + config.logging = true | ||
|
|
10 | + config.log_dir = GRADER_ROOT + "/../log" | ||
|
9 |
|
11 | ||
|
10 | config.report_grader = true |
|
12 | config.report_grader = true |
|
11 |
|
13 | ||
|
12 | - config.report_comment = lambda do |comment| |
|
14 | + config.test_request_input_base_dir = RAILS_ROOT + "/data/test_request/input" |
|
13 | - comment.chomp |
|
15 | + config.test_request_output_base_dir = RAILS_ROOT + "/data/test_request/output" |
|
14 | - end |
|
16 | + config.test_request_problem_templates_dir = config.problems_dir + "/test_request" |
|
15 |
- |
|
17 | + |
|
|
18 | + config.comment_report_style = :full | ||
|
16 | end |
|
19 | end |
@@ -1,25 +1,29 | |||||
|
1 | - |
|
1 | + # |
|
2 | - |
|
2 | + # See documentation in lib/configuration.rb |
|
|
3 | + # | ||
|
3 | Grader::Initializer.run do |config| |
|
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" |
|
8 | + config.talkative = false |
|
6 | - config.user_result_dir = "/home/jittat/grader/scripts/test/sandbox/result" |
|
||
|
7 | - |
|
||
|
8 | - config.talkative = true |
|
||
|
9 |
|
9 | ||
|
10 | config.report_grader = false |
|
10 | config.report_grader = false |
|
11 |
|
11 | ||
|
12 | config.rails_env = 'test' |
|
12 | config.rails_env = 'test' |
|
13 |
|
13 | ||
|
14 | - config.report_comment = lambda do |comment| |
|
14 | + config.comment_report_style = :full |
|
15 | - comment.chomp |
|
||
|
16 | - end |
|
||
|
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 | class << config |
|
23 | class << config |
|
19 | attr_accessor :test_data_dir, :test_sandbox_dir |
|
24 | attr_accessor :test_data_dir, :test_sandbox_dir |
|
20 | end |
|
25 | end |
|
21 |
|
26 | ||
|
22 |
- config.test_data_dir = " |
|
27 | + config.test_data_dir = GRADER_ROOT + "/test/data" |
|
23 |
- config.test_sandbox_dir = " |
|
28 | + config.test_sandbox_dir = GRADER_ROOT + "/test/sandbox" |
|
24 | - |
|
||
|
25 | end |
|
29 | end |
@@ -1,11 +1,10 | |||||
|
1 | - |
|
||
|
2 | # Rails app directory |
|
1 | # Rails app directory |
|
3 | RAILS_ROOT = "/home/jittat/web_grader" |
|
2 | RAILS_ROOT = "/home/jittat/web_grader" |
|
4 |
|
3 | ||
|
5 | GRADER_ROOT = "/home/jittat/grader/scripts" |
|
4 | GRADER_ROOT = "/home/jittat/grader/scripts" |
|
6 |
|
5 | ||
|
|
6 | + # This load all required codes | ||
|
7 | require File.join(File.dirname(__FILE__),'../lib/boot') |
|
7 | require File.join(File.dirname(__FILE__),'../lib/boot') |
|
8 |
|
8 | ||
|
9 | # load the required environment file |
|
9 | # load the required environment file |
|
10 | require File.dirname(__FILE__) + "/env_#{GRADER_ENV}.rb" |
|
10 | require File.dirname(__FILE__) + "/env_#{GRADER_ENV}.rb" |
|
11 | - |
|
@@ -13,22 +13,52 | |||||
|
13 | end |
|
13 | end |
|
14 |
|
14 | ||
|
15 | def config |
|
15 | def config |
|
16 | Grader::Configuration.get_instance |
|
16 | Grader::Configuration.get_instance |
|
17 | end |
|
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 | if config.talkative |
|
25 | if config.talkative |
|
21 | puts str |
|
26 | puts str |
|
22 | end |
|
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 | end |
|
47 | end |
|
24 |
|
48 | ||
|
25 | ######################################### |
|
49 | ######################################### |
|
26 | # main program |
|
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 | # reading environment and options |
|
59 | # reading environment and options |
|
30 | if (ARGV.length >= 1) and (ARGV[0]=='stop') |
|
60 | if (ARGV.length >= 1) and (ARGV[0]=='stop') |
|
31 | stop_grader |
|
61 | stop_grader |
|
32 | puts "stop file created" |
|
62 | puts "stop file created" |
|
33 | exit(0) |
|
63 | exit(0) |
|
34 | end |
|
64 | end |
@@ -49,58 +79,85 | |||||
|
49 | GRADER_ENV = 'exam' |
|
79 | GRADER_ENV = 'exam' |
|
50 | end |
|
80 | end |
|
51 |
|
81 | ||
|
52 | puts "environment: #{GRADER_ENV}" |
|
82 | puts "environment: #{GRADER_ENV}" |
|
53 | require File.join(File.dirname(__FILE__),'config/environment') |
|
83 | require File.join(File.dirname(__FILE__),'config/environment') |
|
54 |
|
84 | ||
|
55 | - #reading rails environment |
|
85 | + # add grader_mode to config |
|
56 | - talk 'Reading rails environment' |
|
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 | + | ||
|
|
92 | + # reading rails environment | ||
|
|
93 | + log 'Reading rails environment' | ||
|
57 |
|
94 | ||
|
58 | RAILS_ENV = config.rails_env |
|
95 | RAILS_ENV = config.rails_env |
|
59 | require RAILS_ROOT + '/config/environment' |
|
96 | require RAILS_ROOT + '/config/environment' |
|
60 |
|
97 | ||
|
61 | - |
|
98 | + # register grader process |
|
62 | - #register grader process |
|
||
|
63 | if config.report_grader |
|
99 | if config.report_grader |
|
64 | grader_proc = GraderProcess.register(config.grader_hostname, |
|
100 | grader_proc = GraderProcess.register(config.grader_hostname, |
|
65 | Process.pid, |
|
101 | Process.pid, |
|
66 | grader_mode) |
|
102 | grader_mode) |
|
67 | else |
|
103 | else |
|
68 | grader_proc = nil |
|
104 | grader_proc = nil |
|
69 | end |
|
105 | end |
|
70 |
|
106 | ||
|
71 | - # create judge engine |
|
107 | + #set loggin environment |
|
72 | - engine = Grader::Engine.new(grader_proc) |
|
108 | + ENV['GRADER_LOGGING'] = log_file_name |
|
|
109 | + | ||
|
|
110 | + # | ||
|
|
111 | + # MAIN LOOP | ||
|
|
112 | + # | ||
|
73 |
|
113 | ||
|
74 | case grader_mode |
|
114 | case grader_mode |
|
75 | - when "queue" |
|
115 | + when "queue", "test_request" |
|
76 | - talk 'Grader queue' |
|
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 | while true |
|
125 | while true |
|
78 |
|
126 | ||
|
79 | if check_stopfile # created by calling grader stop |
|
127 | if check_stopfile # created by calling grader stop |
|
80 | clear_stopfile |
|
128 | clear_stopfile |
|
81 | - puts "stopped" |
|
129 | + log "stopped (with stop file)" |
|
82 | break |
|
130 | break |
|
83 | end |
|
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 | if task==nil |
|
138 | if task==nil |
|
87 |
- sleep( |
|
139 | + sleep(1) |
|
88 | end |
|
140 | end |
|
89 | end |
|
141 | end |
|
90 |
|
142 | ||
|
91 | when "prob" |
|
143 | when "prob" |
|
|
144 | + engine = Grader::Engine.new | ||
|
|
145 | + runner = Grader::Runner.new(engine, grader_proc) | ||
|
92 |
|
146 | ||
|
93 | grader_proc.report_active if grader_proc!=nil |
|
147 | grader_proc.report_active if grader_proc!=nil |
|
94 |
|
148 | ||
|
95 | prob = Problem.find_by_name(ARGV[2]) |
|
149 | prob = Problem.find_by_name(ARGV[2]) |
|
96 | if prob==nil |
|
150 | if prob==nil |
|
97 | puts "cannot find problem: #{ARGV[2]}" |
|
151 | puts "cannot find problem: #{ARGV[2]}" |
|
98 | else |
|
152 | else |
|
99 |
- |
|
153 | + runner.grade_problem(prob) |
|
100 | end |
|
154 | end |
|
101 | - |
|
155 | + |
|
|
156 | + else | ||
|
|
157 | + display_manual | ||
|
|
158 | + exit(0) | ||
|
102 | end |
|
159 | end |
|
103 |
|
160 | ||
|
104 | # report inactive |
|
161 | # report inactive |
|
105 | grader_proc.report_inactive if grader_proc!=nil |
|
162 | grader_proc.report_inactive if grader_proc!=nil |
|
106 |
|
163 |
@@ -2,12 +2,13 | |||||
|
2 |
|
2 | ||
|
3 | # import_problem: |
|
3 | # import_problem: |
|
4 | # * creates a directory for a problem in the current directory, |
|
4 | # * creates a directory for a problem in the current directory, |
|
5 | # * copy testdata in the old format and create standard testcase config file |
|
5 | # * copy testdata in the old format and create standard testcase config file |
|
6 |
|
6 | ||
|
7 | require 'erb' |
|
7 | require 'erb' |
|
|
8 | + require 'fileutils' | ||
|
8 |
|
9 | ||
|
9 | def input_filename(dir,i) |
|
10 | def input_filename(dir,i) |
|
10 | "#{dir}/input-#{i}.txt" |
|
11 | "#{dir}/input-#{i}.txt" |
|
11 | end |
|
12 | end |
|
12 |
|
13 | ||
|
13 | def answer_filename(dir,i) |
|
14 | def answer_filename(dir,i) |
@@ -31,21 +32,24 | |||||
|
31 | i += 1 |
|
32 | i += 1 |
|
32 | end |
|
33 | end |
|
33 | i += 1 |
|
34 | i += 1 |
|
34 | end |
|
35 | end |
|
35 | end |
|
36 | end |
|
36 |
|
37 | ||
|
37 |
- |
|
38 | + SCRIPT_DIR = File.dirname(__FILE__) |
|
38 |
|
39 | ||
|
39 | # print usage |
|
40 | # print usage |
|
40 | if ARGV.length < 3 |
|
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 | * creates a directory for a problem in the current directory, |
|
44 | * creates a directory for a problem in the current directory, |
|
43 | * copy testdata in the old format and create standard testcase config file |
|
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 | * options: -t time-limit (in seconds) |
|
47 | * options: -t time-limit (in seconds) |
|
45 |
- -m memory-limit (in megabytes) |
|
48 | + -m memory-limit (in megabytes) |
|
|
49 | + USAGE | ||
|
46 | exit(127) |
|
50 | exit(127) |
|
47 | end |
|
51 | end |
|
48 |
|
52 | ||
|
49 | # processing arguments |
|
53 | # processing arguments |
|
50 | problem = ARGV[0] |
|
54 | problem = ARGV[0] |
|
51 | testcase_dir = ARGV[1] |
|
55 | testcase_dir = ARGV[1] |
@@ -54,12 +58,13 | |||||
|
54 | options = {:time_limit => 1, :mem_limit => 16} |
|
58 | options = {:time_limit => 1, :mem_limit => 16} |
|
55 | process_options(options) |
|
59 | process_options(options) |
|
56 |
|
60 | ||
|
57 | # start working |
|
61 | # start working |
|
58 | puts "creating directories" |
|
62 | puts "creating directories" |
|
59 |
|
63 | ||
|
|
64 | + | ||
|
60 | system("mkdir #{problem}") |
|
65 | system("mkdir #{problem}") |
|
61 | system("mkdir #{problem}/script") |
|
66 | system("mkdir #{problem}/script") |
|
62 | system("mkdir #{problem}/test_cases") |
|
67 | system("mkdir #{problem}/test_cases") |
|
63 | #system("cp #{GRADER_DIR}/std-script/* #{problem}/script") |
|
68 | #system("cp #{GRADER_DIR}/std-script/* #{problem}/script") |
|
64 |
|
69 | ||
|
65 | puts "copying testcases" |
|
70 | puts "copying testcases" |
@@ -70,14 +75,29 | |||||
|
70 | end |
|
75 | end |
|
71 |
|
76 | ||
|
72 |
|
77 | ||
|
73 | # generating all_tests.cfg |
|
78 | # generating all_tests.cfg |
|
74 | puts "generating testcase config file" |
|
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 | all_test_cfg = ERB.new(template) |
|
82 | all_test_cfg = ERB.new(template) |
|
78 |
|
83 | ||
|
79 | cfg_file = File.open("#{problem}/test_cases/all_tests.cfg","w") |
|
84 | cfg_file = File.open("#{problem}/test_cases/all_tests.cfg","w") |
|
80 | cfg_file.puts all_test_cfg.result |
|
85 | cfg_file.puts all_test_cfg.result |
|
81 | cfg_file.close |
|
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 | puts "done" |
|
103 | puts "done" |
@@ -1,5 +1,10 | |||||
|
1 |
|
1 | ||
|
2 | require File.join(File.dirname(__FILE__), 'configuration') |
|
2 | require File.join(File.dirname(__FILE__), 'configuration') |
|
3 | require File.join(File.dirname(__FILE__), 'initializer') |
|
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 | require File.join(File.dirname(__FILE__), 'engine') |
|
8 | require File.join(File.dirname(__FILE__), 'engine') |
|
|
9 | + require File.join(File.dirname(__FILE__), 'runner') | ||
|
5 |
|
10 |
@@ -1,20 +1,63 | |||||
|
1 | - |
|
||
|
2 |
|
|
1 | module Grader |
|
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 | class Configuration |
|
7 | class Configuration |
|
|
8 | + # Rails' environment: "development", "production" | ||
|
|
9 | + attr_accessor :rails_env | ||
|
5 |
|
10 | ||
|
6 | - private_class_method :new |
|
11 | + # Grader looks for problem [prob] in problem_dir/[prob], and store |
|
7 | - |
|
12 | + # execution results for submission [x] of user [u] in directory |
|
|
13 | + # user_result_dir/[u]/[x] | ||
|
8 | attr_accessor :problems_dir |
|
14 | attr_accessor :problems_dir |
|
9 | attr_accessor :user_result_dir |
|
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 | attr_accessor :report_grader |
|
20 | attr_accessor :report_grader |
|
12 | attr_accessor :grader_hostname |
|
21 | attr_accessor :grader_hostname |
|
13 | - attr_accessor :report_comment |
|
22 | + |
|
14 | - attr_accessor :rails_env |
|
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 | @@instance = nil |
|
59 | @@instance = nil |
|
17 |
|
60 | ||
|
18 | def self.get_instance |
|
61 | def self.get_instance |
|
19 | if @@instance==nil |
|
62 | if @@instance==nil |
|
20 | @@instance = new |
|
63 | @@instance = new |
@@ -22,17 +65,18 | |||||
|
22 | @@instance |
|
65 | @@instance |
|
23 | end |
|
66 | end |
|
24 |
|
67 | ||
|
25 | private |
|
68 | private |
|
26 | def initialize |
|
69 | def initialize |
|
27 | @talkative = false |
|
70 | @talkative = false |
|
|
71 | + @log_file = nil | ||
|
28 | @report_grader = false |
|
72 | @report_grader = false |
|
29 | @grader_hostname = `hostname`.chomp |
|
73 | @grader_hostname = `hostname`.chomp |
|
30 |
|
74 | ||
|
31 | @rails_env = 'development' |
|
75 | @rails_env = 'development' |
|
32 |
|
76 | ||
|
33 | - @report_comment = lambda { |comment| comment } |
|
77 | + @comment_report_style = :full |
|
34 | end |
|
78 | end |
|
35 |
|
79 | ||
|
36 | end |
|
80 | end |
|
37 |
|
81 | ||
|
38 | end |
|
82 | end |
@@ -1,151 +1,99 | |||||
|
|
1 | + require 'fileutils' | ||
|
1 |
|
2 | ||
|
2 | module Grader |
|
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 | class Engine |
|
10 | class Engine |
|
|
11 | + | ||
|
|
12 | + attr_writer :room_maker | ||
|
|
13 | + attr_writer :reporter | ||
|
5 |
|
14 | ||
|
6 |
- def initialize( |
|
15 | + def initialize(room_maker=nil, reporter=nil) |
|
7 | @config = Grader::Configuration.get_instance |
|
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 | end |
|
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 | current_dir = `pwd`.chomp |
|
25 | current_dir = `pwd`.chomp |
|
13 |
|
26 | ||
|
14 |
- |
|
27 | + user = submission.user |
|
15 | - user = sub.user |
|
28 | + problem = submission.problem |
|
16 | - problem = sub.problem |
|
||
|
17 |
|
29 | ||
|
18 | # TODO: will have to create real exception for this |
|
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 |
|
36 | + language = submission.language.name |
|
22 | - lang_ext = sub.language.ext |
|
37 | + lang_ext = submission.language.ext |
|
23 | # FIX THIS |
|
38 | # FIX THIS |
|
24 | talk 'some hack on language' |
|
39 | talk 'some hack on language' |
|
25 | if language == 'cpp' |
|
40 | if language == 'cpp' |
|
26 | language = 'c++' |
|
41 | language = 'c++' |
|
27 | end |
|
42 | end |
|
28 |
|
43 | ||
|
29 | - user_dir = "#{@config.user_result_dir}/#{user.login}" |
|
44 | + # COMMENT: should it be only source.ext? |
|
30 | - problem_out_dir = "#{user_dir}/#{problem.name}" |
|
45 | + if problem!=nil |
|
31 | - submission_out_dir = "#{user_dir}/#{problem.name}/#{submission_id}" |
|
46 | + source_name = "#{problem.name}.#{lang_ext}" |
|
32 | - |
|
47 | + else |
|
33 | - mkdir_if_does_not_exist(user_dir) |
|
48 | + source_name = "source.#{lang_ext}" |
|
34 | - mkdir_if_does_not_exist(problem_out_dir) |
|
49 | + end |
|
35 | - mkdir_if_does_not_exist(submission_out_dir) |
|
||
|
36 |
|
50 | ||
|
37 | - problem_home = "#{@config.problems_dir}/#{problem.name}" |
|
51 | + begin |
|
38 | - source_name = "#{problem.name}.#{lang_ext}" |
|
52 | + grading_dir = @room_maker.produce_grading_room(submission) |
|
39 | - |
|
53 | + @room_maker.save_source(submission,source_name) |
|
40 | - save_source(sub,submission_out_dir,source_name) |
|
54 | + problem_home = @room_maker.find_problem_home(submission) |
|
41 | - |
|
55 | + |
|
42 | - copy_log = copy_script(problem_home) |
|
56 | + # puts "GRADING DIR: #{grading_dir}" |
|
43 | - |
|
57 | + # puts "PROBLEM DIR: #{problem_home}" |
|
44 | - call_judge(problem_home,language,submission_out_dir,source_name) |
|
||
|
45 | - save_result(sub,read_result("#{submission_out_dir}/test-result")) |
|
||
|
46 | - |
|
||
|
47 | - clear_script(copy_log,problem_home) |
|
||
|
48 | - |
|
||
|
49 | - Dir.chdir(current_dir) |
|
||
|
50 | - end |
|
||
|
51 |
|
58 | ||
|
52 | - def grade_oldest_task |
|
59 | + copy_log = copy_script(problem_home) |
|
53 | - task = Task.get_inqueue_and_change_status(Task::STATUS_GRADING) |
|
60 | + |
|
54 | - if task!=nil |
|
61 | + call_judge(problem_home,language,grading_dir,source_name) |
|
55 | - @grader_process.report_active(task) if @grader_process!=nil |
|
62 | + |
|
56 | - |
|
63 | + @reporter.report(submission,"#{grading_dir}/test-result") |
|
57 | - submission = Submission.find(task.submission_id) |
|
||
|
58 | - grade(submission) |
|
||
|
59 | - task.status_complete! |
|
||
|
60 | - end |
|
||
|
61 | - return task |
|
||
|
62 | - end |
|
||
|
63 |
|
64 | ||
|
64 | - def grade_problem(problem) |
|
65 | + clear_script(copy_log,problem_home) |
|
65 | - users = User.find(:all) |
|
66 | + |
|
66 | - users.each do |u| |
|
67 | + rescue RuntimeError => msg |
|
67 | - puts "user: #{u.login}" |
|
68 | + @reporter.report_error(submission,"Grading error: #{msg}") |
|
68 | - last_sub = Submission.find(:first, |
|
69 | + |
|
69 | - :conditions => "user_id = #{u.id} and " + |
|
70 | + ensure |
|
70 | - "problem_id = #{prob.id}", |
|
71 | + @room_maker.clean_up(submission) |
|
71 | - :order => 'submitted_at DESC') |
|
72 | + Dir.chdir(current_dir) # this is really important |
|
72 | - if last_sub!=nil |
|
||
|
73 | - grade(last_sub) |
|
||
|
74 | - end |
|
||
|
75 | end |
|
73 | end |
|
76 | end |
|
74 | end |
|
77 | - |
|
75 | + |
|
78 | protected |
|
76 | protected |
|
79 |
|
77 | ||
|
80 | def talk(str) |
|
78 | def talk(str) |
|
81 | if @config.talkative |
|
79 | if @config.talkative |
|
82 | puts str |
|
80 | puts str |
|
83 | end |
|
81 | end |
|
84 | end |
|
82 | end |
|
85 |
|
83 | ||
|
86 | - def save_source(submission,dir,fname) |
|
84 | + def call_judge(problem_home,language,grading_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) |
|
||
|
93 | ENV['PROBLEM_HOME'] = problem_home |
|
85 | ENV['PROBLEM_HOME'] = problem_home |
|
94 |
|
86 | ||
|
95 |
- talk |
|
87 | + talk grading_dir |
|
96 |
- Dir.chdir |
|
88 | + Dir.chdir grading_dir |
|
97 | cmd = "#{problem_home}/script/judge #{language} #{fname}" |
|
89 | cmd = "#{problem_home}/script/judge #{language} #{fname}" |
|
98 | talk "CMD: #{cmd}" |
|
90 | talk "CMD: #{cmd}" |
|
99 | system(cmd) |
|
91 | system(cmd) |
|
100 | end |
|
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 | def get_std_script_dir |
|
94 | def get_std_script_dir |
|
147 | GRADER_ROOT + '/std-script' |
|
95 | GRADER_ROOT + '/std-script' |
|
148 | end |
|
96 | end |
|
149 |
|
97 | ||
|
150 | def copy_script(problem_home) |
|
98 | def copy_script(problem_home) |
|
151 | script_dir = "#{problem_home}/script" |
|
99 | script_dir = "#{problem_home}/script" |
@@ -17,13 +17,13 | |||||
|
17 | '-' |
|
17 | '-' |
|
18 | elsif comment =~ /[Cc]orrect/ |
|
18 | elsif comment =~ /[Cc]orrect/ |
|
19 | 'P' |
|
19 | 'P' |
|
20 | elsif comment =~ /[Tt]ime/ |
|
20 | elsif comment =~ /[Tt]ime/ |
|
21 | 'T' |
|
21 | 'T' |
|
22 | else |
|
22 | else |
|
23 | - '?' |
|
23 | + 'x' # these are run time errors |
|
24 | end |
|
24 | end |
|
25 | end |
|
25 | end |
|
26 |
|
26 | ||
|
27 | problem_home = ENV['PROBLEM_HOME'] |
|
27 | problem_home = ENV['PROBLEM_HOME'] |
|
28 | require "#{problem_home}/script/test_dsl.rb" |
|
28 | require "#{problem_home}/script/test_dsl.rb" |
|
29 | load "#{problem_home}/test_cases/all_tests.cfg" |
|
29 | load "#{problem_home}/test_cases/all_tests.cfg" |
@@ -9,12 +9,24 | |||||
|
9 | fp = File.open(log_fname,"a") |
|
9 | fp = File.open(log_fname,"a") |
|
10 | fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}") |
|
10 | fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}") |
|
11 | fp.close |
|
11 | fp.close |
|
12 | end |
|
12 | end |
|
13 | end |
|
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 | if ARGV.length < 2 || ARGV.length > 3 |
|
27 | if ARGV.length < 2 || ARGV.length > 3 |
|
16 | puts "Usage: run <language> <test-num> [<program-name>]" |
|
28 | puts "Usage: run <language> <test-num> [<program-name>]" |
|
17 | exit(127) |
|
29 | exit(127) |
|
18 | end |
|
30 | end |
|
19 |
|
31 | ||
|
20 | language = ARGV[0] |
|
32 | language = ARGV[0] |
@@ -52,14 +64,23 | |||||
|
52 | time_limit = problem.get_time_limit test_num |
|
64 | time_limit = problem.get_time_limit test_num |
|
53 | mem_limit = problem.get_mem_limit(test_num) * 1024 |
|
65 | mem_limit = problem.get_mem_limit(test_num) * 1024 |
|
54 |
|
66 | ||
|
55 | # Copy the input file. |
|
67 | # Copy the input file. |
|
56 | #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .` |
|
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 | # Run the program. |
|
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 | log "Running test #{test_num}..." |
|
81 | log "Running test #{test_num}..." |
|
61 | log run_command |
|
82 | log run_command |
|
62 | log |
|
83 | log |
|
63 | system(run_command) |
|
84 | system(run_command) |
|
64 |
|
85 | ||
|
65 | # Create the result file. |
|
86 | # Create the result file. |
@@ -68,12 +89,13 | |||||
|
68 |
|
89 | ||
|
69 | # Check if the program actually produced any output. |
|
90 | # Check if the program actually produced any output. |
|
70 | run_result_file = File.new("run_result", "r") |
|
91 | run_result_file = File.new("run_result", "r") |
|
71 | run_result = run_result_file.readlines |
|
92 | run_result = run_result_file.readlines |
|
72 | run_result_file.close |
|
93 | run_result_file.close |
|
73 | time_elapsed = run_result[run_result.length-1] |
|
94 | time_elapsed = run_result[run_result.length-1] |
|
|
95 | + running_time = extract_time(time_elapsed) | ||
|
74 |
|
96 | ||
|
75 | report = lambda{ |status, points, comment| |
|
97 | report = lambda{ |status, points, comment| |
|
76 | result_file.write status.strip |
|
98 | result_file.write status.strip |
|
77 | result_file.write "\n" |
|
99 | result_file.write "\n" |
|
78 | result_file.write points.to_s.strip |
|
100 | result_file.write points.to_s.strip |
|
79 | result_file.write "\n" |
|
101 | result_file.write "\n" |
@@ -96,12 +118,17 | |||||
|
96 |
|
118 | ||
|
97 | if run_result[0][0,2] != "OK" |
|
119 | if run_result[0][0,2] != "OK" |
|
98 | log "There was a runtime error." |
|
120 | log "There was a runtime error." |
|
99 | report.call(run_result[0], 0, "No comment.\n") |
|
121 | report.call(run_result[0], 0, "No comment.\n") |
|
100 | end |
|
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 | # Run 'check' to evaluate the output. |
|
129 | # Run 'check' to evaluate the output. |
|
103 | #puts "There was no runtime error. Proceed to checking the output." |
|
130 | #puts "There was no runtime error. Proceed to checking the output." |
|
104 | check_command = "#{problem_home}/script/check #{language} #{test_num}" |
|
131 | check_command = "#{problem_home}/script/check #{language} #{test_num}" |
|
105 | log "Checking the output..." |
|
132 | log "Checking the output..." |
|
106 | log check_command |
|
133 | log check_command |
|
107 | if not system(check_command) |
|
134 | if not system(check_command) |
@@ -1,7 +1,8 | |||||
|
1 | #include <stdio.h> |
|
1 | #include <stdio.h> |
|
|
2 | + #include <stdlib.h> | ||
|
2 |
|
3 | ||
|
3 | int main() |
|
4 | int main() |
|
4 | { |
|
5 | { |
|
5 | int a,b; |
|
6 | int a,b; |
|
6 |
|
7 | ||
|
7 | int c=0; |
|
8 | int c=0; |
@@ -12,8 +13,9 | |||||
|
12 | for(a=0; a<2; a++) { |
|
13 | for(a=0; a<2; a++) { |
|
13 | while(c<1150000000) { |
|
14 | while(c<1150000000) { |
|
14 | c++; |
|
15 | c++; |
|
15 | b+=c; |
|
16 | b+=c; |
|
16 | } |
|
17 | } |
|
17 | } |
|
18 | } |
|
|
19 | + exit(0); | ||
|
18 | } |
|
20 | } |
|
19 |
|
21 |
@@ -73,20 +73,20 | |||||
|
73 | submission.expects(:save) |
|
73 | submission.expects(:save) |
|
74 |
|
74 | ||
|
75 | @engine.grade(submission) |
|
75 | @engine.grade(submission) |
|
76 | end |
|
76 | end |
|
77 |
|
77 | ||
|
78 | def test_timeout_submission_running_one_and_a_half_second |
|
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 | @user_user1 = stub(:id => 1, :login => 'user1') |
|
80 | @user_user1 = stub(:id => 1, :login => 'user1') |
|
81 |
|
81 | ||
|
82 | submission = create_submission_from_file(1, @user_user1, @problem_test2, |
|
82 | submission = create_submission_from_file(1, @user_user1, @problem_test2, |
|
83 | "test2_1-5sec.c") |
|
83 | "test2_1-5sec.c") |
|
84 |
|
84 | ||
|
85 | submission.expects(:graded_at=) |
|
85 | submission.expects(:graded_at=) |
|
86 | - submission.expects(:points=).with(0) |
|
86 | + submission.expects(:points=).with(10) |
|
87 | submission.expects(:grader_comment=).with do |value| |
|
87 | submission.expects(:grader_comment=).with do |value| |
|
88 | /^FAILED: TP$/.match value |
|
88 | /^FAILED: TP$/.match value |
|
89 | end |
|
89 | end |
|
90 | submission.expects(:compiler_message=).with('') |
|
90 | submission.expects(:compiler_message=).with('') |
|
91 | submission.expects(:save) |
|
91 | submission.expects(:save) |
|
92 |
|
92 |
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