mutant 0.11.19 → 0.11.21

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: 4809cb65459d6270021f50359d6b799b420fdd142a32524a2ebd772485c7a70b
4
- data.tar.gz: b8284c5b2d2bd165b7ef78662910cb314a6a5b5cd619b5e411290a10a058ee57
3
+ metadata.gz: 47d87c7b5cf12da4906d170d892be1d5121b4f65b86b39365db2d6424f1487c6
4
+ data.tar.gz: e8d9007fc9633eea5343b83282d331fab5e59aaf6f880aa9b52cb525b2f6db81
5
5
  SHA512:
6
- metadata.gz: c68d2e51e917a1b347a036c1619ad3a54fefee4e6dec6710f1d6b488c707437bb58597822dcc2108f55c5e6caa4e3226e52a2eeaa7012d39685203e161ec235b
7
- data.tar.gz: 7894b0df9ce7e54757f04206f501e7e5a676a93694b53af09a2cb667a6fb0a02737747faddefeae0ff5808b629ef1b80b9a59bd0b1fee6fa13ef1984c72fad87
6
+ metadata.gz: 04e36404f3a210cfa4a7045e244a72509e69332dbf736e81f5069a7d632680931747b761fd5f61e2ba915e94a0f9545506c3db2369a44f62a21b7a0254264db6
7
+ data.tar.gz: 2e0db7e6d2ada9525ec054a197f8004e223a31c093ab7b603704044f92f8e1f4305048851b8a583172f3904a028a25a304a605e3e84d5a5e708eccabb6842e9c
@@ -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
@@ -88,7 +88,7 @@ module Mutant
88
88
  config, hooks, world = env.config, env.hooks, env.world
89
89
 
90
90
  env.record(:hooks_env_infection_pre) do
91
- hooks.run(:env_infection_pre, env)
91
+ hooks.run(:env_infection_pre, env: env)
92
92
  end
93
93
 
94
94
  env.record(:require_target) do
@@ -101,7 +101,7 @@ module Mutant
101
101
  end
102
102
 
103
103
  env.record(:hooks_env_infection_post) do
104
- hooks.run(:env_infection_post, env)
104
+ hooks.run(:env_infection_post, env: env)
105
105
  end
106
106
  end
107
107
  end
@@ -18,7 +18,8 @@ module Mutant
18
18
 
19
19
  NO_TESTS_MESSAGE = <<~'MESSAGE'
20
20
  ===============
21
- Mutant found no tests. Mutation testing cannot be started.
21
+ Mutant found no tests available for mutation testing.
22
+ Mutation testing cannot be started.
22
23
 
23
24
  This can have various reasons:
24
25
 
@@ -41,7 +42,7 @@ module Mutant
41
42
  end
42
43
 
43
44
  def validate_tests(environment)
44
- if environment.integration.all_tests.empty?
45
+ if environment.integration.available_tests.empty?
45
46
  Either::Left.new(NO_TESTS_MESSAGE)
46
47
  else
47
48
  Either::Right.new(environment)
@@ -21,7 +21,7 @@ module Mutant
21
21
 
22
22
  def list_tests(env)
23
23
  tests = env.integration.all_tests
24
- print('Tests in environment: %d' % tests.length)
24
+ print('All tests in environment: %d' % tests.length)
25
25
  tests.each do |test|
26
26
  print(test.identification)
27
27
  end
@@ -3,6 +3,7 @@
3
3
  module Mutant
4
4
  module CLI
5
5
  class Command
6
+ # rubocop:disable Metrics/ClassLength
6
7
  class Environment < self
7
8
  NAME = 'environment'
8
9
  SHORT_DESCRIPTION = 'Environment subcommands'
@@ -87,12 +88,24 @@ module Mutant
87
88
  def add_integration_options(parser)
88
89
  parser.separator('Integration:')
89
90
 
