mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Update to ruby/mspec@2bca8cb
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64843 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
2466288d27
commit
e2d74af38b
8 changed files with 150 additions and 77 deletions
|
@ -89,72 +89,13 @@ class MSpecMain < MSpecScript
|
||||||
def register; end
|
def register; end
|
||||||
|
|
||||||
def multi_exec(argv)
|
def multi_exec(argv)
|
||||||
MSpec.register_files @files
|
|
||||||
|
|
||||||
require 'mspec/runner/formatters/multi'
|
require 'mspec/runner/formatters/multi'
|
||||||
formatter = MultiFormatter.new
|
formatter = MultiFormatter.new
|
||||||
if config[:formatter]
|
warn "formatter options is ignored due to multi option" if config[:formatter]
|
||||||
warn "formatter options is ignored due to multi option"
|
|
||||||
end
|
|
||||||
|
|
||||||
output_files = []
|
require 'mspec/runner/parallel'
|
||||||
processes = cores(@files.size)
|
processes = cores(@files.size)
|
||||||
children = processes.times.map { |i|
|
ParallelRunner.new(@files, processes, formatter, argv).run
|
||||||
name = tmp "mspec-multi-#{i}"
|
|
||||||
output_files << name
|
|
||||||
|
|
||||||
env = {
|
|
||||||
"SPEC_TEMP_DIR" => "rubyspec_temp_#{i}",
|
|
||||||
"MSPEC_MULTI" => i.to_s
|
|
||||||
}
|
|
||||||
command = argv + ["-fy", "-o", name]
|
|
||||||
$stderr.puts "$ #{command.join(' ')}" if $MSPEC_DEBUG
|
|
||||||
IO.popen([env, *command, close_others: false], "rb+")
|
|
||||||
}
|
|
||||||
|
|
||||||
puts children.map { |child| child.gets }.uniq
|
|
||||||
formatter.start
|
|
||||||
last_files = {}
|
|
||||||
|
|
||||||
until @files.empty?
|
|
||||||
IO.select(children)[0].each { |io|
|
|
||||||
reply = io.read(1)
|
|
||||||
case reply
|
|
||||||
when '.'
|
|
||||||
formatter.unload
|
|
||||||
when nil
|
|
||||||
raise "Worker died!"
|
|
||||||
else
|
|
||||||
while chunk = (io.read_nonblock(4096) rescue nil)
|
|
||||||
reply += chunk
|
|
||||||
end
|
|
||||||
reply.chomp!('.')
|
|
||||||
msg = "A child mspec-run process printed unexpected output on STDOUT"
|
|
||||||
if last_file = last_files[io]
|
|
||||||
msg += " while running #{last_file}"
|
|
||||||
end
|
|
||||||
abort "\n#{msg}: #{reply.inspect}"
|
|
||||||
end
|
|
||||||
|
|
||||||
unless @files.empty?
|
|
||||||
file = @files.shift
|
|
||||||
last_files[io] = file
|
|
||||||
io.puts file
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
success = true
|
|
||||||
children.each { |child|
|
|
||||||
child.puts "QUIT"
|
|
||||||
_pid, status = Process.wait2(child.pid)
|
|
||||||
success &&= status.success?
|
|
||||||
child.close
|
|
||||||
}
|
|
||||||
|
|
||||||
formatter.aggregate_results(output_files)
|
|
||||||
formatter.finish
|
|
||||||
success
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def run
|
def run
|
||||||
|
|
|
@ -15,15 +15,18 @@ class MultiFormatter < SpinnerFormatter
|
||||||
@exceptions = []
|
@exceptions = []
|
||||||
|
|
||||||
files.each do |file|
|
files.each do |file|
|
||||||
d = File.open(file, "r") { |f| YAML.load f }
|
contents = File.read(file)
|
||||||
|
d = YAML.load(contents)
|
||||||
File.delete file
|
File.delete file
|
||||||
|
|
||||||
@exceptions += Array(d['exceptions'])
|
if d # The file might be empty if the child process died
|
||||||
@tally.files! d['files']
|
@exceptions += Array(d['exceptions'])
|
||||||
@tally.examples! d['examples']
|
@tally.files! d['files']
|
||||||
@tally.expectations! d['expectations']
|
@tally.examples! d['examples']
|
||||||
@tally.errors! d['errors']
|
@tally.expectations! d['expectations']
|
||||||
@tally.failures! d['failures']
|
@tally.errors! d['errors']
|
||||||
|
@tally.failures! d['failures']
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ module MSpec
|
||||||
|
|
||||||
def self.process
|
def self.process
|
||||||
STDOUT.puts RUBY_DESCRIPTION
|
STDOUT.puts RUBY_DESCRIPTION
|
||||||
|
STDOUT.flush
|
||||||
|
|
||||||
actions :start
|
actions :start
|
||||||
files
|
files
|
||||||
|
@ -58,9 +59,8 @@ module MSpec
|
||||||
|
|
||||||
def self.each_file(&block)
|
def self.each_file(&block)
|
||||||
if ENV["MSPEC_MULTI"]
|
if ENV["MSPEC_MULTI"]
|
||||||
STDOUT.print "."
|
while file = STDIN.gets
|
||||||
STDOUT.flush
|
file = file.chomp
|
||||||
while file = STDIN.gets and file = file.chomp
|
|
||||||
return if file == "QUIT"
|
return if file == "QUIT"
|
||||||
yield file
|
yield file
|
||||||
begin
|
begin
|
||||||
|
|
98
spec/mspec/lib/mspec/runner/parallel.rb
Normal file
98
spec/mspec/lib/mspec/runner/parallel.rb
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
class ParallelRunner
|
||||||
|
def initialize(files, processes, formatter, argv)
|
||||||
|
@files = files
|
||||||
|
@processes = processes
|
||||||
|
@formatter = formatter
|
||||||
|
@argv = argv
|
||||||
|
@last_files = {}
|
||||||
|
@output_files = []
|
||||||
|
@success = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def launch_children
|
||||||
|
@children = @processes.times.map { |i|
|
||||||
|
name = tmp "mspec-multi-#{i}"
|
||||||
|
@output_files << name
|
||||||
|
|
||||||
|
env = {
|
||||||
|
"SPEC_TEMP_DIR" => "rubyspec_temp_#{i}",
|
||||||
|
"MSPEC_MULTI" => i.to_s
|
||||||
|
}
|
||||||
|
command = @argv + ["-fy", "-o", name]
|
||||||
|
$stderr.puts "$ #{command.join(' ')}" if $MSPEC_DEBUG
|
||||||
|
IO.popen([env, *command, close_others: false], "rb+")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle(child, message)
|
||||||
|
case message
|
||||||
|
when '.'
|
||||||
|
@formatter.unload
|
||||||
|
send_new_file_or_quit(child)
|
||||||
|
else
|
||||||
|
if message == nil
|
||||||
|
msg = "A child mspec-run process died unexpectedly"
|
||||||
|
else
|
||||||
|
msg = "A child mspec-run process printed unexpected output on STDOUT"
|
||||||
|
while chunk = (child.read_nonblock(4096) rescue nil)
|
||||||
|
message += chunk
|
||||||
|
end
|
||||||
|
message.chomp!('.')
|
||||||
|
msg += ": #{message.inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if last_file = @last_files[child]
|
||||||
|
msg += " while running #{last_file}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@success = false
|
||||||
|
quit(child)
|
||||||
|
abort "\n#{msg}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def quit(child)
|
||||||
|
begin
|
||||||
|
child.puts "QUIT"
|
||||||
|
rescue Errno::EPIPE
|
||||||
|
# The child process already died
|
||||||
|
end
|
||||||
|
_pid, status = Process.wait2(child.pid)
|
||||||
|
@success &&= status.success?
|
||||||
|
child.close
|
||||||
|
@children.delete(child)
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_new_file_or_quit(child)
|
||||||
|
if @files.empty?
|
||||||
|
quit(child)
|
||||||
|
else
|
||||||
|
file = @files.shift
|
||||||
|
@last_files[child] = file
|
||||||
|
child.puts file
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run
|
||||||
|
MSpec.register_files @files
|
||||||
|
launch_children
|
||||||
|
|
||||||
|
puts @children.map { |child| child.gets }.uniq
|
||||||
|
@formatter.start
|
||||||
|
begin
|
||||||
|
@children.each { |child| send_new_file_or_quit(child) }
|
||||||
|
|
||||||
|
until @children.empty?
|
||||||
|
IO.select(@children)[0].each { |child|
|
||||||
|
handle(child, child.read(1))
|
||||||
|
}
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
@children.dup.each { |child| quit(child) }
|
||||||
|
@formatter.aggregate_results(@output_files)
|
||||||
|
@formatter.finish
|
||||||
|
end
|
||||||
|
|
||||||
|
@success
|
||||||
|
end
|
||||||
|
end
|
8
spec/mspec/spec/fixtures/chatty_spec.rb
vendored
Normal file
8
spec/mspec/spec/fixtures/chatty_spec.rb
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
unless defined?(RSpec)
|
||||||
|
describe "Chatty#spec" do
|
||||||
|
it "prints too much" do
|
||||||
|
STDOUT.puts "Hello\nIt's me!"
|
||||||
|
1.should == 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
7
spec/mspec/spec/fixtures/die_spec.rb
vendored
Normal file
7
spec/mspec/spec/fixtures/die_spec.rb
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
unless defined?(RSpec)
|
||||||
|
describe "Deadly#spec" do
|
||||||
|
it "dies" do
|
||||||
|
abort "DEAD"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -24,23 +24,21 @@ EOS
|
||||||
|
|
||||||
a_stats = "1 file, 3 examples, 2 expectations, 1 failure, 1 error, 0 tagged\n"
|
a_stats = "1 file, 3 examples, 2 expectations, 1 failure, 1 error, 0 tagged\n"
|
||||||
ab_stats = "2 files, 4 examples, 3 expectations, 1 failure, 1 error, 0 tagged\n"
|
ab_stats = "2 files, 4 examples, 3 expectations, 1 failure, 1 error, 0 tagged\n"
|
||||||
|
fixtures = "spec/fixtures"
|
||||||
|
|
||||||
it "runs the specs" do
|
it "runs the specs" do
|
||||||
fixtures = "spec/fixtures"
|
|
||||||
out, ret = run_mspec("run", "#{fixtures}/a_spec.rb")
|
out, ret = run_mspec("run", "#{fixtures}/a_spec.rb")
|
||||||
out.should == "RUBY_DESCRIPTION\n.FE\n#{a_spec_output}\n#{a_stats}"
|
out.should == "RUBY_DESCRIPTION\n.FE\n#{a_spec_output}\n#{a_stats}"
|
||||||
ret.success?.should == false
|
ret.success?.should == false
|
||||||
end
|
end
|
||||||
|
|
||||||
it "directly with mspec-run runs the specs" do
|
it "directly with mspec-run runs the specs" do
|
||||||
fixtures = "spec/fixtures"
|
|
||||||
out, ret = run_mspec("-run", "#{fixtures}/a_spec.rb")
|
out, ret = run_mspec("-run", "#{fixtures}/a_spec.rb")
|
||||||
out.should == "RUBY_DESCRIPTION\n.FE\n#{a_spec_output}\n#{a_stats}"
|
out.should == "RUBY_DESCRIPTION\n.FE\n#{a_spec_output}\n#{a_stats}"
|
||||||
ret.success?.should == false
|
ret.success?.should == false
|
||||||
end
|
end
|
||||||
|
|
||||||
it "runs the specs in parallel with -j" do
|
it "runs the specs in parallel with -j" do
|
||||||
fixtures = "spec/fixtures"
|
|
||||||
out, ret = run_mspec("run", "-j #{fixtures}/a_spec.rb #{fixtures}/b_spec.rb")
|
out, ret = run_mspec("run", "-j #{fixtures}/a_spec.rb #{fixtures}/b_spec.rb")
|
||||||
progress_bar =
|
progress_bar =
|
||||||
"\r[/ | 0% | 00:00:00] \e[0;32m 0F \e[0;32m 0E\e[0m " +
|
"\r[/ | 0% | 00:00:00] \e[0;32m 0F \e[0;32m 0E\e[0m " +
|
||||||
|
@ -49,4 +47,22 @@ EOS
|
||||||
out.should == "RUBY_DESCRIPTION\n#{progress_bar}\n#{a_spec_output}\n#{ab_stats}"
|
out.should == "RUBY_DESCRIPTION\n#{progress_bar}\n#{a_spec_output}\n#{ab_stats}"
|
||||||
ret.success?.should == false
|
ret.success?.should == false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "gives a useful error message when a subprocess dies in parallel mode" do
|
||||||
|
out, ret = run_mspec("run", "-j #{fixtures}/b_spec.rb #{fixtures}/die_spec.rb")
|
||||||
|
lines = out.lines
|
||||||
|
lines.should include "A child mspec-run process died unexpectedly while running CWD/spec/fixtures/die_spec.rb\n"
|
||||||
|
lines.should include "Finished in D.DDDDDD seconds\n"
|
||||||
|
lines.last.should =~ /^\d files?, \d examples?, \d expectations?, 0 failures, 0 errors, 0 tagged$/
|
||||||
|
ret.success?.should == false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "gives a useful error message when a subprocess prints unexpected output on STDOUT in parallel mode" do
|
||||||
|
out, ret = run_mspec("run", "-j #{fixtures}/b_spec.rb #{fixtures}/chatty_spec.rb")
|
||||||
|
lines = out.lines
|
||||||
|
lines.should include "A child mspec-run process printed unexpected output on STDOUT: #{'"Hello\nIt\'s me!\n"'} while running CWD/spec/fixtures/chatty_spec.rb\n"
|
||||||
|
lines.should include "Finished in D.DDDDDD seconds\n"
|
||||||
|
lines.last.should == "2 files, 2 examples, 2 expectations, 0 failures, 0 errors, 0 tagged\n"
|
||||||
|
ret.success?.should == false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,10 +9,10 @@ describe MultiFormatter, "#aggregate_results" do
|
||||||
@file = double("file").as_null_object
|
@file = double("file").as_null_object
|
||||||
|
|
||||||
File.stub(:delete)
|
File.stub(:delete)
|
||||||
YAML.stub(:load)
|
File.stub(:read)
|
||||||
|
|
||||||
@hash = { "files"=>1, "examples"=>1, "expectations"=>2, "failures"=>0, "errors"=>0 }
|
@hash = { "files"=>1, "examples"=>1, "expectations"=>2, "failures"=>0, "errors"=>0 }
|
||||||
File.stub(:open).and_yield(@file).and_return(@hash)
|
YAML.stub(:load).and_return(@hash)
|
||||||
|
|
||||||
@formatter = MultiFormatter.new
|
@formatter = MultiFormatter.new
|
||||||
@formatter.timer.stub(:format).and_return("Finished in 42 seconds")
|
@formatter.timer.stub(:format).and_return("Finished in 42 seconds")
|
||||||
|
|
Loading…
Reference in a new issue