mutant 0.8.20 → 0.8.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +0 -2
  3. data/Changelog.md +4 -0
  4. data/Gemfile.lock +17 -18
  5. data/README.md +21 -4
  6. data/config/rubocop.yml +3 -4
  7. data/docs/mutant-minitest.md +4 -2
  8. data/docs/mutant-rspec.md +104 -14
  9. data/lib/mutant.rb +2 -1
  10. data/lib/mutant/ast/meta/send.rb +1 -15
  11. data/lib/mutant/ast/types.rb +47 -22
  12. data/lib/mutant/meta.rb +2 -2
  13. data/lib/mutant/meta/example.rb +2 -1
  14. data/lib/mutant/meta/example/dsl.rb +12 -9
  15. data/lib/mutant/meta/example/verification.rb +34 -16
  16. data/lib/mutant/mutator/node.rb +7 -0
  17. data/lib/mutant/mutator/node/argument.rb +0 -1
  18. data/lib/mutant/mutator/node/arguments.rb +13 -1
  19. data/lib/mutant/mutator/node/block.rb +1 -1
  20. data/lib/mutant/mutator/node/index.rb +129 -0
  21. data/lib/mutant/mutator/node/noop.rb +1 -1
  22. data/lib/mutant/mutator/node/procarg_zero.rb +45 -0
  23. data/lib/mutant/mutator/node/send.rb +1 -27
  24. data/lib/mutant/parser.rb +1 -1
  25. data/lib/mutant/reporter/cli/printer/mutation_result.rb +34 -32
  26. data/lib/mutant/version.rb +1 -1
  27. data/lib/mutant/warning_filter.rb +1 -1
  28. data/lib/mutant/zombifier.rb +1 -1
  29. data/meta/block.rb +22 -3
  30. data/meta/index.rb +133 -0
  31. data/meta/indexasgn.rb +31 -0
  32. data/meta/lambda.rb +9 -0
  33. data/meta/regexp.rb +0 -7
  34. data/meta/regexp/character_types.rb +1 -1
  35. data/meta/send.rb +0 -146
  36. data/mutant.gemspec +2 -3
  37. data/spec/spec_helper.rb +1 -1
  38. data/spec/support/corpus.rb +28 -12
  39. data/spec/unit/mutant/ast/meta/send/proc_predicate_spec.rb +1 -1
  40. data/spec/unit/mutant/ast/meta/send/receiver_possible_top_level_const_predicate_spec.rb +1 -1
  41. data/spec/unit/mutant/ast/meta/send_spec.rb +6 -8
  42. data/spec/unit/mutant/meta/example/dsl_spec.rb +9 -9
  43. data/spec/unit/mutant/meta/example/verification_spec.rb +36 -4
  44. data/spec/unit/mutant/meta/example_spec.rb +4 -4
  45. data/spec/unit/mutant/mutator/node_spec.rb +12 -7
  46. data/spec/unit/mutant/reporter/cli/printer/mutation_result_spec.rb +3 -1
  47. data/spec/unit/mutant/subject_spec.rb +1 -1
  48. data/spec/unit/mutant/zombifier_spec.rb +1 -1
  49. metadata +12 -24
  50. data/config/flay.yml +0 -3
  51. data/config/flog.yml +0 -2
  52. data/lib/mutant/mutator/node/send/index.rb +0 -52
@@ -36,9 +36,8 @@ Gem::Specification.new do |gem|
36
36
  gem.add_runtime_dependency('parser', '~> 2.5.1')
37
37
  gem.add_runtime_dependency('procto', '~> 0.0.2')
38
38
  gem.add_runtime_dependency('regexp_parser', '~> 1.2')
39
- gem.add_runtime_dependency('unparser', '~> 0.3.0')
39
+ gem.add_runtime_dependency('unparser', '~> 0.4.1')
40
40
 
41
- gem.add_development_dependency('bundler', '~> 1.10')
42
- gem.add_development_dependency('devtools', '~> 0.1.21')
41
+ gem.add_development_dependency('devtools', '~> 0.1.22')
43
42
  gem.add_development_dependency('parallel', '~> 1.3')
44
43
  end
@@ -44,7 +44,7 @@ module ParserHelper
44
44
  end
45
45
 
46
46
  def parse(string)
