mutant 0.8.20 → 0.8.21
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +0 -2
- data/Changelog.md +4 -0
- data/Gemfile.lock +17 -18
- data/README.md +21 -4
- data/config/rubocop.yml +3 -4
- data/docs/mutant-minitest.md +4 -2
- data/docs/mutant-rspec.md +104 -14
- data/lib/mutant.rb +2 -1
- data/lib/mutant/ast/meta/send.rb +1 -15
- data/lib/mutant/ast/types.rb +47 -22
- data/lib/mutant/meta.rb +2 -2
- data/lib/mutant/meta/example.rb +2 -1
- data/lib/mutant/meta/example/dsl.rb +12 -9
- data/lib/mutant/meta/example/verification.rb +34 -16
- data/lib/mutant/mutator/node.rb +7 -0
- data/lib/mutant/mutator/node/argument.rb +0 -1
- data/lib/mutant/mutator/node/arguments.rb +13 -1
- data/lib/mutant/mutator/node/block.rb +1 -1
- data/lib/mutant/mutator/node/index.rb +129 -0
- data/lib/mutant/mutator/node/noop.rb +1 -1
- data/lib/mutant/mutator/node/procarg_zero.rb +45 -0
- data/lib/mutant/mutator/node/send.rb +1 -27
- data/lib/mutant/parser.rb +1 -1
- data/lib/mutant/reporter/cli/printer/mutation_result.rb +34 -32
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/warning_filter.rb +1 -1
- data/lib/mutant/zombifier.rb +1 -1
- data/meta/block.rb +22 -3
- data/meta/index.rb +133 -0
- data/meta/indexasgn.rb +31 -0
- data/meta/lambda.rb +9 -0
- data/meta/regexp.rb +0 -7
- data/meta/regexp/character_types.rb +1 -1
- data/meta/send.rb +0 -146
- data/mutant.gemspec +2 -3
- data/spec/spec_helper.rb +1 -1
- data/spec/support/corpus.rb +28 -12
- data/spec/unit/mutant/ast/meta/send/proc_predicate_spec.rb +1 -1
- data/spec/unit/mutant/ast/meta/send/receiver_possible_top_level_const_predicate_spec.rb +1 -1
- data/spec/unit/mutant/ast/meta/send_spec.rb +6 -8
- data/spec/unit/mutant/meta/example/dsl_spec.rb +9 -9
- data/spec/unit/mutant/meta/example/verification_spec.rb +36 -4
- data/spec/unit/mutant/meta/example_spec.rb +4 -4
- data/spec/unit/mutant/mutator/node_spec.rb +12 -7
- data/spec/unit/mutant/reporter/cli/printer/mutation_result_spec.rb +3 -1
- data/spec/unit/mutant/subject_spec.rb +1 -1
- data/spec/unit/mutant/zombifier_spec.rb +1 -1
- metadata +12 -24
- data/config/flay.yml +0 -3
- data/config/flog.yml +0 -2
- data/lib/mutant/mutator/node/send/index.rb +0 -52
data/mutant.gemspec
CHANGED
@@ -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.
|
39
|
+
gem.add_runtime_dependency('unparser', '~> 0.4.1')
|
40
40
|
|
41
|
-
gem.add_development_dependency('
|
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
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/corpus.rb
CHANGED
@@ -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(:
|
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
|
132
|
+
def check_generation(path)
|
133
133
|
relative_path = path.relative_path_from(repo_path)
|
134
134
|
|
135
|
-
|
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
|
-
|
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
|
-
#
|
153
|
+
# Check generation invariants
|
147
154
|
#
|
148
|
-
# @param
|
155
|
+
# @param [Parser::AST::Node] original
|
156
|
+
# @param [Parser::AST::Node] mutation
|
149
157
|
#
|
150
|
-
# @
|
158
|
+
# @return [undefined]
|
151
159
|
#
|
152
|
-
# @
|
153
|
-
def
|
154
|
-
|
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
|
-
|
167
|
+
Mutant::Diff.build(original_source, mutation_source) and return
|
157
168
|
|
158
|
-
Mutant::
|
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) {
|
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
|
-
|
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
|
-
|
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, :
|
16
|
+
include Adamantium, Anima.new(:name, :attribute_assignment, :binary_method_operator)
|
18
17
|
|
19
18
|
ALL = [
|
20
|
-
[:method_call, false, false
|
21
|
-
[:attribute_read, false, false
|
22
|
-
[:
|
23
|
-
[:
|
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,
|
5
|
+
subject { described_class.call(file, types, block) }
|
6
6
|
|
7
|
-
let(:file) { 'foo.rb'
|
8
|
-
let(:node) { s(:false)
|
9
|
-
let(:
|
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:
|
15
|
-
node:
|
16
|
-
|
17
|
-
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:
|
9
|
-
node:
|
10
|
-
|
11
|
-
expected:
|
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:
|
7
|
-
node:
|
8
|
-
|
9
|
-
expected:
|
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
|
-
|
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
|
-
|
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:
|
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:
|
@@ -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'
|
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.
|
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
|
+
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
data/config/flay.yml
DELETED
data/config/flog.yml
DELETED