mutant 0.3.0.beta22 → 0.3.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -5
  3. data/Gemfile.devtools +1 -1
  4. data/README.md +22 -50
  5. data/config/flay.yml +1 -1
  6. data/config/flog.yml +1 -1
  7. data/config/reek.yml +9 -2
  8. data/lib/mutant.rb +3 -4
  9. data/lib/mutant/cli.rb +41 -35
  10. data/lib/mutant/cli/classifier.rb +1 -1
  11. data/lib/mutant/config.rb +2 -1
  12. data/lib/mutant/context/scope.rb +14 -1
  13. data/lib/mutant/killer.rb +10 -0
  14. data/lib/mutant/killer/rspec.rb +52 -18
  15. data/lib/mutant/matcher/method.rb +4 -4
  16. data/lib/mutant/mutator/node/generic.rb +1 -1
  17. data/lib/mutant/mutator/node/literal/float.rb +2 -0
  18. data/lib/mutant/mutator/node/nthref.rb +29 -0
  19. data/lib/mutant/mutator/node/send.rb +17 -0
  20. data/lib/mutant/reporter/cli/printer/killer.rb +9 -3
  21. data/lib/mutant/strategy.rb +9 -9
  22. data/lib/mutant/strategy/rspec.rb +51 -41
  23. data/lib/mutant/subject.rb +16 -6
  24. data/lib/mutant/subject/method.rb +10 -10
  25. data/lib/mutant/version.rb +7 -0
  26. data/lib/mutant/zombifier.rb +2 -3
  27. data/mutant.gemspec +6 -4
  28. data/spec/integration/mutant/rspec_spec.rb +26 -0
  29. data/spec/unit/mutant/cli/class_methods/new_spec.rb +33 -8
  30. data/spec/unit/mutant/killer/rspec/class_methods/new_spec.rb +4 -0
  31. data/spec/unit/mutant/mutator/node/nthref/mutation_spec.rb +19 -0
  32. data/spec/unit/mutant/mutator/node/send/mutation_spec.rb +29 -0
  33. metadata +14 -20
  34. data/lib/mutant/strategy/method_expansion.rb +0 -53
  35. data/lib/mutant/strategy/rspec/dm2.rb +0 -24
  36. data/lib/mutant/strategy/rspec/dm2/lookup.rb +0 -63
  37. data/lib/mutant/strategy/rspec/dm2/lookup/method.rb +0 -145
  38. data/spec/integration/mutant/rspec_killer_spec.rb +0 -29
  39. data/spec/unit/mutant/strategy/method_expansion/class_methods/run_spec.rb +0 -51
  40. data/spec/unit/mutant/strategy/rspec/dm2/lookup/method/instance/spec_files_spec.rb +0 -73
  41. data/spec/unit/mutant/strategy/rspec/dm2/lookup/method/singleton/spec_files_spec.rb +0 -62
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 22c3af1fd28ad61a9e6968e66e5208272a9508cd
4
- data.tar.gz: f8ac63722e87ff6c4899d3ba3e2d75ee6e76579c
3
+ metadata.gz: bd04beb38330a250653e83799b208a0eb92a8656
4
+ data.tar.gz: 4b6512fbb3f2e31fcbd90411a1ea1c0c9f10c261
5
5
  SHA512:
6
- metadata.gz: 636bfb2b5e7bd64d40ef70427c739484cf1db6b1eb3cf17c02b5846cc566e882174ade582b04973034b0f795b6e01a56e169a910283af54cd1d0a7096e1d5d9e
7
- data.tar.gz: 035c70cca872d8a52c9a012e0d18a4e9027495860659d9d2d93c783542fe8fe38b646eca9e489db7029ac751fecbfc01dc18e438d3c797b871be3c645e646ec3
6
+ metadata.gz: 8b11f4a25768e953373c1ce9a854051c9ffa28758a139d369568089a520eb90893585242dcc4586c276e112b886157083ac0165fcb0c83e94fcf768a97c613c8
7
+ data.tar.gz: 8d358b32f4c915ab85779f43106e607576250fa8f8badf1a94ff23cfc84845e919411bba73a6380b869ce7f8525eeca4174e38a313d864c81c3e636e31eb0ea7
data/.travis.yml CHANGED
@@ -2,13 +2,12 @@ language: ruby
2
2
  before_install: gem install bundler
