knapsack_pro 5.6.0 → 5.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/lib/knapsack_pro/adapters/base_adapter.rb +8 -0
- data/lib/knapsack_pro/adapters/rspec_adapter.rb +29 -0
- data/lib/knapsack_pro/base_allocator_builder.rb +6 -25
- data/lib/knapsack_pro/version.rb +1 -1
- data/spec/knapsack_pro/adapters/base_adapter_spec.rb +12 -0
- data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +75 -0
- data/spec/knapsack_pro/base_allocator_builder_spec.rb +22 -48
- data/spec/knapsack_pro/client/connection_spec.rb +54 -7
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87b8a5c7eb2a78f487c9029cdeda3a1cc367b15e248eb1f3b5aaaf1959ebe333
|
4
|
+
data.tar.gz: 12fef31bb8aac248014b3d808190f7f2e7ae6217e83e4af9e40b17b6f98ed379
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 43379ac46ef1c5fa6a3fccf0d598831e009564ba37bdb264c681799aa893d5a9d8fbb569ce2bd83fe297b9c885117e95a5439731fd16ea35aa2f294acb88d936
|
7
|
+
data.tar.gz: 1be1143ac276347a0dee92bb15b7660f36b666c9cb8d025e9b1931f463803f9efc2a38ced34d4ca27bbfaa466fc1b6afc71d2960ced12b09888ca09a6572b14b
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### 5.7.0
|
4
|
+
|
5
|
+
* Performance improvement: don't run `rake knapsack_pro:rspec_test_example_detector` when no slow test files are detected for RSpec.
|
6
|
+
|
7
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/pull/225
|
8
|
+
|
9
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v5.6.0...v5.7.0
|
10
|
+
|
3
11
|
### 5.6.0
|
4
12
|
|
5
13
|
* Use `frozen_string_literal: true` to reduce memory usage
|
@@ -11,6 +11,14 @@ module KnapsackPro
|
|
11
11
|
"#{KnapsackPro::Config::TempFiles::TEMP_DIRECTORY_PATH}/#{adapter_name}-bind_method_called_for_node_#{KnapsackPro::Config::Env.ci_node_index}.txt"
|
12
12
|
end
|
13
13
|
|
14
|
+
def self.split_by_test_cases_enabled?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.test_file_cases_for(slow_test_files)
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
|
14
22
|
def self.slow_test_file?(adapter_class, test_file_path)
|
15
23
|
@slow_test_file_paths ||=
|
16
24
|
begin
|
@@ -5,6 +5,35 @@ module KnapsackPro
|
|
5
5
|
class RSpecAdapter < BaseAdapter
|
6
6
|
TEST_DIR_PATTERN = 'spec/**{,/*/**}/*_spec.rb'
|
7
7
|
|
8
|
+
def self.split_by_test_cases_enabled?
|
9
|
+
return false unless KnapsackPro::Config::Env.rspec_split_by_test_examples?
|
10
|
+
|
11
|
+
require 'rspec/core/version'
|
12
|
+
unless Gem::Version.new(::RSpec::Core::Version::STRING) >= Gem::Version.new('3.3.0')
|
13
|
+
raise "RSpec >= 3.3.0 is required to split test files by test examples. Learn more: #{KnapsackPro::Urls::SPLIT_BY_TEST_EXAMPLES}"
|
14
|
+
end
|
15
|
+
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.test_file_cases_for(slow_test_files)
|
20
|
+
KnapsackPro.logger.info("Generating RSpec test examples JSON report for slow test files to prepare it to be split by test examples (by individual test cases). Thanks to that, a single slow test file can be split across parallel CI nodes. Analyzing #{slow_test_files.size} slow test files.")
|
21
|
+
|
22
|
+
# generate the RSpec JSON report in a separate process to not pollute the RSpec state
|
23
|
+
cmd = [
|
24
|
+
'RACK_ENV=test',
|
25
|
+
'RAILS_ENV=test',
|
26
|
+
KnapsackPro::Config::Env.rspec_test_example_detector_prefix,
|
27
|
+
'rake knapsack_pro:rspec_test_example_detector',
|
28
|
+
].join(' ')
|
29
|
+
unless Kernel.system(cmd)
|
30
|
+
raise "Could not generate JSON report for RSpec. Rake task failed when running #{cmd}"
|
31
|
+
end
|
32
|
+
|
33
|
+
# read the JSON report
|
34
|
+
KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector.new.test_file_example_paths
|
35
|
+
end
|
36
|
+
|
8
37
|
def self.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!(cli_args)
|
9
38
|
if KnapsackPro::Config::Env.rspec_split_by_test_examples? && has_tag_option?(cli_args)
|
10
39
|
error_message = "It is not allowed to use the RSpec tag option together with the RSpec split by test examples feature. Please see: #{KnapsackPro::Urls::RSPEC__SPLIT_BY_TEST_EXAMPLES__TAG}"
|
@@ -35,35 +35,16 @@ module KnapsackPro
|
|
35
35
|
def fast_and_slow_test_files_to_run
|
36
36
|
test_files_to_run = all_test_files_to_run
|
37
37
|
|
38
|
-
if adapter_class
|
39
|
-
require 'rspec/core/version'
|
40
|
-
unless Gem::Version.new(::RSpec::Core::Version::STRING) >= Gem::Version.new('3.3.0')
|
41
|
-
raise "RSpec >= 3.3.0 is required to split test files by test examples. Learn more: #{KnapsackPro::Urls::SPLIT_BY_TEST_EXAMPLES}"
|
42
|
-
end
|
43
|
-
|
38
|
+
if adapter_class.split_by_test_cases_enabled?
|
44
39
|
slow_test_files = get_slow_test_files
|
40
|
+
return test_files_to_run if slow_test_files.empty?
|
45
41
|
|
46
|
-
|
47
|
-
|
48
|
-
# generate RSpec JSON report in separate process to not pollute RSpec state
|
49
|
-
cmd = [
|
50
|
-
'RACK_ENV=test',
|
51
|
-
'RAILS_ENV=test',
|
52
|
-
KnapsackPro::Config::Env.rspec_test_example_detector_prefix,
|
53
|
-
'rake knapsack_pro:rspec_test_example_detector',
|
54
|
-
].join(' ')
|
55
|
-
unless Kernel.system(cmd)
|
56
|
-
raise "Could not generate JSON report for RSpec. Rake task failed when running #{cmd}"
|
57
|
-
end
|
42
|
+
test_file_cases = adapter_class.test_file_cases_for(slow_test_files)
|
58
43
|
|
59
|
-
|
60
|
-
detector = KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector.new
|
61
|
-
test_file_example_paths = detector.test_file_example_paths
|
62
|
-
|
63
|
-
KnapsackPro::TestFilesWithTestCasesComposer.call(test_files_to_run, slow_test_files, test_file_example_paths)
|
64
|
-
else
|
65
|
-
test_files_to_run
|
44
|
+
return KnapsackPro::TestFilesWithTestCasesComposer.call(test_files_to_run, slow_test_files, test_file_cases)
|
66
45
|
end
|
46
|
+
|
47
|
+
test_files_to_run
|
67
48
|
end
|
68
49
|
|
69
50
|
private
|
data/lib/knapsack_pro/version.rb
CHANGED
@@ -42,6 +42,18 @@ describe KnapsackPro::Adapters::BaseAdapter do
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
describe '.split_by_test_cases_enabled?' do
|
46
|
+
subject { described_class.split_by_test_cases_enabled? }
|
47
|
+
|
48
|
+
it { expect(subject).to be false }
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '.test_file_cases_for' do
|
52
|
+
subject { described_class.test_file_cases_for([]) }
|
53
|
+
|
54
|
+
it { expect { subject }.to raise_error NotImplementedError }
|
55
|
+
end
|
56
|
+
|
45
57
|
describe '.slow_test_file?' do
|
46
58
|
let(:adapter_class) { double }
|
47
59
|
let(:slow_test_files) do
|
@@ -12,6 +12,81 @@ describe KnapsackPro::Adapters::RSpecAdapter do
|
|
12
12
|
it_behaves_like 'adapter'
|
13
13
|
end
|
14
14
|
|
15
|
+
describe '.split_by_test_cases_enabled?' do
|
16
|
+
subject { described_class.split_by_test_cases_enabled? }
|
17
|
+
|
18
|
+
before do
|
19
|
+
expect(KnapsackPro::Config::Env).to receive(:rspec_split_by_test_examples?).and_return(rspec_split_by_test_examples_enabled)
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'when the RSpec split by test examples is enabled' do
|
23
|
+
let(:rspec_split_by_test_examples_enabled) { true }
|
24
|
+
|
25
|
+
it { expect(subject).to be true }
|
26
|
+
|
27
|
+
context 'when the RSpec version is < 3.3.0' do
|
28
|
+
before do
|
29
|
+
stub_const('RSpec::Core::Version::STRING', '3.2.0')
|
30
|
+
end
|
31
|
+
|
32
|
+
it do
|
33
|
+
expect { subject }.to raise_error RuntimeError, 'RSpec >= 3.3.0 is required to split test files by test examples. Learn more: https://knapsackpro.com/perma/ruby/split-by-test-examples'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when the RSpec split by test examples is disabled' do
|
39
|
+
let(:rspec_split_by_test_examples_enabled) { false }
|
40
|
+
|
41
|
+
it { expect(subject).to be false }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '.test_file_cases_for' do
|
46
|
+
let(:slow_test_files) do
|
47
|
+
[
|
48
|
+
'1_spec.rb',
|
49
|
+
'2_spec.rb',
|
50
|
+
'3_spec.rb',
|
51
|
+
'4_spec.rb',
|
52
|
+
'5_spec.rb',
|
53
|
+
]
|
54
|
+
end
|
55
|
+
|
56
|
+
subject { described_class.test_file_cases_for(slow_test_files) }
|
57
|
+
|
58
|
+
before do
|
59
|
+
logger = instance_double(Logger)
|
60
|
+
expect(KnapsackPro).to receive(:logger).and_return(logger)
|
61
|
+
expect(logger).to receive(:info).with("Generating RSpec test examples JSON report for slow test files to prepare it to be split by test examples (by individual test cases). Thanks to that, a single slow test file can be split across parallel CI nodes. Analyzing 5 slow test files.")
|
62
|
+
|
63
|
+
cmd = 'RACK_ENV=test RAILS_ENV=test bundle exec rake knapsack_pro:rspec_test_example_detector'
|
64
|
+
expect(Kernel).to receive(:system).with(cmd).and_return(cmd_result)
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'when the rake task to detect RSpec test examples succeeded' do
|
68
|
+
let(:cmd_result) { true }
|
69
|
+
|
70
|
+
it 'returns test example paths for slow test files' do
|
71
|
+
rspec_test_example_detector = instance_double(KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector)
|
72
|
+
expect(KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector).to receive(:new).and_return(rspec_test_example_detector)
|
73
|
+
|
74
|
+
test_file_example_paths = double
|
75
|
+
expect(rspec_test_example_detector).to receive(:test_file_example_paths).and_return(test_file_example_paths)
|
76
|
+
|
77
|
+
expect(subject).to eq test_file_example_paths
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'when the rake task to detect RSpec test examples failed' do
|
82
|
+
let(:cmd_result) { false }
|
83
|
+
|
84
|
+
it do
|
85
|
+
expect { subject }.to raise_error(RuntimeError, 'Could not generate JSON report for RSpec. Rake task failed when running RACK_ENV=test RAILS_ENV=test bundle exec rake knapsack_pro:rspec_test_example_detector')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
15
90
|
describe '.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!' do
|
16
91
|
let(:cli_args) { double }
|
17
92
|
|
@@ -117,8 +117,8 @@ describe KnapsackPro::BaseAllocatorBuilder do
|
|
117
117
|
describe '#fast_and_slow_test_files_to_run' do
|
118
118
|
subject { allocator_builder.fast_and_slow_test_files_to_run }
|
119
119
|
|
120
|
-
context 'when
|
121
|
-
it do
|
120
|
+
context 'when split by test cases disabled' do
|
121
|
+
it 'returns test files to run based on test files on the disk' do
|
122
122
|
test_file_pattern = double
|
123
123
|
expect(KnapsackPro::TestFilePattern).to receive(:call).with(adapter_class).and_return(test_file_pattern)
|
124
124
|
|
@@ -129,70 +129,44 @@ describe KnapsackPro::BaseAllocatorBuilder do
|
|
129
129
|
end
|
130
130
|
end
|
131
131
|
|
132
|
-
context 'when
|
133
|
-
let(:adapter_class) { KnapsackPro::Adapters::RSpecAdapter }
|
132
|
+
context 'when split by test cases enabled' do
|
134
133
|
let(:test_files_to_run) { double }
|
135
|
-
let(:cmd) { 'RACK_ENV=test RAILS_ENV=test bundle exec rake knapsack_pro:rspec_test_example_detector' }
|
136
134
|
|
137
|
-
before
|
138
|
-
expect(
|
135
|
+
before do
|
136
|
+
expect(adapter_class).to receive(:split_by_test_cases_enabled?).and_return(true)
|
139
137
|
|
140
138
|
test_file_pattern = double
|
141
139
|
expect(KnapsackPro::TestFilePattern).to receive(:call).with(adapter_class).and_return(test_file_pattern)
|
142
140
|
|
143
141
|
expect(KnapsackPro::TestFileFinder).to receive(:call).with(test_file_pattern).and_return(test_files_to_run)
|
144
|
-
end
|
145
142
|
|
146
|
-
|
147
|
-
before do
|
148
|
-
stub_const('RSpec::Core::Version::STRING', '3.2.0')
|
149
|
-
end
|
150
|
-
|
151
|
-
it do
|
152
|
-
expect { subject }.to raise_error RuntimeError, 'RSpec >= 3.3.0 is required to split test files by test examples. Learn more: https://knapsackpro.com/perma/ruby/split-by-test-examples'
|
153
|
-
end
|
143
|
+
expect(allocator_builder).to receive(:get_slow_test_files).and_return(slow_test_files)
|
154
144
|
end
|
155
145
|
|
156
|
-
context 'when
|
157
|
-
let(:slow_test_files)
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
before do
|
164
|
-
expect(allocator_builder).to receive(:get_slow_test_files).and_return(slow_test_files)
|
165
|
-
|
166
|
-
expect(KnapsackPro).to receive(:logger).and_return(logger)
|
167
|
-
|
168
|
-
expect(Kernel).to receive(:system).with(cmd).and_return(cmd_result)
|
169
|
-
|
170
|
-
rspec_test_example_detector = instance_double(KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector)
|
171
|
-
expect(KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector).to receive(:new).and_return(rspec_test_example_detector)
|
172
|
-
expect(rspec_test_example_detector).to receive(:test_file_example_paths).and_return(test_file_example_paths)
|
173
|
-
|
174
|
-
expect(KnapsackPro::TestFilesWithTestCasesComposer).to receive(:call).with(test_files_to_run, slow_test_files, test_file_example_paths).and_return(test_files_with_test_cases)
|
146
|
+
context 'when slow test files are detected' do
|
147
|
+
let(:slow_test_files) do
|
148
|
+
[
|
149
|
+
'1_spec.rb',
|
150
|
+
'2_spec.rb',
|
151
|
+
]
|
175
152
|
end
|
176
153
|
|
177
|
-
it do
|
178
|
-
|
154
|
+
it 'returns test files with test cases' do
|
155
|
+
test_file_cases = double
|
156
|
+
expect(adapter_class).to receive(:test_file_cases_for).with(slow_test_files).and_return(test_file_cases)
|
157
|
+
|
158
|
+
test_files_with_test_cases = double
|
159
|
+
expect(KnapsackPro::TestFilesWithTestCasesComposer).to receive(:call).with(test_files_to_run, slow_test_files, test_file_cases).and_return(test_files_with_test_cases)
|
179
160
|
|
180
161
|
expect(subject).to eq test_files_with_test_cases
|
181
162
|
end
|
182
163
|
end
|
183
164
|
|
184
|
-
context 'when
|
185
|
-
let(:slow_test_files) {
|
186
|
-
let(:cmd_result) { false }
|
187
|
-
|
188
|
-
before do
|
189
|
-
expect(allocator_builder).to receive(:get_slow_test_files).and_return(slow_test_files)
|
190
|
-
|
191
|
-
expect(Kernel).to receive(:system).with(cmd).and_return(cmd_result)
|
192
|
-
end
|
165
|
+
context 'when slow test files are not detected' do
|
166
|
+
let(:slow_test_files) { [] }
|
193
167
|
|
194
|
-
it do
|
195
|
-
expect
|
168
|
+
it 'returns test files without test cases' do
|
169
|
+
expect(subject).to eq test_files_to_run
|
196
170
|
end
|
197
171
|
end
|
198
172
|
end
|
@@ -282,15 +282,17 @@ describe KnapsackPro::Client::Connection do
|
|
282
282
|
request_hash: request_hash)
|
283
283
|
end
|
284
284
|
let(:test_suite_token) { '3fa64859337f6e56409d49f865d13fd7' }
|
285
|
-
|
286
285
|
let(:connection) { described_class.new(action) }
|
287
|
-
|
288
|
-
|
289
|
-
stub_const('ENV', {
|
286
|
+
let(:headers) do
|
287
|
+
{
|
290
288
|
'KNAPSACK_PRO_ENDPOINT' => 'http://api.knapsackpro.test:3000',
|
291
289
|
'KNAPSACK_PRO_TEST_SUITE_TOKEN' => test_suite_token,
|
292
290
|
'GITHUB_ACTIONS' => 'true',
|
293
|
-
}
|
291
|
+
}
|
292
|
+
end
|
293
|
+
|
294
|
+
before do
|
295
|
+
stub_const('ENV', headers)
|
294
296
|
end
|
295
297
|
|
296
298
|
describe '#call' do
|
@@ -311,7 +313,7 @@ describe KnapsackPro::Client::Connection do
|
|
311
313
|
expect(http).to receive(:read_timeout=).with(15)
|
312
314
|
end
|
313
315
|
|
314
|
-
context 'when http method is POST' do
|
316
|
+
context 'when http method is POST on GitHub Actions' do
|
315
317
|
let(:http_method) { :post }
|
316
318
|
|
317
319
|
before do
|
@@ -334,7 +336,30 @@ describe KnapsackPro::Client::Connection do
|
|
334
336
|
end
|
335
337
|
end
|
336
338
|
|
337
|
-
context 'when http method is
|
339
|
+
context 'when http method is POST and CI is undetected' do
|
340
|
+
let(:http_method) { :post }
|
341
|
+
|
342
|
+
let(:headers) do
|
343
|
+
{
|
344
|
+
'KNAPSACK_PRO_ENDPOINT' => 'http://api.knapsackpro.test:3000',
|
345
|
+
'KNAPSACK_PRO_TEST_SUITE_TOKEN' => test_suite_token,
|
346
|
+
}
|
347
|
+
end
|
348
|
+
|
349
|
+
before do
|
350
|
+
expect(http).to receive(:post).with(
|
351
|
+
anything,
|
352
|
+
anything,
|
353
|
+
hash_not_including('KNAPSACK-PRO-CI-PROVIDER')
|
354
|
+
).and_return(http_response)
|
355
|
+
end
|
356
|
+
|
357
|
+
it_behaves_like 'when request got response from API' do
|
358
|
+
let(:expected_http_method) { 'POST' }
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
context 'when http method is GET on GitHub Actions' do
|
338
363
|
let(:http_method) { :get }
|
339
364
|
|
340
365
|
before do
|
@@ -358,6 +383,28 @@ describe KnapsackPro::Client::Connection do
|
|
358
383
|
end
|
359
384
|
end
|
360
385
|
|
386
|
+
context 'when http method is GET and CI is undetected' do
|
387
|
+
let(:http_method) { :get }
|
388
|
+
|
389
|
+
let(:headers) do
|
390
|
+
{
|
391
|
+
'KNAPSACK_PRO_ENDPOINT' => 'http://api.knapsackpro.test:3000',
|
392
|
+
'KNAPSACK_PRO_TEST_SUITE_TOKEN' => test_suite_token,
|
393
|
+
}
|
394
|
+
end
|
395
|
+
|
396
|
+
before do
|
397
|
+
expect(http).to receive(:get).with(
|
398
|
+
anything,
|
399
|
+
hash_not_including('KNAPSACK-PRO-CI-PROVIDER')
|
400
|
+
).and_return(http_response)
|
401
|
+
end
|
402
|
+
|
403
|
+
it_behaves_like 'when request got response from API' do
|
404
|
+
let(:expected_http_method) { 'GET' }
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
361
408
|
context 'when retry request for http method POST' do
|
362
409
|
let(:http_method) { :post }
|
363
410
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knapsack_pro
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ArturT
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-09-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|