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 +4 -4
- data/lib/mutant.rb +2 -0
- data/lib/mutant/ast/meta/send.rb +0 -1
- data/lib/mutant/ast/types.rb +0 -9
- data/lib/mutant/env.rb +8 -6
- data/lib/mutant/expression.rb +5 -1
- data/lib/mutant/expression/method.rb +6 -4
- data/lib/mutant/expression/methods.rb +6 -4
- data/lib/mutant/expression/namespace.rb +4 -6
- data/lib/mutant/integration/null.rb +2 -3
- data/lib/mutant/isolation/fork.rb +1 -1
- data/lib/mutant/mutator/node/arguments.rb +0 -2
- data/lib/mutant/mutator/node/block.rb +5 -1
- data/lib/mutant/mutator/node/defined.rb +12 -3
- data/lib/mutant/mutator/node/kwargs.rb +44 -0
- data/lib/mutant/mutator/node/literal/symbol.rb +0 -2
- data/lib/mutant/mutator/node/procarg_zero.rb +0 -6
- data/lib/mutant/mutator/node/send.rb +25 -19
- data/lib/mutant/parallel.rb +43 -28
- data/lib/mutant/parallel/driver.rb +9 -3
- data/lib/mutant/parallel/worker.rb +60 -2
- data/lib/mutant/pipe.rb +94 -0
- data/lib/mutant/reporter/cli/printer/isolation_result.rb +1 -6
- data/lib/mutant/result.rb +9 -6
- data/lib/mutant/runner.rb +7 -10
- data/lib/mutant/runner/sink.rb +12 -2
- data/lib/mutant/subject/method/instance.rb +1 -1
- data/lib/mutant/test.rb +1 -1
- data/lib/mutant/timer.rb +2 -4
- data/lib/mutant/transform.rb +0 -2
- data/lib/mutant/version.rb +1 -1
- metadata +11 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de1bb499dcb7be47ab3ffecf16968d48f17e34825a8cdb64f3a16aded7c76c35
|
4
|
+
data.tar.gz: eccbf7d336879f50a9f018b404ef64289d112f5cf377bccf38c7a6a73d28c7d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec30feb496c9f71dfef0766febd898508f41b252a2a5b678f98cc811947a50b8a57bbaee1eae40175b4593e9292ee858cf337d1ff5d3fb5e951ed93fc4117eb6
|
7
|
+
data.tar.gz: 98b4ec5a27cfef0a94d167d281fa7735c514b34bee7d0f9b677cc49a5a046146e1e06d467bffeed6a8f8b0467201727b6901bffcfa4d48d57a6d43f2016ed15a
|
data/lib/mutant.rb
CHANGED
@@ -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'
|
data/lib/mutant/ast/meta/send.rb
CHANGED
data/lib/mutant/ast/types.rb
CHANGED
@@ -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
|
data/lib/mutant/env.rb
CHANGED
@@ -43,19 +43,21 @@ module Mutant
|
|
43
43
|
end
|
44
44
|
# rubocop:enable Metrics/MethodLength
|
45
45
|
|
46
|
-
#
|
46
|
+
# Cover mutation with specific index
|
47
47
|
#
|
48
|
-
# @param [
|
48
|
+
# @param [Integer] mutation_index
|
49
49
|
#
|
50
|
-
# @return [Result::
|
51
|
-
def
|
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::
|
58
|
+
Result::MutationIndex.new(
|
57
59
|
isolation_result: run_mutation_tests(mutation, tests),
|
58
|
-
|
60
|
+
mutation_index: mutation_index,
|
59
61
|
runtime: timer.now - start
|
60
62
|
)
|
61
63
|
end
|
data/lib/mutant/expression.rb
CHANGED
@@ -4,7 +4,7 @@ module Mutant
|
|
4
4
|
|
5
5
|
# Abstract base class for match expression
|
6
6
|
class Expression
|
7
|
-
include AbstractType
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
@@ -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
|
-
|
17
|
-
|
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
|
-
|
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
|
@@ -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
|
-
|
18
|
-
|
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
|
-
|
34
|
-
|
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
|
-
|
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
|
data/lib/mutant/parallel.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
18
|
+
workers: workers,
|
19
|
+
threads: threads(world, config, workers),
|
21
20
|
**shared
|
22
21
|
)
|
23
22
|
end
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
40
|
-
|
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,
|
60
|
+
def self.shared(klass, world, **attributes)
|
45
61
|
klass.new(
|
46
|
-
condition_variable:
|
47
|
-
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
|
-
:
|
94
|
+
:block,
|
79
95
|
:jobs,
|
80
|
-
:
|
81
|
-
:processor,
|
96
|
+
:process_name,
|
82
97
|
:sink,
|
83
98
|
:source,
|
84
|
-
:
|
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
|
-
:
|
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
|
-
|
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? { |
|
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
|
-
:
|
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 =
|
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
|
data/lib/mutant/pipe.rb
ADDED
@@ -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
|
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
|
|
data/lib/mutant/result.rb
CHANGED
@@ -121,11 +121,7 @@ module Mutant
|
|
121
121
|
|
122
122
|
# Test result
|
123
123
|
class Test
|
124
|
-
include
|
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(
|
data/lib/mutant/runner.rb
CHANGED
@@ -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
|
-
|
41
|
-
jobs:
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
data/lib/mutant/runner/sink.rb
CHANGED
@@ -34,10 +34,12 @@ module Mutant
|
|
34
34
|
|
35
35
|
# Handle mutation finish
|
36
36
|
#
|
37
|
-
# @param [Result::
|
37
|
+
# @param [Result::MutationIndex] mutation_index_result
|
38
38
|
#
|
39
39
|
# @return [self]
|
40
|
-
def 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
|
data/lib/mutant/test.rb
CHANGED
data/lib/mutant/timer.rb
CHANGED
@@ -14,8 +14,8 @@ module Mutant
|
|
14
14
|
class Deadline
|
15
15
|
include Anima.new(:timer, :allowed_time)
|
16
16
|
|
17
|
-
def initialize(
|
18
|
-
super(
|
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]
|
data/lib/mutant/transform.rb
CHANGED
data/lib/mutant/version.rb
CHANGED
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.
|
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-
|
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:
|
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:
|
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.
|
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.
|
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.
|
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.
|
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
|
477
|
+
rubygems_version: 3.2.0.rc.1
|
476
478
|
signing_key:
|
477
479
|
specification_version: 4
|
478
480
|
summary: ''
|