mutant 0.10.21 → 0.10.26
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 +32 -13
- data/lib/mutant/ast/find_metaclass_containing.rb +1 -1
- data/lib/mutant/ast/regexp.rb +54 -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 +92 -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 -2
- data/lib/mutant/bootstrap.rb +1 -1
- data/lib/mutant/cli/command.rb +4 -0
- data/lib/mutant/cli/command/environment.rb +9 -3
- 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 +9 -55
- data/lib/mutant/config/coverage_criteria.rb +61 -0
- data/lib/mutant/context.rb +1 -1
- data/lib/mutant/env.rb +2 -2
- data/lib/mutant/expression.rb +0 -12
- data/lib/mutant/expression/method.rb +4 -4
- data/lib/mutant/expression/methods.rb +5 -4
- data/lib/mutant/expression/namespace.rb +1 -1
- data/lib/mutant/integration.rb +8 -2
- data/lib/mutant/isolation/fork.rb +4 -11
- data/lib/mutant/loader.rb +1 -1
- data/lib/mutant/matcher.rb +3 -3
- data/lib/mutant/matcher/config.rb +30 -8
- data/lib/mutant/matcher/method.rb +10 -9
- data/lib/mutant/matcher/method/instance.rb +6 -2
- data/lib/mutant/matcher/method/metaclass.rb +1 -1
- data/lib/mutant/matcher/methods.rb +2 -4
- data/lib/mutant/meta/example.rb +1 -1
- data/lib/mutant/meta/example/dsl.rb +6 -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/argument.rb +2 -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 +12 -0
- data/lib/mutant/mutator/node/named_value/variable_assignment.rb +3 -3
- 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/named_group.rb +39 -0
- data/lib/mutant/mutator/node/regexp/zero_or_more.rb +34 -0
- data/lib/mutant/mutator/node/regopt.rb +1 -1
- data/lib/mutant/mutator/node/sclass.rb +1 -1
- data/lib/mutant/mutator/node/send.rb +55 -6
- data/lib/mutant/parallel.rb +2 -2
- data/lib/mutant/parallel/driver.rb +1 -1
- data/lib/mutant/parallel/worker.rb +1 -1
- data/lib/mutant/parser.rb +1 -1
- data/lib/mutant/pipe.rb +1 -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 +3 -3
- data/lib/mutant/reporter/cli/printer/env_progress.rb +2 -2
- data/lib/mutant/selector.rb +1 -1
- data/lib/mutant/subject.rb +2 -4
- data/lib/mutant/subject/method/instance.rb +6 -45
- data/lib/mutant/timer.rb +2 -2
- data/lib/mutant/transform.rb +25 -0
- data/lib/mutant/variable.rb +322 -0
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/world.rb +2 -3
- metadata +39 -151
- data/lib/mutant/warnings.rb +0 -106
@@ -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 = 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,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
|
@@ -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 = {
|
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
|
+
}.freeze
|
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
|
@@ -13,10 +13,12 @@ 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?],
|
20
|
+
:=== => %i[is_a?],
|
21
|
+
:=~ => %i[match?],
|
20
22
|
:> => %i[== eql? equal?],
|
21
23
|
:>= => %i[> == eql? equal?],
|
22
24
|
__send__: %i[public_send],
|
@@ -30,6 +32,7 @@ module Mutant
|
|
30
32
|
is_a?: %i[instance_of?],
|
31
33
|
kind_of?: %i[instance_of?],
|
32
34
|
map: %i[each],
|
35
|
+
match: %i[match?],
|
33
36
|
method: %i[public_method],
|
34
37
|
reverse_each: %i[each],
|
35
38
|
reverse_map: %i[map each],
|
@@ -40,13 +43,17 @@ module Mutant
|
|
40
43
|
to_i: %i[to_int],
|
41
44
|
to_s: %i[to_str],
|
42
45
|
values_at: %i[fetch_values]
|
43
|
-
)
|
46
|
+
}.freeze.tap { |hash| hash.values(&:freeze) }
|
44
47
|
|
45
|
-
RECEIVER_SELECTOR_REPLACEMENTS =
|
48
|
+
RECEIVER_SELECTOR_REPLACEMENTS = {
|
46
49
|
Date: {
|
47
50
|
parse: %i[jd civil strptime iso8601 rfc3339 xmlschema rfc2822 rfc822 httpdate jisx0301]
|
48
|
-
}
|
49
|
-
|
51
|
+
}.freeze
|
52
|
+
}.freeze
|
53
|
+
|
54
|
+
REGEXP_MATCH_METHODS = %i[=~ match match?].freeze
|
55
|
+
REGEXP_START_WITH_NODES = %i[regexp_bos_anchor regexp_literal_literal].freeze
|
56
|
+
REGEXP_END_WITH_NODES = %i[regexp_literal_literal regexp_eos_anchor].freeze
|
50
57
|
|
51
58
|
private
|
52
59
|
|
@@ -81,6 +88,8 @@ module Mutant
|
|
81
88
|
end
|
82
89
|
|
83
90
|
def emit_selector_specific_mutations
|
91
|
+
emit_start_end_with_mutations
|
92
|
+
emit_predicate_mutations
|
84
93
|
emit_array_mutation
|
85
94
|
emit_static_send
|
86
95
|
emit_const_get_mutation
|
@@ -90,6 +99,42 @@ module Mutant
|
|
90
99
|
emit_lambda_mutation
|
91
100
|
end
|
92
101
|
|
102
|
+
def emit_start_end_with_mutations # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
103
|
+
return unless REGEXP_MATCH_METHODS.include?(selector) && arguments.one?
|
104
|
+
|
105
|
+
argument = Mutant::Util.one(arguments)
|
106
|
+
|
107
|
+
return unless argument.type.equal?(:regexp) && (
|
108
|
+
regexp_ast = AST::Regexp.expand_regexp_ast(argument)
|
109
|
+
)
|
110
|
+
|
111
|
+
regexp_children = regexp_ast.children
|
112
|
+
|
113
|
+
case regexp_children.map(&:type)
|
114
|
+
when REGEXP_START_WITH_NODES
|
115
|
+
emit_start_with(regexp_children)
|
116
|
+
when REGEXP_END_WITH_NODES
|
117
|
+
emit_end_with(regexp_children)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def emit_start_with(regexp_nodes)
|
122
|
+
literal = Mutant::Util.one(regexp_nodes.last.children)
|
123
|
+
emit_type(receiver, :start_with?, s(:str, literal))
|
124
|
+
end
|
125
|
+
|
126
|
+
def emit_end_with(regexp_nodes)
|
127
|
+
literal = Mutant::Util.one(regexp_nodes.first.children)
|
128
|
+
emit_type(receiver, :end_with?, s(:str, literal))
|
129
|
+
end
|
130
|
+
|
131
|
+
def emit_predicate_mutations
|
132
|
+
return unless selector.match?(/\?\z/) && !selector.equal?(:defined?)
|
133
|
+
|
134
|
+
emit(s(:true))
|
135
|
+
emit(s(:false))
|
136
|
+
end
|
137
|
+
|
93
138
|
def emit_array_mutation
|
94
139
|
return unless selector.equal?(:Array) && possible_kernel_method?
|
95
140
|
|
@@ -177,7 +222,11 @@ module Mutant
|
|
177
222
|
end
|
178
223
|
|
179
224
|
def emit_argument_propagation
|
180
|
-
|
225
|
+
return unless arguments.one?
|
226
|
+
|
227
|
+
argument = Mutant::Util.one(arguments)
|
228
|
+
|
229
|
+
emit_propagation(argument) unless n_kwargs?(argument)
|
181
230
|
end
|
182
231
|
|
183
232
|
def mutate_receiver
|
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
|
data/lib/mutant/parser.rb
CHANGED
data/lib/mutant/pipe.rb
CHANGED
@@ -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
|
#
|
@@ -33,7 +33,7 @@ module Mutant
|
|
33
33
|
__send__(report, format, __send__(value))
|
34
34
|
end
|
35
35
|
end
|
36
|
-
end #
|
36
|
+
end # Env
|
37
37
|
end # Printer
|
38
38
|
end # CLI
|
39
39
|
end # Reporter
|
@@ -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
|
#
|