abstract_unit.rb 13.0 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 17 18 19
require 'drb'
require 'drb/unix'
require 'tempfile'

20 21
PROCESS_COUNT = (ENV['N'] || 4).to_i

22
require 'active_support/testing/autorun'
J
Joshua Peek 已提交
23
require 'abstract_controller'
24
require 'action_controller'
J
Joshua Peek 已提交
25
require 'action_view'
26
require 'action_view/testing/resolvers'
J
Joshua Peek 已提交
27
require 'action_dispatch'
28
require 'active_support/dependencies'
29
require 'active_model'
N
Neeraj Singh 已提交
30 31
require 'active_record'
require 'action_controller/caching'
32

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

35
module Rails
36 37 38 39 40
  class << self
    def env
      @_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "test")
    end
  end
41 42
end

43
ActiveSupport::Dependencies.hook!
44

45 46
Thread.abort_on_exception = true

47 48 49
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true

50 51 52
# Disable available locale checks to avoid warnings running the test suite.
I18n.enforce_available_locales = false

J
Joshua Peek 已提交
53 54
# Register danish language for testing
I18n.backend.store_translations 'da', {}
55
I18n.backend.store_translations 'pt-BR', {}
56
ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort
J
Joshua Peek 已提交
57

Y
Yehuda Katz + Carl Lerche 已提交
58
FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
59
FIXTURES = Pathname.new(FIXTURE_LOAD_PATH)
60

61 62 63 64 65 66 67 68 69 70 71 72 73
module RackTestUtils
  def body_to_string(body)
    if body.respond_to?(:each)
      str = ""
      body.each {|s| str << s }
      str
    else
      body
    end
  end
  extend self
end

C
Carlhuda 已提交
74 75
SharedTestRoutes = ActionDispatch::Routing::RouteSet.new

76 77 78 79 80 81 82 83
module ActionDispatch
  module SharedRoutes
    def before_setup
      @routes = SharedTestRoutes
      super
    end
  end

84 85 86 87 88 89 90 91 92 93 94 95
  # 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

96
      SharedTestRoutes.draw do
97
        get ':controller(/:action)'
C
Carlhuda 已提交
98 99
      end

100
      ActionDispatch::IntegrationTest.app.routes.draw do
101
        get ':controller(/:action)'
C
Carlhuda 已提交
102
      end
103 104

      DrawOnce.drew = true
J
Joshua Peek 已提交
105 106 107 108
    end
  end
end

109 110 111
module ActiveSupport
  class TestCase
    include ActionDispatch::DrawOnce
112 113 114
    if ActiveSupport::Testing::Isolation.forking_env? && PROCESS_COUNT > 0
      parallelize_me!
    end
115 116 117
  end
end

C
Carlhuda 已提交
118
class RoutedRackApp
J
Joshua Peek 已提交
119
  attr_reader :routes
C
Carlhuda 已提交
120

J
Joshua Peek 已提交
121 122 123
  def initialize(routes, &blk)
    @routes = routes
    @stack = ActionDispatch::MiddlewareStack.new(&blk).build(@routes)
C
Carlhuda 已提交
124 125 126 127 128 129 130
  end

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

131
class BasicController
132
  attr_accessor :request
133 134

  def config
135
    @config ||= ActiveSupport::InheritableOptions.new(ActionController::Base.config).tap do |config|
136 137 138 139 140
      # VIEW TODO: View tests should not require a controller
      public_dir = File.expand_path("../fixtures/public", __FILE__)
      config.assets_dir = public_dir
      config.javascripts_dir = "#{public_dir}/javascripts"
      config.stylesheets_dir = "#{public_dir}/stylesheets"
141
      config.assets          = ActiveSupport::InheritableOptions.new({ :prefix => "assets" })
142 143 144 145 146
      config
    end
  end
end

147
class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
148
  include ActionDispatch::SharedRoutes
149

150
  def self.build_app(routes = nil)
C
Carlhuda 已提交
151
    RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware|
152
      middleware.use "ActionDispatch::ShowExceptions", ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public")
153
      middleware.use "ActionDispatch::DebugExceptions"
154 155
      middleware.use "ActionDispatch::Callbacks"
      middleware.use "ActionDispatch::ParamsParser"
J
Joshua Peek 已提交
156
      middleware.use "ActionDispatch::Cookies"
J
Joshua Peek 已提交
157
      middleware.use "ActionDispatch::Flash"
158
      middleware.use "Rack::Head"
159
      yield(middleware) if block_given?
C
Carlhuda 已提交
160
    end
161 162 163 164
  end

  self.app = build_app

165 166 167 168 169 170 171 172 173 174
  # 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}"]]