3
3
  bundler_args: --without yard guard benchmarks
4
4
  script: "bundle exec rake ci"
5
- rvm:
6
- - 1.9.3
7
- - 2.0.0
8
- - ruby-head
9
- - rbx-19mode
10
5
  matrix:
11
6
  include:
7
+ - rvm: 1.9.3
8
+ - rvm: 2.0.0
9
+ - rvm: ruby-head
10
+ - rvm: rbx-19mode
12
11
  - rvm: jruby-19mode
13
12
  env: JRUBY_OPTS="$JRUBY_OPTS --debug"
14
13
  - rvm: jruby-head
data/Gemfile.devtools CHANGED
@@ -36,7 +36,7 @@ group :metrics do
36
36
  gem 'reek', '~> 1.3.1', git: 'https://github.com/troessner/reek.git'
37
37
  gem 'rubocop', '~> 0.10.0', git: 'https://github.com/bbatsov/rubocop.git'
38
38
  gem 'simplecov', '~> 0.7.1'
39
- gem 'yardstick', '~> 0.9.6'
39
+ gem 'yardstick', '~> 0.9.6', git: 'https://github.com/dkubb/yardstick.git'
40
40
 
41
41
  platforms :ruby_19, :ruby_20 do
42
42
  gem 'yard-spellcheck', '~> 0.1.5'
data/README.md CHANGED
@@ -17,10 +17,7 @@ any ruby engine that supports POSIX-fork(2) semantics.
17
17
 
18
18
  Only rspec2 is supported currently. This is subject to change.
19
19
 