90
- parser.on('--use INTEGRATION', 'Use INTEGRATION to kill mutations') do |name|
91
- set(integration: name)
92
- end
91
+ parser.on('--use INTEGRATION', 'deprecated alias for --integration', &method(:assign_integration_name))
92
+ parser.on('--integration NAME', 'Use test integration with NAME', &method(:assign_integration_name))
93
+
94
+ parser.on(
95
+ '--integration-argument ARGUMENT', 'Pass ARGUMENT to integration',
96
+ &method(:add_integration_argument)
97
+ )
98
+ end
99
+
100
+ def add_integration_argument(value)
101
+ config = @config.integration
102
+ set(integration: config.with(arguments: config.arguments + [value]))
103
+ end
104
+
105
+ def assign_integration_name(name)
106
+ set(integration: @config.integration.with(name: name))
93
107
  end
94
108
 
95
- # rubocop:disable Metrics/MethodLength
96
109
  def add_matcher_options(parser)
97
110
  parser.separator('Matcher:')
98
111
 
@@ -103,10 +116,7 @@ module Mutant
103
116
  add_matcher(:start_expressions, @config.expression_parser.call(pattern).from_right)
104
117
  end
105
118
  parser.on('--since REVISION', 'Only select subjects touched since REVISION') do |revision|
106
- add_matcher(
107
- :subject_filters,
108
- Repository::SubjectFilter.new(diff: Repository::Diff.new(to: revision, world: world))
109
- )
119
+ add_matcher(:diffs, Repository::Diff.new(to: revision, world: world))
110
120
  end
111
121
  end
112
122
 
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 || 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),
@@ -158,14 +172,29 @@ module Mutant
158
172
  end
159
173
 
160
174
  def self.deprecations(reporter, hash)
161
- if hash.key?('mutation_timeout')
162
- reporter.warn(MUTATION_TIMEOUT_DEPRECATION)
163
-
164
- (hash['mutation'] ||= {})['timeout'] ||= hash.delete('mutation_timeout')
165
- end
175
+ mutation_timeout_deprecation(reporter, hash)
176
+ integration_deprecation(reporter, hash)
166
177
 
167
178
  hash
168
179
  end
180
+ private_class_method :deprecations
181
+
182
+ def self.mutation_timeout_deprecation(reporter, hash)
183
+ return unless hash.key?('mutation_timeout')
184
+ reporter.warn(MUTATION_TIMEOUT_DEPRECATION)
185
+
186
+ (hash['mutation'] ||= {})['timeout'] ||= hash.delete('mutation_timeout')
187
+ end
188
+ private_class_method :mutation_timeout_deprecation
189
+
190
+ def self.integration_deprecation(reporter, hash)
191
+ value = hash['integration']
192
+ return unless value.instance_of?(String)
193
+ reporter.warn(INTEGRATION_DEPRECATION)
194
+
195
+ hash['integration'] = { 'name' => value }
196
+ end
197
+ private_class_method :integration_deprecation
169
198
 
170
199
  TRANSFORMS = [
171
200
  Transform::Hash.new(
@@ -196,7 +225,7 @@ module Mutant
196
225
  value: 'includes'
197
226
  ),
198
227
  Transform::Hash::Key.new(
199
- transform: Transform::STRING,
228
+ transform: ->(value) { Integration::Config::TRANSFORM.call(value) },
200
229
  value: 'integration'
201
230
  ),
202
231
  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
  ),
@@ -64,6 +66,10 @@ module Mutant
64
66
  )
65
67
  end
66
68
 
69
+ def emit_mutation_worker_process_start(index:)
70
+ hooks.run(:mutation_worker_process_start, index: index)
71
+ end
72
+
67
73
  # The test selections
68
74
  #
69
75
  # @return Hash{Mutation => Enumerable<Test>}
@@ -100,13 +106,21 @@ module Mutant
100
106
  end
101
107
  memoize :amount_mutations
102
108
 
103
- # Amount of tests reachable by integration
109
+ # Amount of all tests the integration provides
104
110
  #
105
111
  # @return [Integer]
106
- def amount_total_tests
112
+ def amount_all_tests
107
113
  integration.all_tests.length
108
114
  end
109
- memoize :amount_total_tests
115
+ memoize :amount_all_tests
116
+
117
+ # Amount of tests available for mutation testing
118
+ #
119
+ # @return [Integer]
120
+ def amount_available_tests
121
+ integration.available_tests.length
122
+ end
123
+ memoize :amount_available_tests
110
124
 