47
- Unparser::Preprocessor.run(Parser::CurrentRuby.parse(string))
47
+ Unparser::Preprocessor.run(Unparser.parse(string))
48
48
  end
49
49
 
50
50
  def parse_expression(string)
@@ -87,7 +87,7 @@ module MutantSpec
87
87
  in_processes: parallel_processes
88
88
  }
89
89
 
90
- total = Parallel.map(effective_ruby_paths, options, &method(:count_mutations_and_check_errors))
90
+ total = Parallel.map(effective_ruby_paths, options, &method(:check_generation))
91
91
  .inject(DEFAULT_MUTATION_COUNT, :+)
92
92
 
93
93
  took = Mutant::Timer.now - start
@@ -129,33 +129,49 @@ module MutantSpec
129
129
  # @param path [Pathname] path responsible for exception
130
130
  #
131
131
  # @return [Integer] mutations generated
132
- def count_mutations_and_check_errors(path)
132
+ def check_generation(path)
133
133
  relative_path = path.relative_path_from(repo_path)
134
134
 
135
- count = count_mutations(path)
135
+ node = Parser::CurrentRuby.parse(path.read)
136
+ fail "Cannot parse: #{path}" unless node
137
+
138
+ mutations = Mutant::Mutator.mutate(node)
139
+
140
+ mutations.each do |mutation|
141
+ check_generation_invariants(node, mutation)
142
+ end
136
143
 
137
144
  expected_errors.assert_success(relative_path)
138
145
 
139
- count
146
+ mutations.length
140
147
  rescue Exception => exception # rubocop:disable Lint/RescueException
141
148
  expected_errors.assert_error(relative_path, exception)
142
149
 
143
150
  DEFAULT_MUTATION_COUNT
144
151
  end
145
152
 
146
- # Count mutations generated for provided source file
153
+ # Check generation invariants
147
154
  #
148
- # @param path [Pathname] path to a source file
155
+ # @param [Parser::AST::Node] original
156
+ # @param [Parser::AST::Node] mutation
149
157
  #
150
- # @raise [Exception] any error specified by integrations.yml
158
+ # @return [undefined]
151
159
  #
152
- # @return [Integer] number of mutations generated
153
- def count_mutations(path)
154
- node = Parser::CurrentRuby.parse(path.read)
160
+ # @raise [Exception]
161
+ def check_generation_invariants(original, mutation)
162
+ return unless ENV['MUTANT_CORPUS_EXPENSIVE']
163
+
164
+ original_source = Unparser.unparse(original)
165
+ mutation_source = Unparser.unparse(mutation)
155
166
 
156
- return DEFAULT_MUTATION_COUNT unless node
167
+ Mutant::Diff.build(original_source, mutation_source) and return
157
168
 
158
- Mutant::Mutator.mutate(node).length
169
+ fail Mutant::Reporter::CLI::NO_DIFF_MESSAGE % [
170
+ original_source,
171
+ original.inspect,
172
+ mutation_source,
173
+ mutation.inspect
174
+ ]
159
175
  end
160
176
 
161
177
  # Install mutant
@@ -4,7 +4,7 @@ RSpec.describe Mutant::AST::Meta::Send, '#proc?' do
4
4
  subject { described_class.new(node).proc? }
5
5
 
6
6
  shared_context 'proc send' do |source|
7
- let(:node) { Parser::CurrentRuby.parse(source).children.first }
7
+ let(:node) { Unparser.parse(source).children.first }
8
8
  end
9
9
 
10
10
  shared_examples 'proc definition' do |*args|
@@ -4,7 +4,7 @@ RSpec.describe Mutant::AST::Meta::Send, '#receiver_possible_top_level_const?' do
4
4
  subject { described_class.new(node).receiver_possible_top_level_const? }
5
5
 
6
6
  def parse(source)
7
- Parser::CurrentRuby.parse(source)
7
+ Unparser.parse(source)
8
8
  end
9
9
 
10
10
  context 'when implicit top level const' do
@@ -4,24 +4,22 @@ RSpec.describe Mutant::AST::Meta::Send do
4
4
  let(:object) { described_class.new(node) }
5
5
 
6
6
  def parse(source)
7
- Parser::CurrentRuby.parse(source)
7
+ Unparser.parse(source)
8
8
  end
