knapsack_pro 5.6.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +2 -0
- data/CHANGELOG.md +18 -0
- data/README.md +1 -1
- data/bin/test +15 -0
- data/knapsack_pro.gemspec +5 -5
- data/lib/knapsack_pro/adapters/base_adapter.rb +8 -0
- data/lib/knapsack_pro/adapters/rspec_adapter.rb +74 -44
- data/lib/knapsack_pro/base_allocator_builder.rb +6 -25
- data/lib/knapsack_pro/formatters/rspec_queue_summary_formatter.rb +6 -3
- data/lib/knapsack_pro/formatters/time_tracker.rb +161 -0
- data/lib/knapsack_pro/formatters/time_tracker_fetcher.rb +12 -0
- data/lib/knapsack_pro/presenter.rb +3 -2
- data/lib/knapsack_pro/report.rb +13 -9
- data/lib/knapsack_pro/runners/queue/rspec_runner.rb +25 -14
- data/lib/knapsack_pro/runners/rspec_runner.rb +19 -3
- data/lib/knapsack_pro/tracker.rb +1 -9
- data/lib/knapsack_pro/version.rb +1 -1
- data/lib/knapsack_pro.rb +0 -1
- data/spec/knapsack_pro/adapters/base_adapter_spec.rb +12 -0
- data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +143 -158
- data/spec/knapsack_pro/base_allocator_builder_spec.rb +22 -48
- data/spec/knapsack_pro/client/connection_spec.rb +54 -7
- data/spec/knapsack_pro/formatters/time_tracker_specs.rb +453 -0
- data/spec/knapsack_pro/runners/queue/rspec_runner_spec.rb +71 -32
- data/spec/knapsack_pro/runners/rspec_runner_spec.rb +2 -5
- data/spec/knapsack_pro/tracker_spec.rb +0 -21
- metadata +9 -7
- data/lib/knapsack_pro/extensions/time.rb +0 -9
- data/spec/knapsack_pro/extensions/time_spec.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 315a4ff2f5885be68529cc0799affd8163fcad51558e4813a254b99bd057992a
|
4
|
+
data.tar.gz: 5709757ca30a22401be4dc170a926b16f5206d494d5b80c583bf03088d8570a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c43ee2474109d5e509fb57534857164ec0596cb018f61b44942ab261bdcb55345ac3025239fd3dde12c7d31f30841aa39aa480fb07bcab1519272788dad9c4b7
|
7
|
+
data.tar.gz: 9511b45fb2b0d59a309517ccc70ca10ebbb2b8a3cf0cfb2f2198e38a776838a402b531f64aea2ca94ce37489288dde8171b5fd36c45038014d781cba0fd5462d
|
data/.circleci/config.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### 6.0.0
|
4
|
+
|
5
|
+
* __(breaking change)__ Dropped support for Turnip < 2.0.0
|
6
|
+
* Use an RSpec Formatter to track tests' execution times more accurately
|
7
|
+
* Removed `Time.raw_now`
|
8
|
+
|
9
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/pull/229
|
10
|
+
|
11
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v5.7.0...UNRELEASED
|
12
|
+
|
13
|
+
### 5.7.0
|
14
|
+
|
15
|
+
* Performance improvement: don't run `rake knapsack_pro:rspec_test_example_detector` when no slow test files are detected for RSpec.
|
16
|
+
|
17
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/pull/225
|
18
|
+
|
19
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v5.6.0...v5.7.0
|
20
|
+
|
3
21
|
### 5.6.0
|
4
22
|
|
5
23
|
* Use `frozen_string_literal: true` to reduce memory usage
|
data/README.md
CHANGED
@@ -74,7 +74,7 @@ bundle update knapsack_pro
|
|
74
74
|
RSpec:
|
75
75
|
|
76
76
|
```bash
|
77
|
-
|
77
|
+
bin/test
|
78
78
|
```
|
79
79
|
|
80
80
|
Scripted tests can be found in the [Rails App With Knapsack Pro repository](https://github.com/KnapsackPro/rails-app-with-knapsack_pro/blob/master/bin/knapsack_pro_all.rb).
|
data/bin/test
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
bundle exec ruby spec/knapsack_pro/formatters/time_tracker_specs.rb
|
4
|
+
export FORMATTERS_EXIT_CODE=$?
|
5
|
+
|
6
|
+
bundle exec rspec spec
|
7
|
+
export RSPEC_EXIT_CODE=$?
|
8
|
+
|
9
|
+
if [ "$FORMATTERS_EXIT_CODE" -ne "0" ]; then
|
10
|
+
exit $FORMATTERS_EXIT_CODE
|
11
|
+
fi
|
12
|
+
|
13
|
+
if [ "$RSPEC_EXIT_CODE" -ne "0" ]; then
|
14
|
+
exit $RSPEC_EXIT_CODE
|
15
|
+
fi
|
data/knapsack_pro.gemspec
CHANGED
@@ -4,10 +4,10 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'knapsack_pro/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'knapsack_pro'
|
8
8
|
spec.version = KnapsackPro::VERSION
|
9
9
|
spec.authors = ['ArturT']
|
10
|
-
spec.email = ['
|
10
|
+
spec.email = ['support@knapsackpro.com']
|
11
11
|
spec.summary = %q{Knapsack Pro splits tests across parallel CI nodes and ensures each parallel job finish work at a similar time.}
|
12
12
|
spec.description = %q{Run tests in parallel across CI server nodes based on tests execution time. Split tests in a dynamic way to ensure parallel jobs are done at a similar time. Thanks to that your CI build time is as fast as possible. It works with many CI providers.}
|
13
13
|
spec.homepage = 'https://knapsackpro.com'
|
@@ -15,15 +15,15 @@ Gem::Specification.new do |spec|
|
|
15
15
|
spec.metadata = {
|
16
16
|
'bug_tracker_uri' => 'https://github.com/KnapsackPro/knapsack_pro-ruby/issues',
|
17
17
|
'changelog_uri' => 'https://github.com/KnapsackPro/knapsack_pro-ruby/blob/master/CHANGELOG.md',
|
18
|
-
'documentation_uri' => 'https://docs.knapsackpro.com/
|
18
|
+
'documentation_uri' => 'https://docs.knapsackpro.com/knapsack_pro-ruby/guide/',
|
19
19
|
'homepage_uri' => 'https://knapsackpro.com',
|
20
20
|
'source_code_uri' => 'https://github.com/KnapsackPro/knapsack_pro-ruby'
|
21
21
|
}
|
22
22
|
|
23
23
|
spec.files = `git ls-files -z`.split("\x0")
|
24
|
-
spec.executables =
|
24
|
+
spec.executables = ['knapsack_pro']
|
25
25
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
26
|
-
spec.require_paths = [
|
26
|
+
spec.require_paths = ['lib']
|
27
27
|
|
28
28
|
spec.add_dependency 'rake', '>= 0'
|
29
29
|
|
@@ -11,6 +11,14 @@ module KnapsackPro
|
|
11
11
|
"#{KnapsackPro::Config::TempFiles::TEMP_DIRECTORY_PATH}/#{adapter_name}-bind_method_called_for_node_#{KnapsackPro::Config::Env.ci_node_index}.txt"
|
12
12
|
end
|
13
13
|
|
14
|
+
def self.split_by_test_cases_enabled?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.test_file_cases_for(slow_test_files)
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
|
14
22
|
def self.slow_test_file?(adapter_class, test_file_path)
|
15
23
|
@slow_test_file_paths ||=
|
16
24
|
begin
|
@@ -1,10 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../formatters/time_tracker_fetcher'
|
4
|
+
|
3
5
|
module KnapsackPro
|
4
6
|
module Adapters
|
5
7
|
class RSpecAdapter < BaseAdapter
|
6
8
|
TEST_DIR_PATTERN = 'spec/**{,/*/**}/*_spec.rb'
|
7
9
|
|
10
|
+
def self.split_by_test_cases_enabled?
|
11
|
+
return false unless KnapsackPro::Config::Env.rspec_split_by_test_examples?
|
12
|
+
|
13
|
+
require 'rspec/core/version'
|
14
|
+
unless Gem::Version.new(::RSpec::Core::Version::STRING) >= Gem::Version.new('3.3.0')
|
15
|
+
raise "RSpec >= 3.3.0 is required to split test files by test examples. Learn more: #{KnapsackPro::Urls::SPLIT_BY_TEST_EXAMPLES}"
|
16
|
+
end
|
17
|
+
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.test_file_cases_for(slow_test_files)
|
22
|
+
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.")
|
23
|
+
|
24
|
+
# generate the RSpec JSON report in a separate process to not pollute the RSpec state
|
25
|
+
cmd = [
|
26
|
+
'RACK_ENV=test',
|
27
|
+
'RAILS_ENV=test',
|
28
|
+
KnapsackPro::Config::Env.rspec_test_example_detector_prefix,
|
29
|
+
'rake knapsack_pro:rspec_test_example_detector',
|
30
|
+
].join(' ')
|
31
|
+
unless Kernel.system(cmd)
|
32
|
+
raise "Could not generate JSON report for RSpec. Rake task failed when running #{cmd}"
|
33
|
+
end
|
34
|
+
|
35
|
+
# read the JSON report
|
36
|
+
KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector.new.test_file_example_paths
|
37
|
+
end
|
38
|
+
|
8
39
|
def self.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!(cli_args)
|
9
40
|
if KnapsackPro::Config::Env.rspec_split_by_test_examples? && has_tag_option?(cli_args)
|
10
41
|
error_message = "It is not allowed to use the RSpec tag option together with the RSpec split by test examples feature. Please see: #{KnapsackPro::Urls::RSPEC__SPLIT_BY_TEST_EXAMPLES__TAG}"
|
@@ -25,63 +56,61 @@ module KnapsackPro
|
|
25
56
|
parsed_options(cli_args)&.[](:order)
|
26
57
|
end
|
27
58
|
|
28
|
-
def self.
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
59
|
+
def self.file_path_for(example)
|
60
|
+
[
|
61
|
+
-> { parse_file_path(example.id) },
|
62
|
+
-> { example.metadata[:file_path] },
|
63
|
+
-> { example.metadata[:example_group][:file_path] },
|
64
|
+
-> { top_level_group(example)[:file_path] },
|
65
|
+
]
|
66
|
+
.each do |path|
|
67
|
+
p = path.call
|
68
|
+
return p if p.include?('_spec.rb')
|
36
69
|
end
|
37
|
-
else
|
38
|
-
until example_group[:parent_example_group].nil?
|
39
|
-
example_group = example_group[:parent_example_group]
|
40
|
-
end
|
41
|
-
end
|
42
70
|
|
43
|
-
|
71
|
+
return ''
|
44
72
|
end
|
45
73
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
74
|
+
def self.parse_file_path(id)
|
75
|
+
# https://github.com/rspec/rspec-core/blob/1eeadce5aa7137ead054783c31ff35cbfe9d07cc/lib/rspec/core/example.rb#L122
|
76
|
+
id.match(/\A(.*?)(?:\[([\d\s:,]+)\])?\z/).captures.first
|
77
|
+
end
|
51
78
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
KnapsackPro.tracker.stop_timer
|
61
|
-
end
|
79
|
+
# private
|
80
|
+
def self.top_level_group(example)
|
81
|
+
group = example.metadata[:example_group]
|
82
|
+
until group[:parent_example_group].nil?
|
83
|
+
group = group[:parent_example_group]
|
84
|
+
end
|
85
|
+
group
|
86
|
+
end
|
62
87
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
current_test_path
|
68
|
-
end
|
88
|
+
def bind_time_tracker
|
89
|
+
ensure_no_focus!
|
90
|
+
log_batch_duration
|
91
|
+
end
|
69
92
|
|
93
|
+
def ensure_no_focus!
|
94
|
+
::RSpec.configure do |config|
|
95
|
+
config.around(:each) do |example|
|
70
96
|
if example.metadata[:focus] && KnapsackPro::Adapters::RSpecAdapter.rspec_configuration.filter.rules[:focus]
|
71
|
-
|
97
|
+
file_path = KnapsackPro::Adapters::RSpecAdapter.file_path_for(example)
|
98
|
+
file_path = KnapsackPro::TestFileCleaner.clean(file_path)
|
99
|
+
|
100
|
+
raise "Knapsack Pro found an example tagged with focus in #{file_path}, please remove it. See more: #{KnapsackPro::Urls::RSPEC__SKIPS_TESTS}"
|
72
101
|
end
|
73
102
|
|
74
103
|
example.run
|
75
104
|
end
|
105
|
+
end
|
106
|
+
end
|
76
107
|
|
77
|
-
|
78
|
-
|
79
|
-
# stop timer to count time for the very last executed test example
|
80
|
-
KnapsackPro.tracker.stop_timer
|
81
|
-
end
|
82
|
-
|
108
|
+
def log_batch_duration
|
109
|
+
::RSpec.configure do |config|
|
83
110
|
config.after(:suite) do
|
84
|
-
KnapsackPro
|
111
|
+
time_tracker = KnapsackPro::Formatters::TimeTrackerFetcher.call
|
112
|
+
formatted = KnapsackPro::Presenter.global_time(time_tracker.batch_duration)
|
113
|
+
KnapsackPro.logger.debug(formatted)
|
85
114
|
end
|
86
115
|
end
|
87
116
|
end
|
@@ -89,7 +118,8 @@ module KnapsackPro
|
|
89
118
|
def bind_save_report
|
90
119
|
::RSpec.configure do |config|
|
91
120
|
config.after(:suite) do
|
92
|
-
KnapsackPro::
|
121
|
+
time_tracker = KnapsackPro::Formatters::TimeTrackerFetcher.call
|
122
|
+
KnapsackPro::Report.save(time_tracker.batch)
|
93
123
|
end
|
94
124
|
end
|
95
125
|
end
|
@@ -35,35 +35,16 @@ module KnapsackPro
|
|
35
35
|
def fast_and_slow_test_files_to_run
|
36
36
|
test_files_to_run = all_test_files_to_run
|
37
37
|
|
38
|
-
if adapter_class
|
39
|
-
require 'rspec/core/version'
|
40
|
-
unless Gem::Version.new(::RSpec::Core::Version::STRING) >= Gem::Version.new('3.3.0')
|
41
|
-
raise "RSpec >= 3.3.0 is required to split test files by test examples. Learn more: #{KnapsackPro::Urls::SPLIT_BY_TEST_EXAMPLES}"
|
42
|
-
end
|
43
|
-
|
38
|
+
if adapter_class.split_by_test_cases_enabled?
|
44
39
|
slow_test_files = get_slow_test_files
|
40
|
+
return test_files_to_run if slow_test_files.empty?
|
45
41
|
|
46
|
-
|
47
|
-
|
48
|
-
# generate RSpec JSON report in separate process to not pollute RSpec state
|
49
|
-
cmd = [
|
50
|
-
'RACK_ENV=test',
|
51
|
-
'RAILS_ENV=test',
|
52
|
-
KnapsackPro::Config::Env.rspec_test_example_detector_prefix,
|
53
|
-
'rake knapsack_pro:rspec_test_example_detector',
|
54
|
-
].join(' ')
|
55
|
-
unless Kernel.system(cmd)
|
56
|
-
raise "Could not generate JSON report for RSpec. Rake task failed when running #{cmd}"
|
57
|
-
end
|
42
|
+
test_file_cases = adapter_class.test_file_cases_for(slow_test_files)
|
58
43
|
|
59
|
-
|
60
|
-
detector = KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector.new
|
61
|
-
test_file_example_paths = detector.test_file_example_paths
|
62
|
-
|
63
|
-
KnapsackPro::TestFilesWithTestCasesComposer.call(test_files_to_run, slow_test_files, test_file_example_paths)
|
64
|
-
else
|
65
|
-
test_files_to_run
|
44
|
+
return KnapsackPro::TestFilesWithTestCasesComposer.call(test_files_to_run, slow_test_files, test_file_cases)
|
66
45
|
end
|
46
|
+
|
47
|
+
test_files_to_run
|
67
48
|
end
|
68
49
|
|
69
50
|
private
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative './time_tracker_fetcher'
|
4
|
+
|
3
5
|
RSpec::Support.require_rspec_core('formatters/base_formatter')
|
4
6
|
RSpec::Support.require_rspec_core('formatters/base_text_formatter')
|
5
7
|
|
@@ -76,11 +78,12 @@ module KnapsackPro
|
|
76
78
|
registered_output.puts(most_recent_summary)
|
77
79
|
end
|
78
80
|
|
79
|
-
def self.print_exit_summary
|
81
|
+
def self.print_exit_summary(all_test_file_paths)
|
80
82
|
registered_output.puts('Knapsack Pro Queue exited/aborted!')
|
81
83
|
registered_output.puts('')
|
82
84
|
|
83
|
-
|
85
|
+
time_tracker = KnapsackPro::Formatters::TimeTrackerFetcher.call
|
86
|
+
unexecuted_test_files = time_tracker&.unexecuted_test_files(all_test_file_paths) || []
|
84
87
|
unless unexecuted_test_files.empty?
|
85
88
|
registered_output.puts('Unexecuted tests on this CI node:')
|
86
89
|
registered_output.puts(unexecuted_test_files)
|
@@ -119,7 +122,7 @@ module KnapsackPro
|
|
119
122
|
|
120
123
|
def dump_summary(summary)
|
121
124
|
colorizer = ::RSpec::Core::Formatters::ConsoleCodes
|
122
|
-
duration = KnapsackPro.
|
125
|
+
duration = KnapsackPro::Formatters::TimeTrackerFetcher.call.duration
|
123
126
|
formatted_duration = ::RSpec::Core::Formatters::Helpers.format_duration(duration)
|
124
127
|
|
125
128
|
formatted = "\nFinished in #{formatted_duration}\n" \
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KnapsackPro
|
4
|
+
module Formatters
|
5
|
+
class TimeTracker
|
6
|
+
::RSpec::Core::Formatters.register self,
|
7
|
+
:example_group_started,
|
8
|
+
:example_started,
|
9
|
+
:example_finished,
|
10
|
+
:example_group_finished,
|
11
|
+
:stop
|
12
|
+
|
13
|
+
# Called at the beginning of each batch,
|
14
|
+
# but only the first instance of this class is used,
|
15
|
+
# so don't rely on the initializer to reset values.
|
16
|
+
def initialize(_output)
|
17
|
+
@time_each = nil
|
18
|
+
@time_all = nil
|
19
|
+
@before_all = 0.0
|
20
|
+
@group = {}
|
21
|
+
@batch = {}
|
22
|
+
@queue = {}
|
23
|
+
@suite_started = now
|
24
|
+
@batch_started = now
|
25
|
+
end
|
26
|
+
|
27
|
+
def example_group_started(notification)
|
28
|
+
return unless top_level_group?(notification.group)
|
29
|
+
@time_all = now
|
30
|
+
end
|
31
|
+
|
32
|
+
def example_started(_notification)
|
33
|
+
@before_all = now - @time_all if @before_all == 0.0
|
34
|
+
@time_each = now
|
35
|
+
end
|
36
|
+
|
37
|
+
def example_finished(notification)
|
38
|
+
record_example(@group, notification.example, @time_each)
|
39
|
+
@time_all = now
|
40
|
+
end
|
41
|
+
|
42
|
+
def example_group_finished(notification)
|
43
|
+
return unless top_level_group?(notification.group)
|
44
|
+
|
45
|
+
add_hooks_time(@group, @before_all, now - @time_all)
|
46
|
+
@batch = merge(@batch, @group)
|
47
|
+
@before_all = 0.0
|
48
|
+
@group = {}
|
49
|
+
end
|
50
|
+
|
51
|
+
# Called at the end of each batch
|
52
|
+
def stop(_notification)
|
53
|
+
@queue = merge(@queue, @batch)
|
54
|
+
@batch = {}
|
55
|
+
@batch_started = now
|
56
|
+
end
|
57
|
+
|
58
|
+
def queue(scheduled_paths)
|
59
|
+
recorded_paths = @queue.values.map do |example|
|
60
|
+
KnapsackPro::Adapters::RSpecAdapter.parse_file_path(example[:path])
|
61
|
+
end
|
62
|
+
|
63
|
+
missing = (scheduled_paths - recorded_paths).each_with_object({}) do |path, object|
|
64
|
+
object[path] = { path: path, time_execution: 0.0 }
|
65
|
+
end
|
66
|
+
|
67
|
+
merge(@queue, missing).values.map do |example|
|
68
|
+
example.transform_keys(&:to_s)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def batch
|
73
|
+
@batch.values.map do |example|
|
74
|
+
example.transform_keys(&:to_s)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def duration
|
79
|
+
now - @suite_started
|
80
|
+
end
|
81
|
+
|
82
|
+
def batch_duration
|
83
|
+
now - @batch_started
|
84
|
+
end
|
85
|
+
|
86
|
+
def unexecuted_test_files(scheduled_paths)
|
87
|
+
pending_paths = (@queue.values + @batch.values)
|
88
|
+
.filter { |example| example[:time_execution] == 0.0 }
|
89
|
+
.map { |example| example[:path] }
|
90
|
+
|
91
|
+
not_run_paths = scheduled_paths -
|
92
|
+
(@queue.values + @batch.values)
|
93
|
+
.map { |example| example[:path] }
|
94
|
+
|
95
|
+
pending_paths + not_run_paths
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def top_level_group?(group)
|
101
|
+
group.metadata[:parent_example_group].nil?
|
102
|
+
end
|
103
|
+
|
104
|
+
def add_hooks_time(group, before_all, after_all)
|
105
|
+
group.each do |_, example|
|
106
|
+
next if example[:time_execution] == 0.0
|
107
|
+
example[:time_execution] += before_all + after_all
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def record_example(accumulator, example, started_at)
|
112
|
+
path = path_for(example)
|
113
|
+
time_execution = time_execution_for(example, started_at)
|
114
|
+
|
115
|
+
if accumulator.key?(path)
|
116
|
+
accumulator[path][:time_execution] += time_execution
|
117
|
+
else
|
118
|
+
accumulator[path] = { path: path, time_execution: time_execution }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def path_for(example)
|
123
|
+
file = file_path_for(example)
|
124
|
+
return "UNKNOWN_PATH" if file == ""
|
125
|
+
path = rspec_split_by_test_example?(file) ? example.id : file
|
126
|
+
KnapsackPro::TestFileCleaner.clean(path)
|
127
|
+
end
|
128
|
+
|
129
|
+
def rspec_split_by_test_example?(file)
|
130
|
+
return false unless KnapsackPro::Config::Env.rspec_split_by_test_examples?
|
131
|
+
return false unless KnapsackPro::Adapters::RSpecAdapter.slow_test_file?(KnapsackPro::Adapters::RSpecAdapter, file)
|
132
|
+
true
|
133
|
+
end
|
134
|
+
|
135
|
+
def file_path_for(example)
|
136
|
+
KnapsackPro::Adapters::RSpecAdapter.file_path_for(example)
|
137
|
+
end
|
138
|
+
|
139
|
+
def time_execution_for(example, started_at)
|
140
|
+
if example.execution_result.status.to_s == "pending"
|
141
|
+
0.0
|
142
|
+
else
|
143
|
+
(now - started_at).to_f
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def merge(h1, h2)
|
148
|
+
h1.merge(h2) do |key, v1, v2|
|
149
|
+
{
|
150
|
+
path: key,
|
151
|
+
time_execution: v1[:time_execution] + v2[:time_execution]
|
152
|
+
}
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def now
|
157
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -3,8 +3,9 @@
|
|
3
3
|
module KnapsackPro
|
4
4
|
class Presenter
|
5
5
|
class << self
|
6
|
-
def global_time
|
7
|
-
|
6
|
+
def global_time(time = nil)
|
7
|
+
time = KnapsackPro.tracker.global_time if time.nil?
|
8
|
+
global_time = pretty_seconds(time)
|
8
9
|
"Global time execution for tests: #{global_time}"
|
9
10
|
end
|
10
11
|
|
data/lib/knapsack_pro/report.rb
CHANGED
@@ -2,15 +2,17 @@
|
|
2
2
|
|
3
3
|
module KnapsackPro
|
4
4
|
class Report
|
5
|
-
def self.save
|
6
|
-
|
5
|
+
def self.save(tests = nil)
|
6
|
+
if tests.nil?
|
7
|
+
tests = KnapsackPro.tracker.to_a
|
8
|
+
end
|
7
9
|
|
8
|
-
if
|
10
|
+
if tests.empty?
|
9
11
|
KnapsackPro.logger.warn("No test files were executed on this CI node.")
|
10
12
|
KnapsackPro.logger.debug("When you use knapsack_pro Regular Mode, the reason for no tests executing might be a very narrow tests list. Most likely, you run only tests with a specified tag, and there were fewer test files with the tag than parallel CI nodes.")
|
11
13
|
end
|
12
14
|
|
13
|
-
create_build_subset(
|
15
|
+
create_build_subset(tests)
|
14
16
|
end
|
15
17
|
|
16
18
|
def self.save_subset_queue_to_file
|
@@ -30,11 +32,13 @@ module KnapsackPro
|
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
33
|
-
def self.save_node_queue_to_api
|
34
|
-
test_files
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
def self.save_node_queue_to_api(test_files = nil)
|
36
|
+
if test_files.nil?
|
37
|
+
test_files = []
|
38
|
+
Dir.glob("#{queue_path}/*.json").each do |file|
|
39
|
+
report = JSON.parse(File.read(file))
|
40
|
+
test_files += report
|
41
|
+
end
|
38
42
|
end
|
39
43
|
|
40
44
|
if test_files.empty?
|