mutant 0.6.7 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +10 -0
  3. data/README.md +1 -1
  4. data/config/flay.yml +1 -1
  5. data/config/reek.yml +11 -40
  6. data/config/rubocop.yml +1 -1
  7. data/lib/mutant.rb +10 -2
  8. data/lib/mutant/actor.rb +64 -0
  9. data/lib/mutant/actor/actor.rb +50 -0
  10. data/lib/mutant/actor/env.rb +35 -0
  11. data/lib/mutant/actor/mailbox.rb +53 -0
  12. data/lib/mutant/actor/receiver.rb +48 -0
  13. data/lib/mutant/actor/sender.rb +27 -0
  14. data/lib/mutant/ast/types.rb +1 -1
  15. data/lib/mutant/cli.rb +2 -0
  16. data/lib/mutant/config.rb +2 -1
  17. data/lib/mutant/env.rb +6 -2
  18. data/lib/mutant/expression/methods.rb +1 -1
  19. data/lib/mutant/integration.rb +11 -1
  20. data/lib/mutant/isolation.rb +1 -2
  21. data/lib/mutant/meta/example.rb +1 -1
  22. data/lib/mutant/mutation.rb +47 -21
  23. data/lib/mutant/mutator/node.rb +1 -1
  24. data/lib/mutant/mutator/node/literal/symbol.rb +1 -1
  25. data/lib/mutant/mutator/node/send.rb +4 -3
  26. data/lib/mutant/reporter/cli.rb +2 -0
  27. data/lib/mutant/reporter/cli/format.rb +23 -36
  28. data/lib/mutant/reporter/cli/printer.rb +66 -27
  29. data/lib/mutant/result.rb +45 -58
  30. data/lib/mutant/runner.rb +47 -154
  31. data/lib/mutant/runner/master.rb +174 -0
  32. data/lib/mutant/runner/scheduler.rb +141 -0
  33. data/lib/mutant/runner/worker.rb +93 -0
  34. data/lib/mutant/subject/method/instance.rb +1 -15
  35. data/lib/mutant/test.rb +2 -15
  36. data/lib/mutant/version.rb +1 -1
  37. data/meta/send.rb +16 -0
  38. data/mutant-rspec.gemspec +1 -1
  39. data/mutant.gemspec +1 -1
  40. data/spec/integration/mutant/rspec_spec.rb +0 -6
  41. data/spec/spec_helper.rb +9 -1
  42. data/spec/support/fake_actor.rb +93 -0
  43. data/spec/support/shared_context.rb +135 -0
  44. data/spec/unit/mutant/actor/actor_spec.rb +35 -0
  45. data/spec/unit/mutant/actor/binding_spec.rb +32 -0
  46. data/spec/unit/mutant/actor/env_spec.rb +49 -0
  47. data/spec/unit/mutant/actor/message_spec.rb +23 -0
  48. data/spec/unit/mutant/actor/receiver_spec.rb +60 -0
  49. data/spec/unit/mutant/actor/sender_spec.rb +22 -0
  50. data/spec/unit/mutant/cli_spec.rb +17 -4
  51. data/spec/unit/mutant/env_spec.rb +2 -2
  52. data/spec/unit/mutant/mailbox_spec.rb +33 -0
  53. data/spec/unit/mutant/mutation_spec.rb +52 -18
  54. data/spec/unit/mutant/mutator/registry_spec.rb +4 -4
  55. data/spec/unit/mutant/reporter/cli_spec.rb +131 -249
  56. data/spec/unit/mutant/result/env_spec.rb +55 -0
  57. data/spec/unit/mutant/result/subject_spec.rb +43 -0
  58. data/spec/unit/mutant/runner/master_spec.rb +199 -0
  59. data/spec/unit/mutant/runner/scheduler_spec.rb +161 -0
  60. data/spec/unit/mutant/runner/worker_spec.rb +73 -0
  61. data/spec/unit/mutant/runner_spec.rb +60 -118
  62. data/spec/unit/mutant/subject/method/instance_spec.rb +18 -31
  63. data/spec/unit/mutant/warning_filter_spec.rb +1 -1
  64. metadata +39 -14
  65. data/lib/mutant/runner/collector.rb +0 -133
  66. data/lib/mutant/warning_expectation.rb +0 -47
  67. data/spec/unit/mutant/runner/collector_spec.rb +0 -198
  68. data/spec/unit/mutant/test_spec.rb +0 -23
  69. data/spec/unit/mutant/warning_expectation_spec.rb +0 -80
  70. data/test_app/Gemfile.rspec2 +0 -6
