knapsack_pro 6.0.4 → 7.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 +80 -19
- data/.github/pull_request_template.md +22 -0
- data/.gitignore +4 -0
- data/CHANGELOG.md +87 -0
- data/Gemfile +9 -0
- data/README.md +0 -4
- data/knapsack_pro.gemspec +2 -1
- data/lib/knapsack_pro/adapters/base_adapter.rb +7 -2
- data/lib/knapsack_pro/adapters/cucumber_adapter.rb +1 -3
- data/lib/knapsack_pro/adapters/rspec_adapter.rb +16 -9
- data/lib/knapsack_pro/config/env.rb +1 -9
- data/lib/knapsack_pro/extensions/rspec_extension.rb +137 -0
- data/lib/knapsack_pro/formatters/time_tracker.rb +10 -26
- data/lib/knapsack_pro/formatters/time_tracker_fetcher.rb +6 -0
- data/lib/knapsack_pro/presenter.rb +1 -1
- data/lib/knapsack_pro/pure/queue/rspec_pure.rb +92 -0
- data/lib/knapsack_pro/runners/queue/base_runner.rb +6 -1
- data/lib/knapsack_pro/runners/queue/cucumber_runner.rb +6 -6
- data/lib/knapsack_pro/runners/queue/minitest_runner.rb +6 -6
- data/lib/knapsack_pro/runners/queue/rspec_runner.rb +124 -173
- data/lib/knapsack_pro/urls.rb +2 -0
- data/lib/knapsack_pro/version.rb +1 -1
- data/lib/knapsack_pro.rb +1 -0
- data/spec/integration/runners/queue/rspec_runner.rb +80 -0
- data/spec/integration/runners/queue/rspec_runner_spec.rb +2232 -0
- data/spec/knapsack_pro/adapters/base_adapter_spec.rb +17 -11
- data/spec/knapsack_pro/adapters/cucumber_adapter_spec.rb +2 -5
- data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +2 -24
- data/spec/knapsack_pro/config/env_spec.rb +1 -35
- data/spec/knapsack_pro/formatters/time_tracker_specs.rb +8 -37
- data/spec/knapsack_pro/hooks/queue_spec.rb +2 -2
- data/spec/knapsack_pro/presenter_spec.rb +1 -1
- data/spec/knapsack_pro/pure/queue/rspec_pure_spec.rb +224 -0
- data/spec/knapsack_pro/runners/queue/cucumber_runner_spec.rb +16 -16
- data/spec/knapsack_pro/runners/queue/minitest_runner_spec.rb +14 -14
- data/spec/knapsack_pro_spec.rb +3 -3
- metadata +17 -12
- data/lib/knapsack_pro/formatters/rspec_queue_profile_formatter_extension.rb +0 -58
- data/lib/knapsack_pro/formatters/rspec_queue_summary_formatter.rb +0 -145
- data/spec/knapsack_pro/runners/queue/rspec_runner_spec.rb +0 -536
@@ -9,24 +9,18 @@ module KnapsackPro
|
|
9
9
|
:example_group_started,
|
10
10
|
:example_started,
|
11
11
|
:example_finished,
|
12
|
-
:example_group_finished
|
13
|
-
:stop
|
12
|
+
:example_group_finished
|
14
13
|
|
15
14
|
attr_reader :output # RSpec < v3.10.2
|
16
15
|
|
17
|
-
# Called at the beginning of each batch,
|
18
|
-
# but only the first instance of this class is used,
|
19
|
-
# so don't rely on the initializer to reset values.
|
20
16
|
def initialize(_output)
|
21
17
|
@output = StringIO.new
|
22
18
|
@time_each = nil
|
23
19
|
@time_all = nil
|
24
20
|
@before_all = 0.0
|
25
21
|
@group = {}
|
26
|
-
@
|
27
|
-
@queue = {}
|
22
|
+
@paths = {}
|
28
23
|
@suite_started = now
|
29
|
-
@batch_started = now
|
30
24
|
end
|
31
25
|
|
32
26
|
def example_group_started(notification)
|
@@ -47,21 +41,15 @@ module KnapsackPro
|
|
47
41
|
def example_group_finished(notification)
|
48
42
|
return unless top_level_group?(notification.group)
|
49
43
|
|
50
|
-
|
51
|
-
@
|
44
|
+
after_all = @time_all.nil? ? 0.0 : now - @time_all
|
45
|
+
add_hooks_time(@group, @before_all, after_all)
|
52
46
|
@before_all = 0.0
|
47
|
+
@paths = merge(@paths, @group)
|
53
48
|
@group = {}
|
54
49
|
end
|
55
50
|
|
56
|
-
# Called at the end of each batch
|
57
|
-
def stop(_notification)
|
58
|
-
@queue = merge(@queue, @batch)
|
59
|
-
@batch = {}
|
60
|
-
@batch_started = now
|
61
|
-
end
|
62
|
-
|
63
51
|
def queue(scheduled_paths)
|
64
|
-
recorded_paths = @
|
52
|
+
recorded_paths = @paths.values.map do |example|
|
65
53
|
KnapsackPro::Adapters::RSpecAdapter.parse_file_path(example[:path])
|
66
54
|
end
|
67
55
|
|
@@ -69,13 +57,13 @@ module KnapsackPro
|
|
69
57
|
object[path] = { path: path, time_execution: 0.0 }
|
70
58
|
end
|
71
59
|
|
72
|
-
merge(@
|
60
|
+
merge(@paths, missing).values.map do |example|
|
73
61
|
example.transform_keys(&:to_s)
|
74
62
|
end
|
75
63
|
end
|
76
64
|
|
77
65
|
def batch
|
78
|
-
@
|
66
|
+
@paths.values.map do |example|
|
79
67
|
example.transform_keys(&:to_s)
|
80
68
|
end
|
81
69
|
end
|
@@ -84,17 +72,13 @@ module KnapsackPro
|
|
84
72
|
now - @suite_started
|
85
73
|
end
|
86
74
|
|
87
|
-
def batch_duration
|
88
|
-
now - @batch_started
|
89
|
-
end
|
90
|
-
|
91
75
|
def unexecuted_test_files(scheduled_paths)
|
92
|
-
pending_paths =
|
76
|
+
pending_paths = @paths.values
|
93
77
|
.filter { |example| example[:time_execution] == 0.0 }
|
94
78
|
.map { |example| example[:path] }
|
95
79
|
|
96
80
|
not_run_paths = scheduled_paths -
|
97
|
-
|
81
|
+
@paths.values
|
98
82
|
.map { |example| example[:path] }
|
99
83
|
|
100
84
|
pending_paths + not_run_paths
|
@@ -9,6 +9,12 @@ module KnapsackPro
|
|
9
9
|
.formatters
|
10
10
|
.find { |f| f.class.to_s == "KnapsackPro::Formatters::TimeTracker" }
|
11
11
|
end
|
12
|
+
|
13
|
+
def self.unexecuted_test_files(scheduled_paths)
|
14
|
+
time_tracker = call
|
15
|
+
return [] unless time_tracker
|
16
|
+
time_tracker.unexecuted_test_files(scheduled_paths)
|
17
|
+
end
|
12
18
|
end
|
13
19
|
end
|
14
20
|
end
|
@@ -6,7 +6,7 @@ module KnapsackPro
|
|
6
6
|
def global_time(time = nil)
|
7
7
|
time = KnapsackPro.tracker.global_time if time.nil?
|
8
8
|
global_time = pretty_seconds(time)
|
9
|
-
"Global
|
9
|
+
"Global test execution duration: #{global_time}"
|
10
10
|
end
|
11
11
|
|
12
12
|
def pretty_seconds(seconds)
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KnapsackPro
|
4
|
+
module Pure
|
5
|
+
module Queue
|
6
|
+
class RSpecPure
|
7
|
+
FAILURE_EXIT_CODE = 1
|
8
|
+
FORMATTERS = [
|
9
|
+
'KnapsackPro::Formatters::TimeTracker',
|
10
|
+
]
|
11
|
+
|
12
|
+
def add_knapsack_pro_formatters_to(spec_opts)
|
13
|
+
return spec_opts unless spec_opts
|
14
|
+
return spec_opts if FORMATTERS.all? { |formatter| spec_opts.include?(formatter) }
|
15
|
+
|
16
|
+
FORMATTERS.each do |formatter|
|
17
|
+
next if spec_opts.include?(formatter)
|
18
|
+
spec_opts += " --format #{formatter}"
|
19
|
+
end
|
20
|
+
|
21
|
+
spec_opts
|
22
|
+
end
|
23
|
+
|
24
|
+
def error_exit_code(rspec_error_exit_code)
|
25
|
+
rspec_error_exit_code || FAILURE_EXIT_CODE
|
26
|
+
end
|
27
|
+
|
28
|
+
def args_with_seed_option_added_when_viable(order_option, seed, args)
|
29
|
+
return args if order_option && !order_option.include?('rand')
|
30
|
+
return args if order_option && order_option.to_s.split(':')[1]
|
31
|
+
|
32
|
+
return args unless seed.used?
|
33
|
+
|
34
|
+
args + ['--seed', seed.value]
|
35
|
+
end
|
36
|
+
|
37
|
+
def prepare_cli_args(args, has_format_option, test_dir)
|
38
|
+
(args || '').split
|
39
|
+
.yield_self { args_with_at_least_one_formatter(_1, has_format_option) }
|
40
|
+
.yield_self { args_with_default_options(_1, test_dir) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def rspec_command(args, test_file_paths, scope)
|
44
|
+
messages = []
|
45
|
+
return messages if test_file_paths.empty?
|
46
|
+
|
47
|
+
case scope
|
48
|
+
when :batch_finished
|
49
|
+
messages << 'To retry the last batch of tests fetched from the Queue API, please run the following command on your machine:'
|
50
|
+
when :queue_finished
|
51
|
+
messages << 'To retry all the tests assigned to this CI node, please run the following command on your machine:'
|
52
|
+
end
|
53
|
+
|
54
|
+
stringified_cli_args = args.join(' ')
|
55
|
+
FORMATTERS.each do |formatter|
|
56
|
+
stringified_cli_args.sub!(" --format #{formatter}", '')
|
57
|
+
end
|
58
|
+
|
59
|
+
messages << "bundle exec rspec #{stringified_cli_args} " + KnapsackPro::TestFilePresenter.stringify_paths(test_file_paths)
|
60
|
+
|
61
|
+
messages
|
62
|
+
end
|
63
|
+
|
64
|
+
def exit_summary(unexecuted_test_files)
|
65
|
+
return if unexecuted_test_files.empty?
|
66
|
+
|
67
|
+
"Unexecuted tests on this CI node (including pending tests): #{unexecuted_test_files.join(' ')}"
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def args_with_at_least_one_formatter(cli_args, has_format_option)
|
73
|
+
return cli_args if has_format_option
|
74
|
+
|
75
|
+
cli_args + ['--format', 'progress']
|
76
|
+
end
|
77
|
+
|
78
|
+
def args_with_default_options(cli_args, test_dir)
|
79
|
+
new_cli_args = cli_args + [
|
80
|
+
'--default-path', test_dir,
|
81
|
+
]
|
82
|
+
|
83
|
+
FORMATTERS.each do |formatter|
|
84
|
+
new_cli_args += ['--format', formatter]
|
85
|
+
end
|
86
|
+
|
87
|
+
new_cli_args
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -4,6 +4,7 @@ module KnapsackPro
|
|
4
4
|
module Runners
|
5
5
|
module Queue
|
6
6
|
class BaseRunner
|
7
|
+
TerminationError = Class.new(StandardError)
|
7
8
|
TERMINATION_SIGNALS = %w(HUP INT TERM ABRT QUIT USR1 USR2)
|
8
9
|
|
9
10
|
@@terminate_process = false
|
@@ -42,13 +43,17 @@ module KnapsackPro
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def self.handle_signal!
|
45
|
-
raise 'Knapsack Pro process was terminated!' if @@terminate_process
|
46
|
+
raise TerminationError.new('Knapsack Pro process was terminated!') if @@terminate_process
|
46
47
|
end
|
47
48
|
|
48
49
|
def self.set_terminate_process
|
49
50
|
@@terminate_process = true
|
50
51
|
end
|
51
52
|
|
53
|
+
def set_terminate_process
|
54
|
+
self.class.set_terminate_process
|
55
|
+
end
|
56
|
+
|
52
57
|
def trap_signals
|
53
58
|
TERMINATION_SIGNALS.each do |signal|
|
54
59
|
Signal.trap(signal) {
|
@@ -21,7 +21,7 @@ module KnapsackPro
|
|
21
21
|
can_initialize_queue: true,
|
22
22
|
args: args,
|
23
23
|
exitstatus: 0,
|
24
|
-
|
24
|
+
node_test_file_paths: [],
|
25
25
|
}
|
26
26
|
while accumulator[:status] == :next
|
27
27
|
handle_signal!
|
@@ -36,15 +36,15 @@ module KnapsackPro
|
|
36
36
|
can_initialize_queue = accumulator.fetch(:can_initialize_queue)
|
37
37
|
args = accumulator.fetch(:args)
|
38
38
|
exitstatus = accumulator.fetch(:exitstatus)
|
39
|
-
|
39
|
+
node_test_file_paths = accumulator.fetch(:node_test_file_paths)
|
40
40
|
|
41
41
|
test_file_paths = runner.test_file_paths(
|
42
42
|
can_initialize_queue: can_initialize_queue,
|
43
|
-
executed_test_files:
|
43
|
+
executed_test_files: node_test_file_paths
|
44
44
|
)
|
45
45
|
|
46
46
|
if test_file_paths.empty?
|
47
|
-
unless
|
47
|
+
unless node_test_file_paths.empty?
|
48
48
|
KnapsackPro::Adapters::CucumberAdapter.verify_bind_method_called
|
49
49
|
end
|
50
50
|
|
@@ -65,7 +65,7 @@ module KnapsackPro
|
|
65
65
|
|
66
66
|
KnapsackPro::Hooks::Queue.call_before_subset_queue
|
67
67
|
|
68
|
-
|
68
|
+
node_test_file_paths += test_file_paths
|
69
69
|
|
70
70
|
result_exitstatus = cucumber_run(runner, test_file_paths, args)
|
71
71
|
exitstatus = result_exitstatus if result_exitstatus != 0
|
@@ -80,7 +80,7 @@ module KnapsackPro
|
|
80
80
|
can_initialize_queue: false,
|
81
81
|
args: args,
|
82
82
|
exitstatus: exitstatus,
|
83
|
-
|
83
|
+
node_test_file_paths: node_test_file_paths,
|
84
84
|
}
|
85
85
|
end
|
86
86
|
end
|
@@ -32,7 +32,7 @@ module KnapsackPro
|
|
32
32
|
can_initialize_queue: true,
|
33
33
|
args: cli_args,
|
34
34
|
exitstatus: 0,
|
35
|
-
|
35
|
+
node_test_file_paths: [],
|
36
36
|
}
|
37
37
|
while accumulator[:status] == :next
|
38
38
|
handle_signal!
|
@@ -47,15 +47,15 @@ module KnapsackPro
|
|
47
47
|
can_initialize_queue = accumulator.fetch(:can_initialize_queue)
|
48
48
|
args = accumulator.fetch(:args)
|
49
49
|
exitstatus = accumulator.fetch(:exitstatus)
|
50
|
-
|
50
|
+
node_test_file_paths = accumulator.fetch(:node_test_file_paths)
|
51
51
|
|
52
52
|
test_file_paths = runner.test_file_paths(
|
53
53
|
can_initialize_queue: can_initialize_queue,
|
54
|
-
executed_test_files:
|
54
|
+
executed_test_files: node_test_file_paths
|
55
55
|
)
|
56
56
|
|
57
57
|
if test_file_paths.empty?
|
58
|
-
unless
|
58
|
+
unless node_test_file_paths.empty?
|
59
59
|
KnapsackPro::Adapters::MinitestAdapter.verify_bind_method_called
|
60
60
|
end
|
61
61
|
|
@@ -76,7 +76,7 @@ module KnapsackPro
|
|
76
76
|
|
77
77
|
KnapsackPro::Hooks::Queue.call_before_subset_queue
|
78
78
|
|
79
|
-
|
79
|
+
node_test_file_paths += test_file_paths
|
80
80
|
|
81
81
|
result = minitest_run(runner, test_file_paths, args)
|
82
82
|
exitstatus = 1 unless result
|
@@ -91,7 +91,7 @@ module KnapsackPro
|
|
91
91
|
can_initialize_queue: false,
|
92
92
|
args: args,
|
93
93
|
exitstatus: exitstatus,
|
94
|
-
|
94
|
+
node_test_file_paths: node_test_file_paths,
|
95
95
|
}
|
96
96
|
end
|
97
97
|
end
|
@@ -4,223 +4,174 @@ module KnapsackPro
|
|
4
4
|
module Runners
|
5
5
|
module Queue
|
6
6
|
class RSpecRunner < BaseRunner
|
7
|
-
|
8
|
-
|
9
|
-
def self.run(args)
|
7
|
+
def self.run(args, stream_error = $stderr, stream_out = $stdout)
|
10
8
|
require 'rspec/core'
|
9
|
+
require_relative '../../extensions/rspec_extension'
|
11
10
|
require_relative '../../formatters/time_tracker'
|
12
11
|
require_relative '../../formatters/time_tracker_fetcher'
|
13
|
-
|
14
|
-
|
12
|
+
|
13
|
+
KnapsackPro::Extensions::RSpecExtension.setup!
|
15
14
|
|
16
15
|
ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN'] = KnapsackPro::Config::Env.test_suite_token_rspec
|
17
|
-
ENV['KNAPSACK_PRO_QUEUE_RECORDING_ENABLED'] = 'true'
|
18
|
-
ENV['KNAPSACK_PRO_QUEUE_ID'] = KnapsackPro::Config::EnvGenerator.set_queue_id
|
19
16
|
|
20
|
-
KnapsackPro::
|
21
|
-
runner = new(adapter_class)
|
22
|
-
|
23
|
-
cli_args = (args || '').split
|
24
|
-
adapter_class.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!(cli_args)
|
25
|
-
|
26
|
-
# when format option is not defined by user then use progress formatter to show tests execution progress
|
27
|
-
cli_args += ['--format', 'progress'] unless adapter_class.has_format_option?(cli_args)
|
28
|
-
|
29
|
-
cli_args += [
|
30
|
-
# shows summary of all tests executed in Queue Mode at the very end
|
31
|
-
'--format', KnapsackPro::Formatters::RSpecQueueSummaryFormatter.to_s,
|
32
|
-
'--format', KnapsackPro::Formatters::TimeTracker.to_s,
|
33
|
-
'--default-path', runner.test_dir,
|
34
|
-
]
|
35
|
-
|
36
|
-
accumulator = {
|
37
|
-
status: :next,
|
38
|
-
runner: runner,
|
39
|
-
can_initialize_queue: true,
|
40
|
-
args: cli_args,
|
41
|
-
exitstatus: 0,
|
42
|
-
all_test_file_paths: [],
|
43
|
-
}
|
44
|
-
while accumulator[:status] == :next
|
45
|
-
handle_signal!
|
46
|
-
accumulator = run_tests(accumulator)
|
47
|
-
end
|
17
|
+
rspec_pure = KnapsackPro::Pure::Queue::RSpecPure.new
|
48
18
|
|
49
|
-
|
19
|
+
queue_runner = new(KnapsackPro::Adapters::RSpecAdapter, rspec_pure, args, stream_error, stream_out)
|
20
|
+
queue_runner.run
|
50
21
|
end
|
51
22
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
23
|
+
def initialize(adapter_class, rspec_pure, args, stream_error, stream_out)
|
24
|
+
super(adapter_class)
|
25
|
+
@adapter_class = adapter_class
|
26
|
+
@rspec_pure = rspec_pure
|
27
|
+
has_format_option = @adapter_class.has_format_option?((args || '').split)
|
28
|
+
@cli_args = rspec_pure.prepare_cli_args(args, has_format_option, test_dir)
|
29
|
+
@stream_error = stream_error
|
30
|
+
@stream_out = stream_out
|
31
|
+
@node_test_file_paths = []
|
32
|
+
@rspec_runner = nil # RSpec::Core::Runner is lazy initialized
|
33
|
+
end
|
58
34
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
35
|
+
# Based on:
|
36
|
+
# https://github.com/rspec/rspec-core/blob/f8c8880dabd8f0544a6f91d8d4c857c1bd8df903/lib/rspec/core/runner.rb#L85
|
37
|
+
#
|
38
|
+
# @return [Fixnum] exit status code.
|
39
|
+
# 0 if all specs passed,
|
40
|
+
# or the configured failure exit code (1 by default) if specs failed.
|
41
|
+
def run
|
42
|
+
pre_run_setup
|
43
|
+
|
44
|
+
if @rspec_runner.knapsack__wants_to_quit?
|
45
|
+
exit_code = @rspec_runner.knapsack__exit_early
|
46
|
+
Kernel.exit(exit_code)
|
47
|
+
end
|
48
|
+
|
49
|
+
begin
|
50
|
+
exit_code = @rspec_runner.knapsack__run_specs(self)
|
51
|
+
rescue KnapsackPro::Runners::Queue::BaseRunner::TerminationError
|
52
|
+
exit_code = @rspec_pure.error_exit_code(@rspec_runner.knapsack__error_exit_code)
|
53
|
+
Kernel.exit(exit_code)
|
54
|
+
rescue Exception => exception
|
55
|
+
KnapsackPro.logger.error("An unexpected exception happened. RSpec cannot handle it. The exception: #{exception.inspect}")
|
63
56
|
|
64
|
-
|
65
|
-
|
66
|
-
KnapsackPro::Adapters::RSpecAdapter.verify_bind_method_called
|
57
|
+
message = @rspec_pure.exit_summary(unexecuted_test_files)
|
58
|
+
KnapsackPro.logger.warn(message) if message
|
67
59
|
|
68
|
-
|
69
|
-
|
60
|
+
exit_code = @rspec_pure.error_exit_code(@rspec_runner.knapsack__error_exit_code)
|
61
|
+
Kernel.exit(exit_code)
|
62
|
+
end
|
70
63
|
|
71
|
-
|
64
|
+
post_run_tasks(exit_code)
|
65
|
+
end
|
72
66
|
|
73
|
-
|
74
|
-
|
67
|
+
def with_batch
|
68
|
+
can_initialize_queue = true
|
75
69
|
|
76
|
-
|
70
|
+
loop do
|
71
|
+
handle_signal!
|
72
|
+
test_file_paths = pull_tests_from_queue(can_initialize_queue: can_initialize_queue)
|
73
|
+
can_initialize_queue = false
|
77
74
|
|
78
|
-
|
79
|
-
KnapsackPro::Report.save_node_queue_to_api(time_tracker&.queue(all_test_file_paths) || [])
|
75
|
+
break if test_file_paths.empty?
|
80
76
|
|
81
|
-
return {
|
82
|
-
status: :completed,
|
83
|
-
exitstatus: exitstatus,
|
84
|
-
}
|
85
|
-
else
|
86
77
|
subset_queue_id = KnapsackPro::Config::EnvGenerator.set_subset_queue_id
|
87
78
|
ENV['KNAPSACK_PRO_SUBSET_QUEUE_ID'] = subset_queue_id
|
88
79
|
|
89
80
|
KnapsackPro::Hooks::Queue.call_before_subset_queue
|
90
81
|
|
91
|
-
|
92
|
-
cli_args = args + test_file_paths
|
93
|
-
|
94
|
-
ensure_spec_opts_have_knapsack_pro_formatters
|
95
|
-
options = ::RSpec::Core::ConfigurationOptions.new(cli_args)
|
96
|
-
rspec_runner = ::RSpec::Core::Runner.new(options)
|
97
|
-
|
98
|
-
begin
|
99
|
-
exit_code = rspec_runner.run($stderr, $stdout)
|
100
|
-
exitstatus = exit_code if exit_code != 0
|
101
|
-
rescue Exception => exception
|
102
|
-
KnapsackPro.logger.error("Having exception when running RSpec: #{exception.inspect}")
|
103
|
-
KnapsackPro::Formatters::RSpecQueueSummaryFormatter.print_exit_summary(all_test_file_paths)
|
104
|
-
KnapsackPro::Hooks::Queue.call_after_subset_queue
|
105
|
-
KnapsackPro::Hooks::Queue.call_after_queue
|
106
|
-
Kernel.exit(1)
|
107
|
-
raise
|
108
|
-
else
|
109
|
-
if rspec_runner.world.wants_to_quit
|
110
|
-
KnapsackPro.logger.warn('RSpec wants to quit.')
|
111
|
-
set_terminate_process
|
112
|
-
end
|
113
|
-
if rspec_runner.world.respond_to?(:rspec_is_quitting) && rspec_runner.world.rspec_is_quitting
|
114
|
-
KnapsackPro.logger.warn('RSpec is quitting.')
|
115
|
-
set_terminate_process
|
116
|
-
end
|
117
|
-
|
118
|
-
printable_args = args_with_seed_option_added_when_viable(args, rspec_runner)
|
119
|
-
log_rspec_command(printable_args, test_file_paths, :subset_queue)
|
120
|
-
|
121
|
-
rspec_clear_examples
|
122
|
-
|
123
|
-
KnapsackPro::Hooks::Queue.call_after_subset_queue
|
124
|
-
|
125
|
-
return {
|
126
|
-
status: :next,
|
127
|
-
runner: runner,
|
128
|
-
can_initialize_queue: false,
|
129
|
-
args: args,
|
130
|
-
exitstatus: exitstatus,
|
131
|
-
all_test_file_paths: all_test_file_paths,
|
132
|
-
}
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
82
|
+
yield test_file_paths
|
136
83
|
|
137
|
-
|
138
|
-
return unless ENV['SPEC_OPTS']
|
84
|
+
KnapsackPro::Hooks::Queue.call_after_subset_queue
|
139
85
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
86
|
+
if @rspec_runner.knapsack__wants_to_quit?
|
87
|
+
KnapsackPro.logger.warn('RSpec wants to quit.')
|
88
|
+
set_terminate_process
|
89
|
+
end
|
90
|
+
if @rspec_runner.knapsack__rspec_is_quitting?
|
91
|
+
KnapsackPro.logger.warn('RSpec is quitting.')
|
92
|
+
set_terminate_process
|
93
|
+
end
|
146
94
|
|
147
|
-
|
148
|
-
ENV['SPEC_OPTS'] = "#{ENV['SPEC_OPTS']} --format #{KnapsackPro::Formatters::RSpecQueueSummaryFormatter}"
|
95
|
+
log_rspec_batch_command(test_file_paths)
|
149
96
|
end
|
97
|
+
end
|
150
98
|
|
151
|
-
|
152
|
-
|
153
|
-
|
99
|
+
def handle_signal!
|
100
|
+
self.class.handle_signal!
|
101
|
+
end
|
102
|
+
|
103
|
+
def log_fail_fast_limit_met
|
104
|
+
KnapsackPro.logger.warn('Test execution has been canceled because the RSpec --fail-fast option is enabled. It will cause other CI nodes to run tests longer because they need to consume more tests from the Knapsack Pro Queue API.')
|
154
105
|
end
|
155
106
|
|
156
107
|
private
|
157
108
|
|
158
|
-
def
|
159
|
-
|
109
|
+
def pre_run_setup
|
110
|
+
ENV['KNAPSACK_PRO_QUEUE_RECORDING_ENABLED'] = 'true'
|
111
|
+
ENV['KNAPSACK_PRO_QUEUE_ID'] = KnapsackPro::Config::EnvGenerator.set_queue_id
|
112
|
+
|
113
|
+
KnapsackPro::Config::Env.set_test_runner_adapter(@adapter_class)
|
114
|
+
|
115
|
+
ENV['SPEC_OPTS'] = @rspec_pure.add_knapsack_pro_formatters_to(ENV['SPEC_OPTS'])
|
116
|
+
@adapter_class.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!(@cli_args)
|
117
|
+
|
118
|
+
rspec_configuration_options = ::RSpec::Core::ConfigurationOptions.new(@cli_args)
|
119
|
+
@rspec_runner = ::RSpec::Core::Runner.new(rspec_configuration_options)
|
120
|
+
@rspec_runner.knapsack__setup(@stream_error, @stream_out)
|
121
|
+
|
122
|
+
ensure_no_deprecated_run_all_when_everything_filtered_option!
|
160
123
|
end
|
161
124
|
|
162
|
-
def
|
163
|
-
|
164
|
-
when :subset_queue
|
165
|
-
KnapsackPro.logger.info("To retry the last batch of tests fetched from the API Queue, please run the following command on your machine:")
|
166
|
-
when :end_of_queue
|
167
|
-
KnapsackPro.logger.info("To retry all the tests assigned to this CI node, please run the following command on your machine:")
|
168
|
-
end
|
125
|
+
def post_run_tasks(exit_code)
|
126
|
+
@adapter_class.verify_bind_method_called
|
169
127
|
|
170
|
-
|
171
|
-
.sub(" --format #{KnapsackPro::Formatters::RSpecQueueSummaryFormatter}", '')
|
172
|
-
.sub(" --format #{KnapsackPro::Formatters::TimeTracker}", '')
|
128
|
+
log_rspec_queue_command
|
173
129
|
|
174
|
-
KnapsackPro.
|
175
|
-
|
176
|
-
|
177
|
-
)
|
130
|
+
time_tracker = KnapsackPro::Formatters::TimeTrackerFetcher.call
|
131
|
+
KnapsackPro::Report.save_node_queue_to_api(time_tracker&.queue(@node_test_file_paths))
|
132
|
+
|
133
|
+
Kernel.exit(exit_code)
|
178
134
|
end
|
179
135
|
|
180
|
-
|
181
|
-
|
182
|
-
#
|
183
|
-
# Keep formatters and report to accumulate info about failed/pending tests
|
184
|
-
def self.rspec_clear_examples
|
185
|
-
if ::RSpec::ExampleGroups.respond_to?(:remove_all_constants)
|
186
|
-
::RSpec::ExampleGroups.remove_all_constants
|
187
|
-
else
|
188
|
-
::RSpec::ExampleGroups.constants.each do |constant|
|
189
|
-
::RSpec::ExampleGroups.__send__(:remove_const, constant)
|
190
|
-
end
|
191
|
-
end
|
192
|
-
::RSpec.world.example_groups.clear
|
193
|
-
::RSpec.configuration.start_time = ::RSpec::Core::Time.now
|
194
|
-
|
195
|
-
if KnapsackPro::Config::Env.rspec_split_by_test_examples?
|
196
|
-
# Reset example group counts to ensure scoped example ids in metadata
|
197
|
-
# have correct index (not increased by each subsequent run).
|
198
|
-
# Solves this problem: https://github.com/rspec/rspec-core/issues/2721
|
199
|
-
::RSpec.world.instance_variable_set(:@example_group_counts_by_spec_file, Hash.new(0))
|
200
|
-
end
|
136
|
+
def ensure_no_deprecated_run_all_when_everything_filtered_option!
|
137
|
+
return unless @rspec_runner.knapsack__deprecated_run_all_when_everything_filtered_enabled?
|
201
138
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
end
|
139
|
+
error_message = "The run_all_when_everything_filtered option is deprecated. See: #{KnapsackPro::Urls::RSPEC__DEPRECATED_RUN_ALL_WHEN_EVERYTHING_FILTERED}"
|
140
|
+
KnapsackPro.logger.error(error_message)
|
141
|
+
raise error_message
|
206
142
|
end
|
207
143
|
|
208
|
-
def
|
209
|
-
|
144
|
+
def pull_tests_from_queue(can_initialize_queue: false)
|
145
|
+
test_file_paths = test_file_paths(
|
146
|
+
can_initialize_queue: can_initialize_queue,
|
147
|
+
executed_test_files: @node_test_file_paths
|
148
|
+
)
|
149
|
+
@node_test_file_paths += test_file_paths
|
150
|
+
test_file_paths
|
151
|
+
end
|
210
152
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
153
|
+
def log_rspec_batch_command(test_file_paths)
|
154
|
+
order_option = @adapter_class.order_option(@cli_args)
|
155
|
+
printable_args = @rspec_pure.args_with_seed_option_added_when_viable(order_option, @rspec_runner.knapsack__seed, @cli_args)
|
156
|
+
messages = @rspec_pure.rspec_command(printable_args, test_file_paths, :batch_finished)
|
157
|
+
log_info_messages(messages)
|
158
|
+
end
|
217
159
|
|
218
|
-
|
219
|
-
|
160
|
+
def log_rspec_queue_command
|
161
|
+
order_option = @adapter_class.order_option(@cli_args)
|
162
|
+
printable_args = @rspec_pure.args_with_seed_option_added_when_viable(order_option, @rspec_runner.knapsack__seed, @cli_args)
|
163
|
+
messages = @rspec_pure.rspec_command(printable_args, @node_test_file_paths, :queue_finished)
|
164
|
+
log_info_messages(messages)
|
165
|
+
end
|
220
166
|
|
221
|
-
|
167
|
+
def log_info_messages(messages)
|
168
|
+
messages.each do |message|
|
169
|
+
KnapsackPro.logger.info(message)
|
170
|
+
end
|
171
|
+
end
|
222
172
|
|
223
|
-
|
173
|
+
def unexecuted_test_files
|
174
|
+
KnapsackPro::Formatters::TimeTrackerFetcher.unexecuted_test_files(@node_test_file_paths)
|
224
175
|
end
|
225
176
|
end
|
226
177
|
end
|