Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 7593

Show
Ignore:
Timestamp:
09/23/07 11:20:25 (2 years ago)
Author:
bitsweat
Message:

RailsFCGIHandler tests. Closes #9630.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/railties/lib/fcgi_handler.rb

    r7095 r7593  
    4242    # Start error timestamp at 11 seconds ago. 
    4343    @last_error_on = Time.now - 11 
    44  
    45     dispatcher_log :info, "starting" 
    4644  end 
    4745 
    4846  def process!(provider = FCGI) 
    49     # Make a note of $" so we can safely reload this instance. 
    50     mark! 
    51  
    52     run_gc! if gc_request_period 
    53  
    54     process_each_request!(provider) 
    55  
    56     GC.enable 
    57     dispatcher_log :info, "terminated gracefully" 
    58  
    59   rescue SystemExit => exit_error 
    60     dispatcher_log :info, "terminated by explicit exit" 
    61  
    62   rescue Exception => fcgi_error  # FCGI errors 
    63     # retry on errors that would otherwise have terminated the FCGI process, 
    64     # but only if they occur more than 10 seconds apart. 
    65     if !(SignalException === fcgi_error) && Time.now - @last_error_on > 10 
    66       @last_error_on = Time.now 
    67       dispatcher_error(fcgi_error, "almost killed by this error") 
    68       retry 
     47    mark_features! 
     48 
     49    dispatcher_log :info, 'starting' 
     50    process_each_request provider 
     51    dispatcher_log :info, 'stopping gracefully' 
     52 
     53  rescue Exception => error 
     54    case error 
     55    when SystemExit 
     56      dispatcher_log :info, 'stopping after explicit exit' 
     57    when SignalException 
     58      dispatcher_error error, 'stopping after unhandled signal' 
    6959    else 
    70       dispatcher_error(fcgi_error, "killed by this error") 
     60      # Retry if exceptions occur more than 10 seconds apart. 
     61      if Time.now - @last_error_on > 10 
     62        @last_error_on = Time.now 
     63        dispatcher_error error, 'retrying after unhandled exception' 
     64        retry 
     65      else 
     66        dispatcher_error error, 'stopping after unhandled exception within 10 seconds of the last' 
     67      end 
    7168    end 
    7269  end 
     
    7471 
    7572  protected 
    76     def logger 
    77       @logger ||= Logger.new(@log_file_path) 
    78     end 
    79  
    80     def dispatcher_log(level, msg) 
    81       time_str = Time.now.strftime("%d/%b/%Y:%H:%M:%S") 
    82       logger.send(level, "[#{time_str} :: #{$$}] #{msg}") 
    83     rescue Exception => log_error  # Logger errors 
    84       STDERR << "Couldn't write to #{@log_file_path.inspect}: #{msg}\n" 
    85       STDERR << "  #{log_error.class}: #{log_error.message}\n" 
    86     end 
    87  
    88     def dispatcher_error(e, msg = "") 
    89       error_message = 
    90         "Dispatcher failed to catch: #{e} (#{e.class})\n" + 
    91         "  #{e.backtrace.join("\n  ")}\n#{msg}" 
    92       dispatcher_log(:error, error_message) 
    93     end 
    94  
    95     def install_signal_handlers 
    96       GLOBAL_SIGNALS.each { |signal| install_signal_handler(signal) } 
    97     end 
    98  
    99     def install_signal_handler(signal, handler = nil) 
    100       handler ||= method("#{SIGNALS[signal]}_handler").to_proc 
    101       trap(signal, handler) 
    102     rescue ArgumentError 
    103       dispatcher_log :warn, "Ignoring unsupported signal #{signal}." 
    104     end 
    105  
    106     def with_signal_handler(signal) 
    107       install_signal_handler(signal) 
    108       yield 
    109     ensure 
    110       install_signal_handler(signal, 'DEFAULT') 
    111     end 
    112  
    113     def exit_now_handler(signal) 
    114       dispatcher_log :info, "asked to terminate immediately" 
    115       exit 
    116     end 
    117  
    118     def exit_handler(signal) 
    119       dispatcher_log :info, "asked to terminate ASAP" 
    120       @when_ready = :exit 
    121     end 
    122  
    123     def reload_handler(signal) 
    124       dispatcher_log :info, "asked to reload ASAP" 
    125       @when_ready = :reload 
    126     end 
    127  
    128     def restart_handler(signal) 
    129       dispatcher_log :info, "asked to restart ASAP" 
    130       @when_ready = :restart 
    131     end 
    132  
    133     def process_each_request!(provider) 
     73    def process_each_request(provider) 
    13474      cgi = nil 
     75 
    13576      provider.each_cgi do |cgi| 
    136         with_signal_handler 'USR1' do 
    137           process_request(cgi) 
    138         end 
     77        process_request(cgi) 
    13978 
    14079        case when_ready 
     
    14887            break 
    14988        end 
    150  
    151         gc_countdown 
    15289      end 
    15390    rescue SignalException => signal 
    15491      raise unless signal.message == 'SIGUSR1' 
    155       close_connection(cgi) if cgi 
     92      close_connection(cgi) 
    15693    end 
    15794 
    15895    def process_request(cgi) 
    159       Dispatcher.dispatch(cgi) 
    160     rescue Exception => e  # errors from CGI dispatch 
    161       raise if SignalException === e 
    162       dispatcher_error(e) 
     96      @when_ready = nil 
     97      gc_countdown 
     98 
     99      with_signal_handler 'USR1' do 
     100        begin 
     101          Dispatcher.dispatch(cgi) 
     102        rescue SignalException, SystemExit 
     103          raise 
     104        rescue Exception => error 
     105          dispatcher_error error, 'unhandled dispatch error' 
     106        end 
     107      end 
     108    end 
     109 
     110    def logger 
     111      @logger ||= Logger.new(@log_file_path) 
     112    end 
     113 
     114    def dispatcher_log(level, msg) 
     115      time_str = Time.now.strftime("%d/%b/%Y:%H:%M:%S") 
     116      logger.send(level, "[#{time_str} :: #{$$}] #{msg}") 
     117    rescue Exception => log_error  # Logger errors 
     118      STDERR << "Couldn't write to #{@log_file_path.inspect}: #{msg}\n" 
     119      STDERR << "  #{log_error.class}: #{log_error.message}\n" 
     120    end 
     121 
     122    def dispatcher_error(e, msg = "") 
     123      error_message = 
     124        "Dispatcher failed to catch: #{e} (#{e.class})\n" + 
     125        "  #{e.backtrace.join("\n  ")}\n#{msg}" 
     126      dispatcher_log(:error, error_message) 
     127    end 
     128 
     129    def install_signal_handlers 
     130      GLOBAL_SIGNALS.each { |signal| install_signal_handler(signal) } 
     131    end 
     132 
     133    def install_signal_handler(signal, handler = nil) 
     134      if SIGNALS.include?(signal) && self.class.method_defined?(name = "#{SIGNALS[signal]}_handler") 
     135        handler ||= method(name).to_proc 
     136        trap(signal, handler) 
     137      else 
     138        dispatcher_log :warn, "Ignoring unsupported signal #{signal}." 
     139      end 
     140    end 
     141 
     142    def with_signal_handler(signal) 
     143      install_signal_handler(signal) 
     144      yield 
     145    ensure 
     146      install_signal_handler(signal, 'DEFAULT') 
     147    end 
     148 
     149    def exit_now_handler(signal) 
     150      dispatcher_log :info, "asked to stop immediately" 
     151      exit 
     152    end 
     153 
     154    def exit_handler(signal) 
     155      dispatcher_log :info, "asked to stop ASAP" 
     156      @when_ready = :exit 
     157    end 
     158 
     159    def reload_handler(signal) 
     160      dispatcher_log :info, "asked to reload ASAP" 
     161      @when_ready = :reload 
     162    end 
     163 
     164    def restart_handler(signal) 
     165      dispatcher_log :info, "asked to restart ASAP" 
     166      @when_ready = :restart 
    163167    end 
    164168 
     
    185189    end 
    186190 
    187     def mark! 
     191    # Make a note of $" so we can safely reload this instance. 
     192    def mark_features! 
    188193      @features = $".clone 
    189194    end 
     
    202207    def gc_countdown 
    203208      if gc_request_period 
     209        @gc_request_countdown ||= gc_request_period 
    204210        @gc_request_countdown -= 1 
    205211        run_gc! if @gc_request_countdown <= 0 
     
    208214 
    209215    def close_connection(cgi) 
    210       cgi.instance_variable_get("@request").finish 
     216      cgi.instance_variable_get("@request").finish if cgi 
    211217    end 
    212218end 
  • trunk/railties/test/abstract_unit.rb

    r7591 r7593  
    55 
    66require 'test/unit' 
     7require 'stringio' 
    78require 'active_support' 
    8  
    9 if defined?(RAILS_ROOT) 
    10   RAILS_ROOT.replace File.dirname(__FILE__) 
    11 else 
    12   RAILS_ROOT = File.dirname(__FILE__) 
    13 end 
    14  
    15 class Test::Unit::TestCase 
    16   # Add stuff here if you need it 
    17 end 
    189 
    1910# Wrap tests that use Mocha and skip if unavailable. 
     
    2617  $stderr.puts "Skipping #{test_name} tests. `gem install mocha` and try again." 
    2718end 
     19 
     20if defined?(RAILS_ROOT) 
     21  RAILS_ROOT.replace File.dirname(__FILE__) 
     22else 
     23  RAILS_ROOT = File.dirname(__FILE__) 
     24end 
  • trunk/railties/test/fcgi_dispatcher_test.rb

    r7552 r7593  
    11require File.dirname(__FILE__) + "/abstract_unit" 
    22 
    3 begin # rescue LoadError 
    4  
    5 require_library_or_gem 'mocha' 
    6    
    7 $:.unshift File.dirname(__FILE__) + "/mocks" 
    8  
    9 require 'stringio' 
    10  
    11 # Stubs 
     3uses_mocha 'fcgi dispatcher tests' do 
     4 
    125require 'fcgi_handler' 
    13 require 'routes' 
    14 require 'stubbed_kernel' 
    15  
    16 class RailsFCGIHandler 
    17   attr_reader :exit_code 
    18   attr_reader :reloaded 
    19   attr_accessor :thread 
    20   attr_reader :gc_runs 
    21  
    22   def trap(signal, handler, &block) 
    23     handler ||= block 
    24     (@signal_handlers ||= Hash.new)[signal] = handler 
    25   end 
    26  
    27   def exit(code=0) 
    28     @exit_code = code 
    29     (thread || Thread.current).exit 
    30   end 
    31  
    32   def send_signal(which) 
    33     @signal_handlers[which].call(which) 
    34   end 
    35    
    36   alias_method :old_run_gc!, :run_gc! 
    37   def run_gc! 
    38     @gc_runs ||= 0 
    39     @gc_runs += 1 
    40     old_run_gc! 
    41   end 
    42 end 
     6 
     7module ActionController; module Routing; module Routes; end end end 
    438 
    449class RailsFCGIHandlerTest < Test::Unit::TestCase 
     
    4611    @log = StringIO.new 
    4712    @handler = RailsFCGIHandler.new(@log) 
    48     FCGI.time_to_sleep = nil 
    49     FCGI.raise_exception = nil 
    50     Dispatcher.time_to_sleep = nil 
    51     Dispatcher.raise_exception = nil 
    5213  end 
    5314 
    5415  def test_process_restart 
    55     @handler.stubs(:when_ready).returns(:restart) 
    56      
    57     @handler.expects(:close_connection) 
     16    cgi = mock 
     17    FCGI.stubs(:each_cgi).yields(cgi) 
     18 
     19    @handler.expects(:process_request).once 
     20    @handler.expects(:dispatcher_error).never 
     21 
     22    @handler.expects(:when_ready).returns(:restart) 
     23    @handler.expects(:close_connection).with(cgi) 
     24    @handler.expects(:reload!).never 
    5825    @handler.expects(:restart!) 
    59     @handler.process! 
    60   end 
    61    
     26 
     27    @handler.process! 
     28  end 
     29 
    6230  def test_process_exit 
    63     @handler.stubs(:when_ready).returns(:exit) 
    64      
    65     @handler.expects(:close_connection) 
    66     @handler.process! 
    67   end 
    68    
     31    cgi = mock 
     32    FCGI.stubs(:each_cgi).yields(cgi) 
     33 
     34    @handler.expects(:process_request).once 
     35    @handler.expects(:dispatcher_error).never 
     36 
     37    @handler.expects(:when_ready).returns(:exit) 
     38    @handler.expects(:close_connection).with(cgi) 
     39    @handler.expects(:reload!).never 
     40    @handler.expects(:restart!).never 
     41 
     42    @handler.process! 
     43  end 
     44 
    6945  def test_process_with_system_exit_exception 
    70     @handler.stubs(:process_request).raises(SystemExit) 
    71      
    72     @handler.expects(:dispatcher_log).with(:info, "terminated by explicit exit") 
    73     @handler.process! 
    74   end 
    75    
     46    cgi = mock 
     47    FCGI.stubs(:each_cgi).yields(cgi) 
     48 
     49    @handler.expects(:process_request).once.raises(SystemExit) 
     50    @handler.stubs(:dispatcher_log) 
     51    @handler.expects(:dispatcher_log).with(:info, regexp_matches(/^stopping/)) 
     52    @handler.expects(:dispatcher_error).never 
     53 
     54    @handler.expects(:when_ready).never 
     55    @handler.expects(:close_connection).never 
     56    @handler.expects(:reload!).never 
     57    @handler.expects(:restart!).never 
     58 
     59    @handler.process! 
     60  end 
     61 
    7662  def test_restart_handler 
    7763    @handler.expects(:dispatcher_log).with(:info, "asked to restart ASAP") 
    78      
     64 
    7965    @handler.send(:restart_handler, nil) 
    8066    assert_equal :restart, @handler.when_ready 
    8167  end 
    82    
     68 
    8369  def test_install_signal_handler_should_log_on_bad_signal 
    8470    @handler.stubs(:trap).raises(ArgumentError) 
     
    8773    @handler.send(:install_signal_handler, "CHEESECAKE", nil) 
    8874  end 
    89    
     75 
    9076  def test_reload 
    9177    @handler.expects(:restore!) 
     
    9581    assert_nil @handler.when_ready 
    9682  end 
    97    
    98    
     83 
     84 
    9985  def test_reload_runs_gc_when_gc_request_period_set 
    10086    @handler.expects(:run_gc!) 
     
    10490    @handler.send(:reload!) 
    10591  end 
    106    
     92 
    10793  def test_reload_doesnt_run_gc_if_gc_request_period_isnt_set 
    10894    @handler.expects(:run_gc!).never 
     
    11197    @handler.send(:reload!) 
    11298  end 
    113    
     99 
    114100  def test_restart! 
    115101    @handler.expects(:dispatcher_log).with(:info, "restarted") 
    116     assert_equal true, @handler.send(:restart!), "Exec wasn't run" 
    117   end 
    118    
     102    @handler.expects(:exec).returns('restarted') 
     103    assert_equal 'restarted', @handler.send(:restart!) 
     104  end 
     105 
    119106  def test_restore! 
    120107    $".expects(:replace) 
     
    125112 
    126113  def test_uninterrupted_processing 
    127     @handler.process! 
    128     assert_nil @handler.exit_code 
    129     assert_nil @handler.when_ready 
     114    cgi = mock 
     115    FCGI.expects(:each_cgi).yields(cgi) 
     116    @handler.expects(:process_request).with(cgi) 
     117 
     118    @handler.process! 
     119 
     120    assert_nil @handler.when_ready 
     121  end 
     122end 
     123 
     124 
     125class RailsFCGIHandlerSignalsTest < Test::Unit::TestCase 
     126  def setup 
     127    @log = StringIO.new 
     128    @handler = RailsFCGIHandler.new(@log) 
    130129  end 
    131130 
    132131  def test_interrupted_via_HUP_when_not_in_request 
    133     @handler.expects(:reload!) 
    134     FCGI.time_to_sleep = 1 
    135     @handler.thread = Thread.new { @handler.process! } 
    136     sleep 0.1 # let the thread get started 
    137     @handler.send_signal("HUP") 
    138     @handler.thread.join 
    139     assert_nil @handler.exit_code 
     132    cgi = mock 
     133    FCGI.expects(:each_cgi).once.yields(cgi) 
     134    @handler.expects(:gc_countdown).returns { Process.kill 'HUP', $$ } 
     135 
     136    @handler.expects(:reload!).once 
     137    @handler.expects(:close_connection).never 
     138    @handler.expects(:exit).never 
     139 
     140    @handler.process! 
    140141    assert_equal :reload, @handler.when_ready 
    141142  end 
    142143 
    143144  def test_interrupted_via_HUP_when_in_request 
    144     @handler.expects(:reload!) 
    145      
    146     Dispatcher.time_to_sleep = 1 
    147     @handler.thread = Thread.new { @handler.process! } 
    148     sleep 0.1 # let the thread get started 
    149     @handler.send_signal("HUP") 
    150     @handler.thread.join 
    151     assert_nil @handler.exit_code 
     145    cgi = mock 
     146    FCGI.expects(:each_cgi).once.yields(cgi) 
     147    Dispatcher.expects(:dispatch).with(cgi).returns { Process.kill 'HUP', $$ } 
     148 
     149    @handler.expects(:reload!).once 
     150    @handler.expects(:close_connection).never 
     151    @handler.expects(:exit).never 
     152 
     153    @handler.process! 
    152154    assert_equal :reload, @handler.when_ready 
    153155  end 
    154156 
    155157  def test_interrupted_via_USR1_when_not_in_request 
    156     FCGI.time_to_sleep = 1 
    157     @handler.thread = Thread.new { @handler.process! } 
    158     sleep 0.1 # let the thread get started 
    159     @handler.send_signal("USR1") 
    160     @handler.thread.join 
    161     assert_nil @handler.exit_code 
     158    cgi = mock 
     159    FCGI.expects(:each_cgi).once.yields(cgi) 
     160    @handler.expects(:gc_countdown).returns { Process.kill 'USR1', $$ } 
     161    @handler.expects(:exit_handler).never 
     162 
     163    @handler.expects(:reload!).never 
     164    @handler.expects(:close_connection).with(cgi).once 
     165    @handler.expects(:exit).never 
     166 
     167    @handler.process! 
     168    assert_nil @handler.when_ready 
     169  end 
     170 
     171  def test_interrupted_via_USR1_when_in_request 
     172    cgi = mock 
     173    FCGI.expects(:each_cgi).once.yields(cgi) 
     174    Dispatcher.expects(:dispatch).with(cgi).returns { Process.kill 'USR1', $$ } 
     175 
     176    @handler.expects(:reload!).never 
     177    @handler.expects(:close_connection).with(cgi).once 
     178    @handler.expects(:exit).never 
     179 
     180    @handler.process! 
    162181    assert_equal :exit, @handler.when_ready 
    163182  end 
    164183 
    165   def test_interrupted_via_USR1_when_in_request 
    166     Dispatcher.time_to_sleep = 1 
    167     @handler.thread = Thread.new { @handler.process! } 
    168     sleep 0.1 # let the thread get started 
    169     @handler.send_signal("USR1") 
    170     @handler.thread.join 
    171     assert_nil @handler.exit_code 
    172     assert_equal :exit, @handler.when_ready 
    173   end 
    174    
    175184  def test_interrupted_via_TERM 
    176     Dispatcher.time_to_sleep = 1 
    177     @handler.thread = Thread.new { @handler.process! } 
    178     sleep 0.1 # let the thread get started 
    179     @handler.send_signal("TERM") 
    180     @handler.thread.join 
    181     assert_equal 0, @handler.exit_code 
    182     assert_nil @handler.when_ready 
    183   end 
    184  
    185   %w(RuntimeError SignalException).each do |exception| 
    186     define_method("test_#{exception}_in_fcgi") do 
    187       FCGI.raise_exception = Object.const_get(exception) 
    188       @handler.process! 
    189       assert_match %r{Dispatcher failed to catch}, @log.string 
    190       case exception 
    191         when "RuntimeError" 
    192           assert_match %r{almost killed}, @log.string 
    193         when "SignalException" 
    194           assert_match %r{^killed}, @log.string 
    195       end 
    196     end 
    197  
    198     define_method("test_#{exception}_in_dispatcher") do 
    199       Dispatcher.raise_exception = Object.const_get(exception) 
    200       @handler.process! 
    201       assert_match %r{Dispatcher failed to catch}, @log.string 
    202       case exception 
    203         when "RuntimeError" 
    204           assert_no_match %r{killed}, @log.string 
    205         when "SignalException" 
    206           assert_match %r{^killed}, @log.string 
    207       end 
    208     end 
     185    cgi = mock 
     186    FCGI.expects(:each_cgi).once.yields(cgi) 
     187    Dispatcher.expects(:dispatch).with(cgi).returns { Process.kill 'TERM', $$ } 
     188 
     189    @handler.expects(:reload!).never 
     190    @handler.expects(:close_connection).never 
     191 
     192    @handler.process! 
     193    assert_nil @handler.when_ready 
     194  end 
     195 
     196  def test_runtime_exception_in_fcgi 
     197    error = RuntimeError.new('foo') 
     198    FCGI.expects(:each_cgi).times(2).raises(error) 
     199    @handler.expects(:dispatcher_error).with(error, regexp_matches(/^retrying/)) 
     200    @handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/)) 
     201    @handler.process! 
     202  end 
     203 
     204  def test_runtime_error_in_dispatcher 
     205    cgi = mock 
     206    error = RuntimeError.new('foo') 
     207    FCGI.expects(:each_cgi).once.yields(cgi) 
     208    Dispatcher.expects(:dispatch).once.with(cgi).raises(error) 
     209    @handler.expects(:dispatcher_error).with(error, regexp_matches(/^unhandled/)) 
     210    @handler.process! 
     211  end 
     212 
     213  def test_signal_exception_in_fcgi 
     214    error = SignalException.new('USR2') 
     215    FCGI.expects(:each_cgi).once.raises(error) 
     216    @handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/)) 
     217    @handler.process! 
     218  end 
     219 
     220  def test_signal_exception_in_dispatcher 
     221    cgi = mock 
     222    error = SignalException.new('USR2') 
     223    FCGI.expects(:each_cgi).once.yields(cgi) 
     224    Dispatcher.expects(:dispatch).once.with(cgi).raises(error) 
     225    @handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/)) 
     226    @handler.process! 
    209227  end 
    210228end 
     229 
    211230 
    212231class RailsFCGIHandlerPeriodicGCTest < Test::Unit::TestCase 
    213232  def setup 
    214233    @log = StringIO.new 
    215     FCGI.time_to_sleep = nil 
    216     FCGI.raise_exception = nil 
    217     FCGI.each_cgi_count = nil 
    218     Dispatcher.time_to_sleep = nil 
    219     Dispatcher.raise_exception = nil 
    220     Dispatcher.dispatch_hook = nil 
    221234  end 
    222235 
    223236  def teardown 
    224     FCGI.each_cgi_count = nil 
    225     Dispatcher.dispatch_hook = nil 
    226237    GC.enable 
    227238  end 
     
    236247 
    237248  def test_periodic_gc 
    238     Dispatcher.dispatch_hook = lambda do |cgi| 
    239       # When GC is disabled, GC.enable enables and returns true. 
    240       assert_equal true, GC.enable 
    241       GC.disable 
    242     end 
    243  
    244249    @handler = RailsFCGIHandler.new(@log, 10) 
    245250    assert_equal 10, @handler.gc_request_period 
    246     FCGI.each_cgi_count = 1 
    247     @handler.process! 
    248     assert_equal 1, @handler.gc_runs 
    249  
    250     FCGI.each_cgi_count = 10 
    251     @handler.process! 
    252     assert_equal 3, @handler.gc_runs 
    253  
    254     FCGI.each_cgi_count = 25 
    255     @handler.process! 
    256     assert_equal 6, @handler.gc_runs 
    257  
    258     assert_nil @handler.exit_code 
     251 
     252    cgi = mock 
     253    FCGI.expects(:each_cgi).times(10).yields(cgi) 
     254    Dispatcher.expects(:dispatch).times(10).with(cgi) 
     255 
     256    @handler.expects(:run_gc!).never 
     257    9.times { @handler.process! } 
     258    @handler.expects(:run_gc!).once 
     259    @handler.process! 
     260 
    259261    assert_nil @handler.when_ready 
    260262  end 
    261263end 
    262264 
    263 rescue LoadError => e 
    264   $stderr.puts "Skipping dispatcher tests. `gem install mocha` and try again. (#{e})" 
    265 end 
     265end # uses_mocha 
  • trunk/railties/test/rails_generator_test.rb

    r7551 r7593  
    3838 
    3939class RailsGeneratorTest < Test::Unit::TestCase 
    40   BUILTINS = %w(controller integration_test mailer migration model observer plugin resource scaffold session_migration web_service
     40  BUILTINS = %w(controller integration_test mailer migration model observer plugin resource scaffold session_migration
    4141  CAPITALIZED_BUILTINS = BUILTINS.map { |b| b.capitalize } 
    4242