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
@@ -1,133 +0,0 @@
1
- module Mutant
2
- class Runner
3
- # Parallel process collector
4
- class Collector
5
- include Concord::Public.new(:env)
6
-
7
- # Initialize object
8
- #
9
- # @return [undefined]
10
- #
11
- # @api private
12
- #
13
- def initialize(*)
14
- super
15
- @start = Time.now
16
- @aggregate = Hash.new { |hash, key| hash[key] = [] }
17
- @active = Set.new
18
- @last_mutation_result = nil
19
- end
20
-
21
- # Return last mutation result
22
- #
23
- # @return [Result::Mutation]
24
- # if there is a last mutation result
25
- #
26
- # @return [nil]
27
- # otherwise
28
- #
29
- # @api private
30
- #
31
- attr_reader :last_mutation_result
32
-
33
- # Return active subject results
34
- #
35
- # @return [Array<Result::Subject>]
36
- #
37
- # @api private
38
- #
39
- def active_subject_results
40
- active_subjects.map(&method(:subject_result))
41
- end
42
-
43
- # Return current result
44
- #
45
- # @return [Result::Env]
46
- #
47
- # @api private
48
- #
49
- def result
50
- Result::Env.new(
51
- env: env,
52
- runtime: Time.now - @start,
53
- subject_results: subject_results
54
- )
55
- end
56
-
57
- # Register mutation start
58
- #
59
- # @param [Mutation] mutation
60
- #
61
- # @return [self]
62
- #
63
- # @api private
64
- #
65
- def start(mutation)
66
- @active << mutation
67
- self
68
- end
69
-
70
- # Handle mutation finish
71
- #
72
- # @param [Result::Mutation] mutation_result
73
- #
74
- # @return [self]
75
- #
76
- # @api private
77
- #
78
- def finish(mutation_result)
79
- @last_mutation_result = mutation_result
80
-
81
- mutation = mutation_result.mutation
82
- @active.delete(mutation)
83
-
84
- @aggregate[mutation.subject] << mutation_result
85
-
86
- self
87
- end
88
-
89
- private
90
-
91
- # Return current subject results
92
- #
93
- # @return [Array<Result::Subject>]
94
- #
95
- # @api private
96
- #
97
- def subject_results
98
- env.subjects.map(&method(:subject_result))
99
- end
100
-
101
- # Return active subjects
102
- #
103
- # @return [Array<Subject>]
104
- #
105
- # @api private
106
- #
107
- def active_subjects
108
- @active.each_with_object(Set.new) do |mutation, subjects|
109
- subjects << mutation.subject
110
- end.sort_by(&:identification)
111
- end
112
-
113
- # Return current subject result
114
- #
115
- # @param [Subject] subject
116
- #
117
- # @return [Array<Subject::Result>]
118
- #
119
- # @api private
120
- #
121
- def subject_result(subject)
122
- mutation_results = @aggregate[subject].sort_by(&:index)
123
-
124
- Result::Subject.new(
125
- subject: subject,
126
- runtime: mutation_results.map(&:runtime).inject(0.0, :+),
127
- mutation_results: mutation_results
128
- )
129
- end
130
-
131
- end # Collector
132
- end # Runner
133
- end # Mutant
@@ -1,47 +0,0 @@
1
- module Mutant
2
- # A class to expect some warning message raising on absence of unexpected warnings
3
- class WarningExpectation
4
- include Adamantium::Flat, Concord.new(:expected)
5
-
6
- # Error raised on expectation miss
7
- class ExpectationError < RuntimeError
8
- include Concord.new(:unexpected, :expected)
9
-
10
- # Return exception message
11
- #
12
- # @return [String]
13
- #
14
- # @api private
15
- #
16
- def message
17
- "Unexpected warnings: #{unexpected.inspect}, expected: #{expected.inspect}"
18
- end
19
- end
20
-
21
- # Execute blocks with warning expectations
22
- #
23
- # @return [self]
24
- #
25
- # @api private
26
- #
27
- def execute(&block)
28
- warnings = WarningFilter.use do
29
- block.call
30
- end
31
-
32
- missing = expected - warnings
33
- unexpected = warnings - expected
34
-
35
- if unexpected.any?
36
- fail ExpectationError.new(unexpected, expected)
37
- end
38
-
39
- if missing.any?
40
- $stderr.puts("Expected but missing warnings: #{missing}")
41
- end
42
-
43
- self
44
- end
45
-
46
- end # WarningExpectation
47
- end # Mutant
@@ -1,198 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Mutant::Runner::Collector 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
- let(:env) do
11
- double(
12
- 'env',
13
- subjects: [mutation_a.subject]
14
- )
15
- end
16
-
17
- let(:mutation_a) do
18
- double(
19
- 'mutation a',
20
- subject: double('subject', identification: 'A')
21
- )
22
- end
23
-
24
- let(:mutation_a_result) do
25
- double(
26
- 'mutation a result',
27
- index: 0,
28
- runtime: 0.0,
29
- mutation: mutation_a
30
- )
31
- end
32
-
33
- let(:subject_a_result) do
34
- Mutant::Result::Subject.new(
35
- subject: mutation_a.subject,
36
- runtime: 0.0,
37
- mutation_results: [mutation_a_result]
38
- )
39
- end
40
-
41
- let(:active_subject_result) do
42
- subject_a_result.update(mutation_results: [])
43
- end
44
-
45
- let(:active_subject_results) do
46
- [active_subject_result]
47
- end
48
-
49
- describe '.new' do
50
- it 'initializes instance variables' do
51
- expect(object.instance_variables).to include(:@last_mutation_result)
52
- end
53
- end
54
-
55
- describe '#start' do
56
- subject { object.start(mutation_a) }
57
-
58
- it 'tracs the mutation as active' do
59
- expect { subject }.to change { object.active_subject_results }.from([]).to(active_subject_results)
60
- end
61
-
62
- it_should_behave_like 'a command method'
63
- end
64
-
65
- describe '#finish' do
66
- subject { object.finish(mutation_a_result) }
67
-
68
- before do
69
- object.start(mutation_a)
70
- end
71
-
72
- it 'removes the tracking of mutation as active' do
73
- expect { subject }.to change { object.active_subject_results }.from(active_subject_results).to([])
74
- end
75
-
76
- it 'sets last mutation result' do
77
- expect { subject }.to change { object.last_mutation_result }.from(nil).to(mutation_a_result)
78
- end
79
-
80
- it 'aggregates results in #result' do
81
- subject
82
- expect(object.result).to eql(
83
- Mutant::Result::Env.new(
84
- env: object.env,
85
- runtime: 0.0,
86
- subject_results: [subject_a_result]
87
- )
88
- )
89
- end
90
-
91
- it_should_behave_like 'a command method'
92
- end
93
-
94
- describe '#last_mutation_result' do
95
- subject { object.last_mutation_result }
96
-
97
- context 'when empty' do
98
- it { should be(nil) }
99
- end
100
-
101
- context 'with partial state' do
102
- before do
103
- object.start(mutation_a)
104
- end
105
-
106
- it { should be(nil) }
107
- end
108
-
109
- context 'with full state' do
110
- before do
111
- object.start(mutation_a)
112
- object.finish(mutation_a_result)
113
- end
114
-
115
- it { should be(mutation_a_result) }
116
- end
117
- end
118
-
119
- describe '#active_subject_results' do
120
- subject { object.active_subject_results }
121
-
122
- context 'when empty' do
123
- it { should eql([]) }
124
- end
125
-
126
- context 'on partial state' do
127
- let(:mutation_b) do
128
- double(
129
- 'mutation b',
130
- subject: double(
131
- 'subject',
132
- identification: 'B'
133
- )
134
- )
135
- end
136
-
137
- let(:mutation_b_result) do
138
- double(
139
- 'mutation b result',
140
- index: 0,
141
- runtime: 0.0,
142
- mutation: mutation_b
143
- )
144
- end
145
-
146
- let(:subject_b_result) do
147
- Mutant::Result::Subject.new(
148
- subject: mutation_b.subject,
149
- runtime: 0.0,
150
- mutation_results: [mutation_b_result]
151
- )
152
- end
153
-
154
- let(:active_subject_results) { [subject_a_result, subject_b_result] }
155
-
156
- before do
157
- object.start(mutation_b)
158
- object.start(mutation_a)
159
- end
160
-
161
- it { should eql(active_subject_results.map { |result| result.update(mutation_results: []) }) }
162
- end
163
-
164
- context 'on full state' do
165
- before do
166
- object.start(mutation_a)
167
- object.finish(mutation_a_result)
168
- end
169
-
170
- it { should eql([]) }
171
- end
172
- end
173
-
174
- describe '#result' do
175
- subject { object.result }
176
-
177
- context 'when empty' do
178
- it { should eql(Mutant::Result::Env.new(env: object.env, runtime: 0.0, subject_results: [active_subject_result])) }
179
- end
180
-
181
- context 'on partial state' do
182
- before do
183
- object.start(mutation_a)
184
- end
185
-
186
- it { should eql(Mutant::Result::Env.new(env: object.env, runtime: 0.0, subject_results: [active_subject_result])) }
187
- end
188
-
189
- context 'on full state' do
190
- before do
191
- object.start(mutation_a)
192
- object.finish(mutation_a_result)
193
- end
194
-
195
- it { should eql(Mutant::Result::Env.new(env: object.env, runtime: 0.0, subject_results: [subject_a_result])) }
196
- end
197
- end
198
- end
@@ -1,23 +0,0 @@
1
- RSpec.describe Mutant::Test do
2
- let(:object) { described_class.new(integration, expression) }
3
-
4
- let(:integration) { double('Integration', name: 'test-integration') }
5
- let(:expression) { double('Expression', syntax: 'test-syntax') }
6
-
7
- describe '#identification' do
8
- subject { object.identification }
9
-
10
- it { should eql('test-integration:test-syntax') }
11
- end
12
-
13
- describe '#run' do
14
- subject { object.run }
15
-
16
- let(:report) { double('Report') }
17
-
18
- it 'runs test via integration' do
19
- expect(integration).to receive(:run).with(object).and_return(report)
20
- expect(subject).to be(report)
21
- end
22
- end
23
- end
@@ -1,80 +0,0 @@
1
- RSpec.describe Mutant::WarningExpectation do
2
- let(:object) { described_class.new(expected_warnings) }
3
-
4
- let(:expected_warnings) { [] }
5
- let(:actual_warnings) { [] }
6
-
7
- let(:warning_a) { "foo.rb:10: warning: We have a problem!\n" }
8
- let(:warning_b) { "bar.rb:10: warning: We have an other problem!\n" }
9
-
10
- describe '#execute' do
11
- subject { object.execute(&block) }
12
-
13
- before do
14
- @called = false
15
- end
16
-
17
- let(:block) do
18
- lambda do
19
- @called = true
20
- actual_warnings.each(&Kernel.method(:warn))
21
- end
22
- end
23
-
24
- it 'executes block' do
25
- expect { subject }.to change { @called }.from(false).to(true)
26
- end
27
-
28
- context 'when no warnings occur during block execution' do
29
-
30
- context 'and no warnings are expected' do
31
- it_should_behave_like 'a command method'
32
- end
33
-
34
- context 'and warnings are expected' do
35
- let(:expected_warnings) { [warning_a] }
36
-
37
- before do
38
- expect($stderr).to receive(:puts).with("Expected but missing warnings: #{expected_warnings}")
39
- end
40
-
41
- it_should_behave_like 'a command method'
42
- end
43
- end
44
-
45
- context 'when warnings occur during block execution' do
46
- let(:actual_warnings) { [warning_a, warning_b] }
47
-
48
- context 'and only some no warnings are expected' do
49
- let(:expected_warnings) { [warning_a] }
50
-
51
- it 'raises an expectation error' do
52
- expect { subject }.to raise_error(Mutant::WarningExpectation::ExpectationError.new([warning_b], expected_warnings))
53
- end
54
- end
55
-
56
- context 'and all warnings are expected' do
57
- let(:expected_warnings) { [warning_a, warning_b] }
58
-
59
- it_should_behave_like 'a command method'
60
- end
61
-
62
- context 'and there is an expected warning missing' do
63
- let(:expected_warnings) { [warning_a] }
64
- let(:actual_warnings) { [warning_b] }
65
-
66
- it 'raises an expectation error' do
67
- expect { subject }.to raise_error(Mutant::WarningExpectation::ExpectationError.new([warning_b], expected_warnings))
68
- end
69
- end
70
- end
71
- end
72
- end
73
-
74
- RSpec.describe Mutant::WarningExpectation::ExpectationError do
75
- describe '#message' do
76
- subject { described_class.new(['unexpected-a'], ['expected-b']).message }
77
-
78
- it { should eql('Unexpected warnings: ["unexpected-a"], expected: ["expected-b"]') }
79
- end
80
- end