111
125
  # Amount of selected subjects
112
126
  #
@@ -147,9 +161,9 @@ module Mutant
147
161
 
148
162
  def run_mutation_tests(mutation, tests)
149
163
  config.isolation.call(config.mutation.timeout) do
150
- hooks.run(:mutation_insert_pre, mutation)
164
+ hooks.run(:mutation_insert_pre, mutation: mutation)
151
165
  result = mutation.insert(world.kernel)
152
- hooks.run(:mutation_insert_post, mutation)
166
+ hooks.run(:mutation_insert_post, mutation: mutation)
153
167
 
154
168
  result.either(
155
169
  ->(_) { Result::Test::VoidValue.instance },
@@ -163,4 +177,5 @@ module Mutant
163
177
  end
164
178
 
165
179
  end # Env
180
+ # rubocop:enable Metrics/ClassLength
166
181
  end # Mutant
data/lib/mutant/hooks.rb CHANGED
@@ -5,10 +5,11 @@ module Mutant
5
5
  include Adamantium, Anima.new(:map)
6
6
 
7
7
  DEFAULTS = %i[
8
- env_infection_pre
9
8
  env_infection_post
9
+ env_infection_pre
10
10
  mutation_insert_post
11
11
  mutation_insert_pre
12
+ mutation_worker_process_start
12
13
  ].product([EMPTY_ARRAY]).to_h.transform_values(&:freeze).freeze
13
14
 
14
15
  MESSAGE = 'Unknown hook %s'
@@ -32,10 +33,10 @@ module Mutant
32
33
  )
33
34
  end
34
35
 
35
- def run(name, payload)
36
+ def run(name, **payload)
36
37
  Hooks.assert_name(name)
37
38
 
38
- map.fetch(name).each { |block| block.call(payload) }
39
+ map.fetch(name).each { |block| block.call(**payload) }
39
40
 
40
41
  self
41
42
  end
@@ -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
- unless env.config.integration
36
- return Either::Left.new(INTEGRATION_MISSING)
37
- end
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|
@@ -88,11 +117,21 @@ module Mutant
88
117
  # @return [Result::Test]
89
118
  abstract_method :call
90
119
 
91
- # Available tests for integration
120
+ # All tests this integration can run
121
+ #
122
+ # Some tests may not be available for mutation testing.
123
+ # See #available_tests
92
124
  #
93
125
  # @return [Enumerable<Test>]
94
126
  abstract_method :all_tests
95
127
 
128
+ # All tests available for mutation testing
129
+ #
130
+ # Subset ofr #al_tests
131
+ #
132
+ # @return [Enumerable<Test>]
133
+ abstract_method :available_tests
134
+
96
135
  private
97
136
 
98
137
  def timer
@@ -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
 
@@ -8,7 +8,7 @@ module Mutant
8
8
  :ignore,
9
9
  :subjects,
10
10
  :start_expressions,
11
- :subject_filters
11
+ :diffs
12
12
  )
13
13
 
14
14
  INSPECT_FORMAT = "#<#{self} %s>"
@@ -19,8 +19,8 @@ module Mutant
19
19
  PRESENTATIONS = {
20
20
  ignore: :syntax,
21
21
  start_expressions: :syntax,
22
- subject_filters: :inspect,
23
- subjects: :syntax
22
+ subjects: :syntax,
23
+ diffs: :inspect
24
24
  }.freeze
25
25
 
26
26
  private_constant(*constants(false))
@@ -12,9 +12,7 @@ module Mutant
12
12
  #
13
13
  # @return [Enumerable<Subject>]
14
14
  def call(env)
15
- matcher
16
- .call(env)
17
- .select(&predicate.method(:call))
15
+ matcher.call(env).select(&predicate)
18
16
  end
19
17
 
20
18
  end # Filter
@@ -120,6 +120,11 @@ module Mutant
120
120
  def matched_view
121
121
  return if source_location.nil?
122
122
 
