mutant 0.10.18 → 0.10.23

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: 9150c0a23d648637691af94ce404b1f1d2317469ca669455b1419948b1c4e106
4
- data.tar.gz: 6a100d36685ffe0fd66e7194d6f5bb075b9d9370450c7e585a5b34a31449ae48
3
+ metadata.gz: de1bb499dcb7be47ab3ffecf16968d48f17e34825a8cdb64f3a16aded7c76c35
4
+ data.tar.gz: eccbf7d336879f50a9f018b404ef64289d112f5cf377bccf38c7a6a73d28c7d7
5
5
  SHA512:
6
- metadata.gz: 95858317d816637debda988640307698dbf32670fc246c980b3d46243d198b546dae36138cb9793efd3575c44464d6a7d22b6ef3457752811fac2204120a5071
7
- data.tar.gz: 6b6f0545d7fbdecd68c24e65fa75c386a1d95c1b06c557cb2d127e74119e7eca0e456922b1f2765e48c15f2dba3b9dc1498deff556844c9fd9eea52bcc20e71a
6
+ metadata.gz: ec30feb496c9f71dfef0766febd898508f41b252a2a5b678f98cc811947a50b8a57bbaee1eae40175b4593e9292ee858cf337d1ff5d3fb5e951ed93fc4117eb6
7
+ data.tar.gz: 98b4ec5a27cfef0a94d167d281fa7735c514b34bee7d0f9b677cc49a5a046146e1e06d467bffeed6a8f8b0467201727b6901bffcfa4d48d57a6d43f2016ed15a
@@ -43,6 +43,7 @@ end # Mutant
43
43
  require 'mutant/bootstrap'
44
44
  require 'mutant/version'
45
45
  require 'mutant/env'
46
+ require 'mutant/pipe'
46
47
  require 'mutant/util'
47
48
  require 'mutant/registry'
48
49
  require 'mutant/ast'
@@ -129,6 +130,7 @@ require 'mutant/mutator/node/rescue'
129
130
  require 'mutant/mutator/node/match_current_line'
130
131
  require 'mutant/mutator/node/index'
131
132
  require 'mutant/mutator/node/procarg_zero'
133
+ require 'mutant/mutator/node/kwargs'
132
134
  require 'mutant/loader'
133
135
  require 'mutant/context'
134
136
  require 'mutant/scope'
@@ -13,7 +13,6 @@ module Mutant
13
13
 
14
14
  public :receiver, :selector
15
15
 
16
- INDEX_ASSIGNMENT_SELECTOR = :[]=
17
16
  ATTRIBUTE_ASSIGNMENT_SELECTOR_SUFFIX = '='
18
17
 
19
18
  # Arguments of mutated node
@@ -6,11 +6,6 @@ module Mutant
6
6
  module Types
7
7
  ASSIGNABLE_VARIABLES = Set.new(%i[ivasgn lvasgn cvasgn gvasgn]).freeze
8
8
 
9
- INDEX_ASSIGN_OPERATOR = :[]=
10
-
11
- # Set of nodes that cannot be on the LHS of an assignment
12
- NOT_ASSIGNABLE = Set.new(%i[int float str dstr class module self nil]).freeze
13
-
14
9
  # Set of op-assign types
15
10
  OP_ASSIGN = Set.new(%i[or_asgn and_asgn op_asgn]).freeze
16
11
  # Set of node types that are not valid when emitted standalone
@@ -53,10 +48,6 @@ module Mutant
53
48
  METHOD_OPERATORS - (INDEX_OPERATORS + UNARY_METHOD_OPERATORS)
54
49
  )
55
50
 
56
- OPERATOR_METHODS = Set.new(
57
- METHOD_OPERATORS + INDEX_OPERATORS + UNARY_METHOD_OPERATORS
58
- ).freeze
59
-
60
51
  # Nodes that are NOT handled by mutant.
61
52
  #
62
53
  # not - 1.8 only, mutant does not support 1.8
@@ -43,19 +43,21 @@ module Mutant
43
43
  end
44
44
  # rubocop:enable Metrics/MethodLength
45
45
 
46
- # Kill mutation
46
+ # Cover mutation with specific index
47
47
  #
48
- # @param [Mutation] mutation
48
+ # @param [Integer] mutation_index
49
49
  #
