knapsack_pro 7.1.0 → 7.4.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 +14 -0
- data/CHANGELOG.md +109 -0
- data/lib/knapsack_pro/batch.rb +20 -0
- data/lib/knapsack_pro/client/connection.rb +9 -1
- data/lib/knapsack_pro/config/env.rb +24 -26
- data/lib/knapsack_pro/extensions/rspec_extension.rb +7 -2
- data/lib/knapsack_pro/hooks/queue.rb +7 -4
- data/lib/knapsack_pro/queue.rb +40 -0
- data/lib/knapsack_pro/runners/queue/rspec_runner.rb +6 -3
- data/lib/knapsack_pro/version.rb +1 -1
- data/lib/knapsack_pro.rb +2 -0
- data/spec/integration/runners/queue/rspec_runner_spec.rb +133 -0
- data/spec/knapsack_pro/config/env_spec.rb +294 -9
- data/spec/knapsack_pro/hooks/queue_spec.rb +90 -48
- data/spec/knapsack_pro/queue_spec.rb +35 -0
- data/spec/spec_helper.rb +4 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 965d1d9b1d138cf0db8abe0fb44fc1672d5992d9f40f014c0f1f72c370d82585
|
4
|
+
data.tar.gz: 9570a9c2ffd20d1324451106f7a6144a298b6efb02981eabdc1f354d5c289940
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7b335edb2e4107b44b0bf69b7cffdf0af110e316ad3d1f07190d919e23fe7172cae3081c5f97450174f1e0597f9d71387dafd8a0861aca604531a9cb1b1c26c
|
7
|
+
data.tar.gz: 269bc36e25e5a2048f2105f973523bc00dfba73b38ebf7eac482b121a8cd22a129f72c671579bd230830bcb3aa600428d2f7a5a3410ce1515c7ee4353fc36dde
|
data/.circleci/config.yml
CHANGED
@@ -152,6 +152,12 @@ jobs:
|
|
152
152
|
command: |
|
153
153
|
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--$CIRCLE_BUILD_NUM--regular"
|
154
154
|
bundle exec rake knapsack_pro:rspec
|
155
|
+
- run:
|
156
|
+
working_directory: ~/rails-app-with-knapsack_pro
|
157
|
+
command: |
|
158
|
+
# retry the same split ||
|
159
|
+
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--$CIRCLE_BUILD_NUM--regular"
|
160
|
+
bundle exec rake knapsack_pro:rspec
|
155
161
|
- run:
|
156
162
|
working_directory: ~/rails-app-with-knapsack_pro
|
157
163
|
command: |
|
@@ -285,6 +291,7 @@ jobs:
|
|
285
291
|
RACK_ENV: test
|
286
292
|
KNAPSACK_PRO_ENDPOINT: https://api-staging.knapsackpro.com
|
287
293
|
KNAPSACK_PRO_TEST_SUITE_TOKEN_MINITEST: $KNAPSACK_PRO_TEST_SUITE_TOKEN_MINITEST
|
294
|
+
KNAPSACK_PRO_RSPEC_DISABLED: true
|
288
295
|
EXTRA_TEST_FILES_DELAY: 10
|
289
296
|
- image: cimg/postgres:14.7
|
290
297
|
environment:
|
@@ -308,6 +315,12 @@ jobs:
|
|
308
315
|
command: |
|
309
316
|
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--$CIRCLE_BUILD_NUM--regular"
|
310
317
|
bundle exec rake knapsack_pro:minitest[--verbose]
|
318
|
+
- run:
|
319
|
+
working_directory: ~/rails-app-with-knapsack_pro
|
320
|
+
command: |
|
321
|
+
# retry the same split ||
|
322
|
+
export KNAPSACK_PRO_BRANCH="$CIRCLE_BRANCH--$CIRCLE_BUILD_NUM--regular"
|
323
|
+
bundle exec rake knapsack_pro:minitest[--verbose]
|
311
324
|
|
312
325
|
e2e-queue-minitest:
|
313
326
|
parameters:
|
@@ -325,6 +338,7 @@ jobs:
|
|
325
338
|
RACK_ENV: test
|
326
339
|
KNAPSACK_PRO_ENDPOINT: https://api-staging.knapsackpro.com
|
327
340
|
KNAPSACK_PRO_TEST_SUITE_TOKEN_MINITEST: $KNAPSACK_PRO_TEST_SUITE_TOKEN_MINITEST
|
341
|
+
KNAPSACK_PRO_RSPEC_DISABLED: true
|
328
342
|
EXTRA_TEST_FILES_DELAY: 10
|
329
343
|
- image: cimg/postgres:14.7
|
330
344
|
environment:
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,76 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### 7.4.0
|
4
|
+
* Warn when `KNAPSACK_PRO_*` environment variables are set manually if their values could be automatically determined from supported CI environments.
|
5
|
+
|
6
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/pull/254
|
7
|
+
|
8
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v7.3.0...v7.4.0
|
9
|
+
|
10
|
+
### 7.3.0
|
11
|
+
|
12
|
+
* [Queue Mode][RSpec] Pass each batch of tests to the queue hooks: `KnapsackPro::Hooks::Queue.before_subset_queue` and `KnapsackPro::Hooks::Queue.after_subset_queue`
|
13
|
+
|
14
|
+
The `KnapsackPro::Hooks::Queue.before_subset_queue` and `KnapsackPro::Hooks::Queue.after_subset_queue` hooks get a 3rd variable - the `queue`.
|
15
|
+
|
16
|
+
The `queue` variable stores an enumerable collection with each batch of tests fetched from the Queue API. The batch has:
|
17
|
+
|
18
|
+
* a list of test file paths (`KnapsackPro::Batch#test_file_paths` returns an array like `['a_spec.rb', 'b_spec.rb']`)
|
19
|
+
* a status of the given set of tests in the batch (`KnapsackPro::Batch#status` returns `:not_executed`, `:passed` or `:failed`)
|
20
|
+
|
21
|
+
Example usage:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
# spec_helper.rb
|
25
|
+
|
26
|
+
KnapsackPro::Hooks::Queue.before_subset_queue do |queue_id, subset_queue_id, queue|
|
27
|
+
print "Tests from all batches fetched from the Queue API so far: "
|
28
|
+
puts queue.map(&:test_file_paths).inspect
|
29
|
+
|
30
|
+
queue.each(&:test_file_paths) # you can use each as well
|
31
|
+
|
32
|
+
print "Current batch tests: "
|
33
|
+
puts queue.current_batch.test_file_paths.inspect
|
34
|
+
|
35
|
+
print "Current batch status: "
|
36
|
+
puts queue.current_batch.status # returns :not_executed in the `before_subset_queue` hook
|
37
|
+
end
|
38
|
+
|
39
|
+
KnapsackPro::Hooks::Queue.after_subset_queue do |queue_id, subset_queue_id, queue|
|
40
|
+
print "Tests from all batches fetched from the Queue API so far: "
|
41
|
+
puts queue.map(&:test_file_paths).inspect
|
42
|
+
|
43
|
+
print "Current batch tests: "
|
44
|
+
puts queue.current_batch.test_file_paths.inspect
|
45
|
+
|
46
|
+
print "Current batch status: "
|
47
|
+
puts queue.current_batch.status # returns :passed or :failed in the `after_subset_queue` hook
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/pull/253
|
52
|
+
|
53
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v7.2.0...v7.3.0
|
54
|
+
|
55
|
+
### 7.2.0
|
56
|
+
|
57
|
+
* Always use the original `Net::HTTP` client, even when WebMock replaces it with its own
|
58
|
+
* No action is required on your side, but you can delete the following code that you may have used to configure Knapsack Pro with WebMock or VCR:
|
59
|
+
```diff
|
60
|
+
WebMock.disable_net_connect!(
|
61
|
+
allow_localhost: true,
|
62
|
+
- allow: ['api.knapsackpro.com']
|
63
|
+
)
|
64
|
+
|
65
|
+
# VCR
|
66
|
+
- config.ignore_hosts('localhost', '127.0.0.1', '0.0.0.0', 'api.knapsackpro.com')
|
67
|
+
+ config.ignore_localhost = true
|
68
|
+
```
|
69
|
+
|
70
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/pull/251
|
71
|
+
|
72
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v7.1.0...v7.2.0
|
73
|
+
|
3
74
|
### 7.1.0
|
4
75
|
|
5
76
|
* [RSpec] [Queue Mode] Log error message and backtrace when unexpected failure is raised
|
@@ -28,6 +99,25 @@ https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v7.0.0...v7.0.1
|
|
28
99
|
__After:__<br>
|
29
100
|
The `before(:suite)` and `after(:suite)` hooks are executed only once: `before(:suite)` is executed before starting tests, `after(:suite)` is executed after all tests are completed. (It is what you would expect from RSpec).
|
30
101
|
|
102
|
+
* It is recommended that you define your `before(:suite)` hooks in `spec_helper.rb` or `rails_helper.rb`. These files should be loaded before any test files so that the hook is registered by RSpec.
|
103
|
+
|
104
|
+
The `before(:suite)` hook is executed first. After that, test files are dynamically loaded in multiple batches from the Knapsack Pro Queue API. __The `before(:suite)` hooks defined in test files won't be executed because it is too late!__
|
105
|
+
|
106
|
+
If you need to have something that is similar to `before(:suite)` and you want to define it in a test file, then you can use this:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
RSpec.configure do |config|
|
110
|
+
config.before(:context) do
|
111
|
+
unless ENV['MY_HOOK_NAME']
|
112
|
+
# your code to run in the hook
|
113
|
+
end
|
114
|
+
ENV['MY_HOOK_NAME'] = 'hook_called'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
Alternatively, if you need to [load code only once for a specific type of specs you can check this](https://docs.knapsackpro.com/ruby/rspec/#load-code-only-once-for-a-specific-type-of-specs).
|
120
|
+
|
31
121
|
* The `KnapsackPro::Hooks::Queue.after_queue` hook change:
|
32
122
|
|
33
123
|
__Before:__<br>
|
@@ -36,6 +126,25 @@ https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v7.0.0...v7.0.1
|
|
36
126
|
__After:__<br>
|
37
127
|
The `KnapsackPro::Hooks::Queue.after_queue` hook is executed __inside__ of the `after(:suite)` hook.
|
38
128
|
|
129
|
+
* The RSpec `filter_run_excluding` option is not supported in Queue Mode.
|
130
|
+
|
131
|
+
__Before:__ The following option won't run tests tagged with `:manual`.<br>
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
# spec_helper.rb
|
135
|
+
RSpec.configure do |config|
|
136
|
+
config.filter_run_excluding :manual
|
137
|
+
end
|
138
|
+
```
|
139
|
+
|
140
|
+
__After:__ The RSpec `filter_run_excluding` option is ignored in Queue Mode. You must manually pass the `--tag ~manual` option to the Knapsack Pro command to skip tests tagged with `:manual`.
|
141
|
+
|
142
|
+
```
|
143
|
+
bundle exec rake "knapsack_pro:queue:rspec[--tag ~manual]"
|
144
|
+
```
|
145
|
+
|
146
|
+
* Please [update the datadog-ci gem to the latest version](https://github.com/DataDog/datadog-ci-rb/issues/147#issuecomment-2099997045) if you use DataDog. This allows DataDog to collect RSpec data correctly in Knapsack Pro Queue Mode.
|
147
|
+
|
39
148
|
* Recommended RSpec changes in your project:
|
40
149
|
* Remove the following code if you use Queue Mode and the `rspec_junit_formatter` gem to generate JUnit XML or JSON reports:
|
41
150
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KnapsackPro
|
4
|
+
class Batch
|
5
|
+
attr_reader :test_file_paths, :status
|
6
|
+
|
7
|
+
def initialize(test_file_paths)
|
8
|
+
@test_file_paths = test_file_paths
|
9
|
+
@status = :not_executed
|
10
|
+
end
|
11
|
+
|
12
|
+
def _passed
|
13
|
+
@status = :passed
|
14
|
+
end
|
15
|
+
|
16
|
+
def _failed
|
17
|
+
@status = :failed
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -141,13 +141,21 @@ module KnapsackPro
|
|
141
141
|
end
|
142
142
|
|
143
143
|
def build_http(uri)
|
144
|
-
http =
|
144
|
+
http = net_http.new(uri.host, uri.port)
|
145
145
|
http.use_ssl = (uri.scheme == 'https')
|
146
146
|
http.open_timeout = TIMEOUT
|
147
147
|
http.read_timeout = TIMEOUT
|
148
148
|
http
|
149
149
|
end
|
150
150
|
|
151
|
+
def net_http
|
152
|
+
if defined?(WebMock::HttpLibAdapters::NetHttpAdapter::OriginalNetHTTP)
|
153
|
+
WebMock::HttpLibAdapters::NetHttpAdapter::OriginalNetHTTP
|
154
|
+
else
|
155
|
+
Net::HTTP
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
151
159
|
def post
|
152
160
|
uri = URI.parse(endpoint_url)
|
153
161
|
http = build_http(uri)
|
@@ -13,30 +13,21 @@ module KnapsackPro
|
|
13
13
|
|
14
14
|
class << self
|
15
15
|
def ci_node_total
|
16
|
-
(
|
17
|
-
ci_env_for(:node_total) ||
|
18
|
-
1).to_i
|
16
|
+
(env_for('KNAPSACK_PRO_CI_NODE_TOTAL', :node_total) || 1).to_i
|
19
17
|
end
|
20
18
|
|
21
19
|
def ci_node_index
|
22
|
-
(
|
23
|
-
ci_env_for(:node_index) ||
|
24
|
-
0).to_i
|
20
|
+
(env_for('KNAPSACK_PRO_CI_NODE_INDEX', :node_index) || 0).to_i
|
25
21
|
end
|
26
22
|
|
27
23
|
def ci_node_build_id
|
28
24
|
env_name = 'KNAPSACK_PRO_CI_NODE_BUILD_ID'
|
29
|
-
|
30
|
-
ci_env_for(:node_build_id) ||
|
25
|
+
env_for(env_name, :node_build_id) ||
|
31
26
|
raise("Missing environment variable #{env_name}. Read more at #{KnapsackPro::Urls::KNAPSACK_PRO_CI_NODE_BUILD_ID}")
|
32
27
|
end
|
33
28
|
|
34
29
|
def ci_node_retry_count
|
35
|
-
(
|
36
|
-
ENV['KNAPSACK_PRO_CI_NODE_RETRY_COUNT'] ||
|
37
|
-
ci_env_for(:node_retry_count) ||
|
38
|
-
0
|
39
|
-
).to_i
|
30
|
+
(env_for('KNAPSACK_PRO_CI_NODE_RETRY_COUNT', :node_retry_count) || 0).to_i
|
40
31
|
end
|
41
32
|
|
42
33
|
def max_request_retries
|
@@ -47,23 +38,19 @@ module KnapsackPro
|
|
47
38
|
end
|
48
39
|
|
49
40
|
def commit_hash
|
50
|
-
|
51
|
-
ci_env_for(:commit_hash)
|
41
|
+
env_for('KNAPSACK_PRO_COMMIT_HASH', :commit_hash)
|
52
42
|
end
|
53
43
|
|
54
44
|
def branch
|
55
|
-
|
56
|
-
ci_env_for(:branch)
|
45
|
+
env_for('KNAPSACK_PRO_BRANCH', :branch)
|
57
46
|
end
|
58
47
|
|
59
48
|
def project_dir
|
60
|
-
|
61
|
-
ci_env_for(:project_dir)
|
49
|
+
env_for('KNAPSACK_PRO_PROJECT_DIR', :project_dir)
|
62
50
|
end
|
63
51
|
|
64
52
|
def user_seat
|
65
|
-
|
66
|
-
ci_env_for(:user_seat)
|
53
|
+
env_for('KNAPSACK_PRO_USER_SEAT', :user_seat)
|
67
54
|
end
|
68
55
|
|
69
56
|
def masked_user_seat
|
@@ -183,7 +170,7 @@ module KnapsackPro
|
|
183
170
|
def fixed_queue_split
|
184
171
|
@fixed_queue_split ||= begin
|
185
172
|
env_name = 'KNAPSACK_PRO_FIXED_QUEUE_SPLIT'
|
186
|
-
computed =
|
173
|
+
computed = env_for(env_name, :fixed_queue_split).to_s
|
187
174
|
|
188
175
|
if !ENV.key?(env_name)
|
189
176
|
KnapsackPro.logger.info("#{env_name} is not set. Using default value: #{computed}. Learn more at #{KnapsackPro::Urls::FIXED_QUEUE_SPLIT}")
|
@@ -249,10 +236,6 @@ module KnapsackPro
|
|
249
236
|
end
|
250
237
|
end
|
251
238
|
|
252
|
-
def ci_env_for(env_name)
|
253
|
-
detected_ci.new.send(env_name)
|
254
|
-
end
|
255
|
-
|
256
239
|
def detected_ci
|
257
240
|
detected = KnapsackPro::Config::CI.constants.map do |constant|
|
258
241
|
Object.const_get("KnapsackPro::Config::CI::#{constant}").new.detected
|
@@ -293,6 +276,21 @@ module KnapsackPro
|
|
293
276
|
def required_env(env_name)
|
294
277
|
ENV[env_name] || raise("Missing environment variable #{env_name}")
|
295
278
|
end
|
279
|
+
|
280
|
+
def env_for(knapsack_env_name, ci_env_method)
|
281
|
+
knapsack_env_value = ENV[knapsack_env_name]
|
282
|
+
ci_env_value = ci_env_for(ci_env_method)
|
283
|
+
|
284
|
+
if !knapsack_env_value.nil? && !ci_env_value.nil? && knapsack_env_value != ci_env_value.to_s
|
285
|
+
KnapsackPro.logger.info("You have set the environment variable #{knapsack_env_name} to #{knapsack_env_value} which could be automatically determined from the CI environment as #{ci_env_value}.")
|
286
|
+
end
|
287
|
+
|
288
|
+
knapsack_env_value != nil ? knapsack_env_value : ci_env_value
|
289
|
+
end
|
290
|
+
|
291
|
+
def ci_env_for(env_name)
|
292
|
+
detected_ci.new.send(env_name)
|
293
|
+
end
|
296
294
|
end
|
297
295
|
end
|
298
296
|
end
|
@@ -90,7 +90,7 @@ module KnapsackPro
|
|
90
90
|
|
91
91
|
configuration.reporter.report(_expected_example_count = 0) do |reporter|
|
92
92
|
configuration.with_suite_hooks do
|
93
|
-
queue_runner.with_batch do |test_file_paths|
|
93
|
+
queue_runner.with_batch do |test_file_paths, queue|
|
94
94
|
knapsack__load_spec_files_batch(test_file_paths)
|
95
95
|
|
96
96
|
examples_passed = ordering_strategy.order(world.example_groups).map do |example_group|
|
@@ -98,7 +98,12 @@ module KnapsackPro
|
|
98
98
|
example_group.run(reporter)
|
99
99
|
end.all?
|
100
100
|
|
101
|
-
|
101
|
+
if examples_passed
|
102
|
+
queue.mark_batch_passed
|
103
|
+
else
|
104
|
+
queue.mark_batch_failed
|
105
|
+
node_examples_passed = false
|
106
|
+
end
|
102
107
|
|
103
108
|
knapsack__persist_example_statuses
|
104
109
|
|
@@ -54,22 +54,25 @@ module KnapsackPro
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
-
|
57
|
+
# `queue` is always present for RSpec
|
58
|
+
def call_before_subset_queue(queue = nil)
|
58
59
|
return unless before_subset_queue_store
|
59
60
|
before_subset_queue_store.each do |block|
|
60
61
|
block.call(
|
61
62
|
KnapsackPro::Config::Env.queue_id,
|
62
|
-
KnapsackPro::Config::Env.subset_queue_id
|
63
|
+
KnapsackPro::Config::Env.subset_queue_id,
|
64
|
+
queue
|
63
65
|
)
|
64
66
|
end
|
65
67
|
end
|
66
68
|
|
67
|
-
def call_after_subset_queue
|
69
|
+
def call_after_subset_queue(queue = nil)
|
68
70
|
return unless after_subset_queue_store
|
69
71
|
after_subset_queue_store.each do |block|
|
70
72
|
block.call(
|
71
73
|
KnapsackPro::Config::Env.queue_id,
|
72
|
-
KnapsackPro::Config::Env.subset_queue_id
|
74
|
+
KnapsackPro::Config::Env.subset_queue_id,
|
75
|
+
queue
|
73
76
|
)
|
74
77
|
end
|
75
78
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KnapsackPro
|
4
|
+
class Queue
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@batches = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def each(&block)
|
12
|
+
@batches.each(&block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_batch_for(test_file_paths)
|
16
|
+
return if test_file_paths.empty?
|
17
|
+
@batches << KnapsackPro::Batch.new(test_file_paths)
|
18
|
+
end
|
19
|
+
|
20
|
+
def mark_batch_passed
|
21
|
+
current_batch._passed
|
22
|
+
end
|
23
|
+
|
24
|
+
def mark_batch_failed
|
25
|
+
current_batch._failed
|
26
|
+
end
|
27
|
+
|
28
|
+
def current_batch
|
29
|
+
@batches.last
|
30
|
+
end
|
31
|
+
|
32
|
+
def size
|
33
|
+
@batches.size
|
34
|
+
end
|
35
|
+
|
36
|
+
def [](index)
|
37
|
+
@batches[index]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -33,6 +33,7 @@ module KnapsackPro
|
|
33
33
|
@stream_out = stream_out
|
34
34
|
@node_test_file_paths = []
|
35
35
|
@rspec_runner = nil # RSpec::Core::Runner is lazy initialized
|
36
|
+
@queue = KnapsackPro::Queue.new
|
36
37
|
end
|
37
38
|
|
38
39
|
# Based on:
|
@@ -82,11 +83,13 @@ module KnapsackPro
|
|
82
83
|
subset_queue_id = KnapsackPro::Config::EnvGenerator.set_subset_queue_id
|
83
84
|
ENV['KNAPSACK_PRO_SUBSET_QUEUE_ID'] = subset_queue_id
|
84
85
|
|
85
|
-
|
86
|
+
@queue.add_batch_for(test_file_paths)
|
86
87
|
|
87
|
-
|
88
|
+
KnapsackPro::Hooks::Queue.call_before_subset_queue(@queue)
|
88
89
|
|
89
|
-
|
90
|
+
yield test_file_paths, @queue
|
91
|
+
|
92
|
+
KnapsackPro::Hooks::Queue.call_after_subset_queue(@queue)
|
90
93
|
|
91
94
|
if @rspec_runner.knapsack__wants_to_quit?
|
92
95
|
KnapsackPro.logger.warn('RSpec wants to quit.')
|
data/lib/knapsack_pro/version.rb
CHANGED
data/lib/knapsack_pro.rb
CHANGED
@@ -67,6 +67,8 @@ require_relative 'knapsack_pro/test_files_with_test_cases_composer'
|
|
67
67
|
require_relative 'knapsack_pro/base_allocator_builder'
|
68
68
|
require_relative 'knapsack_pro/allocator_builder'
|
69
69
|
require_relative 'knapsack_pro/queue_allocator_builder'
|
70
|
+
require_relative 'knapsack_pro/batch'
|
71
|
+
require_relative 'knapsack_pro/queue'
|
70
72
|
require_relative 'knapsack_pro/runners/base_runner'
|
71
73
|
require_relative 'knapsack_pro/runners/rspec_runner'
|
72
74
|
require_relative 'knapsack_pro/runners/cucumber_runner'
|
@@ -739,6 +739,139 @@ describe "#{KnapsackPro::Runners::Queue::RSpecRunner} - Integration tests", :cle
|
|
739
739
|
|
740
740
|
expect(actual.exit_code).to eq 0
|
741
741
|
end
|
742
|
+
|
743
|
+
it 'gives access to batch of tests in queue hooks' do
|
744
|
+
rspec_options = ''
|
745
|
+
|
746
|
+
spec_helper = <<~SPEC
|
747
|
+
require 'knapsack_pro'
|
748
|
+
KnapsackPro::Adapters::RSpecAdapter.bind
|
749
|
+
|
750
|
+
KnapsackPro::Hooks::Queue.before_subset_queue do |queue_id, subset_queue_id, queue|
|
751
|
+
print "Tests in batches in before_subset_queue: "
|
752
|
+
puts queue.map(&:test_file_paths).inspect
|
753
|
+
|
754
|
+
print "Batches' statuses in before_subset_queue: "
|
755
|
+
puts queue.map(&:status).inspect
|
756
|
+
end
|
757
|
+
|
758
|
+
KnapsackPro::Hooks::Queue.after_subset_queue do |queue_id, subset_queue_id, queue|
|
759
|
+
print "Tests in batches in after_subset_queue: "
|
760
|
+
puts queue.map(&:test_file_paths).inspect
|
761
|
+
print "Batches' statuses in after_subset_queue: "
|
762
|
+
puts queue.map(&:status).inspect
|
763
|
+
|
764
|
+
# call public API methods that must be backward compatible
|
765
|
+
print "Current batch tests: "
|
766
|
+
puts queue.current_batch.test_file_paths.inspect
|
767
|
+
print "Current batch status: "
|
768
|
+
puts queue.current_batch.status
|
769
|
+
end
|
770
|
+
SPEC
|
771
|
+
|
772
|
+
spec_a = Spec.new('a_spec.rb', <<~SPEC)
|
773
|
+
describe 'A_describe' do
|
774
|
+
it 'A1 test example' do
|
775
|
+
expect(1).to eq 1
|
776
|
+
end
|
777
|
+
end
|
778
|
+
SPEC
|
779
|
+
|
780
|
+
spec_b = Spec.new('b_spec.rb', <<~SPEC)
|
781
|
+
describe 'B_describe' do
|
782
|
+
it 'B1 test example' do
|
783
|
+
expect(1).to eq 1
|
784
|
+
end
|
785
|
+
end
|
786
|
+
SPEC
|
787
|
+
|
788
|
+
spec_c = Spec.new('c_spec.rb', <<~SPEC)
|
789
|
+
describe 'C_describe' do
|
790
|
+
it 'C1 test example' do
|
791
|
+
expect(1).to eq 1
|
792
|
+
end
|
793
|
+
end
|
794
|
+
SPEC
|
795
|
+
|
796
|
+
failing_spec_d = Spec.new('d_spec.rb', <<~SPEC)
|
797
|
+
describe 'D_describe' do
|
798
|
+
it 'D1 test example' do
|
799
|
+
expect(1).to eq 0
|
800
|
+
end
|
801
|
+
end
|
802
|
+
SPEC
|
803
|
+
|
804
|
+
spec_e = Spec.new('e_spec.rb', <<~SPEC)
|
805
|
+
describe 'E_describe' do
|
806
|
+
it 'E1 test example' do
|
807
|
+
expect(1).to eq 1
|
808
|
+
end
|
809
|
+
end
|
810
|
+
SPEC
|
811
|
+
|
812
|
+
spec_f = Spec.new('f_spec.rb', <<~SPEC)
|
813
|
+
describe 'F_describe' do
|
814
|
+
it 'F1 test example' do
|
815
|
+
expect(1).to eq 1
|
816
|
+
end
|
817
|
+
end
|
818
|
+
SPEC
|
819
|
+
|
820
|
+
failing_spec_g = Spec.new('g_spec.rb', <<~SPEC)
|
821
|
+
describe 'G_describe' do
|
822
|
+
it 'G1 test example' do
|
823
|
+
expect(1).to eq 0
|
824
|
+
end
|
825
|
+
end
|
826
|
+
SPEC
|
827
|
+
|
828
|
+
spec_h = Spec.new('h_spec.rb', <<~SPEC)
|
829
|
+
describe 'h_describe' do
|
830
|
+
it 'H1 test example' do
|
831
|
+
expect(1).to eq 1
|
832
|
+
end
|
833
|
+
end
|
834
|
+
SPEC
|
835
|
+
|
836
|
+
generate_specs(spec_helper, rspec_options, [
|
837
|
+
[spec_a, spec_b],
|
838
|
+
[spec_c, failing_spec_d],
|
839
|
+
[spec_e, spec_f],
|
840
|
+
[failing_spec_g, spec_h],
|
841
|
+
])
|
842
|
+
|
843
|
+
actual = subject
|
844
|
+
|
845
|
+
expect(actual.stdout).to include('Tests in batches in before_subset_queue: [["spec_integration/a_spec.rb", "spec_integration/b_spec.rb"]]')
|
846
|
+
expect(actual.stdout).to include('Tests in batches in before_subset_queue: [["spec_integration/a_spec.rb", "spec_integration/b_spec.rb"], ["spec_integration/c_spec.rb", "spec_integration/d_spec.rb"]]')
|
847
|
+
expect(actual.stdout).to include('Tests in batches in before_subset_queue: [["spec_integration/a_spec.rb", "spec_integration/b_spec.rb"], ["spec_integration/c_spec.rb", "spec_integration/d_spec.rb"], ["spec_integration/e_spec.rb", "spec_integration/f_spec.rb"]]')
|
848
|
+
expect(actual.stdout).to include('Tests in batches in before_subset_queue: [["spec_integration/a_spec.rb", "spec_integration/b_spec.rb"], ["spec_integration/c_spec.rb", "spec_integration/d_spec.rb"], ["spec_integration/e_spec.rb", "spec_integration/f_spec.rb"], ["spec_integration/g_spec.rb", "spec_integration/h_spec.rb"]]')
|
849
|
+
|
850
|
+
expect(actual.stdout).to include('Tests in batches in after_subset_queue: [["spec_integration/a_spec.rb", "spec_integration/b_spec.rb"]]')
|
851
|
+
expect(actual.stdout).to include('Tests in batches in after_subset_queue: [["spec_integration/a_spec.rb", "spec_integration/b_spec.rb"], ["spec_integration/c_spec.rb", "spec_integration/d_spec.rb"]]')
|
852
|
+
expect(actual.stdout).to include('Tests in batches in after_subset_queue: [["spec_integration/a_spec.rb", "spec_integration/b_spec.rb"], ["spec_integration/c_spec.rb", "spec_integration/d_spec.rb"], ["spec_integration/e_spec.rb", "spec_integration/f_spec.rb"]]')
|
853
|
+
expect(actual.stdout).to include('Tests in batches in after_subset_queue: [["spec_integration/a_spec.rb", "spec_integration/b_spec.rb"], ["spec_integration/c_spec.rb", "spec_integration/d_spec.rb"], ["spec_integration/e_spec.rb", "spec_integration/f_spec.rb"], ["spec_integration/g_spec.rb", "spec_integration/h_spec.rb"]]')
|
854
|
+
|
855
|
+
|
856
|
+
expect(actual.stdout).to include("Batches' statuses in before_subset_queue: [:not_executed]")
|
857
|
+
expect(actual.stdout).to include("Batches' statuses in before_subset_queue: [:passed, :not_executed]")
|
858
|
+
expect(actual.stdout).to include("Batches' statuses in before_subset_queue: [:passed, :failed, :not_executed]")
|
859
|
+
expect(actual.stdout).to include("Batches' statuses in before_subset_queue: [:passed, :failed, :passed, :not_executed]")
|
860
|
+
|
861
|
+
expect(actual.stdout).to include("Batches' statuses in after_subset_queue: [:passed]")
|
862
|
+
expect(actual.stdout).to include("Batches' statuses in after_subset_queue: [:passed, :failed]")
|
863
|
+
expect(actual.stdout).to include("Batches' statuses in after_subset_queue: [:passed, :failed, :passed]")
|
864
|
+
expect(actual.stdout).to include("Batches' statuses in after_subset_queue: [:passed, :failed, :passed, :failed]")
|
865
|
+
|
866
|
+
expect(actual.stdout).to include('Current batch tests: ["spec_integration/a_spec.rb", "spec_integration/b_spec.rb"]')
|
867
|
+
expect(actual.stdout).to include('Current batch tests: ["spec_integration/c_spec.rb", "spec_integration/d_spec.rb"]')
|
868
|
+
expect(actual.stdout).to include('Current batch tests: ["spec_integration/e_spec.rb", "spec_integration/f_spec.rb"]')
|
869
|
+
expect(actual.stdout).to include('Current batch tests: ["spec_integration/g_spec.rb", "spec_integration/h_spec.rb"]')
|
870
|
+
expect(actual.stdout).to include('Current batch status: passed').twice
|
871
|
+
expect(actual.stdout).to include('Current batch status: failed').twice
|
872
|
+
|
873
|
+
expect(actual.exit_code).to eq 1
|
874
|
+
end
|
742
875
|
end
|
743
876
|
|
744
877
|
context 'when the RSpec seed is used' do
|
@@ -17,6 +17,40 @@ describe KnapsackPro::Config::Env do
|
|
17
17
|
|
18
18
|
it { should eq 4 }
|
19
19
|
end
|
20
|
+
|
21
|
+
context 'when both KNAPSACK_PRO_CI_NODE_TOTAL and CI environment have value' do
|
22
|
+
let(:logger) { instance_double(Logger, info: nil) }
|
23
|
+
|
24
|
+
before do
|
25
|
+
stub_const("ENV", { 'KNAPSACK_PRO_CI_NODE_TOTAL' => env_value })
|
26
|
+
expect(described_class).to receive(:ci_env_for).with(:node_total).and_return(ci_value)
|
27
|
+
allow(KnapsackPro).to receive(:logger).and_return(logger)
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when values are different' do
|
31
|
+
let(:env_value) { '5' }
|
32
|
+
let(:ci_value) { 4 }
|
33
|
+
|
34
|
+
it { should eq 5 }
|
35
|
+
|
36
|
+
it 'logs a warning' do
|
37
|
+
expect(logger).to receive(:info).with(
|
38
|
+
'You have set the environment variable KNAPSACK_PRO_CI_NODE_TOTAL to 5 which could be automatically determined from the CI environment as 4.'
|
39
|
+
)
|
40
|
+
subject
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when values are the same' do
|
45
|
+
let(:env_value) { '5' }
|
46
|
+
let(:ci_value) { 5 }
|
47
|
+
|
48
|
+
it 'does not log a warning' do
|
49
|
+
expect(logger).not_to receive(:info)
|
50
|
+
subject
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
20
54
|
end
|
21
55
|
|
22
56
|
context "when ENV doesn't exist" do
|
@@ -41,6 +75,40 @@ describe KnapsackPro::Config::Env do
|
|
41
75
|
it { should eq 2 }
|
42
76
|
end
|
43
77
|
|
78
|
+
context 'when both KNAPSACK_PRO_CI_NODE_INDEX and CI environment have value' do
|
79
|
+
let(:logger) { instance_double(Logger, info: nil) }
|
80
|
+
|
81
|
+
before do
|
82
|
+
stub_const("ENV", { 'KNAPSACK_PRO_CI_NODE_INDEX' => env_value })
|
83
|
+
expect(described_class).to receive(:ci_env_for).with(:node_index).and_return(ci_value)
|
84
|
+
allow(KnapsackPro).to receive(:logger).and_return(logger)
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'when values are different' do
|
88
|
+
let(:env_value) { '3' }
|
89
|
+
let(:ci_value) { 2 }
|
90
|
+
|
91
|
+
it { should eq 3 }
|
92
|
+
|
93
|
+
it 'logs a warning' do
|
94
|
+
expect(logger).to receive(:info).with(
|
95
|
+
'You have set the environment variable KNAPSACK_PRO_CI_NODE_INDEX to 3 which could be automatically determined from the CI environment as 2.'
|
96
|
+
)
|
97
|
+
subject
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'when values are the same' do
|
102
|
+
let(:env_value) { '3' }
|
103
|
+
let(:ci_value) { 3 }
|
104
|
+
|
105
|
+
it 'does not log a warning' do
|
106
|
+
expect(logger).not_to receive(:info)
|
107
|
+
subject
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
44
112
|
context 'when order of loading envs does matter' do
|
45
113
|
context 'when GitLab CI' do
|
46
114
|
before { stub_const("ENV", { 'CI_NODE_INDEX' => '2', 'GITLAB_CI' => 'true' }) }
|
@@ -70,6 +138,40 @@ describe KnapsackPro::Config::Env do
|
|
70
138
|
|
71
139
|
it { should eq '8' }
|
72
140
|
end
|
141
|
+
|
142
|
+
context 'when both KNAPSACK_PRO_CI_NODE_BUILD_ID and CI environment have value' do
|
143
|
+
let(:logger) { instance_double(Logger, info: nil) }
|
144
|
+
|
145
|
+
before do
|
146
|
+
stub_const("ENV", { 'KNAPSACK_PRO_CI_NODE_BUILD_ID' => env_value })
|
147
|
+
expect(described_class).to receive(:ci_env_for).with(:node_build_id).and_return(ci_value)
|
148
|
+
allow(KnapsackPro).to receive(:logger).and_return(logger)
|
149
|
+
end
|
150
|
+
|
151
|
+
context 'when values are different' do
|
152
|
+
let(:env_value) { '7' }
|
153
|
+
let(:ci_value) { '8' }
|
154
|
+
|
155
|
+
it { should eq '7' }
|
156
|
+
|
157
|
+
it 'logs a warning' do
|
158
|
+
expect(logger).to receive(:info).with(
|
159
|
+
'You have set the environment variable KNAPSACK_PRO_CI_NODE_BUILD_ID to 7 which could be automatically determined from the CI environment as 8.'
|
160
|
+
)
|
161
|
+
subject
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context 'when values are the same' do
|
166
|
+
let(:env_value) { '7' }
|
167
|
+
let(:ci_value) { '7' }
|
168
|
+
|
169
|
+
it 'does not log a warning' do
|
170
|
+
expect(logger).not_to receive(:info)
|
171
|
+
subject
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
73
175
|
end
|
74
176
|
|
75
177
|
context "when ENV does not exist" do
|
@@ -95,6 +197,40 @@ describe KnapsackPro::Config::Env do
|
|
95
197
|
|
96
198
|
it { should eq 2 }
|
97
199
|
end
|
200
|
+
|
201
|
+
context 'when both KNAPSACK_PRO_CI_NODE_RETRY_COUNT and CI environment have value' do
|
202
|
+
let(:logger) { instance_double(Logger, info: nil) }
|
203
|
+
|
204
|
+
before do
|
205
|
+
stub_const("ENV", { 'KNAPSACK_PRO_CI_NODE_RETRY_COUNT' => env_value })
|
206
|
+
expect(described_class).to receive(:ci_env_for).with(:node_retry_count).and_return(ci_value)
|
207
|
+
allow(KnapsackPro).to receive(:logger).and_return(logger)
|
208
|
+
end
|
209
|
+
|
210
|
+
context 'when values are different' do
|
211
|
+
let(:env_value) { '1' }
|
212
|
+
let(:ci_value) { 2 }
|
213
|
+
|
214
|
+
it { should eq 1 }
|
215
|
+
|
216
|
+
it 'logs a warning' do
|
217
|
+
expect(logger).to receive(:info).with(
|
218
|
+
'You have set the environment variable KNAPSACK_PRO_CI_NODE_RETRY_COUNT to 1 which could be automatically determined from the CI environment as 2.'
|
219
|
+
)
|
220
|
+
subject
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context 'when values are the same' do
|
225
|
+
let(:env_value) { '7' }
|
226
|
+
let(:ci_value) { '7' }
|
227
|
+
|
228
|
+
it 'does not log a warning' do
|
229
|
+
expect(logger).not_to receive(:info)
|
230
|
+
subject
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
98
234
|
end
|
99
235
|
|
100
236
|
context "when ENV doesn't exist" do
|
@@ -131,6 +267,40 @@ describe KnapsackPro::Config::Env do
|
|
131
267
|
|
132
268
|
it { should eq 'fe61a08118d0d52e97c38666eba1eaf3' }
|
133
269
|
end
|
270
|
+
|
271
|
+
context 'when both KNAPSACK_PRO_COMMIT_HASH and CI environment have value' do
|
272
|
+
let(:logger) { instance_double(Logger, info: nil) }
|
273
|
+
|
274
|
+
before do
|
275
|
+
stub_const("ENV", { 'KNAPSACK_PRO_COMMIT_HASH' => env_value })
|
276
|
+
expect(described_class).to receive(:ci_env_for).with(:commit_hash).and_return(ci_value)
|
277
|
+
allow(KnapsackPro).to receive(:logger).and_return(logger)
|
278
|
+
end
|
279
|
+
|
280
|
+
context 'when values are different' do
|
281
|
+
let(:env_value) { '3fa64859337f6e56409d49f865d13fd7' }
|
282
|
+
let(:ci_value) { 'fe61a08118d0d52e97c38666eba1eaf3' }
|
283
|
+
|
284
|
+
it { should eq '3fa64859337f6e56409d49f865d13fd7' }
|
285
|
+
|
286
|
+
it 'logs a warning' do
|
287
|
+
expect(logger).to receive(:info).with(
|
288
|
+
'You have set the environment variable KNAPSACK_PRO_COMMIT_HASH to 3fa64859337f6e56409d49f865d13fd7 which could be automatically determined from the CI environment as fe61a08118d0d52e97c38666eba1eaf3.'
|
289
|
+
)
|
290
|
+
subject
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
context 'when values are the same' do
|
295
|
+
let(:env_value) { '3fa64859337f6e56409d49f865d13fd7' }
|
296
|
+
let(:ci_value) { '3fa64859337f6e56409d49f865d13fd7' }
|
297
|
+
|
298
|
+
it 'does not log a warning' do
|
299
|
+
expect(logger).not_to receive(:info)
|
300
|
+
subject
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
134
304
|
end
|
135
305
|
|
136
306
|
context "when ENV doesn't exist" do
|
@@ -154,6 +324,40 @@ describe KnapsackPro::Config::Env do
|
|
154
324
|
|
155
325
|
it { should eq 'feature-branch' }
|
156
326
|
end
|
327
|
+
|
328
|
+
context 'when both KNAPSACK_PRO_BRANCH and CI environment have value' do
|
329
|
+
let(:logger) { instance_double(Logger, info: nil) }
|
330
|
+
|
331
|
+
before do
|
332
|
+
stub_const("ENV", { 'KNAPSACK_PRO_BRANCH' => env_value })
|
333
|
+
expect(described_class).to receive(:ci_env_for).with(:branch).and_return(ci_value)
|
334
|
+
allow(KnapsackPro).to receive(:logger).and_return(logger)
|
335
|
+
end
|
336
|
+
|
337
|
+
context 'when values are different' do
|
338
|
+
let(:env_value) { 'master' }
|
339
|
+
let(:ci_value) { 'feature-branch' }
|
340
|
+
|
341
|
+
it { should eq 'master' }
|
342
|
+
|
343
|
+
it 'logs a warning' do
|
344
|
+
expect(logger).to receive(:info).with(
|
345
|
+
'You have set the environment variable KNAPSACK_PRO_BRANCH to master which could be automatically determined from the CI environment as feature-branch.'
|
346
|
+
)
|
347
|
+
subject
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
context 'when values are the same' do
|
352
|
+
let(:env_value) { 'master' }
|
353
|
+
let(:ci_value) { 'master' }
|
354
|
+
|
355
|
+
it 'does not log a warning' do
|
356
|
+
expect(logger).not_to receive(:info)
|
357
|
+
subject
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
157
361
|
end
|
158
362
|
|
159
363
|
context "when ENV doesn't exist" do
|
@@ -177,6 +381,40 @@ describe KnapsackPro::Config::Env do
|
|
177
381
|
|
178
382
|
it { should eq '/home/runner/myapp' }
|
179
383
|
end
|
384
|
+
|
385
|
+
context 'when both KNAPSACK_PRO_PROJECT_DIR and CI environment have value' do
|
386
|
+
let(:logger) { instance_double(Logger, info: nil) }
|
387
|
+
|
388
|
+
before do
|
389
|
+
stub_const("ENV", { 'KNAPSACK_PRO_PROJECT_DIR' => env_value })
|
390
|
+
expect(described_class).to receive(:ci_env_for).with(:project_dir).and_return(ci_value)
|
391
|
+
allow(KnapsackPro).to receive(:logger).and_return(logger)
|
392
|
+
end
|
393
|
+
|
394
|
+
context 'when values are different' do
|
395
|
+
let(:env_value) { '/home/user/myapp' }
|
396
|
+
let(:ci_value) { '/home/runner/myapp' }
|
397
|
+
|
398
|
+
it { should eq '/home/user/myapp' }
|
399
|
+
|
400
|
+
it 'logs a warning' do
|
401
|
+
expect(logger).to receive(:info).with(
|
402
|
+
'You have set the environment variable KNAPSACK_PRO_PROJECT_DIR to /home/user/myapp which could be automatically determined from the CI environment as /home/runner/myapp.'
|
403
|
+
)
|
404
|
+
subject
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
context 'when values are the same' do
|
409
|
+
let(:env_value) { '/home/user/myapp' }
|
410
|
+
let(:ci_value) { '/home/user/myapp' }
|
411
|
+
|
412
|
+
it 'does not log a warning' do
|
413
|
+
expect(logger).not_to receive(:info)
|
414
|
+
subject
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
180
418
|
end
|
181
419
|
|
182
420
|
context "when ENV doesn't exist" do
|
@@ -200,6 +438,40 @@ describe KnapsackPro::Config::Env do
|
|
200
438
|
|
201
439
|
it { should eq 'Jane Doe' }
|
202
440
|
end
|
441
|
+
|
442
|
+
context 'when both KNAPSACK_PRO_USER_SEAT and CI environment have value' do
|
443
|
+
let(:logger) { instance_double(Logger, info: nil) }
|
444
|
+
|
445
|
+
before do
|
446
|
+
stub_const("ENV", { 'KNAPSACK_PRO_USER_SEAT' => env_value })
|
447
|
+
expect(described_class).to receive(:ci_env_for).with(:user_seat).and_return(ci_value)
|
448
|
+
allow(KnapsackPro).to receive(:logger).and_return(logger)
|
449
|
+
end
|
450
|
+
|
451
|
+
context 'when values are different' do
|
452
|
+
let(:env_value) { 'John Doe' }
|
453
|
+
let(:ci_value) { 'Jane Doe' }
|
454
|
+
|
455
|
+
it { should eq 'John Doe' }
|
456
|
+
|
457
|
+
it 'logs a warning' do
|
458
|
+
expect(logger).to receive(:info).with(
|
459
|
+
'You have set the environment variable KNAPSACK_PRO_USER_SEAT to John Doe which could be automatically determined from the CI environment as Jane Doe.'
|
460
|
+
)
|
461
|
+
subject
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
context 'when values are the same' do
|
466
|
+
let(:env_value) { 'John Doe' }
|
467
|
+
let(:ci_value) { 'John Doe' }
|
468
|
+
|
469
|
+
it 'does not log a warning' do
|
470
|
+
expect(logger).not_to receive(:info)
|
471
|
+
subject
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|
203
475
|
end
|
204
476
|
|
205
477
|
context "when ENV doesn't exist" do
|
@@ -345,7 +617,6 @@ describe KnapsackPro::Config::Env do
|
|
345
617
|
end
|
346
618
|
end
|
347
619
|
|
348
|
-
|
349
620
|
describe '.regular_mode?' do
|
350
621
|
subject { described_class.regular_mode? }
|
351
622
|
|
@@ -645,11 +916,18 @@ describe KnapsackPro::Config::Env do
|
|
645
916
|
['Unsupported CI', {}],
|
646
917
|
].each do |ci, env|
|
647
918
|
it "on #{ci} it is false" do
|
919
|
+
stub_const("ENV", env.merge({ 'KNAPSACK_PRO_FIXED_QUEUE_SPLIT' => 'false' }))
|
920
|
+
|
648
921
|
logger = instance_double(Logger)
|
649
922
|
allow(KnapsackPro).to receive(:logger).and_return(logger)
|
650
|
-
|
651
|
-
|
652
|
-
|
923
|
+
ci_env = described_class.detected_ci.new.fixed_queue_split
|
924
|
+
if ci_env == false
|
925
|
+
expect(logger).not_to receive(:info)
|
926
|
+
else
|
927
|
+
expect(logger).to receive(:info).with(
|
928
|
+
'You have set the environment variable KNAPSACK_PRO_FIXED_QUEUE_SPLIT to false which could be automatically determined from the CI environment as true.'
|
929
|
+
)
|
930
|
+
end
|
653
931
|
|
654
932
|
expect(subject).to eq(false)
|
655
933
|
end
|
@@ -673,11 +951,18 @@ describe KnapsackPro::Config::Env do
|
|
673
951
|
['Unsupported CI', {}],
|
674
952
|
].each do |ci, env|
|
675
953
|
it "on #{ci} it is true" do
|
954
|
+
stub_const("ENV", env.merge({ 'KNAPSACK_PRO_FIXED_QUEUE_SPLIT' => 'true' }))
|
955
|
+
|
676
956
|
logger = instance_double(Logger)
|
677
957
|
allow(KnapsackPro).to receive(:logger).and_return(logger)
|
678
|
-
|
679
|
-
|
680
|
-
|
958
|
+
ci_env = described_class.detected_ci.new.fixed_queue_split
|
959
|
+
if ci_env == true
|
960
|
+
expect(logger).not_to receive(:info)
|
961
|
+
else
|
962
|
+
expect(logger).to receive(:info).with(
|
963
|
+
'You have set the environment variable KNAPSACK_PRO_FIXED_QUEUE_SPLIT to true which could be automatically determined from the CI environment as false.'
|
964
|
+
)
|
965
|
+
end
|
681
966
|
|
682
967
|
expect(subject).to eq(true)
|
683
968
|
end
|
@@ -702,12 +987,12 @@ describe KnapsackPro::Config::Env do
|
|
702
987
|
['Unsupported CI', {}, true],
|
703
988
|
].each do |ci, env, expected|
|
704
989
|
it "on #{ci} it is #{expected}" do
|
990
|
+
stub_const("ENV", env)
|
991
|
+
|
705
992
|
logger = instance_double(Logger)
|
706
993
|
expect(KnapsackPro).to receive(:logger).and_return(logger)
|
707
994
|
expect(logger).to receive(:info).with("KNAPSACK_PRO_FIXED_QUEUE_SPLIT is not set. Using default value: #{expected}. Learn more at #{KnapsackPro::Urls::FIXED_QUEUE_SPLIT}")
|
708
995
|
|
709
|
-
stub_const("ENV", env)
|
710
|
-
|
711
996
|
expect(subject).to eq(expected)
|
712
997
|
end
|
713
998
|
end
|
@@ -2,34 +2,32 @@ describe KnapsackPro::Hooks::Queue do
|
|
2
2
|
describe '.call_before_queue' do
|
3
3
|
subject { described_class.call_before_queue }
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
end
|
5
|
+
before do
|
6
|
+
described_class.reset_before_queue
|
7
|
+
end
|
9
8
|
|
9
|
+
context 'when no callback is set' do
|
10
10
|
it { should be_nil }
|
11
11
|
end
|
12
12
|
|
13
|
-
context 'when
|
13
|
+
context 'when multiple callbacks are set' do
|
14
14
|
let(:queue_id) { double }
|
15
15
|
|
16
|
-
|
16
|
+
it 'calls each block' do
|
17
17
|
expect(KnapsackPro::Config::Env).to receive(:queue_id).twice.and_return(queue_id)
|
18
18
|
|
19
|
-
|
19
|
+
expected_called_blocks = []
|
20
20
|
|
21
21
|
described_class.before_queue do |q_id|
|
22
|
-
|
22
|
+
expected_called_blocks << [:block_1_called, q_id]
|
23
23
|
end
|
24
24
|
described_class.before_queue do |q_id|
|
25
|
-
|
25
|
+
expected_called_blocks << [:block_2_called, q_id]
|
26
26
|
end
|
27
|
-
end
|
28
27
|
|
29
|
-
it 'each block is called' do
|
30
28
|
subject
|
31
29
|
|
32
|
-
expect(
|
30
|
+
expect(expected_called_blocks).to eq([
|
33
31
|
[:block_1_called, queue_id],
|
34
32
|
[:block_2_called, queue_id],
|
35
33
|
])
|
@@ -40,114 +38,158 @@ describe KnapsackPro::Hooks::Queue do
|
|
40
38
|
describe '.call_before_subset_queue' do
|
41
39
|
subject { described_class.call_before_subset_queue }
|
42
40
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
41
|
+
before do
|
42
|
+
described_class.reset_before_subset_queue
|
43
|
+
end
|
47
44
|
|
45
|
+
context 'when no callback is set' do
|
48
46
|
it { should be_nil }
|
49
47
|
end
|
50
48
|
|
51
|
-
context 'when
|
49
|
+
context 'when multiple callbacks are set' do
|
52
50
|
let(:queue_id) { double }
|
53
51
|
let(:subset_queue_id) { double }
|
54
52
|
|
55
|
-
|
53
|
+
it 'calls each block' do
|
56
54
|
expect(KnapsackPro::Config::Env).to receive(:queue_id).twice.and_return(queue_id)
|
57
55
|
expect(KnapsackPro::Config::Env).to receive(:subset_queue_id).twice.and_return(subset_queue_id)
|
58
56
|
|
59
|
-
|
57
|
+
expected_called_blocks = []
|
60
58
|
|
61
59
|
described_class.before_subset_queue do |q_id, subset_q_id|
|
62
|
-
|
60
|
+
expected_called_blocks << [:block_1_called, q_id, subset_q_id]
|
63
61
|
end
|
64
62
|
described_class.before_subset_queue do |q_id, subset_q_id|
|
65
|
-
|
63
|
+
expected_called_blocks << [:block_2_called, q_id, subset_q_id]
|
66
64
|
end
|
67
|
-
end
|
68
65
|
|
69
|
-
it 'each block is called' do
|
70
66
|
subject
|
71
67
|
|
72
|
-
expect(
|
68
|
+
expect(expected_called_blocks).to eq([
|
73
69
|
[:block_1_called, queue_id, subset_queue_id],
|
74
70
|
[:block_2_called, queue_id, subset_queue_id],
|
75
71
|
])
|
76
72
|
end
|
77
73
|
end
|
74
|
+
|
75
|
+
context 'when a callback is set AND the queue is passed' do
|
76
|
+
let(:queue_id) { double }
|
77
|
+
let(:subset_queue_id) { double }
|
78
|
+
let(:queue) { instance_double(KnapsackPro::Queue) }
|
79
|
+
|
80
|
+
subject { described_class.call_before_subset_queue(queue) }
|
81
|
+
|
82
|
+
it 'calls each block' do
|
83
|
+
expect(KnapsackPro::Config::Env).to receive(:queue_id).and_return(queue_id)
|
84
|
+
expect(KnapsackPro::Config::Env).to receive(:subset_queue_id).and_return(subset_queue_id)
|
85
|
+
|
86
|
+
expected_called_blocks = []
|
87
|
+
|
88
|
+
described_class.before_subset_queue do |q_id, subset_q_id, queue|
|
89
|
+
expected_called_blocks << [:block_1_called, q_id, subset_q_id, queue]
|
90
|
+
end
|
91
|
+
|
92
|
+
subject
|
93
|
+
|
94
|
+
expect(expected_called_blocks).to eq([
|
95
|
+
[:block_1_called, queue_id, subset_queue_id, queue],
|
96
|
+
])
|
97
|
+
end
|
98
|
+
end
|
78
99
|
end
|
79
100
|
|
80
101
|
describe '.call_after_subset_queue' do
|
81
102
|
subject { described_class.call_after_subset_queue }
|
82
103
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
end
|
104
|
+
before do
|
105
|
+
described_class.reset_after_subset_queue
|
106
|
+
end
|
87
107
|
|
108
|
+
context 'when no callback is set' do
|
88
109
|
it { should be_nil }
|
89
110
|
end
|
90
111
|
|
91
|
-
context 'when
|
112
|
+
context 'when multiple callbacks are set' do
|
92
113
|
let(:queue_id) { double }
|
93
114
|
let(:subset_queue_id) { double }
|
94
115
|
|
95
|
-
|
116
|
+
it 'calls each block' do
|
96
117
|
expect(KnapsackPro::Config::Env).to receive(:queue_id).at_least(:once).and_return(queue_id)
|
97
118
|
expect(KnapsackPro::Config::Env).to receive(:subset_queue_id).at_least(:once).and_return(subset_queue_id)
|
98
119
|
|
99
|
-
|
120
|
+
expected_called_blocks = []
|
100
121
|
|
101
122
|
described_class.after_subset_queue do |q_id, subset_q_id|
|
102
|
-
|
123
|
+
expected_called_blocks << [:block_1_called, q_id, subset_q_id]
|
103
124
|
end
|
104
125
|
described_class.after_subset_queue do |q_id, subset_q_id|
|
105
|
-
|
126
|
+
expected_called_blocks << [:block_2_called, q_id, subset_q_id]
|
106
127
|
end
|
107
|
-
end
|
108
128
|
|
109
|
-
it 'each block is called' do
|
110
129
|
subject
|
111
130
|
|
112
|
-
expect(
|
131
|
+
expect(expected_called_blocks).to eq([
|
113
132
|
[:block_1_called, queue_id, subset_queue_id],
|
114
133
|
[:block_2_called, queue_id, subset_queue_id],
|
115
134
|
])
|
116
135
|
end
|
117
136
|
end
|
137
|
+
|
138
|
+
context 'when a callback is set AND the queue is passed' do
|
139
|
+
let(:queue_id) { double }
|
140
|
+
let(:subset_queue_id) { double }
|
141
|
+
let(:queue) { instance_double(KnapsackPro::Queue) }
|
142
|
+
|
143
|
+
subject { described_class.call_after_subset_queue(queue) }
|
144
|
+
|
145
|
+
it 'calls each block' do
|
146
|
+
expect(KnapsackPro::Config::Env).to receive(:queue_id).and_return(queue_id)
|
147
|
+
expect(KnapsackPro::Config::Env).to receive(:subset_queue_id).and_return(subset_queue_id)
|
148
|
+
|
149
|
+
expected_called_blocks = []
|
150
|
+
|
151
|
+
described_class.after_subset_queue do |q_id, subset_q_id, queue|
|
152
|
+
expected_called_blocks << [:block_1_called, q_id, subset_q_id, queue]
|
153
|
+
end
|
154
|
+
|
155
|
+
subject
|
156
|
+
|
157
|
+
expect(expected_called_blocks).to eq([
|
158
|
+
[:block_1_called, queue_id, subset_queue_id, queue],
|
159
|
+
])
|
160
|
+
end
|
161
|
+
end
|
118
162
|
end
|
119
163
|
|
120
164
|
describe '.call_after_queue' do
|
121
165
|
subject { described_class.call_after_queue }
|
122
166
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
end
|
167
|
+
before do
|
168
|
+
described_class.reset_after_queue
|
169
|
+
end
|
127
170
|
|
171
|
+
context 'when no callback is set' do
|
128
172
|
it { should be_nil }
|
129
173
|
end
|
130
174
|
|
131
|
-
context 'when
|
175
|
+
context 'when multiple callbacks are set' do
|
132
176
|
let(:queue_id) { double }
|
133
177
|
|
134
|
-
|
178
|
+
it 'calls each block' do
|
135
179
|
expect(KnapsackPro::Config::Env).to receive(:queue_id).twice.and_return(queue_id)
|
136
180
|
|
137
|
-
|
181
|
+
expected_called_blocks = []
|
138
182
|
|
139
183
|
described_class.after_queue do |q_id|
|
140
|
-
|
184
|
+
expected_called_blocks << [:block_1_called, q_id]
|
141
185
|
end
|
142
186
|
described_class.after_queue do |q_id|
|
143
|
-
|
187
|
+
expected_called_blocks << [:block_2_called, q_id]
|
144
188
|
end
|
145
|
-
end
|
146
189
|
|
147
|
-
it 'each block is called' do
|
148
190
|
subject
|
149
191
|
|
150
|
-
expect(
|
192
|
+
expect(expected_called_blocks).to eq([
|
151
193
|
[:block_1_called, queue_id],
|
152
194
|
[:block_2_called, queue_id],
|
153
195
|
])
|
@@ -0,0 +1,35 @@
|
|
1
|
+
describe KnapsackPro::Queue do
|
2
|
+
it 'simulates a Queue Mode build' do
|
3
|
+
queue = described_class.new
|
4
|
+
|
5
|
+
expect(queue.current_batch).to be_nil
|
6
|
+
|
7
|
+
# 1st batch
|
8
|
+
test_files_paths_1 = ['a_spec.rb', 'b_spec.rb']
|
9
|
+
queue.add_batch_for(test_files_paths_1)
|
10
|
+
|
11
|
+
expect(queue.current_batch.test_file_paths).to eq(['a_spec.rb', 'b_spec.rb'])
|
12
|
+
expect(queue.current_batch.status).to eq :not_executed
|
13
|
+
queue.mark_batch_passed
|
14
|
+
expect(queue.current_batch.status).to eq :passed
|
15
|
+
|
16
|
+
|
17
|
+
# 2nd batch
|
18
|
+
test_files_paths_2 = ['c_spec.rb', 'd_spec.rb']
|
19
|
+
queue.add_batch_for(test_files_paths_2)
|
20
|
+
|
21
|
+
expect(queue.current_batch.test_file_paths).to eq(['c_spec.rb', 'd_spec.rb'])
|
22
|
+
expect(queue.current_batch.status).to eq :not_executed
|
23
|
+
queue.mark_batch_failed
|
24
|
+
expect(queue.current_batch.status).to eq :failed
|
25
|
+
|
26
|
+
|
27
|
+
# last batch from the Queue API is always empty
|
28
|
+
test_files_paths_3 = []
|
29
|
+
queue.add_batch_for(test_files_paths_3)
|
30
|
+
|
31
|
+
expect(queue.size).to eq 2
|
32
|
+
expect(queue[0].status).to eq :passed
|
33
|
+
expect(queue[1].status).to eq :failed
|
34
|
+
end
|
35
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -40,4 +40,8 @@ RSpec.configure do |config|
|
|
40
40
|
FileUtils.rm_r(KNAPSACK_PRO_TMP_DIR) if File.exist?(KNAPSACK_PRO_TMP_DIR)
|
41
41
|
end
|
42
42
|
end
|
43
|
+
|
44
|
+
config.before(:each) do
|
45
|
+
allow_any_instance_of(KnapsackPro::Client::Connection).to receive(:net_http).and_return(Net::HTTP)
|
46
|
+
end
|
43
47
|
end
|
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: 7.
|
4
|
+
version: 7.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ArturT
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-05-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -214,6 +214,7 @@ files:
|
|
214
214
|
- lib/knapsack_pro/allocator.rb
|
215
215
|
- lib/knapsack_pro/allocator_builder.rb
|
216
216
|
- lib/knapsack_pro/base_allocator_builder.rb
|
217
|
+
- lib/knapsack_pro/batch.rb
|
217
218
|
- lib/knapsack_pro/build_distribution_fetcher.rb
|
218
219
|
- lib/knapsack_pro/client/api/action.rb
|
219
220
|
- lib/knapsack_pro/client/api/v1/base.rb
|
@@ -250,6 +251,7 @@ files:
|
|
250
251
|
- lib/knapsack_pro/mask_string.rb
|
251
252
|
- lib/knapsack_pro/presenter.rb
|
252
253
|
- lib/knapsack_pro/pure/queue/rspec_pure.rb
|
254
|
+
- lib/knapsack_pro/queue.rb
|
253
255
|
- lib/knapsack_pro/queue_allocator.rb
|
254
256
|
- lib/knapsack_pro/queue_allocator_builder.rb
|
255
257
|
- lib/knapsack_pro/railtie.rb
|
@@ -348,6 +350,7 @@ files:
|
|
348
350
|
- spec/knapsack_pro/pure/queue/rspec_pure_spec.rb
|
349
351
|
- spec/knapsack_pro/queue_allocator_builder_spec.rb
|
350
352
|
- spec/knapsack_pro/queue_allocator_spec.rb
|
353
|
+
- spec/knapsack_pro/queue_spec.rb
|
351
354
|
- spec/knapsack_pro/report_spec.rb
|
352
355
|
- spec/knapsack_pro/repository_adapter_initiator_spec.rb
|
353
356
|
- spec/knapsack_pro/repository_adapters/base_adapter_spec.rb
|
@@ -412,7 +415,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
412
415
|
- !ruby/object:Gem::Version
|
413
416
|
version: '0'
|
414
417
|
requirements: []
|
415
|
-
rubygems_version: 3.
|
418
|
+
rubygems_version: 3.5.6
|
416
419
|
signing_key:
|
417
420
|
specification_version: 4
|
418
421
|
summary: Knapsack Pro splits tests across parallel CI nodes and ensures each parallel
|
@@ -471,6 +474,7 @@ test_files:
|
|
471
474
|
- spec/knapsack_pro/pure/queue/rspec_pure_spec.rb
|
472
475
|
- spec/knapsack_pro/queue_allocator_builder_spec.rb
|
473
476
|
- spec/knapsack_pro/queue_allocator_spec.rb
|
477
|
+
- spec/knapsack_pro/queue_spec.rb
|
474
478
|
- spec/knapsack_pro/report_spec.rb
|
475
479
|
- spec/knapsack_pro/repository_adapter_initiator_spec.rb
|
476
480
|
- spec/knapsack_pro/repository_adapters/base_adapter_spec.rb
|