Ensure to save Testrun even when an error occurs

This commit is contained in:
Felix Auringer
2021-06-25 12:10:30 +02:00
committed by Sebastian Serth
parent f98a8b9e7a
commit 36578a2817
3 changed files with 37 additions and 37 deletions

View File

@ -134,18 +134,17 @@ class SubmissionsController < ApplicationController
def handle_websockets(tubesock, socket) def handle_websockets(tubesock, socket)
tubesock.send_data JSON.dump({cmd: :status, status: :container_running}) tubesock.send_data JSON.dump({cmd: :status, status: :container_running})
@output = +''
socket.on :output do |data|
@output << data if @output.size + data.size <= max_output_buffer_size
end
socket.on :stdout do |data| socket.on :stdout do |data|
tubesock.send_data(JSON.dump({cmd: :write, stream: :stdout, data: data})) json_data = JSON.dump({cmd: :write, stream: :stdout, data: data})
@output << json_data[0, max_output_buffer_size - @output.size]
tubesock.send_data(json_data)
end end
socket.on :stderr do |data| socket.on :stderr do |data|
tubesock.send_data(JSON.dump({cmd: :write, stream: :stderr, data: data})) json_data = JSON.dump({cmd: :write, stream: :stderr, data: data})
@output << json_data[0, max_output_buffer_size - @output.size]
tubesock.send_data(json_data)
end end
socket.on :exit do |exit_code| socket.on :exit do |exit_code|
@ -180,6 +179,7 @@ class SubmissionsController < ApplicationController
end end
def run def run
@output = +''
hijack do |tubesock| hijack do |tubesock|
return kill_socket(tubesock) if @embed_options[:disable_run] return kill_socket(tubesock) if @embed_options[:disable_run]
@ -188,20 +188,22 @@ class SubmissionsController < ApplicationController
end end
@container_execution_time = durations[:execution_duration] @container_execution_time = durations[:execution_duration]
@waiting_for_container_time = durations[:waiting_duration] @waiting_for_container_time = durations[:waiting_duration]
save_run_output
rescue Runner::Error::ExecutionTimeout => e rescue Runner::Error::ExecutionTimeout => e
tubesock.send_data JSON.dump({cmd: :status, status: :timeout}) tubesock.send_data JSON.dump({cmd: :status, status: :timeout})
kill_socket(tubesock) kill_socket(tubesock)
Rails.logger.debug { "Running a submission timed out: #{e.message}" } Rails.logger.debug { "Running a submission timed out: #{e.message}" }
@output = "timeout: #{@output}"
rescue Runner::Error => e rescue Runner::Error => e
tubesock.send_data JSON.dump({cmd: :status, status: :container_depleted}) tubesock.send_data JSON.dump({cmd: :status, status: :container_depleted})
kill_socket(tubesock) kill_socket(tubesock)
Rails.logger.debug { "Runner error while running a submission: #{e.message}" } Rails.logger.debug { "Runner error while running a submission: #{e.message}" }
ensure
save_run_output
end end
end end
def kill_socket(tubesock) def kill_socket(tubesock)
# search for errors and save them as StructuredError (for scoring runs see submission_scoring.rb) # search for errors and save them as StructuredError (for scoring runs see submission.rb)
errors = extract_errors errors = extract_errors
send_hints(tubesock, errors) send_hints(tubesock, errors)
@ -210,11 +212,8 @@ class SubmissionsController < ApplicationController
tubesock.close tubesock.close
end end
# save the output of this "run" as a "testrun" (scoring runs are saved in submission_scoring.rb) # save the output of this "run" as a "testrun" (scoring runs are saved in submission.rb)
def save_run_output def save_run_output
return if @output.blank?
@output = @output[0, max_output_buffer_size] # trim the string to max_output_buffer_size chars
Testrun.create( Testrun.create(
file: @file, file: @file,
cause: 'run', cause: 'run',
@ -243,12 +242,9 @@ class SubmissionsController < ApplicationController
tubesock.send_data(JSON.dump(format_scoring_results(@submission.calculate_score))) tubesock.send_data(JSON.dump(format_scoring_results(@submission.calculate_score)))
# To enable hints when scoring a submission, uncomment the next line: # To enable hints when scoring a submission, uncomment the next line:
# send_hints(tubesock, StructuredError.where(submission: @submission)) # send_hints(tubesock, StructuredError.where(submission: @submission))
rescue Runner::Error::ExecutionTimeout => e
tubesock.send_data JSON.dump({cmd: :status, status: :timeout})
Rails.logger.debug { "Scoring a submission timed out: #{e.message}" }
rescue Runner::Error => e rescue Runner::Error => e
tubesock.send_data JSON.dump({cmd: :status, status: :container_depleted}) tubesock.send_data JSON.dump({cmd: :status, status: :container_depleted})
Rails.logger.debug { "Runner error while scoring a submission: #{e.message}" } Rails.logger.debug { "Runner error while scoring submission #{@submission.id}: #{e.message}" }
ensure ensure
tubesock.send_data JSON.dump({cmd: :exit}) tubesock.send_data JSON.dump({cmd: :exit})
tubesock.close tubesock.close

View File

@ -143,29 +143,33 @@ class Submission < ApplicationRecord
prepared_runner do |runner, waiting_duration| prepared_runner do |runner, waiting_duration|
file_scores = collect_files.select(&:teacher_defined_assessment?).map do |file| file_scores = collect_files.select(&:teacher_defined_assessment?).map do |file|
score_command = command_for execution_environment.test_command, file.name_with_extension score_command = command_for execution_environment.test_command, file.name_with_extension
output = {file_role: file.role, waiting_for_container_time: waiting_duration}
stdout = +'' stdout = +''
stderr = +'' stderr = +''
exit_code = 1 # default to error begin
execution_time = runner.attach_to_execution(score_command) do |socket| exit_code = 1 # default to error
socket.on :stderr do |data| execution_time = runner.attach_to_execution(score_command) do |socket|
stderr << data socket.on :stderr do |data|
end stderr << data
socket.on :stdout do |data| end
stdout << data socket.on :stdout do |data|
end stdout << data
socket.on :exit do |received_exit_code| end
exit_code = received_exit_code socket.on :exit do |received_exit_code|
EventMachine.stop_event_loop exit_code = received_exit_code
EventMachine.stop_event_loop
end
end end
output.merge!(container_execution_time: execution_time, status: exit_code.zero? ? :ok : :failed)
rescue Runner::Error::ExecutionTimeout => e
Rails.logger.debug("Running tests in #{file.name_with_extension} for submission #{id} timed out: #{e.message}")
output.merge!(status: :timeout)
rescue Runner::Error => e
Rails.logger.debug("Running tests in #{file.name_with_extension} for submission #{id} failed: #{e.message}")
output.merge!(status: :failed)
ensure
output.merge!(stdout: stdout, stderr: stderr)
end end
output = {
file_role: file.role,
waiting_for_container_time: waiting_duration,
container_execution_time: execution_time,
status: exit_code.zero? ? :ok : :failed,
stdout: stdout,
stderr: stderr,
}
score_file(output, file) score_file(output, file)
end end
end end

View File

@ -57,7 +57,7 @@ class Runner::Connection
return unless BACKEND_OUTPUT_SCHEMA.valid?(event) return unless BACKEND_OUTPUT_SCHEMA.valid?(event)
event = event.deep_symbolize_keys event = event.deep_symbolize_keys
message_type = event[:type] message_type = event[:type].to_sym
if WEBSOCKET_MESSAGE_TYPES.include?(message_type) if WEBSOCKET_MESSAGE_TYPES.include?(message_type)
__send__("handle_#{message_type}", event) __send__("handle_#{message_type}", event)
else else