mutant 0.6.7 → 0.7.1
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/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
|