mutant 0.11.0 → 0.11.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dfa3f4386dd2bb68e31b67124aac333cf71d13bb2fec0cf46fea56360475f3f4
4
- data.tar.gz: c1c3284ed18f7e2b268c8536a9103d11b7eb8abb0f3fc73a1890ffc8d461e9e5
3
+ metadata.gz: 3615060fe7453d95369d3a331f37af2ccaf30227b60ed88fe10cbbd647c1aef1
4
+ data.tar.gz: 26883c9dd809504502b21f95f93853144fd0c80bf4ce09b4709c9c3eb75742fe
5
5
  SHA512:
6
- metadata.gz: a20b1be07d23f2a0939bab498c500cf1707f75c647af84409d5ec42437bdd26aa6d4f89c9f70e558408e1280812da0cbc7cf7758af1c80c499289e3554b0b92d
7
- data.tar.gz: 276e169499aa4b48bc2488178de01b3d3ce1cb4647796bef023491c5d21faaed2d88e1ee61ae012c09d5c86f68ac68cda4a705174cc3cb88dab69e7cc4ea1578
6
+ metadata.gz: e99c8649c373b2f001c9e03440947ab456c1b776c0d8841316a9edcc4105ce22229b5227e72f6b1273f11879ca013b7df909286c42b3f9b8fc186827cda47938
7
+ data.tar.gz: 3a699d84bacfc16d70912534b2e68a8645d4f737bc977e0495c98b84390328f7912379bf623c0ca6a68df8884faf766e256fb3a983c0b4ea944e23c09ba5ae98
@@ -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: coverage_criteria.merge(other.coverage_criteria),
52
- fail_fast: fail_fast || other.fail_fast,
53
- hooks: hooks + other.hooks,
54
- includes: includes + other.includes,
55
- integration: other.integration || integration,
56
- jobs: other.jobs || jobs,
57
- matcher: matcher.merge(other.matcher),
58
- mutation_timeout: other.mutation_timeout || mutation_timeout,
59
- requires: requires + other.requires,
60
- zombie: zombie || other.zombie
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('fail_fast', Transform::BOOLEAN),
128
- Transform::Hash::Key.new('hooks', PATHNAME_ARRAY),
129
- Transform::Hash::Key.new('includes', Transform::STRING_ARRAY),
130
- Transform::Hash::Key.new('integration', Transform::STRING),
131
- Transform::Hash::Key.new('jobs', Transform::INTEGER),
132
- Transform::Hash::Key.new('matcher', Matcher::Config::LOADER),
133
- Transform::Hash::Key.new('mutation_timeout', Transform::FLOAT),
134
- Transform::Hash::Key.new('requires', Transform::STRING_ARRAY)
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.owner.equal?(candidate_scope)
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(&candidate_scope.public_method(:public_send))
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
- def access(method_name)
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
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Mutant
4
4
  # Current mutant version
5
- VERSION = '0.11.0'
5
+ VERSION = '0.11.1'
6
6
  end # Mutant
data/lib/mutant/world.rb CHANGED
@@ -5,6 +5,7 @@ module Mutant
5
5
  class World
6
6
  include Adamantium, Anima.new(
7
7
  :condition_variable,
8
+ :environment_variables,
8
9
  :gem,
9
10
  :gem_method,
10
11
  :io,
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: ConditionVariable,
238
- gem: Gem,
239
- gem_method: method(:gem),
240
- io: IO,
241
- json: JSON,
242
- kernel: Kernel,
243
- load_path: $LOAD_PATH,
244
- marshal: Marshal,
245
- mutex: Mutex,
246
- object_space: ObjectSpace,
247
- open3: Open3,
248
- pathname: Pathname,
249
- process: Process,
250
- stderr: $stderr,
251
- stdout: $stdout,
252
- thread: Thread,
253
- timer: Timer.new(Process)
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: Config::CoverageCriteria::EMPTY,
260
- expression_parser: Expression::Parser.new([
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: false,
267
- hooks: EMPTY_ARRAY,
268
- includes: EMPTY_ARRAY,
269
- integration: nil,
270
- isolation: Mutant::Isolation::Fork.new(WORLD),
271
- jobs: nil,
272
- matcher: Matcher::Config::DEFAULT,
273
- mutation_timeout: nil,
274
- reporter: Reporter::CLI.build(WORLD.stdout),
275
- requires: EMPTY_ARRAY,
276
- zombie: false
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.0
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-10-18 00:00:00.000000000 Z
11
+ date: 2021-11-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diff-lcs