123
+ # This is a performance optimization when using --since to avoid the cost of parsing
124
+ # every source file that could possibly map to a subject. A more fine-grained filtering
125
+ # takes places later in the process.
126
+ return unless relevant_source_file?
127
+
123
128
  ast
124
129
  .on_line(source_line)
125
130
  .select { |view| view.node.type.eql?(self.class::MATCH_NODE_TYPE) && match?(view.node) }
@@ -127,6 +132,10 @@ module Mutant
127
132
  end
128
133
  memoize :matched_view
129
134
 
135
+ def relevant_source_file?
136
+ env.config.matcher.diffs.all? { |diff| diff.touches_path?(source_path) }
137
+ end
138
+
130
139
  def visibility
131
140
  # This can be cleaned up once we are on >ruby-3.0
132
141
  # Method#{public,private,protected}? exists there.
@@ -31,7 +31,9 @@ module Mutant
31
31
  private_class_method :allowed_subject?
32
32
 
33
33
  def self.select_subject?(config, subject)
34
- config.subject_filters.all? { |filter| filter.call(subject) }
34
+ config.diffs.all? do |diff|
35
+ diff.touches?(subject.source_path, subject.source_lines)
36
+ end
35
37
  end
36
38
  private_class_method :select_subject?
37
39
 
@@ -41,12 +41,13 @@ module Mutant
41
41
 
42
42
  def self.mutation_test_config(env)
43
43
  Parallel::Config.new(
44
- block: env.method(:cover_index),
45
- jobs: env.config.jobs,
46
- process_name: 'mutant-worker-process',
47
- sink: Sink.new(env: env),
48
- source: Parallel::Source::Array.new(jobs: env.mutations.each_index.to_a),
49
- thread_name: 'mutant-worker-thread'
44
+ block: env.method(:cover_index),
45
+ jobs: env.config.jobs,
46
+ on_process_start: env.method(:emit_mutation_worker_process_start),
47
+ process_name: 'mutant-worker-process',
48
+ sink: Sink.new(env: env),
49
+ source: Parallel::Source::Array.new(jobs: env.mutations.each_index.to_a),
50
+ thread_name: 'mutant-worker-thread'
50
51
  )
51
52
  end
52
53
  private_class_method :mutation_test_config
@@ -3,58 +3,69 @@
3
3
  module Mutant
4
4
  module Parallel
5
5
  class Worker
6
- include Adamantium, Anima.new(
7
- :connection,
8
- :index,
9
- :pid,
10
- :process,
11
- :var_active_jobs,
12
- :var_final,
13
- :var_running,
14
- :var_sink,
15
- :var_source
16
- )
17
-
18
- private(*anima.attribute_names)
19
-
20
- public :index
6
+ class Config
7
+ include Adamantium, Anima.new(
8
+ :block,
9
+ :index,
10
+ :on_process_start,
11
+ :process_name,
12
+ :var_active_jobs,
13
+ :var_final,
14
+ :var_running,
15
+ :var_sink,
16
+ :var_source,
17
+ :world
18
+ )
19
+ end
20
+
21
+ include Adamantium, Anima.new(:connection, :config, :pid)
22
+
23
+ def self.start(**attributes)
24
+ start_config(Config.new(**attributes))
25
+ end
21
26
 
22
27
  # rubocop:disable Metrics/MethodLength
23
- # rubocop:disable Metrics/ParameterLists
24
- def self.start(world:, block:, process_name:, **attributes)
28
+ def self.start_config(config)
29
+ world = config.world
25
30
  io = world.io
26
- process = world.process
31
+ marshal = world.marshal
27
32
 
28
33
  request = Pipe.from_io(io)
29
34
  response = Pipe.from_io(io)
30
35
 
31
- pid = process.fork do
32
- world.thread.current.name = process_name
33
- world.process.setproctitle(process_name)
34
-
35
- Child.new(
36
- block: block,
37
- connection: Pipe::Connection.from_pipes(
38
- marshal: world.marshal,
39
- reader: request,
40
- writer: response
41
- )
42
- ).call
36
+ pid = world.process.fork do
37
+ run_child(
38
+ config: config,
39
+ connection: Pipe::Connection.from_pipes(marshal: marshal, reader: request, writer: response)
40
+ )
43
41
  end
