Description:
locks dir based on temp file, does not copy dir when copying scripts, added proper rescue for ln_s
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r103:933325ce824a - - 4 files changed: 4 inserted, 3 deleted

@@ -1,109 +1,109
1 require 'ftools'
1 require 'ftools'
2
2
3 # DirInit::Manager handles directory initialization and clean-up when
3 # DirInit::Manager handles directory initialization and clean-up when
4 # there are many concurrent processes that wants to modify the
4 # there are many concurrent processes that wants to modify the
5 # directory in the same way.
5 # directory in the same way.
6 #
6 #
7 # An example usage is when each process wants to copy some temporary
7 # An example usage is when each process wants to copy some temporary
8 # files to the directory and delete these files after finishing its
8 # files to the directory and delete these files after finishing its
9 # job. Problems may occur when the first process delete the files
9 # job. Problems may occur when the first process delete the files
10 # while the second process is still using the files.
10 # while the second process is still using the files.
11 #
11 #
12 # This library maintain a reference counter on the processes using the
12 # This library maintain a reference counter on the processes using the
13 # directory. It locks the dir to manage critical section when
13 # directory. It locks the dir to manage critical section when
14 # updating the reference counter.
14 # updating the reference counter.
15
15
16 module DirInit
16 module DirInit
17
17
18 class Manager
18 class Manager
19
19
20 def initialize(dir_name, usage_filename='.usage_counter')
20 def initialize(dir_name, usage_filename='.usage_counter')
21 @dir_name = dir_name
21 @dir_name = dir_name
22 @usage_filename = usage_filename
22 @usage_filename = usage_filename
23 end
23 end
24
24
25 # Check if someone has initialized the dir. If not, call block.
25 # Check if someone has initialized the dir. If not, call block.
26
26
27 def setup # :yields: block
27 def setup # :yields: block
28 - dir = File.new(@dir_name)
28 + dir = File.new(@dir_name + '/lockfile',"w+")
29 dir.flock(File::LOCK_EX)
29 dir.flock(File::LOCK_EX)
30 begin
30 begin
31 counter_filename = get_counter_filename
31 counter_filename = get_counter_filename
32 if File.exist? counter_filename
32 if File.exist? counter_filename
33 # someone is here
33 # someone is here
34 f = File.new(counter_filename,"r+")
34 f = File.new(counter_filename,"r+")
35 counter = f.read.to_i
35 counter = f.read.to_i
36 f.seek(0)
36 f.seek(0)
37 f.write("#{counter+1}\n")
37 f.write("#{counter+1}\n")
38 f.close
38 f.close
39 else
39 else
40 # i'm the first, create the counter file
40 # i'm the first, create the counter file
41 counter = 0
41 counter = 0
42 f = File.new(counter_filename,"w")
42 f = File.new(counter_filename,"w")
43 f.write("1\n")
43 f.write("1\n")
44 f.close
44 f.close
45 end
45 end
46
46
47 # if no one is here
47 # if no one is here
48 if counter == 0
48 if counter == 0
49 if block_given?
49 if block_given?
50 yield
50 yield
51 end
51 end
52 end
52 end
53
53
54 rescue
54 rescue
55 raise
55 raise
56
56
57 ensure
57 ensure
58 # make sure it unlock the directory
58 # make sure it unlock the directory
59 dir.flock(File::LOCK_UN)
59 dir.flock(File::LOCK_UN)
60 dir.close
60 dir.close
61 end
61 end
62 end
62 end
63
63
64 # Check if I am the last one using the dir. If true, call block.
64 # Check if I am the last one using the dir. If true, call block.
65
65
66 def teardown
66 def teardown
67 dir = File.new(@dir_name)
67 dir = File.new(@dir_name)
68 dir.flock(File::LOCK_EX)
68 dir.flock(File::LOCK_EX)
69 begin
69 begin
70 counter_filename = get_counter_filename
70 counter_filename = get_counter_filename
71 if File.exist? counter_filename
71 if File.exist? counter_filename
72 # someone is here
72 # someone is here
73 f = File.new(counter_filename,"r+")
73 f = File.new(counter_filename,"r+")
74 counter = f.read.to_i
74 counter = f.read.to_i
75 f.seek(0)
75 f.seek(0)
76 f.write("#{counter-1}\n")
76 f.write("#{counter-1}\n")
77 f.close
77 f.close
78
78
79 if counter == 1
79 if counter == 1
80 # i'm the last one
80 # i'm the last one
81
81
82 File.delete(counter_filename)
82 File.delete(counter_filename)
83 if block_given?
83 if block_given?
84 yield
84 yield
85 end
85 end
86 end
86 end
87 else
87 else
88 # This is BAD
88 # This is BAD
89 raise "Error: reference count missing"
89 raise "Error: reference count missing"
90 end
90 end
91
91
92 rescue
92 rescue
93 raise
93 raise
94
94
95 ensure
95 ensure
96 # make sure it unlock the directory
96 # make sure it unlock the directory
97 dir.flock(File::LOCK_UN)
97 dir.flock(File::LOCK_UN)
98 dir.close
98 dir.close
99 end
99 end
100 end
100 end
101
101
102 protected
102 protected
103
103
104 def get_counter_filename
104 def get_counter_filename
105 return File.join(@dir_name,@usage_filename)
105 return File.join(@dir_name,@usage_filename)
106 end
106 end
107
107
108 end
108 end
109 end
109 end
@@ -1,186 +1,187
1 require 'fileutils'
1 require 'fileutils'
2 require File.join(File.dirname(__FILE__),'dir_init')
2 require File.join(File.dirname(__FILE__),'dir_init')
3
3
4 module Grader
4 module Grader
5
5
6 #
6 #
7 # A grader engine grades a submission, against anything: a test
7 # A grader engine grades a submission, against anything: a test
8 # data, or a user submitted test data. It uses two helpers objects:
8 # data, or a user submitted test data. It uses two helpers objects:
9 # room_maker and reporter.
9 # room_maker and reporter.
10 #
10 #
11 class Engine
11 class Engine
12
12
13 attr_writer :room_maker
13 attr_writer :room_maker
14 attr_writer :reporter
14 attr_writer :reporter
15
15
16 def initialize(options={})
16 def initialize(options={})
17 # default options
17 # default options
18 if not options.include? :room_maker
18 if not options.include? :room_maker
19 options[:room_maker] = Grader::SubmissionRoomMaker.new
19 options[:room_maker] = Grader::SubmissionRoomMaker.new
20 end
20 end
21 if not options.include? :reporter
21 if not options.include? :reporter
22 options[:reporter] = Grader::SubmissionReporter.new
22 options[:reporter] = Grader::SubmissionReporter.new
23 end
23 end
24
24
25 @config = Grader::Configuration.get_instance
25 @config = Grader::Configuration.get_instance
26
26
27 @room_maker = options[:room_maker]
27 @room_maker = options[:room_maker]
28 @reporter = options[:reporter]
28 @reporter = options[:reporter]
29 end
29 end
30
30
31 # takes a submission, asks room_maker to produce grading directories,
31 # takes a submission, asks room_maker to produce grading directories,
32 # calls grader scripts, and asks reporter to save the result
32 # calls grader scripts, and asks reporter to save the result
33 def grade(submission)
33 def grade(submission)
34 current_dir = FileUtils.pwd
34 current_dir = FileUtils.pwd
35
35
36 user = submission.user
36 user = submission.user
37 problem = submission.problem
37 problem = submission.problem
38
38
39 # TODO: will have to create real exception for this
39 # TODO: will have to create real exception for this
40 if user==nil or problem == nil
40 if user==nil or problem == nil
41 @reporter.report_error(submission,"Grading error: problem with submission")
41 @reporter.report_error(submission,"Grading error: problem with submission")
42 #raise "engine: user or problem is nil"
42 #raise "engine: user or problem is nil"
43 end
43 end
44
44
45 # TODO: this is another hack so that output only task can be judged
45 # TODO: this is another hack so that output only task can be judged
46 if submission.language!=nil
46 if submission.language!=nil
47 language = submission.language.name
47 language = submission.language.name
48 lang_ext = submission.language.ext
48 lang_ext = submission.language.ext
49 else
49 else
50 language = 'c'
50 language = 'c'
51 lang_ext = 'c'
51 lang_ext = 'c'
52 end
52 end
53
53
54 # FIX THIS
54 # FIX THIS
55 talk 'some hack on language'
55 talk 'some hack on language'
56 if language == 'cpp'
56 if language == 'cpp'
57 language = 'c++'
57 language = 'c++'
58 end
58 end
59
59
60 # COMMENT: should it be only source.ext?
60 # COMMENT: should it be only source.ext?
61 if problem!=nil
61 if problem!=nil
62 source_name = "#{problem.name}.#{lang_ext}"
62 source_name = "#{problem.name}.#{lang_ext}"
63 else
63 else
64 source_name = "source.#{lang_ext}"
64 source_name = "source.#{lang_ext}"
65 end
65 end
66
66
67 begin
67 begin
68 grading_dir = @room_maker.produce_grading_room(submission)
68 grading_dir = @room_maker.produce_grading_room(submission)
69 @room_maker.save_source(submission,source_name)
69 @room_maker.save_source(submission,source_name)
70 problem_home = @room_maker.find_problem_home(submission)
70 problem_home = @room_maker.find_problem_home(submission)
71
71
72 # puts "GRADING DIR: #{grading_dir}"
72 # puts "GRADING DIR: #{grading_dir}"
73 # puts "PROBLEM DIR: #{problem_home}"
73 # puts "PROBLEM DIR: #{problem_home}"
74
74
75 if !FileTest.exist?(problem_home)
75 if !FileTest.exist?(problem_home)
76 raise "No test data."
76 raise "No test data."
77 end
77 end
78
78
79 dinit = DirInit::Manager.new(problem_home)
79 dinit = DirInit::Manager.new(problem_home)
80
80
81 dinit.setup do
81 dinit.setup do
82 copy_log = copy_script(problem_home)
82 copy_log = copy_script(problem_home)
83 save_copy_log(problem_home,copy_log)
83 save_copy_log(problem_home,copy_log)
84 end
84 end
85
85
86 call_judge(problem_home,language,grading_dir,source_name)
86 call_judge(problem_home,language,grading_dir,source_name)
87
87
88 @reporter.report(submission,"#{grading_dir}/test-result")
88 @reporter.report(submission,"#{grading_dir}/test-result")
89
89
90 dinit.teardown do
90 dinit.teardown do
91 copy_log = load_copy_log(problem_home)
91 copy_log = load_copy_log(problem_home)
92 clear_copy_log(problem_home)
92 clear_copy_log(problem_home)
93 clear_script(copy_log,problem_home)
93 clear_script(copy_log,problem_home)
94 end
94 end
95
95
96 rescue RuntimeError => msg
96 rescue RuntimeError => msg
97 @reporter.report_error(submission, msg)
97 @reporter.report_error(submission, msg)
98
98
99 ensure
99 ensure
100 @room_maker.clean_up(submission)
100 @room_maker.clean_up(submission)
101 Dir.chdir(current_dir) # this is really important
101 Dir.chdir(current_dir) # this is really important
102 end
102 end
103 end
103 end
104
104
105 protected
105 protected
106
106
107 def talk(str)
107 def talk(str)
108 if @config.talkative
108 if @config.talkative
109 puts str
109 puts str
110 end
110 end
111 end
111 end
112
112
113 def call_judge(problem_home,language,grading_dir,fname)
113 def call_judge(problem_home,language,grading_dir,fname)
114 ENV['PROBLEM_HOME'] = problem_home
114 ENV['PROBLEM_HOME'] = problem_home
115
115
116 talk grading_dir
116 talk grading_dir
117 Dir.chdir grading_dir
117 Dir.chdir grading_dir
118 cmd = "#{problem_home}/script/judge #{language} #{fname}"
118 cmd = "#{problem_home}/script/judge #{language} #{fname}"
119 talk "CMD: #{cmd}"
119 talk "CMD: #{cmd}"
120 system(cmd)
120 system(cmd)
121 end
121 end
122
122
123 def get_std_script_dir
123 def get_std_script_dir
124 GRADER_ROOT + '/std-script'
124 GRADER_ROOT + '/std-script'
125 end
125 end
126
126
127 def copy_script(problem_home)
127 def copy_script(problem_home)
128 script_dir = "#{problem_home}/script"
128 script_dir = "#{problem_home}/script"
129 std_script_dir = get_std_script_dir
129 std_script_dir = get_std_script_dir
130
130
131 raise "std-script directory not found" if !FileTest.exist?(std_script_dir)
131 raise "std-script directory not found" if !FileTest.exist?(std_script_dir)
132
132
133 scripts = Dir[std_script_dir + '/*']
133 scripts = Dir[std_script_dir + '/*']
134
134
135 copied = []
135 copied = []
136
136
137 scripts.each do |s|
137 scripts.each do |s|
138 fname = File.basename(s)
138 fname = File.basename(s)
139 + next if FileTest.directory?(s)
139 if !FileTest.exist?("#{script_dir}/#{fname}")
140 if !FileTest.exist?("#{script_dir}/#{fname}")
140 copied << fname
141 copied << fname
141 FileUtils.cp(s, "#{script_dir}")
142 FileUtils.cp(s, "#{script_dir}")
142 end
143 end
143 end
144 end
144
145
145 return copied
146 return copied
146 end
147 end
147
148
148 def copy_log_filename(problem_home)
149 def copy_log_filename(problem_home)
149 return File.join(problem_home, '.scripts_copied')
150 return File.join(problem_home, '.scripts_copied')
150 end
151 end
151
152
152 def save_copy_log(problem_home, log)
153 def save_copy_log(problem_home, log)
153 f = File.new(copy_log_filename(problem_home),"w")
154 f = File.new(copy_log_filename(problem_home),"w")
154 log.each do |fname|
155 log.each do |fname|
155 f.write("#{fname}\n")
156 f.write("#{fname}\n")
156 end
157 end
157 f.close
158 f.close
158 end
159 end
159
160
160 def load_copy_log(problem_home)
161 def load_copy_log(problem_home)
161 f = File.new(copy_log_filename(problem_home),"r")
162 f = File.new(copy_log_filename(problem_home),"r")
162 log = []
163 log = []
163 f.readlines.each do |line|
164 f.readlines.each do |line|
164 log << line.strip
165 log << line.strip
165 end
166 end
166 f.close
167 f.close
167 log
168 log
168 end
169 end
169
170
170 def clear_copy_log(problem_home)
171 def clear_copy_log(problem_home)
171 File.delete(copy_log_filename(problem_home))
172 File.delete(copy_log_filename(problem_home))
172 end
173 end
173
174
174 def clear_script(log,problem_home)
175 def clear_script(log,problem_home)
175 log.each do |s|
176 log.each do |s|
176 FileUtils.rm("#{problem_home}/script/#{s}")
177 FileUtils.rm("#{problem_home}/script/#{s}")
177 end
178 end
178 end
179 end
179
180
180 def mkdir_if_does_not_exist(dirname)
181 def mkdir_if_does_not_exist(dirname)
181 Dir.mkdir(dirname) if !FileTest.exist?(dirname)
182 Dir.mkdir(dirname) if !FileTest.exist?(dirname)
182 end
183 end
183
184
184 end
185 end
185
186
186 end
187 end
@@ -1,251 +1,251
1 #
1 #
2 # This part contains various test_request helpers for interfacing
2 # This part contains various test_request helpers for interfacing
3 # with Grader::Engine. There are TestRequestRoomMaker and
3 # with Grader::Engine. There are TestRequestRoomMaker and
4 # TestRequestReporter.
4 # TestRequestReporter.
5
5
6 module Grader
6 module Grader
7
7
8 def self.link_or_copy(src, des)
8 def self.link_or_copy(src, des)
9 begin
9 begin
10 FileUtils.ln_s(src, des)
10 FileUtils.ln_s(src, des)
11 - rescue
11 + rescue NotImplementedError
12 FileUtils.cp(src,des)
12 FileUtils.cp(src,des)
13 end
13 end
14 end
14 end
15
15
16 def self.call_and_log(error_message)
16 def self.call_and_log(error_message)
17 begin
17 begin
18 yield
18 yield
19 rescue
19 rescue
20 msg = "ERROR: #{error_message}"
20 msg = "ERROR: #{error_message}"
21 raise msg
21 raise msg
22 end
22 end
23 end
23 end
24
24
25 #
25 #
26 # A TestRequestRoomMaker is a helper object for Engine
26 # A TestRequestRoomMaker is a helper object for Engine
27 # - finds grading room: in user_result_dir/(user)/test_request/ ...
27 # - finds grading room: in user_result_dir/(user)/test_request/ ...
28 # - prepare problem configuration for grading --- basically it copy
28 # - prepare problem configuration for grading --- basically it copy
29 # all config files, and copy user's input into the testcase
29 # all config files, and copy user's input into the testcase
30 # directory. First, it finds the template from problem template
30 # directory. First, it finds the template from problem template
31 # directory; if it can't find a template, it'll use the template
31 # directory; if it can't find a template, it'll use the template
32 # from default template.
32 # from default template.
33 class TestRequestRoomMaker
33 class TestRequestRoomMaker
34 def initialize
34 def initialize
35 @config = Grader::Configuration.get_instance
35 @config = Grader::Configuration.get_instance
36 end
36 end
37
37
38 def produce_grading_room(test_request)
38 def produce_grading_room(test_request)
39 grading_room = grading_room_dir(test_request)
39 grading_room = grading_room_dir(test_request)
40 FileUtils.mkdir_p(grading_room)
40 FileUtils.mkdir_p(grading_room)
41
41
42 #
42 #
43 # Also copy additional submitted file to this directory as well.
43 # Also copy additional submitted file to this directory as well.
44 # The program would see this file only if it is copied
44 # The program would see this file only if it is copied
45 # to the sandbox directory later. The run script should do it.
45 # to the sandbox directory later. The run script should do it.
46 #
46 #
47 if FileTest.exists?("#{test_request.input_file_name}.files")
47 if FileTest.exists?("#{test_request.input_file_name}.files")
48 FileUtils.cp_r("#{test_request.input_file_name}.files/.",
48 FileUtils.cp_r("#{test_request.input_file_name}.files/.",
49 "#{grading_room}")
49 "#{grading_room}")
50 end
50 end
51
51
52 grading_room
52 grading_room
53 end
53 end
54
54
55 def find_problem_home(test_request)
55 def find_problem_home(test_request)
56 problem_name = test_request.problem_name
56 problem_name = test_request.problem_name
57
57
58 template_dir = "#{@config.test_request_problem_templates_dir}/" + problem_name
58 template_dir = "#{@config.test_request_problem_templates_dir}/" + problem_name
59
59
60 raise "Test Request: error template not found" if !File.exists?(template_dir)
60 raise "Test Request: error template not found" if !File.exists?(template_dir)
61
61
62 problem_home = problem_home_dir(test_request)
62 problem_home = problem_home_dir(test_request)
63 FileUtils.mkdir_p(problem_home)
63 FileUtils.mkdir_p(problem_home)
64
64
65 copy_problem_template(template_dir,problem_home)
65 copy_problem_template(template_dir,problem_home)
66 link_input_file(test_request,problem_home)
66 link_input_file(test_request,problem_home)
67
67
68 problem_home
68 problem_home
69 end
69 end
70
70
71 def save_source(test_request,source_name)
71 def save_source(test_request,source_name)
72 dir = self.produce_grading_room(test_request)
72 dir = self.produce_grading_room(test_request)
73 submission = test_request.submission
73 submission = test_request.submission
74 f = File.open("#{dir}/#{source_name}","w")
74 f = File.open("#{dir}/#{source_name}","w")
75 f.write(submission.source)
75 f.write(submission.source)
76 f.close
76 f.close
77 end
77 end
78
78
79 def clean_up(test_request)
79 def clean_up(test_request)
80 problem_home = problem_home_dir(test_request)
80 problem_home = problem_home_dir(test_request)
81 remove_data_files(problem_home)
81 remove_data_files(problem_home)
82 end
82 end
83
83
84 protected
84 protected
85 def grading_room_dir(test_request)
85 def grading_room_dir(test_request)
86 problem_name = test_request.problem_name
86 problem_name = test_request.problem_name
87 user = test_request.user
87 user = test_request.user
88 grading_room = "#{@config.user_result_dir}" +
88 grading_room = "#{@config.user_result_dir}" +
89 "/#{user.login}/test_request" +
89 "/#{user.login}/test_request" +
90 "/#{problem_name}/#{test_request.id}"
90 "/#{problem_name}/#{test_request.id}"
91 grading_room
91 grading_room
92 end
92 end
93
93
94 def problem_home_dir(test_request)
94 def problem_home_dir(test_request)
95 problem_name = test_request.problem_name
95 problem_name = test_request.problem_name
96 user = test_request.user
96 user = test_request.user
97 "#{@config.user_result_dir}" +
97 "#{@config.user_result_dir}" +
98 "/#{user.login}/test_request/#{problem_name}"
98 "/#{user.login}/test_request/#{problem_name}"
99 end
99 end
100
100
101 def copy_problem_template(template_dir,problem_home)
101 def copy_problem_template(template_dir,problem_home)
102 Grader::call_and_log("Test Request: cannot copy problem template") {
102 Grader::call_and_log("Test Request: cannot copy problem template") {
103 FileUtils.cp_r("#{template_dir}/.","#{problem_home}")
103 FileUtils.cp_r("#{template_dir}/.","#{problem_home}")
104 }
104 }
105 end
105 end
106
106
107 def link_input_file(test_request, problem_home)
107 def link_input_file(test_request, problem_home)
108 input_fname = "#{test_request.input_file_name}"
108 input_fname = "#{test_request.input_file_name}"
109 if !File.exists?(input_fname)
109 if !File.exists?(input_fname)
110 raise "Test Request: input file not found."
110 raise "Test Request: input file not found."
111 end
111 end
112
112
113 input_fname_problem_home = "#{problem_home}/test_cases/1/input-1.txt"
113 input_fname_problem_home = "#{problem_home}/test_cases/1/input-1.txt"
114 if File.exists?(input_fname_problem_home)
114 if File.exists?(input_fname_problem_home)
115 FileUtils.rm([input_fname_problem_home], :force => true)
115 FileUtils.rm([input_fname_problem_home], :force => true)
116 end
116 end
117
117
118 Grader::link_or_copy("#{input_fname}", "#{input_fname_problem_home}")
118 Grader::link_or_copy("#{input_fname}", "#{input_fname_problem_home}")
119 end
119 end
120
120
121 def remove_data_files(problem_home)
121 def remove_data_files(problem_home)
122 if File.exists?("#{problem_home}/test_cases/1/input-1.txt")
122 if File.exists?("#{problem_home}/test_cases/1/input-1.txt")
123 Grader::call_and_log("Test Request: cannot remove data files") {
123 Grader::call_and_log("Test Request: cannot remove data files") {
124 FileUtils.rm Dir.glob("#{problem_home}/test_cases/1/*")
124 FileUtils.rm Dir.glob("#{problem_home}/test_cases/1/*")
125 }
125 }
126 end
126 end
127 end
127 end
128
128
129 end
129 end
130
130
131 class TestRequestReporter
131 class TestRequestReporter
132 def initialize
132 def initialize
133 @config = Grader::Configuration.get_instance
133 @config = Grader::Configuration.get_instance
134 end
134 end
135
135
136 def report(test_request,test_result_dir)
136 def report(test_request,test_result_dir)
137 save_result(test_request,read_result(test_result_dir))
137 save_result(test_request,read_result(test_result_dir))
138 end
138 end
139
139
140 def report_error(test_request, msg)
140 def report_error(test_request, msg)
141 save_result(test_request, {:running_stat => {
141 save_result(test_request, {:running_stat => {
142 :msg => "#{msg}",
142 :msg => "#{msg}",
143 :running_time => nil,
143 :running_time => nil,
144 :exit_status => "Some error occured. Program did not run",
144 :exit_status => "Some error occured. Program did not run",
145 :memory_usage => nil
145 :memory_usage => nil
146 }})
146 }})
147 end
147 end
148
148
149 protected
149 protected
150 def read_result(test_result_dir)
150 def read_result(test_result_dir)
151 # TODO:
151 # TODO:
152 cmp_msg_fname = "#{test_result_dir}/compiler_message"
152 cmp_msg_fname = "#{test_result_dir}/compiler_message"
153 cmp_file = File.open(cmp_msg_fname)
153 cmp_file = File.open(cmp_msg_fname)
154 cmp_msg = cmp_file.read
154 cmp_msg = cmp_file.read
155 cmp_file.close
155 cmp_file.close
156
156
157 result_file_name = "#{test_result_dir}/1/result"
157 result_file_name = "#{test_result_dir}/1/result"
158
158
159 if File.exists?(result_file_name)
159 if File.exists?(result_file_name)
160 output_file_name = "#{test_result_dir}/1/output.txt"
160 output_file_name = "#{test_result_dir}/1/output.txt"
161 results = File.open("#{test_result_dir}/1/result").readlines
161 results = File.open("#{test_result_dir}/1/result").readlines
162 stat = extract_running_stat(results)
162 stat = extract_running_stat(results)
163
163
164 return {
164 return {
165 :output_file_name => output_file_name,
165 :output_file_name => output_file_name,
166 :running_stat => stat,
166 :running_stat => stat,
167 :comment => "",
167 :comment => "",
168 :cmp_msg => cmp_msg}
168 :cmp_msg => cmp_msg}
169 else
169 else
170 return {
170 return {
171 :running_stat => nil,
171 :running_stat => nil,
172 :comment => "Compilation error",
172 :comment => "Compilation error",
173 :cmp_msg => cmp_msg}
173 :cmp_msg => cmp_msg}
174 end
174 end
175 end
175 end
176
176
177 def extract_running_stat(results)
177 def extract_running_stat(results)
178 running_stat_line = results[-1]
178 running_stat_line = results[-1]
179
179
180 # extract exit status line
180 # extract exit status line
181 run_stat = ""
181 run_stat = ""
182 if !(/[Cc]orrect/.match(results[0]))
182 if !(/[Cc]orrect/.match(results[0]))
183 run_stat = results[0].chomp
183 run_stat = results[0].chomp
184 else
184 else
185 run_stat = 'Program exited normally'
185 run_stat = 'Program exited normally'
186 end
186 end
187
187
188 # extract running time
188 # extract running time
189 if res = /r(.*)u(.*)s/.match(running_stat_line)
189 if res = /r(.*)u(.*)s/.match(running_stat_line)
190 seconds = (res[1].to_f + res[2].to_f)
190 seconds = (res[1].to_f + res[2].to_f)
191 time_stat = "Time used: #{seconds} sec."
191 time_stat = "Time used: #{seconds} sec."
192 else
192 else
193 seconds = nil
193 seconds = nil
194 time_stat = "Time used: n/a sec."
194 time_stat = "Time used: n/a sec."
195 end
195 end
196
196
197 # extract memory usage
197 # extract memory usage
198 if res = /s(.*)m/.match(running_stat_line)
198 if res = /s(.*)m/.match(running_stat_line)
199 memory_used = res[1].to_i
199 memory_used = res[1].to_i
200 else
200 else
201 memory_used = -1
201 memory_used = -1
202 end
202 end
203
203
204 return {
204 return {
205 :msg => "#{run_stat}\n#{time_stat}",
205 :msg => "#{run_stat}\n#{time_stat}",
206 :running_time => seconds,
206 :running_time => seconds,
207 :exit_status => run_stat,
207 :exit_status => run_stat,
208 :memory_usage => memory_used
208 :memory_usage => memory_used
209 }
209 }
210 end
210 end
211
211
212 def save_result(test_request,result)
212 def save_result(test_request,result)
213 if result[:output_file_name]!=nil
213 if result[:output_file_name]!=nil
214 test_request.output_file_name = link_output_file(test_request,
214 test_request.output_file_name = link_output_file(test_request,
215 result[:output_file_name])
215 result[:output_file_name])
216 end
216 end
217 test_request.graded_at = Time.now
217 test_request.graded_at = Time.now
218 test_request.compiler_message = (result[:cmp_msg] or '')
218 test_request.compiler_message = (result[:cmp_msg] or '')
219 test_request.grader_comment = (result[:comment] or '')
219 test_request.grader_comment = (result[:comment] or '')
220 if result[:running_stat]!=nil
220 if result[:running_stat]!=nil
221 test_request.running_stat = (result[:running_stat][:msg] or '')
221 test_request.running_stat = (result[:running_stat][:msg] or '')
222 test_request.running_time = (result[:running_stat][:running_time] or nil)
222 test_request.running_time = (result[:running_stat][:running_time] or nil)
223 test_request.exit_status = result[:running_stat][:exit_status]
223 test_request.exit_status = result[:running_stat][:exit_status]
224 test_request.memory_usage = result[:running_stat][:memory_usage]
224 test_request.memory_usage = result[:running_stat][:memory_usage]
225 else
225 else
226 test_request.running_stat = ''
226 test_request.running_stat = ''
227 end
227 end
228 test_request.save
228 test_request.save
229 end
229 end
230
230
231 protected
231 protected
232 def link_output_file(test_request, fname)
232 def link_output_file(test_request, fname)
233 target_file_name = random_output_file_name(test_request.user,
233 target_file_name = random_output_file_name(test_request.user,
234 test_request.problem)
234 test_request.problem)
235 FileUtils.mkdir_p(File.dirname(target_file_name))
235 FileUtils.mkdir_p(File.dirname(target_file_name))
236 Grader::link_or_copy("#{fname}", "#{target_file_name}")
236 Grader::link_or_copy("#{fname}", "#{target_file_name}")
237 return target_file_name
237 return target_file_name
238 end
238 end
239
239
240 def random_output_file_name(user,problem)
240 def random_output_file_name(user,problem)
241 problem_name = TestRequest.name_of(problem)
241 problem_name = TestRequest.name_of(problem)
242 begin
242 begin
243 tmpname = "#{@config.test_request_output_base_dir}" +
243 tmpname = "#{@config.test_request_output_base_dir}" +
244 "/#{user.login}/#{problem_name}/#{rand(10000)}"
244 "/#{user.login}/#{problem_name}/#{rand(10000)}"
245 end while File.exists?(tmpname)
245 end while File.exists?(tmpname)
246 tmpname
246 tmpname
247 end
247 end
248
248
249 end
249 end
250
250
251 end
251 end
You need to be logged in to leave comments. Login now