Add better rspec dm2 spec lookup

This commit is contained in:
Markus Schirp 2013-01-13 23:47:00 +01:00
parent e4201b3dfb
commit c9d762dbe9
9 changed files with 332 additions and 401 deletions

View file

@ -100,7 +100,9 @@ require 'mutant/killer/forking'
require 'mutant/strategy' require 'mutant/strategy'
require 'mutant/strategy/method_expansion' require 'mutant/strategy/method_expansion'
require 'mutant/strategy/rspec' require 'mutant/strategy/rspec'
require 'mutant/strategy/rspec/example_lookup' require 'mutant/strategy/rspec/dm2'
require 'mutant/strategy/rspec/dm2/lookup'
require 'mutant/strategy/rspec/dm2/lookup/method'
require 'mutant/runner' require 'mutant/runner'
require 'mutant/cli' require 'mutant/cli'
require 'mutant/color' require 'mutant/color'

View file

@ -42,7 +42,7 @@ module Mutant
def command_line_arguments def command_line_arguments
%W( %W(
--fail-fast --fail-fast
) + strategy.spec_files(mutation) ) + strategy.spec_files(mutation.subject)
end end
end end
end end

View file

@ -0,0 +1,20 @@
module Mutant
class Strategy
class Rspec
# DM2-style strategy
class DM2 < self
# Return filename pattern
#
# @return [Enumerable<String>]
#
# @api private
#
def spec_files(mutation)
ExampleLookup.run(mutation)
end
end
end
end
end

View file

@ -0,0 +1,79 @@
module Mutant
class Strategy
class Rspec
class DM2
# Example lookup for the rspec dm2
class Lookup
include AbstractType, Adamantium::Flat, Equalizer.new(:subject)
# Return subject
#
# @return [Subject]
#
# @api private
#
attr_reader :subject
# Return glob expression
#
# @return [String]
#
# @api private
#
abstract_method :spec_files
# Initalize object
#
# @param [Mutation] mutation
#
# @api private
#
def initialize(subject)
@subject = subject
end
# Perform example lookup
#
# @param [Subject] subject
#
# @return [Enumerable<String>]
#
# @api private
#
def self.run(subject)
new(subject).spec_files
end
REGISTRY = {}
# Register subject hander
#
# @param [Class:Subject]
#
# @return [undefined]
#
# @api private
#
def self.handle(subject_class)
REGISTRY[subject_class]=self
end
private_class_method :handle
# Build lookup object
#
# @param [Subjecŧ] subject
#
# @return [Lookup]
#
# @api private
#
def self.build(subject)
REGISTRY.fetch(subject.class).new(subject)
end
end
end
end
end
end

View file

@ -0,0 +1,135 @@
module Mutant
class Strategy
class Rspec
class DM2
class Lookup
class Method < self
# Return spec files
#
# @return [Enumerable<String>]
#
# @api private
#
def spec_files
Dir.glob(glob_expression)
end
memoize :spec_files
private
# Return base path
#
# @return [String]
#
# @api private
#
def base_path
"spec/unit/#{Inflector.underscore(subject.context_name)}"
end
# Return method name
#
# @return [Symbol]
#
# @api private
#
def method_name
subject.method_name
end
# Test if method is public
#
# @return [true]
# if method is public
#
# @return [false]
#
# @api private
#
def public?
subject.public?
end
# Return expanded name
#
# @return [String]
#
# @api private
#
def expanded_name
MethodExpansion.run(subject.method_name)
end
# Return glob expression
#
# @return [String]
#
# @api private
#
def glob_expression
public? ? public_glob_expression : private_glob_expression
end
# Return public glob expression
#
# @return [String]
#
# @api private
#
def public_glob_expression
"#{base_path}/#{expanded_name}_spec.rb"
end
# Return private glob expression
#
# @return [String]
#
# @api private
#
def private_glob_expression
"#{base_path}/*_spec.rb"
end
class Instance < self
handle(Subject::Method::Instance)
private
# Return glob expression
#
# @return [String]
#
# @api private
#
def glob_expression
if method_name == :initialize and !public?
return "#{private_glob_expression} #{base_path}/class_methods/new_spec.rb"
end
super
end
end
class Singleton < self
handle(Subject::Method::Singleton)
private
# Return base path
#
# @return [String]
#
# @api private
#
def base_path
"#{super}/class_methods"
end
end
end
end
end
end
end
end

View file

