knapsack_pro 9.0.0 → 9.2.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.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/lib/knapsack_pro/adapters/base_adapter.rb +1 -17
  3. data/lib/knapsack_pro/adapters/minitest_adapter.rb +8 -13
  4. data/lib/knapsack_pro/adapters/rspec_adapter.rb +11 -9
  5. data/lib/knapsack_pro/build_distribution_fetcher.rb +22 -18
  6. data/lib/knapsack_pro/client/api/v1/build_distributions.rb +2 -7
  7. data/lib/knapsack_pro/client/api/v1/queues.rb +27 -0
  8. data/lib/knapsack_pro/client/connection.rb +1 -1
  9. data/lib/knapsack_pro/config/env.rb +9 -32
  10. data/lib/knapsack_pro/queue_allocator.rb +2 -1
  11. data/lib/knapsack_pro/queue_initializer.rb +21 -0
  12. data/lib/knapsack_pro/regular_allocator.rb +2 -1
  13. data/lib/knapsack_pro/rspec_slow_test_file_finder.rb +20 -0
  14. data/lib/knapsack_pro/slow_test_file_determiner.rb +0 -22
  15. data/lib/knapsack_pro/test_case_detectors/rspec_test_example_detector.rb +43 -69
  16. data/lib/knapsack_pro/test_case_mergers/rspec_merger.rb +19 -31
  17. data/lib/knapsack_pro/test_file_finder.rb +15 -42
  18. data/lib/knapsack_pro/test_file_presenter.rb +4 -0
  19. data/lib/knapsack_pro/test_suite.rb +8 -35
  20. data/lib/knapsack_pro/version.rb +1 -1
  21. data/lib/knapsack_pro.rb +1 -3
  22. data/lib/tasks/queue/rspec.rake +12 -0
  23. data/lib/tasks/rspec.rake +8 -4
  24. metadata +9 -204
  25. data/.circleci/config.yml +0 -468
  26. data/.github/assets/install-button.png +0 -0
  27. data/.github/assets/knapsack-diamonds.png +0 -0
  28. data/.github/dependabot.yml +0 -11
  29. data/.github/pull_request_template.md +0 -11
  30. data/.gitignore +0 -39
  31. data/.rspec +0 -5
  32. data/CHANGELOG.md +0 -1995
  33. data/Gemfile +0 -13
  34. data/LICENSE +0 -22
  35. data/README.md +0 -90
  36. data/Rakefile +0 -2
  37. data/knapsack_pro.gemspec +0 -43
  38. data/lib/knapsack_pro/slow_test_file_finder.rb +0 -29
  39. data/lib/knapsack_pro/test_case_mergers/base_merger.rb +0 -31
  40. data/lib/knapsack_pro/test_files_with_test_cases_composer.rb +0 -24
  41. data/spec/fixtures/test_file_list_source_file.txt +0 -6
  42. data/spec/fixtures/vcr_cassettes/api/v1/build_distributions/subset/invalid_test_suite_token.yml +0 -59
  43. data/spec/fixtures/vcr_cassettes/api/v1/build_distributions/subset/success.yml +0 -61
  44. data/spec/fixtures/vcr_cassettes/api/v1/build_subsets/create/invalid_test_suite_token.yml +0 -50
  45. data/spec/fixtures/vcr_cassettes/api/v1/build_subsets/create/success.yml +0 -50
  46. data/spec/integration/api/build_distributions_subset_spec.rb +0 -77
  47. data/spec/integration/api/build_subsets_create_spec.rb +0 -77
  48. data/spec/integration/runners/fallback_spec.rb +0 -131
  49. data/spec/integration/runners/queue/cucumber_runner_fallback_spec.rb +0 -129
  50. data/spec/integration/runners/queue/minitest_runner_fallback_spec.rb +0 -129
  51. data/spec/integration/runners/queue/rspec_runner.rb +0 -80
  52. data/spec/integration/runners/queue/rspec_runner_fallback_spec.rb +0 -137
  53. data/spec/integration/runners/queue/rspec_runner_spec.rb +0 -2672
  54. data/spec/knapsack_pro/adapters/base_adapter_spec.rb +0 -221
  55. data/spec/knapsack_pro/adapters/cucumber_adapter_spec.rb +0 -216
  56. data/spec/knapsack_pro/adapters/minitest_adapter_spec.rb +0 -189
  57. data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +0 -427
  58. data/spec/knapsack_pro/adapters/spinach_adapter_spec.rb +0 -61
  59. data/spec/knapsack_pro/adapters/test_unit_adapter_spec.rb +0 -86
  60. data/spec/knapsack_pro/base_allocator_builder_spec.rb +0 -102
  61. data/spec/knapsack_pro/build_distribution_fetcher_spec.rb +0 -89
  62. data/spec/knapsack_pro/client/api/action_spec.rb +0 -17
  63. data/spec/knapsack_pro/client/api/v1/base_spec.rb +0 -2
  64. data/spec/knapsack_pro/client/api/v1/build_distributions_spec.rb +0 -95
  65. data/spec/knapsack_pro/client/api/v1/build_subsets_spec.rb +0 -35
  66. data/spec/knapsack_pro/client/api/v1/queues_spec.rb +0 -83
  67. data/spec/knapsack_pro/client/connection_spec.rb +0 -581
  68. data/spec/knapsack_pro/config/ci/app_veyor_spec.rb +0 -87
  69. data/spec/knapsack_pro/config/ci/base_spec.rb +0 -10
  70. data/spec/knapsack_pro/config/ci/buildkite_spec.rb +0 -135
  71. data/spec/knapsack_pro/config/ci/circle_spec.rb +0 -122
  72. data/spec/knapsack_pro/config/ci/cirrus_ci_spec.rb +0 -87
  73. data/spec/knapsack_pro/config/ci/codefresh_spec.rb +0 -81
  74. data/spec/knapsack_pro/config/ci/codeship_spec.rb +0 -80
  75. data/spec/knapsack_pro/config/ci/github_actions_spec.rb +0 -140
  76. data/spec/knapsack_pro/config/ci/gitlab_ci_spec.rb +0 -137
  77. data/spec/knapsack_pro/config/ci/heroku_spec.rb +0 -87
  78. data/spec/knapsack_pro/config/ci/semaphore2_spec.rb +0 -133
  79. data/spec/knapsack_pro/config/ci/semaphore_spec.rb +0 -87
  80. data/spec/knapsack_pro/config/ci/travis_spec.rb +0 -73
  81. data/spec/knapsack_pro/config/env_generator_spec.rb +0 -52
  82. data/spec/knapsack_pro/config/env_spec.rb +0 -1291
  83. data/spec/knapsack_pro/config/temp_files_spec.rb +0 -25
  84. data/spec/knapsack_pro/crypto/branch_encryptor_spec.rb +0 -53
  85. data/spec/knapsack_pro/crypto/decryptor_spec.rb +0 -83
  86. data/spec/knapsack_pro/crypto/digestor_spec.rb +0 -13
  87. data/spec/knapsack_pro/crypto/encryptor_spec.rb +0 -58
  88. data/spec/knapsack_pro/formatters/time_tracker_fetcher_spec.rb +0 -27
  89. data/spec/knapsack_pro/formatters/time_tracker_spec.rb +0 -448
  90. data/spec/knapsack_pro/hooks/queue_spec.rb +0 -199
  91. data/spec/knapsack_pro/presenter_spec.rb +0 -57
  92. data/spec/knapsack_pro/pure/queue/rspec_pure_spec.rb +0 -248
  93. data/spec/knapsack_pro/queue_spec.rb +0 -35
  94. data/spec/knapsack_pro/report_spec.rb +0 -232
  95. data/spec/knapsack_pro/repository_adapter_initiator_spec.rb +0 -21
  96. data/spec/knapsack_pro/repository_adapters/base_adapter_spec.rb +0 -13
  97. data/spec/knapsack_pro/repository_adapters/env_adapter_spec.rb +0 -27
  98. data/spec/knapsack_pro/repository_adapters/git_adapter_spec.rb +0 -106
  99. data/spec/knapsack_pro/runners/base_runner_spec.rb +0 -87
  100. data/spec/knapsack_pro/runners/cucumber_runner_spec.rb +0 -92
  101. data/spec/knapsack_pro/runners/minitest_runner_spec.rb +0 -57
  102. data/spec/knapsack_pro/runners/queue/base_runner_spec.rb +0 -73
  103. data/spec/knapsack_pro/runners/queue/cucumber_runner_spec.rb +0 -209
  104. data/spec/knapsack_pro/runners/queue/minitest_runner_spec.rb +0 -203
  105. data/spec/knapsack_pro/runners/rspec_runner_spec.rb +0 -111
  106. data/spec/knapsack_pro/runners/spinach_runner_spec.rb +0 -56
  107. data/spec/knapsack_pro/runners/test_unit_runner_spec.rb +0 -62
  108. data/spec/knapsack_pro/slow_test_file_determiner_spec.rb +0 -119
  109. data/spec/knapsack_pro/slow_test_file_finder_spec.rb +0 -42
  110. data/spec/knapsack_pro/task_loader_spec.rb +0 -14
  111. data/spec/knapsack_pro/test_case_detectors/rspec_test_example_detector_spec.rb +0 -217
  112. data/spec/knapsack_pro/test_case_mergers/base_merger_spec.rb +0 -27
  113. data/spec/knapsack_pro/test_case_mergers/rspec_merger_spec.rb +0 -81
  114. data/spec/knapsack_pro/test_file_cleaner_spec.rb +0 -11
  115. data/spec/knapsack_pro/test_file_finder_spec.rb +0 -176
  116. data/spec/knapsack_pro/test_file_pattern_spec.rb +0 -49
  117. data/spec/knapsack_pro/test_file_presenter_spec.rb +0 -22
  118. data/spec/knapsack_pro/test_files_with_test_cases_composer_spec.rb +0 -41
  119. data/spec/knapsack_pro/test_flat_distributor_spec.rb +0 -60
  120. data/spec/knapsack_pro/tracker_spec.rb +0 -184
  121. data/spec/knapsack_pro/utils_spec.rb +0 -55
  122. data/spec/knapsack_pro_spec.rb +0 -116
  123. data/spec/spec_helper.rb +0 -47
  124. data/spec/support/.gitkeep +0 -0
  125. data/spec/support/fakes/cucumber.rb +0 -25
  126. data/spec/support/fakes/minitest.rb +0 -18
  127. data/spec/support/shared_examples/adapter.rb +0 -17
  128. data/spec_fake/controllers/users_controller_spec.rb +0 -0
  129. data/spec_fake/models/admin_spec.rb +0 -0
  130. data/spec_fake/models/user_spec.rb +0 -0
  131. data/spec_fake/spec_helper.rb +0 -0
  132. data/spec_time_tracker/spec_helper.rb +0 -29
  133. data/test_fake/a_test.rb +0 -0
  134. data/test_fake/b_test.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f83ec80d7b3dcbd9415335f5f340e48908fbeaeee13e92203cb78df1232f82e
