Add Mutant::CLI

This commit is contained in:
Markus Schirp 2012-08-28 18:57:39 +02:00
parent 483c295d2d
commit c211a2e065
3 changed files with 221 additions and 0 deletions

112
lib/mutant/cli.rb Normal file
View 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

View 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

View 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