2012-08-28 12:57:39 -04:00
|
|
|
module Mutant
|
2012-09-15 18:51:47 -04:00
|
|
|
# Comandline parser
|
2012-08-28 12:57:39 -04:00
|
|
|
class CLI
|
2012-11-21 16:28:08 -05:00
|
|
|
include Adamantium::Flat, Equalizer.new(:matcher, :filter, :killer)
|
2012-08-28 12:57:39 -04:00
|
|
|
|
|
|
|
# Error raised when CLI argv is inalid
|
|
|
|
Error = Class.new(RuntimeError)
|
|
|
|
|
2012-11-21 16:28:08 -05:00
|
|
|
EXIT_FAILURE = 1
|
|
|
|
EXIT_SUCCESS = 0
|
|
|
|
|
2012-08-28 13:43:15 -04:00
|
|
|
# Run cli with arguments
|
|
|
|
#
|
|
|
|
# @param [Array<String>] arguments
|
|
|
|
#
|
|
|
|
# @return [Fixnum]
|
|
|
|
# returns exit status
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
2012-08-28 12:57:39 -04:00
|
|
|
def self.run(*arguments)
|
2012-11-21 16:28:08 -05:00
|
|
|
error = Runner.run(new(*arguments)).fail?
|
|
|
|
error ? EXIT_FAILURE : EXIT_SUCCESS
|
|
|
|
rescue Error => exception
|
|
|
|
$stderr.puts(exception.message)
|
|
|
|
EXIT_FAILURE
|
|
|
|
end
|
|
|
|
|
|
|
|
# Return matcher
|
|
|
|
#
|
|
|
|
# @return [Mutant::Matcher]
|
|
|
|
#
|
|
|
|
# @raise [CLI::Error]
|
|
|
|
# raises error when matcher is not given
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
|
|
|
def matcher
|
|
|
|
if @matchers.empty?
|
|
|
|
raise Error, 'No matchers given'
|
|
|
|
end
|
|
|
|
|
|
|
|
Mutant::Matcher::Chain.build(@matchers)
|
|
|
|
end
|
|
|
|
memoize :matcher
|
|
|
|
|
|
|
|
# Return mutation filter
|
|
|
|
#
|
|
|
|
# @return [Mutant::Matcher]
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
|
|
|
def filter
|
|
|
|
if @filters.empty?
|
|
|
|
Mutation::Filter::ALL
|
|
|
|
else
|
|
|
|
Mutation::Filter::Whitelist.new(@filters)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
memoize :filter
|
|
|
|
|
|
|
|
# Return killer
|
|
|
|
#
|
|
|
|
# @return [Mutant::Killer]
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
|
|
|
def killer
|
2012-11-21 16:55:53 -05:00
|
|
|
killer = Mutant::Killer::Rspec
|
|
|
|
if @forking
|
|
|
|
Mutant::Killer::Forking.new(killer)
|
|
|
|
else
|
|
|
|
killer
|
|
|
|
end
|
2012-08-28 12:57:39 -04:00
|
|
|
end
|
2012-11-21 16:28:08 -05:00
|
|
|
memoize :killer
|
2012-08-28 12:57:39 -04:00
|
|
|
|
2012-11-21 16:28:08 -05:00
|
|
|
# Return reporter
|
2012-08-28 13:43:15 -04:00
|
|
|
#
|
2012-11-21 16:28:08 -05:00
|
|
|
# @return [Mutant::Reporter::CLI]
|
2012-08-28 13:43:15 -04:00
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
2012-11-21 16:28:08 -05:00
|
|
|
def reporter
|
|
|
|
Mutant::Reporter::CLI.new($stderr)
|
2012-08-28 12:57:39 -04:00
|
|
|
end
|
2012-11-21 16:28:08 -05:00
|
|
|
memoize :reporter
|
2012-08-28 12:57:39 -04:00
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
OPTIONS = {
|
2012-10-26 07:12:42 -04:00
|
|
|
'--code' => [:add_filter, Mutation::Filter::Code],
|
2012-10-26 05:24:29 -04:00
|
|
|
'-I' => [:add_load_path],
|
|
|
|
'--include' => [:add_load_path],
|
|
|
|
'-r' => [:require_library],
|
2012-11-21 16:55:53 -05:00
|
|
|
'--require' => [:require_library],
|
|
|
|
'--fork' => [:set_forking]
|
2012-08-28 12:57:39 -04:00
|
|
|
}.deep_freeze
|
|
|
|
|
2012-10-26 05:24:29 -04:00
|
|
|
OPTION_PATTERN = %r(\A-(?:-)?[a-zA-Z0-9]+\z).freeze
|
2012-08-28 12:57:39 -04:00
|
|
|
|
2012-08-28 13:43:15 -04:00
|
|
|
# Return option for argument with index
|
|
|
|
#
|
|
|
|
# @param [Fixnum] index
|
|
|
|
#
|
|
|
|
# @return [String]
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
2012-08-28 12:57:39 -04:00
|
|
|
def option(index)
|
|
|
|
@arguments.fetch(index+1)
|
|
|
|
end
|
|
|
|
|
2012-08-28 13:43:15 -04:00
|
|
|
# Initialize CLI
|
|
|
|
#
|
|
|
|
# @param [Array<String>] arguments
|
|
|
|
#
|
|
|
|
# @return [undefined]
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
2012-08-28 12:57:39 -04:00
|
|
|
def initialize(arguments)
|
|
|
|
@filters, @matchers = [], []
|
|
|
|
|
2012-09-15 18:51:47 -04:00
|
|
|
@arguments, @index = arguments, 0
|
2012-08-28 12:57:39 -04:00
|
|
|
|
|
|
|
while @index < @arguments.length
|
|
|
|
dispatch
|
|
|
|
end
|
2012-11-21 16:28:08 -05:00
|
|
|
|
|
|
|
matcher
|
2012-08-28 12:57:39 -04:00
|
|
|
end
|
|
|
|
|
2012-08-28 13:43:15 -04:00
|
|
|
# Return current argument
|
|
|
|
#
|
|
|
|
# @return [String]
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
2012-08-28 12:57:39 -04:00
|
|
|
def current_argument
|
|
|
|
@arguments.fetch(@index)
|
|
|
|
end
|
|
|
|
|
2012-08-28 13:43:15 -04:00
|
|
|
# Return current option value
|
|
|
|
#
|
|
|
|
# @return [String]
|
|
|
|
#
|
|
|
|
# @raise [CLI::Error]
|
|
|
|
# raises error when option is missing
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
2012-08-28 12:57:39 -04:00
|
|
|
def current_option_value
|
|
|
|
@arguments.fetch(@index+1)
|
|
|
|
rescue IndexError
|
2012-09-15 18:51:47 -04:00
|
|
|
raise Error, "#{current_argument.inspect} is missing an argument"
|
2012-08-28 12:57:39 -04:00
|
|
|
end
|
|
|
|
|
2012-08-28 13:43:15 -04:00
|
|
|
# Process current argument
|
|
|
|
#
|
|
|
|
# @return [undefined]
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
2012-08-28 12:57:39 -04:00
|
|
|
def dispatch
|
|
|
|
if OPTION_PATTERN.match(current_argument)
|
|
|
|
dispatch_option
|
|
|
|
else
|
|
|
|
dispatch_matcher
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-08-28 13:43:15 -04:00
|
|
|
# Move processed argument by amount
|
|
|
|
#
|
|
|
|
# @param [Fixnum] amount
|
|
|
|
# the amount of arguments to be consumed
|
|
|
|
#
|
|
|
|
# @return [undefined]
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
2012-08-28 12:57:39 -04:00
|
|
|
def consume(amount)
|
|
|
|
@index += amount
|
|
|
|
end
|
|
|
|
|
2012-08-28 13:43:15 -04:00
|
|
|
# Process matcher argument
|
|
|
|
#
|
|
|
|
# @return [undefined]
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
2012-08-28 12:57:39 -04:00
|
|
|
def dispatch_matcher
|
|
|
|
argument = current_argument
|
|
|
|
matcher = Mutant::Matcher.from_string(argument)
|
|
|
|
|
|
|
|
unless matcher
|
|
|
|
raise Error, "Invalid matcher syntax: #{argument.inspect}"
|
|
|
|
end
|
|
|
|
|
|
|
|
@matchers << matcher
|
|
|
|
|
|
|
|
consume(1)
|
|
|
|
end
|
|
|
|
|
2012-08-28 13:43:15 -04:00
|
|
|
# Process option argument
|
|
|
|
#
|
|
|
|
# @return [Undefined]
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
2012-08-28 12:57:39 -04:00
|
|
|
def dispatch_option
|
|
|
|
argument = current_argument
|
|
|
|
arguments = *OPTIONS.fetch(argument) do
|
|
|
|
raise Error, "Unknown option: #{argument.inspect}"
|
|
|
|
end
|
|
|
|
send(*arguments)
|
|
|
|
end
|
|
|
|
|
2012-08-28 13:43:15 -04:00
|
|
|
# Add mutation filter
|
|
|
|
#
|
|
|
|
# @param [Class<Mutant::Filter>]
|
|
|
|
#
|
|
|
|
# @return [undefined]
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
2012-08-28 12:57:39 -04:00
|
|
|
def add_filter(klass)
|
|
|
|
@filters << klass.new(current_option_value)
|
|
|
|
consume(2)
|
|
|
|
end
|
|
|
|
|
2012-10-26 05:24:29 -04:00
|
|
|
# Add load path
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
|
|
|
# @return [undefined]
|
|
|
|
#
|
|
|
|
def add_load_path
|
|
|
|
$LOAD_PATH << current_option_value
|
|
|
|
consume(2)
|
|
|
|
end
|
|
|
|
|
2012-11-21 16:55:53 -05:00
|
|
|
# Set forking
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
|
|
|
# @return [self]
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
|
|
|
def set_forking
|
|
|
|
consume(1)
|
|
|
|
@forking = true
|
|
|
|
end
|
|
|
|
|
2012-10-26 05:24:29 -04:00
|
|
|
# Require library
|
|
|
|
#
|
|
|
|
# @api private
|
|
|
|
#
|
|
|
|
# @return [undefined]
|
|
|
|
#
|
|
|
|
def require_library
|
|
|
|
require(current_option_value)
|
|
|
|
consume(2)
|
|
|
|
end
|
|
|
|
|
2012-08-28 12:57:39 -04:00
|
|
|
end
|
|
|
|
end
|