4
- data.tar.gz: 19c08fe18b1555fd1a5d218b32d8baf1534e9cc87b63fc9d17d1c7ddb0129f46
3
+ metadata.gz: 69f4cd183de10604ce425379105354fd37da56929568c6bac466d682dc893088
4
+ data.tar.gz: 3c9c33a0aed5050e6d6582ff66550d8497b833383b1b34c09e622dd8f57bc6b8
5
5
  SHA512:
6
- metadata.gz: d00064e828c1a90a3f2001cbbbee5086f7ac565b6f6ffe015237b6cab12bbdcc48a6a6199f615aaf1ff73ec4bdf4c666c84df44a8e6bc792e4d33387f4c55d71
7
- data.tar.gz: b5dd5757440d7e38a54a44c69fe0c650185ade87dc00b00ebf24242fb637e3a062cdb45b7b85e39eae28547cd8513ad888aa8c6488a3734750a44530c64b6596
6
+ metadata.gz: bac25fed3f1e309e57f21bbce67391ed2d638d8484c91ce99900aa9ab22bbebde61bf0a968e9bf208f45d9d684574cb8a0b9fd549a0f5875c036b4c0e6aeec30
7
+ data.tar.gz: 35f0c05d901993faa07ceeffc211e596b693aa852671e9a04aa12ca845ef78bb39e332b8011b846a93be6cab17f803197f2e7a008fe9f7d9140ba9df75240561
@@ -15,26 +15,10 @@ module KnapsackPro
15
15
  false
