mutant 0.6.3 → 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/Changelog.md +6 -0
- data/Gemfile.devtools +1 -1
- data/bin/mutant +2 -1
- data/config/flay.yml +1 -1
- data/config/reek.yml +3 -0
- data/lib/mutant.rb +5 -3
- data/lib/mutant/cli.rb +2 -2
- data/lib/mutant/config.rb +1 -1
- data/lib/mutant/diff.rb +1 -1
- data/lib/mutant/env.rb +1 -1
- data/lib/mutant/expression/methods.rb +16 -0
- data/lib/mutant/isolation.rb +16 -2
- data/lib/mutant/mutator/node/const.rb +1 -1
- data/lib/mutant/mutator/node/generic.rb +1 -1
- data/lib/mutant/mutator/registry.rb +1 -1
- data/lib/mutant/reporter/cli.rb +1 -1
- data/lib/mutant/reporter/cli/format.rb +0 -12
- data/lib/mutant/reporter/cli/printer.rb +1 -1
- data/lib/mutant/result.rb +1 -1
- data/lib/mutant/runner.rb +50 -29
- data/lib/mutant/runner/collector.rb +10 -11
- data/lib/mutant/subject.rb +1 -4
- data/lib/mutant/subject/method.rb +11 -0
- data/lib/mutant/version.rb +1 -1
- data/meta/send.rb +13 -0
- data/mutant.gemspec +2 -2
- data/spec/spec_helper.rb +2 -9
- data/spec/support/corpus.rb +5 -5
- data/spec/support/rb_bug.rb +18 -0
- data/spec/unit/mutant/cli_spec.rb +2 -2
- data/spec/unit/mutant/expression/methods_spec.rb +7 -1
- data/spec/unit/mutant/isolation_spec.rb +22 -2
- data/spec/unit/mutant/matcher/method/instance_spec.rb +5 -5
- data/spec/unit/mutant/matcher/method/singleton_spec.rb +5 -5
- data/spec/unit/mutant/reporter/cli_spec.rb +13 -14
- data/spec/unit/mutant/runner/collector_spec.rb +198 -0
- data/spec/unit/mutant/runner_spec.rb +2 -3
- data/spec/unit/mutant/subject/method/instance_spec.rb +8 -0
- data/spec/unit/mutant/subject/method/singleton_spec.rb +8 -0
- data/spec/unit/mutant/warning_filter_spec.rb +1 -1
- data/test_app/Gemfile.devtools +1 -1
- data/test_app/lib/test_app.rb +4 -0
- metadata +22 -19
- data/lib/parser_extensions.rb +0 -25
data/lib/mutant/subject.rb
CHANGED
@@ -32,6 +32,17 @@ module Mutant
|
|
32
32
|
end
|
33
33
|
memoize :expression
|
34
34
|
|
35
|
+
# Return match expressions
|
36
|
+
#
|
37
|
+
# @return [Array<Expression>]
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
#
|
41
|
+
def match_expressions
|
42
|
+
[expression].concat(context.match_expressions)
|
43
|
+
end
|
44
|
+
memoize :match_expressions
|
45
|
+
|
35
46
|
private
|
36
47
|
|
37
48
|
# Return scope
|
data/lib/mutant/version.rb
CHANGED
data/meta/send.rb
CHANGED
@@ -145,6 +145,19 @@ Mutant::Meta::Example.add do
|
|
145
145
|
mutation 'foo.instance_of?(bar)'
|
146
146
|
end
|
147
147
|
|
148
|
+
Mutant::Meta::Example.add do
|
149
|
+
source 'foo.is_a?(bar)'
|
150
|
+
|
151
|
+
singleton_mutations
|
152
|
+
mutation 'foo'
|
153
|
+
mutation 'bar'
|
154
|
+
mutation 'foo.is_a?'
|
155
|
+
mutation 'foo.is_a?(nil)'
|
156
|
+
mutation 'foo.is_a?(self)'
|
157
|
+
mutation 'self.is_a?(bar)'
|
158
|
+
mutation 'foo.instance_of?(bar)'
|
159
|
+
end
|
160
|
+
|
148
161
|
Mutant::Meta::Example.add do
|
149
162
|
source 'foo.kind_of?(bar)'
|
150
163
|
|
data/mutant.gemspec
CHANGED
@@ -30,14 +30,14 @@ Gem::Specification.new do |gem|
|
|
30
30
|
gem.add_runtime_dependency('morpher', '~> 0.2.3')
|
31
31
|
gem.add_runtime_dependency('procto', '~> 0.0.2')
|
32
32
|
gem.add_runtime_dependency('abstract_type', '~> 0.0.7')
|
33
|
-
gem.add_runtime_dependency('unparser', '~> 0.1.
|
33
|
+
gem.add_runtime_dependency('unparser', '~> 0.1.15')
|
34
34
|
gem.add_runtime_dependency('ice_nine', '~> 0.11.0')
|
35
35
|
gem.add_runtime_dependency('adamantium', '~> 0.2.0')
|
36
36
|
gem.add_runtime_dependency('memoizable', '~> 0.4.2')
|
37
37
|
gem.add_runtime_dependency('equalizer', '~> 0.0.9')
|
38
|
-
gem.add_runtime_dependency('inflecto', '~> 0.0.2')
|
39
38
|
gem.add_runtime_dependency('anima', '~> 0.2.0')
|
40
39
|
gem.add_runtime_dependency('concord', '~> 0.1.5')
|
41
40
|
|
42
41
|
gem.add_development_dependency('bundler', '~> 1.3', '>= 1.3.5')
|
42
|
+
gem.add_development_dependency('ffi', '~> 1.9.6')
|
43
43
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
1
|
if ENV['COVERAGE'] == 'true'
|
2
2
|
require 'simplecov'
|
3
3
|
|
4
|
-
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
5
|
-
SimpleCov::Formatter::HTMLFormatter,
|
6
|
-
]
|
7
|
-
|
8
4
|
SimpleCov.start do
|
9
5
|
command_name 'spec:unit'
|
10
6
|
|
@@ -16,10 +12,11 @@ if ENV['COVERAGE'] == 'true'
|
|
16
12
|
add_filter 'lib/mutant/zombifier'
|
17
13
|
add_filter 'lib/mutant/zombifier/*'
|
18
14
|
|
19
|
-
minimum_coverage
|
15
|
+
minimum_coverage 97.64 # TODO: raise this to 100, then mutation test
|
20
16
|
end
|
21
17
|
end
|
22
18
|
|
19
|
+
require 'tempfile'
|
23
20
|
require 'concord'
|
24
21
|
require 'adamantium'
|
25
22
|
require 'devtools/spec_helper'
|
@@ -52,8 +49,4 @@ RSpec.configure do |config|
|
|
52
49
|
config.include(CompressHelper)
|
53
50
|
config.include(ParserHelper)
|
54
51
|
config.include(Mutant::AST::Sexp)
|
55
|
-
|
56
|
-
config.expect_with :rspec do |rspec|
|
57
|
-
rspec.syntax = :expect
|
58
|
-
end
|
59
52
|
end
|
data/spec/support/corpus.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'morpher'
|
2
2
|
require 'anima'
|
3
|
+
require 'mutant'
|
3
4
|
|
4
5
|
# Namespace module for corpus testing
|
5
6
|
module Corpus
|
@@ -51,8 +52,8 @@ module Corpus
|
|
51
52
|
start = Time.now
|
52
53
|
paths = Pathname.glob(repo_path.join('**/*.rb')).sort_by(&:size).reverse
|
53
54
|
options = {
|
54
|
-
finish:
|
55
|
-
start:
|
55
|
+
finish: method(:finish),
|
56
|
+
start: method(:start),
|
56
57
|
in_processes: parallel_processes
|
57
58
|
}
|
58
59
|
total = Parallel.map(paths, options) do |path|
|
@@ -135,9 +136,8 @@ module Corpus
|
|
135
136
|
# @api private
|
136
137
|
#
|
137
138
|
def parallel_processes
|
138
|
-
|
139
|
-
|
140
|
-
CIRCLE_CI_CONTAINER_PROCESSES
|
139
|
+
if ENV['CI']
|
140
|
+
Mutant::Config::DEFAULT.jobs
|
141
141
|
else
|
142
142
|
Parallel.processor_count
|
143
143
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module RbBug
|
4
|
+
extend FFI::Library
|
5
|
+
ffi_lib 'ruby'
|
6
|
+
attach_function :rb_bug, [:string, :varargs], :void
|
7
|
+
|
8
|
+
# Call the test bug
|
9
|
+
#
|
10
|
+
# @return [undefined]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
def self.call
|
15
|
+
rb_bug('%s', :string, 'test bug')
|
16
|
+
end
|
17
|
+
|
18
|
+
end # RbBug
|
@@ -128,7 +128,7 @@ Environment:
|
|
128
128
|
--zombie Run mutant zombified
|
129
129
|
-I, --include DIRECTORY Add DIRECTORY to $LOAD_PATH
|
130
130
|
-r, --require NAME Require file with NAME
|
131
|
-
-j, --jobs NUMBER Number of kill
|
131
|
+
-j, --jobs NUMBER Number of kill jobs. Defaults to number of processors.
|
132
132
|
|
133
133
|
Options:
|
134
134
|
--score COVERAGE Fail unless COVERAGE is not reached exactly
|
@@ -178,7 +178,7 @@ Options:
|
|
178
178
|
it_should_behave_like 'a cli parser'
|
179
179
|
|
180
180
|
it 'configures expected coverage' do
|
181
|
-
expect(subject.config.
|
181
|
+
expect(subject.config.jobs).to eql(0)
|
182
182
|
end
|
183
183
|
end
|
184
184
|
|
@@ -16,7 +16,13 @@ RSpec.describe Mutant::Expression::Methods do
|
|
16
16
|
it { should be(object.syntax.length) }
|
17
17
|
end
|
18
18
|
|
19
|
-
context 'when other is
|
19
|
+
context 'when other is matched' do
|
20
|
+
let(:other) { described_class.parse('TestApp::Literal#foo') }
|
21
|
+
|
22
|
+
it { should be(object.syntax.length) }
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when other is an not matched expression' do
|
20
26
|
let(:other) { described_class.parse('Foo*') }
|
21
27
|
|
22
28
|
it { should be(0) }
|
@@ -39,9 +39,29 @@ RSpec.describe Mutant::Isolation::Fork do
|
|
39
39
|
expect(object.call { :foo }).to be(:foo)
|
40
40
|
end
|
41
41
|
|
42
|
-
it 'wraps
|
43
|
-
expect { object.call { fail
|
42
|
+
it 'wraps exceptions' do
|
43
|
+
expect { object.call { fail } }.to raise_error(Mutant::Isolation::Error)
|
44
44
|
end
|
45
45
|
|
46
|
+
it 'wraps exceptions caused by crashing ruby' do
|
47
|
+
expect do
|
48
|
+
object.call do
|
49
|
+
fail RbBug.call
|
50
|
+
end
|
51
|
+
end.to raise_error(Mutant::Isolation::Error)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'redirects $stderr of children to /dev/null' do
|
55
|
+
begin
|
56
|
+
Tempfile.open('mutant-test') do |file|
|
57
|
+
$stderr = file
|
58
|
+
object.call { $stderr.puts('test') }
|
59
|
+
file.rewind
|
60
|
+
expect(file.read).to eql('')
|
61
|
+
end
|
62
|
+
ensure
|
63
|
+
$stderr = STDERR
|
64
|
+
end
|
65
|
+
end
|
46
66
|
end
|
47
67
|
end
|
@@ -43,14 +43,14 @@ RSpec.describe Mutant::Matcher::Method::Instance do
|
|
43
43
|
|
44
44
|
context 'when method is defined once' do
|
45
45
|
let(:scope) { base::DefinedOnce }
|
46
|
-
let(:method_line) {
|
46
|
+
let(:method_line) { 10 }
|
47
47
|
|
48
48
|
it_should_behave_like 'a method matcher'
|
49
49
|
end
|
50
50
|
|
51
51
|
context 'when method is defined once with a memoizer' do
|
52
52
|
let(:scope) { base::WithMemoizer }
|
53
|
-
let(:method_line) {
|
53
|
+
let(:method_line) { 15 }
|
54
54
|
|
55
55
|
it_should_behave_like 'a method matcher'
|
56
56
|
end
|
@@ -58,7 +58,7 @@ RSpec.describe Mutant::Matcher::Method::Instance do
|
|
58
58
|
context 'when method is defined multiple times' do
|
59
59
|
context 'on different lines' do
|
60
60
|
let(:scope) { base::DefinedMultipleTimes::DifferentLines }
|
61
|
-
let(:method_line) {
|
61
|
+
let(:method_line) { 24 }
|
62
62
|
let(:method_arity) { 1 }
|
63
63
|
|
64
64
|
it_should_behave_like 'a method matcher'
|
@@ -66,7 +66,7 @@ RSpec.describe Mutant::Matcher::Method::Instance do
|
|
66
66
|
|
67
67
|
context 'on the same line' do
|
68
68
|
let(:scope) { base::DefinedMultipleTimes::SameLineSameScope }
|
69
|
-
let(:method_line) {
|
69
|
+
let(:method_line) { 29 }
|
70
70
|
let(:method_arity) { 1 }
|
71
71
|
|
72
72
|
it_should_behave_like 'a method matcher'
|
@@ -74,7 +74,7 @@ RSpec.describe Mutant::Matcher::Method::Instance do
|
|
74
74
|
|
75
75
|
context 'on the same line with different scope' do
|
76
76
|
let(:scope) { base::DefinedMultipleTimes::SameLineDifferentScope }
|
77
|
-
let(:method_line) {
|
77
|
+
let(:method_line) { 33 }
|
78
78
|
let(:method_arity) { 1 }
|
79
79
|
|
80
80
|
it_should_behave_like 'a method matcher'
|
@@ -23,7 +23,7 @@ RSpec.describe Mutant::Matcher::Method::Singleton, '#each' do
|
|
23
23
|
|
24
24
|
context 'when also defined on lvar' do
|
25
25
|
let(:scope) { base::AlsoDefinedOnLvar }
|
26
|
-
let(:method_line) {
|
26
|
+
let(:method_line) { 66 }
|
27
27
|
|
28
28
|
it_should_behave_like 'a method matcher'
|
29
29
|
|
@@ -37,7 +37,7 @@ RSpec.describe Mutant::Matcher::Method::Singleton, '#each' do
|
|
37
37
|
|
38
38
|
context 'when defined on self' do
|
39
39
|
let(:scope) { base::DefinedOnSelf }
|
40
|
-
let(:method_line) {
|
40
|
+
let(:method_line) { 61 }
|
41
41
|
|
42
42
|
it_should_behave_like 'a method matcher'
|
43
43
|
end
|
@@ -46,13 +46,13 @@ RSpec.describe Mutant::Matcher::Method::Singleton, '#each' do
|
|
46
46
|
|
47
47
|
context 'inside namespace' do
|
48
48
|
let(:scope) { base::DefinedOnConstant::InsideNamespace }
|
49
|
-
let(:method_line) {
|
49
|
+
let(:method_line) { 71 }
|
50
50
|
|
51
51
|
it_should_behave_like 'a method matcher'
|
52
52
|
end
|
53
53
|
|
54
54
|
context 'outside namespace' do
|
55
|
-
let(:method_line) {
|
55
|
+
let(:method_line) { 78 }
|
56
56
|
let(:scope) { base::DefinedOnConstant::OutsideNamespace }
|
57
57
|
|
58
58
|
it_should_behave_like 'a method matcher'
|
@@ -62,7 +62,7 @@ RSpec.describe Mutant::Matcher::Method::Singleton, '#each' do
|
|
62
62
|
context 'when defined multiple times in the same line' do
|
63
63
|
context 'with method on different scope' do
|
64
64
|
let(:scope) { base::DefinedMultipleTimes::SameLine::DifferentScope }
|
65
|
-
let(:method_line) {
|
65
|
+
let(:method_line) { 97 }
|
66
66
|
let(:method_arity) { 1 }
|
67
67
|
|
68
68
|
it_should_behave_like 'a method matcher'
|
@@ -33,7 +33,6 @@ RSpec.describe Mutant::Reporter::CLI do
|
|
33
33
|
|
34
34
|
let(:result) do
|
35
35
|
Mutant::Result::Env.new(
|
36
|
-
done: true,
|
37
36
|
env: env,
|
38
37
|
runtime: 1.1,
|
39
38
|
subject_results: subject_results
|
@@ -51,7 +50,7 @@ RSpec.describe Mutant::Reporter::CLI do
|
|
51
50
|
)
|
52
51
|
end
|
53
52
|
|
54
|
-
let(:config) { Mutant::Config::DEFAULT.update(
|
53
|
+
let(:config) { Mutant::Config::DEFAULT.update(jobs: 1) }
|
55
54
|
let(:mutation_class) { Mutant::Mutation::Evil }
|
56
55
|
let(:matchable_scopes) { double('Matchable Scopes', length: 10) }
|
57
56
|
|
@@ -199,7 +198,7 @@ RSpec.describe Mutant::Reporter::CLI do
|
|
199
198
|
Matcher: #<Mutant::Matcher::Config match_expressions=[] subject_ignores=[] subject_selects=[]>
|
200
199
|
Integration: null
|
201
200
|
Expect Coverage: 100.00%
|
202
|
-
|
201
|
+
Jobs: 1
|
203
202
|
Includes: []
|
204
203
|
Requires: []
|
205
204
|
REPORT
|
@@ -257,7 +256,7 @@ RSpec.describe Mutant::Reporter::CLI do
|
|
257
256
|
Matcher: #<Mutant::Matcher::Config match_expressions=[] subject_ignores=[] subject_selects=[]>
|
258
257
|
Integration: null
|
259
258
|
Expect Coverage: 100.00%
|
260
|
-
|
259
|
+
Jobs: 1
|
261
260
|
Includes: []
|
262
261
|
Requires: []
|
263
262
|
Available Subjects: 1
|
@@ -286,7 +285,7 @@ RSpec.describe Mutant::Reporter::CLI do
|
|
286
285
|
Matcher: #<Mutant::Matcher::Config match_expressions=[] subject_ignores=[] subject_selects=[]>
|
287
286
|
Integration: null
|
288
287
|
Expect Coverage: 100.00%
|
289
|
-
|
288
|
+
Jobs: 1
|
290
289
|
Includes: []
|
291
290
|
Requires: []
|
292
291
|
Available Subjects: 1
|
@@ -323,7 +322,7 @@ RSpec.describe Mutant::Reporter::CLI do
|
|
323
322
|
Matcher: #<Mutant::Matcher::Config match_expressions=[] subject_ignores=[] subject_selects=[]>
|
324
323
|
Integration: null
|
325
324
|
Expect Coverage: 100.00%
|
326
|
-
|
325
|
+
Jobs: 1
|
327
326
|
Includes: []
|
328
327
|
Requires: []
|
329
328
|
Available Subjects: 1
|
@@ -350,7 +349,7 @@ RSpec.describe Mutant::Reporter::CLI do
|
|
350
349
|
Matcher: #<Mutant::Matcher::Config match_expressions=[] subject_ignores=[] subject_selects=[]>
|
351
350
|
Integration: null
|
352
351
|
Expect Coverage: 100.00%
|
353
|
-
|
352
|
+
Jobs: 1
|
354
353
|
Includes: []
|
355
354
|
Requires: []
|
356
355
|
Available Subjects: 1
|
@@ -385,7 +384,7 @@ RSpec.describe Mutant::Reporter::CLI do
|
|
385
384
|
Matcher: #<Mutant::Matcher::Config match_expressions=[] subject_ignores=[] subject_selects=[]>
|
386
385
|
Integration: null
|
387
386
|
Expect Coverage: 100.00%
|
388
|
-
|
387
|
+
Jobs: 1
|
389
388
|
Includes: []
|
390
389
|
Requires: []
|
391
390
|
Available Subjects: 1
|
@@ -418,7 +417,7 @@ RSpec.describe Mutant::Reporter::CLI do
|
|
418
417
|
Matcher: #<Mutant::Matcher::Config match_expressions=[] subject_ignores=[] subject_selects=[]>
|
419
418
|
Integration: null
|
420
419
|
Expect Coverage: 100.00%
|
421
|
-
|
420
|
+
Jobs: 1
|
422
421
|
Includes: []
|
423
422
|
Requires: []
|
424
423
|
Available Subjects: 1
|
@@ -451,7 +450,7 @@ RSpec.describe Mutant::Reporter::CLI do
|
|
451
450
|
Matcher: #<Mutant::Matcher::Config match_expressions=[] subject_ignores=[] subject_selects=[]>
|
452
451
|
Integration: null
|
453
452
|
Expect Coverage: 100.00%
|
454
|
-
|
453
|
+
Jobs: 1
|
455
454
|
Includes: []
|
456
455
|
Requires: []
|
457
456
|
Available Subjects: 1
|
@@ -492,7 +491,7 @@ RSpec.describe Mutant::Reporter::CLI do
|
|
492
491
|
Matcher: #<Mutant::Matcher::Config match_expressions=[] subject_ignores=[] subject_selects=[]>
|
493
492
|
Integration: null
|
494
493
|
Expect Coverage: 100.00%
|
495
|
-
|
494
|
+
Jobs: 1
|
496
495
|
Includes: []
|
497
496
|
Requires: []
|
498
497
|
Available Subjects: 1
|
@@ -527,7 +526,7 @@ RSpec.describe Mutant::Reporter::CLI do
|
|
527
526
|
Matcher: #<Mutant::Matcher::Config match_expressions=[] subject_ignores=[] subject_selects=[]>
|
528
527
|
Integration: null
|
529
528
|
Expect Coverage: 100.00%
|
530
|
-
|
529
|
+
Jobs: 1
|
531
530
|
Includes: []
|
532
531
|
Requires: []
|
533
532
|
Available Subjects: 1
|
@@ -548,14 +547,14 @@ RSpec.describe Mutant::Reporter::CLI do
|
|
548
547
|
let(:subjects) { [] }
|
549
548
|
let(:subject_results) { [] }
|
550
549
|
|
551
|
-
let(:config) { Mutant::Config::DEFAULT.update(
|
550
|
+
let(:config) { Mutant::Config::DEFAULT.update(jobs: 1, includes: %w[include-dir], requires: %w[require-name]) }
|
552
551
|
|
553
552
|
it_reports(<<-REPORT)
|
554
553
|
Mutant configuration:
|
555
554
|
Matcher: #<Mutant::Matcher::Config match_expressions=[] subject_ignores=[] subject_selects=[]>
|
556
555
|
Integration: null
|
557
556
|
Expect Coverage: 100.00%
|
558
|
-
|
557
|
+
Jobs: 1
|
559
558
|
Includes: ["include-dir"]
|
560
559
|
Requires: ["require-name"]
|
561
560
|
Available Subjects: 0
|
@@ -0,0 +1,198 @@
|
|
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
|