mutant 0.10.6 → 0.10.7
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 +4 -3
- data/lib/mutant/cli/command/run.rb +5 -2
- data/lib/mutant/config.rb +59 -34
- data/lib/mutant/env.rb +14 -4
- data/lib/mutant/integration.rb +7 -10
- data/lib/mutant/integration/null.rb +0 -1
- data/lib/mutant/isolation.rb +11 -48
- data/lib/mutant/isolation/fork.rb +107 -40
- data/lib/mutant/isolation/none.rb +18 -5
- data/lib/mutant/license/subscription/commercial.rb +2 -3
- data/lib/mutant/license/subscription/opensource.rb +0 -1
- data/lib/mutant/matcher/method/instance.rb +0 -2
- data/lib/mutant/mutator/node/send.rb +1 -1
- data/lib/mutant/parallel.rb +0 -1
- data/lib/mutant/parallel/worker.rb +0 -2
- data/lib/mutant/reporter/cli.rb +0 -2
- data/lib/mutant/reporter/cli/printer/config.rb +9 -5
- data/lib/mutant/reporter/cli/printer/coverage_result.rb +19 -0
- data/lib/mutant/reporter/cli/printer/env_progress.rb +2 -0
- data/lib/mutant/reporter/cli/printer/isolation_result.rb +19 -35
- data/lib/mutant/reporter/cli/printer/mutation_result.rb +4 -9
- data/lib/mutant/reporter/cli/printer/subject_result.rb +2 -2
- data/lib/mutant/result.rb +81 -30
- data/lib/mutant/runner/sink.rb +12 -5
- data/lib/mutant/timer.rb +60 -11
- data/lib/mutant/transform.rb +25 -21
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/warnings.rb +0 -1
- data/lib/mutant/world.rb +15 -0
- metadata +3 -5
- data/lib/mutant/reporter/cli/printer/mutation_progress_result.rb +0 -28
- data/lib/mutant/reporter/cli/printer/subject_progress.rb +0 -58
- data/lib/mutant/reporter/cli/printer/test_result.rb +0 -32
@@ -12,12 +12,25 @@ module Mutant
|
|
12
12
|
#
|
13
13
|
# @return [Result]
|
14
14
|
#
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
# rubocop:disable Lint/SuppressedException
|
16
|
+
# rubocop:disable Metrics/MethodLength
|
17
|
+
# ^^ it actually isn not suppressed, it assigns an lvar
|
18
|
+
def call(_timeout)
|
19
|
+
begin
|
20
|
+
value = yield
|
21
|
+
rescue => exception
|
22
|
+
end
|
23
|
+
|
24
|
+
Result.new(
|
25
|
+
exception: exception,
|
26
|
+
log: '',
|
27
|
+
process_status: nil,
|
28
|
+
timeout: nil,
|
29
|
+
value: value
|
30
|
+
)
|
20
31
|
end
|
32
|
+
# rubocop:enable Lint/SuppressedException
|
33
|
+
# rubocop:enable Metrics/MethodLength
|
21
34
|
|
22
35
|
end # None
|
23
36
|
end # Isolation
|
@@ -12,7 +12,7 @@ module Mutant
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def self.from_json(value)
|
15
|
-
new(value.fetch('authors').map(&Author.
|
15
|
+
new(value.fetch('authors').map(&Author.public_method(:new)).to_set)
|
16
16
|
end
|
17
17
|
|
18
18
|
def apply(world)
|
@@ -39,12 +39,11 @@ module Mutant
|
|
39
39
|
capture(world, %w[git show --quiet --pretty=format:%ae])
|
40
40
|
end
|
41
41
|
|
42
|
-
# ignore :reek:UtilityFunction
|
43
42
|
def capture(world, command)
|
44
43
|
world
|
45
44
|
.capture_stdout(command)
|
46
45
|
.fmap(&:chomp)
|
47
|
-
.fmap(&Author.
|
46
|
+
.fmap(&Author.public_method(:new))
|
48
47
|
.fmap { |value| Set.new([value]) }
|
49
48
|
.from_right { Set.new }
|
50
49
|
end
|
data/lib/mutant/parallel.rb
CHANGED
data/lib/mutant/reporter/cli.rb
CHANGED
@@ -12,13 +12,17 @@ module Mutant
|
|
12
12
|
# @param [Mutant::Config] config
|
13
13
|
#
|
14
14
|
# @return [undefined]
|
15
|
+
#
|
16
|
+
# rubocop:disable Metrics/AbcSize
|
15
17
|
def run
|
16
|
-
info 'Matcher: %s',
|
17
|
-
info 'Integration: %s',
|
18
|
-
info 'Jobs: %s',
|
19
|
-
info 'Includes: %s',
|
20
|
-
info 'Requires: %s',
|
18
|
+
info 'Matcher: %s', object.matcher.inspect
|
19
|
+
info 'Integration: %s', object.integration || 'null'
|
20
|
+
info 'Jobs: %s', object.jobs || 'auto'
|
21
|
+
info 'Includes: %s', object.includes
|
22
|
+
info 'Requires: %s', object.requires
|
23
|
+
info 'MutationTimeout: %0.9g', object.mutation_timeout if object.mutation_timeout
|
21
24
|
end
|
25
|
+
# rubocop:enable Metrics/AbcSize
|
22
26
|
|
23
27
|
end # Config
|
24
28
|
end # Printer
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
class Reporter
|
5
|
+
class CLI
|
6
|
+
class Printer
|
7
|
+
# Reporter for mutation coverage results
|
8
|
+
class CoverageResult < self
|
9
|
+
# Run report printer
|
10
|
+
#
|
11
|
+
# @return [undefined]
|
12
|
+
def run
|
13
|
+
visit(MutationResult, object.mutation_result)
|
14
|
+
end
|
15
|
+
end # Printer
|
16
|
+
end # Coverage
|
17
|
+
end # CLI
|
18
|
+
end # Reporter
|
19
|
+
end # Mutant
|
@@ -10,6 +10,7 @@ module Mutant
|
|
10
10
|
:amount_mutation_results,
|
11
11
|
:amount_mutations_alive,
|
12
12
|
:amount_mutations_killed,
|
13
|
+
:amount_timeouts,
|
13
14
|
:coverage,
|
14
15
|
:env,
|
15
16
|
:killtime,
|
@@ -21,6 +22,7 @@ module Mutant
|
|
21
22
|
[:info, 'Results: %s', :amount_mutation_results],
|
22
23
|
[:info, 'Kills: %s', :amount_mutations_killed],
|
23
24
|
[:info, 'Alive: %s', :amount_mutations_alive ],
|
25
|
+
[:info, 'Timeouts: %s', :amount_timeouts ],
|
24
26
|
[:info, 'Runtime: %0.2fs', :runtime ],
|
25
27
|
[:info, 'Killtime: %0.2fs', :killtime ],
|
26
28
|
[:info, 'Overhead: %0.2f%%', :overhead_percent ],
|
@@ -6,7 +6,7 @@ module Mutant
|
|
6
6
|
class Printer
|
7
7
|
# Reporter for mutation results
|
8
8
|
class IsolationResult < self
|
9
|
-
|
9
|
+
PROCESS_ERROR_MESSAGE = <<~'MESSAGE'
|
10
10
|
Killfork exited nonzero. Its result (if any) was ignored.
|
11
11
|
Process status:
|
12
12
|
%s
|
@@ -36,40 +36,24 @@ module Mutant
|
|
36
36
|
```
|
37
37
|
MESSAGE
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
This meant that either the RubyVM or your OS was under too much
|
42
|
-
pressure to add another child process.
|
43
|
-
|
44
|
-
Possible solutions are:
|
45
|
-
* Reduce concurrency
|
46
|
-
* Reduce locks
|
39
|
+
TIMEOUT_ERROR_MESSAGE =<<~'MESSAGE'
|
40
|
+
Mutation analysis ran into the configured timeout of %0.9<timeout>g seconds.
|
47
41
|
MESSAGE
|
48
42
|
|
49
|
-
MAP = {
|
50
|
-
Isolation::Fork::ChildError => :visit_child_error,
|
51
|
-
Isolation::Fork::ForkError => :visit_fork_error,
|
52
|
-
Isolation::Result::ErrorChain => :visit_chain,
|
53
|
-
Isolation::Result::Exception => :visit_exception,
|
54
|
-
Isolation::Result::Success => :visit_success
|
55
|
-
}.freeze
|
56
|
-
|
57
43
|
private_constant(*constants(false))
|
58
44
|
|
59
45
|
# Run report printer
|
60
46
|
#
|
61
47
|
# @return [undefined]
|
62
48
|
def run
|
49
|
+
print_timeout
|
50
|
+
print_process_status
|
63
51
|
print_log_messages
|
64
|
-
|
52
|
+
print_exception
|
65
53
|
end
|
66
54
|
|
67
55
|
private
|
68
56
|
|
69
|
-
def visit_success
|
70
|
-
visit(TestResult, object.value)
|
71
|
-
end
|
72
|
-
|
73
57
|
def print_log_messages
|
74
58
|
log = object.log
|
75
59
|
|
@@ -82,16 +66,23 @@ module Mutant
|
|
82
66
|
end
|
83
67
|
end
|
84
68
|
|
85
|
-
def
|
86
|
-
|
69
|
+
def print_process_status
|
70
|
+
process_status = object.process_status or return
|
71
|
+
|
72
|
+
if process_status.success?
|
73
|
+
puts("Killfork: #{process_status.inspect}")
|
74
|
+
else
|
75
|
+
puts(PROCESS_ERROR_MESSAGE % process_status.inspect)
|
76
|
+
end
|
87
77
|
end
|
88
78
|
|
89
|
-
def
|
90
|
-
|
79
|
+
def print_timeout
|
80
|
+
timeout = object.timeout or return
|
81
|
+
puts(TIMEOUT_ERROR_MESSAGE % { timeout: timeout })
|
91
82
|
end
|
92
83
|
|
93
|
-
def
|
94
|
-
exception = object.
|
84
|
+
def print_exception
|
85
|
+
exception = object.exception or return
|
95
86
|
|
96
87
|
puts(
|
97
88
|
EXCEPTION_ERROR_MESSAGE % [
|
@@ -100,13 +91,6 @@ module Mutant
|
|
100
91
|
]
|
101
92
|
)
|
102
93
|
end
|
103
|
-
|
104
|
-
def visit_chain
|
105
|
-
printer = self.class
|
106
|
-
|
107
|
-
visit(printer, object.value)
|
108
|
-
visit(printer, object.next)
|
109
|
-
end
|
110
94
|
end # IsolationResult
|
111
95
|
end # Printer
|
112
96
|
end # CLI
|
@@ -46,24 +46,23 @@ module Mutant
|
|
46
46
|
This is typically a problem of your specs not passing unmutated.
|
47
47
|
MESSAGE
|
48
48
|
|
49
|
-
|
49
|
+
SEPARATOR = '-----------------------'
|
50
50
|
|
51
51
|
# Run report printer
|
52
52
|
#
|
53
53
|
# @return [undefined]
|
54
54
|
def run
|
55
55
|
puts(mutation.identification)
|
56
|
+
puts(SEPARATOR)
|
56
57
|
print_details
|
57
|
-
puts(
|
58
|
+
puts(SEPARATOR)
|
58
59
|
end
|
59
60
|
|
60
61
|
private
|
61
62
|
|
62
63
|
def print_details
|
64
|
+
visit(IsolationResult, isolation_result)
|
63
65
|
__send__(MAP.fetch(mutation.class))
|
64
|
-
|
65
|
-
puts(FOOTER)
|
66
|
-
visit_isolation_result
|
67
66
|
end
|
68
67
|
|
69
68
|
def evil_details
|
@@ -94,10 +93,6 @@ module Mutant
|
|
94
93
|
info(NEUTRAL_MESSAGE, original_node.inspect, mutation.source)
|
95
94
|
end
|
96
95
|
|
97
|
-
def visit_isolation_result
|
98
|
-
visit(IsolationResult, isolation_result)
|
99
|
-
end
|
100
|
-
|
101
96
|
def original_node
|
102
97
|
mutation.subject.node
|
103
98
|
end
|
@@ -7,7 +7,7 @@ module Mutant
|
|
7
7
|
# Subject result printer
|
8
8
|
class SubjectResult < self
|
9
9
|
|
10
|
-
delegate :subject, :
|
10
|
+
delegate :subject, :uncovered_results, :tests
|
11
11
|
|
12
12
|
# Run report printer
|
13
13
|
#
|
@@ -17,7 +17,7 @@ module Mutant
|
|
17
17
|
tests.each do |test|
|
18
18
|
puts("- #{test.identification}")
|
19
19
|
end
|
20
|
-
visit_collection(
|
20
|
+
visit_collection(CoverageResult, uncovered_results)
|
21
21
|
end
|
22
22
|
|
23
23
|
end # SubjectResult
|
data/lib/mutant/result.rb
CHANGED
@@ -4,8 +4,8 @@ module Mutant
|
|
4
4
|
# Namespace and mixin module for results
|
5
5
|
module Result
|
6
6
|
|
7
|
-
#
|
8
|
-
module
|
7
|
+
# CoverageMetric mixin
|
8
|
+
module CoverageMetric
|
9
9
|
FULL_COVERAGE = Rational(1).freeze
|
10
10
|
private_constant(*constants(false))
|
11
11
|
|
@@ -19,7 +19,7 @@ module Mutant
|
|
19
19
|
Rational(amount_mutations_killed, amount_mutation_results)
|
20
20
|
end
|
21
21
|
end
|
22
|
-
end #
|
22
|
+
end # CoverageMetric
|
23
23
|
|
24
24
|
# Class level mixin
|
25
25
|
module ClassMethods
|
@@ -39,6 +39,13 @@ module Mutant
|
|
39
39
|
end
|
40
40
|
memoize(name)
|
41
41
|
end
|
42
|
+
|
43
|
+
# Delegate a method to child
|
44
|
+
def delegate(name, target)
|
45
|
+
define_method(name) do
|
46
|
+
public_send(target).public_send(name)
|
47
|
+
end
|
48
|
+
end
|
42
49
|
end # ClassMethods
|
43
50
|
|
44
51
|
private_constant(*constants(false))
|
@@ -68,7 +75,7 @@ module Mutant
|
|
68
75
|
|
69
76
|
# Env result object
|
70
77
|
class Env
|
71
|
-
include
|
78
|
+
include CoverageMetric, Result, Anima.new(
|
72
79
|
:env,
|
73
80
|
:runtime,
|
74
81
|
:subject_results
|
@@ -92,6 +99,7 @@ module Mutant
|
|
92
99
|
sum :amount_mutation_results, :subject_results
|
93
100
|
sum :amount_mutations_alive, :subject_results
|
94
101
|
sum :amount_mutations_killed, :subject_results
|
102
|
+
sum :amount_timeouts, :subject_results
|
95
103
|
sum :killtime, :subject_results
|
96
104
|
|
97
105
|
# Amount of mutations
|
@@ -114,7 +122,6 @@ module Mutant
|
|
114
122
|
# Test result
|
115
123
|
class Test
|
116
124
|
include Result, Anima.new(
|
117
|
-
:output,
|
118
125
|
:passed,
|
119
126
|
:runtime,
|
120
127
|
:tests
|
@@ -128,7 +135,6 @@ module Mutant
|
|
128
135
|
# @return [undefined]
|
129
136
|
def initialize
|
130
137
|
super(
|
131
|
-
output: '',
|
132
138
|
passed: false,
|
133
139
|
runtime: 0.0,
|
134
140
|
tests: []
|
@@ -139,35 +145,42 @@ module Mutant
|
|
139
145
|
|
140
146
|
# Subject result
|
141
147
|
class Subject
|
142
|
-
include
|
143
|
-
:
|
148
|
+
include CoverageMetric, Result, Anima.new(
|
149
|
+
:coverage_results,
|
144
150
|
:subject,
|
145
151
|
:tests
|
146
152
|
)
|
147
153
|
|
148
|
-
sum :killtime, :
|
149
|
-
sum :runtime, :
|
154
|
+
sum :killtime, :coverage_results
|
155
|
+
sum :runtime, :coverage_results
|
150
156
|
|
151
157
|
# Test if subject was processed successful
|
152
158
|
#
|
153
159
|
# @return [Boolean]
|
154
160
|
def success?
|
155
|
-
|
161
|
+
uncovered_results.empty?
|
156
162
|
end
|
157
163
|
|
158
164
|
# Alive mutations
|
159
165
|
#
|
160
|
-
# @return [Array<Result::
|
161
|
-
def
|
162
|
-
|
166
|
+
# @return [Array<Result::Coverage>]
|
167
|
+
def uncovered_results
|
168
|
+
coverage_results.reject(&:success?)
|
163
169
|
end
|
164
|
-
memoize :
|
170
|
+
memoize :uncovered_results
|
165
171
|
|
166
172
|
# Amount of mutations
|
167
173
|
#
|
168
174
|
# @return [Integer]
|
169
175
|
def amount_mutation_results
|
170
|
-
|
176
|
+
coverage_results.length
|
177
|
+
end
|
178
|
+
|
179
|
+
# Amount of mutations
|
180
|
+
#
|
181
|
+
# @return [Integer]
|
182
|
+
def amount_timeouts
|
183
|
+
coverage_results.count(&:timeout?)
|
171
184
|
end
|
172
185
|
|
173
186
|
# Amount of mutations
|
@@ -181,25 +194,49 @@ module Mutant
|
|
181
194
|
#
|
182
195
|
# @return [Integer]
|
183
196
|
def amount_mutations_killed
|
184
|
-
|
197
|
+
covered_results.length
|
185
198
|
end
|
186
199
|
|
187
200
|
# Number of alive mutations
|
188
201
|
#
|
189
202
|
# @return [Integer]
|
190
203
|
def amount_mutations_alive
|
191
|
-
|
204
|
+
uncovered_results.length
|
192
205
|
end
|
193
206
|
|
194
207
|
private
|
195
208
|
|
196
|
-
def
|
197
|
-
|
209
|
+
def covered_results
|
210
|
+
coverage_results.select(&:success?)
|
198
211
|
end
|
199
|
-
memoize :
|
212
|
+
memoize :covered_results
|
200
213
|
|
201
214
|
end # Subject
|
202
215
|
|
216
|
+
# Coverage of a mutation against criteria
|
217
|
+
class Coverage
|
218
|
+
include Result, Anima.new(
|
219
|
+
:mutation_result,
|
220
|
+
:criteria_result
|
221
|
+
)
|
222
|
+
|
223
|
+
delegate :killtime, :mutation_result
|
224
|
+
delegate :runtime, :mutation_result
|
225
|
+
delegate :success?, :criteria_result
|
226
|
+
delegate :timeout?, :mutation_result
|
227
|
+
end # Coverage
|
228
|
+
|
229
|
+
class CoverageCriteria
|
230
|
+
include Result, Anima.new(*Config::CoverageCriteria.anima.attribute_names)
|
231
|
+
|
232
|
+
# Test if one coverage criteria indicates success
|
233
|
+
#
|
234
|
+
# @return [Boolean]
|
235
|
+
def success?
|
236
|
+
test_result || timeout
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
203
240
|
# Mutation result
|
204
241
|
class Mutation
|
205
242
|
include Result, Anima.new(
|
@@ -208,25 +245,39 @@ module Mutant
|
|
208
245
|
:runtime
|
209
246
|
)
|
210
247
|
|
248
|
+
# Create mutation criteria results
|
249
|
+
#
|
250
|
+
# @praam [Result::CoverageCriteria]
|
251
|
+
def criteria_result(coverage_criteria)
|
252
|
+
CoverageCriteria.new(
|
253
|
+
test_result: coverage_criteria.test_result && test_result_success?,
|
254
|
+
timeout: coverage_criteria.timeout && timeout?
|
255
|
+
)
|
256
|
+
end
|
257
|
+
|
211
258
|
# Time the tests had been running
|
212
259
|
#
|
213
260
|
# @return [Float]
|
214
261
|
def killtime
|
215
|
-
|
216
|
-
isolation_result.value.runtime
|
217
|
-
else
|
218
|
-
0.0
|
219
|
-
end
|
262
|
+
isolation_result.value&.runtime || 0.0
|
220
263
|
end
|
221
264
|
|
265
|
+
# Test for timeout
|
266
|
+
#
|
267
|
+
# @return [Boolean]
|
268
|
+
def timeout?
|
269
|
+
!isolation_result.timeout.nil?
|
270
|
+
end
|
271
|
+
|
272
|
+
private
|
273
|
+
|
222
274
|
# Test if mutation was handled successfully
|
223
275
|
#
|
224
276
|
# @return [Boolean]
|
225
|
-
def
|
226
|
-
isolation_result.
|
227
|
-
mutation.class.success?(isolation_result.value)
|
277
|
+
def test_result_success?
|
278
|
+
isolation_result.valid_value? && mutation.class.success?(isolation_result.value)
|
228
279
|
end
|
229
|
-
memoize :
|
280
|
+
memoize :test_result_success?
|
230
281
|
|
231
282
|
end # Mutation
|
232
283
|
end # Result
|