9
9
 
10
10
  let(:method_call) { parse('foo.bar(baz)') }
11
11
  let(:attribute_read) { parse('foo.bar') }
12
- let(:index_assignment) { parse('foo[0] = bar') }
13
12
  let(:attribute_assignment) { parse('foo.bar = baz') }
14
13
  let(:binary_method_operator) { parse('foo == bar') }
15
14
 
16
15
  class Expectation
17
- include Adamantium, Anima.new(:name, :assignment, :attribute_assignment, :index_assignment, :binary_method_operator)
16
+ include Adamantium, Anima.new(:name, :attribute_assignment, :binary_method_operator)
18
17
 
19
18
  ALL = [
20
- [:method_call, false, false, false, false],
21
- [:attribute_read, false, false, false, false],
22
- [:index_assignment, true, false, true, false],
23
- [:attribute_assignment, true, true, false, false],
24
- [:binary_method_operator, false, false, false, true]
19
+ [:method_call, false, false],
20
+ [:attribute_read, false, false],
21
+ [:attribute_assignment, true, false],
22
+ [:binary_method_operator, false, true]
25
23
  ].map do |values|
26
24
  new(Hash[anima.attribute_names.zip(values)])
27
25
  end.freeze
@@ -2,19 +2,19 @@
2
2
 
3
3
  RSpec.describe Mutant::Meta::Example::DSL do
4
4
  describe '.call' do
5
- subject { described_class.call(file, type, block) }
5
+ subject { described_class.call(file, types, block) }
6
6
 
7
- let(:file) { 'foo.rb' }
8
- let(:node) { s(:false) }
9
- let(:type) { node.type }
10
- let(:expected) { [] }
7
+ let(:file) { 'foo.rb' }
8
+ let(:node) { s(:false) }
9
+ let(:types) { Set.new([node.type]) }
10
+ let(:expected) { [] }
11
11
 
12
12
  let(:expected_example) do
13
13
  Mutant::Meta::Example.new(
14
- file: file,
15
- node: node,
16
- node_type: type,
17
- expected: expected
14
+ file: file,
15
+ node: node,
16
+ types: types,
17
+ expected: expected
18
18
  )
19
19
  end
20
20
 
@@ -5,10 +5,10 @@ RSpec.describe Mutant::Meta::Example::Verification do
5
5
 
6
6
  let(:example) do
7
7
  Mutant::Meta::Example.new(
8
- file: 'foo.rb',
9
- node: s(:true),
10
- node_type: :true,
11
- expected: expected_nodes
8
+ file: 'foo.rb',
9
+ node: s(:true),
10
+ types: [:true],
11
+ expected: expected_nodes
12
12
  )
13
13
  end
14
14
 
@@ -75,6 +75,7 @@ RSpec.describe Mutant::Meta::Example::Verification do
75
75
  - node: s(:nil)
76
76
  source: nil
77
77
  unexpected: []
78
+ invalid_syntax: []
78
79
  no_diff: []
79
80
  REPORT
80
81
  end
@@ -95,6 +96,7 @@ RSpec.describe Mutant::Meta::Example::Verification do
95
96
  source: 'false'
96
97
  - node: s(:nil)
97
98
  source: nil
99
+ invalid_syntax: []
98
100
  no_diff: []
99
101
  REPORT
100
102
  end
@@ -112,11 +114,41 @@ RSpec.describe Mutant::Meta::Example::Verification do
112
114
  original_source: 'true'
113
115
  missing: []
114
116
  unexpected: []
117
+ invalid_syntax: []
115
118
  no_diff:
116
119
  - node: s(:true)
117
120
  source: 'true'
118
121
  REPORT
119
122
  end
120
123
  end
124
+
125
+ context 'when the generated node is invalid syntax after unparsed' do
126
+ let(:invalid_node) do
127
+ s(:op_asgn, s(:send, s(:self), :at, s(:int, 1)), :+, s(:int, 1))
128
+ end
129
+
130
+ let(:expected_nodes) { [invalid_node] }
131
+ let(:generated_nodes) { [invalid_node] }
132
+
133
+ specify do
134
+ should eql(<<~'REPORT')
135
+ ---
136
+ file: foo.rb
137
+ original_ast: s(:true)
138
+ original_source: 'true'
139
+ missing: []
140
+ unexpected: []
141
+ invalid_syntax:
142
+ - node: |-
143
+ s(:op_asgn,
144
+ s(:send,
145
+ s(:self), :at,
146
+ s(:int, 1)), :+,
147
+ s(:int, 1))
148
+ source: self.at(1) += 1
149
+ no_diff: []
150
+ REPORT
151
+ end
152
+ end
121
153
  end
