mutant 0.8.20 → 0.8.21

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