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.
Files changed (59) hide show
  1. data/.gitignore +5 -0
  2. data/.rvmrc +1 -1
  3. data/.travis.yml +6 -0
  4. data/Readme.md +13 -0
  5. data/exe/mutate +6 -0
  6. data/lib/mutant.rb +21 -0
  7. data/lib/mutant/extensions.rb +8 -0
  8. data/lib/mutant/formatter.rb +19 -0
  9. data/lib/mutant/implementation.rb +70 -0
  10. data/lib/mutant/literal.rb +134 -0
  11. data/lib/mutant/method.rb +31 -0
  12. data/lib/mutant/mutatee.rb +61 -0
  13. data/lib/mutant/mutation.rb +24 -0
  14. data/lib/mutant/mutator.rb +49 -0
  15. data/lib/mutant/node.rb +26 -0
  16. data/lib/mutant/random.rb +37 -0
  17. data/lib/mutant/reporter.rb +38 -0
  18. data/lib/mutant/runners/rspec.rb +26 -4
  19. data/lib/mutant/version.rb +1 -1
  20. data/mutant.gemspec +5 -3
  21. data/spec/functional/class_spec.rb +46 -0
  22. data/spec/functional/instance_method/array_spec.rb +53 -0
  23. data/spec/functional/instance_method/boolean_spec.rb +101 -0
  24. data/spec/functional/instance_method/call_spec.rb +161 -0
  25. data/spec/functional/instance_method/fixnum_spec.rb +53 -0
  26. data/spec/functional/instance_method/float_spec.rb +53 -0
  27. data/spec/functional/instance_method/hash_spec.rb +53 -0
  28. data/spec/functional/instance_method/if_spec.rb +57 -0
  29. data/spec/functional/instance_method/range_spec.rb +53 -0
  30. data/spec/functional/instance_method/regex_spec.rb +55 -0
  31. data/spec/functional/instance_method/string_spec.rb +53 -0
  32. data/spec/functional/instance_method/symbol_spec.rb +53 -0
  33. data/spec/functional/reporter/method_loaded_spec.rb +62 -0
  34. data/spec/functional/reporter/running_mutations_spec.rb +60 -0
  35. data/spec/functional/runners/rspec_spec.rb +26 -0
  36. data/spec/functional/singleton_method/array_spec.rb +53 -0
  37. data/spec/functional/singleton_method/boolean_spec.rb +101 -0
  38. data/spec/functional/singleton_method/call_spec.rb +161 -0
  39. data/spec/functional/singleton_method/fixnum_spec.rb +53 -0
  40. data/spec/functional/singleton_method/float_spec.rb +53 -0
  41. data/spec/functional/singleton_method/hash_spec.rb +53 -0
  42. data/spec/functional/singleton_method/if_spec.rb +57 -0
  43. data/spec/functional/singleton_method/range_spec.rb +53 -0
  44. data/spec/functional/singleton_method/regex_spec.rb +55 -0
  45. data/spec/functional/singleton_method/string_spec.rb +53 -0
  46. data/spec/functional/singleton_method/symbol_spec.rb +53 -0
  47. data/spec/mutant/extensions_spec.rb +13 -0
  48. data/spec/mutant/implementation_spec.rb +223 -0
  49. data/spec/mutant/literal_spec.rb +129 -0
  50. data/spec/mutant/mutatee_spec.rb +28 -0
  51. data/spec/mutant/node_spec.rb +41 -0
  52. data/spec/mutant/random_spec.rb +33 -0
  53. data/spec/mutant/reporter_spec.rb +17 -0
  54. data/spec/mutant_spec.rb +28 -0
  55. data/spec/spec_helper.rb +11 -3
  56. data/spec/support/example_group_helpers.rb +11 -0
  57. data/spec/support/example_helpers.rb +5 -0
  58. metadata +149 -81
  59. data/spec/functional/boolean_spec.rb +0 -49
@@ -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
@@ -2,11 +2,33 @@ require 'rspec/core'
2
2
 
3
3
  module Mutant
4
4
  module Runners
5
- module RSpec
6
- def self.run(args, output)
7
- raise ::RSpec::Core::Runner.run(args, $stderr, output).inspect
8
- output.puts 'passed'
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
+
@@ -1,3 +1,3 @@
1
1
  module Mutant
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
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 = ["jko170@gmail.com"]
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.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
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