* lib/test/unit.rb: Refactoring; Worker never use Hash for internal storage.
* lib/test/unit.rb: Never use Kernel#spawn. Use IO.popen instead. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@30968 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
6725dff1a1
commit
c6598ffcae
@ -1,3 +1,10 @@
|
|||||||
|
Sun Feb 27 18:00:09 2011 Shota Fukumori <sorah@tubusu.net>
|
||||||
|
|
||||||
|
* lib/test/unit.rb: Refactoring; Worker never use Hash for internal
|
||||||
|
storage.
|
||||||
|
|
||||||
|
* lib/test/unit.rb: Never use Kernel#spawn. Use IO.popen instead.
|
||||||
|
|
||||||
Sun Feb 27 13:16:48 2011 Tanaka Akira <akr@fsij.org>
|
Sun Feb 27 13:16:48 2011 Tanaka Akira <akr@fsij.org>
|
||||||
|
|
||||||
* ext/openssl/ossl_ns_spki.c: parenthesize macro arguments.
|
* ext/openssl/ossl_ns_spki.c: parenthesize macro arguments.
|
||||||
|
105
lib/test/unit.rb
105
lib/test/unit.rb
@ -4,6 +4,7 @@ require 'minitest/unit'
|
|||||||
require 'test/unit/assertions'
|
require 'test/unit/assertions'
|
||||||
require 'test/unit/testcase'
|
require 'test/unit/testcase'
|
||||||
require 'optparse'
|
require 'optparse'
|
||||||
|
require 'io/console'
|
||||||
|
|
||||||
module Test
|
module Test
|
||||||
module Unit
|
module Unit
|
||||||
@ -83,8 +84,7 @@ module Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
opts.on '--jobs-status [TYPE]', "Show status of jobs every file; Disabled when --jobs isn't specified." do |type|
|
opts.on '--jobs-status [TYPE]', "Show status of jobs every file; Disabled when --jobs isn't specified." do |type|
|
||||||
options[:job_status] = true
|
options[:job_status] = (type && type.to_sym) || :normal
|
||||||
options[:job_status_type] = type.to_sym if type
|
|
||||||
end
|
end
|
||||||
|
|
||||||
opts.on '-j N', '--jobs N', "Allow run tests with N jobs at once" do |a|
|
opts.on '-j N', '--jobs N', "Allow run tests with N jobs at once" do |a|
|
||||||
@ -231,30 +231,35 @@ module Test
|
|||||||
|
|
||||||
class Worker
|
class Worker
|
||||||
def self.launch(ruby,args=[])
|
def self.launch(ruby,args=[])
|
||||||
i,o = IO.pipe("ASCII-8BIT") # worker o>|i> master
|
io = IO.popen([*ruby,
|
||||||
j,k = IO.pipe("ASCII-8BIT") # worker <j|<k master
|
"#{File.dirname(__FILE__)}/unit/parallel.rb",
|
||||||
k.sync = true
|
*args], "r+")
|
||||||
pid = spawn(*ruby,
|
io.sync = true
|
||||||
"#{File.dirname(__FILE__)}/unit/parallel.rb",
|
new(io: io, pid: io.pid, status: :waiting)
|
||||||
*args, out: o, in: j)
|
|
||||||
[o,j].each(&:close)
|
|
||||||
new(in: k, out: i, pid: pid, status: :waiting)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(h={})
|
def initialize(h={})
|
||||||
@worker = h
|
@io = h[:io]
|
||||||
|
@pid = h[:pid]
|
||||||
|
@status = h[:status]
|
||||||
|
@file = nil
|
||||||
|
@real_file = nil
|
||||||
|
@loadpath = []
|
||||||
@hooks = {}
|
@hooks = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def puts(*args)
|
||||||
|
@io.puts(*args)
|
||||||
|
end
|
||||||
|
|
||||||
def run(task,type)
|
def run(task,type)
|
||||||
@worker[:file] = File.basename(task).gsub(/\.rb/,"")
|
@file = File.basename(task).gsub(/\.rb/,"")
|
||||||
@worker[:real_file] = task
|
@real_file = task
|
||||||
begin
|
begin
|
||||||
@worker[:loadpath] ||= []
|
puts "loadpath #{[Marshal.dump($:-@loadpath)].pack("m").gsub("\n","")}"
|
||||||
@worker[:in].puts "loadpath #{[Marshal.dump($:-@worker[:loadpath])].pack("m").gsub("\n","")}"
|
@loadpath = $:.dup
|
||||||
@worker[:loadpath] = $:.dup
|
puts "run #{task} #{type}"
|
||||||
@worker[:in].puts "run #{task} #{type}"
|
@status = :prepare
|
||||||
@worker[:status] = :prepare
|
|
||||||
rescue Errno::EPIPE
|
rescue Errno::EPIPE
|
||||||
dead
|
dead
|
||||||
rescue IOError
|
rescue IOError
|
||||||
@ -270,28 +275,33 @@ module Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def read
|
def read
|
||||||
((self[:status] == :quit) ? self[:out].read : self[:out].gets).chomp
|
((@status == :quit) ? @io.read : @io.gets).chomp
|
||||||
end
|
end
|
||||||
|
|
||||||
def [](k); @worker[k]; end
|
def close
|
||||||
def []=(k,v); @worker[k]=v; end
|
@io.close
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
def dead(*additional)
|
def dead(*additional)
|
||||||
@worker[:status] = :quit
|
@status = :quit
|
||||||
@worker[:in].close
|
@in.close
|
||||||
@worker[:out].close
|
@out.close
|
||||||
|
|
||||||
call_hook(:dead,*additional)
|
call_hook(:dead,*additional)
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
if self[:file]
|
if @file
|
||||||
"#{self[:pid]}=#{self[:file]}"
|
"#{@pid}=#{@file}"
|
||||||
else
|
else
|
||||||
"#{self[:pid]}:#{self[:status].to_s.ljust(7)}"
|
"#{@pid}:#{@status.to_s.ljust(7)}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
attr_reader :io, :pid
|
||||||
|
attr_accessor :status, :file, :real_file, :loadpath
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def call_hook(id,*additional)
|
def call_hook(id,*additional)
|
||||||
@ -342,15 +352,13 @@ module Test
|
|||||||
return unless @opts[:job_status]
|
return unless @opts[:job_status]
|
||||||
puts "" unless @opts[:verbose]
|
puts "" unless @opts[:verbose]
|
||||||
status_line = @workers.map(&:to_s).join(" ")
|
status_line = @workers.map(&:to_s).join(" ")
|
||||||
if @opts[:job_status_type] == :replace
|
if @opts[:job_status] == :replace
|
||||||
@terminal_width ||= %x{stty size 2>/dev/null}.split[1].to_i.nonzero? \
|
@terminal_width ||= $stdout.winsize[1] || ENV["COLUMNS"] || 80
|
||||||
|| %x{tput cols 2>/dev/null}.to_i.nonzero? \
|
|
||||||
|| 80
|
|
||||||
@jstr_size ||= 0
|
@jstr_size ||= 0
|
||||||
del_jobs_status
|
del_jobs_status
|
||||||
STDOUT.flush
|
$stdout.flush
|
||||||
print status_line[0...@terminal_width]
|
print status_line[0...@terminal_width]
|
||||||
STDOUT.flush
|
$stdout.flush
|
||||||
@jstr_size = status_line.size > @terminal_width ? \
|
@jstr_size = status_line.size > @terminal_width ? \
|
||||||
@terminal_width : status_line.size
|
@terminal_width : status_line.size
|
||||||
else
|
else
|
||||||
@ -359,7 +367,7 @@ module Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def del_jobs_status
|
def del_jobs_status
|
||||||
return unless @opts[:job_status_type] == :replace && @jstr_size
|
return unless @opts[:job_status] == :replace && @jstr_size
|
||||||
print "\r"+" "*@jstr_size+"\r"
|
print "\r"+" "*@jstr_size+"\r"
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -368,7 +376,7 @@ module Test
|
|||||||
return if @interrupt
|
return if @interrupt
|
||||||
@workers.delete(worker)
|
@workers.delete(worker)
|
||||||
@dead_workers << worker
|
@dead_workers << worker
|
||||||
@ios = @workers.map{|w| w[:out] }
|
@ios = @workers.map(&:io)
|
||||||
end
|
end
|
||||||
|
|
||||||
def _run_parallel suites, type, result
|
def _run_parallel suites, type, result
|
||||||
@ -385,32 +393,29 @@ module Test
|
|||||||
|
|
||||||
# Array of workers.
|
# Array of workers.
|
||||||
@workers = @opts[:parallel].times.map {
|
@workers = @opts[:parallel].times.map {
|
||||||
begin
|
|
||||||
worker = Worker.launch(@opts[:ruby],@args)
|
worker = Worker.launch(@opts[:ruby],@args)
|
||||||
worker.hook(:dead) do |w,info|
|
worker.hook(:dead) do |w,info|
|
||||||
after_worker_dead w
|
after_worker_dead w
|
||||||
after_worker_down w, *info unless info.empty?
|
after_worker_down w, *info unless info.empty?
|
||||||
end
|
end
|
||||||
worker
|
worker
|
||||||
rescue Exception; puts "#{$!.class}: #{$!.message}\n#{$!.backtrace}"
|
|
||||||
end
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Thread: watchdog
|
# Thread: watchdog
|
||||||
watchdog = Thread.new do
|
watchdog = Thread.new do
|
||||||
while stat = Process.wait2
|
while stat = Process.wait2
|
||||||
break if @interrupt # Break when interrupt
|
break if @interrupt # Break when interrupt
|
||||||
w = (@workers + @dead_workers).find{|x| stat[0] == x[:pid] }.dup
|
w = (@workers + @dead_workers).find{|x| stat[0] == x.pid }.dup
|
||||||
next unless w
|
next unless w
|
||||||
unless w[:status] == :quit
|
unless w.status == :quit
|
||||||
# Worker down
|
# Worker down
|
||||||
w.dead(nil, stat[1].to_i)
|
w.dead(nil, stat[1].to_i)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@workers_hash = Hash[@workers.map {|w| [w[:out],w] }] # out-IO => worker
|
@workers_hash = Hash[@workers.map {|w| [w.io,w] }] # out-IO => worker
|
||||||
@ios = @workers.map{|w| w[:out] } # Array of worker IOs
|
@ios = @workers.map{|w| w.io } # Array of worker IOs
|
||||||
|
|
||||||
while _io = IO.select(@ios)[0]
|
while _io = IO.select(@ios)[0]
|
||||||
break unless _io.each do |io|
|
break unless _io.each do |io|
|
||||||
@ -418,12 +423,12 @@ module Test
|
|||||||
worker = @workers_hash[io]
|
worker = @workers_hash[io]
|
||||||
case worker.read
|
case worker.read
|
||||||
when /^okay$/
|
when /^okay$/
|
||||||
worker[:status] = :running
|
worker.status = :running
|
||||||
jobs_status
|
jobs_status
|
||||||
when /^ready$/
|
when /^ready$/
|
||||||
worker[:status] = :ready
|
worker.status = :ready
|
||||||
if @tasks.empty?
|
if @tasks.empty?
|
||||||
break unless @workers.find{|x| x[:status] == :running }
|
break unless @workers.find{|x| x.status == :running }
|
||||||
else
|
else
|
||||||
worker.run(@tasks.shift, type)
|
worker.run(@tasks.shift, type)
|
||||||
end
|
end
|
||||||
@ -432,13 +437,13 @@ module Test
|
|||||||
when /^done (.+?)$/
|
when /^done (.+?)$/
|
||||||
r = Marshal.load($1.unpack("m")[0])
|
r = Marshal.load($1.unpack("m")[0])
|
||||||
result << r[0..1]
|
result << r[0..1]
|
||||||
rep << {file: worker[:real_file],
|
rep << {file: worker.real_file,
|
||||||
report: r[2], result: r[3], testcase: r[5]}
|
report: r[2], result: r[3], testcase: r[5]}
|
||||||
$:.push(*r[4]).uniq!
|
$:.push(*r[4]).uniq!
|
||||||
when /^p (.+?)$/
|
when /^p (.+?)$/
|
||||||
del_jobs_status
|
del_jobs_status
|
||||||
print $1.unpack("m")[0]
|
print $1.unpack("m")[0]
|
||||||
jobs_status if @opts[:job_status_type] == :replace
|
jobs_status if @opts[:job_status] == :replace
|
||||||
when /^after (.+?)$/
|
when /^after (.+?)$/
|
||||||
@warnings << Marshal.load($1.unpack("m")[0])
|
@warnings << Marshal.load($1.unpack("m")[0])
|
||||||
when /^bye (.+?)$/
|
when /^bye (.+?)$/
|
||||||
@ -463,12 +468,12 @@ module Test
|
|||||||
@workers.each do |worker|
|
@workers.each do |worker|
|
||||||
begin
|
begin
|
||||||
timeout(1) do
|
timeout(1) do
|
||||||
worker[:in].puts "quit"
|
worker.puts "quit"
|
||||||
end
|
end
|
||||||
rescue Errno::EPIPE
|
rescue Errno::EPIPE
|
||||||
rescue Timeout::Error
|
rescue Timeout::Error
|
||||||
end
|
end
|
||||||
[:in,:out].each { |name| worker[name].close }
|
worker.close
|
||||||
end
|
end
|
||||||
begin
|
begin
|
||||||
timeout(0.2*@workers.size) do
|
timeout(0.2*@workers.size) do
|
||||||
@ -477,7 +482,7 @@ module Test
|
|||||||
rescue Timeout::Error
|
rescue Timeout::Error
|
||||||
@workers.each do |worker|
|
@workers.each do |worker|
|
||||||
begin
|
begin
|
||||||
Process.kill(:KILL,worker[:pid])
|
Process.kill(:KILL,worker.pid)
|
||||||
rescue Errno::ESRCH; end
|
rescue Errno::ESRCH; end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user