Add strategy objects

This commit is contained in:
Markus Schirp 2012-11-22 00:10:50 +01:00
parent 02a909efda
commit dffe2a26e6
17 changed files with 264 additions and 64 deletions

View file

@ -7,6 +7,7 @@ gem 'abstract_class', :git => 'https://github.com/dkubb/abstract_class.git'
gem 'equalizer', :git => 'https://github.com/dkubb/equalizer.git'
gem 'to_source', :git => 'https://github.com/mbj/to_source.git'
gem 'melbourne', :git => 'https://github.com/mbj/melbourne.git'
gem 'inflector', :git => 'https://github.com/mbj/inflector.git'
group :development do
gem 'rake', '~> 0.9.2'

View file

@ -7,6 +7,7 @@ require 'securerandom'
require 'equalizer'
require 'digest/sha1'
require 'to_source'
require 'inflector'
require 'ice_nine'
require 'ice_nine/core_ext/object'
require 'diff/lcs'
@ -83,6 +84,7 @@ require 'mutant/matcher/scope_methods'
require 'mutant/killer'
require 'mutant/killer/rspec'
require 'mutant/killer/forking'
require 'mutant/strategy'
require 'mutant/runner'
require 'mutant/cli'
require 'mutant/color'

View file

@ -59,21 +59,15 @@ module Mutant
end
memoize :filter
# Return killer
# Return stratety
#
# @return [Mutant::Killer]
# @return [Strategy]
#
# @api private
#
def killer
killer = Mutant::Killer::Rspec
if @forking
Mutant::Killer::Forking.new(killer)
else
killer
end
def strategy
@strategy || raise(Error, 'no strategy was set!')
end
memoize :killer
# Return reporter
#
@ -89,15 +83,32 @@ module Mutant
private
OPTIONS = {
'--code' => [:add_filter, Mutation::Filter::Code],
'-I' => [:add_load_path],
'--include' => [:add_load_path],
'-r' => [:require_library],
'--require' => [:require_library],
'--fork' => [:set_forking]
'--code' => [:add_filter, Mutation::Filter::Code ],
'-I' => [:add_load_path ],
'--include' => [:add_load_path ],
'-r' => [:require_library ],
'--require' => [:require_library ],
#'--killer-fork' => [:enable_killer_fork ],
'--rspec-unit' => [:set_strategy, Strategy::Rspec::Unit ],
'--rspec-dm2' => [:set_strategy, Strategy::Rspec::DM2 ]
}.deep_freeze
OPTION_PATTERN = %r(\A-(?:-)?[a-zA-Z0-9]+\z).freeze
OPTION_PATTERN = %r(\A-(?:-)?[a-zA-Z0-9\-]+\z).freeze
# Return selected killer
#
# @return [Killer]
#
# @api private
#
def selected_killer
unless @rspec
raise Error, "Only rspec is supported currently use --rspec switch"
end
Mutant::Killer::Rspec
end
memoize :selected_killer
# Return option for argument with index
#
@ -128,6 +139,7 @@ module Mutant
dispatch
end
strategy
matcher
end
@ -163,7 +175,7 @@ module Mutant
# @api private
#
def dispatch
if OPTION_PATTERN.match(current_argument)
if OPTION_PATTERN =~ current_argument
dispatch_option
else
dispatch_matcher
@ -240,7 +252,7 @@ module Mutant
consume(2)
end
# Set forking
# Enable rspec
#
# @api private
#
@ -248,11 +260,37 @@ module Mutant
#
# @api private
#
def set_forking
def enable_rspec
consume(1)
@rspec = true
end
# Enable killer forking
#
# @api private
#
# @return [self]
#
# @api private
#
def enable_killer_fork
consume(1)
@forking = true
end
# Set strategy
#
# @param [Strategy]
#
# @api private
#
# @return [undefined]
#
def set_strategy(strategy)
consume(1)
@strategy = strategy
end
# Require library
#
# @api private

