mutant 0.10.20 → 0.10.25
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/mutant.rb +23 -4
- data/lib/mutant/ast/meta/send.rb +0 -1
- data/lib/mutant/ast/regexp.rb +37 -0
- data/lib/mutant/ast/regexp/transformer.rb +150 -0
- data/lib/mutant/ast/regexp/transformer/direct.rb +121 -0
- data/lib/mutant/ast/regexp/transformer/named_group.rb +50 -0
- data/lib/mutant/ast/regexp/transformer/options_group.rb +68 -0
- data/lib/mutant/ast/regexp/transformer/quantifier.rb +90 -0
- data/lib/mutant/ast/regexp/transformer/recursive.rb +56 -0
- data/lib/mutant/ast/regexp/transformer/root.rb +28 -0
- data/lib/mutant/ast/regexp/transformer/text.rb +58 -0
- data/lib/mutant/ast/types.rb +115 -11
- data/lib/mutant/cli/command/environment.rb +9 -3
- data/lib/mutant/config.rb +8 -54
- data/lib/mutant/config/coverage_criteria.rb +61 -0
- data/lib/mutant/env.rb +8 -6
- data/lib/mutant/expression.rb +0 -12
- data/lib/mutant/expression/namespace.rb +1 -1
- data/lib/mutant/isolation/fork.rb +1 -1
- data/lib/mutant/loader.rb +1 -1
- data/lib/mutant/matcher.rb +2 -2
- data/lib/mutant/matcher/config.rb +27 -6
- data/lib/mutant/matcher/method.rb +2 -3
- data/lib/mutant/matcher/method/metaclass.rb +1 -1
- data/lib/mutant/meta/example/dsl.rb +6 -1
- data/lib/mutant/mutator/node/arguments.rb +0 -2
- data/lib/mutant/mutator/node/block.rb +5 -1
- data/lib/mutant/mutator/node/dynamic_literal.rb +1 -1
- data/lib/mutant/mutator/node/kwargs.rb +44 -0
- data/lib/mutant/mutator/node/literal/regex.rb +26 -0
- data/lib/mutant/mutator/node/literal/symbol.rb +0 -2
- data/lib/mutant/mutator/node/regexp.rb +20 -0
- data/lib/mutant/mutator/node/regexp/alternation_meta.rb +20 -0
- data/lib/mutant/mutator/node/regexp/beginning_of_line_anchor.rb +20 -0
- data/lib/mutant/mutator/node/regexp/capture_group.rb +25 -0
- data/lib/mutant/mutator/node/regexp/character_type.rb +31 -0
- data/lib/mutant/mutator/node/regexp/end_of_line_anchor.rb +20 -0
- data/lib/mutant/mutator/node/regexp/end_of_string_or_before_end_of_line_anchor.rb +20 -0
- data/lib/mutant/mutator/node/regexp/zero_or_more.rb +34 -0
- data/lib/mutant/mutator/node/sclass.rb +1 -1
- data/lib/mutant/mutator/node/send.rb +36 -19
- data/lib/mutant/parallel.rb +43 -28
- data/lib/mutant/parallel/driver.rb +9 -3
- data/lib/mutant/parallel/worker.rb +60 -2
- data/lib/mutant/pipe.rb +94 -0
- data/lib/mutant/reporter/cli/printer/env.rb +1 -1
- data/lib/mutant/reporter/cli/printer/isolation_result.rb +1 -6
- data/lib/mutant/result.rb +8 -0
- data/lib/mutant/runner.rb +7 -10
- data/lib/mutant/runner/sink.rb +12 -2
- data/lib/mutant/subject.rb +1 -3
- data/lib/mutant/subject/method/instance.rb +2 -4
- data/lib/mutant/timer.rb +2 -4
- data/lib/mutant/transform.rb +25 -2
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/world.rb +1 -2
- metadata +48 -9
- data/lib/mutant/warnings.rb +0 -106
@@ -19,6 +19,7 @@ module Mutant
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def dispatch
|
22
|
+
mutate_body
|
22
23
|
emit_singletons unless parent_node
|
23
24
|
children.each_with_index do |child, index|
|
24
25
|
mutate_child(index) unless n_str?(child)
|
@@ -27,6 +28,31 @@ module Mutant
|
|
27
28
|
emit_type(s(:str, NULL_REGEXP_SOURCE), options)
|
28
29
|
end
|
29
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
|
+
def mutate_body
|
35
|
+
return unless body.all?(&method(:n_str?))
|
36
|
+
|
37
|
+
Mutator.mutate(body_ast).each do |mutation|
|
38
|
+
source = AST::Regexp.to_expression(mutation).to_s
|
39
|
+
emit_type(s(:str, source), options)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
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
|
+
|
30
56
|
end # Regex
|
31
57
|
end # Literal
|
32
58
|
end # Node
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Mutator
|
5
|
+
class Node
|
6
|
+
module Regexp
|
7
|
+
# Mutator for root expression regexp wrapper
|
8
|
+
class RootExpression < Node
|
9
|
+
handle(:regexp_root_expression)
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def dispatch
|
14
|
+
children.each_index(&method(:mutate_child))
|
15
|
+
end
|
16
|
+
end # RootExpression
|
17
|
+
end # Regexp
|
18
|
+
end # Node
|
19
|
+
end # Mutator
|
20
|
+
end # Mutant
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Mutator
|
5
|
+
class Node
|
6
|
+
module Regexp
|
7
|
+
# Mutator for pipe in `/foo|bar/` regexp
|
8
|
+
class AlternationMeta < Node
|
9
|
+
handle(:regexp_alternation_meta)
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def dispatch
|
14
|
+
children.each_index(&method(:delete_child))
|
15
|
+
end
|
16
|
+
end # AlternationMeta
|
17
|
+
end # Regexp
|
18
|
+
end # Node
|
19
|
+
end # Mutator
|
20
|
+
end # Mutant
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Mutator
|
5
|
+
class Node
|
6
|
+
module Regexp
|
7
|
+
# Mutator for beginning of line anchor `^`
|
8
|
+
class BeginningOfLineAnchor < Node
|
9
|
+
handle(:regexp_bol_anchor)
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def dispatch
|
14
|
+
emit(s(:regexp_bos_anchor))
|
15
|
+
end
|
16
|
+
end # BeginningOfLineAnchor
|
17
|
+
end # Regexp
|
18
|
+
end # Node
|
19
|
+
end # Mutator
|
20
|
+
end # Mutant
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Mutator
|
5
|
+
class Node
|
6
|
+
module Regexp
|
7
|
+
# Mutator for regexp capture groups, such as `/(foo)/`
|
8
|
+
class CaptureGroup < Node
|
9
|
+
handle(:regexp_capture_group)
|
10
|
+
|
11
|
+
children :group
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def dispatch
|
16
|
+
return unless group
|
17
|
+
|
18
|
+
emit(s(:regexp_passive_group, group))
|
19
|
+
emit_group_mutations
|
20
|
+
end
|
21
|
+
end # EndOfLineAnchor
|
22
|
+
end # Regexp
|
23
|
+
end # Node
|
24
|
+
end # Mutator
|
25
|
+
end # Mutant
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Mutator
|
5
|
+
class Node
|
6
|
+
module Regexp
|
7
|
+
# Character type mutator
|
8
|
+
class CharacterType < Node
|
9
|
+
map = {
|
10
|
+
regexp_digit_type: :regexp_nondigit_type,
|
11
|
+
regexp_hex_type: :regexp_nonhex_type,
|
12
|
+
regexp_space_type: :regexp_nonspace_type,
|
13
|
+
regexp_word_boundary_anchor: :regexp_nonword_boundary_anchor,
|
14
|
+
regexp_word_type: :regexp_nonword_type,
|
15
|
+
regexp_xgrapheme_type: :regexp_linebreak_type
|
16
|
+
}
|
17
|
+
|
18
|
+
MAP = IceNine.deep_freeze(map.merge(map.invert))
|
19
|
+
|
20
|
+
handle(*MAP.keys)
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def dispatch
|
25
|
+
emit(s(MAP.fetch(node.type)))
|
26
|
+
end
|
27
|
+
end # CharacterType
|
28
|
+
end # Regexp
|
29
|
+
end # Node
|
30
|
+
end # Mutator
|
31
|
+
end # Mutant
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Mutator
|
5
|
+
class Node
|
6
|
+
module Regexp
|
7
|
+
# Mutator for end of line anchor `$`
|
8
|
+
class EndOfLineAnchor < Node
|
9
|
+
handle(:regexp_eol_anchor)
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def dispatch
|
14
|
+
emit(s(:regexp_eos_anchor))
|
15
|
+
end
|
16
|
+
end # EndOfLineAnchor
|
17
|
+
end # Regexp
|
18
|
+
end # Node
|
19
|
+
end # Mutator
|
20
|
+
end # Mutant
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Mutator
|
5
|
+
class Node
|
6
|
+
module Regexp
|
7
|
+
# Mutator for end of line or before end of string anchor `\Z`
|
8
|
+
class EndOfStringOrBeforeEndOfLineAnchor < Node
|
9
|
+
handle(:regexp_eos_ob_eol_anchor)
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def dispatch
|
14
|
+
emit(s(:regexp_eos_anchor))
|
15
|
+
end
|
16
|
+
end # EndOfStringOrBeforeEndOfLineAnchor
|
17
|
+
end # Regexp
|
18
|
+
end # Node
|
19
|
+
end # Mutator
|
20
|
+
end # Mutant
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Mutator
|
5
|
+
class Node
|
6
|
+
module Regexp
|
7
|
+
# Mutator for zero-or-more quantifier, `*`
|
8
|
+
class ZeroOrMore < Node
|
9
|
+
MAP = IceNine.deep_freeze(
|
10
|
+
regexp_greedy_zero_or_more: :regexp_greedy_one_or_more,
|
11
|
+
regexp_reluctant_zero_or_more: :regexp_reluctant_one_or_more,
|
12
|
+
regexp_possessive_zero_or_more: :regexp_possessive_one_or_more
|
13
|
+
)
|
14
|
+
|
15
|
+
handle(*MAP.keys)
|
16
|
+
|
17
|
+
children :min, :max, :subject
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Replace:
|
22
|
+
# * `/a*/` with `/a+/`
|
23
|
+
# * `/a*?/` with `/a+?/`
|
24
|
+
# * `/a*+/` with `/a++/`
|
25
|
+
def dispatch
|
26
|
+
emit(s(MAP.fetch(node.type), *children))
|
27
|
+
emit_subject_mutations
|
28
|
+
emit(subject)
|
29
|
+
end
|
30
|
+
end # ZeroOrMore
|
31
|
+
end # Regexp
|
32
|
+
end # Node
|
33
|
+
end # Mutator
|
34
|
+
end # Mutant
|
@@ -14,30 +14,35 @@ module Mutant
|
|
14
14
|
children :receiver, :selector
|
15
15
|
|
16
16
|
SELECTOR_REPLACEMENTS = IceNine.deep_freeze(
|
17
|
-
|
18
|
-
|
17
|
+
:< => %i[== eql? equal?],
|
18
|
+
:<= => %i[< == eql? equal?],
|
19
|
+
:== => %i[eql? equal?],
|
20
|
+
:=== => %i[is_a?],
|
21
|
+
:=~ => %i[match?],
|
22
|
+
:> => %i[== eql? equal?],
|
23
|
+
:>= => %i[> == eql? equal?],
|
24
|
+
__send__: %i[public_send],
|
25
|
+
all?: %i[any?],
|
26
|
+
any?: %i[all?],
|
27
|
+
at: %i[fetch key?],
|
28
|
+
eql?: %i[equal?],
|
29
|
+
fetch: %i[key?],
|
30
|
+
flat_map: %i[map],
|
31
|
+
gsub: %i[sub],
|
19
32
|
is_a?: %i[instance_of?],
|
33
|
+
kind_of?: %i[instance_of?],
|
34
|
+
map: %i[each],
|
35
|
+
method: %i[public_method],
|
36
|
+
match: %i[match?],
|
20
37
|
reverse_each: %i[each],
|
38
|
+
reverse_map: %i[map each],
|
21
39
|
reverse_merge: %i[merge],
|
22
|
-
map: %i[each],
|
23
|
-
flat_map: %i[map],
|
24
40
|
send: %i[public_send __send__],
|
25
|
-
__send__: %i[public_send],
|
26
|
-
method: %i[public_method],
|
27
|
-
gsub: %i[sub],
|
28
|
-
eql?: %i[equal?],
|
29
|
-
to_s: %i[to_str],
|
30
|
-
to_i: %i[to_int],
|
31
41
|
to_a: %i[to_ary],
|
32
42
|
to_h: %i[to_hash],
|
33
|
-
|
34
|
-
|
35
|
-
values_at: %i[fetch_values]
|
36
|
-
:== => %i[eql? equal?],
|
37
|
-
:>= => %i[> == eql? equal?],
|
38
|
-
:<= => %i[< == eql? equal?],
|
39
|
-
:> => %i[== eql? equal?],
|
40
|
-
:< => %i[== eql? equal?]
|
43
|
+
to_i: %i[to_int],
|
44
|
+
to_s: %i[to_str],
|
45
|
+
values_at: %i[fetch_values]
|
41
46
|
)
|
42
47
|
|
43
48
|
RECEIVER_SELECTOR_REPLACEMENTS = IceNine.deep_freeze(
|
@@ -79,6 +84,7 @@ module Mutant
|
|
79
84
|
end
|
80
85
|
|
81
86
|
def emit_selector_specific_mutations
|
87
|
+
emit_predicate_mutations
|
82
88
|
emit_array_mutation
|
83
89
|
emit_static_send
|
84
90
|
emit_const_get_mutation
|
@@ -88,6 +94,13 @@ module Mutant
|
|
88
94
|
emit_lambda_mutation
|
89
95
|
end
|
90
96
|
|
97
|
+
def emit_predicate_mutations
|
98
|
+
return unless selector.match?(/\?\z/) && !selector.equal?(:defined?)
|
99
|
+
|
100
|
+
emit(s(:true))
|
101
|
+
emit(s(:false))
|
102
|
+
end
|
103
|
+
|
91
104
|
def emit_array_mutation
|
92
105
|
return unless selector.equal?(:Array) && possible_kernel_method?
|
93
106
|
|
@@ -175,7 +188,11 @@ module Mutant
|
|
175
188
|
end
|
176
189
|
|
177
190
|
def emit_argument_propagation
|
178
|
-
|
191
|
+
return unless arguments.one?
|
192
|
+
|
193
|
+
argument = Mutant::Util.one(arguments)
|
194
|
+
|
195
|
+
emit_propagation(argument) unless n_kwargs?(argument)
|
179
196
|
end
|
180
197
|
|
181
198
|
def mutate_receiver
|
data/lib/mutant/parallel.rb
CHANGED
@@ -6,45 +6,61 @@ module Mutant
|
|
6
6
|
|
7
7
|
# Run async computation returning driver
|
8
8
|
#
|
9
|
+
# @param [World] world
|
9
10
|
# @param [Config] config
|
10
11
|
#
|
11
12
|
# @return [Driver]
|
12
|
-
def self.async(config)
|
13
|
-
shared
|
14
|
-
|
15
|
-
var_final: shared(Variable::IVar, config),
|
16
|
-
var_sink: shared(Variable::IVar, config, value: config.sink)
|
17
|
-
}
|
13
|
+
def self.async(world, config)
|
14
|
+
shared = shared_state(world, config)
|
15
|
+
workers = workers(world, config, shared)
|
18
16
|
|
19
17
|
Driver.new(
|
20
|
-
|
18
|
+
workers: workers,
|
19
|
+
threads: threads(world, config, workers),
|
21
20
|
**shared
|
22
21
|
)
|
23
22
|
end
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
**shared
|
36
|
-
)
|
24
|
+
def self.workers(world, config, shared)
|
25
|
+
Array.new(config.jobs) do |index|
|
26
|
+
Worker.start(
|
27
|
+
block: config.block,
|
28
|
+
index: index,
|
29
|
+
process_name: "#{config.process_name}-#{index}",
|
30
|
+
world: world,
|
31
|
+
**shared
|
32
|
+
)
|
33
|
+
end
|
37
34
|
end
|
35
|
+
private_class_method :workers
|
36
|
+
|
37
|
+
def self.shared_state(world, config)
|
38
|
+
{
|
39
|
+
var_active_jobs: shared(Variable::IVar, world, value: Set.new),
|
40
|
+
var_final: shared(Variable::IVar, world),
|
41
|
+
var_running: shared(Variable::MVar, world, value: config.jobs),
|
42
|
+
var_sink: shared(Variable::IVar, world, value: config.sink),
|
43
|
+
var_source: shared(Variable::IVar, world, value: config.source)
|
44
|
+
}
|
45
|
+
end
|
46
|
+
private_class_method :shared_state
|
47
|
+
|
48
|
+
def self.threads(world, config, workers)
|
49
|
+
thread = world.thread
|
38
50
|
|
39
|
-
|
40
|
-
|
51
|
+
workers.map do |worker|
|
52
|
+
thread.new do
|
53
|
+
thread.current.name = "#{config.thread_name}-#{worker.index}"
|
54
|
+
worker.call
|
55
|
+
end
|
56
|
+
end
|
41
57
|
end
|
42
58
|
private_class_method :threads
|
43
59
|
|
44
|
-
def self.shared(klass,
|
60
|
+
def self.shared(klass, world, **attributes)
|
45
61
|
klass.new(
|
46
|
-
condition_variable:
|
47
|
-
mutex:
|
62
|
+
condition_variable: world.condition_variable,
|
63
|
+
mutex: world.mutex,
|
48
64
|
**attributes
|
49
65
|
)
|
50
66
|
end
|
@@ -75,13 +91,12 @@ module Mutant
|
|
75
91
|
# Parallel run configuration
|
76
92
|
class Config
|
77
93
|
include Adamantium::Flat, Anima.new(
|
78
|
-
:
|
94
|
+
:block,
|
79
95
|
:jobs,
|
80
|
-
:
|
81
|
-
:processor,
|
96
|
+
:process_name,
|
82
97
|
:sink,
|
83
98
|
:source,
|
84
|
-
:
|
99
|
+
:thread_name
|
85
100
|
)
|
86
101
|
end # Config
|
87
102
|
|