50
- # @return [Result::Mutation]
51
- def kill(mutation)
50
+ # @return [Result::MutationIndex]
51
+ def cover_index(mutation_index)
52
+ mutation = mutations.fetch(mutation_index)
53
+
52
54
  start = timer.now
53
55
 
54
56
  tests = selections.fetch(mutation.subject)
55
57
 
56
- Result::Mutation.new(
58
+ Result::MutationIndex.new(
57
59
  isolation_result: run_mutation_tests(mutation, tests),
58
- mutation: mutation,
60
+ mutation_index: mutation_index,
59
61
  runtime: timer.now - start
60
62
  )
61
63
  end
@@ -4,7 +4,7 @@ module Mutant
4
4
 
5
5
  # Abstract base class for match expression
6
6
  class Expression
7
- include AbstractType, Adamantium::Flat
7
+ include AbstractType
8
8
 
9
9
  fragment = /[A-Za-z][A-Za-z\d_]*/.freeze
10
10
  SCOPE_NAME_PATTERN = /(?<scope_name>#{fragment}(?:#{SCOPE_OPERATOR}#{fragment})*)/.freeze
@@ -12,6 +12,10 @@ module Mutant
12
12
 
13
13
  private_constant(*constants(false))
14
14
 
15
+ def self.new(*)
16
+ super.freeze
17
+ end
18
+
15
19
  # Syntax of expression
16
20
  #
17
21
  # @return [Matcher]
@@ -26,13 +26,15 @@ module Mutant
26
26
 
27
27
  REGEXP = /\A#{SCOPE_NAME_PATTERN}#{SCOPE_SYMBOL_PATTERN}#{METHOD_NAME_PATTERN}\z/.freeze
28
28
 
29
+ def initialize(*)
30
+ super
31
+ @syntax = [scope_name, scope_symbol, method_name].join.freeze
32
+ end
33
+
29
34
  # Syntax of expression
30
35
  #
31
36
  # @return [String]
32
- def syntax
33
- [scope_name, scope_symbol, method_name].join
34
- end
35
- memoize :syntax
37
+ attr_reader :syntax
36
38
 
37
39
  # Matcher for expression
38
40
  #
@@ -20,13 +20,15 @@ module Mutant
20
20
 
21
21
  REGEXP = /\A#{SCOPE_NAME_PATTERN}#{SCOPE_SYMBOL_PATTERN}\z/.freeze
22
22
 
23
+ def initialize(*)
24
+ super
25
+ @syntax = [scope_name, scope_symbol].join.freeze
26
+ end
27
+
23
28
  # Syntax of expression
24
29
  #
25
30
  # @return [String]
26
- def syntax
27
- [scope_name, scope_symbol].join
28
- end
29
- memoize :syntax
31
+ attr_reader :syntax
30
32
 
31
33
  # Matcher on expression
32
34
  #
@@ -16,6 +16,9 @@ module Mutant
16
16
  # @return [undefined]
17
17
  def initialize(*)
18
18
  super
19
+
20
+ @syntax = "#{scope_name}*".freeze # rubocop:disable Style/RedundantFreeze
21
+
19
22
  @recursion_pattern = Regexp.union(
20
23
  /\A#{scope_name}\z/,
21
24
  /\A#{scope_name}::/,
@@ -26,10 +29,7 @@ module Mutant
26
29
  # Syntax for expression
27
30
  #
28
31
  # @return [String]
29
- def syntax
30
- "#{scope_name}*"
31
- end
32
- memoize :syntax
32
+ attr_reader :syntax
33
33
 
34
34
  # Matcher for expression
35
35
  #
@@ -52,7 +52,6 @@ module Mutant
52
52
  0
53
53
  end
54
54
  end
55
-
56
55
  end # Recursive
57
56
 
58
57
  # Exact namespace expression
@@ -88,7 +87,6 @@ module Mutant
88
87
  Object.const_get(scope_name)
89
88
  rescue NameError # rubocop:disable Lint/SuppressedException
90
89
  end
91
-
92
90
  end # Exact
93
91
  end # Namespace
94
92
  end # Expression
@@ -16,11 +16,10 @@ module Mutant
16
16
  # @param [Enumerable<Mutant::Test>] tests
17
17
  #
18
18
  # @return [Result::Test]
19
- def call(tests)
19
+ def call(_tests)
20
20
  Result::Test.new(
21
21
  passed: true,
22
- runtime: 0.0,
23
- tests: tests
22
+ runtime: 0.0
24
23
  )
25
24
  end
26
25
 
@@ -230,7 +230,7 @@ module Mutant
230
230
 
231
231
  end # Child
232
232
 
233
- private_constant(*(constants(false) - %i[ChildError ForkError]))
233
+ private_constant(*constants(false))
234
234
 
235
235
  # Call block in isolation
236
236
  #
@@ -8,8 +8,6 @@ module Mutant
8
8
 
9
9
  handle(:args)
10
10
 
11
- PROCARG = %i[restarg mlhs].freeze
12
-
13
11
  private
14
12
 
15
13
  def dispatch
@@ -21,7 +21,7 @@ module Mutant
21
21
  end
22
22
 
23
23
  def mutate_body
24
- emit_body(nil)
24
+ emit_body(nil) unless unconditional_loop?
25
25
  emit_body(N_RAISE)
26
26
 
27
27
  return unless body
@@ -31,6 +31,10 @@ module Mutant
31
31
  mutate_body_receiver
32
32
  end
33
33
 
34
+ def unconditional_loop?
35
+ send.eql?(s(:send, nil, :loop))
36
+ end
37
+
34
38
  def body_has_control?
35
39
  AST.find_last_path(body) do |node|
36
40
  n_break?(node) || n_next?(node)
@@ -13,10 +13,19 @@ module Mutant
13
13
  private
14
14
 
15
15
  def dispatch
16
- emit_singletons
17
- emit(N_TRUE)
16
+ emit(N_NIL)
17
+ emit_instance_variable_mutation
18
+ end
19
+
20
+ def emit_instance_variable_mutation
21
+ return unless n_ivar?(expression)
22
+
23
+ instance_variable_name = Mutant::Util.one(expression.children)
18
24
 
19
- emit_expression_mutations { |node| !n_self?(node) }
25
+ emit(
26
+ s(:send, nil, :instance_variable_defined?,
27
+ s(:sym, instance_variable_name))
28
+ )
20
29
  end
21
30
 
22
31
  end # Defined
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ class Mutator
5
+ class Node
6
+ # Mutator for kwargs node
7
+ class Kwargs < self
8
+
9
+ DISALLOW = %i[nil self].freeze
10
+
11
+ private_constant(*constants(false))
12
+
13
+ handle(:kwargs)
14
+
15
+ private
16
+
17
+ def dispatch
18
+ emit_argument_presence
19
+ emit_argument_mutations
20
+ end
21
+
22
+ def emit_argument_presence
23
+ Util::Array::Presence.call(children).each do |children|
24
+ emit_type(*children) unless children.empty?
25
+ end
26
+ end
27
+
28
+ def emit_argument_mutations
29
+ children.each_with_index do |child, index|
30
+ Mutator.mutate(child).each do |mutant|
31
+ if forbid_argument?(mutant)
32
+ emit_child_update(index, mutant)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def forbid_argument?(node)
39
+ !(n_pair?(node) && DISALLOW.include?(node.children.first.type))
40
+ end
41
+ end # Kwargs
42
+ end # Node
43
+ end # Mutator
44
+ end # Mutant
@@ -11,8 +11,6 @@ module Mutant
11
11
 
12
12
  children :value
13
13
 
14
- PREFIX = '__mutant__'
15
-
16
14
  private
17
15
 
18
16
  def dispatch
@@ -4,12 +4,6 @@ module Mutant
4
4
  class Mutator
5
5
  class Node
6
6
  class ProcargZero < self
7
- MAP = {
8
- ::Parser::AST::Node => :emit_argument_node_mutations,
9
- Symbol => :emit_argument_symbol_mutations
10
- }.freeze
11
-
12
- private_constant(*constants(false))
13
7
 
14
8
  handle :procarg0
15
9
 
@@ -14,30 +14,32 @@ module Mutant
14
14
  children :receiver, :selector
15
15
 
16
16
  SELECTOR_REPLACEMENTS = IceNine.deep_freeze(
17
- reverse_map: %i[map each],
18
- kind_of?: %i[instance_of?],
17
+ :< => %i[== eql? equal?],
18
+ :<= => %i[< == eql? equal?],
19
+ :== => %i[eql? equal?],
20
+ :> => %i[== eql? equal?],
21
+ :>= => %i[> == eql? equal?],
22
+ __send__: %i[public_send],
23
+ all?: %i[any?],
24
+ any?: %i[all?],
25
+ at: %i[fetch key?],
26
+ eql?: %i[equal?],
27
+ fetch: %i[key?],
28
+ flat_map: %i[map],
29
+ gsub: %i[sub],
19
30
  is_a?: %i[instance_of?],
31
+ kind_of?: %i[instance_of?],
32
+ map: %i[each],
33
+ method: %i[public_method],
20
34
  reverse_each: %i[each],
35
+ reverse_map: %i[map each],
21
36
  reverse_merge: %i[merge],
22
- map: %i[each],
23
- flat_map: %i[map],
24
37
  send: %i[public_send __send__],
25
- __send__: %i[public_send],
26
- method: %i[public_method],
27
- gsub: %i[sub],
28
- eql?: %i[equal?],
29
- to_s: %i[to_str],
30
- to_i: %i[to_int],
31
38
  to_a: %i[to_ary],
32
39
  to_h: %i[to_hash],
33
- at: %i[fetch key?],
34
- fetch: %i[key?],
35
- values_at: %i[fetch_values],
36
- :== => %i[eql? equal?],
37
- :>= => %i[> == eql? equal?],
38
- :<= => %i[< == eql? equal?],
39
- :> => %i[== eql? equal?],
40
- :< => %i[== eql? equal?]
40
+ to_i: %i[to_int],
41
+ to_s: %i[to_str],
42
+ values_at: %i[fetch_values]
41
43
  )
42
44
 
43
45
  RECEIVER_SELECTOR_REPLACEMENTS = IceNine.deep_freeze(
@@ -175,7 +177,11 @@ module Mutant
175
177
  end
176
178
 
177
179
  def emit_argument_propagation
178
- emit_propagation(Mutant::Util.one(arguments)) if arguments.one?
180
+ return unless arguments.one?
181
+
182
+ argument = Mutant::Util.one(arguments)
183
+
184
+ emit_propagation(argument) unless n_kwargs?(argument)
179
185
  end
180
186
 
181
187
  def mutate_receiver
@@ -6,45 +6,61 @@ module Mutant
6
6
 
7
7
  # Run async computation returning driver
8
8
  #
9
+ # @param [World] world
9
10
  # @param [Config] config
10
11
  #
11
12
  # @return [Driver]
12
- def self.async(config)
13
- shared = {
14
- var_active_jobs: shared(Variable::IVar, config, value: Set.new),
15
- var_final: shared(Variable::IVar, config),
16
- var_sink: shared(Variable::IVar, config, value: config.sink)
17
- }
13
+ def self.async(world, config)
14
+ shared = shared_state(world, config)
15
+ workers = workers(world, config, shared)
18
16
 
19
17
  Driver.new(
20
- threads: threads(config, worker(config, **shared)),
18
+ workers: workers,
19
+ threads: threads(world, config, workers),
21
20
  **shared
22
21
  )
23
22
  end
24
23
 
25
- # The worker
26
- #
27
- # @param [Config] config
28
- #
29
- # @return [Worker]
30
- def self.worker(config, **shared)
31
- Worker.new(
32
- processor: config.processor,
33
- var_running: shared(Variable::MVar, config, value: config.jobs),
34
- var_source: shared(Variable::IVar, config, value: config.source),
35
- **shared
36
- )
24
+ def self.workers(world, config, shared)
25
+ Array.new(config.jobs) do |index|
26
+ Worker.start(
27
+ block: config.block,
28
+ index: index,
29
+ process_name: "#{config.process_name}-#{index}",
30
+ world: world,
31
+ **shared
32
+ )
33
+ end
37
34
  end
35
+ private_class_method :workers
36
+
37
+ def self.shared_state(world, config)
38
+ {
39
+ var_active_jobs: shared(Variable::IVar, world, value: Set.new),
40
+ var_final: shared(Variable::IVar, world),
41
+ var_running: shared(Variable::MVar, world, value: config.jobs),
42
+ var_sink: shared(Variable::IVar, world, value: config.sink),
43
+ var_source: shared(Variable::IVar, world, value: config.source)
44
+ }
45
+ end
46
+ private_class_method :shared_state
47
+
48
+ def self.threads(world, config, workers)
49
+ thread = world.thread
38
50
 
39
- def self.threads(config, worker)
40
- Array.new(config.jobs) { config.thread.new(&worker.method(:call)) }
51
+ workers.map do |worker|
52
+ thread.new do
53
+ thread.current.name = "#{config.thread_name}-#{worker.index}"
54
+ worker.call
55
+ end
56
+ end
41
57
  end
42
58
  private_class_method :threads
43
59
 
44
- def self.shared(klass, config, **attributes)
60
+ def self.shared(klass, world, **attributes)
45
61
  klass.new(
46
- condition_variable: config.condition_variable,
47
- mutex: config.mutex,
62
+ condition_variable: world.condition_variable,
63
+ mutex: world.mutex,
48
64
  **attributes
49
65
  )
50
66
  end
@@ -75,13 +91,12 @@ module Mutant
75
91
  # Parallel run configuration
76
92
  class Config
77
93
  include Adamantium::Flat, Anima.new(
78
- :condition_variable,
94
+ :block,
79
95
  :jobs,
80
- :mutex,
81
- :processor,
96
+ :process_name,
82
97
  :sink,
83
98
  :source,
84
- :thread
99
+ :thread_name
85
100
  )
86
101
  end # Config
87
102
 
@@ -8,7 +8,10 @@ module Mutant
8
8
  :threads,
9
9
  :var_active_jobs,
10
10
  :var_final,
11
- :var_sink
11
+ :var_running,
12
+ :var_sink,
13
+ :var_source,
14
+ :workers
12
15
  )
13
16
 
14
17
  private(*anima.attribute_names)
@@ -29,7 +32,10 @@ module Mutant
29
32
 
30
33
  def finalize(status)
31
34
  status.tap do
32
- threads.each(&:join) if status.done?
35
+ if status.done?
36
+ workers.each(&:join)
37
+ threads.each(&:join)
38
+ end
33
39
  end
34
40
  end
35
41
 
@@ -38,7 +44,7 @@ module Mutant
38
44
  var_sink.with do |sink|
39
45
  Status.new(
40
46
  active_jobs: active_jobs.dup.freeze,
41
- done: threads.all? { |thread| !thread.alive? },
47
+ done: threads.all? { |worker| !worker.alive? },
42
48
  payload: sink.status
43
49
  )
44
50
  end
@@ -4,7 +4,10 @@ module Mutant
4
4
  module Parallel
5
5
  class Worker
6
6
  include Adamantium::Flat, Anima.new(
7
- :processor,
7
+ :connection,
8
+ :index,
9
+ :pid,
10
+ :process,
8
11
  :var_active_jobs,
9
12
  :var_final,
10
13
  :var_running,
@@ -14,6 +17,45 @@ module Mutant
14
17
 
15
18
  private(*anima.attribute_names)
16
19
 
20
+ public :index
21
+
22
+ # rubocop:disable Metrics/MethodLength
23
+ # rubocop:disable Metrics/ParameterLists
24
+ def self.start(world:, block:, process_name:, **attributes)
25
+ io = world.io
26
+ process = world.process
27
+
28
+ request = Pipe.from_io(io)
29
+ response = Pipe.from_io(io)
30
+
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
43
+ end
44
+
45
+ new(
46
+ pid: pid,
47
+ process: process,
48
+ connection: Pipe::Connection.from_pipes(
49
+ marshal: world.marshal,
50
+ reader: response,
51
+ writer: request
52
+ ),
53
+ **attributes
54
+ )
55
+ end
56
+ # rubocop:enable Metrics/MethodLength
57
+ # rubocop:enable Metrics/ParameterLists
58
+
17
59
  # Run worker payload
18
60
  #
19
61
  # @return [self]
@@ -23,7 +65,7 @@ module Mutant
23
65
 
24
66
  job_start(job)
25
67
 
26
- result = processor.call(job.payload)
68
+ result = connection.call(job.payload)
27
69
 
28
70
  job_done(job)
29
71
 
@@ -35,6 +77,12 @@ module Mutant
35
77
  self
36
78
  end
37
79
 
80
+ def join
81
+ process.kill('TERM', pid)
82
+ process.wait(pid)
83
+ self
84
+ end
85
+
38
86
  private
39
87
 
40
88
  def next_job
@@ -66,6 +114,16 @@ module Mutant
66
114
  var_final.put(nil) if var_running.modify(&:pred).zero?
67
115
  end
68
116
 
117
+ class Child
118
+ include Anima.new(:block, :connection)
119
+
120
+ def call
121
+ loop do
122
+ connection.send_value(block.call(connection.receive_value))
123
+ end
124
+ end
125
+ end
126
+ private_constant :Child
69
127
  end # Worker
70
128
  end # Parallel
71
129
  end # Mutant
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ # Pipe abstraction
5
+ class Pipe
6
+ include Adamantium::Flat, Anima.new(:reader, :writer)
7
+
8
+ # Run block with pipe in binmode
9
+ #
10
+ # @return [undefined]
11
+ def self.with(io)
12
+ io.pipe(binmode: true) do |(reader, writer)|
13
+ yield new(reader: reader, writer: writer)
14
+ end
15
+ end
16
+
17
+ def self.from_io(io)
18
+ reader, writer = io.pipe(binmode: true)
19
+ new(reader: reader, writer: writer)
20
+ end
21
+
22
+ # Writer end of the pipe
23
+ #
24
+ # @return [IO]
25
+ def to_writer
26
+ reader.close
27
+ writer
28
+ end
29
+
30
+ # Parent reader end of the pipe
31
+ #
32
+ # @return [IO]
33
+ def to_reader
34
+ writer.close
35
+ reader
36
+ end
37
+
38
+ class Connection
39
+ include Anima.new(:marshal, :reader, :writer)
40
+
41
+ Error = Class.new(RuntimeError)
42
+
43
+ class Frame
44
+ include Concord.new(:io)
45
+
46
+ HEADER_FORMAT = 'N'
47
+ MAX_BYTES = (2**32).pred
48
+ HEADER_SIZE = 4
49
+
50
+ def receive_value
51
+ header = read(HEADER_SIZE)
52
+ read(Util.one(header.unpack(HEADER_FORMAT)))
53
+ end
54
+
55
+ def send_value(body)
56
+ bytesize = body.bytesize
57
+
58
+ fail Error, 'message to big' if bytesize > MAX_BYTES
59
+
60
+ io.write([bytesize].pack(HEADER_FORMAT))
61
+ io.write(body)
62
+ end
63
+
64
+ private
65
+
66
+ def read(bytes)
67
+ io.read(bytes) or fail Error, 'Unexpected EOF'
68
+ end
69
+ end
70
+
71
+ def call(payload)
72
+ send_value(payload)
73
+ receive_value
74
+ end
75
+
76
+ def receive_value
77
+ marshal.load(reader.receive_value)
78
+ end
79
+
80
+ def send_value(value)
81
+ writer.send_value(marshal.dump(value))
82
+ self
83
+ end
84
+
85
+ def self.from_pipes(marshal:, reader:, writer:)
86
+ new(
87
+ marshal: marshal,
88
+ reader: Frame.new(reader.to_reader),
89
+ writer: Frame.new(writer.to_writer)
90
+ )
91
+ end
92
+ end
93
+ end # Pipe
94
+ end # Mutant
@@ -12,11 +12,6 @@ module Mutant
12
12
  %s
13
13
  MESSAGE
14
14
 
15
- LOG_MESSAGES = <<~'MESSAGE'
16
- Log messages (combined stderr and stdout):
17
- %s
18
- MESSAGE
19
-
20
15
  EXCEPTION_ERROR_MESSAGE = <<~'MESSAGE'
21
16
  Killing the mutation resulted in an integration error.
22
17
  This is the case when the tests selected for the current mutation
@@ -36,7 +31,7 @@ module Mutant
36
31
  ```
37
32
  MESSAGE
38
33
 
39
- TIMEOUT_ERROR_MESSAGE =<<~'MESSAGE'
34
+ TIMEOUT_ERROR_MESSAGE = <<~'MESSAGE'
40
35
  Mutation analysis ran into the configured timeout of %0.9<timeout>g seconds.
41
36
  MESSAGE
42
37
 
@@ -121,11 +121,7 @@ module Mutant
121
121
 
122
122
  # Test result
123
123
  class Test
124
- include Result, Anima.new(
125
- :passed,
126
- :runtime,
127
- :tests
128
- )
124
+ include Anima.new(:passed, :runtime)
129
125
 
130
126
  class VoidValue < self
131
127
  include Singleton
@@ -137,7 +133,6 @@ module Mutant
137
133
  super(
138
134
  passed: false,
139
135
  runtime: 0.0,
140
- tests: []
141
136
  )
142
137
  end
143
138
  end # VoidValue
@@ -237,6 +232,14 @@ module Mutant
237
232
  end
238
233
  end
239
234
 
235
+ class MutationIndex
236
+ include Anima.new(
237
+ :isolation_result,
238
+ :mutation_index,
239
+ :runtime
240
+ )
241
+ end # MutationIndex
242
+
240
243
  # Mutation result
241
244
  class Mutation
242
245
  include Result, Anima.new(
@@ -17,7 +17,7 @@ module Mutant
17
17
 
18
18
  run_driver(
19
19
  reporter,
20
- Parallel.async(mutation_test_config(env))
20
+ Parallel.async(env.world, mutation_test_config(env))
21
21
  ).tap do |result|
22
22
  reporter.report(result)
23
23
  end
@@ -34,16 +34,13 @@ module Mutant
34
34
  private_class_method :run_driver
35
35
 
36
36
  def self.mutation_test_config(env)
37
- world = env.world
38
-
39
37
  Parallel::Config.new(
40
- condition_variable: world.condition_variable,
41
- jobs: env.config.jobs,
42
- mutex: world.mutex,
43
- processor: env.method(:kill),
44
- sink: Sink.new(env),
45
- source: Parallel::Source::Array.new(env.mutations),
46
- thread: world.thread
38
+ block: env.method(:cover_index),
39
+ jobs: env.config.jobs,
40
+ process_name: 'mutant-worker-process',
41
+ sink: Sink.new(env),
42
+ source: Parallel::Source::Array.new(env.mutations.each_index.to_a),
43
+ thread_name: 'mutant-worker-thread'
47
44
  )
48
45
  end
49
46
  private_class_method :mutation_test_config
@@ -34,10 +34,12 @@ module Mutant
34
34
 
35
35
  # Handle mutation finish
36
36
  #
37
- # @param [Result::Mutation] mutation_result
37
+ # @param [Result::MutationIndex] mutation_index_result
38
38
  #
39
39
  # @return [self]
40
- def result(mutation_result)
40
+ def result(mutation_index_result)
41
+ mutation_result = mutation_result(mutation_index_result)
42
+
41
43
  subject = mutation_result.mutation.subject
42
44
 
43
45
  @subject_results[subject] = Result::Subject.new(
@@ -58,6 +60,14 @@ module Mutant
58
60
  )
59
61
  end
60
62
 
63
+ def mutation_result(mutation_index_result)
64
+ Result::Mutation.new(
65
+ isolation_result: mutation_index_result.isolation_result,
66
+ mutation: env.mutations.fetch(mutation_index_result.mutation_index),
67
+ runtime: mutation_index_result.runtime
68
+ )
69
+ end
70
+
61
71
  def previous_coverage_results(subject)
62
72
  subject_result = @subject_results.fetch(subject) { return EMPTY_ARRAY }
63
73
  subject_result.coverage_results
@@ -52,7 +52,7 @@ module Mutant
52
52
  # rubocop:disable Style/GuardClause
53
53
  if FREEZER_OPTION_VALUES.key?(freezer)
54
54
  [
55
- s(:hash,
55
+ s(:kwargs,
56
56
  s(:pair,
57
57
  s(:sym, :freezer),
58
58
  s(:sym, FREEZER_OPTION_VALUES.fetch(freezer))))
@@ -3,7 +3,7 @@
3
3
  module Mutant
4
4
  # Abstract base class for test that might kill a mutation
5
5
  class Test
6
- include Adamantium::Flat, Anima.new(
6
+ include Anima.new(
7
7
  :expressions,
8
8
  :id
9
9
  )
@@ -14,8 +14,8 @@ module Mutant
14
14
  class Deadline
15
15
  include Anima.new(:timer, :allowed_time)
16
16
 
17
- def initialize(**arguments)
18
- super(**arguments)
17
+ def initialize(*arguments)
18
+ super(*arguments)
19
19
  @start_at = timer.now
20
20
  end
21
21
 
@@ -54,8 +54,6 @@ module Mutant
54
54
  class None < self
55
55
  include Concord.new
56
56
 
57
- STATUS = Status.new(nil)
58
-
59
57
  # The time left
60
58
  #
61
59
  # @return [Float, nil]
@@ -383,8 +383,6 @@ module Mutant
383
383
  #
384
384
  # @param [Object]
385
385
  #
386
- # ignore :reek:NestedIterators
387
- #
388
386
  # @return [Either<Error, Object>]
389
387
  def call(input)
390
388
  current = input
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Mutant
4
4
  # Current mutant version
5
- VERSION = '0.10.18'
5
+ VERSION = '0.10.23'
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.18
4
+ version: 0.10.23
5
5
  platform: ruby
6
6
  authors:
7
7
  - Markus Schirp
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-13 00:00:00.000000000 Z
11
+ date: 2020-12-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: abstract_type
@@ -156,14 +156,14 @@ dependencies:
156
156
  requirements:
157
157
  - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: 2.7.1
159
+ version: 3.0.0
160
160
  type: :runtime
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
- version: 2.7.1
166
+ version: 3.0.0
167
167
  - !ruby/object:Gem::Dependency
168
168
  name: procto
169
169
  requirement: !ruby/object:Gem::Requirement
@@ -184,14 +184,14 @@ dependencies:
184
184
  requirements:
185
185
  - - "~>"
186
186
  - !ruby/object:Gem::Version
187
- version: 0.5.4
187
+ version: 0.5.6
188
188
  type: :runtime
189
189
  prerelease: false
190
190
  version_requirements: !ruby/object:Gem::Requirement
191
191
  requirements:
192
192
  - - "~>"
193
193
  - !ruby/object:Gem::Version
194
- version: 0.5.4
194
+ version: 0.5.6
195
195
  - !ruby/object:Gem::Dependency
196
196
  name: variable
197
197
  requirement: !ruby/object:Gem::Requirement
@@ -268,14 +268,14 @@ dependencies:
268
268
  requirements:
269
269
  - - "~>"
270
270
  - !ruby/object:Gem::Version
271
- version: '1.2'
271
+ version: '1.7'
272
272
  type: :development
273
273
  prerelease: false
274
274
  version_requirements: !ruby/object:Gem::Requirement
275
275
  requirements:
276
276
  - - "~>"
277
277
  - !ruby/object:Gem::Version
278
- version: '1.2'
278
+ version: '1.7'
279
279
  description: Mutation Testing for Ruby.
280
280
  email:
281
281
  - mbj@schirp-dso.com
@@ -366,6 +366,7 @@ files:
366
366
  - lib/mutant/mutator/node/generic.rb
367
367
  - lib/mutant/mutator/node/if.rb
368
368
  - lib/mutant/mutator/node/index.rb
369
+ - lib/mutant/mutator/node/kwargs.rb
369
370
  - lib/mutant/mutator/node/kwbegin.rb
370
371
  - lib/mutant/mutator/node/literal.rb
371
372
  - lib/mutant/mutator/node/literal/array.rb
@@ -412,6 +413,7 @@ files:
412
413
  - lib/mutant/parallel/source.rb
413
414
  - lib/mutant/parallel/worker.rb
414
415
  - lib/mutant/parser.rb
416
+ - lib/mutant/pipe.rb
415
417
  - lib/mutant/range.rb
416
418
  - lib/mutant/registry.rb
417
419
  - lib/mutant/reporter.rb
@@ -472,7 +474,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
472
474
  - !ruby/object:Gem::Version
473
475
  version: '0'
474
476
  requirements: []
475
- rubygems_version: 3.1.4
477
+ rubygems_version: 3.2.0.rc.1
476
478
  signing_key:
477
479
  specification_version: 4
478
480
  summary: ''