175 176 177 178 179 180 181 182 183 184 185 186 187
    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

188
  def with_routing(&block)
189
    temporary_routes = ActionDispatch::Routing::RouteSet.new
C
Carlhuda 已提交
190 191 192
    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) }
193 194 195

    yield temporary_routes
  ensure
C
Carlhuda 已提交
196 197
    self.class.app = old_app
    silence_warnings { Object.const_set(:SharedTestRoutes, old_routes) }
198
  end
199 200

  def with_autoload_path(path)
201
    path = File.join(File.dirname(__FILE__), "fixtures", path)
202 203 204 205 206 207 208 209 210 211 212 213
    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
214 215
end

J
Joshua Peek 已提交
216
# Temporary base class
217
class Rack::TestCase < ActionDispatch::IntegrationTest
J
Joshua Peek 已提交
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
  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)
235
    assert_equal body, Array(response.body).join
J
Joshua Peek 已提交
236 237 238 239 240 241 242
  end

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

  def assert_response(body, status = 200, headers = {})
243
    assert_body body
J
Joshua Peek 已提交
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
    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

259 260
module ActionController
  class Base
261 262
    # This stub emulates the Railtie including the URL helpers from a Rails application
    include SharedTestRoutes.url_helpers
263
    include SharedTestRoutes.mounted_helpers
264

265 266 267 268 269 270 271 272
    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
273

274
  class TestCase
J
Joshua Peek 已提交
275
    include ActionDispatch::TestProcess
276
    include ActionDispatch::SharedRoutes
277 278
  end
end
C
Carlhuda 已提交
279

280

281 282 283
class ::ApplicationController < ActionController::Base
end

A
Aaron Patterson 已提交
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
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 已提交
301 302

module ActionDispatch
303 304 305 306 307 308 309
  class DebugExceptions
    private
    remove_method :stderr_logger
    # Silence logger
    def stderr_logger
      nil
    end
A
Aaron Patterson 已提交
310 311 312
  end
end

313 314
module ActionDispatch
  module RoutingVerbs
315
    def send_request(uri_or_host, method, path)
316 317 318 319
      host = uri_or_host.host unless path
      path ||= uri_or_host.path

      params = {'PATH_INFO'      => path,
320
                'REQUEST_METHOD' => method,
321 322
                'HTTP_HOST'      => host}

323 324 325
      routes.call(params)
    end

326 327
    def request_path_params(path, options = {})
      method = options[:method] || 'GET'
328
      resp = send_request URI('http://localhost' + path), method.to_s.upcase, nil
A
Aaron Patterson 已提交
329
      status = resp.first
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
      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
354 355 356 357
    end
  end
end

L
lest 已提交
358
module RoutingTestHelpers
A
Aaron Patterson 已提交
359
  def url_for(set, options)
360 361
    route_name = options.delete :use_route
    set.url_for options.merge(:only_path => true), route_name
L
lest 已提交
362
  end
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404

  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 已提交
405
end
406 407 408 409 410 411 412 413 414

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
415
class ReviewsController < ResourcesController; end
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
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
433 434 435 436 437 438 439 440 441

# 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
442

443
require 'mocha/setup' # FIXME: stop using mocha
444

445 446 447 448 449 450 451 452 453
class ForkingExecutor
  class Server
    include DRb::DRbUndumped

    def initialize
      @queue = Queue.new
    end

    def record reporter, result
454
      reporter.record result
455 456
    end

457 458 459 460
    def << o
      o[2] = DRbObject.new(o[2]) if o
      @queue << o
    end
461 462 463 464 465 466
    def pop; @queue.pop; end
  end

  def initialize size
    @size  = size
    @queue = Server.new
A
Agis- 已提交
467
    file   = File.join Dir.tmpdir, Dir::Tmpname.make_tmpname('rails-tests', 'fd')
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
    @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
485 486 487
          if result.error?
            translate_exceptions result
          end
488 489 490 491 492 493 494
          queue.record reporter, result
        end
      }
    }
    @size.times { @queue << nil }
    pool.each { |pid| Process.waitpid pid }
  end
495 496 497 498 499 500 501 502 503 504 505 506 507 508

  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
509 510
end

511
if ActiveSupport::Testing::Isolation.forking_env? && PROCESS_COUNT > 0
512
  # Use N processes (N defaults to 4)
513
  Minitest.parallel_executor = ForkingExecutor.new(PROCESS_COUNT)
514
end
515 516 517 518

# FIXME: we have tests that depend on run order, we should fix that and
# remove this method call.
require 'active_support/test_case'
519
ActiveSupport::TestCase.test_order = :sorted