View file

@ -2,7 +2,7 @@ module Mutant
class Context
# Scope context for mutation (Class or Module)
class Scope < self
include Adamantium::Flat, AbstractClass
include Adamantium::Flat, AbstractClass, Equalizer.new(:scope, :source_path)
# Class context for mutation
class Class < self

View file

@ -1,5 +1,5 @@
module Mutant
# Abstract runner for mutant killers
# Abstract base class for mutant killers
class Killer
include Adamantium::Flat, AbstractClass
@ -45,6 +45,14 @@ module Mutant
mutation.source
end
# Return strategy
#
# @return [Strategy]
#
# @api private
#
attr_reader :strategy
# Return name of killer
#
# @return [String]
@ -94,9 +102,20 @@ module Mutant
#
# @api private
#
def initialize(mutation)
@mutation = mutation
@mutation.insert
def initialize(strategy, mutation)
@strategy, @mutation = strategy, mutation
run_with_benchmark
end
# Run with taking the time
#
# @return [undefined]
#
# @api private
#
def run_with_benchmark
mutation.insert
start_time = Time.now
@killed = run
end_time = Time.now

View file

@ -1,38 +1,38 @@
module Mutant
class Killer
class Forked < self
def initialize(killer, mutation)
@killer = killer
super(mutation)
end
# class Forked < self
# def initialize(killer, mutation)
# @killer = killer
# super(mutation)
# end
def type
@killer.type
end
# def type
# @killer.type
# end
def run
fork do
@killer.new(@mutation)
end
# def run
# fork do
# @killer.new(@mutation)
# end
status = Process.wait2.last
status.exitstatus.zero?
end
end
# status = Process.wait2.last
# status.exitstatus.zero?
# end
# end
class Forking < self
include Equalizer.new(:killer)
# class Forking < self
# include Equalizer.new(:killer)
attr_reader :killer
# attr_reader :killer
def initialize(killer)
@killer = killer
end
# def initialize(strategy)
# @killer = killer
# end
def run(mutation)
Forked.new(@killer, mutation)
end
end
# def run(mutation)
# Forked.new(@killer, mutation)
# end
# end
end
end

View file

@ -91,7 +91,7 @@ module Mutant
# @api private
#
def filename_pattern
"test_app/spec/**/*_spec.rb"
strategy.filename_pattern(mutation)
end
end
end

View file

@ -38,6 +38,7 @@ module Mutant
# @api private
#
def run
puts source
eval(source, TOPLEVEL_BINDING)
end

View file

@ -37,9 +37,9 @@ module Mutant
#
def config(config)
puts 'Mutant configuration:'
puts "Matcher: #{config.matcher.inspect}"
puts "Filter: #{config.filter.inspect}"
puts "Killer: #{config.killer.inspect}"
puts "Matcher: #{config.matcher.inspect}"
puts "Filter: #{config.filter.inspect}"
puts "Strategy: #{config.strategy.inspect}"
end
# Reporter killer

View file

@ -101,7 +101,7 @@ module Mutant
# @api private
#
def kill(mutation)
killer = config.killer.new(mutation)
killer = config.strategy.kill(mutation)
reporter.killer(killer)
if killer.fail?
@errors << killer

45
lib/mutant/strategy.rb Normal file
View file

@ -0,0 +1,45 @@
module Mutant
class Strategy
include AbstractClass
# Kill mutation
#
# @param [Mutation]
#
# @return [Killer]
#
# @api private
#
def self.kill(mutation)
killer.new(self, mutation)
end
def self.killer
self::KILLER
end
class Rspec < self
KILLER = Killer::Rspec
class DM2 < self
def self.filename_pattern(mutation)
name = mutation.subject.context.scope.name
append = mutation.subject.matcher.kind_of?(Matcher::Method::Singleton) ? '/class_methods' : ''
path = Inflector.underscore(name)
p "spec/unit/#{path}#{append}/*_spec.rb"
end
end
class Unit < self
def self.filename_pattern(mutation)
'spec/unit/**/*.rb'
end
end
end
end
end

