File: //usr/local/rvm/src/ruby-2.5.9/spec/mspec/lib/mspec/commands/mspec.rb
#!/usr/bin/env ruby
require 'mspec/version'
require 'mspec/utils/options'
require 'mspec/utils/script'
require 'mspec/helpers/tmp'
require 'mspec/runner/actions/filter'
require 'mspec/runner/actions/timer'
class MSpecMain < MSpecScript
def initialize
super
config[:loadpath] = []
config[:requires] = []
config[:target] = ENV['RUBY'] || 'ruby'
config[:flags] = []
config[:command] = nil
config[:options] = []
config[:launch] = []
end
def options(argv=ARGV)
config[:command] = argv.shift if ["ci", "run", "tag"].include?(argv[0])
options = MSpecOptions.new "mspec [COMMAND] [options] (FILE|DIRECTORY|GLOB)+", 30, config
options.doc " The mspec command sets up and invokes the sub-commands"
options.doc " (see below) to enable, for instance, running the specs"
options.doc " with different implementations like ruby, jruby, rbx, etc.\n"
options.configure do |f|
load f
config[:options] << '-B' << f
end
options.targets
options.on("--warnings", "Don't suppress warnings") do
config[:flags] << '-w'
ENV['OUTPUT_WARNINGS'] = '1'
end
options.on("-j", "--multi", "Run multiple (possibly parallel) subprocesses") do
config[:multi] = true
end
options.version MSpec::VERSION do
if config[:command]
config[:options] << "-v"
else
puts "#{File.basename $0} #{MSpec::VERSION}"
exit
end
end
options.help do
if config[:command]
config[:options] << "-h"
else
puts options
exit 1
end
end
options.doc "\n Custom options"
custom_options options
# The rest of the help output
options.doc "\n where COMMAND is one of:\n"
options.doc " run - Run the specified specs (default)"
options.doc " ci - Run the known good specs"
options.doc " tag - Add or remove tags\n"
options.doc " mspec COMMAND -h for more options\n"
options.doc " example: $ mspec run -h\n"
options.on_extra { |o| config[:options] << o }
options.parse(argv)
if config[:multi]
options = MSpecOptions.new "mspec", 30, config
options.all
patterns = options.parse(config[:options])
@files = files_from_patterns(patterns)
end
end
def register; end
def multi_exec(argv)
MSpec.register_files @files
require 'mspec/runner/formatters/multi'
formatter = MultiFormatter.new
if config[:formatter]
warn "formatter options is ignored due to multi option"
end
output_files = []
processes = cores(@files.size)
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+")
}
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
def run
argv = config[:target].split(/\s+/)
argv.concat config[:launch]
argv.concat config[:flags]
argv.concat config[:loadpath]
argv.concat config[:requires]
argv << "#{MSPEC_HOME}/bin/mspec-#{config[:command] || 'run'}"
argv.concat config[:options]
if config[:multi]
exit multi_exec(argv)
else
$stderr.puts "$ #{argv.join(' ')}"
$stderr.flush
exec(*argv, close_others: false)
end
end
end