mutant 0.11.16 → 0.11.18
Sign up to get free protection for your applications and to get access to all the features.
- 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
|