16
16
  end
17
17
 
18
- def self.test_file_cases_for(slow_test_files)
18
+ def self.calculate_slow_id_paths
19
19
  raise NotImplementedError
20
20
  end
21
21
 
22
- def self.slow_test_file?(adapter_class, test_file_path)
23
- @slow_test_file_paths ||=
24
- begin
25
- slow_test_files =
26
- if KnapsackPro::Config::Env.slow_test_file_pattern
27
- KnapsackPro::TestFileFinder.slow_test_files_by_pattern(adapter_class)
28
- else
29
- # get slow test files from JSON file based on data from API
30
- KnapsackPro::SlowTestFileDeterminer.read_from_json_report
31
- end
32
- KnapsackPro::TestFilePresenter.paths(slow_test_files)
33
- end
34
- clean_path = KnapsackPro::TestFileCleaner.clean(test_file_path)
35
- @slow_test_file_paths.include?(clean_path)
36
- end
37
-
38
22
  def self.bind
39
23
  adapter = new
40
24
  adapter.bind
@@ -7,19 +7,14 @@ module KnapsackPro
7
7
  @@parent_of_test_dir = nil
8
8
 
9
9
  def self.test_path(obj)
10
- # Pick the first public method in the class itself, that starts with "test_"
11
- test_method_name = obj.public_methods(false).select{|m| m =~ /^test_/ }.first
12
- if test_method_name.nil?
13
- # case for shared examples
14
- method_object = obj.method(obj.location.sub(/.*?test_/, 'test_'))
15
- else
16
- method_object = obj.method(test_method_name)
17
- end
18
- full_test_path = method_object.source_location.first
19
- parent_of_test_dir_regexp = Regexp.new("^#{@@parent_of_test_dir}")
20
- test_path = full_test_path.gsub(parent_of_test_dir_regexp, '.')
21
- # test_path will look like ./test/dir/unit_test.rb
22
- test_path
10
+ path, _line =
11
+ begin
12
+ Object.const_source_location(obj.class.to_s)
13
+ rescue NameError
14
+ test_method_name = obj.class.runnable_methods.first
15
+ obj.method(test_method_name).source_location
16
+ end
17
+ path.gsub(Regexp.new("^#{@@parent_of_test_dir}"), '.')
23
18
  end
24
19
 
25
20
  # See how to write hooks and plugins
@@ -20,22 +20,17 @@ module KnapsackPro
20
20
  true
21
21
  end
22
22
 
