mutant 0.10.18 → 0.10.23

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