mutant 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|