From 20d6938453f439531a13e2ef1fd0905edf56294c Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 5 Oct 2009 10:36:05 -0500 Subject: Rewrite FCGI handler test --- railties/lib/rails/fcgi_handler.rb | 4 +- railties/test/abstract_unit.rb | 12 - railties/test/application/fcgi_dispatcher_test.rb | 290 ++++++++++++++++++++++ railties/test/fcgi_dispatcher_test.rb | 268 -------------------- 4 files changed, 293 insertions(+), 281 deletions(-) create mode 100644 railties/test/application/fcgi_dispatcher_test.rb delete mode 100644 railties/test/fcgi_dispatcher_test.rb diff --git a/railties/lib/rails/fcgi_handler.rb b/railties/lib/rails/fcgi_handler.rb index ef6f3b094c..77dce5f325 100644 --- a/railties/lib/rails/fcgi_handler.rb +++ b/railties/lib/rails/fcgi_handler.rb @@ -216,7 +216,9 @@ class RailsFCGIHandler def restore! $".replace @features - Dispatcher.reset_application! + # TODO: Reloading the application should be the "Application"s + # responsibility + ActionDispatch::Callbacks.new(lambda {}, true) ActionController::Routing::Routes.reload end diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb index 50c427dc64..6c6af0b2bf 100644 --- a/railties/test/abstract_unit.rb +++ b/railties/test/abstract_unit.rb @@ -25,15 +25,3 @@ if defined?(RAILS_ROOT) else RAILS_ROOT = File.dirname(__FILE__) end - -def uses_gem(gem_name, test_name, version = '> 0') - begin - require gem_name.to_s - rescue LoadError - gem gem_name.to_s, version - require gem_name.to_s - end - yield -rescue LoadError - $stderr.puts "Skipping #{test_name} tests. `gem install #{gem_name}` and try again." -end 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 diff --git a/railties/test/fcgi_dispatcher_test.rb b/railties/test/fcgi_dispatcher_test.rb deleted file mode 100644 index 4d77a321a0..0000000000 --- a/railties/test/fcgi_dispatcher_test.rb +++ /dev/null @@ -1,268 +0,0 @@ -require 'abstract_unit' - -uses_gem "fcgi", "0.8.7" do - -require 'action_controller' -require 'rails/fcgi_handler' - -module Rails - def self.application - ActionController::Routing::Routes - end -end - -class RailsFCGIHandlerTest < Test::Unit::TestCase - def setup - @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) - Dispatcher.expects(:reset_application!) - 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 - class ::RailsFCGIHandler - attr_accessor :signal - alias_method :old_gc_countdown, :gc_countdown - def gc_countdown - signal ? Process.kill(signal, $$) : old_gc_countdown - end - end - - def setup - @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 - 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 - - 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 -end # uses_gem "fcgi" -- cgit v1.2.3