mutant 0.14.2 → 0.15.0
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/VERSION +1 -1
- data/lib/mutant/ast/named_children.rb +0 -4
- data/lib/mutant/ast/nodes.rb +5 -0
- data/lib/mutant/ast/pattern/lexer.rb +0 -1
- data/lib/mutant/ast/structure.rb +10 -0
- data/lib/mutant/context.rb +2 -6
- data/lib/mutant/env.rb +8 -27
- data/lib/mutant/hooks.rb +2 -6
- data/lib/mutant/integration/null.rb +1 -3
- data/lib/mutant/integration.rb +2 -6
- data/lib/mutant/isolation/fork.rb +0 -2
- data/lib/mutant/matcher/method/instance.rb +2 -2
- data/lib/mutant/matcher/method.rb +0 -1
- data/lib/mutant/meta/example/verification.rb +0 -1
- data/lib/mutant/mutation/operators.rb +141 -44
- data/lib/mutant/mutation.rb +6 -18
- data/lib/mutant/mutator/node/and_asgn.rb +1 -0
- data/lib/mutant/mutator/node/binary.rb +5 -0
- data/lib/mutant/mutator/node/block.rb +10 -0
- data/lib/mutant/mutator/node/case_match.rb +46 -0
- data/lib/mutant/mutator/node/conditional_loop.rb +30 -1
- data/lib/mutant/mutator/node/define.rb +26 -0
- data/lib/mutant/mutator/node/defined.rb +1 -0
- data/lib/mutant/mutator/node/ensure.rb +24 -0
- data/lib/mutant/mutator/node/guard.rb +23 -0
- data/lib/mutant/mutator/node/in_pattern.rb +25 -0
- data/lib/mutant/mutator/node/literal/complex.rb +31 -0
- data/lib/mutant/mutator/node/literal/rational.rb +31 -0
- data/lib/mutant/mutator/node/literal/string.rb +1 -0
- data/lib/mutant/mutator/node/match_alt.rb +26 -0
- data/lib/mutant/mutator/node/match_pattern_p.rb +25 -0
- data/lib/mutant/mutator/node/op_asgn.rb +21 -1
- data/lib/mutant/mutator/node/or_asgn.rb +1 -4
- data/lib/mutant/mutator/node/regopt.rb +4 -6
- data/lib/mutant/mutator/node/rescue.rb +27 -0
- data/lib/mutant/mutator/node/send/binary.rb +45 -0
- data/lib/mutant/mutator/node/send.rb +23 -7
- data/lib/mutant/mutator/node/super.rb +2 -0
- data/lib/mutant/mutator/node/zsuper.rb +17 -0
- data/lib/mutant/mutator/node.rb +3 -9
- data/lib/mutant/mutator.rb +1 -3
- data/lib/mutant/parallel/connection.rb +9 -23
- data/lib/mutant/parallel/driver.rb +1 -5
- data/lib/mutant/parallel/source.rb +2 -1
- data/lib/mutant/parallel/worker.rb +8 -14
- data/lib/mutant/registry.rb +2 -1
- data/lib/mutant/reporter/cli/format.rb +4 -12
- data/lib/mutant/reporter/cli/printer.rb +2 -6
- data/lib/mutant/reporter/cli/progress_bar.rb +4 -12
- data/lib/mutant/reporter/cli.rb +6 -23
- data/lib/mutant/reporter/sequence.rb +1 -3
- data/lib/mutant/repository/diff.rb +1 -3
- data/lib/mutant/result.rb +16 -48
- data/lib/mutant/scope.rb +2 -6
- data/lib/mutant/segment.rb +1 -3
- data/lib/mutant/subject/method/instance.rb +3 -9
- data/lib/mutant/subject/method/metaclass.rb +1 -4
- data/lib/mutant/subject/method/singleton.rb +2 -8
- data/lib/mutant/subject/method.rb +3 -9
- data/lib/mutant/subject.rb +6 -18
- data/lib/mutant/timer.rb +2 -6
- data/lib/mutant/transform.rb +11 -33
- data/lib/mutant/usage.rb +6 -18
- data/lib/mutant/variable.rb +2 -6
- data/lib/mutant/world.rb +1 -3
- data/lib/mutant/zombifier.rb +3 -1
- data/lib/mutant.rb +8 -0
- metadata +39 -13
|
@@ -7,7 +7,14 @@ module Mutant
|
|
|
7
7
|
# Mutator for while expressions
|
|
8
8
|
class ConditionalLoop < self
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
INVERSE = {
|
|
11
|
+
until: :while,
|
|
12
|
+
until_post: :while_post,
|
|
13
|
+
while: :until,
|
|
14
|
+
while_post: :until_post
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
handle(*INVERSE.keys)
|
|
11
18
|
|
|
12
19
|
children :condition, :body
|
|
13
20
|
|
|
@@ -16,11 +23,33 @@ module Mutant
|
|
|
16
23
|
def dispatch
|
|
17
24
|
emit_singletons
|
|
18
25
|
emit_condition_mutations
|
|
26
|
+
emit_type_swap
|
|
27
|
+
if post?
|
|
28
|
+
dispatch_post
|
|
29
|
+
else
|
|
30
|
+
dispatch_regular
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def dispatch_post
|
|
35
|
+
emit_body_mutations { |node| node.type.equal?(:kwbegin) }
|
|
36
|
+
emit_body(s(:kwbegin, N_RAISE))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def dispatch_regular
|
|
19
40
|
emit_body_mutations if body
|
|
20
41
|
emit_body(nil)
|
|
21
42
|
emit_body(N_RAISE)
|
|
22
43
|
end
|
|
23
44
|
|
|
45
|
+
def post?
|
|
46
|
+
node.type.end_with?('_post')
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def emit_type_swap
|
|
50
|
+
emit(s(INVERSE.fetch(node.type), *children))
|
|
51
|
+
end
|
|
52
|
+
|
|
24
53
|
end # ConditionalLoop
|
|
25
54
|
end # Node
|
|
26
55
|
end # Mutator
|
|
@@ -6,6 +6,15 @@ module Mutant
|
|
|
6
6
|
# Namespace for define mutations
|
|
7
7
|
class Define < self
|
|
8
8
|
|
|
9
|
+
# Mapping from AST node types to their type-aware empty/default values
|
|
10
|
+
TYPE_TO_DEFAULT = {
|
|
11
|
+
array: N_EMPTY_ARRAY,
|
|
12
|
+
hash: N_EMPTY_HASH,
|
|
13
|
+
str: N_EMPTY_STRING,
|
|
14
|
+
int: N_ZERO_INTEGER,
|
|
15
|
+
float: N_ZERO_FLOAT
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
9
18
|
private
|
|
10
19
|
|
|
11
20
|
def dispatch
|
|
@@ -13,6 +22,7 @@ module Mutant
|
|
|
13
22
|
emit_optarg_body_assignments
|
|
14
23
|
emit_body(N_RAISE)
|
|
15
24
|
emit_body(N_ZSUPER)
|
|
25
|
+
emit_type_aware_defaults
|
|
16
26
|
|
|
17
27
|
return if !body || ignore?(body)
|
|
18
28
|
|
|
@@ -21,6 +31,22 @@ module Mutant
|
|
|
21
31
|
emit_body_mutations
|
|
22
32
|
end
|
|
23
33
|
|
|
34
|
+
def emit_type_aware_defaults
|
|
35
|
+
return unless body
|
|
36
|
+
|
|
37
|
+
default_node = TYPE_TO_DEFAULT[return_expression.type]
|
|
38
|
+
|
|
39
|
+
emit_body(default_node) if default_node
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def return_expression
|
|
43
|
+
if n_begin?(body)
|
|
44
|
+
body.children.last
|
|
45
|
+
else
|
|
46
|
+
body
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
24
50
|
def emit_optarg_body_assignments
|
|
25
51
|
arguments.children.each do |argument|
|
|
26
52
|
next unless n_optarg?(argument) && AST::Meta::Optarg.new(node: argument).used?
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mutant
|
|
4
|
+
class Mutator
|
|
5
|
+
class Node
|
|
6
|
+
# Mutator for ensure nodes
|
|
7
|
+
class Ensure < self
|
|
8
|
+
|
|
9
|
+
handle(:ensure)
|
|
10
|
+
|
|
11
|
+
children :body, :ensure_body
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def dispatch
|
|
16
|
+
emit(body) if body
|
|
17
|
+
emit_body_mutations if body
|
|
18
|
+
emit_ensure_body_mutations if ensure_body
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end # Ensure
|
|
22
|
+
end # Node
|
|
23
|
+
end # Mutator
|
|
24
|
+
end # Mutant
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mutant
|
|
4
|
+
class Mutator
|
|
5
|
+
class Node
|
|
6
|
+
# Mutator for pattern match guard nodes (if_guard, unless_guard)
|
|
7
|
+
class Guard < self
|
|
8
|
+
handle(:if_guard, :unless_guard)
|
|
9
|
+
|
|
10
|
+
children :condition
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def dispatch
|
|
15
|
+
emit_condition_mutations
|
|
16
|
+
emit_type(N_TRUE)
|
|
17
|
+
emit_type(N_FALSE)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end # Guard
|
|
21
|
+
end # Node
|
|
22
|
+
end # Mutator
|
|
23
|
+
end # Mutant
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mutant
|
|
4
|
+
class Mutator
|
|
5
|
+
class Node
|
|
6
|
+
|
|
7
|
+
# Mutator for in pattern clauses
|
|
8
|
+
class InPattern < self
|
|
9
|
+
|
|
10
|
+
handle(:in_pattern)
|
|
11
|
+
|
|
12
|
+
children :pattern, :guard, :body
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def dispatch
|
|
17
|
+
emit_pattern_mutations
|
|
18
|
+
emit_guard_mutations if guard
|
|
19
|
+
emit_body_mutations if body
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end # InPattern
|
|
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
|
+
class Literal < self
|
|
7
|
+
# Mutator for complex literals
|
|
8
|
+
class Complex < self
|
|
9
|
+
|
|
10
|
+
ONE = 1i
|
|
11
|
+
|
|
12
|
+
handle(:complex)
|
|
13
|
+
|
|
14
|
+
children :value
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def dispatch
|
|
19
|
+
emit_singletons
|
|
20
|
+
emit_values
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def values
|
|
24
|
+
[0i, ONE, value + ONE, value - ONE]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end # Complex
|
|
28
|
+
end # Literal
|
|
29
|
+
end # Node
|
|
30
|
+
end # Mutator
|
|
31
|
+
end # Mutant
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mutant
|
|
4
|
+
class Mutator
|
|
5
|
+
class Node
|
|
6
|
+
class Literal < self
|
|
7
|
+
# Mutator for rational literals
|
|
8
|
+
class Rational < self
|
|
9
|
+
|
|
10
|
+
ONE = 1r
|
|
11
|
+
|
|
12
|
+
handle(:rational)
|
|
13
|
+
|
|
14
|
+
children :value
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def dispatch
|
|
19
|
+
emit_singletons
|
|
20
|
+
emit_values
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def values
|
|
24
|
+
[0r, ONE, value + ONE, value - ONE]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end # Rational
|
|
28
|
+
end # Literal
|
|
29
|
+
end # Node
|
|
30
|
+
end # Mutator
|
|
31
|
+
end # Mutant
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mutant
|
|
4
|
+
class Mutator
|
|
5
|
+
class Node
|
|
6
|
+
|
|
7
|
+
# Mutator for pattern alternation nodes
|
|
8
|
+
class MatchAlt < self
|
|
9
|
+
|
|
10
|
+
handle(:match_alt)
|
|
11
|
+
|
|
12
|
+
children :left, :right
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def dispatch
|
|
17
|
+
emit(left)
|
|
18
|
+
emit(right)
|
|
19
|
+
emit_left_mutations
|
|
20
|
+
emit_right_mutations
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end # MatchAlt
|
|
24
|
+
end # Node
|
|
25
|
+
end # Mutator
|
|
26
|
+
end # Mutant
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mutant
|
|
4
|
+
class Mutator
|
|
5
|
+
class Node
|
|
6
|
+
|
|
7
|
+
# Mutator for `expr in pattern` predicate nodes
|
|
8
|
+
class MatchPatternP < self
|
|
9
|
+
|
|
10
|
+
handle(:match_pattern_p)
|
|
11
|
+
|
|
12
|
+
children :expression, :pattern
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def dispatch
|
|
17
|
+
emit(N_FALSE)
|
|
18
|
+
emit_expression_mutations
|
|
19
|
+
emit_pattern_mutations
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end # MatchPatternP
|
|
23
|
+
end # Node
|
|
24
|
+
end # Mutator
|
|
25
|
+
end # Mutant
|
|
@@ -11,14 +11,34 @@ module Mutant
|
|
|
11
11
|
|
|
12
12
|
children :left, :operation, :right
|
|
13
13
|
|
|
14
|
+
OPERATOR_SWAPS = {
|
|
15
|
+
:+ => %i[-],
|
|
16
|
+
:- => %i[+],
|
|
17
|
+
:* => %i[/],
|
|
18
|
+
:/ => %i[*],
|
|
19
|
+
:% => %i[/],
|
|
20
|
+
:** => %i[*],
|
|
21
|
+
:& => %i[|],
|
|
22
|
+
:| => %i[& ^],
|
|
23
|
+
:^ => %i[& |],
|
|
24
|
+
:<< => %i[>>],
|
|
25
|
+
:>> => %i[<<]
|
|
26
|
+
}.each_value(&:freeze).freeze
|
|
27
|
+
|
|
14
28
|
private
|
|
15
29
|
|
|
16
30
|
def dispatch
|
|
17
31
|
left_mutations
|
|
18
|
-
|
|
32
|
+
emit_operator_replacements
|
|
19
33
|
emit_right_mutations
|
|
20
34
|
end
|
|
21
35
|
|
|
36
|
+
def emit_operator_replacements
|
|
37
|
+
OPERATOR_SWAPS.fetch(operation).each do |replacement|
|
|
38
|
+
emit_operation(replacement)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
22
42
|
def left_mutations
|
|
23
43
|
emit_left_mutations do |node|
|
|
24
44
|
!n_self?(node)
|
|
@@ -16,10 +16,7 @@ module Mutant
|
|
|
16
16
|
def dispatch
|
|
17
17
|
emit_singletons
|
|
18
18
|
emit_right_mutations
|
|
19
|
-
|
|
20
|
-
emit_left_mutations do |node|
|
|
21
|
-
AST::Types::ASSIGNABLE_VARIABLES.include?(node.type)
|
|
22
|
-
end
|
|
19
|
+
emit(s(:and_asgn, *children))
|
|
23
20
|
end
|
|
24
21
|
|
|
25
22
|
end # OrAsgn
|
|
@@ -7,18 +7,16 @@ module Mutant
|
|
|
7
7
|
# Regular expression options mutation
|
|
8
8
|
class Regopt < self
|
|
9
9
|
|
|
10
|
-
MUTATED_FLAGS = %i[i].freeze
|
|
10
|
+
MUTATED_FLAGS = %i[i m].freeze
|
|
11
11
|
|
|
12
12
|
handle(:regopt)
|
|
13
13
|
|
|
14
14
|
private
|
|
15
15
|
|
|
16
16
|
def dispatch
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def mutated_flags
|
|
21
|
-
(children - MUTATED_FLAGS)
|
|
17
|
+
MUTATED_FLAGS.each do |flag|
|
|
18
|
+
emit_type(*(children - [flag]))
|
|
19
|
+
end
|
|
22
20
|
end
|
|
23
21
|
|
|
24
22
|
end # Regopt
|
|
@@ -20,6 +20,11 @@ module Mutant
|
|
|
20
20
|
mutate_body
|
|
21
21
|
mutate_rescue_bodies
|
|
22
22
|
mutate_else_body
|
|
23
|
+
emit_singletons if standalone?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def standalone?
|
|
27
|
+
parent_type.nil?
|
|
23
28
|
end
|
|
24
29
|
|
|
25
30
|
def mutate_rescue_bodies
|
|
@@ -30,6 +35,28 @@ module Mutant
|
|
|
30
35
|
emit_concat(resbody.body)
|
|
31
36
|
end
|
|
32
37
|
end
|
|
38
|
+
emit_rescue_clause_removals
|
|
39
|
+
emit_handler_promotion
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def emit_handler_promotion
|
|
43
|
+
return unless standalone?
|
|
44
|
+
|
|
45
|
+
children_indices(RESCUE_INDICES).each do |index|
|
|
46
|
+
resbody = AST::Meta::Resbody.new(node: children.fetch(index))
|
|
47
|
+
emit(resbody.body)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def emit_rescue_clause_removals
|
|
52
|
+
rescue_indices = children_indices(RESCUE_INDICES).to_a
|
|
53
|
+
return unless rescue_indices.length > 1
|
|
54
|
+
|
|
55
|
+
rescue_indices.each do |index|
|
|
56
|
+
dup_children = children.dup
|
|
57
|
+
dup_children.delete_at(index)
|
|
58
|
+
emit_type(*dup_children)
|
|
59
|
+
end
|
|
33
60
|
end
|
|
34
61
|
|
|
35
62
|
def emit_concat(child)
|
|
@@ -10,6 +10,13 @@ module Mutant
|
|
|
10
10
|
|
|
11
11
|
children :left, :operator, :right
|
|
12
12
|
|
|
13
|
+
# Pairs of operators where swapping produces equivalent mutants
|
|
14
|
+
# when the right operand is a multiplicative identity (1 or -1).
|
|
15
|
+
MULTIPLICATION_DIVISION_SWAP = {
|
|
16
|
+
%i[* /].freeze => true,
|
|
17
|
+
%i[/ *].freeze => true
|
|
18
|
+
}.freeze
|
|
19
|
+
|
|
13
20
|
private
|
|
14
21
|
|
|
15
22
|
def dispatch
|
|
@@ -21,6 +28,44 @@ module Mutant
|
|
|
21
28
|
emit_not_equality_mutations
|
|
22
29
|
end
|
|
23
30
|
|
|
31
|
+
def emit_selector_replacement
|
|
32
|
+
config
|
|
33
|
+
.operators
|
|
34
|
+
.selector_replacements
|
|
35
|
+
.fetch(operator, EMPTY_ARRAY)
|
|
36
|
+
.each do |replacement|
|
|
37
|
+
emit_selector(replacement) unless equivalent_multiplication_division_swap?(replacement)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Multiplication and division by 1 or -1 are equivalent operations:
|
|
42
|
+
# a * 1 == a / 1 (both equal a)
|
|
43
|
+
# a * -1 == a / -1 (both equal -a)
|
|
44
|
+
#
|
|
45
|
+
# Swapping * <-> / when RIGHT operand is 1 or -1 produces an equivalent
|
|
46
|
+
# mutant that can never be killed, wasting test resources.
|
|
47
|
+
#
|
|
48
|
+
# Note: LEFT operand identity (e.g., 1 * a -> 1 / a) produces different
|
|
49
|
+
# results (a vs 1/a) and should NOT be skipped.
|
|
50
|
+
def equivalent_multiplication_division_swap?(replacement)
|
|
51
|
+
MULTIPLICATION_DIVISION_SWAP.key?([operator, replacement]) && multiplicative_identity?(right)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def multiplicative_identity?(node)
|
|
55
|
+
return false unless %i[int float].include?(node.type)
|
|
56
|
+
|
|
57
|
+
value = Mutant::Util.one(node.children)
|
|
58
|
+
|
|
59
|
+
case node.type
|
|
60
|
+
when :int
|
|
61
|
+
value.equal?(1) || value.equal?(-1)
|
|
62
|
+
when :float
|
|
63
|
+
# rubocop:disable Lint/FloatComparison
|
|
64
|
+
value.equal?(1.0) || value.equal?(-1.0)
|
|
65
|
+
# rubocop:enable Lint/FloatComparison
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
24
69
|
def emit_not_equality_mutations
|
|
25
70
|
return unless operator.equal?(:'!=')
|
|
26
71
|
|
|
@@ -69,15 +69,20 @@ module Mutant
|
|
|
69
69
|
emit_reduce_to_sum_mutation
|
|
70
70
|
emit_start_end_with_mutations
|
|
71
71
|
emit_predicate_mutations
|
|
72
|
-
|
|
72
|
+
emit_type_coercion_mutations
|
|
73
73
|
emit_static_send
|
|
74
74
|
emit_const_get_mutation
|
|
75
|
-
emit_integer_mutation
|
|
76
75
|
emit_dig_mutation
|
|
77
76
|
emit_double_negation_mutation
|
|
78
77
|
emit_lambda_mutation
|
|
79
78
|
end
|
|
80
79
|
|
|
80
|
+
def emit_type_coercion_mutations
|
|
81
|
+
emit_array_mutation
|
|
82
|
+
emit_integer_mutation
|
|
83
|
+
emit_empty_collection_mutation
|
|
84
|
+
end
|
|
85
|
+
|
|
81
86
|
def emit_reduce_to_sum_mutation
|
|
82
87
|
return unless selector.equal?(:reduce)
|
|
83
88
|
|
|
@@ -93,8 +98,6 @@ module Mutant
|
|
|
93
98
|
end
|
|
94
99
|
end
|
|
95
100
|
|
|
96
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
97
|
-
# rubocop:disable Metrics/MethodLength
|
|
98
101
|
def emit_start_end_with_mutations
|
|
99
102
|
return unless REGEXP_MATCH_METHODS.include?(selector) && arguments.one?
|
|
100
103
|
|
|
@@ -102,6 +105,10 @@ module Mutant
|
|
|
102
105
|
|
|
103
106
|
return unless argument.type.equal?(:regexp)
|
|
104
107
|
|
|
108
|
+
emit_regexp_to_prefix_suffix(argument)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def emit_regexp_to_prefix_suffix(argument)
|
|
105
112
|
string = Regexp.regexp_body(argument) or return
|
|
106
113
|
|
|
107
114
|
expressions = ::Regexp::Parser.parse(string)
|
|
@@ -171,6 +178,17 @@ module Mutant
|
|
|
171
178
|
emit(s(:send, nil, :lambda)) if meta.proc?
|
|
172
179
|
end
|
|
173
180
|
|
|
181
|
+
def emit_empty_collection_mutation
|
|
182
|
+
case selector
|
|
183
|
+
when :to_a, :to_ary
|
|
184
|
+
emit(s(:array))
|
|
185
|
+
when :to_h, :to_hash
|
|
186
|
+
emit(s(:hash))
|
|
187
|
+
when :to_s, :to_str
|
|
188
|
+
emit(s(:str, ''))
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
174
192
|
def emit_dig_mutation
|
|
175
193
|
return if !selector.equal?(:dig) || arguments.none?
|
|
176
194
|
|
|
@@ -206,11 +224,9 @@ module Mutant
|
|
|
206
224
|
emit(receiver) if receiver && !left_op_assignment?
|
|
207
225
|
end
|
|
208
226
|
|
|
209
|
-
# rubocop:disable Style/HashEachMethods
|
|
210
|
-
# - its not a hash ;)
|
|
211
227
|
def mutate_arguments
|
|
212
228
|
emit_type(receiver, selector)
|
|
213
|
-
|
|
229
|
+
remaining_children_indices.each do |index|
|
|
214
230
|
mutate_argument_index(index)
|
|
215
231
|
delete_child(index)
|
|
216
232
|
end
|
|
@@ -6,6 +6,7 @@ module Mutant
|
|
|
6
6
|
|
|
7
7
|
# Mutator for super with parentheses
|
|
8
8
|
class Super < self
|
|
9
|
+
include AST::Nodes
|
|
9
10
|
|
|
10
11
|
handle(:super)
|
|
11
12
|
|
|
@@ -13,6 +14,7 @@ module Mutant
|
|
|
13
14
|
|
|
14
15
|
def dispatch
|
|
15
16
|
emit_singletons
|
|
17
|
+
emit(N_ZSUPER) unless children.empty?
|
|
16
18
|
children.each_index do |index|
|
|
17
19
|
mutate_child(index)
|
|
18
20
|
delete_child(index)
|
|
@@ -9,10 +9,27 @@ module Mutant
|
|
|
9
9
|
|
|
10
10
|
handle(:zsuper)
|
|
11
11
|
|
|
12
|
+
include AST::Nodes
|
|
13
|
+
|
|
14
|
+
ARGUMENTS_DESCENDANT = {
|
|
15
|
+
def: AST::Structure.for(:def).descendant(:arguments),
|
|
16
|
+
defs: AST::Structure.for(:defs).descendant(:arguments)
|
|
17
|
+
}.freeze
|
|
18
|
+
|
|
12
19
|
private
|
|
13
20
|
|
|
14
21
|
def dispatch
|
|
15
22
|
emit_singletons
|
|
23
|
+
emit(N_EMPTY_SUPER) if enclosing_method_has_arguments?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def enclosing_method_has_arguments?
|
|
27
|
+
current = parent
|
|
28
|
+
while current
|
|
29
|
+
descendant = ARGUMENTS_DESCENDANT[current.node.type]
|
|
30
|
+
return !descendant.value(current.node).children.empty? if descendant
|
|
31
|
+
current = current.parent
|
|
32
|
+
end
|
|
16
33
|
end
|
|
17
34
|
|
|
18
35
|
end # ZSuper
|
data/lib/mutant/mutator/node.rb
CHANGED
|
@@ -98,21 +98,15 @@ module Mutant
|
|
|
98
98
|
emit(node) unless AST::Types::NOT_STANDALONE.include?(node.type)
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
-
def emit_singletons
|
|
102
|
-
emit_nil
|
|
103
|
-
end
|
|
101
|
+
def emit_singletons = emit_nil
|
|
104
102
|
|
|
105
103
|
def emit_nil
|
|
106
104
|
emit(N_NIL) unless left_op_assignment?
|
|
107
105
|
end
|
|
108
106
|
|
|
109
|
-
def parent_node
|
|
110
|
-
parent&.node
|
|
111
|
-
end
|
|
107
|
+
def parent_node = parent&.node
|
|
112
108
|
|
|
113
|
-
def parent_type
|
|
114
|
-
parent_node&.type
|
|
115
|
-
end
|
|
109
|
+
def parent_type = parent_node&.type
|
|
116
110
|
|
|
117
111
|
def left_op_assignment?
|
|
118
112
|
AST::Types::OP_ASSIGN.include?(parent_type) && parent.node.children.first.equal?(node)
|