mutant 0.6.3 → 0.6.4

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -2
  3. data/Changelog.md +6 -0
  4. data/Gemfile.devtools +1 -1
  5. data/bin/mutant +2 -1
  6. data/config/flay.yml +1 -1
  7. data/config/reek.yml +3 -0
  8. data/lib/mutant.rb +5 -3
  9. data/lib/mutant/cli.rb +2 -2
  10. data/lib/mutant/config.rb +1 -1
  11. data/lib/mutant/diff.rb +1 -1
  12. data/lib/mutant/env.rb +1 -1
  13. data/lib/mutant/expression/methods.rb +16 -0
  14. data/lib/mutant/isolation.rb +16 -2
  15. data/lib/mutant/mutator/node/const.rb +1 -1
  16. data/lib/mutant/mutator/node/generic.rb +1 -1
  17. data/lib/mutant/mutator/registry.rb +1 -1
  18. data/lib/mutant/reporter/cli.rb +1 -1
  19. data/lib/mutant/reporter/cli/format.rb +0 -12
  20. data/lib/mutant/reporter/cli/printer.rb +1 -1
  21. data/lib/mutant/result.rb +1 -1
  22. data/lib/mutant/runner.rb +50 -29
  23. data/lib/mutant/runner/collector.rb +10 -11
  24. data/lib/mutant/subject.rb +1 -4
  25. data/lib/mutant/subject/method.rb +11 -0
  26. data/lib/mutant/version.rb +1 -1
  27. data/meta/send.rb +13 -0
  28. data/mutant.gemspec +2 -2
  29. data/spec/spec_helper.rb +2 -9
  30. data/spec/support/corpus.rb +5 -5
  31. data/spec/support/rb_bug.rb +18 -0
  32. data/spec/unit/mutant/cli_spec.rb +2 -2
  33. data/spec/unit/mutant/expression/methods_spec.rb +7 -1
  34. data/spec/unit/mutant/isolation_spec.rb +22 -2
  35. data/spec/unit/mutant/matcher/method/instance_spec.rb +5 -5
  36. data/spec/unit/mutant/matcher/method/singleton_spec.rb +5 -5
  37. data/spec/unit/mutant/reporter/cli_spec.rb +13 -14
  38. data/spec/unit/mutant/runner/collector_spec.rb +198 -0
  39. data/spec/unit/mutant/runner_spec.rb +2 -3
  40. data/spec/unit/mutant/subject/method/instance_spec.rb +8 -0
  41. data/spec/unit/mutant/subject/method/singleton_spec.rb +8 -0
  42. data/spec/unit/mutant/warning_filter_spec.rb +1 -1
  43. data/test_app/Gemfile.devtools +1 -1
  44. data/test_app/lib/test_app.rb +4 -0
  45. metadata +22 -19
  46. data/lib/parser_extensions.rb +0 -25
@@ -114,10 +114,7 @@ module Mutant
114
114
  #
115
115
  # @api private
116
116
  #
117
- def match_expressions
118
- [expression].concat(context.match_expressions)
119
- end
120
- memoize :match_expressions
117
+ abstract_method :match_expressions
121
118
 
122
119
  private
123
120
 
@@ -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
@@ -1,4 +1,4 @@
1
1
  module Mutant
2
2
  # The current mutant version
3
- VERSION = '0.6.3'.freeze
3
+ VERSION = '0.6.4'.freeze
4
4
  end # Mutant
@@ -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
 
@@ -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.14')
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
@@ -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 89.77 # TODO: raise this to 100, then mutation test
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
@@ -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: method(:finish),
55
- start: method(: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
- case
139
- when Devtools.circle_ci?
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 processes. Defaults to number of processors.
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.processes).to eql(0)
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 an unequivalent expression' do
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 Parallel::DeadWorker exceptions' do
43
- expect { object.call { fail Parallel::DeadWorker } }.to raise_error(Mutant::Isolation::Error)
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) { 7 }
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) { 12 }
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) { 21 }
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) { 26 }
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) { 30 }
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) { 63 }
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) { 58 }
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) { 68 }
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) { 75 }
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) { 94 }
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(processes: 1) }
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
- Processes: 1
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
- Processes: 1
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
- Processes: 1
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
- Processes: 1
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
- Processes: 1
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
- Processes: 1
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
- Processes: 1
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
- Processes: 1
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
- Processes: 1
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
- Processes: 1
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(processes: 1, includes: %w[include-dir], requires: %w[require-name]) }
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
- Processes: 1
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