knapsack_pro 5.6.0 → 5.7.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/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
|