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.
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