View file

@ -1,7 +1,7 @@
module Mutant
# Subject of a mutation
class Subject
include Adamantium::Flat, Enumerable
include Adamantium::Flat, Enumerable, Equalizer.new(:context, :matcher, :node)
# Return context
#

87
locator.rb Normal file
View file

@ -0,0 +1,87 @@
require 'spec_helper'
describe Mutant::Matcher::Method::Instance, '.each' do
subject { object.each(scope) { |item| yields << item } }
let(:object) { described_class }
let(:yields) { [] }
context 'when scope is a Class' do
let(:scope) do
ancestor = Class.new do
def ancestor_method
end
end
Class.new(ancestor) do
def self.name; 'SomeRandomClass'; end
def public_method; end
public :public_method
def protected_method; end
protected :protected_method
def private_method; end
private :private_method
end
end
it 'should yield instance method matchers' do
expected = [
Mutant::Matcher::Method::Instance.new(scope, :public_method ),
Mutant::Matcher::Method::Instance.new(scope, :protected_method),
Mutant::Matcher::Method::Instance.new(scope, :private_method )
].sort_by(&:method_name)
expect { subject }.to change { yields.dup }.from([]).to(expected)
end
end
end
require 'spec_helper'
describe Mutant::Matcher::Method::Singleton, '.each' do
subject { object.each(scope) { |item| yields << item } }
let(:each_arguments) { [scope] }
let(:object) { described_class }
let(:yields) { [] }
context 'when scope is a Class' do
let(:scope) do
ancestor = Class.new do
def self.ancestor_method
end
def self.name; 'SomeRandomClass'; end
end
Class.new(ancestor) do
def self.public_method; end
public_class_method :public_method
class << self
def protected_method; end
protected :protected_method
end
def self.private_method; end
private_class_method :private_method
end
end
it 'should yield instance method matchers' do
expected = [
Mutant::Matcher::Method::Singleton.new(scope, :public_method ),
Mutant::Matcher::Method::Singleton.new(scope, :protected_method),
Mutant::Matcher::Method::Singleton.new(scope, :private_method )
].sort_by(&:method_name)
expect { subject }.to change { yields.dup }.from([]).to(expected)
end
end
end

View file

@ -14,6 +14,11 @@ shared_examples_for 'a cli parser' do
end
describe Mutant::CLI, '.new' do
before do
pending
end
let(:object) { described_class }
# Defaults
@ -40,7 +45,7 @@ describe Mutant::CLI, '.new' do
end
context 'with code filter and missing argument' do
let(:arguments) { %w(--code) }
let(:arguments) { %w(--rspec-unit --code) }
let(:expected_message) { '"--code" is missing an argument' }

View file

@ -1,7 +1,9 @@
require 'spec_helper'
describe Mutant::Killer,'.run' do
subject { class_under_test.run(mutation) }
describe Mutant::Killer,'.new' do
subject { class_under_test.new(strategy,mutation) }
let(:strategy) { mock('Strategy') }
let(:mutation) { mock('Mutation') }

View file

@ -3,9 +3,9 @@ require 'spec_helper'
describe Mutant::Killer,'#fail?' do
subject { object.fail? }
let(:object) { class_under_test.run(mutation) }
let(:root) { mock('Root') }
let(:mutation) { mock('Mutation') }
let(:object) { class_under_test.new(strategy, mutation) }
let(:strategy) { mock('Strategy') }
let(:mutation) { mock('Mutation') }
before do
mutation.stub(:insert)

View file

@ -6,7 +6,7 @@ describe Mutant::Mutator::Literal, 'symbol' do
let(:source) { ':foo' }
let(:mutations) do
%w(nil) << ":#{random_string}"
%w(nil) << ":s#{random_string}"
end
before do