@ -1,163 +0,0 @@
module Mutant
class Strategy
class Rspec
# Example lookup for rspec
class ExampleLookup
include Adamantium::Flat, Equalizer.new(:mutation)
# Perform example lookup
#
# @param [Mutation] mutation
#
# @return [Enumerable<String>]
#
# @api private
#
def self.run(mutation)
new(mutation).spec_files
end
# Return mutation
#
# @return [Mutation]
#
# @api private
#
attr_reader :mutation
# Return spec files
#
# @return [Enumerable<String>]
#
# @api private
#
def spec_files
expression = glob_expression
files = Dir[expression]
if files.empty?
$stderr.puts("Spec file(s): #{expression.inspect} not found for #{mutation.identification}")
end
files
end
memoize :spec_files
private
# Return method matcher
#
# @return [Matcher]
#
# @api private
#
def matcher
mutation.subject.matcher
end
EXPANSIONS = {
/\?\z/ => '_predicate',
/=\z/ => '_writer',
/!\z/ => '_bang'
}
# Return spec file
#
# @return [String]
#
# @api private
#
def spec_file
"#{mapped_name || expanded_name}_spec.rb"
end
memoize :spec_file
# Return mapped name
#
# @return [Symbol]
# if name was mapped
#
# @return [nil]
# otherwise
#
# @api private
#
def mapped_name
OPERATOR_EXPANSIONS[method_name]
end
# Return expanded name
#
# @return [Symbol]
#
# @api private
#
def expanded_name
EXPANSIONS.inject(method_name) do |name, (regexp, expansion)|
name.to_s.gsub(regexp, expansion)
end.to_sym
end
# Return method name
#
# @return [Symbol]
#
# @api private
#
def method_name
matcher.method_name
end
# Return glob expression
#
# @return [String]
#
# @api private
#
def glob_expression
base = base_path
if mutation.subject.matcher.public?
"#{base}/#{spec_file}"
else
"#{base}/*_spec.rb"
end
end
# Return instance of singleton path appendo
#
# @return [String]
#
# @api private
#
def scope_append
matcher.kind_of?(Matcher::Method::Singleton) ? '/class_methods' : ''
end
memoize :scope_append
# Return base path
#
# @return [String]
#
# @api private
#
def base_path
"spec/unit/#{Inflector.underscore(mutation.subject.context.scope.name)}#{scope_append}"
end
memoize :base_path
# Initalize object
#
# @param [Mutation] mutation
#
# @api private
#
def initialize(mutation)
@mutation = mutation
end
end
end
end
end

View file

@ -0,0 +1,52 @@
require 'spec_helper'
describe Mutant::Strategy::Rspec::DM2::Lookup::Method::Instance, '#spec_files' do
subject { object.spec_files }
let(:object) { described_class.new(mutation_subject) }
let(:mutation_subject) { mock('Subject', :method_name => method_name, :public? => is_public, :context_name => context_name) }
let(:context_name) { 'Foo' }
let(:method_name) { :bar }
let(:files) { 'Files'.freeze }
this_example_group = 'Mutant::Strategy::Rspec::DM2::Lookup::Method::Instance#spec_files'
shared_examples_for this_example_group do
it_should_behave_like 'an idempotent method'
before do
if is_public
Mutant::Strategy::MethodExpansion.should_receive(:run).with(method_name).and_return(:expanded_name)
end
Dir.should_receive(:glob).with(expected_glob_expression).and_return(files)
end
it { should be(files) }
it { should be_frozen }
end
context 'with public method' do
let(:is_public) { true }
let(:expected_glob_expression) { 'spec/unit/foo/expanded_name_spec.rb' }
it_should_behave_like this_example_group
end
context 'with nonpublic method' do
let(:is_public) { false }
context 'non initialize' do
let(:expected_glob_expression) { 'spec/unit/foo/*_spec.rb' }
it_should_behave_like this_example_group
end
context 'initialize' do
let(:method_name) { :initialize }
let(:expected_glob_expression) { 'spec/unit/foo/*_spec.rb spec/unit/foo/class_methods/new_spec.rb' }
it_should_behave_like this_example_group
end
end
end

View file

@ -0,0 +1,42 @@
require 'spec_helper'
describe Mutant::Strategy::Rspec::DM2::Lookup::Method::Singleton, '#spec_files' do
subject { object.spec_files }
let(:object) { described_class.new(mutation_subject) }
let(:mutation_subject) { mock('Subject', :method_name => method_name, :public? => is_public, :context_name => context_name) }
let(:context_name) { 'Foo' }
let(:method_name) { :bar }
let(:files) { 'Files'.freeze }
this_example_group = 'Mutant::Strategy::Rspec::DM2::Lookup::Method::Singleton#spec_files'
shared_examples_for this_example_group do
it_should_behave_like 'an idempotent method'
before do
if is_public
Mutant::Strategy::MethodExpansion.should_receive(:run).with(method_name).and_return(:expanded_name)
end
Dir.should_receive(:glob).with(expected_glob_expression).and_return(files)
end
it { should be(files) }
it { should be_frozen }
end
context 'with public method' do
let(:is_public) { true }
let(:expected_glob_expression) { 'spec/unit/foo/class_methods/expanded_name_spec.rb' }
it_should_behave_like this_example_group
end
context 'with nonpublic method' do
let(:is_public) { false }
let(:expected_glob_expression) { 'spec/unit/foo/class_methods/*_spec.rb' }
it_should_behave_like this_example_group
end
end

