knapsack_pro 1.22.3 → 2.3.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: 36d79c8a60d76dff5b549e0507820264cf35c2f07f90eee9934f30e593191664
4
- data.tar.gz: 532d70f5b50274cca485f527d3f553929c8a1e0432ede3bb517f0b29a5414df4
3
+ metadata.gz: a36ff1388dd1ad1c95c0fd5041fc771d27f28b36c0d02773fa8205ae4812818b
4
+ data.tar.gz: c07a145c9d4f62d9901cb1bb2122662ca57c69a75d8dd500771799af215204a0
5
5
  SHA512:
6
- metadata.gz: 9b8755b6d9d1deb27982fc4c156baa1160fcf531799e77996248aa828070dbdcdbe8d5475b591bd2c5f3257de309056b2b39f685b42dd1c2378ce6dc9222f339
7
- data.tar.gz: 7c24ae262d1691331b840783c530645440e22872372a56d8584e39b8e2a350c6235e75c9eac79dd5ecc4d0d05d74d65bdc4b176e937ee2b0277028d4c870b2f3
6
+ metadata.gz: 8aeb03d459916e73d5281fc28293300acc6e375747954afd070ef34cc062e08d603dee4bea5e614850ca723f15f3e926a69e7f0efee1399c02320baee0d18100
7
+ data.tar.gz: 1ac8483c14d798c0b4f8f163846e2f3e922223dad285516674568998ada15a917633a34b243b571b5f2e11f3d2a8d1086390ac1c39abd3205cb3c17aedf26c4f
@@ -1,5 +1,61 @@
1
1
  # Change Log
2
2
 