23
- def self.test_file_cases_for(slow_test_files)
24
- KnapsackPro.logger.info("Generating RSpec test examples JSON report for slow test files to prepare it to be split by test examples (by individual test cases). Thanks to that, a single slow test file can be split across parallel CI nodes. Analyzing #{slow_test_files.size} slow test files.")
25
-
26
- # generate the RSpec JSON report in a separate process to not pollute the RSpec state
23
+ def self.calculate_slow_id_paths
24
+ # Shell out not to pollute the RSpec state
27
25
  cmd = [
28
26
  'RACK_ENV=test',
29
27
  'RAILS_ENV=test',
30
28
  KnapsackPro::Config::Env.rspec_test_example_detector_prefix,
31
29
  'rake knapsack_pro:rspec_test_example_detector',
32
30
  ].join(' ')
33
- unless Kernel.system(cmd)
34
- raise "Could not generate JSON report for RSpec. Rake task failed when running #{cmd}"
35
- end
31
+ raise "Failed to calculate Split by Test Examples: #{cmd}" unless Kernel.system(cmd)
36
32
 
37
- # read the JSON report
38
- KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector.new.test_file_example_paths
33
+ KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector.new.slow_id_paths!
39
34
  end
40
35
 
41
36
  def self.has_format_option?(cli_args)
@@ -86,6 +81,13 @@ module KnapsackPro
86
81
  !id.nil?
87
82
  end
88
83
 
84
+ def self.concat_paths(test_files, id_paths)
85
+ paths = KnapsackPro::TestFilePresenter.paths(test_files)
86
+ file_paths = id_paths.map { |id_path| parse_file_path(id_path) }
87
+ acc = paths + id_paths - file_paths
88
+ KnapsackPro::TestFilePresenter.test_files(acc)
89
+ end
90
+
89
91
  def self.rails_helper_exists?(test_dir)
90
92
  File.exist?("#{test_dir}/rails_helper.rb")
91
93
  end
@@ -7,10 +7,6 @@ module KnapsackPro
7
7
  @response = response
8
8
  end
9
9
 
10
- def time_execution
11
- response.fetch('time_execution')
12
- end
13
-
14
10
  def test_files
15
11
  response.fetch('test_files')
16
12
  end
@@ -20,12 +16,6 @@ module KnapsackPro
20
16
  attr_reader :response
21
17
  end
22
18
 
23
- def self.call
24
- new.call
25
- end
26
-
27
- # get test files and time execution for last build distribution matching:
28
- # branch, node_total, node_index
29
19
  def call
30
20
  connection = KnapsackPro::Client::Connection.new(build_action)
31
21
  response = connection.call
@@ -33,11 +23,8 @@ module KnapsackPro
33
23
  raise ArgumentError.new(response) if connection.errors?
34
24
  BuildDistributionEntity.new(response)
35
25
  else
36
- KnapsackPro.logger.warn("Slow test files fallback behaviour started. We could not connect with Knapsack Pro API to fetch last CI build test files that are needed to determine slow test files. No test files will be split by test cases. It means all test files will be split by the whole test files as if split by test cases would be disabled #{KnapsackPro::Urls::SPLIT_BY_TEST_EXAMPLES}")
37
- BuildDistributionEntity.new({
38
- 'time_execution' => 0.0,
39
- 'test_files' => [],
40
- })
26
+ KnapsackPro.logger.warn("Failed to fetch slow test files. Split by Test Examples disabled. See: #{KnapsackPro::Urls::SPLIT_BY_TEST_EXAMPLES}")
27
+ BuildDistributionEntity.new({ 'time_execution' => 0.0, 'test_files' => [] })
41
28
  end
42
29
  end
43
30
 
@@ -48,12 +35,29 @@ module KnapsackPro
48
35
  end
49
36
 
50
37
  def build_action
51
- KnapsackPro::Client::API::V1::BuildDistributions.last(
38
+ request_hash = {
52
39
  commit_hash: repository_adapter.commit_hash,
53
40
  branch: repository_adapter.branch,
54
41
  node_total: KnapsackPro::Config::Env.ci_node_total,
55
- node_index: KnapsackPro::Config::Env.ci_node_index,
56
- )
42
+ node_index: KnapsackPro::Config::Env.ci_node_index
43
+ }.merge(additional_params)
44
+
45
+ KnapsackPro::Client::API::V1::BuildDistributions.last(request_hash)
46
+ end
47
+
48
+ def additional_params
49
+ {}
50
+ end
51
+ end
52
+
53
+ class OptimizedBuildDistributionFetcher < BuildDistributionFetcher
54
+ private
55
+
56
+ def additional_params
57
+ {
58
+ node_build_id: KnapsackPro::Config::Env.ci_node_build_id,
59
+ none_if_queue_initialized: true
60
+ }
57
61
  end
58
62
  end
59
63
  end
@@ -35,16 +35,11 @@ module KnapsackPro
35
35
  )
36
36
  end
37
37
 
