mutant 0.3.0.beta22 → 0.3.0.rc1

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 (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