mutant 0.8.3 → 0.8.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog.md +8 -0
- data/Gemfile +1 -4
- data/Gemfile.shared +3 -0
- data/Rakefile +0 -2
- data/config/flay.yml +1 -1
- data/config/rubocop.yml +8 -0
- data/lib/mutant.rb +2 -0
- data/lib/mutant/ast/meta/restarg.rb +15 -0
- data/lib/mutant/ast/meta/symbol.rb +15 -0
- data/lib/mutant/cli.rb +11 -11
- data/lib/mutant/config.rb +1 -1
- data/lib/mutant/env.rb +1 -1
- data/lib/mutant/matcher/config.rb +2 -2
- data/lib/mutant/mutator/node/block.rb +16 -0
- data/lib/mutant/mutator/node/define.rb +14 -0
- data/lib/mutant/mutator/node/send.rb +12 -1
- data/lib/mutant/mutator/node/send/binary.rb +25 -1
- data/lib/mutant/parallel.rb +2 -2
- data/lib/mutant/result.rb +1 -1
- data/lib/mutant/runner/sink.rb +2 -2
- data/lib/mutant/version.rb +1 -1
- data/meta/begin.rb +0 -2
- data/meta/block.rb +15 -0
- data/meta/def.rb +25 -0
- data/meta/or_asgn.rb +9 -0
- data/meta/send.rb +41 -1
- data/mutant-rspec.gemspec +0 -2
- data/mutant.gemspec +5 -6
- data/spec/spec_helper.rb +1 -1
- data/spec/support/corpus.rb +1 -0
- data/spec/support/shared_context.rb +5 -5
- data/spec/unit/mutant/cli_spec.rb +6 -6
- data/spec/unit/mutant/env/boostrap_spec.rb +3 -3
- data/spec/unit/mutant/env_spec.rb +1 -1
- data/spec/unit/mutant/matcher/compiler_spec.rb +1 -1
- data/spec/unit/mutant/parallel/master_spec.rb +2 -2
- data/spec/unit/mutant/reporter/cli/printer/config_spec.rb +1 -1
- data/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb +3 -3
- data/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb +1 -1
- data/spec/unit/mutant/reporter/cli/printer/mutation_progress_result_spec.rb +2 -2
- data/spec/unit/mutant/reporter/cli/printer/mutation_result_spec.rb +3 -3
- data/spec/unit/mutant/reporter/cli/printer/status_progressive_spec.rb +5 -5
- data/spec/unit/mutant/reporter/cli/printer/status_spec.rb +5 -5
- data/spec/unit/mutant/reporter/cli/printer/subject_progress_spec.rb +2 -2
- data/spec/unit/mutant/reporter/cli/printer/subject_result_spec.rb +2 -2
- data/spec/unit/mutant/reporter/cli_spec.rb +6 -4
- data/spec/unit/mutant/result_spec.rb +23 -0
- data/spec/unit/mutant/runner/sink/mutation_spec.rb +8 -8
- data/spec/unit/mutant/selector/expression_spec.rb +1 -1
- data/test_app/Gemfile.rspec3.2 +1 -0
- data/test_app/Gemfile.rspec3.3 +1 -0
- data/test_app/spec/unit/test_app/literal_spec.rb +0 -2
- metadata +27 -18
- data/Guardfile +0 -16
- data/test_app/Gemfile.rspec3.0 +0 -6
- data/test_app/Gemfile.rspec3.1 +0 -6
data/meta/or_asgn.rb
CHANGED
@@ -21,6 +21,15 @@ Mutant::Meta::Example.add do
|
|
21
21
|
mutation '@a ||= 2'
|
22
22
|
end
|
23
23
|
|
24
|
+
Mutant::Meta::Example.add do
|
25
|
+
source '@a ||= self.bar'
|
26
|
+
|
27
|
+
singleton_mutations
|
28
|
+
mutation '@a ||= nil'
|
29
|
+
mutation '@a ||= self'
|
30
|
+
mutation '@a ||= bar'
|
31
|
+
end
|
32
|
+
|
24
33
|
Mutant::Meta::Example.add do
|
25
34
|
source 'foo[:bar] ||= 1'
|
26
35
|
|
data/meta/send.rb
CHANGED
@@ -14,6 +14,32 @@ Mutant::Meta::Example.add do
|
|
14
14
|
mutation 'b'
|
15
15
|
end
|
16
16
|
|
17
|
+
Mutant::Meta::Example.add do
|
18
|
+
source 'A.const_get(:B)'
|
19
|
+
|
20
|
+
singleton_mutations
|
21
|
+
mutation 'A::B'
|
22
|
+
mutation 'A.const_get'
|
23
|
+
mutation 'A'
|
24
|
+
mutation ':B'
|
25
|
+
mutation 'A.const_get(nil)'
|
26
|
+
mutation 'A.const_get(self)'
|
27
|
+
mutation 'A.const_get(:B__mutant__)'
|
28
|
+
mutation 'self.const_get(:B)'
|
29
|
+
end
|
30
|
+
|
31
|
+
Mutant::Meta::Example.add do
|
32
|
+
source 'A.const_get(bar)'
|
33
|
+
|
34
|
+
singleton_mutations
|
35
|
+
mutation 'A.const_get'
|
36
|
+
mutation 'A'
|
37
|
+
mutation 'bar'
|
38
|
+
mutation 'A.const_get(nil)'
|
39
|
+
mutation 'A.const_get(self)'
|
40
|
+
mutation 'self.const_get(bar)'
|
41
|
+
end
|
42
|
+
|
17
43
|
Mutant::Meta::Example.add do
|
18
44
|
source 'a >= b'
|
19
45
|
|
@@ -425,7 +451,7 @@ Mutant::Meta::Example.add do
|
|
425
451
|
mutation 'self[*bar]'
|
426
452
|
end
|
427
453
|
|
428
|
-
(Mutant::AST::Types::BINARY_METHOD_OPERATORS - %i[<= >= < > == eql?]).each do |operator|
|
454
|
+
(Mutant::AST::Types::BINARY_METHOD_OPERATORS - %i[<= >= < > == != eql?]).each do |operator|
|
429
455
|
Mutant::Meta::Example.add do
|
430
456
|
source "true #{operator} false"
|
431
457
|
|
@@ -438,3 +464,17 @@ end
|
|
438
464
|
mutation "true #{operator} nil"
|
439
465
|
end
|
440
466
|
end
|
467
|
+
|
468
|
+
Mutant::Meta::Example.add do
|
469
|
+
source 'a != b'
|
470
|
+
|
471
|
+
singleton_mutations
|
472
|
+
mutation 'nil != b'
|
473
|
+
mutation 'self != b'
|
474
|
+
mutation 'a'
|
475
|
+
mutation 'b'
|
476
|
+
mutation 'a != nil'
|
477
|
+
mutation 'a != self'
|
478
|
+
mutation '!a.eql?(b)'
|
479
|
+
mutation '!a.equal?(b)'
|
480
|
+
end
|
data/mutant-rspec.gemspec
CHANGED
data/mutant.gemspec
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
#
|
3
1
|
require File.expand_path('../lib/mutant/version', __FILE__)
|
4
2
|
|
5
3
|
Gem::Specification.new do |gem|
|
@@ -27,7 +25,7 @@ Gem::Specification.new do |gem|
|
|
27
25
|
gem.add_runtime_dependency('ast', '~> 2.1')
|
28
26
|
gem.add_runtime_dependency('diff-lcs', '~> 1.2')
|
29
27
|
gem.add_runtime_dependency('parallel', '~> 1.3')
|
30
|
-
gem.add_runtime_dependency('morpher', '~> 0.2.
|
28
|
+
gem.add_runtime_dependency('morpher', '~> 0.2.5')
|
31
29
|
gem.add_runtime_dependency('procto', '~> 0.0.2')
|
32
30
|
gem.add_runtime_dependency('abstract_type', '~> 0.0.7')
|
33
31
|
gem.add_runtime_dependency('unparser', '~> 0.2.4')
|
@@ -35,9 +33,10 @@ Gem::Specification.new do |gem|
|
|
35
33
|
gem.add_runtime_dependency('adamantium', '~> 0.2.0')
|
36
34
|
gem.add_runtime_dependency('memoizable', '~> 0.4.2')
|
37
35
|
gem.add_runtime_dependency('equalizer', '~> 0.0.9')
|
38
|
-
gem.add_runtime_dependency('anima', '~> 0.
|
36
|
+
gem.add_runtime_dependency('anima', '~> 0.3.0')
|
39
37
|
gem.add_runtime_dependency('concord', '~> 0.1.5')
|
40
38
|
|
41
|
-
gem.add_development_dependency('
|
42
|
-
gem.add_development_dependency('
|
39
|
+
gem.add_development_dependency('devtools', '~> 0.1.1')
|
40
|
+
gem.add_development_dependency('bundler', '~> 1.10')
|
41
|
+
gem.add_development_dependency('ffi', '~> 1.9.6')
|
43
42
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -33,7 +33,7 @@ $LOAD_PATH << File.join(TestApp.root, 'lib')
|
|
33
33
|
require 'test_app'
|
34
34
|
|
35
35
|
module Fixtures
|
36
|
-
TEST_CONFIG = Mutant::Config::DEFAULT.
|
36
|
+
TEST_CONFIG = Mutant::Config::DEFAULT.with(reporter: Mutant::Reporter::Trace.new)
|
37
37
|
TEST_CACHE = Mutant::Cache.new
|
38
38
|
TEST_ENV = Mutant::Env::Bootstrap.(TEST_CONFIG, TEST_CACHE)
|
39
39
|
end # Fixtures
|
data/spec/support/corpus.rb
CHANGED
@@ -134,6 +134,7 @@ module MutantSpec
|
|
134
134
|
repo_path.join('Gemfile').open('a') do |file|
|
135
135
|
file << "gem 'mutant', path: '#{relative}'\n"
|
136
136
|
file << "gem 'mutant-rspec', path: '#{relative}'\n"
|
137
|
+
file << "eval_gemfile File.expand_path('#{relative.join('Gemfile.shared')}')\n"
|
137
138
|
end
|
138
139
|
lockfile = repo_path.join('Gemfile.lock')
|
139
140
|
lockfile.delete if lockfile.exist?
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# rubocop:disable ModuleLength
|
2
2
|
module SharedContext
|
3
|
-
def
|
3
|
+
def with(name, &block)
|
4
4
|
define_method(name) do
|
5
|
-
super().
|
5
|
+
super().with(instance_eval(&block))
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
@@ -51,7 +51,7 @@ module SharedContext
|
|
51
51
|
end
|
52
52
|
|
53
53
|
let(:config) do
|
54
|
-
Mutant::Config::DEFAULT.
|
54
|
+
Mutant::Config::DEFAULT.with(
|
55
55
|
jobs: 1,
|
56
56
|
reporter: Mutant::Reporter::Trace.new
|
57
57
|
)
|
@@ -120,11 +120,11 @@ module SharedContext
|
|
120
120
|
end
|
121
121
|
|
122
122
|
let(:empty_subject_a_result) do
|
123
|
-
subject_a_result.
|
123
|
+
subject_a_result.with(mutation_results: [])
|
124
124
|
end
|
125
125
|
|
126
126
|
let(:partial_subject_a_result) do
|
127
|
-
subject_a_result.
|
127
|
+
subject_a_result.with(mutation_results: [mutation_a_result])
|
128
128
|
end
|
129
129
|
end
|
130
130
|
end
|
@@ -73,7 +73,7 @@ RSpec.describe Mutant::CLI do
|
|
73
73
|
|
74
74
|
let(:default_matcher_config) do
|
75
75
|
Mutant::Matcher::Config::DEFAULT
|
76
|
-
.
|
76
|
+
.with(match_expressions: expressions.map(&method(:parse_expression)))
|
77
77
|
end
|
78
78
|
|
79
79
|
let(:flags) { [] }
|
@@ -221,13 +221,13 @@ Options:
|
|
221
221
|
end
|
222
222
|
end
|
223
223
|
|
224
|
-
context 'with require
|
225
|
-
let(:flags) { %w[--require foo] }
|
224
|
+
context 'with require flags' do
|
225
|
+
let(:flags) { %w[--require foo --require bar] }
|
226
226
|
|
227
227
|
it_should_behave_like 'a cli parser'
|
228
228
|
|
229
229
|
it 'configures requires' do
|
230
|
-
expect(subject.config.requires).to eql(%w[foo])
|
230
|
+
expect(subject.config.requires).to eql(%w[foo bar])
|
231
231
|
end
|
232
232
|
end
|
233
233
|
|
@@ -235,7 +235,7 @@ Options:
|
|
235
235
|
let(:flags) { %w[--since master] }
|
236
236
|
|
237
237
|
let(:expected_matcher_config) do
|
238
|
-
default_matcher_config.
|
238
|
+
default_matcher_config.with(
|
239
239
|
subject_filters: [
|
240
240
|
Mutant::Repository::SubjectFilter.new(Mutant::Repository::Diff.new('HEAD', 'master'))
|
241
241
|
]
|
@@ -249,7 +249,7 @@ Options:
|
|
249
249
|
let(:flags) { %w[--ignore-subject Foo::Bar] }
|
250
250
|
|
251
251
|
let(:expected_matcher_config) do
|
252
|
-
default_matcher_config.
|
252
|
+
default_matcher_config.with(ignore_expressions: [parse_expression('Foo::Bar')])
|
253
253
|
end
|
254
254
|
|
255
255
|
it_should_behave_like 'a cli parser'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
RSpec.describe Mutant::Env::Bootstrap do
|
2
2
|
let(:config) do
|
3
|
-
Mutant::Config::DEFAULT.
|
3
|
+
Mutant::Config::DEFAULT.with(
|
4
4
|
jobs: 1,
|
5
5
|
reporter: Mutant::Reporter::Trace.new,
|
6
6
|
includes: [],
|
@@ -69,7 +69,7 @@ RSpec.describe Mutant::Env::Bootstrap do
|
|
69
69
|
end
|
70
70
|
|
71
71
|
context 'when includes are present' do
|
72
|
-
let(:config) { super().
|
72
|
+
let(:config) { super().with(includes: %w[foo bar]) }
|
73
73
|
|
74
74
|
before do
|
75
75
|
%w[foo bar].each do |component|
|
@@ -121,7 +121,7 @@ RSpec.describe Mutant::Env::Bootstrap do
|
|
121
121
|
end
|
122
122
|
|
123
123
|
let(:expected_env) do
|
124
|
-
super().
|
124
|
+
super().with(
|
125
125
|
subjects: subjects,
|
126
126
|
mutations: mutations
|
127
127
|
)
|
@@ -16,7 +16,7 @@ RSpec.describe Mutant::Env do
|
|
16
16
|
let(:integration) { integration_class.new(config) }
|
17
17
|
|
18
18
|
let(:config) do
|
19
|
-
Mutant::Config::DEFAULT.
|
19
|
+
Mutant::Config::DEFAULT.with(isolation: isolation, integration: integration_class)
|
20
20
|
end
|
21
21
|
|
22
22
|
let(:isolation) { double('Isolation') }
|
@@ -18,7 +18,7 @@ RSpec.describe Mutant::Matcher::Compiler do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
describe '.call' do
|
21
|
-
subject { object.call(env, matcher_config.
|
21
|
+
subject { object.call(env, matcher_config.with(attributes)) }
|
22
22
|
|
23
23
|
let(:matcher_config) { Mutant::Matcher::Config::DEFAULT }
|
24
24
|
|
@@ -92,8 +92,8 @@ RSpec.describe Mutant::Parallel::Master do
|
|
92
92
|
subject { described_class.call(config) }
|
93
93
|
|
94
94
|
context 'with multiple workers configured' do
|
95
|
-
let(:config) { super().
|
96
|
-
let(:expected_results) { []
|
95
|
+
let(:config) { super().with(jobs: 2) }
|
96
|
+
let(:expected_results) { [] }
|
97
97
|
|
98
98
|
before do
|
99
99
|
expect(Mutant::Parallel::Worker).to receive(:run).with(
|
@@ -17,7 +17,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer::Config do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
context 'with non default coverage expectation' do
|
20
|
-
|
20
|
+
with(:config) { { expected_coverage: 0.1r } }
|
21
21
|
|
22
22
|
it_reports(<<-REPORT)
|
23
23
|
Mutant configuration:
|
@@ -1,13 +1,13 @@
|
|
1
1
|
RSpec.describe Mutant::Reporter::CLI::Printer::EnvProgress do
|
2
2
|
setup_shared_context
|
3
3
|
|
4
|
-
|
4
|
+
with(:config) { { expected_coverage: 0.1r } }
|
5
5
|
|
6
6
|
let(:reportable) { env_result }
|
7
7
|
|
8
8
|
describe '.call' do
|
9
9
|
context 'without progress' do
|
10
|
-
|
10
|
+
with(:subject_a_result) { { mutation_results: [] } }
|
11
11
|
|
12
12
|
it_reports <<-'STR'
|
13
13
|
Mutant configuration:
|
@@ -51,7 +51,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer::EnvProgress do
|
|
51
51
|
end
|
52
52
|
|
53
53
|
context 'on partial coverage' do
|
54
|
-
|
54
|
+
with(:mutation_a_test_result) { { passed: true } }
|
55
55
|
|
56
56
|
it_reports <<-'STR'
|
57
57
|
Mutant configuration:
|
@@ -9,13 +9,13 @@ RSpec.describe Mutant::Reporter::CLI::Printer::MutationProgressResult do
|
|
9
9
|
|
10
10
|
describe '.run' do
|
11
11
|
context 'on killed mutant' do
|
12
|
-
|
12
|
+
with(:mutation_a_test_result) { { passed: true } }
|
13
13
|
|
14
14
|
it_reports Mutant::Color::RED.format('F')
|
15
15
|
end
|
16
16
|
|
17
17
|
context 'on alive mutant' do
|
18
|
-
|
18
|
+
with(:mutation_a_test_result) { { passed: false } }
|
19
19
|
|
20
20
|
it_reports Mutant::Color::GREEN.format('.')
|
21
21
|
end
|
@@ -5,7 +5,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer::MutationResult do
|
|
5
5
|
|
6
6
|
describe '.call' do
|
7
7
|
context 'failed kill' do
|
8
|
-
|
8
|
+
with(:mutation_a_test_result) { { passed: true } }
|
9
9
|
|
10
10
|
context 'on evil mutation' do
|
11
11
|
context 'with a diff' do
|
@@ -61,7 +61,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer::MutationResult do
|
|
61
61
|
end
|
62
62
|
|
63
63
|
context 'on neutral mutation' do
|
64
|
-
|
64
|
+
with(:mutation_a_test_result) { { passed: false } }
|
65
65
|
|
66
66
|
let(:mutation_a) do
|
67
67
|
Mutant::Mutation::Neutral.new(subject_a, s(:true))
|
@@ -86,7 +86,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer::MutationResult do
|
|
86
86
|
end
|
87
87
|
|
88
88
|
context 'on noop mutation' do
|
89
|
-
|
89
|
+
with(:mutation_a_test_result) { { passed: false } }
|
90
90
|
|
91
91
|
let(:mutation_a) do
|
92
92
|
Mutant::Mutation::Noop.new(subject_a, s(:true))
|
@@ -5,14 +5,14 @@ RSpec.describe Mutant::Reporter::CLI::Printer::StatusProgressive do
|
|
5
5
|
|
6
6
|
describe '.call' do
|
7
7
|
context 'with empty scheduler' do
|
8
|
-
|
8
|
+
with(:env_result) { { subject_results: [] } }
|
9
9
|
|
10
10
|
it_reports <<-REPORT
|
11
11
|
(00/02) 100% - killtime: 0.00s runtime: 4.00s overhead: 4.00s
|
12
12
|
REPORT
|
13
13
|
|
14
14
|
context 'on non default coverage expectation' do
|
15
|
-
|
15
|
+
with(:config) { { expected_coverage: 0.1r } }
|
16
16
|
|
17
17
|
it_reports <<-REPORT
|
18
18
|
(00/02) 100% - killtime: 0.00s runtime: 4.00s overhead: 4.00s
|
@@ -22,7 +22,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer::StatusProgressive do
|
|
22
22
|
|
23
23
|
context 'with scheduler active on one subject' do
|
24
24
|
context 'without progress' do
|
25
|
-
|
25
|
+
with(:status) { { active_jobs: [].to_set } }
|
26
26
|
|
27
27
|
it_reports(<<-REPORT)
|
28
28
|
(02/02) 100% - killtime: 2.00s runtime: 4.00s overhead: 2.00s
|
@@ -30,10 +30,10 @@ RSpec.describe Mutant::Reporter::CLI::Printer::StatusProgressive do
|
|
30
30
|
end
|
31
31
|
|
32
32
|
context 'with progress' do
|
33
|
-
|
33
|
+
with(:status) { { active_jobs: [job_b, job_a].to_set } }
|
34
34
|
|
35
35
|
context 'on failure' do
|
36
|
-
|
36
|
+
with(:mutation_a_test_result) { { passed: true } }
|
37
37
|
|
38
38
|
it_reports(<<-REPORT)
|
39
39
|
(01/02) 50% - killtime: 2.00s runtime: 4.00s overhead: 2.00s
|
@@ -5,7 +5,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer::Status do
|
|
5
5
|
|
6
6
|
describe '.call' do
|
7
7
|
context 'with empty scheduler' do
|
8
|
-
|
8
|
+
with(:env_result) { { subject_results: [] } }
|
9
9
|
|
10
10
|
it_reports <<-REPORT
|
11
11
|
Mutant configuration:
|
@@ -28,7 +28,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer::Status do
|
|
28
28
|
REPORT
|
29
29
|
|
30
30
|
context 'on non default coverage expectation' do
|
31
|
-
|
31
|
+
with(:config) { { expected_coverage: 0.1r } }
|
32
32
|
|
33
33
|
it_reports <<-REPORT
|
34
34
|
Mutant configuration:
|
@@ -54,7 +54,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer::Status do
|
|
54
54
|
|
55
55
|
context 'with scheduler active on one subject' do
|
56
56
|
context 'without progress' do
|
57
|
-
|
57
|
+
with(:status) { { active_jobs: [].to_set } }
|
58
58
|
|
59
59
|
it_reports(<<-REPORT)
|
60
60
|
Mutant configuration:
|
@@ -78,10 +78,10 @@ RSpec.describe Mutant::Reporter::CLI::Printer::Status do
|
|
78
78
|
end
|
79
79
|
|
80
80
|
context 'with progress' do
|
81
|
-
|
81
|
+
with(:status) { { active_jobs: [job_b, job_a].to_set } }
|
82
82
|
|
83
83
|
context 'on failure' do
|
84
|
-
|
84
|
+
with(:mutation_a_test_result) { { passed: true } }
|
85
85
|
|
86
86
|
it_reports(<<-REPORT)
|
87
87
|
Mutant configuration:
|
@@ -13,7 +13,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer::SubjectProgress do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
context 'on partial coverage' do
|
16
|
-
|
16
|
+
with(:mutation_a_test_result) { { passed: true } }
|
17
17
|
|
18
18
|
it_reports <<-'STR'
|
19
19
|
subject-a mutations: 2
|
@@ -23,7 +23,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer::SubjectProgress do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
context 'without results' do
|
26
|
-
|
26
|
+
with(:subject_a_result) { { mutation_results: [] } }
|
27
27
|
|
28
28
|
it_reports <<-'STR'
|
29
29
|
subject-a mutations: 2
|