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