mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
8dc784292b
This commit eagerly builds the route helper module after the routes have been drawn and finalized. This allows us to cache the helper module but not have to worry about people accessing the module while route definition is "in-flight", and automatically deals with cache invalidation as the module is regenerated anytime someone redraws the routes. The restriction this commit introduces is that the url helper module can only be accessed *after* the routes are done being drawn. Refs #24554 and #32892
463 lines
11 KiB
Ruby
463 lines
11 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
$:.unshift File.expand_path("lib", __dir__)
|
|
$:.unshift File.expand_path("fixtures/helpers", __dir__)
|
|
$:.unshift File.expand_path("fixtures/alternate_helpers", __dir__)
|
|
|
|
require "active_support/core_ext/kernel/reporting"
|
|
|
|
# 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 = Encoding::UTF_8
|
|
Encoding.default_external = Encoding::UTF_8
|
|
end
|
|
|
|
require "drb"
|
|
begin
|
|
require "drb/unix"
|
|
rescue LoadError
|
|
puts "'drb/unix' is not available"
|
|
end
|
|
|
|
if ENV["TRAVIS"]
|
|
PROCESS_COUNT = 0
|
|
else
|
|
PROCESS_COUNT = (ENV["N"] || 4).to_i
|
|
end
|
|
|
|
require "active_support/testing/autorun"
|
|
require "abstract_controller"
|
|
require "abstract_controller/railties/routes_helpers"
|
|
require "action_controller"
|
|
require "action_view"
|
|
require "action_view/testing/resolvers"
|
|
require "action_dispatch"
|
|
require "active_support/dependencies"
|
|
require "active_model"
|
|
|
|
require "pp" # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late
|
|
|
|
module Rails
|
|
class << self
|
|
def env
|
|
@_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "test")
|
|
end
|
|
|
|
def root; end
|
|
end
|
|
end
|
|
|
|
ActiveSupport::Dependencies.hook!
|
|
|
|
Thread.abort_on_exception = true
|
|
|
|
# Show backtraces for deprecated behavior for quicker cleanup.
|
|
ActiveSupport::Deprecation.debug = true
|
|
|
|
# Disable available locale checks to avoid warnings running the test suite.
|
|
I18n.enforce_available_locales = false
|
|
|
|
FIXTURE_LOAD_PATH = File.join(__dir__, "fixtures")
|
|
|
|
SharedTestRoutes = ActionDispatch::Routing::RouteSet.new
|
|
|
|
SharedTestRoutes.draw do
|
|
ActiveSupport::Deprecation.silence do
|
|
get ":controller(/:action)"
|
|
end
|
|
end
|
|
|
|
module ActionDispatch
|
|
module SharedRoutes
|
|
def before_setup
|
|
@routes = SharedTestRoutes
|
|
super
|
|
end
|
|
end
|
|
end
|
|
|
|
module ActiveSupport
|
|
class TestCase
|
|
if RUBY_ENGINE == "ruby" && PROCESS_COUNT > 0
|
|
parallelize_me!
|
|
end
|
|
end
|
|
end
|
|
|
|
class RoutedRackApp
|
|
attr_reader :routes
|
|
|
|
def initialize(routes, &blk)
|
|
@routes = routes
|
|
@stack = ActionDispatch::MiddlewareStack.new(&blk).build(@routes)
|
|
end
|
|
|
|
def call(env)
|
|
@stack.call(env)
|
|
end
|
|
end
|
|
|
|
class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
|
|
def self.build_app(routes = nil)
|
|
routes ||= ActionDispatch::Routing::RouteSet.new.tap { |rs|
|
|
rs.draw { }
|
|
}
|
|
RoutedRackApp.new(routes) do |middleware|
|
|
middleware.use ActionDispatch::ShowExceptions, ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public")
|
|
middleware.use ActionDispatch::DebugExceptions
|
|
middleware.use ActionDispatch::Callbacks
|
|
middleware.use ActionDispatch::Cookies
|
|
middleware.use ActionDispatch::Flash
|
|
middleware.use Rack::MethodOverride
|
|
middleware.use Rack::Head
|
|
yield(middleware) if block_given?
|
|
end
|
|
end
|
|
|
|
self.app = build_app
|
|
|
|
app.routes.draw do
|
|
ActiveSupport::Deprecation.silence do
|
|
get ":controller(/:action)"
|
|
end
|
|
end
|
|
|
|
class DeadEndRoutes < ActionDispatch::Routing::RouteSet
|
|
# Stub Rails dispatcher so it does not get controller references and
|
|
# simply return the controller#action as Rack::Body.
|
|
class NullController < ::ActionController::Metal
|
|
def self.dispatch(action, req, res)
|
|
[200, { "Content-Type" => "text/html" }, ["#{req.params[:controller]}##{action}"]]
|
|
end
|
|
end
|
|
|
|
class NullControllerRequest < ActionDispatch::Request
|
|
def controller_class
|
|
NullController
|
|
end
|
|
end
|
|
|
|
def make_request(env)
|
|
NullControllerRequest.new env
|
|
end
|
|
end
|
|
|
|
def self.stub_controllers(config = ActionDispatch::Routing::RouteSet::DEFAULT_CONFIG)
|
|
yield DeadEndRoutes.new(config)
|
|
end
|
|
|
|
def with_routing(&block)
|
|
temporary_routes = ActionDispatch::Routing::RouteSet.new
|
|
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) }
|
|
|
|
yield temporary_routes
|
|
ensure
|
|
self.class.app = old_app
|
|
remove!
|
|
silence_warnings { Object.const_set(:SharedTestRoutes, old_routes) }
|
|
end
|
|
|
|
def with_autoload_path(path)
|
|
path = File.join(__dir__, "fixtures", path)
|
|
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
|
|
end
|
|
|
|
# Temporary base class
|
|
class Rack::TestCase < ActionDispatch::IntegrationTest
|
|
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)
|
|
assert_equal body, Array(response.body).join
|
|
end
|
|
|
|
def assert_status(code)
|
|
assert_equal code, response.status
|
|
end
|
|
|
|
def assert_response(body, status = 200, headers = {})
|
|
assert_body body
|
|
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
|
|
|
|
module ActionController
|
|
class API
|
|
extend AbstractController::Railties::RoutesHelpers.with(SharedTestRoutes)
|
|
end
|
|
|
|
class Base
|
|
# This stub emulates the Railtie including the URL helpers from a Rails application
|
|
extend AbstractController::Railties::RoutesHelpers.with(SharedTestRoutes)
|
|
include SharedTestRoutes.mounted_helpers
|
|
|
|
self.view_paths = FIXTURE_LOAD_PATH
|
|
|
|
def self.test_routes(&block)
|
|
routes = ActionDispatch::Routing::RouteSet.new
|
|
routes.draw(&block)
|
|
include routes.url_helpers
|
|
routes
|
|
end
|
|
end
|
|
|
|
class TestCase
|
|
include ActionDispatch::TestProcess
|
|
include ActionDispatch::SharedRoutes
|
|
end
|
|
end
|
|
|
|
class ::ApplicationController < ActionController::Base
|
|
end
|
|
|
|
module ActionDispatch
|
|
class DebugExceptions
|
|
private
|
|
remove_method :stderr_logger
|
|
# Silence logger
|
|
def stderr_logger
|
|
nil
|
|
end
|
|
end
|
|
end
|
|
|
|
module ActionDispatch
|
|
module RoutingVerbs
|
|
def send_request(uri_or_host, method, path)
|
|
host = uri_or_host.host unless path
|
|
path ||= uri_or_host.path
|
|
|
|
params = { "PATH_INFO" => path,
|
|
"REQUEST_METHOD" => method,
|
|
"HTTP_HOST" => host }
|
|
|
|
routes.call(params)
|
|
end
|
|
|
|
def request_path_params(path, options = {})
|
|
method = options[:method] || "GET"
|
|
resp = send_request URI("http://localhost" + path), method.to_s.upcase, nil
|
|
status = resp.first
|
|
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
|
|
end
|
|
end
|
|
end
|
|
|
|
module RoutingTestHelpers
|
|
def url_for(set, options)
|
|
route_name = options.delete :use_route
|
|
set.url_for options.merge(only_path: true), route_name
|
|
end
|
|
|
|
def make_set(strict = true)
|
|
tc = self
|
|
TestSet.new ->(c) { tc.controller = c }, strict
|
|
end
|
|
|
|
class TestSet < ActionDispatch::Routing::RouteSet
|
|
class Request < DelegateClass(ActionDispatch::Request)
|
|
def initialize(target, helpers, block, strict)
|
|
super(target)
|
|
@helpers = helpers
|
|
@block = block
|
|
@strict = strict
|
|
end
|
|
|
|
def controller_class
|
|
helpers = @helpers
|
|
block = @block
|
|
Class.new(@strict ? super : ActionController::Base) {
|
|
include helpers
|
|
define_method(:process) { |name| block.call(self) }
|
|
def to_a; [200, {}, []]; end
|
|
}
|
|
end
|
|
end
|
|
|
|
attr_reader :strict
|
|
|
|
def initialize(block, strict = false)
|
|
@block = block
|
|
@strict = strict
|
|
super()
|
|
end
|
|
|
|
private
|
|
|
|
def make_request(env)
|
|
Request.new super, url_helpers, @block, strict
|
|
end
|
|
end
|
|
end
|
|
|
|
class ResourcesController < ActionController::Base
|
|
def index() head :ok end
|
|
alias_method :show, :index
|
|
end
|
|
|
|
class CommentsController < ResourcesController; end
|
|
class AccountsController < ResourcesController; end
|
|
class ImagesController < ResourcesController; end
|
|
|
|
require "active_support/testing/method_call_assertions"
|
|
|
|
class ForkingExecutor
|
|
class Server
|
|
include DRb::DRbUndumped
|
|
|
|
def initialize
|
|
@queue = Queue.new
|
|
end
|
|
|
|
def record(reporter, result)
|
|
reporter.record result
|
|
end
|
|
|
|
def <<(o)
|
|
o[2] = DRbObject.new(o[2]) if o
|
|
@queue << o
|
|
end
|
|
def pop; @queue.pop; end
|
|
end
|
|
|
|
def initialize(size)
|
|
@size = size
|
|
@queue = Server.new
|
|
@pool = nil
|
|
@url = DRb.start_service("drbunix:", @queue).uri
|
|
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
|
|
if result.error?
|
|
translate_exceptions result
|
|
end
|
|
queue.record reporter, result
|
|
end
|
|
}
|
|
}
|
|
@size.times { @queue << nil }
|
|
pool.each { |pid| Process.waitpid pid }
|
|
end
|
|
|
|
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
|
|
end
|
|
|
|
if RUBY_ENGINE == "ruby" && PROCESS_COUNT > 0
|
|
# Use N processes (N defaults to 4)
|
|
Minitest.parallel_executor = ForkingExecutor.new(PROCESS_COUNT)
|
|
end
|
|
|
|
class ActiveSupport::TestCase
|
|
include ActiveSupport::Testing::MethodCallAssertions
|
|
|
|
private
|
|
# 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
|
|
end
|
|
|
|
class DrivenByRackTest < ActionDispatch::SystemTestCase
|
|
driven_by :rack_test
|
|
end
|
|
|
|
class DrivenBySeleniumWithChrome < ActionDispatch::SystemTestCase
|
|
driven_by :selenium, using: :chrome
|
|
end
|
|
|
|
class DrivenBySeleniumWithHeadlessChrome < ActionDispatch::SystemTestCase
|
|
driven_by :selenium, using: :headless_chrome
|
|
end
|
|
|
|
class DrivenBySeleniumWithHeadlessFirefox < ActionDispatch::SystemTestCase
|
|
driven_by :selenium, using: :headless_firefox
|
|
end
|