mutant 0.10.20 → 0.10.25
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/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
|
|