mutant 0.10.29 → 0.10.30

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 287c627187afa2b42509eccfbb1f6c9d6a0122b6fc27bc518746ecc4db406da5
4
- data.tar.gz: a7310138beb4d7b415f627d82cfcb7cd6149f9d9f06272c1b31e15bfa0ec7375
3
+ metadata.gz: 92495691b68b4f556972ce8ec9bc8db2a30acb78980f509b27f2d12d4883b1f6
4
+ data.tar.gz: b7e46f8a0e0544243aab53a9f873bc602ec52e83caa9de3e851e4415b8f15899
5
5
  SHA512:
6
- metadata.gz: 607876e535726ad8c39b1ba7d2b9cd18ca93e6579ee62142a38b31c9f4cd856d49f828398b7bee7e963ae8029b6b3f41772d2e3df6e527dd71bf303009f80f15
7
- data.tar.gz: 7957de07e7554b3921be9311d4ea2fa67a4068c62794466853972157beeb504a20de97856001cf17decc9730b99df509c45a539db81769c42c0ecb31abd9d610
6
+ metadata.gz: e7e49890adfcebdb4158d0e3d4afdc45589c49f1f66a4ecd700a7003dc3bf73acb967d6a51c1ea3c014edc1367ee1f5c79c4ebce8f5ca8c9dfac7f721649ad26
7
+ data.tar.gz: 644ec9e3cb8a5d768b52d709aaf86222fdf588822d139f008aaed3aadd2a9606b0524760e4cbec230d3ba17012374006febd1cbad1eab9839e4166560e3add51
data/bin/mutant CHANGED
@@ -22,7 +22,7 @@ status =
22
22
  kernel: Kernel,
23
23
  pathname: Pathname,
24
24
  require_highjack: Mutant::RequireHighjack
25
- .method(:call)
25
+ .public_method(:call)
26
26
  .to_proc
27
27
  .curry
28
28
  .call(Kernel),
data/lib/mutant.rb CHANGED
@@ -186,6 +186,7 @@ require 'mutant/selector'
186
186
  require 'mutant/selector/expression'
187
187
  require 'mutant/selector/null'
188
188
  require 'mutant/world'
189
+ require 'mutant/hooks'
189
190
  require 'mutant/config'
190
191
  require 'mutant/config/coverage_criteria'
191
192
  require 'mutant/cli'
@@ -257,6 +258,7 @@ module Mutant
257
258
  Expression::Namespace::Recursive
258
259
  ]),
259
260
  fail_fast: false,
261
+ hooks: EMPTY_ARRAY,
260
262
  includes: EMPTY_ARRAY,
261
263
  integration: nil,
262
264
  isolation: Mutant::Isolation::Fork.new(WORLD),
@@ -31,8 +31,7 @@ module Mutant
31
31
  #
32
32
  # rubocop:disable Metrics/MethodLength
33
33
  def self.call(world, config)
34
- env = Env
35
- .empty(world, config)
34
+ env = load_hooks(Env.empty(world, config))
36
35
  .tap(&method(:infect))
37
36
  .with(matchable_scopes: matchable_scopes(world, config))
38
37
 
@@ -49,6 +48,11 @@ module Mutant
49
48
  end
50
49
  # rubocop:enable Metrics/MethodLength
51
50
 
51
+ def self.load_hooks(env)
52
+ env.with(hooks: Hooks.load_config(env.config))
53
+ end
54
+ private_class_method :load_hooks
55
+
52
56
  def self.start_subject(env, subjects)
53
57
  start_expressions = env.config.matcher.start_expressions
54
58
 
@@ -63,10 +67,14 @@ module Mutant
63
67
  private_class_method :start_subject
64
68
 
65
69
  def self.infect(env)
66
- config, world = env.config, env.world
70
+ config, hooks, world = env.config, env.hooks, env.world
71
+
72
+ hooks.run(:env_infection_pre, env)
73
+
74
+ config.includes.each(&world.load_path.public_method(:<<))
75
+ config.requires.each(&world.kernel.public_method(:require))
67
76
 
68
- config.includes.each(&world.load_path.method(:<<))
69
- config.requires.each(&world.kernel.method(:require))
77
+ hooks.run(:env_infection_post, env)
70
78
  end
71
79
  private_class_method :infect
72
80
 
data/lib/mutant/config.rb CHANGED
@@ -10,6 +10,7 @@ module Mutant
10
10
  :coverage_criteria,
11
11
  :expression_parser,
12
12
  :fail_fast,
13
+ :hooks,
13
14
  :includes,
14
15
  :integration,
15
16
  :isolation,