20
- It is easy to write a mutation killer for other test/spec frameworks than rspec2.
21
- Just create your own Mutant::Killer subclass, and make sure I get a PR!
22
-
23
- See this [ASCII-Cast](http://ascii.io/a/1707) for mutant in action! (v0.2.1)
20
+ It is easy to write a mutation killer/strategy for other test/spec frameworks than rspec2.
24
21
 
25
22
  Projects using Mutant
26
23
  ---------------------
@@ -62,7 +59,7 @@ emits around 3-6 mutations.
62
59
 
63
60
  Currently mutant covers the majority of ruby's complex nodes that often occur in method bodies.
64
61
 
65
- A some stats from the [axiom](https://github.com/dkubb/axiom) library:
62
+ Some stats from the [axiom](https://github.com/dkubb/axiom) library:
66
63
 
67
64
  ```
68
65
  Subjects: 417 # Amount of subjects being mutated (currently only methods)
@@ -84,63 +81,38 @@ the Generic handler altogether.
84
81
  Examples
85
82
  --------
86
83
 
87
- CLI will be simplified in the next releases, but currently stick with this:
88
-
89
84
  ```
90
85
  cd virtus
91
- # Run mutant on virtus namespace (that uses the dm-2 style spec layout)
92
- mutant --rspec-dm2 '::Virtus*'
86
+ # Run mutant on virtus namespace
87
+ mutant --rspec '::Virtus*'
93
88
  # Run mutant on specific virtus class
94
- mutant --rspec-dm2 ::Virtus::Attribute
89
+ mutant --rspec ::Virtus::Attribute
95
90
  # Run mutant on specific virtus class method
96
- mutant --rspec-dm2 ::Virtus::Attribute.build
91
+ mutant --rspec ::Virtus::Attribute.build
97
92
  # Run mutant on specific virtus instance method
98
- mutant --rspec-dm2 ::Virtus::Attribute#name
93
+ mutant --rspec ::Virtus::Attribute#name
99
94
  ```
100
95
 
101
- Strategies
102
- ----------
103
-
104
- Mutation testing is slow. The key to making it fast is selecting the correct set of tests to run.
105
- Mutant currently supports the following built-in strategies for selecting tests/specs.
106
-
107
- ### --rspec-dm2
108
-
109
- This strategy is the *fastest* but requires discipline in spec file naming.
110
-
111
- The following specs are executed to kill a mutation on:
112
- ```
113
- Public instance methods: spec/unit/#{namespace}/#{class_name}/#{method_name}_spec.rb
114
- Public singleton methods: spec/unit/#{namespace}/#{class_name}/class_methods/#{method_name}_spec.rb
115
- Private instance methods: spec/unit/#{namespace}/#{class_name}/*_spec.rb
116
- Private singleton methods: spec/unit/#{namespace}/#{class_name}/class_methods/*_spec.rb
117
- ```
118
-
119
- #### Expansions:
120
-
121
- Symbolic operator-like methods are expanded, e.g. ```Foo#<<``` is expanded to:
122
- ```
123
- spec/unit/foo/left_shift_operator_spec.rb
124
- ````
125
-
126
- The full list of expansions can be found here:
127
-
128
- https://github.com/mbj/mutant/blob/master/lib/mutant/constants.rb
129
-
130
- ### --rspec-unit
96
+ Subjects:
97
+ ---------
131
98
 
132
- This strategy executes all specs under ``./spec/unit`` for each mutation.
99
+ Mutant currently mutates code in instance and singleton methods. It is planned to support mutation
100
+ of constant definitions and domain specific languages, DSL probably as plugins.
133
101
 
134
- ### --rspec-integration
102
+ Test-Selection
103
+ --------------
135
104
 
136
- This strategy executes all specs under ``./spec/integration`` for each mutation.
105
+ Mutation testing is slow. The key to making it fast is selecting the correct set of tests to run.
106
+ Mutant currently supports the following built-in strategy for selecting tests/specs:
137
107
 
138
- ### --rspec-full
108
+ Mutant uses the "longest rspec example group descriptions prefix match" to select the tests to run.
139
109
 
140
- This strategy executes all specs under ``./spec`` for each mutation.
110
+ Example for a subject like `Foo::Bar#baz` it will run all example groups with description prefixes in
111
+ `Foo::Bar#baz`, `Foo::Bar` and `Foo`. The order is important, so if mutant finds example groups in the
112
+ current prefix level, these example groups *must* kill the mutation.
141
113
 
142
- In the future, we plan on allowing explicit selections on the specs to be run, as well as support for other test frameworks.
143
- Custom project specific strategies are also on the roadmap.
114
+ This test selection strategy is compatible with the old `--rspec-dm2` and `--rspec-unit` strategy.
115
+ The old flags where removed. It allows to define very fine grained specs, or coarse grained - as you like.
144
116
 
145
117
  Alternatives
146
118
  ------------
@@ -156,7 +128,7 @@ Your options:
156
128
 
157
129
  * GitHub Issues https://github.com/mbj/mutant/issues
158
130
  * Ping me on https://twitter.com/_m_b_j_
159
- * #mutant channel on freenode, I hang around on CET daytimes. (nick mbj)
131
+ * #mutant channel on freenode, I hang around on CET daytimes. (nick mbj)
160
132
  You'll also find others [ROM](https://github.com/rom-rb) team members here that can answer questions.
161
133
 
162
134
  Credits
data/config/flay.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 16
3
- total_score: 764
3
+ total_score: 738
data/config/flog.yml CHANGED
@@ -1,2 +1,2 @@
1
1
  ---
2
- threshold: 20.5
2
+ threshold: 20.0
data/config/reek.yml CHANGED
@@ -25,11 +25,13 @@ FeatureEnvy:
25
25
  enabled: true
26
26
  exclude:
27
27
  - Mutant::CLI#parse
28
+ - Mutant::CLI#add_strategies
28
29
  - Mutant::Matcher::Method::Instance#match?
29
30
  - Mutant::Matcher::Method::Singleton#receiver?
30
31
  - Mutant::Mutation::Evil#success?
31
32
  - Mutant::Mutation::Neutral#success?
32
33
  - Mutant::Reporter::CLI#subject_results
34
+ - Mutant::Strategy::Rspec#options
33
35
  IrresponsibleModule:
34
36
  enabled: true
35
37
  exclude: []
@@ -78,6 +80,8 @@ TooManyStatements:
78
80
  exclude:
79
81
  - Mutant#self.singleton_subclass_instance
80
82
  - Mutant::CLI#parse
83
+ - Mutant::CLI#add_options
84
+ - Mutant::CLI#add_strategies
81
85
  - Mutant::Killer::Rspec#run
82
86
  - Mutant::Reporter::CLI#colorized_diff
83
87
  - Mutant::Reporter::CLI::Printer::Config::Runner#run
@@ -96,8 +100,7 @@ UncommunicativeMethodName:
96
100
  accept: []
97
101
  UncommunicativeModuleName:
98
102
  enabled: true
99
- exclude:
100
- - Mutant::Strategy::Rspec::DM2
103
+ exclude: []
101
104
  reject:
102
105
  - !ruby/regexp /^.$/
103
106
  - !ruby/regexp /[0-9]$/
@@ -128,4 +131,8 @@ UtilityFunction:
128
131
  - Mutant::Mutation::Evil#success?
129
132
  - Mutant::Mutation::Neutral#success?
130
133
  - Mutant::NodeHelpers#s
134
+ - Mutant::Strategy::Rspec#configuration
135
+ - Mutant::Strategy::Rspec#options
136
+ - Mutant::Strategy::Rspec#world
137
+
131
138
  max_helper_calls: 0
data/lib/mutant.rb CHANGED
@@ -19,6 +19,7 @@ require 'diff/lcs/hunk'
19
19
  require 'rspec'
20
20
  require 'anima'
21
21
  require 'concord'
22
+ require 'rspec'
22
23
 
23
24
  # Library namespace
24
25
  module Mutant
@@ -26,6 +27,7 @@ module Mutant
26
27
  EMPTY_STRING = ''.freeze
27
28
  end # Mutant
28
29
 
30
+ require 'mutant/version'
29
31
  require 'mutant/cache'
30
32
  require 'mutant/node_helpers'
31
33
  require 'mutant/singleton_methods'
@@ -73,6 +75,7 @@ require 'mutant/mutator/node/send/binary'
73
75
  require 'mutant/mutator/node/when'
74
76
  require 'mutant/mutator/node/define'
75
77
  require 'mutant/mutator/node/mlhs'
78
+ require 'mutant/mutator/node/nthref'
76
79
  require 'mutant/mutator/node/masgn'
77
80
  require 'mutant/mutator/node/return'
78
81
  require 'mutant/mutator/node/block'
@@ -103,11 +106,7 @@ require 'mutant/killer/forking'
103
106
  require 'mutant/killer/forked'
104
107
  require 'mutant/strategy'
105
108
  require 'mutant/strategy/static'
106
- require 'mutant/strategy/method_expansion'
107
109
  require 'mutant/strategy/rspec'
108
- require 'mutant/strategy/rspec/dm2'
109
- require 'mutant/strategy/rspec/dm2/lookup'
110
- require 'mutant/strategy/rspec/dm2/lookup/method'
111
110
  require 'mutant/runner'
112
111
  require 'mutant/runner/config'
113
112
  require 'mutant/runner/subject'
data/lib/mutant/cli.rb CHANGED
@@ -59,6 +59,7 @@ module Mutant
59
59
  def config
60
60
  Config.new(
61
61
  :cache => @cache,
62
+ :zombie => @zombie,
62
63
  :debug => debug?,
63
64
  :matcher => matcher,
64
65
  :filter => filter,
@@ -154,26 +155,6 @@ module Mutant
154
155
  @filters << klass.new(filter)
155
156
  end
156
157
 
157
- # Set fail fast
158
- #
159
- # @api private
160
- #
161
- # @return [undefined]
162
- #
163
- def set_fail_fast
164
- @fail_fast = true
165
- end
166
-
167
- # Set debug mode
168
- #
169
- # @api private
170
- #
171
- # @return [undefined]
172
- #
173
- def set_debug
174
- @debug = true
175
- end
176
-
177
158
  # Set strategy
178
159
  #
179
160
  # @param [Strategy] strategy
@@ -204,9 +185,8 @@ module Mutant
204
185
  builder.separator ''
205
186
  builder.separator 'Strategies:'
206
187
 
207
- builder.on('--zombie', 'Run mutant zombified')
208
-
209
188
  add_strategies(builder)
189
+ add_environmental_options(builder)
210
190
  add_options(builder)
211
191
  end
212
192
 
@@ -237,25 +217,48 @@ module Mutant
237
217
 
238
218
  # Add strategies
239
219
  #
240
- # @param [Object]
220
+ # @param [Object] opts
241
221
  #
242
222
  # @return [undefined]
243
223
  #
244
224
  # @api private
245
225
  #
246
226
  def add_strategies(opts)
247
- opts.on('--rspec-unit', 'executes all specs under ./spec/unit') do
248
- set_strategy Strategy::Rspec::Unit
249
- end.on('--rspec-full', 'executes all specs under ./spec') do
250
- set_strategy Strategy::Rspec::Full
251
- end.on('--rspec-dm2', 'executes spec/unit/$nesting/$method_spec.rb') do
252
- set_strategy Strategy::Rspec::DM2
227
+ opts.separator ''
228
+ opts.separator 'Strategies:'
229
+
230
+ opts.on('--static-success', 'does succeed on all mutations') do
231
+ set_strategy Strategy::Static::Success.new
232
+ end
233
+ opts.on('--static-fail', 'does fail on all mutations') do
234
+ set_strategy Strategy::Static::Fail.new
235
+ end
236
+ opts.on('--rspec', 'kills mutations with rspec') do
237
+ set_strategy Strategy::Rspec.new
238
+ end
239
+ end
240
+
241
+ # Add environmental options
242
+ #
243
+ # @param [Object] opts
244
+ #
245
+ # @return [undefined]
246
+ #
247
+ # @api private
248
+ #
249
+ def add_environmental_options(opts)
250
+ opts.on('--zombie', 'Run mutant zombified') do
251
+ @zombie = true
252
+ end.on('-I', 'Add directory to $LOAD_PATH') do |directory|
253
+ $LOAD_PATH << directory
254
+ end.on('-r', '--require NAME', 'Require file with NAME') do |name|
255
+ require name
253
256
  end
254
257
  end
255
258
 
256
259
  # Add options
257
260
  #
258
- # @param [Object]
261
+ # @param [Object] opts
259
262
  #
260
263
  # @return [undefined]
261
264
  #
@@ -265,14 +268,17 @@ module Mutant
265
268
  opts.separator ''
266
269
  opts.separator 'Options:'
267
270
 
268
- opts.on('--code FILTER', 'Adds a code filter') do |filter|
269
- add_filter Mutation::Filter::Code, filter
271
+ opts.on('--version', 'Print mutants version') do |name|
272
+ puts("mutant-#{Mutant::VERSION}")
273
+ Kernel.exit(0)
274
+ end.on('--code FILTER', 'Adds a code filter') do |filter|
275
+ add_filter(Mutation::Filter::Code, filter)
270
276
  end.on('--fail-fast', 'Fail fast') do
271
- set_fail_fast
277
+ @fail_fast = true
272
278
  end.on('-d', '--debug', 'Enable debugging output') do
273
- set_debug
279
+ @debug = true
274
280
  end.on_tail('-h', '--help', 'Show this message') do
275
- puts opts
281
+ puts(opts)
276
282
  exit
277
283
  end
278
284
  end
@@ -49,7 +49,7 @@ module Mutant
49
49
  .split(SCOPE_OPERATOR)
50
50
  .reduce(Object) do |parent, name|
51
51
  parent.const_get(name, nil)
52
- end
52
+ end
53
53
  end
54
54
 
55
55
  # Return matchers for input
data/lib/mutant/config.rb CHANGED
@@ -4,7 +4,8 @@ module Mutant
4
4
  # The configuration of a mutator run
5
5
  class Config
6
6
  include Adamantium::Flat, Anima.new(
7
- :cache, :debug, :strategy, :matcher, :filter, :reporter, :fail_fast
7
+ :cache, :debug, :strategy, :matcher, :filter,
8
+ :reporter, :fail_fast, :zombie
8
9
  )
9
10
 
10
11
  # Enumerate subjects
@@ -23,7 +23,7 @@ module Mutant
23
23
  #
24
24
  # @return [String]
25
25
  #
26
- # @ai private
26
+ # @api private
27
27
  #
28
28
  def identification
29
29
  scope.name
@@ -89,6 +89,19 @@ module Mutant
89
89
  scope.name
90
90
  end
91
91
 
92
+ # Return match prefixes
93
+ #
94
+ # @return [Enumerable<String>]
95
+ #
96
+ # @api private
97
+ #
98
+ def match_prefixes
99
+ name_nesting.length.downto(1).map do |last|
100
+ name_nesting[0...last].join('::')
101
+ end
102
+ end
103
+ memoize :match_prefixes
104
+
92
105
  # Return scope wrapped by context
93
106
  #
94
107
  # @return [::Module|::Class]
data/lib/mutant/killer.rb CHANGED
@@ -98,6 +98,16 @@ module Mutant
98
98
  @runtime = times.real
99
99
  end
100
100
 
101
+ # Return subject
102
+ #
103
+ # @return [Subject]
104
+ #
105
+ # @api private
106
+ #
107
+ def subject
108
+ mutation.subject
109
+ end
110
+
101
111
  # Run killer
102
112
  #
103
113
  # @return [true]
@@ -19,36 +19,70 @@ module Mutant
19
19
  #
20
20
  def run
21
21
  mutation.insert
22
- # TODO: replace with real streams from configuration
23
- require 'stringio'
24
- # Note: We assume interesting output from a failed rspec run is stderr.
25
- rspec_err = StringIO.new
26
22
 
27
- exit_code = ::RSpec::Core::Runner.run(cli_arguments, nil, rspec_err)
23
+ groups = example_groups
28
24
 
29
- killed = !exit_code.zero?
25
+ unless groups
26
+ $stderr.puts "No rspec example groups found for: #{match_prefixes.join(', ')}"
27
+ return false
28
+ end
30
29
 
31
- if killed and mutation.should_survive?
32
- rspec_err.rewind
30
+ reporter = RSpec::Core::Reporter.new
33
31
 
34
- puts "#{mutation.class} test failed."
35
- puts 'RSpec stderr:'
36
- puts rspec_err.read
32
+ example_groups.each do |group|
33
+ return true unless group.run(reporter)
37
34
  end
38
35
 
39
- killed
36
+ false
37
+ end
38
+
39
+ # Return match prefixes
40
+ #
41
+ # @return [Enumerble<String>]
42
+ #
43
+ # @api private
44
+ #
45
+ def match_prefixes
46
+ subject.match_prefixes
47
+ end
48
+
49
+ # Return example groups
50
+ #
51
+ # @return [Array<RSpec::Example>]
52
+ #
53
+ # @api private
54
+ #
55
+ def example_groups
56
+ match_prefixes.each do |match_expression|
57
+ example_groups = find_with(match_expression)
58
+ return example_groups unless example_groups.empty?
59
+ end
60
+
61
+ nil
62
+ end
63
+
64
+ # Return example groups that match expression
65
+ #
66
+ # @param [String] match_expression
67
+ #
68
+ # @return [Enumerable<String>]
69
+ #
70
+ # @api private
71
+ #
72
+ def find_with(match_expression)
73
+ all_example_groups.select do |example_group|
74
+ example_group.description.start_with?(match_expression)
75
+ end
40
76
  end
41
77
 
42
- # Return command line arguments
78
+ # Return all example groups
43
79
  #
44
- # @return [Array]
80
+ # @return [Enumerable<RSpec::Example>]
45
81
  #
46
82
  # @api private
47
83
  #
48
- def cli_arguments
49
- %W(
50
- --fail-fast
51
- ) + strategy.spec_files(mutation.subject)
84
+ def all_example_groups
85
+ strategy.example_groups
52
86
  end
53
87
 
54
88
  end # Rspec