Remove indeterministic mutations

A mutation test that passes/fails on code today should also pass/fail on
the same code tomorrow.

* Closes #173
* Closes #127
* Closes #27
This commit is contained in:
Markus Schirp 2014-05-10 14:32:18 +00:00
parent 9e0fe8b03b
commit caf6c368d0
27 changed files with 66 additions and 214 deletions

View file

@ -1,3 +1,3 @@
---
threshold: 18
total_score: 829
total_score: 839

View file

@ -5,7 +5,6 @@ require 'set'
require 'adamantium'
require 'ice_nine'
require 'abstract_type'
require 'securerandom'
require 'equalizer'
require 'digest/sha1'
require 'inflecto'
@ -83,7 +82,6 @@ require 'mutant/node_helpers'
require 'mutant/warning_filter'
require 'mutant/warning_expectation'
require 'mutant/constants'
require 'mutant/random'
require 'mutant/walker'
require 'mutant/require_highjack'
require 'mutant/mutator'

View file

@ -20,7 +20,6 @@ module Mutant
def dispatch
emit_nil
emit_values(values)
emit_new { new_self(Random.fixnum) }
end
# Return values to mutate against

View file

@ -21,7 +21,6 @@ module Mutant
emit_nil
emit_values(values)
emit_special_cases
emit_new { new_self(Random.float) }
end
SPECIAL = [

View file

@ -19,7 +19,6 @@ module Mutant
#
def dispatch
emit_nil
emit_new { new_self(Random.hex_string) }
end
end # String

View file

@ -9,7 +9,9 @@ module Mutant
handle(:sym)
PREFIX = 's'
children :value
PREFIX = '__mutant__'.freeze
private
@ -21,7 +23,9 @@ module Mutant
#
def dispatch
emit_nil
emit_new { new_self((PREFIX + Random.hex_string).to_sym) }
Mutator::Util::Symbol.each(value, self) do |value|
emit_self(value)
end
end
end # Symbol

View file

@ -10,11 +10,15 @@ module Mutant
children :name, :value
MAP = IceNine.deep_freeze(
map = {
gvasgn: '$',
cvasgn: '@@',
ivasgn: '@',
lvasgn: ''
}
MAP = IceNine.deep_freeze(
Hash[map.map { |type, prefix| [type, [prefix, /^#{Regexp.escape(prefix)}/]] }]
)
handle(*MAP.keys)
@ -40,8 +44,9 @@ module Mutant
# @api private
#
def mutate_name
prefix = MAP.fetch(node.type)
Mutator::Util::Symbol.each(name, self) do |name|
prefix, regexp = MAP.fetch(node.type)
stripped = name.to_s.sub(regexp, EMPTY_STRING)
Mutator::Util::Symbol.each(stripped, self) do |name|
emit_name(:"#{prefix}#{name}")
end
end

View file

@ -9,6 +9,8 @@ module Mutant
handle(::Symbol)
POSTFIX = '__mutant__'.freeze
private
# Emit mutations
@ -18,7 +20,7 @@ module Mutant
# @api private
#
def dispatch
emit_new { :"s#{Random.hex_string}" }
emit((input.to_s + POSTFIX).to_sym)
end
end # Symbol

View file

@ -1,38 +0,0 @@
# encoding: utf-8
module Mutant
# Module for generating random values
module Random
# Return random hex string
#
# @return [String]
#
# @api private
#
def self.hex_string
SecureRandom.hex(10)
end
# Return random fixnum
#
# @return [Fixnum]
#
# @api private
#
def self.fixnum
::Random.rand(1000)
end
# Return random float
#
# @return [Float]
#
# @api private
#
def self.float
::Random.rand
end
end # Random
end # Mutant

View file

@ -3,25 +3,17 @@
require 'spec_helper'
describe Mutant::Mutator::Node::OpAsgn, 'and_asgn' do
let(:random_fixnum) { 5 }
let(:random_string) { 'random' }
let(:source) { 'a &&= 1' }
let(:mutations) do
mutations = []
mutations << 'srandom &&= 1'
mutations << 'a__mutant__ &&= 1'
mutations << 'a &&= nil'
mutations << 'a &&= 0'
mutations << 'a &&= -1'
mutations << 'a &&= 2'
mutations << 'a &&= 5'
mutations << 'nil'
end
before do
Mutant::Random.stub(fixnum: random_fixnum, hex_string: random_string)
end
it_should_behave_like 'a mutator'
end

View file

@ -25,16 +25,12 @@ describe Mutant::Mutator, 'block' do
let(:source) { 'foo { |a, b| }' }
before do
Mutant::Random.stub(hex_string: 'random')
end
let(:mutations) do
mutations = []
mutations << 'foo'
mutations << 'foo { |a, b| raise }'
mutations << 'foo { |a, srandom| }'
mutations << 'foo { |srandom, b| }'
mutations << 'foo { |a, b__mutant__| }'
mutations << 'foo { |a__mutant__, b| }'
mutations << 'foo { |a| }'
mutations << 'foo { |b| }'
mutations << 'foo { || }'
@ -46,10 +42,6 @@ describe Mutant::Mutator, 'block' do
context 'with block pattern args' do
before do
Mutant::Random.stub(hex_string: 'random')
end
let(:source) { 'foo { |(a, b), c| }' }
let(:mutations) do
@ -61,9 +53,9 @@ describe Mutant::Mutator, 'block' do
mutations << 'foo { |(b), c| }'
mutations << 'foo { |(a, b)| }'
mutations << 'foo { |c| }'
mutations << 'foo { |(srandom, b), c| }'
mutations << 'foo { |(a, srandom), c| }'
mutations << 'foo { |(a, b), srandom| }'
mutations << 'foo { |(a__mutant__, b), c| }'
mutations << 'foo { |(a, b__mutant__), c| }'
mutations << 'foo { |(a, b), c__mutant__| }'
mutations << 'foo'
mutations << 'nil'
end
@ -73,10 +65,6 @@ describe Mutant::Mutator, 'block' do
context 'with mini block pattern arg' do
before do
Mutant::Random.stub(hex_string: 'random')
end
let(:source) { 'foo { |(a)| }' }
let(:mutations) do
@ -84,7 +72,7 @@ describe Mutant::Mutator, 'block' do
mutations << 'foo { || }'
mutations << 'foo { |a| }'
mutations << 'foo { |(a)| raise }'
mutations << 'foo { |(srandom)| }'
mutations << 'foo { |(a__mutant__)| }'
mutations << 'foo'
mutations << 'nil'
end

View file

@ -3,12 +3,6 @@
require 'spec_helper'
describe Mutant::Mutator::Node::Case do
let(:random_string) { 'random' }
before do
Mutant::Random.stub(hex_string: random_string)
end
context 'without condition' do
let(:source) do
<<-RUBY
@ -97,7 +91,7 @@ describe Mutant::Mutator::Node::Case do
end
RUBY
mutations << <<-RUBY
case :srandom
case :condition__mutant__
when :foo
when :bar, :baz
:barbaz
@ -121,7 +115,7 @@ describe Mutant::Mutator::Node::Case do
case :condition
when :foo
when :bar, :baz
:srandom
:barbaz__mutant__
else
:else
end
@ -141,7 +135,7 @@ describe Mutant::Mutator::Node::Case do
when :bar, :baz
:barbaz
else
:srandom
:else__mutant__
end
RUBY
mutations << <<-RUBY
@ -157,7 +151,7 @@ describe Mutant::Mutator::Node::Case do
# Mutations of when conditions
mutations << <<-RUBY
case :condition
when :srandom
when :foo__mutant__
when :bar, :baz
:barbaz
else
@ -176,7 +170,7 @@ describe Mutant::Mutator::Node::Case do
mutations << <<-RUBY
case :condition
when :foo
when :srandom, :baz
when :bar__mutant__, :baz
:barbaz
else
:else
@ -203,7 +197,7 @@ describe Mutant::Mutator::Node::Case do
mutations << <<-RUBY
case :condition
when :foo
when :bar, :srandom
when :bar, :baz__mutant__
:barbaz
else
:else
@ -267,7 +261,7 @@ describe Mutant::Mutator::Node::Case do
end
RUBY
mutations << <<-RUBY
case :srandom
case :condition__mutant__
when :foo
:foo
else
@ -287,7 +281,7 @@ describe Mutant::Mutator::Node::Case do
mutations << <<-RUBY
case :condition
when :foo
:srandom
:foo__mutant__
else
:else
end
@ -297,7 +291,7 @@ describe Mutant::Mutator::Node::Case do
when :foo
:foo
else
:srandom
:else__mutant__
end
RUBY
mutations << <<-RUBY
@ -312,7 +306,7 @@ describe Mutant::Mutator::Node::Case do
# Mutations of when conditions
mutations << <<-RUBY
case :condition
when :srandom
when :foo__mutant__
:foo
else
:else

View file

@ -55,10 +55,6 @@ describe Mutant::Mutator, 'def' do
context 'with arguments' do
let(:source) { 'def foo(a, b); end' }
before do
Mutant::Random.stub(hex_string: 'random')
end
let(:mutations) do
mutations = []
@ -70,8 +66,8 @@ describe Mutant::Mutator, 'def' do
mutations << 'def foo; end'
# Rename each argument
mutations << 'def foo(srandom, b); end'
mutations << 'def foo(a, srandom); end'
mutations << 'def foo(a__mutant__, b); end'
mutations << 'def foo(a, b__mutant__); end'
# Mutation of body
mutations << 'def foo(a, b); raise; end'
@ -110,17 +106,13 @@ describe Mutant::Mutator, 'def' do
context 'default argument' do
let(:source) { 'def foo(a = true); end' }
before do
Mutant::Random.stub(hex_string: 'random')
end
let(:mutations) do
mutations = []
mutations << 'def foo(a); end'
mutations << 'def foo(); end'
mutations << 'def foo(a = false); end'
mutations << 'def foo(a = nil); end'
mutations << 'def foo(srandom = true); end'
mutations << 'def foo(a__mutant__ = true); end'
mutations << 'def foo(a = true); raise; end'
end
@ -154,10 +146,6 @@ describe Mutant::Mutator, 'def' do
context 'define on singleton with argument' do
before do
Mutant::Random.stub(hex_string: 'random')
end
let(:source) { 'def self.foo(a, b); end' }
let(:mutations) do
@ -171,8 +159,8 @@ describe Mutant::Mutator, 'def' do
mutations << 'def self.foo; end'
# Rename each argument
mutations << 'def self.foo(srandom, b); end'
mutations << 'def self.foo(a, srandom); end'
mutations << 'def self.foo(a__mutant__, b); end'
mutations << 'def self.foo(a, b__mutant__); end'
# Mutation of body
mutations << 'def self.foo(a, b); raise; end'

View file

@ -3,17 +3,11 @@
require 'spec_helper'
describe Mutant::Mutator::Node::Dstr, 'dstr' do
before do
Mutant::Random.stub(hex_string: 'random')
end
let(:source) { '"foo#{bar}baz"' }
let(:mutations) do
mutations = []
mutations << '"random#{bar}baz"'
mutations << '"#{nil}#{bar}baz"'
mutations << '"foo#{bar}random"'
mutations << '"foo#{bar}#{nil}"'
mutations << '"foo#{nil}baz"'
mutations << 'nil'

View file

@ -3,17 +3,12 @@
require 'spec_helper'
describe Mutant::Mutator::Node::Dsym, 'dsym' do
before do
Mutant::Random.stub(hex_string: 'random')
end
let(:source) { ':"foo#{bar}baz"' }
let(:mutations) do
mutations = []
mutations << ':"random#{bar}baz"'
mutations << ':"#{nil}#{bar}baz"'
mutations << ':"foo#{bar}random"'
mutations << ':"foo#{bar}#{nil}"'
mutations << ':"foo#{nil}baz"'
mutations << 'nil'

View file

@ -3,10 +3,6 @@
require 'spec_helper'
describe Mutant::Mutator, 'if' do
before do
Mutant::Random.stub(hex_string: 'random')
end
context 'with if and else branches' do
let(:source) { 'if :condition; true; else false; end' }
@ -14,11 +10,11 @@ describe Mutant::Mutator, 'if' do
mutants = []
# mutations of condition
mutants << 'if :srandom; true; else false; end'
mutants << 'if !:condition; true; else false; end'
mutants << 'if nil; true; else false; end'
mutants << 'if true; true; else false; end'
mutants << 'if false; true; else false; end'
mutants << 'if :condition__mutant__; true; else false; end'
mutants << 'if !:condition; true; else false; end'
mutants << 'if nil; true; else false; end'
mutants << 'if true; true; else false; end'
mutants << 'if false; true; else false; end'
# Deleted else branch
mutants << 'if :condition; true end'
@ -65,14 +61,14 @@ describe Mutant::Mutator, 'if' do
let(:mutations) do
mutants = []
mutants << 'unless !:condition; true; end'
mutants << 'unless :srandom; true; end'
mutants << 'unless nil; true; end'
mutants << 'unless :condition; false; end'
mutants << 'unless :condition; nil; end'
mutants << 'unless true; true; end'
mutants << 'unless false; true; end'
mutants << 'if :condition; true; end'
mutants << 'unless !:condition; true; end'
mutants << 'unless :condition__mutant__; true; end'
mutants << 'unless nil; true; end'
mutants << 'unless :condition; false; end'
mutants << 'unless :condition; nil; end'
mutants << 'unless true; true; end'
mutants << 'unless false; true; end'
mutants << 'if :condition; true; end'
mutants << 'nil'
end

View file

@ -3,16 +3,10 @@
require 'spec_helper'
describe Mutant::Mutator::Node::Literal, 'fixnum' do
let(:random_fixnum) { 5 }
let(:source) { '10' }
let(:mutations) do
%W(nil 0 1 #{random_fixnum} -10 9 11)
end
before do
Mutant::Random.stub(fixnum: random_fixnum)
%W(nil 0 1 -10 9 11)
end
it_should_behave_like 'a mutator'

View file

@ -4,12 +4,6 @@ require 'spec_helper'
describe Mutant::Mutator::Node::Literal, 'float' do
before do
Mutant::Random.stub(float: random_float)
end
let(:random_float) { 7.123 }
context 'positive number' do
let(:source) { '10.0' }
@ -18,7 +12,6 @@ describe Mutant::Mutator::Node::Literal, 'float' do
mutations << 'nil'
mutations << '0.0'
mutations << '1.0'
mutations << random_float.to_s
mutations << '(0.0 / 0.0)'
mutations << '(1.0 / 0.0)'
mutations << '(-1.0 / 0.0)'
@ -35,7 +28,6 @@ describe Mutant::Mutator::Node::Literal, 'float' do
mutations = []
mutations << 'nil'
mutations << '1.0'
mutations << random_float.to_s
mutations << '(0.0 / 0.0)'
mutations << '(1.0 / 0.0)'
mutations << '(-1.0 / 0.0)'
@ -51,7 +43,6 @@ describe Mutant::Mutator::Node::Literal, 'float' do
mutations = []
mutations << 'nil'
mutations << '1.0'
mutations << random_float.to_s
mutations << '(0.0 / 0.0)'
mutations << '(1.0 / 0.0)'
mutations << '(-1.0 / 0.0)'

View file

@ -4,12 +4,6 @@ require 'spec_helper'
describe Mutant::Mutator::Node::Literal, 'range' do
before :each do
Mutant::Random.stub(fixnum: random_fixnum)
end
let(:random_fixnum) { 5 }
context 'inclusive range literal' do
let(:source) { '1..100' }
@ -21,7 +15,6 @@ describe Mutant::Mutator::Node::Literal, 'range' do
mutations << '1..(1.0 / 0.0)'
mutations << '1..(0.0 / 0.0)'
mutations << '-1..100'
mutations << '5..100'
mutations << '0..100'
mutations << '2..100'
mutations << 'nil..100'
@ -31,7 +24,6 @@ describe Mutant::Mutator::Node::Literal, 'range' do
mutations << '1..99'
mutations << '1..101'
mutations << '1..-100'
mutations << '1..5'
end
it_should_behave_like 'a mutator'
@ -48,7 +40,6 @@ describe Mutant::Mutator::Node::Literal, 'range' do
mutations << '1...(1.0 / 0.0)'
mutations << '1...(0.0 / 0.0)'
mutations << '-1...100'
mutations << '5...100'
mutations << '0...100'
mutations << '2...100'
mutations << 'nil...100'
@ -58,7 +49,6 @@ describe Mutant::Mutator::Node::Literal, 'range' do
mutations << '1...99'
mutations << '1...101'
mutations << '1...-100'
mutations << '1...5'
end
it_should_behave_like 'a mutator'

View file

@ -8,11 +8,7 @@ describe Mutant::Mutator::Node::Literal, 'string' do
let(:source) { '"foo"' }
let(:mutations) do
%W(nil "#{random_string}")
end
before do
Mutant::Random.stub(hex_string: random_string)
%W(nil)
end
it_should_behave_like 'a mutator'

View file

@ -8,11 +8,7 @@ describe Mutant::Mutator::Node::Literal, 'symbol' do
let(:source) { ':foo' }
let(:mutations) do
%w(nil) << ":s#{random_string}"
end
before do
Mutant::Random.stub(hex_string: random_string)
%w(nil) << ':foo__mutant__'
end
it_should_behave_like 'a mutator'

View file

@ -3,9 +3,6 @@
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' }
@ -15,7 +12,7 @@ describe Mutant::Mutator::Node::NamedValue::Access, 'mutations' do
mutants << '$a = nil; nil'
mutants << '$a = nil'
mutants << '$a'
mutants << '$srandom = nil; $a'
mutants << '$a__mutant__ = nil; $a'
mutants << 'nil; $a'
end
@ -30,7 +27,7 @@ describe Mutant::Mutator::Node::NamedValue::Access, 'mutations' do
mutants << '@@a = nil; nil'
mutants << '@@a = nil'
mutants << '@@a'
mutants << '@@srandom = nil; @@a'
mutants << '@@a__mutant__ = nil; @@a'
mutants << 'nil; @@a'
end
end
@ -43,7 +40,7 @@ describe Mutant::Mutator::Node::NamedValue::Access, 'mutations' do
mutants << '@a = nil; nil'
mutants << '@a = nil'
mutants << '@a'
mutants << '@srandom = nil; @a'
mutants << '@a__mutant__ = nil; @a'
mutants << 'nil; @a'
end
@ -60,7 +57,7 @@ describe Mutant::Mutator::Node::NamedValue::Access, 'mutations' do
# TODO: fix invalid AST
# These ASTs are not valid and should NOT be emitted
# Mutations of lvarasgn need to be special cased to avoid this.
mutants << s(:begin, s(:lvasgn, :srandom, s(:nil)), s(:lvar, :a))
mutants << s(:begin, s(:lvasgn, :a__mutant__, s(:nil)), s(:lvar, :a))
mutants << s(:begin, s(:nil), s(:lvar, :a))
mutants << s(:lvar, :a)
end

View file

@ -3,15 +3,11 @@
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__MUTANT__ = true'
mutations << 'A = false'
mutations << 'A = nil'
end

View file

@ -3,16 +3,12 @@
require 'spec_helper'
describe Mutant::Mutator::Node::NamedValue::VariableAssignment, 'mutations' do
before do
Mutant::Random.stub(hex_string: 'random')
end
context 'global variable' do
let(:source) { '$a = true' }
let(:mutations) do
mutations = []
mutations << '$srandom = true'
mutations << '$a__mutant__ = true'
mutations << '$a = false'
mutations << '$a = nil'
mutations << 'nil'
@ -26,7 +22,7 @@ describe Mutant::Mutator::Node::NamedValue::VariableAssignment, 'mutations' do
let(:mutations) do
mutations = []
mutations << '@@srandom = true'
mutations << '@@a__mutant__ = true'
mutations << '@@a = false'
mutations << '@@a = nil'
mutations << 'nil'
@ -40,7 +36,7 @@ describe Mutant::Mutator::Node::NamedValue::VariableAssignment, 'mutations' do
let(:mutations) do
mutations = []
mutations << '@srandom = true'
mutations << '@a__mutant__ = true'
mutations << '@a = false'
mutations << '@a = nil'
mutations << 'nil'
@ -54,7 +50,7 @@ describe Mutant::Mutator::Node::NamedValue::VariableAssignment, 'mutations' do
let(:mutations) do
mutations = []
mutations << 'srandom = true'
mutations << 'a__mutant__ = true'
mutations << 'a = false'
mutations << 'a = nil'
mutations << 'nil'

View file

@ -3,8 +3,6 @@
require 'spec_helper'
describe Mutant::Mutator::Node::Generic, 'op_asgn' do
let(:random_fixnum) { 5 }
let(:source) { '@a.b += 1' }
let(:mutations) do
@ -13,7 +11,6 @@ describe Mutant::Mutator::Node::Generic, 'op_asgn' do
mutations << '@a.b += 2'
mutations << '@a.b += 0'
mutations << '@a.b += nil'
mutations << '@a.b += 5'
mutations << 'nil.b += 1'
mutations << 'nil'
# TODO: fix invalid AST
@ -21,9 +18,5 @@ describe Mutant::Mutator::Node::Generic, 'op_asgn' do
mutations << s(:op_asgn, s(:ivar, :@a), :+, s(:int, 1))
end
before do
Mutant::Random.stub(fixnum: random_fixnum)
end
it_should_behave_like 'a mutator'
end

View file

@ -3,25 +3,17 @@
require 'spec_helper'
describe Mutant::Mutator::Node::OpAsgn, 'or_asgn' do
let(:random_fixnum) { 5 }
let(:random_string) { 'random' }
let(:source) { 'a ||= 1' }
let(:mutations) do
mutations = []
mutations << 'srandom ||= 1'
mutations << 'a__mutant__ ||= 1'
mutations << 'a ||= nil'
mutations << 'a ||= 0'
mutations << 'a ||= -1'
mutations << 'a ||= 2'
mutations << 'a ||= 5'
mutations << 'nil'
end
before do
Mutant::Random.stub(fixnum: random_fixnum, hex_string: random_string)
end
it_should_behave_like 'a mutator'
end

View file

@ -4,10 +4,6 @@ require 'spec_helper'
describe Mutant::Mutator::Node::Generic, 'rescue' do
before do
Mutant::Random.stub(hex_string: 'random')
end
context 'multiple exception selectors and assignment' do
let(:source) { 'begin; rescue ExceptionA, ExceptionB => error; true; end' }