Merge branch 'master' into flexible-rspec

Conflicts:
	lib/mutant/cli.rb
	lib/mutant/constants.rb
	lib/mutant/strategy/rspec/dm2/lookup.rb
	spec/integration/mutant/rspec_killer_spec.rb
	spec/unit/mutant/cli/class_methods/new_spec.rb
	spec/unit/mutant/strategy/rspec/dm2/lookup/method/instance/spec_files_spec.rb
	spec/unit/mutant/strategy/rspec/dm2/lookup/method/singleton/spec_files_spec.rb
This commit is contained in:
Markus Schirp 2013-07-29 01:22:37 +02:00
commit cb60db3078
83 changed files with 576 additions and 337 deletions

43
.gitignore vendored
View file

@ -1,7 +1,38 @@
/.rbx
/Gemfile.lock
/tmp
/coverage
/test_app/.rbx
/.bundle
## MAC OS
.DS_Store
## TEXTMATE
*.tmproj
tmtags
## EMACS
*~
\#*
.\#*
## VIM
*.sw[op]
## Rubinius
*.rbc
.rbx
## PROJECT::GENERAL
*.gem
coverage
profiling
turbulence
rdoc
pkg
tmp
doc
log
.yardoc
measurements
## BUNDLER
.bundle
Gemfile.lock
## PROJECT::SPECIFIC
/vendor

1
.ruby-gemset Normal file
View file

@ -0,0 +1 @@
mutant

11
Gemfile
View file

@ -1,8 +1,13 @@
# encoding: utf-8
source 'https://rubygems.org'
gemspec
gem 'mutant', path: '.'
gem 'mutant', path: '.'
gem 'devtools', git: 'https://github.com/rom-rb/devtools.git'
eval(File.read(File.join(File.dirname(__FILE__), 'Gemfile.devtools')))
group :development, :test do
gem 'devtools', git: 'https://github.com/rom-rb/devtools.git'
end
eval_gemfile File.join(File.dirname(__FILE__), 'Gemfile.devtools')

View file

@ -3,7 +3,7 @@
group :development do
gem 'rake', '~> 10.1.0'
gem 'rspec', '~> 2.14.1'
gem 'yard', '~> 0.8.6.2'
gem 'yard', '~> 0.8.7'
end
group :yard do
@ -31,7 +31,7 @@ end
group :metrics do
gem 'coveralls', '~> 0.6.7'
gem 'flay', '~> 2.3.1'
gem 'flay', '~> 2.4.0'
gem 'flog', '~> 4.1.1'
gem 'reek', '~> 1.3.1', git: 'https://github.com/troessner/reek.git'
gem 'rubocop', '~> 0.10.0', git: 'https://github.com/bbatsov/rubocop.git'
@ -41,10 +41,6 @@ group :metrics do
platforms :ruby_19, :ruby_20 do
gem 'yard-spellcheck', '~> 0.1.5'
end
platforms :rbx do
gem 'pelusa', '~> 0.2.2'
end
end
group :benchmarks do

View file

@ -2,17 +2,31 @@
guard :bundler do
watch('Gemfile')
watch('Gemfile.lock')
watch(%w{.+.gemspec\z})
end
guard :rspec, :cli => '--fail-fast', :all_on_start => false, :all_after_pass => false do
# run all specs if the spec_helper or supporting files files are modified
watch('spec/spec_helper.rb') { 'spec/unit' }
watch(%r{\Aspec/(?:lib|support|shared)/.+\.rb\z}) { 'spec/unit' }
guard :rspec, cli: File.read('.rspec').split.push('--fail-fast').join(' '), keep_failed: false do
# Run all specs if configuration is modified
watch('.rspec') { 'spec' }
watch('Guardfile') { 'spec' }
watch('Gemfile.lock') { 'spec' }
watch('spec/spec_helper.rb') { 'spec' }
# run unit specs if associated lib code is modified
watch(%r{\Alib/(.+)\.rb\z}) { |m| Dir["spec/unit/#{m[1]}"] }
watch("lib/#{File.basename(File.expand_path('../', __FILE__))}.rb") { 'spec/unit' }
# Run all specs if supporting files files are modified
watch(%r{\Aspec/(?:fixtures|lib|support|shared)/.+\.rb\z}) { 'spec' }
# run a spec if it is modified
# Run unit specs if associated lib code is modified
watch(%r{\Alib/(.+)\.rb\z}) { |m| Dir["spec/unit/#{m[1]}*"] }
watch(%r{\Alib/(.+)/support/(.+)\.rb\z}) { |m| Dir["spec/unit/#{m[1]}/#{m[2]}*"] }
watch("lib/#{File.basename(File.expand_path('../', __FILE__))}.rb") { 'spec' }
# Run a spec if it is modified
watch(%r{\Aspec/(?:unit|integration)/.+_spec\.rb\z})
end
guard :rubocop, cli: %w[--config config/rubocop.yml] do
watch(%r{.+\.(?:rb|rake)\z})
watch(%r{\Aconfig/rubocop\.yml\z}) { |m| File.dirname(m[0]) }
watch(%r{(?:.+/)?\.rubocop\.yml\z}) { |m| File.dirname(m[0]) }
end

View file

