mutant 0.10.25 → 0.10.30
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/mutant +1 -1
- data/lib/mutant.rb +15 -10
- data/lib/mutant/ast/find_metaclass_containing.rb +1 -1
- data/lib/mutant/ast/regexp.rb +17 -0
- data/lib/mutant/ast/regexp/transformer.rb +2 -2
- data/lib/mutant/ast/regexp/transformer/quantifier.rb +4 -2
- data/lib/mutant/bootstrap.rb +14 -6
- data/lib/mutant/cli/command.rb +4 -0
- data/lib/mutant/cli/command/environment/subject.rb +0 -4
- data/lib/mutant/cli/command/environment/test.rb +36 -0
- data/lib/mutant/cli/command/root.rb +1 -1
- data/lib/mutant/config.rb +18 -6
- data/lib/mutant/context.rb +1 -1
- data/lib/mutant/env.rb +6 -2
- data/lib/mutant/expression.rb +1 -1
- data/lib/mutant/expression/method.rb +4 -4
- data/lib/mutant/expression/methods.rb +5 -4
- data/lib/mutant/hooks.rb +77 -0
- data/lib/mutant/integration.rb +8 -2
- data/lib/mutant/isolation/exception.rb +22 -0
- data/lib/mutant/isolation/fork.rb +9 -12
- data/lib/mutant/matcher.rb +1 -1
- data/lib/mutant/matcher/config.rb +4 -3
- data/lib/mutant/matcher/method.rb +12 -10
- data/lib/mutant/matcher/method/instance.rb +6 -2
- data/lib/mutant/matcher/methods.rb +3 -5
- data/lib/mutant/meta/example.rb +1 -1
- data/lib/mutant/meta/example/dsl.rb +0 -1
- data/lib/mutant/meta/example/verification.rb +1 -1
- data/lib/mutant/mutation.rb +1 -1
- data/lib/mutant/mutator.rb +8 -1
- data/lib/mutant/mutator/node.rb +0 -5
- data/lib/mutant/mutator/node/argument.rb +2 -2
- data/lib/mutant/mutator/node/index.rb +1 -0
- data/lib/mutant/mutator/node/literal/float.rb +1 -3
- data/lib/mutant/mutator/node/literal/integer.rb +3 -6
- data/lib/mutant/mutator/node/literal/range.rb +1 -1
- data/lib/mutant/mutator/node/literal/regex.rb +3 -17
- data/lib/mutant/mutator/node/module.rb +19 -0
- data/lib/mutant/mutator/node/named_value/variable_assignment.rb +3 -3
- data/lib/mutant/mutator/node/regexp/character_type.rb +1 -1
- data/lib/mutant/mutator/node/regexp/named_group.rb +39 -0
- data/lib/mutant/mutator/node/regexp/zero_or_more.rb +2 -2
- data/lib/mutant/mutator/node/regopt.rb +1 -1
- data/lib/mutant/mutator/node/send.rb +63 -7
- data/lib/mutant/parallel.rb +2 -2
- data/lib/mutant/parallel/driver.rb +1 -1
- data/lib/mutant/parallel/worker.rb +4 -1
- data/lib/mutant/parser.rb +1 -1
- data/lib/mutant/pipe.rb +13 -1
- data/lib/mutant/procto.rb +23 -0
- data/lib/mutant/reporter/cli/printer.rb +10 -4
- data/lib/mutant/reporter/cli/printer/env.rb +2 -2
- data/lib/mutant/reporter/cli/printer/env_progress.rb +2 -2
- data/lib/mutant/reporter/cli/printer/isolation_result.rb +3 -1
- data/lib/mutant/repository/diff.rb +1 -1
- data/lib/mutant/selector.rb +1 -1
- data/lib/mutant/subject.rb +1 -1
- data/lib/mutant/subject/method/instance.rb +5 -42
- data/lib/mutant/transform.rb +1 -1
- data/lib/mutant/variable.rb +322 -0
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/world.rb +1 -1
- metadata +12 -159
@@ -28,11 +28,10 @@ module Mutant
|
|
28
28
|
emit_type(s(:str, NULL_REGEXP_SOURCE), options)
|
29
29
|
end
|
30
30
|
|
31
|
-
# NOTE: will only mutate parts of regexp body if the
|
32
|
-
# body is composed of only strings. Regular expressions
|
33
|
-
# with interpolation are skipped
|
34
31
|
def mutate_body
|
35
|
-
|
32
|
+
# NOTE: will only mutate parts of regexp body if the body is composed of only strings.
|
33
|
+
# Regular expressions with interpolation are skipped.
|
34
|
+
return unless (body_ast = AST::Regexp.expand_regexp_ast(input))
|
36
35
|
|
37
36
|
Mutator.mutate(body_ast).each do |mutation|
|
38
37
|
source = AST::Regexp.to_expression(mutation).to_s
|
@@ -40,19 +39,6 @@ module Mutant
|
|
40
39
|
end
|
41
40
|
end
|
42
41
|
|
43
|
-
def body_ast
|
44
|
-
AST::Regexp.to_ast(body_expression)
|
45
|
-
end
|
46
|
-
|
47
|
-
def body_expression
|
48
|
-
AST::Regexp.parse(body.map(&:children).join)
|
49
|
-
end
|
50
|
-
memoize :body_expression
|
51
|
-
|
52
|
-
def body
|
53
|
-
children.slice(0...-1)
|
54
|
-
end
|
55
|
-
|
56
42
|
end # Regex
|
57
43
|
end # Literal
|
58
44
|
end # Node
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Mutator
|
5
|
+
class Node
|
6
|
+
class Module < self
|
7
|
+
handle :module
|
8
|
+
|
9
|
+
children :klass, :body
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def dispatch
|
14
|
+
emit_body_mutations if body
|
15
|
+
end
|
16
|
+
end # Module
|
17
|
+
end # Node
|
18
|
+
end # Mutator
|
19
|
+
end # Mutant
|
@@ -17,9 +17,9 @@ module Mutant
|
|
17
17
|
lvasgn: EMPTY_STRING
|
18
18
|
}
|
19
19
|
|
20
|
-
MAP =
|
21
|
-
|
22
|
-
|
20
|
+
MAP = map
|
21
|
+
.transform_values { |prefix| [prefix, /^#{::Regexp.escape(prefix)}/] }
|
22
|
+
.freeze
|
23
23
|
|
24
24
|
handle(*MAP.keys)
|
25
25
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Mutator
|
5
|
+
class Node
|
6
|
+
module Regexp
|
7
|
+
# Mutator for regexp named capture groups, such as `/(?<foo>bar)/`
|
8
|
+
class NamedGroup < Node
|
9
|
+
handle(:regexp_named_group)
|
10
|
+
|
11
|
+
children :name, :group
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def dispatch
|
16
|
+
return unless group
|
17
|
+
|
18
|
+
emit_group_mutations
|
19
|
+
|
20
|
+
# Allows unused captures to be kept and named if they are explicitly prefixed with an
|
21
|
+
# underscore, like we allow with unused local variables.
|
22
|
+
return if name_underscored?
|
23
|
+
|
24
|
+
emit(s(:regexp_passive_group, group))
|
25
|
+
emit_name_underscore_mutation
|
26
|
+
end
|
27
|
+
|
28
|
+
def emit_name_underscore_mutation
|
29
|
+
emit_type("_#{name}", group)
|
30
|
+
end
|
31
|
+
|
32
|
+
def name_underscored?
|
33
|
+
name.start_with?('_')
|
34
|
+
end
|
35
|
+
end # EndOfLineAnchor
|
36
|
+
end # Regexp
|
37
|
+
end # Node
|
38
|
+
end # Mutator
|
39
|
+
end # Mutant
|
@@ -6,11 +6,11 @@ module Mutant
|
|
6
6
|
module Regexp
|
7
7
|
# Mutator for zero-or-more quantifier, `*`
|
8
8
|
class ZeroOrMore < Node
|
9
|
-
MAP =
|
9
|
+
MAP = {
|
10
10
|
regexp_greedy_zero_or_more: :regexp_greedy_one_or_more,
|
11
11
|
regexp_reluctant_zero_or_more: :regexp_reluctant_one_or_more,
|
12
12
|
regexp_possessive_zero_or_more: :regexp_possessive_one_or_more
|
13
|
-
|
13
|
+
}.freeze
|
14
14
|
|
15
15
|
handle(*MAP.keys)
|
16
16
|
|
@@ -13,7 +13,7 @@ module Mutant
|
|
13
13
|
|
14
14
|
children :receiver, :selector
|
15
15
|
|
16
|
-
SELECTOR_REPLACEMENTS =
|
16
|
+
SELECTOR_REPLACEMENTS = {
|
17
17
|
:< => %i[== eql? equal?],
|
18
18
|
:<= => %i[< == eql? equal?],
|
19
19
|
:== => %i[eql? equal?],
|
@@ -25,15 +25,14 @@ module Mutant
|
|
25
25
|
all?: %i[any?],
|
26
26
|
any?: %i[all?],
|
27
27
|
at: %i[fetch key?],
|
28
|
-
eql?: %i[equal?],
|
29
28
|
fetch: %i[key?],
|
30
29
|
flat_map: %i[map],
|
31
30
|
gsub: %i[sub],
|
32
31
|
is_a?: %i[instance_of?],
|
33
32
|
kind_of?: %i[instance_of?],
|
34
33
|
map: %i[each],
|
35
|
-
method: %i[public_method],
|
36
34
|
match: %i[match?],
|
35
|
+
method: %i[public_method],
|
37
36
|
reverse_each: %i[each],
|
38
37
|
reverse_map: %i[map each],
|
39
38
|
reverse_merge: %i[merge],
|
@@ -43,13 +42,17 @@ module Mutant
|
|
43
42
|
to_i: %i[to_int],
|
44
43
|
to_s: %i[to_str],
|
45
44
|
values_at: %i[fetch_values]
|
46
|
-
)
|
45
|
+
}.freeze.tap { |hash| hash.values(&:freeze) }
|
47
46
|
|
48
|
-
RECEIVER_SELECTOR_REPLACEMENTS =
|
47
|
+
RECEIVER_SELECTOR_REPLACEMENTS = {
|
49
48
|
Date: {
|
50
49
|
parse: %i[jd civil strptime iso8601 rfc3339 xmlschema rfc2822 rfc822 httpdate jisx0301]
|
51
|
-
}
|
52
|
-
|
50
|
+
}.freeze
|
51
|
+
}.freeze
|
52
|
+
|
53
|
+
REGEXP_MATCH_METHODS = %i[=~ match match?].freeze
|
54
|
+
REGEXP_START_WITH_NODES = %i[regexp_bos_anchor regexp_literal_literal].freeze
|
55
|
+
REGEXP_END_WITH_NODES = %i[regexp_literal_literal regexp_eos_anchor].freeze
|
53
56
|
|
54
57
|
private
|
55
58
|
|
@@ -84,6 +87,8 @@ module Mutant
|
|
84
87
|
end
|
85
88
|
|
86
89
|
def emit_selector_specific_mutations
|
90
|
+
emit_reduce_to_sum_mutation
|
91
|
+
emit_start_end_with_mutations
|
87
92
|
emit_predicate_mutations
|
88
93
|
emit_array_mutation
|
89
94
|
emit_static_send
|
@@ -94,6 +99,50 @@ module Mutant
|
|
94
99
|
emit_lambda_mutation
|
95
100
|
end
|
96
101
|
|
102
|
+
def emit_reduce_to_sum_mutation
|
103
|
+
return unless selector.equal?(:reduce)
|
104
|
+
|
105
|
+
reducer = arguments.last
|
106
|
+
|
107
|
+
return unless reducer.eql?(s(:sym, :+)) || reducer.eql?(s(:block_pass, s(:sym, :+)))
|
108
|
+
|
109
|
+
if arguments.length > 1
|
110
|
+
initial_value = arguments.first
|
111
|
+
emit_type(receiver, :sum, initial_value)
|
112
|
+
else
|
113
|
+
emit_type(receiver, :sum)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def emit_start_end_with_mutations # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
118
|
+
return unless REGEXP_MATCH_METHODS.include?(selector) && arguments.one?
|
119
|
+
|
120
|
+
argument = Mutant::Util.one(arguments)
|
121
|
+
|
122
|
+
return unless argument.type.equal?(:regexp) && (
|
123
|
+
regexp_ast = AST::Regexp.expand_regexp_ast(argument)
|
124
|
+
)
|
125
|
+
|
126
|
+
regexp_children = regexp_ast.children
|
127
|
+
|
128
|
+
case regexp_children.map(&:type)
|
129
|
+
when REGEXP_START_WITH_NODES
|
130
|
+
emit_start_with(regexp_children)
|
131
|
+
when REGEXP_END_WITH_NODES
|
132
|
+
emit_end_with(regexp_children)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def emit_start_with(regexp_nodes)
|
137
|
+
literal = Mutant::Util.one(regexp_nodes.last.children)
|
138
|
+
emit_type(receiver, :start_with?, s(:str, literal))
|
139
|
+
end
|
140
|
+
|
141
|
+
def emit_end_with(regexp_nodes)
|
142
|
+
literal = Mutant::Util.one(regexp_nodes.first.children)
|
143
|
+
emit_type(receiver, :end_with?, s(:str, literal))
|
144
|
+
end
|
145
|
+
|
97
146
|
def emit_predicate_mutations
|
98
147
|
return unless selector.match?(/\?\z/) && !selector.equal?(:defined?)
|
99
148
|
|
@@ -198,11 +247,18 @@ module Mutant
|
|
198
247
|
def mutate_receiver
|
199
248
|
return unless receiver
|
200
249
|
emit_implicit_self
|
250
|
+
emit_explicit_self
|
201
251
|
emit_receiver_mutations do |node|
|
202
252
|
!n_nil?(node)
|
203
253
|
end
|
204
254
|
end
|
205
255
|
|
256
|
+
def emit_explicit_self
|
257
|
+
return if UNARY_METHOD_OPERATORS.include?(selector)
|
258
|
+
|
259
|
+
emit_receiver(N_SELF) unless n_nil?(receiver)
|
260
|
+
end
|
261
|
+
|
206
262
|
def emit_implicit_self
|
207
263
|
emit_receiver(nil) if n_self?(receiver) && !(
|
208
264
|
KEYWORDS.include?(selector) ||
|
data/lib/mutant/parallel.rb
CHANGED
@@ -90,7 +90,7 @@ module Mutant
|
|
90
90
|
|
91
91
|
# Parallel run configuration
|
92
92
|
class Config
|
93
|
-
include Adamantium
|
93
|
+
include Adamantium, Anima.new(
|
94
94
|
:block,
|
95
95
|
:jobs,
|
96
96
|
:process_name,
|
@@ -102,7 +102,7 @@ module Mutant
|
|
102
102
|
|
103
103
|
# Parallel execution status
|
104
104
|
class Status
|
105
|
-
include Adamantium
|
105
|
+
include Adamantium, Anima.new(
|
106
106
|
:active_jobs,
|
107
107
|
:done,
|
108
108
|
:payload
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Mutant
|
4
4
|
module Parallel
|
5
5
|
class Worker
|
6
|
-
include Adamantium
|
6
|
+
include Adamantium, Anima.new(
|
7
7
|
:connection,
|
8
8
|
:index,
|
9
9
|
:pid,
|
@@ -29,6 +29,9 @@ module Mutant
|
|
29
29
|
response = Pipe.from_io(io)
|
30
30
|
|
31
31
|
pid = process.fork do
|
32
|
+
request.reset_binmode
|
33
|
+
response.reset_binmode
|
34
|
+
|
32
35
|
world.thread.current.name = process_name
|
33
36
|
world.process.setproctitle(process_name)
|
34
37
|
|
data/lib/mutant/parser.rb
CHANGED
data/lib/mutant/pipe.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Mutant
|
4
4
|
# Pipe abstraction
|
5
5
|
class Pipe
|
6
|
-
include Adamantium
|
6
|
+
include Adamantium, Anima.new(:reader, :writer)
|
7
7
|
|
8
8
|
# Run block with pipe in binmode
|
9
9
|
#
|
@@ -35,6 +35,18 @@ module Mutant
|
|
35
35
|
reader
|
36
36
|
end
|
37
37
|
|
38
|
+
# Set binmode (again)
|
39
|
+
#
|
40
|
+
# Ruby has a bug where the binmode setting may be lost duringa fork.
|
41
|
+
# This API allows to set the binmode again.
|
42
|
+
#
|
43
|
+
# @return [self]
|
44
|
+
def reset_binmode
|
45
|
+
reader.binmode
|
46
|
+
writer.binmode
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
38
50
|
class Connection
|
39
51
|
include Anima.new(:marshal, :reader, :writer)
|
40
52
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
module Procto
|
5
|
+
# Define the .call method on +host+
|
6
|
+
#
|
7
|
+
# @param [Object] host
|
8
|
+
# the hosting object
|
9
|
+
#
|
10
|
+
# @return [undefined]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
def self.included(host)
|
14
|
+
host.extend(ClassMethods)
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
def call(*arguments)
|
19
|
+
new(*arguments).call
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end # Procto
|
23
|
+
end # Unparser
|
@@ -5,13 +5,19 @@ module Mutant
|
|
5
5
|
class CLI
|
6
6
|
# CLI runner status printer base class
|
7
7
|
class Printer
|
8
|
-
include
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
include(
|
9
|
+
AbstractType,
|
10
|
+
Adamantium,
|
11
|
+
Concord.new(:output, :object),
|
12
|
+
Procto
|
13
|
+
)
|
12
14
|
|
13
15
|
private_class_method :new
|
14
16
|
|
17
|
+
def call
|
18
|
+
run
|
19
|
+
end
|
20
|
+
|
15
21
|
# Create delegators to object
|
16
22
|
#
|
17
23
|
# @return [undefined]
|
@@ -15,13 +15,13 @@ module Mutant
|
|
15
15
|
:test_subject_ratio
|
16
16
|
)
|
17
17
|
|
18
|
-
FORMATS =
|
18
|
+
FORMATS = [
|
19
19
|
[:info, 'Subjects: %s', :amount_subjects ],
|
20
20
|
[:info, 'Total-Tests: %s', :amount_total_tests ],
|
21
21
|
[:info, 'Selected-Tests: %s', :amount_selected_tests],
|
22
22
|
[:info, 'Tests/Subject: %0.2f avg', :test_subject_ratio ],
|
23
23
|
[:info, 'Mutations: %s', :amount_mutations ]
|
24
|
-
])
|
24
|
+
].each(&:freeze)
|
25
25
|
|
26
26
|
# Run printer
|
27
27
|
#
|
@@ -18,7 +18,7 @@ module Mutant
|
|
18
18
|
:runtime
|
19
19
|
)
|
20
20
|
|
21
|
-
FORMATS =
|
21
|
+
FORMATS = [
|
22
22
|
[:info, 'Results: %s', :amount_mutation_results],
|
23
23
|
[:info, 'Kills: %s', :amount_mutations_killed],
|
24
24
|
[:info, 'Alive: %s', :amount_mutations_alive ],
|
@@ -28,7 +28,7 @@ module Mutant
|
|
28
28
|
[:info, 'Overhead: %0.2f%%', :overhead_percent ],
|
29
29
|
[:info, 'Mutations/s: %0.2f', :mutations_per_second ],
|
30
30
|
[:status, 'Coverage: %0.2f%%', :coverage_percent ]
|
31
|
-
])
|
31
|
+
].each(&:freeze)
|
32
32
|
|
33
33
|
# Run printer
|
34
34
|
#
|