Merge branch 'master' of github.com:mbj/mutant into flexible-rspec
Conflicts: config/flay.yml
This commit is contained in:
commit
6bd9d2bf21
49 changed files with 689 additions and 87 deletions
6
Gemfile
6
Gemfile
|
@ -2,7 +2,7 @@ source 'https://rubygems.org'
|
||||||
|
|
||||||
gemspec
|
gemspec
|
||||||
|
|
||||||
gem 'mutant', :path => '.'
|
gem 'mutant', path: '.'
|
||||||
|
|
||||||
gem 'devtools', :git => 'https://github.com/rom-rb/devtools.git'
|
gem 'devtools', git: 'https://github.com/rom-rb/devtools.git'
|
||||||
eval(File.read(File.join(File.dirname(__FILE__),'Gemfile.devtools')))
|
eval(File.read(File.join(File.dirname(__FILE__), 'Gemfile.devtools')))
|
||||||
|
|
|
@ -14,26 +14,27 @@ group :guard do
|
||||||
gem 'guard', '~> 1.8.1'
|
gem 'guard', '~> 1.8.1'
|
||||||
gem 'guard-bundler', '~> 1.0.0'
|
gem 'guard-bundler', '~> 1.0.0'
|
||||||
gem 'guard-rspec', '~> 3.0.2'
|
gem 'guard-rspec', '~> 3.0.2'
|
||||||
# gem 'guard-rubocop', '~> 0.2.0'
|
gem 'guard-rubocop', '~> 0.2.0'
|
||||||
|
gem 'guard-mutant', '~> 0.0.1'
|
||||||
|
|
||||||
# file system change event handling
|
# file system change event handling
|
||||||
gem 'listen', '~> 1.2.2'
|
gem 'listen', '~> 1.2.2'
|
||||||
gem 'rb-fchange', '~> 0.0.6', :require => false
|
gem 'rb-fchange', '~> 0.0.6', require: false
|
||||||
gem 'rb-fsevent', '~> 0.9.3', :require => false
|
gem 'rb-fsevent', '~> 0.9.3', require: false
|
||||||
gem 'rb-inotify', '~> 0.9.0', :require => false
|
gem 'rb-inotify', '~> 0.9.0', require: false
|
||||||
|
|
||||||
# notification handling
|
# notification handling
|
||||||
gem 'libnotify', '~> 0.8.0', :require => false
|
gem 'libnotify', '~> 0.8.0', require: false
|
||||||
gem 'rb-notifu', '~> 0.0.4', :require => false
|
gem 'rb-notifu', '~> 0.0.4', require: false
|
||||||
gem 'terminal-notifier-guard', '~> 1.5.3', :require => false
|
gem 'terminal-notifier-guard', '~> 1.5.3', require: false
|
||||||
end
|
end
|
||||||
|
|
||||||
group :metrics do
|
group :metrics do
|
||||||
gem 'coveralls', '~> 0.6.7'
|
gem 'coveralls', '~> 0.6.7'
|
||||||
gem 'flay', '~> 2.3.1'
|
gem 'flay', '~> 2.3.1'
|
||||||
gem 'flog', '~> 4.1.1'
|
gem 'flog', '~> 4.1.1'
|
||||||
gem 'reek', '~> 1.3.1', :git => 'https://github.com/troessner/reek.git'
|
gem 'reek', '~> 1.3.1', git: 'https://github.com/troessner/reek.git'
|
||||||
# gem 'rubocop', '~> 0.9.1'
|
gem 'rubocop', '~> 0.10.0', git: 'https://github.com/bbatsov/rubocop.git'
|
||||||
gem 'simplecov', '~> 0.7.1'
|
gem 'simplecov', '~> 0.7.1'
|
||||||
gem 'yardstick', '~> 0.9.6'
|
gem 'yardstick', '~> 0.9.6'
|
||||||
|
|
||||||
|
@ -41,10 +42,6 @@ group :metrics do
|
||||||
gem 'yard-spellcheck', '~> 0.1.5'
|
gem 'yard-spellcheck', '~> 0.1.5'
|
||||||
end
|
end
|
||||||
|
|
||||||
platforms :ruby_19 do
|
|
||||||
gem 'json', '~> 1.8.0'
|
|
||||||
end
|
|
||||||
|
|
||||||
platforms :rbx do
|
platforms :rbx do
|
||||||
gem 'pelusa', '~> 0.2.2'
|
gem 'pelusa', '~> 0.2.2'
|
||||||
end
|
end
|
||||||
|
|
32
README.md
32
README.md
|
@ -5,7 +5,7 @@ mutant
|
||||||
[![Dependency Status](https://gemnasium.com/mbj/mutant.png)](https://gemnasium.com/mbj/mutant)
|
[![Dependency Status](https://gemnasium.com/mbj/mutant.png)](https://gemnasium.com/mbj/mutant)
|
||||||
[![Code Climate](https://codeclimate.com/github/mbj/mutant.png)](https://codeclimate.com/github/mbj/mutant)
|
[![Code Climate](https://codeclimate.com/github/mbj/mutant.png)](https://codeclimate.com/github/mbj/mutant)
|
||||||
|
|
||||||
Mutant is a mutation testing tool for ruby. It aims to be better than existing mutation testers.
|
Mutant is a mutation testing tool for ruby.
|
||||||
|
|
||||||
The idea is that if code can be changed and your tests do not notice, either that code isn't being covered
|
The idea is that if code can be changed and your tests do not notice, either that code isn't being covered
|
||||||
or it does not have a speced side effect.
|
or it does not have a speced side effect.
|
||||||
|
@ -47,6 +47,36 @@ The 0.2 series is stable but has outdated dependencies. The 0.3 series is in bet
|
||||||
gem install mutant --pre
|
gem install mutant --pre
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Mutations
|
||||||
|
---------
|
||||||
|
|
||||||
|
Mutant supports a very wide range of mutation operators. Listing them all in detail would blow this document up.
|
||||||
|
|
||||||
|
It is planned to parse a list of mutation operators from the source. In the meantime please refer to the
|
||||||
|
[code](https://github.com/mbj/mutant/tree/master/lib/mutant/mutator/node) each subclass of `Mutant::Mutator::Node`
|
||||||
|
emits around 3-6 mutations.
|
||||||
|
|
||||||
|
Currently mutant covers the majority of ruby's complex nodes that often occur in method bodies.
|
||||||
|
|
||||||
|
A some stats from the [axiom](https://github.com/dkubb/axiom) library:
|
||||||
|
|
||||||
|
```
|
||||||
|
Subjects: 417 # Amount of subjects being mutated (currently only methods)
|
||||||
|
Mutations: 5442 # Amount of mutations mutant generated (~13 mutations per method)
|
||||||
|
Kills: 5385 # Amount of successfully killed mutations
|
||||||
|
Runtime: 1898.11s # Total runtime
|
||||||
|
Killtime: 1884.17s # Time spend killing mutations
|
||||||
|
Overhead: 0.73%
|
||||||
|
Coverage: 98.95% # Coverage score
|
||||||
|
Alive: 57 # Amount of alive mutations.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Nodes still missing a dedicated mutator are handled via the
|
||||||
|
[Generic](https://github.com/mbj/mutant/blob/master/lib/mutant/mutator/node/generic.rb) mutator.
|
||||||
|
The goal is to remove this mutator and have dedicated mutator for every type of node and removing
|
||||||
|
the Generic handler altogether.
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
trap('INT') do |status|
|
trap('INT') do |status|
|
||||||
exit! 128+status
|
exit! 128 + status
|
||||||
end
|
end
|
||||||
|
|
||||||
require 'mutant'
|
require 'mutant'
|
||||||
|
|
||||||
namespace =
|
namespace =
|
||||||
if File.basename($0) == 'zombie'
|
if ARGV.include?('--zombie')
|
||||||
$stderr.puts('Detected zombie environment...')
|
$stderr.puts('Running mutant zombified!')
|
||||||
Mutant::Zombifier.zombify
|
Mutant::Zombifier.zombify
|
||||||
Zombie::Mutant
|
Zombie::Mutant
|
||||||
else
|
else
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
mutant
|
|
|
@ -1,3 +1,3 @@
|
||||||
---
|
---
|
||||||
threshold: 16
|
threshold: 16
|
||||||
total_score: 685
|
total_score: 737
|
||||||
|
|
|
@ -47,6 +47,7 @@ NestedIterators:
|
||||||
exclude:
|
exclude:
|
||||||
- Mutant#self.singleton_subclass_instance
|
- Mutant#self.singleton_subclass_instance
|
||||||
- Mutant::Mutator::Util::Array::Element#dispatch
|
- Mutant::Mutator::Util::Array::Element#dispatch
|
||||||
|
- Mutant::Reporter::CLI::Printer::Config::Runner#generic_stats
|
||||||
- Mutant::CLI#parse
|
- Mutant::CLI#parse
|
||||||
max_allowed_nesting: 1
|
max_allowed_nesting: 1
|
||||||
ignore_iterators: []
|
ignore_iterators: []
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
AllCops:
|
AllCops:
|
||||||
Includes:
|
Includes:
|
||||||
- '../**/*.rake'
|
- '../**/*.rake'
|
||||||
|
- 'Gemfile'
|
||||||
|
- 'Gemfile.devtools'
|
||||||
Excludes:
|
Excludes:
|
||||||
- '../vendor/**'
|
- '**/vendor/**'
|
||||||
|
- '**/benchmarks/**'
|
||||||
|
|
||||||
# Avoid parameter lists longer than five parameters.
|
# Avoid parameter lists longer than five parameters.
|
||||||
ParameterLists:
|
ParameterLists:
|
||||||
|
|
|
@ -20,7 +20,9 @@ require 'concord'
|
||||||
|
|
||||||
# Library namespace
|
# Library namespace
|
||||||
module Mutant
|
module Mutant
|
||||||
end
|
# The empty string used within this namespace
|
||||||
|
EMPTY_STRING = ''.freeze
|
||||||
|
end # Mutant
|
||||||
|
|
||||||
require 'mutant/cache'
|
require 'mutant/cache'
|
||||||
require 'mutant/node_helpers'
|
require 'mutant/node_helpers'
|
||||||
|
@ -52,16 +54,21 @@ require 'mutant/mutator/node/literal/array'
|
||||||
require 'mutant/mutator/node/literal/hash'
|
require 'mutant/mutator/node/literal/hash'
|
||||||
require 'mutant/mutator/node/literal/regex'
|
require 'mutant/mutator/node/literal/regex'
|
||||||
require 'mutant/mutator/node/literal/nil'
|
require 'mutant/mutator/node/literal/nil'
|
||||||
require 'mutant/mutator/node/assignment'
|
|
||||||
require 'mutant/mutator/node/argument'
|
require 'mutant/mutator/node/argument'
|
||||||
require 'mutant/mutator/node/arguments'
|
require 'mutant/mutator/node/arguments'
|
||||||
require 'mutant/mutator/node/begin'
|
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/while'
|
require 'mutant/mutator/node/while'
|
||||||
require 'mutant/mutator/node/super'
|
require 'mutant/mutator/node/super'
|
||||||
|
require 'mutant/mutator/node/zsuper'
|
||||||
require 'mutant/mutator/node/send'
|
require 'mutant/mutator/node/send'
|
||||||
require 'mutant/mutator/node/send/binary'
|
require 'mutant/mutator/node/send/binary'
|
||||||
require 'mutant/mutator/node/when'
|
require 'mutant/mutator/node/when'
|
||||||
require 'mutant/mutator/node/assignment'
|
|
||||||
require 'mutant/mutator/node/define'
|
require 'mutant/mutator/node/define'
|
||||||
require 'mutant/mutator/node/mlhs'
|
require 'mutant/mutator/node/mlhs'
|
||||||
require 'mutant/mutator/node/masgn'
|
require 'mutant/mutator/node/masgn'
|
||||||
|
|
|
@ -202,6 +202,8 @@ module Mutant
|
||||||
opts.separator ''
|
opts.separator ''
|
||||||
opts.separator 'Strategies:'
|
opts.separator 'Strategies:'
|
||||||
|
|
||||||
|
opts.on('--zombie', 'Run mutant zombified')
|
||||||
|
|
||||||
add_strategies(opts)
|
add_strategies(opts)
|
||||||
add_options(opts)
|
add_options(opts)
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,8 @@ module Mutant
|
||||||
OPERATOR_PATTERN = Regexp.union(*OPERATOR_METHODS.map(&:to_s)).freeze
|
OPERATOR_PATTERN = Regexp.union(*OPERATOR_METHODS.map(&:to_s)).freeze
|
||||||
METHOD_NAME_PATTERN = /([_A-Za-z][A-Za-z0-9_]*[!?=]?|#{OPERATOR_PATTERN})/.freeze
|
METHOD_NAME_PATTERN = /([_A-Za-z][A-Za-z0-9_]*[!?=]?|#{OPERATOR_PATTERN})/.freeze
|
||||||
SCOPE_PATTERN = /(?:::)?#{SCOPE_NAME_PATTERN}(?:::#{SCOPE_NAME_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 = %r(\A(#{SCOPE_PATTERN})\z).freeze
|
||||||
|
|
||||||
REGISTRY = []
|
REGISTRY = []
|
||||||
|
@ -35,7 +36,7 @@ module Mutant
|
||||||
# @api private
|
# @api private
|
||||||
#
|
#
|
||||||
def self.constant_lookup(location)
|
def self.constant_lookup(location)
|
||||||
location.gsub(%r(\A::), '').split('::').inject(Object) do |parent, name|
|
location.gsub(CBASE_PATTERN, EMPTY_STRING).split(SCOPE_OPERATOR).inject(Object) do |parent, name|
|
||||||
parent.const_get(name)
|
parent.const_get(name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -71,7 +71,7 @@ module Mutant
|
||||||
:kwarg, :restarg, :arg, :block_pass, :or, :and,
|
:kwarg, :restarg, :arg, :block_pass, :or, :and,
|
||||||
:next, :undef, :if, :module, :cbase, :block, :send,
|
:next, :undef, :if, :module, :cbase, :block, :send,
|
||||||
:zsuper, :super, :empty, :alias, :for, :redo,
|
:zsuper, :super, :empty, :alias, :for, :redo,
|
||||||
:return, :splat, :not, :defined?, :op_asgn, :self,
|
:return, :splat, :defined?, :op_asgn, :self,
|
||||||
:true, :false, :nil, :dstr, :dsym, :regexp,
|
:true, :false, :nil, :dstr, :dsym, :regexp,
|
||||||
:regopt, :int, :str, :float, :sym, :pair, :hash, :array,
|
:regopt, :int, :str, :float, :sym, :pair, :hash, :array,
|
||||||
:xstr, :def, :defs, :case, :when, :ivar, :lvar, :cvar, :gvar,
|
:xstr, :def, :defs, :case, :when, :ivar, :lvar, :cvar, :gvar,
|
||||||
|
|
|
@ -18,14 +18,21 @@ module Mutant
|
||||||
def run
|
def run
|
||||||
mutation.insert
|
mutation.insert
|
||||||
# TODO: replace with real streams from configuration
|
# TODO: replace with real streams from configuration
|
||||||
|
# Note: we assume the only interesting output from a failed rspec run is stderr.
|
||||||
require 'stringio'
|
require 'stringio'
|
||||||
null = StringIO.new
|
rspec_err = StringIO.new
|
||||||
argv = command_line_arguments
|
|
||||||
begin
|
killed = !::RSpec::Core::Runner.run(command_line_arguments, nil, rspec_err).zero?
|
||||||
!::RSpec::Core::Runner.run(argv, null, null).zero?
|
|
||||||
rescue StandardError
|
if killed and mutation.should_survive?
|
||||||
true
|
rspec_err.rewind
|
||||||
|
|
||||||
|
puts "#{mutation.class} test failed."
|
||||||
|
puts 'RSpec stderr:'
|
||||||
|
puts rspec_err.read
|
||||||
end
|
end
|
||||||
|
|
||||||
|
killed
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return command line arguments
|
# Return command line arguments
|
||||||
|
|
|
@ -28,6 +28,18 @@ module Mutant
|
||||||
#
|
#
|
||||||
abstract_method :success?
|
abstract_method :success?
|
||||||
|
|
||||||
|
# Indicate if a killer should treat a kill as problematic
|
||||||
|
#
|
||||||
|
# @return [true]
|
||||||
|
# if killing is unexpected
|
||||||
|
#
|
||||||
|
# @return [false]
|
||||||
|
# if killing is expected
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
abstract_method :should_survive?
|
||||||
|
|
||||||
# Insert mutated node
|
# Insert mutated node
|
||||||
#
|
#
|
||||||
# FIXME:
|
# FIXME:
|
||||||
|
|
|
@ -30,6 +30,16 @@ module Mutant
|
||||||
killer.killed?
|
killer.killed?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Indicate if a killer should treat a kill as problematic.
|
||||||
|
#
|
||||||
|
# @return [false] Killing evil mutants is not problematic.
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def should_survive?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
end # Evil
|
end # Evil
|
||||||
end # Mutation
|
end # Mutation
|
||||||
end # Mutant
|
end # Mutant
|
||||||
|
|
|
@ -10,6 +10,19 @@ module Mutant
|
||||||
|
|
||||||
SYMBOL = 'noop'
|
SYMBOL = 'noop'
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def should_survive?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return identification
|
# Return identification
|
||||||
|
@ -39,6 +52,16 @@ module Mutant
|
||||||
!killer.killed?
|
!killer.killed?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Indicate if a killer should treat a kill as problematic.
|
||||||
|
#
|
||||||
|
# @return [true] Neutral mutants must die.
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def should_survive?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
end # Neutral
|
end # Neutral
|
||||||
end # Mutation
|
end # Mutation
|
||||||
end # Mutant
|
end # Mutant
|
||||||
|
|
|
@ -21,7 +21,8 @@ module Mutant
|
||||||
emit_self(*children)
|
emit_self(*children)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
children.each do |child|
|
children.each_with_index do |child, index|
|
||||||
|
mutate_child(index)
|
||||||
emit(child)
|
emit(child)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
25
lib/mutant/mutator/node/cbase.rb
Normal file
25
lib/mutant/mutator/node/cbase.rb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
module Mutant
|
||||||
|
class Mutator
|
||||||
|
class Node
|
||||||
|
|
||||||
|
# Mutation emitter to handle cbase nodes
|
||||||
|
class Cbase < self
|
||||||
|
|
||||||
|
handle(:cbase)
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Emit mutations
|
||||||
|
#
|
||||||
|
# @return [undefined]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def dispatch
|
||||||
|
# noop, for now
|
||||||
|
end
|
||||||
|
|
||||||
|
end # Cbase
|
||||||
|
end # Node
|
||||||
|
end # Mutator
|
||||||
|
end # Mutant
|
59
lib/mutant/mutator/node/connective/binary.rb
Normal file
59
lib/mutant/mutator/node/connective/binary.rb
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
module Mutant
|
||||||
|
class Mutator
|
||||||
|
class Node
|
||||||
|
module Connective
|
||||||
|
|
||||||
|
# Mutation emitter to handle binary connectives
|
||||||
|
class Binary < Node
|
||||||
|
|
||||||
|
INVERSE = {
|
||||||
|
:and => :or,
|
||||||
|
:or => :and,
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
handle *INVERSE.keys
|
||||||
|
|
||||||
|
children :left, :right
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Emit mutations
|
||||||
|
#
|
||||||
|
# @return [undefined]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def dispatch
|
||||||
|
emit_nil
|
||||||
|
emit(left)
|
||||||
|
emit(right)
|
||||||
|
mutate_operator
|
||||||
|
mutate_operands
|
||||||
|
end
|
||||||
|
|
||||||
|
# Emit operator mutations
|
||||||
|
#
|
||||||
|
# @return [undefined]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def mutate_operator
|
||||||
|
emit(s(INVERSE.fetch(node.type), left, right))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Emit condition mutations
|
||||||
|
#
|
||||||
|
# @return [undefined]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def mutate_operands
|
||||||
|
emit(s(node.type, n_not(left), right))
|
||||||
|
emit(n_not(node))
|
||||||
|
end
|
||||||
|
|
||||||
|
end # Binary
|
||||||
|
end # Connective
|
||||||
|
end # Node
|
||||||
|
end # Mutator
|
||||||
|
end # Mutant
|
28
lib/mutant/mutator/node/const.rb
Normal file
28
lib/mutant/mutator/node/const.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
module Mutant
|
||||||
|
class Mutator
|
||||||
|
class Node
|
||||||
|
|
||||||
|
# Mutation emitter to handle const nodes
|
||||||
|
class Const < self
|
||||||
|
|
||||||
|
handle(:const)
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Emit mutations
|
||||||
|
#
|
||||||
|
# @return [undefined]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def dispatch
|
||||||
|
emit_nil
|
||||||
|
children.each_with_index do |child, index|
|
||||||
|
mutate_child(index) if child.kind_of?(Parser::AST::Node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end # Const
|
||||||
|
end # Node
|
||||||
|
end # Mutator
|
||||||
|
end # Mutant
|
|
@ -1,20 +1,19 @@
|
||||||
module Mutant
|
module Mutant
|
||||||
class Mutator
|
class Mutator
|
||||||
class Node
|
class Node
|
||||||
|
|
||||||
# Generic mutator
|
# Generic mutator
|
||||||
class Generic < self
|
class Generic < self
|
||||||
|
|
||||||
handle(:self)
|
|
||||||
|
|
||||||
# These nodes still need a dedicated mutator,
|
# These nodes still need a dedicated mutator,
|
||||||
# your contribution is that close!
|
# your contribution is that close!
|
||||||
handle(
|
handle(
|
||||||
:zsuper, :not, :or, :and, :defined,
|
:defined,
|
||||||
:next, :break, :match, :gvar, :cvar, :ensure,
|
:next, :break, :match, :ensure,
|
||||||
:dstr, :dsym, :yield, :rescue, :redo, :defined?,
|
:dstr, :dsym, :yield, :rescue, :redo, :defined?,
|
||||||
:lvar, :const, :blockarg, :block_pass, :op_asgn, :and_asgn,
|
:blockarg, :block_pass, :op_asgn, :and_asgn,
|
||||||
:regopt, :ivar, :restarg, :casgn, :resbody, :retry, :arg_expr,
|
:regopt, :restarg, :resbody, :retry, :arg_expr,
|
||||||
:kwrestarg, :kwoptarg, :kwarg, :undef, :module, :cbase, :empty,
|
:kwrestarg, :kwoptarg, :kwarg, :undef, :module, :empty,
|
||||||
:alias, :for, :xstr, :back_ref, :nth_ref, :class,
|
:alias, :for, :xstr, :back_ref, :nth_ref, :class,
|
||||||
:sclass, :match_with_lvasgn, :match_current_line, :or_asgn, :kwbegin
|
:sclass, :match_with_lvasgn, :match_current_line, :or_asgn, :kwbegin
|
||||||
)
|
)
|
||||||
|
@ -29,8 +28,7 @@ module Mutant
|
||||||
#
|
#
|
||||||
def dispatch
|
def dispatch
|
||||||
children.each_with_index do |child, index|
|
children.each_with_index do |child, index|
|
||||||
next unless child.kind_of?(Parser::AST::Node)
|
mutate_child(index) if child.kind_of?(Parser::AST::Node)
|
||||||
mutate_child(index)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -30,9 +30,9 @@ module Mutant
|
||||||
#
|
#
|
||||||
def mutate_condition
|
def mutate_condition
|
||||||
emit_condition_mutations
|
emit_condition_mutations
|
||||||
emit_self(s(:send, condition, :!), if_branch, else_branch)
|
emit_self(n_not(condition), if_branch, else_branch)
|
||||||
emit_self(s(:true), if_branch, else_branch)
|
emit_self(N_TRUE, if_branch, else_branch)
|
||||||
emit_self(s(:false), if_branch, else_branch)
|
emit_self(N_FALSE, if_branch, else_branch)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Emit if branch mutations
|
# Emit if branch mutations
|
||||||
|
@ -45,7 +45,7 @@ module Mutant
|
||||||
emit_self(condition, else_branch, nil) if else_branch
|
emit_self(condition, else_branch, nil) if else_branch
|
||||||
if if_branch
|
if if_branch
|
||||||
emit_if_branch_mutations
|
emit_if_branch_mutations
|
||||||
emit_self(condition, if_branch, nil)
|
emit_self(condition, if_branch, nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -7,15 +7,21 @@ module Mutant
|
||||||
|
|
||||||
handle(:regexp)
|
handle(:regexp)
|
||||||
|
|
||||||
EMPTY_STRING = ''.freeze
|
|
||||||
|
|
||||||
# No input can ever be matched with this
|
# No input can ever be matched with this
|
||||||
NULL_REGEXP_SOURCE = 'a\A'.freeze
|
NULL_REGEXP_SOURCE = 'a\A'.freeze
|
||||||
|
|
||||||
children :source, :options
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
# Return options
|
||||||
|
#
|
||||||
|
# @return [Parser::AST::Node]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def options
|
||||||
|
children.last
|
||||||
|
end
|
||||||
|
|
||||||
# Emit mutants
|
# Emit mutants
|
||||||
#
|
#
|
||||||
# @return [undefined]
|
# @return [undefined]
|
||||||
|
@ -24,6 +30,9 @@ module Mutant
|
||||||
#
|
#
|
||||||
def dispatch
|
def dispatch
|
||||||
emit_nil
|
emit_nil
|
||||||
|
children.each_with_index do |child, index|
|
||||||
|
mutate_child(index) unless child.type == :str
|
||||||
|
end
|
||||||
emit_self(s(:str, EMPTY_STRING), options)
|
emit_self(s(:str, EMPTY_STRING), options)
|
||||||
emit_self(s(:str, NULL_REGEXP_SOURCE), options)
|
emit_self(s(:str, NULL_REGEXP_SOURCE), options)
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@ module Mutant
|
||||||
class Mutator
|
class Mutator
|
||||||
class Node
|
class Node
|
||||||
|
|
||||||
# Mutation emitter to handle multipl assignment nodes
|
# Mutation emitter to handle multiple assignment nodes
|
||||||
class MultipleAssignment < self
|
class MultipleAssignment < self
|
||||||
|
|
||||||
handle(:masgn)
|
handle(:masgn)
|
||||||
|
|
27
lib/mutant/mutator/node/named_value/access.rb
Normal file
27
lib/mutant/mutator/node/named_value/access.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
module Mutant
|
||||||
|
class Mutator
|
||||||
|
class Node
|
||||||
|
module NamedValue
|
||||||
|
|
||||||
|
# Mutation emitter to handle named value access nodes
|
||||||
|
class Access < Node
|
||||||
|
|
||||||
|
handle(:gvar, :cvar, :ivar, :lvar, :self)
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Emit mutations
|
||||||
|
#
|
||||||
|
# @return [undefined]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def dispatch
|
||||||
|
emit_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end # Access
|
||||||
|
end # NamedValue
|
||||||
|
end # Node
|
||||||
|
end # Mutator
|
||||||
|
end # Mutant
|
42
lib/mutant/mutator/node/named_value/constant_assignment.rb
Normal file
42
lib/mutant/mutator/node/named_value/constant_assignment.rb
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
module Mutant
|
||||||
|
class Mutator
|
||||||
|
class Node
|
||||||
|
module NamedValue
|
||||||
|
|
||||||
|
# Mutation emitter to handle constant assignment nodes
|
||||||
|
class ConstantAssignment < Node
|
||||||
|
|
||||||
|
children :cbase, :name, :value
|
||||||
|
|
||||||
|
handle :casgn
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Perform dispatch
|
||||||
|
#
|
||||||
|
# @return [undefined]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def dispatch
|
||||||
|
mutate_name
|
||||||
|
emit_value_mutations if value
|
||||||
|
end
|
||||||
|
|
||||||
|
# Emit name mutations
|
||||||
|
#
|
||||||
|
# @return [undefined]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def mutate_name
|
||||||
|
Mutator::Util::Symbol.each(name, self) do |name|
|
||||||
|
emit_name(name.upcase)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end # ConstantAssignment
|
||||||
|
end # NamedValue
|
||||||
|
end # Node
|
||||||
|
end # Mutator
|
||||||
|
end # Mutant
|
|
@ -1,11 +1,10 @@
|
||||||
module Mutant
|
module Mutant
|
||||||
class Mutator
|
class Mutator
|
||||||
class Node
|
class Node
|
||||||
# Mutator base class for assignments
|
module NamedValue
|
||||||
class Assignment < self
|
|
||||||
|
|
||||||
# Mutator for variable assignment
|
# Mutation emitter to handle variable assignment nodes
|
||||||
class Variable < self
|
class VariableAssignment < Node
|
||||||
|
|
||||||
children :name, :value
|
children :name, :value
|
||||||
|
|
||||||
|
@ -40,12 +39,12 @@ module Mutant
|
||||||
def mutate_name
|
def mutate_name
|
||||||
prefix = MAP.fetch(node.type)
|
prefix = MAP.fetch(node.type)
|
||||||
Mutator::Util::Symbol.each(name, self) do |name|
|
Mutator::Util::Symbol.each(name, self) do |name|
|
||||||
emit_name("#{prefix}#{name}")
|
emit_name(prefix + name.to_s)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end # Variable
|
end # VariableAssignment
|
||||||
end # Assignment
|
end # NamedValue
|
||||||
end # Node
|
end # Node
|
||||||
end # Mutator
|
end # Mutator
|
||||||
end # Mutant
|
end # Mutant
|
|
@ -2,7 +2,7 @@ module Mutant
|
||||||
class Mutator
|
class Mutator
|
||||||
class Node
|
class Node
|
||||||
|
|
||||||
# Mutator for super with parantheses
|
# Mutator for super with parentheses
|
||||||
class Super < self
|
class Super < self
|
||||||
|
|
||||||
handle(:super)
|
handle(:super)
|
||||||
|
|
25
lib/mutant/mutator/node/zsuper.rb
Normal file
25
lib/mutant/mutator/node/zsuper.rb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
module Mutant
|
||||||
|
class Mutator
|
||||||
|
class Node
|
||||||
|
|
||||||
|
# Mutator for super without parentheses
|
||||||
|
class ZSuper < self
|
||||||
|
|
||||||
|
handle(:zsuper)
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Emit mutations
|
||||||
|
#
|
||||||
|
# @return [undefined]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def dispatch
|
||||||
|
emit_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end # ZSuper
|
||||||
|
end # Node
|
||||||
|
end # Mutator
|
||||||
|
end # Mutant
|
|
@ -15,7 +15,6 @@ module Mutant
|
||||||
end
|
end
|
||||||
module_function :s
|
module_function :s
|
||||||
|
|
||||||
|
|
||||||
NAN = s(:send, s(:float, 0.0), :/, s(:args, s(:float, 0.0)))
|
NAN = s(:send, s(:float, 0.0), :/, s(:args, s(:float, 0.0)))
|
||||||
NEGATIVE_INFINITY = s(:send, s(:float, -1.0), :/, s(:args, s(:float, 0.0)))
|
NEGATIVE_INFINITY = s(:send, s(:float, -1.0), :/, s(:args, s(:float, 0.0)))
|
||||||
INFINITY = s(:send, s(:float, 1.0), :/, s(:args, s(:float, 0.0)))
|
INFINITY = s(:send, s(:float, 1.0), :/, s(:args, s(:float, 0.0)))
|
||||||
|
@ -23,8 +22,22 @@ module Mutant
|
||||||
|
|
||||||
RAISE = s(:send, nil, :raise)
|
RAISE = s(:send, nil, :raise)
|
||||||
|
|
||||||
|
N_TRUE = s(:true)
|
||||||
|
N_FALSE = s(:false)
|
||||||
N_NIL = s(:nil)
|
N_NIL = s(:nil)
|
||||||
N_EMPTY = s(:empty)
|
N_EMPTY = s(:empty)
|
||||||
|
|
||||||
|
# Build a negated boolean node
|
||||||
|
#
|
||||||
|
# @param [Parser::AST::Node] node
|
||||||
|
#
|
||||||
|
# @return [Parser::AST::Node]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def n_not(node)
|
||||||
|
s(:send, node, :!)
|
||||||
|
end
|
||||||
|
|
||||||
end # NodeHelpers
|
end # NodeHelpers
|
||||||
end # Mutant
|
end # Mutant
|
||||||
|
|
|
@ -45,6 +45,7 @@ module Mutant
|
||||||
info 'Overhead: %0.2f%%', overhead
|
info 'Overhead: %0.2f%%', overhead
|
||||||
status 'Coverage: %0.2f%%', coverage
|
status 'Coverage: %0.2f%%', coverage
|
||||||
status 'Alive: %s', amount_alive
|
status 'Alive: %s', amount_alive
|
||||||
|
print_generic_stats
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -60,6 +61,81 @@ module Mutant
|
||||||
object.subjects
|
object.subjects
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Walker for all ast nodes
|
||||||
|
class Walker
|
||||||
|
|
||||||
|
# Run walkter
|
||||||
|
#
|
||||||
|
# @param [Parser::AST::Node] root
|
||||||
|
#
|
||||||
|
# @return [self]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def self.run(root, &block)
|
||||||
|
new(root, block)
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
private_class_method :new
|
||||||
|
|
||||||
|
# Initialize and run walker
|
||||||
|
#
|
||||||
|
# @param [Parser::AST::Node] root
|
||||||
|
# @param [#call(node)] block
|
||||||
|
#
|
||||||
|
# @return [undefined]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def initialize(root, block)
|
||||||
|
@root, @block = root, block
|
||||||
|
dispatch(root)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Perform dispatch
|
||||||
|
#
|
||||||
|
# @return [undefined]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def dispatch(node)
|
||||||
|
@block.call(node)
|
||||||
|
node.children.grep(Parser::AST::Node).each(&method(:dispatch))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Print generic stats
|
||||||
|
#
|
||||||
|
# @return [undefined]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
def print_generic_stats
|
||||||
|
stats = generic_stats.to_a.sort_by(&:last)
|
||||||
|
info('Nodes handled by generic mutator (type:occurrences):')
|
||||||
|
stats.reverse_each do |type, amount|
|
||||||
|
info('%-10s: %d', type, amount)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return stats for nodes handled by generic mutator
|
||||||
|
#
|
||||||
|
# @return [Hash<Symbo, Fixnum>]
|
||||||
|
#
|
||||||
|
# @api private
|
||||||
|
#
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Return amount of subjects
|
# Return amount of subjects
|
||||||
#
|
#
|
||||||
# @return [Fixnum]
|
# @return [Fixnum]
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
Gem::Specification.new do |gem|
|
Gem::Specification.new do |gem|
|
||||||
gem.name = 'mutant'
|
gem.name = 'mutant'
|
||||||
gem.version = '0.3.0.beta17'
|
gem.version = '0.3.0.beta21'
|
||||||
gem.authors = [ 'Markus Schirp' ]
|
gem.authors = [ 'Markus Schirp' ]
|
||||||
gem.email = [ 'mbj@schirp-dso.com' ]
|
gem.email = [ 'mbj@schirp-dso.com' ]
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ RSpec.configure do |config|
|
||||||
config.include(CompressHelper)
|
config.include(CompressHelper)
|
||||||
config.include(ParserHelper)
|
config.include(ParserHelper)
|
||||||
config.include(Mutant::NodeHelpers)
|
config.include(Mutant::NodeHelpers)
|
||||||
config.mock_with :rspec do |config|
|
config.mock_with :rspec do |rspec|
|
||||||
config.syntax = [:expect, :should]
|
rspec.syntax = [:expect, :should]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,7 @@ describe Mutant::Killer::Rspec, '.new' do
|
||||||
|
|
||||||
let(:strategy) { double('Strategy', :spec_files => ['foo'], :error_stream => $stderr, :output_stream => $stdout) }
|
let(:strategy) { double('Strategy', :spec_files => ['foo'], :error_stream => $stderr, :output_stream => $stdout) }
|
||||||
let(:context) { double('Context') }
|
let(:context) { double('Context') }
|
||||||
let(:mutation) { double('Mutation', :subject => mutation_subject) }
|
let(:mutation) { double('Mutation', :subject => mutation_subject, :should_survive? => false) }
|
||||||
let(:mutation_subject) { double('Mutation Subject') }
|
let(:mutation_subject) { double('Mutation Subject') }
|
||||||
|
|
||||||
let(:object) { described_class }
|
let(:object) { described_class }
|
||||||
|
|
17
spec/unit/mutant/mutator/node/cbase/mutation_spec.rb
Normal file
17
spec/unit/mutant/mutator/node/cbase/mutation_spec.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Mutant::Mutator::Node::NamedValue::Access, 'cbase' do
|
||||||
|
|
||||||
|
before do
|
||||||
|
Mutant::Random.stub(:hex_string => :random)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:source) { '::A' }
|
||||||
|
|
||||||
|
let(:mutations) do
|
||||||
|
mutants = []
|
||||||
|
mutants << 'nil'
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like 'a mutator'
|
||||||
|
end
|
|
@ -0,0 +1,39 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Mutant::Mutator::Node::Connective::Binary, 'mutations' do
|
||||||
|
context 'and' do
|
||||||
|
let(:source) { 'true and false' }
|
||||||
|
|
||||||
|
let(:mutations) do
|
||||||
|
mutations = []
|
||||||
|
mutations << 'nil'
|
||||||
|
mutations << 'true'
|
||||||
|
mutations << 'false'
|
||||||
|
|
||||||
|
mutations << 'true or false'
|
||||||
|
|
||||||
|
mutations << 'not true and false'
|
||||||
|
mutations << 'not(true and false)'
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like 'a mutator'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'or' do
|
||||||
|
let(:source) { 'true or false' }
|
||||||
|
|
||||||
|
let(:mutations) do
|
||||||
|
mutations = []
|
||||||
|
mutations << 'nil'
|
||||||
|
mutations << 'true'
|
||||||
|
mutations << 'false'
|
||||||
|
|
||||||
|
mutations << 'true and false'
|
||||||
|
|
||||||
|
mutations << 'not true or false'
|
||||||
|
mutations << 'not(true or false)'
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like 'a mutator'
|
||||||
|
end
|
||||||
|
end
|
18
spec/unit/mutant/mutator/node/const/mutation_spec.rb
Normal file
18
spec/unit/mutant/mutator/node/const/mutation_spec.rb
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Mutant::Mutator::Node::NamedValue::Access, 'const' do
|
||||||
|
|
||||||
|
before do
|
||||||
|
Mutant::Random.stub(:hex_string => :random)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:source) { 'A::B' }
|
||||||
|
|
||||||
|
let(:mutations) do
|
||||||
|
mutants = []
|
||||||
|
mutants << 'nil'
|
||||||
|
mutants << 'nil::B'
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like 'a mutator'
|
||||||
|
end
|
|
@ -2,14 +2,31 @@ require 'spec_helper'
|
||||||
|
|
||||||
describe Mutant::Mutator::Node::Literal, 'regex' do
|
describe Mutant::Mutator::Node::Literal, 'regex' do
|
||||||
|
|
||||||
let(:source) { '/foo/' }
|
context 'literal' do
|
||||||
|
let(:source) { '/foo/' }
|
||||||
|
|
||||||
let(:mutations) do
|
let(:mutations) do
|
||||||
mutations = []
|
mutations = []
|
||||||
mutations << 'nil'
|
mutations << 'nil'
|
||||||
mutations << '//' # match all
|
mutations << '//' # match all
|
||||||
mutations << '/a\A/' # match nothing
|
mutations << '/a\A/' # match nothing
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like 'a mutator'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'interpolated' do
|
||||||
|
let(:source) { '/#{foo.bar}n/' }
|
||||||
|
|
||||||
|
let(:mutations) do
|
||||||
|
mutations = []
|
||||||
|
mutations << 'nil'
|
||||||
|
mutations << '//' # match all
|
||||||
|
mutations << '/#{foo}n/' # match all
|
||||||
|
mutations << '/a\A/' # match nothing
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like 'a mutator'
|
||||||
end
|
end
|
||||||
|
|
||||||
it_should_behave_like 'a mutator'
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,6 @@ describe Mutant::Mutator, 'masgn' do
|
||||||
Mutant::Random.stub(:hex_string => 'random')
|
Mutant::Random.stub(:hex_string => 'random')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
let(:source) { 'a, b = c, d' }
|
let(:source) { 'a, b = c, d' }
|
||||||
|
|
||||||
let(:mutations) do
|
let(:mutations) do
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Mutant::Mutator::Node::NamedValue::Access, 'mutations' do
|
||||||
|
|
||||||
|
before do
|
||||||
|
Mutant::Random.stub(:hex_string => :random)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'global variable' do
|
||||||
|
let(:source) { '$a = nil; $a' }
|
||||||
|
|
||||||
|
let(:mutations) do
|
||||||
|
mutants = []
|
||||||
|
mutants << '$a = nil; nil'
|
||||||
|
mutants << '$a = nil'
|
||||||
|
mutants << '$a'
|
||||||
|
mutants << '$a = ::Object.new; $a'
|
||||||
|
mutants << '$srandom = nil; $a'
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like 'a mutator'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'class variable' do
|
||||||
|
let(:source) { '@@a = nil; @@a' }
|
||||||
|
|
||||||
|
let(:mutations) do
|
||||||
|
mutants = []
|
||||||
|
mutants << '@@a = nil; nil'
|
||||||
|
mutants << '@@a = nil'
|
||||||
|
mutants << '@@a'
|
||||||
|
mutants << '@@a = ::Object.new; @@a'
|
||||||
|
mutants << '@@srandom = nil; @@a'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'instance variable' do
|
||||||
|
let(:source) { '@a = nil; @a' }
|
||||||
|
|
||||||
|
let(:mutations) do
|
||||||
|
mutants = []
|
||||||
|
mutants << '@a = nil; nil'
|
||||||
|
mutants << '@a = nil'
|
||||||
|
mutants << '@a'
|
||||||
|
mutants << '@a = ::Object.new; @a'
|
||||||
|
mutants << '@srandom = nil; @a'
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like 'a mutator'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'local variable' do
|
||||||
|
let(:source) { 'a = nil; a' }
|
||||||
|
|
||||||
|
let(:mutations) do
|
||||||
|
mutants = []
|
||||||
|
mutants << 'a = nil; nil'
|
||||||
|
mutants << 'a = nil'
|
||||||
|
mutants << 'a'
|
||||||
|
mutants << 'a = ::Object.new; a'
|
||||||
|
mutants << 'srandom = nil; a'
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like 'a mutator'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'self' do
|
||||||
|
let(:source) { 'self' }
|
||||||
|
|
||||||
|
let(:mutations) do
|
||||||
|
mutants = []
|
||||||
|
mutants << 'nil'
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like 'a mutator'
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,20 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Mutant::Mutator::Node::NamedValue::VariableAssignment, 'mutations' do
|
||||||
|
|
||||||
|
before do
|
||||||
|
Mutant::Random.stub(:hex_string => :random)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:source) { 'A = true' }
|
||||||
|
|
||||||
|
let(:mutations) do
|
||||||
|
mutations = []
|
||||||
|
|
||||||
|
mutations << 'SRANDOM = true'
|
||||||
|
mutations << 'A = false'
|
||||||
|
mutations << 'A = nil'
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like 'a mutator'
|
||||||
|
end
|
|
@ -1,6 +1,6 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe Mutant::Mutator::Node::Assignment, 'mutations' do
|
describe Mutant::Mutator::Node::NamedValue::VariableAssignment, 'mutations' do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
Mutant::Random.stub(:hex_string => :random)
|
Mutant::Random.stub(:hex_string => :random)
|
|
@ -9,6 +9,7 @@ describe Mutant::Mutator, 'send' do
|
||||||
let(:mutations) do
|
let(:mutations) do
|
||||||
mutations = []
|
mutations = []
|
||||||
mutations << 'foo ||= expression'
|
mutations << 'foo ||= expression'
|
||||||
|
mutations << 'nil.foo ||= expression'
|
||||||
end
|
end
|
||||||
|
|
||||||
it_should_behave_like 'a mutator'
|
it_should_behave_like 'a mutator'
|
||||||
|
@ -74,6 +75,7 @@ describe Mutant::Mutator, 'send' do
|
||||||
mutations = []
|
mutations = []
|
||||||
mutations << 'foo'
|
mutations << 'foo'
|
||||||
mutations << 'self'
|
mutations << 'self'
|
||||||
|
mutations << 'nil.foo'
|
||||||
end
|
end
|
||||||
|
|
||||||
it_should_behave_like 'a mutator'
|
it_should_behave_like 'a mutator'
|
||||||
|
@ -111,6 +113,7 @@ describe Mutant::Mutator, 'send' do
|
||||||
mutations = []
|
mutations = []
|
||||||
mutations << 'self.class'
|
mutations << 'self.class'
|
||||||
mutations << 'self.foo'
|
mutations << 'self.foo'
|
||||||
|
mutations << 'nil.class.foo'
|
||||||
end
|
end
|
||||||
|
|
||||||
it_should_behave_like 'a mutator'
|
it_should_behave_like 'a mutator'
|
||||||
|
@ -142,6 +145,7 @@ describe Mutant::Mutator, 'send' do
|
||||||
mutations << 'foo(nil)'
|
mutations << 'foo(nil)'
|
||||||
mutations << 'nil'
|
mutations << 'nil'
|
||||||
mutations << 'self.foo(::Object.new)'
|
mutations << 'self.foo(::Object.new)'
|
||||||
|
mutations << 'nil.foo(nil)'
|
||||||
end
|
end
|
||||||
|
|
||||||
it_should_behave_like 'a mutator'
|
it_should_behave_like 'a mutator'
|
||||||
|
@ -187,6 +191,8 @@ describe Mutant::Mutator, 'send' do
|
||||||
mutations = []
|
mutations = []
|
||||||
mutations << 'foo'
|
mutations << 'foo'
|
||||||
mutations << 'left - right'
|
mutations << 'left - right'
|
||||||
|
mutations << 'left / foo'
|
||||||
|
mutations << 'right / foo'
|
||||||
end
|
end
|
||||||
|
|
||||||
it_should_behave_like 'a mutator'
|
it_should_behave_like 'a mutator'
|
||||||
|
|
|
@ -5,7 +5,12 @@ describe Mutant::Mutator, 'super' do
|
||||||
context 'with no arguments' do
|
context 'with no arguments' do
|
||||||
let(:source) { 'super' }
|
let(:source) { 'super' }
|
||||||
|
|
||||||
it_should_behave_like 'a noop mutator'
|
let(:mutations) do
|
||||||
|
mutations = []
|
||||||
|
mutations << 'nil'
|
||||||
|
end
|
||||||
|
|
||||||
|
it_should_behave_like 'a mutator'
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with explicit empty arguments' do
|
context 'with explicit empty arguments' do
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
describe Mutant::Mutator, 'self' do
|
|
||||||
let(:source) { 'self' }
|
|
||||||
|
|
||||||
it_should_behave_like 'a noop mutator'
|
|
||||||
end
|
|
12
spec/unit/mutant/node_helpers/n_not_spec.rb
Normal file
12
spec/unit/mutant/node_helpers/n_not_spec.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Mutant::NodeHelpers, '#n_not' do
|
||||||
|
subject { object.n_not(node) }
|
||||||
|
|
||||||
|
let(:object) { Object.new.extend(described_class) }
|
||||||
|
let(:node) { described_class::N_TRUE }
|
||||||
|
|
||||||
|
it 'returns the negated node' do
|
||||||
|
expect(subject).to eq(parse('not true'))
|
||||||
|
end
|
||||||
|
end
|
|
@ -30,7 +30,10 @@ shared_examples_for 'a mutator' do
|
||||||
|
|
||||||
unless subject == expected_mutations
|
unless subject == expected_mutations
|
||||||
message = "Missing mutations: %s\nUnexpected mutations: %s" %
|
message = "Missing mutations: %s\nUnexpected mutations: %s" %
|
||||||
[expected_mutations - subject, subject - expected_mutations ].map(&:to_a).map(&:inspect)
|
[
|
||||||
|
expected_mutations - subject,
|
||||||
|
subject - expected_mutations
|
||||||
|
].map(&:to_a).map(&:inspect)
|
||||||
fail message
|
fail message
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,4 +6,6 @@ require 'test_app'
|
||||||
require 'rspec'
|
require 'rspec'
|
||||||
|
|
||||||
# require spec support files and shared behavior
|
# require spec support files and shared behavior
|
||||||
Dir[File.expand_path('../{support,shared}/**/*.rb', __FILE__)].each { |f| require f }
|
Dir[File.expand_path('../{support,shared}/**/*.rb', __FILE__)].each do |file|
|
||||||
|
require file
|
||||||
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe TestApp::Literal,'#string' do
|
describe TestApp::Literal, '#string' do
|
||||||
subject { object.string }
|
subject { object.string }
|
||||||
|
|
||||||
let(:object) { described_class.new }
|
let(:object) { described_class.new }
|
||||||
|
|
Loading…
Reference in a new issue