knapsack_pro 5.7.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 +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