require "optparse"
require "rake/file_list"
require "method_source"
module Rails
class TestRunner
class Options
def self.parse(args)
options = { backtrace: !ENV["BACKTRACE"].nil?, name: nil, environment: "test" }
opt_parser = ::OptionParser.new do |opts|
opts.banner = "Usage: bin/rails test [options] [file or directory]"
opts.separator ""
opts.on("-e", "--environment [ENV]",
"Run tests in the ENV environment") do |env|
options[:environment] = env.strip
end
opts.separator ""
opts.separator "Filter options:"
opts.separator ""
opts.separator <<-DESC
You can run a single test by appending the line number to filename:
bin/rails test test/models/user_test.rb:27
DESC
opts.on("-n", "--name [NAME]",
"Only run tests matching NAME") do |name|
options[:name] = name
end
opts.on("-p", "--pattern [PATTERN]",
"Only run tests matching PATTERN") do |pattern|
options[:name] = "/#{pattern}/"
end
opts.separator ""
opts.separator "Output options:"
opts.on("-b", "--backtrace",
"Show the complete backtrace") do
options[:backtrace] = true
end
opts.separator ""
opts.separator "Common options:"
opts.on_tail("-h", "--help", "Show this message") do
puts opts
exit
end
end
opt_parser.order!(args)
options[:patterns] = []
while arg = args.shift
if (file_and_line = arg.split(':')).size > 1
options[:filename], options[:line] = file_and_line
options[:filename] = File.expand_path options[:filename]
options[:line] &&= options[:line].to_i
else
arg = arg.gsub(':', '')
if Dir.exist?("#{arg}")
options[:patterns] << File.expand_path("#{arg}/**/*_test.rb")
elsif File.file?(arg)
options[:patterns] << File.expand_path(arg)
end
end
end
options
end
end
def initialize(options = {})
@options = options
end
def self.run(arguments)
options = Rails::TestRunner::Options.parse(arguments)
Rails::TestRunner.new(options).run
end
def run
$rails_test_runner = self
ENV["RAILS_ENV"] = @options[:environment]
run_tests
end
def find_method
return @options[:name] if @options[:name]
return unless @options[:line]
method = test_methods.find do |location, test_method, start_line, end_line|
location == @options[:filename] &&
(start_line..end_line).include?(@options[:line].to_i)
end
method[1] if method
end
def show_backtrace?
@options[:backtrace]
end
def test_files
return [@options[:filename]] if @options[:filename]
if @options[:patterns] && @options[:patterns].count > 0
pattern = @options[:patterns]
else
pattern = "test/**/*_test.rb"
end
Rake::FileList[pattern]
end
private
def run_tests
test_files.to_a.each do |file|
require File.expand_path file
end
end
def test_methods
methods_map = []
suites = Minitest::Runnable.runnables.shuffle
suites.each do |suite_class|
suite_class.runnable_methods.each do |test_method|
method = suite_class.instance_method(test_method)
location = method.source_location
start_line = location.last
end_line = method.source.split("\n").size + start_line - 1
methods_map << [File.expand_path(location.first), test_method, start_line, end_line]
end
end
methods_map
end
end
end