mutant 0.11.0 → 0.11.1
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/bootstrap.rb +4 -0
- data/lib/mutant/cli/command/environment.rb +8 -0
- data/lib/mutant/config.rb +50 -18
- data/lib/mutant/matcher/methods.rb +30 -9
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/world.rb +1 -0
- data/lib/mutant.rb +37 -30
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3615060fe7453d95369d3a331f37af2ccaf30227b60ed88fe10cbbd647c1aef1
|
4
|
+
data.tar.gz: 26883c9dd809504502b21f95f93853144fd0c80bf4ce09b4709c9c3eb75742fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e99c8649c373b2f001c9e03440947ab456c1b776c0d8841316a9edcc4105ce22229b5227e72f6b1273f11879ca013b7df909286c42b3f9b8fc186827cda47938
|
7
|
+
data.tar.gz: 3a699d84bacfc16d70912534b2e68a8645d4f737bc977e0495c98b84390328f7912379bf623c0ca6a68df8884faf766e256fb3a983c0b4ea944e23c09ba5ae98
|
data/lib/mutant/bootstrap.rb
CHANGED
@@ -71,6 +71,10 @@ module Mutant
|
|
71
71
|
|
72
72
|
hooks.run(:env_infection_pre, env)
|
73
73
|
|
74
|
+
config.environment_variables.each do |key, value|
|
75
|
+
world.environment_variables[key] = value
|
76
|
+
end
|
77
|
+
|
74
78
|
config.includes.each(&world.load_path.public_method(:<<))
|
75
79
|
config.requires.each(&world.kernel.public_method(:require))
|
76
80
|
|
@@ -62,6 +62,7 @@ module Mutant
|
|
62
62
|
set(matcher: @config.matcher.add(attribute, value))
|
63
63
|
end
|
64
64
|
|
65
|
+
# rubocop:disable Metrics/MethodLength
|
65
66
|
def add_environment_options(parser)
|
66
67
|
parser.separator('Environment:')
|
67
68
|
parser.on('--zombie', 'Run mutant zombified') do
|
@@ -73,7 +74,14 @@ module Mutant
|
|
73
74
|
parser.on('-r', '--require NAME', 'Require file with NAME') do |name|
|
74
75
|
add(:requires, name)
|
75
76
|
end
|
77
|
+
parser.on('--env KEY=VALUE', 'Set environment variable') do |value|
|
78
|
+
match = ENV_VARIABLE_KEY_VALUE_REGEXP.match(value) || fail("Invalid env variable: #{value.inspect}")
|
79
|
+
set(
|
80
|
+
environment_variables: @config.environment_variables.merge(match[:key] => match[:value])
|
81
|
+
)
|
82
|
+
end
|
76
83
|
end
|
84
|
+
# rubocop:enable Metrics/MethodLength
|
77
85
|
|
78
86
|
def add_integration_options(parser)
|
79
87
|
parser.separator('Integration:')
|
data/lib/mutant/config.rb
CHANGED
@@ -5,9 +5,12 @@ module Mutant
|
|
5
5
|
#
|
6
6
|
# Does not reference any "external" volatile state. The configuration applied
|
7
7
|
# to current environment is being represented by the Mutant::Env object.
|
8
|
+
#
|
9
|
+
# rubocop:disable Metrics/ClassLength
|
8
10
|
class Config
|
9
11
|
include Adamantium, Anima.new(
|
10
12
|
:coverage_criteria,
|
13
|
+
:environment_variables,
|
11
14
|
:expression_parser,
|
12
15
|
:fail_fast,
|
13
16
|
:hooks,
|
@@ -48,16 +51,17 @@ module Mutant
|
|
48
51
|
# rubocop:disable Metrics/MethodLength
|
49
52
|
def merge(other)
|
50
53
|
other.with(
|
51
|
-
coverage_criteria:
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
54
|
+
coverage_criteria: coverage_criteria.merge(other.coverage_criteria),
|
55
|
+
environment_variables: environment_variables.merge(other.environment_variables),
|
56
|
+
fail_fast: fail_fast || other.fail_fast,
|
57
|
+
hooks: hooks + other.hooks,
|
58
|
+
includes: includes + other.includes,
|
59
|
+
integration: other.integration || integration,
|
60
|
+
jobs: other.jobs || jobs,
|
61
|
+
matcher: matcher.merge(other.matcher),
|
62
|
+
mutation_timeout: other.mutation_timeout || mutation_timeout,
|
63
|
+
requires: requires + other.requires,
|
64
|
+
zombie: zombie || other.zombie
|
61
65
|
)
|
62
66
|
end
|
63
67
|
# rubocop:enable Metrics/AbcSize
|
@@ -117,6 +121,24 @@ module Mutant
|
|
117
121
|
)
|
118
122
|
)
|
119
123
|
|
124
|
+
# Parse a hash of environment variables
|
125
|
+
#
|
126
|
+
# @param [Hash<Object,Object>]
|
127
|
+
#
|
128
|
+
# @return [Either<String,Hash<String,String>]
|
129
|
+
#
|
130
|
+
def self.parse_environment_variables(hash)
|
131
|
+
invalid = hash.keys.reject { |key| key.instance_of?(String) }
|
132
|
+
return Either::Left.new("Non string keys: #{invalid}") if invalid.any?
|
133
|
+
|
134
|
+
invalid = hash.keys.grep_v(ENV_VARIABLE_KEY_REGEXP)
|
135
|
+
return Either::Left.new("Invalid keys: #{invalid}") if invalid.any?
|
136
|
+
|
137
|
+
invalid = hash.values.reject { |value| value.instance_of?(String) }
|
138
|
+
return Either::Left.new("Non string values: #{invalid}") if invalid.any?
|
139
|
+
|
140
|
+
Either::Right.new(hash)
|
141
|
+
end
|
120
142
|
TRANSFORM = Transform::Sequence.new(
|
121
143
|
[
|
122
144
|
Transform::Exception.new(SystemCallError, :read.to_proc),
|
@@ -124,14 +146,23 @@ module Mutant
|
|
124
146
|
Transform::Hash.new(
|
125
147
|
optional: [
|
126
148
|
Transform::Hash::Key.new('coverage_criteria', ->(value) { CoverageCriteria::TRANSFORM.call(value) }),
|
127
|
-
Transform::Hash::Key.new(
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
149
|
+
Transform::Hash::Key.new(
|
150
|
+
'environment_variables',
|
151
|
+
Transform::Sequence.new(
|
152
|
+
[
|
153
|
+
Transform::Primitive.new(Hash),
|
154
|
+
Transform::Block.capture(:environment_variables, &method(:parse_environment_variables))
|
155
|
+
]
|
156
|
+
)
|
157
|
+
),
|
158
|
+
Transform::Hash::Key.new('fail_fast', Transform::BOOLEAN),
|
159
|
+
Transform::Hash::Key.new('hooks', PATHNAME_ARRAY),
|
160
|
+
Transform::Hash::Key.new('includes', Transform::STRING_ARRAY),
|
161
|
+
Transform::Hash::Key.new('integration', Transform::STRING),
|
162
|
+
Transform::Hash::Key.new('jobs', Transform::INTEGER),
|
163
|
+
Transform::Hash::Key.new('matcher', Matcher::Config::LOADER),
|
164
|
+
Transform::Hash::Key.new('mutation_timeout', Transform::FLOAT),
|
165
|
+
Transform::Hash::Key.new('requires', Transform::STRING_ARRAY)
|
135
166
|
],
|
136
167
|
required: []
|
137
168
|
),
|
@@ -141,4 +172,5 @@ module Mutant
|
|
141
172
|
|
142
173
|
private_constant(:TRANSFORM)
|
143
174
|
end # Config
|
175
|
+
# rubocop:enable Metrics/ClassLength
|
144
176
|
end # Mutant
|
@@ -21,7 +21,7 @@ module Mutant
|
|
21
21
|
# @return [Enumerable<Subject>]
|
22
22
|
def call(env)
|
23
23
|
Chain.new(
|
24
|
-
methods.map { |method| matcher.new(scope, method) }
|
24
|
+
methods(env).map { |method| matcher.new(scope, method) }
|
25
25
|
).call(env)
|
26
26
|
end
|
27
27
|
|
@@ -31,17 +31,16 @@ module Mutant
|
|
31
31
|
self.class::MATCHER
|
32
32
|
end
|
33
33
|
|
34
|
-
def methods
|
34
|
+
def methods(env)
|
35
35
|
candidate_names.each_with_object([]) do |name, methods|
|
36
|
-
method = access(name)
|
37
|
-
methods << method if method
|
36
|
+
method = access(env, name)
|
37
|
+
methods << method if method&.owner.equal?(candidate_scope)
|
38
38
|
end
|
39
39
|
end
|
40
|
-
memoize :methods
|
41
40
|
|
42
41
|
def candidate_names
|
43
42
|
CANDIDATE_NAMES
|
44
|
-
.map
|
43
|
+
.map { |name| candidate_scope.public_send(name, false) }
|
45
44
|
.reduce(:+)
|
46
45
|
.sort
|
47
46
|
end
|
@@ -55,7 +54,7 @@ module Mutant
|
|
55
54
|
|
56
55
|
private
|
57
56
|
|
58
|
-
def access(method_name)
|
57
|
+
def access(_env, method_name)
|
59
58
|
scope.method(method_name)
|
60
59
|
end
|
61
60
|
|
@@ -71,7 +70,7 @@ module Mutant
|
|
71
70
|
|
72
71
|
private
|
73
72
|
|
74
|
-
def access(method_name)
|
73
|
+
def access(_env, method_name)
|
75
74
|
scope.method(method_name)
|
76
75
|
end
|
77
76
|
|
@@ -84,11 +83,33 @@ module Mutant
|
|
84
83
|
class Instance < self
|
85
84
|
MATCHER = Matcher::Method::Instance
|
86
85
|
|
86
|
+
MESSAGE = <<~'MESSAGE'
|
87
|
+
Caught an exception while accessing a method with
|
88
|
+
#instance_method that is part of #{public,privat,protected}_instance_methods.
|
89
|
+
|
90
|
+
This is a bug in your ruby implementation its stdlib, libaries our your code.
|
91
|
+
|
92
|
+
Mutant will ignore this method:
|
93
|
+
|
94
|
+
Object: %<scope>s
|
95
|
+
Method: %<method_name>s
|
96
|
+
Exception: %<exception>s
|
97
|
+
|
98
|
+
See: https://github.com/mbj/mutant/issues/1273
|
99
|
+
MESSAGE
|
100
|
+
|
87
101
|
private
|
88
102
|
|
89
|
-
|
103
|
+
# rubocop:disable Lint/RescueException
|
104
|
+
def access(env, method_name)
|
90
105
|
scope.instance_method(method_name)
|
106
|
+
rescue Exception => exception
|
107
|
+
env.warn(
|
108
|
+
MESSAGE % { scope: scope, method_name: method_name, exception: exception }
|
109
|
+
)
|
110
|
+
nil
|
91
111
|
end
|
112
|
+
# rubocop:enable Lint/RescueException
|
92
113
|
|
93
114
|
def candidate_scope
|
94
115
|
scope
|
data/lib/mutant/version.rb
CHANGED
data/lib/mutant/world.rb
CHANGED
data/lib/mutant.rb
CHANGED
@@ -38,6 +38,11 @@ module Mutant
|
|
38
38
|
EMPTY_ARRAY = [].freeze
|
39
39
|
EMPTY_HASH = {}.freeze
|
40
40
|
SCOPE_OPERATOR = '::'
|
41
|
+
|
42
|
+
env_key = /[a-zA-Z_\d]+/
|
43
|
+
|
44
|
+
ENV_VARIABLE_KEY_VALUE_REGEXP = /\A(?<key>#{env_key}+)=(?<value>.*)\z/.freeze
|
45
|
+
ENV_VARIABLE_KEY_REGEXP = /\A#{env_key}\z/.freeze
|
41
46
|
end # Mutant
|
42
47
|
|
43
48
|
require 'mutant/procto'
|
@@ -234,46 +239,48 @@ require 'mutant/license/subscription/commercial'
|
|
234
239
|
|
235
240
|
module Mutant
|
236
241
|
WORLD = World.new(
|
237
|
-
condition_variable:
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
242
|
+
condition_variable: ConditionVariable,
|
243
|
+
environment_variables: ENV,
|
244
|
+
gem: Gem,
|
245
|
+
gem_method: method(:gem),
|
246
|
+
io: IO,
|
247
|
+
json: JSON,
|
248
|
+
kernel: Kernel,
|
249
|
+
load_path: $LOAD_PATH,
|
250
|
+
marshal: Marshal,
|
251
|
+
mutex: Mutex,
|
252
|
+
object_space: ObjectSpace,
|
253
|
+
open3: Open3,
|
254
|
+
pathname: Pathname,
|
255
|
+
process: Process,
|
256
|
+
stderr: $stderr,
|
257
|
+
stdout: $stdout,
|
258
|
+
thread: Thread,
|
259
|
+
timer: Timer.new(Process)
|
254
260
|
)
|
255
261
|
|
256
262
|
# Reopen class to initialize constant to avoid dep circle
|
257
263
|
class Config
|
258
264
|
DEFAULT = new(
|
259
|
-
coverage_criteria:
|
260
|
-
expression_parser:
|
265
|
+
coverage_criteria: Config::CoverageCriteria::EMPTY,
|
266
|
+
expression_parser: Expression::Parser.new([
|
261
267
|
Expression::Method,
|
262
268
|
Expression::Methods,
|
263
269
|
Expression::Namespace::Exact,
|
264
270
|
Expression::Namespace::Recursive
|
265
271
|
]),
|
266
|
-
fail_fast:
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
272
|
+
fail_fast: false,
|
273
|
+
environment_variables: EMPTY_HASH,
|
274
|
+
hooks: EMPTY_ARRAY,
|
275
|
+
includes: EMPTY_ARRAY,
|
276
|
+
integration: nil,
|
277
|
+
isolation: Mutant::Isolation::Fork.new(WORLD),
|
278
|
+
jobs: nil,
|
279
|
+
matcher: Matcher::Config::DEFAULT,
|
280
|
+
mutation_timeout: nil,
|
281
|
+
reporter: Reporter::CLI.build(WORLD.stdout),
|
282
|
+
requires: EMPTY_ARRAY,
|
283
|
+
zombie: false
|
277
284
|
)
|
278
285
|
end # Config
|
279
286
|
|
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.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Markus Schirp
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: diff-lcs
|