38
- def last(args)
38
+ def last(request_hash)
39
39
  action_class.new(
40
40
  endpoint_path: '/v1/build_distributions/last',
41
41
  http_method: :get,
42
- request_hash: {
43
- :commit_hash => args.fetch(:commit_hash),
44
- :branch => args.fetch(:branch),
45
- :node_total => args.fetch(:node_total),
46
- :node_index => args.fetch(:node_index),
47
- }
42
+ request_hash: request_hash
48
43
  )
49
44
  end
50
45
  end
@@ -36,6 +36,33 @@ module KnapsackPro
36
36
  request_hash: request_hash
37
37
  )
38
38
  end
39
+
40
+ def initialize(paths)
41
+ git_adapter = KnapsackPro::RepositoryAdapters::GitAdapter.new
42
+ repository_adapter = KnapsackPro::RepositoryAdapterInitiator.call
43
+
44
+ request_hash = {
45
+ attempt_connect_to_queue: false,
46
+ branch: KnapsackPro::Crypto::BranchEncryptor.call(repository_adapter.branch),
47
+ build_author: git_adapter.build_author,
48
+ can_initialize_queue: true,
49
+ commit_authors: git_adapter.commit_authors,
50
+ commit_hash: repository_adapter.commit_hash,
51
+ fixed_queue_split: KnapsackPro::Config::Env.fixed_queue_split,
52
+ node_build_id: KnapsackPro::Config::Env.ci_node_build_id,
53
+ node_index: KnapsackPro::Config::Env.ci_node_index,
54
+ node_total: KnapsackPro::Config::Env.ci_node_total,
55
+ skip_pull: true,
56
+ test_files: KnapsackPro::Crypto::Encryptor.call(paths),
57
+ user_seat: KnapsackPro::Config::Env.masked_user_seat,
58
+ }
59
+
60
+ action_class.new(
61
+ endpoint_path: '/v1/queues/queue',
62
+ http_method: :post,
63
+ request_hash: request_hash
64
+ )
65
+ end
39
66
  end
40
67
  end
41
68
  end
@@ -142,7 +142,7 @@ module KnapsackPro
142
142
  "curl -v #{endpoint_uri.host}:#{endpoint_uri.port}",
143
143
  "nc -vz #{endpoint_uri.host} #{endpoint_uri.port}",
144
144
  "openssl s_client -connect #{endpoint_uri.host}:#{endpoint_uri.port} < /dev/null",
145
- 'env'
145
+ 'env | grep KNAPSACK_PRO | grep -v TOKEN'
146
146
  ].each do |cmd|
147
147
  logger.warn(cmd)
148
148
  logger.warn('=' * cmd.size)
@@ -67,6 +67,14 @@ module KnapsackPro
67
67
  ENV['KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN']
68
68
  end
69
69
 
70
+ def slow_test_file_threshold
71
+ ENV.fetch('KNAPSACK_PRO_SLOW_TEST_FILE_THRESHOLD', nil)&.to_f
72
+ end
73
+
74
+ def slow_test_file_threshold?
75
+ !!slow_test_file_threshold
76
+ end
77
+
70
78
  def test_file_exclude_pattern
71
79
  ENV['KNAPSACK_PRO_TEST_FILE_EXCLUDE_PATTERN']
72
80
  end
@@ -132,19 +140,7 @@ module KnapsackPro
132
140
  end
133
141
 
134
142
  def endpoint
135
- env_name = 'KNAPSACK_PRO_ENDPOINT'
136
- return ENV[env_name] if ENV[env_name]
137
-
138
- case mode
139
- when :development
140
- 'http://api.knapsackpro.test:3000'
141
- when :test
142
- 'https://api-staging.knapsackpro.com'
143
- when :production
144
- 'https://api.knapsackpro.com'
145
- else
146
- required_env(env_name)
147
- end
143
+ ENV.fetch('KNAPSACK_PRO_ENDPOINT', 'https://api.knapsackpro.com')
148
144
  end
149
145
 
150
146
  def fixed_test_suite_split
@@ -200,14 +196,6 @@ module KnapsackPro
200
196
  ENV.fetch('KNAPSACK_PRO_RSPEC_TEST_EXAMPLE_DETECTOR_PREFIX', 'bundle exec')
201
197
  end
202
198
 
203
- def slow_test_file_threshold
204
- ENV.fetch('KNAPSACK_PRO_SLOW_TEST_FILE_THRESHOLD', nil)&.to_f
205
- end
206
-
207
- def slow_test_file_threshold?
208
- !!slow_test_file_threshold
209
- end
210
-
211
199
  def test_suite_token
212
200
  env_name = 'KNAPSACK_PRO_TEST_SUITE_TOKEN'
213
201
  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.")
@@ -233,17 +221,6 @@ module KnapsackPro
233
221
  ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN_SPINACH']
234
222
  end
235
223
 
236
- def mode
237
- mode = ENV['KNAPSACK_PRO_MODE']
238
- return :production if mode.nil?
239
- mode = mode.to_sym
240
- if [:development, :test, :production].include?(mode)
241
- mode
242
- else
243
- raise ArgumentError.new('Wrong mode name')
244
- end
245
- end
246
-
247
224
  def detected_ci
