mutant 0.6.7 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog.md +10 -0
- data/README.md +1 -1
- data/config/flay.yml +1 -1
- data/config/reek.yml +11 -40
- data/config/rubocop.yml +1 -1
- data/lib/mutant.rb +10 -2
- data/lib/mutant/actor.rb +64 -0
- data/lib/mutant/actor/actor.rb +50 -0
- data/lib/mutant/actor/env.rb +35 -0
- data/lib/mutant/actor/mailbox.rb +53 -0
- data/lib/mutant/actor/receiver.rb +48 -0
- data/lib/mutant/actor/sender.rb +27 -0
- data/lib/mutant/ast/types.rb +1 -1
- data/lib/mutant/cli.rb +2 -0
- data/lib/mutant/config.rb +2 -1
- data/lib/mutant/env.rb +6 -2
- data/lib/mutant/expression/methods.rb +1 -1
- data/lib/mutant/integration.rb +11 -1
- data/lib/mutant/isolation.rb +1 -2
- data/lib/mutant/meta/example.rb +1 -1
- data/lib/mutant/mutation.rb +47 -21
- data/lib/mutant/mutator/node.rb +1 -1
- data/lib/mutant/mutator/node/literal/symbol.rb +1 -1
- data/lib/mutant/mutator/node/send.rb +4 -3
- data/lib/mutant/reporter/cli.rb +2 -0
- data/lib/mutant/reporter/cli/format.rb +23 -36
- data/lib/mutant/reporter/cli/printer.rb +66 -27
- data/lib/mutant/result.rb +45 -58
- data/lib/mutant/runner.rb +47 -154
- data/lib/mutant/runner/master.rb +174 -0
- data/lib/mutant/runner/scheduler.rb +141 -0
- data/lib/mutant/runner/worker.rb +93 -0
- data/lib/mutant/subject/method/instance.rb +1 -15
- data/lib/mutant/test.rb +2 -15
- data/lib/mutant/version.rb +1 -1
- data/meta/send.rb +16 -0
- data/mutant-rspec.gemspec +1 -1
- data/mutant.gemspec +1 -1
- data/spec/integration/mutant/rspec_spec.rb +0 -6
- data/spec/spec_helper.rb +9 -1
- data/spec/support/fake_actor.rb +93 -0
- data/spec/support/shared_context.rb +135 -0
- data/spec/unit/mutant/actor/actor_spec.rb +35 -0
- data/spec/unit/mutant/actor/binding_spec.rb +32 -0
- data/spec/unit/mutant/actor/env_spec.rb +49 -0
- data/spec/unit/mutant/actor/message_spec.rb +23 -0
- data/spec/unit/mutant/actor/receiver_spec.rb +60 -0
- data/spec/unit/mutant/actor/sender_spec.rb +22 -0
- data/spec/unit/mutant/cli_spec.rb +17 -4
- data/spec/unit/mutant/env_spec.rb +2 -2
- data/spec/unit/mutant/mailbox_spec.rb +33 -0
- data/spec/unit/mutant/mutation_spec.rb +52 -18
- data/spec/unit/mutant/mutator/registry_spec.rb +4 -4
- data/spec/unit/mutant/reporter/cli_spec.rb +131 -249
- data/spec/unit/mutant/result/env_spec.rb +55 -0
- data/spec/unit/mutant/result/subject_spec.rb +43 -0
- data/spec/unit/mutant/runner/master_spec.rb +199 -0
- data/spec/unit/mutant/runner/scheduler_spec.rb +161 -0
- data/spec/unit/mutant/runner/worker_spec.rb +73 -0
- data/spec/unit/mutant/runner_spec.rb +60 -118
- data/spec/unit/mutant/subject/method/instance_spec.rb +18 -31
- data/spec/unit/mutant/warning_filter_spec.rb +1 -1
- metadata +39 -14
- data/lib/mutant/runner/collector.rb +0 -133
- data/lib/mutant/warning_expectation.rb +0 -47
- data/spec/unit/mutant/runner/collector_spec.rb +0 -198
- data/spec/unit/mutant/test_spec.rb +0 -23
- data/spec/unit/mutant/warning_expectation_spec.rb +0 -80
- data/test_app/Gemfile.rspec2 +0 -6
@@ -0,0 +1,135 @@
|
|
1
|
+
module SharedContext
|
2
|
+
def update(name, &block)
|
3
|
+
define_method(name) do
|
4
|
+
super().update(instance_eval(&block))
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def messages(&block)
|
9
|
+
let(:message_sequence) do
|
10
|
+
FakeActor::MessageSequence.new.tap do |sequence|
|
11
|
+
sequence.instance_eval(&block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# rubocop:disable MethodLength
|
17
|
+
def setup_shared_context
|
18
|
+
let(:env) { double('env', config: config, subjects: [subject_a], mutations: mutations) }
|
19
|
+
let(:job_a) { Mutant::Runner::Job.new(index: 0, mutation: mutation_a) }
|
20
|
+
let(:job_b) { Mutant::Runner::Job.new(index: 1, mutation: mutation_b) }
|
21
|
+
let(:job_a_result) { Mutant::Runner::JobResult.new(job: job_a, result: mutation_a_result) }
|
22
|
+
let(:job_b_result) { Mutant::Runner::JobResult.new(job: job_b, result: mutation_b_result) }
|
23
|
+
let(:mutations) { [mutation_a, mutation_b] }
|
24
|
+
let(:matchable_scopes) { double('matchable scopes', length: 10) }
|
25
|
+
let(:test_a) { double('test a', identification: 'test-a') }
|
26
|
+
let(:test_b) { double('test b', identification: 'test-b') }
|
27
|
+
let(:actor_names) { [] }
|
28
|
+
let(:message_sequence) { FakeActor::MessageSequence.new }
|
29
|
+
|
30
|
+
let(:config) do
|
31
|
+
Mutant::Config::DEFAULT.update(
|
32
|
+
actor_env: actor_env,
|
33
|
+
jobs: 1,
|
34
|
+
reporter: Mutant::Reporter::Trace.new
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
let(:actor_env) do
|
39
|
+
FakeActor::Env.new(message_sequence, actor_names)
|
40
|
+
end
|
41
|
+
|
42
|
+
let(:subject_a) do
|
43
|
+
double(
|
44
|
+
'subject a',
|
45
|
+
node: s(:true),
|
46
|
+
source: 'true',
|
47
|
+
tests: [test_a],
|
48
|
+
identification: 'subject-a'
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
before do
|
53
|
+
allow(subject_a).to receive(:mutations).and_return([mutation_a, mutation_b])
|
54
|
+
end
|
55
|
+
|
56
|
+
let(:empty_status) do
|
57
|
+
Mutant::Runner::Status.new(
|
58
|
+
active_jobs: Set.new,
|
59
|
+
env_result: env_result.update(subject_results: [], runtime: 0.0),
|
60
|
+
done: false
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
let(:status) do
|
65
|
+
Mutant::Runner::Status.new(
|
66
|
+
active_jobs: Set.new,
|
67
|
+
env_result: env_result,
|
68
|
+
done: true
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
let(:env_result) do
|
73
|
+
Mutant::Result::Env.new(
|
74
|
+
env: env,
|
75
|
+
runtime: 4.0,
|
76
|
+
subject_results: [subject_a_result]
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
let(:mutation_a_node) { s(:false) }
|
81
|
+
let(:mutation_b_node) { s(:nil) }
|
82
|
+
|
83
|
+
let(:mutation_b) { Mutant::Mutation::Evil.new(subject_a, mutation_b_node) }
|
84
|
+
let(:mutation_a) { Mutant::Mutation::Evil.new(subject_a, mutation_a_node) }
|
85
|
+
|
86
|
+
let(:mutation_a_result) do
|
87
|
+
Mutant::Result::Mutation.new(
|
88
|
+
index: 1,
|
89
|
+
mutation: mutation_a,
|
90
|
+
test_result: mutation_a_test_result
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
let(:mutation_b_result) do
|
95
|
+
Mutant::Result::Mutation.new(
|
96
|
+
index: 1,
|
97
|
+
mutation: mutation_a,
|
98
|
+
test_result: mutation_b_test_result
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
let(:mutation_a_test_result) do
|
103
|
+
Mutant::Result::Test.new(
|
104
|
+
tests: [test_a],
|
105
|
+
passed: false,
|
106
|
+
runtime: 1.0,
|
107
|
+
output: 'mutation a test result output'
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
let(:mutation_b_test_result) do
|
112
|
+
Mutant::Result::Test.new(
|
113
|
+
tests: [test_a],
|
114
|
+
passed: false,
|
115
|
+
runtime: 1.0,
|
116
|
+
output: 'mutation b test result output'
|
117
|
+
)
|
118
|
+
end
|
119
|
+
|
120
|
+
let(:subject_a_result) do
|
121
|
+
Mutant::Result::Subject.new(
|
122
|
+
subject: subject_a,
|
123
|
+
mutation_results: [mutation_a_result, mutation_b_result]
|
124
|
+
)
|
125
|
+
end
|
126
|
+
|
127
|
+
let(:empty_subject_a_result) do
|
128
|
+
subject_a_result.update(mutation_results: [])
|
129
|
+
end
|
130
|
+
|
131
|
+
let(:partial_subject_a_result) do
|
132
|
+
subject_a_result.update(mutation_results: [mutation_a_result])
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
RSpec.describe Mutant::Actor do
|
2
|
+
let(:mutex) { double('Mutex') }
|
3
|
+
let(:thread) { double('Thread') }
|
4
|
+
|
5
|
+
before do
|
6
|
+
expect(Mutex).to receive(:new).and_return(mutex)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Mutant::Actor::Actor do
|
10
|
+
let(:mailbox) { Mutant::Actor::Mailbox.new }
|
11
|
+
|
12
|
+
let(:object) { described_class.new(thread, mailbox) }
|
13
|
+
|
14
|
+
describe '#bind' do
|
15
|
+
let(:other) { double('Sender') }
|
16
|
+
|
17
|
+
subject { object.bind(other) }
|
18
|
+
|
19
|
+
it { should eql(Mutant::Actor::Binding.new(object, other)) }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#sender' do
|
23
|
+
subject { object.sender }
|
24
|
+
it { should eql(Mutant::Actor::Sender.new(thread, mutex, [])) }
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#receiver' do
|
28
|
+
subject { object.receiver }
|
29
|
+
|
30
|
+
it 'returns receiver' do
|
31
|
+
should eql(Mutant::Actor::Receiver.new(mutex, []))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
RSpec.describe Mutant::Actor::Binding do
|
2
|
+
let(:actor_a) { double('Actor-A', sender: sender_a, receiver: receiver_a) }
|
3
|
+
let(:sender_a) { double('Sender-A') }
|
4
|
+
let(:sender_b) { double('Sender-B') }
|
5
|
+
let(:receiver_a) { double('Receiver-A') }
|
6
|
+
let(:payload) { double('Payload') }
|
7
|
+
let(:type) { double('Type') }
|
8
|
+
|
9
|
+
let(:object) { described_class.new(actor_a, sender_b) }
|
10
|
+
|
11
|
+
describe '#call' do
|
12
|
+
subject { object.call(type) }
|
13
|
+
|
14
|
+
before do
|
15
|
+
expect(sender_b).to receive(:call).with(message(type, sender_a)).ordered
|
16
|
+
expect(receiver_a).to receive(:call).ordered.and_return(message(response_type, payload))
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'when return type equals request type' do
|
20
|
+
let(:response_type) { type }
|
21
|
+
it { should be(payload) }
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when return type NOT equals request type' do
|
25
|
+
let(:response_type) { double('Other Type') }
|
26
|
+
|
27
|
+
it 'raises error' do
|
28
|
+
expect { subject }.to raise_error(Mutant::Actor::ProtocolError, "Expected #{type} but got #{response_type}")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
RSpec.describe Mutant::Actor::Env do
|
2
|
+
let(:mutex) { double('Mutex') }
|
3
|
+
let(:thread) { double('Thread') }
|
4
|
+
let(:thread_root) { double('Thread Root') }
|
5
|
+
let(:actor) { Mutant::Actor::Actor.new(thread, mailbox) }
|
6
|
+
|
7
|
+
let(:object) { described_class.new(thread_root) }
|
8
|
+
|
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
|
+
describe '#spawn' do
|
27
|
+
subject { object.spawn(&block) }
|
28
|
+
|
29
|
+
let!(:mailbox) { Mutant::Actor::Mailbox.new }
|
30
|
+
|
31
|
+
let(:yields) { [] }
|
32
|
+
|
33
|
+
let(:block) { ->(actor) { yields << actor } }
|
34
|
+
|
35
|
+
before do
|
36
|
+
expect(Mutant::Actor::Mailbox).to receive(:new).and_return(mailbox).ordered
|
37
|
+
expect(thread_root).to receive(:new).and_yield.and_return(thread).ordered
|
38
|
+
expect(thread_root).to receive(:current).and_return(thread).ordered
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'returns sender' do
|
42
|
+
should eql(actor.sender)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'yields actor' do
|
46
|
+
expect { subject }.to change { yields }.from([]).to([actor])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
RSpec.describe Mutant::Actor::Message do
|
2
|
+
|
3
|
+
let(:type) { double('Type') }
|
4
|
+
let(:payload) { double('Payload') }
|
5
|
+
|
6
|
+
describe '.new' do
|
7
|
+
subject { described_class.new(*arguments) }
|
8
|
+
|
9
|
+
context 'with one argument' do
|
10
|
+
let(:arguments) { [type] }
|
11
|
+
|
12
|
+
its(:type) { should be(type) }
|
13
|
+
its(:payload) { should be(Mutant::Actor::Undefined) }
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'with two arguments' do
|
17
|
+
let(:arguments) { [type, payload] }
|
18
|
+
|
19
|
+
its(:type) { should be(type) }
|
20
|
+
its(:payload) { should be(payload) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
RSpec.describe Mutant::Actor::Receiver do
|
2
|
+
let(:mailbox) { double('Mailbox') }
|
3
|
+
let(:mutex) { double('Mutex') }
|
4
|
+
let(:message) { double('Message') }
|
5
|
+
|
6
|
+
let(:object) { described_class.new(mutex, mailbox) }
|
7
|
+
|
8
|
+
describe '#call' do
|
9
|
+
subject { object.call }
|
10
|
+
|
11
|
+
context 'when mailbox contains a message' do
|
12
|
+
before do
|
13
|
+
expect(mutex).to receive(:lock).ordered
|
14
|
+
expect(mailbox).to receive(:empty?).and_return(false).ordered
|
15
|
+
expect(mailbox).to receive(:shift).and_return(message).ordered
|
16
|
+
expect(mutex).to receive(:unlock).ordered
|
17
|
+
end
|
18
|
+
|
19
|
+
it { should be(message) }
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'when mailbox initially contains no message' do
|
23
|
+
before do
|
24
|
+
# 1rst failing try
|
25
|
+
expect(mutex).to receive(:lock).ordered
|
26
|
+
expect(mailbox).to receive(:empty?).and_return(true).ordered
|
27
|
+
expect(mutex).to receive(:unlock).ordered
|
28
|
+
expect(Thread).to receive(:stop).ordered
|
29
|
+
# 2nd successful try
|
30
|
+
expect(mutex).to receive(:lock).ordered
|
31
|
+
expect(mailbox).to receive(:empty?).and_return(false).ordered
|
32
|
+
expect(mailbox).to receive(:shift).and_return(message).ordered
|
33
|
+
expect(mutex).to receive(:unlock).ordered
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'waits for message' do
|
37
|
+
should be(message)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when mailbox contains no message but thread gets waken without message arrived' do
|
42
|
+
before do
|
43
|
+
# 1rst failing try
|
44
|
+
expect(mutex).to receive(:lock).ordered
|
45
|
+
expect(mailbox).to receive(:empty?).and_return(true).ordered
|
46
|
+
expect(mutex).to receive(:unlock).ordered
|
47
|
+
expect(Thread).to receive(:stop).ordered
|
48
|
+
# 2nd failing try
|
49
|
+
expect(mutex).to receive(:lock).ordered
|
50
|
+
expect(mailbox).to receive(:empty?).and_return(true).ordered
|
51
|
+
expect(mutex).to receive(:unlock).ordered
|
52
|
+
expect(Thread).to receive(:stop).ordered
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'waits for message' do
|
56
|
+
expect { subject }.to raise_error(Mutant::Actor::ProtocolError)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
RSpec.describe Mutant::Actor::Sender do
|
2
|
+
let(:object) { described_class.new(thread, mutex, mailbox) }
|
3
|
+
|
4
|
+
let(:thread) { double('Thread') }
|
5
|
+
let(:mutex) { double('Mutex') }
|
6
|
+
let(:mailbox) { double('Mailbox') }
|
7
|
+
let(:type) { double('Type') }
|
8
|
+
let(:payload) { double('Payload') }
|
9
|
+
let(:_message) { message(type, payload) }
|
10
|
+
|
11
|
+
describe '#call' do
|
12
|
+
subject { object.call(_message) }
|
13
|
+
|
14
|
+
before do
|
15
|
+
expect(mutex).to receive(:synchronize).ordered.and_yield
|
16
|
+
expect(mailbox).to receive(:<<).with(_message)
|
17
|
+
expect(thread).to receive(:run)
|
18
|
+
end
|
19
|
+
|
20
|
+
it_should_behave_like 'a command method'
|
21
|
+
end
|
22
|
+
end
|
@@ -29,7 +29,7 @@ RSpec.describe Mutant::CLI do
|
|
29
29
|
expect(Mutant::Runner).to receive(:call).with(env).and_return(report)
|
30
30
|
end
|
31
31
|
|
32
|
-
context 'when report
|
32
|
+
context 'when report signals success' do
|
33
33
|
let(:report_success) { true }
|
34
34
|
|
35
35
|
it 'exits failure' do
|
@@ -155,11 +155,24 @@ Options:
|
|
155
155
|
end
|
156
156
|
|
157
157
|
context 'with use flag' do
|
158
|
-
|
158
|
+
context 'when integration exists' do
|
159
|
+
let(:flags) { %w[--use rspec] }
|
159
160
|
|
160
|
-
|
161
|
+
it_should_behave_like 'a cli parser'
|
162
|
+
|
163
|
+
let(:expected_integration) { Mutant::Integration::Rspec.new }
|
164
|
+
end
|
161
165
|
|
162
|
-
|
166
|
+
context 'when integration does NOT exist' do
|
167
|
+
let(:flags) { %w[--use other] }
|
168
|
+
|
169
|
+
it 'raises error' do
|
170
|
+
expect { subject }.to raise_error(
|
171
|
+
Mutant::CLI::Error,
|
172
|
+
'Could not load integration "other" (you may want to try installing the gem mutant-other)'
|
173
|
+
)
|
174
|
+
end
|
175
|
+
end
|
163
176
|
end
|
164
177
|
|
165
178
|
context 'with version flag' do
|
@@ -12,7 +12,7 @@ RSpec.describe Mutant::Env do
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
expected_warnings = ["Class#name from: #{klass} raised an error: RuntimeError
|
15
|
+
expected_warnings = ["Class#name from: #{klass} raised an error: RuntimeError. #{Mutant::Env::SEMANTICS_MESSAGE}"]
|
16
16
|
|
17
17
|
expect { subject }.to change { config.reporter.warn_calls }.from([]).to(expected_warnings)
|
18
18
|
|
@@ -33,7 +33,7 @@ RSpec.describe Mutant::Env do
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
expected_warnings = ["Class#name from: #{klass.inspect} returned #{
|
36
|
+
expected_warnings = ["Class#name from: #{klass.inspect} returned Object. #{Mutant::Env::SEMANTICS_MESSAGE}"]
|
37
37
|
|
38
38
|
expect { subject }.to change { config.reporter.warn_calls }.from([]).to(expected_warnings)
|
39
39
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
RSpec.describe Mutant::Actor::Mailbox do
|
2
|
+
describe '.new' do
|
3
|
+
subject { described_class.new }
|
4
|
+
|
5
|
+
its(:frozen?) { should be(true) }
|
6
|
+
end
|
7
|
+
|
8
|
+
before do
|
9
|
+
allow(Mutex).to receive(:new).and_return(mutex)
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:mutex) { double('Mutex') }
|
13
|
+
let(:object) { described_class.new }
|
14
|
+
let(:thread) { double('Thread') }
|
15
|
+
|
16
|
+
describe '#sender' do
|
17
|
+
subject { object.sender(thread) }
|
18
|
+
|
19
|
+
it { should eql(Mutant::Actor::Sender.new(thread, mutex, [])) }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#receiver' do
|
23
|
+
subject { object.receiver }
|
24
|
+
|
25
|
+
it { should eql(Mutant::Actor::Receiver.new(mutex, [])) }
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#actor' do
|
29
|
+
subject { object.actor(thread) }
|
30
|
+
|
31
|
+
it { should eql(Mutant::Actor::Actor.new(thread, object)) }
|
32
|
+
end
|
33
|
+
end
|