mutant 0.5.24 → 0.5.25

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +8 -0
  3. data/config/flay.yml +1 -1
  4. data/config/flog.yml +1 -1
  5. data/config/reek.yml +15 -13
  6. data/lib/mutant.rb +28 -12
  7. data/lib/mutant/ast/meta.rb +0 -10
  8. data/lib/mutant/ast/named_children.rb +1 -0
  9. data/lib/mutant/ast/types.rb +5 -5
  10. data/lib/mutant/cli.rb +84 -64
  11. data/lib/mutant/config.rb +7 -39
  12. data/lib/mutant/delegator.rb +2 -0
  13. data/lib/mutant/env.rb +119 -16
  14. data/lib/mutant/expression.rb +8 -2
  15. data/lib/mutant/expression/method.rb +6 -16
  16. data/lib/mutant/expression/methods.rb +5 -5
  17. data/lib/mutant/expression/namespace.rb +7 -7
  18. data/lib/mutant/integration.rb +0 -10
  19. data/lib/mutant/isolation.rb +41 -15
  20. data/lib/mutant/matcher/chain.rb +1 -17
  21. data/lib/mutant/matcher/compiler.rb +108 -0
  22. data/lib/mutant/matcher/config.rb +28 -0
  23. data/lib/mutant/matcher/method.rb +1 -1
  24. data/lib/mutant/matcher/namespace.rb +5 -52
  25. data/lib/mutant/matcher/null.rb +1 -1
  26. data/lib/mutant/matcher/scope.rb +1 -1
  27. data/lib/mutant/mutation.rb +29 -13
  28. data/lib/mutant/mutator/node.rb +2 -12
  29. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +1 -1
  30. data/lib/mutant/reporter/cli.rb +0 -2
  31. data/lib/mutant/reporter/cli/printer.rb +14 -0
  32. data/lib/mutant/reporter/cli/progress.rb +1 -3
  33. data/lib/mutant/reporter/cli/progress/config.rb +5 -9
  34. data/lib/mutant/reporter/cli/progress/env.rb +30 -0
  35. data/lib/mutant/reporter/cli/progress/noop.rb +4 -1
  36. data/lib/mutant/reporter/cli/progress/result.rb +12 -0
  37. data/lib/mutant/reporter/cli/progress/result/mutation.rb +45 -0
  38. data/lib/mutant/reporter/cli/progress/result/subject.rb +54 -0
  39. data/lib/mutant/reporter/cli/progress/subject.rb +7 -90
  40. data/lib/mutant/reporter/cli/registry.rb +2 -0
  41. data/lib/mutant/reporter/cli/report/env.rb +92 -0
  42. data/lib/mutant/reporter/cli/report/mutation.rb +58 -77
  43. data/lib/mutant/reporter/cli/report/subject.rb +4 -3
  44. data/lib/mutant/reporter/cli/report/test.rb +28 -0
  45. data/lib/mutant/reporter/null.rb +1 -1
  46. data/lib/mutant/reporter/trace.rb +16 -3
  47. data/lib/mutant/result.rb +302 -0
  48. data/lib/mutant/runner.rb +77 -123
  49. data/lib/mutant/subject.rb +32 -16
  50. data/lib/mutant/subject/method.rb +0 -15
  51. data/lib/mutant/subject/method/instance.rb +3 -3
  52. data/lib/mutant/version.rb +1 -1
  53. data/lib/mutant/warning_expectation.rb +12 -5
  54. data/spec/integration/mutant/corpus_spec.rb +1 -1
  55. data/spec/spec_helper.rb +5 -1
  56. data/spec/unit/mutant/cli_spec.rb +248 -0
  57. data/spec/unit/mutant/expression/namespace/flat_spec.rb +1 -1
  58. data/spec/unit/mutant/expression_spec.rb +55 -0
  59. data/spec/unit/mutant/integration_spec.rb +0 -5
  60. data/spec/unit/mutant/isolation_spec.rb +36 -5
  61. data/spec/unit/mutant/matcher/chain_spec.rb +1 -13
  62. data/spec/unit/mutant/matcher/compiler_spec.rb +95 -0
  63. data/spec/unit/mutant/matcher/filter_spec.rb +31 -0
  64. data/spec/unit/mutant/matcher/method/instance_spec.rb +33 -2
  65. data/spec/unit/mutant/matcher/method/singleton_spec.rb +1 -1
  66. data/spec/unit/mutant/matcher/methods/instance_spec.rb +1 -1
  67. data/spec/unit/mutant/matcher/methods/singleton_spec.rb +1 -1
  68. data/spec/unit/mutant/matcher/namespace_spec.rb +10 -6
  69. data/spec/unit/mutant/matcher/null_spec.rb +26 -0
  70. data/spec/unit/mutant/reporter/cli_spec.rb +337 -0
  71. data/spec/unit/mutant/reporter/null_spec.rb +12 -0
  72. data/spec/unit/mutant/runner_spec.rb +130 -0
  73. data/spec/unit/mutant/subject/context_spec.rb +4 -3
  74. data/spec/unit/mutant/subject/method/instance_spec.rb +5 -3
  75. data/spec/unit/mutant/subject/method/singleton_spec.rb +3 -2
  76. data/spec/unit/mutant/subject_spec.rb +36 -1
  77. data/spec/unit/mutant/test_spec.rb +25 -0
  78. data/spec/unit/mutant/warning_expectation.rb +11 -8
  79. data/spec/unit/mutant_spec.rb +11 -2
  80. metadata +27 -28
  81. data/lib/mutant/killer.rb +0 -44
  82. data/lib/mutant/matcher/builder.rb +0 -142
  83. data/lib/mutant/mutation/evil.rb +0 -23
  84. data/lib/mutant/mutation/neutral.rb +0 -18
  85. data/lib/mutant/reporter/cli/progress/mutation.rb +0 -46
  86. data/lib/mutant/reporter/cli/report/config.rb +0 -116
  87. data/lib/mutant/rspec.rb +0 -0
  88. data/lib/mutant/runner/config.rb +0 -138
  89. data/lib/mutant/runner/killer.rb +0 -75
  90. data/lib/mutant/runner/mutation.rb +0 -78
  91. data/lib/mutant/runner/subject.rb +0 -85
  92. data/lib/mutant/test/report.rb +0 -59
  93. data/spec/unit/mutant/cli_new_spec.rb +0 -147
  94. data/spec/unit/mutant/cli_run_spec.rb +0 -46
  95. data/spec/unit/mutant/runner/config_spec.rb +0 -157
  96. data/spec/unit/mutant/runner/mutation_spec.rb +0 -101
  97. data/spec/unit/mutant/runner/subject_spec.rb +0 -59
  98. data/spec/unit/mutant/subject/mutations_spec.rb +0 -23
  99. data/spec/unit/mutant/subject/node_spec.rb +0 -17