44
42
 
45
43
  new(
46
44
  pid: pid,
47
- process: process,
48
- connection: Pipe::Connection.from_pipes(
49
- marshal: world.marshal,
50
- reader: response,
51
- writer: request
52
- ),
53
- **attributes
45
+ config: config,
46
+ connection: Pipe::Connection.from_pipes(marshal: marshal, reader: response, writer: request)
54
47
  )
55
48
  end
49
+ private_class_method :start_config
56
50
  # rubocop:enable Metrics/MethodLength
57
- # rubocop:enable Metrics/ParameterLists
51
+
52
+ def self.run_child(config:, connection:)
53
+ world = config.world
54
+
55
+ world.thread.current.name = config.process_name
56
+ world.process.setproctitle(config.process_name)
57
+
58
+ config.on_process_start.call(index: config.index)
59
+
60
+ loop do
61
+ connection.send_value(config.block.call(connection.receive_value))
62
+ end
63
+ end
64
+ private_class_method :run_child
65
+
66
+ def index
67
+ config.index
68
+ end
58
69
 
59
70
  # Run worker payload
60
71
  #
@@ -89,45 +100,38 @@ module Mutant
89
100
 
90
101
  private
91
102
 
103
+ def process
104
+ config.world.process
105
+ end
106
+
92
107
  def next_job
93
- var_source.with do |source|
108
+ config.var_source.with do |source|
94
109
  source.next if source.next?
95
110
  end
96
111
  end
97
112
 
98
113
  def add_result(result)
99
- var_sink.with do |sink|
114
+ config.var_sink.with do |sink|
100
115
  sink.result(result)
101
116
  sink.stop?
102
117
  end
103
118
  end
104
119
 
105
120
  def job_start(job)
106
- var_active_jobs.with do |active_jobs|
121
+ config.var_active_jobs.with do |active_jobs|
107
122
  active_jobs << job
108
123
  end
109
124
  end
110
125
 
111
126
  def job_done(job)
112
- var_active_jobs.with do |active_jobs|
127
+ config.var_active_jobs.with do |active_jobs|
113
128
  active_jobs.delete(job)
114
129
  end
115
130
  end
116
131
 
117
132
  def finalize
118
- var_final.put(nil) if var_running.modify(&:pred).zero?
119
- end
120
-
121
- class Child
122
- include Anima.new(:block, :connection)
123
-
124
- def call
125
- loop do
126
- connection.send_value(block.call(connection.receive_value))
127
- end
128
- end
133
+ config.var_final.put(nil) if config.var_running.modify(&:pred).zero?
129
134
  end
130
- private_constant :Child
131
135
  end # Worker
132
136
  end # Parallel
133
137
  end # Mutant
@@ -3,7 +3,6 @@
3
3
  module Mutant
4
4
  # Parallel execution engine of arbitrary payloads
5
5
  module Parallel
6
-
7
6
  # Run async computation returning driver
8
7
  #
9
8
  # @param [World] world
@@ -24,10 +23,11 @@ module Mutant
24
23
  def self.workers(world, config, shared)
25
24
  Array.new(config.jobs) do |index|
26
25
  Worker.start(
27
- block: config.block,
28
- index: index,
29
- process_name: "#{config.process_name}-#{index}",
30
- world: world,
26
+ block: config.block,
27
+ index: index,
28
+ on_process_start: config.on_process_start,
29
+ process_name: "#{config.process_name}-#{index}",
30
+ world: world,
31
31
  **shared
32
32
  )
33
33
  end
