Improve rspec dm2 strategy
* Expand foo? to foo_predicate_spec.rb * Expand foo! to foo_bang_spec.rb * Execute all public method specs on mutation of private method * Warn on stderr when no spec file was found (better than nothing to be iproved later)
This commit is contained in:
parent
31d718c46d
commit
7be00708b6
10 changed files with 236 additions and 48 deletions
1
TODO
1
TODO
|
@ -8,3 +8,4 @@
|
|||
* Mutate options on Regexp literals
|
||||
* Support the numerous Rubinius::AST::SendWithArguments mutations.
|
||||
* Fix rubinius to allow setting @vcall_style variable in Rubinius::AST::Send nodes.
|
||||
* Aggregate warnings on missing spec files
|
||||
|
|
|
@ -91,6 +91,8 @@ require 'mutant/killer/static'
|
|||
require 'mutant/killer/rspec'
|
||||
require 'mutant/killer/forking'
|
||||
require 'mutant/strategy'
|
||||
require 'mutant/strategy/rspec'
|
||||
require 'mutant/strategy/rspec/example_lookup'
|
||||
require 'mutant/runner'
|
||||
require 'mutant/cli'
|
||||
require 'mutant/color'
|
||||
|
|
|
@ -52,17 +52,7 @@ module Mutant
|
|||
def command_line_arguments
|
||||
%W(
|
||||
--fail-fast
|
||||
) + Dir[filename_pattern]
|
||||
end
|
||||
|
||||
# Return filename pattern
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def filename_pattern
|
||||
strategy.filename_pattern(mutation)
|
||||
) + strategy.spec_files(mutation)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -59,6 +59,18 @@ module Mutant
|
|||
#
|
||||
attr_reader :method_name
|
||||
|
||||
# Test if method is public
|
||||
#
|
||||
# @return [true]
|
||||
# if method is public
|
||||
#
|
||||
# @retur [false]
|
||||
# otherwise
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
abstract_method :public?
|
||||
|
||||
private
|
||||
|
||||
# Initialize method filter
|
||||
|
@ -77,7 +89,7 @@ module Mutant
|
|||
|
||||
# Return method
|
||||
#
|
||||
# @return [UnboundMethod]
|
||||
# @return [UnboundMethod, Method]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
|
|
|
@ -15,6 +15,20 @@ module Mutant
|
|||
"#{scope.name}##{method_name}"
|
||||
end
|
||||
|
||||
# Test if method is public
|
||||
#
|
||||
# @return [true]
|
||||
# if method is public
|
||||
#
|
||||
# @return [false]
|
||||
# otherwise
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def public?
|
||||
scope.public_method_defined?(method_name)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Check if node is matched
|
||||
|
|
|
@ -13,6 +13,22 @@ module Mutant
|
|||
def identification
|
||||
"#{scope.name}.#{method_name}"
|
||||
end
|
||||
memoize :identification
|
||||
|
||||
# Test if method is public
|
||||
#
|
||||
# @return [true]
|
||||
# if method is public
|
||||
#
|
||||
# @return [false]
|
||||
# otherwise
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def public?
|
||||
scope.singleton_class.public_method_defined?(method_name)
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
|
|
|
@ -14,54 +14,29 @@ module Mutant
|
|||
killer.new(self, mutation)
|
||||
end
|
||||
|
||||
# Return killer
|
||||
#
|
||||
# @return [Class:Killer]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def self.killer
|
||||
self::KILLER
|
||||
end
|
||||
|
||||
# Static strategies
|
||||
class Static < self
|
||||
|
||||
# Always fail to kill strategy
|
||||
class Fail < self
|
||||
KILLER = Killer::Static::Fail
|
||||
end
|
||||
|
||||
# Always succeed to kill strategy
|
||||
class Success < self
|
||||
KILLER = Killer::Static::Success
|
||||
end
|
||||
end
|
||||
|
||||
class Rspec < self
|
||||
|
||||
KILLER = Killer::Forking.new(Killer::Rspec)
|
||||
|
||||
class DM2 < self
|
||||
|
||||
def self.filename_pattern(mutation)
|
||||
subject = mutation.subject
|
||||
name = subject.context.scope.name
|
||||
|
||||
matcher = subject.matcher
|
||||
|
||||
append = matcher.kind_of?(Matcher::Method::Singleton) ? '/class_methods' : ''
|
||||
|
||||
path = Inflector.underscore(name)
|
||||
|
||||
base = "spec/unit/#{path}#{append}"
|
||||
|
||||
"#{base}/#{matcher.method_name}_spec.rb"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Unit < self
|
||||
def self.filename_pattern(mutation)
|
||||
'spec/unit/**/*_spec.rb'
|
||||
end
|
||||
end
|
||||
|
||||
class Full < self
|
||||
def self.filename_pattern(mutation)
|
||||
'spec/**/*_spec.rb'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
59
lib/mutant/strategy/rspec.rb
Normal file
59
lib/mutant/strategy/rspec.rb
Normal file
|
@ -0,0 +1,59 @@
|
|||
module Mutant
|
||||
class Strategy
|
||||
|
||||
# Rspec strategy base class
|
||||
class Rspec < self
|
||||
|
||||
KILLER = Killer::Forking.new(Killer::Rspec)
|
||||
|
||||
# DM2-style strategy
|
||||
class DM2 < self
|
||||
|
||||
# Return filename pattern
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def self.spec_files(mutation)
|
||||
ExampleLookup.run(mutation)
|
||||
end
|
||||
end
|
||||
|
||||
# Run all unit specs per mutation
|
||||
class Unit < self
|
||||
|
||||
# Return file name pattern for mutation
|
||||
#
|
||||
# @return [Mutation]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def self.spec_files(mutation)
|
||||
Dir['spec/unit/**/*_spec.rb']
|
||||
end
|
||||
end
|
||||
|
||||
# Run all integration specs per mutation
|
||||
class Unit < self
|
||||
|
||||
# Return file name pattern for mutation
|
||||
#
|
||||
# @return [Mutation]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def self.spec_files(mutation)
|
||||
Dir['spec/integration/**/*_spec.rb']
|
||||
end
|
||||
end
|
||||
|
||||
# Run all specs per mutation
|
||||
class Full < self
|
||||
def self.spec_files(mutation)
|
||||
Dir['spec/**/*_spec.rb']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
119
lib/mutant/strategy/rspec/example_lookup.rb
Normal file
119
lib/mutant/strategy/rspec/example_lookup.rb
Normal file
|
@ -0,0 +1,119 @@
|
|||
module Mutant
|
||||
class Strategy
|
||||
class Rspec
|
||||
|
||||
# Example lookup for rspec
|
||||
class ExampleLookup
|
||||
include Adamantium::Flat, Equalizer.new(:mutation)
|
||||
|
||||
# Perform example lookup
|
||||
#
|
||||
# @param [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]
|
||||
|
||||
$stderr.puts("Spec file(s): #{expression.inspect} not found for #{mutation.inspect}")
|
||||
|
||||
files
|
||||
end
|
||||
memoize :spec_files
|
||||
|
||||
private
|
||||
|
||||
# Return method matcher
|
||||
#
|
||||
# @return [Matcher]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def matcher
|
||||
mutation.subject.matcher
|
||||
end
|
||||
|
||||
# Return spec file
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def spec_file
|
||||
matcher.method_name.to_s.
|
||||
gsub(/\?\z/, 'predicate').
|
||||
gsub(/!\z/, 'bang') + '_spec.rb'
|
||||
end
|
||||
memoize :spec_file
|
||||
|
||||
# Return glob expression
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def glob_expression
|
||||
if mutation.subject.matcher.public?
|
||||
"#{base_path}/#{spec_file}"
|
||||
else
|
||||
"#{base_path}/*_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
|
|
@ -4,7 +4,7 @@ describe Mutant::Killer::Rspec, '.new' do
|
|||
|
||||
subject { object.new(strategy, mutation) }
|
||||
|
||||
let(:strategy) { mock('Strategy', :filename_pattern => 'foo') }
|
||||
let(:strategy) { mock('Strategy', :spec_files => ['foo']) }
|
||||
let(:context) { mock('Context') }
|
||||
let(:mutation) { mock('Mutation') }
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue