mutant 0.11.16 → 0.11.18
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/bin/mutant +55 -46
- data/lib/mutant/bootstrap.rb +66 -35
- data/lib/mutant/cli/command/environment/run.rb +1 -1
- data/lib/mutant/cli/command/environment.rb +2 -2
- data/lib/mutant/cli/command.rb +24 -11
- data/lib/mutant/env.rb +9 -0
- data/lib/mutant/mutator/node/literal/regex.rb +8 -8
- data/lib/mutant/mutator/node/send.rb +27 -17
- data/lib/mutant/mutator/regexp.rb +211 -0
- data/lib/mutant/parallel/driver.rb +1 -0
- data/lib/mutant/parallel/worker.rb +5 -1
- data/lib/mutant/runner.rb +8 -6
- data/lib/mutant/segment/recorder.rb +124 -0
- data/lib/mutant/segment.rb +25 -0
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/world.rb +5 -0
- data/lib/mutant.rb +288 -228
- metadata +12 -33
- data/lib/mutant/ast/regexp/transformer/direct.rb +0 -145
- data/lib/mutant/ast/regexp/transformer/named_group.rb +0 -50
- data/lib/mutant/ast/regexp/transformer/options_group.rb +0 -68
- data/lib/mutant/ast/regexp/transformer/quantifier.rb +0 -92
- data/lib/mutant/ast/regexp/transformer/recursive.rb +0 -56
- data/lib/mutant/ast/regexp/transformer/root.rb +0 -28
- data/lib/mutant/ast/regexp/transformer/text.rb +0 -58
- data/lib/mutant/ast/regexp/transformer.rb +0 -152
- data/lib/mutant/ast/regexp.rb +0 -54
- data/lib/mutant/mutator/node/regexp/alternation_meta.rb +0 -20
- data/lib/mutant/mutator/node/regexp/beginning_of_line_anchor.rb +0 -20
- data/lib/mutant/mutator/node/regexp/capture_group.rb +0 -23
- data/lib/mutant/mutator/node/regexp/character_type.rb +0 -31
- data/lib/mutant/mutator/node/regexp/end_of_line_anchor.rb +0 -20
- data/lib/mutant/mutator/node/regexp/end_of_string_or_before_end_of_line_anchor.rb +0 -20
- data/lib/mutant/mutator/node/regexp/named_group.rb +0 -39
- data/lib/mutant/mutator/node/regexp/zero_or_more.rb +0 -34
- data/lib/mutant/mutator/node/regexp.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 327c0520e73601c447d4081b428de0111e59627961c82a955e86b2015fef7ce7
|
4
|
+
data.tar.gz: 93dd7338a29a416bce5025d8c95a39cb88cc5e96932435797b60102689726e3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 433b57a38e8b5d68f579d26cb5352cabc222137eb875183364dcd1f39ff8852c8a7f2a95b7ee64a3258ed87f9620670160d8d98df6b0a4513e174ff4b8775a6d
|
7
|
+
data.tar.gz: 84b5b86c80958651592da077cfbd5e965587ce02b05c9b27fc9cf86506a80f963351011cf4ebfe23e084ab211a2f9ea2ac6e89f0efcfeb90cc019a28ab42a0b1
|
data/bin/mutant
CHANGED
@@ -1,55 +1,64 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
module Mutant
|
5
|
+
# Record executable timestamp
|
6
|
+
@executable_timestamp = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
7
|
+
|
8
|
+
trap('INT') do |status|
|
9
|
+
effective_status = status ? status + 128 : 128
|
10
|
+
exit! effective_status
|
11
|
+
end
|
8
12
|
|
9
|
-
require 'mutant'
|
13
|
+
require 'mutant'
|
10
14
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
WORLD.record(:cli_parse) do
|
16
|
+
CLI.parse(
|
17
|
+
arguments: ARGV,
|
18
|
+
world: Mutant::WORLD
|
19
|
+
)
|
20
|
+
end.either(
|
21
|
+
->(message) { Mutant::WORLD.stderr.puts(message); Kernel.exit(false) },
|
22
|
+
# rubocop:disable Metrics/BlockLength
|
23
|
+
lambda do |command|
|
19
24
|
if command.zombie?
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
25
|
+
command = WORLD.record(:zombify) do
|
26
|
+
$stderr.puts('Running mutant zombified!')
|
27
|
+
Zombifier.call(
|
28
|
+
namespace: :Zombie,
|
29
|
+
load_path: $LOAD_PATH,
|
30
|
+
kernel: Kernel,
|
31
|
+
pathname: Pathname,
|
32
|
+
require_highjack: RequireHighjack
|
33
|
+
.public_method(:call)
|
34
|
+
.to_proc
|
35
|
+
.curry
|
36
|
+
.call(Kernel),
|
37
|
+
root_require: 'mutant',
|
38
|
+
includes: %w[
|
39
|
+
adamantium
|
40
|
+
anima
|
41
|
+
concord
|
42
|
+
equalizer
|
43
|
+
mprelude
|
44
|
+
mutant
|
45
|
+
unparser
|
46
|
+
variable
|
47
|
+
]
|
48
|
+
)
|
43
49
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
command.call
|
50
|
+
Zombie::Mutant::CLI.parse(
|
51
|
+
arguments: ARGV,
|
52
|
+
world: Mutant::WORLD
|
53
|
+
).from_right
|
54
|
+
end
|
50
55
|
end
|
51
56
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
57
|
+
WORLD.record(:execute) { command.call }.tap do |status|
|
58
|
+
WORLD.recorder.print_profile(WORLD.stderr) if command.print_profile?
|
59
|
+
WORLD.kernel.exit(status)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
# rubocop:enable Metrics/BlockLength
|
63
|
+
)
|
64
|
+
end
|
data/lib/mutant/bootstrap.rb
CHANGED
@@ -7,6 +7,8 @@ module Mutant
|
|
7
7
|
# the impure world to produce an environment.
|
8
8
|
#
|
9
9
|
# env = config interpreted against the world
|
10
|
+
#
|
11
|
+
# rubocop:disable Metrics/ModuleLength
|
10
12
|
module Bootstrap
|
11
13
|
include Adamantium, Anima.new(:config, :parser, :world)
|
12
14
|
|
@@ -30,66 +32,94 @@ module Mutant
|
|
30
32
|
#
|
31
33
|
# rubocop:disable Metrics/MethodLength
|
32
34
|
def self.call(env)
|
33
|
-
env
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
35
|
+
env.record(:bootstrap) do
|
36
|
+
env = load_hooks(env)
|
37
|
+
.tap(&method(:infect))
|
38
|
+
.with(matchable_scopes: matchable_scopes(env))
|
39
|
+
|
40
|
+
matched_subjects = env.record(:subject_match) do
|
41
|
+
Matcher.from_config(env.config.matcher).call(env)
|
42
|
+
end
|
43
|
+
|
44
|
+
selected_subjects = subject_select(env, matched_subjects)
|
45
|
+
|
46
|
+
mutations = env.record(:mutation_generate) do
|
47
|
+
selected_subjects.flat_map(&:mutations)
|
48
|
+
end
|
49
|
+
|
50
|
+
Integration.setup(env).fmap do |integration|
|
51
|
+
env.with(
|
52
|
+
integration: integration,
|
53
|
+
mutations: mutations,
|
54
|
+
selector: Selector::Expression.new(integration),
|
55
|
+
subjects: selected_subjects
|
56
|
+
)
|
57
|
+
end
|
46
58
|
end
|
47
59
|
end
|
48
60
|
# rubocop:enable Metrics/MethodLength
|
49
61
|
|
50
62
|
def self.load_hooks(env)
|
51
|
-
env.
|
63
|
+
env.record(__method__) do
|
64
|
+
env.with(hooks: Hooks.load_config(env.config))
|
65
|
+
end
|
52
66
|
end
|
53
67
|
private_class_method :load_hooks
|
54
68
|
|
55
|
-
def self.
|
56
|
-
|
69
|
+
def self.subject_select(env, subjects)
|
70
|
+
env.record(__method__) do
|
71
|
+
start_expressions = env.config.matcher.start_expressions
|
57
72
|
|
58
|
-
|
73
|
+
return subjects if start_expressions.empty?
|
59
74
|
|
60
|
-
|
61
|
-
|
62
|
-
|
75
|
+
subjects.drop_while do |subject|
|
76
|
+
start_expressions.none? do |expression|
|
77
|
+
expression.prefix?(subject.expression)
|
78
|
+
end
|
63
79
|
end
|
64
80
|
end
|
65
81
|
end
|
66
|
-
private_class_method :
|
82
|
+
private_class_method :subject_select
|
67
83
|
|
84
|
+
# rubocop:disable Metrics/AbcSize
|
85
|
+
# rubocop:disable Metrics/MethodLength
|
68
86
|
def self.infect(env)
|
69
|
-
|
87
|
+
env.record(__method__) do
|
88
|
+
config, hooks, world = env.config, env.hooks, env.world
|
70
89
|
|
71
|
-
|
90
|
+
env.record(:hooks_env_infection_pre) do
|
91
|
+
hooks.run(:env_infection_pre, env)
|
92
|
+
end
|
72
93
|
|
73
|
-
|
74
|
-
|
75
|
-
|
94
|
+
env.record(:require_target) do
|
95
|
+
config.environment_variables.each do |key, value|
|
96
|
+
world.environment_variables[key] = value
|
97
|
+
end
|
76
98
|
|
77
|
-
|
78
|
-
|
99
|
+
config.includes.each(&world.load_path.public_method(:<<))
|
100
|
+
config.requires.each(&world.kernel.public_method(:require))
|
101
|
+
end
|
79
102
|
|
80
|
-
|
103
|
+
env.record(:hooks_env_infection_post) do
|
104
|
+
hooks.run(:env_infection_post, env)
|
105
|
+
end
|
106
|
+
end
|
81
107
|
end
|
82
108
|
private_class_method :infect
|
109
|
+
# rubocop:enable Metrics/AbcSize
|
110
|
+
# rubocop:enable Metrics/MethodLength
|
83
111
|
|
84
112
|
def self.matchable_scopes(env)
|
85
|
-
|
113
|
+
env.record(__method__) do
|
114
|
+
config = env.config
|
86
115
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
116
|
+
scopes = env.world.object_space.each_object(Module).with_object([]) do |scope, aggregate|
|
117
|
+
expression = expression(config.reporter, config.expression_parser, scope) || next
|
118
|
+
aggregate << Scope.new(scope, expression)
|
119
|
+
end
|
91
120
|
|
92
|
-
|
121
|
+
scopes.sort_by { |scope| scope.expression.syntax }
|
122
|
+
end
|
93
123
|
end
|
94
124
|
private_class_method :matchable_scopes
|
95
125
|
|
@@ -132,4 +162,5 @@ module Mutant
|
|
132
162
|
end
|
133
163
|
private_class_method :semantics_warning
|
134
164
|
end # Bootstrap
|
165
|
+
# rubocop:enable Metrics/ModuleLength
|
135
166
|
end # Mutant
|
@@ -27,8 +27,8 @@ module Mutant
|
|
27
27
|
def bootstrap
|
28
28
|
env = Env.empty(world, @config)
|
29
29
|
|
30
|
-
|
31
|
-
.fmap(&method(:expand))
|
30
|
+
env
|
31
|
+
.record(:config) { Config.load_config_file(env).fmap(&method(:expand)) }
|
32
32
|
.bind { Bootstrap.call(env.with(config: @config)) }
|
33
33
|
end
|
34
34
|
|
data/lib/mutant/cli/command.rb
CHANGED
@@ -4,7 +4,13 @@ module Mutant
|
|
4
4
|
module CLI
|
5
5
|
# rubocop:disable Metrics/ClassLength
|
6
6
|
class Command
|
7
|
-
include AbstractType, Anima.new(
|
7
|
+
include AbstractType, Anima.new(
|
8
|
+
:main,
|
9
|
+
:parent,
|
10
|
+
:print_profile,
|
11
|
+
:world,
|
12
|
+
:zombie
|
13
|
+
)
|
8
14
|
|
9
15
|
OPTIONS = [].freeze
|
10
16
|
SUBCOMMANDS = [].freeze
|
@@ -21,12 +27,13 @@ module Mutant
|
|
21
27
|
# @return [Command]
|
22
28
|
#
|
23
29
|
# rubocop:disable Metrics/ParameterLists
|
24
|
-
def self.parse(arguments:, parent: nil, world:, zombie: false)
|
30
|
+
def self.parse(arguments:, parent: nil, print_profile: false, world:, zombie: false)
|
25
31
|
new(
|
26
|
-
main:
|
27
|
-
parent:
|
28
|
-
|
29
|
-
|
32
|
+
main: nil,
|
33
|
+
parent: parent,
|
34
|
+
print_profile: print_profile,
|
35
|
+
world: world,
|
36
|
+
zombie: zombie
|
30
37
|
).__send__(:parse, arguments)
|
31
38
|
end
|
32
39
|
# rubocop:enable Metrics/ParameterLists
|
@@ -59,7 +66,8 @@ module Mutant
|
|
59
66
|
[*parent&.full_name, self.class.command_name].join(' ')
|
60
67
|
end
|
61
68
|
|
62
|
-
alias_method :
|
69
|
+
alias_method :print_profile?, :print_profile
|
70
|
+
alias_method :zombie?, :zombie
|
63
71
|
|
64
72
|
abstract_method :action
|
65
73
|
|
@@ -136,6 +144,10 @@ module Mutant
|
|
136
144
|
capture_main { world.stdout.puts("mutant-#{VERSION}"); true }
|
137
145
|
end
|
138
146
|
|
147
|
+
parser.on('--profile', 'Profile mutant execution') do
|
148
|
+
@print_profile = true
|
149
|
+
end
|
150
|
+
|
139
151
|
parser.on('--zombie', 'Run mutant zombified') do
|
140
152
|
@zombie = true
|
141
153
|
end
|
@@ -176,10 +188,11 @@ module Mutant
|
|
176
188
|
else
|
177
189
|
find_command(command_name).bind do |command|
|
178
190
|
command.parse(
|
179
|
-
arguments:
|
180
|
-
parent:
|
181
|
-
|
182
|
-
|
191
|
+
arguments: arguments,
|
192
|
+
parent: self,
|
193
|
+
print_profile: print_profile,
|
194
|
+
world: world,
|
195
|
+
zombie: zombie
|
183
196
|
)
|
184
197
|
end
|
185
198
|
end
|
data/lib/mutant/env.rb
CHANGED
@@ -134,6 +134,15 @@ module Mutant
|
|
134
134
|
end
|
135
135
|
memoize :test_subject_ratio
|
136
136
|
|
137
|
+
# Record segment
|
138
|
+
#
|
139
|
+
# @param [Symbol] name
|
140
|
+
#
|
141
|
+
# @return [self]
|
142
|
+
def record(name, &block)
|
143
|
+
world.record(name, &block)
|
144
|
+
end
|
145
|
+
|
137
146
|
private
|
138
147
|
|
139
148
|
def run_mutation_tests(mutation, tests)
|
@@ -29,17 +29,17 @@ module Mutant
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def mutate_body
|
32
|
-
|
33
|
-
# Regular expressions with interpolation are skipped.
|
34
|
-
return unless (body_ast = AST::Regexp.expand_regexp_ast(input))
|
32
|
+
string = Regexp.regexp_body(input) or return
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
34
|
+
Regexp
|
35
|
+
.mutate(::Regexp::Parser.parse(string))
|
36
|
+
.each(&method(:emit_regexp_body))
|
40
37
|
end
|
41
38
|
|
42
|
-
|
39
|
+
def emit_regexp_body(expression)
|
40
|
+
emit_type(s(:str, expression.to_str), options)
|
41
|
+
end
|
42
|
+
end # Regexp
|
43
43
|
end # Literal
|
44
44
|
end # Node
|
45
45
|
end # Mutator
|
@@ -50,9 +50,19 @@ module Mutant
|
|
50
50
|
}.freeze
|
51
51
|
}.freeze
|
52
52
|
|
53
|
-
REGEXP_MATCH_METHODS
|
54
|
-
|
55
|
-
|
53
|
+
REGEXP_MATCH_METHODS = %i[=~ match match?].freeze
|
54
|
+
|
55
|
+
REGEXP_START_WITH_NODES =
|
56
|
+
[
|
57
|
+
::Regexp::Expression::Anchor::BeginningOfString,
|
58
|
+
::Regexp::Expression::Literal
|
59
|
+
].freeze
|
60
|
+
|
61
|
+
REGEXP_END_WITH_NODES =
|
62
|
+
[
|
63
|
+
::Regexp::Expression::Literal,
|
64
|
+
::Regexp::Expression::Anchor::EndOfString
|
65
|
+
].freeze
|
56
66
|
|
57
67
|
private
|
58
68
|
|
@@ -114,33 +124,33 @@ module Mutant
|
|
114
124
|
end
|
115
125
|
end
|
116
126
|
|
117
|
-
|
127
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
128
|
+
# rubocop:disable Metrics/MethodLength
|
129
|
+
def emit_start_end_with_mutations
|
118
130
|
return unless REGEXP_MATCH_METHODS.include?(selector) && arguments.one?
|
119
131
|
|
120
132
|
argument = Mutant::Util.one(arguments)
|
121
133
|
|
122
|
-
return unless argument.type.equal?(:regexp)
|
123
|
-
|
124
|
-
)
|
134
|
+
return unless argument.type.equal?(:regexp)
|
135
|
+
|
136
|
+
string = Regexp.regexp_body(argument) or return
|
125
137
|
|
126
|
-
|
138
|
+
expressions = ::Regexp::Parser.parse(string)
|
127
139
|
|
128
|
-
case
|
140
|
+
case expressions.map(&:class)
|
129
141
|
when REGEXP_START_WITH_NODES
|
130
|
-
emit_start_with(
|
142
|
+
emit_start_with(expressions.last.text)
|
131
143
|
when REGEXP_END_WITH_NODES
|
132
|
-
emit_end_with(
|
144
|
+
emit_end_with(expressions.first.text)
|
133
145
|
end
|
134
146
|
end
|
135
147
|
|
136
|
-
def emit_start_with(
|
137
|
-
|
138
|
-
emit_type(receiver, :start_with?, s(:str, literal))
|
148
|
+
def emit_start_with(string)
|
149
|
+
emit_type(receiver, :start_with?, s(:str, string))
|
139
150
|
end
|
140
151
|
|
141
|
-
def emit_end_with(
|
142
|
-
|
143
|
-
emit_type(receiver, :end_with?, s(:str, literal))
|
152
|
+
def emit_end_with(string)
|
153
|
+
emit_type(receiver, :end_with?, s(:str, string))
|
144
154
|
end
|
145
155
|
|
146
156
|
def emit_predicate_mutations
|