2017-07-25 15:01:34 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-06-25 13:08:26 -04:00
|
|
|
require "shellwords"
|
|
|
|
require "method_source"
|
|
|
|
require "rake/file_list"
|
|
|
|
require "active_support/core_ext/module/attribute_accessors"
|
|
|
|
|
|
|
|
module Rails
|
|
|
|
module TestUnit
|
|
|
|
class Runner
|
|
|
|
mattr_reader :filters, default: []
|
|
|
|
|
|
|
|
class << self
|
Show minitest options in test runner help
Since #29572, minitest options are available but are no longer showed
in help.
This fixed to show minitest option in help as with Rails 5.1.2.
**before**
```
./bin/rails t --help
You can run a single test by appending a line number to a filename:
bin/rails test test/models/user_test.rb:27
You can run multiple files and directories at the same time:
bin/rails test test/controllers test/integration/login_test.rb
By default test failures and errors are reported inline during a run.
Rails options:
-w, --warnings Run with Ruby warnings enabled
-e, --environment Run tests in the ENV environment
-b, --backtrace Show the complete backtrace
-d, --defer-output Output test failures and errors after the test run
-f, --fail-fast Abort test run on first failure or error
-c, --[no-]color Enable color in the output
```
**after**
```
./bin/rails t --help
You can run a single test by appending a line number to a filename:
bin/rails test test/models/user_test.rb:27
You can run multiple files and directories at the same time:
bin/rails test test/controllers test/integration/login_test.rb
By default test failures and errors are reported inline during a run.
minitest options:
-h, --help Display this help.
-s, --seed SEED Sets random seed. Also via env. Eg: SEED=n rake
-v, --verbose Verbose. Show progress processing files.
-n, --name PATTERN Filter run on /regexp/ or string.
--exclude PATTERN Exclude /regexp/ or string from run.
Known extensions: rails, pride
-w, --warnings Run with Ruby warnings enabled
-e, --environment Run tests in the ENV environment
-b, --backtrace Show the complete backtrace
-d, --defer-output Output test failures and errors after the test run
-f, --fail-fast Abort test run on first failure or error
-c, --[no-]color Enable color in the output
-p, --pride Pride. Show your testing pride!
```
2017-07-29 19:21:22 -04:00
|
|
|
def attach_before_load_options(opts)
|
2018-09-25 13:18:20 -04:00
|
|
|
opts.on("--warnings", "-w", "Run with Ruby warnings enabled") { }
|
|
|
|
opts.on("-e", "--environment ENV", "Run tests in the ENV environment") { }
|
2017-06-25 13:08:26 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def parse_options(argv)
|
|
|
|
# Perform manual parsing and cleanup since option parser raises on unknown options.
|
|
|
|
env_index = argv.index("--environment") || argv.index("-e")
|
|
|
|
if env_index
|
|
|
|
argv.delete_at(env_index)
|
|
|
|
environment = argv.delete_at(env_index).strip
|
|
|
|
end
|
|
|
|
ENV["RAILS_ENV"] = environment || "test"
|
|
|
|
|
|
|
|
w_index = argv.index("--warnings") || argv.index("-w")
|
|
|
|
$VERBOSE = argv.delete_at(w_index) if w_index
|
|
|
|
end
|
|
|
|
|
|
|
|
def rake_run(argv = [])
|
|
|
|
ARGV.replace Shellwords.split(ENV["TESTOPTS"] || "")
|
|
|
|
|
|
|
|
run(argv)
|
|
|
|
end
|
|
|
|
|
|
|
|
def run(argv = [])
|
|
|
|
load_tests(argv)
|
|
|
|
|
|
|
|
require "active_support/testing/autorun"
|
|
|
|
end
|
|
|
|
|
|
|
|
def load_tests(argv)
|
|
|
|
patterns = extract_filters(argv)
|
|
|
|
|
|
|
|
tests = Rake::FileList[patterns.any? ? patterns : "test/**/*_test.rb"]
|
2019-05-13 12:44:06 -04:00
|
|
|
tests.exclude("test/system/**/*", "test/dummy/**/*") if patterns.empty?
|
2017-06-25 13:08:26 -04:00
|
|
|
|
|
|
|
tests.to_a.each { |path| require File.expand_path(path) }
|
|
|
|
end
|
|
|
|
|
|
|
|
def compose_filter(runnable, filter)
|
|
|
|
if filters.any? { |_, lines| lines.any? }
|
|
|
|
CompositeFilter.new(runnable, filter, filters)
|
|
|
|
else
|
|
|
|
filter
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
def extract_filters(argv)
|
2017-07-25 15:01:34 -04:00
|
|
|
# Extract absolute and relative paths but skip -n /.*/ regexp filters.
|
2020-03-24 20:54:12 -04:00
|
|
|
argv.select { |arg| path_argument?(arg) && !regexp_filter?(arg) }.map do |path|
|
2017-06-25 13:08:26 -04:00
|
|
|
case
|
2018-07-28 17:37:17 -04:00
|
|
|
when /(:\d+)+$/.match?(path)
|
2017-06-25 13:08:26 -04:00
|
|
|
file, *lines = path.split(":")
|
|
|
|
filters << [ file, lines ]
|
|
|
|
file
|
|
|
|
when Dir.exist?(path)
|
|
|
|
"#{path}/**/*_test.rb"
|
|
|
|
else
|
|
|
|
filters << [ path, [] ]
|
|
|
|
path
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2020-03-24 20:54:12 -04:00
|
|
|
|
|
|
|
def regexp_filter?(arg)
|
|
|
|
arg.start_with?("/") && arg.end_with?("/")
|
|
|
|
end
|
|
|
|
|
|
|
|
def path_argument?(arg)
|
|
|
|
%r%^/?\w+/%.match?(arg)
|
|
|
|
end
|
2017-06-25 13:08:26 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class CompositeFilter # :nodoc:
|
|
|
|
attr_reader :named_filter
|
|
|
|
|
|
|
|
def initialize(runnable, filter, patterns)
|
|
|
|
@runnable = runnable
|
|
|
|
@named_filter = derive_named_filter(filter)
|
|
|
|
@filters = [ @named_filter, *derive_line_filters(patterns) ].compact
|
|
|
|
end
|
|
|
|
|
2018-09-11 17:52:27 -04:00
|
|
|
# minitest uses === to find matching filters.
|
2017-06-25 13:08:26 -04:00
|
|
|
def ===(method)
|
|
|
|
@filters.any? { |filter| filter === method }
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
def derive_named_filter(filter)
|
|
|
|
if filter.respond_to?(:named_filter)
|
|
|
|
filter.named_filter
|
2018-09-11 17:52:27 -04:00
|
|
|
elsif filter =~ %r%/(.*)/% # Regexp filtering copied from minitest.
|
2017-06-25 13:08:26 -04:00
|
|
|
Regexp.new $1
|
|
|
|
elsif filter.is_a?(String)
|
|
|
|
filter
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def derive_line_filters(patterns)
|
|
|
|
patterns.flat_map do |file, lines|
|
|
|
|
if lines.empty?
|
|
|
|
Filter.new(@runnable, file, nil) if file
|
|
|
|
else
|
|
|
|
lines.map { |line| Filter.new(@runnable, file, line) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Filter # :nodoc:
|
|
|
|
def initialize(runnable, file, line)
|
|
|
|
@runnable, @file = runnable, File.expand_path(file)
|
|
|
|
@line = line.to_i if line
|
|
|
|
end
|
|
|
|
|
|
|
|
def ===(method)
|
|
|
|
return unless @runnable.method_defined?(method)
|
|
|
|
|
|
|
|
if @line
|
|
|
|
test_file, test_range = definition_for(@runnable.instance_method(method))
|
|
|
|
test_file == @file && test_range.include?(@line)
|
|
|
|
else
|
|
|
|
@runnable.instance_method(method).source_location.first == @file
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
def definition_for(method)
|
|
|
|
file, start_line = method.source_location
|
|
|
|
end_line = method.source.count("\n") + start_line - 1
|
|
|
|
|
|
|
|
return file, start_line..end_line
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|