knapsack_pro 3.8.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +377 -23
- data/.github/dependabot.yml +11 -0
- data/.github/pull_request_template.md +22 -0
- data/.gitignore +4 -0
- data/CHANGELOG.md +325 -1
- data/Gemfile +9 -0
- data/README.md +3 -10
- data/bin/test +15 -0
- data/knapsack_pro.gemspec +7 -6
- data/lib/knapsack_pro/adapters/base_adapter.rb +17 -2
- data/lib/knapsack_pro/adapters/cucumber_adapter.rb +3 -3
- data/lib/knapsack_pro/adapters/minitest_adapter.rb +2 -0
- data/lib/knapsack_pro/adapters/rspec_adapter.rb +88 -49
- data/lib/knapsack_pro/adapters/spinach_adapter.rb +2 -0
- data/lib/knapsack_pro/adapters/test_unit_adapter.rb +2 -0
- data/lib/knapsack_pro/allocator.rb +2 -0
- data/lib/knapsack_pro/allocator_builder.rb +2 -0
- data/lib/knapsack_pro/base_allocator_builder.rb +8 -25
- data/lib/knapsack_pro/build_distribution_fetcher.rb +2 -0
- data/lib/knapsack_pro/client/api/action.rb +2 -0
- data/lib/knapsack_pro/client/api/v1/base.rb +2 -0
- data/lib/knapsack_pro/client/api/v1/build_distributions.rb +5 -0
- data/lib/knapsack_pro/client/api/v1/build_subsets.rb +2 -0
- data/lib/knapsack_pro/client/api/v1/queues.rb +6 -1
- data/lib/knapsack_pro/client/connection.rb +5 -6
- data/lib/knapsack_pro/config/ci/app_veyor.rb +18 -0
- data/lib/knapsack_pro/config/ci/base.rb +27 -0
- data/lib/knapsack_pro/config/ci/buildkite.rb +18 -0
- data/lib/knapsack_pro/config/ci/circle.rb +18 -0
- data/lib/knapsack_pro/config/ci/cirrus_ci.rb +18 -0
- data/lib/knapsack_pro/config/ci/codefresh.rb +18 -0
- data/lib/knapsack_pro/config/ci/codeship.rb +18 -0
- data/lib/knapsack_pro/config/ci/github_actions.rb +26 -0
- data/lib/knapsack_pro/config/ci/gitlab_ci.rb +20 -1
- data/lib/knapsack_pro/config/ci/heroku.rb +18 -0
- data/lib/knapsack_pro/config/ci/semaphore.rb +16 -0
- data/lib/knapsack_pro/config/ci/semaphore2.rb +19 -0
- data/lib/knapsack_pro/config/ci/travis.rb +18 -0
- data/lib/knapsack_pro/config/env.rb +46 -22
- data/lib/knapsack_pro/config/env_generator.rb +2 -0
- data/lib/knapsack_pro/config/temp_files.rb +8 -4
- data/lib/knapsack_pro/crypto/branch_encryptor.rb +2 -0
- data/lib/knapsack_pro/crypto/decryptor.rb +2 -0
- data/lib/knapsack_pro/crypto/digestor.rb +2 -0
- data/lib/knapsack_pro/crypto/encryptor.rb +2 -0
- data/lib/knapsack_pro/extensions/rspec_extension.rb +137 -0
- data/lib/knapsack_pro/formatters/rspec_json_formatter.rb +2 -0
- data/lib/knapsack_pro/formatters/time_tracker.rb +152 -0
- data/lib/knapsack_pro/formatters/time_tracker_fetcher.rb +20 -0
- data/lib/knapsack_pro/hooks/queue.rb +2 -0
- data/lib/knapsack_pro/logger_wrapper.rb +2 -0
- data/lib/knapsack_pro/mask_string.rb +9 -0
- data/lib/knapsack_pro/presenter.rb +6 -3
- data/lib/knapsack_pro/pure/queue/rspec_pure.rb +92 -0
- data/lib/knapsack_pro/queue_allocator.rb +2 -0
- data/lib/knapsack_pro/queue_allocator_builder.rb +2 -0
- data/lib/knapsack_pro/railtie.rb +2 -0
- data/lib/knapsack_pro/report.rb +15 -9
- data/lib/knapsack_pro/repository_adapter_initiator.rb +2 -0
- data/lib/knapsack_pro/repository_adapters/base_adapter.rb +2 -0
- data/lib/knapsack_pro/repository_adapters/env_adapter.rb +2 -0
- data/lib/knapsack_pro/repository_adapters/git_adapter.rb +50 -0
- data/lib/knapsack_pro/runners/base_runner.rb +2 -0
- data/lib/knapsack_pro/runners/cucumber_runner.rb +2 -0
- data/lib/knapsack_pro/runners/minitest_runner.rb +2 -0
- data/lib/knapsack_pro/runners/queue/base_runner.rb +29 -0
- data/lib/knapsack_pro/runners/queue/cucumber_runner.rb +9 -6
- data/lib/knapsack_pro/runners/queue/minitest_runner.rb +13 -6
- data/lib/knapsack_pro/runners/queue/rspec_runner.rb +128 -135
- data/lib/knapsack_pro/runners/rspec_runner.rb +22 -3
- data/lib/knapsack_pro/runners/spinach_runner.rb +2 -0
- data/lib/knapsack_pro/runners/test_unit_runner.rb +2 -0
- data/lib/knapsack_pro/slow_test_file_determiner.rb +2 -0
- data/lib/knapsack_pro/slow_test_file_finder.rb +2 -0
- data/lib/knapsack_pro/task_loader.rb +2 -0
- data/lib/knapsack_pro/test_case_detectors/rspec_test_example_detector.rb +2 -0
- data/lib/knapsack_pro/test_case_mergers/base_merger.rb +2 -0
- data/lib/knapsack_pro/test_case_mergers/rspec_merger.rb +2 -0
- data/lib/knapsack_pro/test_file_cleaner.rb +2 -0
- data/lib/knapsack_pro/test_file_finder.rb +2 -0
- data/lib/knapsack_pro/test_file_pattern.rb +2 -0
- data/lib/knapsack_pro/test_file_presenter.rb +2 -0
- data/lib/knapsack_pro/test_files_with_test_cases_composer.rb +2 -0
- data/lib/knapsack_pro/test_flat_distributor.rb +2 -0
- data/lib/knapsack_pro/tracker.rb +3 -3
- data/lib/knapsack_pro/urls.rb +4 -0
- data/lib/knapsack_pro/utils.rb +2 -0
- data/lib/knapsack_pro/version.rb +3 -1
- data/lib/knapsack_pro.rb +5 -3
- data/lib/tasks/cucumber.rake +2 -0
- data/lib/tasks/encrypted_branch_names.rake +2 -0
- data/lib/tasks/encrypted_test_file_names.rake +2 -0
- data/lib/tasks/minitest.rake +2 -0
- data/lib/tasks/queue/cucumber.rake +13 -0
- data/lib/tasks/queue/minitest.rake +13 -0
- data/lib/tasks/queue/rspec.rake +13 -0
- data/lib/tasks/rspec.rake +5 -0
- data/lib/tasks/salt.rake +2 -0
- data/lib/tasks/spinach.rake +2 -0
- data/lib/tasks/test_unit.rake +2 -0
- data/spec/integration/api/build_distributions_subset_spec.rb +1 -0
- data/spec/integration/runners/queue/rspec_runner.rb +80 -0
- data/spec/integration/runners/queue/rspec_runner_spec.rb +2232 -0
- data/spec/knapsack_pro/adapters/base_adapter_spec.rb +30 -11
- data/spec/knapsack_pro/adapters/cucumber_adapter_spec.rb +2 -5
- data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +146 -174
- data/spec/knapsack_pro/base_allocator_builder_spec.rb +22 -48
- data/spec/knapsack_pro/client/api/v1/build_distributions_spec.rb +19 -27
- data/spec/knapsack_pro/client/api/v1/queues_spec.rb +23 -43
- data/spec/knapsack_pro/client/connection_spec.rb +59 -7
- data/spec/knapsack_pro/config/ci/app_veyor_spec.rb +22 -8
- data/spec/knapsack_pro/config/ci/base_spec.rb +1 -0
- data/spec/knapsack_pro/config/ci/buildkite_spec.rb +51 -16
- data/spec/knapsack_pro/config/ci/circle_spec.rb +48 -13
- data/spec/knapsack_pro/config/ci/cirrus_ci_spec.rb +12 -12
- data/spec/knapsack_pro/config/ci/codefresh_spec.rb +21 -6
- data/spec/knapsack_pro/config/ci/codeship_spec.rb +20 -6
- data/spec/knapsack_pro/config/ci/github_actions_spec.rb +37 -10
- data/spec/knapsack_pro/config/ci/gitlab_ci_spec.rb +48 -13
- data/spec/knapsack_pro/config/ci/heroku_spec.rb +12 -12
- data/spec/knapsack_pro/config/ci/semaphore2_spec.rb +11 -11
- data/spec/knapsack_pro/config/ci/semaphore_spec.rb +12 -12
- data/spec/knapsack_pro/config/ci/travis_spec.rb +8 -8
- data/spec/knapsack_pro/config/env_spec.rb +204 -124
- data/spec/knapsack_pro/formatters/time_tracker_specs.rb +424 -0
- data/spec/knapsack_pro/hooks/queue_spec.rb +2 -2
- data/spec/knapsack_pro/presenter_spec.rb +1 -1
- data/spec/knapsack_pro/pure/queue/rspec_pure_spec.rb +224 -0
- data/spec/knapsack_pro/repository_adapters/git_adapter_spec.rb +72 -0
- data/spec/knapsack_pro/runners/queue/cucumber_runner_spec.rb +18 -16
- data/spec/knapsack_pro/runners/queue/minitest_runner_spec.rb +17 -14
- data/spec/knapsack_pro/runners/rspec_runner_spec.rb +40 -23
- data/spec/knapsack_pro/test_case_detectors/rspec_test_example_detector_spec.rb +1 -0
- data/spec/knapsack_pro/tracker_spec.rb +0 -4
- data/spec/knapsack_pro_spec.rb +3 -3
- data/spec/spec_helper.rb +0 -1
- metadata +26 -23
- data/lib/knapsack_pro/config/ci/snap_ci.rb +0 -35
- data/lib/knapsack_pro/config/ci/solano_ci.rb +0 -32
- data/lib/knapsack_pro/extensions/time.rb +0 -7
- data/lib/knapsack_pro/formatters/rspec_queue_profile_formatter_extension.rb +0 -56
- data/lib/knapsack_pro/formatters/rspec_queue_summary_formatter.rb +0 -112
- data/spec/knapsack_pro/config/ci/snap_ci_spec.rb +0 -104
- data/spec/knapsack_pro/config/ci/solano_ci_spec.rb +0 -73
- data/spec/knapsack_pro/extensions/time_spec.rb +0 -5
- data/spec/knapsack_pro/runners/queue/rspec_runner_spec.rb +0 -342
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module KnapsackPro
|
|
2
4
|
module Config
|
|
3
5
|
module CI
|
|
6
|
+
# Semaphore Classic is deprecated
|
|
7
|
+
# https://semaphoreci.com/blog/semaphore-classic-deprecation
|
|
4
8
|
class Semaphore < Base
|
|
5
9
|
def node_total
|
|
6
10
|
ENV['SEMAPHORE_THREAD_COUNT']
|
|
@@ -26,6 +30,18 @@ module KnapsackPro
|
|
|
26
30
|
def project_dir
|
|
27
31
|
ENV['SEMAPHORE_PROJECT_DIR']
|
|
28
32
|
end
|
|
33
|
+
|
|
34
|
+
def detected
|
|
35
|
+
ENV.key?('SEMAPHORE_BUILD_NUMBER') ? self.class : nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def fixed_queue_split
|
|
39
|
+
false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def ci_provider
|
|
43
|
+
"Semaphore CI 1.0"
|
|
44
|
+
end
|
|
29
45
|
end
|
|
30
46
|
end
|
|
31
47
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# https://docs.semaphoreci.com/article/12-environment-variables
|
|
2
4
|
module KnapsackPro
|
|
3
5
|
module Config
|
|
@@ -29,6 +31,23 @@ module KnapsackPro
|
|
|
29
31
|
"#{ENV['HOME']}/#{ENV['SEMAPHORE_GIT_DIR']}"
|
|
30
32
|
end
|
|
31
33
|
end
|
|
34
|
+
|
|
35
|
+
def user_seat
|
|
36
|
+
# not provided
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def detected
|
|
40
|
+
# check 2 keys to be sure we are using Semaphore 2.0
|
|
41
|
+
ENV.key?('SEMAPHORE') && ENV.key?('SEMAPHORE_WORKFLOW_ID') ? self.class : nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def fixed_queue_split
|
|
45
|
+
false
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def ci_provider
|
|
49
|
+
"Semaphore CI 2.0"
|
|
50
|
+
end
|
|
32
51
|
end
|
|
33
52
|
end
|
|
34
53
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module KnapsackPro
|
|
2
4
|
module Config
|
|
3
5
|
module CI
|
|
@@ -17,6 +19,22 @@ module KnapsackPro
|
|
|
17
19
|
def project_dir
|
|
18
20
|
ENV['TRAVIS_BUILD_DIR']
|
|
19
21
|
end
|
|
22
|
+
|
|
23
|
+
def user_seat
|
|
24
|
+
# not provided
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def detected
|
|
28
|
+
ENV.key?('TRAVIS') ? self.class : nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def fixed_queue_split
|
|
32
|
+
true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def ci_provider
|
|
36
|
+
"Travis CI"
|
|
37
|
+
end
|
|
20
38
|
end
|
|
21
39
|
end
|
|
22
40
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module KnapsackPro
|
|
2
4
|
module Config
|
|
3
5
|
class Env
|
|
@@ -23,9 +25,10 @@ module KnapsackPro
|
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
def ci_node_build_id
|
|
26
|
-
|
|
28
|
+
env_name = 'KNAPSACK_PRO_CI_NODE_BUILD_ID'
|
|
29
|
+
ENV[env_name] ||
|
|
27
30
|
ci_env_for(:node_build_id) ||
|
|
28
|
-
|
|
31
|
+
raise("Missing environment variable #{env_name}. Read more at #{KnapsackPro::Urls::KNAPSACK_PRO_CI_NODE_BUILD_ID}")
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
def ci_node_retry_count
|
|
@@ -58,6 +61,17 @@ module KnapsackPro
|
|
|
58
61
|
ci_env_for(:project_dir)
|
|
59
62
|
end
|
|
60
63
|
|
|
64
|
+
def user_seat
|
|
65
|
+
ENV['KNAPSACK_PRO_USER_SEAT'] ||
|
|
66
|
+
ci_env_for(:user_seat)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def masked_user_seat
|
|
70
|
+
return unless user_seat
|
|
71
|
+
|
|
72
|
+
KnapsackPro::MaskString.call(user_seat)
|
|
73
|
+
end
|
|
74
|
+
|
|
61
75
|
def test_file_pattern
|
|
62
76
|
ENV['KNAPSACK_PRO_TEST_FILE_PATTERN']
|
|
63
77
|
end
|
|
@@ -130,14 +144,6 @@ module KnapsackPro
|
|
|
130
144
|
test_files_encrypted == 'true'
|
|
131
145
|
end
|
|
132
146
|
|
|
133
|
-
def modify_default_rspec_formatters
|
|
134
|
-
ENV.fetch('KNAPSACK_PRO_MODIFY_DEFAULT_RSPEC_FORMATTERS', true)
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def modify_default_rspec_formatters?
|
|
138
|
-
modify_default_rspec_formatters.to_s == 'true'
|
|
139
|
-
end
|
|
140
|
-
|
|
141
147
|
def branch_encrypted
|
|
142
148
|
ENV['KNAPSACK_PRO_BRANCH_ENCRYPTED']
|
|
143
149
|
end
|
|
@@ -175,7 +181,16 @@ module KnapsackPro
|
|
|
175
181
|
end
|
|
176
182
|
|
|
177
183
|
def fixed_queue_split
|
|
178
|
-
|
|
184
|
+
@fixed_queue_split ||= begin
|
|
185
|
+
env_name = 'KNAPSACK_PRO_FIXED_QUEUE_SPLIT'
|
|
186
|
+
computed = ENV.fetch(env_name, ci_env_for(:fixed_queue_split)).to_s
|
|
187
|
+
|
|
188
|
+
if !ENV.key?(env_name)
|
|
189
|
+
KnapsackPro.logger.info("#{env_name} is not set. Using default value: #{computed}. Learn more at #{KnapsackPro::Urls::FIXED_QUEUE_SPLIT}")
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
computed
|
|
193
|
+
end
|
|
179
194
|
end
|
|
180
195
|
|
|
181
196
|
def fixed_queue_split?
|
|
@@ -235,21 +250,25 @@ module KnapsackPro
|
|
|
235
250
|
end
|
|
236
251
|
|
|
237
252
|
def ci_env_for(env_name)
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
ci = ci_class.new
|
|
245
|
-
value = ci.send(env_name)
|
|
246
|
-
break unless value.nil?
|
|
253
|
+
detected_ci.new.send(env_name)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def detected_ci
|
|
257
|
+
detected = KnapsackPro::Config::CI.constants.map do |constant|
|
|
258
|
+
Object.const_get("KnapsackPro::Config::CI::#{constant}").new.detected
|
|
247
259
|
end
|
|
248
|
-
|
|
260
|
+
.compact
|
|
261
|
+
.first
|
|
262
|
+
|
|
263
|
+
detected || KnapsackPro::Config::CI::Base
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def ci_provider
|
|
267
|
+
detected_ci.new.ci_provider
|
|
249
268
|
end
|
|
250
269
|
|
|
251
270
|
def log_level
|
|
252
|
-
LOG_LEVELS[ENV['KNAPSACK_PRO_LOG_LEVEL'].to_s.downcase] || ::Logger::
|
|
271
|
+
LOG_LEVELS[ENV['KNAPSACK_PRO_LOG_LEVEL'].to_s.downcase] || ::Logger::INFO
|
|
253
272
|
end
|
|
254
273
|
|
|
255
274
|
def log_dir
|
|
@@ -264,6 +283,11 @@ module KnapsackPro
|
|
|
264
283
|
ENV['KNAPSACK_PRO_TEST_RUNNER_ADAPTER'] = adapter_class.to_s.split('::').last
|
|
265
284
|
end
|
|
266
285
|
|
|
286
|
+
def ci?
|
|
287
|
+
ENV.fetch('CI', 'false').downcase == 'true' ||
|
|
288
|
+
detected_ci != KnapsackPro::Config::CI::Base
|
|
289
|
+
end
|
|
290
|
+
|
|
267
291
|
private
|
|
268
292
|
|
|
269
293
|
def required_env(env_name)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module KnapsackPro
|
|
2
4
|
module Config
|
|
3
5
|
class TempFiles
|
|
@@ -22,10 +24,12 @@ module KnapsackPro
|
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
def self.gitignore_file_content
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
<<~GITIGNORE
|
|
28
|
+
# This directory is used by knapsack_pro gem for storing temporary files during tests runtime.
|
|
29
|
+
# Ignore all files, and do not commit this directory into your repository.
|
|
30
|
+
# Learn more at https://knapsackpro.com
|
|
31
|
+
*
|
|
32
|
+
GITIGNORE
|
|
29
33
|
end
|
|
30
34
|
|
|
31
35
|
def self.create_gitignore_file!
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KnapsackPro
|
|
4
|
+
module Extensions
|
|
5
|
+
# Facade to abstract calls to internal RSpec methods.
|
|
6
|
+
# To allow comparing the monkey patch with the original RSpec code, keep a similar method structure and permalink to the source.
|
|
7
|
+
module RSpecExtension
|
|
8
|
+
Seed = Struct.new(:value, :used?)
|
|
9
|
+
|
|
10
|
+
def self.setup!
|
|
11
|
+
RSpec::Core::World.prepend(World)
|
|
12
|
+
RSpec::Core::Runner.prepend(Runner)
|
|
13
|
+
RSpec::Core::Configuration.prepend(Configuration)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module World
|
|
17
|
+
# Based on:
|
|
18
|
+
# https://github.com/rspec/rspec-core/blob/f8c8880dabd8f0544a6f91d8d4c857c1bd8df903/lib/rspec/core/world.rb#L171
|
|
19
|
+
#
|
|
20
|
+
# Filters are not announced because we do not load tests during setup. It would print `No examples found.` and we don't want that.
|
|
21
|
+
def knapsack__announce_filters
|
|
22
|
+
fail_if_config_and_cli_options_invalid
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
module Runner
|
|
27
|
+
# Based on:
|
|
28
|
+
# https://github.com/rspec/rspec-core/blob/f8c8880dabd8f0544a6f91d8d4c857c1bd8df903/lib/rspec/core/runner.rb#L98
|
|
29
|
+
#
|
|
30
|
+
# `@configuration.load_spec_files` is not called because we load tests in batches with `knapsack__load_spec_files_batch` later on.
|
|
31
|
+
def knapsack__setup(stream_error = $stderr, stream_out = $stdout)
|
|
32
|
+
configure(stream_error, stream_out)
|
|
33
|
+
ensure
|
|
34
|
+
world.knapsack__announce_filters
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def knapsack__wants_to_quit?
|
|
38
|
+
world.wants_to_quit
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def knapsack__rspec_is_quitting?
|
|
42
|
+
world.respond_to?(:rspec_is_quitting) && world.rspec_is_quitting
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def knapsack__exit_early
|
|
46
|
+
_exit_status = configuration.reporter.exit_early(exit_code)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Based on:
|
|
50
|
+
# https://github.com/rspec/rspec-core/blob/f8c8880dabd8f0544a6f91d8d4c857c1bd8df903/lib/rspec/core/configuration.rb#L546
|
|
51
|
+
def knapsack__error_exit_code
|
|
52
|
+
configuration.error_exit_code # nil unless `--error-exit-code` is specified
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# must be called after `Runner#knapsack__setup` that loads the `spec_helper.rb` configuration
|
|
56
|
+
def knapsack__deprecated_run_all_when_everything_filtered_enabled?
|
|
57
|
+
configuration.respond_to?(:run_all_when_everything_filtered) && !!configuration.run_all_when_everything_filtered
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def knapsack__seed
|
|
61
|
+
Seed.new(configuration.seed.to_s, configuration.seed_used?)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @param test_file_paths Array[String]
|
|
65
|
+
# Example: ['a_spec.rb', 'b_spec.rb[1:1]']
|
|
66
|
+
def knapsack__load_spec_files_batch(test_file_paths)
|
|
67
|
+
world.reset
|
|
68
|
+
|
|
69
|
+
# Reset filters, but do not reset `configuration.static_config_filter_manager` to preserve the --tag option
|
|
70
|
+
filter_manager = RSpec::Core::FilterManager.new
|
|
71
|
+
options.configure_filter_manager(filter_manager)
|
|
72
|
+
configuration.filter_manager = filter_manager
|
|
73
|
+
|
|
74
|
+
configuration.knapsack__load_spec_files(test_file_paths)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Based on:
|
|
78
|
+
# https://github.com/rspec/rspec-core/blob/f8c8880dabd8f0544a6f91d8d4c857c1bd8df903/lib/rspec/core/runner.rb#L113
|
|
79
|
+
#
|
|
80
|
+
# Ignore `configuration.fail_if_no_examples` in Queue Mode:
|
|
81
|
+
# * a late CI node, started after all tests were executed by other nodes, is expected to receive an empty batch
|
|
82
|
+
# * a batch could contain tests with no examples (e.g. commented out)
|
|
83
|
+
#
|
|
84
|
+
# @return [Fixnum] exit status code.
|
|
85
|
+
def knapsack__run_specs(queue_runner)
|
|
86
|
+
# Based on:
|
|
87
|
+
# https://github.com/rspec/rspec-core/blob/f8c8880dabd8f0544a6f91d8d4c857c1bd8df903/lib/rspec/core/world.rb#L53
|
|
88
|
+
ordering_strategy = configuration.ordering_registry.fetch(:global)
|
|
89
|
+
node_examples_passed = true
|
|
90
|
+
|
|
91
|
+
configuration.reporter.report(_expected_example_count = 0) do |reporter|
|
|
92
|
+
configuration.with_suite_hooks do
|
|
93
|
+
queue_runner.with_batch do |test_file_paths|
|
|
94
|
+
knapsack__load_spec_files_batch(test_file_paths)
|
|
95
|
+
|
|
96
|
+
examples_passed = ordering_strategy.order(world.example_groups).map do |example_group|
|
|
97
|
+
queue_runner.handle_signal!
|
|
98
|
+
example_group.run(reporter)
|
|
99
|
+
end.all?
|
|
100
|
+
|
|
101
|
+
node_examples_passed = false unless examples_passed
|
|
102
|
+
|
|
103
|
+
knapsack__persist_example_statuses
|
|
104
|
+
|
|
105
|
+
if reporter.fail_fast_limit_met?
|
|
106
|
+
queue_runner.log_fail_fast_limit_met
|
|
107
|
+
break
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
exit_code(node_examples_passed)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Based on:
|
|
117
|
+
# https://github.com/rspec/rspec-core/blob/f8c8880dabd8f0544a6f91d8d4c857c1bd8df903/lib/rspec/core/runner.rb#L90
|
|
118
|
+
def knapsack__persist_example_statuses
|
|
119
|
+
persist_example_statuses
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
module Configuration
|
|
124
|
+
# Based on:
|
|
125
|
+
# https://github.com/rspec/rspec-core/blob/f8c8880dabd8f0544a6f91d8d4c857c1bd8df903/lib/rspec/core/configuration.rb#L1619
|
|
126
|
+
def knapsack__load_spec_files(test_file_paths)
|
|
127
|
+
batch_of_files_to_run = get_files_to_run(test_file_paths)
|
|
128
|
+
batch_of_files_to_run.each do |f|
|
|
129
|
+
file = File.expand_path(f)
|
|
130
|
+
load_file_handling_errors(:load, file)
|
|
131
|
+
loaded_spec_files << file
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'stringio'
|
|
4
|
+
|
|
5
|
+
module KnapsackPro
|
|
6
|
+
module Formatters
|
|
7
|
+
class TimeTracker
|
|
8
|
+
::RSpec::Core::Formatters.register self,
|
|
9
|
+
:example_group_started,
|
|
10
|
+
:example_started,
|
|
11
|
+
:example_finished,
|
|
12
|
+
:example_group_finished
|
|
13
|
+
|
|
14
|
+
attr_reader :output # RSpec < v3.10.2
|
|
15
|
+
|
|
16
|
+
def initialize(_output)
|
|
17
|
+
@output = StringIO.new
|
|
18
|
+
@time_each = nil
|
|
19
|
+
@time_all = nil
|
|
20
|
+
@before_all = 0.0
|
|
21
|
+
@group = {}
|
|
22
|
+
@paths = {}
|
|
23
|
+
@suite_started = now
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def example_group_started(notification)
|
|
27
|
+
return unless top_level_group?(notification.group)
|
|
28
|
+
@time_all = now
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def example_started(_notification)
|
|
32
|
+
@before_all = now - @time_all if @before_all == 0.0
|
|
33
|
+
@time_each = now
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def example_finished(notification)
|
|
37
|
+
record_example(@group, notification.example, @time_each)
|
|
38
|
+
@time_all = now
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def example_group_finished(notification)
|
|
42
|
+
return unless top_level_group?(notification.group)
|
|
43
|
+
|
|
44
|
+
after_all = @time_all.nil? ? 0.0 : now - @time_all
|
|
45
|
+
add_hooks_time(@group, @before_all, after_all)
|
|
46
|
+
@before_all = 0.0
|
|
47
|
+
@paths = merge(@paths, @group)
|
|
48
|
+
@group = {}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def queue(scheduled_paths)
|
|
52
|
+
recorded_paths = @paths.values.map do |example|
|
|
53
|
+
KnapsackPro::Adapters::RSpecAdapter.parse_file_path(example[:path])
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
missing = (scheduled_paths - recorded_paths).each_with_object({}) do |path, object|
|
|
57
|
+
object[path] = { path: path, time_execution: 0.0 }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
merge(@paths, missing).values.map do |example|
|
|
61
|
+
example.transform_keys(&:to_s)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def batch
|
|
66
|
+
@paths.values.map do |example|
|
|
67
|
+
example.transform_keys(&:to_s)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def duration
|
|
72
|
+
now - @suite_started
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def unexecuted_test_files(scheduled_paths)
|
|
76
|
+
pending_paths = @paths.values
|
|
77
|
+
.filter { |example| example[:time_execution] == 0.0 }
|
|
78
|
+
.map { |example| example[:path] }
|
|
79
|
+
|
|
80
|
+
not_run_paths = scheduled_paths -
|
|
81
|
+
@paths.values
|
|
82
|
+
.map { |example| example[:path] }
|
|
83
|
+
|
|
84
|
+
pending_paths + not_run_paths
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def top_level_group?(group)
|
|
90
|
+
group.metadata[:parent_example_group].nil?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def add_hooks_time(group, before_all, after_all)
|
|
94
|
+
group.each do |_, example|
|
|
95
|
+
next if example[:time_execution] == 0.0
|
|
96
|
+
example[:time_execution] += before_all + after_all
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def record_example(accumulator, example, started_at)
|
|
101
|
+
path = path_for(example)
|
|
102
|
+
return if path.nil?
|
|
103
|
+
|
|
104
|
+
time_execution = time_execution_for(example, started_at)
|
|
105
|
+
if accumulator.key?(path)
|
|
106
|
+
accumulator[path][:time_execution] += time_execution
|
|
107
|
+
else
|
|
108
|
+
accumulator[path] = { path: path, time_execution: time_execution }
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def path_for(example)
|
|
113
|
+
file = file_path_for(example)
|
|
114
|
+
return nil if file == ""
|
|
115
|
+
|
|
116
|
+
path = rspec_split_by_test_example?(file) ? example.id : file
|
|
117
|
+
KnapsackPro::TestFileCleaner.clean(path)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def rspec_split_by_test_example?(file)
|
|
121
|
+
return false unless KnapsackPro::Config::Env.rspec_split_by_test_examples?
|
|
122
|
+
return false unless KnapsackPro::Adapters::RSpecAdapter.slow_test_file?(KnapsackPro::Adapters::RSpecAdapter, file)
|
|
123
|
+
true
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def file_path_for(example)
|
|
127
|
+
KnapsackPro::Adapters::RSpecAdapter.file_path_for(example)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def time_execution_for(example, started_at)
|
|
131
|
+
if example.execution_result.status.to_s == "pending"
|
|
132
|
+
0.0
|
|
133
|
+
else
|
|
134
|
+
(now - started_at).to_f
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def merge(h1, h2)
|
|
139
|
+
h1.merge(h2) do |key, v1, v2|
|
|
140
|
+
{
|
|
141
|
+
path: key,
|
|
142
|
+
time_execution: v1[:time_execution] + v2[:time_execution]
|
|
143
|
+
}
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def now
|
|
148
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KnapsackPro
|
|
4
|
+
module Formatters
|
|
5
|
+
class TimeTrackerFetcher
|
|
6
|
+
def self.call
|
|
7
|
+
::RSpec
|
|
8
|
+
.configuration
|
|
9
|
+
.formatters
|
|
10
|
+
.find { |f| f.class.to_s == "KnapsackPro::Formatters::TimeTracker" }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.unexecuted_test_files(scheduled_paths)
|
|
14
|
+
time_tracker = call
|
|
15
|
+
return [] unless time_tracker
|
|
16
|
+
time_tracker.unexecuted_test_files(scheduled_paths)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module KnapsackPro
|
|
2
4
|
class Presenter
|
|
3
5
|
class << self
|
|
4
|
-
def global_time
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
def global_time(time = nil)
|
|
7
|
+
time = KnapsackPro.tracker.global_time if time.nil?
|
|
8
|
+
global_time = pretty_seconds(time)
|
|
9
|
+
"Global test execution duration: #{global_time}"
|
|
7
10
|
end
|
|
8
11
|
|
|
9
12
|
def pretty_seconds(seconds)
|