Add Mutant::CLI
This commit is contained in:
parent
483c295d2d
commit
c211a2e065
3 changed files with 221 additions and 0 deletions
112
lib/mutant/cli.rb
Normal file
112
lib/mutant/cli.rb
Normal file
|
@ -0,0 +1,112 @@
|
|||
module Mutant
|
||||
# Comandline adapter or mutant runner
|
||||
class CLI
|
||||
include Immutable
|
||||
|
||||
# Error raised when CLI argv is inalid
|
||||
Error = Class.new(RuntimeError)
|
||||
|
||||
def self.run(*arguments)
|
||||
Runner.run(new(*arguments).runner_options)
|
||||
end
|
||||
|
||||
def runner_options
|
||||
{
|
||||
:mutation_filter => mutation_filter,
|
||||
:matcher => matcher,
|
||||
:reporter => Reporter::CLI.new($stderr),
|
||||
:killer => Killer::Rspec
|
||||
}
|
||||
end
|
||||
memoize :runner_options
|
||||
|
||||
private
|
||||
|
||||
OPTIONS = {
|
||||
'--code' => [:add_filter, Mutation::Filter::Code].freeze
|
||||
}.deep_freeze
|
||||
|
||||
OPTION_PATTERN = %r(\A-(?:-)?[a-z0-9]+\z).freeze
|
||||
|
||||
def option(index)
|
||||
@arguments.fetch(index+1)
|
||||
end
|
||||
|
||||
def initialize(arguments)
|
||||
@filters, @matchers = [], []
|
||||
|
||||
@arguments = arguments
|
||||
|
||||
@index = 0
|
||||
|
||||
while @index < @arguments.length
|
||||
dispatch
|
||||
end
|
||||
end
|
||||
|
||||
def current_argument
|
||||
@arguments.fetch(@index)
|
||||
end
|
||||
|
||||
def current_option_value
|
||||
@arguments.fetch(@index+1)
|
||||
rescue IndexError
|
||||
raise Error,"#{current_argument.inspect} is missing an argument"
|
||||
end
|
||||
|
||||
def dispatch
|
||||
if OPTION_PATTERN.match(current_argument)
|
||||
dispatch_option
|
||||
else
|
||||
dispatch_matcher
|
||||
end
|
||||
end
|
||||
|
||||
def consume(amount)
|
||||
@index += amount
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
def dispatch_option
|
||||
argument = current_argument
|
||||
arguments = *OPTIONS.fetch(argument) do
|
||||
raise Error, "Unknown option: #{argument.inspect}"
|
||||
end
|
||||
send(*arguments)
|
||||
end
|
||||
|
||||
def add_filter(klass)
|
||||
@filters << klass.new(current_option_value)
|
||||
consume(2)
|
||||
end
|
||||
|
||||
def matcher
|
||||
if @matchers.empty?
|
||||
raise Error, 'No matchers given'
|
||||
end
|
||||
|
||||
Mutant::Matcher::Chain.new(@matchers)
|
||||
end
|
||||
|
||||
def mutation_filter
|
||||
if @filters.empty?
|
||||
Mutation::Filter::ALL
|
||||
else
|
||||
Mutation::Filter::Whitelist.new(@filters)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
23
spec/unit/mutant/cli/class_methods/run_spec.rb
Normal file
23
spec/unit/mutant/cli/class_methods/run_spec.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Mutant::CLI, '.run' do
|
||||
subject { object.run(argv) }
|
||||
|
||||
let(:object) { described_class }
|
||||
let(:argv) { mock('ARGV') }
|
||||
let(:options) { mock('Options') }
|
||||
let(:runner) { mock('Runner') }
|
||||
let(:instance) { mock(described_class.name, :runner_options => options) }
|
||||
|
||||
before do
|
||||
described_class.stub(:new => instance)
|
||||
Mutant::Runner.stub(:run => runner)
|
||||
end
|
||||
|
||||
it { should be(runner) }
|
||||
|
||||
it 'should run with options' do
|
||||
Mutant::Runner.should_receive(:run).with(options).and_return(runner)
|
||||
should be(runner)
|
||||
end
|
||||
end
|
86
spec/unit/mutant/cli/runner_options_spec.rb
Normal file
86
spec/unit/mutant/cli/runner_options_spec.rb
Normal file
|
@ -0,0 +1,86 @@
|
|||
require 'spec_helper'
|
||||
|
||||
shared_examples_for 'an invalid cli run' do
|
||||
it 'should raise error' do
|
||||
expect { subject }.to raise_error(described_class::Error, expected_message)
|
||||
end
|
||||
end
|
||||
|
||||
describe Mutant::CLI, '#runner_options' do
|
||||
subject { object.runner_options }
|
||||
|
||||
let(:object) { described_class.new(arguments) }
|
||||
|
||||
context 'with unknown option' do
|
||||
let(:arguments) { %w(--invalid Foo) }
|
||||
|
||||
let(:expected_message) { 'Unknown option: "--invalid"' }
|
||||
|
||||
it_should_behave_like 'an invalid cli run'
|
||||
end
|
||||
|
||||
context 'without arguments' do
|
||||
let(:arguments) { [] }
|
||||
|
||||
let(:expected_message) { 'No matchers given' }
|
||||
|
||||
it_should_behave_like 'an invalid cli run'
|
||||
end
|
||||
|
||||
context 'with code filter and missing argument' do
|
||||
let(:arguments) { %w(--code) }
|
||||
|
||||
let(:expected_message) { '"--code" is missing an argument' }
|
||||
|
||||
it_should_behave_like 'an invalid cli run'
|
||||
end
|
||||
|
||||
context 'with explicit method matcher' do
|
||||
let(:arguments) { %w(TestApp::Literal#float) }
|
||||
|
||||
let(:expected_options) do
|
||||
{
|
||||
:matcher => Mutant::Matcher::Chain.new([Mutant::Matcher::Method.parse('TestApp::Literal#float')]),
|
||||
:mutation_filter => Mutant::Mutation::Filter::ALL,
|
||||
:killer => Mutant::Killer::Rspec
|
||||
}
|
||||
end
|
||||
|
||||
it { should eql(expected_options) }
|
||||
end
|
||||
|
||||
context 'with library name' do
|
||||
let(:arguments) { %w(::TestApp) }
|
||||
|
||||
let(:expected_options) do
|
||||
{
|
||||
:matcher => Mutant::Matcher::Chain.new([Mutant::Matcher::ObjectSpace.new(%r(\ATestApp(::)?\z))]),
|
||||
:mutation_filter => Mutant::Mutation::Filter::ALL,
|
||||
:killer => Mutant::Killer::Rspec
|
||||
}
|
||||
end
|
||||
|
||||
it { should eql(expected_options) }
|
||||
end
|
||||
|
||||
context 'with code filter' do
|
||||
let(:arguments) { %w(--code faa --code bbb TestApp::Literal#float) }
|
||||
|
||||
let(:filters) do
|
||||
[
|
||||
Mutant::Mutation::Filter::Code.new('faa'),
|
||||
Mutant::Mutation::Filter::Code.new('bbb'),
|
||||
]
|
||||
end
|
||||
|
||||
let(:expected_options) do
|
||||
{
|
||||
:mutation_filter => Mutant::Mutation::Filter::Whitelist.new(filters),
|
||||
:matcher => Mutant::Matcher::Chain.new([Mutant::Matcher::Method.parse('TestApp::Literal#float')]),
|
||||
:killer => Mutant::Killer::Rspec
|
||||
}
|
||||
end
|
||||
|
||||
it { should eql(expected_options) }
|
||||
end
|
||||
end
|
Loading…
Add table
Reference in a new issue