@@ -0,0 +1,55 @@
1
+ RSpec.describe Mutant::Result::Env do
2
+ let(:object) do
3
+ described_class.new(
4
+ env: double('Env', config: config),
5
+ runtime: double('Runtime'),
6
+ subject_results: subject_results
7
+ )
8
+ end
9
+
10
+ let(:config) { double('Config', fail_fast: fail_fast) }
11
+
12
+ describe '#continue?' do
13
+ subject { object.continue? }
14
+
15
+ context 'config sets fail_fast flag' do
16
+ let(:fail_fast) { true }
17
+
18
+ context 'when mutation results are empty' do
19
+ let(:subject_results) { [] }
20
+
21
+ it { should be(true) }
22
+ end
23
+
24
+ context 'with failing mutation result' do
25
+ let(:subject_results) { [double('Subject Result', success?: false)] }
26
+
27
+ it { should be(false) }
28
+ end
29
+
30
+ context 'with successful mutation result' do
31
+ let(:subject_results) { [double('Subject Result', success?: true)] }
32
+
33
+ it { should be(true) }
34
+ end
35
+
36
+ context 'with failed and successful mutation result' do
37
+ let(:subject_results) do
38
+ [
39
+ double('Subject Result', success?: true),
40
+ double('Subject Result', success?: false)
41
+ ]
42
+ end
43
+
44
+ it { should be(false) }
45
+ end
46
+ end
47
+
48
+ context 'config does not set fail fast flag' do
49
+ let(:fail_fast) { false }
50
+ let(:subject_results) { double('subject results') }
51
+
52
+ it { should be(true) }
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,43 @@
1
+ RSpec.describe Mutant::Result::Subject do
2
+ let(:object) do
3
+ described_class.new(
4
+ subject: mutation_subject,
5
+ mutation_results: mutation_results
6
+ )
7
+ end
8
+
9
+ let(:mutation_subject) { double('Subject') }
10
+
11
+ describe '#continue?' do
12
+ subject { object.continue? }
13
+
14
+ context 'when mutation results are empty' do
15
+ let(:mutation_results) { [] }
16
+
17
+ it { should be(true) }
18
+ end
19
+
20
+ context 'with failing mutation result' do
21
+ let(:mutation_results) { [double('Mutation Result', success?: false)] }
22
+
23
+ it { should be(false) }
24
+ end
25
+
26
+ context 'with successful mutation result' do
27
+ let(:mutation_results) { [double('Mutation Result', success?: true)] }
28
+
29
+ it { should be(true) }
30
+ end
31
+
32
+ context 'with failed and successful mutation result' do
33
+ let(:mutation_results) do
34
+ [
35
+ double('Mutation Result', success?: true),
36
+ double('Mutation Result', success?: false)
37
+ ]
38
+ end
39
+
40
+ it { should be(false) }
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,199 @@
1
+ RSpec.describe Mutant::Runner::Master do
2
+ setup_shared_context
3
+
4
+ describe 'object initialization' do
5
+ subject { described_class.__send__(:new, env, double('actor')) }
6
+
7
+ it 'initialized instance variables' do
8
+ expect(subject.instance_variable_get(:@stop)).to be(false)
9
+ expect(subject.instance_variable_get(:@stopping)).to be(false)
10
+ end
11
+ end
12
+
13
+ describe '.call' do
14
+ let(:actor_names) { [:master, :worker_a] }
15
+ let(:worker_a) { actor_env.actor(:worker_a).sender }
16
+ let(:worker_b) { actor_env.actor(:worker_b).sender }
17
+ let(:parent) { actor_env.actor(:parent).sender }
18
+
19
+ let(:job) { double('Job') }
20
+
21
+ before do
22
+ expect(Time).to receive(:now).and_return(Time.at(0)).at_most(5).times
23
+ expect(Mutant::Runner::Worker).to receive(:run).with(
24
+ id: 0,
25
+ config: env.config,
26
+ parent: actor_env.actor(:master).sender
27
+ ).and_return(worker_a)
28
+ end
29
+
30
+ subject { described_class.call(env) }
31
+
32
+ context 'jobs done before external stop' do
33
+ before do
34
+ message_sequence.add(:master, :ready, worker_a)
35
+ message_sequence.add(:worker_a, :job, job_a)
36
+ message_sequence.add(:master, :result, job_a_result)
37
+
38
+ message_sequence.add(:master, :ready, worker_a)
39
+ message_sequence.add(:worker_a, :job, job_b)
40
+ message_sequence.add(:master, :result, job_b_result)
41
+
42
+ message_sequence.add(:master, :ready, worker_a)
43
+ message_sequence.add(:worker_a, :stop)
44
+
45
+ message_sequence.add(:master, :stop, parent)
46
+ message_sequence.add(:parent, :stop)
47
+ end
48
+
49
+ it { should eql(actor_env.actor(:master).sender) }
50
+
51
+ it 'consumes all messages' do
52
+ expect { subject }.to change(&message_sequence.method(:consumed?)).from(false).to(true)
53
+ end
54
+ end
55
+
56
+ context 'stop by fail fast trigger first' do
57
+ update(:config) { { fail_fast: true } }
58
+ update(:mutation_b_test_result) { { passed: true } }
59
+
60
+ before do
61
+ message_sequence.add(:master, :ready, worker_a)
62
+ message_sequence.add(:worker_a, :job, job_a)
63
+ message_sequence.add(:master, :result, job_a_result)
64
+
65
+ message_sequence.add(:master, :ready, worker_a)
66
+ message_sequence.add(:worker_a, :job, job_b)
67
+ message_sequence.add(:master, :result, job_b_result)
68
+
69
+ message_sequence.add(:master, :ready, worker_a)
70
+ message_sequence.add(:worker_a, :stop)
71
+
72
+ message_sequence.add(:master, :stop, parent)
73
+ message_sequence.add(:parent, :stop)
74
+ end
75
+
76
+ it { should eql(actor_env.actor(:master).sender) }
77
+
78
+ it 'consumes all messages' do
79
+ expect { subject }.to change(&message_sequence.method(:consumed?)).from(false).to(true)
80
+ end
81
+ end
82
+
83
+ context 'stop by fail fast trigger last' do
84
+ update(:config) { { fail_fast: true } }
85
+ update(:mutation_a_test_result) { { passed: true } }
86
+
87
+ before do
88
+ message_sequence.add(:master, :ready, worker_a)
89
+ message_sequence.add(:worker_a, :job, job_a)
90
+ message_sequence.add(:master, :result, job_a_result)
91
+
92
+ message_sequence.add(:master, :ready, worker_a)
93
+ message_sequence.add(:worker_a, :stop)
94
+
95
+ message_sequence.add(:master, :stop, parent)
96
+ message_sequence.add(:parent, :stop)
97
+ end
98
+
99
+ it { should eql(actor_env.actor(:master).sender) }
100
+
101
+ it 'consumes all messages' do
102
+ expect { subject }.to change(&message_sequence.method(:consumed?)).from(false).to(true)
103
+ end
104
+ end
105
+
106
+ context 'jobs active while external stop' do
107
+ before do
108
+ message_sequence.add(:master, :ready, worker_a)
109
+ message_sequence.add(:worker_a, :job, job_a)
110
+ message_sequence.add(:master, :stop, parent)
111
+ message_sequence.add(:master, :result, job_a_result)
112
+
113
+ message_sequence.add(:master, :status, parent)
114
+ message_sequence.add(:parent, :status, empty_status.update(active_jobs: [job_a].to_set))
115
+
116
+ message_sequence.add(:master, :ready, worker_a)
117
+ message_sequence.add(:worker_a, :stop)
118
+
119
+ message_sequence.add(:parent, :stop)
120
+ end
121
+
122
+ it { should eql(actor_env.actor(:master).sender) }
123
+
124
+ it 'consumes all messages' do
125
+ expect { subject }.to change(&message_sequence.method(:consumed?)).from(false).to(true)
126
+ end
127
+ end
128
+
129
+ context 'stop with pending jobs' do
130
+ before do
131
+ message_sequence.add(:master, :stop, parent)
132
+ message_sequence.add(:master, :ready, worker_a)
133
+ message_sequence.add(:worker_a, :stop)
134
+ message_sequence.add(:parent, :stop)
135
+ end
136
+
137
+ it { should eql(actor_env.actor(:master).sender) }
138
+
139
+ it 'consumes all messages' do
140
+ expect { subject }.to change(&message_sequence.method(:consumed?)).from(false).to(true)
141
+ end
142
+ end
143
+
144
+ context 'unhandled message received' do
145
+ before do
146
+ message_sequence.add(:master, :foo, parent)
147
+ end
148
+
149
+ it 'raises message' do
150
+ expect { subject }.to raise_error(Mutant::Actor::ProtocolError, 'Unexpected message: :foo')
151
+ end
152
+ end
153
+
154
+ context 'request status late' do
155
+ let(:expected_status) { status.update(env_result: env_result.update(runtime: 0.0)) }
156
+
157
+ before do
158
+ message_sequence.add(:master, :ready, worker_a)
159
+ message_sequence.add(:worker_a, :job, job_a)
160
+ message_sequence.add(:master, :result, job_a_result)
161
+
162
+ message_sequence.add(:master, :ready, worker_a)
163
+ message_sequence.add(:worker_a, :job, job_b)
164
+ message_sequence.add(:master, :result, job_b_result)
165
+
166
+ message_sequence.add(:master, :ready, worker_a)
167
+ message_sequence.add(:worker_a, :stop)
168
+
169
+ message_sequence.add(:master, :status, parent)
170
+ message_sequence.add(:parent, :status, expected_status)
171
+ message_sequence.add(:master, :stop, parent)
172
+ message_sequence.add(:parent, :stop)
173
+ end
174
+
175
+ it { should eql(actor_env.actor(:master).sender) }
176
+
177
+ it 'consumes all messages' do
178
+ expect { subject }.to change(&message_sequence.method(:consumed?)).from(false).to(true)
179
+ end
180
+ end
181
+
182
+ context 'request status early' do
183
+ before do
184
+ message_sequence.add(:master, :status, parent)
185
+ message_sequence.add(:parent, :status, empty_status)
186
+ message_sequence.add(:master, :stop, parent)
187
+ message_sequence.add(:master, :ready, worker_a)
188
+ message_sequence.add(:worker_a, :stop)
189
+ message_sequence.add(:parent, :stop)
190
+ end
191
+
192
+ it { should eql(actor_env.actor(:master).sender) }
193
+
194
+ it 'consumes all messages' do
195
+ expect { subject }.to change(&message_sequence.method(:consumed?)).from(false).to(true)
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,161 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mutant::Runner::Scheduler do
4
+ let(:object) { described_class.new(env) }
5
+
6
+ before do
7
+ allow(Time).to receive(:now).and_return(Time.now)
8
+ end
9
+
10
+ setup_shared_context
11
+
12
+ let(:active_subject_a_result) do
13
+ subject_a_result.update(mutation_results: [])
14
+ end
15
+
16
+ describe '#job_result' do
17
+ subject { object.job_result(job_a_result) }
18
+
19
+ before do
20
+ expect(object.next_job).to eql(job_a)
21
+ end
22
+
23
+ it 'removes the tracking of job as active' do
24
+ expect { subject }.to change { object.status.active_jobs }.from([job_a].to_set).to(Set.new)
25
+ end
26
+
27
+ it 'aggregates results in #status' do
28
+ subject
29
+ object.job_result(job_b_result)
30
+ expect(object.status.env_result).to eql(
31
+ Mutant::Result::Env.new(
32
+ env: env,
33
+ runtime: 0.0,
34
+ subject_results: [subject_a_result]
35
+ )
36
+ )
37
+ end
38
+
39
+ it_should_behave_like 'a command method'
40
+ end
41
+
42
+ describe '#next_job' do
43
+ subject { object.next_job }
44
+
45
+ context 'when there is a next job' do
46
+ let(:mutations) { [mutation_a, mutation_b] }
47
+
48
+ it { should eql(job_a) }
49
+
50
+ it 'does not return the same job again' do
51
+ subject
52
+ expect(object.next_job).to eql(job_b)
53
+ expect(object.next_job).to be(nil)
54
+ end
55
+
56
+ it 'does record job as active' do
57
+ expect { subject }.to change { object.status.active_jobs }.from(Set.new).to([job_a].to_set)
58
+ end
59
+ end
60
+
61
+ context 'when there is no next job' do
62
+ let(:mutations) { [] }
63
+ it { should be(nil) }
64
+ end
65
+ end
66
+
67
+ describe '#status' do
68
+ subject { object.status }
69
+
70
+ context 'when empty' do
71
+ let(:expected_status) do
72
+ Mutant::Runner::Status.new(
73
+ env_result: Mutant::Result::Env.new(env: env, runtime: 0.0, subject_results: []),
74
+ active_jobs: Set.new,
75
+ done: false
76
+ )
77
+ end
78
+
79
+ it { should eql(expected_status) }
80
+ end
81
+
82
+ context 'when jobs are active' do
83
+ before do
84
+ object.next_job
85
+ object.next_job
86
+ end
87
+
88
+ let(:expected_status) do
89
+ Mutant::Runner::Status.new(
90
+ env_result: Mutant::Result::Env.new(env: env, runtime: 0.0, subject_results: []),
91
+ active_jobs: [job_a, job_b].to_set,
92
+ done: false
93
+ )
94
+ end
95
+
96
+ it { should eql(expected_status) }
97
+ end
98
+
99
+ context 'remaining jobs are active' do
100
+ before do
101
+ object.next_job
102
+ object.next_job
103
+ object.job_result(job_a_result)
104
+ end
105
+
106
+ update(:subject_a_result) { { mutation_results: [mutation_a_result] } }
107
+
108
+ let(:expected_status) do
109
+ Mutant::Runner::Status.new(
110
+ env_result: Mutant::Result::Env.new(env: env, runtime: 0.0, subject_results: [subject_a_result]),
111
+ active_jobs: [job_b].to_set,
112
+ done: false
113
+ )
114
+ end
115
+
116
+ it { should eql(expected_status) }
117
+ end
118
+
119
+ context 'under fail fast config with failed result' do
120
+ before do
121
+ object.next_job
122
+ object.next_job
123
+ object.job_result(job_a_result)
124
+ end
125
+
126
+ update(:subject_a_result) { { mutation_results: [mutation_a_result] } }
127
+ update(:mutation_a_test_result) { { passed: true } }
128
+ update(:config) { { fail_fast: true } }
129
+
130
+ let(:expected_status) do
131
+ Mutant::Runner::Status.new(
132
+ env_result: Mutant::Result::Env.new(env: env, runtime: 0.0, subject_results: [subject_a_result]),
133
+ active_jobs: [job_b].to_set,
134
+ done: true
135
+ )
136
+ end
137
+
138
+ it { should eql(expected_status) }
139
+ end
140
+
141
+ context 'when done' do
142
+ before do
143
+ object.next_job
144
+ object.next_job
145
+ object.status
146
+ object.job_result(job_a_result)
147
+ object.job_result(job_b_result)
148
+ end
149
+
150
+ let(:expected_status) do
151
+ Mutant::Runner::Status.new(
152
+ env_result: Mutant::Result::Env.new(env: env, runtime: 0.0, subject_results: [subject_a_result]),
153
+ active_jobs: Set.new,
154
+ done: true
155
+ )
156
+ end
157
+
158
+ it { should eql(expected_status) }
159
+ end
160
+ end
161
+ end