abstract_unit.rb 12.1 KB
Newer Older
1
require File.expand_path('../../../load_paths', __FILE__)
2

3
$:.unshift(File.dirname(__FILE__) + '/lib')
4
$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers')
5
$:.unshift(File.dirname(__FILE__) + '/fixtures/alternate_helpers')
D
Initial  
David Heinemeier Hansson 已提交
6

7 8
require 'active_support/core_ext/kernel/reporting'

9 10 11 12 13
# These are the normal settings that will be set up by Railties
# TODO: Have these tests support other combinations of these values
silence_warnings do
  Encoding.default_internal = "UTF-8"
  Encoding.default_external = "UTF-8"
14 15
end

16
require 'drb'
17 18 19 20 21
begin
  require 'drb/unix'
rescue LoadError
  puts "'drb/unix' is not available"
end
22 23
require 'tempfile'

24 25
PROCESS_COUNT = (ENV['N'] || 4).to_i

26
require 'active_support/testing/autorun'
J
Joshua Peek 已提交
27
require 'abstract_controller'
28
require 'abstract_controller/railties/routes_helpers'
29
require 'action_controller'
J
Joshua Peek 已提交
30
require 'action_view'
31
require 'action_view/testing/resolvers'
J
Joshua Peek 已提交
32
require 'action_dispatch'
33
require 'active_support/dependencies'
34
require 'active_model'
N
Neeraj Singh 已提交
35 36
require 'active_record'
require 'action_controller/caching'
37

J
Joshua Peek 已提交
38 39
require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late

40
module Rails
41 42
  class << self
    def env
43
      @_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "test")
44 45
    end
  end
46 47
end

48
ActiveSupport::Dependencies.hook!
49

50 51
Thread.abort_on_exception = true

52 53 54
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true

55 56 57
# Disable available locale checks to avoid warnings running the test suite.
I18n.enforce_available_locales = false

J
Joshua Peek 已提交
58 59
# Register danish language for testing
I18n.backend.store_translations 'da', {}
60
I18n.backend.store_translations 'pt-BR', {}
J
Joshua Peek 已提交
61

Y
Yehuda Katz + Carl Lerche 已提交
62
FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
63

C
Carlhuda 已提交
64 65
SharedTestRoutes = ActionDispatch::Routing::RouteSet.new

66 67 68 69 70 71 72 73
module ActionDispatch
  module SharedRoutes
    def before_setup
      @routes = SharedTestRoutes
      super
    end
  end

74 75 76 77 78 79 80 81 82 83 84 85
  # Hold off drawing routes until all the possible controller classes
  # have been loaded.
  module DrawOnce
    class << self
      attr_accessor :drew
    end
    self.drew = false

    def before_setup
      super
      return if DrawOnce.drew

86
      SharedTestRoutes.draw do
87
        get ':controller(/:action)'
C
Carlhuda 已提交
88 89
      end

90
      ActionDispatch::IntegrationTest.app.routes.draw do
91
        get ':controller(/:action)'
C
Carlhuda 已提交
92
      end
93 94

      DrawOnce.drew = true
J
Joshua Peek 已提交
95 96 97 98
    end
  end
end

99 100 101
module ActiveSupport
  class TestCase
    include ActionDispatch::DrawOnce
102
    if RUBY_ENGINE == "ruby" && PROCESS_COUNT > 0
103 104
      parallelize_me!
    end
105 106 107
  end
end

C
Carlhuda 已提交
108
class RoutedRackApp
J
Joshua Peek 已提交
109
  attr_reader :routes
C
Carlhuda 已提交
110

J
Joshua Peek 已提交
111 112 113
  def initialize(routes, &blk)
    @routes = routes
    @stack = ActionDispatch::MiddlewareStack.new(&blk).build(@routes)
C
Carlhuda 已提交
114 115 116 117 118 119 120
  end

  def call(env)
    @stack.call(env)
  end
end

121
class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
122
  include ActionDispatch::SharedRoutes
123

124
  def self.build_app(routes = nil)
C
Carlhuda 已提交
125
    RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware|
126
      middleware.use "ActionDispatch::ShowExceptions", ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public")
127
      middleware.use "ActionDispatch::DebugExceptions"
128 129
      middleware.use "ActionDispatch::Callbacks"
      middleware.use "ActionDispatch::ParamsParser"
J
Joshua Peek 已提交
130
      middleware.use "ActionDispatch::Cookies"
J
Joshua Peek 已提交
131
      middleware.use "ActionDispatch::Flash"
132
      middleware.use "Rack::Head"
133
      yield(middleware) if block_given?
C
Carlhuda 已提交
134
    end
135 136 137 138
  end

  self.app = build_app

139 140 141 142 143 144 145 146 147 148
  # Stub Rails dispatcher so it does not get controller references and
  # simply return the controller#action as Rack::Body.
  class StubDispatcher < ::ActionDispatch::Routing::RouteSet::Dispatcher
    protected
    def controller_reference(controller_param)
      controller_param
    end

    def dispatch(controller, action, env)
      [200, {'Content-Type' => 'text/html'}, ["#{controller}##{action}"]]