248
225
  detected = KnapsackPro::Config::CI.constants.map do |constant|
249
226
  Object.const_get("KnapsackPro::Config::CI::#{constant}").new.detected
@@ -141,8 +141,9 @@ module KnapsackPro
141
141
  end
142
142
  end
143
143
 
144
+ # Run file paths to guarantee at-least-once execution across CI nodes.
144
145
  def fallback_test_files(executed_test_files)
145
- test_flat_distributor = KnapsackPro::TestFlatDistributor.new(test_suite.fallback_test_files, ci_node_total)
146
+ test_flat_distributor = KnapsackPro::TestFlatDistributor.new(test_suite.all_test_files_to_run, ci_node_total)
146
147
  test_files_for_node_index = test_flat_distributor.test_files_for_node(ci_node_index)
147
148
  KnapsackPro::TestFilePresenter.paths(test_files_for_node_index) - executed_test_files
148
149
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KnapsackPro
4
+ module RSpec
5
+ class QueueInitializer
6
+ def call(args)
7
+ slow_id_paths = KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector.new.calculate_slow_id_paths(args.to_s)
8
+ all_test_files_to_run = KnapsackPro::TestSuite.new(KnapsackPro::Adapters::RSpecAdapter).all_test_files_to_run
9
+ paths = KnapsackPro::Adapters::RSpecAdapter.concat_paths(all_test_files_to_run, slow_id_paths)
10
+
11
+ raise 'No paths to run' if paths.empty?
12
+ action = KnapsackPro::Client::API::V1::Queues.initialize(paths)
13
+ connection = KnapsackPro::Client::Connection.new(action)
14
+ response = connection.call
15
+ return unless response.key?('url') # Race to initialize lost to another parallel node
16
+
17
+ KnapsackPro.logger.info "Build URL: #{response.fetch('url')}"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -131,8 +131,9 @@ module KnapsackPro
131
131
  end
132
132
  end
133
133
 
134
+ # Run file paths to guarantee at-least-once execution across CI nodes.
134
135
  def fallback_test_files
135
- test_flat_distributor = KnapsackPro::TestFlatDistributor.new(test_suite.fallback_test_files, ci_node_total)
136
+ test_flat_distributor = KnapsackPro::TestFlatDistributor.new(test_suite.all_test_files_to_run, ci_node_total)
136
137
  test_files_for_node_index = test_flat_distributor.test_files_for_node(ci_node_index)
137
138
  KnapsackPro::TestFilePresenter.paths(test_files_for_node_index)
138
139
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KnapsackPro
4
+ class RSpecSlowTestFileFinder
5
+ def initialize(build_distribution_fetcher)
6
+ @build_distribution_fetcher = build_distribution_fetcher
7
+ end
8
+
9
+ def call
10
+ if KnapsackPro::Config::Env.test_files_encrypted?
11
+ raise "Split by test cases is not possible when you have enabled test file names encryption ( #{KnapsackPro::Urls::ENCRYPTION} ). You need to disable encryption with KNAPSACK_PRO_TEST_FILES_ENCRYPTED=false in order to use split by test cases #{KnapsackPro::Urls::SPLIT_BY_TEST_EXAMPLES}"
12
+ end
13
+
14
+ test_files_from_api = @build_distribution_fetcher.call.test_files
15
+ merged_test_files_from_api = KnapsackPro::TestCaseMergers::RSpecMerger.new(test_files_from_api).call
16
+ test_files_existing_on_disk = KnapsackPro::TestFileFinder.select_test_files_that_can_be_run(KnapsackPro::Adapters::RSpecAdapter, merged_test_files_from_api)
17
+ KnapsackPro::SlowTestFileDeterminer.call(test_files_existing_on_disk)
18
+ end
19
+ end
20
+ end
@@ -18,27 +18,5 @@ module KnapsackPro
18
18
  execution_time >= KnapsackPro::Config::Env.slow_test_file_threshold
19
19
  end
20
20
  end
21
-
22
- def self.save_to_json_report(test_files)
23
- KnapsackPro::Config::TempFiles.ensure_temp_directory_exists!
24
- FileUtils.mkdir_p(report_dir)
25
- File.write(report_path, test_files.to_json)
26
- end
27
-
28
- def self.read_from_json_report
29
- raise "The report with slow test files has not been generated yet. If you have enabled split by test cases #{KnapsackPro::Urls::SPLIT_BY_TEST_EXAMPLES} and you see this error it means that your tests accidentally cleaned up the .knapsack_pro directory. Please do not remove this directory during tests runtime!" unless File.exist?(report_path)
30
- slow_test_files_json_report = File.read(report_path)
31
- JSON.parse(slow_test_files_json_report)
32
- end
33
-
34
- private
35
-
36
- def self.report_path
37
- "#{report_dir}/slow_test_files_node_#{KnapsackPro::Config::Env.ci_node_index}.json"
38
- end
39
-
40
- def self.report_dir
41
- "#{KnapsackPro::Config::TempFiles::TEMP_DIRECTORY_PATH}/slow_test_file_determiner"
42
- end
43
21
  end
