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,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 signalls success' do
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
- let(:flags) { %w[--use rspec] }
158
+ context 'when integration exists' do
159
+ let(:flags) { %w[--use rspec] }
159
160
 
160
- it_should_behave_like 'a cli parser'
161
+ it_should_behave_like 'a cli parser'
162
+
163
+ let(:expected_integration) { Mutant::Integration::Rspec.new }
164
+ end
161
165
 
162
- let(:expected_integration) { Mutant::Integration::Rspec.build }
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 fix your lib to follow normal ruby semantics!"]
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 #{Object.inspect} instead String or nil. Fix your lib to follow normal ruby semantics!"]
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