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
data/lib/mutant/result.rb
CHANGED
@@ -28,7 +28,7 @@ module Mutant
|
|
28
28
|
def self.included(host)
|
29
29
|
super
|
30
30
|
|
31
|
-
host.memoize
|
31
|
+
host.memoize(:coverage)
|
32
32
|
end
|
33
33
|
|
34
34
|
end # Coverage
|
@@ -52,7 +52,7 @@ module Mutant
|
|
52
52
|
define_method(name) do
|
53
53
|
public_send(collection).map(&name).reduce(0, :+)
|
54
54
|
end
|
55
|
-
memoize
|
55
|
+
memoize(name)
|
56
56
|
end
|
57
57
|
end # ClassMethods
|
58
58
|
|
@@ -248,7 +248,7 @@ module Mutant
|
|
248
248
|
|
249
249
|
# Mutation result
|
250
250
|
class Mutation
|
251
|
-
include Result, Anima.new(:mutation, :test_result
|
251
|
+
include Result, Anima.new(:mutation, :test_result)
|
252
252
|
|
253
253
|
# Return runtime
|
254
254
|
#
|
data/lib/mutant/runner.rb
CHANGED
@@ -22,9 +22,6 @@ module Mutant
|
|
22
22
|
include Adamantium::Flat, Anima.new(:job, :result)
|
23
23
|
end
|
24
24
|
|
25
|
-
REPORT_FREQUENCY = 20.0
|
26
|
-
REPORT_DELAY = 1 / REPORT_FREQUENCY
|
27
|
-
|
28
25
|
# Initialize object
|
29
26
|
#
|
30
27
|
# @return [undefined]
|
@@ -37,7 +34,7 @@ module Mutant
|
|
37
34
|
reporter.start(env)
|
38
35
|
config.integration.setup
|
39
36
|
|
40
|
-
@master = config.actor_env.
|
37
|
+
@master = config.actor_env.new_mailbox.bind(Master.call(env))
|
41
38
|
|
42
39
|
status = nil
|
43
40
|
|
@@ -45,7 +42,7 @@ module Mutant
|
|
45
42
|
status = current_status
|
46
43
|
break if status.done
|
47
44
|
reporter.progress(status)
|
48
|
-
Kernel.sleep(
|
45
|
+
Kernel.sleep(reporter.delay)
|
49
46
|
end
|
50
47
|
|
51
48
|
reporter.progress(status)
|
data/lib/mutant/runner/worker.rb
CHANGED
@@ -17,7 +17,7 @@ module Mutant
|
|
17
17
|
def self.run(attributes)
|
18
18
|
attributes.fetch(:config).actor_env.spawn do |actor|
|
19
19
|
worker = new(attributes)
|
20
|
-
worker.
|
20
|
+
worker.__send__(:run, actor)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -67,22 +67,20 @@ module Mutant
|
|
67
67
|
# @api private
|
68
68
|
#
|
69
69
|
def handle_job(job)
|
70
|
-
parent.call(Actor::Message.new(:result, JobResult.new(job: job, result: run_mutation(job))))
|
70
|
+
parent.call(Actor::Message.new(:result, JobResult.new(job: job, result: run_mutation(job.mutation))))
|
71
71
|
end
|
72
72
|
|
73
73
|
# Run mutation
|
74
74
|
#
|
75
75
|
# @param [Mutation] mutation
|
76
76
|
#
|
77
|
-
# @return [
|
77
|
+
# @return [Result::Mutation]
|
78
78
|
#
|
79
79
|
# @api private
|
80
80
|
#
|
81
|
-
def run_mutation(
|
82
|
-
mutation = job.mutation
|
81
|
+
def run_mutation(mutation)
|
83
82
|
test_result = mutation.kill(config.isolation, config.integration)
|
84
83
|
Result::Mutation.new(
|
85
|
-
index: job.index,
|
86
84
|
mutation: mutation,
|
87
85
|
test_result: test_result
|
88
86
|
)
|
data/lib/mutant/subject.rb
CHANGED
@@ -13,7 +13,9 @@ module Mutant
|
|
13
13
|
#
|
14
14
|
def mutations
|
15
15
|
mutations = [neutral_mutation]
|
16
|
-
|
16
|
+
Mutator.each(node) do |mutant|
|
17
|
+
mutations << Mutation::Evil.new(self, wrap_node(mutant))
|
18
|
+
end
|
17
19
|
mutations
|
18
20
|
end
|
19
21
|
memoize :mutations
|
@@ -56,6 +58,18 @@ module Mutant
|
|
56
58
|
self
|
57
59
|
end
|
58
60
|
|
61
|
+
# Return source lines
|
62
|
+
#
|
63
|
+
# @return [Range<Fixnum>]
|
64
|
+
#
|
65
|
+
# @api private
|
66
|
+
#
|
67
|
+
def source_lines
|
68
|
+
expression = node.location.expression
|
69
|
+
expression.line..expression.source_buffer.decompose_position(expression.end_pos).first
|
70
|
+
end
|
71
|
+
memoize :source_lines
|
72
|
+
|
59
73
|
# Return source line
|
60
74
|
#
|
61
75
|
# @return [Fixnum]
|
@@ -63,7 +77,7 @@ module Mutant
|
|
63
77
|
# @api private
|
64
78
|
#
|
65
79
|
def source_line
|
66
|
-
|
80
|
+
source_lines.begin
|
67
81
|
end
|
68
82
|
|
69
83
|
# Return subject identification
|
@@ -84,7 +98,7 @@ module Mutant
|
|
84
98
|
# @api private
|
85
99
|
#
|
86
100
|
def source
|
87
|
-
Unparser.unparse(node)
|
101
|
+
Unparser.unparse(wrap_node(node))
|
88
102
|
end
|
89
103
|
memoize :source
|
90
104
|
|
@@ -113,21 +127,19 @@ module Mutant
|
|
113
127
|
# @api private
|
114
128
|
#
|
115
129
|
def neutral_mutation
|
116
|
-
Mutation::Neutral.new(self, node)
|
130
|
+
Mutation::Neutral.new(self, wrap_node(node))
|
117
131
|
end
|
118
132
|
|
119
|
-
#
|
133
|
+
# Wrap node into subject specific container
|
120
134
|
#
|
121
|
-
# @param [
|
135
|
+
# @param [Parser::AST::Node] node
|
122
136
|
#
|
123
|
-
# @return [
|
137
|
+
# @return [Parser::AST::Node]
|
124
138
|
#
|
125
139
|
# @api private
|
126
140
|
#
|
127
|
-
def
|
128
|
-
|
129
|
-
emitter << Mutation::Evil.new(self, mutant)
|
130
|
-
end
|
141
|
+
def wrap_node(node)
|
142
|
+
node
|
131
143
|
end
|
132
144
|
|
133
145
|
end # Subject
|
@@ -25,7 +25,7 @@ module Mutant
|
|
25
25
|
# @api private
|
26
26
|
#
|
27
27
|
def prepare
|
28
|
-
scope.
|
28
|
+
scope.__send__(:undef_method, name)
|
29
29
|
self
|
30
30
|
end
|
31
31
|
|
@@ -33,17 +33,6 @@ module Mutant
|
|
33
33
|
class Memoized < self
|
34
34
|
include AST::Sexp
|
35
35
|
|
36
|
-
# Return source
|
37
|
-
#
|
38
|
-
# @return [String]
|
39
|
-
#
|
40
|
-
# @api private
|
41
|
-
#
|
42
|
-
def source
|
43
|
-
Unparser.unparse(memoizer_node(node))
|
44
|
-
end
|
45
|
-
memoize :source
|
46
|
-
|
47
36
|
# Prepare subject for mutation insertion
|
48
37
|
#
|
49
38
|
# @return [self]
|
@@ -51,37 +40,13 @@ module Mutant
|
|
51
40
|
# @api private
|
52
41
|
#
|
53
42
|
def prepare
|
54
|
-
scope.
|
43
|
+
scope.__send__(:memoized_methods).instance_variable_get(:@memory).delete(name)
|
55
44
|
super
|
56
45
|
self
|
57
46
|
end
|
58
47
|
|
59
48
|
private
|
60
49
|
|
61
|
-
# Return mutations
|
62
|
-
#
|
63
|
-
# @param [#<<] emitter
|
64
|
-
#
|
65
|
-
# @return [undefined]
|
66
|
-
#
|
67
|
-
# @api private
|
68
|
-
#
|
69
|
-
def generate_mutations(emitter)
|
70
|
-
Mutator.each(node) do |mutant|
|
71
|
-
emitter << Mutation::Evil.new(self, memoizer_node(mutant))
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
# Return neutral mutation
|
76
|
-
#
|
77
|
-
# @return [Mutation::Neutral]
|
78
|
-
#
|
79
|
-
# @api private
|
80
|
-
#
|
81
|
-
def neutral_mutation
|
82
|
-
Mutation::Neutral.new(self, memoizer_node(node))
|
83
|
-
end
|
84
|
-
|
85
50
|
# Return memoizer node for mutant
|
86
51
|
#
|
87
52
|
# @param [Parser::AST::Node] mutant
|
@@ -90,7 +55,7 @@ module Mutant
|
|
90
55
|
#
|
91
56
|
# @api private
|
92
57
|
#
|
93
|
-
def
|
58
|
+
def wrap_node(mutant)
|
94
59
|
s(:begin, mutant, s(:send, nil, :memoize, s(:args, s(:sym, name))))
|
95
60
|
end
|
96
61
|
|
data/lib/mutant/version.rb
CHANGED
data/mutant.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |gem|
|
|
21
21
|
gem.extra_rdoc_files = %w[TODO LICENSE]
|
22
22
|
gem.executables = %w[mutant]
|
23
23
|
|
24
|
-
gem.required_ruby_version = '>=
|
24
|
+
gem.required_ruby_version = '>= 2.0.0'
|
25
25
|
|
26
26
|
gem.add_runtime_dependency('parser', '~> 2.2.pre.7')
|
27
27
|
gem.add_runtime_dependency('ast', '~> 2.0')
|
data/spec/spec_helper.rb
CHANGED
@@ -11,6 +11,8 @@ if ENV['COVERAGE'] == 'true'
|
|
11
11
|
add_filter 'lib/mutant/meta/*'
|
12
12
|
add_filter 'lib/mutant/zombifier'
|
13
13
|
add_filter 'lib/mutant/zombifier/*'
|
14
|
+
# Trace points shadow each other under 2.0 (fixed in 2.1)
|
15
|
+
add_filter 'lib/mutant/line_trace.rb' if RUBY_VERSION.eql?('2.0.0')
|
14
16
|
|
15
17
|
minimum_coverage 100
|
16
18
|
end
|
data/spec/support/fake_actor.rb
CHANGED
@@ -40,26 +40,32 @@ module FakeActor
|
|
40
40
|
end
|
41
41
|
|
42
42
|
class Env
|
43
|
-
include Concord.new(:messages, :
|
43
|
+
include Concord.new(:messages, :mailbox_names)
|
44
44
|
|
45
45
|
def spawn
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
yield actor if block_given?
|
50
|
-
actor.sender
|
46
|
+
mailbox = mailbox(next_name)
|
47
|
+
yield mailbox if block_given?
|
48
|
+
mailbox.sender
|
51
49
|
end
|
52
50
|
|
53
|
-
def
|
54
|
-
|
51
|
+
def mailbox(name)
|
52
|
+
Mailbox.new(name, @messages)
|
55
53
|
end
|
56
54
|
|
57
|
-
def
|
58
|
-
|
55
|
+
def new_mailbox
|
56
|
+
mailbox(:current)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def next_name
|
62
|
+
@mailbox_names.shift.tap do |name|
|
63
|
+
name or fail 'Tried to spawn actor when no name available'
|
64
|
+
end
|
59
65
|
end
|
60
66
|
end # Env
|
61
67
|
|
62
|
-
class
|
68
|
+
class Mailbox
|
63
69
|
include Concord.new(:name, :messages)
|
64
70
|
|
65
71
|
def receiver
|
@@ -85,7 +85,6 @@ module SharedContext
|
|
85
85
|
|
86
86
|
let(:mutation_a_result) do
|
87
87
|
Mutant::Result::Mutation.new(
|
88
|
-
index: 1,
|
89
88
|
mutation: mutation_a,
|
90
89
|
test_result: mutation_a_test_result
|
91
90
|
)
|
@@ -93,7 +92,6 @@ module SharedContext
|
|
93
92
|
|
94
93
|
let(:mutation_b_result) do
|
95
94
|
Mutant::Result::Mutation.new(
|
96
|
-
index: 1,
|
97
95
|
mutation: mutation_a,
|
98
96
|
test_result: mutation_b_test_result
|
99
97
|
)
|
@@ -1,32 +1,13 @@
|
|
1
1
|
RSpec.describe Mutant::Actor::Env do
|
2
|
-
let(:
|
3
|
-
let(:
|
4
|
-
let(:thread_root) { double('Thread Root') }
|
5
|
-
let(:actor) { Mutant::Actor::Actor.new(thread, mailbox) }
|
2
|
+
let(:thread) { double('Thread') }
|
3
|
+
let(:thread_root) { double('Thread Root') }
|
6
4
|
|
7
5
|
let(:object) { described_class.new(thread_root) }
|
8
6
|
|
9
|
-
before do
|
10
|
-
expect(Mutex).to receive(:new).and_return(mutex)
|
11
|
-
end
|
12
|
-
|
13
|
-
describe '#current' do
|
14
|
-
subject { object.current }
|
15
|
-
|
16
|
-
let!(:mailbox) { Mutant::Actor::Mailbox.new }
|
17
|
-
|
18
|
-
before do
|
19
|
-
expect(Mutant::Actor::Mailbox).to receive(:new).and_return(mailbox).ordered
|
20
|
-
expect(thread_root).to receive(:current).and_return(thread)
|
21
|
-
end
|
22
|
-
|
23
|
-
it { should eql(actor) }
|
24
|
-
end
|
25
|
-
|
26
7
|
describe '#spawn' do
|
27
8
|
subject { object.spawn(&block) }
|
28
9
|
|
29
|
-
let!(:mailbox)
|
10
|
+
let!(:mailbox) { Mutant::Actor::Mailbox.new }
|
30
11
|
|
31
12
|
let(:yields) { [] }
|
32
13
|
|
@@ -35,15 +16,14 @@ RSpec.describe Mutant::Actor::Env do
|
|
35
16
|
before do
|
36
17
|
expect(Mutant::Actor::Mailbox).to receive(:new).and_return(mailbox).ordered
|
37
18
|
expect(thread_root).to receive(:new).and_yield.and_return(thread).ordered
|
38
|
-
expect(thread_root).to receive(:current).and_return(thread).ordered
|
39
19
|
end
|
40
20
|
|
41
21
|
it 'returns sender' do
|
42
|
-
should
|
22
|
+
should be(mailbox.sender)
|
43
23
|
end
|
44
24
|
|
45
25
|
it 'yields actor' do
|
46
|
-
expect { subject }.to change { yields }.from([]).to([
|
26
|
+
expect { subject }.to change { yields }.from([]).to([mailbox])
|
47
27
|
end
|
48
28
|
end
|
49
29
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
RSpec.describe Mutant::Actor::Mailbox do
|
2
|
+
let(:mutex) { double('Mutex') }
|
3
|
+
let(:condition_variable) { double('Mutex') }
|
4
|
+
|
5
|
+
before do
|
6
|
+
allow(Mutex).to receive(:new).and_return(mutex)
|
7
|
+
allow(ConditionVariable).to receive(:new).and_return(condition_variable)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '.new' do
|
11
|
+
subject { described_class.new }
|
12
|
+
|
13
|
+
let(:object) { described_class.new }
|
14
|
+
let(:thread) { double('Thread') }
|
15
|
+
|
16
|
+
its(:sender) { should eql(Mutant::Actor::Sender.new(condition_variable, mutex, [])) }
|
17
|
+
its(:receiver) { should eql(Mutant::Actor::Receiver.new(condition_variable, mutex, [])) }
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#bind' do
|
22
|
+
let(:object) { described_class.new }
|
23
|
+
let(:other) { double('Sender') }
|
24
|
+
|
25
|
+
subject { object.bind(other) }
|
26
|
+
|
27
|
+
it { should eql(Mutant::Actor::Binding.new(object, other)) }
|
28
|
+
end
|
29
|
+
end
|
@@ -1,36 +1,34 @@
|
|
1
1
|
RSpec.describe Mutant::Actor::Receiver do
|
2
|
-
let(:
|
3
|
-
let(:mutex)
|
4
|
-
let(:
|
2
|
+
let(:messages) { double('Messages') }
|
3
|
+
let(:mutex) { double('Mutex') }
|
4
|
+
let(:condition_variable) { double('Condition Variable') }
|
5
|
+
let(:message) { double('Message') }
|
5
6
|
|
6
|
-
let(:object) { described_class.new(mutex,
|
7
|
+
let(:object) { described_class.new(condition_variable, mutex, messages) }
|
7
8
|
|
8
9
|
describe '#call' do
|
9
10
|
subject { object.call }
|
10
11
|
|
11
|
-
context 'when
|
12
|
+
context 'when messages contains a message' do
|
12
13
|
before do
|
13
|
-
expect(mutex).to receive(:
|
14
|
-
expect(
|
15
|
-
expect(
|
16
|
-
expect(mutex).to receive(:unlock).ordered
|
14
|
+
expect(mutex).to receive(:synchronize).and_yield.ordered
|
15
|
+
expect(messages).to receive(:empty?).and_return(false).ordered
|
16
|
+
expect(messages).to receive(:shift).and_return(message).ordered
|
17
17
|
end
|
18
18
|
|
19
19
|
it { should be(message) }
|
20
20
|
end
|
21
21
|
|
22
|
-
context 'when
|
22
|
+
context 'when messages initially contains no message' do
|
23
23
|
before do
|
24
24
|
# 1rst failing try
|
25
|
-
expect(mutex).to receive(:
|
26
|
-
expect(
|
27
|
-
expect(
|
28
|
-
expect(Thread).to receive(:stop).ordered
|
25
|
+
expect(mutex).to receive(:synchronize).and_yield.ordered
|
26
|
+
expect(messages).to receive(:empty?).and_return(true).ordered
|
27
|
+
expect(condition_variable).to receive(:wait).with(mutex).ordered
|
29
28
|
# 2nd successful try
|
30
|
-
expect(mutex).to receive(:
|
31
|
-
expect(
|
32
|
-
expect(
|
33
|
-
expect(mutex).to receive(:unlock).ordered
|
29
|
+
expect(mutex).to receive(:synchronize).and_yield.ordered
|
30
|
+
expect(messages).to receive(:empty?).and_return(false).ordered
|
31
|
+
expect(messages).to receive(:shift).and_return(message).ordered
|
34
32
|
end
|
35
33
|
|
36
34
|
it 'waits for message' do
|
@@ -38,21 +36,19 @@ RSpec.describe Mutant::Actor::Receiver do
|
|
38
36
|
end
|
39
37
|
end
|
40
38
|
|
41
|
-
context 'when
|
39
|
+
context 'when messages contains no message but thread gets waken without message arrived' do
|
42
40
|
before do
|
43
41
|
# 1rst failing try
|
44
|
-
expect(mutex).to receive(:
|
45
|
-
expect(
|
46
|
-
expect(
|
47
|
-
expect(Thread).to receive(:stop).ordered
|
42
|
+
expect(mutex).to receive(:synchronize).and_yield.ordered
|
43
|
+
expect(messages).to receive(:empty?).and_return(true).ordered
|
44
|
+
expect(condition_variable).to receive(:wait).with(mutex).ordered
|
48
45
|
# 2nd failing try
|
49
|
-
expect(mutex).to receive(:
|
50
|
-
expect(
|
51
|
-
expect(
|
52
|
-
expect(Thread).to receive(:stop).ordered
|
46
|
+
expect(mutex).to receive(:synchronize).and_yield.ordered
|
47
|
+
expect(messages).to receive(:empty?).and_return(true).ordered
|
48
|
+
expect(condition_variable).to receive(:wait).with(mutex).ordered
|
53
49
|
end
|
54
50
|
|
55
|
-
it '
|
51
|
+
it 'fails with error' do
|
56
52
|
expect { subject }.to raise_error(Mutant::Actor::ProtocolError)
|
57
53
|
end
|
58
54
|
end
|