knapsack_pro 6.0.3 → 7.0.0
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/.circleci/config.yml +375 -28
- data/.github/pull_request_template.md +22 -0
- data/.gitignore +4 -0
- data/CHANGELOG.md +95 -0
- data/Gemfile +9 -0
- data/README.md +0 -7
- data/knapsack_pro.gemspec +2 -1
- data/lib/knapsack_pro/adapters/base_adapter.rb +7 -2
- data/lib/knapsack_pro/adapters/cucumber_adapter.rb +1 -3
- data/lib/knapsack_pro/adapters/rspec_adapter.rb +16 -9
- data/lib/knapsack_pro/config/env.rb +1 -9
- data/lib/knapsack_pro/extensions/rspec_extension.rb +137 -0
- data/lib/knapsack_pro/formatters/time_tracker.rb +10 -26
- data/lib/knapsack_pro/formatters/time_tracker_fetcher.rb +8 -0
- data/lib/knapsack_pro/presenter.rb +1 -1
- data/lib/knapsack_pro/pure/queue/rspec_pure.rb +92 -0
- data/lib/knapsack_pro/runners/queue/base_runner.rb +6 -1
- data/lib/knapsack_pro/runners/queue/cucumber_runner.rb +6 -6
- data/lib/knapsack_pro/runners/queue/minitest_runner.rb +10 -6
- data/lib/knapsack_pro/runners/queue/rspec_runner.rb +124 -173
- data/lib/knapsack_pro/urls.rb +2 -0
- data/lib/knapsack_pro/version.rb +1 -1
- data/lib/knapsack_pro.rb +1 -0
- data/spec/integration/runners/queue/rspec_runner.rb +80 -0
- data/spec/integration/runners/queue/rspec_runner_spec.rb +2232 -0
- data/spec/knapsack_pro/adapters/base_adapter_spec.rb +17 -11
- data/spec/knapsack_pro/adapters/cucumber_adapter_spec.rb +2 -5
- data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +2 -24
- data/spec/knapsack_pro/config/env_spec.rb +1 -35
- data/spec/knapsack_pro/formatters/time_tracker_specs.rb +8 -37
- data/spec/knapsack_pro/hooks/queue_spec.rb +2 -2
- data/spec/knapsack_pro/presenter_spec.rb +1 -1
- data/spec/knapsack_pro/pure/queue/rspec_pure_spec.rb +224 -0
- data/spec/knapsack_pro/runners/queue/cucumber_runner_spec.rb +16 -16
- data/spec/knapsack_pro/runners/queue/minitest_runner_spec.rb +14 -14
- data/spec/knapsack_pro_spec.rb +3 -3
- data/spec/spec_helper.rb +0 -1
- metadata +17 -12
- data/lib/knapsack_pro/formatters/rspec_queue_profile_formatter_extension.rb +0 -58
- data/lib/knapsack_pro/formatters/rspec_queue_summary_formatter.rb +0 -145
- data/spec/knapsack_pro/runners/queue/rspec_runner_spec.rb +0 -536
@@ -0,0 +1,2232 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'json'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
describe "#{KnapsackPro::Runners::Queue::RSpecRunner} - Integration tests", :clear_tmp do
|
6
|
+
SPEC_DIRECTORY = 'spec_integration'
|
7
|
+
|
8
|
+
class Spec
|
9
|
+
attr_reader :path, :content
|
10
|
+
|
11
|
+
def initialize(path, content)
|
12
|
+
@path = "#{SPEC_DIRECTORY}/#{path}"
|
13
|
+
@content = content
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param rspec_options String
|
18
|
+
# @param spec_batches Array[Array[String]]
|
19
|
+
def generate_specs(spec_helper, rspec_options, spec_batches)
|
20
|
+
ENV['TEST__RSPEC_OPTIONS'] = rspec_options
|
21
|
+
|
22
|
+
spec_helper_path = "#{SPEC_DIRECTORY}/spec_helper.rb"
|
23
|
+
File.open(spec_helper_path, 'w') { |file| file.write(spec_helper) }
|
24
|
+
|
25
|
+
paths = spec_batches.flatten.map do |spec_item|
|
26
|
+
File.open(spec_item.path, 'w') { |file| file.write(spec_item.content) }
|
27
|
+
spec_item.path
|
28
|
+
end
|
29
|
+
|
30
|
+
stub_spec_batches(
|
31
|
+
spec_batches.map { _1.map(&:path) }
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def stub_spec_batches(batched_tests)
|
36
|
+
ENV['TEST__SPEC_BATCHES'] = batched_tests.to_json
|
37
|
+
end
|
38
|
+
|
39
|
+
# @param test_file_paths Array[String]
|
40
|
+
# Example: ['spec_integration/a_spec.rb[1:1]']
|
41
|
+
def mock_test_cases_for_slow_test_files(test_file_paths)
|
42
|
+
ENV['TEST__TEST_FILE_CASES_FOR_SLOW_TEST_FILES'] = test_file_paths.to_json
|
43
|
+
end
|
44
|
+
|
45
|
+
def log_command_result(stdout, stderr, status)
|
46
|
+
return if ENV['TEST__SHOW_DEBUG_LOG'] != 'true'
|
47
|
+
|
48
|
+
puts '='*50
|
49
|
+
puts 'STDOUT:'
|
50
|
+
puts stdout
|
51
|
+
puts
|
52
|
+
|
53
|
+
puts '='*50
|
54
|
+
puts 'STDERR:'
|
55
|
+
puts stderr
|
56
|
+
puts
|
57
|
+
|
58
|
+
puts '='*50
|
59
|
+
puts 'Exit status code:'
|
60
|
+
puts status
|
61
|
+
puts
|
62
|
+
end
|
63
|
+
|
64
|
+
let(:spec_helper_with_knapsack) do
|
65
|
+
<<~SPEC
|
66
|
+
require 'knapsack_pro'
|
67
|
+
KnapsackPro::Adapters::RSpecAdapter.bind
|
68
|
+
SPEC
|
69
|
+
end
|
70
|
+
|
71
|
+
subject do
|
72
|
+
command = 'ruby spec/integration/runners/queue/rspec_runner.rb'
|
73
|
+
stdout, stderr, status = Open3.capture3(command)
|
74
|
+
log_command_result(stdout, stderr, status)
|
75
|
+
OpenStruct.new(stdout: stdout, stderr: stderr, exit_code: status.exitstatus)
|
76
|
+
end
|
77
|
+
|
78
|
+
before do
|
79
|
+
FileUtils.mkdir_p(SPEC_DIRECTORY)
|
80
|
+
|
81
|
+
ENV['KNAPSACK_PRO_LOG_LEVEL'] = 'debug'
|
82
|
+
# Useful when creating or editing a test:
|
83
|
+
# ENV['TEST__SHOW_DEBUG_LOG'] = 'true'
|
84
|
+
end
|
85
|
+
after do
|
86
|
+
FileUtils.rm_rf(SPEC_DIRECTORY)
|
87
|
+
FileUtils.mkdir_p(SPEC_DIRECTORY)
|
88
|
+
|
89
|
+
ENV.delete('KNAPSACK_PRO_LOG_LEVEL')
|
90
|
+
ENV.keys.select { _1.start_with?('TEST__') }.each do |key|
|
91
|
+
ENV.delete(key)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'when a few batches of tests returned by the Queue API' do
|
96
|
+
it 'runs tests' do
|
97
|
+
rspec_options = '--format d'
|
98
|
+
|
99
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
100
|
+
describe 'A_describe' do
|
101
|
+
it 'A1 test example' do
|
102
|
+
expect(1).to eq 1
|
103
|
+
end
|
104
|
+
end
|
105
|
+
SPEC
|
106
|
+
|
107
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
108
|
+
describe 'B_describe' do
|
109
|
+
it 'B1 test example' do
|
110
|
+
expect(1).to eq 1
|
111
|
+
end
|
112
|
+
end
|
113
|
+
SPEC
|
114
|
+
|
115
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
116
|
+
describe 'C_describe' do
|
117
|
+
it 'C1 test example' do
|
118
|
+
expect(1).to eq 1
|
119
|
+
end
|
120
|
+
end
|
121
|
+
SPEC
|
122
|
+
|
123
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
124
|
+
[spec_a, spec_b],
|
125
|
+
[spec_c],
|
126
|
+
])
|
127
|
+
|
128
|
+
actual = subject
|
129
|
+
|
130
|
+
expect(actual.stdout).to include('DEBUG -- : [knapsack_pro] Queue Mode enabled.')
|
131
|
+
|
132
|
+
expect(actual.stdout).to include('A1 test example')
|
133
|
+
expect(actual.stdout).to include('B1 test example')
|
134
|
+
expect(actual.stdout).to include('C1 test example')
|
135
|
+
|
136
|
+
expect(actual.stdout).to include('INFO -- : [knapsack_pro] To retry the last batch of tests fetched from the Queue API, please run the following command on your machine:')
|
137
|
+
expect(actual.stdout).to include('INFO -- : [knapsack_pro] bundle exec rspec --format d --default-path spec_integration "spec_integration/a_spec.rb" "spec_integration/b_spec.rb"')
|
138
|
+
expect(actual.stdout).to include('INFO -- : [knapsack_pro] bundle exec rspec --format d --default-path spec_integration "spec_integration/c_spec.rb"')
|
139
|
+
|
140
|
+
expect(actual.stdout).to include('INFO -- : [knapsack_pro] To retry all the tests assigned to this CI node, please run the following command on your machine:')
|
141
|
+
expect(actual.stdout).to include('INFO -- : [knapsack_pro] bundle exec rspec --format d --default-path spec_integration "spec_integration/a_spec.rb" "spec_integration/b_spec.rb" "spec_integration/c_spec.rb"')
|
142
|
+
|
143
|
+
expect(actual.stdout).to include('3 examples, 0 failures')
|
144
|
+
|
145
|
+
expect(actual.stdout).to include('DEBUG -- : [knapsack_pro] Global test execution duration:')
|
146
|
+
|
147
|
+
expect(actual.exit_code).to eq 0
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'detects test execution times correctly before sending it to API' do
|
151
|
+
ENV['TEST__LOG_EXECUTION_TIMES'] = 'true'
|
152
|
+
|
153
|
+
rspec_options = '--format d'
|
154
|
+
|
155
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
156
|
+
describe 'A_describe' do
|
157
|
+
it 'A1 test example' do
|
158
|
+
expect(1).to eq 1
|
159
|
+
end
|
160
|
+
end
|
161
|
+
SPEC
|
162
|
+
|
163
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
164
|
+
describe 'B_describe' do
|
165
|
+
it 'B1 test example' do
|
166
|
+
expect(1).to eq 1
|
167
|
+
end
|
168
|
+
end
|
169
|
+
SPEC
|
170
|
+
|
171
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
172
|
+
describe 'C_describe' do
|
173
|
+
it 'C1 test example' do
|
174
|
+
expect(1).to eq 1
|
175
|
+
end
|
176
|
+
end
|
177
|
+
SPEC
|
178
|
+
|
179
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
180
|
+
[spec_a, spec_b],
|
181
|
+
[spec_c],
|
182
|
+
])
|
183
|
+
|
184
|
+
actual = subject
|
185
|
+
|
186
|
+
expect(actual.stdout).to include('[INTEGRATION TEST] test_files: 3, test files have execution time: true')
|
187
|
+
|
188
|
+
expect(actual.exit_code).to eq 0
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context 'when spec_helper.rb has a missing KnapsackPro::Adapters::RSpecAdapter.bind method' do
|
193
|
+
it do
|
194
|
+
rspec_options = ''
|
195
|
+
|
196
|
+
spec_helper = <<~SPEC
|
197
|
+
require 'knapsack_pro'
|
198
|
+
SPEC
|
199
|
+
|
200
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
201
|
+
describe 'A_describe' do
|
202
|
+
it 'A1 test example' do
|
203
|
+
expect(1).to eq 1
|
204
|
+
end
|
205
|
+
end
|
206
|
+
SPEC
|
207
|
+
|
208
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
209
|
+
describe 'B_describe' do
|
210
|
+
it 'B1 test example' do
|
211
|
+
expect(1).to eq 1
|
212
|
+
end
|
213
|
+
end
|
214
|
+
SPEC
|
215
|
+
|
216
|
+
generate_specs(spec_helper, rspec_options, [
|
217
|
+
[spec_a],
|
218
|
+
[spec_b],
|
219
|
+
])
|
220
|
+
|
221
|
+
actual = subject
|
222
|
+
|
223
|
+
expect(actual.stdout).to include('ERROR -- : [knapsack_pro] You forgot to call KnapsackPro::Adapters::RSpecAdapter.bind method in your test runner configuration file. It is needed to record test files time execution. Please follow the installation guide to configure your project properly https://knapsackpro.com/perma/ruby/installation-guide')
|
224
|
+
|
225
|
+
expect(actual.exit_code).to eq 1
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
context 'when RSpec options are not set' do
|
230
|
+
before do
|
231
|
+
ENV['KNAPSACK_PRO_LOG_LEVEL'] = 'info'
|
232
|
+
end
|
233
|
+
|
234
|
+
after do
|
235
|
+
ENV.delete('KNAPSACK_PRO_LOG_LEVEL')
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'uses a default progress formatter' do
|
239
|
+
rspec_options = ''
|
240
|
+
|
241
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
242
|
+
describe 'A_describe' do
|
243
|
+
it {}
|
244
|
+
it {}
|
245
|
+
it {}
|
246
|
+
end
|
247
|
+
SPEC
|
248
|
+
|
249
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
250
|
+
describe 'B_describe' do
|
251
|
+
it {}
|
252
|
+
it {}
|
253
|
+
end
|
254
|
+
SPEC
|
255
|
+
|
256
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
257
|
+
describe 'C_describe' do
|
258
|
+
it {}
|
259
|
+
it {}
|
260
|
+
it {}
|
261
|
+
end
|
262
|
+
SPEC
|
263
|
+
|
264
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
265
|
+
[spec_a, spec_b],
|
266
|
+
[spec_c],
|
267
|
+
])
|
268
|
+
|
269
|
+
actual = subject
|
270
|
+
|
271
|
+
beginning_of_knapsack_pro_log_info_message = 'I, ['
|
272
|
+
|
273
|
+
# shows dots for the 1st batch of tests
|
274
|
+
expect(actual.stdout).to include('.....' + beginning_of_knapsack_pro_log_info_message)
|
275
|
+
# shows dots for the 2nd batch of tests
|
276
|
+
expect(actual.stdout).to include('...' + beginning_of_knapsack_pro_log_info_message)
|
277
|
+
|
278
|
+
expect(actual.exit_code).to eq 0
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
context 'when RSpec options are not set AND Knapsack Pro log level is warn' do
|
283
|
+
before do
|
284
|
+
ENV['KNAPSACK_PRO_LOG_LEVEL'] = 'warn'
|
285
|
+
ENV.delete('TEST__SHOW_DEBUG_LOG')
|
286
|
+
end
|
287
|
+
after do
|
288
|
+
ENV.delete('KNAPSACK_PRO_LOG_LEVEL')
|
289
|
+
end
|
290
|
+
|
291
|
+
it 'uses a default progress formatter AND shows dots for all test examples' do
|
292
|
+
rspec_options = ''
|
293
|
+
|
294
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
295
|
+
describe 'A_describe' do
|
296
|
+
it {}
|
297
|
+
it {}
|
298
|
+
it {}
|
299
|
+
end
|
300
|
+
SPEC
|
301
|
+
|
302
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
303
|
+
describe 'B_describe' do
|
304
|
+
it {}
|
305
|
+
it {}
|
306
|
+
end
|
307
|
+
SPEC
|
308
|
+
|
309
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
310
|
+
describe 'C_describe' do
|
311
|
+
it {}
|
312
|
+
it {}
|
313
|
+
it {}
|
314
|
+
end
|
315
|
+
SPEC
|
316
|
+
|
317
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
318
|
+
[spec_a, spec_b],
|
319
|
+
[spec_c],
|
320
|
+
])
|
321
|
+
|
322
|
+
actual = subject
|
323
|
+
|
324
|
+
expect(actual.stdout).to include('.'*8)
|
325
|
+
|
326
|
+
expect(actual.exit_code).to eq 0
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
context 'when hooks are defined' do
|
331
|
+
it 'calls RSpec before/after hooks only once for multiple batches of tests' do
|
332
|
+
rspec_options = ''
|
333
|
+
|
334
|
+
spec_helper = <<~SPEC
|
335
|
+
require 'knapsack_pro'
|
336
|
+
KnapsackPro::Adapters::RSpecAdapter.bind
|
337
|
+
|
338
|
+
RSpec.configure do |config|
|
339
|
+
config.before(:suite) do
|
340
|
+
puts 'RSpec_before_suite_hook'
|
341
|
+
end
|
342
|
+
config.after(:suite) do
|
343
|
+
puts 'RSpec_after_suite_hook'
|
344
|
+
end
|
345
|
+
end
|
346
|
+
SPEC
|
347
|
+
|
348
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
349
|
+
describe 'A_describe' do
|
350
|
+
it 'A1 test example' do
|
351
|
+
expect(1).to eq 1
|
352
|
+
end
|
353
|
+
end
|
354
|
+
SPEC
|
355
|
+
|
356
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
357
|
+
describe 'B_describe' do
|
358
|
+
it 'B1 test example' do
|
359
|
+
expect(1).to eq 1
|
360
|
+
end
|
361
|
+
end
|
362
|
+
SPEC
|
363
|
+
|
364
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
365
|
+
describe 'C_describe' do
|
366
|
+
it 'C1 test example' do
|
367
|
+
expect(1).to eq 1
|
368
|
+
end
|
369
|
+
end
|
370
|
+
SPEC
|
371
|
+
|
372
|
+
generate_specs(spec_helper, rspec_options, [
|
373
|
+
[spec_a, spec_b],
|
374
|
+
[spec_c],
|
375
|
+
])
|
376
|
+
|
377
|
+
actual = subject
|
378
|
+
|
379
|
+
expect(actual.stdout.scan(/RSpec_before_suite_hook/).size).to eq 1
|
380
|
+
expect(actual.stdout.scan(/RSpec_after_suite_hook/).size).to eq 1
|
381
|
+
|
382
|
+
expect(actual.exit_code).to eq 0
|
383
|
+
end
|
384
|
+
|
385
|
+
it 'calls queue hooks for multiple batches of tests (queue hooks can be defined multiple times)' do
|
386
|
+
rspec_options = ''
|
387
|
+
|
388
|
+
spec_helper = <<~SPEC
|
389
|
+
require 'knapsack_pro'
|
390
|
+
KnapsackPro::Adapters::RSpecAdapter.bind
|
391
|
+
|
392
|
+
KnapsackPro::Hooks::Queue.before_queue do |queue_id|
|
393
|
+
puts '1st before_queue - run before the test suite'
|
394
|
+
end
|
395
|
+
KnapsackPro::Hooks::Queue.before_queue do |queue_id|
|
396
|
+
puts '2nd before_queue - run before the test suite'
|
397
|
+
end
|
398
|
+
|
399
|
+
KnapsackPro::Hooks::Queue.before_subset_queue do |queue_id, subset_queue_id|
|
400
|
+
puts '1st before_subset_queue - run before the next subset of tests'
|
401
|
+
end
|
402
|
+
KnapsackPro::Hooks::Queue.before_subset_queue do |queue_id, subset_queue_id|
|
403
|
+
puts '2nd before_subset_queue - run before the next subset of tests'
|
404
|
+
end
|
405
|
+
|
406
|
+
KnapsackPro::Hooks::Queue.after_subset_queue do |queue_id, subset_queue_id|
|
407
|
+
puts '1st after_subset_queue - run after the previous subset of tests'
|
408
|
+
end
|
409
|
+
KnapsackPro::Hooks::Queue.after_subset_queue do |queue_id, subset_queue_id|
|
410
|
+
puts '2nd after_subset_queue - run after the previous subset of tests'
|
411
|
+
end
|
412
|
+
|
413
|
+
KnapsackPro::Hooks::Queue.after_queue do |queue_id|
|
414
|
+
puts '1st after_queue - run after the test suite'
|
415
|
+
end
|
416
|
+
KnapsackPro::Hooks::Queue.after_queue do |queue_id|
|
417
|
+
puts '2nd after_queue - run after the test suite'
|
418
|
+
end
|
419
|
+
SPEC
|
420
|
+
|
421
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
422
|
+
describe 'A_describe' do
|
423
|
+
it 'A1 test example' do
|
424
|
+
expect(1).to eq 1
|
425
|
+
end
|
426
|
+
end
|
427
|
+
SPEC
|
428
|
+
|
429
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
430
|
+
describe 'B_describe' do
|
431
|
+
it 'B1 test example' do
|
432
|
+
expect(1).to eq 1
|
433
|
+
end
|
434
|
+
end
|
435
|
+
SPEC
|
436
|
+
|
437
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
438
|
+
describe 'C_describe' do
|
439
|
+
it 'C1 test example' do
|
440
|
+
expect(1).to eq 1
|
441
|
+
end
|
442
|
+
end
|
443
|
+
SPEC
|
444
|
+
|
445
|
+
generate_specs(spec_helper, rspec_options, [
|
446
|
+
[spec_a, spec_b],
|
447
|
+
[spec_c],
|
448
|
+
])
|
449
|
+
|
450
|
+
actual = subject
|
451
|
+
|
452
|
+
expect(actual.stdout.scan(/1st before_queue - run before the test suite/).size).to eq 1
|
453
|
+
expect(actual.stdout.scan(/2nd before_queue - run before the test suite/).size).to eq 1
|
454
|
+
expect(actual.stdout.scan(/1st before_subset_queue - run before the next subset of tests/).size).to eq 2
|
455
|
+
expect(actual.stdout.scan(/2nd before_subset_queue - run before the next subset of tests/).size).to eq 2
|
456
|
+
expect(actual.stdout.scan(/1st after_subset_queue - run after the previous subset of tests/).size).to eq 2
|
457
|
+
expect(actual.stdout.scan(/2nd after_subset_queue - run after the previous subset of tests/).size).to eq 2
|
458
|
+
expect(actual.stdout.scan(/1st after_queue - run after the test suite/).size).to eq 1
|
459
|
+
expect(actual.stdout.scan(/2nd after_queue - run after the test suite/).size).to eq 1
|
460
|
+
|
461
|
+
expect(actual.exit_code).to eq 0
|
462
|
+
end
|
463
|
+
|
464
|
+
it 'calls hooks defined with when_first_matching_example_defined only once for multiple batches of tests' do
|
465
|
+
rspec_options = '--format documentation'
|
466
|
+
|
467
|
+
spec_helper = <<~SPEC
|
468
|
+
require 'knapsack_pro'
|
469
|
+
KnapsackPro::Adapters::RSpecAdapter.bind
|
470
|
+
|
471
|
+
def when_first_matching_example_defined(type:)
|
472
|
+
env_var_name = "WHEN_FIRST_MATCHING_EXAMPLE_DEFINED_FOR_" + type.to_s.upcase
|
473
|
+
|
474
|
+
RSpec.configure do |config|
|
475
|
+
config.when_first_matching_example_defined(type: type) do
|
476
|
+
config.before(:context) do
|
477
|
+
unless ENV[env_var_name]
|
478
|
+
yield
|
479
|
+
end
|
480
|
+
ENV[env_var_name] = 'hook_called'
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
when_first_matching_example_defined(type: :model) do
|
487
|
+
puts 'RSpec_custom_hook_called_once_for_model'
|
488
|
+
end
|
489
|
+
|
490
|
+
when_first_matching_example_defined(type: :system) do
|
491
|
+
puts 'RSpec_custom_hook_called_once_for_system'
|
492
|
+
end
|
493
|
+
|
494
|
+
RSpec.configure do |config|
|
495
|
+
config.before(:suite) do
|
496
|
+
puts 'RSpec_before_suite_hook'
|
497
|
+
end
|
498
|
+
|
499
|
+
config.when_first_matching_example_defined(type: :model) do
|
500
|
+
config.before(:suite) do
|
501
|
+
puts 'RSpec_before_suite_hook_for_model'
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
505
|
+
config.when_first_matching_example_defined(type: :system) do
|
506
|
+
config.before(:suite) do
|
507
|
+
puts 'RSpec_before_suite_hook_for_system'
|
508
|
+
end
|
509
|
+
end
|
510
|
+
end
|
511
|
+
SPEC
|
512
|
+
|
513
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
514
|
+
describe 'A_describe', type: :model do
|
515
|
+
it 'A1 test example' do
|
516
|
+
expect(1).to eq 1
|
517
|
+
end
|
518
|
+
end
|
519
|
+
SPEC
|
520
|
+
|
521
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
522
|
+
describe 'B_describe', type: :system do
|
523
|
+
it 'B1 test example' do
|
524
|
+
expect(1).to eq 1
|
525
|
+
end
|
526
|
+
end
|
527
|
+
SPEC
|
528
|
+
|
529
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
530
|
+
describe 'C_describe' do
|
531
|
+
it 'C1 test example' do
|
532
|
+
expect(1).to eq 1
|
533
|
+
end
|
534
|
+
|
535
|
+
it 'C1 test example', :model do
|
536
|
+
expect(1).to eq 1
|
537
|
+
end
|
538
|
+
end
|
539
|
+
SPEC
|
540
|
+
|
541
|
+
spec_d = Spec.new('d_spec.rb', <<~SPEC)
|
542
|
+
describe 'D_describe', type: :system do
|
543
|
+
it 'D1 test example' do
|
544
|
+
expect(1).to eq 1
|
545
|
+
end
|
546
|
+
end
|
547
|
+
SPEC
|
548
|
+
|
549
|
+
generate_specs(spec_helper, rspec_options, [
|
550
|
+
[spec_a],
|
551
|
+
[spec_b],
|
552
|
+
[spec_c],
|
553
|
+
[spec_d],
|
554
|
+
])
|
555
|
+
|
556
|
+
actual = subject
|
557
|
+
|
558
|
+
expect(actual.stdout.scan(/RSpec_before_suite_hook/).size).to eq 1
|
559
|
+
|
560
|
+
# skips before(:suite) hooks that were defined too late in 1st & 2nd batch of tests after before(:suite) hook is already executed
|
561
|
+
expect(actual.stdout.scan(/RSpec_before_suite_hook_for_model/).size).to eq 0
|
562
|
+
expect(actual.stdout.scan(/RSpec_before_suite_hook_for_system/).size).to eq 0
|
563
|
+
|
564
|
+
expect(actual.stdout.scan(/RSpec_custom_hook_called_once_for_model/).size).to eq 1
|
565
|
+
expect(actual.stdout.scan(/RSpec_custom_hook_called_once_for_system/).size).to eq 1
|
566
|
+
|
567
|
+
expect(actual.exit_code).to eq 0
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
context 'when the RSpec seed is used' do
|
572
|
+
it do
|
573
|
+
rspec_options = '--order rand:123'
|
574
|
+
|
575
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
576
|
+
describe 'A_describe' do
|
577
|
+
it 'A1 test example' do
|
578
|
+
expect(1).to eq 1
|
579
|
+
end
|
580
|
+
end
|
581
|
+
SPEC
|
582
|
+
|
583
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
584
|
+
describe 'B_describe' do
|
585
|
+
it 'B1 test example' do
|
586
|
+
expect(1).to eq 1
|
587
|
+
end
|
588
|
+
end
|
589
|
+
SPEC
|
590
|
+
|
591
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
592
|
+
describe 'C_describe' do
|
593
|
+
it 'C1 test example' do
|
594
|
+
expect(1).to eq 1
|
595
|
+
end
|
596
|
+
end
|
597
|
+
SPEC
|
598
|
+
|
599
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
600
|
+
[spec_a, spec_b],
|
601
|
+
[spec_c],
|
602
|
+
])
|
603
|
+
|
604
|
+
actual = subject
|
605
|
+
|
606
|
+
expect(actual.stdout).to include('Randomized with seed 123')
|
607
|
+
|
608
|
+
# 1st batch
|
609
|
+
expect(actual.stdout).to include('INFO -- : [knapsack_pro] bundle exec rspec --order rand:123 --format progress --default-path spec_integration "spec_integration/a_spec.rb" "spec_integration/b_spec.rb"')
|
610
|
+
# 2nd batch
|
611
|
+
expect(actual.stdout).to include('INFO -- : [knapsack_pro] bundle exec rspec --order rand:123 --format progress --default-path spec_integration "spec_integration/c_spec.rb"')
|
612
|
+
|
613
|
+
# the final RSpec command with seed
|
614
|
+
expect(actual.stdout).to include('INFO -- : [knapsack_pro] bundle exec rspec --order rand:123 --format progress --default-path spec_integration "spec_integration/a_spec.rb" "spec_integration/b_spec.rb" "spec_integration/c_spec.rb"')
|
615
|
+
|
616
|
+
expect(actual.exit_code).to eq 0
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
context 'when a failing test in a batch of tests that is not the last batch fetched from the Queue API' do
|
621
|
+
it 'returns 1 as exit code (it remembers that one of the batches has a failing test)' do
|
622
|
+
rspec_options = '--format documentation'
|
623
|
+
|
624
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
625
|
+
describe 'A_describe' do
|
626
|
+
it 'A1 test example' do
|
627
|
+
expect(1).to eq 1
|
628
|
+
end
|
629
|
+
end
|
630
|
+
SPEC
|
631
|
+
|
632
|
+
failing_spec = Spec.new('failing_spec.rb', <<~SPEC)
|
633
|
+
describe 'B_describe' do
|
634
|
+
it 'B1 test example' do
|
635
|
+
expect(1).to eq 0
|
636
|
+
end
|
637
|
+
end
|
638
|
+
SPEC
|
639
|
+
|
640
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
641
|
+
describe 'C_describe' do
|
642
|
+
it 'C1 test example' do
|
643
|
+
expect(1).to eq 1
|
644
|
+
end
|
645
|
+
end
|
646
|
+
SPEC
|
647
|
+
|
648
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
649
|
+
[spec_a, failing_spec],
|
650
|
+
[spec_c],
|
651
|
+
])
|
652
|
+
|
653
|
+
actual = subject
|
654
|
+
|
655
|
+
expect(actual.stdout).to include('B1 test example (FAILED - 1)')
|
656
|
+
expect(actual.stdout).to include('Failure/Error: expect(1).to eq 0')
|
657
|
+
expect(actual.stdout).to include('3 examples, 1 failure')
|
658
|
+
|
659
|
+
expect(actual.exit_code).to eq 1
|
660
|
+
end
|
661
|
+
end
|
662
|
+
|
663
|
+
context 'when a failing test raises an exception' do
|
664
|
+
it 'returns 1 as exit code AND the exception does not leak outside of the RSpec runner context' do
|
665
|
+
rspec_options = '--format documentation'
|
666
|
+
|
667
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
668
|
+
describe 'A_describe' do
|
669
|
+
it 'A1 test example' do
|
670
|
+
expect(1).to eq 1
|
671
|
+
end
|
672
|
+
end
|
673
|
+
SPEC
|
674
|
+
|
675
|
+
failing_spec = Spec.new('failing_spec.rb', <<~SPEC)
|
676
|
+
describe 'B_describe' do
|
677
|
+
it 'B1 test example' do
|
678
|
+
raise 'A custom exception from a test'
|
679
|
+
end
|
680
|
+
end
|
681
|
+
SPEC
|
682
|
+
|
683
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
684
|
+
describe 'C_describe' do
|
685
|
+
it 'C1 test example' do
|
686
|
+
expect(1).to eq 1
|
687
|
+
end
|
688
|
+
end
|
689
|
+
SPEC
|
690
|
+
|
691
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
692
|
+
[spec_a, failing_spec],
|
693
|
+
[spec_c],
|
694
|
+
])
|
695
|
+
|
696
|
+
actual = subject
|
697
|
+
|
698
|
+
expect(actual.stdout).to include('B1 test example (FAILED - 1)')
|
699
|
+
expect(actual.stdout).to include("Failure/Error: raise 'A custom exception from a test'")
|
700
|
+
expect(actual.stdout).to include('3 examples, 1 failure')
|
701
|
+
|
702
|
+
expect(actual.exit_code).to eq 1
|
703
|
+
end
|
704
|
+
end
|
705
|
+
|
706
|
+
context 'when a spec file has a syntax error outside of the test example' do
|
707
|
+
it 'stops running tests on the batch that has a test file with the syntax error AND returns 1 as exit code' do
|
708
|
+
rspec_options = '--format documentation'
|
709
|
+
|
710
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
711
|
+
describe 'A_describe' do
|
712
|
+
it 'A1 test example' do
|
713
|
+
expect(1).to eq 1
|
714
|
+
end
|
715
|
+
end
|
716
|
+
SPEC
|
717
|
+
|
718
|
+
failing_spec = Spec.new('failing_spec.rb', <<~SPEC)
|
719
|
+
describe 'B_describe' do
|
720
|
+
a_fake_method
|
721
|
+
|
722
|
+
it 'B1 test example' do
|
723
|
+
expect(1).to eq 1
|
724
|
+
end
|
725
|
+
end
|
726
|
+
SPEC
|
727
|
+
|
728
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
729
|
+
describe 'C_describe' do
|
730
|
+
it 'C1 test example' do
|
731
|
+
expect(1).to eq 1
|
732
|
+
end
|
733
|
+
end
|
734
|
+
SPEC
|
735
|
+
|
736
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
737
|
+
[spec_a],
|
738
|
+
[failing_spec],
|
739
|
+
[spec_c],
|
740
|
+
])
|
741
|
+
|
742
|
+
actual = subject
|
743
|
+
|
744
|
+
# 1st batch of tests executed correctly
|
745
|
+
expect(actual.stdout).to include('A1 test example')
|
746
|
+
# 2nd batch contains the test file that cannot be loaded and the test file is not executed
|
747
|
+
expect(actual.stdout).to_not include('B1 test example')
|
748
|
+
# 3rd batch is never executed
|
749
|
+
expect(actual.stdout).to_not include('C1 test example')
|
750
|
+
|
751
|
+
expect(actual.stdout).to include('An error occurred while loading ./spec_integration/failing_spec.rb')
|
752
|
+
expect(actual.stdout).to match(/undefined local variable or method `a_fake_method' for.* RSpec::ExampleGroups::BDescribe/)
|
753
|
+
expect(actual.stdout).to include('WARN -- : [knapsack_pro] RSpec wants to quit')
|
754
|
+
expect(actual.stdout).to include('1 example, 0 failures, 1 error occurred outside of examples')
|
755
|
+
|
756
|
+
expect(actual.exit_code).to eq 1
|
757
|
+
end
|
758
|
+
end
|
759
|
+
|
760
|
+
context 'when a syntax error (an exception) in spec_helper.rb' do
|
761
|
+
it 'exits early with 1 as the exit code without running tests because RSpec wants to quit' do
|
762
|
+
rspec_options = '--format documentation'
|
763
|
+
|
764
|
+
spec_helper = <<~SPEC
|
765
|
+
require 'knapsack_pro'
|
766
|
+
KnapsackPro::Adapters::RSpecAdapter.bind
|
767
|
+
|
768
|
+
a_fake_method
|
769
|
+
SPEC
|
770
|
+
|
771
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
772
|
+
describe 'A_describe' do
|
773
|
+
it 'A1 test example' do
|
774
|
+
expect(1).to eq 1
|
775
|
+
end
|
776
|
+
end
|
777
|
+
SPEC
|
778
|
+
|
779
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
780
|
+
describe 'B_describe' do
|
781
|
+
it 'B1 test example' do
|
782
|
+
expect(1).to eq 1
|
783
|
+
end
|
784
|
+
end
|
785
|
+
SPEC
|
786
|
+
|
787
|
+
generate_specs(spec_helper, rspec_options, [
|
788
|
+
[spec_a],
|
789
|
+
[spec_b],
|
790
|
+
])
|
791
|
+
|
792
|
+
actual = subject
|
793
|
+
|
794
|
+
expect(actual.stdout).to include('An error occurred while loading spec_helper.')
|
795
|
+
expect(actual.stdout).to include("undefined local variable or method `a_fake_method' for main")
|
796
|
+
expect(actual.stdout).to include('0 examples, 0 failures, 1 error occurred outside of examples')
|
797
|
+
|
798
|
+
expect(actual.exit_code).to eq 1
|
799
|
+
end
|
800
|
+
end
|
801
|
+
|
802
|
+
# Based on:
|
803
|
+
# https://github.com/rspec/rspec-core/pull/2926/files
|
804
|
+
context 'when RSpec is quitting' do
|
805
|
+
let(:helper_with_exit_location) { "#{SPEC_DIRECTORY}/helper_with_exit.rb" }
|
806
|
+
|
807
|
+
it 'returns non zero exit code because RSpec is quitting' do
|
808
|
+
skip 'Not supported by this RSpec version' if RSpec::Core::Version::STRING == '3.10.2'
|
809
|
+
|
810
|
+
File.open(helper_with_exit_location, 'w') { |file| file.write('exit 123') }
|
811
|
+
|
812
|
+
rspec_options = "--format documentation --require ./#{helper_with_exit_location}"
|
813
|
+
|
814
|
+
spec_helper = <<~SPEC
|
815
|
+
require 'knapsack_pro'
|
816
|
+
KnapsackPro::Adapters::RSpecAdapter.bind
|
817
|
+
SPEC
|
818
|
+
|
819
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
820
|
+
describe 'A_describe' do
|
821
|
+
it 'A1 test example' do
|
822
|
+
expect(1).to eq 1
|
823
|
+
end
|
824
|
+
end
|
825
|
+
SPEC
|
826
|
+
|
827
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
828
|
+
describe 'B_describe' do
|
829
|
+
it 'B1 test example' do
|
830
|
+
expect(1).to eq 1
|
831
|
+
end
|
832
|
+
end
|
833
|
+
SPEC
|
834
|
+
|
835
|
+
generate_specs(spec_helper, rspec_options, [
|
836
|
+
[spec_a],
|
837
|
+
[spec_b],
|
838
|
+
])
|
839
|
+
|
840
|
+
actual = subject
|
841
|
+
|
842
|
+
expect(actual.stdout).to include('While loading ./spec_integration/helper_with_exit.rb an `exit` / `raise SystemExit` occurred, RSpec will now quit.')
|
843
|
+
|
844
|
+
expect(actual.stdout).to_not include('A1 test example')
|
845
|
+
expect(actual.stdout).to_not include('B1 test example')
|
846
|
+
|
847
|
+
expect(actual.exit_code).to eq 123
|
848
|
+
end
|
849
|
+
end
|
850
|
+
|
851
|
+
context 'when the test suite has pending tests' do
|
852
|
+
it 'shows the summary of pending tests' do
|
853
|
+
rspec_options = '--format documentation'
|
854
|
+
|
855
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
856
|
+
describe 'A_describe' do
|
857
|
+
it 'A1 test example' do
|
858
|
+
expect(1).to eq 1
|
859
|
+
end
|
860
|
+
end
|
861
|
+
SPEC
|
862
|
+
|
863
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
864
|
+
describe 'B_describe' do
|
865
|
+
xit 'B1 test example' do
|
866
|
+
expect(1).to eq 1
|
867
|
+
end
|
868
|
+
end
|
869
|
+
SPEC
|
870
|
+
|
871
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
872
|
+
describe 'C_describe' do
|
873
|
+
it 'C1 test example' do
|
874
|
+
expect(1).to eq 1
|
875
|
+
end
|
876
|
+
|
877
|
+
xit 'C2 test example' do
|
878
|
+
expect(1).to eq 1
|
879
|
+
end
|
880
|
+
end
|
881
|
+
SPEC
|
882
|
+
|
883
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
884
|
+
[spec_a, spec_b],
|
885
|
+
[spec_c],
|
886
|
+
])
|
887
|
+
|
888
|
+
actual = subject
|
889
|
+
|
890
|
+
expect(actual.stdout).to include('B1 test example (PENDING: Temporarily skipped with xit)')
|
891
|
+
expect(actual.stdout).to include('C2 test example (PENDING: Temporarily skipped with xit)')
|
892
|
+
|
893
|
+
expect(actual.stdout).to include("Pending: (Failures listed here are expected and do not affect your suite's status)")
|
894
|
+
expect(actual.stdout).to include('1) B_describe B1 test example')
|
895
|
+
expect(actual.stdout).to include('2) C_describe C2 test example')
|
896
|
+
|
897
|
+
expect(actual.stdout).to include('4 examples, 0 failures, 2 pending')
|
898
|
+
|
899
|
+
expect(actual.exit_code).to eq 0
|
900
|
+
end
|
901
|
+
end
|
902
|
+
|
903
|
+
context 'when a test file raises an exception that cannot be handle by RSpec' do
|
904
|
+
it 'stops running tests when unhandled exception happens AND sets 1 as exit code AND shows summary of unexecuted tests' do
|
905
|
+
rspec_options = '--format documentation'
|
906
|
+
|
907
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
908
|
+
describe 'A_describe' do
|
909
|
+
it 'A1 test example' do
|
910
|
+
expect(1).to eq 1
|
911
|
+
end
|
912
|
+
end
|
913
|
+
SPEC
|
914
|
+
|
915
|
+
# list of unhandled exceptions:
|
916
|
+
# RSpec::Support::AllExceptionsExceptOnesWeMustNotRescue::AVOID_RESCUING
|
917
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
918
|
+
describe 'B_describe' do
|
919
|
+
it 'B1 test example' do
|
920
|
+
raise NoMemoryError.new
|
921
|
+
end
|
922
|
+
end
|
923
|
+
SPEC
|
924
|
+
|
925
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
926
|
+
describe 'C_describe' do
|
927
|
+
it 'C1 test example' do
|
928
|
+
expect(1).to eq 1
|
929
|
+
end
|
930
|
+
end
|
931
|
+
SPEC
|
932
|
+
|
933
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
934
|
+
[spec_a],
|
935
|
+
[spec_b],
|
936
|
+
[spec_c],
|
937
|
+
])
|
938
|
+
|
939
|
+
actual = subject
|
940
|
+
|
941
|
+
expect(actual.stdout).to include('A1 test example')
|
942
|
+
|
943
|
+
expect(actual.stdout).to include('B_describe')
|
944
|
+
expect(actual.stdout).to include('An unexpected exception happened. RSpec cannot handle it. The exception: #<NoMemoryError: NoMemoryError>')
|
945
|
+
expect(actual.stdout).to_not include('B1 test example')
|
946
|
+
|
947
|
+
expect(actual.stdout).to_not include('C1 test example')
|
948
|
+
|
949
|
+
# 2nd test example raised unhandled exception during runtime.
|
950
|
+
# It breaks RSpec so it was not marked as failed.
|
951
|
+
expect(actual.stdout).to include('2 examples, 0 failures')
|
952
|
+
|
953
|
+
expect(actual.stdout).to include('WARN -- : [knapsack_pro] Unexecuted tests on this CI node (including pending tests): spec_integration/b_spec.rb')
|
954
|
+
|
955
|
+
expect(actual.exit_code).to eq 1
|
956
|
+
end
|
957
|
+
end
|
958
|
+
|
959
|
+
context 'when a test file raises an exception that cannot be handle by RSpec AND --error-exit-code is set' do
|
960
|
+
it 'sets a custom exit code' do
|
961
|
+
rspec_options = '--format documentation --error-exit-code 2'
|
962
|
+
|
963
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
964
|
+
describe 'A_describe' do
|
965
|
+
it 'A1 test example' do
|
966
|
+
expect(1).to eq 1
|
967
|
+
end
|
968
|
+
end
|
969
|
+
SPEC
|
970
|
+
|
971
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
972
|
+
describe 'B_describe' do
|
973
|
+
it 'B1 test example' do
|
974
|
+
raise NoMemoryError.new
|
975
|
+
end
|
976
|
+
end
|
977
|
+
SPEC
|
978
|
+
|
979
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
980
|
+
describe 'C_describe' do
|
981
|
+
it 'C1 test example' do
|
982
|
+
expect(1).to eq 1
|
983
|
+
end
|
984
|
+
end
|
985
|
+
SPEC
|
986
|
+
|
987
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
988
|
+
[spec_a],
|
989
|
+
[spec_b],
|
990
|
+
[spec_c],
|
991
|
+
])
|
992
|
+
|
993
|
+
actual = subject
|
994
|
+
|
995
|
+
expect(actual.exit_code).to eq 2
|
996
|
+
end
|
997
|
+
end
|
998
|
+
|
999
|
+
context 'when a termination signal is received by the process' do
|
1000
|
+
it 'terminates the process after tests from the current RSpec ExampleGroup are executed and sets 1 as exit code' do
|
1001
|
+
rspec_options = '--format documentation'
|
1002
|
+
|
1003
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
1004
|
+
describe 'A_describe' do
|
1005
|
+
it 'A1 test example' do
|
1006
|
+
expect(1).to eq 1
|
1007
|
+
end
|
1008
|
+
end
|
1009
|
+
SPEC
|
1010
|
+
|
1011
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
1012
|
+
describe 'B1_describe' do
|
1013
|
+
describe 'B1.1_describe' do
|
1014
|
+
xit 'B1.1.1 test example' do
|
1015
|
+
expect(1).to eq 1
|
1016
|
+
end
|
1017
|
+
it 'B1.1.2 test example' do
|
1018
|
+
Process.kill("INT", Process.pid)
|
1019
|
+
end
|
1020
|
+
it 'B1.1.3 test example' do
|
1021
|
+
expect(1).to eq 0
|
1022
|
+
end
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
describe 'B1.2_describe' do
|
1026
|
+
it 'B1.2.1 test example' do
|
1027
|
+
expect(1).to eq 1
|
1028
|
+
end
|
1029
|
+
end
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
describe 'B2_describe' do
|
1033
|
+
it 'B2.1 test example' do
|
1034
|
+
expect(1).to eq 1
|
1035
|
+
end
|
1036
|
+
end
|
1037
|
+
SPEC
|
1038
|
+
|
1039
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
1040
|
+
describe 'C_describe' do
|
1041
|
+
it 'C1 test example' do
|
1042
|
+
expect(1).to eq 1
|
1043
|
+
end
|
1044
|
+
end
|
1045
|
+
SPEC
|
1046
|
+
|
1047
|
+
spec_d = Spec.new('d_spec.rb', <<~SPEC)
|
1048
|
+
describe 'D_describe' do
|
1049
|
+
it 'D1 test example' do
|
1050
|
+
expect(1).to eq 1
|
1051
|
+
end
|
1052
|
+
end
|
1053
|
+
SPEC
|
1054
|
+
|
1055
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
1056
|
+
[spec_a],
|
1057
|
+
[spec_b, spec_c],
|
1058
|
+
[spec_d],
|
1059
|
+
])
|
1060
|
+
|
1061
|
+
actual = subject
|
1062
|
+
|
1063
|
+
expect(actual.stdout).to include('B1.1.1 test example (PENDING: Temporarily skipped with xit)')
|
1064
|
+
expect(actual.stdout).to include('INT signal has been received. Terminating Knapsack Pro...')
|
1065
|
+
expect(actual.stdout).to include('B1.1.2 test example')
|
1066
|
+
expect(actual.stdout).to include('B1.1.3 test example (FAILED - 1)')
|
1067
|
+
expect(actual.stdout).to include('B1.2.1 test example')
|
1068
|
+
|
1069
|
+
# next ExampleGroup within the same b_spec.rb is not executed
|
1070
|
+
expect(actual.stdout).to_not include('B2.1 test example')
|
1071
|
+
|
1072
|
+
# next test file from the same batch is not executed
|
1073
|
+
expect(actual.stdout).to_not include('C1 test example')
|
1074
|
+
|
1075
|
+
# next batch of tests is not pulled from the Queue API and is not executed
|
1076
|
+
expect(actual.stdout).to_not include('D1 test example')
|
1077
|
+
|
1078
|
+
|
1079
|
+
expect(actual.stdout).to include(
|
1080
|
+
<<~OUTPUT
|
1081
|
+
Pending: (Failures listed here are expected and do not affect your suite's status)
|
1082
|
+
|
1083
|
+
1) B1_describe B1.1_describe B1.1.1 test example
|
1084
|
+
OUTPUT
|
1085
|
+
)
|
1086
|
+
|
1087
|
+
expect(actual.stdout).to include(
|
1088
|
+
<<~OUTPUT
|
1089
|
+
Failures:
|
1090
|
+
|
1091
|
+
1) B1_describe B1.1_describe B1.1.3 test example
|
1092
|
+
OUTPUT
|
1093
|
+
)
|
1094
|
+
|
1095
|
+
expect(actual.exit_code).to eq 1
|
1096
|
+
end
|
1097
|
+
end
|
1098
|
+
|
1099
|
+
context 'when a termination signal is received by the process AND --error-exit-code is set' do
|
1100
|
+
it 'terminates the process AND sets a custom exit code' do
|
1101
|
+
rspec_options = '--format documentation --error-exit-code 3'
|
1102
|
+
|
1103
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
1104
|
+
describe 'A_describe' do
|
1105
|
+
it 'A1 test example' do
|
1106
|
+
expect(1).to eq 1
|
1107
|
+
end
|
1108
|
+
end
|
1109
|
+
SPEC
|
1110
|
+
|
1111
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
1112
|
+
describe 'B_describe' do
|
1113
|
+
it 'B1 test example' do
|
1114
|
+
Process.kill("INT", Process.pid)
|
1115
|
+
end
|
1116
|
+
end
|
1117
|
+
SPEC
|
1118
|
+
|
1119
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
1120
|
+
describe 'C_describe' do
|
1121
|
+
it 'C1 test example' do
|
1122
|
+
expect(1).to eq 1
|
1123
|
+
end
|
1124
|
+
end
|
1125
|
+
SPEC
|
1126
|
+
|
1127
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
1128
|
+
[spec_a],
|
1129
|
+
[spec_b],
|
1130
|
+
[spec_c],
|
1131
|
+
])
|
1132
|
+
|
1133
|
+
actual = subject
|
1134
|
+
|
1135
|
+
expect(actual.stdout).to include('INT signal has been received. Terminating Knapsack Pro...')
|
1136
|
+
|
1137
|
+
expect(actual.exit_code).to eq 3
|
1138
|
+
end
|
1139
|
+
end
|
1140
|
+
|
1141
|
+
context 'when deprecated run_all_when_everything_filtered option is true' do
|
1142
|
+
it 'shows an error message AND sets 1 as exit code' do
|
1143
|
+
rspec_options = '--format documentation'
|
1144
|
+
|
1145
|
+
spec_helper = <<~SPEC
|
1146
|
+
require 'knapsack_pro'
|
1147
|
+
KnapsackPro::Adapters::RSpecAdapter.bind
|
1148
|
+
|
1149
|
+
RSpec.configure do |config|
|
1150
|
+
config.run_all_when_everything_filtered = true
|
1151
|
+
end
|
1152
|
+
SPEC
|
1153
|
+
|
1154
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
1155
|
+
describe 'A_describe' do
|
1156
|
+
it 'A1 test example' do
|
1157
|
+
expect(1).to eq 1
|
1158
|
+
end
|
1159
|
+
end
|
1160
|
+
SPEC
|
1161
|
+
|
1162
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
1163
|
+
describe 'B_describe' do
|
1164
|
+
it 'B1 test example' do
|
1165
|
+
expect(1).to eq 1
|
1166
|
+
end
|
1167
|
+
end
|
1168
|
+
SPEC
|
1169
|
+
|
1170
|
+
generate_specs(spec_helper, rspec_options, [
|
1171
|
+
[spec_a],
|
1172
|
+
[spec_b],
|
1173
|
+
])
|
1174
|
+
|
1175
|
+
actual = subject
|
1176
|
+
|
1177
|
+
expect(actual.stdout).to include('ERROR -- : [knapsack_pro] The run_all_when_everything_filtered option is deprecated. See: https://knapsackpro.com/perma/ruby/rspec-deprecated-run-all-when-everything-filtered')
|
1178
|
+
|
1179
|
+
expect(actual.stdout).to_not include('A1 test example')
|
1180
|
+
expect(actual.stdout).to_not include('B1 test example')
|
1181
|
+
|
1182
|
+
expect(actual.exit_code).to eq 1
|
1183
|
+
end
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
context 'when filter_run_when_matching is set to :focus and some tests are tagged with the focus tag' do
|
1187
|
+
it 'shows an error message for :focus tagged tests AND sets 1 as exit code (shows the error because the batch of tests that has no focus tagged tests will run tests instead of not running them)' do
|
1188
|
+
rspec_options = '--format documentation'
|
1189
|
+
|
1190
|
+
spec_helper = <<~SPEC
|
1191
|
+
require 'knapsack_pro'
|
1192
|
+
KnapsackPro::Adapters::RSpecAdapter.bind
|
1193
|
+
|
1194
|
+
RSpec.configure do |config|
|
1195
|
+
config.filter_run_when_matching :focus
|
1196
|
+
end
|
1197
|
+
SPEC
|
1198
|
+
|
1199
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
1200
|
+
describe 'A_describe' do
|
1201
|
+
it 'A1 test example' do
|
1202
|
+
expect(1).to eq 1
|
1203
|
+
end
|
1204
|
+
end
|
1205
|
+
SPEC
|
1206
|
+
|
1207
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
1208
|
+
describe 'B_describe' do
|
1209
|
+
it 'B1 test example', :focus do
|
1210
|
+
expect(1).to eq 1
|
1211
|
+
end
|
1212
|
+
it 'B2 test example' do
|
1213
|
+
expect(1).to eq 1
|
1214
|
+
end
|
1215
|
+
end
|
1216
|
+
SPEC
|
1217
|
+
|
1218
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
1219
|
+
describe 'C_describe' do
|
1220
|
+
it 'C1 test example' do
|
1221
|
+
expect(1).to eq 1
|
1222
|
+
end
|
1223
|
+
end
|
1224
|
+
SPEC
|
1225
|
+
|
1226
|
+
generate_specs(spec_helper, rspec_options, [
|
1227
|
+
[spec_a],
|
1228
|
+
[spec_b],
|
1229
|
+
[spec_c],
|
1230
|
+
])
|
1231
|
+
|
1232
|
+
actual = subject
|
1233
|
+
|
1234
|
+
expect(actual.stdout).to include('A1 test example')
|
1235
|
+
|
1236
|
+
expect(actual.stdout).to include('B1 test example (FAILED - 1)')
|
1237
|
+
expect(actual.stdout).to_not include('B2 test example') # skips B2 test due to tagged B1
|
1238
|
+
|
1239
|
+
expect(actual.stdout).to include('C1 test example')
|
1240
|
+
|
1241
|
+
expect(actual.stdout).to include('Knapsack Pro found an example tagged with focus in spec_integration/b_spec.rb, please remove it. See more: https://knapsackpro.com/perma/ruby/rspec-skips-tests')
|
1242
|
+
|
1243
|
+
expect(actual.exit_code).to eq 1
|
1244
|
+
end
|
1245
|
+
end
|
1246
|
+
|
1247
|
+
context 'when the late CI node has an empty batch of tests because other CI nodes already consumed tests from the Queue API' do
|
1248
|
+
it 'sets 0 as exit code' do
|
1249
|
+
rspec_options = '--format documentation'
|
1250
|
+
|
1251
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [])
|
1252
|
+
|
1253
|
+
actual = subject
|
1254
|
+
|
1255
|
+
expect(actual.stdout).to include('0 examples, 0 failures')
|
1256
|
+
expect(actual.stdout).to include('WARN -- : [knapsack_pro] No test files were executed on this CI node.')
|
1257
|
+
expect(actual.stdout).to include('DEBUG -- : [knapsack_pro] This CI node likely started work late after the test files were already executed by other CI nodes consuming the queue.')
|
1258
|
+
|
1259
|
+
expect(actual.exit_code).to eq 0
|
1260
|
+
end
|
1261
|
+
end
|
1262
|
+
|
1263
|
+
context 'when the fail_if_no_examples option is true AND the late CI node has an empty batch of tests because other CI nodes already consumed tests from the Queue API' do
|
1264
|
+
it 'sets 0 as exit code to ignore the fail_if_no_examples option' do
|
1265
|
+
rspec_options = '--format documentation'
|
1266
|
+
|
1267
|
+
spec_helper = <<~SPEC
|
1268
|
+
require 'knapsack_pro'
|
1269
|
+
KnapsackPro::Adapters::RSpecAdapter.bind
|
1270
|
+
|
1271
|
+
RSpec.configure do |config|
|
1272
|
+
config.fail_if_no_examples = true
|
1273
|
+
end
|
1274
|
+
SPEC
|
1275
|
+
|
1276
|
+
generate_specs(spec_helper, rspec_options, [])
|
1277
|
+
|
1278
|
+
actual = subject
|
1279
|
+
|
1280
|
+
expect(actual.stdout).to include('0 examples, 0 failures')
|
1281
|
+
expect(actual.stdout).to include('WARN -- : [knapsack_pro] No test files were executed on this CI node.')
|
1282
|
+
expect(actual.stdout).to include('DEBUG -- : [knapsack_pro] This CI node likely started work late after the test files were already executed by other CI nodes consuming the queue.')
|
1283
|
+
|
1284
|
+
expect(actual.exit_code).to eq 0
|
1285
|
+
end
|
1286
|
+
end
|
1287
|
+
|
1288
|
+
context 'when the fail_if_no_examples option is true AND a batch of tests has a test file without test examples' do
|
1289
|
+
it 'sets 0 as exit code to ignore the fail_if_no_examples option' do
|
1290
|
+
rspec_options = '--format documentation'
|
1291
|
+
|
1292
|
+
spec_helper = <<~SPEC
|
1293
|
+
require 'knapsack_pro'
|
1294
|
+
KnapsackPro::Adapters::RSpecAdapter.bind
|
1295
|
+
|
1296
|
+
RSpec.configure do |config|
|
1297
|
+
config.fail_if_no_examples = true
|
1298
|
+
end
|
1299
|
+
SPEC
|
1300
|
+
|
1301
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
1302
|
+
describe 'A_describe' do
|
1303
|
+
it 'A1 test example' do
|
1304
|
+
expect(1).to eq 1
|
1305
|
+
end
|
1306
|
+
end
|
1307
|
+
SPEC
|
1308
|
+
|
1309
|
+
spec_b_with_no_examples = Spec.new('b_spec.rb', <<~SPEC)
|
1310
|
+
describe 'B_describe' do
|
1311
|
+
end
|
1312
|
+
SPEC
|
1313
|
+
|
1314
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
1315
|
+
describe 'C_describe' do
|
1316
|
+
it 'C1 test example' do
|
1317
|
+
expect(1).to eq 1
|
1318
|
+
end
|
1319
|
+
end
|
1320
|
+
SPEC
|
1321
|
+
|
1322
|
+
generate_specs(spec_helper, rspec_options, [
|
1323
|
+
[spec_a],
|
1324
|
+
[spec_b_with_no_examples],
|
1325
|
+
[spec_c],
|
1326
|
+
])
|
1327
|
+
|
1328
|
+
actual = subject
|
1329
|
+
|
1330
|
+
expect(actual.stdout).to include('2 examples, 0 failures')
|
1331
|
+
|
1332
|
+
expect(actual.exit_code).to eq 0
|
1333
|
+
end
|
1334
|
+
end
|
1335
|
+
|
1336
|
+
context 'when tests are failing AND --failure-exit-code is set' do
|
1337
|
+
it 'returns a custom exit code' do
|
1338
|
+
rspec_options = '--format documentation --failure-exit-code 4'
|
1339
|
+
|
1340
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
1341
|
+
describe 'A_describe' do
|
1342
|
+
it 'A1 test example' do
|
1343
|
+
expect(1).to eq 1
|
1344
|
+
end
|
1345
|
+
end
|
1346
|
+
SPEC
|
1347
|
+
|
1348
|
+
failing_spec = Spec.new('failing_spec.rb', <<~SPEC)
|
1349
|
+
describe 'B_describe' do
|
1350
|
+
it 'B1 test example' do
|
1351
|
+
expect(1).to eq 0
|
1352
|
+
end
|
1353
|
+
end
|
1354
|
+
SPEC
|
1355
|
+
|
1356
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
1357
|
+
describe 'C_describe' do
|
1358
|
+
it 'C1 test example' do
|
1359
|
+
expect(1).to eq 1
|
1360
|
+
end
|
1361
|
+
end
|
1362
|
+
SPEC
|
1363
|
+
|
1364
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
1365
|
+
[spec_a, failing_spec],
|
1366
|
+
[spec_c],
|
1367
|
+
])
|
1368
|
+
|
1369
|
+
actual = subject
|
1370
|
+
|
1371
|
+
expect(actual.stdout).to include('B1 test example (FAILED - 1)')
|
1372
|
+
expect(actual.stdout).to include('Failure/Error: expect(1).to eq 0')
|
1373
|
+
expect(actual.stdout).to include('3 examples, 1 failure')
|
1374
|
+
|
1375
|
+
expect(actual.exit_code).to eq 4
|
1376
|
+
end
|
1377
|
+
end
|
1378
|
+
|
1379
|
+
context 'when --profile is set' do
|
1380
|
+
it 'shows top slowest examples AND top slowest example groups' do
|
1381
|
+
rspec_options = '--format d --profile'
|
1382
|
+
|
1383
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
1384
|
+
describe 'A_describe' do
|
1385
|
+
it 'A1 test example' do
|
1386
|
+
expect(1).to eq 1
|
1387
|
+
end
|
1388
|
+
end
|
1389
|
+
SPEC
|
1390
|
+
|
1391
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
1392
|
+
describe 'B_describe' do
|
1393
|
+
it 'B1 test example' do
|
1394
|
+
expect(1).to eq 1
|
1395
|
+
end
|
1396
|
+
end
|
1397
|
+
SPEC
|
1398
|
+
|
1399
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
1400
|
+
describe 'C_describe' do
|
1401
|
+
it 'C1 test example' do
|
1402
|
+
expect(1).to eq 1
|
1403
|
+
end
|
1404
|
+
end
|
1405
|
+
SPEC
|
1406
|
+
|
1407
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
1408
|
+
[spec_a, spec_b],
|
1409
|
+
[spec_c],
|
1410
|
+
])
|
1411
|
+
|
1412
|
+
actual = subject
|
1413
|
+
|
1414
|
+
expect(actual.stdout).to include('Top 3 slowest examples')
|
1415
|
+
expect(actual.stdout).to include('A_describe A1 test example')
|
1416
|
+
expect(actual.stdout).to include('B_describe B1 test example')
|
1417
|
+
expect(actual.stdout).to include('C_describe C1 test example')
|
1418
|
+
|
1419
|
+
expect(actual.stdout).to include('Top 3 slowest example groups')
|
1420
|
+
|
1421
|
+
expect(actual.exit_code).to eq 0
|
1422
|
+
end
|
1423
|
+
end
|
1424
|
+
|
1425
|
+
context 'when an invalid RSpec option is set' do
|
1426
|
+
it 'returns 1 as exit code AND shows an error message to stderr' do
|
1427
|
+
rspec_options = '--format d --fake-rspec-option'
|
1428
|
+
|
1429
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
1430
|
+
describe 'A_describe' do
|
1431
|
+
it 'A1 test example' do
|
1432
|
+
expect(1).to eq 1
|
1433
|
+
end
|
1434
|
+
end
|
1435
|
+
SPEC
|
1436
|
+
|
1437
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
1438
|
+
describe 'B_describe' do
|
1439
|
+
it 'B1 test example' do
|
1440
|
+
expect(1).to eq 1
|
1441
|
+
end
|
1442
|
+
end
|
1443
|
+
SPEC
|
1444
|
+
|
1445
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
1446
|
+
[spec_a],
|
1447
|
+
[spec_b],
|
1448
|
+
])
|
1449
|
+
|
1450
|
+
actual = subject
|
1451
|
+
|
1452
|
+
expect(actual.stderr).to include('invalid option: --fake-rspec-option')
|
1453
|
+
|
1454
|
+
expect(actual.exit_code).to eq 1
|
1455
|
+
end
|
1456
|
+
end
|
1457
|
+
|
1458
|
+
context 'when --fail-fast is set' do
|
1459
|
+
it 'stops running tests on the failing test AND returns 1 as exit code AND shows a warning message' do
|
1460
|
+
rspec_options = '--format d --fail-fast'
|
1461
|
+
|
1462
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
1463
|
+
describe 'A_describe' do
|
1464
|
+
it 'A1 test example' do
|
1465
|
+
expect(1).to eq 1
|
1466
|
+
end
|
1467
|
+
end
|
1468
|
+
SPEC
|
1469
|
+
|
1470
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
1471
|
+
describe 'B_describe' do
|
1472
|
+
it 'B1 test example' do
|
1473
|
+
expect(1).to eq 0
|
1474
|
+
end
|
1475
|
+
end
|
1476
|
+
SPEC
|
1477
|
+
|
1478
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
1479
|
+
describe 'C_describe' do
|
1480
|
+
it 'C1 test example' do
|
1481
|
+
expect(1).to eq 1
|
1482
|
+
end
|
1483
|
+
it 'C2 test example' do
|
1484
|
+
expect(1).to eq 0
|
1485
|
+
end
|
1486
|
+
end
|
1487
|
+
SPEC
|
1488
|
+
|
1489
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
1490
|
+
[spec_a, spec_b],
|
1491
|
+
[spec_c],
|
1492
|
+
])
|
1493
|
+
|
1494
|
+
actual = subject
|
1495
|
+
|
1496
|
+
expect(actual.stdout).to include('A1 test example')
|
1497
|
+
expect(actual.stdout).to include('B1 test example')
|
1498
|
+
expect(actual.stdout).to_not include('C1 test example')
|
1499
|
+
expect(actual.stdout).to_not include('C2 test example')
|
1500
|
+
|
1501
|
+
expect(actual.stdout).to include('WARN -- : [knapsack_pro] Test execution has been canceled because the RSpec --fail-fast option is enabled. It will cause other CI nodes to run tests longer because they need to consume more tests from the Knapsack Pro Queue API.')
|
1502
|
+
|
1503
|
+
expect(actual.stdout).to include('2 examples, 1 failure')
|
1504
|
+
|
1505
|
+
expect(actual.exit_code).to eq 1
|
1506
|
+
end
|
1507
|
+
end
|
1508
|
+
|
1509
|
+
context 'when the fail_fast option is set with a specific number of tests' do
|
1510
|
+
it 'stops running tests on the 2nd failing test AND returns 1 as exit code AND shows a warning message when fail fast limit met' do
|
1511
|
+
rspec_options = '--format d'
|
1512
|
+
|
1513
|
+
spec_helper = <<~SPEC
|
1514
|
+
require 'knapsack_pro'
|
1515
|
+
KnapsackPro::Adapters::RSpecAdapter.bind
|
1516
|
+
|
1517
|
+
RSpec.configure do |config|
|
1518
|
+
config.fail_fast = 2
|
1519
|
+
end
|
1520
|
+
SPEC
|
1521
|
+
|
1522
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
1523
|
+
describe 'A_describe' do
|
1524
|
+
it 'A1 test example' do
|
1525
|
+
expect(1).to eq 0
|
1526
|
+
end
|
1527
|
+
end
|
1528
|
+
SPEC
|
1529
|
+
|
1530
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
1531
|
+
describe 'B_describe' do
|
1532
|
+
it 'B1 test example' do
|
1533
|
+
expect(1).to eq 1
|
1534
|
+
end
|
1535
|
+
it 'B2 test example' do
|
1536
|
+
expect(1).to eq 0
|
1537
|
+
end
|
1538
|
+
end
|
1539
|
+
SPEC
|
1540
|
+
|
1541
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
1542
|
+
describe 'C_describe' do
|
1543
|
+
it 'C1 test example' do
|
1544
|
+
expect(1).to eq 1
|
1545
|
+
end
|
1546
|
+
it 'C2 test example' do
|
1547
|
+
expect(1).to eq 1
|
1548
|
+
end
|
1549
|
+
end
|
1550
|
+
SPEC
|
1551
|
+
|
1552
|
+
generate_specs(spec_helper, rspec_options, [
|
1553
|
+
[spec_a, spec_b],
|
1554
|
+
[spec_c],
|
1555
|
+
])
|
1556
|
+
|
1557
|
+
actual = subject
|
1558
|
+
|
1559
|
+
expect(actual.stdout).to include('A1 test example (FAILED - 1)')
|
1560
|
+
expect(actual.stdout).to include('B1 test example')
|
1561
|
+
expect(actual.stdout).to include('B2 test example (FAILED - 2)')
|
1562
|
+
expect(actual.stdout).to_not include('C1 test example')
|
1563
|
+
expect(actual.stdout).to_not include('C2 test example')
|
1564
|
+
|
1565
|
+
expect(actual.stdout).to include('WARN -- : [knapsack_pro] Test execution has been canceled because the RSpec --fail-fast option is enabled. It will cause other CI nodes to run tests longer because they need to consume more tests from the Knapsack Pro Queue API.')
|
1566
|
+
|
1567
|
+
expect(actual.stdout).to include('3 examples, 2 failures')
|
1568
|
+
|
1569
|
+
expect(actual.exit_code).to eq 1
|
1570
|
+
end
|
1571
|
+
end
|
1572
|
+
|
1573
|
+
context 'when --tag is set' do
|
1574
|
+
it 'runs only tagged test examples from multiple batches of tests fetched from the Queue API' do
|
1575
|
+
rspec_options = '--format d --tag my_tag'
|
1576
|
+
|
1577
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
1578
|
+
describe 'A_describe' do
|
1579
|
+
it 'A1 test example' do
|
1580
|
+
expect(1).to eq 1
|
1581
|
+
end
|
1582
|
+
it 'A2 test example', :my_tag do
|
1583
|
+
expect(1).to eq 1
|
1584
|
+
end
|
1585
|
+
end
|
1586
|
+
SPEC
|
1587
|
+
|
1588
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
1589
|
+
describe 'B_describe', :my_tag do
|
1590
|
+
it 'B1 test example' do
|
1591
|
+
expect(1).to eq 1
|
1592
|
+
end
|
1593
|
+
end
|
1594
|
+
SPEC
|
1595
|
+
|
1596
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
1597
|
+
describe 'C_describe' do
|
1598
|
+
it 'C1 test example' do
|
1599
|
+
expect(1).to eq 1
|
1600
|
+
end
|
1601
|
+
end
|
1602
|
+
SPEC
|
1603
|
+
|
1604
|
+
spec_d = Spec.new('d_spec.rb', <<~SPEC)
|
1605
|
+
describe 'D_describe' do
|
1606
|
+
it 'D1 test example' do
|
1607
|
+
expect(1).to eq 1
|
1608
|
+
end
|
1609
|
+
it 'D2 test example', :my_tag do
|
1610
|
+
expect(1).to eq 1
|
1611
|
+
end
|
1612
|
+
end
|
1613
|
+
SPEC
|
1614
|
+
|
1615
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
1616
|
+
[spec_a, spec_b],
|
1617
|
+
[spec_c],
|
1618
|
+
[spec_d],
|
1619
|
+
])
|
1620
|
+
|
1621
|
+
actual = subject
|
1622
|
+
|
1623
|
+
expect(actual.stdout).to_not include('A1 test example')
|
1624
|
+
expect(actual.stdout).to include('A2 test example')
|
1625
|
+
|
1626
|
+
expect(actual.stdout).to include('B1 test example')
|
1627
|
+
|
1628
|
+
expect(actual.stdout).to_not include('C1 test example')
|
1629
|
+
|
1630
|
+
expect(actual.stdout).to_not include('D1 test example')
|
1631
|
+
expect(actual.stdout).to include('D2 test example')
|
1632
|
+
|
1633
|
+
expect(actual.stdout).to include('3 examples, 0 failures')
|
1634
|
+
|
1635
|
+
expect(actual.exit_code).to eq 0
|
1636
|
+
end
|
1637
|
+
end
|
1638
|
+
|
1639
|
+
context 'when the RSpec split by examples is enabled' do
|
1640
|
+
before do
|
1641
|
+
ENV['KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES'] = 'true'
|
1642
|
+
|
1643
|
+
# remember to mock Queue API batches to include test examples (example: a_spec.rb[1:1])
|
1644
|
+
# for the following slow test files
|
1645
|
+
ENV['KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN'] = "#{SPEC_DIRECTORY}/a_spec.rb"
|
1646
|
+
end
|
1647
|
+
after do
|
1648
|
+
ENV.delete('KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES')
|
1649
|
+
ENV.delete('KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN')
|
1650
|
+
end
|
1651
|
+
|
1652
|
+
it 'splits slow test files by examples AND ensures the test examples are executed only once' do
|
1653
|
+
rspec_options = '--format d'
|
1654
|
+
|
1655
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
1656
|
+
describe 'A_describe' do
|
1657
|
+
it 'A1 test example' do
|
1658
|
+
expect(1).to eq 1
|
1659
|
+
end
|
1660
|
+
it 'A2 test example' do
|
1661
|
+
expect(1).to eq 1
|
1662
|
+
end
|
1663
|
+
end
|
1664
|
+
SPEC
|
1665
|
+
|
1666
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
1667
|
+
describe 'B_describe' do
|
1668
|
+
it 'B1 test example' do
|
1669
|
+
expect(1).to eq 1
|
1670
|
+
end
|
1671
|
+
it 'B2 test example' do
|
1672
|
+
expect(1).to eq 1
|
1673
|
+
end
|
1674
|
+
end
|
1675
|
+
SPEC
|
1676
|
+
|
1677
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
1678
|
+
describe 'C_describe' do
|
1679
|
+
it 'C1 test example' do
|
1680
|
+
expect(1).to eq 1
|
1681
|
+
end
|
1682
|
+
it 'C2 test example' do
|
1683
|
+
expect(1).to eq 1
|
1684
|
+
end
|
1685
|
+
end
|
1686
|
+
SPEC
|
1687
|
+
|
1688
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
1689
|
+
[spec_a, spec_b, spec_c]
|
1690
|
+
])
|
1691
|
+
mock_test_cases_for_slow_test_files([
|
1692
|
+
"#{spec_a.path}[1:1]",
|
1693
|
+
"#{spec_a.path}[1:2]",
|
1694
|
+
])
|
1695
|
+
stub_spec_batches([
|
1696
|
+
["#{spec_a.path}[1:1]", spec_b.path],
|
1697
|
+
["#{spec_a.path}[1:2]", spec_c.path],
|
1698
|
+
])
|
1699
|
+
|
1700
|
+
actual = subject
|
1701
|
+
|
1702
|
+
expect(actual.stdout).to include('DEBUG -- : [knapsack_pro] Detected 1 slow test files: [{"path"=>"spec_integration/a_spec.rb"}]')
|
1703
|
+
|
1704
|
+
expect(actual.stdout).to include(
|
1705
|
+
<<~OUTPUT
|
1706
|
+
A_describe
|
1707
|
+
A1 test example
|
1708
|
+
|
1709
|
+
B_describe
|
1710
|
+
B1 test example
|
1711
|
+
B2 test example
|
1712
|
+
OUTPUT
|
1713
|
+
)
|
1714
|
+
|
1715
|
+
expect(actual.stdout).to include(
|
1716
|
+
<<~OUTPUT
|
1717
|
+
A_describe
|
1718
|
+
A2 test example
|
1719
|
+
|
1720
|
+
C_describe
|
1721
|
+
C1 test example
|
1722
|
+
C2 test example
|
1723
|
+
OUTPUT
|
1724
|
+
)
|
1725
|
+
|
1726
|
+
expect(actual.stdout.scan(/A1 test example/).size).to eq 1
|
1727
|
+
expect(actual.stdout.scan(/A2 test example/).size).to eq 1
|
1728
|
+
|
1729
|
+
expect(actual.stdout).to include('6 examples, 0 failures')
|
1730
|
+
|
1731
|
+
expect(actual.exit_code).to eq 0
|
1732
|
+
end
|
1733
|
+
end
|
1734
|
+
|
1735
|
+
context 'when the RSpec split by examples is enabled AND --tag is set' do
|
1736
|
+
before do
|
1737
|
+
ENV['KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES'] = 'true'
|
1738
|
+
|
1739
|
+
# remember to mock Queue API batches to include test examples (example: a_spec.rb[1:1])
|
1740
|
+
# for the following slow test files
|
1741
|
+
ENV['KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN'] = "#{SPEC_DIRECTORY}/a_spec.rb"
|
1742
|
+
end
|
1743
|
+
after do
|
1744
|
+
ENV.delete('KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES')
|
1745
|
+
ENV.delete('KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN')
|
1746
|
+
end
|
1747
|
+
|
1748
|
+
it 'sets 1 as exit code AND raises an error (a test example path as a_spec.rb[1:1] would always be executed even when it does not have the tag that is set via the --tag option. We cannot run tests because it could lead to running unintentional tests)' do
|
1749
|
+
rspec_options = '--format d --tag my_tag'
|
1750
|
+
|
1751
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
1752
|
+
describe 'A_describe' do
|
1753
|
+
it 'A1 test example' do
|
1754
|
+
expect(1).to eq 1
|
1755
|
+
end
|
1756
|
+
it 'A2 test example' do
|
1757
|
+
expect(1).to eq 1
|
1758
|
+
end
|
1759
|
+
end
|
1760
|
+
SPEC
|
1761
|
+
|
1762
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
1763
|
+
describe 'B_describe', :my_tag do
|
1764
|
+
it 'B1 test example' do
|
1765
|
+
expect(1).to eq 1
|
1766
|
+
end
|
1767
|
+
it 'B2 test example' do
|
1768
|
+
expect(1).to eq 1
|
1769
|
+
end
|
1770
|
+
end
|
1771
|
+
SPEC
|
1772
|
+
|
1773
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
1774
|
+
describe 'C_describe' do
|
1775
|
+
it 'C1 test example' do
|
1776
|
+
expect(1).to eq 1
|
1777
|
+
end
|
1778
|
+
it 'C2 test example' do
|
1779
|
+
expect(1).to eq 1
|
1780
|
+
end
|
1781
|
+
end
|
1782
|
+
SPEC
|
1783
|
+
|
1784
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
1785
|
+
[spec_a, spec_b, spec_c]
|
1786
|
+
])
|
1787
|
+
mock_test_cases_for_slow_test_files([
|
1788
|
+
"#{spec_a.path}[1:1]",
|
1789
|
+
"#{spec_a.path}[1:2]",
|
1790
|
+
])
|
1791
|
+
stub_spec_batches([
|
1792
|
+
["#{spec_a.path}[1:1]", spec_b.path],
|
1793
|
+
["#{spec_a.path}[1:2]", spec_c.path],
|
1794
|
+
])
|
1795
|
+
|
1796
|
+
actual = subject
|
1797
|
+
|
1798
|
+
expect(actual.stdout).to include('ERROR -- : [knapsack_pro] It is not allowed to use the RSpec tag option together with the RSpec split by test examples feature. Please see: https://knapsackpro.com/perma/ruby/rspec-split-by-test-examples-tag')
|
1799
|
+
|
1800
|
+
expect(actual.stdout).to_not include('A1 test example')
|
1801
|
+
expect(actual.stdout).to_not include('A2 test example')
|
1802
|
+
expect(actual.stdout).to_not include('B1 test example')
|
1803
|
+
expect(actual.stdout).to_not include('B2 test example')
|
1804
|
+
expect(actual.stdout).to_not include('C1 test example')
|
1805
|
+
expect(actual.stdout).to_not include('C2 test example')
|
1806
|
+
|
1807
|
+
expect(actual.exit_code).to eq 1
|
1808
|
+
end
|
1809
|
+
end
|
1810
|
+
|
1811
|
+
context 'when the RSpec split by examples is enabled AND JSON formatter is used' do
|
1812
|
+
let(:json_file) { "#{SPEC_DIRECTORY}/rspec.json" }
|
1813
|
+
|
1814
|
+
before do
|
1815
|
+
ENV['KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES'] = 'true'
|
1816
|
+
|
1817
|
+
# remember to mock Queue API batches to include test examples (example: a_spec.rb[1:1])
|
1818
|
+
# for the following slow test files
|
1819
|
+
ENV['KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN'] = "#{SPEC_DIRECTORY}/a_spec.rb"
|
1820
|
+
end
|
1821
|
+
after do
|
1822
|
+
ENV.delete('KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES')
|
1823
|
+
ENV.delete('KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN')
|
1824
|
+
end
|
1825
|
+
|
1826
|
+
it 'produces a JSON report' do
|
1827
|
+
rspec_options = "--format documentation --format json --out ./#{json_file}"
|
1828
|
+
|
1829
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
1830
|
+
describe 'A_describe' do
|
1831
|
+
it 'A1 test example' do
|
1832
|
+
expect(1).to eq 1
|
1833
|
+
end
|
1834
|
+
it 'A2 test example' do
|
1835
|
+
expect(1).to eq 1
|
1836
|
+
end
|
1837
|
+
end
|
1838
|
+
SPEC
|
1839
|
+
|
1840
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
1841
|
+
describe 'B_describe' do
|
1842
|
+
it 'B1 test example' do
|
1843
|
+
expect(1).to eq 1
|
1844
|
+
end
|
1845
|
+
it 'B2 test example' do
|
1846
|
+
expect(1).to eq 1
|
1847
|
+
end
|
1848
|
+
end
|
1849
|
+
SPEC
|
1850
|
+
|
1851
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
1852
|
+
describe 'C_describe' do
|
1853
|
+
it 'C1 test example' do
|
1854
|
+
expect(1).to eq 1
|
1855
|
+
end
|
1856
|
+
it 'C2 test example' do
|
1857
|
+
expect(1).to eq 1
|
1858
|
+
end
|
1859
|
+
end
|
1860
|
+
SPEC
|
1861
|
+
|
1862
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
1863
|
+
[spec_a, spec_b, spec_c]
|
1864
|
+
])
|
1865
|
+
mock_test_cases_for_slow_test_files([
|
1866
|
+
"#{spec_a.path}[1:1]",
|
1867
|
+
"#{spec_a.path}[1:2]",
|
1868
|
+
])
|
1869
|
+
stub_spec_batches([
|
1870
|
+
["#{spec_a.path}[1:1]", spec_b.path],
|
1871
|
+
["#{spec_a.path}[1:2]", spec_c.path],
|
1872
|
+
])
|
1873
|
+
|
1874
|
+
actual = subject
|
1875
|
+
|
1876
|
+
file_content = File.read(json_file)
|
1877
|
+
json = JSON.load(file_content)
|
1878
|
+
examples = json.fetch('examples')
|
1879
|
+
|
1880
|
+
example_ids = examples.map do
|
1881
|
+
_1.fetch('id')
|
1882
|
+
end
|
1883
|
+
expect(example_ids).to match_array([
|
1884
|
+
'./spec_integration/a_spec.rb[1:1]',
|
1885
|
+
'./spec_integration/b_spec.rb[1:1]',
|
1886
|
+
'./spec_integration/b_spec.rb[1:2]',
|
1887
|
+
'./spec_integration/a_spec.rb[1:2]',
|
1888
|
+
'./spec_integration/c_spec.rb[1:1]',
|
1889
|
+
'./spec_integration/c_spec.rb[1:2]'
|
1890
|
+
])
|
1891
|
+
|
1892
|
+
example_full_descriptions = examples.map do
|
1893
|
+
_1.fetch('full_description')
|
1894
|
+
end
|
1895
|
+
expect(example_full_descriptions).to match_array([
|
1896
|
+
'A_describe A1 test example',
|
1897
|
+
'B_describe B1 test example',
|
1898
|
+
'B_describe B2 test example',
|
1899
|
+
'A_describe A2 test example',
|
1900
|
+
'C_describe C1 test example',
|
1901
|
+
'C_describe C2 test example'
|
1902
|
+
])
|
1903
|
+
|
1904
|
+
expect(json.fetch('summary_line')).to eq '6 examples, 0 failures'
|
1905
|
+
|
1906
|
+
expect(actual.exit_code).to eq 0
|
1907
|
+
end
|
1908
|
+
end
|
1909
|
+
|
1910
|
+
context 'when the RSpec split by examples is enabled AND JUnit XML formatter is used' do
|
1911
|
+
let(:xml_file) { "#{SPEC_DIRECTORY}/rspec.xml" }
|
1912
|
+
|
1913
|
+
before do
|
1914
|
+
ENV['KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES'] = 'true'
|
1915
|
+
|
1916
|
+
# remember to mock Queue API batches to include test examples (example: a_spec.rb[1:1])
|
1917
|
+
# for the following slow test files
|
1918
|
+
ENV['KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN'] = "#{SPEC_DIRECTORY}/a_spec.rb"
|
1919
|
+
end
|
1920
|
+
after do
|
1921
|
+
ENV.delete('KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES')
|
1922
|
+
ENV.delete('KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN')
|
1923
|
+
end
|
1924
|
+
|
1925
|
+
it 'produces a JUnit XML report' do
|
1926
|
+
rspec_options = "--format documentation --format RspecJunitFormatter --out ./#{xml_file}"
|
1927
|
+
|
1928
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
1929
|
+
describe 'A_describe' do
|
1930
|
+
it 'A1 test example' do
|
1931
|
+
expect(1).to eq 1
|
1932
|
+
end
|
1933
|
+
it 'A2 test example' do
|
1934
|
+
expect(1).to eq 1
|
1935
|
+
end
|
1936
|
+
end
|
1937
|
+
SPEC
|
1938
|
+
|
1939
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
1940
|
+
describe 'B_describe' do
|
1941
|
+
it 'B1 test example' do
|
1942
|
+
expect(1).to eq 1
|
1943
|
+
end
|
1944
|
+
it 'B2 test example' do
|
1945
|
+
expect(1).to eq 1
|
1946
|
+
end
|
1947
|
+
end
|
1948
|
+
SPEC
|
1949
|
+
|
1950
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
1951
|
+
describe 'C_describe' do
|
1952
|
+
it 'C1 test example' do
|
1953
|
+
expect(1).to eq 1
|
1954
|
+
end
|
1955
|
+
it 'C2 test example' do
|
1956
|
+
expect(1).to eq 1
|
1957
|
+
end
|
1958
|
+
end
|
1959
|
+
SPEC
|
1960
|
+
|
1961
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
1962
|
+
[spec_a, spec_b, spec_c]
|
1963
|
+
])
|
1964
|
+
mock_test_cases_for_slow_test_files([
|
1965
|
+
"#{spec_a.path}[1:1]",
|
1966
|
+
"#{spec_a.path}[1:2]",
|
1967
|
+
])
|
1968
|
+
stub_spec_batches([
|
1969
|
+
["#{spec_a.path}[1:1]", spec_b.path],
|
1970
|
+
["#{spec_a.path}[1:2]", spec_c.path],
|
1971
|
+
])
|
1972
|
+
|
1973
|
+
actual = subject
|
1974
|
+
|
1975
|
+
file_content = File.read(xml_file)
|
1976
|
+
doc = Nokogiri::XML(file_content)
|
1977
|
+
|
1978
|
+
files = doc.xpath('//testcase').map do |testcase|
|
1979
|
+
testcase['file']
|
1980
|
+
end
|
1981
|
+
expect(files).to eq([
|
1982
|
+
'./spec_integration/a_spec.rb',
|
1983
|
+
'./spec_integration/b_spec.rb',
|
1984
|
+
'./spec_integration/b_spec.rb',
|
1985
|
+
'./spec_integration/a_spec.rb',
|
1986
|
+
'./spec_integration/c_spec.rb',
|
1987
|
+
'./spec_integration/c_spec.rb',
|
1988
|
+
])
|
1989
|
+
|
1990
|
+
examples = doc.xpath('//testcase').map do |testcase|
|
1991
|
+
testcase['name']
|
1992
|
+
end
|
1993
|
+
expect(examples).to eq([
|
1994
|
+
'A_describe A1 test example',
|
1995
|
+
'B_describe B1 test example',
|
1996
|
+
'B_describe B2 test example',
|
1997
|
+
'A_describe A2 test example',
|
1998
|
+
'C_describe C1 test example',
|
1999
|
+
'C_describe C2 test example',
|
2000
|
+
])
|
2001
|
+
|
2002
|
+
expect(actual.exit_code).to eq 0
|
2003
|
+
end
|
2004
|
+
end
|
2005
|
+
|
2006
|
+
context 'when the RSpec split by examples is enabled AND simplecov is used' do
|
2007
|
+
let(:coverage_dir) { "#{KNAPSACK_PRO_TMP_DIR}/coverage" }
|
2008
|
+
let(:coverage_file) { "#{coverage_dir}/index.html" }
|
2009
|
+
|
2010
|
+
before do
|
2011
|
+
ENV['KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES'] = 'true'
|
2012
|
+
|
2013
|
+
# remember to mock Queue API batches to include test examples (example: a_spec.rb[1:1])
|
2014
|
+
# for the following slow test files
|
2015
|
+
ENV['KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN'] = "#{SPEC_DIRECTORY}/a_spec.rb"
|
2016
|
+
end
|
2017
|
+
after do
|
2018
|
+
ENV.delete('KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES')
|
2019
|
+
ENV.delete('KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN')
|
2020
|
+
end
|
2021
|
+
|
2022
|
+
it 'produces a code coverage report' do
|
2023
|
+
rspec_options = '--format documentation'
|
2024
|
+
|
2025
|
+
spec_helper = <<~SPEC
|
2026
|
+
require 'knapsack_pro'
|
2027
|
+
KnapsackPro::Adapters::RSpecAdapter.bind
|
2028
|
+
|
2029
|
+
require 'simplecov'
|
2030
|
+
SimpleCov.start do
|
2031
|
+
coverage_dir '#{coverage_dir}'
|
2032
|
+
end
|
2033
|
+
SPEC
|
2034
|
+
|
2035
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
2036
|
+
describe 'A_describe' do
|
2037
|
+
it 'A1 test example' do
|
2038
|
+
expect(1).to eq 1
|
2039
|
+
end
|
2040
|
+
it 'A2 test example' do
|
2041
|
+
expect(1).to eq 1
|
2042
|
+
end
|
2043
|
+
end
|
2044
|
+
SPEC
|
2045
|
+
|
2046
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
2047
|
+
describe 'B_describe' do
|
2048
|
+
it 'B1 test example' do
|
2049
|
+
expect(1).to eq 1
|
2050
|
+
end
|
2051
|
+
it 'B2 test example' do
|
2052
|
+
expect(1).to eq 1
|
2053
|
+
end
|
2054
|
+
end
|
2055
|
+
SPEC
|
2056
|
+
|
2057
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
2058
|
+
describe 'C_describe' do
|
2059
|
+
it 'C1 test example' do
|
2060
|
+
expect(1).to eq 1
|
2061
|
+
end
|
2062
|
+
it 'C2 test example' do
|
2063
|
+
expect(1).to eq 1
|
2064
|
+
end
|
2065
|
+
end
|
2066
|
+
SPEC
|
2067
|
+
|
2068
|
+
generate_specs(spec_helper, rspec_options, [
|
2069
|
+
[spec_a, spec_b, spec_c]
|
2070
|
+
])
|
2071
|
+
mock_test_cases_for_slow_test_files([
|
2072
|
+
"#{spec_a.path}[1:1]",
|
2073
|
+
"#{spec_a.path}[1:2]",
|
2074
|
+
])
|
2075
|
+
stub_spec_batches([
|
2076
|
+
["#{spec_a.path}[1:1]", spec_b.path],
|
2077
|
+
["#{spec_a.path}[1:2]", spec_c.path],
|
2078
|
+
])
|
2079
|
+
|
2080
|
+
actual = subject
|
2081
|
+
|
2082
|
+
file_content = File.read(coverage_file)
|
2083
|
+
|
2084
|
+
expect(file_content).to include(spec_a.path)
|
2085
|
+
expect(file_content).to include(spec_b.path)
|
2086
|
+
expect(file_content).to include(spec_c.path)
|
2087
|
+
|
2088
|
+
expect(actual.exit_code).to eq 0
|
2089
|
+
end
|
2090
|
+
end
|
2091
|
+
|
2092
|
+
context 'when the example_status_persistence_file_path option is used and multiple batches of tests are fetched from the Queue API and some tests are pending and failing' do
|
2093
|
+
let(:examples_file_path) { "#{SPEC_DIRECTORY}/examples.txt" }
|
2094
|
+
|
2095
|
+
after do
|
2096
|
+
File.delete(examples_file_path) if File.exist?(examples_file_path)
|
2097
|
+
end
|
2098
|
+
|
2099
|
+
it 'runs tests AND creates the example status persistence file' do
|
2100
|
+
rspec_options = '--format d'
|
2101
|
+
|
2102
|
+
spec_helper = <<~SPEC
|
2103
|
+
require 'knapsack_pro'
|
2104
|
+
KnapsackPro::Adapters::RSpecAdapter.bind
|
2105
|
+
|
2106
|
+
RSpec.configure do |config|
|
2107
|
+
config.example_status_persistence_file_path = '#{examples_file_path}'
|
2108
|
+
end
|
2109
|
+
SPEC
|
2110
|
+
|
2111
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
2112
|
+
describe 'A_describe' do
|
2113
|
+
xit 'A1 test example' do
|
2114
|
+
expect(1).to eq 1
|
2115
|
+
end
|
2116
|
+
end
|
2117
|
+
SPEC
|
2118
|
+
|
2119
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
2120
|
+
describe 'B_describe' do
|
2121
|
+
it 'B1 test example' do
|
2122
|
+
expect(1).to eq 0
|
2123
|
+
end
|
2124
|
+
end
|
2125
|
+
SPEC
|
2126
|
+
|
2127
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
2128
|
+
describe 'C_describe' do
|
2129
|
+
it 'C1 test example' do
|
2130
|
+
expect(1).to eq 1
|
2131
|
+
end
|
2132
|
+
it 'C2 test example' do
|
2133
|
+
expect(1).to eq 0
|
2134
|
+
end
|
2135
|
+
end
|
2136
|
+
SPEC
|
2137
|
+
|
2138
|
+
generate_specs(spec_helper, rspec_options, [
|
2139
|
+
[spec_a, spec_b],
|
2140
|
+
[spec_c],
|
2141
|
+
])
|
2142
|
+
|
2143
|
+
actual = subject
|
2144
|
+
|
2145
|
+
expect(actual.stdout).to include('4 examples, 2 failures, 1 pending')
|
2146
|
+
|
2147
|
+
expect(File.exist?(examples_file_path)).to be true
|
2148
|
+
|
2149
|
+
examples_file_content = File.read(examples_file_path)
|
2150
|
+
|
2151
|
+
expect(examples_file_content).to include './spec_integration/a_spec.rb[1:1] | pending'
|
2152
|
+
expect(examples_file_content).to include './spec_integration/b_spec.rb[1:1] | failed'
|
2153
|
+
expect(examples_file_content).to include './spec_integration/c_spec.rb[1:1] | passed'
|
2154
|
+
expect(examples_file_content).to include './spec_integration/c_spec.rb[1:2] | failed'
|
2155
|
+
|
2156
|
+
expect(actual.exit_code).to eq 1
|
2157
|
+
end
|
2158
|
+
end
|
2159
|
+
|
2160
|
+
context 'when the .rspec file has RSpec options' do
|
2161
|
+
let(:dot_rspec_file) { "#{SPEC_DIRECTORY}/.rspec" }
|
2162
|
+
|
2163
|
+
it 'ignores options from the .rspec file' do
|
2164
|
+
File.open(dot_rspec_file, 'w') { |file| file.write('--format documentation') }
|
2165
|
+
|
2166
|
+
rspec_options = ''
|
2167
|
+
|
2168
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
2169
|
+
describe 'A_describe' do
|
2170
|
+
it 'A1 test example' do
|
2171
|
+
expect(1).to eq 1
|
2172
|
+
end
|
2173
|
+
end
|
2174
|
+
SPEC
|
2175
|
+
|
2176
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
2177
|
+
[spec_a],
|
2178
|
+
])
|
2179
|
+
|
2180
|
+
actual = subject
|
2181
|
+
|
2182
|
+
expect(actual.stdout).not_to include('A1 test example')
|
2183
|
+
|
2184
|
+
expect(actual.stdout).to include('1 example, 0 failures')
|
2185
|
+
|
2186
|
+
expect(actual.exit_code).to eq 0
|
2187
|
+
end
|
2188
|
+
end
|
2189
|
+
|
2190
|
+
context 'when --options is set' do
|
2191
|
+
let(:rspec_custom_options_file) { "#{SPEC_DIRECTORY}/.rspec_custom_options" }
|
2192
|
+
|
2193
|
+
it 'uses options from the custom rspec file' do
|
2194
|
+
rspec_custom_options = <<~FILE
|
2195
|
+
--require spec_helper
|
2196
|
+
--profile
|
2197
|
+
FILE
|
2198
|
+
File.open(rspec_custom_options_file, 'w') { |file| file.write(rspec_custom_options) }
|
2199
|
+
|
2200
|
+
rspec_options = "--options ./#{rspec_custom_options_file}"
|
2201
|
+
|
2202
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
2203
|
+
describe 'A_describe' do
|
2204
|
+
it 'A1 test example' do
|
2205
|
+
expect(1).to eq 1
|
2206
|
+
end
|
2207
|
+
end
|
2208
|
+
SPEC
|
2209
|
+
|
2210
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
2211
|
+
describe 'B_describe' do
|
2212
|
+
it 'B1 test example' do
|
2213
|
+
expect(1).to eq 1
|
2214
|
+
end
|
2215
|
+
end
|
2216
|
+
SPEC
|
2217
|
+
|
2218
|
+
generate_specs(spec_helper_with_knapsack, rspec_options, [
|
2219
|
+
[spec_a],
|
2220
|
+
[spec_b],
|
2221
|
+
])
|
2222
|
+
|
2223
|
+
actual = subject
|
2224
|
+
|
2225
|
+
expect(actual.stdout).to include('2 examples, 0 failures')
|
2226
|
+
|
2227
|
+
expect(actual.stdout).to include('Top 2 slowest example groups')
|
2228
|
+
|
2229
|
+
expect(actual.exit_code).to eq 0
|
2230
|
+
end
|
2231
|
+
end
|
2232
|
+
end
|