knapsack_pro 5.7.0 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +2 -0
- data/CHANGELOG.md +10 -0
- data/README.md +1 -1
- data/bin/test +15 -0
- data/knapsack_pro.gemspec +5 -5
- data/lib/knapsack_pro/adapters/rspec_adapter.rb +45 -44
- 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/rspec_adapter_spec.rb +68 -158
- 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,15 @@
|
|
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
|
+
|
3
13
|
### 5.7.0
|
4
14
|
|
5
15
|
* Performance improvement: don't run `rake knapsack_pro:rspec_test_example_detector` when no slow test files are detected for RSpec.
|
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
|
|
@@ -1,5 +1,7 @@
|
|
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
|
@@ -54,63 +56,61 @@ module KnapsackPro
|
|
54
56
|
parsed_options(cli_args)&.[](:order)
|
55
57
|
end
|
56
58
|
|
57
|
-
def self.
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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')
|
65
69
|
end
|
66
|
-
else
|
67
|
-
until example_group[:parent_example_group].nil?
|
68
|
-
example_group = example_group[:parent_example_group]
|
69
|
-
end
|
70
|
-
end
|
71
70
|
|
72
|
-
|
71
|
+
return ''
|
73
72
|
end
|
74
73
|
|
75
|
-
def
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
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
|
80
78
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
KnapsackPro.tracker.stop_timer
|
90
|
-
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
|
91
87
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
current_test_path
|
97
|
-
end
|
88
|
+
def bind_time_tracker
|
89
|
+
ensure_no_focus!
|
90
|
+
log_batch_duration
|
91
|
+
end
|
98
92
|
|
93
|
+
def ensure_no_focus!
|
94
|
+
::RSpec.configure do |config|
|
95
|
+
config.around(:each) do |example|
|
99
96
|
if example.metadata[:focus] && KnapsackPro::Adapters::RSpecAdapter.rspec_configuration.filter.rules[:focus]
|
100
|
-
|
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}"
|
101
101
|
end
|
102
102
|
|
103
103
|
example.run
|
104
104
|
end
|
105
|
+
end
|
106
|
+
end
|
105
107
|
|
106
|
-
|
107
|
-
|
108
|
-
# stop timer to count time for the very last executed test example
|
109
|
-
KnapsackPro.tracker.stop_timer
|
110
|
-
end
|
111
|
-
|
108
|
+
def log_batch_duration
|
109
|
+
::RSpec.configure do |config|
|
112
110
|
config.after(:suite) do
|
113
|
-
KnapsackPro
|
111
|
+
time_tracker = KnapsackPro::Formatters::TimeTrackerFetcher.call
|
112
|
+
formatted = KnapsackPro::Presenter.global_time(time_tracker.batch_duration)
|
113
|
+
KnapsackPro.logger.debug(formatted)
|
114
114
|
end
|
115
115
|
end
|
116
116
|
end
|
@@ -118,7 +118,8 @@ module KnapsackPro
|
|
118
118
|
def bind_save_report
|
119
119
|
::RSpec.configure do |config|
|
120
120
|
config.after(:suite) do
|
121
|
-
KnapsackPro::
|
121
|
+
time_tracker = KnapsackPro::Formatters::TimeTrackerFetcher.call
|
122
|
+
KnapsackPro::Report.save(time_tracker.batch)
|
122
123
|
end
|
123
124
|
end
|
124
125
|
end
|
@@ -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?
|
@@ -8,6 +8,8 @@ module KnapsackPro
|
|
8
8
|
|
9
9
|
def self.run(args)
|
10
10
|
require 'rspec/core'
|
11
|
+
require_relative '../../formatters/time_tracker'
|
12
|
+
require_relative '../../formatters/time_tracker_fetcher'
|
11
13
|
require_relative '../../formatters/rspec_queue_summary_formatter'
|
12
14
|
require_relative '../../formatters/rspec_queue_profile_formatter_extension'
|
13
15
|
|
@@ -27,6 +29,7 @@ module KnapsackPro
|
|
27
29
|
cli_args += [
|
28
30
|
# shows summary of all tests executed in Queue Mode at the very end
|
29
31
|
'--format', KnapsackPro::Formatters::RSpecQueueSummaryFormatter.to_s,
|
32
|
+
'--format', KnapsackPro::Formatters::TimeTracker.to_s,
|
30
33
|
'--default-path', runner.test_dir,
|
31
34
|
]
|
32
35
|
|
@@ -72,7 +75,8 @@ module KnapsackPro
|
|
72
75
|
|
73
76
|
KnapsackPro::Hooks::Queue.call_after_queue
|
74
77
|
|
75
|
-
KnapsackPro::
|
78
|
+
time_tracker = KnapsackPro::Formatters::TimeTrackerFetcher.call
|
79
|
+
KnapsackPro::Report.save_node_queue_to_api(time_tracker&.queue(all_test_file_paths) || [])
|
76
80
|
|
77
81
|
return {
|
78
82
|
status: :completed,
|
@@ -82,15 +86,12 @@ module KnapsackPro
|
|
82
86
|
subset_queue_id = KnapsackPro::Config::EnvGenerator.set_subset_queue_id
|
83
87
|
ENV['KNAPSACK_PRO_SUBSET_QUEUE_ID'] = subset_queue_id
|
84
88
|
|
85
|
-
KnapsackPro.tracker.reset!
|
86
|
-
KnapsackPro.tracker.set_prerun_tests(test_file_paths)
|
87
|
-
|
88
89
|
KnapsackPro::Hooks::Queue.call_before_subset_queue
|
89
90
|
|
90
91
|
all_test_file_paths += test_file_paths
|
91
92
|
cli_args = args + test_file_paths
|
92
93
|
|
93
|
-
|
94
|
+
ensure_spec_opts_have_knapsack_pro_formatters
|
94
95
|
options = ::RSpec::Core::ConfigurationOptions.new(cli_args)
|
95
96
|
rspec_runner = ::RSpec::Core::Runner.new(options)
|
96
97
|
|
@@ -99,7 +100,7 @@ module KnapsackPro
|
|
99
100
|
exitstatus = exit_code if exit_code != 0
|
100
101
|
rescue Exception => exception
|
101
102
|
KnapsackPro.logger.error("Having exception when running RSpec: #{exception.inspect}")
|
102
|
-
KnapsackPro::Formatters::RSpecQueueSummaryFormatter.print_exit_summary
|
103
|
+
KnapsackPro::Formatters::RSpecQueueSummaryFormatter.print_exit_summary(all_test_file_paths)
|
103
104
|
KnapsackPro::Hooks::Queue.call_after_subset_queue
|
104
105
|
KnapsackPro::Hooks::Queue.call_after_queue
|
105
106
|
Kernel.exit(1)
|
@@ -121,8 +122,6 @@ module KnapsackPro
|
|
121
122
|
|
122
123
|
KnapsackPro::Hooks::Queue.call_after_subset_queue
|
123
124
|
|
124
|
-
KnapsackPro::Report.save_subset_queue_to_file
|
125
|
-
|
126
125
|
return {
|
127
126
|
status: :next,
|
128
127
|
runner: runner,
|
@@ -135,13 +134,23 @@ module KnapsackPro
|
|
135
134
|
end
|
136
135
|
end
|
137
136
|
|
138
|
-
def self.
|
139
|
-
|
137
|
+
def self.ensure_spec_opts_have_knapsack_pro_formatters
|
138
|
+
return unless ENV['SPEC_OPTS']
|
139
|
+
|
140
|
+
if [
|
141
|
+
ENV['SPEC_OPTS'].include?(KnapsackPro::Formatters::RSpecQueueSummaryFormatter.to_s),
|
142
|
+
ENV['SPEC_OPTS'].include?(KnapsackPro::Formatters::TimeTracker.to_s),
|
143
|
+
].all?
|
144
|
+
return
|
145
|
+
end
|
140
146
|
|
141
|
-
|
142
|
-
|
147
|
+
unless ENV['SPEC_OPTS'].include?(KnapsackPro::Formatters::RSpecQueueSummaryFormatter.to_s)
|
148
|
+
ENV['SPEC_OPTS'] = "#{ENV['SPEC_OPTS']} --format #{KnapsackPro::Formatters::RSpecQueueSummaryFormatter}"
|
149
|
+
end
|
143
150
|
|
144
|
-
ENV['SPEC_OPTS']
|
151
|
+
unless ENV['SPEC_OPTS'].include?(KnapsackPro::Formatters::TimeTracker.to_s)
|
152
|
+
ENV['SPEC_OPTS'] = "#{ENV['SPEC_OPTS']} --format #{KnapsackPro::Formatters::TimeTracker}"
|
153
|
+
end
|
145
154
|
end
|
146
155
|
|
147
156
|
private
|
@@ -158,7 +167,9 @@ module KnapsackPro
|
|
158
167
|
KnapsackPro.logger.info("To retry all the tests assigned to this CI node, please run the following command on your machine:")
|
159
168
|
end
|
160
169
|
|
161
|
-
stringified_cli_args = cli_args.join(' ')
|
170
|
+
stringified_cli_args = cli_args.join(' ')
|
171
|
+
.sub(" --format #{KnapsackPro::Formatters::RSpecQueueSummaryFormatter}", '')
|
172
|
+
.sub(" --format #{KnapsackPro::Formatters::TimeTracker}", '')
|
162
173
|
|
163
174
|
KnapsackPro.logger.info(
|
164
175
|
"bundle exec rspec #{stringified_cli_args} " +
|
@@ -19,8 +19,6 @@ module KnapsackPro
|
|
19
19
|
cli_args = (args || '').split
|
20
20
|
adapter_class.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!(cli_args)
|
21
21
|
|
22
|
-
KnapsackPro.tracker.set_prerun_tests(runner.test_file_paths)
|
23
|
-
|
24
22
|
require 'rspec/core/rake_task'
|
25
23
|
|
26
24
|
task_name = 'knapsack_pro:rspec_run'
|
@@ -33,12 +31,30 @@ module KnapsackPro
|
|
33
31
|
# because pattern does not accept test example path like spec/a_spec.rb[1:2]
|
34
32
|
# instead we pass test files and test example paths to t.rspec_opts
|
35
33
|
t.pattern = []
|
36
|
-
t.rspec_opts = "#{args} --default-path #{runner.test_dir} #{runner.stringify_test_file_paths}"
|
34
|
+
t.rspec_opts = "#{args} #{self.formatters} --default-path #{runner.test_dir} #{runner.stringify_test_file_paths}"
|
37
35
|
t.verbose = KnapsackPro::Config::Env.log_level < ::Logger::WARN
|
38
36
|
end
|
39
37
|
Rake::Task[task_name].invoke
|
40
38
|
end
|
41
39
|
end
|
40
|
+
|
41
|
+
# Use RSpec::Core::ConfigurationOptions to respect external configurations like .rspec
|
42
|
+
def self.formatters
|
43
|
+
require_relative '../formatters/time_tracker'
|
44
|
+
|
45
|
+
formatters = ::RSpec::Core::ConfigurationOptions
|
46
|
+
.new([])
|
47
|
+
.options
|
48
|
+
.fetch(:formatters, [])
|
49
|
+
.map do |formatter, output|
|
50
|
+
arg = "--format #{formatter}"
|
51
|
+
arg += " --out #{output}" if output
|
52
|
+
arg
|
53
|
+
end
|
54
|
+
formatters = ['--format progress'] if formatters.empty?
|
55
|
+
formatters += ["--format #{KnapsackPro::Formatters::TimeTracker}"]
|
56
|
+
formatters.join(' ')
|
57
|
+
end
|
42
58
|
end
|
43
59
|
end
|
44
60
|
end
|
data/lib/knapsack_pro/tracker.rb
CHANGED
@@ -8,11 +8,10 @@ module KnapsackPro
|
|
8
8
|
# to better allocate it in Queue Mode for future CI build runs
|
9
9
|
DEFAULT_TEST_FILE_TIME = 0.0 # seconds
|
10
10
|
|
11
|
-
attr_reader :
|
11
|
+
attr_reader :global_time, :test_files_with_time, :prerun_tests_loaded
|
12
12
|
attr_writer :current_test_path
|
13
13
|
|
14
14
|
def initialize
|
15
|
-
@global_time_since_beginning = 0
|
16
15
|
KnapsackPro::Config::TempFiles.ensure_temp_directory_exists!
|
17
16
|
FileUtils.mkdir_p(tracker_dir_path)
|
18
17
|
set_defaults
|
@@ -70,12 +69,6 @@ module KnapsackPro
|
|
70
69
|
@prerun_tests_loaded = true
|
71
70
|
end
|
72
71
|
|
73
|
-
def unexecuted_test_files
|
74
|
-
@test_files_with_time.map do |path, hash|
|
75
|
-
path unless hash[:measured_time]
|
76
|
-
end.compact
|
77
|
-
end
|
78
|
-
|
79
72
|
def to_a
|
80
73
|
# When the test files are not loaded in the memory then load them from the disk.
|
81
74
|
# Useful for the Regular Mode when the memory is not shared between tracker instances.
|
@@ -141,7 +134,6 @@ module KnapsackPro
|
|
141
134
|
|
142
135
|
def update_global_time(execution_time)
|
143
136
|
@global_time += execution_time
|
144
|
-
@global_time_since_beginning += execution_time
|
145
137
|
end
|
146
138
|
|
147
139
|
def update_test_file_time(execution_time)
|
data/lib/knapsack_pro/version.rb
CHANGED