Move MiniTest::Unit to under Test::Unit::Runner

This commit is contained in:
Hiroshi SHIBATA 2021-09-08 10:00:41 +09:00
Родитель 78ec066347
Коммит c18e953937
2 изменённых файлов: 396 добавлений и 398 удалений

Просмотреть файл

@ -69,401 +69,4 @@ module MiniTest
backtrace_filter.filter bt
end
class Unit # :nodoc:
VERSION = "4.7.5" # :nodoc:
attr_accessor :report, :failures, :errors, :skips # :nodoc:
attr_accessor :assertion_count # :nodoc:
attr_writer :test_count # :nodoc:
attr_accessor :start_time # :nodoc:
attr_accessor :help # :nodoc:
attr_accessor :verbose # :nodoc:
attr_writer :options # :nodoc:
##
# :attr:
#
# if true, installs an "INFO" signal handler (only available to BSD and
# OS X users) which prints diagnostic information about the test run.
#
# This is auto-detected by default but may be overridden by custom
# runners.
attr_accessor :info_signal
##
# Lazy accessor for options.
def options
@options ||= {seed: 42}
end
@@installed_at_exit ||= false
@@out = $stdout
@@after_tests = []
@@current_repeat_count = 0
##
# A simple hook allowing you to run a block of code after _all_ of
# the tests are done. Eg:
#
# MiniTest::Unit.after_tests { p $debugging_info }
def self.after_tests &block
@@after_tests << block
end
##
# Registers MiniTest::Unit to run tests at process exit
def self.autorun
at_exit {
# don't run if there was a non-exit exception
next if $! and not $!.kind_of? SystemExit
# the order here is important. The at_exit handler must be
# installed before anyone else gets a chance to install their
# own, that way we can be assured that our exit will be last
# to run (at_exit stacks).
exit_code = nil
at_exit {
@@after_tests.reverse_each(&:call)
exit false if exit_code && exit_code != 0
}
exit_code = MiniTest::Unit.new.run ARGV
} unless @@installed_at_exit
@@installed_at_exit = true
end
##
# Returns the stream to use for output.
def self.output
@@out
end
##
# Sets MiniTest::Unit to write output to +stream+. $stdout is the default
# output
def self.output= stream
@@out = stream
end
##
# Tells MiniTest::Unit to delegate to +runner+, an instance of a
# MiniTest::Unit subclass, when MiniTest::Unit#run is called.
def self.runner= runner
@@runner = runner
end
##
# Returns the MiniTest::Unit subclass instance that will be used
# to run the tests. A MiniTest::Unit instance is the default
# runner.
def self.runner
@@runner ||= self.new
end
##
# Return all plugins' run methods (methods that start with "run_").
def self.plugins
@@plugins ||= (["run_tests"] +
public_instance_methods(false).
grep(/^run_/).map { |s| s.to_s }).uniq
end
##
# Return the IO for output.
def output
self.class.output
end
def puts *a # :nodoc:
output.puts(*a)
end
def print *a # :nodoc:
output.print(*a)
end
def test_count # :nodoc:
@test_count ||= 0
end
##
# Runner for a given +type+ (eg, test vs bench).
def self.current_repeat_count
@@current_repeat_count
end
def _run_anything type
suites = Test::Unit::TestCase.send "#{type}_suites"
return if suites.empty?
puts
puts "# Running #{type}s:"
puts
@test_count, @assertion_count = 0, 0
test_count = assertion_count = 0
sync = output.respond_to? :"sync=" # stupid emacs
old_sync, output.sync = output.sync, true if sync
@@current_repeat_count = 0
begin
start = Time.now
results = _run_suites suites, type
@test_count = results.inject(0) { |sum, (tc, _)| sum + tc }
@assertion_count = results.inject(0) { |sum, (_, ac)| sum + ac }
test_count += @test_count
assertion_count += @assertion_count
t = Time.now - start
@@current_repeat_count += 1
unless @repeat_count
puts
puts
end
puts "Finished%s %ss in %.6fs, %.4f tests/s, %.4f assertions/s.\n" %
[(@repeat_count ? "(#{@@current_repeat_count}/#{@repeat_count}) " : ""), type,
t, @test_count.fdiv(t), @assertion_count.fdiv(t)]
end while @repeat_count && @@current_repeat_count < @repeat_count &&
report.empty? && failures.zero? && errors.zero?
output.sync = old_sync if sync
report.each_with_index do |msg, i|
puts "\n%3d) %s" % [i + 1, msg]
end
puts
@test_count = test_count
@assertion_count = assertion_count
status
end
##
# Runs all the +suites+ for a given +type+.
#
def _run_suites suites, type
suites.map { |suite| _run_suite suite, type }
end
##
# Run a single +suite+ for a given +type+.
def _run_suite suite, type
header = "#{type}_suite_header"
puts send(header, suite) if respond_to? header
filter = options[:filter] || '/./'
filter = Regexp.new $1 if filter =~ /\/(.*)\//
all_test_methods = suite.send "#{type}_methods"
filtered_test_methods = all_test_methods.find_all { |m|
filter === m || filter === "#{suite}##{m}"
}
leakchecker = LeakChecker.new
if ENV["LEAK_CHECKER_TRACE_OBJECT_ALLOCATION"]
require "objspace"
trace = true
end
assertions = filtered_test_methods.map { |method|
inst = suite.new method
inst._assertions = 0
print "#{suite}##{method} = " if @verbose
start_time = Time.now if @verbose
result =
if trace
ObjectSpace.trace_object_allocations {inst.run self}
else
inst.run self
end
print "%.2f s = " % (Time.now - start_time) if @verbose
print result
puts if @verbose
$stdout.flush
unless defined?(RubyVM::JIT) && RubyVM::JIT.enabled? # compiler process is wrongly considered as leak
leakchecker.check("#{inst.class}\##{inst.__name__}")
end
inst._assertions
}
return assertions.size, assertions.inject(0) { |sum, n| sum + n }
end
##
# Record the result of a single test. Makes it very easy to gather
# information. Eg:
#
# class StatisticsRecorder < MiniTest::Unit
# def record suite, method, assertions, time, error
# # ... record the results somewhere ...
# end
# end
#
# MiniTest::Unit.runner = StatisticsRecorder.new
#
# NOTE: record might be sent more than once per test. It will be
# sent once with the results from the test itself. If there is a
# failure or error in teardown, it will be sent again with the
# error or failure.
def record suite, method, assertions, time, error
end
def location e # :nodoc:
last_before_assertion = ""
return '<empty>' unless e.backtrace # SystemStackError can return nil.
e.backtrace.reverse_each do |s|
break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
last_before_assertion = s
end
last_before_assertion.sub(/:in .*$/, '')
end
##
# Writes status for failed test +meth+ in +klass+ which finished with
# exception +e+
def puke klass, meth, e
e = case e
when MiniTest::Skip then
@skips += 1
return "S" unless @verbose
"Skipped:\n#{klass}##{meth} [#{location e}]:\n#{e.message}\n"
when MiniTest::Assertion then
@failures += 1
"Failure:\n#{klass}##{meth} [#{location e}]:\n#{e.message}\n"
else
@errors += 1
bt = MiniTest::filter_backtrace(e.backtrace).join "\n "
"Error:\n#{klass}##{meth}:\n#{e.class}: #{e.message.b}\n #{bt}\n"
end
@report << e
e[0, 1]
end
def initialize # :nodoc:
@report = []
@errors = @failures = @skips = 0
@verbose = false
@mutex = Thread::Mutex.new
@info_signal = Signal.list['INFO']
@repeat_count = nil
end
def synchronize # :nodoc:
if @mutex then
@mutex.synchronize { yield }
else
yield
end
end
def process_args args = [] # :nodoc:
options = {}
orig_args = args.dup
OptionParser.new do |opts|
opts.banner = 'minitest options:'
opts.version = MiniTest::Unit::VERSION
opts.on '-h', '--help', 'Display this help.' do
puts opts
exit
end
opts.on '-s', '--seed SEED', Integer, "Sets random seed" do |m|
options[:seed] = m.to_i
end
opts.on '-v', '--verbose', "Verbose. Show progress processing files." do
options[:verbose] = true
end
opts.on '-n', '--name PATTERN', "Filter test names on pattern (e.g. /foo/)" do |a|
options[:filter] = a
end
opts.parse! args
orig_args -= args
end
unless options[:seed] then
srand
options[:seed] = srand % 0xFFFF
orig_args << "--seed" << options[:seed].to_s
end
srand options[:seed]
self.verbose = options[:verbose]
@help = orig_args.map { |s| s =~ /[\s|&<>$()]/ ? s.inspect : s }.join " "
options
end
##
# Begins the full test run. Delegates to +runner+'s #_run method.
def run args = []
self.class.runner._run(args)
end
##
# Top level driver, controls all output and filtering.
def _run args = []
args = process_args args # ARGH!! blame test/unit process_args
self.options.merge! args
puts "Run options: #{help}"
self.class.plugins.each do |plugin|
send plugin
break unless report.empty?
end
return failures + errors if self.test_count > 0 # or return nil...
rescue Interrupt
abort 'Interrupted'
end
##
# Runs test suites matching +filter+.
def run_tests
_run_anything :test
end
##
# Writes status to +io+
def status io = self.output
format = "%d tests, %d assertions, %d failures, %d errors, %d skips"
io.puts format % [test_count, assertion_count, failures, errors, skips]
end
end # class Unit
end # module MiniTest

