mutant 0.11.21 → 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/cli/command/environment.rb +2 -13
- data/lib/mutant/config.rb +16 -12
- data/lib/mutant/matcher/config.rb +5 -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 +1 -0
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant.rb +2 -1
- metadata +3 -2
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
|
@@ -3,7 +3,6 @@
|
|
3
3
|
module Mutant
|
4
4
|
module CLI
|
5
5
|
class Command
|
6
|
-
# rubocop:disable Metrics/ClassLength
|
7
6
|
class Environment < self
|
8
7
|
NAME = 'environment'
|
9
8
|
SHORT_DESCRIPTION = 'Environment subcommands'
|
@@ -29,18 +28,8 @@ module Mutant
|
|
29
28
|
env = Env.empty(world, @config)
|
30
29
|
|
31
30
|
env
|
32
|
-
.record(:config) { Config.
|
33
|
-
.bind { Bootstrap.call(env.with(config:
|
34
|
-
end
|
35
|
-
|
36
|
-
def expand(file_config)
|
37
|
-
if @config.matcher.subjects.any?
|
38
|
-
file_config = file_config.with(
|
39
|
-
matcher: file_config.matcher.with(subjects: [])
|
40
|
-
)
|
41
|
-
end
|
42
|
-
|
43
|
-
@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)) }
|
44
33
|
end
|
45
34
|
|
46
35
|
def parse_remaining_arguments(arguments)
|
data/lib/mutant/config.rb
CHANGED
@@ -93,30 +93,41 @@ module Mutant
|
|
93
93
|
# rubocop:enable Metrics/AbcSize
|
94
94
|
# rubocop:enable Metrics/MethodLength
|
95
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
|
+
|
96
106
|
# Load config file
|
97
107
|
#
|
98
108
|
# @param [Env] env
|
99
109
|
#
|
100
110
|
# @return [Either<String,Config>]
|
101
|
-
def self.load_config_file(
|
111
|
+
def self.load_config_file(reporter:, world:)
|
102
112
|
files = CANDIDATES
|
103
|
-
.map(&
|
113
|
+
.map(&world.pathname.public_method(:new))
|
104
114
|
.select(&:readable?)
|
105
115
|
|
106
116
|
if files.one?
|
107
|
-
load_contents(
|
117
|
+
load_contents(reporter: reporter, path: files.first).fmap(&DEFAULT.public_method(:with))
|
108
118
|
elsif files.empty?
|
109
119
|
Either::Right.new(DEFAULT)
|
110
120
|
else
|
111
121
|
Either::Left.new(MORE_THAN_ONE_CONFIG_FILE % files.join(', '))
|
112
122
|
end
|
113
123
|
end
|
124
|
+
private_class_method :load_config_file
|
114
125
|
|
115
|
-
def self.load_contents(
|
126
|
+
def self.load_contents(reporter:, path:)
|
116
127
|
Transform::Named
|
117
128
|
.new(
|
118
129
|
name: path.to_s,
|
119
|
-
transform: sequence(
|
130
|
+
transform: sequence(reporter)
|
120
131
|
)
|
121
132
|
.call(path)
|
122
133
|
.lmap(&:compact_message)
|
@@ -136,13 +147,6 @@ module Mutant
|
|
136
147
|
end
|
137
148
|
private_class_method :sequence
|
138
149
|
|
139
|
-
# The configuration from the environment
|
140
|
-
#
|
141
|
-
# @return [Config]
|
142
|
-
def self.env
|
143
|
-
DEFAULT.with(jobs: Etc.nprocessors)
|
144
|
-
end
|
145
|
-
|
146
150
|
PATHNAME_ARRAY = Transform::Array.new(
|
147
151
|
transform: Transform::Sequence.new(
|
148
152
|
steps: [
|
@@ -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
|
@@ -20,6 +20,7 @@ module Mutant
|
|
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'
|
@@ -350,7 +351,7 @@ module Mutant
|
|
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
|
@@ -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
|