aboutsummaryrefslogtreecommitdiffstats
path: root/railties/test/application/fcgi_dispatcher_test.rb
diff options
context:
space:
mode:
Diffstat (limited to 'railties/test/application/fcgi_dispatcher_test.rb')
-rw-r--r--railties/test/application/fcgi_dispatcher_test.rb290
1 files changed, 290 insertions, 0 deletions
diff --git a/railties/test/application/fcgi_dispatcher_test.rb b/railties/test/application/fcgi_dispatcher_test.rb
new file mode 100644
index 0000000000..b8ab9f8996
--- /dev/null
+++ b/railties/test/application/fcgi_dispatcher_test.rb
@@ -0,0 +1,290 @@
+require 'isolation/abstract_unit'
+require 'mocha'
+
+begin
+
+begin
+ require 'fcgi'
+rescue LoadError
+ gem 'fcgi', '0.8.7'
+ require 'fcgi'
+end
+
+class RailsFCGIHandlerTest < Test::Unit::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+
+ require "#{rails_root}/config/environment"
+ require 'rails/fcgi_handler'
+
+ @log = StringIO.new
+ @handler = RailsFCGIHandler.new(@log)
+ end
+
+ def test_process_restart
+ request = mock
+ FCGI.stubs(:each).yields(request)
+
+ @handler.expects(:process_request).once
+ @handler.expects(:dispatcher_error).never
+
+ @handler.expects(:when_ready).returns(:restart)
+ @handler.expects(:close_connection).with(request)
+ @handler.expects(:reload!).never
+ @handler.expects(:restart!)
+
+ @handler.process!
+ end
+
+ def test_process_exit
+ request = mock
+ FCGI.stubs(:each).yields(request)
+
+ @handler.expects(:process_request).once
+ @handler.expects(:dispatcher_error).never
+
+ @handler.expects(:when_ready).returns(:exit)
+ @handler.expects(:close_connection).with(request)
+ @handler.expects(:reload!).never
+ @handler.expects(:restart!).never
+
+ @handler.process!
+ end
+
+ def test_process_with_system_exit_exception
+ request = mock
+ FCGI.stubs(:each).yields(request)
+
+ @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)
+ ActionController::Routing::Routes.expects(:reload)
+ @handler.send(:restore!)
+ end
+
+ def test_uninterrupted_processing
+ request = mock
+ FCGI.expects(:each).yields(request)
+ @handler.expects(:process_request).with(request)
+
+ @handler.process!
+
+ assert_nil @handler.when_ready
+ end
+end
+
+class RailsFCGIHandlerSignalsTest < Test::Unit::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+
+ require "#{rails_root}/config/environment"
+ require 'rails/fcgi_handler'
+
+ ::RailsFCGIHandler.class_eval do
+ attr_accessor :signal
+ alias_method :old_gc_countdown, :gc_countdown
+ def gc_countdown
+ signal ? Process.kill(signal, $$) : old_gc_countdown
+ end
+ end
+
+ @log = StringIO.new
+ @handler = RailsFCGIHandler.new(@log)
+ @dispatcher = mock
+ Dispatcher.stubs(:new).returns(@dispatcher)
+ end
+
+ def test_interrupted_via_HUP_when_not_in_request
+ request = mock
+ FCGI.expects(:each).once.yields(request)
+ @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_USR1_when_not_in_request
+ request = mock
+ FCGI.expects(:each).once.yields(request)
+ @handler.expects(:signal).times(2).returns('USR1')
+ @handler.expects(:exit_handler).never
+
+ @handler.expects(:reload!).never
+ @handler.expects(:close_connection).with(request).once
+ @handler.expects(:exit).never
+
+ @handler.process!
+ assert_nil @handler.when_ready
+ end
+
+ def test_restart_via_USR2_when_in_request
+ request = mock
+ FCGI.expects(:each).once.yields(request)
+ @handler.expects(:signal).times(2).returns('USR2')
+ @handler.expects(:exit_handler).never
+
+ @handler.expects(:reload!).never
+ @handler.expects(:close_connection).with(request).once
+ @handler.expects(:exit).never
+ @handler.expects(:restart!).once
+
+ @handler.process!
+ assert_equal :restart, @handler.when_ready
+ end
+
+ def test_interrupted_via_TERM
+ request = mock
+ FCGI.expects(:each).once.yields(request)
+ ::Rack::Handler::FastCGI.expects(:serve).once.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).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
+ request = mock
+ error = RuntimeError.new('foo')
+ FCGI.expects(:each).once.yields(request)
+ ::Rack::Handler::FastCGI.expects(:serve).once.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).once.raises(error)
+ @handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/))
+ @handler.process!
+ end
+
+ def test_signal_exception_in_dispatcher
+ request = mock
+ error = SignalException.new('USR2')
+ FCGI.expects(:each).once.yields(request)
+ ::Rack::Handler::FastCGI.expects(:serve).once.raises(error)
+ @handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/))
+ @handler.process!
+ end
+end
+
+class RailsFCGIHandlerPeriodicGCTest < Test::Unit::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+
+ require "#{rails_root}/config/environment"
+ require 'rails/fcgi_handler'
+
+ @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
+
+ request = mock
+ FCGI.expects(:each).times(10).yields(request)
+
+ @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
+ $stderr.puts 'Skipping fcgi tests. `gem install fcgi` and try again.'
+end