@ -32,6 +32,7 @@ The following projects adopted mutant, and aim 100% mutation coverage:
* [virtus](https://github.com/solnic/virtus)
* [quacky](https://github.com/benmoss/quacky)
* [substation](https://github.com/snusnu/substation)
* [large_binomials](https://github.com/filipvanlaenen/large_binomials)
* various small/minor stuff under https://github.com/mbj
Feel free to ping me to add your project to the list!

View file

@ -1,3 +1,3 @@
---
threshold: 16
total_score: 737
total_score: 773

View file

@ -1,2 +1,2 @@
---
threshold: 19.8
threshold: 20.5

View file

@ -24,9 +24,9 @@ DuplicateMethodCall:
FeatureEnvy:
enabled: true
exclude:
- Mutant::Matcher::Method::Singleton#receiver?
- Mutant::Matcher::Method::Instance#match?
- Mutant::CLI#parse
- Mutant::Matcher::Method::Instance#match?
- Mutant::Matcher::Method::Singleton#receiver?
- Mutant::Mutation::Evil#success?
- Mutant::Mutation::Neutral#success?
- Mutant::Reporter::CLI#subject_results
@ -46,9 +46,9 @@ NestedIterators:
enabled: true
exclude:
- Mutant#self.singleton_subclass_instance
- Mutant::CLI#parse
- Mutant::Mutator::Util::Array::Element#dispatch
- Mutant::Reporter::CLI::Printer::Config::Runner#generic_stats
- Mutant::CLI#parse
max_allowed_nesting: 1
ignore_iterators: []
NilCheck:
@ -76,12 +76,13 @@ TooManyMethods:
TooManyStatements:
enabled: true
exclude:
- Mutant#self.singleton_subclass_instance
- Mutant::CLI#parse
- Mutant::Killer::Rspec#run
- Mutant::Reporter::CLI#colorized_diff
- Mutant::Reporter::CLI::Printer::Config::Runner#run
- Mutant#self.singleton_subclass_instance
- Mutant::Zombifier::File#self.find_uncached
- Mutant::Runner#dispatch
- Mutant::Zombifier::File#self.find_uncached
max_statements: 6
UncommunicativeMethodName:
enabled: true
@ -122,8 +123,8 @@ UnusedParameters:
UtilityFunction:
enabled: true
exclude:
- Mutant::NodeHelpers#s
- Mutant::CLI#reporter
- Mutant::Mutation::Evil#success?
- Mutant::Mutation::Neutral#success?
- Mutant::NodeHelpers#s
max_helper_calls: 0

View file

@ -33,9 +33,19 @@ CollectionMethods:
AccessControl:
Enabled: false
MethodLength:
CountComments: false
Max: 17 # TODO: Bring down to 10
RegexpLiteral: # I do not agree %r(\A) is more readable than /\A/
Enabled: false
Eval:
Enabled: false # Mutant must use Kernel#eval to inject mutated source
# Limit line length
LineLength:
Max: 79
Max: 124 # TODO: lower to 79
# Disable documentation checking until a class needs to be documented once
Documentation:
@ -45,6 +55,18 @@ Documentation:
IfUnlessModifier:
Enabled: false
# Mutant needs to define methods like def bar; end in specs
Semicolon:
Enabled: false
# Mutant needs to define multiple methods on same line in specs
EmptyLineBetweenDefs:
Enabled: false
# Mutant needs to define singleton methods like Foo.bar in specs
ClassMethods:
Enabled: false
# Allow case equality operator (in limited use within the specs)
CaseEquality:
Enabled: false

View file

@ -57,12 +57,12 @@ require 'mutant/mutator/node/literal/nil'
require 'mutant/mutator/node/argument'
require 'mutant/mutator/node/arguments'
require 'mutant/mutator/node/begin'
require 'mutant/mutator/node/cbase'
require 'mutant/mutator/node/connective/binary'
require 'mutant/mutator/node/const'
require 'mutant/mutator/node/named_value/access'
require 'mutant/mutator/node/named_value/constant_assignment'
require 'mutant/mutator/node/named_value/variable_assignment'
require 'mutant/mutator/node/noop'
require 'mutant/mutator/node/while'
require 'mutant/mutator/node/super'
require 'mutant/mutator/node/zsuper'

View file

@ -38,7 +38,7 @@ module Mutant
#
# @api private
#
def initialize(arguments=[])
def initialize(arguments = [])
@filters, @matchers = [], []
@cache = Mutant::Cache.new
@ -148,7 +148,7 @@ module Mutant
#
# @api private
#
def add_filter(klass,filter)
def add_filter(klass, filter)
@filters << klass.new(filter)
end
@ -197,15 +197,15 @@ module Mutant
# @api private
#
def parse(arguments)
opts = OptionParser.new do |opts|
opts.banner = 'usage: mutant STRATEGY [options] MATCHERS ...'
opts.separator ''
opts.separator 'Strategies:'
opts = OptionParser.new do |builder|
builder.banner = 'usage: mutant STRATEGY [options] MATCHERS ...'
builder.separator ''
builder.separator 'Strategies:'
opts.on('--zombie', 'Run mutant zombified')
builder.on('--zombie', 'Run mutant zombified')
add_strategies(opts)
add_options(opts)
add_strategies(builder)
add_options(builder)
end
matchers =
@ -269,7 +269,7 @@ module Mutant
add_filter Mutation::Filter::Code, filter
end.on('--fail-fast', 'Fail fast') do
set_fail_fast
end.on('-d','--debug', 'Enable debugging output') do
end.on('-d', '--debug', 'Enable debugging output') do
set_debug
end.on_tail('-h', '--help', 'Show this message') do
puts opts

View file

@ -8,11 +8,13 @@ module Mutant
SCOPE_NAME_PATTERN = /[A-Za-z][A-Za-z_0-9]*/.freeze
OPERATOR_PATTERN = Regexp.union(*OPERATOR_METHODS.map(&:to_s)).freeze
METHOD_NAME_PATTERN = /([_A-Za-z][A-Za-z0-9_]*[!?=]?|#{OPERATOR_PATTERN})/.freeze
SCOPE_PATTERN = /(?:::)?#{SCOPE_NAME_PATTERN}(?:::#{SCOPE_NAME_PATTERN})*/.freeze
METHOD_NAME_PATTERN =
/([_A-Za-z][A-Za-z0-9_]*[!?=]?|#{OPERATOR_PATTERN})/.freeze
SCOPE_PATTERN =
/(?:::)?#{SCOPE_NAME_PATTERN}(?:::#{SCOPE_NAME_PATTERN})*/.freeze
CBASE_PATTERN = /\A::/.freeze
SCOPE_OPERATOR = '::'.freeze
SINGLETON_PATTERN = %r(\A(#{SCOPE_PATTERN})\z).freeze
SINGLETON_PATTERN = /\A(#{SCOPE_PATTERN})\z/.freeze
REGISTRY = []
@ -36,9 +38,12 @@ module Mutant
# @api private
#
def self.constant_lookup(location)
location.gsub(CBASE_PATTERN, EMPTY_STRING).split(SCOPE_OPERATOR).inject(Object) do |parent, name|
parent.const_get(name)
end
location
.gsub(CBASE_PATTERN, EMPTY_STRING)
.split(SCOPE_OPERATOR)
.reduce(Object) do |parent, name|
parent.const_get(name)
end
end
# Return matchers for input

View file

@ -10,7 +10,8 @@ module Mutant
'#' => Matcher::Methods::Instance
}.freeze
REGEXP = %r(\A(#{SCOPE_PATTERN})([.#])(#{METHOD_NAME_PATTERN}\z)).freeze
REGEXP =
%r(\A(#{SCOPE_PATTERN})([.#])(#{METHOD_NAME_PATTERN}\z)).freeze
# Positions of captured regexp groups
SCOPE_NAME_POSITION = 1

View file

@ -29,14 +29,14 @@ module Mutant
# Recursive namespace classifier
class Recursive < self
REGEXP = %r(\A(#{SCOPE_PATTERN})\*\z).freeze
REGEXP = /\A(#{SCOPE_PATTERN})\*\z/.freeze
MATCHER = Matcher::Namespace
register
end # Recursive
# Recursive namespace classifier
class Flat < self
REGEXP = %r(\A(#{SCOPE_PATTERN})\z).freeze
REGEXP = /\A(#{SCOPE_PATTERN})\z/.freeze
MATCHER = Matcher::Scope
register
end # Flat

View file

@ -5,7 +5,7 @@ module Mutant
# Scope classifier
class Scope < self
REGEXP = %r(\A(#{SCOPE_PATTERN})\z).freeze
REGEXP = /\A(#{SCOPE_PATTERN})\z/.freeze
private

View file

@ -27,7 +27,8 @@ module Mutant
"\e[#{@code}m#{text}\e[0m"
end
NONE = Class.new(self) do
Mutant.singleton_subclass_instance('NONE', self) do
# Format null color
#
# @param [String] text
@ -49,13 +50,14 @@ module Mutant
#
# @api private
#
def initialize(*)
def initialize
end
end.new.freeze
end
RED = Color.new(31)
GREEN = Color.new(32)
BLUE = Color.new(34)
end # Color
end # Mutant

View file

@ -17,7 +17,7 @@ module Mutant
].to_set.freeze
# Set of node types that are not valid when emitted standalone
NOT_STANDALONE = [ :splat, :block_pass ].to_set.freeze
NOT_STANDALONE = [:splat, :restarg, :block_pass].to_set.freeze
OPERATOR_EXPANSIONS = {
:<=> => :spaceship_operator,
@ -49,7 +49,7 @@ module Mutant
:'!' => :negation_operator
}.freeze
INDEX_OPERATORS = [ :[], :[]= ].freeze
INDEX_OPERATORS = [:[], :[]=].freeze
UNARY_METHOD_OPERATORS = [
:~@, :+@, :-@, :'!'
@ -77,7 +77,7 @@ module Mutant
:xstr, :def, :defs, :case, :when, :ivar, :lvar, :cvar, :gvar,
:back_ref, :const, :nth_ref, :class, :sclass, :yield,
:match_with_lvasgn, :match_current_line, :irange, :erange,
:or_asgn, :kwbegin, :and_asgn
:or_asgn, :kwbegin, :and_asgn, :while
].to_set.freeze
end # Mutant

View file

@ -12,7 +12,7 @@ module Mutant
# @api private
#
def root(node)
nesting.reverse.inject(node) do |current, scope|
nesting.reverse.reduce(node) do |current, scope|
self.class.wrap(scope, current)
end
end

View file

@ -19,10 +19,16 @@ module Mutant
when 0
nil
when 1
output = Diff::LCS::Hunk.new(old, new, diffs.first, max_length, 0).diff(:unified)
output =
Diff::LCS::Hunk.new(
old, new, diffs.first, max_length, 0
).diff(:unified)
output << "\n"
else
$stderr.puts 'Mutation resulted in more than one diff, should not happen! PLS report a bug!'
$stderr.puts(
'Mutation resulted in more than one diff, should not happen! ' +
'PLS report a bug!'
)
nil
end
end

View file

@ -1,7 +1,8 @@
module Mutant
# Abstract base class for mutant killers
class Killer
include Adamantium::Flat, AbstractType, Equalizer.new(:strategy, :mutation, :killed?)
include Adamantium::Flat, AbstractType
include Equalizer.new(:strategy, :mutation, :killed?)
# Return strategy
#

View file

@ -18,11 +18,13 @@ module Mutant
def run
mutation.insert
# TODO: replace with real streams from configuration
# Note: we assume the only interesting output from a failed rspec run is stderr.
require 'stringio'
# Note: We assume interesting output from a failed rspec run is stderr.
rspec_err = StringIO.new
killed = !::RSpec::Core::Runner.run(command_line_arguments, nil, rspec_err).zero?
exit_code = ::RSpec::Core::Runner.run(cli_arguments, nil, rspec_err)
killed = !exit_code.zero?
if killed and mutation.should_survive?
rspec_err.rewind
@ -41,7 +43,7 @@ module Mutant
#
# @api private
#
def command_line_arguments
def cli_arguments
%W(
--fail-fast
) + strategy.spec_files(mutation.subject)

View file

@ -40,7 +40,12 @@ module Mutant
# @api private
#
def run
eval(source, TOPLEVEL_BINDING, @subject.source_path.to_s, @subject.source_line)
eval(
source,
TOPLEVEL_BINDING,
@subject.source_path.to_s,
@subject.source_line
)
end
# Return source

View file

@ -6,7 +6,8 @@ module Mutant
# Methods within rbx kernel directory are precompiled and their source
# cannot be accessed via reading source location
BLACKLIST = %r(\A#{Regexp.union('kernel/', '(eval)')}).freeze
SKIP_METHODS = %w[kernel/ (eval)].freeze
BLACKLIST = /\A#{Regexp.union(*SKIP_METHODS)}/.freeze
# Enumerate matches
#
@ -25,7 +26,12 @@ module Mutant
if subject
yield subject
else
$stderr.puts "Cannot find definition of: #{identification} in #{source_location.join(':')}"
message = sprintf(
'Cannot find definition of: %s in %s',
identification,
source_location.join(':')
)
$stderr.puts(message)
end
end
@ -47,7 +53,11 @@ module Mutant
def skip?
location = source_location
if location.nil? or BLACKLIST.match(location.first)
$stderr.puts "#{method.inspect} does not have valid source location so mutant is unable to emit matcher"
message = sprintf(
'%s does not have valid source location unable to emit matcher',
method.inspect
)
$stderr.puts(message)
return true
end

View file

@ -16,7 +16,8 @@ module Mutant
# @api private
#
def self.build(cache, scope, method)
if scope.ancestors.include?(::Adamantium) and scope.memoized?(method.name)
name = method.name
if scope.ancestors.include?(::Adamantium) and scope.memoized?(name)
return Memoized.new(cache, scope, method)
end
super

View file

@ -92,7 +92,11 @@ module Mutant
when :const
receiver_name?(receiver)
else
$stderr.puts "Can only match self or const, got #{receiver.type}, unable to match receiver of defs node"
message = sprintf(
'Can only match :defs on :self or :const got %s unable to match',
receiver.type.inspect
)
$stderr.puts(message)
false
end
end

View file

@ -1,6 +1,6 @@
module Mutant
class Matcher
# Abstract base class for matcher that returns method subjects extracted from scope
# Abstract base class for matcher that returns method subjects from scope
class Methods < self
include AbstractType, Concord::Public.new(:cache, :scope)

View file

@ -34,7 +34,7 @@ module Mutant
# @api private
#
def pattern
%r(\A#{Regexp.escape(namespace.name)}(?:::)?)
/\A#{Regexp.escape(namespace.name)}(?:::)?/
end
memoize :pattern
@ -62,7 +62,7 @@ module Mutant
#
def emit_scope(scope)
name = scope.name
# FIXME Fix nokogiri to return a string here
# FIXME: Fix nokogiri to return a string here
return unless name.kind_of?(String)
if pattern =~ name
yield scope

View file

@ -1,7 +1,8 @@
module Mutant
# Represent a mutated node with its subject
class Mutation
include AbstractType, Adamantium::Flat, Concord::Public.new(:subject, :node)
include AbstractType, Adamantium::Flat
include Concord::Public.new(:subject, :node)
# Return mutated root node
#
@ -42,10 +43,9 @@ module Mutant
# Insert mutated node
#
# FIXME:
# Cache subject visibility in a better way! Ideally dont mutate it implicitly.
# Also subject.public? should NOT be a public interface it is a detail of method
# mutations.
# FIXME: Cache subject visibility in a better way! Ideally dont mutate it
# implicitly. Also subject.public? should NOT be a public interface it
# is a detail of method mutations.
#
# @return [self]
#

View file

@ -30,9 +30,9 @@ module Mutant
killer.killed?
end
# Indicate if a killer should treat a kill as problematic.
# Indicate if a killer should treat a kill as problematic
#
# @return [false] Killing evil mutants is not problematic.
# @return [false] Killing evil mutants is not problematic
#
# @api private
#

View file

@ -21,7 +21,7 @@ module Mutant
mutation.code.eql?(code)
end
PATTERN = %r(\Acode:([a-f0-9]{1,6})\z).freeze
PATTERN = /\Acode:([a-f0-9]{1,6})\z/.freeze
# Test if class handles string
#

View file

@ -10,12 +10,12 @@ module Mutant
SYMBOL = 'noop'
# Indicate if a killer should treat a kill as problematic.
# Indicate if a killer should treat a kill as problematic
#
# @return [false] Killing noop mutants is a serious problem. Failures
# in noop may indicate a broken test suite, but they can also be an
# indication mutant has altered the runtime environment in a subtle
# way and tickled an odd bug.
# way and tickled an odd bug
#
# @api private
#
@ -52,9 +52,9 @@ module Mutant
!killer.killed?
end
# Indicate if a killer should treat a kill as problematic.
# Indicate if a killer should treat a kill as problematic
#
# @return [true] Neutral mutants must die.
# @return [true] Neutral mutants must die
#
# @api private
#

View file

@ -12,7 +12,7 @@ module Mutant
#
# @api private
#
def self.each(node, parent=nil, &block)
def self.each(node, parent = nil, &block)
return to_enum(__method__, node, parent) unless block_given?
Registry.lookup(node).new(node, parent, block)
@ -135,17 +135,13 @@ module Mutant
# Call block until it generates a mutation
#
# The primary use of this method is to give the random generated object
# a nice interface for retring generation when generation accidentally generated the
# input
#
# @yield
# Execute block until object is generated where new?(object) returns true
#
# @return [self]
#
# @raise [RuntimeError]
# raises RuntimeError in case no new ast node can be generated after MAX_TRIES.
# raises RuntimeError when no new node can be generated after MAX_TRIES.
#
# @api private
#

View file

@ -8,10 +8,9 @@ module Mutant
# These nodes still need a dedicated mutator,
# your contribution is that close!
handle(
:defined,
:next, :break, :match, :ensure,
:next, :break, :ensure,
:dstr, :dsym, :yield, :rescue, :redo, :defined?,
:blockarg, :block_pass, :op_asgn, :and_asgn,
:blockarg, :op_asgn, :and_asgn,
:regopt, :restarg, :resbody, :retry, :arg_expr,
:kwrestarg, :kwoptarg, :kwarg, :undef, :module, :empty,
:alias, :for, :xstr, :back_ref, :nth_ref, :class,

View file

@ -2,10 +2,10 @@ module Mutant
class Mutator
class Node
# Mutation emitter to handle cbase nodes
class Cbase < self
# Mutation emitter to handle noop nodes
class Noop < self
handle(:cbase)
handle(:block_pass, :cbase)
private
@ -16,10 +16,10 @@ module Mutant
# @api private
#
def dispatch
# noop, for now
# noop
end
end # Cbase
end # Noop
end # Node
end # Mutator
end # Mutant

View file

@ -2,16 +2,12 @@ module Mutant
class Mutator
# Registry for mutators
module Registry
# Return registry state
#
# @return [Hash]
#
# @api private
#
def self.registry
@registry ||= {}
end
private_class_method :registry
# Raised when the type is an invalid type
InvalidTypeError = Class.new(TypeError)
# Raised when the type is a duplicate
DuplicateTypeError = Class.new(ArgumentError)
# Register mutator class for AST node class
#
@ -23,7 +19,8 @@ module Mutant
# @return [self]
#
def self.register(type, mutator_class)
raise "duplicate type registration: #{type}" if registry.key?(type)
assert_valid_type(type)
assert_unique_type(type)
registry[type] = mutator_class
self
end
@ -46,6 +43,49 @@ module Mutant
end
end
# Return registry state
#
# @return [Hash]
#
# @api private
#
def self.registry
@registry ||= {}
end
private_class_method :registry
# Assert the node type is valid
#
# @return [undefined]
#
# @raise [InvalidTypeError]
# raised when the node type is invalid
#
# @api private
#
def self.assert_valid_type(type)
unless NODE_TYPES.include?(type) || type.kind_of?(Class)
raise InvalidTypeError, "invalid type registration: #{type}"
end
end
private_class_method :assert_valid_type
# Assert the node type is unique and not already registered
#
# @return [undefined]
#
# @raise [DuplcateTypeError]
# raised when the node type is a duplicate
#
# @api private
#
def self.assert_unique_type(type)
if registry.key?(type)
raise DuplicateTypeError, "duplicate type registration: #{type}"
end
end
private_class_method :assert_unique_type
end # Registry
end # Mutator
end # Mutant

View file

@ -15,10 +15,14 @@ module Mutant
end
module_function :s
NAN = s(:begin, s(:send, s(:float, 0.0), :/, s(:args, s(:float, 0.0))))
NEGATIVE_INFINITY = s(:begin, s(:send, s(:float, -1.0), :/, s(:args, s(:float, 0.0))))
INFINITY = s(:begin, s(:send, s(:float, 1.0), :/, s(:args, s(:float, 0.0))))
NEW_OBJECT = s(:send, s(:const, s(:cbase), :Object), :new)
NAN =
s(:begin, s(:send, s(:float, 0.0), :/, s(:args, s(:float, 0.0))))
INFINITY =
s(:begin, s(:send, s(:float, 1.0), :/, s(:args, s(:float, 0.0))))
NEW_OBJECT =
s(:send, s(:const, s(:cbase), :Object), :new)
NEGATIVE_INFINITY =
s(:begin, s(:send, s(:float, -1.0), :/, s(:args, s(:float, 0.0))))
RAISE = s(:send, nil, :raise)

View file

@ -130,8 +130,9 @@ module Mutant
def generic_stats
object.subjects.each_with_object(Hash.new(0)) do |runner, stats|
Walker.run(runner.subject.node) do |node|
next unless Mutator::Registry.lookup(node) == Mutator::Node::Generic
stats[node.type] += 1
if Mutator::Registry.lookup(node) == Mutator::Node::Generic
stats[node.type] += 1
end
end
end
end

View file

@ -54,11 +54,12 @@ module Mutant
# Reporter for noop mutations
class Noop < self
MESSAGE =
"Parsed subject AST:\n" \
"%s\n" \
"Unparsed source:\n" \
"%s\n"
MESSAGE = [
'Parsed subject AST:',
'%s',
'Unparsed source:',
'%s',
].join("\n")
private
@ -69,7 +70,11 @@ module Mutant
# @api private
#
def details
sprintf(MESSAGE, mutation.subject.node.inspect, mutation.original_source)
sprintf(
MESSAGE,
mutation.subject.node.inspect,
mutation.original_source
)
end
end # Noop

View file

@ -59,6 +59,8 @@ module Mutant
object.subject
end
FORMAT = '(%02d/%02d) %3d%% - %0.02fs'.freeze
# Print stats
#
# @return [undefned
@ -66,7 +68,7 @@ module Mutant
# @api private
#
def print_stats
status('(%02d/%02d) %3d%% - %0.02fs', amount_kills, amount_mutations, coverage, time)
status(FORMAT, amount_kills, amount_mutations, coverage, time)
end
# Print progress bar finish

View file

@ -12,13 +12,13 @@ module Mutant
#
def self.singleton_subclass_instance(name, superclass, &block)
klass = Class.new(superclass) do
def inspect; self.class.name; end
def inspect
self.class.name
end
define_singleton_method(:name) do
"#{superclass.name}::#{name}".freeze
end
end
klass.class_eval(&block)
superclass.const_set(name, klass.new)

View file

@ -1,7 +1,8 @@
module Mutant
# Subject of a mutation
class Subject
include AbstractType, Adamantium::Flat, Enumerable, Concord::Public.new(:context, :node)
include AbstractType, Adamantium::Flat, Enumerable
include Concord::Public.new(:context, :node)
# Return mutations
#

View file

@ -4,7 +4,7 @@ module Mutant
# Excluded from zombification, reasons
#
# * Relies dynamic require, zombifier does not know how to recurse here (racc)
# * Relies dynamic require, zombifier does not know how to recurse (racc)
# * Unparser bug (optparse)
# * Toplevel reference/cbase nodes in code (rspec)
# * Creates useless toplevel modules that get vendored under ::Zombie (set)
@ -141,10 +141,10 @@ module Mutant
#
def self.find_uncached(logical_name)
file_name =
unless logical_name.end_with?('.rb')
"#{logical_name}.rb"
else
if logical_name.end_with?('.rb')
logical_name
else
"#{logical_name}.rb"
end
$LOAD_PATH.each do |path|
@ -218,7 +218,9 @@ module Mutant
children = node.type == :begin ? node.children : [node]
children.select do |node|
children = node.children
node.type == :send && children.at(RECEIVER_INDEX).nil? && children.at(SELECTOR_INDEX) == :require
node.type == :send &&
children.at(RECEIVER_INDEX).nil? &&
children.at(SELECTOR_INDEX) == :require
end
end
@ -248,7 +250,7 @@ module Mutant
# @api private
#
def root_file
File.find(name) || raise("No root file!")
File.find(name) or raise 'No root file!'
end
memoize :root_file

View file

@ -1,6 +1,6 @@
require 'spec_helper'
describe Mutant,'rspec integration' do
describe Mutant, 'rspec integration' do
around do |example|
Dir.chdir(TestApp.root) do
@ -8,15 +8,20 @@ describe Mutant,'rspec integration' do
end
end
let(:strategy) { Mutant::Strategy::Rspec::DM2 }
pending 'allows to kill mutations' do
Kernel.system('bundle exec mutant --rspec ::TestApp::Literal#string').should be(true)
cli = 'bundle exec mutant --rspec ::TestApp::Literal#string'
Kernel.system(cli).should be(true)
end
pending 'fails to kill mutations when they are not covered' do
Kernel.system('bundle exec mutant --rspec ::TestApp::Literal#uncovered_string').should be(false)
cli = 'bundle exec mutant --rspec ::TestApp::Literal#uncovered_string'
Kernel.system(cli).should be(false)
end
pending 'fails when some mutations are not covered' do
Kernel.system('bundle exec mutant --rspec ::TestApp::Literal').should be(false)
pending 'fails when some mutations when are not covered' do
cli = 'bundle exec mutant --rspec ::TestApp::Literal'
Kernel.system(cli).should be(false)
end
end

View file

@ -3,7 +3,7 @@ require 'spec_helper'
describe Mutant do
specify 'mutant should not crash for any node parser can generate' do
Mutant::NODE_TYPES.each do |type|
Mutant::Mutator::Node::Registry.lookup(Mutant::NodeHelpers.s(type))
Mutant::Mutator::Registry.lookup(Mutant::NodeHelpers.s(type))
end
end
end

View file

@ -32,7 +32,7 @@ class Subject
again = Unparser.generate(parsed)
unless generated == again
# mostly an unparser bug!
fail "Untransitive:\n%s\n---\n%s" % [generated, again]
fail sprintf("Untransitive:\n%s\n---\n%s", generated, again)
end
self
end
@ -74,16 +74,25 @@ shared_examples_for 'a mutator' do
message = []
if missing.any?
message << 'Missing mutations (%i):' % missing.length
message << sprintf('Missing mutations (%i):', missing.length)
message.concat(missing)
end
if unexpected.any?
message << 'Unexpected mutations (%i):' % unexpected.length
message << sprintf('Unexpected mutations (%i):', unexpected.length)
message.concat(unexpected)
end
fail "Original:\n#{generate(node)}\n-----\n#{message.join("\n-----\n")}" if message.any?
if message.any?
message = sprintf(
"Original:\n%s\n-----\n%s",
generate(node),
message.join("\n-----\n")
)
fail message
end
end
end
end

View file

@ -2,7 +2,7 @@ require 'mutant'
require 'devtools'
Devtools.init_spec_helper
$: << File.join(TestApp.root,'lib')
$LOAD_PATH << File.join(TestApp.root, 'lib')
require 'test_app'

View file

@ -1,10 +1,8 @@
module CompressHelper
def strip_indent(string)
lines = string.lines
match = /\A( *)/.match(lines.first)
lines = string.lines
match = /\A( *)/.match(lines.first)
whitespaces = match[1].to_s.length
stripped = lines.map do |line|
line[whitespaces..-1]
end.join
lines.map { |line| line[whitespaces..-1] }.join
end
end

View file

@ -2,7 +2,9 @@ require 'spec_helper'
shared_examples_for 'an invalid cli run' do
it 'should raise error' do
expect { subject }.to raise_error(Mutant::CLI::Error, expected_message)
expect do
subject
end.to raise_error(Mutant::CLI::Error, expected_message)
end
end
@ -28,6 +30,9 @@ describe Mutant::CLI, '.new' do
let(:expected_strategy) { Mutant::Strategy::Rspec.new }
let(:expected_reporter) { Mutant::Reporter::CLI.new($stdout) }
let(:ns) { Mutant::CLI::Classifier }
let(:cache) { Mutant::Cache.new }
let(:cli) { object.new(arguments) }
subject { cli }
@ -71,21 +76,23 @@ describe Mutant::CLI, '.new' do
end
context 'with explicit method matcher' do
let(:arguments) { %w(--rspec TestApp::Literal#float) }
let(:expected_matcher) { Mutant::CLI::Classifier::Method.new(Mutant::Cache.new, 'TestApp::Literal#float') }
let(:arguments) { %w(--rspec TestApp::Literal#float) }
let(:expected_matcher) { ns::Method.new(cache, 'TestApp::Literal#float') }
it_should_behave_like 'a cli parser'
end
context 'with namespace matcher' do
let(:arguments) { %w(--rspec ::TestApp*) }
let(:expected_matcher) { Mutant::CLI::Classifier::Namespace::Recursive.new(Mutant::Cache.new, '::TestApp*') }
let(:matcher) { '::TestApp*' }
let(:arguments) { %W(--rspec #{matcher}) }
let(:expected_matcher) { ns::Namespace::Recursive.new(cache, matcher) }
it_should_behave_like 'a cli parser'
end
context 'with code filter' do
let(:arguments) { %w(--rspec --code faa --code bbb TestApp::Literal#float) }
let(:matcher) { 'TestApp::Literal#float' }
let(:arguments) { %W(--rspec --code faa --code bbb #{matcher}) }
let(:filters) do
[
@ -94,8 +101,8 @@ describe Mutant::CLI, '.new' do
]
end
let(:expected_matcher) { Mutant::CLI::Classifier::Method.new(Mutant::Cache.new, 'TestApp::Literal#float') }
let(:expected_filter) { Mutant::Mutation::Filter::Whitelist.new(filters) }
let(:expected_matcher) { ns::Method.new(cache, 'TestApp::Literal#float') }
let(:expected_filter) { Mutant::Mutation::Filter::Whitelist.new(filters) }
it_should_behave_like 'a cli parser'
end

View file

@ -21,7 +21,10 @@ describe Mutant::CLI, '.run' do
it { should be(0) }
it 'should run with attributes' do
Mutant::Runner::Config.should_receive(:run).with(config).and_return(runner)
Mutant::Runner::Config
.should_receive(:run)
.with(config)
.and_return(runner)
should be(0)
end
end
@ -32,7 +35,10 @@ describe Mutant::CLI, '.run' do
it { should be(1) }
it 'should run with attributes' do
Mutant::Runner::Config.should_receive(:run).with(config).and_return(runner)
Mutant::Runner::Config
.should_receive(:run)
.with(config)
.and_return(runner)
should be(1)
end
end

View file

@ -9,7 +9,8 @@ describe Mutant::CLI::Classifier, '.build' do
shared_examples_for this_spec do
it 'shoud return expected instance' do
should eql(expected_class.new(cache, expected_class::REGEXP.match(input)))
regexp = expected_class::REGEXP
should eql(expected_class.new(cache, regexp.match(input)))
end
let(:expected_class) { Mutant::CLI::Classifier::Method }

View file

@ -6,6 +6,8 @@ describe Mutant::Context, '#root' do
let(:object) { described_class.allocate }
it 'should raise error' do
expect { subject }.to raise_error('Mutant::Context#root is not implemented')
expect do
subject
end.to raise_error('Mutant::Context#root is not implemented')
end
end

View file

@ -9,7 +9,16 @@ describe Mutant::Differ, '#diff' do
let(:old) { %w(foo bar) }
let(:new) { %w(baz bar) }
it { should eql("@@ -1,3 +1,3 @@\n-foo\n+baz\n bar\n") }
let(:expectation) do
strip_indent(<<-STR)
@@ -1,3 +1,3 @@
-foo
+baz
bar
STR
end
it { should eql(expectation) }
it_should_behave_like 'an idempotent method'
end
@ -18,7 +27,16 @@ describe Mutant::Differ, '#diff' do
let(:old) { %w(foo bar) }
let(:new) { %w(foo baz bar) }
it { should eql("@@ -1,3 +1,4 @@\n foo\n+baz\n bar\n") }
let(:expectation) do
strip_indent(<<-STR)
@@ -1,3 +1,4 @@
foo
+baz
bar
STR
end
it { should eql(expectation) }
it_should_behave_like 'an idempotent method'
end
@ -27,7 +45,21 @@ describe Mutant::Differ, '#diff' do
let(:old) { %w(foo bar baz boz a b c) }
let(:new) { %w(foo bar baz boz a b c other) }
it { should eql("@@ -1,8 +1,9 @@\n foo\n bar\n baz\n boz\n a\n b\n c\n+other\n") }
let(:expectation) do
strip_indent(<<-STR)
@@ -1,8 +1,9 @@
foo
bar
baz
boz
a
b
c
+other
STR
end
it { should eql(expectation) }
it_should_behave_like 'an idempotent method'
end
@ -36,7 +68,21 @@ describe Mutant::Differ, '#diff' do
let(:old) { %w(other foo bar baz boz a b c) }
let(:new) { %w(foo bar baz boz a b c) }
it { should eql("@@ -1,9 +1,8 @@\n-other\n foo\n bar\n baz\n boz\n a\n b\n c\n") }
let(:expectation) do
strip_indent(<<-STR)
@@ -1,9 +1,8 @@
-other
foo
bar
baz
boz
a
b
c
STR
end
it { should eql(expectation) }
it_should_behave_like 'an idempotent method'
end
@ -45,7 +91,21 @@ describe Mutant::Differ, '#diff' do
let(:old) { %w(foo bar baz boz a b c) }
let(:new) { %w(other foo bar baz boz a b c) }
it { should eql("@@ -1,8 +1,9 @@\n+other\n foo\n bar\n baz\n boz\n a\n b\n c\n") }
let(:expectation) do
strip_indent(<<-STR)
@@ -1,8 +1,9 @@
+other
foo
bar
baz
boz
a
b
c
STR
end
it { should eql(expectation) }
it_should_behave_like 'an idempotent method'
end

View file

@ -4,13 +4,28 @@ describe Mutant::Killer::Rspec, '.new' do
subject { object.new(strategy, mutation) }
let(:strategy) { double('Strategy', :spec_files => ['foo'], :error_stream => $stderr, :output_stream => $stdout) }
let(:context) { double('Context') }
let(:mutation) { double('Mutation', :subject => mutation_subject, :should_survive? => false) }
let(:mutation_subject) { double('Mutation Subject') }
let(:context) { double('Context') }
let(:mutation_subject) { double('Mutation Subject') }
let(:object) { described_class }
let(:mutation) do
double(
'Mutation',
:subject => mutation_subject,
:should_survive? => false
)
end
let(:strategy) do
double(
'Strategy',
:spec_files => ['foo'],
:error_stream => $stderr,
:output_stream => $stdout
)
end
before do
mutation.stub(:insert)
mutation.stub(:reset)

View file

@ -4,10 +4,13 @@ describe Mutant::Loader::Eval, '.run' do
subject { object.run(node, mutation_subject) }
let(:object) { described_class }
let(:mutation_subject) { double('Subject', :source_path => path, :source_line => line) }
let(:path) { 'test.rb' }
let(:line) { 1 }
let(:object) { described_class }
let(:path) { 'test.rb' }
let(:line) { 1 }
let(:mutation_subject) do
double('Subject', :source_path => path, :source_line => line)
end
let(:source) do
<<-RUBY
@ -36,6 +39,8 @@ describe Mutant::Loader::Eval, '.run' do
it 'should set file and line correctly' do
subject
::SomeNamespace::Bar.instance_method(:some_method).source_location.should eql(['test.rb', 3])
::SomeNamespace::Bar
.instance_method(:some_method)
.source_location.should eql(['test.rb', 3])
end
end

View file

@ -9,6 +9,11 @@ describe Mutant::Matcher, '#each' do
let(:object) { described_class.allocate }
it 'should raise error' do
expect { subject }.to raise_error(NotImplementedError, 'Mutant::Matcher#each is not implemented')
expect do
subject
end.to raise_error(
NotImplementedError,
'Mutant::Matcher#each is not implemented'
)
end
end

View file

@ -28,13 +28,13 @@ describe Mutant::Matcher::Method::Instance, '.build' do
context 'with unmemoized method' do
let(:method_name) { :bar }
it { should eql(Mutant::Matcher::Method::Instance.new(cache, scope, method)) }
it { should eql(described_class.new(cache, scope, method)) }
end
context 'with memoized method' do
let(:method_name) { :foo }
it { should eql(Mutant::Matcher::Method::Instance::Memoized.new(cache, scope, method)) }
it { should eql(described_class::Memoized.new(cache, scope, method)) }
end
end
end

View file

@ -1,23 +1,17 @@
require 'spec_helper'
describe Mutant::Matcher::Method::Instance, '#each' do
let(:cache) { Fixtures::AST_CACHE }
let(:object) { described_class.new(cache, scope, method) }
let(:method) { scope.instance_method(method_name) }
let(:yields) { [] }
let(:namespace) do
klass = self.class
end
let(:scope) { self.class::Foo }
subject { object.each { |subject| yields << subject } }
let(:type) { :def }
let(:method_name) { :bar }
let(:method_arity) { 0 }
let(:cache) { Fixtures::AST_CACHE }
let(:object) { described_class.new(cache, scope, method) }
let(:method) { scope.instance_method(method_name) }
let(:yields) { [] }
let(:namespace) { self.class }
let(:scope) { self.class::Foo }
let(:type) { :def }
let(:method_name) { :bar }
let(:method_arity) { 0 }
def name
node.children[0]
@ -42,11 +36,14 @@ describe Mutant::Matcher::Method::Instance, '#each' do
context 'on differend lines' do
let(:base) { __LINE__ }
class self::Foo
def bar; end
def bar(arg); end
def bar
end
def bar(arg)
end
end
let(:method_line) { 3 }
let(:method_line) { 5 }
let(:method_arity) { 1 }
it_should_behave_like 'a method matcher'
@ -83,7 +80,8 @@ describe Mutant::Matcher::Method::Instance, '#each' do
let(:base) { __LINE__ }
class self::Foo
class Bar
def baz; end
def baz
end
end
end
@ -98,7 +96,8 @@ describe Mutant::Matcher::Method::Instance, '#each' do
let(:base) { __LINE__ }
module self::Foo
class Bar
def baz; end
def baz
end
end
end

View file

@ -1,22 +1,16 @@
require 'spec_helper'
describe Mutant::Matcher::Method::Singleton, '#each' do
let(:object) { described_class.new(cache, scope, method) }
let(:method) { scope.method(method_name) }
let(:cache) { Fixtures::AST_CACHE }
let(:yields) { [] }
let(:namespace) do
klass = self.class
end
let(:scope) { self.class::Foo }
subject { object.each { |subject| yields << subject } }
let(:type) { :defs }
let(:method_arity) { 0 }
let(:object) { described_class.new(cache, scope, method) }
let(:method) { scope.method(method_name) }
let(:cache) { Fixtures::AST_CACHE }
let(:yields) { [] }
let(:namespace) { self.class }
let(:scope) { self.class::Foo }
let(:type) { :defs }
let(:method_arity) { 0 }
def name
node.children[1]
@ -46,7 +40,8 @@ describe Mutant::Matcher::Method::Singleton, '#each' do
let(:base) { __LINE__ }
module self::Namespace
class Foo
def Foo.bar; end
def Foo.bar
end
end
end
@ -60,12 +55,15 @@ describe Mutant::Matcher::Method::Singleton, '#each' do
context 'outside namespace' do
let(:base) { __LINE__ }
module self::Namespace
class Foo; end;
def Foo.bar; end
class Foo
end
def Foo.bar
end
end
let(:method_name) { :bar }
let(:method_line) { 3 }
let(:method_line) { 5 }
let(:scope) { self.class::Namespace::Foo }
it_should_behave_like 'a method matcher'
@ -78,7 +76,10 @@ describe Mutant::Matcher::Method::Singleton, '#each' do
module self::Namespace
module Foo; end
module Bar
def self.baz; end; def Foo.baz(arg); end
def self.baz
end
def Foo.baz(arg)
end
end
end

View file

@ -46,9 +46,12 @@ describe Mutant::Matcher::Methods::Instance, '#each' do
before do
matcher = Mutant::Matcher::Method::Instance
matcher.stub(:new).with(cache, Foo, Foo.instance_method(:method_a)).and_return([subject_a])
matcher.stub(:new).with(cache, Foo, Foo.instance_method(:method_b)).and_return([subject_b])
matcher.stub(:new).with(cache, Foo, Foo.instance_method(:method_c)).and_return([subject_c])
matcher.stub(:new)
.with(cache, Foo, Foo.instance_method(:method_a)).and_return([subject_a])
matcher.stub(:new)
.with(cache, Foo, Foo.instance_method(:method_b)).and_return([subject_b])
matcher.stub(:new)
.with(cache, Foo, Foo.instance_method(:method_c)).and_return([subject_c])
end
it 'should yield expected subjects' do

View file

@ -40,9 +40,12 @@ describe Mutant::Matcher::Methods::Singleton, '#each' do
before do
matcher = Mutant::Matcher::Method::Singleton
matcher.stub(:new).with(cache, Foo, Foo.method(:method_a)).and_return([subject_a])
matcher.stub(:new).with(cache, Foo, Foo.method(:method_b)).and_return([subject_b])
matcher.stub(:new).with(cache, Foo, Foo.method(:method_c)).and_return([subject_c])
matcher.stub(:new)
.with(cache, Foo, Foo.method(:method_a)).and_return([subject_a])
matcher.stub(:new)
.with(cache, Foo, Foo.method(:method_b)).and_return([subject_b])
matcher.stub(:new)
.with(cache, Foo, Foo.method(:method_c)).and_return([subject_c])
end
it 'should yield expected subjects' do

View file

@ -14,8 +14,12 @@ describe Mutant::Matcher::Namespace, '#each' do
let(:subject_b) { double('SubjectB') }
before do
Mutant::Matcher::Methods::Singleton.stub(:each).with(cache, singleton_a).and_yield(subject_a)
Mutant::Matcher::Methods::Instance.stub(:each).with(cache, singleton_a).and_yield(subject_b)
Mutant::Matcher::Methods::Singleton.stub(:each)
.with(cache, singleton_a)
.and_yield(subject_a)
Mutant::Matcher::Methods::Instance.stub(:each)
.with(cache, singleton_a)
.and_yield(subject_b)
ObjectSpace.stub(:each_object => [singleton_a, singleton_b])
end

View file

@ -4,7 +4,7 @@ describe Mutant::Mutator, '#emit_new' do
subject { object.send(:emit_new) { generated } }
class Block
def arguments; @arguments; end
attr_reader :arguments
def called?
defined?(@arguments)
@ -23,7 +23,7 @@ describe Mutant::Mutator, '#emit_new' do
let(:class_under_test) do
Class.new(described_class) do
def dispatch
#noop
# noop
end
end
end
@ -46,7 +46,12 @@ describe Mutant::Mutator, '#emit_new' do
let(:generated) { input }
it 'should raise error' do
expect { subject }.to raise_error(RuntimeError, 'New AST could not be generated after 3 attempts')
expect do
subject
end.to raise_error(
RuntimeError,
'New AST could not be generated after 3 attempts'
)
end
end
end

View file

@ -4,7 +4,7 @@ describe Mutant::Mutator, '#emit' do
subject { object.send(:emit, generated) }
class Block
def arguments; @arguments; end
attr_reader :arguments
def called?
defined?(@arguments)
@ -23,7 +23,7 @@ describe Mutant::Mutator, '#emit' do
let(:class_under_test) do
Class.new(described_class) do
def dispatch
#noop
# noop
end
end
end

View file

@ -21,7 +21,7 @@ describe Mutant::Mutator, 'block' do
let(:source) { 'foo { |a, b| }' }
before do
Mutant::Random.stub(:hex_string => :random)
Mutant::Random.stub(:hex_string => 'random')
end
let(:mutations) do
@ -41,7 +41,7 @@ describe Mutant::Mutator, 'block' do
context 'with block pattern args' do
before do
Mutant::Random.stub(:hex_string => :random)
Mutant::Random.stub(:hex_string => 'random')
end
let(:source) { 'foo { |(a, b), c| }' }

View file

@ -0,0 +1,12 @@
require 'spec_helper'
describe Mutant::Mutator::Node::NamedValue::Access, 'block_pass' do
let(:source) { 'foo(&bar)' }
let(:mutations) do
mutants = []
mutants << 'foo'
end
it_should_behave_like 'a mutator'
end

View file

@ -1,11 +1,6 @@
require 'spec_helper'
describe Mutant::Mutator::Node::NamedValue::Access, 'cbase' do
before do
Mutant::Random.stub(:hex_string => :random)
end
describe Mutant::Mutator::Node::Noop, 'cbase' do
let(:source) { '::A' }
let(:mutations) do

View file

@ -1,11 +1,6 @@
require 'spec_helper'
describe Mutant::Mutator::Node::NamedValue::Access, 'const' do
before do
Mutant::Random.stub(:hex_string => :random)
end
describe Mutant::Mutator::Node::Const, 'const' do
let(:source) { 'A::B' }
let(:mutations) do

View file

@ -1,7 +1,6 @@
require 'spec_helper'
describe Mutant::Mutator, 'def' do
context 'empty' do
let(:source) { 'def foo; end' }

View file

@ -1,7 +1,6 @@
require 'spec_helper'
describe Mutant::Mutator, 'if' do
before do
Mutant::Random.stub(:hex_string => 'random')
end

View file

@ -1,11 +1,8 @@
require 'spec_helper'
describe Mutant::Mutator::Node::Literal, 'nil' do
let(:source) { 'nil' }
let(:mutations) do
[ '::Object.new' ]
end
let(:source) { 'nil' }
let(:mutations) { ['::Object.new'] }
it_should_behave_like 'a mutator'
end

View file

@ -9,7 +9,6 @@ describe Mutant::Mutator::Node::Literal, 'range' do
mutations << 'nil'
mutations << '1...100'
mutations << '(0.0 / 0.0)..100'
#mutations << [:dot2, [:negate, [:call, [:lit, 1.0], :/, [:arglist, [:lit, 0.0]]]], [:lit, 100]]
mutations << '1..(1.0 / 0.0)'
mutations << '1..(0.0 / 0.0)'
end
@ -25,7 +24,6 @@ describe Mutant::Mutator::Node::Literal, 'range' do
mutations << 'nil'
mutations << '1..100'
mutations << '(0.0 / 0.0)...100'
#mutations << [:dot3, [:negate, [:call, [:lit, 1.0], :/, [:arglist, [:lit, 0.0]]]], [:lit, 100]]
mutations << '1...(1.0 / 0.0)'
mutations << '1...(0.0 / 0.0)'
end

View file

@ -1,16 +1,8 @@
require 'spec_helper'
describe Mutant::Mutator, 'masgn' do
before do
Mutant::Random.stub(:hex_string => 'random')
end
let(:source) { 'a, b = c, d' }
let(:mutations) do
mutants = []
end
let(:source) { 'a, b = c, d' }
let(:mutations) { [] }
it_should_behave_like 'a mutator'
end

View file

@ -1,9 +1,8 @@
require 'spec_helper'
describe Mutant::Mutator::Node::NamedValue::Access, 'mutations' do
before do
Mutant::Random.stub(:hex_string => :random)
Mutant::Random.stub(:hex_string => 'random')
end
context 'global variable' do

View file

@ -1,9 +1,8 @@
require 'spec_helper'
describe Mutant::Mutator::Node::NamedValue::VariableAssignment, 'mutations' do
before do
Mutant::Random.stub(:hex_string => :random)
Mutant::Random.stub(:hex_string => 'random')
end
let(:source) { 'A = true' }

View file

@ -1,9 +1,8 @@
require 'spec_helper'
describe Mutant::Mutator::Node::NamedValue::VariableAssignment, 'mutations' do
before do
Mutant::Random.stub(:hex_string => :random)
Mutant::Random.stub(:hex_string => 'random')
end
context 'global variable' do

View file

@ -3,7 +3,7 @@ require 'spec_helper'
describe Mutant::Mutator::Node::While do
context 'with more than one statement' do
let(:source) { "while true; foo; bar; end" }
let(:source) { 'while true; foo; bar; end' }
let(:mutations) do
mutations = []

View file

@ -33,16 +33,18 @@ describe Mutant::Runner::Config, '#subjects' do
context 'without earily stop' do
let(:stop_a) { false }
let(:stop_b) { false }
it { should eql([runner_a, runner_b]) }
it_should_behave_like 'an idempotent method'
end
context 'with earily stop' do
let(:stop_a) { true }
let(:stop_b) { false }
it { should eql([runner_a]) }
it_should_behave_like 'an idempotent method'
end
end

View file

@ -14,12 +14,18 @@ describe Mutant::Runner::Config, '#success?' do
)
end
let(:reporter) { double('Reporter') }
let(:strategy) { double('Strategy') }
let(:subject_a) { double('Subject A') }
let(:subject_b) { double('Subject B') }
let(:runner_a) { double('Runner A', :stop? => stop_a, :success? => success_a) }
let(:runner_b) { double('Runner B', :stop? => stop_b, :success? => success_b) }
let(:reporter) { double('Reporter') }
let(:strategy) { double('Strategy') }
let(:subject_a) { double('Subject A') }
let(:subject_b) { double('Subject B') }
let(:runner_a) do
double('Runner A', :stop? => stop_a, :success? => success_a)
end
let(:runner_b) do
double('Runner B', :stop? => stop_b, :success? => success_b)
end
before do
reporter.stub(:report => reporter)
@ -34,6 +40,7 @@ describe Mutant::Runner::Config, '#success?' do
let(:stop_b) { false }
let(:success_a) { true }
let(:success_b) { true }
it { should be(true) }
end

View file

@ -27,7 +27,7 @@ describe Mutant::Runner::Mutation, '#killer' do
end
it 'should call configuration to identify strategy' do
config.should_receive(:strategy).with().and_return(strategy)
config.should_receive(:strategy).with(no_args).and_return(strategy)
should be(killer)
end

View file

@ -5,21 +5,26 @@ describe Mutant::Runner::Subject, '#success?' do
let(:object) { described_class.new(config, mutation_subject) }
let(:mutation_subject) {
let(:mutation_subject) do
double(
'Subject',
:class => Mutant::Subject,
:mutations => [mutation_a, mutation_b]
)
}
end
let(:reporter) { double('Reporter') }
let(:config) { double('Config', :reporter => reporter) }
let(:mutation_a) { double('Mutation A') }
let(:mutation_b) { double('Mutation B') }
let(:reporter) { double('Reporter') }
let(:config) { double('Config', :reporter => reporter) }
let(:mutation_a) { double('Mutation A') }
let(:mutation_b) { double('Mutation B') }
let(:runner_a) { double('Runner A', :success? => success_a, :stop? => stop_a) }
let(:runner_b) { double('Runner B', :success? => success_b, :stop? => stop_b) }
let(:runner_a) do
double('Runner A', :success? => success_a, :stop? => stop_a)
end
let(:runner_b) do
double('Runner B', :success? => success_b, :stop? => stop_b)
end
before do
reporter.stub(:report => reporter)

View file

@ -8,7 +8,9 @@ shared_examples_for 'a method filter parse result' do
it { should be(response) }
it 'should initialize method filter with correct arguments' do
expected_class.should_receive(:new).with(TestApp::Literal, :string).and_return(response)
expected_class.should_receive(:new)
.with(TestApp::Literal, :string)
.and_return(response)
subject
end
end

View file

@ -1,47 +0,0 @@
shared_examples_for 'a mutator' do
subject { object.each(node) { |item| yields << item } }
let(:yields) { [] }
let(:object) { described_class }
unless instance_methods.map(&:to_s).include?('node')
let(:node) { source.to_ast }
end
it_should_behave_like 'a command method'
context 'with no block' do
subject { object.each(node) }
it { should be_instance_of(to_enum.class) }
let(:expected_mutations) do
mutations.map do |mutation|
if mutation.respond_to?(:to_ast)
mutation.to_ast.to_sexp
else
mutation
end
end.to_set
end
it 'generates the expected mutations' do
subject = self.subject.map(&:to_sexp).to_set
unless subject == expected_mutations
message = "Missing mutations: %s\nUnexpected mutations: %s" %
[
expected_mutations - subject,
subject - expected_mutations
].map(&:to_a).map(&:inspect)
fail message
end
end
end
end
shared_examples_for 'a noop mutator' do
let(:mutations) { [] }
it_should_behave_like 'a mutator'
end