knapsack_pro 9.1.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.
- checksums.yaml +4 -4
- data/lib/knapsack_pro/adapters/base_adapter.rb +1 -17
- data/lib/knapsack_pro/adapters/rspec_adapter.rb +11 -9
- data/lib/knapsack_pro/build_distribution_fetcher.rb +22 -18
- data/lib/knapsack_pro/client/api/v1/build_distributions.rb +2 -7
- data/lib/knapsack_pro/client/api/v1/queues.rb +27 -0
- data/lib/knapsack_pro/config/env.rb +8 -8
- data/lib/knapsack_pro/queue_allocator.rb +2 -1
- data/lib/knapsack_pro/queue_initializer.rb +21 -0
- data/lib/knapsack_pro/regular_allocator.rb +2 -1
- data/lib/knapsack_pro/rspec_slow_test_file_finder.rb +20 -0
- data/lib/knapsack_pro/slow_test_file_determiner.rb +0 -22
- data/lib/knapsack_pro/test_case_detectors/rspec_test_example_detector.rb +43 -69
- data/lib/knapsack_pro/test_case_mergers/rspec_merger.rb +19 -31
- data/lib/knapsack_pro/test_file_finder.rb +15 -42
- data/lib/knapsack_pro/test_file_presenter.rb +4 -0
- data/lib/knapsack_pro/test_suite.rb +8 -35
- data/lib/knapsack_pro/version.rb +1 -1
- data/lib/knapsack_pro.rb +1 -3
- data/lib/tasks/queue/rspec.rake +12 -0
- data/lib/tasks/rspec.rake +8 -4
- metadata +8 -9
- data/lib/knapsack_pro/slow_test_file_finder.rb +0 -29
- data/lib/knapsack_pro/test_case_mergers/base_merger.rb +0 -31
- data/lib/knapsack_pro/test_files_with_test_cases_composer.rb +0 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 69f4cd183de10604ce425379105354fd37da56929568c6bac466d682dc893088
|
|
4
|
+
data.tar.gz: 3c9c33a0aed5050e6d6582ff66550d8497b833383b1b34c09e622dd8f57bc6b8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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
|
|
@@ -20,22 +20,17 @@ module KnapsackPro
|
|
|
20
20
|
true
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def self.
|
|
24
|
-
|
|
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
|
-
|
|
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("
|
|
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
|
-
|
|
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(
|
|
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
|
|
@@ -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
|
|
@@ -188,14 +196,6 @@ module KnapsackPro
|
|
|
188
196
|
ENV.fetch('KNAPSACK_PRO_RSPEC_TEST_EXAMPLE_DETECTOR_PREFIX', 'bundle exec')
|
|
189
197
|
end
|
|
190
198
|
|
|
191
|
-
def slow_test_file_threshold
|
|
192
|
-
ENV.fetch('KNAPSACK_PRO_SLOW_TEST_FILE_THRESHOLD', nil)&.to_f
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
def slow_test_file_threshold?
|
|
196
|
-
!!slow_test_file_threshold
|
|
197
|
-
end
|
|
198
|
-
|
|
199
199
|
def test_suite_token
|
|
200
200
|
env_name = 'KNAPSACK_PRO_TEST_SUITE_TOKEN'
|
|
201
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.")
|
|
@@ -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.
|
|
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.
|
|
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
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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(
|
|
39
|
-
exit_code =
|
|
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
|
|
25
|
+
KnapsackPro.logger.error("Failed to calculate Split by Test Examples: #{command}")
|
|
51
26
|
exit exit_code
|
|
52
27
|
end
|
|
53
28
|
|
|
54
|
-
def
|
|
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
|
-
|
|
58
|
-
hash_report = JSON.parse(json_report)
|
|
59
|
-
hash_report
|
|
37
|
+
JSON.parse(File.read(report_path))
|
|
60
38
|
.fetch('examples')
|
|
61
|
-
.map { |
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
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
|
-
|
|
56
|
+
def dry_run(cli_args)
|
|
57
|
+
require 'rspec/core'
|
|
76
58
|
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
"#{
|
|
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
|
|
5
|
+
class RSpecMerger
|
|
6
|
+
def initialize(test_files)
|
|
7
|
+
@test_files = test_files
|
|
8
|
+
end
|
|
9
|
+
|
|
6
10
|
def call
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
file_paths = {}
|
|
12
|
+
id_paths = {}
|
|
9
13
|
|
|
10
|
-
test_files.each do |test_file|
|
|
11
|
-
|
|
12
|
-
|
|
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?(
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
22
|
+
file_paths[file_path] = test_file.fetch('time_execution') # may be nil
|
|
19
23
|
end
|
|
20
24
|
end
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
@@ -6,41 +6,22 @@ module KnapsackPro
|
|
|
6
6
|
new(test_file_pattern, test_file_list_enabled).call
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
# finds slow test files on disk based on ENV patterns
|
|
10
|
-
# returns example: [{ 'path' => 'a_spec.rb' }]
|
|
11
9
|
def self.slow_test_files_by_pattern(adapter_class)
|
|
12
10
|
raise 'KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN is not defined' unless KnapsackPro::Config::Env.slow_test_file_pattern
|
|
13
11
|
|
|
14
12
|
test_file_pattern = KnapsackPro::TestFilePattern.call(adapter_class)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# slow test files (KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN)
|
|
20
|
-
# should be subset of test file pattern (KNAPSACK_PRO_TEST_FILE_PATTERN)
|
|
21
|
-
slow_test_file_entities & test_file_entities
|
|
13
|
+
scheduled_test_files = call(test_file_pattern)
|
|
14
|
+
slow_test_files = call(KnapsackPro::Config::Env.slow_test_file_pattern, test_file_list_enabled: false)
|
|
15
|
+
scheduled_test_files & slow_test_files
|
|
22
16
|
end
|
|
23
17
|
|
|
24
|
-
|
|
25
|
-
# test_file_entities_to_run - it can be list of slow test files that you want to run
|
|
26
|
-
# Return:
|
|
27
|
-
# subset of test_file_entities_to_run that are present on disk and it is subset of tests matching pattern KNAPSACK_PRO_TEST_FILE_PATTERN
|
|
28
|
-
# Thanks to that we can select only slow test files that are within list of test file pattern we want to run tests for
|
|
29
|
-
def self.select_test_files_that_can_be_run(adapter_class, test_file_entities_to_run)
|
|
18
|
+
def self.select_test_files_that_can_be_run(adapter_class, candidate_test_files)
|
|
30
19
|
test_file_pattern = KnapsackPro::TestFilePattern.call(adapter_class)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
test_file_entities_to_run.each do |test_file_entity|
|
|
38
|
-
if test_file_paths_existing_on_disk.include?(test_file_entity.fetch('path'))
|
|
39
|
-
selected_test_files << test_file_entity
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
selected_test_files
|
|
20
|
+
scheduled_test_files = call(test_file_pattern)
|
|
21
|
+
scheduled_paths = KnapsackPro::TestFilePresenter.paths(scheduled_test_files)
|
|
22
|
+
candidate_paths = KnapsackPro::TestFilePresenter.paths(candidate_test_files)
|
|
23
|
+
paths = scheduled_paths & candidate_paths
|
|
24
|
+
candidate_test_files.filter { |test_file| paths.include? test_file.fetch('path') }
|
|
44
25
|
end
|
|
45
26
|
|
|
46
27
|
def initialize(test_file_pattern, test_file_list_enabled)
|
|
@@ -49,18 +30,16 @@ module KnapsackPro
|
|
|
49
30
|
end
|
|
50
31
|
|
|
51
32
|
def call
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
test_file_hashes << test_file_hash_for(test_file_path)
|
|
33
|
+
file_paths.map do |file_path|
|
|
34
|
+
{ 'path' => TestFileCleaner.clean(file_path) }
|
|
55
35
|
end
|
|
56
|
-
test_file_hashes
|
|
57
36
|
end
|
|
58
37
|
|
|
59
38
|
private
|
|
60
39
|
|
|
61
40
|
attr_reader :test_file_pattern, :test_file_list_enabled
|
|
62
41
|
|
|
63
|
-
def
|
|
42
|
+
def file_paths
|
|
64
43
|
if test_file_list_enabled && KnapsackPro::Config::Env.test_file_list
|
|
65
44
|
return KnapsackPro::Config::Env.test_file_list.split(',').map(&:strip)
|
|
66
45
|
end
|
|
@@ -69,22 +48,16 @@ module KnapsackPro
|
|
|
69
48
|
return File.read(KnapsackPro::Config::Env.test_file_list_source_file).split(/\n/)
|
|
70
49
|
end
|
|
71
50
|
|
|
72
|
-
|
|
51
|
+
included_paths = Dir.glob(test_file_pattern).uniq
|
|
73
52
|
|
|
74
|
-
|
|
53
|
+
excluded_paths =
|
|
75
54
|
if KnapsackPro::Config::Env.test_file_exclude_pattern
|
|
76
55
|
Dir.glob(KnapsackPro::Config::Env.test_file_exclude_pattern).uniq
|
|
77
56
|
else
|
|
78
57
|
[]
|
|
79
58
|
end
|
|
80
59
|
|
|
81
|
-
(
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def test_file_hash_for(test_file_path)
|
|
85
|
-
{
|
|
86
|
-
'path' => TestFileCleaner.clean(test_file_path)
|
|
87
|
-
}
|
|
60
|
+
(included_paths - excluded_paths).sort
|
|
88
61
|
end
|
|
89
62
|
end
|
|
90
63
|
end
|
|
@@ -8,8 +8,6 @@ module KnapsackPro
|
|
|
8
8
|
@adapter_class = adapter_class
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
# Detect test files present on the disk that should be run.
|
|
12
|
-
# This may include fast test files + slow test files split by test cases.
|
|
13
11
|
def calculate_test_files
|
|
14
12
|
return @result if defined?(@result)
|
|
15
13
|
|
|
@@ -17,47 +15,22 @@ module KnapsackPro
|
|
|
17
15
|
return @result = Result.new(all_test_files_to_run, true)
|
|
18
16
|
end
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
else
|
|
24
|
-
[KnapsackPro::SlowTestFileFinder.call(adapter_class), false]
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
KnapsackPro.logger.debug("Detected #{slow_test_files.size} slow test files: #{slow_test_files.inspect}")
|
|
28
|
-
|
|
29
|
-
if slow_test_files.empty?
|
|
30
|
-
return @result = Result.new(all_test_files_to_run, quick)
|
|
18
|
+
if KnapsackPro::Config::Env.slow_test_file_pattern
|
|
19
|
+
slow_test_files = KnapsackPro::TestFileFinder.slow_test_files_by_pattern(adapter_class)
|
|
20
|
+
return @result = Result.new(all_test_files_to_run, true) if slow_test_files.empty?
|
|
31
21
|
end
|
|
32
22
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
@result = Result.new(fast_files_and_cases_for_slow_tests, false)
|
|
23
|
+
slow_id_paths = adapter_class.calculate_slow_id_paths
|
|
24
|
+
test_files = adapter_class.concat_paths(all_test_files_to_run, slow_id_paths)
|
|
25
|
+
@result = Result.new(test_files, false)
|
|
38
26
|
end
|
|
39
27
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# across parallel CI nodes.
|
|
43
|
-
def fallback_test_files
|
|
44
|
-
all_test_files_to_run
|
|
28
|
+
def all_test_files_to_run
|
|
29
|
+
@all_test_files_to_run ||= KnapsackPro::TestFileFinder.call(TestFilePattern.call(adapter_class))
|
|
45
30
|
end
|
|
46
31
|
|
|
47
32
|
private
|
|
48
33
|
|
|
49
34
|
attr_reader :adapter_class
|
|
50
|
-
|
|
51
|
-
def all_test_files_to_run
|
|
52
|
-
@all_test_files_to_run ||= KnapsackPro::TestFileFinder.call(test_file_pattern)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def test_file_pattern
|
|
56
|
-
TestFilePattern.call(adapter_class)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def slow_test_file_pattern
|
|
60
|
-
KnapsackPro::Config::Env.slow_test_file_pattern
|
|
61
|
-
end
|
|
62
35
|
end
|
|
63
36
|
end
|
data/lib/knapsack_pro/version.rb
CHANGED
data/lib/knapsack_pro.rb
CHANGED
|
@@ -58,12 +58,10 @@ require_relative 'knapsack_pro/regular_allocator'
|
|
|
58
58
|
require_relative 'knapsack_pro/queue_allocator'
|
|
59
59
|
require_relative 'knapsack_pro/mask_string'
|
|
60
60
|
require_relative 'knapsack_pro/test_suite'
|
|
61
|
-
require_relative 'knapsack_pro/test_case_mergers/base_merger'
|
|
62
61
|
require_relative 'knapsack_pro/test_case_mergers/rspec_merger'
|
|
63
62
|
require_relative 'knapsack_pro/build_distribution_fetcher'
|
|
64
63
|
require_relative 'knapsack_pro/slow_test_file_determiner'
|
|
65
|
-
require_relative 'knapsack_pro/
|
|
66
|
-
require_relative 'knapsack_pro/test_files_with_test_cases_composer'
|
|
64
|
+
require_relative 'knapsack_pro/rspec_slow_test_file_finder'
|
|
67
65
|
require_relative 'knapsack_pro/base_allocator_builder'
|
|
68
66
|
require_relative 'knapsack_pro/regular_allocator_builder'
|
|
69
67
|
require_relative 'knapsack_pro/queue_allocator_builder'
|
data/lib/tasks/queue/rspec.rake
CHANGED
|
@@ -12,5 +12,17 @@ namespace :knapsack_pro do
|
|
|
12
12
|
Rake::Task.clear
|
|
13
13
|
KnapsackPro::Runners::Queue::RSpecRunner.run(args[:rspec_args])
|
|
14
14
|
end
|
|
15
|
+
|
|
16
|
+
namespace :rspec do
|
|
17
|
+
desc 'Initialize the test queue to be consumed later.'
|
|
18
|
+
task :initialize, [:rspec_args] do |_, args|
|
|
19
|
+
require_relative '../../knapsack_pro/queue_initializer'
|
|
20
|
+
|
|
21
|
+
ENV.delete('SPEC_OPTS') # Ignore `SPEC_OPTS` to not affect the RSpec execution within this rake task
|
|
22
|
+
ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN'] = KnapsackPro::Config::Env.test_suite_token_rspec
|
|
23
|
+
|
|
24
|
+
KnapsackPro::RSpec::QueueInitializer.new.call(args[:rspec_args].to_s)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
15
27
|
end
|
|
16
28
|
end
|
data/lib/tasks/rspec.rake
CHANGED
|
@@ -7,12 +7,16 @@ namespace :knapsack_pro do
|
|
|
7
7
|
KnapsackPro::Runners::RSpecRunner.run(args[:rspec_args])
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
# private
|
|
11
11
|
task :rspec_test_example_detector do
|
|
12
|
-
|
|
12
|
+
key = 'KNAPSACK_PRO_RSPEC_OPTIONS'
|
|
13
|
+
raise "The internal #{key} environment variable is unset. Ensure it is not overridden accidentally. Otherwise, please report this as a bug: #{KnapsackPro::Urls::SUPPORT}" if ENV[key].nil?
|
|
14
|
+
|
|
15
|
+
# Ignore `SPEC_OPTS` to not affect the RSpec execution within this rake task
|
|
13
16
|
ENV.delete('SPEC_OPTS')
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector
|
|
19
|
+
.new
|
|
20
|
+
.dry_run_to_file(ENV[key])
|
|
17
21
|
end
|
|
18
22
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: knapsack_pro
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 9.
|
|
4
|
+
version: 9.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ArturT
|
|
@@ -111,16 +111,16 @@ dependencies:
|
|
|
111
111
|
name: rspec-its
|
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
|
113
113
|
requirements:
|
|
114
|
-
- - "
|
|
114
|
+
- - ">="
|
|
115
115
|
- !ruby/object:Gem::Version
|
|
116
|
-
version: '
|
|
116
|
+
version: '0'
|
|
117
117
|
type: :development
|
|
118
118
|
prerelease: false
|
|
119
119
|
version_requirements: !ruby/object:Gem::Requirement
|
|
120
120
|
requirements:
|
|
121
|
-
- - "
|
|
121
|
+
- - ">="
|
|
122
122
|
- !ruby/object:Gem::Version
|
|
123
|
-
version: '
|
|
123
|
+
version: '0'
|
|
124
124
|
- !ruby/object:Gem::Dependency
|
|
125
125
|
name: spinach
|
|
126
126
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -250,6 +250,7 @@ files:
|
|
|
250
250
|
- lib/knapsack_pro/queue.rb
|
|
251
251
|
- lib/knapsack_pro/queue_allocator.rb
|
|
252
252
|
- lib/knapsack_pro/queue_allocator_builder.rb
|
|
253
|
+
- lib/knapsack_pro/queue_initializer.rb
|
|
253
254
|
- lib/knapsack_pro/railtie.rb
|
|
254
255
|
- lib/knapsack_pro/regular_allocator.rb
|
|
255
256
|
- lib/knapsack_pro/regular_allocator_builder.rb
|
|
@@ -258,6 +259,7 @@ files:
|
|
|
258
259
|
- lib/knapsack_pro/repository_adapters/base_adapter.rb
|
|
259
260
|
- lib/knapsack_pro/repository_adapters/env_adapter.rb
|
|
260
261
|
- lib/knapsack_pro/repository_adapters/git_adapter.rb
|
|
262
|
+
- lib/knapsack_pro/rspec_slow_test_file_finder.rb
|
|
261
263
|
- lib/knapsack_pro/runners/base_runner.rb
|
|
262
264
|
- lib/knapsack_pro/runners/cucumber_runner.rb
|
|
263
265
|
- lib/knapsack_pro/runners/minitest_runner.rb
|
|
@@ -269,16 +271,13 @@ files:
|
|
|
269
271
|
- lib/knapsack_pro/runners/spinach_runner.rb
|
|
270
272
|
- lib/knapsack_pro/runners/test_unit_runner.rb
|
|
271
273
|
- lib/knapsack_pro/slow_test_file_determiner.rb
|
|
272
|
-
- lib/knapsack_pro/slow_test_file_finder.rb
|
|
273
274
|
- lib/knapsack_pro/task_loader.rb
|
|
274
275
|
- lib/knapsack_pro/test_case_detectors/rspec_test_example_detector.rb
|
|
275
|
-
- lib/knapsack_pro/test_case_mergers/base_merger.rb
|
|
276
276
|
- lib/knapsack_pro/test_case_mergers/rspec_merger.rb
|
|
277
277
|
- lib/knapsack_pro/test_file_cleaner.rb
|
|
278
278
|
- lib/knapsack_pro/test_file_finder.rb
|
|
279
279
|
- lib/knapsack_pro/test_file_pattern.rb
|
|
280
280
|
- lib/knapsack_pro/test_file_presenter.rb
|
|
281
|
-
- lib/knapsack_pro/test_files_with_test_cases_composer.rb
|
|
282
281
|
- lib/knapsack_pro/test_flat_distributor.rb
|
|
283
282
|
- lib/knapsack_pro/test_suite.rb
|
|
284
283
|
- lib/knapsack_pro/tracker.rb
|
|
@@ -319,7 +318,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
319
318
|
- !ruby/object:Gem::Version
|
|
320
319
|
version: '0'
|
|
321
320
|
requirements: []
|
|
322
|
-
rubygems_version:
|
|
321
|
+
rubygems_version: 4.0.3
|
|
323
322
|
specification_version: 4
|
|
324
323
|
summary: Knapsack Pro splits tests across parallel CI nodes and ensures each parallel
|
|
325
324
|
job finish work at a similar time.
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module KnapsackPro
|
|
4
|
-
class SlowTestFileFinder
|
|
5
|
-
# Get recorded test files from API.
|
|
6
|
-
# Find slow tests among them that are still present on the disk.
|
|
7
|
-
# Save slow test files in json file on the disk.
|
|
8
|
-
# Returns slow test files.
|
|
9
|
-
def self.call(adapter_class)
|
|
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
|
-
# get list of recorded test files for last CI Build
|
|
15
|
-
build_distribution_entity = KnapsackPro::BuildDistributionFetcher.call
|
|
16
|
-
test_files_from_api = build_distribution_entity.test_files
|
|
17
|
-
|
|
18
|
-
merged_test_files_from_api = KnapsackPro::TestCaseMergers::BaseMerger.call(adapter_class, test_files_from_api)
|
|
19
|
-
|
|
20
|
-
test_files_existing_on_disk = KnapsackPro::TestFileFinder.select_test_files_that_can_be_run(adapter_class, merged_test_files_from_api)
|
|
21
|
-
|
|
22
|
-
slow_test_files = KnapsackPro::SlowTestFileDeterminer.call(test_files_existing_on_disk)
|
|
23
|
-
|
|
24
|
-
KnapsackPro::SlowTestFileDeterminer.save_to_json_report(slow_test_files)
|
|
25
|
-
|
|
26
|
-
slow_test_files
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module KnapsackPro
|
|
4
|
-
module TestCaseMergers
|
|
5
|
-
class BaseMerger
|
|
6
|
-
# values must be string to avoid circular dependency problem during loading files
|
|
7
|
-
ADAPTER_TO_MERGER_MAP = {
|
|
8
|
-
KnapsackPro::Adapters::RSpecAdapter => 'KnapsackPro::TestCaseMergers::RSpecMerger',
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
def self.call(adapter_class, test_files)
|
|
12
|
-
merger_class =
|
|
13
|
-
ADAPTER_TO_MERGER_MAP[adapter_class] ||
|
|
14
|
-
raise("Test case merger does not exist for adapter_class: #{adapter_class}")
|
|
15
|
-
Kernel.const_get(merger_class).new(test_files).call
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def initialize(test_files)
|
|
19
|
-
@test_files = test_files
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def call
|
|
23
|
-
raise NotImplementedError
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
private
|
|
27
|
-
|
|
28
|
-
attr_reader :test_files
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module KnapsackPro
|
|
4
|
-
class TestFilesWithTestCasesComposer
|
|
5
|
-
# Args:
|
|
6
|
-
# All 3 arguments have structure: [{ 'path' => 'spec/a_spec.rb', 'time_execution' => 0.0 }]
|
|
7
|
-
# time_execution is not always present (but it's not relevant here)
|
|
8
|
-
#
|
|
9
|
-
# test_files - list of test files that you want to run tests for
|
|
10
|
-
# slow_test_files - list of slow test files that should be split by test cases
|
|
11
|
-
# test_file_cases - list of paths to test cases (test examples) inside of all slow test files (slow_test_files)
|
|
12
|
-
# Return:
|
|
13
|
-
# Test files and test cases paths (it excludes test files that should be split by test cases (test examples))
|
|
14
|
-
def self.call(test_files, slow_test_files, test_file_cases)
|
|
15
|
-
slow_test_file_paths = KnapsackPro::TestFilePresenter.paths(slow_test_files)
|
|
16
|
-
|
|
17
|
-
test_files_without_slow_test_files = test_files.reject do |test_file|
|
|
18
|
-
slow_test_file_paths.include?(test_file.fetch('path'))
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
test_files_without_slow_test_files + test_file_cases
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|