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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 832322e0370ece75c8c2e53870b526e6bc4cd3f43214d4ee893a48cef6b209af
4
- data.tar.gz: a3847464969d15278d84d00d9638c9e6e2cecea0190be5f773c70da0117c0d44
3
+ metadata.gz: 965d1d9b1d138cf0db8abe0fb44fc1672d5992d9f40f014c0f1f72c370d82585
4
+ data.tar.gz: 9570a9c2ffd20d1324451106f7a6144a298b6efb02981eabdc1f354d5c289940
5
5
  SHA512:
6
- metadata.gz: d3648e3a7f77b8acfac104dc32a6f99cca7d86456cadd2127ff3b8e6d0a874df452398b5a266a887d54c9c239bf28727501a524cc3351adbb676b8887e272014
7
- data.tar.gz: c7212ef6b958a7bb7aec2add6e6dc9d02e753936401f8d7705b4799c4ef1bb2b20c3456d1ad165b9e8ece86873b7393ee7afe1aa260c798b8644e6ace039fb17
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 = Net::HTTP.new(uri.host, uri.port)
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
- (ENV['KNAPSACK_PRO_CI_NODE_TOTAL'] ||
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
- (ENV['KNAPSACK_PRO_CI_NODE_INDEX'] ||
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
- ENV[env_name] ||
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
- ENV['KNAPSACK_PRO_COMMIT_HASH'] ||
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
- ENV['KNAPSACK_PRO_BRANCH'] ||
56
- ci_env_for(:branch)
45
+ env_for('KNAPSACK_PRO_BRANCH', :branch)
57
46
  end
58
47
 
59
48
  def project_dir
60
- ENV['KNAPSACK_PRO_PROJECT_DIR'] ||
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
- ENV['KNAPSACK_PRO_USER_SEAT'] ||
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 = ENV.fetch(env_name, ci_env_for(:fixed_queue_split)).to_s
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
- node_examples_passed = false unless examples_passed
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
- def call_before_subset_queue
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
- KnapsackPro::Hooks::Queue.call_before_subset_queue
86
+ @queue.add_batch_for(test_file_paths)
86
87
 
87
- yield test_file_paths
88
+ KnapsackPro::Hooks::Queue.call_before_subset_queue(@queue)
88
89
 
89
- KnapsackPro::Hooks::Queue.call_after_subset_queue
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.')
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KnapsackPro
4
- VERSION = '7.1.0'
4
+ VERSION = '7.4.0'
5
5
  end
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
- expect(logger).not_to receive(:info)
651
-
652
- stub_const("ENV", env.merge({ 'KNAPSACK_PRO_FIXED_QUEUE_SPLIT' => 'false' }))
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
- expect(logger).not_to receive(:info)
679
-
680
- stub_const("ENV", env.merge({ 'KNAPSACK_PRO_FIXED_QUEUE_SPLIT' => 'true' }))
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
- context 'when callback is not set' do
6
- before do
7
- described_class.reset_before_queue
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 callback is set multiple times' do
13
+ context 'when multiple callbacks are set' do
14
14
  let(:queue_id) { double }
15
15
 
16
- before do
16
+ it 'calls each block' do
17
17
  expect(KnapsackPro::Config::Env).to receive(:queue_id).twice.and_return(queue_id)
18
18
 
19
- $expected_called_blocks = []
19
+ expected_called_blocks = []
20
20
 
21
21
  described_class.before_queue do |q_id|
22
- $expected_called_blocks << [:block_1_called, q_id]
22
+ expected_called_blocks << [:block_1_called, q_id]
23
23
  end
24
24
  described_class.before_queue do |q_id|
25
- $expected_called_blocks << [:block_2_called, q_id]
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($expected_called_blocks).to eq([
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
- context 'when callback is not set' do
44
- before do
45
- described_class.reset_before_subset_queue
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 callback is set multiple times' do
49
+ context 'when multiple callbacks are set' do
52
50
  let(:queue_id) { double }
53
51
  let(:subset_queue_id) { double }
54
52
 
55
- before do
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
- $expected_called_blocks = []
57
+ expected_called_blocks = []
60
58
 
61
59
  described_class.before_subset_queue do |q_id, subset_q_id|
62
- $expected_called_blocks << [:block_1_called, q_id, subset_q_id]
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
- $expected_called_blocks << [:block_2_called, q_id, subset_q_id]
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($expected_called_blocks).to eq([
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
- context 'when callback is not set' do
84
- before do
85
- described_class.reset_after_subset_queue
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 callback is set multiple times' do
112
+ context 'when multiple callbacks are set' do
92
113
  let(:queue_id) { double }
93
114
  let(:subset_queue_id) { double }
94
115
 
95
- before do
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
- $expected_called_blocks = []
120
+ expected_called_blocks = []
100
121
 
101
122
  described_class.after_subset_queue do |q_id, subset_q_id|
102
- $expected_called_blocks << [:block_1_called, q_id, subset_q_id]
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
- $expected_called_blocks << [:block_2_called, q_id, subset_q_id]
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($expected_called_blocks).to eq([
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
- context 'when callback is not set' do
124
- before do
125
- described_class.reset_after_queue
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 callback is set multiple times' do
175
+ context 'when multiple callbacks are set' do
132
176
  let(:queue_id) { double }
133
177
 
134
- before do
178
+ it 'calls each block' do
135
179
  expect(KnapsackPro::Config::Env).to receive(:queue_id).twice.and_return(queue_id)
136
180
 
137
- $expected_called_blocks = []
181
+ expected_called_blocks = []
138
182
 
139
183
  described_class.after_queue do |q_id|
140
- $expected_called_blocks << [:block_1_called, q_id]
184
+ expected_called_blocks << [:block_1_called, q_id]
141
185
  end
142
186
  described_class.after_queue do |q_id|
143
- $expected_called_blocks << [:block_2_called, q_id]
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($expected_called_blocks).to eq([
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.1.0
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-04-30 00:00:00.000000000 Z
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.4.19
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