knapsack_pro 1.20.1 → 1.22.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +45 -0
- data/README.md +42 -17
- data/lib/knapsack_pro.rb +6 -0
- data/lib/knapsack_pro/adapters/base_adapter.rb +16 -0
- data/lib/knapsack_pro/adapters/rspec_adapter.rb +11 -9
- data/lib/knapsack_pro/allocator.rb +7 -5
- data/lib/knapsack_pro/allocator_builder.rb +2 -1
- data/lib/knapsack_pro/base_allocator_builder.rb +41 -10
- data/lib/knapsack_pro/build_distribution_fetcher.rb +57 -0
- data/lib/knapsack_pro/client/api/v1/build_distributions.rb +13 -0
- data/lib/knapsack_pro/client/connection.rb +38 -16
- data/lib/knapsack_pro/config/env.rb +4 -0
- data/lib/knapsack_pro/queue_allocator.rb +7 -5
- data/lib/knapsack_pro/queue_allocator_builder.rb +2 -1
- data/lib/knapsack_pro/report.rb +1 -1
- data/lib/knapsack_pro/runners/queue/cucumber_runner.rb +10 -1
- data/lib/knapsack_pro/runners/queue/rspec_runner.rb +7 -0
- data/lib/knapsack_pro/runners/rspec_runner.rb +5 -2
- data/lib/knapsack_pro/slow_test_file_determiner.rb +33 -0
- data/lib/knapsack_pro/slow_test_file_finder.rb +27 -0
- data/lib/knapsack_pro/test_case_detectors/rspec_test_example_detector.rb +26 -7
- data/lib/knapsack_pro/test_case_mergers/base_merger.rb +29 -0
- data/lib/knapsack_pro/test_case_mergers/rspec_merger.rb +34 -0
- data/lib/knapsack_pro/test_file_finder.rb +43 -5
- data/lib/knapsack_pro/test_files_with_test_cases_composer.rb +22 -0
- data/lib/knapsack_pro/version.rb +1 -1
- data/spec/knapsack_pro/adapters/base_adapter_spec.rb +55 -0
- data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +61 -25
- data/spec/knapsack_pro/allocator_builder_spec.rb +7 -3
- data/spec/knapsack_pro/allocator_spec.rb +7 -5
- data/spec/knapsack_pro/base_allocator_builder_spec.rb +79 -27
- data/spec/knapsack_pro/build_distribution_fetcher_spec.rb +89 -0
- data/spec/knapsack_pro/client/api/v1/build_distributions_spec.rb +31 -0
- data/spec/knapsack_pro/client/connection_spec.rb +235 -104
- data/spec/knapsack_pro/config/env_spec.rb +14 -0
- data/spec/knapsack_pro/queue_allocator_builder_spec.rb +7 -3
- data/spec/knapsack_pro/queue_allocator_spec.rb +7 -5
- data/spec/knapsack_pro/report_spec.rb +1 -1
- data/spec/knapsack_pro/runners/queue/cucumber_runner_spec.rb +38 -25
- data/spec/knapsack_pro/runners/rspec_runner_spec.rb +4 -4
- data/spec/knapsack_pro/slow_test_file_determiner_spec.rb +74 -0
- data/spec/knapsack_pro/slow_test_file_finder_spec.rb +43 -0
- data/spec/knapsack_pro/test_case_detectors/rspec_test_example_detector_spec.rb +83 -37
- data/spec/knapsack_pro/test_case_mergers/base_merger_spec.rb +27 -0
- data/spec/knapsack_pro/test_case_mergers/rspec_merger_spec.rb +59 -0
- data/spec/knapsack_pro/test_file_finder_spec.rb +105 -29
- data/spec/knapsack_pro/test_files_with_test_cases_composer_spec.rb +41 -0
- metadata +20 -2
@@ -4,8 +4,8 @@ module KnapsackPro
|
|
4
4
|
class ServerError < StandardError; end
|
5
5
|
|
6
6
|
TIMEOUT = 15
|
7
|
-
MAX_RETRY = 3
|
8
|
-
REQUEST_RETRY_TIMEBOX =
|
7
|
+
MAX_RETRY = -> { KnapsackPro::Config::Env.fallback_mode_enabled? ? 3 : 6 }
|
8
|
+
REQUEST_RETRY_TIMEBOX = 8
|
9
9
|
|
10
10
|
def initialize(action)
|
11
11
|
@action = action
|
@@ -48,11 +48,7 @@ module KnapsackPro
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def request_hash
|
51
|
-
action
|
52
|
-
.request_hash
|
53
|
-
.merge({
|
54
|
-
test_suite_token: test_suite_token
|
55
|
-
})
|
51
|
+
action.request_hash
|
56
52
|
end
|
57
53
|
|
58
54
|
def request_body
|
@@ -69,6 +65,7 @@ module KnapsackPro
|
|
69
65
|
'Accept' => 'application/json',
|
70
66
|
'KNAPSACK-PRO-CLIENT-NAME' => client_name,
|
71
67
|
'KNAPSACK-PRO-CLIENT-VERSION' => KnapsackPro::VERSION,
|
68
|
+
'KNAPSACK-PRO-TEST-SUITE-TOKEN' => test_suite_token,
|
72
69
|
}
|
73
70
|
end
|
74
71
|
|
@@ -95,19 +92,15 @@ module KnapsackPro
|
|
95
92
|
!seed.nil?
|
96
93
|
end
|
97
94
|
|
98
|
-
def
|
95
|
+
def make_request(&block)
|
99
96
|
retries ||= 0
|
100
|
-
uri = URI.parse(endpoint_url)
|
101
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
102
|
-
http.use_ssl = (uri.scheme == 'https')
|
103
|
-
http.open_timeout = TIMEOUT
|
104
|
-
http.read_timeout = TIMEOUT
|
105
97
|
|
106
|
-
@http_response =
|
98
|
+
@http_response = block.call
|
107
99
|
@response_body = parse_response_body(http_response.body)
|
108
100
|
|
109
101
|
request_uuid = http_response.header['X-Request-Id'] || 'N/A'
|
110
102
|
|
103
|
+
logger.debug("#{action.http_method.to_s.upcase} #{endpoint_url}")
|
111
104
|
logger.debug("API request UUID: #{request_uuid}")
|
112
105
|
logger.debug("Test suite split seed: #{seed}") if has_seed?
|
113
106
|
logger.debug('API response:')
|
@@ -125,15 +118,44 @@ module KnapsackPro
|
|
125
118
|
rescue ServerError, Errno::ECONNREFUSED, Errno::ETIMEDOUT, Errno::EPIPE, EOFError, SocketError, Net::OpenTimeout, Net::ReadTimeout, OpenSSL::SSL::SSLError => e
|
126
119
|
logger.warn(e.inspect)
|
127
120
|
retries += 1
|
128
|
-
if retries < MAX_RETRY
|
121
|
+
if retries < MAX_RETRY.call
|
129
122
|
wait = retries * REQUEST_RETRY_TIMEBOX
|
130
123
|
logger.warn("Wait #{wait}s and retry request to Knapsack Pro API.")
|
131
|
-
|
124
|
+
print_every = 2 # seconds
|
125
|
+
(wait / print_every).ceil.times do |i|
|
126
|
+
logger.warn("Next request in #{wait - i * print_every}s...")
|
127
|
+
Kernel.sleep(print_every)
|
128
|
+
end
|
132
129
|
retry
|
133
130
|
else
|
134
131
|
response_body
|
135
132
|
end
|
136
133
|
end
|
134
|
+
|
135
|
+
def build_http(uri)
|
136
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
137
|
+
http.use_ssl = (uri.scheme == 'https')
|
138
|
+
http.open_timeout = TIMEOUT
|
139
|
+
http.read_timeout = TIMEOUT
|
140
|
+
http
|
141
|
+
end
|
142
|
+
|
143
|
+
def post
|
144
|
+
uri = URI.parse(endpoint_url)
|
145
|
+
http = build_http(uri)
|
146
|
+
make_request do
|
147
|
+
http.post(uri.path, request_body, json_headers)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def get
|
152
|
+
uri = URI.parse(endpoint_url)
|
153
|
+
uri.query = URI.encode_www_form(request_hash)
|
154
|
+
http = build_http(uri)
|
155
|
+
make_request do
|
156
|
+
http.get(uri, json_headers)
|
157
|
+
end
|
158
|
+
end
|
137
159
|
end
|
138
160
|
end
|
139
161
|
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
module KnapsackPro
|
2
2
|
class QueueAllocator
|
3
3
|
def initialize(args)
|
4
|
-
@
|
4
|
+
@fast_and_slow_test_files_to_run = args.fetch(:fast_and_slow_test_files_to_run)
|
5
|
+
@fallback_mode_test_files = args.fetch(:fallback_mode_test_files)
|
5
6
|
@ci_node_total = args.fetch(:ci_node_total)
|
6
7
|
@ci_node_index = args.fetch(:ci_node_index)
|
7
8
|
@ci_node_build_id = args.fetch(:ci_node_build_id)
|
@@ -36,14 +37,15 @@ module KnapsackPro
|
|
36
37
|
|
37
38
|
private
|
38
39
|
|
39
|
-
attr_reader :
|
40
|
+
attr_reader :fast_and_slow_test_files_to_run,
|
41
|
+
:fallback_mode_test_files,
|
40
42
|
:ci_node_total,
|
41
43
|
:ci_node_index,
|
42
44
|
:ci_node_build_id,
|
43
45
|
:repository_adapter
|
44
46
|
|
45
47
|
def encrypted_test_files
|
46
|
-
KnapsackPro::Crypto::Encryptor.call(
|
48
|
+
KnapsackPro::Crypto::Encryptor.call(fast_and_slow_test_files_to_run)
|
47
49
|
end
|
48
50
|
|
49
51
|
def encrypted_branch
|
@@ -63,12 +65,12 @@ module KnapsackPro
|
|
63
65
|
end
|
64
66
|
|
65
67
|
def prepare_test_files(response)
|
66
|
-
decrypted_test_files = KnapsackPro::Crypto::Decryptor.call(
|
68
|
+
decrypted_test_files = KnapsackPro::Crypto::Decryptor.call(fast_and_slow_test_files_to_run, response['test_files'])
|
67
69
|
KnapsackPro::TestFilePresenter.paths(decrypted_test_files)
|
68
70
|
end
|
69
71
|
|
70
72
|
def fallback_test_files(executed_test_files)
|
71
|
-
test_flat_distributor = KnapsackPro::TestFlatDistributor.new(
|
73
|
+
test_flat_distributor = KnapsackPro::TestFlatDistributor.new(fallback_mode_test_files, ci_node_total)
|
72
74
|
test_files_for_node_index = test_flat_distributor.test_files_for_node(ci_node_index)
|
73
75
|
KnapsackPro::TestFilePresenter.paths(test_files_for_node_index) - executed_test_files
|
74
76
|
end
|
@@ -2,7 +2,8 @@ module KnapsackPro
|
|
2
2
|
class QueueAllocatorBuilder < BaseAllocatorBuilder
|
3
3
|
def allocator
|
4
4
|
KnapsackPro::QueueAllocator.new(
|
5
|
-
|
5
|
+
fast_and_slow_test_files_to_run: fast_and_slow_test_files_to_run,
|
6
|
+
fallback_mode_test_files: fallback_mode_test_files,
|
6
7
|
ci_node_total: env.ci_node_total,
|
7
8
|
ci_node_index: env.ci_node_index,
|
8
9
|
ci_node_build_id: env.ci_node_build_id,
|
data/lib/knapsack_pro/report.rb
CHANGED
@@ -37,7 +37,7 @@ module KnapsackPro
|
|
37
37
|
if test_files.empty?
|
38
38
|
KnapsackPro.logger.warn("No test files were executed on this CI node.")
|
39
39
|
KnapsackPro.logger.debug("When you use knapsack_pro queue mode then probably reason might be that CI node was started after the test files from the queue were already executed by other CI nodes. That is why this CI node has no test files to execute.")
|
40
|
-
KnapsackPro.logger.debug("Another reason might be when your CI node failed in a way that prevented knapsack_pro to save time execution data to Knapsack Pro API and you have just tried to retry failed CI node but instead you got no test files to execute. In that case knapsack_pro don't know what
|
40
|
+
KnapsackPro.logger.debug("Another reason might be when your CI node failed in a way that prevented knapsack_pro to save time execution data to Knapsack Pro API and you have just tried to retry failed CI node but instead you got no test files to execute. In that case knapsack_pro don't know what tests should be executed here.")
|
41
41
|
end
|
42
42
|
|
43
43
|
measured_test_files = test_files
|
@@ -91,7 +91,16 @@ module KnapsackPro
|
|
91
91
|
# which is defined in lib/knapsack_pro/adapters/cucumber_adapter.rb
|
92
92
|
ENV['KNAPSACK_PRO_BEFORE_QUEUE_HOOK_CALLED'] = 'true'
|
93
93
|
|
94
|
-
|
94
|
+
process_status = $?
|
95
|
+
|
96
|
+
unless process_status.exited?
|
97
|
+
raise "Cucumber process execution failed. It's likely that your CI server has exceeded"\
|
98
|
+
" its available memory. Please try changing CI config or retrying the CI build.\n"\
|
99
|
+
"Failed command: #{cmd}\n"\
|
100
|
+
"Process status: #{process_status.inspect}"
|
101
|
+
end
|
102
|
+
|
103
|
+
process_status.exitstatus
|
95
104
|
end
|
96
105
|
end
|
97
106
|
end
|
@@ -134,6 +134,13 @@ module KnapsackPro
|
|
134
134
|
RSpec.world.example_groups.clear
|
135
135
|
RSpec.configuration.start_time = ::RSpec::Core::Time.now
|
136
136
|
|
137
|
+
if KnapsackPro::Config::Env.rspec_split_by_test_examples?
|
138
|
+
# Reset example group counts to ensure scoped example ids in metadata
|
139
|
+
# have correct index (not increased by each subsequent run).
|
140
|
+
# Solves this problem: https://github.com/rspec/rspec-core/issues/2721
|
141
|
+
RSpec.world.instance_variable_set(:@example_group_counts_by_spec_file, Hash.new(0))
|
142
|
+
end
|
143
|
+
|
137
144
|
# skip reset filters for old RSpec versions
|
138
145
|
if RSpec.configuration.respond_to?(:reset_filters)
|
139
146
|
RSpec.configuration.reset_filters
|
@@ -16,8 +16,11 @@ module KnapsackPro
|
|
16
16
|
end
|
17
17
|
|
18
18
|
RSpec::Core::RakeTask.new(task_name) do |t|
|
19
|
-
|
20
|
-
|
19
|
+
# we cannot pass runner.test_file_paths array to t.pattern
|
20
|
+
# because pattern does not accept test example path like spec/a_spec.rb[1:2]
|
21
|
+
# instead we pass test files and test example paths to t.rspec_opts
|
22
|
+
t.pattern = []
|
23
|
+
t.rspec_opts = "#{args} --default-path #{runner.test_dir} #{runner.stringify_test_file_paths}"
|
21
24
|
end
|
22
25
|
Rake::Task[task_name].invoke
|
23
26
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module KnapsackPro
|
2
|
+
class SlowTestFileDeterminer
|
3
|
+
TIME_THRESHOLD_PER_CI_NODE = 0.7 # 70%
|
4
|
+
REPORT_DIR = 'tmp/knapsack_pro/slow_test_file_determiner'
|
5
|
+
|
6
|
+
# test_files: { 'path' => 'a_spec.rb', 'time_execution' => 0.0 }
|
7
|
+
# time_execution: of build distribution (total time of CI build run)
|
8
|
+
def self.call(test_files, time_execution)
|
9
|
+
time_threshold = (time_execution / KnapsackPro::Config::Env.ci_node_total) * TIME_THRESHOLD_PER_CI_NODE
|
10
|
+
|
11
|
+
test_files.select do |test_file|
|
12
|
+
test_file.fetch('time_execution') >= time_threshold
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.save_to_json_report(test_files)
|
17
|
+
FileUtils.mkdir_p(REPORT_DIR)
|
18
|
+
File.write(report_path, test_files.to_json)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.read_from_json_report
|
22
|
+
raise 'Report with slow test files was not generated yet. If you have enabled split by test cases https://github.com/KnapsackPro/knapsack_pro-ruby#split-test-files-by-test-cases and you see this error it means that your tests accidentally cleaned up tmp/knapsack_pro directory. Please do not remove this directory during tests runtime!' unless File.exists?(report_path)
|
23
|
+
slow_test_files_json_report = File.read(report_path)
|
24
|
+
JSON.parse(slow_test_files_json_report)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def self.report_path
|
30
|
+
"#{REPORT_DIR}/slow_test_files_node_#{KnapsackPro::Config::Env.ci_node_index}.json"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module KnapsackPro
|
2
|
+
class SlowTestFileFinder
|
3
|
+
# Get recorded test files from API.
|
4
|
+
# Find slow tests among them that are still present on the disk.
|
5
|
+
# Save slow test files in json file on the disk.
|
6
|
+
# Returns slow test files.
|
7
|
+
def self.call(adapter_class)
|
8
|
+
if KnapsackPro::Config::Env.test_files_encrypted?
|
9
|
+
raise 'Split by test cases is not possible when you have enabled test file names encryption ( https://github.com/KnapsackPro/knapsack_pro-ruby#test-file-names-encryption ). You need to disable encryption with KNAPSACK_PRO_TEST_FILES_ENCRYPTED=false in order to use split by test cases https://github.com/KnapsackPro/knapsack_pro-ruby#split-test-files-by-test-cases'
|
10
|
+
end
|
11
|
+
|
12
|
+
# get list of recorded test files for last CI Build
|
13
|
+
build_distribution_entity = KnapsackPro::BuildDistributionFetcher.call
|
14
|
+
test_files_from_api = build_distribution_entity.test_files
|
15
|
+
|
16
|
+
merged_test_files_from_api = KnapsackPro::TestCaseMergers::BaseMerger.call(adapter_class, test_files_from_api)
|
17
|
+
|
18
|
+
test_files_existing_on_disk = KnapsackPro::TestFileFinder.select_test_files_that_can_be_run(adapter_class, merged_test_files_from_api)
|
19
|
+
|
20
|
+
slow_test_files = KnapsackPro::SlowTestFileDeterminer.call(test_files_existing_on_disk, build_distribution_entity.time_execution)
|
21
|
+
|
22
|
+
KnapsackPro::SlowTestFileDeterminer.save_to_json_report(slow_test_files)
|
23
|
+
|
24
|
+
slow_test_files
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -2,7 +2,6 @@ module KnapsackPro
|
|
2
2
|
module TestCaseDetectors
|
3
3
|
class RSpecTestExampleDetector
|
4
4
|
REPORT_DIR = 'tmp/knapsack_pro/test_case_detectors/rspec'
|
5
|
-
REPORT_PATH = "#{REPORT_DIR}/rspec_dry_run_json_report.json"
|
6
5
|
|
7
6
|
def generate_json_report
|
8
7
|
require 'rspec/core'
|
@@ -18,13 +17,19 @@ module KnapsackPro
|
|
18
17
|
ensure_report_dir_exists
|
19
18
|
remove_old_json_report
|
20
19
|
|
21
|
-
|
20
|
+
test_file_entities = slow_test_files
|
21
|
+
|
22
|
+
if test_file_entities.empty?
|
23
|
+
no_examples_json = { examples: [] }.to_json
|
24
|
+
File.write(report_path, no_examples_json)
|
25
|
+
return
|
26
|
+
end
|
22
27
|
|
23
28
|
cli_args = cli_format + [
|
24
29
|
'--dry-run',
|
25
|
-
'--out',
|
30
|
+
'--out', report_path,
|
26
31
|
'--default-path', test_dir,
|
27
|
-
] +
|
32
|
+
] + KnapsackPro::TestFilePresenter.paths(test_file_entities)
|
28
33
|
options = RSpec::Core::ConfigurationOptions.new(cli_args)
|
29
34
|
exit_code = RSpec::Core::Runner.new(options).run($stderr, $stdout)
|
30
35
|
if exit_code != 0
|
@@ -33,9 +38,9 @@ module KnapsackPro
|
|
33
38
|
end
|
34
39
|
|
35
40
|
def test_file_example_paths
|
36
|
-
raise "No report found at #{
|
41
|
+
raise "No report found at #{report_path}" unless File.exists?(report_path)
|
37
42
|
|
38
|
-
json_report = File.read(
|
43
|
+
json_report = File.read(report_path)
|
39
44
|
hash_report = JSON.parse(json_report)
|
40
45
|
hash_report
|
41
46
|
.fetch('examples')
|
@@ -43,8 +48,22 @@ module KnapsackPro
|
|
43
48
|
.map { |path_with_example_id| test_file_hash_for(path_with_example_id) }
|
44
49
|
end
|
45
50
|
|
51
|
+
def slow_test_files
|
52
|
+
if KnapsackPro::Config::Env.slow_test_file_pattern
|
53
|
+
KnapsackPro::TestFileFinder.slow_test_files_by_pattern(adapter_class)
|
54
|
+
else
|
55
|
+
# read slow test files from JSON file on disk that was generated
|
56
|
+
# by lib/knapsack_pro/base_allocator_builder.rb
|
57
|
+
KnapsackPro::SlowTestFileDeterminer.read_from_json_report
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
46
61
|
private
|
47
62
|
|
63
|
+
def report_path
|
64
|
+
"#{REPORT_DIR}/rspec_dry_run_json_report_node_#{KnapsackPro::Config::Env.ci_node_index}.json"
|
65
|
+
end
|
66
|
+
|
48
67
|
def adapter_class
|
49
68
|
KnapsackPro::Adapters::RSpecAdapter
|
50
69
|
end
|
@@ -62,7 +81,7 @@ module KnapsackPro
|
|
62
81
|
end
|
63
82
|
|
64
83
|
def remove_old_json_report
|
65
|
-
File.delete(
|
84
|
+
File.delete(report_path) if File.exists?(report_path)
|
66
85
|
end
|
67
86
|
|
68
87
|
def test_file_hash_for(test_file_path)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module KnapsackPro
|
2
|
+
module TestCaseMergers
|
3
|
+
class BaseMerger
|
4
|
+
# values must be string to avoid circular dependency problem during loading files
|
5
|
+
ADAPTER_TO_MERGER_MAP = {
|
6
|
+
KnapsackPro::Adapters::RSpecAdapter => 'KnapsackPro::TestCaseMergers::RSpecMerger',
|
7
|
+
}
|
8
|
+
|
9
|
+
def self.call(adapter_class, test_files)
|
10
|
+
merger_class =
|
11
|
+
ADAPTER_TO_MERGER_MAP[adapter_class] ||
|
12
|
+
raise("Test case merger does not exist for adapter_class: #{adapter_class}")
|
13
|
+
Kernel.const_get(merger_class).new(test_files).call
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(test_files)
|
17
|
+
@test_files = test_files
|
18
|
+
end
|
19
|
+
|
20
|
+
def call
|
21
|
+
raise NotImplementedError
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :test_files
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module KnapsackPro
|
2
|
+
module TestCaseMergers
|
3
|
+
class RSpecMerger < BaseMerger
|
4
|
+
def call
|
5
|
+
merged_test_files_hash = {}
|
6
|
+
test_files.each do |test_file|
|
7
|
+
test_file_path = extract_test_file_path(test_file.fetch('path'))
|
8
|
+
|
9
|
+
# must be float (default type for time execution from API)
|
10
|
+
merged_test_files_hash[test_file_path] ||= 0.0
|
11
|
+
merged_test_files_hash[test_file_path] += test_file.fetch('time_execution')
|
12
|
+
end
|
13
|
+
|
14
|
+
merged_test_files = []
|
15
|
+
merged_test_files_hash.each do |path, time_execution|
|
16
|
+
merged_test_files << {
|
17
|
+
'path' => path,
|
18
|
+
'time_execution' => time_execution
|
19
|
+
}
|
20
|
+
end
|
21
|
+
merged_test_files
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# path - can be:
|
27
|
+
# test file path: spec/a_spec.rb
|
28
|
+
# or test example path: spec/a_spec.rb[1:1]
|
29
|
+
def extract_test_file_path(path)
|
30
|
+
path.gsub(/\.rb\[.+\]$/, '.rb')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,11 +1,49 @@
|
|
1
1
|
module KnapsackPro
|
2
2
|
class TestFileFinder
|
3
|
-
def self.call(test_file_pattern)
|
4
|
-
new(test_file_pattern).call
|
3
|
+
def self.call(test_file_pattern, test_file_list_enabled: true)
|
4
|
+
new(test_file_pattern, test_file_list_enabled).call
|
5
5
|
end
|
6
6
|
|
7
|
-
|
7
|
+
# finds slow test files on disk based on ENV patterns
|
8
|
+
# returns example: [{ 'path' => 'a_spec.rb' }]
|
9
|
+
def self.slow_test_files_by_pattern(adapter_class)
|
10
|
+
raise 'KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN is not defined' unless KnapsackPro::Config::Env.slow_test_file_pattern
|
11
|
+
|
12
|
+
test_file_pattern = KnapsackPro::TestFilePattern.call(adapter_class)
|
13
|
+
test_file_entities = call(test_file_pattern)
|
14
|
+
|
15
|
+
slow_test_file_entities = call(KnapsackPro::Config::Env.slow_test_file_pattern, test_file_list_enabled: false)
|
16
|
+
|
17
|
+
# slow test files (KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN)
|
18
|
+
# should be subset of test file pattern (KNAPSACK_PRO_TEST_FILE_PATTERN)
|
19
|
+
slow_test_file_entities & test_file_entities
|
20
|
+
end
|
21
|
+
|
22
|
+
# Args:
|
23
|
+
# test_file_entities_to_run - it can be list of slow test files that you want to run
|
24
|
+
# Return:
|
25
|
+
# 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
|
26
|
+
# Thanks to that we can select only slow test files that are within list of test file pattern we want to run tests for
|
27
|
+
def self.select_test_files_that_can_be_run(adapter_class, test_file_entities_to_run)
|
28
|
+
test_file_pattern = KnapsackPro::TestFilePattern.call(adapter_class)
|
29
|
+
test_file_entities = call(test_file_pattern)
|
30
|
+
|
31
|
+
test_file_paths_existing_on_disk = KnapsackPro::TestFilePresenter.paths(test_file_entities)
|
32
|
+
|
33
|
+
selected_test_files = []
|
34
|
+
|
35
|
+
test_file_entities_to_run.each do |test_file_entity|
|
36
|
+
if test_file_paths_existing_on_disk.include?(test_file_entity.fetch('path'))
|
37
|
+
selected_test_files << test_file_entity
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
selected_test_files
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(test_file_pattern, test_file_list_enabled)
|
8
45
|
@test_file_pattern = test_file_pattern
|
46
|
+
@test_file_list_enabled = test_file_list_enabled
|
9
47
|
end
|
10
48
|
|
11
49
|
def call
|
@@ -18,10 +56,10 @@ module KnapsackPro
|
|
18
56
|
|
19
57
|
private
|
20
58
|
|
21
|
-
attr_reader :test_file_pattern
|
59
|
+
attr_reader :test_file_pattern, :test_file_list_enabled
|
22
60
|
|
23
61
|
def test_files
|
24
|
-
if KnapsackPro::Config::Env.test_file_list
|
62
|
+
if test_file_list_enabled && KnapsackPro::Config::Env.test_file_list
|
25
63
|
return KnapsackPro::Config::Env.test_file_list.split(',').map(&:strip)
|
26
64
|
end
|
27
65
|
|