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:
commit
cb60db3078
83 changed files with 576 additions and 337 deletions
43
.gitignore
vendored
43
.gitignore
vendored
|
@ -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
1
.ruby-gemset
Normal file
|
@ -0,0 +1 @@
|
|||
mutant
|
11
Gemfile
11
Gemfile
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
30
Guardfile
30
Guardfile
|
@ -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
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
---
|
||||
threshold: 16
|
||||
total_score: 737
|
||||
total_score: 773
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
---
|
||||
threshold: 19.8
|
||||
threshold: 20.5
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
#
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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| }' }
|
||||
|
|
12
spec/unit/mutant/mutator/node/block_pass/mutation_spec.rb
Normal file
12
spec/unit/mutant/mutator/node/block_pass/mutation_spec.rb
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Mutant::Mutator, 'def' do
|
||||
|
||||
context 'empty' do
|
||||
let(:source) { 'def foo; end' }
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Mutant::Mutator, 'if' do
|
||||
|
||||
before do
|
||||
Mutant::Random.stub(:hex_string => 'random')
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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' }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in a new issue