149 150 151 152 153 154 155 156 157 158 159 160 161
    end
  end

  def self.stub_controllers
    old_dispatcher = ActionDispatch::Routing::RouteSet::Dispatcher
    ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher }
    ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, StubDispatcher }
    yield ActionDispatch::Routing::RouteSet.new
  ensure
    ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher }
    ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, old_dispatcher }
  end

162
  def with_routing(&block)
163
    temporary_routes = ActionDispatch::Routing::RouteSet.new
C
Carlhuda 已提交
164 165 166
    old_app, self.class.app = self.class.app, self.class.build_app(temporary_routes)
    old_routes = SharedTestRoutes
    silence_warnings { Object.const_set(:SharedTestRoutes, temporary_routes) }
167 168 169

    yield temporary_routes
  ensure
C
Carlhuda 已提交
170
    self.class.app = old_app
171
    self.remove!
C
Carlhuda 已提交
172
    silence_warnings { Object.const_set(:SharedTestRoutes, old_routes) }
173
  end
174 175

  def with_autoload_path(path)
176
    path = File.join(File.dirname(__FILE__), "fixtures", path)
177 178 179 180 181 182 183 184 185 186 187 188
    if ActiveSupport::Dependencies.autoload_paths.include?(path)
      yield
    else
      begin
        ActiveSupport::Dependencies.autoload_paths << path
        yield
      ensure
        ActiveSupport::Dependencies.autoload_paths.reject! {|p| p == path}
        ActiveSupport::Dependencies.clear
      end
    end
  end
189 190
end

J
Joshua Peek 已提交
191
# Temporary base class
192
class Rack::TestCase < ActionDispatch::IntegrationTest
J
Joshua Peek 已提交
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
  def self.testing(klass = nil)
    if klass
      @testing = "/#{klass.name.underscore}".sub!(/_controller$/, '')
    else
      @testing
    end
  end

  def get(thing, *args)
    if thing.is_a?(Symbol)
      super("#{self.class.testing}/#{thing}", *args)
    else
      super
    end
  end

  def assert_body(body)
210
    assert_equal body, Array(response.body).join
J
Joshua Peek 已提交
211 212 213 214 215 216 217
  end

  def assert_status(code)
    assert_equal code, response.status
  end

  def assert_response(body, status = 200, headers = {})
218
    assert_body body
J
Joshua Peek 已提交
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
    assert_status status
    headers.each do |header, value|
      assert_header header, value
    end
  end

  def assert_content_type(type)
    assert_equal type, response.headers["Content-Type"]
  end

  def assert_header(name, value)
    assert_equal value, response.headers[name]
  end
end

234 235
module ActionController
  class Base
236
    # This stub emulates the Railtie including the URL helpers from a Rails application
237
    extend AbstractController::Railties::RoutesHelpers.with(SharedTestRoutes)
238
    include SharedTestRoutes.mounted_helpers
239

240 241 242 243 244 245 246 247
    self.view_paths = FIXTURE_LOAD_PATH

    def self.test_routes(&block)
      routes = ActionDispatch::Routing::RouteSet.new
      routes.draw(&block)
      include routes.url_helpers
    end
  end
248

249
  class TestCase
J
Joshua Peek 已提交
250
    include ActionDispatch::TestProcess
251
    include ActionDispatch::SharedRoutes
252 253
  end
end
C
Carlhuda 已提交
254

255

256 257 258
class ::ApplicationController < ActionController::Base
end

A
Aaron Patterson 已提交
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
class Workshop
  extend ActiveModel::Naming
  include ActiveModel::Conversion
  attr_accessor :id

  def initialize(id)
    @id = id
  end

  def persisted?
    id.present?
  end

  def to_s
    id.to_s
  end
end
A
Aaron Patterson 已提交
276 277

module ActionDispatch
278 279 280 281 282 283 284
  class DebugExceptions
    private
    remove_method :stderr_logger
    # Silence logger
    def stderr_logger
      nil
    end
A
Aaron Patterson 已提交
285 286 287
  end
end

288 289
module ActionDispatch
  module RoutingVerbs
290
    def send_request(uri_or_host, method, path)
291 292 293 294
      host = uri_or_host.host unless path
      path ||= uri_or_host.path

      params = {'PATH_INFO'      => path,
295
                'REQUEST_METHOD' => method,
296 297
                'HTTP_HOST'      => host}

298 299 300
      routes.call(params)
    end

301 302
    def request_path_params(path, options = {})
      method = options[:method] || 'GET'
303
      resp = send_request URI('http://localhost' + path), method.to_s.upcase, nil
