mutant 0.8.9 → 0.8.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog.md +4 -0
- data/config/devtools.yml +1 -1
- data/config/flay.yml +1 -1
- data/lib/mutant.rb +1 -2
- data/lib/mutant/ast/types.rb +3 -2
- data/lib/mutant/context.rb +78 -6
- data/lib/mutant/diff.rb +7 -13
- data/lib/mutant/matcher/method.rb +2 -2
- data/lib/mutant/mutation.rb +1 -1
- data/lib/mutant/mutator/node/generic.rb +1 -1
- data/lib/mutant/reporter/cli/printer.rb +25 -1
- data/lib/mutant/reporter/sequence.rb +22 -0
- data/lib/mutant/result.rb +4 -9
- data/lib/mutant/version.rb +1 -1
- data/meta/dsym.rb +7 -7
- data/mutant.gemspec +5 -5
- data/spec/spec_helper.rb +0 -1
- data/spec/unit/mutant/ast/named_children_spec.rb +12 -2
- data/spec/unit/mutant/context_spec.rb +85 -1
- data/spec/unit/mutant/diff_spec.rb +9 -0
- data/spec/unit/mutant/mutation_spec.rb +55 -4
- data/spec/unit/mutant/reporter/cli/printer/mutation_result_spec.rb +3 -3
- data/spec/unit/mutant/reporter/cli/printer_spec.rb +27 -6
- data/spec/unit/mutant/reporter/null_spec.rb +5 -15
- data/spec/unit/mutant/reporter/sequence_spec.rb +29 -0
- data/spec/unit/mutant/result/class_methods_spec.rb +49 -0
- data/spec/unit/mutant/result/mutation_spec.rb +49 -0
- data/spec/unit/mutant/result/subject_spec.rb +84 -19
- data/spec/unit/mutant/result_spec.rb +18 -10
- data/spec/unit/mutant/subject/method/instance_spec.rb +3 -3
- data/spec/unit/mutant/subject/method/singleton_spec.rb +1 -1
- metadata +19 -22
- data/lib/mutant/context/scope.rb +0 -94
- data/lib/mutant/delegator.rb +0 -43
- data/spec/unit/mutant/context/root_spec.rb +0 -11
- data/spec/unit/mutant/context/scope/root_spec.rb +0 -32
- data/spec/unit/mutant/context/scope/unqualified_name_spec.rb +0 -25
- data/spec/unit/mutant/context/scope_spec.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37c56c026986c92dd3afb4306adaea0d80b130f2
|
4
|
+
data.tar.gz: 0513911185fbe4a6967146e75ebec39e77537875
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 838d0e823d5666c5b615aa4c3f94af69c90034873cf07bc113524dfad2b265cdda16b350d189e410ff0229657fc425ddb0d34d442f19091a11151f2757f10b06
|
7
|
+
data.tar.gz: 60493346edfd51f98857e94c286de02c6293933ad379331f4513f18ec82b35f5c93ae5b6f14f8a196ac69d071004ba8529bd0ef902acf8ea8a32eb471b41f4b3
|
data/Changelog.md
CHANGED
data/config/devtools.yml
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
---
|
2
|
-
unit_test_timeout: 10
|
2
|
+
unit_test_timeout: 10.0
|
data/config/flay.yml
CHANGED
data/lib/mutant.rb
CHANGED
@@ -61,7 +61,6 @@ require 'mutant/actor/sender'
|
|
61
61
|
require 'mutant/actor/mailbox'
|
62
62
|
require 'mutant/actor/env'
|
63
63
|
require 'mutant/parser'
|
64
|
-
require 'mutant/delegator'
|
65
64
|
require 'mutant/isolation'
|
66
65
|
require 'mutant/parallel'
|
67
66
|
require 'mutant/parallel/master'
|
@@ -131,7 +130,6 @@ require 'mutant/mutator/node/rescue'
|
|
131
130
|
require 'mutant/mutator/node/match_current_line'
|
132
131
|
require 'mutant/loader'
|
133
132
|
require 'mutant/context'
|
134
|
-
require 'mutant/context/scope'
|
135
133
|
require 'mutant/scope'
|
136
134
|
require 'mutant/subject'
|
137
135
|
require 'mutant/subject/method'
|
@@ -168,6 +166,7 @@ require 'mutant/runner/sink'
|
|
168
166
|
require 'mutant/result'
|
169
167
|
require 'mutant/reporter'
|
170
168
|
require 'mutant/reporter/null'
|
169
|
+
require 'mutant/reporter/sequence'
|
171
170
|
require 'mutant/reporter/cli'
|
172
171
|
require 'mutant/reporter/cli/printer'
|
173
172
|
require 'mutant/reporter/cli/printer/config'
|
data/lib/mutant/ast/types.rb
CHANGED
@@ -41,8 +41,9 @@ module Mutant
|
|
41
41
|
# Nodes that are NOT generated by parser but used by mutant / unparser.
|
42
42
|
EXTRA = symbolset.(%w[empty])
|
43
43
|
|
44
|
-
# Nodes that are currently missing from
|
45
|
-
|
44
|
+
# Nodes that are currently missing from Parser::Meta::NODE_TYPES
|
45
|
+
# * https://github.com/whitequark/parser/pull/251
|
46
|
+
MISSING = symbolset.(%w[csend])
|
46
47
|
|
47
48
|
# All node types mutant handles
|
48
49
|
ALL = symbolset.((Parser::Meta::NODE_TYPES + EXTRA + MISSING) - BLACKLIST)
|
data/lib/mutant/context.rb
CHANGED
@@ -1,19 +1,91 @@
|
|
1
1
|
module Mutant
|
2
2
|
# An abstract context where mutations can be applied to.
|
3
3
|
class Context
|
4
|
-
include Adamantium::Flat,
|
4
|
+
include Adamantium::Flat, Concord::Public.new(:scope, :source_path)
|
5
|
+
extend AST::Sexp
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
#
|
7
|
+
NAMESPACE_DELIMITER = '::'.freeze
|
8
|
+
|
9
|
+
# Return root node for mutation
|
9
10
|
#
|
10
11
|
# @return [Parser::AST::Node]
|
11
|
-
|
12
|
+
def root(node)
|
13
|
+
nesting.reverse.reduce(node) do |current, scope|
|
14
|
+
self.class.wrap(scope, current)
|
15
|
+
end
|
16
|
+
end
|
12
17
|
|
13
18
|
# Identification string
|
14
19
|
#
|
15
20
|
# @return [String]
|
16
|
-
|
21
|
+
def identification
|
22
|
+
scope.name
|
23
|
+
end
|
24
|
+
|
25
|
+
# Wrap node into ast node
|
26
|
+
#
|
27
|
+
# @param [Class, Module] scope
|
28
|
+
# @param [Parser::AST::Node] node
|
29
|
+
#
|
30
|
+
# @return [Parser::AST::Class]
|
31
|
+
# if scope is of kind Class
|
32
|
+
#
|
33
|
+
# @return [Parser::AST::Module]
|
34
|
+
# if scope is of kind module
|
35
|
+
def self.wrap(scope, node)
|
36
|
+
name = s(:const, nil, scope.name.split(NAMESPACE_DELIMITER).last.to_sym)
|
37
|
+
case scope
|
38
|
+
when Class
|
39
|
+
s(:class, name, nil, node)
|
40
|
+
when Module
|
41
|
+
s(:module, name, node)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Nesting of scope
|
46
|
+
#
|
47
|
+
# @return [Enumerable<Class,Module>]
|
48
|
+
def nesting
|
49
|
+
const = Object
|
50
|
+
name_nesting.map do |name|
|
51
|
+
const = const.const_get(name)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
memoize :nesting
|
55
|
+
|
56
|
+
# Unqualified name of scope
|
57
|
+
#
|
58
|
+
# @return [String]
|
59
|
+
def unqualified_name
|
60
|
+
name_nesting.last
|
61
|
+
end
|
62
|
+
|
63
|
+
# Match expressions for scope
|
64
|
+
#
|
65
|
+
# @return [Enumerable<Expression>]
|
66
|
+
def match_expressions
|
67
|
+
name_nesting.each_index.reverse_each.map do |index|
|
68
|
+
Expression::Namespace::Recursive.new(
|
69
|
+
scope_name: name_nesting.take(index.succ).join(NAMESPACE_DELIMITER)
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
memoize :match_expressions
|
74
|
+
|
75
|
+
# Scope wrapped by context
|
76
|
+
#
|
77
|
+
# @return [Module|Class]
|
78
|
+
attr_reader :scope
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# Nesting of names in scope
|
83
|
+
#
|
84
|
+
# @return [Array<String>]
|
85
|
+
def name_nesting
|
86
|
+
scope.name.split(NAMESPACE_DELIMITER)
|
87
|
+
end
|
88
|
+
memoize :name_nesting
|
17
89
|
|
18
90
|
end # Context
|
19
91
|
end # Mutant
|
data/lib/mutant/diff.rb
CHANGED
@@ -17,9 +17,7 @@ module Mutant
|
|
17
17
|
def diff
|
18
18
|
return if diffs.empty?
|
19
19
|
|
20
|
-
|
21
|
-
hunk.diff(:unified) << NEWLINE
|
22
|
-
end.join
|
20
|
+
minimized_hunk.diff(:unified) << NEWLINE
|
23
21
|
end
|
24
22
|
memoize :diff
|
25
23
|
|
@@ -74,19 +72,15 @@ module Mutant
|
|
74
72
|
end
|
75
73
|
end
|
76
74
|
|
77
|
-
# Minimized
|
75
|
+
# Minimized hunk
|
78
76
|
#
|
79
|
-
# @return
|
80
|
-
def
|
77
|
+
# @return Diff::LCS::Hunk
|
78
|
+
def minimized_hunk
|
81
79
|
head, *tail = hunks
|
82
80
|
|
83
|
-
tail.
|
84
|
-
left
|
85
|
-
|
86
|
-
right.merge(left)
|
87
|
-
aggregate.pop
|
88
|
-
end
|
89
|
-
aggregate << right
|
81
|
+
tail.reduce(head) do |left, right|
|
82
|
+
right.merge(left)
|
83
|
+
right
|
90
84
|
end
|
91
85
|
end
|
92
86
|
|
data/lib/mutant/mutation.rb
CHANGED
@@ -8,7 +8,7 @@ module Mutant
|
|
8
8
|
# These nodes still need a dedicated mutator,
|
9
9
|
# your contribution is that close!
|
10
10
|
handle(
|
11
|
-
:ensure, :redo, :regopt, :retry, :arg_expr,
|
11
|
+
:csend, :ensure, :redo, :regopt, :retry, :arg_expr,
|
12
12
|
:kwrestarg, :kwoptarg, :kwarg, :undef, :module, :empty,
|
13
13
|
:alias, :for, :xstr, :back_ref, :class,
|
14
14
|
:sclass, :match_with_lvasgn, :while_post,
|
@@ -3,10 +3,34 @@ module Mutant
|
|
3
3
|
class CLI
|
4
4
|
# CLI runner status printer base class
|
5
5
|
class Printer
|
6
|
-
include AbstractType,
|
6
|
+
include AbstractType,
|
7
|
+
Adamantium::Flat,
|
8
|
+
Concord.new(:output, :object),
|
9
|
+
Procto.call(:run)
|
7
10
|
|
8
11
|
private_class_method :new
|
9
12
|
|
13
|
+
# Create delegators to object
|
14
|
+
#
|
15
|
+
# @return [undefined]
|
16
|
+
def self.delegate(*names)
|
17
|
+
names.each(&method(:define_delegator))
|
18
|
+
end
|
19
|
+
private_class_method :delegate
|
20
|
+
|
21
|
+
# Create delegator to object
|
22
|
+
#
|
23
|
+
# @param [Symbol] name
|
24
|
+
#
|
25
|
+
# @return [undefined]
|
26
|
+
def self.define_delegator(name)
|
27
|
+
define_method(name) do
|
28
|
+
object.public_send(name)
|
29
|
+
end
|
30
|
+
private(name)
|
31
|
+
end
|
32
|
+
private_class_method :define_delegator
|
33
|
+
|
10
34
|
delegate :success?
|
11
35
|
|
12
36
|
NL = "\n".freeze
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Mutant
|
2
|
+
class Reporter
|
3
|
+
class Sequence < self
|
4
|
+
include Concord.new(:reporters)
|
5
|
+
|
6
|
+
%i[warn progress report start].each do |name|
|
7
|
+
define_method(name) do |value|
|
8
|
+
reporters.each do |reporter|
|
9
|
+
reporter.public_send(name, value)
|
10
|
+
end
|
11
|
+
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def delay
|
17
|
+
reporters.map(&:delay).min
|
18
|
+
end
|
19
|
+
|
20
|
+
end # Sequence
|
21
|
+
end # Reporter
|
22
|
+
end # Mutant
|
data/lib/mutant/result.rb
CHANGED
@@ -136,14 +136,7 @@ module Mutant
|
|
136
136
|
alive_mutation_results.empty?
|
137
137
|
end
|
138
138
|
|
139
|
-
#
|
140
|
-
#
|
141
|
-
# @return [Boolean]
|
142
|
-
def continue?
|
143
|
-
mutation_results.all?(&:success?)
|
144
|
-
end
|
145
|
-
|
146
|
-
# Killed mutations
|
139
|
+
# Alive mutations
|
147
140
|
#
|
148
141
|
# @return [Array<Result::Mutation>]
|
149
142
|
def alive_mutation_results
|
@@ -179,7 +172,9 @@ module Mutant
|
|
179
172
|
alive_mutation_results.length
|
180
173
|
end
|
181
174
|
|
182
|
-
|
175
|
+
private
|
176
|
+
|
177
|
+
# Killed mutation results
|
183
178
|
#
|
184
179
|
# @return [Array<Result::Mutation>]
|
185
180
|
def killed_mutation_results
|
data/lib/mutant/version.rb
CHANGED
data/meta/dsym.rb
CHANGED
@@ -2,11 +2,11 @@ Mutant::Meta::Example.add do
|
|
2
2
|
source ':"foo#{bar}baz"'
|
3
3
|
|
4
4
|
singleton_mutations
|
5
|
-
|
6
|
-
mutation
|
7
|
-
mutation
|
8
|
-
mutation
|
9
|
-
mutation
|
10
|
-
mutation
|
11
|
-
mutation
|
5
|
+
|
6
|
+
mutation ':"#{nil}#{bar}#{"baz"}"'
|
7
|
+
mutation ':"#{self}#{bar}#{"baz"}"'
|
8
|
+
mutation ':"#{"foo"}#{nil}#{"baz"}"'
|
9
|
+
mutation ':"#{"foo"}#{self}#{"baz"}"'
|
10
|
+
mutation ':"#{"foo"}#{bar}#{nil}"'
|
11
|
+
mutation ':"#{"foo"}#{bar}#{self}"'
|
12
12
|
end
|
data/mutant.gemspec
CHANGED
@@ -21,14 +21,14 @@ Gem::Specification.new do |gem|
|
|
21
21
|
|
22
22
|
gem.required_ruby_version = '>= 2.1'
|
23
23
|
|
24
|
-
gem.add_runtime_dependency('parser', '~> 2.
|
25
|
-
gem.add_runtime_dependency('ast', '~> 2.
|
24
|
+
gem.add_runtime_dependency('parser', '~> 2.3.0')
|
25
|
+
gem.add_runtime_dependency('ast', '~> 2.2')
|
26
26
|
gem.add_runtime_dependency('diff-lcs', '~> 1.2')
|
27
27
|
gem.add_runtime_dependency('parallel', '~> 1.3')
|
28
|
-
gem.add_runtime_dependency('morpher', '~> 0.2.
|
28
|
+
gem.add_runtime_dependency('morpher', '~> 0.2.6')
|
29
29
|
gem.add_runtime_dependency('procto', '~> 0.0.2')
|
30
30
|
gem.add_runtime_dependency('abstract_type', '~> 0.0.7')
|
31
|
-
gem.add_runtime_dependency('unparser', '~> 0.2.
|
31
|
+
gem.add_runtime_dependency('unparser', '~> 0.2.5')
|
32
32
|
gem.add_runtime_dependency('ice_nine', '~> 0.11.1')
|
33
33
|
gem.add_runtime_dependency('adamantium', '~> 0.2.0')
|
34
34
|
gem.add_runtime_dependency('memoizable', '~> 0.4.2')
|
@@ -36,7 +36,7 @@ Gem::Specification.new do |gem|
|
|
36
36
|
gem.add_runtime_dependency('anima', '~> 0.3.0')
|
37
37
|
gem.add_runtime_dependency('concord', '~> 0.1.5')
|
38
38
|
|
39
|
-
gem.add_development_dependency('devtools', '~> 0.1.
|
39
|
+
gem.add_development_dependency('devtools', '~> 0.1.3')
|
40
40
|
gem.add_development_dependency('bundler', '~> 1.10')
|
41
41
|
gem.add_development_dependency('ffi', '~> 1.9.6')
|
42
42
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -42,8 +42,18 @@ RSpec.describe Mutant::AST::NamedChildren do
|
|
42
42
|
end
|
43
43
|
|
44
44
|
describe '#bar' do
|
45
|
-
|
46
|
-
|
45
|
+
context 'when node is present' do
|
46
|
+
it 'returns named child bar' do
|
47
|
+
expect(instance.bar).to be(node_bar)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when node is NOT present' do
|
52
|
+
let(:node) { s(:node, node_foo) }
|
53
|
+
|
54
|
+
it 'returns nil' do
|
55
|
+
expect(instance.bar).to be(nil)
|
56
|
+
end
|
47
57
|
end
|
48
58
|
end
|
49
59
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# rubocop:disable ClosingParenthesisIndentation
|
2
|
-
RSpec.describe Mutant::Context
|
2
|
+
RSpec.describe Mutant::Context do
|
3
3
|
describe '.wrap' do
|
4
4
|
subject { described_class.wrap(scope, node) }
|
5
5
|
|
@@ -32,4 +32,88 @@ RSpec.describe Mutant::Context::Scope do
|
|
32
32
|
it { should eql(expected) }
|
33
33
|
end
|
34
34
|
end
|
35
|
+
|
36
|
+
let(:object) { described_class.new(scope, source_path) }
|
37
|
+
let(:scope_name) { instance_double(String) }
|
38
|
+
let(:source_path) { instance_double(Pathname) }
|
39
|
+
let(:scope) { TestApp::Literal }
|
40
|
+
|
41
|
+
describe '#identification' do
|
42
|
+
subject { object.identification }
|
43
|
+
|
44
|
+
it { should eql(scope.name) }
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#root' do
|
48
|
+
subject { object.root(node) }
|
49
|
+
|
50
|
+
let(:node) { s(:sym, :node) }
|
51
|
+
|
52
|
+
let(:expected_source) do
|
53
|
+
generate(parse(<<-RUBY))
|
54
|
+
module TestApp
|
55
|
+
class Literal
|
56
|
+
:node
|
57
|
+
end
|
58
|
+
end
|
59
|
+
RUBY
|
60
|
+
end
|
61
|
+
|
62
|
+
let(:generated_source) do
|
63
|
+
Unparser.unparse(subject)
|
64
|
+
end
|
65
|
+
|
66
|
+
let(:round_tripped_source) do
|
67
|
+
Unparser.unparse(parse(expected_source))
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should create correct source' do
|
71
|
+
expect(generated_source).to eql(expected_source)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#unqualified_name' do
|
76
|
+
subject { object.unqualified_name }
|
77
|
+
|
78
|
+
let(:path) { instance_double(Pathname) }
|
79
|
+
|
80
|
+
context 'with top level constant name' do
|
81
|
+
let(:scope) { TestApp }
|
82
|
+
|
83
|
+
it 'should return the unqualified name' do
|
84
|
+
should eql('TestApp')
|
85
|
+
end
|
86
|
+
|
87
|
+
it_should_behave_like 'an idempotent method'
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'with scoped constant name' do
|
91
|
+
it 'should return the unqualified name' do
|
92
|
+
should eql('Literal')
|
93
|
+
end
|
94
|
+
|
95
|
+
it_should_behave_like 'an idempotent method'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#match_expressions' do
|
100
|
+
subject { object.match_expressions }
|
101
|
+
|
102
|
+
context 'on toplevel scope' do
|
103
|
+
let(:scope) { TestApp }
|
104
|
+
|
105
|
+
it { should eql([parse_expression('TestApp*')]) }
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'on nested scope' do
|
109
|
+
specify do
|
110
|
+
should eql(
|
111
|
+
[
|
112
|
+
parse_expression('TestApp::Literal*'),
|
113
|
+
parse_expression('TestApp*')
|
114
|
+
]
|
115
|
+
)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
35
119
|
end
|