@@ -0,0 +1,337 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mutant::Reporter::CLI do
4
+ let(:object) { described_class.new(output) }
5
+ let(:output) { StringIO.new }
6
+
7
+ def contents
8
+ output.rewind
9
+ output.read
10
+ end
11
+
12
+ describe '#warn' do
13
+ subject { object.warn(message) }
14
+
15
+ let(:message) { 'message' }
16
+
17
+ it 'writes message to output' do
18
+ expect { subject }.to change { contents }.from('').to("message\n")
19
+ end
20
+ end
21
+
22
+ let(:result) do
23
+ Mutant::Result::Env.new(
24
+ env: env,
25
+ runtime: 1.1,
26
+ subject_results: subject_results
27
+ )
28
+ end
29
+
30
+ let(:env) do
31
+ double(
32
+ 'Env',
33
+ class: Mutant::Env,
34
+ matchable_scopes: matchable_scopes,
35
+ config: config,
36
+ subjects: subjects
37
+ )
38
+ end
39
+
40
+ let(:config) { Mutant::Config::DEFAULT }
41
+ let(:mutation_class) { Mutant::Mutation::Evil }
42
+ let(:matchable_scopes) { double('Matchable Scopes', length: 10) }
43
+
44
+ before do
45
+ allow(mutation).to receive(:subject).and_return(_subject)
46
+ end
47
+
48
+ let(:mutation) do
49
+ double(
50
+ 'Mutation',
51
+ identification: 'mutation_id',
52
+ class: mutation_class,
53
+ original_source: 'true',
54
+ source: mutation_source
55
+ )
56
+ end
57
+
58
+ let(:mutation_source) { 'false' }
59
+
60
+ let(:_subject) do
61
+ double(
62
+ 'Subject',
63
+ class: Mutant::Subject,
64
+ node: s(:true),
65
+ identification: 'subject_id',
66
+ mutations: [mutation],
67
+ tests: [
68
+ double('Test', identification: 'test_id')
69
+ ]
70
+ )
71
+ end
72
+
73
+ let(:test_results) do
74
+ [
75
+ double(
76
+ 'Test Result',
77
+ class: Mutant::Result::Test,
78
+ test: _subject.tests.first,
79
+ runtime: 1.0,
80
+ output: 'test-output',
81
+ success?: mutation_result_success
82
+ )
83
+ ]
84
+ end
85
+
86
+ let(:subject_results) do
87
+ [
88
+ Mutant::Result::Subject.new(
89
+ subject: _subject,
90
+ runtime: 1.0,
91
+ mutation_results: [
92
+ double(
93
+ 'Mutation Result',
94
+ class: Mutant::Result::Mutation,
95
+ mutation: mutation,
96
+ killtime: 0.5,
97
+ success?: mutation_result_success,
98
+ test_results: test_results,
99
+ failed_test_results: mutation_result_success ? [] : test_results
100
+ )
101
+ ]
102
+ )
103
+ ]
104
+ end
105
+
106
+ let(:subjects) { [_subject] }
107
+
108
+ describe '#progress' do
109
+ subject { object.progress(reportable) }
110
+
111
+ context 'with env' do
112
+ let(:reportable) { env }
113
+
114
+ it 'writes report to output' do
115
+ subject
116
+ expect(contents).to eql(strip_indent(<<-REPORT))
117
+ Mutant configuration:
118
+ Matcher: #<Mutant::Matcher::Config match_expressions=[] subject_ignores=[] subject_selects=[]>
119
+ Integration: null
120
+ Expect Coverage: 100.00%
121
+ Available Subjects: 10
122
+ Subjects: 1
123
+ REPORT
124
+ end
125
+ end
126
+
127
+ context 'with subject' do
128
+ let(:reportable) { _subject }
129
+
130
+ it 'writes report to output' do
131
+ subject
132
+ expect(contents).to eql(strip_indent(<<-REPORT))
133
+ subject_id mutations: 1
134
+ - test_id
135
+ REPORT
136
+ end
137
+ end
138
+
139
+ context 'with subject report' do
140
+ let(:reportable) { subject_results.first }
141
+ let(:mutation_result_success) { true }
142
+
143
+ it 'writes report to output' do
144
+ subject
145
+ expect(contents).to eql("\n(01/01) 100% - killtime: 0.50s runtime: 1.00s overhead: 0.50s\n")
146
+ end
147
+ end
148
+
149
+ context 'with mutation result' do
150
+ let(:reportable) { subject_results.first.mutation_results.first }
151
+
152
+ context 'when mutation results in success' do
153
+ let(:mutation_result_success) { true }
154
+
155
+ it 'writes report to output' do
156
+ subject
157
+ expect(contents).to eql('.')
158
+ end
159
+ end
160
+
161
+ context 'when mutation results in failure' do
162
+ let(:mutation_result_success) { false }
163
+
164
+ it 'writes report to output' do
165
+ subject
166
+ expect(contents).to eql('F')
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ describe '#report' do
173
+ subject { object.report(result) }
174
+
175
+ context 'with subjects' do
176
+
177
+ context 'and full covergage' do
178
+ let(:mutation_result_success) { true }
179
+
180
+ it 'writes report to output' do
181
+ subject
182
+ expect(contents).to eql(strip_indent(<<-REPORT))
183
+ Subjects: 1
184
+ Mutations: 1
185
+ Kills: 1
186
+ Alive: 0
187
+ Runtime: 1.10s
188
+ Killtime: 0.50s
189
+ Overhead: 120.00%
190
+ Coverage: 100.00%
191
+ Expected: 100.00%
192
+ REPORT
193
+ end
194
+ end
195
+
196
+ context 'and partial covergage' do
197
+ let(:mutation_result_success) { false }
198
+
199
+ context 'on evil mutation' do
200
+ context 'with a diff' do
201
+ it 'writes report to output' do
202
+ subject
203
+ expect(contents).to eql(strip_indent(<<-REPORT))
204
+ subject_id
205
+ - test_id
206
+ mutation_id
207
+ @@ -1,2 +1,2 @@
208
+ -true
209
+ +false
210
+ -----------------------
211
+ Subjects: 1
212
+ Mutations: 1
213
+ Kills: 0
214
+ Alive: 1
215
+ Runtime: 1.10s
216
+ Killtime: 0.50s
217
+ Overhead: 120.00%
218
+ Coverage: 0.00%
219
+ Expected: 100.00%
220
+ REPORT
221
+ end
222
+ end
223
+
224
+ context 'without a diff' do
225
+ let(:mutation_source) { 'true' }
226
+
227
+ it 'writes report to output' do
228
+ subject
229
+ expect(contents).to eql(strip_indent(<<-REPORT))
230
+ subject_id
231
+ - test_id
232
+ mutation_id
233
+ BUG: Mutation NOT resulted in exactly one diff. Please report a reproduction!
234
+ -----------------------
235
+ Subjects: 1
236
+ Mutations: 1
237
+ Kills: 0
238
+ Alive: 1
239
+ Runtime: 1.10s
240
+ Killtime: 0.50s
241
+ Overhead: 120.00%
242
+ Coverage: 0.00%
243
+ Expected: 100.00%
244
+ REPORT
245
+ end
246
+ end
247
+ end
248
+
249
+ context 'on neutral mutation' do
250
+ let(:mutation_class) { Mutant::Mutation::Neutral }
251
+ let(:mutation_source) { 'true' }
252
+
253
+ it 'writes report to output' do
254
+ subject
255
+ expect(contents).to eql(strip_indent(<<-REPORT))
256
+ subject_id
257
+ - test_id
258
+ mutation_id
259
+ --- Neutral failure ---
260
+ Original code was inserted unmutated. And the test did NOT PASS.
261
+ Your tests do not pass initially or you found a bug in mutant / unparser.
262
+ Subject AST:
263
+ (true)
264
+ Unparsed Source:
265
+ true
266
+ Test Reports: 1
267
+ - test_id / runtime: 1.0
268
+ Test Output:
269
+ test-output
270
+ -----------------------
271
+ Subjects: 1
272
+ Mutations: 1
273
+ Kills: 0
274
+ Alive: 1
275
+ Runtime: 1.10s
276
+ Killtime: 0.50s
277
+ Overhead: 120.00%
278
+ Coverage: 0.00%
279
+ Expected: 100.00%
280
+ REPORT
281
+ end
282
+ end
283
+
284
+ context 'on neutral mutation' do
285
+ let(:mutation_class) { Mutant::Mutation::Noop }
286
+
287
+ it 'writes report to output' do
288
+ subject
289
+ expect(contents).to eql(strip_indent(<<-REPORT))
290
+ subject_id
291
+ - test_id
292
+ mutation_id
293
+ ---- Noop failure -----
294
+ No code was inserted. And the test did NOT PASS.
295
+ This is typically a problem of your specs not passing unmutated.
296
+ Test Reports: 1
297
+ - test_id / runtime: 1.0
298
+ Test Output:
299
+ test-output
300
+ -----------------------
301
+ Subjects: 1
302
+ Mutations: 1
303
+ Kills: 0
304
+ Alive: 1
305
+ Runtime: 1.10s
306
+ Killtime: 0.50s
307
+ Overhead: 120.00%
308
+ Coverage: 0.00%
309
+ Expected: 100.00%
310
+ REPORT
311
+ end
312
+ end
313
+ end
314
+ end
315
+
316
+ context 'without subjects' do
317
+
318
+ let(:subjects) { [] }
319
+ let(:subject_results) { [] }
320
+
321
+ it 'writes report to output' do
322
+ subject
323
+ expect(contents).to eql(strip_indent(<<-REPORT))
324
+ Subjects: 0
325
+ Mutations: 0
326
+ Kills: 0
327
+ Alive: 0
328
+ Runtime: 1.10s
329
+ Killtime: 0.00s
330
+ Overhead: Inf%
331
+ Coverage: 0.00%
332
+ Expected: 100.00%
333
+ REPORT
334
+ end
335
+ end
336
+ end
337
+ end
@@ -8,4 +8,16 @@ describe Mutant::Reporter::Null do
8
8
 