@@ -49,11 +50,12 @@ module Mutant
49
50
  other.with(
50
51
  coverage_criteria: coverage_criteria.merge(other.coverage_criteria),
51
52
  fail_fast: fail_fast || other.fail_fast,
53
+ hooks: hooks + other.hooks,
52
54
  includes: includes + other.includes,
53
- jobs: other.jobs || jobs,
54
55
  integration: other.integration || integration,
55
- mutation_timeout: other.mutation_timeout || mutation_timeout,
56
+ jobs: other.jobs || jobs,
56
57
  matcher: matcher.merge(other.matcher),
58
+ mutation_timeout: other.mutation_timeout || mutation_timeout,
57
59
  requires: requires + other.requires,
58
60
  zombie: zombie || other.zombie
59
61
  )
@@ -106,20 +108,30 @@ module Mutant
106
108
  DEFAULT.with(jobs: Etc.nprocessors)
107
109
  end
108
110
 
111
+ PATHNAME_ARRAY = Transform::Array.new(
112
+ Transform::Sequence.new(
113
+ [
114
+ Transform::STRING,
115
+ Transform::Exception.new(ArgumentError, Pathname.public_method(:new))
116
+ ]
117
+ )
118
+ )
119
+
109
120
  TRANSFORM = Transform::Sequence.new(
110
121
  [
111
122
  Transform::Exception.new(SystemCallError, :read.to_proc),
112
- Transform::Exception.new(YAML::SyntaxError, YAML.method(:safe_load)),
123
+ Transform::Exception.new(YAML::SyntaxError, YAML.public_method(:safe_load)),
113
124
  Transform::Hash.new(
114
125
  optional: [
115
126
  Transform::Hash::Key.new('coverage_criteria', ->(value) { CoverageCriteria::TRANSFORM.call(value) }),
116
127
  Transform::Hash::Key.new('fail_fast', Transform::BOOLEAN),
128
+ Transform::Hash::Key.new('hooks', PATHNAME_ARRAY),
117
129
  Transform::Hash::Key.new('includes', Transform::STRING_ARRAY),
118
130
  Transform::Hash::Key.new('integration', Transform::STRING),
119
131
  Transform::Hash::Key.new('jobs', Transform::INTEGER),
132
+ Transform::Hash::Key.new('matcher', Matcher::Config::LOADER),
120
133
  Transform::Hash::Key.new('mutation_timeout', Transform::FLOAT),
121
- Transform::Hash::Key.new('requires', Transform::STRING_ARRAY),
122
- Transform::Hash::Key.new('matcher', Matcher::Config::LOADER)
134
+ Transform::Hash::Key.new('requires', Transform::STRING_ARRAY)
123
135
  ],
124
136
  required: []
125
137
  ),
data/lib/mutant/env.rb CHANGED
@@ -5,6 +5,7 @@ module Mutant
5
5
  class Env
6
6
  include Adamantium, Anima.new(
7
7
  :config,
8
+ :hooks,
8
9
  :integration,
9
10
  :matchable_scopes,
10
11
  :mutations,
@@ -29,6 +30,7 @@ module Mutant
29
30
  def self.empty(world, config)
30
31
  new(
31
32
  config: config,
33
+ hooks: Hooks.empty,
32
34
  integration: Integration::Null.new(
33
35
  expression_parser: config.expression_parser,
34
36
  world: world
@@ -136,7 +138,9 @@ module Mutant
136
138
 
137
139
  def run_mutation_tests(mutation, tests)
138
140
  config.isolation.call(config.mutation_timeout) do
141
+ hooks.run(:mutation_insert_pre, mutation)
139
142
  result = mutation.insert(world.kernel)
143
+ hooks.run(:mutation_insert_post, mutation)
140
144
 
141
145
  if result.equal?(Loader::Result::VoidValue.instance)
142
146
  Result::Test::VoidValue.instance
@@ -52,7 +52,7 @@ module Mutant
52
52
 
53
53
  def self.from_match(match)
54
54
  names = anima.attribute_names
55
- new(Hash[names.zip(names.map(&match.public_method(:[])))])
55
+ new(names.zip(names.map(&match.public_method(:[]))).to_h)
56
56
  end
57
57
  private_class_method :from_match
58
58
 
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ class Hooks
5
+ include Adamantium, Concord::Public.new(:map)
6
+
7
+ DEFAULTS = %i[
8
+ env_infection_pre
9
+ env_infection_post
10
+ mutation_insert_post
11
+ mutation_insert_pre
12
+ ].product([EMPTY_ARRAY]).to_h.transform_values(&:freeze).freeze
13
+
14
+ MESSAGE = 'Unknown hook %s'
15
+
16
+ private_constant(*constants(false))
17
+
18
+ class UnknownHook < RuntimeError; end
19
+
20
+ def self.assert_name(name)
21
+ fail UnknownHook, MESSAGE % name.inspect unless DEFAULTS.key?(name)
22
+ self
23
+ end
24
+
25
+ def self.empty
26
+ new(DEFAULTS)
27
+ end
28
+
29
+ def merge(other)
30
+ self.class.new(
31
+ other.map.merge(map) { |_key, new, old| (old + new).freeze }.freeze
32
+ )
33
+ end
34
+
35
+ def run(name, payload)
36
+ Hooks.assert_name(name)
37
+
38
+ map.fetch(name).each { |block| block.call(payload) }
39
+
40
+ self
41
+ end
42
+
43
+ class Builder
44
+ def initialize
45
+ @map = DEFAULTS.transform_values(&:dup)
46
+ end
47
+
48
+ def register(name, &block)
49
+ Hooks.assert_name(name)
50
+
51
+ @map.fetch(name) << block
52
+
53
+ self
54
+ end
55
+
56
+ def to_hooks
57
+ Hooks.new(@map.transform_values(&:freeze).freeze)
58
+ end
59
+ end # Builder
60
+
61
+ # rubocop:disable Security/Eval
62
+ def self.load_pathname(pathname)
63
+ hooks = Builder.new
64
+
65
+ binding.eval(pathname.read, pathname.to_s)
66
+
67
+ hooks.to_hooks
68
+ end
69
+ # rubocop:enable Security/Eval
70
+
71
+ def self.load_config(config)
72
+ config.hooks.reduce(empty) do |current, path|
73
+ current.merge(load_pathname(path))
74
+ end
75
+ end
76
+ end # Hooks
77
+ end # Mutant
@@ -25,7 +25,7 @@ module Mutant
25
25
 
26
26
  private_constant(*constants(false))
27
27
 
28
- DEFAULT = new(Hash[anima.attribute_names.map { |name| [name, []] }])
28
+ DEFAULT = new(anima.attribute_names.map { |name| [name, []] }.to_h)
29
29
 
30
30
  expression = Transform::Block.capture(:expression) do |input|
31
31
  Mutant::Config::DEFAULT.expression_parser.call(input)
@@ -41,7 +41,7 @@ module Mutant
41
41
 
42
42
  def candidate_names
43
43
  CANDIDATE_NAMES
44
- .map(&candidate_scope.method(:public_send))
44
+ .map(&candidate_scope.public_method(:public_send))
45
45
  .reduce(:+)
46
46
  .sort
47
47
  end
@@ -29,6 +29,9 @@ module Mutant
29
29
  response = Pipe.from_io(io)
30
30
 
31
31
  pid = process.fork do
32
+ request.reset_binmode
33
+ response.reset_binmode
34
+
32
35
  world.thread.current.name = process_name
33
36
  world.process.setproctitle(process_name)
34
37
 
data/lib/mutant/pipe.rb CHANGED
@@ -35,6 +35,18 @@ module Mutant
35
35
  reader
36
36
  end
37
37
 
38
+ # Set binmode (again)
39
+ #
40
+ # Ruby has a bug where the binmode setting may be lost duringa fork.
41
+ # This API allows to set the binmode again.
42
+ #
43
+ # @return [self]
44
+ def reset_binmode
45
+ reader.binmode
46
+ writer.binmode
47
+ self
48
+ end
49
+
38
50
  class Connection
39
51
  include Anima.new(:marshal, :reader, :writer)
40
52
 
@@ -98,7 +98,7 @@ module Mutant
98
98
  def diff_ranges
99
99
  world
100
100
  .capture_stdout(%W[git diff --unified=0 #{to} -- #{path}])
101
- .fmap(&Ranges.method(:parse))
101
+ .fmap(&Ranges.public_method(:parse))
102
102
  .from_right
103
103
  end
104
104
  memoize :diff_ranges
@@ -332,7 +332,7 @@ module Mutant
332
332
 
333
333
  def transform(input)
334
334
  transform_required(input).bind do |required|
335
- transform_optional(input).fmap(&required.method(:merge))
335
+ transform_optional(input).fmap(&required.public_method(:merge))
336
336
  end
337
337
  end
338
338
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Mutant
4
4
  # Current mutant version
5
- VERSION = '0.10.29'
5
+ VERSION = '0.10.30'
6
6
  end # Mutant
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.10.29
4
+ version: 0.10.30
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-03-08 00:00:00.000000000 Z
11
+ date: 2021-04-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diff-lcs
@@ -195,6 +195,7 @@ files:
195
195
  - lib/mutant/expression/methods.rb
196
196
  - lib/mutant/expression/namespace.rb
197
197
  - lib/mutant/expression/parser.rb
198
+ - lib/mutant/hooks.rb
198
199
  - lib/mutant/integration.rb
199
200
  - lib/mutant/integration/null.rb
200
201
  - lib/mutant/isolation.rb
@@ -363,7 +364,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
363
364
  - !ruby/object:Gem::Version
364
365
  version: '0'
365
366
  requirements: []
366
- rubygems_version: 3.1.4
367
+ rubygems_version: 3.0.3
367
368
  signing_key:
368
369
  specification_version: 4
369
370
  summary: ''