knapsack_pro 7.1.0 → 7.4.0

Sign up to get free protection for your applications and to get access to all the features.
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