mutant 0.11.19 → 0.11.21

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: 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