mutant 0.8.9 → 0.8.10
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.
- 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
|