knapsack_pro 6.0.4 → 7.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 +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
|