mutant 0.7.1 → 0.7.2
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/.travis.yml +0 -1
- data/Changelog.md +5 -0
- data/TODO +0 -8
- data/config/flay.yml +1 -1
- data/config/reek.yml +4 -1
- data/lib/mutant.rb +1 -1
- data/lib/mutant/actor/env.rb +8 -8
- data/lib/mutant/actor/mailbox.rb +16 -31
- data/lib/mutant/actor/receiver.rb +7 -9
- data/lib/mutant/actor/sender.rb +3 -3
- data/lib/mutant/line_trace.rb +34 -0
- data/lib/mutant/reporter/cli.rb +17 -0
- data/lib/mutant/reporter/cli/format.rb +16 -17
- data/lib/mutant/reporter/cli/printer.rb +64 -17
- data/lib/mutant/reporter/trace.rb +12 -0
- data/lib/mutant/result.rb +3 -3
- data/lib/mutant/runner.rb +2 -5
- data/lib/mutant/runner/worker.rb +4 -6
- data/lib/mutant/subject.rb +23 -11
- data/lib/mutant/subject/method/instance.rb +3 -38
- data/lib/mutant/subject/method/singleton.rb +1 -1
- data/lib/mutant/version.rb +1 -1
- data/mutant.gemspec +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/support/fake_actor.rb +17 -11
- data/spec/support/shared_context.rb +0 -2
- data/spec/unit/mutant/actor/env_spec.rb +5 -25
- data/spec/unit/mutant/actor/mailbox_spec.rb +29 -0
- data/spec/unit/mutant/actor/receiver_spec.rb +24 -28
- data/spec/unit/mutant/actor/sender_spec.rb +9 -9
- data/spec/unit/mutant/line_trace_spec.rb +38 -0
- data/spec/unit/mutant/reporter/cli_spec.rb +154 -157
- data/spec/unit/mutant/runner/master_spec.rb +11 -11
- data/spec/unit/mutant/runner/worker_spec.rb +2 -3
- data/spec/unit/mutant/runner_spec.rb +13 -10
- data/spec/unit/mutant/subject_spec.rb +17 -2
- metadata +51 -50
- data/lib/mutant/actor/actor.rb +0 -50
- data/spec/unit/mutant/actor/actor_spec.rb +0 -35
- data/spec/unit/mutant/mailbox_spec.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90851c1c4c4d7c0f23438bae72fe95f70a680514
|
4
|
+
data.tar.gz: 7ac31c28d9329ec976bfac638a48a8fe515e3604
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab96c53e2d7ded947c58742c06bb5934b1b0f17437fefbe9e510d9ce25a7aea422d115aa420fc0538bd23260b2fd85c20d4aa25cb84e8f82362f08d8156f28c5
|
7
|
+
data.tar.gz: db2ace76cbabbb97d18a0a32cfc75f7ad3a014424bf4b3ae52b566f38bf47fe3ff8929edcaf31776390fc52bc9c5a2d42323c6be26fc8c617067179977edeb9a
|
data/.travis.yml
CHANGED
data/Changelog.md
CHANGED
data/TODO
CHANGED
@@ -1,23 +1,15 @@
|
|
1
1
|
Code:
|
2
2
|
* Test mutant with dynamically created zombie.
|
3
|
-
* Log all warnings through reporter, so remove random $stderr.puts calls
|
4
3
|
|
5
4
|
Mutations:
|
6
5
|
* Add true masgn mutations
|
7
|
-
* Add binary operator specific mutations (YAY, finally reached this point)
|
8
6
|
* Add some kind of a "do not touch me object" that raises on all messages.
|
9
7
|
It can be used to make sure each literal value is touched.
|
10
8
|
* Replace nil or add "do not touch me object" to literal mutations.
|
11
9
|
* Mutate options on Regexp literals
|
12
10
|
* Add mutations for dynamic regexp symbol and string literals
|
13
|
-
* Mutate Block catch "def foo(&block)" and block pass "foo(&block)"
|
14
|
-
* Binary operator mutations
|
15
11
|
* Add timeout to terminate infinite loops
|
16
12
|
|
17
|
-
Example of a negative mutation:
|
18
|
-
Mutations on local variables and arguments prefixed with an underscore would be emitted as
|
19
|
-
negative mutations that must be alive.
|
20
|
-
|
21
13
|
Loader:
|
22
14
|
* Make sure loader does not change visibility of injected mutants
|
23
15
|
|
data/config/flay.yml
CHANGED
data/config/reek.yml
CHANGED
@@ -37,6 +37,7 @@ FeatureEnvy:
|
|
37
37
|
- Mutant::Runner::Master#stop_worker
|
38
38
|
- Mutant::Runner::Worker#run_mutation
|
39
39
|
- Mutant::Runner::Worker#handle
|
40
|
+
- Mutant::Subject#source_lines
|
40
41
|
IrresponsibleModule:
|
41
42
|
enabled: true
|
42
43
|
exclude: []
|
@@ -136,9 +137,11 @@ UnusedParameters:
|
|
136
137
|
UtilityFunction:
|
137
138
|
enabled: true
|
138
139
|
exclude:
|
140
|
+
- Mutant::Actor::Env#new_mailbox
|
139
141
|
- Mutant::AST::Sexp#s
|
140
142
|
- Mutant::CLI#reporter
|
141
143
|
- Mutant::Integration::Rspec#parse_example
|
142
|
-
- Mutant::Meta::Example::Verification#format_mutation
|
144
|
+
- Mutant::Meta::Example::Verification#format_mutation
|
143
145
|
- Mutant::Reporter::CLI::Format::Progressive#new_buffer
|
146
|
+
- Mutant::Reporter::CLI::Printer::StatusProgressive#object # False positive calls super
|
144
147
|
max_helper_calls: 0
|
data/lib/mutant.rb
CHANGED
@@ -104,7 +104,6 @@ require 'mutant/actor'
|
|
104
104
|
require 'mutant/actor/receiver'
|
105
105
|
require 'mutant/actor/sender'
|
106
106
|
require 'mutant/actor/mailbox'
|
107
|
-
require 'mutant/actor/actor'
|
108
107
|
require 'mutant/actor/env'
|
109
108
|
require 'mutant/cache'
|
110
109
|
require 'mutant/delegator'
|
@@ -212,6 +211,7 @@ require 'mutant/reporter/cli'
|
|
212
211
|
require 'mutant/reporter/cli/printer'
|
213
212
|
require 'mutant/reporter/cli/tput'
|
214
213
|
require 'mutant/reporter/cli/format'
|
214
|
+
require 'mutant/line_trace'
|
215
215
|
require 'mutant/zombifier'
|
216
216
|
require 'mutant/zombifier/file'
|
217
217
|
|
data/lib/mutant/actor/env.rb
CHANGED
@@ -11,23 +11,23 @@ module Mutant
|
|
11
11
|
# @api private
|
12
12
|
#
|
13
13
|
def spawn
|
14
|
-
mailbox =
|
14
|
+
mailbox = new_mailbox
|
15
15
|
|
16
|
-
|
17
|
-
yield mailbox
|
16
|
+
thread_root.new do
|
17
|
+
yield mailbox
|
18
18
|
end
|
19
19
|
|
20
|
-
mailbox.sender
|
20
|
+
mailbox.sender
|
21
21
|
end
|
22
22
|
|
23
|
-
# Return
|
23
|
+
# Return new unbound mailbox
|
24
24
|
#
|
25
|
-
# @return [
|
25
|
+
# @return [Mailbox]
|
26
26
|
#
|
27
27
|
# @api private
|
28
28
|
#
|
29
|
-
def
|
30
|
-
Mailbox.new
|
29
|
+
def new_mailbox
|
30
|
+
Mailbox.new
|
31
31
|
end
|
32
32
|
|
33
33
|
end # Env
|
data/lib/mutant/actor/mailbox.rb
CHANGED
@@ -2,50 +2,35 @@ module Mutant
|
|
2
2
|
module Actor
|
3
3
|
# Unbound mailbox
|
4
4
|
class Mailbox
|
5
|
+
include Adamantium::Flat, Concord::Public.new(:receiver, :sender)
|
5
6
|
|
6
|
-
#
|
7
|
+
# Return new mailbox
|
7
8
|
#
|
8
|
-
# @return [
|
9
|
+
# @return [Mailbox]
|
9
10
|
#
|
10
11
|
# @api private
|
11
12
|
#
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
freeze
|
17
|
-
end
|
18
|
-
|
19
|
-
# Return receiver
|
20
|
-
#
|
21
|
-
# @return [Receiver]
|
22
|
-
#
|
23
|
-
# @api private
|
24
|
-
#
|
25
|
-
attr_reader :receiver
|
13
|
+
def self.new
|
14
|
+
mutex = Mutex.new
|
15
|
+
condition_variable = ConditionVariable.new
|
16
|
+
messages = []
|
26
17
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
# @return [Actor]
|
32
|
-
#
|
33
|
-
# @api private
|
34
|
-
#
|
35
|
-
def actor(thread)
|
36
|
-
Actor.new(thread, self)
|
18
|
+
super(
|
19
|
+
Receiver.new(condition_variable, mutex, messages),
|
20
|
+
Sender.new(condition_variable, mutex, messages)
|
21
|
+
)
|
37
22
|
end
|
38
23
|
|
39
|
-
# Return
|
24
|
+
# Return binding for RPC to other actors
|
40
25
|
#
|
41
|
-
# @param [
|
26
|
+
# @param [Actor::Sender] other
|
42
27
|
#
|
43
|
-
# @return [
|
28
|
+
# @return [Binding]
|
44
29
|
#
|
45
30
|
# @api private
|
46
31
|
#
|
47
|
-
def
|
48
|
-
|
32
|
+
def bind(other)
|
33
|
+
Binding.new(self, other)
|
49
34
|
end
|
50
35
|
|
51
36
|
end # Mailbox
|
@@ -2,7 +2,7 @@ module Mutant
|
|
2
2
|
module Actor
|
3
3
|
# Receiver side of an actor
|
4
4
|
class Receiver
|
5
|
-
include Concord.new(:mutex, :
|
5
|
+
include Adamantium::Flat, Concord.new(:condition_variable, :mutex, :messages)
|
6
6
|
|
7
7
|
# Receives a message, blocking
|
8
8
|
#
|
@@ -31,14 +31,12 @@ module Mutant
|
|
31
31
|
# @api private
|
32
32
|
#
|
33
33
|
def try_blocking_receive
|
34
|
-
mutex.
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
mailbox.shift.tap do
|
41
|
-
mutex.unlock
|
34
|
+
mutex.synchronize do
|
35
|
+
if messages.empty?
|
36
|
+
condition_variable.wait(mutex)
|
37
|
+
Undefined
|
38
|
+
else
|
39
|
+
messages.shift
|
42
40
|
end
|
43
41
|
end
|
44
42
|
end
|
data/lib/mutant/actor/sender.rb
CHANGED
@@ -3,7 +3,7 @@ module Mutant
|
|
3
3
|
|
4
4
|
# Sender for messages to acting thread
|
5
5
|
class Sender
|
6
|
-
include Concord.new(:
|
6
|
+
include Adamantium::Flat, Concord.new(:condition_variable, :mutex, :messages)
|
7
7
|
|
8
8
|
# Send a message to actor
|
9
9
|
#
|
@@ -15,8 +15,8 @@ module Mutant
|
|
15
15
|
#
|
16
16
|
def call(message)
|
17
17
|
mutex.synchronize do
|
18
|
-
|
19
|
-
|
18
|
+
messages << message
|
19
|
+
condition_variable.signal
|
20
20
|
end
|
21
21
|
|
22
22
|
self
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Mutant
|
2
|
+
# Line tracer
|
3
|
+
class LineTrace
|
4
|
+
include Adamantium::Flat, Concord.new(:contents)
|
5
|
+
|
6
|
+
private_class_method :new
|
7
|
+
|
8
|
+
# Test if trace coveres file at lineno
|
9
|
+
#
|
10
|
+
# @param [String] file
|
11
|
+
# @param [Fixnum] lineno
|
12
|
+
#
|
13
|
+
# @return [Boolean]
|
14
|
+
#
|
15
|
+
def cover?(file, lineno)
|
16
|
+
contents.fetch(file) { return false }.include?(lineno)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Run block
|
20
|
+
#
|
21
|
+
# @return [Traces]
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
#
|
25
|
+
def self.call(&block)
|
26
|
+
traces = Hash.new { |hash, file| hash[file] = Set.new }
|
27
|
+
TracePoint.trace(:return, :line) do |point|
|
28
|
+
traces[point.path] << point.lineno
|
29
|
+
end.tap(&block).disable
|
30
|
+
new(IceNine.deep_freeze(traces))
|
31
|
+
end
|
32
|
+
|
33
|
+
end # LineTrace
|
34
|
+
end # Mutant
|
data/lib/mutant/reporter/cli.rb
CHANGED
@@ -51,6 +51,23 @@ module Mutant
|
|
51
51
|
self
|
52
52
|
end
|
53
53
|
|
54
|
+
# Return report delay in seconds
|
55
|
+
#
|
56
|
+
# TODO: Move this to a callback registration
|
57
|
+
#
|
58
|
+
# Reporters other than CLI that might exist in futures
|
59
|
+
# may only the final report. So providing a noop callback
|
60
|
+
# registration makes more sense than. As only CLI reporters
|
61
|
+
# exist currently I do not really care right now.
|
62
|
+
#
|
63
|
+
# @return [Float]
|
64
|
+
#
|
65
|
+
# @api private
|
66
|
+
#
|
67
|
+
def delay
|
68
|
+
format.delay
|
69
|
+
end
|
70
|
+
|
54
71
|
# Report warning
|
55
72
|
#
|
56
73
|
# @param [String] message
|
@@ -25,6 +25,16 @@ module Mutant
|
|
25
25
|
#
|
26
26
|
abstract_method :progress
|
27
27
|
|
28
|
+
# Return report delay in seconds
|
29
|
+
#
|
30
|
+
# @return [Float]
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
#
|
34
|
+
def delay
|
35
|
+
self.class::REPORT_DELAY
|
36
|
+
end
|
37
|
+
|
28
38
|
# Output abstraction to decouple tty? from buffer
|
29
39
|
class Output
|
30
40
|
include Concord.new(:tty, :buffer)
|
@@ -67,17 +77,8 @@ module Mutant
|
|
67
77
|
# Format for progressive non rewindable output
|
68
78
|
class Progressive < self
|
69
79
|
|
70
|
-
|
71
|
-
|
72
|
-
# @return [undefined]
|
73
|
-
#
|
74
|
-
# @api private
|
75
|
-
#
|
76
|
-
def initialize(*)
|
77
|
-
@seen = Set.new
|
78
|
-
|
79
|
-
super
|
80
|
-
end
|
80
|
+
REPORT_FREQUENCY = 1.0
|
81
|
+
REPORT_DELAY = 1 / REPORT_FREQUENCY
|
81
82
|
|
82
83
|
# Return start representation
|
83
84
|
#
|
@@ -96,12 +97,7 @@ module Mutant
|
|
96
97
|
# @api private
|
97
98
|
#
|
98
99
|
def progress(status)
|
99
|
-
|
100
|
-
new = current.reject(&@seen.method(:include?))
|
101
|
-
@seen = current.to_set
|
102
|
-
new.map do |mutation_result|
|
103
|
-
format(Printer::MutationProgressResult, mutation_result)
|
104
|
-
end.join(EMPTY_STRING)
|
100
|
+
format(Printer::StatusProgressive, status)
|
105
101
|
end
|
106
102
|
|
107
103
|
private
|
@@ -124,6 +120,9 @@ module Mutant
|
|
124
120
|
|
125
121
|
BUFFER_FLAGS = 'a+'.freeze
|
126
122
|
|
123
|
+
REPORT_FREQUENCY = 20.0
|
124
|
+
REPORT_DELAY = 1 / REPORT_FREQUENCY
|
125
|
+
|
127
126
|
# Format start
|
128
127
|
#
|
129
128
|
# @param [Env] env
|
@@ -147,7 +147,7 @@ module Mutant
|
|
147
147
|
#
|
148
148
|
def run
|
149
149
|
visit(EnvProgress, object.env_result)
|
150
|
-
info('Active subjects:
|
150
|
+
info('Active subjects: %d', active_subject_results.length)
|
151
151
|
visit_collection(SubjectProgress, active_subject_results)
|
152
152
|
job_status
|
153
153
|
self
|
@@ -198,12 +198,12 @@ module Mutant
|
|
198
198
|
#
|
199
199
|
def run
|
200
200
|
info 'Mutant configuration:'
|
201
|
-
info 'Matcher:
|
202
|
-
info 'Integration:
|
203
|
-
info 'Expect Coverage:
|
204
|
-
info 'Jobs:
|
205
|
-
info 'Includes:
|
206
|
-
info 'Requires:
|
201
|
+
info 'Matcher: %s', object.matcher_config.inspect
|
202
|
+
info 'Integration: %s', object.integration.name
|
203
|
+
info 'Expect Coverage: %0.2f%%', object.expected_coverage.inspect
|
204
|
+
info 'Jobs: %d', object.jobs
|
205
|
+
info 'Includes: %s', object.includes.inspect
|
206
|
+
info 'Requires: %s', object.requires.inspect
|
207
207
|
self
|
208
208
|
end
|
209
209
|
|
@@ -232,16 +232,15 @@ module Mutant
|
|
232
232
|
#
|
233
233
|
def run
|
234
234
|
visit(Config, env.config)
|
235
|
-
info '
|
236
|
-
info '
|
237
|
-
info '
|
238
|
-
info '
|
239
|
-
info '
|
240
|
-
info '
|
241
|
-
info '
|
242
|
-
|
243
|
-
status '
|
244
|
-
status 'Expected: %0.2f%%', env.config.expected_coverage
|
235
|
+
info 'Subjects: %s', amount_subjects
|
236
|
+
info 'Mutations: %s', amount_mutations
|
237
|
+
info 'Kills: %s', amount_mutations_killed
|
238
|
+
info 'Alive: %s', amount_mutations_alive
|
239
|
+
info 'Runtime: %0.2fs', runtime
|
240
|
+
info 'Killtime: %0.2fs', killtime
|
241
|
+
info 'Overhead: %0.2f%%', overhead_percent
|
242
|
+
status 'Coverage: %0.2f%%', coverage_percent
|
243
|
+
status 'Expected: %0.2f%%', env.config.expected_coverage
|
245
244
|
self
|
246
245
|
end
|
247
246
|
|
@@ -342,6 +341,54 @@ module Mutant
|
|
342
341
|
|
343
342
|
end # MutationProgressResult
|
344
343
|
|
344
|
+
# Reporter for progressive output format on scheduler Status objects
|
345
|
+
class StatusProgressive < self
|
346
|
+
|
347
|
+
FORMAT = '(%02d/%02d) %3d%% - killtime: %0.02fs runtime: %0.02fs overhead: %0.02fs'.freeze
|
348
|
+
|
349
|
+
delegate(
|
350
|
+
:coverage,
|
351
|
+
:runtime,
|
352
|
+
:amount_mutations_killed,
|
353
|
+
:amount_mutations,
|
354
|
+
:amount_mutation_results,
|
355
|
+
:killtime,
|
356
|
+
:overhead
|
357
|
+
)
|
358
|
+
|
359
|
+
# Run printer
|
360
|
+
#
|
361
|
+
# @return [self]
|
362
|
+
#
|
363
|
+
# @api private
|
364
|
+
#
|
365
|
+
def run
|
366
|
+
status(
|
367
|
+
FORMAT,
|
368
|
+
amount_mutations_killed,
|
369
|
+
amount_mutations,
|
370
|
+
coverage * 100,
|
371
|
+
killtime,
|
372
|
+
runtime,
|
373
|
+
overhead
|
374
|
+
)
|
375
|
+
|
376
|
+
self
|
377
|
+
end
|
378
|
+
|
379
|
+
private
|
380
|
+
|
381
|
+
# Return object being printed
|
382
|
+
#
|
383
|
+
# @return [Result::Env]
|
384
|
+
#
|
385
|
+
# @api private
|
386
|
+
#
|
387
|
+
def object
|
388
|
+
super().env_result
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
345
392
|
# Reporter for subject progress
|
346
393
|
class SubjectProgress < self
|
347
394
|
|