1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/tool/lib/test/unit/parallel.rb
Koichi Sasada e79f1941b2 tune parallel test
This patch contains the fowllowing hacks:

(1) Add "--timetable-data=FILE" option for test-all
    This option enables to dump timeline event
    contains worker, suite, and start/end time.
(2) remove TestJIT in test_jit_debug.rb on parallel test.
    it is duplicated test.
(3) move test_jit.rb and test_jit_debug.rb at first
    because these two tests are bottleneck of parallel tests.

On my environment, `make test-all TESTS=-j12` reduced the total time
190 seconds -> 140 seconds.
2020-12-01 09:39:09 +09:00

209 lines
5.5 KiB
Ruby

# frozen_string_literal: true
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../.."
require 'test/unit'
require "profile_test_all" if ENV.key?('RUBY_TEST_ALL_PROFILE')
require "tracepointchecker"
require "zombie_hunter"
require "iseq_loader_checker"
require "gc_compact_checker"
module Test
module Unit
class Worker < Runner # :nodoc:
class << self
undef autorun
end
alias orig_run_suite mini_run_suite
undef _run_suite
undef _run_suites
undef run
def increment_io(orig) # :nodoc:
*rest, io = 32.times.inject([orig.dup]){|ios, | ios << ios.last.dup }
rest.each(&:close)
io
end
def _run_suites(suites, type) # :nodoc:
suites.map do |suite|
_run_suite(suite, type)
end
end
def _run_suite(suite, type) # :nodoc:
@partial_report = []
orig_testout = MiniTest::Unit.output
i,o = IO.pipe
MiniTest::Unit.output = o
orig_stdin, orig_stdout = $stdin, $stdout
th = Thread.new do
begin
while buf = (self.verbose ? i.gets : i.readpartial(1024))
_report "p", buf or break
end
rescue IOError
end
end
e, f, s = @errors, @failures, @skips
begin
result = orig_run_suite(suite, type)
rescue Interrupt
@need_exit = true
result = [nil,nil]
end
MiniTest::Unit.output = orig_testout
$stdin = orig_stdin
$stdout = orig_stdout
o.close
begin
th.join
rescue IOError
raise unless /stream closed|closed stream/ =~ $!.message
end
i.close
result << @partial_report
@partial_report = nil
result << [@errors-e,@failures-f,@skips-s]
result << ($: - @old_loadpath)
result << suite.name
_report "done", Marshal.dump(result)
return result
ensure
MiniTest::Unit.output = orig_stdout
$stdin = orig_stdin if orig_stdin
$stdout = orig_stdout if orig_stdout
o.close if o && !o.closed?
i.close if i && !i.closed?
end
def run(args = []) # :nodoc:
process_args args
@@stop_auto_run = true
@opts = @options.dup
@need_exit = false
@old_loadpath = []
begin
begin
@stdout = increment_io(STDOUT)
@stdin = increment_io(STDIN)
rescue
exit 2
end
exit 2 unless @stdout && @stdin
@stdout.sync = true
_report "ready!"
while buf = @stdin.gets
case buf.chomp
when /^loadpath (.+?)$/
@old_loadpath = $:.dup
$:.push(*Marshal.load($1.unpack("m")[0].force_encoding("ASCII-8BIT"))).uniq!
when /^run (.+?) (.+?)$/
_report "okay"
@options = @opts.dup
suites = MiniTest::Unit::TestCase.test_suites
begin
require File.realpath($1)
rescue LoadError
_report "after", Marshal.dump([$1, ProxyError.new($!)])
_report "ready"
next
end
_run_suites MiniTest::Unit::TestCase.test_suites-suites, $2.to_sym
if @need_exit
_report "bye"
exit
else
_report "ready"
end
when /^quit$/
_report "bye"
exit
end
end
rescue Exception => e
trace = e.backtrace || ['unknown method']
err = ["#{trace.shift}: #{e.message} (#{e.class})"] + trace.map{|t| "\t" + t }
if @stdout
_report "bye", Marshal.dump(err.join("\n"))
else
raise "failed to report a failure due to lack of @stdout"
end
exit
ensure
@stdin.close if @stdin
@stdout.close if @stdout
end
end
def _report(res, *args) # :nodoc:
@stdout.write(args.empty? ? "#{res}\n" : "#{res} #{args.pack("m0")}\n")
true
rescue Errno::EPIPE
rescue TypeError => e
abort("#{e.inspect} in _report(#{res.inspect}, #{args.inspect})\n#{e.backtrace.join("\n")}")
end
def puke(klass, meth, e) # :nodoc:
if e.is_a?(MiniTest::Skip)
new_e = MiniTest::Skip.new(e.message)
new_e.set_backtrace(e.backtrace)
e = new_e
end
@partial_report << [klass.name, meth, e.is_a?(MiniTest::Assertion) ? e : ProxyError.new(e)]
super
end
def record(suite, method, assertions, time, error) # :nodoc:
case error
when nil
when MiniTest::Assertion, MiniTest::Skip
case error.cause
when nil, MiniTest::Assertion, MiniTest::Skip
else
bt = error.backtrace
error = error.class.new(error.message)
error.set_backtrace(bt)
end
else
error = ProxyError.new(error)
end
_report "record", Marshal.dump([suite.name, method, assertions, time, error])
super
end
end
end
end
if $0 == __FILE__
module Test
module Unit
class TestCase < MiniTest::Unit::TestCase # :nodoc: all
undef on_parallel_worker?
def on_parallel_worker?
true
end
def self.on_parallel_worker?
true
end
end
end
end
require 'rubygems'
Test::Unit::Worker.new.run(ARGV)
end