diff options
Diffstat (limited to 'spec/mspec/lib/mspec/runner/actions/timeout.rb')
-rw-r--r-- | spec/mspec/lib/mspec/runner/actions/timeout.rb | 89 |
1 files changed, 87 insertions, 2 deletions
diff --git a/spec/mspec/lib/mspec/runner/actions/timeout.rb b/spec/mspec/lib/mspec/runner/actions/timeout.rb index c85bf49ad3..1200926872 100644 --- a/spec/mspec/lib/mspec/runner/actions/timeout.rb +++ b/spec/mspec/lib/mspec/runner/actions/timeout.rb @@ -3,11 +3,14 @@ class TimeoutAction @timeout = timeout @queue = Queue.new @started = now + @fail = false + @error_message = "took longer than the configured timeout of #{@timeout}s" end def register MSpec.register :start, self MSpec.register :before, self + MSpec.register :after, self MSpec.register :finish, self end @@ -35,9 +38,27 @@ class TimeoutAction if @queue.empty? elapsed = now - @started if elapsed > @timeout - STDERR.puts "\n#{@current_state.description}" + if @current_state + STDERR.puts "\nExample #{@error_message}:" + STDERR.puts "#{@current_state.description}" + else + STDERR.puts "\nSome code outside an example #{@error_message}" + end STDERR.flush - abort "Example took longer than the configured timeout of #{@timeout}s" + + show_backtraces + if MSpec.subprocesses.empty? + exit! 2 + else + # Do not exit but signal the subprocess so we can get their output + MSpec.subprocesses.each do |pid| + kill_wait_one_second :SIGTERM, pid + hard_kill :SIGKILL, pid + end + @fail = true + @current_state = nil + break # stop this thread, will fail in #after + end end end end @@ -53,8 +74,72 @@ class TimeoutAction end end + def after(state = nil) + @queue << -> do + @current_state = nil + end + + if @fail + STDERR.puts "\n\nThe last example #{@error_message}. See above for the subprocess stacktrace." + exit! 2 + end + end + def finish @thread.kill @thread.join end + + private def hard_kill(signal, pid) + begin + Process.kill signal, pid + rescue Errno::ESRCH + # Process already terminated + end + end + + private def kill_wait_one_second(signal, pid) + begin + Process.kill signal, pid + sleep 1 + rescue Errno::ESRCH + # Process already terminated + end + end + + private def show_backtraces + java_stacktraces = -> pid { + if RUBY_ENGINE == 'truffleruby' || RUBY_ENGINE == 'jruby' + STDERR.puts 'Java stacktraces:' + kill_wait_one_second :SIGQUIT, pid + end + } + + if MSpec.subprocesses.empty? + java_stacktraces.call Process.pid + + STDERR.puts "\nRuby backtraces:" + if defined?(Truffle::Debug.show_backtraces) + Truffle::Debug.show_backtraces + else + Thread.list.each do |thread| + unless thread == Thread.current + STDERR.puts thread.inspect, thread.backtrace, '' + end + end + end + else + MSpec.subprocesses.each do |pid| + STDERR.puts "\nFor subprocess #{pid}" + java_stacktraces.call pid + + if RUBY_ENGINE == 'truffleruby' + STDERR.puts "\nRuby backtraces:" + kill_wait_one_second :SIGALRM, pid + else + STDERR.puts "Don't know how to print backtraces of a subprocess on #{RUBY_ENGINE}" + end + end + end + end end |