A
Aaron Patterson 已提交
304
      status = resp.first
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
      if status == 404
        raise ActionController::RoutingError, "No route matches #{path.inspect}"
      end
      controller.request.path_parameters
    end

    def get(uri_or_host, path = nil)
      send_request(uri_or_host, 'GET', path)[2].join
    end

    def post(uri_or_host, path = nil)
      send_request(uri_or_host, 'POST', path)[2].join
    end

    def put(uri_or_host, path = nil)
      send_request(uri_or_host, 'PUT', path)[2].join
    end

    def delete(uri_or_host, path = nil)
      send_request(uri_or_host, 'DELETE', path)[2].join
    end

    def patch(uri_or_host, path = nil)
      send_request(uri_or_host, 'PATCH', path)[2].join
329 330 331 332
    end
  end
end

L
lest 已提交
333
module RoutingTestHelpers
A
Aaron Patterson 已提交
334
  def url_for(set, options)
335 336
    route_name = options.delete :use_route
    set.url_for options.merge(:only_path => true), route_name
L
lest 已提交
337
  end
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379

  def make_set(strict = true)
    tc = self
    TestSet.new ->(c) { tc.controller = c }, strict
  end

  class TestSet < ActionDispatch::Routing::RouteSet
    attr_reader :strict

    def initialize(block, strict = false)
      @block = block
      @strict = strict
      super()
    end

    class Dispatcher < ActionDispatch::Routing::RouteSet::Dispatcher
      def initialize(defaults, set, block)
        super(defaults)
        @block = block
        @set = set
      end

      def controller(params, default_controller=true)
        super(params, @set.strict)
      end

      def controller_reference(controller_param)
        block = @block
        set = @set
        super if @set.strict
        Class.new(ActionController::Base) {
          include set.url_helpers
          define_method(:process) { |name| block.call(self) }
          def to_a; [200, {}, []]; end
        }
      end
    end

    def dispatcher defaults
      TestSet::Dispatcher.new defaults, self, @block
    end
  end
L
lest 已提交
380
end
381 382 383 384 385 386 387 388 389

class ResourcesController < ActionController::Base
  def index() render :nothing => true end
  alias_method :show, :index
end

class ThreadsController  < ResourcesController; end
class MessagesController < ResourcesController; end
class CommentsController < ResourcesController; end
390
class ReviewsController < ResourcesController; end
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
class LogosController < ResourcesController; end

class AccountsController <  ResourcesController; end
class AdminController   <  ResourcesController; end
class ProductsController < ResourcesController; end
class ImagesController < ResourcesController; end
class PreferencesController < ResourcesController; end

module Backoffice
  class ProductsController < ResourcesController; end
  class ImagesController < ResourcesController; end

  module Admin
    class ProductsController < ResourcesController; end
    class ImagesController < ResourcesController; end
  end
end
408 409 410 411 412 413 414 415 416

# Skips the current run on Rubinius using Minitest::Assertions#skip
def rubinius_skip(message = '')
  skip message if RUBY_ENGINE == 'rbx'
end
# Skips the current run on JRuby using Minitest::Assertions#skip
def jruby_skip(message = '')
  skip message if defined?(JRUBY_VERSION)
end
417

418
require 'mocha/setup' # FIXME: stop using mocha
419

420 421 422 423 424 425 426 427 428
class ForkingExecutor
  class Server
    include DRb::DRbUndumped

    def initialize
      @queue = Queue.new
    end

    def record reporter, result
429
      reporter.record result
430 431
    end

432 433 434 435
    def << o
      o[2] = DRbObject.new(o[2]) if o
      @queue << o
    end
436 437 438 439 440 441
    def pop; @queue.pop; end
  end

  def initialize size
    @size  = size
    @queue = Server.new
A
Agis- 已提交
442
    file   = File.join Dir.tmpdir, Dir::Tmpname.make_tmpname('rails-tests', 'fd')
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
    @url   = "drbunix://#{file}"
    @pool  = nil
    DRb.start_service @url, @queue
  end

  def << work; @queue << work; end

  def shutdown
    pool = @size.times.map {
      fork {
        DRb.stop_service
        queue = DRbObject.new_with_uri @url
        while job = queue.pop
          klass    = job[0]
          method   = job[1]
          reporter = job[2]
          result = Minitest.run_one_method klass, method
460 461 462
          if result.error?
            translate_exceptions result
          end
463 464 465 466 467 468 469
          queue.record reporter, result
        end
      }
    }
    @size.times { @queue << nil }
    pool.each { |pid| Process.waitpid pid }
  end
470 471 472 473 474 475 476 477 478 479 480 481 482 483

  private
  def translate_exceptions(result)
    result.failures.map! { |e|
      begin
        Marshal.dump e
        e
      rescue TypeError
        ex = Exception.new e.message
        ex.set_backtrace e.backtrace
        Minitest::UnexpectedError.new ex
      end
    }
  end
484 485
end

486
if RUBY_ENGINE == "ruby" && PROCESS_COUNT > 0
487
  # Use N processes (N defaults to 4)
488
  Minitest.parallel_executor = ForkingExecutor.new(PROCESS_COUNT)
489
end