require 'abstract_unit' begin require 'fcgi_handler' module ActionController; module Routing; module Routes; end end end class RailsFCGIHandlerTest < Test::Unit::TestCase def setup @log = StringIO.new @handler = RailsFCGIHandler.new(@log) end def test_process_restart cgi = mock FCGI.stubs(:each_cgi).yields(cgi) @handler.expects(:process_request).once @handler.expects(:dispatcher_error).never @handler.expects(:when_ready).returns(:restart) @handler.expects(:close_connection).with(cgi) @handler.expects(:reload!).never @handler.expects(:restart!) @handler.process! end def test_process_exit cgi = mock FCGI.stubs(:each_cgi).yields(cgi) @handler.expects(:process_request).once @handler.expects(:dispatcher_error).never @handler.expects(:when_ready).returns(:exit) @handler.expects(:close_connection).with(cgi) @handler.expects(:reload!).never @handler.expects(:restart!).never @handler.process! end def test_process_with_system_exit_exception cgi = mock FCGI.stubs(:each_cgi).yields(cgi) @handler.expects(:process_request).once.raises(SystemExit) @handler.stubs(:dispatcher_log) @handler.expects(:dispatcher_log).with(:info, regexp_matches(/^stopping/)) @handler.expects(:dispatcher_error).never @handler.expects(:when_ready).never @handler.expects(:close_connection).never @handler.expects(:reload!).never @handler.expects(:restart!).never @handler.process! end def test_restart_handler_outside_request @handler.expects(:dispatcher_log).with(:info, "asked to restart ASAP") @handler.expects(:restart!).once @handler.send(:restart_handler, nil) assert_equal nil, @handler.when_ready end def test_install_signal_handler_should_log_on_bad_signal @handler.stubs(:trap).raises(ArgumentError) @handler.expects(:dispatcher_log).with(:warn, "Ignoring unsupported signal CHEESECAKE.") @handler.send(:install_signal_handler, "CHEESECAKE", nil) end def test_reload @handler.expects(:restore!) @handler.expects(:dispatcher_log).with(:info, "reloaded") @handler.send(:reload!) assert_nil @handler.when_ready end def test_reload_runs_gc_when_gc_request_period_set @handler.expects(:run_gc!) @handler.expects(:restore!) @handler.expects(:dispatcher_log).with(:info, "reloaded") @handler.gc_request_period = 10 @handler.send(:reload!) end def test_reload_doesnt_run_gc_if_gc_request_period_isnt_set @handler.expects(:run_gc!).never @handler.expects(:restore!) @handler.expects(:dispatcher_log).with(:info, "reloaded") @handler.send(:reload!) end def test_restart! @handler.expects(:dispatcher_log).with(:info, "restarted") @handler.expects(:exec).returns('restarted') assert_equal 'restarted', @handler.send(:restart!) end def test_restore! $".expects(:replace) Dispatcher.expects(:reset_application!) ActionController::Routing::Routes.expects(:reload) @handler.send(:restore!) end def test_uninterrupted_processing cgi = mock FCGI.expects(:each_cgi).yields(cgi) @handler.expects(:process_request).with(cgi) @handler.process! assert_nil @handler.when_ready end end class RailsFCGIHandlerSignalsTest < Test::Unit::TestCase class ::RailsFCGIHandler attr_accessor :signal alias_method :old_gc_countdown, :gc_countdown def gc_countdown signal ? Process.kill(signal, $$) : old_gc_countdown end end class ::Dispatcher class << self attr_accessor :signal alias_method :old_dispatch, :dispatch def dispatch(cgi) signal ? Process.kill(signal, $$) : old_dispatch end end end def setup @log = StringIO.new @handler = RailsFCGIHandler.new(@log) end def test_interrupted_via_HUP_when_not_in_request cgi = mock FCGI.expects(:each_cgi).once.yields(cgi) @handler.expects(:signal).times(2).returns('HUP') @handler.expects(:reload!).once @handler.expects(:close_connection).never @handler.expects(:exit).never @handler.process! assert_equal :reload, @handler.when_ready end def test_interrupted_via_HUP_when_in_request cgi = mock FCGI.expects(:each_cgi).once.yields(cgi) Dispatcher.expects(:signal).times(2).returns('HUP') @handler.expects(:reload!).once @handler.expects(:close_connection).never @handler.expects(:exit).never @handler.process! assert_equal :reload, @handler.when_ready end def test_interrupted_via_USR1_when_not_in_request cgi = mock FCGI.expects(:each_cgi).once.yields(cgi) @handler.expects(:signal).times(2).returns('USR1') @handler.expects(:exit_handler).never @handler.expects(:reload!).never @handler.expects(:close_connection).with(cgi).once @handler.expects(:exit).never @handler.process! assert_nil @handler.when_ready end def test_interrupted_via_USR1_when_in_request cgi = mock FCGI.expects(:each_cgi).once.yields(cgi) Dispatcher.expects(:signal).times(2).returns('USR1') @handler.expects(:reload!).never @handler.expects(:close_connection).with(cgi).once @handler.expects(:exit).never @handler.process! assert_equal :exit, @handler.when_ready end def test_restart_via_USR2_when_in_request cgi = mock FCGI.expects(:each_cgi).once.yields(cgi) @handler.expects(:signal).times(2).returns('USR2') @handler.expects(:exit_handler).never @handler.expects(:reload!).never @handler.expects(:close_connection).with(cgi).once @handler.expects(:exit).never @handler.expects(:restart!).once @handler.process! assert_equal :restart, @handler.when_ready end def test_interrupted_via_TERM cgi = mock FCGI.expects(:each_cgi).once.yields(cgi) Dispatcher.expects(:signal).times(2).returns('TERM') @handler.expects(:reload!).never @handler.expects(:close_connection).never @handler.process! assert_nil @handler.when_ready end def test_runtime_exception_in_fcgi error = RuntimeError.new('foo') FCGI.expects(:each_cgi).times(2).raises(error) @handler.expects(:dispatcher_error).with(error, regexp_matches(/^retrying/)) @handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/)) @handler.process! end def test_runtime_error_in_dispatcher cgi = mock error = RuntimeError.new('foo') FCGI.expects(:each_cgi).once.yields(cgi) Dispatcher.expects(:dispatch).once.with(cgi).raises(error) @handler.expects(:dispatcher_error).with(error, regexp_matches(/^unhandled/)) @handler.process! end def test_signal_exception_in_fcgi error = SignalException.new('USR2') FCGI.expects(:each_cgi).once.raises(error) @handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/)) @handler.process! end def test_signal_exception_in_dispatcher cgi = mock error = SignalException.new('USR2') FCGI.expects(:each_cgi).once.yields(cgi) Dispatcher.expects(:dispatch).once.with(cgi).raises(error) @handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/)) @handler.process! end end class RailsFCGIHandlerPeriodicGCTest < Test::Unit::TestCase def setup @log = StringIO.new end def teardown GC.enable end def test_normal_gc @handler = RailsFCGIHandler.new(@log) assert_nil @handler.gc_request_period # When GC is enabled, GC.disable disables and returns false. assert_equal false, GC.disable end def test_periodic_gc @handler = RailsFCGIHandler.new(@log, 10) assert_equal 10, @handler.gc_request_period cgi = mock FCGI.expects(:each_cgi).times(10).yields(cgi) Dispatcher.expects(:dispatch).times(10).with(cgi) @handler.expects(:run_gc!).never 9.times { @handler.process! } @handler.expects(:run_gc!).once @handler.process! assert_nil @handler.when_ready end end rescue LoadError => e raise unless e.message =~ /fcgi/ end