threads.rb 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
  1. # Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
  2. module ThreadsHelper
  3. # Ensure that any new threads which might be spawned by the block will be cleaned up
  4. # to not interfere with any subsequent tests.
  5. def ensure_threads_exited()
  6. initial_threads = Thread.list
  7. yield
  8. ensure
  9. superfluous_threads = -> { Thread.list - initial_threads }
  10. # Keep going until no more changes are needed to catch threads spawned in between.
  11. 3.times do
  12. superfluous_threads.call.each(&:kill)
  13. break if superfluous_threads.call.count.zero?
  14. sleep 1 # Wait a bit for stuff to settle before trying again.
  15. end
  16. if superfluous_threads.call.count.positive?
  17. superfluous_threads.each do |thread|
  18. warn "Error: found a superfluous thread after clean-up: #{thread}"
  19. warn "Backtrace: #{thread.backtrace.join("\n")}"
  20. end
  21. raise 'Superfluous threads found after clean-up.'
  22. end
  23. # Sometimes connections are not checked back in after thread is killed
  24. # This recovers connections from the workers
  25. ActiveRecord::Base.connection_pool.reap
  26. end
  27. # Thread control loops usually run forever. This method can test that they were started.
  28. def ensure_block_keeps_running(timeout: 2.seconds, &block)
  29. # Stop after timeout and return true if everything was ok.
  30. Timeout.timeout(timeout, &block)
  31. raise 'Process ended unexpectedly.'
  32. rescue SystemExit
  33. # Convert SystemExit to a RuntimeError as otherwise rspec will shut down without an error.
  34. raise 'Process tried to shut down unexpectedly.'
  35. rescue Timeout::Error
  36. # Default case: process started fine and kept running, interrupted by timeout.
  37. true
  38. end
  39. end
  40. RSpec.configure do |config|
  41. config.include ThreadsHelper
  42. config.around(:each, :ensure_threads_exited) do |example|
  43. ensure_threads_exited { example.run }
  44. end
  45. end