@@ -93,6 +93,7 @@ module Mutant
93
93
  include Adamantium, Anima.new(
94
94
  :block,
95
95
  :jobs,
96
+ :on_process_start,
96
97
  :process_name,
97
98
  :sink,
98
99
  :source,
@@ -16,7 +16,7 @@ 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
@@ -7,20 +7,22 @@ module Mutant
7
7
  # Env printer
8
8
  class Env < self
9
9
  delegate(
10
+ :amount_available_tests,
10
11
  :amount_mutations,
11
12
  :amount_selected_tests,
12
13
  :amount_subjects,
13
- :amount_total_tests,
14
+ :amount_all_tests,
14
15
  :config,
15
16
  :test_subject_ratio
16
17
  )
17
18
 
18
19
  FORMATS = [
19
- [:info, 'Subjects: %s', :amount_subjects ],
20
- [:info, 'Total-Tests: %s', :amount_total_tests ],
21
- [:info, 'Selected-Tests: %s', :amount_selected_tests],
22
- [:info, 'Tests/Subject: %0.2f avg', :test_subject_ratio ],
23
- [:info, 'Mutations: %s', :amount_mutations ]
20
+ [:info, 'Subjects: %s', :amount_subjects ],
21
+ [:info, 'All-Tests: %s', :amount_all_tests ],
22
+ [:info, 'Available-Tests: %s', :amount_available_tests],
23
+ [:info, 'Selected-Tests: %s', :amount_selected_tests ],
24
+ [:info, 'Tests/Subject: %0.2f avg', :test_subject_ratio ],
25
+ [:info, 'Mutations: %s', :amount_mutations ]
24
26
  ].each(&:freeze)
25
27
 
26
28
  # Run printer
@@ -22,12 +22,16 @@ module Mutant
22
22
  # @raise [RepositoryError]
23
23
  # when git command failed
24
24
  def touches?(path, line_range)
25
- touched_paths
26
- .from_right { |message| fail Error, message }
27
- .fetch(path) { return false }
25
+ touched_path(path) { return false }
28
26
  .touches?(line_range)
29
27
  end
30
28
 
29
+ def touches_path?(path)
30
+ touched_path(path) { return false }
31
+
32
+ true
33
+ end
34
+
31
35
  private
32
36
 
33
37
  def repository_root
@@ -37,6 +41,10 @@ module Mutant
37
41
  .fmap(&world.pathname.public_method(:new))
38
42
  end
39
43
 
44
+ def touched_path(path, &block)
45
+ touched_paths.from_right { |message| fail Error, message }.fetch(path, &block)
46
+ end
47
+
40
48
  def touched_paths
41
49
  repository_root.bind(&method(:diff_index))
42
50
  end
@@ -2,19 +2,5 @@
2
2
 
3
3
  module Mutant
4
4
  module Repository
5
- # Subject filter based on repository diff
6
- class SubjectFilter
7
- include Adamantium, Anima.new(:diff)
8
-
9
- # Test if subject was touched in diff
10
- #
11
- # @param [Subject] subject
12
- #
13
- # @return [Boolean]
14
- def call(subject)
15
- diff.touches?(subject.source_path, subject.source_lines)
16
- end
17
-
18
- end # SubjectFilter
19
5
  end # Repository
20
6
  end # Mutant
@@ -13,7 +13,7 @@ module Mutant
13
13
  # @return [Enumerable<Test>]
14
14
  def call(subject)
15
15
  subject.match_expressions.each do |match_expression|
16
- subject_tests = integration.all_tests.select do |test|
16
+ subject_tests = integration.available_tests.select do |test|
17
17
  test.expressions.any? do |test_expression|
18
18
  match_expression.prefix?(test_expression)
19
19
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Mutant
4
4
  # Current mutant version
5
- VERSION = '0.11.19'
5
+ VERSION = '0.11.21'
6
6
  end # Mutant
data/lib/mutant.rb CHANGED
@@ -346,7 +346,7 @@ module Mutant
346
346
  fail_fast: false,
347
347
  hooks: EMPTY_ARRAY,
348
348
  includes: EMPTY_ARRAY,
349
- integration: nil,
349
+ integration: Integration::Config::DEFAULT,
350
350
  isolation: Mutant::Isolation::Fork.new(world: WORLD),
351
351
  jobs: nil,
352
352
  matcher: Matcher::Config::DEFAULT,
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.19
4
+ version: 0.11.21
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-05-06 00:00:00.000000000 Z
11
+ date: 2023-06-15 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.7
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.7
82
+ version: 0.6.8
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: parallel
85
85
  requirement: !ruby/object:Gem::Requirement