3
+ ### 2.3.0
4
+
5
+ * When you use Regular Mode then try 6 attempts to connect to the API instead of 3 attempts
6
+
7
+ Add `KNAPSACK_PRO_MAX_REQUEST_RETRIES` environment variable to let user define their own number of request retries to the API. It is useful to set it to `0` for [forked repos](https://knapsackpro.com/faq/question/how-to-make-knapsack_pro-works-for-forked-repositories-of-my-project) when you want to relay on Fallback Mode.
8
+
9
+ https://github.com/KnapsackPro/knapsack_pro-ruby/pull/124
10
+
11
+ https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v2.2.1...v2.3.0
12
+
13
+ ### 2.2.1
14
+
15
+ * Improve detection of test file path in test-unit runner for test files with shared examples
16
+
17
+ https://github.com/KnapsackPro/knapsack_pro-ruby/pull/123
18
+
19
+ https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v2.2.0...v2.2.1
20
+
21
+ ### 2.2.0
22
+
23
+ * Allow defining Queue Mode hooks multiple times (`KnapsackPro::Hooks::Queue.before_queue`, `KnapsackPro::Hooks::Queue.after_subset_queue`, `KnapsackPro::Hooks::Queue.after_queue`)
24
+
25
+ https://github.com/KnapsackPro/knapsack_pro-ruby/pull/122
26
+
27
+ https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v2.1.1...v2.2.0
28
+
29
+ ### 2.1.1
30
+
31
+ * Explicitly call root test runner class to avoid a confusing error when test runner gem is not loaded
32
+
33
+ https://github.com/KnapsackPro/knapsack_pro-ruby/pull/120
34
+
35
+ https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v2.1.0...v2.1.1
36
+
37
+ ### 2.1.0
38
+
39
+ * Add `KNAPSACK_PRO_RSPEC_TEST_EXAMPLE_DETECTOR_PREFIX` to customize prefix for generating test examples report when using RSpec split by test examples
40
+
41
+ https://github.com/KnapsackPro/knapsack_pro-ruby/pull/118
42
+
43
+ https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v2.0.0...v2.1.0
44
+
45
+ ### 2.0.0
46
+
47
+ * Add support for CI build ID for Github Actions
48
+
49
+ https://github.com/KnapsackPro/knapsack_pro-ruby/pull/116
50
+
51
+ __Migration path for Github Actions users - required__
52
+
53
+ If you use Github Actions and Knapsack Pro Queue Mode then you must set in Github Actions environment variable: `KNAPSACK_PRO_FIXED_QUEUE_SPLIT=true`. Thanks to that when you retry CI build then tests will run based on previously recorded tests. This solves problem mentioned in the [PR](https://github.com/KnapsackPro/knapsack_pro-ruby/pull/116).
54
+
55
+ __Migration path for other users__ - just update `knapsack_pro` gem. Nothing to change in your code :)
56
+
57
+ https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v1.22.3...v2.0.0
58
+
3
59
  ### 1.22.3
4
60
 
5
61
  * Support for non-delimited formatting params of RSpec like `-fMyCustomFormatter`
data/README.md CHANGED
@@ -84,6 +84,7 @@ We keep this old FAQ in README to not break old links spread across the web. You
84
84
  - [Supported test runners in queue mode](#supported-test-runners-in-queue-mode)
85
85
  - [Split test files by test cases](#split-test-files-by-test-cases)
86
86
  - [RSpec split test files by test examples (by individual `it`s)](#rspec-split-test-files-by-test-examples-by-individual-its)
87
+ - [Why I see error: Don't know how to build task 'knapsack_pro:rspec_test_example_detector'?](#why-i-see-error-dont-know-how-to-build-task-knapsack_prorspec_test_example_detector)
87
88
  - [How to manually define a list of slow test files to be split by test cases](#how-to-manually-define-a-list-of-slow-test-files-to-be-split-by-test-cases)
88
89
  - [Extra configuration for CI server](#extra-configuration-for-ci-server)
89
90
  - [Info about ENV variables](#info-about-env-variables)
@@ -618,6 +619,16 @@ KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES=true
618
619
 
619
620
  Thanks to that your CI build speed can be faster. We recommend using this feature with [Queue Mode](https://youtu.be/hUEB1XDKEFY) to ensure parallel CI nodes finish work at a similar time which gives you the shortest CI build time.
620
621
 
622
+ #### Why I see error: Don't know how to build task 'knapsack_pro:rspec_test_example_detector'?
623
+
624
+ If you will see error like:
625
+
626
+ ```
627
+ Don't know how to build task 'knapsack_pro:rspec_test_example_detector' (See the list of available tasks with `rake --tasks`)
628
+ ```
629
+
630
+ It probably means bundler can't find the rake task. You can try to remove the default prefix `bundle exec` used by knapsack_pro gem by setting `KNAPSACK_PRO_RSPEC_TEST_EXAMPLE_DETECTOR_PREFIX=""`.
631
+
621
632
  ### How to manually define a list of slow test files to be split by test cases
622
633
 
623
634
  If you don't want to rely on a list of test files from Knapsack Pro API to determine slow test files that should be split by test cases then you can define your own list of slow test files.
@@ -1639,6 +1650,9 @@ jobs:
1639
1650
  KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC: ${{ secrets.KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC }}
1640
1651
  KNAPSACK_PRO_CI_NODE_TOTAL: ${{ matrix.ci_node_total }}
1641
1652
  KNAPSACK_PRO_CI_NODE_INDEX: ${{ matrix.ci_node_index }}
1653
+ # if you use Knapsack Pro Queue Mode you must set below env variable
1654
+ # to be able to retry CI build and run previously recorded tests
1655
+ KNAPSACK_PRO_FIXED_QUEUE_SPLIT: true
1642
1656
  run: |
1643
1657
  # run tests in Knapsack Pro Regular Mode
1644
1658
  bundle exec rake knapsack_pro:rspec
@@ -2306,11 +2320,19 @@ There are a few ways to reproduce tests executed on CI node in your development
2306
2320
 
2307
2321
  ##### for knapsack_pro regular mode
2308
2322
 
2309
- knapsack_pro gem will retry requests to Knapsack Pro API multiple times every few seconds til it switch to fallback behavior and it will split test files across CI nodes based on popular test directory names. When knapsack_pro starts fallback mode then you will see a warning in the output.
2323
+ knapsack_pro gem will retry requests to Knapsack Pro API multiple times every few seconds till it switchs to fallback behavior (Fallback Mode) and it will split test files across CI nodes based on popular test directory names. When knapsack_pro starts Fallback Mode then you will see a warning in the output.
2324
+
2325
+ Note there is an unlikely scenario when some of the CI nodes may start in Fallback Mode but others don't and then it could happen that some of test files might be skipped. You should [read this to learn more](https://github.com/KnapsackPro/knapsack_pro-ruby/pull/124) and decide if you like to use Fallback Mode when running tests with knapsack_pro Regular Mode.
2326
+
2327
+ If your CI provider allows to retry only one of parallel CI nodes then please [read about this edge case as well](#required-ci-configuration-if-you-use-retry-single-failed-ci-node-feature-on-your-ci-server-when-knapsack_pro_fixed_queue_splittrue-in-queue-mode-or-knapsack_pro_fixed_test_suite_splittrue-in-regular-mode).
2310
2328
 
2311
2329
  ##### for knapsack_pro queue mode
2312
2330
 
2313
- knapsack_pro gem will retry requests to Knapsack Pro API multiple times every few seconds till it switches to fallback behavior and it will split test files across CI nodes based on popular test directory names. Note that if one of CI nodes will lose connection to Knapsack Pro API but other not then you may see that some of the test files will be executed on multiple CI nodes. Fallback mode guarantees each of test files is run at least once across CI nodes. Thanks to that we know if the whole test suite is green or not. When knapsack_pro starts fallback mode then you will see a warning in the output.
2331
+ knapsack_pro gem will retry requests to Knapsack Pro API multiple times every few seconds till it switches to fallback behavior (Fallback Mode) and it will split test files across CI nodes based on popular test directory names.
2332
+
2333
+ Note that if one of the CI nodes will lose connection to Knapsack Pro API but other not then you may see that some of the test files will be executed on multiple CI nodes. **Fallback Mode guarantees each of the test files is run at least once across CI nodes when you use knapsack_pro in Queue Mode.** Thanks to that we know if the whole test suite is green or not. When knapsack_pro starts Fallback Mode then you will see a warning in the output.
2334
+
2335
+ If your CI provider allows to retry only one of parallel CI nodes then please [read about this edge case as well](#required-ci-configuration-if-you-use-retry-single-failed-ci-node-feature-on-your-ci-server-when-knapsack_pro_fixed_queue_splittrue-in-queue-mode-or-knapsack_pro_fixed_test_suite_splittrue-in-regular-mode).
2314
2336
 
2315
2337
  #### How can I change log level?
2316
2338
 
@@ -2723,6 +2745,8 @@ end
2723
2745
 
2724
2746
  #### What hooks are supported in Queue Mode?
2725
2747
 
2748
+ Note: Each hook type can be defined multiple times. For instance, if you define `KnapsackPro::Hooks::Queue.before_queue` twice then both block of code will be called when running your tests.
2749
+
2726
2750
  * RSpec in knapsack_pro Queue Mode supports hooks:
2727
2751
 
2728
2752
  ```ruby
@@ -4,7 +4,7 @@ module KnapsackPro
4
4
  TEST_DIR_PATTERN = 'features/**{,/*/**}/*.feature'
5
5
 
6
6
  def self.test_path(object)
7
- if Cucumber::VERSION.to_i >= 2
7
+ if ::Cucumber::VERSION.to_i >= 2
8
8
  test_case = object
9
9
  test_case.location.file
10
10
  else
@@ -85,10 +85,10 @@ module KnapsackPro
85
85
  private
86
86
 
87
87
  def add_post_run_callback(&block)
88
- if Minitest.respond_to?(:after_run)
89
- Minitest.after_run { block.call }
88
+ if ::Minitest.respond_to?(:after_run)
89
+ ::Minitest.after_run { block.call }
90
90
  else
91
- Minitest::Unit.after_tests { block.call }
91
+ ::Minitest::Unit.after_tests { block.call }
92
92
  end
93
93
  end
94
94
  end
@@ -4,7 +4,7 @@ module KnapsackPro
4
4
  TEST_DIR_PATTERN = 'spec/**{,/*/**}/*_spec.rb'
5
5
 
6
6
  def self.test_path(example_group)
7
- if defined?(Turnip) && Turnip::VERSION.to_i < 2
7
+ if defined?(::Turnip) && ::Turnip::VERSION.to_i < 2
8
8
  unless example_group[:turnip]
9
9
  until example_group[:parent_example_group].nil?
10
10
  example_group = example_group[:parent_example_group]
@@ -5,9 +5,29 @@ module KnapsackPro
5
5
  @@parent_of_test_dir = nil
6
6
 
7
7
  def self.test_path(obj)
8
- first_test = obj.tests.first
9
- method = first_test.method_name
10
- full_test_path = first_test.method(method).source_location.first
8
+ full_test_path = nil
9
+ found_valid_test_file_path = false
10
+
11
+ obj.tests.each do |test_obj|
12
+ method = test_obj.method_name
13
+ full_test_path = test_obj.method(method).source_location.first
14
+ # if we find a test file path that is a valid test file path within test suite directory
15
+ # then break to stop looking further.
16
+ # If we won't find a valid test file path then the last found path will be used as full_test_path
17
+ # For instance if test file contains only shared examples then it's not possible to properly detect test file path
18
+ # so the wrong path can be used like:
19
+ # /Users/artur/.rvm/gems/ruby-2.6.5/gems/shared_should-0.10.0/lib/shared_should/shared_context.rb
20
+ if full_test_path.include?(@@parent_of_test_dir)
21
+ found_valid_test_file_path = true
22
+ break
23
+ end
24
+ end
25
+
26
+ unless found_valid_test_file_path
27
+ KnapsackPro.logger.warn('cannot detect a valid test file path. Probably the test file contains only shared examples. Please add test cases to your test file. Read more at https://github.com/KnapsackPro/knapsack_pro-ruby/pull/123')
28
+ KnapsackPro.logger.warn("See test file for #{obj.inspect}")
29
+ end
30
+
11
31
  parent_of_test_dir_regexp = Regexp.new("^#{@@parent_of_test_dir}")
12
32
  test_path = full_test_path.gsub(parent_of_test_dir_regexp, '.')
13
33
  # test_path will look like ./test/dir/unit_test.rb
@@ -42,7 +62,7 @@ module KnapsackPro
42
62
  end
43
63
 
44
64
  def bind_time_tracker
45
- Test::Unit::TestSuite.send(:prepend, BindTimeTrackerTestUnitPlugin)
65
+ ::Test::Unit::TestSuite.send(:prepend, BindTimeTrackerTestUnitPlugin)
46
66
 
47
67
  add_post_run_callback do
48
68
  KnapsackPro.logger.debug(KnapsackPro::Presenter.global_time)
@@ -63,7 +83,7 @@ module KnapsackPro
63
83
  private
64
84
 
65
85
  def add_post_run_callback(&block)
66
- Test::Unit.at_exit do
86
+ ::Test::Unit.at_exit do
67
87
  block.call
68
88
  end
69
89
  end
@@ -34,7 +34,7 @@ module KnapsackPro
34
34
  test_files_to_run = all_test_files_to_run
35
35
 
36
36
  if adapter_class == KnapsackPro::Adapters::RSpecAdapter && KnapsackPro::Config::Env.rspec_split_by_test_examples?
37
- unless Gem::Version.new(RSpec::Core::Version::STRING) >= Gem::Version.new('3.3.0')
37
+ unless Gem::Version.new(::RSpec::Core::Version::STRING) >= Gem::Version.new('3.3.0')
38
38
  raise 'RSpec >= 3.3.0 is required to split test files by test examples. Learn more: https://github.com/KnapsackPro/knapsack_pro-ruby#split-test-files-by-test-cases'
39
39
  end
40
40
 
@@ -43,7 +43,12 @@ module KnapsackPro
43
43
  KnapsackPro.logger.info("Generating RSpec test examples JSON report for slow test files to prepare it to be split by test examples (by individual 'it's. Thanks to that a single slow test file can be split across parallel CI nodes). Analyzing #{slow_test_files.size} slow test files.")
44
44
 
45
45
  # generate RSpec JSON report in separate process to not pollute RSpec state
46
- cmd = 'RACK_ENV=test RAILS_ENV=test bundle exec rake knapsack_pro:rspec_test_example_detector'
46
+ cmd = [
47
+ 'RACK_ENV=test',
48
+ 'RAILS_ENV=test',
49
+ KnapsackPro::Config::Env.rspec_test_example_detector_prefix,
50
+ 'rake knapsack_pro:rspec_test_example_detector',
51
+ ].join(' ')
47
52
  unless Kernel.system(cmd)
48
53
  raise "Could not generate JSON report for RSpec. Rake task failed when running #{cmd}"
49
54
  end
@@ -4,7 +4,6 @@ module KnapsackPro
4
4
  class ServerError < StandardError; end
5
5
 
6
6
  TIMEOUT = 15
7
- MAX_RETRY = -> { KnapsackPro::Config::Env.fallback_mode_enabled? ? 3 : 6 }
8
7
  REQUEST_RETRY_TIMEBOX = 8
9
8
 
10
9
  def initialize(action)
@@ -118,7 +117,7 @@ module KnapsackPro
118
117
  rescue ServerError, Errno::ECONNREFUSED, Errno::ETIMEDOUT, Errno::EPIPE, EOFError, SocketError, Net::OpenTimeout, Net::ReadTimeout, OpenSSL::SSL::SSLError => e
119
118
  logger.warn(e.inspect)
120
119
  retries += 1
121
- if retries < MAX_RETRY.call
120
+ if retries < max_request_retries
122
121
  wait = retries * REQUEST_RETRY_TIMEBOX
123
122
  logger.warn("Wait #{wait}s and retry request to Knapsack Pro API.")
124
123
  print_every = 2 # seconds
@@ -156,6 +155,22 @@ module KnapsackPro
156
155
  http.get(uri, json_headers)
157
156
  end
158
157
  end
158
+
159
+ def max_request_retries
160
+ # when user defined max request retries
161
+ return KnapsackPro::Config::Env.max_request_retries if KnapsackPro::Config::Env.max_request_retries
162
+
163
+ # when Fallback Mode is disabled then try more attempts to connect to the API
164
+ return 6 unless KnapsackPro::Config::Env.fallback_mode_enabled?
165
+
166
+ # when Regular Mode then try more attempts to connect to the API
167
+ # if only one CI node starts Fallback Mode instead of all then we can't guarantee all test files will be run
168
+ # https://github.com/KnapsackPro/knapsack_pro-ruby/pull/124
169
+ return 6 if KnapsackPro::Config::Env.regular_mode?
170
+
171
+ # default number of attempts
172
+ 3
173
+ end
159
174
  end
160
175
  end
161
176
  end
@@ -1,4 +1,4 @@
1
- # https://help.github.com/en/articles/virtual-environments-for-github-actions#environment-variables
1
+ # https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables
2
2
  module KnapsackPro
3
3
  module Config
4
4
  module CI
@@ -12,7 +12,8 @@ module KnapsackPro
12
12
  end
13
13
 
14
14
  def node_build_id
15
- # not provided
15
+ # A unique number for each run within a repository. This number does not change if you re-run the workflow run.
16
+ ENV['GITHUB_RUN_ID']
16
17
  end
17
18
 
18
19
  def commit_hash
@@ -36,6 +36,13 @@ module KnapsackPro
36
36
  ).to_i
37
37
  end
38
38
 
39
+ def max_request_retries
40
+ number = ENV['KNAPSACK_PRO_MAX_REQUEST_RETRIES']
41
+ if number
42
+ number.to_i
43
+ end
44
+ end
45
+
39
46
  def commit_hash
40
47
  ENV['KNAPSACK_PRO_COMMIT_HASH'] ||
41
48
  ci_env_for(:commit_hash)
@@ -83,6 +90,10 @@ module KnapsackPro
83
90
  recording_enabled == 'true'
84
91
  end
85
92
 
93
+ def regular_mode?
94
+ recording_enabled?
95
+ end
96
+
86
97
  def queue_recording_enabled
87
98
  ENV['KNAPSACK_PRO_QUEUE_RECORDING_ENABLED']
88
99
  end
@@ -179,6 +190,10 @@ module KnapsackPro
179
190
  rspec_split_by_test_examples.to_s == 'true'
180
191
  end
181
192
 
193
+ def rspec_test_example_detector_prefix
194
+ ENV.fetch('KNAPSACK_PRO_RSPEC_TEST_EXAMPLE_DETECTOR_PREFIX', 'bundle exec')
195
+ end
196
+
182
197
  def test_suite_token
183
198
  env_name = 'KNAPSACK_PRO_TEST_SUITE_TOKEN'
184
199
  ENV[env_name] || raise("Missing environment variable #{env_name}. You should set environment variable like #{env_name}_RSPEC (note there is suffix _RSPEC at the end). knapsack_pro gem will set #{env_name} based on #{env_name}_RSPEC value. If you use other test runner than RSpec then use proper suffix.")
@@ -3,8 +3,8 @@ RSpec::Support.require_rspec_core('formatters/json_formatter')
3
3
  # based on https://github.com/rspec/rspec-core/blob/master/lib/rspec/core/formatters/json_formatter.rb
4
4
  module KnapsackPro
5
5
  module Formatters
6
- class RSpecJsonFormatter < RSpec::Core::Formatters::JsonFormatter
7
- RSpec::Core::Formatters.register self
6
+ class RSpecJsonFormatter < ::RSpec::Core::Formatters::JsonFormatter
7
+ ::RSpec::Core::Formatters.register self
8
8
 
9
9
  private
10
10
 
@@ -5,7 +5,7 @@ module KnapsackPro
5
5
  module RSpecQueueProfileFormatterExtension
6
6
  def self.print_summary
7
7
  return unless KnapsackPro::Config::Env.modify_default_rspec_formatters?
8
- RSpec::Core::Formatters::ProfileFormatter.print_profile_summary
8
+ ::RSpec::Core::Formatters::ProfileFormatter.print_profile_summary
9
9
  end
10
10
 
11
11
  def initialize(output)
@@ -9,8 +9,8 @@ module KnapsackPro
9
9
  def dump_summary(summary); end
10
10
  end
11
11
 
12
- class RSpecQueueSummaryFormatter < RSpec::Core::Formatters::BaseFormatter
13
- RSpec::Core::Formatters.register self, :dump_summary, :dump_failures, :dump_pending
12
+ class RSpecQueueSummaryFormatter < ::RSpec::Core::Formatters::BaseFormatter
13
+ ::RSpec::Core::Formatters.register self, :dump_summary, :dump_failures, :dump_pending
14
14
 
15
15
  def self.registered_output=(output)
16
16
  @registered_output = {
@@ -2,54 +2,63 @@ module KnapsackPro
2
2
  module Hooks
3
3
  class Queue
4
4
  class << self
5
- attr_reader :before_queue,
6
- :after_subset_queue,
7
- :after_queue
5
+ attr_reader :before_queue_store,
6
+ :after_subset_queue_store,
7
+ :after_queue_store
8
8
 
9
9
  def reset_before_queue
10
- @before_queue = nil
10
+ @before_queue_store = nil
11
11
  end
12
12
 
13
13
  def reset_after_subset_queue
14
- @after_subset_queue = nil
14
+ @after_subset_queue_store = nil
15
15
  end
16
16
 
17
17
  def reset_after_queue
18
- @after_queue = nil
18
+ @after_queue_store = nil
19
19
  end
20
20
 
21
21
  def before_queue(&block)
22
- @before_queue ||= block
22
+ @before_queue_store ||= []
23
+ @before_queue_store << block
23
24
  end
24
25
 
25
26
  def after_subset_queue(&block)
26
- @after_subset_queue ||= block
27
+ @after_subset_queue_store ||= []
28
+ @after_subset_queue_store << block
27
29
  end
28
30
 
29
31
  def after_queue(&block)
30
- @after_queue ||= block
32
+ @after_queue_store ||= []
33
+ @after_queue_store << block
31
34
  end
32
35
 
33
36
  def call_before_queue
34
- return unless before_queue
35
- before_queue.call(
36
- KnapsackPro::Config::Env.queue_id
37
- )
37
+ return unless before_queue_store
38
+ before_queue_store.each do |block|
39
+ block.call(
40
+ KnapsackPro::Config::Env.queue_id
41
+ )
42
+ end
38
43
  end
39
44
 
40
45
  def call_after_subset_queue
41
- return unless after_subset_queue
42
- after_subset_queue.call(
43
- KnapsackPro::Config::Env.queue_id,
44
- KnapsackPro::Config::Env.subset_queue_id
45
- )
46
+ return unless after_subset_queue_store
47
+ after_subset_queue_store.each do |block|
48
+ block.call(
49
+ KnapsackPro::Config::Env.queue_id,
50
+ KnapsackPro::Config::Env.subset_queue_id
51
+ )
52
+ end
46
53
  end
47
54
 
48
55
  def call_after_queue
49
- return unless after_queue
50
- after_queue.call(
51
- KnapsackPro::Config::Env.queue_id
52
- )
56
+ return unless after_queue_store
57
+ after_queue_store.each do |block|
58
+ block.call(
59
+ KnapsackPro::Config::Env.queue_id
60
+ )
61
+ end
53
62
  end
54
63
  end
55
64
  end
@@ -15,7 +15,7 @@ module KnapsackPro
15
15
  Rake::Task[task_name].clear
16
16
  end
17
17
 
18
- Cucumber::Rake::Task.new(task_name) do |t|
18
+ ::Cucumber::Rake::Task.new(task_name) do |t|
19
19
  t.cucumber_opts = "#{args} --require #{runner.test_dir} -- #{runner.stringify_test_file_paths}"
20
20
  end
21
21
  Rake::Task[task_name].invoke
@@ -89,9 +89,9 @@ module KnapsackPro
89
89
  end
90
90
 
91
91
  # duplicate args because Minitest modifies args
92
- result = Minitest.run(args.dup)
92
+ result = ::Minitest.run(args.dup)
93
93
 
94
- Minitest::Runnable.reset
94
+ ::Minitest::Runnable.reset
95
95
 
96
96
  result
97
97
  end
@@ -79,8 +79,8 @@ module KnapsackPro
79
79
 
80
80
  log_rspec_command(args, test_file_paths, :subset_queue)
81
81
 
82
- options = RSpec::Core::ConfigurationOptions.new(cli_args)
83
- exit_code = RSpec::Core::Runner.new(options).run($stderr, $stdout)
82
+ options = ::RSpec::Core::ConfigurationOptions.new(cli_args)
83
+ exit_code = ::RSpec::Core::Runner.new(options).run($stderr, $stdout)
84
84
  exitstatus = exit_code if exit_code != 0
85
85
 
86
86
  rspec_clear_examples
@@ -124,26 +124,26 @@ module KnapsackPro
124
124
  #
125
125
  # Keep formatters and report to accumulate info about failed/pending tests
126
126
  def self.rspec_clear_examples
127
- if RSpec::ExampleGroups.respond_to?(:remove_all_constants)
128
- RSpec::ExampleGroups.remove_all_constants
127
+ if ::RSpec::ExampleGroups.respond_to?(:remove_all_constants)
128
+ ::RSpec::ExampleGroups.remove_all_constants
129
129
  else
130
- RSpec::ExampleGroups.constants.each do |constant|
131
- RSpec::ExampleGroups.__send__(:remove_const, constant)
130
+ ::RSpec::ExampleGroups.constants.each do |constant|
131
+ ::RSpec::ExampleGroups.__send__(:remove_const, constant)
132
132
  end
133
133
  end
134
- RSpec.world.example_groups.clear
135
- RSpec.configuration.start_time = ::RSpec::Core::Time.now
134
+ ::RSpec.world.example_groups.clear
135
+ ::RSpec.configuration.start_time = ::RSpec::Core::Time.now
136
136
 
137
137
  if KnapsackPro::Config::Env.rspec_split_by_test_examples?
138
138
  # Reset example group counts to ensure scoped example ids in metadata
139
139
  # have correct index (not increased by each subsequent run).
140
140
  # Solves this problem: https://github.com/rspec/rspec-core/issues/2721
141
- RSpec.world.instance_variable_set(:@example_group_counts_by_spec_file, Hash.new(0))
141
+ ::RSpec.world.instance_variable_set(:@example_group_counts_by_spec_file, Hash.new(0))
142
142
  end
143
143
 
144
144
  # skip reset filters for old RSpec versions
145
- if RSpec.configuration.respond_to?(:reset_filters)
146
- RSpec.configuration.reset_filters
145
+ if ::RSpec.configuration.respond_to?(:reset_filters)
146
+ ::RSpec.configuration.reset_filters
147
147
  end
148
148
  end
149
149
  end
@@ -15,7 +15,7 @@ module KnapsackPro
15
15
  Rake::Task[task_name].clear
16
16
  end
17
17
 
18
- RSpec::Core::RakeTask.new(task_name) do |t|
18
+ ::RSpec::Core::RakeTask.new(task_name) do |t|
19
19
  # we cannot pass runner.test_file_paths array to t.pattern
20
20
  # because pattern does not accept test example path like spec/a_spec.rb[1:2]
21
21
  # instead we pass test files and test example paths to t.rspec_opts
@@ -16,7 +16,7 @@ module KnapsackPro
16
16
  File.expand_path(f)
17
17
  end
18
18
 
19
- exit Test::Unit::AutoRunner.run(
19
+ exit ::Test::Unit::AutoRunner.run(
20
20
  true,
21
21
  runner.test_dir,
22
22
  cli_args
@@ -7,7 +7,7 @@ module KnapsackPro
7
7
  require 'rspec/core'
8
8
 
9
9
  cli_format =
10
- if Gem::Version.new(RSpec::Core::Version::STRING) < Gem::Version.new('3.6.0')
10
+ if Gem::Version.new(::RSpec::Core::Version::STRING) < Gem::Version.new('3.6.0')
11
11
  require_relative '../formatters/rspec_json_formatter'
12
12
  ['--format', KnapsackPro::Formatters::RSpecJsonFormatter.to_s]
13
13
  else
@@ -30,8 +30,8 @@ module KnapsackPro
30
30
  '--out', report_path,
31
31
  '--default-path', test_dir,
32
32
  ] + KnapsackPro::TestFilePresenter.paths(test_file_entities)
33
- options = RSpec::Core::ConfigurationOptions.new(cli_args)
34
- exit_code = RSpec::Core::Runner.new(options).run($stderr, $stdout)
33
+ options = ::RSpec::Core::ConfigurationOptions.new(cli_args)
34
+ exit_code = ::RSpec::Core::Runner.new(options).run($stderr, $stdout)
35
35
  if exit_code != 0
36
36
  raise 'There was problem to generate test examples for test suite'
37
37
  end
@@ -1,3 +1,3 @@
1
1
  module KnapsackPro
2
- VERSION = '1.22.3'
2
+ VERSION = '2.3.0'
3
3
  end
@@ -10,8 +10,7 @@ describe KnapsackPro::Adapters::TestUnitAdapter do
10
10
 
11
11
  before do
12
12
  parent_of_test_dir = File.expand_path('../../../', File.dirname(__FILE__))
13
- parent_of_test_dir_regexp = Regexp.new("^#{parent_of_test_dir}")
14
- described_class.class_variable_set(:@@parent_of_test_dir, parent_of_test_dir_regexp)
13
+ described_class.class_variable_set(:@@parent_of_test_dir, parent_of_test_dir)
15
14
  end
16
15
 
17
16
  context 'when regular test' do
@@ -108,6 +108,53 @@ shared_examples 'when retry request' do
108
108
  expect(connection.errors?).to be true
109
109
  end
110
110
 
111
+ context 'when max request retries defined' do
112
+ before do
113
+ expect(KnapsackPro::Config::Env).to receive(:max_request_retries).at_least(1).and_return(4)
114
+ end
115
+
116
+ it do
117
+ expect(logger).to receive(:debug).exactly(4).with("#{expected_http_method} http://api.knapsackpro.test:3000/v1/fake_endpoint")
118
+ expect(logger).to receive(:debug).exactly(4).with('API request UUID: fake-uuid')
119
+ expect(logger).to receive(:debug).exactly(4).with('API response:')
120
+
121
+ parsed_response = { 'error' => 'Internal Server Error' }
122
+
123
+ expect(logger).to receive(:error).exactly(4).with(parsed_response)
124
+
125
+ server_error = described_class::ServerError.new(parsed_response)
126
+ expect(logger).to receive(:warn).exactly(4).with(server_error.inspect)
127
+
128
+ expect(logger).to receive(:warn).with("Wait 8s and retry request to Knapsack Pro API.")
129
+ expect(logger).to receive(:warn).with("Next request in 8s...")
130
+ expect(logger).to receive(:warn).with("Next request in 6s...")
131
+ expect(logger).to receive(:warn).with("Next request in 4s...")
132
+ expect(logger).to receive(:warn).with("Next request in 2s...")
133
+
134
+ expect(logger).to receive(:warn).with("Wait 16s and retry request to Knapsack Pro API.")
135
+ expect(logger).to receive(:warn).with("Next request in 16s...")
136
+ expect(logger).to receive(:warn).with("Next request in 14s...")
137
+ expect(logger).to receive(:warn).with("Next request in 12s...")
138
+ expect(logger).to receive(:warn).with("Next request in 10s...")
139
+ expect(logger).to receive(:warn).with("Next request in 8s...")
140
+ expect(logger).to receive(:warn).with("Next request in 6s...")
141
+ expect(logger).to receive(:warn).with("Next request in 4s...")
142
+ expect(logger).to receive(:warn).with("Next request in 2s...")
143
+
144
+ expect(logger).to receive(:warn).with("Wait 24s and retry request to Knapsack Pro API.")
145
+ 12.times do |i|
146
+ expect(logger).to receive(:warn).with("Next request in #{(i+1)*2}s...")
147
+ end
148
+
149
+ expect(Kernel).to receive(:sleep).exactly(4+8+12).with(2)
150
+
151
+ expect(subject).to eq(parsed_response)
152
+
153
+ expect(connection.success?).to be false
154
+ expect(connection.errors?).to be true
155
+ end
156
+ end
157
+
111
158
  context 'when Fallback Mode is disabled' do
112
159
  before do
113
160
  expect(KnapsackPro::Config::Env).to receive(:fallback_mode_enabled?).at_least(1).and_return(false)
@@ -164,6 +211,63 @@ shared_examples 'when retry request' do
164
211
  expect(connection.errors?).to be true
165
212
  end
166
213
  end
214
+
215
+ context 'when Regular Mode' do
216
+ before do
217
+ expect(KnapsackPro::Config::Env).to receive(:regular_mode?).at_least(1).and_return(true)
218
+ end
219
+
220
+ it do
221
+ expect(logger).to receive(:debug).exactly(6).with("#{expected_http_method} http://api.knapsackpro.test:3000/v1/fake_endpoint")
222
+ expect(logger).to receive(:debug).exactly(6).with('API request UUID: fake-uuid')
223
+ expect(logger).to receive(:debug).exactly(6).with('API response:')
224
+
225
+ parsed_response = { 'error' => 'Internal Server Error' }
226
+
227
+ expect(logger).to receive(:error).exactly(6).with(parsed_response)
228
+
229
+ server_error = described_class::ServerError.new(parsed_response)
230
+ expect(logger).to receive(:warn).exactly(6).with(server_error.inspect)
231
+
232
+ expect(logger).to receive(:warn).with("Wait 8s and retry request to Knapsack Pro API.")
233
+ expect(logger).to receive(:warn).with("Next request in 8s...")
234
+ expect(logger).to receive(:warn).with("Next request in 6s...")
235
+ expect(logger).to receive(:warn).with("Next request in 4s...")
236
+ expect(logger).to receive(:warn).with("Next request in 2s...")
237
+
238
+ expect(logger).to receive(:warn).with("Wait 16s and retry request to Knapsack Pro API.")
239
+ expect(logger).to receive(:warn).with("Next request in 16s...")
240
+ expect(logger).to receive(:warn).with("Next request in 14s...")
241
+ expect(logger).to receive(:warn).with("Next request in 12s...")
242
+ expect(logger).to receive(:warn).with("Next request in 10s...")
243
+ expect(logger).to receive(:warn).with("Next request in 8s...")
244
+ expect(logger).to receive(:warn).with("Next request in 6s...")
245
+ expect(logger).to receive(:warn).with("Next request in 4s...")
246
+ expect(logger).to receive(:warn).with("Next request in 2s...")
247
+
248
+ expect(logger).to receive(:warn).with("Wait 24s and retry request to Knapsack Pro API.")
249
+ 12.times do |i|
250
+ expect(logger).to receive(:warn).with("Next request in #{(i+1)*2}s...")
251
+ end
252
+
253
+ expect(logger).to receive(:warn).with("Wait 32s and retry request to Knapsack Pro API.")
254
+ 16.times do |i|
255
+ expect(logger).to receive(:warn).with("Next request in #{(i+1)*2}s...")
256
+ end
257
+
258
+ expect(logger).to receive(:warn).with("Wait 40s and retry request to Knapsack Pro API.")
259
+ 20.times do |i|
260
+ expect(logger).to receive(:warn).with("Next request in #{(i+1)*2}s...")
261
+ end
262
+
263
+ expect(Kernel).to receive(:sleep).exactly(60).with(2)
264
+
265
+ expect(subject).to eq(parsed_response)
266
+
267
+ expect(connection.success?).to be false
268
+ expect(connection.errors?).to be true
269
+ end
270
+ end
167
271
  end
168
272
  end
169
273
 
@@ -22,7 +22,14 @@ describe KnapsackPro::Config::CI::GithubActions do
22
22
  describe '#node_build_id' do
23
23
  subject { described_class.new.node_build_id }
24
24
 
25
- it { should be nil }
25
+ context 'when environment exists' do
26
+ let(:env) { { 'GITHUB_RUN_ID' => 2706 } }
27
+ it { should eql 2706 }
28
+ end
29
+
30
+ context "when environment doesn't exist" do
31
+ it { should be nil }
32
+ end
26
33
  end
27
34
 
28
35
  describe '#commit_hash' do
@@ -100,6 +100,19 @@ describe KnapsackPro::Config::Env do
100
100
  end
101
101
  end
102
102
 
103
+ describe '.max_request_retries' do
104
+ subject { described_class.max_request_retries }
105
+
106
+ context 'when ENV exists' do
107
+ before { stub_const("ENV", { 'KNAPSACK_PRO_MAX_REQUEST_RETRIES' => '2' }) }
108
+ it { should eq 2 }
109
+ end
110
+
111
+ context "when ENV doesn't exist" do
112
+ it { should be_nil }
113
+ end
114
+ end
115
+
103
116
  describe '.commit_hash' do
104
117
  subject { described_class.commit_hash }
105
118
 
@@ -267,6 +280,25 @@ describe KnapsackPro::Config::Env do
267
280
  end
268
281
  end
269
282
 
283
+
284
+ describe '.regular_mode?' do
285
+ subject { described_class.regular_mode? }
286
+
287
+ before do
288
+ expect(described_class).to receive(:recording_enabled?).and_return(recording_enabled)
289
+ end
290
+
291
+ context 'when recording is enabled' do
292
+ let(:recording_enabled) { true }
293
+ it { should be true }
294
+ end
295
+
296
+ context 'when recording is not enabled' do
297
+ let(:recording_enabled) { false }
298
+ it { should be false }
299
+ end
300
+ end
301
+
270
302
  describe '.recording_enabled?' do
271
303
  subject { described_class.recording_enabled? }
272
304
 
@@ -658,6 +690,20 @@ describe KnapsackPro::Config::Env do
658
690
  end
659
691
  end
660
692
 
693
+ describe '.rspec_test_example_detector_prefix' do
694
+ subject { described_class.rspec_test_example_detector_prefix }
695
+
696
+ context 'when ENV exists' do
697
+ before { stub_const("ENV", { 'KNAPSACK_PRO_RSPEC_TEST_EXAMPLE_DETECTOR_PREFIX' => '' }) }
698
+ it { should eq '' }
699
+ end
700
+
701
+ context "when ENV doesn't exist" do
702
+ before { stub_const("ENV", {}) }
703
+ it { should eq 'bundle exec' }
704
+ end
705
+ end
706
+
661
707
  describe '.test_suite_token' do
662
708
  subject { described_class.test_suite_token }
663
709
 
@@ -10,18 +10,30 @@ describe KnapsackPro::Hooks::Queue do
10
10
  it { should be_nil }
11
11
  end
12
12
 
13
- context 'when callback is set' do
13
+ context 'when callback is set multiple times' do
14
14
  let(:queue_id) { double }
15
15
 
16
16
  before do
17
- expect(KnapsackPro::Config::Env).to receive(:queue_id).and_return(queue_id)
17
+ expect(KnapsackPro::Config::Env).to receive(:queue_id).twice.and_return(queue_id)
18
18
 
19
+ $expected_called_blocks = []
20
+
21
+ described_class.before_queue do |q_id|
22
+ $expected_called_blocks << [:block_1_called, q_id]
23
+ end
19
24
  described_class.before_queue do |q_id|
20
- [:fake_value, q_id]
25
+ $expected_called_blocks << [:block_2_called, q_id]
21
26
  end
22
27
  end
23
28
 
24
- it { should eq [:fake_value, queue_id] }
29
+ it 'each block is called' do
30
+ subject
31
+
32
+ expect($expected_called_blocks).to eq([
33
+ [:block_1_called, queue_id],
34
+ [:block_2_called, queue_id],
35
+ ])
36
+ end
25
37
  end
26
38
  end
27
39
 
@@ -36,20 +48,32 @@ describe KnapsackPro::Hooks::Queue do
36
48
  it { should be_nil }
37
49
  end
38
50
 
39
- context 'when callback is set' do
51
+ context 'when callback is set multiple times' do
40
52
  let(:queue_id) { double }
41
53
  let(:subset_queue_id) { double }
42
54
 
43
55
  before do
44
- expect(KnapsackPro::Config::Env).to receive(:queue_id).and_return(queue_id)
45
- expect(KnapsackPro::Config::Env).to receive(:subset_queue_id).and_return(subset_queue_id)
56
+ expect(KnapsackPro::Config::Env).to receive(:queue_id).twice.and_return(queue_id)
57
+ expect(KnapsackPro::Config::Env).to receive(:subset_queue_id).twice.and_return(subset_queue_id)
58
+
59
+ $expected_called_blocks = []
46
60
 
47
61
  described_class.after_subset_queue do |q_id, subset_q_id|
48
- [:fake_value, q_id, subset_q_id]
62
+ $expected_called_blocks << [:block_1_called, q_id, subset_q_id]
63
+ end
64
+ described_class.after_subset_queue do |q_id, subset_q_id|
65
+ $expected_called_blocks << [:block_2_called, q_id, subset_q_id]
49
66
  end
50
67
  end
51
68
 
52
- it { should eq [:fake_value, queue_id, subset_queue_id] }
69
+ it 'each block is called' do
70
+ subject
71
+
72
+ expect($expected_called_blocks).to eq([
73
+ [:block_1_called, queue_id, subset_queue_id],
74
+ [:block_2_called, queue_id, subset_queue_id],
75
+ ])
76
+ end
53
77
  end
54
78
  end
55
79
 
@@ -64,18 +88,30 @@ describe KnapsackPro::Hooks::Queue do
64
88
  it { should be_nil }
65
89
  end
66
90
 
67
- context 'when callback is set' do
91
+ context 'when callback is set multiple times' do
68
92
  let(:queue_id) { double }
69
93
 
70
94
  before do
71
- expect(KnapsackPro::Config::Env).to receive(:queue_id).and_return(queue_id)
95
+ expect(KnapsackPro::Config::Env).to receive(:queue_id).twice.and_return(queue_id)
96
+
97
+ $expected_called_blocks = []
72
98
 
73
99
  described_class.after_queue do |q_id|
74
- [:fake_value, q_id]
100
+ $expected_called_blocks << [:block_1_called, q_id]
101
+ end
102
+ described_class.after_queue do |q_id|
103
+ $expected_called_blocks << [:block_2_called, q_id]
75
104
  end
76
105
  end
77
106
 
78
- it { should eq [:fake_value, queue_id] }
107
+ it 'each block is called' do
108
+ subject
109
+
110
+ expect($expected_called_blocks).to eq([
111
+ [:block_1_called, queue_id],
112
+ [:block_2_called, queue_id],
113
+ ])
114
+ end
79
115
  end
80
116
  end
81
117
  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: 1.22.3
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ArturT
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-22 00:00:00.000000000 Z
11
+ date: 2020-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake