mutant 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/.rvmrc +1 -1
- data/.travis.yml +6 -0
- data/Readme.md +13 -0
- data/exe/mutate +6 -0
- data/lib/mutant.rb +21 -0
- data/lib/mutant/extensions.rb +8 -0
- data/lib/mutant/formatter.rb +19 -0
- data/lib/mutant/implementation.rb +70 -0
- data/lib/mutant/literal.rb +134 -0
- data/lib/mutant/method.rb +31 -0
- data/lib/mutant/mutatee.rb +61 -0
- data/lib/mutant/mutation.rb +24 -0
- data/lib/mutant/mutator.rb +49 -0
- data/lib/mutant/node.rb +26 -0
- data/lib/mutant/random.rb +37 -0
- data/lib/mutant/reporter.rb +38 -0
- data/lib/mutant/runners/rspec.rb +26 -4
- data/lib/mutant/version.rb +1 -1
- data/mutant.gemspec +5 -3
- data/spec/functional/class_spec.rb +46 -0
- data/spec/functional/instance_method/array_spec.rb +53 -0
- data/spec/functional/instance_method/boolean_spec.rb +101 -0
- data/spec/functional/instance_method/call_spec.rb +161 -0
- data/spec/functional/instance_method/fixnum_spec.rb +53 -0
- data/spec/functional/instance_method/float_spec.rb +53 -0
- data/spec/functional/instance_method/hash_spec.rb +53 -0
- data/spec/functional/instance_method/if_spec.rb +57 -0
- data/spec/functional/instance_method/range_spec.rb +53 -0
- data/spec/functional/instance_method/regex_spec.rb +55 -0
- data/spec/functional/instance_method/string_spec.rb +53 -0
- data/spec/functional/instance_method/symbol_spec.rb +53 -0
- data/spec/functional/reporter/method_loaded_spec.rb +62 -0
- data/spec/functional/reporter/running_mutations_spec.rb +60 -0
- data/spec/functional/runners/rspec_spec.rb +26 -0
- data/spec/functional/singleton_method/array_spec.rb +53 -0
- data/spec/functional/singleton_method/boolean_spec.rb +101 -0
- data/spec/functional/singleton_method/call_spec.rb +161 -0
- data/spec/functional/singleton_method/fixnum_spec.rb +53 -0
- data/spec/functional/singleton_method/float_spec.rb +53 -0
- data/spec/functional/singleton_method/hash_spec.rb +53 -0
- data/spec/functional/singleton_method/if_spec.rb +57 -0
- data/spec/functional/singleton_method/range_spec.rb +53 -0
- data/spec/functional/singleton_method/regex_spec.rb +55 -0
- data/spec/functional/singleton_method/string_spec.rb +53 -0
- data/spec/functional/singleton_method/symbol_spec.rb +53 -0
- data/spec/mutant/extensions_spec.rb +13 -0
- data/spec/mutant/implementation_spec.rb +223 -0
- data/spec/mutant/literal_spec.rb +129 -0
- data/spec/mutant/mutatee_spec.rb +28 -0
- data/spec/mutant/node_spec.rb +41 -0
- data/spec/mutant/random_spec.rb +33 -0
- data/spec/mutant/reporter_spec.rb +17 -0
- data/spec/mutant_spec.rb +28 -0
- data/spec/spec_helper.rb +11 -3
- data/spec/support/example_group_helpers.rb +11 -0
- data/spec/support/example_helpers.rb +5 -0
- metadata +149 -81
- data/spec/functional/boolean_spec.rb +0 -49
data/lib/mutant/node.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Mutant
|
2
|
+
class Node
|
3
|
+
attr_reader :item, :copy
|
4
|
+
|
5
|
+
def initialize(item)
|
6
|
+
@item = item
|
7
|
+
@copy = item.clone
|
8
|
+
end
|
9
|
+
|
10
|
+
def line
|
11
|
+
item.line
|
12
|
+
end
|
13
|
+
|
14
|
+
def from
|
15
|
+
@from ||= Formatter.new(item)
|
16
|
+
end
|
17
|
+
|
18
|
+
def to
|
19
|
+
@to ||= Formatter.new(copy)
|
20
|
+
end
|
21
|
+
|
22
|
+
def swap
|
23
|
+
@copy = Literal.new(copy).swap
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Mutant
|
2
|
+
class Random
|
3
|
+
ALLOWED_SYMBOL_CHARACTERS = Array('a'..'z') + Array('A'..'Z')
|
4
|
+
RANDOM_METHOD = Rubinius.ruby19? ? :sample : :choice
|
5
|
+
|
6
|
+
def self.string
|
7
|
+
ENV.fetch('RANDOM_STRING') {
|
8
|
+
Array.new(rand(50)) { rand(126).chr }.join
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.symbol
|
13
|
+
ENV.fetch('RANDOM_SYMBOL') {
|
14
|
+
Array.new(rand(50).next) { ALLOWED_SYMBOL_CHARACTERS.send RANDOM_METHOD }.join
|
15
|
+
}.to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.fixnum
|
19
|
+
ENV.fetch('RANDOM_FIXNUM') { rand(100) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.float
|
23
|
+
ENV.fetch('RANDOM_FLOAT') { (rand(100) + 0.5) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.range
|
27
|
+
if ENV['RANDOM_RANGE_MIN'] and ENV['RANDOM_RANGE_MAX']
|
28
|
+
min, max = ENV['RANDOM_RANGE_MIN'].to_i, ENV['RANDOM_RANGE_MAX'].to_i
|
29
|
+
else
|
30
|
+
min, max = rand(50)
|
31
|
+
max = min + rand(50)
|
32
|
+
end
|
33
|
+
|
34
|
+
Range.new(min, max)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Mutant
|
2
|
+
class Reporter
|
3
|
+
WIDTH = 70
|
4
|
+
|
5
|
+
def self.mutating(mutation)
|
6
|
+
puts "Mutating line #{mutation.line}"
|
7
|
+
puts " #{mutation.from} >>>#{mutation.to.nested? ? "\n " : " "}#{mutation.to}"
|
8
|
+
puts
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.method_loaded(mutatee)
|
12
|
+
info "#{mutatee} loaded with #{mutatee.mutations.size} " \
|
13
|
+
"possible #{pluralize(mutatee.mutations.size, 'mutation')}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.no_mutations(mutatee)
|
17
|
+
warning "#{mutatee} has no possible mutations"
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.info(message)
|
21
|
+
puts "*" * WIDTH
|
22
|
+
puts "*** #{message}"
|
23
|
+
puts "*" * WIDTH
|
24
|
+
puts
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.warning(message)
|
28
|
+
puts "!" * WIDTH
|
29
|
+
puts "!!! #{message}"
|
30
|
+
puts "!" * WIDTH
|
31
|
+
puts
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.pluralize(count, singular, plural = nil)
|
35
|
+
count == 1 ? singular : plural || singular.insert(-1, 's')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/mutant/runners/rspec.rb
CHANGED
@@ -2,11 +2,33 @@ require 'rspec/core'
|
|
2
2
|
|
3
3
|
module Mutant
|
4
4
|
module Runners
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
class RSpec
|
6
|
+
ABORT_MESSAGE = 'Initial run of specs failed... fix and run mutant again'
|
7
|
+
|
8
|
+
def self.run(args)
|
9
|
+
runner = new(args)
|
10
|
+
runner.initial_run
|
11
|
+
runner.mutate
|
12
|
+
puts runner.run.zero? ? 'failed' : 'passed'
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(args)
|
16
|
+
@implementation = Implementation.new(args.shift)
|
17
|
+
@args = args
|
18
|
+
end
|
19
|
+
|
20
|
+
def initial_run
|
21
|
+
abort ABORT_MESSAGE unless run.zero?
|
22
|
+
end
|
23
|
+
|
24
|
+
def run
|
25
|
+
::RSpec::Core::Runner.run(@args)
|
26
|
+
end
|
27
|
+
|
28
|
+
def mutate
|
29
|
+
Mutant.mutate(@implementation)
|
9
30
|
end
|
10
31
|
end
|
11
32
|
end
|
12
33
|
end
|
34
|
+
|
data/lib/mutant/version.rb
CHANGED
data/mutant.gemspec
CHANGED
@@ -5,8 +5,8 @@ require "mutant/version"
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = 'mutant'
|
7
7
|
s.version = Mutant::VERSION
|
8
|
-
s.authors = ["Justin Ko"]
|
9
|
-
s.email = ["
|
8
|
+
s.authors = ["Justin Ko", "Josep M. Bach"]
|
9
|
+
s.email = ["justin@kospecinc.com", "josep.m.bach@gmail.com"]
|
10
10
|
s.homepage = ""
|
11
11
|
s.summary = %q{Mutation tester}
|
12
12
|
s.description = %q{Mutation tester}
|
@@ -15,9 +15,11 @@ Gem::Specification.new do |s|
|
|
15
15
|
|
16
16
|
s.files = `git ls-files`.split("\n")
|
17
17
|
s.test_files = `git ls-files -- spec/*`.split("\n")
|
18
|
-
s.
|
18
|
+
s.bindir = 'exe'
|
19
|
+
s.executables = `git ls-files -- exe/*`.split("\n").map{ |f| File.basename(f) }
|
19
20
|
s.require_paths = ["lib"]
|
20
21
|
|
22
|
+
s.add_runtime_dependency 'to_source'
|
21
23
|
s.add_development_dependency 'rake'
|
22
24
|
s.add_development_dependency 'rspec', '~> 2.7'
|
23
25
|
s.add_development_dependency 'aruba'
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Mutating a class' do
|
4
|
+
before do
|
5
|
+
write_file 'thing.rb', """
|
6
|
+
class Thing
|
7
|
+
def alive?; true end
|
8
|
+
def dead?; false end
|
9
|
+
def mood; :happy end
|
10
|
+
def gender; 'male' end
|
11
|
+
def alphabet_range; 'a'..'k' end
|
12
|
+
end
|
13
|
+
"""
|
14
|
+
write_file 'spec/thing_spec.rb', """
|
15
|
+
$: << '.'
|
16
|
+
require 'thing'
|
17
|
+
|
18
|
+
describe Thing do
|
19
|
+
describe '#alive?' do
|
20
|
+
specify { Thing.new.should be_alive }
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#dead?' do
|
24
|
+
specify { Thing.new.should_not be_dead }
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#mood' do
|
28
|
+
specify { Thing.new.mood.should eq(:happy) }
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#gender' do
|
32
|
+
specify { Thing.new.gender.should eq('male') }
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#alphabet_range' do
|
36
|
+
specify { Thing.new.alphabet_range.should eq('a'..'k') }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
"""
|
40
|
+
mutate 'Thing spec/thing_spec.rb'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'runs all possible mutations' do
|
44
|
+
all_output.should include('passed')
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Mutating arrays' do
|
4
|
+
context 'for an instance method' do
|
5
|
+
context 'that contains [1,2,3]' do
|
6
|
+
before do
|
7
|
+
write_file 'thing.rb', """
|
8
|
+
class Thing
|
9
|
+
def to_a
|
10
|
+
[1,2,3]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
"""
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'with an expectation that the array is [1,2,3]' do
|
17
|
+
before do
|
18
|
+
write_file 'spec/thing_spec.rb', """
|
19
|
+
$: << '.'
|
20
|
+
require 'thing'
|
21
|
+
|
22
|
+
describe 'Thing#to_a' do
|
23
|
+
specify { Thing.new.to_a.should eq([1,2,3]) }
|
24
|
+
end
|
25
|
+
"""
|
26
|
+
mutate 'Thing#to_a spec/thing_spec.rb'
|
27
|
+
end
|
28
|
+
|
29
|
+
specify 'the mutation passes' do
|
30
|
+
all_output.should include('passed')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'with an expectation that the array responds to length' do
|
35
|
+
before do
|
36
|
+
write_file 'spec/thing_spec.rb', """
|
37
|
+
$: << '.'
|
38
|
+
require 'thing'
|
39
|
+
|
40
|
+
describe 'Thing#to_a' do
|
41
|
+
specify { Thing.new.to_a.should respond_to(:length) }
|
42
|
+
end
|
43
|
+
"""
|
44
|
+
mutate 'Thing#to_a spec/thing_spec.rb'
|
45
|
+
end
|
46
|
+
|
47
|
+
specify 'the mutation fails' do
|
48
|
+
all_output.should include('failed')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Mutating booleans' do
|
4
|
+
context 'for an instance method' do
|
5
|
+
context 'that contains `true`' do
|
6
|
+
before do
|
7
|
+
write_file 'thing.rb', """
|
8
|
+
class Thing
|
9
|
+
def alive?
|
10
|
+
true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
"""
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'with an expectation that the return value is true' do
|
17
|
+
before do
|
18
|
+
write_file 'spec/thing_spec.rb', """
|
19
|
+
$: << '.'
|
20
|
+
require 'thing'
|
21
|
+
|
22
|
+
describe 'Thing#alive?' do
|
23
|
+
specify { Thing.new.should be_alive }
|
24
|
+
end
|
25
|
+
"""
|
26
|
+
mutate 'Thing#alive? spec/thing_spec.rb'
|
27
|
+
end
|
28
|
+
|
29
|
+
specify 'the mutation passes' do
|
30
|
+
all_output.should include('passed')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'with an expectation that the return value is true or false' do
|
35
|
+
before do
|
36
|
+
write_file 'spec/thing_spec.rb', """
|
37
|
+
$: << '.'
|
38
|
+
require 'thing'
|
39
|
+
|
40
|
+
describe 'Thing#alive?' do
|
41
|
+
specify { String(Thing.new.alive?).should =~ /true|false/ }
|
42
|
+
end
|
43
|
+
"""
|
44
|
+
mutate 'Thing#alive? spec/thing_spec.rb'
|
45
|
+
end
|
46
|
+
|
47
|
+
specify 'the mutation fails' do
|
48
|
+
all_output.should include('failed')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'that contains `false`' do
|
54
|
+
before do
|
55
|
+
write_file 'thing.rb', """
|
56
|
+
class Thing
|
57
|
+
def alive?
|
58
|
+
false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
"""
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'with an expectation that the return value is false' do
|
65
|
+
before do
|
66
|
+
write_file 'spec/thing_spec.rb', """
|
67
|
+
$: << '.'
|
68
|
+
require 'thing'
|
69
|
+
|
70
|
+
describe 'Thing.alive?' do
|
71
|
+
specify { Thing.new.should_not be_alive }
|
72
|
+
end
|
73
|
+
"""
|
74
|
+
mutate 'Thing#alive? spec/thing_spec.rb'
|
75
|
+
end
|
76
|
+
|
77
|
+
specify 'the mutation passes' do
|
78
|
+
all_output.should include('passed')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'with an expectation that the return value is true or false' do
|
83
|
+
before do
|
84
|
+
write_file 'spec/thing_spec.rb', """
|
85
|
+
$: << '.'
|
86
|
+
require 'thing'
|
87
|
+
|
88
|
+
describe 'Thing#alive?' do
|
89
|
+
specify { String(Thing.new.alive?).should =~ /true|false/ }
|
90
|
+
end
|
91
|
+
"""
|
92
|
+
mutate 'Thing#alive? spec/thing_spec.rb'
|
93
|
+
end
|
94
|
+
|
95
|
+
specify 'the mutation fails' do
|
96
|
+
all_output.should include('failed')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Mutating calls' do
|
4
|
+
context 'for an instance method' do
|
5
|
+
context 'that contains a method call without arguments' do
|
6
|
+
before do
|
7
|
+
write_file 'life.rb', """
|
8
|
+
class Life
|
9
|
+
def helper
|
10
|
+
42
|
11
|
+
end
|
12
|
+
|
13
|
+
def answer
|
14
|
+
helper
|
15
|
+
end
|
16
|
+
end
|
17
|
+
"""
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'with an expectation that the answer is 42' do
|
21
|
+
before do
|
22
|
+
write_file 'spec/life_spec.rb', """
|
23
|
+
$: << '.'
|
24
|
+
require 'life'
|
25
|
+
|
26
|
+
describe 'Life#answer' do
|
27
|
+
specify { Life.new.answer.should eq(42) }
|
28
|
+
end
|
29
|
+
"""
|
30
|
+
run_simple '../../bin/mutate Life#answer spec/life_spec.rb'
|
31
|
+
end
|
32
|
+
|
33
|
+
specify 'the mutation passes' do
|
34
|
+
all_output.should include('passed')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'with an expectation that life respond to :answer' do
|
39
|
+
before do
|
40
|
+
write_file 'spec/life_spec.rb', """
|
41
|
+
$: << '.'
|
42
|
+
require 'life'
|
43
|
+
|
44
|
+
describe 'Life#answer' do
|
45
|
+
specify { Life.new.should respond_to(:answer) }
|
46
|
+
end
|
47
|
+
"""
|
48
|
+
run_simple '../../bin/mutate Life#answer spec/life_spec.rb'
|
49
|
+
end
|
50
|
+
|
51
|
+
specify 'the mutation fails' do
|
52
|
+
all_output.should include('failed')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'that contains a method call with arguments' do
|
58
|
+
before do
|
59
|
+
write_file 'life.rb', """
|
60
|
+
class Life
|
61
|
+
def helper(num)
|
62
|
+
num + 2
|
63
|
+
end
|
64
|
+
|
65
|
+
def answer
|
66
|
+
helper(40)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
"""
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with an expectation that the answer is 42' do
|
73
|
+
before do
|
74
|
+
write_file 'spec/life_spec.rb', """
|
75
|
+
$: << '.'
|
76
|
+
require 'life'
|
77
|
+
|
78
|
+
describe 'Life#answer' do
|
79
|
+
specify { Life.new.answer.should eq(42) }
|
80
|
+
end
|
81
|
+
"""
|
82
|
+
run_simple '../../bin/mutate Life#answer spec/life_spec.rb'
|
83
|
+
end
|
84
|
+
|
85
|
+
specify 'the mutation passes' do
|
86
|
+
all_output.should include('passed')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'with an expectation that life respond to :answer' do
|
91
|
+
before do
|
92
|
+
write_file 'spec/life_spec.rb', """
|
93
|
+
$: << '.'
|
94
|
+
require 'life'
|
95
|
+
|
96
|
+
describe 'Life#answer' do
|
97
|
+
specify { Life.new.should respond_to(:answer) }
|
98
|
+
end
|
99
|
+
"""
|
100
|
+
run_simple '../../bin/mutate Life#answer spec/life_spec.rb'
|
101
|
+
end
|
102
|
+
|
103
|
+
specify 'the mutation fails' do
|
104
|
+
all_output.should include('failed')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'that contains a method call with a block' do
|
110
|
+
before do
|
111
|
+
write_file 'life.rb', """
|
112
|
+
class Life
|
113
|
+
def helper
|
114
|
+
yield
|
115
|
+
end
|
116
|
+
|
117
|
+
def answer
|
118
|
+
helper { 42 }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
"""
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'with an expectation that the answer is 42' do
|
125
|
+
before do
|
126
|
+
write_file 'spec/life_spec.rb', """
|
127
|
+
$: << '.'
|
128
|
+
require 'life'
|
129
|
+
|
130
|
+
describe 'Life#answer' do
|
131
|
+
specify { Life.new.answer.should eq(42) }
|
132
|
+
end
|
133
|
+
"""
|
134
|
+
run_simple '../../bin/mutate Life#answer spec/life_spec.rb'
|
135
|
+
end
|
136
|
+
|
137
|
+
specify 'the mutation passes' do
|
138
|
+
all_output.should include('passed')
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'with an expectation that life respond to :answer' do
|
143
|
+
before do
|
144
|
+
write_file 'spec/life_spec.rb', """
|
145
|
+
$: << '.'
|
146
|
+
require 'life'
|
147
|
+
|
148
|
+
describe 'Life#answer' do
|
149
|
+
specify { Life.new.should respond_to(:answer) }
|
150
|
+
end
|
151
|
+
"""
|
152
|
+
run_simple '../../bin/mutate Life#answer spec/life_spec.rb'
|
153
|
+
end
|
154
|
+
|
155
|
+
specify 'the mutation fails' do
|
156
|
+
all_output.should include('failed')
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|