122
154
  end
@@ -3,10 +3,10 @@
3
3
  RSpec.describe Mutant::Meta::Example do
4
4
  let(:object) do
5
5
  described_class.new(
6
- file: file,
7
- node: node,
8
- node_type: node.type,
9
- expected: mutation_nodes
6
+ file: file,
7
+ node: node,
8
+ types: [node.type],
9
+ expected: mutation_nodes
10
10
  )
11
11
  end
12
12
 
@@ -1,15 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- Mutant::Meta::Example::ALL.each.group_by(&:node_type).each do |type, examples|
4
- RSpec.describe Mutant::Mutator::REGISTRY.lookup(type) do
5
- toplevel_nodes = examples.map { |example| example.node.type }.uniq
3
+ aggregate = Hash.new { |hash, key| hash[key] = [] }
6
4
 
7
- it "generates the correct mutations on #{toplevel_nodes} toplevel examples" do
5
+ Mutant::Meta::Example::ALL
6
+ .each_with_object(aggregate) do |example, agg|
7
+ example.types.each do |type|
8
+ agg[Mutant::Mutator::REGISTRY.lookup(type)] << example
9
+ end
10
+ end
11
+
12
+ aggregate.each do |mutator, examples|
13
+ RSpec.describe mutator do
14
+ it 'generates expected mutations' do
8
15
  examples.each do |example|
9
16
  verification = example.verification
10
- unless verification.success?
11
- fail verification.error_report
12
- end
17
+ fail verification.error_report unless verification.success?
13
18
  end
14
19
  end
15
20
  end
@@ -48,7 +48,9 @@ RSpec.describe Mutant::Reporter::CLI::Printer::MutationResult do
48
48
  it_reports(<<~REPORT)
49
49
  evil:subject-a:a5bc7
50
50
  --- Internal failure ---
51
- BUG: Mutation NOT resulted in exactly one diff hunk. Please report a reproduction!
51
+ BUG: A generted mutation did not result in exactly one diff hunk!
52
+ This is an invariant violation by the mutation generation engine.
53
+ Please report a reproduction to https://github.com/mbj/mutant
52
54
  Original unparsed source:
53
55
  super
54
56
  Original AST:
@@ -19,7 +19,7 @@ RSpec.describe Mutant::Subject do
19
19
  let(:object) { class_under_test.new(context, node) }
20
20
 
21
21
  let(:node) do
22
- Parser::CurrentRuby.parse(<<-RUBY)
22
+ Unparser.parse(<<-RUBY)
23
23
  def foo
24
24
  end
25
25
  RUBY
@@ -80,7 +80,7 @@ RSpec.describe Mutant::Zombifier do
80
80
  let(:file_entries) do
81
81
  {
82
82
  'a/project.rb' => { file: true, contents: 'module Project; end' },
83
- 'b/bar.rb' => { file: true, contents: 'module Bar; end' }
83
+ 'b/bar.rb' => { file: true, contents: 'module Bar; end' }
84
84
  }
85
85
  end
86
86
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mutant
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.20
4
+ version: 0.8.21
5
5
  platform: ruby
6
6
  authors:
7
7
  - Markus Schirp
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-27 00:00:00.000000000 Z
11
+ date: 2018-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: abstract_type
@@ -198,42 +198,28 @@ dependencies:
198
198
  requirements:
199
199
  - - "~>"
200
200
  - !ruby/object:Gem::Version
201
- version: 0.3.0
201
+ version: 0.4.1
202
202
  type: :runtime
203
203
  prerelease: false
204
204
  version_requirements: !ruby/object:Gem::Requirement
205
205
  requirements:
206
206
  - - "~>"
207
207
  - !ruby/object:Gem::Version