Просмотреть файл

@ -17,6 +17,7 @@ module Test
# Test::Unit has been left in the standard library to support legacy test
# suites.
module Unit
VERSION = "4.7.5" # :nodoc:
TEST_UNIT_IMPLEMENTATION = 'test/unit compatibility layer using minitest' # :nodoc:
module RunCount # :nodoc: all
@ -1158,7 +1159,401 @@ module Test
end
end
class Runner < MiniTest::Unit # :nodoc: all
class Runner # :nodoc: all
attr_accessor :report, :failures, :errors, :skips # :nodoc:
attr_accessor :assertion_count # :nodoc:
attr_writer :test_count # :nodoc:
attr_accessor :start_time # :nodoc:
attr_accessor :help # :nodoc:
attr_accessor :verbose # :nodoc:
attr_writer :options # :nodoc:
##
# :attr:
#
# if true, installs an "INFO" signal handler (only available to BSD and
# OS X users) which prints diagnostic information about the test run.
#
# This is auto-detected by default but may be overridden by custom
# runners.
attr_accessor :info_signal
##
# Lazy accessor for options.
def options
@options ||= {seed: 42}
end
@@installed_at_exit ||= false
@@out = $stdout
@@after_tests = []
@@current_repeat_count = 0
##
# A simple hook allowing you to run a block of code after _all_ of
# the tests are done. Eg:
#
# MiniTest::Unit.after_tests { p $debugging_info }
def self.after_tests &block
@@after_tests << block
end
##
# Registers MiniTest::Unit to run tests at process exit
def self.autorun
at_exit {
# don't run if there was a non-exit exception
next if $! and not $!.kind_of? SystemExit
# the order here is important. The at_exit handler must be
# installed before anyone else gets a chance to install their
# own, that way we can be assured that our exit will be last
# to run (at_exit stacks).
exit_code = nil
at_exit {
@@after_tests.reverse_each(&:call)
exit false if exit_code && exit_code != 0
}
exit_code = MiniTest::Unit.new.run ARGV
} unless @@installed_at_exit
@@installed_at_exit = true
end
##
# Returns the stream to use for output.
def self.output
@@out
end
##
# Sets MiniTest::Unit to write output to +stream+. $stdout is the default
# output
def self.output= stream
@@out = stream
end
##
# Tells MiniTest::Unit to delegate to +runner+, an instance of a
# MiniTest::Unit subclass, when MiniTest::Unit#run is called.
def self.runner= runner
@@runner = runner
end
##
# Returns the MiniTest::Unit subclass instance that will be used
# to run the tests. A MiniTest::Unit instance is the default
# runner.
def self.runner
@@runner ||= self.new
end
##
# Return all plugins' run methods (methods that start with "run_").
def self.plugins
@@plugins ||= (["run_tests"] +
public_instance_methods(false).
grep(/^run_/).map { |s| s.to_s }).uniq
end
##
# Return the IO for output.
def output
self.class.output
end
def puts *a # :nodoc:
output.puts(*a)
end
def print *a # :nodoc:
output.print(*a)
end
def test_count # :nodoc:
@test_count ||= 0
end
##
# Runner for a given +type+ (eg, test vs bench).
def self.current_repeat_count
@@current_repeat_count
end
def _run_anything type
suites = Test::Unit::TestCase.send "#{type}_suites"
return if suites.empty?
puts
puts "# Running #{type}s:"
puts
@test_count, @assertion_count = 0, 0
test_count = assertion_count = 0
sync = output.respond_to? :"sync=" # stupid emacs
old_sync, output.sync = output.sync, true if sync
@@current_repeat_count = 0
begin
start = Time.now
results = _run_suites suites, type
@test_count = results.inject(0) { |sum, (tc, _)| sum + tc }
@assertion_count = results.inject(0) { |sum, (_, ac)| sum + ac }
test_count += @test_count
assertion_count += @assertion_count
t = Time.now - start
@@current_repeat_count += 1
unless @repeat_count
puts
puts
end
puts "Finished%s %ss in %.6fs, %.4f tests/s, %.4f assertions/s.\n" %
[(@repeat_count ? "(#{@@current_repeat_count}/#{@repeat_count}) " : ""), type,
t, @test_count.fdiv(t), @assertion_count.fdiv(t)]
end while @repeat_count && @@current_repeat_count < @repeat_count &&
report.empty? && failures.zero? && errors.zero?
output.sync = old_sync if sync
report.each_with_index do |msg, i|
puts "\n%3d) %s" % [i + 1, msg]
end
puts
@test_count = test_count
@assertion_count = assertion_count
status
end
##
# Runs all the +suites+ for a given +type+.
#
def _run_suites suites, type
suites.map { |suite| _run_suite suite, type }
end
##
# Run a single +suite+ for a given +type+.
def _run_suite suite, type
header = "#{type}_suite_header"
puts send(header, suite) if respond_to? header
filter = options[:filter] || '/./'
filter = Regexp.new $1 if filter =~ /\/(.*)\//
all_test_methods = suite.send "#{type}_methods"
filtered_test_methods = all_test_methods.find_all { |m|
filter === m || filter === "#{suite}##{m}"
}
leakchecker = LeakChecker.new
if ENV["LEAK_CHECKER_TRACE_OBJECT_ALLOCATION"]
require "objspace"
trace = true
end
assertions = filtered_test_methods.map { |method|
inst = suite.new method
inst._assertions = 0
print "#{suite}##{method} = " if @verbose
start_time = Time.now if @verbose
result =
if trace
ObjectSpace.trace_object_allocations {inst.run self}
else
inst.run self
end
print "%.2f s = " % (Time.now - start_time) if @verbose
print result
puts if @verbose
$stdout.flush
unless defined?(RubyVM::JIT) && RubyVM::JIT.enabled? # compiler process is wrongly considered as leak
leakchecker.check("#{inst.class}\##{inst.__name__}")
end
inst._assertions
}
return assertions.size, assertions.inject(0) { |sum, n| sum + n }
end
##
# Record the result of a single test. Makes it very easy to gather
# information. Eg:
#
# class StatisticsRecorder < MiniTest::Unit
# def record suite, method, assertions, time, error
# # ... record the results somewhere ...
# end
# end
#
# MiniTest::Unit.runner = StatisticsRecorder.new
#
# NOTE: record might be sent more than once per test. It will be
# sent once with the results from the test itself. If there is a
# failure or error in teardown, it will be sent again with the
# error or failure.
def record suite, method, assertions, time, error
end
def location e # :nodoc:
last_before_assertion = ""
return '<empty>' unless e.backtrace # SystemStackError can return nil.
e.backtrace.reverse_each do |s|
break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
last_before_assertion = s
end
last_before_assertion.sub(/:in .*$/, '')
end
##
# Writes status for failed test +meth+ in +klass+ which finished with
# exception +e+
def puke klass, meth, e
e = case e
when MiniTest::Skip then
@skips += 1
return "S" unless @verbose
"Skipped:\n#{klass}##{meth} [#{location e}]:\n#{e.message}\n"
when MiniTest::Assertion then
@failures += 1
"Failure:\n#{klass}##{meth} [#{location e}]:\n#{e.message}\n"
else
@errors += 1
bt = MiniTest::filter_backtrace(e.backtrace).join "\n "
"Error:\n#{klass}##{meth}:\n#{e.class}: #{e.message.b}\n #{bt}\n"
end
@report << e
e[0, 1]
end
def initialize # :nodoc:
@report = []
@errors = @failures = @skips = 0
@verbose = false
@mutex = Thread::Mutex.new
@info_signal = Signal.list['INFO']
@repeat_count = nil
end
def synchronize # :nodoc:
if @mutex then
@mutex.synchronize { yield }
else
yield
end
end
def process_args args = [] # :nodoc:
options = {}
orig_args = args.dup
OptionParser.new do |opts|
opts.banner = 'minitest options:'
opts.version = MiniTest::Unit::VERSION
opts.on '-h', '--help', 'Display this help.' do
puts opts
exit
end
opts.on '-s', '--seed SEED', Integer, "Sets random seed" do |m|
options[:seed] = m.to_i
end
opts.on '-v', '--verbose', "Verbose. Show progress processing files." do
options[:verbose] = true
end
opts.on '-n', '--name PATTERN', "Filter test names on pattern (e.g. /foo/)" do |a|
options[:filter] = a
end
opts.parse! args
orig_args -= args
end
unless options[:seed] then
srand
options[:seed] = srand % 0xFFFF
orig_args << "--seed" << options[:seed].to_s
end
srand options[:seed]
self.verbose = options[:verbose]
@help = orig_args.map { |s| s =~ /[\s|&<>$()]/ ? s.inspect : s }.join " "
options
end
##
# Begins the full test run. Delegates to +runner+'s #_run method.
def run args = []
self.class.runner._run(args)
end
##
# Top level driver, controls all output and filtering.
def _run args = []
args = process_args args # ARGH!! blame test/unit process_args
self.options.merge! args
puts "Run options: #{help}"
self.class.plugins.each do |plugin|
send plugin
break unless report.empty?
end
return failures + errors if self.test_count > 0 # or return nil...
rescue Interrupt
abort 'Interrupted'
end
##
# Runs test suites matching +filter+.
def run_tests
_run_anything :test
end
##
# Writes status to +io+
def status io = self.output
format = "%d tests, %d assertions, %d failures, %d errors, %d skips"
io.puts format % [test_count, assertion_count, failures, errors, skips]
end
include Test::Unit::Options
include Test::Unit::StatusLine
include Test::Unit::Parallel