free_mutant/lib/mutant/cli.rb
Markus Schirp 104afe3bcf Fix add into duplicated collection into binary op
* Preference is to not mutate values, even when the mutation appears to
  be "safe" as the mutated value looks "safe". Monkeypatching,
  refactoring whatever in the future can violate what appears to be "safe"
  now.
2015-09-08 17:21:57 +00:00

229 lines
5.6 KiB
Ruby

module Mutant
# Commandline parser
class CLI
include Adamantium::Flat, Equalizer.new(:config), Procto.call(:config)
# Error failed when CLI argv is invalid
Error = Class.new(RuntimeError)
EXIT_FAILURE = 1
EXIT_SUCCESS = 0
# Run cli with arguments
#
# @param [Array<String>] arguments
#
# @return [Fixnum]
# the exit status
#
# @api private
def self.run(arguments)
Runner.call(Env::Bootstrap.call(call(arguments))).success? ? EXIT_SUCCESS : EXIT_FAILURE
rescue Error => exception
$stderr.puts(exception.message)
EXIT_FAILURE
end
# Initialize objecct
#
# @param [Array<String>]
#
# @return [undefined]
#
# @api private
def initialize(arguments)
@config = Config::DEFAULT
parse(arguments)
end
# Config parsed from CLI
#
# @return [Config]
#
# @api private
attr_reader :config
private
# Parse the command-line options
#
# @param [Array<String>] arguments
# Command-line options and arguments to be parsed.
#
# @fail [Error]
# An error occurred while parsing the options.
#
# @return [undefined]
#
# @api private
def parse(arguments)
opts = OptionParser.new do |builder|
builder.banner = 'usage: mutant [options] MATCH_EXPRESSION ...'
%w[add_environment_options add_mutation_options add_filter_options add_debug_options].each do |name|
__send__(name, builder)
end
end
parse_match_expressions(opts.parse!(arguments))
rescue OptionParser::ParseError => error
raise(Error, error)
end
# Parse matchers
#
# @param [Array<String>] expressions
#
# @return [undefined]
#
# @api private
def parse_match_expressions(expressions)
fail Error, 'No expressions given' if expressions.empty?
expressions.each do |expression|
add_matcher(:match_expressions, config.expression_parser.(expression))
end
end
# Add environmental options
#
# @param [Object] opts
#
# @return [undefined]
#
# rubocop:disable MethodLength
#
# @api private
def add_environment_options(opts)
opts.separator('Environment:')
opts.on('--zombie', 'Run mutant zombified') do
update(zombie: true)
end
opts.on('-I', '--include DIRECTORY', 'Add DIRECTORY to $LOAD_PATH') do |directory|
add(:includes, directory)
end
opts.on('-r', '--require NAME', 'Require file with NAME') do |name|
add(:requires, name)
end
opts.on('-j', '--jobs NUMBER', 'Number of kill jobs. Defaults to number of processors.') do |number|
update(jobs: Integer(number))
end
end
# Use integration
#
# @param [String] name
#
# @return [undefined]
#
# @api private
def setup_integration(name)
update(integration: Integration.setup(name))
rescue LoadError
raise Error, "Could not load integration #{name.inspect} (you may want to try installing the gem mutant-#{name})"
end
# Add mutation options
#
# @param [OptionParser] opts
#
# @return [undefined]
#
# @api private
def add_mutation_options(opts)
opts.separator(nil)
opts.separator('Options:')
opts.on(
'--expected-coverage COVERAGE',
'Fail unless COVERAGE is not reached exactly, parsed via Rational()'
) do |coverage|
update(expected_coverage: Rational(coverage))
end
opts.on('--use INTEGRATION', 'Use INTEGRATION to kill mutations', &method(:setup_integration))
end
# Add filter options
#
# @param [OptionParser] opts
#
# @return [undefined]
#
# @api private
def add_filter_options(opts)
opts.on('--ignore-subject EXPRESSION', 'Ignore subjects that match EXPRESSION as prefix') do |pattern|
add_matcher(:ignore_expressions, config.expression_parser.(pattern))
end
opts.on('--since REVISION', 'Only select subjects touched since REVISION') do |revision|
add_matcher(:subject_filters, Repository::SubjectFilter.new(Repository::Diff.from_head(revision)))
end
end
# Add debug options
#
# @param [OptionParser] opts
#
# @return [undefined]
#
# @api private
def add_debug_options(opts)
opts.on('--fail-fast', 'Fail fast') do
update(fail_fast: true)
end
opts.on('--version', 'Print mutants version') do
puts("mutant-#{VERSION}")
Kernel.exit(EXIT_SUCCESS)
end
opts.on('-d', '--debug', 'Enable debugging output') do
update(debug: true)
end
opts.on_tail('-h', '--help', 'Show this message') do
puts(opts.to_s)
Kernel.exit(EXIT_SUCCESS)
end
end
# Update configuration
#
# @param [Hash<Symbol, Object>] attributes
#
# @return [undefined]
#
# @api private
def update(attributes)
@config = @config.update(attributes)
end
# Add configuration
#
# @param [Symbol] attribute
# the attribute to add to
#
# @param [Object] value
# the value to add
#
# @return [undefined]
#
# @api private
def add(attribute, value)
update(attribute => config.public_send(attribute) + [value])
end
# Add matcher configuration
#
# @param [Symbol] attribute
# the attribute to add to
#
# @param [Object] value
# the value to add
#
# @return [undefined]
#
# @api private
def add_matcher(attribute, value)
update(matcher: config.matcher.add(attribute, value))
end
end # CLI
end # Mutant