mutant 0.11.20 → 0.11.22
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/ast/meta/send.rb +1 -2
- data/lib/mutant/cli/command/environment.rb +18 -15
- data/lib/mutant/config.rb +52 -19
- data/lib/mutant/env.rb +3 -0
- data/lib/mutant/integration.rb +35 -6
- data/lib/mutant/isolation/fork.rb +0 -4
- data/lib/mutant/matcher/config.rb +5 -3
- data/lib/mutant/matcher/filter.rb +1 -3
- data/lib/mutant/meta/example/dsl.rb +11 -9
- data/lib/mutant/meta/example/verification.rb +1 -1
- data/lib/mutant/meta/example.rb +2 -1
- data/lib/mutant/meta.rb +7 -2
- data/lib/mutant/mutation/config.rb +20 -4
- data/lib/mutant/mutation/operators.rb +83 -0
- data/lib/mutant/mutator/node/block_pass.rb +6 -3
- data/lib/mutant/mutator/node/send.rb +4 -32
- data/lib/mutant/reporter/cli/printer/config.rb +2 -1
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant.rb +3 -2
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf5fb7800ce81d8fa90585c0dbd00ef9e2f2d21fa1ffbb788cbb59bb9323f677
|
4
|
+
data.tar.gz: 206c2db89f0cfca28b088059a68b0715974dffee6062a8825dd27c703ecbb245
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3923e0d8c6927da2c7f6ca9915fd84c98e032708bb41b9c10c91cca92503226a1d04d09b20970ffad44b06e0ca7ff20215609d385f927ea0880a8b60bafcad93
|
7
|
+
data.tar.gz: 43834f9ec8af1dd4a65b307cf43af4acaffcb839c3c76fe52069dedb2201d80c7a81e313c6e4742fd9d28f81229f36f72bbfb5f4a7c119d13a001705648993b8
|
data/lib/mutant/ast/meta/send.rb
CHANGED
@@ -33,8 +33,7 @@ module Mutant
|
|
33
33
|
#
|
34
34
|
# @return [Boolean]
|
35
35
|
def attribute_assignment?
|
36
|
-
!Types::METHOD_OPERATORS.include?(selector) &&
|
37
|
-
selector.to_s.end_with?(ATTRIBUTE_ASSIGNMENT_SELECTOR_SUFFIX)
|
36
|
+
!Types::METHOD_OPERATORS.include?(selector) && selector.end_with?(ATTRIBUTE_ASSIGNMENT_SELECTOR_SUFFIX)
|
38
37
|
end
|
39
38
|
|
40
39
|
# Test for binary operator implemented as method
|
@@ -28,18 +28,8 @@ module Mutant
|
|
28
28
|
env = Env.empty(world, @config)
|
29
29
|
|
30
30
|
env
|
31
|
-
.record(:config) { Config.
|
32
|
-
.bind { Bootstrap.call(env.with(config:
|
33
|
-
end
|
34
|
-
|
35
|
-
def expand(file_config)
|
36
|
-
if @config.matcher.subjects.any?
|
37
|
-
file_config = file_config.with(
|
38
|
-
matcher: file_config.matcher.with(subjects: [])
|
39
|
-
)
|
40
|
-
end
|
41
|
-
|
42
|
-
@config = Config.env.merge(file_config).merge(@config)
|
31
|
+
.record(:config) { Config.load(cli_config: @config, world: world) }
|
32
|
+
.bind { |config| Bootstrap.call(env.with(config: config)) }
|
43
33
|
end
|
44
34
|
|
45
35
|
def parse_remaining_arguments(arguments)
|
@@ -87,9 +77,22 @@ module Mutant
|
|
87
77
|
def add_integration_options(parser)
|
88
78
|
parser.separator('Integration:')
|
89
79
|
|
90
|
-
parser.on('--use INTEGRATION', '
|
91
|
-
|
92
|
-
|
80
|
+
parser.on('--use INTEGRATION', 'deprecated alias for --integration', &method(:assign_integration_name))
|
81
|
+
parser.on('--integration NAME', 'Use test integration with NAME', &method(:assign_integration_name))
|
82
|
+
|
83
|
+
parser.on(
|
84
|
+
'--integration-argument ARGUMENT', 'Pass ARGUMENT to integration',
|
85
|
+
&method(:add_integration_argument)
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
def add_integration_argument(value)
|
90
|
+
config = @config.integration
|
91
|
+
set(integration: config.with(arguments: config.arguments + [value]))
|
92
|
+
end
|
93
|
+
|
94
|
+
def assign_integration_name(name)
|
95
|
+
set(integration: @config.integration.with(name: name))
|
93
96
|
end
|
94
97
|
|
95
98
|
def add_matcher_options(parser)
|
data/lib/mutant/config.rb
CHANGED
@@ -52,6 +52,20 @@ module Mutant
|
|
52
52
|
```
|
53
53
|
MESSAGE
|
54
54
|
|
55
|
+
INTEGRATION_DEPRECATION = <<~'MESSAGE'
|
56
|
+
Deprecated configuration toplevel string key `integration` found.
|
57
|
+
|
58
|
+
This key will be removed in the next major version.
|
59
|
+
Instead place your integration configuration under the `integration.name` key
|
60
|
+
like this:
|
61
|
+
|
62
|
+
```
|
63
|
+
# mutant.yml
|
64
|
+
integration:
|
65
|
+
name: your_integration # typically rspec or minitest
|
66
|
+
```
|
67
|
+
MESSAGE
|
68
|
+
|
55
69
|
private_constant(*constants(false))
|
56
70
|
|
57
71
|
# Merge with other config
|
@@ -69,7 +83,7 @@ module Mutant
|
|
69
83
|
fail_fast: fail_fast || other.fail_fast,
|
70
84
|
hooks: hooks + other.hooks,
|
71
85
|
includes: includes + other.includes,
|
72
|
-
integration: other.integration
|
86
|
+
integration: integration.merge(other.integration),
|
73
87
|
jobs: other.jobs || jobs,
|
74
88
|
matcher: matcher.merge(other.matcher),
|
75
89
|
mutation: mutation.merge(other.mutation),
|
@@ -79,30 +93,41 @@ module Mutant
|
|
79
93
|
# rubocop:enable Metrics/AbcSize
|
80
94
|
# rubocop:enable Metrics/MethodLength
|
81
95
|
|
96
|
+
# Load the configuration
|
97
|
+
def self.load(cli_config:, world:)
|
98
|
+
load_config_file(reporter: cli_config.reporter, world: world).fmap do |file_config|
|
99
|
+
DEFAULT.with(
|
100
|
+
jobs: Etc.nprocessors,
|
101
|
+
mutation: Mutation::Config::DEFAULT
|
102
|
+
).merge(file_config.merge(cli_config))
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
82
106
|
# Load config file
|
83
107
|
#
|
84
108
|
# @param [Env] env
|
85
109
|
#
|
86
110
|
# @return [Either<String,Config>]
|
87
|
-
def self.load_config_file(
|
111
|
+
def self.load_config_file(reporter:, world:)
|
88
112
|
files = CANDIDATES
|
89
|
-
.map(&
|
113
|
+
.map(&world.pathname.public_method(:new))
|
90
114
|
.select(&:readable?)
|
91
115
|
|
92
116
|
if files.one?
|
93
|
-
load_contents(
|
117
|
+
load_contents(reporter: reporter, path: files.first).fmap(&DEFAULT.public_method(:with))
|
94
118
|
elsif files.empty?
|
95
119
|
Either::Right.new(DEFAULT)
|
96
120
|
else
|
97
121
|
Either::Left.new(MORE_THAN_ONE_CONFIG_FILE % files.join(', '))
|
98
122
|
end
|
99
123
|
end
|
124
|
+
private_class_method :load_config_file
|
100
125
|
|
101
|
-
def self.load_contents(
|
126
|
+
def self.load_contents(reporter:, path:)
|
102
127
|
Transform::Named
|
103
128
|
.new(
|
104
129
|
name: path.to_s,
|
105
|
-
transform: sequence(
|
130
|
+
transform: sequence(reporter)
|
106
131
|
)
|
107
132
|
.call(path)
|
108
133
|
.lmap(&:compact_message)
|
@@ -122,13 +147,6 @@ module Mutant
|
|
122
147
|
end
|
123
148
|
private_class_method :sequence
|
124
149
|
|
125
|
-
# The configuration from the environment
|
126
|
-
#
|
127
|
-
# @return [Config]
|
128
|
-
def self.env
|
129
|
-
DEFAULT.with(jobs: Etc.nprocessors)
|
130
|
-
end
|
131
|
-
|
132
150
|
PATHNAME_ARRAY = Transform::Array.new(
|
133
151
|
transform: Transform::Sequence.new(
|
134
152
|
steps: [
|
@@ -158,14 +176,29 @@ module Mutant
|
|
158
176
|
end
|
159
177
|
|
160
178
|
def self.deprecations(reporter, hash)
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
(hash['mutation'] ||= {})['timeout'] ||= hash.delete('mutation_timeout')
|
165
|
-
end
|
179
|
+
mutation_timeout_deprecation(reporter, hash)
|
180
|
+
integration_deprecation(reporter, hash)
|
166
181
|
|
167
182
|
hash
|
168
183
|
end
|
184
|
+
private_class_method :deprecations
|
185
|
+
|
186
|
+
def self.mutation_timeout_deprecation(reporter, hash)
|
187
|
+
return unless hash.key?('mutation_timeout')
|
188
|
+
reporter.warn(MUTATION_TIMEOUT_DEPRECATION)
|
189
|
+
|
190
|
+
(hash['mutation'] ||= {})['timeout'] ||= hash.delete('mutation_timeout')
|
191
|
+
end
|
192
|
+
private_class_method :mutation_timeout_deprecation
|
193
|
+
|
194
|
+
def self.integration_deprecation(reporter, hash)
|
195
|
+
value = hash['integration']
|
196
|
+
return unless value.instance_of?(String)
|
197
|
+
reporter.warn(INTEGRATION_DEPRECATION)
|
198
|
+
|
199
|
+
hash['integration'] = { 'name' => value }
|
200
|
+
end
|
201
|
+
private_class_method :integration_deprecation
|
169
202
|
|
170
203
|
TRANSFORMS = [
|
171
204
|
Transform::Hash.new(
|
@@ -196,7 +229,7 @@ module Mutant
|
|
196
229
|
value: 'includes'
|
197
230
|
),
|
198
231
|
Transform::Hash::Key.new(
|
199
|
-
transform:
|
232
|
+
transform: ->(value) { Integration::Config::TRANSFORM.call(value) },
|
200
233
|
value: 'integration'
|
201
234
|
),
|
202
235
|
Transform::Hash::Key.new(
|
data/lib/mutant/env.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Mutant
|
4
4
|
# Mutation testing execution environment
|
5
|
+
# rubocop:disable Metrics/ClassLength
|
5
6
|
class Env
|
6
7
|
include Adamantium, Anima.new(
|
7
8
|
:config,
|
@@ -32,6 +33,7 @@ module Mutant
|
|
32
33
|
config: config,
|
33
34
|
hooks: Hooks.empty,
|
34
35
|
integration: Integration::Null.new(
|
36
|
+
arguments: EMPTY_ARRAY,
|
35
37
|
expression_parser: config.expression_parser,
|
36
38
|
world: world
|
37
39
|
),
|
@@ -175,4 +177,5 @@ module Mutant
|
|
175
177
|
end
|
176
178
|
|
177
179
|
end # Env
|
180
|
+
# rubocop:enable Metrics/ClassLength
|
178
181
|
end # Mutant
|
data/lib/mutant/integration.rb
CHANGED
@@ -4,7 +4,7 @@ module Mutant
|
|
4
4
|
|
5
5
|
# Abstract base class mutant test framework integrations
|
6
6
|
class Integration
|
7
|
-
include AbstractType, Adamantium, Anima.new(:expression_parser, :world)
|
7
|
+
include AbstractType, Adamantium, Anima.new(:arguments, :expression_parser, :world)
|
8
8
|
|
9
9
|
LOAD_MESSAGE = <<~'MESSAGE'
|
10
10
|
Unable to load integration mutant-%<integration_name>s:
|
@@ -26,18 +26,47 @@ module Mutant
|
|
26
26
|
|
27
27
|
private_constant(*constants(false))
|
28
28
|
|
29
|
+
class Config
|
30
|
+
include Adamantium, Anima.new(:name, :arguments)
|
31
|
+
|
32
|
+
DEFAULT = new(arguments: EMPTY_ARRAY, name: nil)
|
33
|
+
|
34
|
+
TRANSFORM = Transform::Sequence.new(
|
35
|
+
steps: [
|
36
|
+
Transform::Primitive.new(primitive: Hash),
|
37
|
+
Transform::Hash.new(
|
38
|
+
optional: [
|
39
|
+
Transform::Hash::Key.new(transform: Transform::STRING, value: 'name'),
|
40
|
+
Transform::Hash::Key.new(transform: Transform::STRING_ARRAY, value: 'arguments')
|
41
|
+
],
|
42
|
+
required: []
|
43
|
+
),
|
44
|
+
Transform::Hash::Symbolize.new,
|
45
|
+
Transform::Success.new(block: DEFAULT.method(:with))
|
46
|
+
]
|
47
|
+
)
|
48
|
+
|
49
|
+
def merge(other)
|
50
|
+
self.class.new(
|
51
|
+
name: other.name || name,
|
52
|
+
arguments: arguments + other.arguments
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end # Config
|
56
|
+
|
29
57
|
# Setup integration
|
30
58
|
#
|
31
59
|
# @param env [Bootstrap]
|
32
60
|
#
|
33
61
|
# @return [Either<String, Integration>]
|
34
62
|
def self.setup(env)
|
35
|
-
|
36
|
-
|
37
|
-
|
63
|
+
integration_config = env.config.integration
|
64
|
+
|
65
|
+
return Either::Left.new(INTEGRATION_MISSING) unless integration_config.name
|
38
66
|
|
39
67
|
attempt_require(env).bind { attempt_const_get(env) }.fmap do |klass|
|
40
68
|
klass.new(
|
69
|
+
arguments: integration_config.arguments,
|
41
70
|
expression_parser: env.config.expression_parser,
|
42
71
|
world: env.world
|
43
72
|
).setup
|
@@ -46,7 +75,7 @@ module Mutant
|
|
46
75
|
|
47
76
|
# rubocop:disable Style/MultilineBlockChain
|
48
77
|
def self.attempt_require(env)
|
49
|
-
integration_name = env.config.integration
|
78
|
+
integration_name = env.config.integration.name
|
50
79
|
|
51
80
|
Either.wrap_error(LoadError) do
|
52
81
|
env.world.kernel.require("mutant/integration/#{integration_name}")
|
@@ -61,7 +90,7 @@ module Mutant
|
|
61
90
|
# rubocop:enable Style/MultilineBlockChain
|
62
91
|
|
63
92
|
def self.attempt_const_get(env)
|
64
|
-
integration_name = env.config.integration
|
93
|
+
integration_name = env.config.integration.name
|
65
94
|
constant_name = integration_name.capitalize
|
66
95
|
|
67
96
|
Either.wrap_error(NameError) { const_get(constant_name) }.lmap do |exception|
|
@@ -205,10 +205,6 @@ module Mutant
|
|
205
205
|
_pid, status = world.process.wait2(@pid, Process::WNOHANG)
|
206
206
|
status
|
207
207
|
end
|
208
|
-
|
209
|
-
def add_result(result)
|
210
|
-
@result = defined?(@result) ? @result.add_error(result) : result
|
211
|
-
end
|
212
208
|
end # Parent
|
213
209
|
# rubocop:enable Metrics/ClassLength
|
214
210
|
|
@@ -78,9 +78,11 @@ module Mutant
|
|
78
78
|
#
|
79
79
|
# @return [Config]
|
80
80
|
def merge(other)
|
81
|
-
|
82
|
-
|
83
|
-
|
81
|
+
with(
|
82
|
+
ignore: ignore + other.ignore,
|
83
|
+
start_expressions: start_expressions + other.start_expressions,
|
84
|
+
subjects: other.subjects.any? ? other.subjects : subjects,
|
85
|
+
diffs: diffs + other.diffs
|
84
86
|
)
|
85
87
|
end
|
86
88
|
|
@@ -11,25 +11,26 @@ module Mutant
|
|
11
11
|
#
|
12
12
|
# @param [Thread::Backtrace::Location] location
|
13
13
|
# @param [Set<Symbol>] types
|
14
|
+
# @param [Mutation::Operators] operators
|
14
15
|
#
|
15
16
|
# @return [Example]
|
16
|
-
|
17
|
-
|
17
|
+
#
|
18
|
+
def self.call(location:, types:, operators:, block:) # rubocop:disable Metrics/ParameterLists
|
19
|
+
instance = new(location, types, operators)
|
18
20
|
instance.instance_eval(&block)
|
19
21
|
instance.example
|
20
22
|
end
|
21
|
-
|
22
23
|
private_class_method :new
|
23
24
|
|
24
25
|
# Initialize object
|
25
26
|
#
|
26
27
|
# @return [undefined]
|
27
|
-
def initialize(location, types)
|
28
|
-
@expected
|
29
|
-
@location
|
30
|
-
@lvars
|
31
|
-
@
|
32
|
-
@types
|
28
|
+
def initialize(location, types, operators)
|
29
|
+
@expected = []
|
30
|
+
@location = location
|
31
|
+
@lvars = []
|
32
|
+
@operators = operators
|
33
|
+
@types = types
|
33
34
|
end
|
34
35
|
|
35
36
|
# Example captured by DSL
|
@@ -46,6 +47,7 @@ module Mutant
|
|
46
47
|
location: @location,
|
47
48
|
lvars: @lvars,
|
48
49
|
node: @node,
|
50
|
+
operators: @operators,
|
49
51
|
original_source: @source,
|
50
52
|
types: @types
|
51
53
|
)
|
data/lib/mutant/meta/example.rb
CHANGED
@@ -10,6 +10,7 @@ module Mutant
|
|
10
10
|
:location,
|
11
11
|
:lvars,
|
12
12
|
:node,
|
13
|
+
:operators,
|
13
14
|
:original_source,
|
14
15
|
:types
|
15
16
|
)
|
@@ -56,7 +57,7 @@ module Mutant
|
|
56
57
|
# @return [Enumerable<Mutant::Mutation>]
|
57
58
|
def generated
|
58
59
|
Mutator::Node.mutate(
|
59
|
-
config: Mutation::Config::DEFAULT,
|
60
|
+
config: Mutation::Config::DEFAULT.with(operators: operators),
|
60
61
|
node: node
|
61
62
|
).map do |node|
|
62
63
|
Mutation::Evil.new(subject: self, node: node)
|
data/lib/mutant/meta.rb
CHANGED
@@ -16,8 +16,13 @@ module Mutant
|
|
16
16
|
# Add example
|
17
17
|
#
|
18
18
|
# @return [undefined]
|
19
|
-
def self.add(*types, &block)
|
20
|
-
ALL << DSL.call(
|
19
|
+
def self.add(*types, operators: :full, &block)
|
20
|
+
ALL << DSL.call(
|
21
|
+
block: block,
|
22
|
+
location: caller_locations(1).first,
|
23
|
+
operators: Mutation::Operators.parse(operators.to_s).from_right,
|
24
|
+
types: Set.new(types)
|
25
|
+
)
|
21
26
|
end
|
22
27
|
|
23
28
|
Pathname.glob(Pathname.new(__dir__).parent.parent.join('meta', '*.rb'))
|
@@ -3,11 +3,22 @@
|
|
3
3
|
module Mutant
|
4
4
|
class Mutation
|
5
5
|
class Config
|
6
|
-
include Anima.new(
|
6
|
+
include Anima.new(
|
7
|
+
:ignore_patterns,
|
8
|
+
:operators,
|
9
|
+
:timeout
|
10
|
+
)
|
11
|
+
|
12
|
+
EMPTY = new(
|
13
|
+
ignore_patterns: [],
|
14
|
+
operators: nil,
|
15
|
+
timeout: nil
|
16
|
+
)
|
7
17
|
|
8
18
|
DEFAULT = new(
|
9
|
-
|
10
|
-
|
19
|
+
ignore_patterns: [],
|
20
|
+
operators: Mutation::Operators::Light.new,
|
21
|
+
timeout: nil
|
11
22
|
)
|
12
23
|
|
13
24
|
ignore_pattern = Transform::Block.capture('ignore pattern', &AST::Pattern.method(:parse))
|
@@ -20,6 +31,10 @@ module Mutant
|
|
20
31
|
transform: Transform::Array.new(transform: ignore_pattern),
|
21
32
|
value: 'ignore_patterns'
|
22
33
|
),
|
34
|
+
Transform::Hash::Key.new(
|
35
|
+
transform: Operators::TRANSFORM,
|
36
|
+
value: 'operators'
|
37
|
+
),
|
23
38
|
Transform::Hash::Key.new(
|
24
39
|
transform: Transform::FLOAT,
|
25
40
|
value: 'timeout'
|
@@ -28,13 +43,14 @@ module Mutant
|
|
28
43
|
required: []
|
29
44
|
),
|
30
45
|
Transform::Hash::Symbolize.new,
|
31
|
-
Transform::Success.new(block:
|
46
|
+
Transform::Success.new(block: EMPTY.method(:with))
|
32
47
|
]
|
33
48
|
)
|
34
49
|
|
35
50
|
def merge(other)
|
36
51
|
with(
|
37
52
|
ignore_patterns: other.ignore_patterns,
|
53
|
+
operators: other.operators || operators,
|
38
54
|
timeout: other.timeout || timeout
|
39
55
|
)
|
40
56
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
# Represent a mutated node with its subject
|
5
|
+
class Mutation
|
6
|
+
class Operators
|
7
|
+
include Equalizer.new
|
8
|
+
|
9
|
+
class Full < self
|
10
|
+
NAME = :full
|
11
|
+
|
12
|
+
SELECTOR_REPLACEMENTS = {
|
13
|
+
:< => %i[== eql? equal?],
|
14
|
+
:<= => %i[< == eql? equal?],
|
15
|
+
:== => %i[eql? equal?],
|
16
|
+
:=== => %i[is_a?],
|
17
|
+
:=~ => %i[match?],
|
18
|
+
:> => %i[== eql? equal?],
|
19
|
+
:>= => %i[> == eql? equal?],
|
20
|
+
__send__: %i[public_send],
|
21
|
+
all?: %i[any?],
|
22
|
+
any?: %i[all?],
|
23
|
+
at: %i[fetch key?],
|
24
|
+
fetch: %i[key?],
|
25
|
+
flat_map: %i[map],
|
26
|
+
gsub: %i[sub],
|
27
|
+
is_a?: %i[instance_of?],
|
28
|
+
kind_of?: %i[instance_of?],
|
29
|
+
map: %i[each],
|
30
|
+
match: %i[match?],
|
31
|
+
method: %i[public_method],
|
32
|
+
reverse_each: %i[each],
|
33
|
+
reverse_map: %i[map each],
|
34
|
+
reverse_merge: %i[merge],
|
35
|
+
send: %i[public_send __send__],
|
36
|
+
to_a: %i[to_ary],
|
37
|
+
to_h: %i[to_hash],
|
38
|
+
to_i: %i[to_int],
|
39
|
+
to_s: %i[to_str],
|
40
|
+
values_at: %i[fetch_values]
|
41
|
+
}.freeze.tap { |hash| hash.values(&:freeze) }
|
42
|
+
end
|
43
|
+
|
44
|
+
class Light < self
|
45
|
+
NAME = :light
|
46
|
+
|
47
|
+
SELECTOR_REPLACEMENTS = Full::SELECTOR_REPLACEMENTS
|
48
|
+
.dup
|
49
|
+
.tap do |replacements|
|
50
|
+
replacements.delete(:==)
|
51
|
+
replacements.delete(:eql?)
|
52
|
+
end
|
53
|
+
.freeze
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.operators_name
|
57
|
+
self::NAME
|
58
|
+
end
|
59
|
+
|
60
|
+
def selector_replacements
|
61
|
+
self.class::SELECTOR_REPLACEMENTS
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.parse(value)
|
65
|
+
klass = [Light, Full].detect { |candidate| candidate.operators_name.to_s.eql?(value) }
|
66
|
+
|
67
|
+
if klass
|
68
|
+
Either::Right.new(klass.new)
|
69
|
+
else
|
70
|
+
Either::Left.new("Unknown operators: #{value}")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
TRANSFORM =
|
75
|
+
Transform::Sequence.new(
|
76
|
+
steps: [
|
77
|
+
Transform::STRING,
|
78
|
+
Transform::Block.capture('parse operator', &method(:parse))
|
79
|
+
]
|
80
|
+
)
|
81
|
+
end # Operators
|
82
|
+
end # Mutation
|
83
|
+
end # Mutant
|
@@ -20,9 +20,12 @@ module Mutant
|
|
20
20
|
def emit_symbol_to_proc_mutations
|
21
21
|
return unless n_sym?(argument)
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
config
|
24
|
+
.operators
|
25
|
+
.selector_replacements
|
26
|
+
.fetch(*argument, EMPTY_ARRAY).each do |method|
|
27
|
+
emit_argument(s(:sym, method))
|
28
|
+
end
|
26
29
|
end
|
27
30
|
end # Block
|
28
31
|
end # Node
|
@@ -13,37 +13,6 @@ module Mutant
|
|
13
13
|
|
14
14
|
children :receiver, :selector
|
15
15
|
|
16
|
-
SELECTOR_REPLACEMENTS = {
|
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
|
-
fetch: %i[key?],
|
29
|
-
flat_map: %i[map],
|
30
|
-
gsub: %i[sub],
|
31
|
-
is_a?: %i[instance_of?],
|
32
|
-
kind_of?: %i[instance_of?],
|
33
|
-
map: %i[each],
|
34
|
-
match: %i[match?],
|
35
|
-
method: %i[public_method],
|
36
|
-
reverse_each: %i[each],
|
37
|
-
reverse_map: %i[map each],
|
38
|
-
reverse_merge: %i[merge],
|
39
|
-
send: %i[public_send __send__],
|
40
|
-
to_a: %i[to_ary],
|
41
|
-
to_h: %i[to_hash],
|
42
|
-
to_i: %i[to_int],
|
43
|
-
to_s: %i[to_str],
|
44
|
-
values_at: %i[fetch_values]
|
45
|
-
}.freeze.tap { |hash| hash.values(&:freeze) }
|
46
|
-
|
47
16
|
RECEIVER_SELECTOR_REPLACEMENTS = {
|
48
17
|
Date: {
|
49
18
|
parse: %i[jd civil strptime iso8601 rfc3339 xmlschema rfc2822 rfc822 httpdate jisx0301]
|
@@ -227,7 +196,10 @@ module Mutant
|
|
227
196
|
end
|
228
197
|
|
229
198
|
def emit_selector_replacement
|
230
|
-
|
199
|
+
config
|
200
|
+
.operators
|
201
|
+
.selector_replacements
|
202
|
+
.fetch(selector, EMPTY_ARRAY).each(&public_method(:emit_selector))
|
231
203
|
end
|
232
204
|
|
233
205
|
def emit_naked_receiver
|
@@ -16,10 +16,11 @@ module Mutant
|
|
16
16
|
# rubocop:disable Metrics/AbcSize
|
17
17
|
def run
|
18
18
|
info 'Matcher: %s', object.matcher.inspect
|
19
|
-
info 'Integration: %s', object.integration || 'null'
|
19
|
+
info 'Integration: %s', object.integration.name || 'null'
|
20
20
|
info 'Jobs: %s', object.jobs || 'auto'
|
21
21
|
info 'Includes: %s', object.includes
|
22
22
|
info 'Requires: %s', object.requires
|
23
|
+
info 'Operators: %s', object.mutation.operators.class.operators_name
|
23
24
|
info 'MutationTimeout: %0.9g', object.mutation.timeout if object.mutation.timeout
|
24
25
|
end
|
25
26
|
# rubocop:enable Metrics/AbcSize
|
data/lib/mutant/version.rb
CHANGED
data/lib/mutant.rb
CHANGED
@@ -107,6 +107,7 @@ module Mutant
|
|
107
107
|
require 'mutant/parallel/worker'
|
108
108
|
require 'mutant/require_highjack'
|
109
109
|
require 'mutant/mutation'
|
110
|
+
require 'mutant/mutation/operators'
|
110
111
|
require 'mutant/mutation/config'
|
111
112
|
require 'mutant/mutator'
|
112
113
|
require 'mutant/mutator/util'
|
@@ -346,11 +347,11 @@ module Mutant
|
|
346
347
|
fail_fast: false,
|
347
348
|
hooks: EMPTY_ARRAY,
|
348
349
|
includes: EMPTY_ARRAY,
|
349
|
-
integration:
|
350
|
+
integration: Integration::Config::DEFAULT,
|
350
351
|
isolation: Mutant::Isolation::Fork.new(world: WORLD),
|
351
352
|
jobs: nil,
|
352
353
|
matcher: Matcher::Config::DEFAULT,
|
353
|
-
mutation: Mutation::Config::
|
354
|
+
mutation: Mutation::Config::EMPTY,
|
354
355
|
reporter: Reporter::CLI.build(WORLD.stdout),
|
355
356
|
requires: EMPTY_ARRAY
|
356
357
|
)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mutant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.11.
|
4
|
+
version: 0.11.22
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Markus Schirp
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: diff-lcs
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.6.
|
75
|
+
version: 0.6.8
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.6.
|
82
|
+
version: 0.6.8
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: parallel
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -235,6 +235,7 @@ files:
|
|
235
235
|
- lib/mutant/meta/example/verification.rb
|
236
236
|
- lib/mutant/mutation.rb
|
237
237
|
- lib/mutant/mutation/config.rb
|
238
|
+
- lib/mutant/mutation/operators.rb
|
238
239
|
- lib/mutant/mutation/runner.rb
|
239
240
|
- lib/mutant/mutation/runner/sink.rb
|
240
241
|
- lib/mutant/mutator.rb
|