9
9
  it_should_behave_like 'a command method'
10
10
  end
11
+
12
+ describe '#warn' do
13
+ subject { object.warn(double('some input')) }
14
+
15
+ it_should_behave_like 'a command method'
16
+ end
17
+
18
+ describe '#progress' do
19
+ subject { object.progress(double('some input')) }
20
+
21
+ it_should_behave_like 'a command method'
22
+ end
11
23
  end
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+
3
+ # FIXME: This is not even close to a mutation covering spec.
4
+ describe Mutant::Runner do
5
+ let(:object) { described_class.new(env) }
6
+
7
+ let(:reporter) { Mutant::Reporter::Trace.new }
8
+ let(:config) { Mutant::Config::DEFAULT.update(reporter: reporter, isolation: Mutant::Isolation::None) }
9
+ let(:subjects) { [subject_a, subject_b] }
10
+
11
+ class Double
12
+ include Concord.new(:name, :attributes)
13
+
14
+ def self.new(name, attributes = {})
15
+ super
16
+ end
17
+
18
+ def update(_attributes)
19
+ self
20
+ end
21
+
22
+ def method_missing(name, *arguments)
23
+ super unless attributes.key?(name)
24
+ fail "Arguments provided for #{name}" if arguments.any?
25
+ attributes.fetch(name)
26
+ end
27
+ end
28
+
29
+ let(:subject_a) { Double.new('Subject A', mutations: mutations_a, tests: subject_a_tests) }
30
+ let(:subject_b) { Double.new('Subject B', mutations: mutations_b) }
31
+
32
+ let(:subject_a_tests) { [test_a1, test_a2] }
33
+
34
+ let(:env) do
35
+ subjects = self.subjects
36
+ Class.new(Mutant::Env) do
37
+ define_method(:subjects) { subjects }
38
+ end.new(config)
39
+ end
40
+
41
+ let(:mutations_a) { [mutation_a1, mutation_a2] }
42
+ let(:mutations_b) { [] }
43
+
44
+ let(:mutation_a1) { Double.new('Mutation A1') }
45
+ let(:mutation_a2) { Double.new('Mutation A2') }
46
+
47
+ let(:test_a1) { Double.new('Test A1') }
48
+ let(:test_a2) { Double.new('Test A2') }
49
+
50
+ let(:test_report_a1) { Double.new('Test Report A1') }
51
+
52
+ before do
53
+ allow(mutation_a1).to receive(:subject).and_return(subject_a)
54
+ allow(mutation_a1).to receive(:insert)
55
+ allow(mutation_a2).to receive(:subject).and_return(subject_a)
56
+ allow(mutation_a2).to receive(:insert)
57
+ allow(test_a1).to receive(:run).and_return(test_report_a1)
58
+ allow(mutation_a1).to receive(:killed_by?).with(test_report_a1).and_return(true)
59
+ allow(mutation_a2).to receive(:killed_by?).with(test_report_a1).and_return(true)
60
+ end
61
+
62
+ before do
63
+ time = Time.at(0)
64
+ allow(Time).to receive(:now).and_return(time)
65
+ end
66
+
67
+ let(:expected_subject_results) do
68
+ [
69
+ Mutant::Result::Subject.new(
70
+ subject: subject_a,
71
+ mutation_results: [
72
+ Mutant::Result::Mutation.new(
73
+ mutation: mutation_a1,
74
+ runtime: 0.0,
75
+ test_results: [test_report_a1]
76
+ ),
77
+ Mutant::Result::Mutation.new(
78
+ mutation: mutation_a2,
79
+ runtime: 0.0,
80
+ test_results: [test_report_a1]
81
+ )
82
+ ],
83
+ runtime: 0.0
84
+ ),
85
+ Mutant::Result::Subject.new(
86
+ subject: subject_b,
87
+ mutation_results: [],
88
+ runtime: 0.0
89
+ )
90
+ ]
91
+ end
92
+
93
+ describe '#result' do
94
+ context 'on normal execution' do
95
+ subject { object.result }
96
+
97
+ its(:env) { should be(env) }
98
+ its(:subject_results) { should eql(expected_subject_results) }
99
+ end
100
+
101
+ context 'when isolation raises error' do
102
+ subject { object.result }
103
+
104
+ its(:env) { should be(env) }
105
+ its(:subject_results) { should eql(expected_subject_results) }
106
+
107
+ before do
108
+ expect(Mutant::Isolation::None).to receive(:call)
109
+ .twice
110
+ .and_raise(Mutant::Isolation::Error.new('test-exception-message'))
111
+
112
+ expect(Mutant::Result::Test).to receive(:new).with(
113
+ test: test_a1,
114
+ mutation: mutation_a1,
115
+ runtime: 0.0,
116
+ output: 'test-exception-message',
117
+ passed: false
118
+ ).and_return(test_report_a1)
119
+ expect(Mutant::Result::Test).to receive(:new).with(
120
+ test: test_a1,
121
+ mutation: mutation_a2,
122
+ runtime: 0.0,
123
+ output: 'test-exception-message',
124
+ passed: false
125
+ ).and_return(test_report_a1)
126
+ end
127
+
128
+ end
129
+ end
130
+ end