Description:
Combined judge project to the web project.
git-svn-id: http://theory.cpe.ku.ac.th/grader/web/trunk@378 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
r176:2448930b9ab8 - - 93 files changed: 4322 inserted, 0 deleted
@@ -0,0 +1,20 | |||||
|
|
1 | + # | ||
|
|
2 | + # See documentation in lib/configuration.rb | ||
|
|
3 | + # | ||
|
|
4 | + Grader::Initializer.run do |config| | ||
|
|
5 | + | ||
|
|
6 | + config.problems_dir = GRADER_ROOT + "/../ev" | ||
|
|
7 | + config.user_result_dir = GRADER_ROOT + "/../result" | ||
|
|
8 | + | ||
|
|
9 | + config.talkative = true | ||
|
|
10 | + config.logging = true | ||
|
|
11 | + config.log_dir = GRADER_ROOT + "/../log" | ||
|
|
12 | + | ||
|
|
13 | + config.report_grader = true | ||
|
|
14 | + | ||
|
|
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 | ||
|
|
20 | + end |
@@ -0,0 +1,19 | |||||
|
|
1 | + # | ||
|
|
2 | + # See documentation in lib/configuration.rb | ||
|
|
3 | + # | ||
|
|
4 | + Grader::Initializer.run do |config| | ||
|
|
5 | + config.problems_dir = GRADER_ROOT + "/../ev" | ||
|
|
6 | + config.user_result_dir = GRADER_ROOT + "/../result" | ||
|
|
7 | + | ||
|
|
8 | + config.talkative = true | ||
|
|
9 | + config.logging = true | ||
|
|
10 | + config.log_dir = GRADER_ROOT + "/../log" | ||
|
|
11 | + | ||
|
|
12 | + config.report_grader = true | ||
|
|
13 | + | ||
|
|
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 | ||
|
|
19 | + end |
@@ -0,0 +1,29 | |||||
|
|
1 | + # | ||
|
|
2 | + # See documentation in lib/configuration.rb | ||
|
|
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" | ||
|
|
7 | + | ||
|
|
8 | + config.talkative = false | ||
|
|
9 | + | ||
|
|
10 | + config.report_grader = false | ||
|
|
11 | + | ||
|
|
12 | + config.rails_env = 'test' | ||
|
|
13 | + | ||
|
|
14 | + config.comment_report_style = :full | ||
|
|
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 | + # | ||
|
|
23 | + class << config | ||
|
|
24 | + attr_accessor :test_data_dir, :test_sandbox_dir | ||
|
|
25 | + end | ||
|
|
26 | + | ||
|
|
27 | + config.test_data_dir = GRADER_ROOT + "/test/data" | ||
|
|
28 | + config.test_sandbox_dir = GRADER_ROOT + "/test/sandbox" | ||
|
|
29 | + end |
@@ -0,0 +1,10 | |||||
|
|
1 | + # Rails app directory | ||
|
|
2 | + RAILS_ROOT = "/home/jittat/web_grader" | ||
|
|
3 | + | ||
|
|
4 | + GRADER_ROOT = "/home/jittat/grader/scripts" | ||
|
|
5 | + | ||
|
|
6 | + # This load all required codes | ||
|
|
7 | + require File.join(File.dirname(__FILE__),'../lib/boot') | ||
|
|
8 | + | ||
|
|
9 | + # load the required environment file | ||
|
|
10 | + require File.dirname(__FILE__) + "/env_#{GRADER_ENV}.rb" |
@@ -0,0 +1,217 | |||||
|
|
1 | + #!/usr/bin/ruby | ||
|
|
2 | + | ||
|
|
3 | + def stop_grader(id) | ||
|
|
4 | + if id==:all | ||
|
|
5 | + File.open(File.dirname(__FILE__) + "/stop.all",'w').close | ||
|
|
6 | + else | ||
|
|
7 | + File.open(File.dirname(__FILE__) + "/stop.#{id}",'w').close | ||
|
|
8 | + end | ||
|
|
9 | + end | ||
|
|
10 | + | ||
|
|
11 | + def check_stopfile | ||
|
|
12 | + FileTest.exist?(File.dirname(__FILE__) + "/stop.all") or | ||
|
|
13 | + FileTest.exist?(File.dirname(__FILE__) + "/stop.#{Process.pid}") | ||
|
|
14 | + end | ||
|
|
15 | + | ||
|
|
16 | + def clear_stopfile | ||
|
|
17 | + if FileTest.exist?(File.dirname(__FILE__) + "/stop.#{Process.pid}") | ||
|
|
18 | + system("rm " + File.dirname(__FILE__) + "/stop.#{Process.pid}") | ||
|
|
19 | + end | ||
|
|
20 | + end | ||
|
|
21 | + | ||
|
|
22 | + def config | ||
|
|
23 | + Grader::Configuration.get_instance | ||
|
|
24 | + end | ||
|
|
25 | + | ||
|
|
26 | + def log_file_name | ||
|
|
27 | + if !File.exists?(config.log_dir) | ||
|
|
28 | + raise "Log directory does not exist: #{config.log_dir}" | ||
|
|
29 | + end | ||
|
|
30 | + config.log_dir + | ||
|
|
31 | + "/#{GRADER_ENV}_#{config.grader_mode}.#{Process.pid}" | ||
|
|
32 | + end | ||
|
|
33 | + | ||
|
|
34 | + def log(str) | ||
|
|
35 | + if config.talkative | ||
|
|
36 | + puts str | ||
|
|
37 | + end | ||
|
|
38 | + if config.logging | ||
|
|
39 | + fp = File.open(log_file_name,"a") | ||
|
|
40 | + fp.puts("GRADER: #{Time.new.strftime("%H:%M")} #{str}") | ||
|
|
41 | + fp.close | ||
|
|
42 | + end | ||
|
|
43 | + end | ||
|
|
44 | + | ||
|
|
45 | + def display_manual | ||
|
|
46 | + puts <<USAGE | ||
|
|
47 | + Grader. | ||
|
|
48 | + using: (1) grader | ||
|
|
49 | + (2) grader environment [mode] | ||
|
|
50 | + (3) grader stop [all|pids-list] | ||
|
|
51 | + (4) grader --help | ||
|
|
52 | + (1) call grader with environment = 'exam', mode = 'queue' | ||
|
|
53 | + (2) possible modes are: 'queue', 'prob', 'test_request' | ||
|
|
54 | + (3) create stop-file to stop running grader in queue mode | ||
|
|
55 | + (4) You are here. | ||
|
|
56 | + USAGE | ||
|
|
57 | + end | ||
|
|
58 | + | ||
|
|
59 | + ######################################### | ||
|
|
60 | + # main program | ||
|
|
61 | + ######################################### | ||
|
|
62 | + | ||
|
|
63 | + # with --help | ||
|
|
64 | + if (ARGV.length==1) and (/help/.match(ARGV[0])) | ||
|
|
65 | + display_manual | ||
|
|
66 | + exit(0) | ||
|
|
67 | + end | ||
|
|
68 | + | ||
|
|
69 | + # reading environment and options | ||
|
|
70 | + if (ARGV.length >= 1) and (ARGV[0]=='stop') | ||
|
|
71 | + if ARGV.length==1 | ||
|
|
72 | + puts "you should specify pid-list or 'all'" | ||
|
|
73 | + display_manual | ||
|
|
74 | + elsif (ARGV.length==2) and (ARGV[1]=='all') | ||
|
|
75 | + stop_grader(:all) | ||
|
|
76 | + puts "A global stop file ('stop.all') created." | ||
|
|
77 | + puts "You should remove it manually later." | ||
|
|
78 | + else | ||
|
|
79 | + (1..ARGV.length-1).each do |i| | ||
|
|
80 | + stop_grader(ARGV[i]) | ||
|
|
81 | + end | ||
|
|
82 | + puts "stop file(s) created" | ||
|
|
83 | + end | ||
|
|
84 | + exit(0) | ||
|
|
85 | + end | ||
|
|
86 | + | ||
|
|
87 | + if check_stopfile | ||
|
|
88 | + puts "Stop file exists. Terminated." | ||
|
|
89 | + clear_stopfile | ||
|
|
90 | + exit(0) | ||
|
|
91 | + end | ||
|
|
92 | + | ||
|
|
93 | + grader_mode = 'queue' | ||
|
|
94 | + if ARGV.length >= 1 | ||
|
|
95 | + GRADER_ENV = ARGV[0] | ||
|
|
96 | + if ARGV.length >=2 | ||
|
|
97 | + grader_mode = ARGV[1] | ||
|
|
98 | + end | ||
|
|
99 | + else | ||
|
|
100 | + GRADER_ENV = 'exam' | ||
|
|
101 | + end | ||
|
|
102 | + | ||
|
|
103 | + puts "environment: #{GRADER_ENV}" | ||
|
|
104 | + require File.join(File.dirname(__FILE__),'config/environment') | ||
|
|
105 | + | ||
|
|
106 | + # add grader_mode to config | ||
|
|
107 | + # this is needed because method log needs it. TODO: clean this up | ||
|
|
108 | + class << config | ||
|
|
109 | + attr_accessor :grader_mode | ||
|
|
110 | + end | ||
|
|
111 | + config.grader_mode = grader_mode | ||
|
|
112 | + | ||
|
|
113 | + # reading rails environment | ||
|
|
114 | + log 'Reading rails environment' | ||
|
|
115 | + | ||
|
|
116 | + RAILS_ENV = config.rails_env | ||
|
|
117 | + require RAILS_ROOT + '/config/environment' | ||
|
|
118 | + | ||
|
|
119 | + # register grader process | ||
|
|
120 | + if config.report_grader | ||
|
|
121 | + grader_proc = GraderProcess.register(config.grader_hostname, | ||
|
|
122 | + Process.pid, | ||
|
|
123 | + grader_mode) | ||
|
|
124 | + else | ||
|
|
125 | + grader_proc = nil | ||
|
|
126 | + end | ||
|
|
127 | + | ||
|
|
128 | + #set loggin environment | ||
|
|
129 | + ENV['GRADER_LOGGING'] = log_file_name | ||
|
|
130 | + | ||
|
|
131 | + # register exit handler to report inactive, and terminated | ||
|
|
132 | + at_exit do | ||
|
|
133 | + if grader_proc!=nil | ||
|
|
134 | + grader_proc.report_inactive | ||
|
|
135 | + grader_proc.terminate | ||
|
|
136 | + end | ||
|
|
137 | + end | ||
|
|
138 | + | ||
|
|
139 | + # | ||
|
|
140 | + # MAIN LOOP | ||
|
|
141 | + # | ||
|
|
142 | + | ||
|
|
143 | + case grader_mode | ||
|
|
144 | + when "queue", "test_request" | ||
|
|
145 | + log "Grader: #{grader_mode}" | ||
|
|
146 | + if grader_mode=="queue" | ||
|
|
147 | + engine = Grader::Engine.new | ||
|
|
148 | + else | ||
|
|
149 | + engine = Grader::Engine.new(Grader::TestRequestRoomMaker.new, | ||
|
|
150 | + Grader::TestRequestReporter.new) | ||
|
|
151 | + end | ||
|
|
152 | + | ||
|
|
153 | + runner = Grader::Runner.new(engine, grader_proc) | ||
|
|
154 | + while true | ||
|
|
155 | + | ||
|
|
156 | + if check_stopfile # created by calling grader stop | ||
|
|
157 | + clear_stopfile | ||
|
|
158 | + log "stopped (with stop file)" | ||
|
|
159 | + break | ||
|
|
160 | + end | ||
|
|
161 | + | ||
|
|
162 | + if grader_mode=="queue" | ||
|
|
163 | + task = runner.grade_oldest_task | ||
|
|
164 | + else | ||
|
|
165 | + task = runner.grade_oldest_test_request | ||
|
|
166 | + end | ||
|
|
167 | + if task==nil | ||
|
|
168 | + sleep(1) | ||
|
|
169 | + end | ||
|
|
170 | + end | ||
|
|
171 | + | ||
|
|
172 | + when "prob" | ||
|
|
173 | + engine = Grader::Engine.new | ||
|
|
174 | + runner = Grader::Runner.new(engine, grader_proc) | ||
|
|
175 | + | ||
|
|
176 | + grader_proc.report_active if grader_proc!=nil | ||
|
|
177 | + | ||
|
|
178 | + ARGV.shift | ||
|
|
179 | + ARGV.shift | ||
|
|
180 | + | ||
|
|
181 | + ARGV.each do |prob_name| | ||
|
|
182 | + prob = Problem.find_by_name(prob_name) | ||
|
|
183 | + if prob==nil | ||
|
|
184 | + puts "cannot find problem: #{prob_name}" | ||
|
|
185 | + else | ||
|
|
186 | + runner.grade_problem(prob) | ||
|
|
187 | + end | ||
|
|
188 | + end | ||
|
|
189 | + | ||
|
|
190 | + when "sub" | ||
|
|
191 | + engine = Grader::Engine.new | ||
|
|
192 | + runner = Grader::Runner.new(engine, grader_proc) | ||
|
|
193 | + | ||
|
|
194 | + grader_proc.report_active if grader_proc!=nil | ||
|
|
195 | + | ||
|
|
196 | + ARGV.shift | ||
|
|
197 | + ARGV.shift | ||
|
|
198 | + | ||
|
|
199 | + ARGV.each do |sub_id| | ||
|
|
200 | + puts "Grading #{sub_id}" | ||
|
|
201 | + begin | ||
|
|
202 | + submission = Submission.find(sub_id.to_i) | ||
|
|
203 | + rescue ActiveRecord::RecordNotFound | ||
|
|
204 | + puts "Record not found" | ||
|
|
205 | + submission = nil | ||
|
|
206 | + end | ||
|
|
207 | + | ||
|
|
208 | + if submission!=nil | ||
|
|
209 | + runner.grade_submission(submission) | ||
|
|
210 | + end | ||
|
|
211 | + end | ||
|
|
212 | + | ||
|
|
213 | + else | ||
|
|
214 | + display_manual | ||
|
|
215 | + exit(0) | ||
|
|
216 | + end | ||
|
|
217 | + |
@@ -0,0 +1,110 | |||||
|
|
1 | + #!/usr/bin/ruby | ||
|
|
2 | + | ||
|
|
3 | + def talk(str) | ||
|
|
4 | + if TALKATIVE | ||
|
|
5 | + puts str | ||
|
|
6 | + end | ||
|
|
7 | + end | ||
|
|
8 | + | ||
|
|
9 | + def execute(command, error_message="") | ||
|
|
10 | + if not system(command) | ||
|
|
11 | + puts "ERROR: #{error_message}" | ||
|
|
12 | + exit(127) | ||
|
|
13 | + end | ||
|
|
14 | + end | ||
|
|
15 | + | ||
|
|
16 | + def save_source(submission,dir,fname) | ||
|
|
17 | + f = File.open("#{dir}/#{fname}","w") | ||
|
|
18 | + f.write(submission.source) | ||
|
|
19 | + f.close | ||
|
|
20 | + end | ||
|
|
21 | + | ||
|
|
22 | + def call_judge(problem_home,language,problem_out_dir,fname) | ||
|
|
23 | + ENV['PROBLEM_HOME'] = problem_home | ||
|
|
24 | + Dir.chdir problem_out_dir | ||
|
|
25 | + cmd = "#{problem_home}/script/judge #{language} #{fname}" | ||
|
|
26 | + # puts "CMD: #{cmd}" | ||
|
|
27 | + system(cmd) | ||
|
|
28 | + end | ||
|
|
29 | + | ||
|
|
30 | + def read_result(test_result_dir) | ||
|
|
31 | + cmp_msg_fname = "#{test_result_dir}/compiler_message" | ||
|
|
32 | + cmp_msg = File.open(cmp_msg_fname).read | ||
|
|
33 | + | ||
|
|
34 | + result_fname = "#{test_result_dir}/result" | ||
|
|
35 | + comment_fname = "#{test_result_dir}/comment" | ||
|
|
36 | + if FileTest.exist?(result_fname) | ||
|
|
37 | + result = File.open(result_fname).readline.to_i | ||
|
|
38 | + comment = File.open(comment_fname).readline.chomp | ||
|
|
39 | + return {:points => result, | ||
|
|
40 | + :comment => comment, | ||
|
|
41 | + :cmp_msg => cmp_msg} | ||
|
|
42 | + else | ||
|
|
43 | + return {:points => 0, | ||
|
|
44 | + :comment => 'compile error', | ||
|
|
45 | + :cmp_msg => cmp_msg} | ||
|
|
46 | + end | ||
|
|
47 | + end | ||
|
|
48 | + | ||
|
|
49 | + def save_result(submission,result) | ||
|
|
50 | + submission.graded_at = Time.now | ||
|
|
51 | + submission.points = result[:points] | ||
|
|
52 | + submission.grader_comment = report_comment(result[:comment]) | ||
|
|
53 | + submission.compiler_message = result[:cmp_msg] | ||
|
|
54 | + submission.save | ||
|
|
55 | + end | ||
|
|
56 | + | ||
|
|
57 | + def grade(submission_id) | ||
|
|
58 | + sub = Submission.find(submission_id) | ||
|
|
59 | + user = sub.user | ||
|
|
60 | + problem = sub.problem | ||
|
|
61 | + | ||
|
|
62 | + language = sub.language.name | ||
|
|
63 | + lang_ext = sub.language.ext | ||
|
|
64 | + # FIX THIS | ||
|
|
65 | + talk 'some hack on language' | ||
|
|
66 | + if language == 'cpp' | ||
|
|
67 | + language = 'c++' | ||
|
|
68 | + end | ||
|
|
69 | + | ||
|
|
70 | + user_dir = "#{USER_RESULT_DIR}/#{user.login}" | ||
|
|
71 | + Dir.mkdir(user_dir) if !FileTest.exist?(user_dir) | ||
|
|
72 | + | ||
|
|
73 | + problem_out_dir = "#{user_dir}/#{problem.name}" | ||
|
|
74 | + Dir.mkdir(problem_out_dir) if !FileTest.exist?(problem_out_dir) | ||
|
|
75 | + | ||
|
|
76 | + problem_home = "#{PROBLEMS_DIR}/#{problem.name}" | ||
|
|
77 | + source_name = "#{problem.name}.#{lang_ext}" | ||
|
|
78 | + | ||
|
|
79 | + save_source(sub,problem_out_dir,source_name) | ||
|
|
80 | + call_judge(problem_home,language,problem_out_dir,source_name) | ||
|
|
81 | + save_result(sub,read_result("#{problem_out_dir}/test-result")) | ||
|
|
82 | + end | ||
|
|
83 | + | ||
|
|
84 | + def stop_grader | ||
|
|
85 | + File.open(File.dirname(__FILE__) + '/stop','w') | ||
|
|
86 | + end | ||
|
|
87 | + | ||
|
|
88 | + def check_stopfile | ||
|
|
89 | + FileTest.exist?(File.dirname(__FILE__) + '/stop') | ||
|
|
90 | + end | ||
|
|
91 | + | ||
|
|
92 | + def clear_stopfile | ||
|
|
93 | + system("rm " + File.dirname(__FILE__) + '/stop') | ||
|
|
94 | + end | ||
|
|
95 | + | ||
|
|
96 | + # reading environment and options | ||
|
|
97 | + GRADER_ENV = 'exam' | ||
|
|
98 | + puts "environment: #{GRADER_ENV}" | ||
|
|
99 | + require File.dirname(__FILE__) + "/environment.rb" | ||
|
|
100 | + | ||
|
|
101 | + #main program | ||
|
|
102 | + talk 'Reading rails environment' | ||
|
|
103 | + | ||
|
|
104 | + RAILS_ENV = 'development' | ||
|
|
105 | + require RAILS_APP_DIR + '/config/environment' | ||
|
|
106 | + | ||
|
|
107 | + current_dir = `pwd` | ||
|
|
108 | + grade(ARGV[0].to_i) | ||
|
|
109 | + | ||
|
|
110 | + |
@@ -0,0 +1,164 | |||||
|
|
1 | + #!/usr/bin/ruby | ||
|
|
2 | + | ||
|
|
3 | + # import_problem: | ||
|
|
4 | + # * creates a directory for a problem in the current directory, | ||
|
|
5 | + # * copy testdata in the old format and create standard testcase config file | ||
|
|
6 | + | ||
|
|
7 | + require 'erb' | ||
|
|
8 | + require 'fileutils' | ||
|
|
9 | + require File.join(File.dirname(__FILE__),'lib/import_helper') | ||
|
|
10 | + | ||
|
|
11 | + def input_filename(dir,i) | ||
|
|
12 | + "#{dir}/input-#{i}.txt" | ||
|
|
13 | + end | ||
|
|
14 | + | ||
|
|
15 | + def answer_filename(dir,i) | ||
|
|
16 | + "#{dir}/answer-#{i}.txt" | ||
|
|
17 | + end | ||
|
|
18 | + | ||
|
|
19 | + def build_testrun_info_from_dir(num_testruns,importing_test_dir) | ||
|
|
20 | + filenames = Dir["#{importing_test_dir}/*.in"].collect do |filename| | ||
|
|
21 | + File.basename((/(.*)\.in/.match(filename))[1]) | ||
|
|
22 | + end | ||
|
|
23 | + build_testrun_info(num_testruns,filenames) | ||
|
|
24 | + end | ||
|
|
25 | + | ||
|
|
26 | + def copy_testcase(importing_test_dir,fname,dir,i) | ||
|
|
27 | + system("cp #{importing_test_dir}/#{fname}.in #{input_filename(dir,i)}") | ||
|
|
28 | + system("cp #{importing_test_dir}/#{fname}.sol #{answer_filename(dir,i)}") | ||
|
|
29 | + end | ||
|
|
30 | + | ||
|
|
31 | + def process_options(options) | ||
|
|
32 | + i = 4 | ||
|
|
33 | + while i<ARGV.length | ||
|
|
34 | + if ARGV[i]=='-t' | ||
|
|
35 | + options[:time_limit] = ARGV[i+1].to_f if ARGV.length>i+1 | ||
|
|
36 | + i += 1 | ||
|
|
37 | + end | ||
|
|
38 | + if ARGV[i]=='-m' | ||
|
|
39 | + options[:mem_limit] = ARGV[i+1].to_i if ARGV.length>i+1 | ||
|
|
40 | + i += 1 | ||
|
|
41 | + end | ||
|
|
42 | + i += 1 | ||
|
|
43 | + end | ||
|
|
44 | + end | ||
|
|
45 | + | ||
|
|
46 | + SCRIPT_DIR = File.dirname(__FILE__) | ||
|
|
47 | + | ||
|
|
48 | + # print usage | ||
|
|
49 | + if (ARGV.length < 4) or (ARGV[3][0,1]=="-") | ||
|
|
50 | + puts <<USAGE | ||
|
|
51 | + using: import_problem name dir num check [options] | ||
|
|
52 | + where: name = problem_name (put '-' (dash) to use dir name) | ||
|
|
53 | + dir = importing testcase directory | ||
|
|
54 | + num = number of testruns | ||
|
|
55 | + check = check script, which can be | ||
|
|
56 | + 'integer', 'text' (for standard script), | ||
|
|
57 | + path_to_your_script, or | ||
|
|
58 | + 'wrapper:(path_to_your_wrapped_script)' | ||
|
|
59 | + options: -t time-limit (in seconds) | ||
|
|
60 | + -m memory-limit (in megabytes) | ||
|
|
61 | + What it does: | ||
|
|
62 | + * creates a directory for a problem in the current directory, | ||
|
|
63 | + * copies testdata in the old format and create standard testcase config file | ||
|
|
64 | + * copies a check script for grading | ||
|
|
65 | + * creates a test_request template in the current directory + '/test_request' | ||
|
|
66 | + | ||
|
|
67 | + For wrapped checked script see comment in templates/check_wrapper for | ||
|
|
68 | + information. | ||
|
|
69 | + USAGE | ||
|
|
70 | + exit(127) | ||
|
|
71 | + end | ||
|
|
72 | + | ||
|
|
73 | + # processing arguments | ||
|
|
74 | + problem = ARGV[0] | ||
|
|
75 | + testcase_dir = ARGV[1] | ||
|
|
76 | + problem = File.basename(testcase_dir) if problem=="-" | ||
|
|
77 | + num_testruns = ARGV[2].to_i | ||
|
|
78 | + check_script = ARGV[3] | ||
|
|
79 | + options = {:time_limit => 1, :mem_limit => 16} | ||
|
|
80 | + process_options(options) | ||
|
|
81 | + | ||
|
|
82 | + testrun_info = build_testrun_info_from_dir(num_testruns, testcase_dir) | ||
|
|
83 | + | ||
|
|
84 | + # start working | ||
|
|
85 | + puts "creating directories" | ||
|
|
86 | + | ||
|
|
87 | + system("mkdir #{problem}") | ||
|
|
88 | + system("mkdir #{problem}/script") | ||
|
|
89 | + system("mkdir #{problem}/test_cases") | ||
|
|
90 | + #system("cp #{GRADER_DIR}/std-script/* #{problem}/script") | ||
|
|
91 | + | ||
|
|
92 | + puts "copying testcases" | ||
|
|
93 | + | ||
|
|
94 | + tr_num = 0 | ||
|
|
95 | + | ||
|
|
96 | + num_testcases = 0 | ||
|
|
97 | + | ||
|
|
98 | + testrun_info.each do |testrun| | ||
|
|
99 | + tr_num += 1 | ||
|
|
100 | + puts "testrun: #{tr_num}" | ||
|
|
101 | + | ||
|
|
102 | + testrun.each do |testcase_info| | ||
|
|
103 | + testcase_num, testcase_fname = testcase_info | ||
|
|
104 | + | ||
|
|
105 | + puts "copy #{testcase_fname} to #{testcase_num}" | ||
|
|
106 | + | ||
|
|
107 | + system("mkdir #{problem}/test_cases/#{testcase_num}") | ||
|
|
108 | + copy_testcase("#{testcase_dir}",testcase_fname,"#{problem}/test_cases/#{testcase_num}",testcase_num) | ||
|
|
109 | + | ||
|
|
110 | + num_testcases += 1 | ||
|
|
111 | + end | ||
|
|
112 | + end | ||
|
|
113 | + | ||
|
|
114 | + # generating all_tests.cfg | ||
|
|
115 | + puts "generating testcase config file" | ||
|
|
116 | + | ||
|
|
117 | + template = File.open(SCRIPT_DIR + "/templates/all_tests.cfg.erb").read | ||
|
|
118 | + all_test_cfg = ERB.new(template) | ||
|
|
119 | + | ||
|
|
120 | + cfg_file = File.open("#{problem}/test_cases/all_tests.cfg","w") | ||
|
|
121 | + cfg_file.puts all_test_cfg.result | ||
|
|
122 | + cfg_file.close | ||
|
|
123 | + | ||
|
|
124 | + | ||
|
|
125 | + # copy check script | ||
|
|
126 | + if res = /^wrapper:(.*)$/.match(check_script) | ||
|
|
127 | + # wrapper script | ||
|
|
128 | + check_script_fname = res[1] | ||
|
|
129 | + script_name = File.basename(check_script_fname) | ||
|
|
130 | + check_wrapper_template = File.open(SCRIPT_DIR + "/templates/check_wrapper").read | ||
|
|
131 | + check_wrapper = ERB.new(check_wrapper_template) | ||
|
|
132 | + | ||
|
|
133 | + check_file = File.open("#{problem}/script/check","w") | ||
|
|
134 | + check_file.puts check_wrapper.result | ||
|
|
135 | + check_file.close | ||
|
|
136 | + | ||
|
|
137 | + File.chmod(0755,"#{problem}/script/check") | ||
|
|
138 | + | ||
|
|
139 | + system("cp #{check_script_fname} #{problem}/script/#{script_name}") | ||
|
|
140 | + else | ||
|
|
141 | + if File.exists?(SCRIPT_DIR + "/templates/check.#{check_script}") | ||
|
|
142 | + check_script_fname = SCRIPT_DIR + "/templates/check.#{check_script}" | ||
|
|
143 | + else | ||
|
|
144 | + check_script_fname = check_script | ||
|
|
145 | + end | ||
|
|
146 | + system("cp #{check_script_fname} #{problem}/script/check") | ||
|
|
147 | + end | ||
|
|
148 | + | ||
|
|
149 | + # generating test_request directory | ||
|
|
150 | + puts "generating test_request template" | ||
|
|
151 | + FileUtils.mkdir_p("test_request/#{problem}/script") | ||
|
|
152 | + FileUtils.mkdir_p("test_request/#{problem}/test_cases/1") | ||
|
|
153 | + | ||
|
|
154 | + template = File.open(SCRIPT_DIR + "/templates/test_request_all_tests.cfg.erb").read | ||
|
|
155 | + test_request_all_test_cfg = ERB.new(template) | ||
|
|
156 | + | ||
|
|
157 | + cfg_file = File.open("test_request/#{problem}/test_cases/all_tests.cfg","w") | ||
|
|
158 | + cfg_file.puts test_request_all_test_cfg.result | ||
|
|
159 | + cfg_file.close | ||
|
|
160 | + | ||
|
|
161 | + system("cp #{SCRIPT_DIR}/templates/check_empty test_request/#{problem}/script/check") | ||
|
|
162 | + system("cp #{SCRIPT_DIR}/templates/answer-1.txt test_request/#{problem}/test_cases/1") | ||
|
|
163 | + | ||
|
|
164 | + puts "done" |
@@ -0,0 +1,10 | |||||
|
|
1 | + | ||
|
|
2 | + require File.join(File.dirname(__FILE__), 'configuration') | ||
|
|
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 | + | ||
|
|
8 | + require File.join(File.dirname(__FILE__), 'engine') | ||
|
|
9 | + require File.join(File.dirname(__FILE__), 'runner') | ||
|
|
10 | + |
@@ -0,0 +1,84 | |||||
|
|
1 | + module Grader | ||
|
|
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. | ||
|
|
7 | + class Configuration | ||
|
|
8 | + # Rails' environment: "development", "production" | ||
|
|
9 | + attr_accessor :rails_env | ||
|
|
10 | + | ||
|
|
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] | ||
|
|
14 | + attr_accessor :problems_dir | ||
|
|
15 | + attr_accessor :user_result_dir | ||
|
|
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. | ||
|
|
20 | + attr_accessor :report_grader | ||
|
|
21 | + attr_accessor :grader_hostname | ||
|
|
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 | + elsif comment.chomp =~ /[Cc]ompil.*[Ee]rror/ | ||
|
|
48 | + 'compilation error' | ||
|
|
49 | + else | ||
|
|
50 | + 'failed' | ||
|
|
51 | + end | ||
|
|
52 | + | ||
|
|
53 | + when :full | ||
|
|
54 | + comment.chomp | ||
|
|
55 | + end | ||
|
|
56 | + end | ||
|
|
57 | + | ||
|
|
58 | + # Codes for singleton | ||
|
|
59 | + private_class_method :new | ||
|
|
60 | + | ||
|
|
61 | + @@instance = nil | ||
|
|
62 | + | ||
|
|
63 | + def self.get_instance | ||
|
|
64 | + if @@instance==nil | ||
|
|
65 | + @@instance = new | ||
|
|
66 | + end | ||
|
|
67 | + @@instance | ||
|
|
68 | + end | ||
|
|
69 | + | ||
|
|
70 | + private | ||
|
|
71 | + def initialize | ||
|
|
72 | + @talkative = false | ||
|
|
73 | + @log_file = nil | ||
|
|
74 | + @report_grader = false | ||
|
|
75 | + @grader_hostname = `hostname`.chomp | ||
|
|
76 | + | ||
|
|
77 | + @rails_env = 'development' | ||
|
|
78 | + | ||
|
|
79 | + @comment_report_style = :full | ||
|
|
80 | + end | ||
|
|
81 | + | ||
|
|
82 | + end | ||
|
|
83 | + | ||
|
|
84 | + end |
@@ -0,0 +1,138 | |||||
|
|
1 | + require 'fileutils' | ||
|
|
2 | + | ||
|
|
3 | + module Grader | ||
|
|
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 | + # | ||
|
|
10 | + class Engine | ||
|
|
11 | + | ||
|
|
12 | + attr_writer :room_maker | ||
|
|
13 | + attr_writer :reporter | ||
|
|
14 | + | ||
|
|
15 | + def initialize(room_maker=nil, reporter=nil) | ||
|
|
16 | + @config = Grader::Configuration.get_instance | ||
|
|
17 | + | ||
|
|
18 | + @room_maker = room_maker || Grader::SubmissionRoomMaker.new | ||
|
|
19 | + @reporter = reporter || Grader::SubmissionReporter.new | ||
|
|
20 | + end | ||
|
|
21 | + | ||
|
|
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) | ||
|
|
25 | + current_dir = `pwd`.chomp | ||
|
|
26 | + | ||
|
|
27 | + user = submission.user | ||
|
|
28 | + problem = submission.problem | ||
|
|
29 | + | ||
|
|
30 | + # TODO: will have to create real exception for this | ||
|
|
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 | ||
|
|
35 | + | ||
|
|
36 | + # TODO: this is another hack so that output only task can be judged | ||
|
|
37 | + if submission.language!=nil | ||
|
|
38 | + language = submission.language.name | ||
|
|
39 | + lang_ext = submission.language.ext | ||
|
|
40 | + else | ||
|
|
41 | + language = 'c' | ||
|
|
42 | + lang_ext = 'c' | ||
|
|
43 | + end | ||
|
|
44 | + | ||
|
|
45 | + # FIX THIS | ||
|
|
46 | + talk 'some hack on language' | ||
|
|
47 | + if language == 'cpp' | ||
|
|
48 | + language = 'c++' | ||
|
|
49 | + end | ||
|
|
50 | + | ||
|
|
51 | + # COMMENT: should it be only source.ext? | ||
|
|
52 | + if problem!=nil | ||
|
|
53 | + source_name = "#{problem.name}.#{lang_ext}" | ||
|
|
54 | + else | ||
|
|
55 | + source_name = "source.#{lang_ext}" | ||
|
|
56 | + end | ||
|
|
57 | + | ||
|
|
58 | + begin | ||
|
|
59 | + grading_dir = @room_maker.produce_grading_room(submission) | ||
|
|
60 | + @room_maker.save_source(submission,source_name) | ||
|
|
61 | + problem_home = @room_maker.find_problem_home(submission) | ||
|
|
62 | + | ||
|
|
63 | + # puts "GRADING DIR: #{grading_dir}" | ||
|
|
64 | + # puts "PROBLEM DIR: #{problem_home}" | ||
|
|
65 | + | ||
|
|
66 | + copy_log = copy_script(problem_home) | ||
|
|
67 | + | ||
|
|
68 | + call_judge(problem_home,language,grading_dir,source_name) | ||
|
|
69 | + | ||
|
|
70 | + @reporter.report(submission,"#{grading_dir}/test-result") | ||
|
|
71 | + | ||
|
|
72 | + clear_script(copy_log,problem_home) | ||
|
|
73 | + | ||
|
|
74 | + rescue RuntimeError => msg | ||
|
|
75 | + @reporter.report_error(submission,"Grading error: #{msg}") | ||
|
|
76 | + | ||
|
|
77 | + ensure | ||
|
|
78 | + @room_maker.clean_up(submission) | ||
|
|
79 | + Dir.chdir(current_dir) # this is really important | ||
|
|
80 | + end | ||
|
|
81 | + end | ||
|
|
82 | + | ||
|
|
83 | + protected | ||
|
|
84 | + | ||
|
|
85 | + def talk(str) | ||
|
|
86 | + if @config.talkative | ||
|
|
87 | + puts str | ||
|
|
88 | + end | ||
|
|
89 | + end | ||
|
|
90 | + | ||
|
|
91 | + def call_judge(problem_home,language,grading_dir,fname) | ||
|
|
92 | + ENV['PROBLEM_HOME'] = problem_home | ||
|
|
93 | + | ||
|
|
94 | + talk grading_dir | ||
|
|
95 | + Dir.chdir grading_dir | ||
|
|
96 | + cmd = "#{problem_home}/script/judge #{language} #{fname}" | ||
|
|
97 | + talk "CMD: #{cmd}" | ||
|
|
98 | + system(cmd) | ||
|
|
99 | + end | ||
|
|
100 | + | ||
|
|
101 | + def get_std_script_dir | ||
|
|
102 | + GRADER_ROOT + '/std-script' | ||
|
|
103 | + end | ||
|
|
104 | + | ||
|
|
105 | + def copy_script(problem_home) | ||
|
|
106 | + script_dir = "#{problem_home}/script" | ||
|
|
107 | + std_script_dir = get_std_script_dir | ||
|
|
108 | + | ||
|
|
109 | + raise "std-script directory not found" if !FileTest.exist?(std_script_dir) | ||
|
|
110 | + | ||
|
|
111 | + scripts = Dir[std_script_dir + '/*'] | ||
|
|
112 | + | ||
|
|
113 | + copied = [] | ||
|
|
114 | + | ||
|
|
115 | + scripts.each do |s| | ||
|
|
116 | + fname = File.basename(s) | ||
|
|
117 | + if !FileTest.exist?("#{script_dir}/#{fname}") | ||
|
|
118 | + copied << fname | ||
|
|
119 | + system("cp #{s} #{script_dir}") | ||
|
|
120 | + end | ||
|
|
121 | + end | ||
|
|
122 | + | ||
|
|
123 | + return copied | ||
|
|
124 | + end | ||
|
|
125 | + | ||
|
|
126 | + def clear_script(log,problem_home) | ||
|
|
127 | + log.each do |s| | ||
|
|
128 | + system("rm #{problem_home}/script/#{s}") | ||
|
|
129 | + end | ||
|
|
130 | + end | ||
|
|
131 | + | ||
|
|
132 | + def mkdir_if_does_not_exist(dirname) | ||
|
|
133 | + Dir.mkdir(dirname) if !FileTest.exist?(dirname) | ||
|
|
134 | + end | ||
|
|
135 | + | ||
|
|
136 | + end | ||
|
|
137 | + | ||
|
|
138 | + end |
@@ -0,0 +1,28 | |||||
|
|
1 | + | ||
|
|
2 | + def filter_filename_for_testrun(testrun, filename_list) | ||
|
|
3 | + l = [] | ||
|
|
4 | + regex = Regexp.new("^(#{testrun}[a-z]*|#{testrun}-.*)$") | ||
|
|
5 | + filename_list.each do |filename| | ||
|
|
6 | + if regex.match(filename) | ||
|
|
7 | + l << filename | ||
|
|
8 | + end | ||
|
|
9 | + end | ||
|
|
10 | + l | ||
|
|
11 | + end | ||
|
|
12 | + | ||
|
|
13 | + def build_testrun_info(num_testruns, input_filename_list) | ||
|
|
14 | + info = [] | ||
|
|
15 | + num_testcases = 0 | ||
|
|
16 | + num_testruns.times do |i| | ||
|
|
17 | + r = i+1 | ||
|
|
18 | + testrun_info = [] | ||
|
|
19 | + filenames = filter_filename_for_testrun(r, input_filename_list) | ||
|
|
20 | + filenames.each do |fname| | ||
|
|
21 | + num_testcases += 1 | ||
|
|
22 | + testrun_info << [num_testcases,fname] | ||
|
|
23 | + end | ||
|
|
24 | + info << testrun_info | ||
|
|
25 | + end | ||
|
|
26 | + info | ||
|
|
27 | + end | ||
|
|
28 | + |
@@ -0,0 +1,13 | |||||
|
|
1 | + | ||
|
|
2 | + module Grader | ||
|
|
3 | + | ||
|
|
4 | + class Initializer | ||
|
|
5 | + | ||
|
|
6 | + def self.run(&block) | ||
|
|
7 | + config = Grader::Configuration.get_instance | ||
|
|
8 | + yield config | ||
|
|
9 | + end | ||
|
|
10 | + | ||
|
|
11 | + end | ||
|
|
12 | + | ||
|
|
13 | + end |
@@ -0,0 +1,58 | |||||
|
|
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 | + @grader_process.report_inactive(task) if @grader_process!=nil | ||
|
|
23 | + end | ||
|
|
24 | + return task | ||
|
|
25 | + end | ||
|
|
26 | + | ||
|
|
27 | + def grade_problem(problem) | ||
|
|
28 | + users = User.find(:all) | ||
|
|
29 | + users.each do |u| | ||
|
|
30 | + puts "user: #{u.login}" | ||
|
|
31 | + last_sub = Submission.find_last_by_user_and_problem(u.id,problem.id) | ||
|
|
32 | + if last_sub!=nil | ||
|
|
33 | + @engine.grade(last_sub) | ||
|
|
34 | + end | ||
|
|
35 | + end | ||
|
|
36 | + end | ||
|
|
37 | + | ||
|
|
38 | + def grade_submission(submission) | ||
|
|
39 | + puts "Submission: #{submission.id} by #{submission.user.full_name}" | ||
|
|
40 | + @engine.grade(submission) | ||
|
|
41 | + end | ||
|
|
42 | + | ||
|
|
43 | + def grade_oldest_test_request | ||
|
|
44 | + test_request = TestRequest.get_inqueue_and_change_status(Task::STATUS_GRADING) | ||
|
|
45 | + if test_request!=nil | ||
|
|
46 | + @grader_process.report_active(test_request) if @grader_process!=nil | ||
|
|
47 | + | ||
|
|
48 | + @engine.grade(test_request) | ||
|
|
49 | + test_request.status_complete! | ||
|
|
50 | + @grader_process.report_inactive(test_request) if @grader_process!=nil | ||
|
|
51 | + end | ||
|
|
52 | + return test_request | ||
|
|
53 | + end | ||
|
|
54 | + | ||
|
|
55 | + end | ||
|
|
56 | + | ||
|
|
57 | + end | ||
|
|
58 | + |
@@ -0,0 +1,123 | |||||
|
|
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 | + if FileTest.exist?(cmp_msg_fname) | ||
|
|
52 | + cmp_file = File.open(cmp_msg_fname) | ||
|
|
53 | + cmp_msg = cmp_file.read | ||
|
|
54 | + cmp_file.close | ||
|
|
55 | + else | ||
|
|
56 | + cmp_msg = "" | ||
|
|
57 | + end | ||
|
|
58 | + | ||
|
|
59 | + result_fname = "#{test_result_dir}/result" | ||
|
|
60 | + comment_fname = "#{test_result_dir}/comment" | ||
|
|
61 | + if FileTest.exist?(result_fname) | ||
|
|
62 | + comment = "" | ||
|
|
63 | + begin | ||
|
|
64 | + result_file = File.open(result_fname) | ||
|
|
65 | + result = result_file.readline.to_i | ||
|
|
66 | + result_file.close | ||
|
|
67 | + rescue | ||
|
|
68 | + result = 0 | ||
|
|
69 | + comment = "error reading result file." | ||
|
|
70 | + end | ||
|
|
71 | + | ||
|
|
72 | + begin | ||
|
|
73 | + comment_file = File.open(comment_fname) | ||
|
|
74 | + comment += comment_file.readline.chomp | ||
|
|
75 | + comment_file.close | ||
|
|
76 | + rescue | ||
|
|
77 | + comment += "" | ||
|
|
78 | + end | ||
|
|
79 | + | ||
|
|
80 | + return {:points => result, | ||
|
|
81 | + :comment => comment, | ||
|
|
82 | + :cmp_msg => cmp_msg} | ||
|
|
83 | + else | ||
|
|
84 | + if FileTest.exist?("#{test_result_dir}/a.out") | ||
|
|
85 | + return {:points => 0, | ||
|
|
86 | + :comment => 'error during grading', | ||
|
|
87 | + :cmp_msg => cmp_msg} | ||
|
|
88 | + else | ||
|
|
89 | + return {:points => 0, | ||
|
|
90 | + :comment => 'compilation error', | ||
|
|
91 | + :cmp_msg => cmp_msg} | ||
|
|
92 | + end | ||
|
|
93 | + end | ||
|
|
94 | + end | ||
|
|
95 | + | ||
|
|
96 | + def save_result(submission,result) | ||
|
|
97 | + problem = submission.problem | ||
|
|
98 | + submission.graded_at = Time.now.gmtime | ||
|
|
99 | + points = result[:points] | ||
|
|
100 | + submission.points = points | ||
|
|
101 | + comment = @config.report_comment(result[:comment]) | ||
|
|
102 | + | ||
|
|
103 | + # | ||
|
|
104 | + # TODO: FIX THIS MESSAGE | ||
|
|
105 | + # | ||
|
|
106 | + if problem == nil | ||
|
|
107 | + submission.grader_comment = 'PASSED: ' + comment + '(problem is nil)' | ||
|
|
108 | + elsif points == problem.full_score | ||
|
|
109 | + #submission.grader_comment = 'PASSED: ' + comment | ||
|
|
110 | + submission.grader_comment = comment | ||
|
|
111 | + elsif result[:comment].chomp =~ /^[\[\]P]+$/ | ||
|
|
112 | + submission.grader_comment = 'PASSED: ' + comment + '(inconsistent score)' | ||
|
|
113 | + else | ||
|
|
114 | + #submission.grader_comment = 'FAILED: ' + comment | ||
|
|
115 | + submission.grader_comment = comment | ||
|
|
116 | + end | ||
|
|
117 | + submission.compiler_message = result[:cmp_msg] or '' | ||
|
|
118 | + submission.save | ||
|
|
119 | + end | ||
|
|
120 | + | ||
|
|
121 | + end | ||
|
|
122 | + | ||
|
|
123 | + end |
@@ -0,0 +1,242 | |||||
|
|
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 | + | ||
|
|
25 | + # | ||
|
|
26 | + # Also copy additional submitted file to this directory as well. | ||
|
|
27 | + # The program would see this file only if it is copied | ||
|
|
28 | + # to the sandbox directory later. The run script should do it. | ||
|
|
29 | + # | ||
|
|
30 | + if FileTest.exists?("#{test_request.input_file_name}.files") | ||
|
|
31 | + cmd = "cp #{test_request.input_file_name}.files/* #{grading_room}" | ||
|
|
32 | + system(cmd) | ||
|
|
33 | + end | ||
|
|
34 | + | ||
|
|
35 | + grading_room | ||
|
|
36 | + end | ||
|
|
37 | + | ||
|
|
38 | + def find_problem_home(test_request) | ||
|
|
39 | + problem_name = test_request.problem_name | ||
|
|
40 | + | ||
|
|
41 | + template_dir = "#{@config.test_request_problem_templates_dir}/" + problem_name | ||
|
|
42 | + | ||
|
|
43 | + raise "Test Request: error template not found" if !File.exists?(template_dir) | ||
|
|
44 | + | ||
|
|
45 | + problem_home = problem_home_dir(test_request) | ||
|
|
46 | + FileUtils.mkdir_p(problem_home) | ||
|
|
47 | + | ||
|
|
48 | + copy_problem_template(template_dir,problem_home) | ||
|
|
49 | + link_input_file(test_request,problem_home) | ||
|
|
50 | + | ||
|
|
51 | + problem_home | ||
|
|
52 | + end | ||
|
|
53 | + | ||
|
|
54 | + def save_source(test_request,source_name) | ||
|
|
55 | + dir = self.produce_grading_room(test_request) | ||
|
|
56 | + submission = test_request.submission | ||
|
|
57 | + f = File.open("#{dir}/#{source_name}","w") | ||
|
|
58 | + f.write(submission.source) | ||
|
|
59 | + f.close | ||
|
|
60 | + end | ||
|
|
61 | + | ||
|
|
62 | + def clean_up(test_request) | ||
|
|
63 | + problem_home = problem_home_dir(test_request) | ||
|
|
64 | + remove_data_files(problem_home) | ||
|
|
65 | + end | ||
|
|
66 | + | ||
|
|
67 | + protected | ||
|
|
68 | + def grading_room_dir(test_request) | ||
|
|
69 | + problem_name = test_request.problem_name | ||
|
|
70 | + user = test_request.user | ||
|
|
71 | + grading_room = "#{@config.user_result_dir}" + | ||
|
|
72 | + "/#{user.login}/test_request" + | ||
|
|
73 | + "/#{problem_name}/#{test_request.id}" | ||
|
|
74 | + grading_room | ||
|
|
75 | + end | ||
|
|
76 | + | ||
|
|
77 | + def problem_home_dir(test_request) | ||
|
|
78 | + problem_name = test_request.problem_name | ||
|
|
79 | + user = test_request.user | ||
|
|
80 | + "#{@config.user_result_dir}" + | ||
|
|
81 | + "/#{user.login}/test_request/#{problem_name}" | ||
|
|
82 | + end | ||
|
|
83 | + | ||
|
|
84 | + def copy_problem_template(template_dir,problem_home) | ||
|
|
85 | + cmd = "cp -R #{template_dir}/* #{problem_home}" | ||
|
|
86 | + system_and_raise_when_fail(cmd,"Test Request: cannot copy problem template") | ||
|
|
87 | + end | ||
|
|
88 | + | ||
|
|
89 | + def link_input_file(test_request,problem_home) | ||
|
|
90 | + input_fname = "#{test_request.input_file_name}" | ||
|
|
91 | + if !File.exists?(input_fname) | ||
|
|
92 | + raise "Test Request: input file not found." | ||
|
|
93 | + end | ||
|
|
94 | + | ||
|
|
95 | + input_fname_problem_home = "#{problem_home}/test_cases/1/input-1.txt" | ||
|
|
96 | + if File.exists?(input_fname_problem_home) | ||
|
|
97 | + FileUtils.rm([input_fname_problem_home], :force => true) | ||
|
|
98 | + end | ||
|
|
99 | + | ||
|
|
100 | + cmd = "ln -s #{input_fname} #{input_fname_problem_home}" | ||
|
|
101 | + system_and_raise_when_fail(cmd,"Test Request: cannot link input file") | ||
|
|
102 | + end | ||
|
|
103 | + | ||
|
|
104 | + def remove_data_files(problem_home) | ||
|
|
105 | + if File.exists?("#{problem_home}/test_cases/1/input-1.txt") | ||
|
|
106 | + cmd = "rm #{problem_home}/test_cases/1/*" | ||
|
|
107 | + system_and_raise_when_fail(cmd,"Test Request: cannot remove data files") | ||
|
|
108 | + end | ||
|
|
109 | + end | ||
|
|
110 | + | ||
|
|
111 | + def system_and_raise_when_fail(cmd,msg) | ||
|
|
112 | + if !system(cmd) | ||
|
|
113 | + raise msg | ||
|
|
114 | + end | ||
|
|
115 | + end | ||
|
|
116 | + | ||
|
|
117 | + end | ||
|
|
118 | + | ||
|
|
119 | + class TestRequestReporter | ||
|
|
120 | + def initialize | ||
|
|
121 | + @config = Grader::Configuration.get_instance | ||
|
|
122 | + end | ||
|
|
123 | + | ||
|
|
124 | + def report(test_request,test_result_dir) | ||
|
|
125 | + save_result(test_request,read_result(test_result_dir)) | ||
|
|
126 | + end | ||
|
|
127 | + | ||
|
|
128 | + def report_error(test_request, msg) | ||
|
|
129 | + save_result(test_request, {:running_stat => { | ||
|
|
130 | + :msg => "#{msg}", | ||
|
|
131 | + :running_time => nil, | ||
|
|
132 | + :exit_status => "Some error occured. Program did not run", | ||
|
|
133 | + :memory_usage => nil | ||
|
|
134 | + }}) | ||
|
|
135 | + end | ||
|
|
136 | + | ||
|
|
137 | + protected | ||
|
|
138 | + def read_result(test_result_dir) | ||
|
|
139 | + # TODO: | ||
|
|
140 | + cmp_msg_fname = "#{test_result_dir}/compiler_message" | ||
|
|
141 | + cmp_file = File.open(cmp_msg_fname) | ||
|
|
142 | + cmp_msg = cmp_file.read | ||
|
|
143 | + cmp_file.close | ||
|
|
144 | + | ||
|
|
145 | + result_file_name = "#{test_result_dir}/1/result" | ||
|
|
146 | + | ||
|
|
147 | + if File.exists?(result_file_name) | ||
|
|
148 | + output_file_name = "#{test_result_dir}/1/output.txt" | ||
|
|
149 | + results = File.open("#{test_result_dir}/1/result").readlines | ||
|
|
150 | + stat = extract_running_stat(results) | ||
|
|
151 | + | ||
|
|
152 | + return { | ||
|
|
153 | + :output_file_name => output_file_name, | ||
|
|
154 | + :running_stat => stat, | ||
|
|
155 | + :comment => "", | ||
|
|
156 | + :cmp_msg => cmp_msg} | ||
|
|
157 | + else | ||
|
|
158 | + return { | ||
|
|
159 | + :running_stat => nil, | ||
|
|
160 | + :comment => "Compilation error", | ||
|
|
161 | + :cmp_msg => cmp_msg} | ||
|
|
162 | + end | ||
|
|
163 | + end | ||
|
|
164 | + | ||
|
|
165 | + def extract_running_stat(results) | ||
|
|
166 | + running_stat_line = results[-1] | ||
|
|
167 | + | ||
|
|
168 | + # extract exit status line | ||
|
|
169 | + run_stat = "" | ||
|
|
170 | + if !(/[Cc]orrect/.match(results[0])) | ||
|
|
171 | + run_stat = results[0].chomp | ||
|
|
172 | + else | ||
|
|
173 | + run_stat = 'Program exited normally' | ||
|
|
174 | + end | ||
|
|
175 | + | ||
|
|
176 | + # extract running time | ||
|
|
177 | + if res = /r(.*)u(.*)s/.match(running_stat_line) | ||
|
|
178 | + seconds = (res[1].to_f + res[2].to_f) | ||
|
|
179 | + time_stat = "Time used: #{seconds} sec." | ||
|
|
180 | + else | ||
|
|
181 | + seconds = nil | ||
|
|
182 | + time_stat = "Time used: n/a sec." | ||
|
|
183 | + end | ||
|
|
184 | + | ||
|
|
185 | + # extract memory usage | ||
|
|
186 | + if res = /s(.*)m/.match(running_stat_line) | ||
|
|
187 | + memory_used = res[1].to_i | ||
|
|
188 | + else | ||
|
|
189 | + memory_used = -1 | ||
|
|
190 | + end | ||
|
|
191 | + | ||
|
|
192 | + return { | ||
|
|
193 | + :msg => "#{run_stat}\n#{time_stat}", | ||
|
|
194 | + :running_time => seconds, | ||
|
|
195 | + :exit_status => run_stat, | ||
|
|
196 | + :memory_usage => memory_used | ||
|
|
197 | + } | ||
|
|
198 | + end | ||
|
|
199 | + | ||
|
|
200 | + def save_result(test_request,result) | ||
|
|
201 | + if result[:output_file_name]!=nil | ||
|
|
202 | + test_request.output_file_name = link_output_file(test_request, | ||
|
|
203 | + result[:output_file_name]) | ||
|
|
204 | + end | ||
|
|
205 | + test_request.graded_at = Time.now | ||
|
|
206 | + test_request.compiler_message = (result[:cmp_msg] or '') | ||
|
|
207 | + test_request.grader_comment = (result[:comment] or '') | ||
|
|
208 | + if result[:running_stat]!=nil | ||
|
|
209 | + test_request.running_stat = (result[:running_stat][:msg] or '') | ||
|
|
210 | + test_request.running_time = (result[:running_stat][:running_time] or nil) | ||
|
|
211 | + test_request.exit_status = result[:running_stat][:exit_status] | ||
|
|
212 | + test_request.memory_usage = result[:running_stat][:memory_usage] | ||
|
|
213 | + else | ||
|
|
214 | + test_request.running_stat = '' | ||
|
|
215 | + end | ||
|
|
216 | + test_request.save | ||
|
|
217 | + end | ||
|
|
218 | + | ||
|
|
219 | + protected | ||
|
|
220 | + def link_output_file(test_request, fname) | ||
|
|
221 | + target_file_name = random_output_file_name(test_request.user, | ||
|
|
222 | + test_request.problem) | ||
|
|
223 | + FileUtils.mkdir_p(File.dirname(target_file_name)) | ||
|
|
224 | + cmd = "ln -s #{fname} #{target_file_name}" | ||
|
|
225 | + if !system(cmd) | ||
|
|
226 | + raise "TestRequestReporter: cannot move output file" | ||
|
|
227 | + end | ||
|
|
228 | + return target_file_name | ||
|
|
229 | + end | ||
|
|
230 | + | ||
|
|
231 | + def random_output_file_name(user,problem) | ||
|
|
232 | + problem_name = TestRequest.name_of(problem) | ||
|
|
233 | + begin | ||
|
|
234 | + tmpname = "#{@config.test_request_output_base_dir}" + | ||
|
|
235 | + "/#{user.login}/#{problem_name}/#{rand(10000)}" | ||
|
|
236 | + end while File.exists?(tmpname) | ||
|
|
237 | + tmpname | ||
|
|
238 | + end | ||
|
|
239 | + | ||
|
|
240 | + end | ||
|
|
241 | + | ||
|
|
242 | + end |
@@ -0,0 +1,73 | |||||
|
|
1 | + #!/usr/bin/ruby | ||
|
|
2 | + | ||
|
|
3 | + # new_problem: | ||
|
|
4 | + # * creates a directory for a problem in the current directory, | ||
|
|
5 | + # * create standard testcase config file | ||
|
|
6 | + | ||
|
|
7 | + require 'erb' | ||
|
|
8 | + | ||
|
|
9 | + def process_options(options) | ||
|
|
10 | + i = 2 | ||
|
|
11 | + while i<ARGV.length | ||
|
|
12 | + if ARGV[i]=='-t' | ||
|
|
13 | + options[:time_limit] = ARGV[i+1].to_i if ARGV.length>i+1 | ||
|
|
14 | + i += 1 | ||
|
|
15 | + end | ||
|
|
16 | + if ARGV[i]=='-m' | ||
|
|
17 | + options[:mem_limit] = ARGV[i+1].to_i if ARGV.length>i+1 | ||
|
|
18 | + i += 1 | ||
|
|
19 | + end | ||
|
|
20 | + i += 1 | ||
|
|
21 | + end | ||
|
|
22 | + end | ||
|
|
23 | + | ||
|
|
24 | + | ||
|
|
25 | + puts "This script is out of dated, shall be fixed soon" | ||
|
|
26 | + puts "Right now, you can create raw_ev and import" | ||
|
|
27 | + exit(0) | ||
|
|
28 | + | ||
|
|
29 | + GRADER_DIR = File.dirname(__FILE__) | ||
|
|
30 | + | ||
|
|
31 | + # print usage | ||
|
|
32 | + if ARGV.length < 2 | ||
|
|
33 | + puts <<USAGE | ||
|
|
34 | + using: new_problem problem number_of_testcase [options] | ||
|
|
35 | + * creates a directory for a problem in the current directory, | ||
|
|
36 | + * create standard testcase config file | ||
|
|
37 | + * options: -t time-limit (in seconds) | ||
|
|
38 | + -m mem-limit (in MB) | ||
|
|
39 | + USAGE | ||
|
|
40 | + exit(127) | ||
|
|
41 | + end | ||
|
|
42 | + | ||
|
|
43 | + # processing arguments | ||
|
|
44 | + problem = ARGV[0] | ||
|
|
45 | + num_testcases = ARGV[1].to_i | ||
|
|
46 | + | ||
|
|
47 | + options = {:time_limit => 1, :mem_limit => 16} | ||
|
|
48 | + process_options(options) | ||
|
|
49 | + | ||
|
|
50 | + # start working | ||
|
|
51 | + puts "creating directories" | ||
|
|
52 | + | ||
|
|
53 | + system("mkdir #{problem}") | ||
|
|
54 | + system("mkdir #{problem}/script") | ||
|
|
55 | + system("mkdir #{problem}/test_cases") | ||
|
|
56 | + | ||
|
|
57 | + puts "creating testcases directories" | ||
|
|
58 | + | ||
|
|
59 | + 1.upto(num_testcases) do |i| | ||
|
|
60 | + system("mkdir #{problem}/test_cases/#{i}") | ||
|
|
61 | + end | ||
|
|
62 | + | ||
|
|
63 | + # generating all_tests.cfg | ||
|
|
64 | + puts "generating testcase config file" | ||
|
|
65 | + | ||
|
|
66 | + template = File.open(File.dirname(__FILE__) + "/templates/all_tests.cfg.erb").read | ||
|
|
67 | + all_test_cfg = ERB.new(template) | ||
|
|
68 | + | ||
|
|
69 | + cfg_file = File.open("#{problem}/test_cases/all_tests.cfg","w") | ||
|
|
70 | + cfg_file.puts all_test_cfg.result | ||
|
|
71 | + cfg_file.close | ||
|
|
72 | + | ||
|
|
73 | + puts "done" |
This diff has been collapsed as it changes many lines, (684 lines changed) Show them Hide them | |||||
@@ -0,0 +1,684 | |||||
|
|
1 | + /* | ||
|
|
2 | + * A Simple Testing Sandbox | ||
|
|
3 | + * | ||
|
|
4 | + * (c) 2001--2004 Martin Mares <mj@ucw.cz> | ||
|
|
5 | + */ | ||
|
|
6 | + | ||
|
|
7 | + #define _LARGEFILE64_SOURCE | ||
|
|
8 | + //#define _GNU_SOURCE | ||
|
|
9 | + | ||
|
|
10 | + #include <errno.h> | ||
|
|
11 | + #include <stdio.h> | ||
|
|
12 | + #include <fcntl.h> | ||
|
|
13 | + #include <stdlib.h> | ||
|
|
14 | + #include <string.h> | ||
|
|
15 | + #include <stdarg.h> | ||
|
|
16 | + #include <unistd.h> | ||
|
|
17 | + #include <getopt.h> | ||
|
|
18 | + #include <time.h> | ||
|
|
19 | + #include <sys/wait.h> | ||
|
|
20 | + #include <sys/user.h> | ||
|
|
21 | + #include <sys/time.h> | ||
|
|
22 | + #include <sys/ptrace.h> | ||
|
|
23 | + #include <sys/signal.h> | ||
|
|
24 | + #include <sys/sysinfo.h> | ||
|
|
25 | + #include <sys/syscall.h> | ||
|
|
26 | + #include <sys/resource.h> | ||
|
|
27 | + | ||
|
|
28 | + #define NONRET __attribute__((noreturn)) | ||
|
|
29 | + #define UNUSED __attribute__((unused)) | ||
|
|
30 | + | ||
|
|
31 | + static int filter_syscalls; /* 0=off, 1=liberal, 2=totalitarian */ | ||
|
|
32 | + static double timeout; | ||
|
|
33 | + static int pass_environ; | ||
|
|
34 | + static int use_wall_clock; | ||
|
|
35 | + static int file_access; | ||
|
|
36 | + static int verbose; | ||
|
|
37 | + static int memory_limit; | ||
|
|
38 | + static int allow_times; | ||
|
|
39 | + static char *redir_stdin, *redir_stdout; | ||
|
|
40 | + static char *set_cwd; | ||
|
|
41 | + | ||
|
|
42 | + static pid_t box_pid; | ||
|
|
43 | + static int is_ptraced; | ||
|
|
44 | + static volatile int timer_tick; | ||
|
|
45 | + static time_t start_time; | ||
|
|
46 | + static int ticks_per_sec; | ||
|
|
47 | + static int page_size; | ||
|
|
48 | + | ||
|
|
49 | + #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0 | ||
|
|
50 | + /* glibc 2.1 or newer -> has lseek64 */ | ||
|
|
51 | + #define long_seek(f,o,w) lseek64(f,o,w) | ||
|
|
52 | + #else | ||
|
|
53 | + /* Touching clandestine places in glibc */ | ||
|
|
54 | + extern loff_t llseek(int fd, loff_t pos, int whence); | ||
|
|
55 | + #define long_seek(f,o,w) llseek(f,o,w) | ||
|
|
56 | + #endif | ||
|
|
57 | + | ||
|
|
58 | + int max_mem_used = 0; | ||
|
|
59 | + | ||
|
|
60 | + void print_running_stat(double wall_time, | ||
|
|
61 | + double user_time, | ||
|
|
62 | + double system_time, | ||
|
|
63 | + int mem_usage) | ||
|
|
64 | + { | ||
|
|
65 | + fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dm\n", | ||
|
|
66 | + wall_time, user_time, system_time, mem_usage); | ||
|
|
67 | + } | ||
|
|
68 | + | ||
|
|
69 | + static void NONRET | ||
|
|
70 | + box_exit(void) | ||
|
|
71 | + { | ||
|
|
72 | + if (box_pid > 0) { | ||
|
|
73 | + if (is_ptraced) | ||
|
|
74 | + ptrace(PTRACE_KILL, box_pid); | ||
|
|
75 | + kill(-box_pid, SIGKILL); | ||
|
|
76 | + kill(box_pid, SIGKILL); | ||
|
|
77 | + } | ||
|
|
78 | + | ||
|
|
79 | + struct timeval total; | ||
|
|
80 | + int wall; | ||
|
|
81 | + struct rusage rus; | ||
|
|
82 | + int stat; | ||
|
|
83 | + pid_t p; | ||
|
|
84 | + | ||
|
|
85 | + // wait so that we can get information | ||
|
|
86 | + p = wait4(box_pid, &stat, WUNTRACED, &rus); | ||
|
|
87 | + if (p < 0) { | ||
|
|
88 | + fprintf(stderr,"wait4: error\n"); | ||
|
|
89 | + print_running_stat(0,0,0,max_mem_used); | ||
|
|
90 | + } else if (p != box_pid) { | ||
|
|
91 | + fprintf(stderr,"wait4: unknown pid %d exited!\n", p); | ||
|
|
92 | + print_running_stat(0,0,0,max_mem_used); | ||
|
|
93 | + } else { | ||
|
|
94 | + if (!WIFEXITED(stat)) | ||
|
|
95 | + fprintf(stderr,"wait4: unknown status\n"); | ||
|
|
96 | + struct timeval total; | ||
|
|
97 | + int wall; | ||
|
|
98 | + wall = time(NULL) - start_time; | ||
|
|
99 | + timeradd(&rus.ru_utime, &rus.ru_stime, &total); | ||
|
|
100 | + | ||
|
|
101 | + print_running_stat((double)wall, | ||
|
|
102 | + (double) rus.ru_utime.tv_sec + | ||
|
|
103 | + ((double) rus.ru_utime.tv_usec/1000000.0), | ||
|
|
104 | + (double) rus.ru_stime.tv_sec + | ||
|
|
105 | + ((double) rus.ru_stime.tv_usec/1000000.0), | ||
|
|
106 | + max_mem_used); | ||
|
|
107 | + } | ||
|
|
108 | + exit(1); | ||
|
|
109 | + } | ||
|
|
110 | + | ||
|
|
111 | + static void NONRET __attribute__((format(printf,1,2))) | ||
|
|
112 | + die(char *msg, ...) | ||
|
|
113 | + { | ||
|
|
114 | + va_list args; | ||
|
|
115 | + va_start(args, msg); | ||
|
|
116 | + vfprintf(stderr, msg, args); | ||
|
|
117 | + fputc('\n', stderr); | ||
|
|
118 | + box_exit(); | ||
|
|
119 | + } | ||
|
|
120 | + | ||
|
|
121 | + static void __attribute__((format(printf,1,2))) | ||
|
|
122 | + log(char *msg, ...) | ||
|
|
123 | + { | ||
|
|
124 | + va_list args; | ||
|
|
125 | + va_start(args, msg); | ||
|
|
126 | + if (verbose) | ||
|
|
127 | + { | ||
|
|
128 | + vfprintf(stderr, msg, args); | ||
|
|
129 | + fflush(stderr); | ||
|
|
130 | + } | ||
|
|
131 | + va_end(args); | ||
|
|
132 | + } | ||
|
|
133 | + | ||
|
|
134 | + static void | ||
|
|
135 | + valid_filename(unsigned long addr) | ||
|
|
136 | + { | ||
|
|
137 | + char namebuf[4096], *p, *end; | ||
|
|
138 | + static int mem_fd; | ||
|
|
139 | + | ||
|
|
140 | + if (!file_access) | ||
|
|
141 | + die("File access forbidden."); | ||
|
|
142 | + if (file_access >= 9) | ||
|
|
143 | + return; | ||
|
|
144 | + | ||
|
|
145 | + if (!mem_fd) | ||
|
|
146 | + { | ||
|
|
147 | + sprintf(namebuf, "/proc/%d/mem", (int) box_pid); | ||
|
|
148 | + mem_fd = open(namebuf, O_RDONLY); | ||
|
|
149 | + if (mem_fd < 0) | ||
|
|
150 | + die("open(%s): %m", namebuf); | ||
|
|
151 | + } | ||
|
|
152 | + p = end = namebuf; | ||
|
|
153 | + do | ||
|
|
154 | + { | ||
|
|
155 | + if (p >= end) | ||
|
|
156 | + { | ||
|
|
157 | + int remains = PAGE_SIZE - (addr & (PAGE_SIZE-1)); | ||
|
|
158 | + int l = namebuf + sizeof(namebuf) - end; | ||
|
|
159 | + if (l > remains) | ||
|
|
160 | + l = remains; | ||
|
|
161 | + if (!l) | ||
|
|
162 | + die("Access to file with name too long."); | ||
|
|
163 | + if (long_seek(mem_fd, addr, SEEK_SET) < 0) | ||
|
|
164 | + die("long_seek(mem): %m"); | ||
|
|
165 | + remains = read(mem_fd, end, l); | ||
|
|
166 | + if (remains < 0) | ||
|
|
167 | + die("read(mem): %m"); | ||
|
|
168 | + if (!remains) | ||
|
|
169 | + die("Access to file with name out of memory."); | ||
|
|
170 | + end += l; | ||
|
|
171 | + addr += l; | ||
|
|
172 | + } | ||
|
|
173 | + } | ||
|
|
174 | + while (*p++); | ||
|
|
175 | + | ||
|
|
176 | + log("[%s] ", namebuf); | ||
|
|
177 | + if (file_access >= 3) | ||
|
|
178 | + return; | ||
|
|
179 | + if (!strchr(namebuf, '/') && strcmp(namebuf, "..")) | ||
|
|
180 | + return; | ||
|
|
181 | + if (file_access >= 2) | ||
|
|
182 | + { | ||
|
|
183 | + if ((!strncmp(namebuf, "/etc/", 5) || | ||
|
|
184 | + !strncmp(namebuf, "/lib/", 5) || | ||
|
|
185 | + !strncmp(namebuf, "/usr/lib/", 9)) | ||
|
|
186 | + && !strstr(namebuf, "..")) | ||
|
|
187 | + return; | ||
|
|
188 | + if (!strcmp(namebuf, "/dev/null") || | ||
|
|
189 | + !strcmp(namebuf, "/dev/zero") || | ||
|
|
190 | + !strcmp(namebuf, "/proc/meminfo") || | ||
|
|
191 | + !strcmp(namebuf, "/proc/self/stat") || | ||
|
|
192 | + !strncmp(namebuf, "/usr/share/zoneinfo/", 20)) | ||
|
|
193 | + return; | ||
|
|
194 | + } | ||
|
|
195 | + die("Forbidden access to file `%s'.", namebuf); | ||
|
|
196 | + } | ||
|
|
197 | + | ||
|
|
198 | + static int | ||
|
|
199 | + valid_syscall(struct user *u) | ||
|
|
200 | + { | ||
|
|
201 | + switch (u->regs.orig_eax) | ||
|
|
202 | + { | ||
|
|
203 | + case __NR_execve: | ||
|
|
204 | + { | ||
|
|
205 | + static int exec_counter; | ||
|
|
206 | + return !exec_counter++; | ||
|
|
207 | + } | ||
|
|
208 | + case __NR_open: | ||
|
|
209 | + case __NR_creat: | ||
|
|
210 | + case __NR_unlink: | ||
|
|
211 | + case __NR_oldstat: | ||
|
|
212 | + case __NR_access: | ||
|
|
213 | + case __NR_oldlstat: | ||
|
|
214 | + case __NR_truncate: | ||
|
|
215 | + case __NR_stat: | ||
|
|
216 | + case __NR_lstat: | ||
|
|
217 | + case __NR_truncate64: | ||
|
|
218 | + case __NR_stat64: | ||
|
|
219 | + case __NR_lstat64: | ||
|
|
220 | + valid_filename(u->regs.ebx); | ||
|
|
221 | + return 1; | ||
|
|
222 | + case __NR_exit: | ||
|
|
223 | + case __NR_read: | ||
|
|
224 | + case __NR_write: | ||
|
|
225 | + case __NR_close: | ||
|
|
226 | + case __NR_lseek: | ||
|
|
227 | + case __NR_getpid: | ||
|
|
228 | + case __NR_getuid: | ||
|
|
229 | + case __NR_oldfstat: | ||
|
|
230 | + case __NR_dup: | ||
|
|
231 | + case __NR_brk: | ||
|
|
232 | + case __NR_getgid: | ||
|
|
233 | + case __NR_geteuid: | ||
|
|
234 | + case __NR_getegid: | ||
|
|
235 | + case __NR_dup2: | ||
|
|
236 | + case __NR_ftruncate: | ||
|
|
237 | + case __NR_fstat: | ||
|
|
238 | + case __NR_personality: | ||
|
|
239 | + case __NR__llseek: | ||
|
|
240 | + case __NR_readv: | ||
|
|
241 | + case __NR_writev: | ||
|
|
242 | + case __NR_getresuid: | ||
|
|
243 | + #ifdef __NR_pread64 | ||
|
|
244 | + case __NR_pread64: | ||
|
|
245 | + case __NR_pwrite64: | ||
|
|
246 | + #else | ||
|
|
247 | + case __NR_pread: | ||
|
|
248 | + case __NR_pwrite: | ||
|
|
249 | + #endif | ||
|
|
250 | + case __NR_ftruncate64: | ||
|
|
251 | + case __NR_fstat64: | ||
|
|
252 | + case __NR_fcntl: | ||
|
|
253 | + case __NR_fcntl64: | ||
|
|
254 | + case __NR_mmap: | ||
|
|
255 | + case __NR_munmap: | ||
|
|
256 | + case __NR_ioctl: | ||
|
|
257 | + case __NR_uname: | ||
|
|
258 | + case 252: | ||
|
|
259 | + case 243: | ||
|
|
260 | + // added for free pascal | ||
|
|
261 | + case __NR_ugetrlimit: | ||
|
|
262 | + case __NR_readlink: | ||
|
|
263 | + return 1; | ||
|
|
264 | + // case __NR_time: | ||
|
|
265 | + case __NR_alarm: | ||
|
|
266 | + // case __NR_pause: | ||
|
|
267 | + case __NR_signal: | ||
|
|
268 | + case __NR_fchmod: | ||
|
|
269 | + case __NR_sigaction: | ||
|
|
270 | + case __NR_sgetmask: | ||
|
|
271 | + case __NR_ssetmask: | ||
|
|
272 | + case __NR_sigsuspend: | ||
|
|
273 | + case __NR_sigpending: | ||
|
|
274 | + case __NR_getrlimit: | ||
|
|
275 | + case __NR_getrusage: | ||
|
|
276 | + case __NR_gettimeofday: | ||
|
|
277 | + case __NR_select: | ||
|
|
278 | + case __NR_readdir: | ||
|
|
279 | + case __NR_setitimer: | ||
|
|
280 | + case __NR_getitimer: | ||
|
|
281 | + case __NR_sigreturn: | ||
|
|
282 | + case __NR_mprotect: | ||
|
|
283 | + case __NR_sigprocmask: | ||
|
|
284 | + case __NR_getdents: | ||
|
|
285 | + case __NR_getdents64: | ||
|
|
286 | + case __NR__newselect: | ||
|
|
287 | + case __NR_fdatasync: | ||
|
|
288 | + case __NR_mremap: | ||
|
|
289 | + case __NR_poll: | ||
|
|
290 | + case __NR_getcwd: | ||
|
|
291 | + case __NR_nanosleep: | ||
|
|
292 | + case __NR_rt_sigreturn: | ||
|
|
293 | + case __NR_rt_sigaction: | ||
|
|
294 | + case __NR_rt_sigprocmask: | ||
|
|
295 | + case __NR_rt_sigpending: | ||
|
|
296 | + case __NR_rt_sigtimedwait: | ||
|
|
297 | + case __NR_rt_sigqueueinfo: | ||
|
|
298 | + case __NR_rt_sigsuspend: | ||
|
|
299 | + case __NR_mmap2: | ||
|
|
300 | + case __NR__sysctl: | ||
|
|
301 | + return (filter_syscalls == 1); | ||
|
|
302 | + case __NR_times: | ||
|
|
303 | + case __NR_time: | ||
|
|
304 | + return allow_times; | ||
|
|
305 | + case __NR_kill: | ||
|
|
306 | + if (u->regs.ebx == box_pid) | ||
|
|
307 | + die("Commited suicide by signal %d.", (int)u->regs.ecx); | ||
|
|
308 | + return 0; | ||
|
|
309 | + default: | ||
|
|
310 | + return 0; | ||
|
|
311 | + } | ||
|
|
312 | + } | ||
|
|
313 | + | ||
|
|
314 | + static void | ||
|
|
315 | + signal_alarm(int unused UNUSED) | ||
|
|
316 | + { | ||
|
|
317 | + /* Time limit checks are synchronous, so we only schedule them there. */ | ||
|
|
318 | + timer_tick = 1; | ||
|
|
319 | + | ||
|
|
320 | + //NOTE: do not use alarm, changed to setitimer for precision | ||
|
|
321 | + // alarm(1); | ||
|
|
322 | + } | ||
|
|
323 | + | ||
|
|
324 | + static void | ||
|
|
325 | + signal_int(int unused UNUSED) | ||
|
|
326 | + { | ||
|
|
327 | + /* Interrupts are fatal, so no synchronization requirements. */ | ||
|
|
328 | + die("Interrupted."); | ||
|
|
329 | + } | ||
|
|
330 | + | ||
|
|
331 | + static void | ||
|
|
332 | + check_timeout(void) | ||
|
|
333 | + { | ||
|
|
334 | + double sec; | ||
|
|
335 | + | ||
|
|
336 | + if (use_wall_clock) | ||
|
|
337 | + sec = (double)(time(NULL) - start_time); | ||
|
|
338 | + else | ||
|
|
339 | + { | ||
|
|
340 | + char buf[4096], *x; | ||
|
|
341 | + int c, utime, stime; | ||
|
|
342 | + static int proc_status_fd; | ||
|
|
343 | + if (!proc_status_fd) | ||
|
|
344 | + { | ||
|
|
345 | + sprintf(buf, "/proc/%d/stat", (int) box_pid); | ||
|
|
346 | + proc_status_fd = open(buf, O_RDONLY); | ||
|
|
347 | + if (proc_status_fd < 0) | ||
|
|
348 | + die("open(%s): %m", buf); | ||
|
|
349 | + } | ||
|
|
350 | + lseek(proc_status_fd, 0, SEEK_SET); | ||
|
|
351 | + if ((c = read(proc_status_fd, buf, sizeof(buf)-1)) < 0) | ||
|
|
352 | + die("read on /proc/$pid/stat: %m"); | ||
|
|
353 | + if (c >= (int) sizeof(buf) - 1) | ||
|
|
354 | + die("/proc/$pid/stat too long"); | ||
|
|
355 | + buf[c] = 0; | ||
|
|
356 | + x = buf; | ||
|
|
357 | + while (*x && *x != ' ') | ||
|
|
358 | + x++; | ||
|
|
359 | + while (*x == ' ') | ||
|
|
360 | + x++; | ||
|
|
361 | + if (*x++ != '(') | ||
|
|
362 | + die("proc syntax error 1"); | ||
|
|
363 | + while (*x && (*x != ')' || x[1] != ' ')) | ||
|
|
364 | + x++; | ||
|
|
365 | + while (*x == ')' || *x == ' ') | ||
|
|
366 | + x++; | ||
|
|
367 | + if (sscanf(x, "%*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d", &utime, &stime) != 2) | ||
|
|
368 | + die("proc syntax error 2"); | ||
|
|
369 | + //printf("%s - %d\n",x,ticks_per_sec); | ||
|
|
370 | + sec = ((double)(utime + stime))/(double)ticks_per_sec; | ||
|
|
371 | + } | ||
|
|
372 | + if (verbose > 1) | ||
|
|
373 | + fprintf(stderr, "[timecheck: %d seconds]\n", sec); | ||
|
|
374 | + if (sec > timeout) { | ||
|
|
375 | + die("Time limit exceeded.",sec,timeout); | ||
|
|
376 | + } | ||
|
|
377 | + } | ||
|
|
378 | + | ||
|
|
379 | + static void | ||
|
|
380 | + check_memory_usage() | ||
|
|
381 | + { | ||
|
|
382 | + char proc_fname[100]; | ||
|
|
383 | + sprintf(proc_fname,"/proc/%d/statm",box_pid); | ||
|
|
384 | + //printf("proc fname: %s\n",proc_fname); | ||
|
|
385 | + FILE *fp = fopen(proc_fname,"r"); | ||
|
|
386 | + if(fp!=NULL) { | ||
|
|
387 | + char line[1000]; | ||
|
|
388 | + fgets(line,999,fp); | ||
|
|
389 | + //printf("%s\n",line); | ||
|
|
390 | + int m; | ||
|
|
391 | + | ||
|
|
392 | + if(sscanf(line,"%d",&m)==1) { | ||
|
|
393 | + m = (m*page_size+1023)/1024; | ||
|
|
394 | + if(m>max_mem_used) | ||
|
|
395 | + max_mem_used = m; | ||
|
|
396 | + } | ||
|
|
397 | + | ||
|
|
398 | + fclose(fp); | ||
|
|
399 | + } | ||
|
|
400 | + } | ||
|
|
401 | + | ||
|
|
402 | + static void | ||
|
|
403 | + boxkeeper(void) | ||
|
|
404 | + { | ||
|
|
405 | + int syscall_count = 0; | ||
|
|
406 | + struct sigaction sa; | ||
|
|
407 | + | ||
|
|
408 | + is_ptraced = 1; | ||
|
|
409 | + bzero(&sa, sizeof(sa)); | ||
|
|
410 | + sa.sa_handler = signal_int; | ||
|
|
411 | + sigaction(SIGINT, &sa, NULL); | ||
|
|
412 | + start_time = time(NULL); | ||
|
|
413 | + ticks_per_sec = sysconf(_SC_CLK_TCK); | ||
|
|
414 | + page_size = getpagesize(); | ||
|
|
415 | + if (ticks_per_sec <= 0) | ||
|
|
416 | + die("Invalid ticks_per_sec!"); | ||
|
|
417 | + | ||
|
|
418 | + check_memory_usage(); | ||
|
|
419 | + | ||
|
|
420 | + sa.sa_handler = signal_alarm; | ||
|
|
421 | + sigaction(SIGALRM, &sa, NULL); | ||
|
|
422 | + //alarm(1); | ||
|
|
423 | + | ||
|
|
424 | + struct itimerval val; | ||
|
|
425 | + val.it_interval.tv_sec = 0; | ||
|
|
426 | + val.it_interval.tv_usec = 50000; | ||
|
|
427 | + val.it_value.tv_sec = 0; | ||
|
|
428 | + val.it_value.tv_usec = 50000; | ||
|
|
429 | + setitimer(ITIMER_REAL,&val,NULL); | ||
|
|
430 | + | ||
|
|
431 | + /* | ||
|
|
432 | + --- add alarm handler no matter what.. | ||
|
|
433 | + if (timeout) | ||
|
|
434 | + { | ||
|
|
435 | + sa.sa_handler = signal_alarm; | ||
|
|
436 | + sigaction(SIGALRM, &sa, NULL); | ||
|
|
437 | + alarm(1); | ||
|
|
438 | + } | ||
|
|
439 | + */ | ||
|
|
440 | + | ||
|
|
441 | + for(;;) | ||
|
|
442 | + { | ||
|
|
443 | + struct rusage rus; | ||
|
|
444 | + int stat; | ||
|
|
445 | + pid_t p; | ||
|
|
446 | + | ||
|
|
447 | + if (timer_tick) | ||
|
|
448 | + { | ||
|
|
449 | + check_timeout(); | ||
|
|
450 | + check_memory_usage(); | ||
|
|
451 | + timer_tick = 0; | ||
|
|
452 | + } | ||
|
|
453 | + p = wait4(box_pid, &stat, WUNTRACED, &rus); | ||
|
|
454 | + | ||
|
|
455 | + if (p < 0) | ||
|
|
456 | + { | ||
|
|
457 | + if (errno == EINTR) | ||
|
|
458 | + continue; | ||
|
|
459 | + die("wait4: %m"); | ||
|
|
460 | + } | ||
|
|
461 | + if (p != box_pid) | ||
|
|
462 | + die("wait4: unknown pid %d exited!", p); | ||
|
|
463 | + if (WIFEXITED(stat)) | ||
|
|
464 | + { | ||
|
|
465 | + struct timeval total; | ||
|
|
466 | + int wall; | ||
|
|
467 | + wall = time(NULL) - start_time; | ||
|
|
468 | + timeradd(&rus.ru_utime, &rus.ru_stime, &total); | ||
|
|
469 | + | ||
|
|
470 | + box_pid = 0; | ||
|
|
471 | + if (WEXITSTATUS(stat)) | ||
|
|
472 | + fprintf(stderr,"Exited with error status %d.\n", WEXITSTATUS(stat)); | ||
|
|
473 | + else if ((use_wall_clock ? | ||
|
|
474 | + wall : | ||
|
|
475 | + (double) total.tv_sec + | ||
|
|
476 | + ((double) total.tv_usec/1000000.0)) > timeout) | ||
|
|
477 | + fprintf(stderr,"Time limit exceeded.\n"); | ||
|
|
478 | + else | ||
|
|
479 | + // report OK and statistics | ||
|
|
480 | + fprintf(stderr,"OK\n"); | ||
|
|
481 | + | ||
|
|
482 | + print_running_stat((double) wall, | ||
|
|
483 | + (double) rus.ru_utime.tv_sec + | ||
|
|
484 | + ((double) rus.ru_utime.tv_usec/1000000.0), | ||
|
|
485 | + (double) rus.ru_stime.tv_sec + | ||
|
|
486 | + ((double) rus.ru_stime.tv_usec/1000000.0), | ||
|
|
487 | + max_mem_used); | ||
|
|
488 | + /* | ||
|
|
489 | + (%.4lf sec real (%d), %d sec wall, %d syscalls, %d kb)\n", | ||
|
|
490 | + (double) total.tv_sec + ((double)total.tv_usec / 1000000.0), | ||
|
|
491 | + (int) total.tv_usec, | ||
|
|
492 | + wall, | ||
|
|
493 | + syscall_count, | ||
|
|
494 | + max_mem_used); | ||
|
|
495 | + */ | ||
|
|
496 | + exit(0); | ||
|
|
497 | + } | ||
|
|
498 | + if (WIFSIGNALED(stat)) | ||
|
|
499 | + { | ||
|
|
500 | + box_pid = 0; | ||
|
|
501 | + fprintf(stderr,"Caught fatal signal %d.\n", WTERMSIG(stat)); | ||
|
|
502 | + | ||
|
|
503 | + struct timeval total; | ||
|
|
504 | + int wall; | ||
|
|
505 | + wall = time(NULL) - start_time; | ||
|
|
506 | + timeradd(&rus.ru_utime, &rus.ru_stime, &total); | ||
|
|
507 | + print_running_stat((double) wall, | ||
|
|
508 | + (double) rus.ru_utime.tv_sec + | ||
|
|
509 | + ((double) rus.ru_utime.tv_usec/1000000.0), | ||
|
|
510 | + (double) rus.ru_stime.tv_sec + | ||
|
|
511 | + ((double) rus.ru_stime.tv_usec/1000000.0), | ||
|
|
512 | + max_mem_used); | ||
|
|
513 | + exit(0); | ||
|
|
514 | + } | ||
|
|
515 | + if (WIFSTOPPED(stat)) | ||
|
|
516 | + { | ||
|
|
517 | + int sig = WSTOPSIG(stat); | ||
|
|
518 | + if (sig == SIGTRAP) | ||
|
|
519 | + { | ||
|
|
520 | + struct user u; | ||
|
|
521 | + static int stop_count = -1; | ||
|
|
522 | + if (ptrace(PTRACE_GETREGS, box_pid, NULL, &u) < 0) | ||
|
|
523 | + die("ptrace(PTRACE_GETREGS): %m"); | ||
|
|
524 | + stop_count++; | ||
|
|
525 | + if (!stop_count) /* Traceme request */ | ||
|
|
526 | + log(">> Traceme request caught\n"); | ||
|
|
527 | + else if (stop_count & 1) /* Syscall entry */ | ||
|
|
528 | + { | ||
|
|
529 | + log(">> Syscall %3ld (%08lx,%08lx,%08lx) ", u.regs.orig_eax, u.regs.ebx, u.regs.ecx, u.regs.edx); | ||
|
|
530 | + syscall_count++; | ||
|
|
531 | + if (!valid_syscall(&u)) | ||
|
|
532 | + { | ||
|
|
533 | + /* | ||
|
|
534 | + * Unfortunately, PTRACE_KILL kills _after_ the syscall completes, | ||
|
|
535 | + * so we have to change it to something harmless (e.g., an undefined | ||
|
|
536 | + * syscall) and make the program continue. | ||
|
|
537 | + */ | ||
|
|
538 | + unsigned int sys = u.regs.orig_eax; | ||
|
|
539 | + u.regs.orig_eax = 0xffffffff; | ||
|
|
540 | + if (ptrace(PTRACE_SETREGS, box_pid, NULL, &u) < 0) | ||
|
|
541 | + die("ptrace(PTRACE_SETREGS): %m"); | ||
|
|
542 | + die("Forbidden syscall %d.", sys); | ||
|
|
543 | + } | ||
|
|
544 | + } | ||
|
|
545 | + else /* Syscall return */ | ||
|
|
546 | + log("= %ld\n", u.regs.eax); | ||
|
|
547 | + ptrace(PTRACE_SYSCALL, box_pid, 0, 0); | ||
|
|
548 | + } | ||
|
|
549 | + else if (sig != SIGSTOP && sig != SIGXCPU && sig != SIGXFSZ) | ||
|
|
550 | + { | ||
|
|
551 | + log(">> Signal %d\n", sig); | ||
|
|
552 | + ptrace(PTRACE_SYSCALL, box_pid, 0, sig); | ||
|
|
553 | + } | ||
|
|
554 | + else | ||
|
|
555 | + die("Received signal %d.", sig); | ||
|
|
556 | + } | ||
|
|
557 | + else | ||
|
|
558 | + die("wait4: unknown status %x, giving up!", stat); | ||
|
|
559 | + } | ||
|
|
560 | + } | ||
|
|
561 | + | ||
|
|
562 | + static void | ||
|
|
563 | + box_inside(int argc, char **argv) | ||
|
|
564 | + { | ||
|
|
565 | + struct rlimit rl; | ||
|
|
566 | + char *args[argc+1]; | ||
|
|
567 | + char *env[1] = { NULL }; | ||
|
|
568 | + | ||
|
|
569 | + memcpy(args, argv, argc * sizeof(char *)); | ||
|
|
570 | + args[argc] = NULL; | ||
|
|
571 | + if (set_cwd && chdir(set_cwd)) | ||
|
|
572 | + die("chdir: %m"); | ||
|
|
573 | + if (redir_stdin) | ||
|
|
574 | + { | ||
|
|
575 | + close(0); | ||
|
|
576 | + if (open(redir_stdin, O_RDONLY) != 0) | ||
|
|
577 | + die("open(\"%s\"): %m", redir_stdin); | ||
|
|
578 | + } | ||
|
|
579 | + if (redir_stdout) | ||
|
|
580 | + { | ||
|
|
581 | + close(1); | ||
|
|
582 | + if (open(redir_stdout, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 1) | ||
|
|
583 | + die("open(\"%s\"): %m", redir_stdout); | ||
|
|
584 | + } | ||
|
|
585 | + dup2(1, 2); | ||
|
|
586 | + setpgrp(); | ||
|
|
587 | + if (memory_limit) | ||
|
|
588 | + { | ||
|
|
589 | + rl.rlim_cur = rl.rlim_max = memory_limit * 1024; | ||
|
|
590 | + if (setrlimit(RLIMIT_AS, &rl) < 0) | ||
|
|
591 | + die("setrlimit: %m"); | ||
|
|
592 | + } | ||
|
|
593 | + rl.rlim_cur = rl.rlim_max = 64; | ||
|
|
594 | + if (setrlimit(RLIMIT_NOFILE, &rl) < 0) | ||
|
|
595 | + die("setrlimit: %m"); | ||
|
|
596 | + if (filter_syscalls && ptrace(PTRACE_TRACEME) < 0) | ||
|
|
597 | + die("ptrace(PTRACE_TRACEME): %m"); | ||
|
|
598 | + execve(args[0], args, (pass_environ ? environ : env)); | ||
|
|
599 | + die("execve(\"%s\"): %m", args[0]); | ||
|
|
600 | + } | ||
|
|
601 | + | ||
|
|
602 | + static void | ||
|
|
603 | + usage(void) | ||
|
|
604 | + { | ||
|
|
605 | + fprintf(stderr, "Invalid arguments!\n"); | ||
|
|
606 | + printf("\ | ||
|
|
607 | + Usage: box [<options>] -- <command> <arguments>\n\ | ||
|
|
608 | + \n\ | ||
|
|
609 | + Options:\n\ | ||
|
|
610 | + -a <level>\tSet file access level (0=none, 1=cwd, 2=/etc,/lib,..., 3=whole fs, 9=no checks; needs -f)\n\ | ||
|
|
611 | + -c <dir>\tChange directory to <dir> first\n\ | ||
|
|
612 | + -e\t\tPass full environment of parent process\n\ | ||
|
|
613 | + -f\t\tFilter system calls (-ff=very restricted)\n\ | ||
|
|
614 | + -i <file>\tRedirect stdin from <file>\n\ | ||
|
|
615 | + -m <size>\tLimit address space to <size> KB\n\ | ||
|
|
616 | + -o <file>\tRedirect stdout to <file>\n\ | ||
|
|
617 | + -t <time>\tStop after <time> seconds\n\ | ||
|
|
618 | + -T\t\tAllow syscalls for measuring run time\n\ | ||
|
|
619 | + -v\t\tBe verbose\n\ | ||
|
|
620 | + -w\t\tMeasure wall clock time instead of run time\n\ | ||
|
|
621 | + "); | ||
|
|
622 | + exit(1); | ||
|
|
623 | + } | ||
|
|
624 | + | ||
|
|
625 | + int | ||
|
|
626 | + main(int argc, char **argv) | ||
|
|
627 | + { | ||
|
|
628 | + int c; | ||
|
|
629 | + uid_t uid; | ||
|
|
630 | + | ||
|
|
631 | + while ((c = getopt(argc, argv, "a:c:efi:m:o:t:Tvw")) >= 0) | ||
|
|
632 | + switch (c) | ||
|
|
633 | + { | ||
|
|
634 | + case 'a': | ||
|
|
635 | + file_access = atol(optarg); | ||
|
|
636 | + break; | ||
|
|
637 | + case 'c': | ||
|
|
638 | + set_cwd = optarg; | ||
|
|
639 | + break; | ||
|
|
640 | + case 'e': | ||
|
|
641 | + pass_environ = 1; | ||
|
|
642 | + break; | ||
|
|
643 | + case 'f': | ||
|
|
644 | + filter_syscalls++; | ||
|
|
645 | + break; | ||
|
|
646 | + case 'i': | ||
|
|
647 | + redir_stdin = optarg; | ||
|
|
648 | + break; | ||
|
|
649 | + case 'm': | ||
|
|
650 | + memory_limit = atol(optarg); | ||
|
|
651 | + break; | ||
|
|
652 | + case 'o': | ||
|
|
653 | + redir_stdout = optarg; | ||
|
|
654 | + break; | ||
|
|
655 | + case 't': | ||
|
|
656 | + timeout = atof(optarg); | ||
|
|
657 | + break; | ||
|
|
658 | + case 'T': | ||
|
|
659 | + allow_times++; | ||
|
|
660 | + break; | ||
|
|
661 | + case 'v': | ||
|
|
662 | + verbose++; | ||
|
|
663 | + break; | ||
|
|
664 | + case 'w': | ||
|
|
665 | + use_wall_clock = 1; | ||
|
|
666 | + break; | ||
|
|
667 | + default: | ||
|
|
668 | + usage(); | ||
|
|
669 | + } | ||
|
|
670 | + if (optind >= argc) | ||
|
|
671 | + usage(); | ||
|
|
672 | + | ||
|
|
673 | + uid = geteuid(); | ||
|
|
674 | + if (setreuid(uid, uid) < 0) | ||
|
|
675 | + die("setreuid: %m"); | ||
|
|
676 | + box_pid = fork(); | ||
|
|
677 | + if (box_pid < 0) | ||
|
|
678 | + die("fork: %m"); | ||
|
|
679 | + if (!box_pid) | ||
|
|
680 | + box_inside(argc-optind, argv+optind); | ||
|
|
681 | + else | ||
|
|
682 | + boxkeeper(); | ||
|
|
683 | + die("Internal error: fell over edge of the world"); | ||
|
|
684 | + } |
@@ -0,0 +1,84 | |||||
|
|
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 | + | ||
|
|
48 | + puts "YOU HAVE TO EDIT THE CHECKING CODE HERE: #{__FILE__}" | ||
|
|
49 | + exit(127) | ||
|
|
50 | + | ||
|
|
51 | + # below are codes for checking integer and text | ||
|
|
52 | + | ||
|
|
53 | + ########### THIS IS FOR CHECKING INTEGER ########## | ||
|
|
54 | + num_pattern = /^[0-9]*/ | ||
|
|
55 | + if (output_file_content =~ num_pattern) == nil | ||
|
|
56 | + report_wrong.call | ||
|
|
57 | + end | ||
|
|
58 | + | ||
|
|
59 | + output_i = output_file_content.to_i | ||
|
|
60 | + answer_i = answer_file_content.to_i | ||
|
|
61 | + | ||
|
|
62 | + if output_i == answer_i | ||
|
|
63 | + report_correct.call | ||
|
|
64 | + else | ||
|
|
65 | + report_wrong.call | ||
|
|
66 | + end | ||
|
|
67 | + | ||
|
|
68 | + ########### THIS IS FOR CHECKING TEXT ########## | ||
|
|
69 | + | ||
|
|
70 | + # check visible text | ||
|
|
71 | + | ||
|
|
72 | + out_items = output_file_content.split | ||
|
|
73 | + ans_items = answer_file_content.split | ||
|
|
74 | + | ||
|
|
75 | + if out_items.length != ans_items.length | ||
|
|
76 | + report_wrong.call | ||
|
|
77 | + else | ||
|
|
78 | + out_items.length.times do |i| | ||
|
|
79 | + if out_items[i]!=ans_items[i] | ||
|
|
80 | + report_wrong.call | ||
|
|
81 | + end | ||
|
|
82 | + end | ||
|
|
83 | + report_correct.call | ||
|
|
84 | + end |
@@ -0,0 +1,107 | |||||
|
|
1 | + #!/bin/sh | ||
|
|
2 | + | ||
|
|
3 | + ############################## | ||
|
|
4 | + # | ||
|
|
5 | + # Standard Compile Script | ||
|
|
6 | + # | ||
|
|
7 | + # Supported compilers: | ||
|
|
8 | + # gcc, g++, and fpc. | ||
|
|
9 | + # | ||
|
|
10 | + ############################## | ||
|
|
11 | + | ||
|
|
12 | + talk () | ||
|
|
13 | + { | ||
|
|
14 | + if [ "$TALKATIVE" != "" ]; then | ||
|
|
15 | + echo "$1" | ||
|
|
16 | + fi | ||
|
|
17 | + } | ||
|
|
18 | + | ||
|
|
19 | + export C_COMPILER=/usr/bin/gcc | ||
|
|
20 | + export CPLUSPLUS_COMPILER=/usr/bin/g++ | ||
|
|
21 | + export PASCAL_COMPILER=/usr/bin/fpc | ||
|
|
22 | + | ||
|
|
23 | + export C_OPTIONS="-O2 -s -static -std=c99 -DCONTEST -lm -Wall" | ||
|
|
24 | + export CPLUSPLUS_OPTIONS="-O2 -s -static -DCONTEST -lm -Wall" | ||
|
|
25 | + export PASCAL_OPTIONS="-O1 -XS -dCONTEST" | ||
|
|
26 | + | ||
|
|
27 | + # Check for the correct number of arguments. Otherwise, print usage. | ||
|
|
28 | + if [ $# -eq 0 -o $# -gt 4 ] | ||
|
|
29 | + then | ||
|
|
30 | + echo "Usage: $0 <language> [<source-file>] [<output-file>] [<message-file>]" | ||
|
|
31 | + echo | ||
|
|
32 | + echo "<source-file> is defaulted to \"source\"." | ||
|
|
33 | + echo "<output-file> is defaulted to \"a.out\"." | ||
|
|
34 | + echo "<message-file> is defaulted to \"compiler_message\"." | ||
|
|
35 | + echo | ||
|
|
36 | + exit 127 | ||
|
|
37 | + fi | ||
|
|
38 | + | ||
|
|
39 | + # Retrieve the arguments. | ||
|
|
40 | + if [ $# -ge 1 ] | ||
|
|
41 | + then | ||
|
|
42 | + export PROG_LANG=$1 | ||
|
|
43 | + talk "programming language: ${PROG_LANG}" | ||
|
|
44 | + fi | ||
|
|
45 | + | ||
|
|
46 | + if [ $# -ge 2 ] | ||
|
|
47 | + then | ||
|
|
48 | + export SOURCE_FILE=$2 | ||
|
|
49 | + else | ||
|
|
50 | + export SOURCE_FILE=source | ||
|
|
51 | + fi | ||
|
|
52 | + talk " source file: $SOURCE_FILE" | ||
|
|
53 | + | ||
|
|
54 | + if [ $# -ge 3 ] | ||
|
|
55 | + then | ||
|
|
56 | + export OUTPUT_FILE=$3 | ||
|
|
57 | + else | ||
|
|
58 | + export OUTPUT_FILE=a.out | ||
|
|
59 | + fi | ||
|
|
60 | + talk " output file: $OUTPUT_FILE" | ||
|
|
61 | + | ||
|
|
62 | + if [ $# -eq 4 ] | ||
|
|
63 | + then | ||
|
|
64 | + export MESSAGE_FILE=$4 | ||
|
|
65 | + else | ||
|
|
66 | + export MESSAGE_FILE=compiler_message | ||
|
|
67 | + fi | ||
|
|
68 | + talk " message file: $MESSAGE_FILE" | ||
|
|
69 | + | ||
|
|
70 | + # Remove any remaining output files or message files. | ||
|
|
71 | + rm -Rf $OUTPUT_FILE | ||
|
|
72 | + rm -Rf $MESSAGE_FILE | ||
|
|
73 | + | ||
|
|
74 | + # Check if the source file exists before attempt compiling. | ||
|
|
75 | + if [ ! -f $SOURCE_FILE ] | ||
|
|
76 | + then | ||
|
|
77 | + talk "ERROR: The source file does not exist!" | ||
|
|
78 | + echo "ERROR: The source file did not exist." > $MESSAGE_FILE | ||
|
|
79 | + exit 127 | ||
|
|
80 | + fi | ||
|
|
81 | + | ||
|
|
82 | + # Compile. | ||
|
|
83 | + if [ $PROG_LANG = "c" ] | ||
|
|
84 | + then | ||
|
|
85 | + $C_COMPILER $SOURCE_FILE -o $OUTPUT_FILE $C_OPTIONS 2>$MESSAGE_FILE | ||
|
|
86 | + elif [ $PROG_LANG = "c++" ] | ||
|
|
87 | + then | ||
|
|
88 | + $CPLUSPLUS_COMPILER $SOURCE_FILE -o $OUTPUT_FILE $CPLUSPLUS_OPTIONS 2>$MESSAGE_FILE | ||
|
|
89 | + elif [ $PROG_LANG = "pas" ] | ||
|
|
90 | + then | ||
|
|
91 | + $PASCAL_COMPILER $SOURCE_FILE -ooutpas $PASCAL_OPTIONS >$MESSAGE_FILE | ||
|
|
92 | + mv outpas $OUTPUT_FILE | ||
|
|
93 | + else | ||
|
|
94 | + talk "ERROR: Invalid language specified!" | ||
|
|
95 | + echo "ERROR: Invalid language specified!" > $MESSAGE_FILE | ||
|
|
96 | + exit 127 | ||
|
|
97 | + fi | ||
|
|
98 | + | ||
|
|
99 | + # Report success or failure. | ||
|
|
100 | + if [ -f $OUTPUT_FILE ] | ||
|
|
101 | + then | ||
|
|
102 | + talk "Compilation was successful!" | ||
|
|
103 | + else | ||
|
|
104 | + talk "ERROR: Something was wrong during the compilation!" | ||
|
|
105 | + talk "Dumping compiler message:" | ||
|
|
106 | + #cat $MESSAGE_FILE | ||
|
|
107 | + fi |
@@ -0,0 +1,106 | |||||
|
|
1 | + #!/usr/bin/ruby | ||
|
|
2 | + | ||
|
|
3 | + CORRECT_MARK = 'P' | ||
|
|
4 | + INCORRECT_MARK = '-' | ||
|
|
5 | + TIMEOUT_MARK = 'T' | ||
|
|
6 | + RUN_ERROR_MARK = 'x' | ||
|
|
7 | + | ||
|
|
8 | + def log(str='') | ||
|
|
9 | + if ENV['TALKATIVE']!=nil | ||
|
|
10 | + puts str | ||
|
|
11 | + end | ||
|
|
12 | + if ENV['GRADER_LOGGING']!=nil | ||
|
|
13 | + log_fname = ENV['GRADER_LOGGING'] | ||
|
|
14 | + fp = File.open(log_fname,"a") | ||
|
|
15 | + fp.puts("grade: #{Time.new.strftime("%H:%M")} #{str}") | ||
|
|
16 | + fp.close | ||
|
|
17 | + end | ||
|
|
18 | + end | ||
|
|
19 | + | ||
|
|
20 | + def char_comment(comment) | ||
|
|
21 | + if comment =~ /[Ii]ncorrect/ | ||
|
|
22 | + INCORRECT_MARK | ||
|
|
23 | + elsif comment =~ /[Cc]orrect/ | ||
|
|
24 | + CORRECT_MARK | ||
|
|
25 | + elsif comment =~ /[Tt]ime/ | ||
|
|
26 | + TIMEOUT_MARK | ||
|
|
27 | + elsif res = /^[Cc]omment:(.*)$/.match(comment) | ||
|
|
28 | + res[1] | ||
|
|
29 | + else | ||
|
|
30 | + RUN_ERROR_MARK # these are run time errors | ||
|
|
31 | + end | ||
|
|
32 | + end | ||
|
|
33 | + | ||
|
|
34 | + problem_home = ENV['PROBLEM_HOME'] | ||
|
|
35 | + require "#{problem_home}/script/test_dsl.rb" | ||
|
|
36 | + load "#{problem_home}/test_cases/all_tests.cfg" | ||
|
|
37 | + problem = Problem.get_instance | ||
|
|
38 | + | ||
|
|
39 | + if problem.well_formed? == false | ||
|
|
40 | + log "The problem specification is not well formed." | ||
|
|
41 | + exit(127) | ||
|
|
42 | + end | ||
|
|
43 | + | ||
|
|
44 | + all_score = 0 | ||
|
|
45 | + all_comment = '' | ||
|
|
46 | + (1..(problem.runs.length-1)).each do |k| | ||
|
|
47 | + log "grade run #{k}" | ||
|
|
48 | + run = problem.runs[k] | ||
|
|
49 | + run_score = nil | ||
|
|
50 | + run_comment = '' | ||
|
|
51 | + run_comment_short = '' | ||
|
|
52 | + run.tests.each do |test_num| | ||
|
|
53 | + result_file_name = "#{test_num}/result" | ||
|
|
54 | + if not File.exists?(result_file_name) | ||
|
|
55 | + run_comment += "result file for test #{test_num} not found\n" | ||
|
|
56 | + run_comment_short += RUN_ERROR_MARK | ||
|
|
57 | + log "Cannot find the file #{test_num}/result!" | ||
|
|
58 | + else | ||
|
|
59 | + result_file = File.new(result_file_name, "r") | ||
|
|
60 | + result_file_lines = result_file.readlines | ||
|
|
61 | + if result_file_lines.length>=2 | ||
|
|
62 | + current_run_score = result_file_lines[1].to_i | ||
|
|
63 | + run_comment += result_file_lines[0] | ||
|
|
64 | + run_comment_short += char_comment(result_file_lines[0].chomp) | ||
|
|
65 | + else | ||
|
|
66 | + current_run_score = 0 | ||
|
|
67 | + run_comment += "result file for test #{test_num} error\n" | ||
|
|
68 | + run_comment_short += RUN_ERROR_MARK | ||
|
|
69 | + log "Error in #{test_num}/result!" | ||
|
|
70 | + end | ||
|
|
71 | + | ||
|
|
72 | + # the score of this run should be the minimum of the score for | ||
|
|
73 | + # each test case | ||
|
|
74 | + if (run_score==nil) or (run_score>current_run_score) | ||
|
|
75 | + run_score = current_run_score | ||
|
|
76 | + end | ||
|
|
77 | + result_file.close | ||
|
|
78 | + end | ||
|
|
79 | + end | ||
|
|
80 | + | ||
|
|
81 | + run_result_file = File.new("result-#{k}", "w") | ||
|
|
82 | + run_result_file.write run_score | ||
|
|
83 | + run_result_file.write "\n" | ||
|
|
84 | + run_result_file.close | ||
|
|
85 | + | ||
|
|
86 | + run_comment_file = File.new("comment-#{k}", "w") | ||
|
|
87 | + run_comment_file.write "#{run_comment}\n" | ||
|
|
88 | + run_comment_file.close | ||
|
|
89 | + | ||
|
|
90 | + all_score = all_score + run_score | ||
|
|
91 | + | ||
|
|
92 | + # append comment for test run with many test cases | ||
|
|
93 | + if run.tests.length > 1 | ||
|
|
94 | + run_comment_short = '[' + run_comment_short + ']' | ||
|
|
95 | + end | ||
|
|
96 | + all_comment += run_comment_short | ||
|
|
97 | + end | ||
|
|
98 | + | ||
|
|
99 | + result_file = File.new("result", "w") | ||
|
|
100 | + result_file.write all_score | ||
|
|
101 | + result_file.write "\n" | ||
|
|
102 | + result_file.close | ||
|
|
103 | + | ||
|
|
104 | + comment_file = File.new("comment", "w") | ||
|
|
105 | + comment_file.write "#{all_comment}\n" | ||
|
|
106 | + comment_file.close |
@@ -0,0 +1,138 | |||||
|
|
1 | + #!/usr/bin/ruby | ||
|
|
2 | + | ||
|
|
3 | + def log(str='') | ||
|
|
4 | + if ENV['TALKATIVE']!=nil | ||
|
|
5 | + puts str | ||
|
|
6 | + end | ||
|
|
7 | + if ENV['GRADER_LOGGING']!=nil | ||
|
|
8 | + log_fname = ENV['GRADER_LOGGING'] | ||
|
|
9 | + fp = File.open(log_fname,"a") | ||
|
|
10 | + fp.puts("judge: #{Time.new.strftime("%H:%M")} #{str}") | ||
|
|
11 | + fp.close | ||
|
|
12 | + end | ||
|
|
13 | + end | ||
|
|
14 | + | ||
|
|
15 | + problem_home = ENV['PROBLEM_HOME'] | ||
|
|
16 | + | ||
|
|
17 | + def execute(command, error_message="") | ||
|
|
18 | + if not system(command) | ||
|
|
19 | + msg = "ERROR: #{error_message}" | ||
|
|
20 | + log msg | ||
|
|
21 | + raise msg | ||
|
|
22 | + end | ||
|
|
23 | + end | ||
|
|
24 | + | ||
|
|
25 | + # ARGV[0] --- language | ||
|
|
26 | + # ARGV[1] --- program source file | ||
|
|
27 | + # ARGV[2] --- test result directory | ||
|
|
28 | + # ARGV[3] --- sandbox directory | ||
|
|
29 | + | ||
|
|
30 | + if ARGV.length < 2 || ARGV.length > 4 | ||
|
|
31 | + puts "Usage: judge <language> <program-source> [<test-result-directory>] [<sandbox-directory>]" | ||
|
|
32 | + puts " <sandbox-directory> is defaulted to ./sandbox" | ||
|
|
33 | + puts " <test-result-directory> is defaulted to ./test-result" | ||
|
|
34 | + puts "WARNING: The judge script will forcefully create the (implicitly and explicitly) specified directories and remove anything inside it." | ||
|
|
35 | + exit(127) | ||
|
|
36 | + end | ||
|
|
37 | + | ||
|
|
38 | + language = ARGV[0] | ||
|
|
39 | + if language != "c" && language != "c++" && language != "pas" | ||
|
|
40 | + log "You specified a language that is not supported: #{language}." | ||
|
|
41 | + exit(127) | ||
|
|
42 | + end | ||
|
|
43 | + | ||
|
|
44 | + source_file = ARGV[1] | ||
|
|
45 | + if File.exist?(source_file) == false | ||
|
|
46 | + log "The source file does not exist." | ||
|
|
47 | + exit(127) | ||
|
|
48 | + end | ||
|
|
49 | + | ||
|
|
50 | + log "Making test result and sandbox directories..." | ||
|
|
51 | + | ||
|
|
52 | + current_dir = `pwd` | ||
|
|
53 | + current_dir.strip! | ||
|
|
54 | + | ||
|
|
55 | + if ARGV.length >= 3 | ||
|
|
56 | + test_result_dir = ARGV[2] | ||
|
|
57 | + else | ||
|
|
58 | + test_result_dir = "#{current_dir}/test-result" | ||
|
|
59 | + end | ||
|
|
60 | + log "Test result directory: #{test_result_dir}" | ||
|
|
61 | + system("rm -Rf #{test_result_dir}") | ||
|
|
62 | + execute("mkdir #{test_result_dir}", "Cannot make directory #{test_result_dir}.") | ||
|
|
63 | + | ||
|
|
64 | + if ARGV.length >= 4 | ||
|
|
65 | + sandbox_dir = ARGV[3] | ||
|
|
66 | + else | ||
|
|
67 | + sandbox_dir = "#{current_dir}/sandbox" | ||
|
|
68 | + end | ||
|
|
69 | + log "Sandbox directory: #{sandbox_dir}" | ||
|
|
70 | + system("rm -Rf #{sandbox_dir}") | ||
|
|
71 | + execute("mkdir #{sandbox_dir}", "Cannot make directory #{sandbox_dir}") | ||
|
|
72 | + | ||
|
|
73 | + # Compile | ||
|
|
74 | + log | ||
|
|
75 | + log "Compiling..." | ||
|
|
76 | + execute("cp #{source_file} #{sandbox_dir}", "Cannot copy the source file to #{sandbox_dir}") | ||
|
|
77 | + begin | ||
|
|
78 | + Dir.chdir sandbox_dir | ||
|
|
79 | + rescue | ||
|
|
80 | + log "ERROR: Cannot change directory to #{sandbox_dir}." | ||
|
|
81 | + exit(127) | ||
|
|
82 | + end | ||
|
|
83 | + execute("#{problem_home}/script/compile #{language} #{source_file}", "Compilation error!") | ||
|
|
84 | + compile_message = `cat compiler_message` | ||
|
|
85 | + compile_message.strip! | ||
|
|
86 | + execute("mv compiler_message #{test_result_dir}", "Cannot move the compiler message to #{test_result_dir}.") | ||
|
|
87 | + if !FileTest.exist?("a.out") | ||
|
|
88 | + log "Cannot compile the source code. See message in #{test_result_dir}/compile_message" | ||
|
|
89 | + exit(127) | ||
|
|
90 | + else | ||
|
|
91 | + execute("mv a.out #{test_result_dir}", "Cannot move the compiled program to #{test_result_dir}") | ||
|
|
92 | + system("rm -Rf #{sandbox_dir}/*") | ||
|
|
93 | + end | ||
|
|
94 | + | ||
|
|
95 | + require "#{problem_home}/script/test_dsl.rb" | ||
|
|
96 | + load "#{problem_home}/test_cases/all_tests.cfg" | ||
|
|
97 | + problem = Problem.get_instance | ||
|
|
98 | + | ||
|
|
99 | + if problem.well_formed? == false | ||
|
|
100 | + log "The problem specification is not well formed." | ||
|
|
101 | + exit(127) | ||
|
|
102 | + end | ||
|
|
103 | + | ||
|
|
104 | + # Doing the testing. | ||
|
|
105 | + (1..(problem.num_tests)).each do |test_num| | ||
|
|
106 | + | ||
|
|
107 | + $stdout.print "[#{test_num}]" | ||
|
|
108 | + $stdout.flush | ||
|
|
109 | + | ||
|
|
110 | + log "Test number: #{test_num}" | ||
|
|
111 | + execute("cp #{test_result_dir}/a.out #{sandbox_dir}", "Cannot copy the compiled program into #{sandbox_dir}") | ||
|
|
112 | + begin | ||
|
|
113 | + execute("#{problem_home}/script/run #{language} #{test_num}", "Error occured during execution of the run script") | ||
|
|
114 | + rescue | ||
|
|
115 | + # do nothing | ||
|
|
116 | + end | ||
|
|
117 | + execute("mkdir #{test_result_dir}/#{test_num}", "Cannot create directory #{test_result_dir}/#{test_num}") | ||
|
|
118 | + execute("mv #{sandbox_dir}/result #{test_result_dir}/#{test_num}", "Cannot copy the result file into #{test_result_dir}/#{test_num}") | ||
|
|
119 | + execute("mv #{sandbox_dir}/comment #{test_result_dir}/#{test_num}", "Cannot copy the comment file into #{test_result_dir}/#{test_num}") | ||
|
|
120 | + execute("mv #{sandbox_dir}/output.txt #{test_result_dir}/#{test_num}", "Cannot copy the output file into #{test_result_dir}/#{test_num}") | ||
|
|
121 | + execute("rm -Rf #{sandbox_dir}/*", "Cannot clear #{sandbox_dir}") | ||
|
|
122 | + end | ||
|
|
123 | + | ||
|
|
124 | + $stdout.print "[done]\n" | ||
|
|
125 | + | ||
|
|
126 | + # Grade | ||
|
|
127 | + log | ||
|
|
128 | + log "Grading..." | ||
|
|
129 | + begin | ||
|
|
130 | + Dir.chdir test_result_dir | ||
|
|
131 | + rescue | ||
|
|
132 | + log "ERROR: Cannot change directory to #{test_result_dir}." | ||
|
|
133 | + exit(127) | ||
|
|
134 | + end | ||
|
|
135 | + execute("#{problem_home}/script/grade", "An error occured during grading!") | ||
|
|
136 | + | ||
|
|
137 | + log | ||
|
|
138 | + log "All done!" |
@@ -0,0 +1,155 | |||||
|
|
1 | + #!/usr/bin/ruby | ||
|
|
2 | + | ||
|
|
3 | + def log(str='') | ||
|
|
4 | + if ENV['TALKATIVE']!=nil | ||
|
|
5 | + puts str | ||
|
|
6 | + end | ||
|
|
7 | + if ENV['GRADER_LOGGING']!=nil | ||
|
|
8 | + log_fname = ENV['GRADER_LOGGING'] | ||
|
|
9 | + fp = File.open(log_fname,"a") | ||
|
|
10 | + fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}") | ||
|
|
11 | + fp.close | ||
|
|
12 | + end | ||
|
|
13 | + end | ||
|
|
14 | + | ||
|
|
15 | + def extract_time(t) | ||
|
|
16 | + # puts "TIME: #{t}" | ||
|
|
17 | + if (result=/^(.*)r(.*)u(.*)s/.match(t)) | ||
|
|
18 | + {:real => result[1], :user => result[2], :sys => result[3]} | ||
|
|
19 | + else | ||
|
|
20 | + #{:real => 0, :user => 0, :sys => 0} | ||
|
|
21 | + #puts "ERROR READING RUNNING TIME: #{t}" | ||
|
|
22 | + raise "Error reading running time: #{t}" | ||
|
|
23 | + end | ||
|
|
24 | + end | ||
|
|
25 | + | ||
|
|
26 | + def compile_box(source,bin) | ||
|
|
27 | + system("g++ #{source} -o #{bin}") | ||
|
|
28 | + end | ||
|
|
29 | + | ||
|
|
30 | + if ARGV.length < 2 || ARGV.length > 3 | ||
|
|
31 | + puts "Usage: run <language> <test-num> [<program-name>]" | ||
|
|
32 | + exit(127) | ||
|
|
33 | + end | ||
|
|
34 | + | ||
|
|
35 | + language = ARGV[0] | ||
|
|
36 | + test_num = ARGV[1].to_i | ||
|
|
37 | + if ARGV.length > 2 | ||
|
|
38 | + program_name = ARGV[2] | ||
|
|
39 | + else | ||
|
|
40 | + program_name = "a.out" | ||
|
|
41 | + end | ||
|
|
42 | + | ||
|
|
43 | + problem_home = ENV['PROBLEM_HOME'] | ||
|
|
44 | + require "#{problem_home}/script/test_dsl.rb" | ||
|
|
45 | + load "#{problem_home}/test_cases/all_tests.cfg" | ||
|
|
46 | + problem = Problem.get_instance | ||
|
|
47 | + | ||
|
|
48 | + if problem.well_formed? == false | ||
|
|
49 | + log "The problem specification is not well formed." | ||
|
|
50 | + exit(127) | ||
|
|
51 | + end | ||
|
|
52 | + | ||
|
|
53 | + # Check if the test number is okay. | ||
|
|
54 | + if test_num <= 0 || test_num > problem.num_tests | ||
|
|
55 | + log "You have specified a wrong test number." | ||
|
|
56 | + exit(127) | ||
|
|
57 | + end | ||
|
|
58 | + | ||
|
|
59 | + ##################################### | ||
|
|
60 | + # Set the relavant file names here. # | ||
|
|
61 | + ##################################### | ||
|
|
62 | + | ||
|
|
63 | + input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt" | ||
|
|
64 | + | ||
|
|
65 | + ##################################### | ||
|
|
66 | + | ||
|
|
67 | + time_limit = problem.get_time_limit test_num | ||
|
|
68 | + mem_limit = problem.get_mem_limit(test_num) * 1024 | ||
|
|
69 | + | ||
|
|
70 | + # Copy the input file. | ||
|
|
71 | + #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .` | ||
|
|
72 | + | ||
|
|
73 | + # check if box is there, if not, compile it! | ||
|
|
74 | + if !File.exists?("#{problem_home}/script/box") | ||
|
|
75 | + log "WARNING: Compiling box: to increase efficiency, it should be compile manually" | ||
|
|
76 | + compile_box("#{problem_home}/script/box.cc", | ||
|
|
77 | + "#{problem_home}/script/box") | ||
|
|
78 | + end | ||
|
|
79 | + | ||
|
|
80 | + # Hide PROBLEM_HOME | ||
|
|
81 | + ENV['PROBLEM_HOME'] = nil | ||
|
|
82 | + | ||
|
|
83 | + # Run the program. | ||
|
|
84 | + #run_command = "/usr/bin/time -f \"#{time_output_format}\" 2>run_result #{problem_home}/script/box_new -a 2 -f -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name}" | ||
|
|
85 | + run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name} 2>run_result" | ||
|
|
86 | + log "Running test #{test_num}..." | ||
|
|
87 | + log run_command | ||
|
|
88 | + log | ||
|
|
89 | + system(run_command) | ||
|
|
90 | + | ||
|
|
91 | + # Restore PROBLEM_HOME | ||
|
|
92 | + ENV['PROBLEM_HOME'] = problem_home | ||
|
|
93 | + | ||
|
|
94 | + # Create the result file. | ||
|
|
95 | + result_file = File.new("result", "w") | ||
|
|
96 | + comment_file = File.new("comment", "w") | ||
|
|
97 | + | ||
|
|
98 | + # Check if the program actually produced any output. | ||
|
|
99 | + run_result_file = File.new("run_result", "r") | ||
|
|
100 | + run_result = run_result_file.readlines | ||
|
|
101 | + run_result_file.close | ||
|
|
102 | + | ||
|
|
103 | + run_stat = run_result[run_result.length-1] | ||
|
|
104 | + running_time = extract_time(run_stat) | ||
|
|
105 | + | ||
|
|
106 | + report = lambda{ |status, points, comment| | ||
|
|
107 | + result_file.write status.strip | ||
|
|
108 | + result_file.write "\n" | ||
|
|
109 | + result_file.write points.to_s.strip | ||
|
|
110 | + result_file.write "\n" | ||
|
|
111 | + result_file.write run_stat.strip | ||
|
|
112 | + result_file.write "\n" | ||
|
|
113 | + result_file.close | ||
|
|
114 | + `rm run_result` | ||
|
|
115 | + # `rm output.txt` --- keep the output | ||
|
|
116 | + | ||
|
|
117 | + comment_file.write comment | ||
|
|
118 | + | ||
|
|
119 | + # added for debuggin --- jittat | ||
|
|
120 | + comment_file.write "--run-result--\n" | ||
|
|
121 | + run_result.each do |l| | ||
|
|
122 | + comment_file.write l | ||
|
|
123 | + end | ||
|
|
124 | + | ||
|
|
125 | + comment_file.close | ||
|
|
126 | + | ||
|
|
127 | + log "Done!" | ||
|
|
128 | + exit(0) | ||
|
|
129 | + } | ||
|
|
130 | + | ||
|
|
131 | + if run_result[0][0,2] != "OK" | ||
|
|
132 | + log "There was a runtime error." | ||
|
|
133 | + report.call(run_result[0], 0, "No comment.\n") | ||
|
|
134 | + end | ||
|
|
135 | + | ||
|
|
136 | + if running_time[:user].to_f + running_time[:sys].to_f > time_limit | ||
|
|
137 | + log "Time limit exceeded." | ||
|
|
138 | + report.call("Time limit exceeded", 0, "No comment.\n") | ||
|
|
139 | + end | ||
|
|
140 | + | ||
|
|
141 | + # Run 'check' to evaluate the output. | ||
|
|
142 | + #puts "There was no runtime error. Proceed to checking the output." | ||
|
|
143 | + check_command = "#{problem_home}/script/check #{language} #{test_num}" | ||
|
|
144 | + log "Checking the output..." | ||
|
|
145 | + log check_command | ||
|
|
146 | + if not system(check_command) | ||
|
|
147 | + log "Problem with check script" | ||
|
|
148 | + report.call("Incorrect",0,"Check script error.\n") | ||
|
|
149 | + exit(127) | ||
|
|
150 | + end | ||
|
|
151 | + | ||
|
|
152 | + check_file = File.new("check_result", "r") | ||
|
|
153 | + check_file_lines = check_file.readlines | ||
|
|
154 | + | ||
|
|
155 | + report.call(check_file_lines[0], check_file_lines[1], "No comment.\n") |
@@ -0,0 +1,180 | |||||
|
|
1 | + class DSLNode | ||
|
|
2 | + def DSLNode.scalar_attr(*names) | ||
|
|
3 | + names.each do |name| | ||
|
|
4 | + define_method name do |*a| | ||
|
|
5 | + if a.length == 0 | ||
|
|
6 | + instance_variable_get( "@#{name}" ) | ||
|
|
7 | + else | ||
|
|
8 | + instance_variable_set( "@#{name}", a[0] ) | ||
|
|
9 | + end | ||
|
|
10 | + end | ||
|
|
11 | + end | ||
|
|
12 | + end | ||
|
|
13 | + | ||
|
|
14 | + def DSLNode.array_attr(*names) | ||
|
|
15 | + names.each do |name| | ||
|
|
16 | + define_method name do |*a| | ||
|
|
17 | + if a.length == 0 | ||
|
|
18 | + instance_variable_get( "@#{name}" ) | ||
|
|
19 | + else | ||
|
|
20 | + instance_variable_set( "@#{name}", a ) | ||
|
|
21 | + end | ||
|
|
22 | + end | ||
|
|
23 | + end | ||
|
|
24 | + end | ||
|
|
25 | + end | ||
|
|
26 | + | ||
|
|
27 | + class Problem < DSLNode | ||
|
|
28 | + def initialize | ||
|
|
29 | + @runs = [] | ||
|
|
30 | + @tests = [] | ||
|
|
31 | + end | ||
|
|
32 | + | ||
|
|
33 | + def Problem.getter(name, plural_name, each_name) | ||
|
|
34 | + eval "def get_#{name}(index) \n \ | ||
|
|
35 | + if defined?(@tests) and @tests[index] != nil \n \ | ||
|
|
36 | + if @tests[index].#{name} != nil \n \ | ||
|
|
37 | + return @tests[index].#{name} \n \ | ||
|
|
38 | + end \n \ | ||
|
|
39 | + end \n \ | ||
|
|
40 | + \n \ | ||
|
|
41 | + (1..@runs.length-1).each do |i| \n \ | ||
|
|
42 | + run = @runs[i] \n \ | ||
|
|
43 | + k = run.tests.index(index) \n \ | ||
|
|
44 | + if k == nil \n \ | ||
|
|
45 | + next \n \ | ||
|
|
46 | + end \n \ | ||
|
|
47 | + \n \ | ||
|
|
48 | + if run.#{plural_name} != nil && run.#{plural_name}[k] != nil \n \ | ||
|
|
49 | + return run.#{plural_name}[k] \n \ | ||
|
|
50 | + end \n \ | ||
|
|
51 | + \n \ | ||
|
|
52 | + if run.#{each_name} != nil \n \ | ||
|
|
53 | + return run.#{each_name} \n \ | ||
|
|
54 | + end \n \ | ||
|
|
55 | + end \n \ | ||
|
|
56 | + \n \ | ||
|
|
57 | + if @#{each_name} != nil \n \ | ||
|
|
58 | + return @#{each_name} \n \ | ||
|
|
59 | + else \n \ | ||
|
|
60 | + raise 'The problem is malformed (possibly in more than one way)!' \n \ | ||
|
|
61 | + end \n \ | ||
|
|
62 | + end" | ||
|
|
63 | + end | ||
|
|
64 | + | ||
|
|
65 | + scalar_attr :num_tests, :full_score, :score_each, :time_limit_each, :mem_limit_each | ||
|
|
66 | + array_attr :runs, :tests | ||
|
|
67 | + getter "score", "scores", "score_each" | ||
|
|
68 | + getter "mem_limit", "mem_limits", "mem_limit_each" | ||
|
|
69 | + getter "time_limit", "time_limits", "time_limit_each" | ||
|
|
70 | + | ||
|
|
71 | + def run(index, &block) | ||
|
|
72 | + new_run = Run.new | ||
|
|
73 | + new_run.instance_eval &block | ||
|
|
74 | + @runs[index] = new_run | ||
|
|
75 | + end | ||
|
|
76 | + | ||
|
|
77 | + def test(index, &block) | ||
|
|
78 | + new_test = Test.new | ||
|
|
79 | + new_test.instance_eval &block | ||
|
|
80 | + @tests[index] = new_test | ||
|
|
81 | + end | ||
|
|
82 | + | ||
|
|
83 | + def read_test(index) | ||
|
|
84 | + filename = ENV['PROBLEM_HOME'] + "/test_cases/#{index}/test.cfg" | ||
|
|
85 | + if File.exists?(filename) | ||
|
|
86 | + @tests[index] ||= Test.new | ||
|
|
87 | + content = File.read(filename) | ||
|
|
88 | + @tests[index].instance_eval content | ||
|
|
89 | + end | ||
|
|
90 | + end | ||
|
|
91 | + | ||
|
|
92 | + def Problem.set_instance(prob) | ||
|
|
93 | + @instance = prob | ||
|
|
94 | + end | ||
|
|
95 | + | ||
|
|
96 | + def Problem.get_instance | ||
|
|
97 | + return @instance | ||
|
|
98 | + end | ||
|
|
99 | + | ||
|
|
100 | + def well_formed? | ||
|
|
101 | + # Check if run 1 to run @runs.length are present. | ||
|
|
102 | + (1..(@runs.length-1)).each do |i| | ||
|
|
103 | + if @runs[i] == nil | ||
|
|
104 | + puts "run #{i} is not present" | ||
|
|
105 | + return false | ||
|
|
106 | + end | ||
|
|
107 | + end | ||
|
|
108 | + | ||
|
|
109 | + # Check if all tests are in one and only one run. | ||
|
|
110 | + test_present = [] | ||
|
|
111 | + (1..(@num_tests)).each do |i| | ||
|
|
112 | + test_present[i] = false | ||
|
|
113 | + end | ||
|
|
114 | + (1..(@runs.length-1)).each do |j| | ||
|
|
115 | + run = @runs[j] | ||
|
|
116 | + if run.tests!=nil | ||
|
|
117 | + run.tests.each do |t| | ||
|
|
118 | + if test_present[t] == false | ||
|
|
119 | + test_present[t] = true | ||
|
|
120 | + else | ||
|
|
121 | + puts "test #{t} is present in more than one run" | ||
|
|
122 | + return false | ||
|
|
123 | + end | ||
|
|
124 | + end | ||
|
|
125 | + end | ||
|
|
126 | + end | ||
|
|
127 | + (1..(@num_tests)).each do |i| | ||
|
|
128 | + if test_present[i] == false | ||
|
|
129 | + puts "test #{i} is not present" | ||
|
|
130 | + return false | ||
|
|
131 | + end | ||
|
|
132 | + end | ||
|
|
133 | + | ||
|
|
134 | + # Check if we can find the score, mem limit, and time limit for all tests. | ||
|
|
135 | + (1..(@num_tests)).each do |i| | ||
|
|
136 | + begin | ||
|
|
137 | + get_score i | ||
|
|
138 | + rescue | ||
|
|
139 | + puts "cannot get score for test #{i}" | ||
|
|
140 | + return false | ||
|
|
141 | + end | ||
|
|
142 | + | ||
|
|
143 | + begin | ||
|
|
144 | + get_mem_limit i | ||
|
|
145 | + rescue | ||
|
|
146 | + puts "cannot get mem limit for test #{i}" | ||
|
|
147 | + return false | ||
|
|
148 | + end | ||
|
|
149 | + | ||
|
|
150 | + begin | ||
|
|
151 | + get_time_limit i | ||
|
|
152 | + rescue | ||
|
|
153 | + puts "cannot get time limit for test #{i}" | ||
|
|
154 | + return false | ||
|
|
155 | + end | ||
|
|
156 | + end | ||
|
|
157 | + | ||
|
|
158 | + return true | ||
|
|
159 | + end | ||
|
|
160 | + end | ||
|
|
161 | + | ||
|
|
162 | + class Run < DSLNode | ||
|
|
163 | + scalar_attr :score_each, :time_limit_each, :mem_limit_each | ||
|
|
164 | + array_attr :tests, :scores, :time_limits, :mem_limits | ||
|
|
165 | + end | ||
|
|
166 | + | ||
|
|
167 | + class Test < DSLNode | ||
|
|
168 | + scalar_attr :score, :time_limit, :mem_limit | ||
|
|
169 | + end | ||
|
|
170 | + | ||
|
|
171 | + def problem(&blk) | ||
|
|
172 | + prob = Problem.new | ||
|
|
173 | + prob.instance_eval &blk | ||
|
|
174 | + Problem.set_instance prob | ||
|
|
175 | + p = Problem.get_instance | ||
|
|
176 | + (1..(p.num_tests)).each do |i| | ||
|
|
177 | + p.read_test i | ||
|
|
178 | + end | ||
|
|
179 | + p.well_formed? | ||
|
|
180 | + end |
@@ -0,0 +1,20 | |||||
|
|
1 | + problem do | ||
|
|
2 | + num_tests <%= num_testcases %> | ||
|
|
3 | + full_score <%= num_testruns*10 %> | ||
|
|
4 | + time_limit_each <%= options[:time_limit] %> | ||
|
|
5 | + mem_limit_each <%= options[:mem_limit] %> | ||
|
|
6 | + score_each 10 | ||
|
|
7 | + | ||
|
|
8 | + <% tr_num = 0 %> | ||
|
|
9 | + <% testrun_info.each do |testrun| %> | ||
|
|
10 | + <% tr_num += 1 %> | ||
|
|
11 | + run <%= tr_num %> do | ||
|
|
12 | + tests <%= (testrun.collect {|testcase| testcase[0]}).join(", ") %> | ||
|
|
13 | + <% if testrun.length==1 %> | ||
|
|
14 | + scores 10 | ||
|
|
15 | + <% else %> | ||
|
|
16 | + scores 10 <% (testrun.length-1).times do %>,10 <% end %> | ||
|
|
17 | + <% end %> | ||
|
|
18 | + end | ||
|
|
19 | + <% end %> | ||
|
|
20 | + end |
new file 100644 |
@@ -0,0 +1,62 | |||||
|
|
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 | + | ||
|
|
48 | + ########### THIS IS FOR CHECKING INTEGER ########## | ||
|
|
49 | + num_pattern = /^[0-9]*/ | ||
|
|
50 | + if (output_file_content =~ num_pattern) == nil | ||
|
|
51 | + report_wrong.call | ||
|
|
52 | + end | ||
|
|
53 | + | ||
|
|
54 | + output_i = output_file_content.to_i | ||
|
|
55 | + answer_i = answer_file_content.to_i | ||
|
|
56 | + | ||
|
|
57 | + if output_i == answer_i | ||
|
|
58 | + report_correct.call | ||
|
|
59 | + else | ||
|
|
60 | + report_wrong.call | ||
|
|
61 | + end | ||
|
|
62 | + |
@@ -0,0 +1,64 | |||||
|
|
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 | + | ||
|
|
48 | + ########### THIS IS FOR CHECKING TEXT ########## | ||
|
|
49 | + | ||
|
|
50 | + # check visible text | ||
|
|
51 | + | ||
|
|
52 | + out_items = output_file_content.split | ||
|
|
53 | + ans_items = answer_file_content.split | ||
|
|
54 | + | ||
|
|
55 | + if out_items.length != ans_items.length | ||
|
|
56 | + report_wrong.call | ||
|
|
57 | + else | ||
|
|
58 | + out_items.length.times do |i| | ||
|
|
59 | + if out_items[i]!=ans_items[i] | ||
|
|
60 | + report_wrong.call | ||
|
|
61 | + end | ||
|
|
62 | + end | ||
|
|
63 | + report_correct.call | ||
|
|
64 | + 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 |
@@ -0,0 +1,48 | |||||
|
|
1 | + #!/usr/bin/ruby | ||
|
|
2 | + | ||
|
|
3 | + # | ||
|
|
4 | + # This is a check script wrapper. It read all required information | ||
|
|
5 | + # and call a real check script call REAL_CHECK_SCRIPT in directory | ||
|
|
6 | + # [problem_home]/script | ||
|
|
7 | + # | ||
|
|
8 | + | ||
|
|
9 | + REAL_CHECK_SCRIPT = "<%= script_name %>" | ||
|
|
10 | + | ||
|
|
11 | + # The REAL_CHECK_SCRIPT is called with: | ||
|
|
12 | + # | ||
|
|
13 | + # (script) <lang> <test-num> <in-file> <out-file> <ans-file> <full-score> | ||
|
|
14 | + # | ||
|
|
15 | + # and REAL_CHECK_SCRIPT's output to standard out is redirected to | ||
|
|
16 | + # 'check_result' as required by normal check script. | ||
|
|
17 | + | ||
|
|
18 | + problem_home = ENV['PROBLEM_HOME'] | ||
|
|
19 | + require "#{problem_home}/script/test_dsl.rb" | ||
|
|
20 | + | ||
|
|
21 | + if ARGV.length < 2 | ||
|
|
22 | + puts "Usage: check <language> <test-number> [<output-file>]" | ||
|
|
23 | + exit(0) | ||
|
|
24 | + end | ||
|
|
25 | + | ||
|
|
26 | + language = ARGV[0] | ||
|
|
27 | + test_num = ARGV[1].to_i | ||
|
|
28 | + if ARGV.length >= 3 | ||
|
|
29 | + output_file_name = ARGV[2] | ||
|
|
30 | + else | ||
|
|
31 | + output_file_name = "output.txt" | ||
|
|
32 | + end | ||
|
|
33 | + | ||
|
|
34 | + load "#{problem_home}/test_cases/all_tests.cfg" | ||
|
|
35 | + problem = Problem.get_instance | ||
|
|
36 | + | ||
|
|
37 | + answer_file_name = "#{problem_home}/test_cases/#{test_num}/answer-#{test_num}.txt" | ||
|
|
38 | + input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt" | ||
|
|
39 | + | ||
|
|
40 | + score = problem.get_score(test_num) | ||
|
|
41 | + | ||
|
|
42 | + cmd = "#{problem_home}/script/#{REAL_CHECK_SCRIPT} " + | ||
|
|
43 | + "#{language} #{test_num} #{input_file_name} #{output_file_name} " + | ||
|
|
44 | + "#{answer_file_name} #{score} > check_result" | ||
|
|
45 | + | ||
|
|
46 | + #puts "wrapper-CMD: #{cmd}" | ||
|
|
47 | + | ||
|
|
48 | + system(cmd) |
@@ -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,13 | |||||
|
|
1 | + #include <stdio.h> | ||
|
|
2 | + | ||
|
|
3 | + int main() | ||
|
|
4 | + { | ||
|
|
5 | + int a,b; | ||
|
|
6 | + scanf("%d %d",&a,&b); | ||
|
|
7 | + if((a==1) && (b==1)) | ||
|
|
8 | + printf("100\n"); | ||
|
|
9 | + else | ||
|
|
10 | + printf("%d\n",a+b); | ||
|
|
11 | + return 0; | ||
|
|
12 | + } | ||
|
|
13 | + |
@@ -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,40 | |||||
|
|
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 30, 30 | ||
|
|
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 50 | ||
|
|
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 55 | ||
|
|
28 | + time_limit 3 | ||
|
|
29 | + mem_limit 10 | ||
|
|
30 | + end | ||
|
|
31 | + | ||
|
|
32 | + test 9 do | ||
|
|
33 | + score 55 | ||
|
|
34 | + end | ||
|
|
35 | + | ||
|
|
36 | + test 10 do | ||
|
|
37 | + score 55 | ||
|
|
38 | + time_limit 1 | ||
|
|
39 | + end | ||
|
|
40 | + 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,24 | |||||
|
|
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 | + test 1 do | ||
|
|
9 | + time_limit 0.5 | ||
|
|
10 | + end | ||
|
|
11 | + | ||
|
|
12 | + run 1 do | ||
|
|
13 | + tests 1 | ||
|
|
14 | + end | ||
|
|
15 | + | ||
|
|
16 | + test 2 do | ||
|
|
17 | + time_limit 0.6 | ||
|
|
18 | + end | ||
|
|
19 | + | ||
|
|
20 | + run 2 do | ||
|
|
21 | + tests 2 | ||
|
|
22 | + end | ||
|
|
23 | + | ||
|
|
24 | + end |
@@ -0,0 +1,64 | |||||
|
|
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 | + | ||
|
|
48 | + ########### THIS IS FOR CHECKING TEXT ########## | ||
|
|
49 | + | ||
|
|
50 | + # check visible text | ||
|
|
51 | + | ||
|
|
52 | + out_items = output_file_content.split | ||
|
|
53 | + ans_items = answer_file_content.split | ||
|
|
54 | + | ||
|
|
55 | + if out_items.length != ans_items.length | ||
|
|
56 | + report_wrong.call | ||
|
|
57 | + else | ||
|
|
58 | + out_items.length.times do |i| | ||
|
|
59 | + if out_items[i]!=ans_items[i] | ||
|
|
60 | + report_wrong.call | ||
|
|
61 | + end | ||
|
|
62 | + end | ||
|
|
63 | + report_correct.call | ||
|
|
64 | + end |
@@ -0,0 +1,12 | |||||
|
|
1 | + problem do | ||
|
|
2 | + num_tests 1 | ||
|
|
3 | + full_score 10 | ||
|
|
4 | + time_limit_each 1 | ||
|
|
5 | + mem_limit_each 64 | ||
|
|
6 | + score_each 10 | ||
|
|
7 | + | ||
|
|
8 | + run 1 do | ||
|
|
9 | + tests 1 | ||
|
|
10 | + end | ||
|
|
11 | + | ||
|
|
12 | + end |
@@ -0,0 +1,13 | |||||
|
|
1 | + /* | ||
|
|
2 | + LANG: C | ||
|
|
3 | + */ | ||
|
|
4 | + #include <stdio.h> | ||
|
|
5 | + | ||
|
|
6 | + int main() | ||
|
|
7 | + { | ||
|
|
8 | + int a, | ||
|
|
9 | + scanf("%d %d",&a,&b); | ||
|
|
10 | + printf("%d\n",a+b); | ||
|
|
11 | + return 0; | ||
|
|
12 | + } | ||
|
|
13 | + |
@@ -0,0 +1,13 | |||||
|
|
1 | + /* | ||
|
|
2 | + LANG: C | ||
|
|
3 | + */ | ||
|
|
4 | + #include <stdio.h> | ||
|
|
5 | + | ||
|
|
6 | + int main() | ||
|
|
7 | + { | ||
|
|
8 | + int a,b; | ||
|
|
9 | + scanf("%d %d",&a,&b); | ||
|
|
10 | + printf("%d\n",a+b); | ||
|
|
11 | + return 0; | ||
|
|
12 | + } | ||
|
|
13 | + |
@@ -0,0 +1,38 | |||||
|
|
1 | + #include <stdio.h> | ||
|
|
2 | + #include <stdlib.h> | ||
|
|
3 | + #include <unistd.h> | ||
|
|
4 | + #include <sys/time.h> | ||
|
|
5 | + #include <time.h> | ||
|
|
6 | + #include <sys/resource.h> | ||
|
|
7 | + | ||
|
|
8 | + // run it for 1.5 s | ||
|
|
9 | + | ||
|
|
10 | + int main() | ||
|
|
11 | + { | ||
|
|
12 | + int a,b; | ||
|
|
13 | + | ||
|
|
14 | + int c=0; | ||
|
|
15 | + | ||
|
|
16 | + scanf("%d %d",&a,&b); | ||
|
|
17 | + printf("%d\n",a+b); | ||
|
|
18 | + | ||
|
|
19 | + struct rusage ru; | ||
|
|
20 | + | ||
|
|
21 | + while(1) { | ||
|
|
22 | + c++; | ||
|
|
23 | + b+=c; | ||
|
|
24 | + while(c<100000) { | ||
|
|
25 | + c++; | ||
|
|
26 | + b+=c; | ||
|
|
27 | + } | ||
|
|
28 | + getrusage(RUSAGE_SELF,&ru); | ||
|
|
29 | + double rtime = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec; | ||
|
|
30 | + rtime += (double)ru.ru_utime.tv_usec / 1000000.0; | ||
|
|
31 | + rtime += (double)ru.ru_stime.tv_usec / 1000000.0; | ||
|
|
32 | + if(rtime > 0.5) | ||
|
|
33 | + break; | ||
|
|
34 | + } | ||
|
|
35 | + printf("%d\n",b); | ||
|
|
36 | + exit(0); | ||
|
|
37 | + } | ||
|
|
38 | + |
@@ -0,0 +1,11 | |||||
|
|
1 | + #include <stdio.h> | ||
|
|
2 | + | ||
|
|
3 | + int main() | ||
|
|
4 | + { | ||
|
|
5 | + int a,b; | ||
|
|
6 | + | ||
|
|
7 | + scanf("%d %d",&a,&b); | ||
|
|
8 | + while(1) | ||
|
|
9 | + ; | ||
|
|
10 | + } | ||
|
|
11 | + |
@@ -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,29 | |||||
|
|
1 | + /* | ||
|
|
2 | + LANG: C | ||
|
|
3 | + */ | ||
|
|
4 | + #include <stdio.h> | ||
|
|
5 | + #include <stdlib.h> | ||
|
|
6 | + | ||
|
|
7 | + int main() | ||
|
|
8 | + { | ||
|
|
9 | + int a,b,d; | ||
|
|
10 | + int i,j; | ||
|
|
11 | + char *c = malloc(100000); | ||
|
|
12 | + | ||
|
|
13 | + scanf("%d %d",&a,&b); | ||
|
|
14 | + d = a+b; | ||
|
|
15 | + // printf("%d\n",a+b); | ||
|
|
16 | + for(j=0; j<1; j++) | ||
|
|
17 | + for(i=0; i<100000000; i++) { | ||
|
|
18 | + b+=a; | ||
|
|
19 | + a++; | ||
|
|
20 | + } | ||
|
|
21 | + if((c!=NULL) || (b<100)) | ||
|
|
22 | + b++; | ||
|
|
23 | + if(b==100) | ||
|
|
24 | + printf("hello"); | ||
|
|
25 | + else | ||
|
|
26 | + printf("%d\n",d); | ||
|
|
27 | + return 0; | ||
|
|
28 | + } | ||
|
|
29 | + |
@@ -0,0 +1,12 | |||||
|
|
1 | + #include <stdlib.h> | ||
|
|
2 | + #include <stdio.h> | ||
|
|
3 | + | ||
|
|
4 | + int main() | ||
|
|
5 | + { | ||
|
|
6 | + char* prob_home = getenv("PROBLEM_HOME"); | ||
|
|
7 | + if(prob_home!=NULL) | ||
|
|
8 | + printf("yes\n"); | ||
|
|
9 | + else | ||
|
|
10 | + printf("no\n"); | ||
|
|
11 | + exit(0); | ||
|
|
12 | + } |
@@ -0,0 +1,13 | |||||
|
|
1 | + #include <stdlib.h> | ||
|
|
2 | + #include <stdio.h> | ||
|
|
3 | + | ||
|
|
4 | + int main() | ||
|
|
5 | + { | ||
|
|
6 | + FILE *fp = fopen("/bin/ls","r"); | ||
|
|
7 | + if(fp!=NULL) { | ||
|
|
8 | + printf("yes\n"); | ||
|
|
9 | + fclose(fp); | ||
|
|
10 | + } else | ||
|
|
11 | + printf("no\n"); | ||
|
|
12 | + exit(0); | ||
|
|
13 | + } |
@@ -0,0 +1,316 | |||||
|
|
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 => /^(\[|P|\])+/}) | ||
|
|
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 => 'compilation 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 => 'TT'}) | ||
|
|
49 | + end | ||
|
|
50 | + | ||
|
|
51 | + it "should produce timeout error correctly with fractional running time and fractional time limits" do | ||
|
|
52 | + @problem_test_timeout = stub(Problem, | ||
|
|
53 | + :id => 1, :name => 'test_timeout', | ||
|
|
54 | + :full_score => 20) | ||
|
|
55 | + grader_should(:grade => "test2_05sec.c", | ||
|
|
56 | + :on => @problem_test_timeout, | ||
|
|
57 | + :and_report => { | ||
|
|
58 | + :score => 10, | ||
|
|
59 | + :comment => '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 => /[^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 => /[^P]P/}) | ||
|
|
82 | + end | ||
|
|
83 | + | ||
|
|
84 | + it "should score test runs correctly when submission fails in some test case" do | ||
|
|
85 | + grader_should(:grade => "add_fail_test_case_1.c", | ||
|
|
86 | + :on => @problem_test_normal, | ||
|
|
87 | + :and_report => { | ||
|
|
88 | + :score => 105, | ||
|
|
89 | + :comment => /^.*(-|x).*$/}) | ||
|
|
90 | + end | ||
|
|
91 | + | ||
|
|
92 | + it "should fail submission with non-zero exit status" do | ||
|
|
93 | + grader_should(:grade => "add_nonzero_exit_status.c", | ||
|
|
94 | + :on => @problem_test_normal, | ||
|
|
95 | + :and_report => { | ||
|
|
96 | + :score => 0, | ||
|
|
97 | + :comment => /^(\[|x|\])+$/}) | ||
|
|
98 | + end | ||
|
|
99 | + | ||
|
|
100 | + it "should not allow malicious submission to see PROBLEM_HOME" do | ||
|
|
101 | + problem_test_yesno = stub(Problem, | ||
|
|
102 | + :id => 1, :name => 'test_yesno', | ||
|
|
103 | + :full_score => 10) | ||
|
|
104 | + grader_should(:grade => "yesno_access_problem_home.c", | ||
|
|
105 | + :on => problem_test_yesno, | ||
|
|
106 | + :and_report => { | ||
|
|
107 | + :score => 0, | ||
|
|
108 | + :comment => /(-|x)/}) | ||
|
|
109 | + end | ||
|
|
110 | + | ||
|
|
111 | + it "should not allow malicious submission to open files" do | ||
|
|
112 | + problem_test_yesno = stub(Problem, | ||
|
|
113 | + :id => 1, :name => 'test_yesno', | ||
|
|
114 | + :full_score => 10) | ||
|
|
115 | + grader_should(:grade => "yesno_open_file.c", | ||
|
|
116 | + :on => problem_test_yesno, | ||
|
|
117 | + :and_report => { | ||
|
|
118 | + :score => 0, | ||
|
|
119 | + :comment => /(-|x)/}) | ||
|
|
120 | + end | ||
|
|
121 | + | ||
|
|
122 | + def grader_should(args) | ||
|
|
123 | + @user1 = stub(User, | ||
|
|
124 | + :id => 1, :login => 'user1') | ||
|
|
125 | + submission = | ||
|
|
126 | + create_submission_from_file(1, @user1, args[:on], args[:grade]) | ||
|
|
127 | + submission.should_receive(:graded_at=) | ||
|
|
128 | + | ||
|
|
129 | + expected_score = args[:and_report][:score] | ||
|
|
130 | + expected_comment = args[:and_report][:comment] | ||
|
|
131 | + if args[:and_report][:compiler_message]!=nil | ||
|
|
132 | + expected_compiler_message = args[:and_report][:compiler_message] | ||
|
|
133 | + else | ||
|
|
134 | + expected_compiler_message = '' | ||
|
|
135 | + end | ||
|
|
136 | + | ||
|
|
137 | + submission.should_receive(:points=).with(expected_score) | ||
|
|
138 | + submission.should_receive(:grader_comment=).with(expected_comment) | ||
|
|
139 | + submission.should_receive(:compiler_message=).with(expected_compiler_message) | ||
|
|
140 | + submission.should_receive(:save) | ||
|
|
141 | + | ||
|
|
142 | + @engine.grade(submission) | ||
|
|
143 | + end | ||
|
|
144 | + | ||
|
|
145 | + protected | ||
|
|
146 | + | ||
|
|
147 | + def create_normal_submission_mock_from_file(source_fname) | ||
|
|
148 | + create_submission_from_file(1, @user_user1, @problem_test_normal, source_fname) | ||
|
|
149 | + end | ||
|
|
150 | + | ||
|
|
151 | + end | ||
|
|
152 | + | ||
|
|
153 | + describe "A grader engine, when grading test requests" do | ||
|
|
154 | + | ||
|
|
155 | + include GraderEngineHelperMethods | ||
|
|
156 | + | ||
|
|
157 | + before(:each) do | ||
|
|
158 | + @config = Grader::Configuration.get_instance | ||
|
|
159 | + @engine = Grader::Engine.new(Grader::TestRequestRoomMaker.new, | ||
|
|
160 | + Grader::TestRequestReporter.new) | ||
|
|
161 | + init_sandbox | ||
|
|
162 | + end | ||
|
|
163 | + | ||
|
|
164 | + it "should report error if there is no problem template" do | ||
|
|
165 | + problem = stub(Problem, | ||
|
|
166 | + :id => 1, :name => 'nothing') | ||
|
|
167 | + grader_should(:grade => 'test1_correct.c', | ||
|
|
168 | + :on => problem, | ||
|
|
169 | + :with => 'in1.txt', | ||
|
|
170 | + :and_report => { | ||
|
|
171 | + :graded_at= => nil, | ||
|
|
172 | + :compiler_message= => '', | ||
|
|
173 | + :grader_comment= => '', | ||
|
|
174 | + :running_stat= => /template not found/, | ||
|
|
175 | + :running_time= => nil, | ||
|
|
176 | + :exit_status= => nil, | ||
|
|
177 | + :memory_usage= => nil, | ||
|
|
178 | + :save => nil}) | ||
|
|
179 | + end | ||
|
|
180 | + | ||
|
|
181 | + it "should run test request and produce output file" do | ||
|
|
182 | + problem = stub(Problem, | ||
|
|
183 | + :id => 1, :name => 'test_normal') | ||
|
|
184 | + grader_should(:grade => 'test1_correct.c', | ||
|
|
185 | + :on => problem, | ||
|
|
186 | + :with => 'in1.txt', | ||
|
|
187 | + :and_report => { | ||
|
|
188 | + :graded_at= => nil, | ||
|
|
189 | + :compiler_message= => '', | ||
|
|
190 | + :grader_comment= => '', | ||
|
|
191 | + :running_stat= => /0.0\d* sec./, | ||
|
|
192 | + :output_file_name= => lambda { |fname| | ||
|
|
193 | + File.exists?(fname).should be_true | ||
|
|
194 | + }, | ||
|
|
195 | + :running_time= => nil, | ||
|
|
196 | + :exit_status= => nil, | ||
|
|
197 | + :memory_usage= => nil, | ||
|
|
198 | + :save => nil}) | ||
|
|
199 | + end | ||
|
|
200 | + | ||
|
|
201 | + it "should clean up problem directory after running test request" do | ||
|
|
202 | + problem = stub(Problem, | ||
|
|
203 | + :id => 1, :name => 'test_normal') | ||
|
|
204 | + grader_should(:grade => 'test1_correct.c', | ||
|
|
205 | + :on => problem, | ||
|
|
206 | + :with => 'in1.txt', | ||
|
|
207 | + :and_report => { | ||
|
|
208 | + :graded_at= => nil, | ||
|
|
209 | + :compiler_message= => '', | ||
|
|
210 | + :grader_comment= => '', | ||
|
|
211 | + :running_stat= => nil, | ||
|
|
212 | + :output_file_name= => nil, | ||
|
|
213 | + :running_time= => nil, | ||
|
|
214 | + :exit_status= => nil, | ||
|
|
215 | + :memory_usage= => nil, | ||
|
|
216 | + :save => nil}) | ||
|
|
217 | + File.exists?(@config.user_result_dir + "/test_request/test_normal/test_cases/1/input-1.txt").should be_false | ||
|
|
218 | + end | ||
|
|
219 | + | ||
|
|
220 | + it "should compile test request with error and report compilation error" do | ||
|
|
221 | + problem = stub(Problem, | ||
|
|
222 | + :id => 1, :name => 'test_normal') | ||
|
|
223 | + grader_should(:grade => 'test1_compile_error.c', | ||
|
|
224 | + :on => problem, | ||
|
|
225 | + :with => 'in1.txt', | ||
|
|
226 | + :and_report => { | ||
|
|
227 | + :graded_at= => nil, | ||
|
|
228 | + :compiler_message= => /.+/, | ||
|
|
229 | + :grader_comment= => /[Cc]ompil.*error/, | ||
|
|
230 | + :running_stat= => '', | ||
|
|
231 | + :save => nil}) | ||
|
|
232 | + end | ||
|
|
233 | + | ||
|
|
234 | + it "should report exit status" do | ||
|
|
235 | + problem = stub(Problem, | ||
|
|
236 | + :id => 1, :name => 'test_normal') | ||
|
|
237 | + grader_should(:grade => 'add_nonzero_exit_status.c', | ||
|
|
238 | + :on => problem, | ||
|
|
239 | + :with => 'in1.txt', | ||
|
|
240 | + :and_report => { | ||
|
|
241 | + :graded_at= => nil, | ||
|
|
242 | + :compiler_message= => '', | ||
|
|
243 | + :grader_comment= => '', | ||
|
|
244 | + :running_stat= => /[Ee]xit.*status.*10.*0\.0\d* sec/m, | ||
|
|
245 | + :output_file_name= => lambda { |fname| | ||
|
|
246 | + File.exists?(fname).should be_true | ||
|
|
247 | + }, | ||
|
|
248 | + :running_time= => nil, | ||
|
|
249 | + :exit_status= => /10/, | ||
|
|
250 | + :memory_usage= => nil, | ||
|
|
251 | + :save => nil}) | ||
|
|
252 | + end | ||
|
|
253 | + | ||
|
|
254 | + it "should produce running statistics for normal submission" do | ||
|
|
255 | + problem = stub(Problem, | ||
|
|
256 | + :id => 1, :name => 'test_normal') | ||
|
|
257 | + grader_should(:grade => 'test_run_stat.c', | ||
|
|
258 | + :on => problem, | ||
|
|
259 | + :with => 'in1.txt', | ||
|
|
260 | + :and_report => { | ||
|
|
261 | + :graded_at= => nil, | ||
|
|
262 | + :compiler_message= => '', | ||
|
|
263 | + :grader_comment= => '', | ||
|
|
264 | + :running_stat= => nil, | ||
|
|
265 | + :output_file_name= => lambda { |fname| | ||
|
|
266 | + File.exists?(fname).should be_true | ||
|
|
267 | + }, | ||
|
|
268 | + :running_time= => lambda { |t| | ||
|
|
269 | + (t>=0.14) and (t<=0.16) | ||
|
|
270 | + }, | ||
|
|
271 | + :exit_status= => nil, | ||
|
|
272 | + :memory_usage= => lambda { |s| | ||
|
|
273 | + (s>=500) and (s<=1000) | ||
|
|
274 | + }, | ||
|
|
275 | + :save => nil}) | ||
|
|
276 | + end | ||
|
|
277 | + | ||
|
|
278 | + protected | ||
|
|
279 | + def grader_should(args) | ||
|
|
280 | + @user1 = stub(User, | ||
|
|
281 | + :id => 1, :login => 'user1') | ||
|
|
282 | + | ||
|
|
283 | + problem = args[:on] | ||
|
|
284 | + input_file = @config.test_request_input_base_dir + "/" + args[:with] | ||
|
|
285 | + | ||
|
|
286 | + submission = | ||
|
|
287 | + create_submission_from_file(1, @user1, args[:on], args[:grade]) | ||
|
|
288 | + | ||
|
|
289 | + test_request = stub(TestRequest, | ||
|
|
290 | + :id => 1, | ||
|
|
291 | + :user => @user1, | ||
|
|
292 | + :problem => problem, | ||
|
|
293 | + :submission => submission, | ||
|
|
294 | + :input_file_name => input_file, | ||
|
|
295 | + :language => submission.language, | ||
|
|
296 | + :problem_name => problem.name) | ||
|
|
297 | + | ||
|
|
298 | + expectations = args[:and_report] | ||
|
|
299 | + | ||
|
|
300 | + expectations.each do |key,val| | ||
|
|
301 | + if val==nil | ||
|
|
302 | + test_request.should_receive(key) | ||
|
|
303 | + elsif val.class == Proc | ||
|
|
304 | + test_request.should_receive(key) { |fname| | ||
|
|
305 | + val.call(fname) | ||
|
|
306 | + } | ||
|
|
307 | + else | ||
|
|
308 | + test_request.should_receive(key).with(val) | ||
|
|
309 | + end | ||
|
|
310 | + end | ||
|
|
311 | + | ||
|
|
312 | + @engine.grade(test_request) | ||
|
|
313 | + end | ||
|
|
314 | + | ||
|
|
315 | + end | ||
|
|
316 | + |
@@ -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 | + FileUtils.mkdir_p 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,152 | |||||
|
|
1 | + require 'test/unit' | ||
|
|
2 | + require 'rubygems' | ||
|
|
3 | + require 'mocha' | ||
|
|
4 | + | ||
|
|
5 | + require File.join(File.dirname(__FILE__),'test_helper') | ||
|
|
6 | + | ||
|
|
7 | + class GraderEngineTest < UnitTest.TestCase | ||
|
|
8 | + | ||
|
|
9 | + def setup | ||
|
|
10 | + @@lang_c = stub(:name => 'c', :ext => 'c') | ||
|
|
11 | + @@lang_cpp = stub(:name => 'cpp', :ext => 'cpp') | ||
|
|
12 | + @@lang_pas = stub(:name => 'pas', :ext => 'pas') | ||
|
|
13 | + | ||
|
|
14 | + @config = Grader::Configuration.get_instance | ||
|
|
15 | + | ||
|
|
16 | + @problem_test1 = stub(:id => 1, :name => 'test1', :full_score => 135) | ||
|
|
17 | + @user_user1 = stub(:id => 1, :login => 'user1') | ||
|
|
18 | + | ||
|
|
19 | + @engine = Grader::Engine.new | ||
|
|
20 | + | ||
|
|
21 | + init_sandbox | ||
|
|
22 | + end | ||
|
|
23 | + | ||
|
|
24 | + def teardown | ||
|
|
25 | + end | ||
|
|
26 | + | ||
|
|
27 | + def test_grade_oldest_task_with_no_submission | ||
|
|
28 | + Task.expects(:get_inqueue_and_change_status).returns(nil) | ||
|
|
29 | + assert_equal nil, @engine.grade_oldest_task, 'should return nil when there is no task' | ||
|
|
30 | + end | ||
|
|
31 | + | ||
|
|
32 | + def test_normal_submission | ||
|
|
33 | + submission = create_test1_submission_mock_from_file("test1_correct.c") | ||
|
|
34 | + | ||
|
|
35 | + submission.expects(:graded_at=) | ||
|
|
36 | + submission.expects(:points=).with(135) | ||
|
|
37 | + submission.expects(:grader_comment=).with do |value| | ||
|
|
38 | + /^PASSED/.match(value) | ||
|
|
39 | + end | ||
|
|
40 | + submission.expects(:compiler_message=).with('') | ||
|
|
41 | + submission.expects(:save) | ||
|
|
42 | + | ||
|
|
43 | + @engine.grade(submission) | ||
|
|
44 | + end | ||
|
|
45 | + | ||
|
|
46 | + def test_compile_error_submission | ||
|
|
47 | + submission = create_test1_submission_mock_from_file("test1_compile_error.c") | ||
|
|
48 | + | ||
|
|
49 | + submission.expects(:graded_at=) | ||
|
|
50 | + submission.expects(:points=).with(0) | ||
|
|
51 | + submission.expects(:grader_comment=).with('FAILED: compile error') | ||
|
|
52 | + submission.expects(:compiler_message=) do |value| | ||
|
|
53 | + /[Ee]rror/.match value | ||
|
|
54 | + end | ||
|
|
55 | + submission.expects(:save) | ||
|
|
56 | + | ||
|
|
57 | + @engine.grade(submission) | ||
|
|
58 | + end | ||
|
|
59 | + | ||
|
|
60 | + def test_timeout_submission | ||
|
|
61 | + @problem_test2 = stub(:id => 1, :name => 'test2', :full_score => 10) | ||
|
|
62 | + @user_user1 = stub(:id => 1, :login => 'user1') | ||
|
|
63 | + | ||
|
|
64 | + submission = create_submission_from_file(1, @user_user1, @problem_test2, | ||
|
|
65 | + "test2_timeout.c") | ||
|
|
66 | + | ||
|
|
67 | + submission.expects(:graded_at=) | ||
|
|
68 | + submission.expects(:points=).with(0) | ||
|
|
69 | + submission.expects(:grader_comment=).with do |value| | ||
|
|
70 | + /^FAILED: TT$/.match value | ||
|
|
71 | + end | ||
|
|
72 | + submission.expects(:compiler_message=).with('') | ||
|
|
73 | + submission.expects(:save) | ||
|
|
74 | + | ||
|
|
75 | + @engine.grade(submission) | ||
|
|
76 | + end | ||
|
|
77 | + | ||
|
|
78 | + def test_timeout_submission_running_one_and_a_half_second | ||
|
|
79 | + @problem_test2 = stub(:id => 1, :name => 'test2', :full_score => 20) | ||
|
|
80 | + @user_user1 = stub(:id => 1, :login => 'user1') | ||
|
|
81 | + | ||
|
|
82 | + submission = create_submission_from_file(1, @user_user1, @problem_test2, | ||
|
|
83 | + "test2_1-5sec.c") | ||
|
|
84 | + | ||
|
|
85 | + submission.expects(:graded_at=) | ||
|
|
86 | + submission.expects(:points=).with(10) | ||
|
|
87 | + submission.expects(:grader_comment=).with do |value| | ||
|
|
88 | + /^FAILED: TP$/.match value | ||
|
|
89 | + end | ||
|
|
90 | + submission.expects(:compiler_message=).with('') | ||
|
|
91 | + submission.expects(:save) | ||
|
|
92 | + | ||
|
|
93 | + @engine.grade(submission) | ||
|
|
94 | + end | ||
|
|
95 | + | ||
|
|
96 | + def test_grade_oldest_task | ||
|
|
97 | + # mock submission | ||
|
|
98 | + submission = create_test1_submission_mock_from_file("test1_correct.c") | ||
|
|
99 | + | ||
|
|
100 | + submission.expects(:graded_at=) | ||
|
|
101 | + submission.expects(:points=).with(135) | ||
|
|
102 | + submission.expects(:grader_comment=).with do |value| | ||
|
|
103 | + /^PASSED/.match(value) | ||
|
|
104 | + end | ||
|
|
105 | + submission.expects(:compiler_message=).with('') | ||
|
|
106 | + submission.expects(:save) | ||
|
|
107 | + | ||
|
|
108 | + # mock task | ||
|
|
109 | + task = stub(:id => 1, :submission_id => submission.id) | ||
|
|
110 | + Task.expects(:get_inqueue_and_change_status).returns(task) | ||
|
|
111 | + task.expects(:status_complete!) | ||
|
|
112 | + | ||
|
|
113 | + # mock Submission | ||
|
|
114 | + Submission.expects(:find).with(task.submission_id).returns(submission) | ||
|
|
115 | + | ||
|
|
116 | + @engine.grade_oldest_task | ||
|
|
117 | + end | ||
|
|
118 | + | ||
|
|
119 | + def test_grade_oldest_task_with_grader_process | ||
|
|
120 | + grader_process = stub | ||
|
|
121 | + grader_process.expects(:report_active) | ||
|
|
122 | + | ||
|
|
123 | + @engine = Grader::Engine.new(grader_process) | ||
|
|
124 | + | ||
|
|
125 | + test_grade_oldest_task | ||
|
|
126 | + end | ||
|
|
127 | + | ||
|
|
128 | + protected | ||
|
|
129 | + | ||
|
|
130 | + def clear_sandbox | ||
|
|
131 | + clear_cmd = "rm -rf #{@config.test_sandbox_dir}/*" | ||
|
|
132 | + system(clear_cmd) | ||
|
|
133 | + end | ||
|
|
134 | + | ||
|
|
135 | + def init_sandbox | ||
|
|
136 | + clear_sandbox | ||
|
|
137 | + Dir.mkdir @config.user_result_dir | ||
|
|
138 | + cp_cmd = "cp -R #{@config.test_data_dir}/ev #{@config.test_sandbox_dir}" | ||
|
|
139 | + system(cp_cmd) | ||
|
|
140 | + end | ||
|
|
141 | + | ||
|
|
142 | + def create_submission_from_file(id, user, problem, source_fname, language = @@lang_c) | ||
|
|
143 | + source = File.open(@config.test_data_dir + "/" + source_fname).read | ||
|
|
144 | + stub(:id => id, :user => user, :problem => problem, | ||
|
|
145 | + :source => source, :language => language) | ||
|
|
146 | + end | ||
|
|
147 | + | ||
|
|
148 | + def create_test1_submission_mock_from_file(source_fname) | ||
|
|
149 | + create_submission_from_file(1, @user_user1, @problem_test1, source_fname) | ||
|
|
150 | + end | ||
|
|
151 | + | ||
|
|
152 | + end |
@@ -0,0 +1,40 | |||||
|
|
1 | + require 'test/unit' | ||
|
|
2 | + require 'rubygems' | ||
|
|
3 | + require 'mocha' | ||
|
|
4 | + | ||
|
|
5 | + require File.join(File.dirname(__FILE__),'../lib/import_helper') | ||
|
|
6 | + | ||
|
|
7 | + class ImportHelperTest < Test::Unit::TestCase | ||
|
|
8 | + | ||
|
|
9 | + def setup | ||
|
|
10 | + end | ||
|
|
11 | + | ||
|
|
12 | + def teardown | ||
|
|
13 | + end | ||
|
|
14 | + | ||
|
|
15 | + def test_build_only_singleton_testruns | ||
|
|
16 | + testrun_info = build_testrun_info(3,['1','2','3']) | ||
|
|
17 | + assert_equal [[[1,'1']],[[2,'2']],[[3,'3']]], testrun_info, 'should build singleton testruns' | ||
|
|
18 | + end | ||
|
|
19 | + | ||
|
|
20 | + def test_build_only_singleton_testruns2 | ||
|
|
21 | + testrun_info = build_testrun_info(4,['1','2','3','4']) | ||
|
|
22 | + assert_equal [[[1,'1']],[[2,'2']],[[3,'3']],[[4,'4']]], testrun_info, 'should build singleton testruns' | ||
|
|
23 | + end | ||
|
|
24 | + | ||
|
|
25 | + def test_build_testruns_when_testcases_defined_by_appending_alphabets | ||
|
|
26 | + testrun_info = build_testrun_info(4,['1a','1b','2','3a','3b','4']) | ||
|
|
27 | + assert_equal [[[1,'1a'],[2,'1b']], | ||
|
|
28 | + [[3,'2']], | ||
|
|
29 | + [[4,'3a'],[5,'3b']], | ||
|
|
30 | + [[6,'4']]], testrun_info | ||
|
|
31 | + end | ||
|
|
32 | + | ||
|
|
33 | + def test_build_testruns_when_testcases_defined_by_appending_dashed_numbers | ||
|
|
34 | + testrun_info = build_testrun_info(4,['1-1','1-2','2','3-1','3-2','4']) | ||
|
|
35 | + assert_equal [[[1,'1-1'],[2,'1-2']], | ||
|
|
36 | + [[3,'2']], | ||
|
|
37 | + [[4,'3-1'],[5,'3-2']], | ||
|
|
38 | + [[6,'4']]], testrun_info | ||
|
|
39 | + end | ||
|
|
40 | + end |
@@ -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 |
@@ -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 |
You need to be logged in to leave comments.
Login now