44
22
  end
@@ -3,87 +3,80 @@
3
3
  module KnapsackPro
4
4
  module TestCaseDetectors
5
5
  class RSpecTestExampleDetector
6
- def generate_json_report(rspec_args)
7
- raise "The internal KNAPSACK_PRO_RSPEC_OPTIONS environment variable is unset. Ensure it is not overridden accidentally. Otherwise, please report this as a bug: #{KnapsackPro::Urls::SUPPORT}" if rspec_args.nil?
8
-
9
- require 'rspec/core'
10
-
11
- cli_format =
12
- if Gem::Version.new(::RSpec::Core::Version::STRING) < Gem::Version.new('3.6.0')
13
- require_relative '../formatters/rspec_json_formatter'
14
- ['--format', KnapsackPro::Formatters::RSpecJsonFormatter.to_s]
15
- else
16
- ['--format', 'json']
17
- end
18
-
19
- ensure_report_dir_exists
20
- remove_old_json_report
21
-
22
- test_file_entities = slow_test_files
23
-
24
- if test_file_entities.empty?
25
- no_examples_json = { examples: [] }.to_json
26
- File.write(report_path, no_examples_json)
27
- return
28
- end
6
+ def dry_run_to_file(rspec_args, slow_test_files = slow_test_files(KnapsackPro::BuildDistributionFetcher.new))
7
+ KnapsackPro::Config::TempFiles.ensure_temp_directory_exists!
8
+ FileUtils.mkdir_p(File.dirname(report_path))
9
+ File.delete(report_path) if File.exist?(report_path)
10
+ return File.write(report_path, { examples: [] }.to_json) if slow_test_files.empty?
29
11
 
12
+ KnapsackPro.logger.info("Calculating Split by Test Examples. Analyzing #{slow_test_files.size} slow test files.")
30
13
  args = (rspec_args || '').split
31
14
  cli_args_without_formatters = KnapsackPro::Adapters::RSpecAdapter.remove_formatters(args)
32
-
33
- # Apply a --format option which overrides formatters from the RSpec custom option files like `.rspec`.
34
15
  cli_args = cli_args_without_formatters + cli_format + [
35
16
  '--dry-run',
36
17
  '--out', report_path,
37
18
  '--default-path', test_dir
38
- ] + KnapsackPro::TestFilePresenter.paths(test_file_entities)
39
- exit_code = begin
40
- options = ::RSpec::Core::ConfigurationOptions.new(cli_args)
41
- ::RSpec::Core::Runner.new(options).run($stderr, $stdout)
42
- rescue SystemExit => e
43
- e.status
44
- end
45
-
19
+ ] + KnapsackPro::TestFilePresenter.paths(slow_test_files)
20
+ exit_code = dry_run(cli_args)
46
21
  return if exit_code.zero?
47
22
 
48
23
  report.fetch('messages', []).each { |message| puts message }
49
24
  command = (['bundle exec rspec'] + cli_args).join(' ')
50
- KnapsackPro.logger.error("Failed to generate the slow test files report: #{command}")
25
+ KnapsackPro.logger.error("Failed to calculate Split by Test Examples: #{command}")
51
26
  exit exit_code
52
27
  end
53
28
 
54
- def test_file_example_paths
29
+ def calculate_slow_id_paths(rspec_args)
30
+ dry_run_to_file(rspec_args, slow_test_files(KnapsackPro::OptimizedBuildDistributionFetcher.new))
31
+ slow_id_paths!
32
+ end
33
+
34
+ def slow_id_paths!
55
35
  raise "No report found at #{report_path}" unless File.exist?(report_path)
56
36
 
57
- json_report = File.read(report_path)
58
- hash_report = JSON.parse(json_report)
59
- hash_report
37
+ JSON.parse(File.read(report_path))
60
38
  .fetch('examples')
61
- .map { |e| e.fetch('id') }
62
- .map { |path_with_example_id| test_file_hash_for(path_with_example_id) }
39
+ .map { |example| TestFileCleaner.clean(example.fetch('id')) }
63
40
  end
64
41
 
65
- def slow_test_files
66
- if KnapsackPro::Config::Env.slow_test_file_pattern
67
- KnapsackPro::TestFileFinder.slow_test_files_by_pattern(adapter_class)
42
+ private
43
+
44
+ # Apply a --format option which overrides formatters from the RSpec custom option files like `.rspec`.
45
+ def cli_format
46
+ require 'rspec/core'
47
+
48
+ if Gem::Version.new(::RSpec::Core::Version::STRING) < Gem::Version.new('3.6.0')
49
+ require_relative '../formatters/rspec_json_formatter'
50
+ ['--format', KnapsackPro::Formatters::RSpecJsonFormatter.to_s]
68
51
  else
69
- # read slow test files from JSON file on disk that was generated
70
- # by lib/knapsack_pro/base_allocator_builder.rb
71
- KnapsackPro::SlowTestFileDeterminer.read_from_json_report
52
+ ['--format', 'json']
72
53
  end
