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.
- checksums.yaml +4 -4
- data/Changelog.md +10 -0
- data/README.md +1 -1
- data/config/flay.yml +1 -1
- data/config/reek.yml +11 -40
- data/config/rubocop.yml +1 -1
- data/lib/mutant.rb +10 -2
- data/lib/mutant/actor.rb +64 -0
- data/lib/mutant/actor/actor.rb +50 -0
- data/lib/mutant/actor/env.rb +35 -0
- data/lib/mutant/actor/mailbox.rb +53 -0
- data/lib/mutant/actor/receiver.rb +48 -0
- data/lib/mutant/actor/sender.rb +27 -0
- data/lib/mutant/ast/types.rb +1 -1
- data/lib/mutant/cli.rb +2 -0
- data/lib/mutant/config.rb +2 -1
- data/lib/mutant/env.rb +6 -2
- data/lib/mutant/expression/methods.rb +1 -1
- data/lib/mutant/integration.rb +11 -1
- data/lib/mutant/isolation.rb +1 -2
- data/lib/mutant/meta/example.rb +1 -1
- data/lib/mutant/mutation.rb +47 -21
- data/lib/mutant/mutator/node.rb +1 -1
- data/lib/mutant/mutator/node/literal/symbol.rb +1 -1
- data/lib/mutant/mutator/node/send.rb +4 -3
- data/lib/mutant/reporter/cli.rb +2 -0
- data/lib/mutant/reporter/cli/format.rb +23 -36
- data/lib/mutant/reporter/cli/printer.rb +66 -27
- data/lib/mutant/result.rb +45 -58
- data/lib/mutant/runner.rb +47 -154
- data/lib/mutant/runner/master.rb +174 -0
- data/lib/mutant/runner/scheduler.rb +141 -0
- data/lib/mutant/runner/worker.rb +93 -0
- data/lib/mutant/subject/method/instance.rb +1 -15
- data/lib/mutant/test.rb +2 -15
- data/lib/mutant/version.rb +1 -1
- data/meta/send.rb +16 -0
- data/mutant-rspec.gemspec +1 -1
- data/mutant.gemspec +1 -1
- data/spec/integration/mutant/rspec_spec.rb +0 -6
- data/spec/spec_helper.rb +9 -1
- data/spec/support/fake_actor.rb +93 -0
- data/spec/support/shared_context.rb +135 -0
- data/spec/unit/mutant/actor/actor_spec.rb +35 -0
- data/spec/unit/mutant/actor/binding_spec.rb +32 -0
- data/spec/unit/mutant/actor/env_spec.rb +49 -0
- data/spec/unit/mutant/actor/message_spec.rb +23 -0
- data/spec/unit/mutant/actor/receiver_spec.rb +60 -0
- data/spec/unit/mutant/actor/sender_spec.rb +22 -0
- data/spec/unit/mutant/cli_spec.rb +17 -4
- data/spec/unit/mutant/env_spec.rb +2 -2
- data/spec/unit/mutant/mailbox_spec.rb +33 -0
- data/spec/unit/mutant/mutation_spec.rb +52 -18
- data/spec/unit/mutant/mutator/registry_spec.rb +4 -4
- data/spec/unit/mutant/reporter/cli_spec.rb +131 -249
- data/spec/unit/mutant/result/env_spec.rb +55 -0
- data/spec/unit/mutant/result/subject_spec.rb +43 -0
- data/spec/unit/mutant/runner/master_spec.rb +199 -0
- data/spec/unit/mutant/runner/scheduler_spec.rb +161 -0
- data/spec/unit/mutant/runner/worker_spec.rb +73 -0
- data/spec/unit/mutant/runner_spec.rb +60 -118
- data/spec/unit/mutant/subject/method/instance_spec.rb +18 -31
- data/spec/unit/mutant/warning_filter_spec.rb +1 -1
- metadata +39 -14
- data/lib/mutant/runner/collector.rb +0 -133
- data/lib/mutant/warning_expectation.rb +0 -47
- data/spec/unit/mutant/runner/collector_spec.rb +0 -198
- data/spec/unit/mutant/test_spec.rb +0 -23
- data/spec/unit/mutant/warning_expectation_spec.rb +0 -80
- 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
|