View file

@ -1,236 +0,0 @@
require 'spec_helper'
describe Mutant::Strategy::Rspec::ExampleLookup, '#spec_file' do
let(:object) { described_class.new(mutation) }
let(:mutation) { mock('Mutation', :subject => mutation_subject) }
let(:mutation_subject) { mock('Subject', :matcher => matcher) }
let(:matcher) { mock('Matcher', :method_name => method_name) }
subject { object.send(:spec_file) }
shared_examples_for 'Mutant::Strategy::Rspec::ExampleLookup#spec_file' do
it_should_behave_like 'an idempotent method'
it { should eql(expected_spec_file) }
it { should be_frozen }
end
context 'negation operator' do
let(:method_name) { :'!' }
let(:expected_spec_file) { 'negation_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with unary match method' do
let(:method_name) { :~@ }
let(:expected_spec_file) { 'unary_match_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with unary substraction method' do
let(:method_name) { :-@ }
let(:expected_spec_file) { 'unary_substraction_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with unary addition method' do
let(:method_name) { :+@ }
let(:expected_spec_file) { 'unary_addition_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with bitwise xor method' do
let(:method_name) { :^ }
let(:expected_spec_file) { 'bitwise_xor_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with bitwise or method' do
let(:method_name) { :| }
let(:expected_spec_file) { 'bitwise_or_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with bitwise and method' do
let(:method_name) { :& }
let(:expected_spec_file) { 'bitwise_and_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with spaceship method' do
let(:method_name) { :<=> }
let(:expected_spec_file) { 'spaceship_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with case equality operator method' do
let(:method_name) { :=== }
let(:expected_spec_file) { 'case_equality_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with modulo operator method' do
let(:method_name) { :% }
let(:expected_spec_file) { 'modulo_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with exponentation operator method' do
let(:method_name) { :** }
let(:expected_spec_file) { 'exponentation_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with substraction operator method' do
let(:method_name) { :- }
let(:expected_spec_file) { 'substraction_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with addition operator method' do
let(:method_name) { :+ }
let(:expected_spec_file) { 'addition_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with greater than or equal to operator method' do
let(:method_name) { :>= }
let(:expected_spec_file) { 'greater_than_or_equal_to_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with less than or equal to operator method' do
let(:method_name) { :<= }
let(:expected_spec_file) { 'less_than_or_equal_to_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with greater than operator method' do
let(:method_name) { :> }
let(:expected_spec_file) { 'greater_than_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with less than operator method' do
let(:method_name) { :< }
let(:expected_spec_file) { 'less_than_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with right shift operator method' do
let(:method_name) { :>> }
let(:expected_spec_file) { 'right_shift_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with left shift operator method' do
let(:method_name) { :<< }
let(:expected_spec_file) { 'left_shift_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with division operator method' do
let(:method_name) { :/ }
let(:expected_spec_file) { 'division_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with multiplication operator method' do
let(:method_name) { :* }
let(:expected_spec_file) { 'multiplication_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with nomatch operator method' do
let(:method_name) { :'!~' }
let(:expected_spec_file) { 'nomatch_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with match operator method' do
let(:method_name) { :=~ }
let(:expected_spec_file) { 'match_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with inequality operator method' do
let(:method_name) { :'!=' }
let(:expected_spec_file) { 'inequality_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with equality operator method' do
let(:method_name) { :== }
let(:expected_spec_file) { 'equality_operator_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with element reader method' do
let(:method_name) { :[] }
let(:expected_spec_file) { 'element_reader_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with element writer method' do
let(:method_name) { :[]= }
let(:expected_spec_file) { 'element_writer_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with writer method' do
let(:method_name) { :foo= }
let(:expected_spec_file) { 'foo_writer_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with bang method' do
let(:method_name) { :foo! }
let(:expected_spec_file) { 'foo_bang_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with predicate method' do
let(:method_name) { :foo? }
let(:expected_spec_file) { 'foo_predicate_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
context 'with regular method' do
let(:method_name) { :foo }
let(:expected_spec_file) { 'foo_spec.rb' }
it_should_behave_like 'Mutant::Strategy::Rspec::ExampleLookup#spec_file'
end
end