73
54
  end
74
55
 
75
- private
56
+ def dry_run(cli_args)
57
+ require 'rspec/core'
76
58
 
77
- def report_dir
78
- "#{KnapsackPro::Config::TempFiles::TEMP_DIRECTORY_PATH}/test_case_detectors/rspec"
59
+ options = ::RSpec::Core::ConfigurationOptions.new(cli_args)
60
+ ::RSpec::Core::Runner.new(options).run($stderr, $stdout)
61
+ rescue SystemExit => e
62
+ e.status
79
63
  end
80
64
 
81
65
  def report_path
82
- "#{report_dir}/rspec_dry_run_json_report_node_#{KnapsackPro::Config::Env.ci_node_index}.json"
66
+ "#{KnapsackPro::Config::TempFiles::TEMP_DIRECTORY_PATH}/test_case_detectors/rspec/rspec_dry_run_json_report_node_#{KnapsackPro::Config::Env.ci_node_index}.json"
67
+ end
68
+
69
+ def slow_test_files(build_distribution_fetcher)
70
+ if KnapsackPro::Config::Env.slow_test_file_pattern
71
+ KnapsackPro::TestFileFinder.slow_test_files_by_pattern(adapter_class)
72
+ else
73
+ KnapsackPro::RSpecSlowTestFileFinder.new(build_distribution_fetcher).call
74
+ end
83
75
  end
84
76
 
85
77
  def report
86
78
  return {} unless File.exist?(report_path)
79
+
87
80
  JSON.parse(File.read(report_path))
88
81
  end
89
82
 
@@ -94,25 +87,6 @@ module KnapsackPro
94
87
  def test_dir
95
88
  KnapsackPro::Config::Env.test_dir || KnapsackPro::TestFilePattern.test_dir(adapter_class)
96
89
  end
97
-
98
- def test_file_pattern
99
- KnapsackPro::TestFilePattern.call(adapter_class)
100
- end
101
-
102
- def ensure_report_dir_exists
103
- KnapsackPro::Config::TempFiles.ensure_temp_directory_exists!
104
- FileUtils.mkdir_p(report_dir)
105
- end
106
-
107
- def remove_old_json_report
108
- File.delete(report_path) if File.exist?(report_path)
109
- end
110
-
111
- def test_file_hash_for(test_file_path)
112
- {
113
- 'path' => TestFileCleaner.clean(test_file_path)
114
- }
115
- end
116
90
  end
117
91
  end
118
92
  end
@@ -2,44 +2,32 @@
2
2
 
3
3
  module KnapsackPro
4
4
  module TestCaseMergers
5
- class RSpecMerger < BaseMerger
5
+ class RSpecMerger
6
+ def initialize(test_files)
7
+ @test_files = test_files
8
+ end
9
+
6
10
  def call
7
- all_test_files_hash = {}
8
- merged_test_file_examples_hash = {}
11
+ file_paths = {}
12
+ id_paths = {}
9
13
 
10
- test_files.each do |test_file|
11
- path = test_file.fetch('path')
12
- test_file_path = extract_test_file_path(path)
14
+ @test_files.each do |test_file|
15
+ raw_path = test_file.fetch('path')
16
+ file_path = KnapsackPro::Adapters::RSpecAdapter.parse_file_path(raw_path)
13
17
 
14
- if KnapsackPro::Adapters::RSpecAdapter.id_path?(path)
15
- merged_test_file_examples_hash[test_file_path] ||= 0.0
16
- merged_test_file_examples_hash[test_file_path] += test_file.fetch('time_execution')
18
+ if KnapsackPro::Adapters::RSpecAdapter.id_path?(raw_path)
19
+ id_paths[file_path] ||= 0.0
20
+ id_paths[file_path] += test_file.fetch('time_execution')
17
21
  else
18
- all_test_files_hash[test_file_path] = test_file.fetch('time_execution')
22
+ file_paths[file_path] = test_file.fetch('time_execution') # may be nil
19
23
  end
20
24
  end
21
25
 
22
- merged_test_file_examples_hash.each do |path, time_execution|
23
- all_test_files_hash[path] = [time_execution, all_test_files_hash[path]].compact.max
24
- end
25
-
26
- merged_test_files = []
27
- all_test_files_hash.each do |path, time_execution|
28
- merged_test_files << {
29
- 'path' => path,
30
- 'time_execution' => time_execution
31
- }
32
- end
33
- merged_test_files
34
- end
35
-
36
- private
37
-
38
- # path - can be:
39
- # test file path: spec/a_spec.rb
40
- # or test example path: spec/a_spec.rb[1:1]
41
- def extract_test_file_path(path)
42
- path.gsub(/\.rb\[.+\]$/, '.rb')
26
+ file_paths
27
+ .merge(id_paths) { |_, v1, v2| [v1, v2].compact.max }
28
+ .map do |file_path, time_execution|
29
+ { 'path' => file_path, 'time_execution' => time_execution }
30
+ end
43
31
  end
44
32
  end
45
33
  end