208
- version: 0.3.0
209
- - !ruby/object:Gem::Dependency
210
- name: bundler
211
- requirement: !ruby/object:Gem::Requirement
212
- requirements:
213
- - - "~>"
214
- - !ruby/object:Gem::Version
215
- version: '1.10'
216
- type: :development
217
- prerelease: false
218
- version_requirements: !ruby/object:Gem::Requirement
219
- requirements:
220
- - - "~>"
221
- - !ruby/object:Gem::Version
222
- version: '1.10'
208
+ version: 0.4.1
223
209
  - !ruby/object:Gem::Dependency
224
210
  name: devtools
225
211
  requirement: !ruby/object:Gem::Requirement
226
212
  requirements:
227
213
  - - "~>"
228
214
  - !ruby/object:Gem::Version
229
- version: 0.1.21
215
+ version: 0.1.22
230
216
  type: :development
231
217
  prerelease: false
232
218
  version_requirements: !ruby/object:Gem::Requirement
233
219
  requirements:
234
220
  - - "~>"
235
221
  - !ruby/object:Gem::Version
236
- version: 0.1.21
222
+ version: 0.1.22
237
223
  - !ruby/object:Gem::Dependency
238
224
  name: parallel
239
225
  requirement: !ruby/object:Gem::Requirement
@@ -272,8 +258,6 @@ files:
272
258
  - Rakefile
273
259
  - bin/mutant
274
260
  - config/devtools.yml
275
- - config/flay.yml
276
- - config/flog.yml
277
261
  - config/reek.yml
278
262
  - config/rubocop.yml
279
263
  - config/triage.yml
@@ -367,6 +351,7 @@ files:
367
351
  - lib/mutant/mutator/node/dsym.rb
368
352
  - lib/mutant/mutator/node/generic.rb
369
353
  - lib/mutant/mutator/node/if.rb
354
+ - lib/mutant/mutator/node/index.rb
370
355
  - lib/mutant/mutator/node/kwbegin.rb
371
356
  - lib/mutant/mutator/node/literal.rb
372
357
  - lib/mutant/mutator/node/literal/array.rb
@@ -390,6 +375,7 @@ files:
390
375
  - lib/mutant/mutator/node/nthref.rb
391
376
  - lib/mutant/mutator/node/op_asgn.rb
392
377
  - lib/mutant/mutator/node/or_asgn.rb
378
+ - lib/mutant/mutator/node/procarg_zero.rb
393
379
  - lib/mutant/mutator/node/regexp.rb
394
380
  - lib/mutant/mutator/node/regexp/alternation_meta.rb
395
381
  - lib/mutant/mutator/node/regexp/capture_group.rb
@@ -405,7 +391,6 @@ files:
405
391
  - lib/mutant/mutator/node/send/attribute_assignment.rb
406
392
  - lib/mutant/mutator/node/send/binary.rb
407
393
  - lib/mutant/mutator/node/send/conditional.rb
408
- - lib/mutant/mutator/node/send/index.rb
409
394
  - lib/mutant/mutator/node/splat.rb
410
395
  - lib/mutant/mutator/node/super.rb
411
396
  - lib/mutant/mutator/node/when.rb
@@ -483,12 +468,15 @@ files:
483
468
  - meta/gvasgn.rb
484
469
  - meta/hash.rb
485
470
  - meta/if.rb
471
+ - meta/index.rb
472
+ - meta/indexasgn.rb
486
473
  - meta/int.rb
487
474
  - meta/ivar.rb
488
475
  - meta/ivasgn.rb
489
476
  - meta/kwarg.rb
490
477
  - meta/kwbegin.rb
491
478
  - meta/kwoptarg.rb
479
+ - meta/lambda.rb
492
480
  - meta/lvar.rb
493
481
  - meta/lvasgn.rb
494
482
  - meta/masgn.rb
@@ -671,7 +659,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
671
659
  version: '0'
672
660
  requirements: []
673
661
  rubyforge_project:
674
- rubygems_version: 2.7.7
662
+ rubygems_version: 2.7.6
675
663
  signing_key:
676
664
  specification_version: 4
677
665
  summary: Mutation testing tool for ruby under MRI and Rubinius
@@ -1,3 +0,0 @@
1
- ---
2
- threshold: 16
3
- total_score: 1395
@@ -1,2 +0,0 @@
1
- ---
2
- threshold: 27.7