turbo_tests2 3.0.0 → 3.1.1
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
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +78 -1
- data/CONTRIBUTING.md +37 -0
- data/LICENSE.md +22 -0
- data/README.md +22 -38
- data/lib/turbo_tests/cli.rb +65 -10
- data/lib/turbo_tests/json_rows_formatter.rb +64 -18
- data/lib/turbo_tests/reporter.rb +30 -9
- data/lib/turbo_tests/runner.rb +99 -31
- data/lib/turbo_tests/shim.rb +10 -1
- data/lib/turbo_tests/version.rb +1 -1
- data/lib/turbo_tests.rb +7 -5
- data/lib/turbo_tests2/rspec/shared_contexts/simplecov_spawn.rb +6 -2
- data/sig/turbo_tests/version.rbs +0 -1
- data.tar.gz.sig +0 -0
- metadata +23 -16
- metadata.gz.sig +0 -0
|
@@ -20,6 +20,12 @@ RSpec::Core::Runner.singleton_class.prepend(RSpecExt)
|
|
|
20
20
|
module TurboTests
|
|
21
21
|
# An RSpec formatter used for each subprocess during parallel test execution
|
|
22
22
|
class JsonRowsFormatter
|
|
23
|
+
INTERNAL_BACKTRACE_PATTERNS = [
|
|
24
|
+
%r{/bin/turbo_tests2\b},
|
|
25
|
+
%r{/exe/turbo_tests2\b},
|
|
26
|
+
%r{/lib/turbo_tests(?:\.rb|/)}
|
|
27
|
+
].freeze
|
|
28
|
+
|
|
23
29
|
RSpec::Core::Formatters.register(
|
|
24
30
|
self,
|
|
25
31
|
:start,
|
|
@@ -31,6 +37,8 @@ module TurboTests
|
|
|
31
37
|
:example_group_finished,
|
|
32
38
|
:message,
|
|
33
39
|
:seed,
|
|
40
|
+
:deprecation,
|
|
41
|
+
:dump_profile
|
|
34
42
|
)
|
|
35
43
|
|
|
36
44
|
attr_reader :output
|
|
@@ -42,78 +50,115 @@ module TurboTests
|
|
|
42
50
|
def start(notification)
|
|
43
51
|
output_row(
|
|
44
52
|
type: :load_summary,
|
|
45
|
-
summary: load_summary_to_json(notification)
|
|
53
|
+
summary: load_summary_to_json(notification)
|
|
46
54
|
)
|
|
47
55
|
end
|
|
48
56
|
|
|
49
57
|
def example_group_started(notification)
|
|
50
58
|
output_row(
|
|
51
59
|
type: :group_started,
|
|
52
|
-
group: group_to_json(notification)
|
|
60
|
+
group: group_to_json(notification)
|
|
53
61
|
)
|
|
54
62
|
end
|
|
55
63
|
|
|
56
64
|
def example_group_finished(notification)
|
|
57
65
|
output_row(
|
|
58
66
|
type: :group_finished,
|
|
59
|
-
group: group_to_json(notification)
|
|
67
|
+
group: group_to_json(notification)
|
|
60
68
|
)
|
|
61
69
|
end
|
|
62
70
|
|
|
63
71
|
def example_passed(notification)
|
|
64
72
|
output_row(
|
|
65
73
|
type: :example_passed,
|
|
66
|
-
example: example_to_json(notification.example)
|
|
74
|
+
example: example_to_json(notification.example)
|
|
67
75
|
)
|
|
68
76
|
end
|
|
69
77
|
|
|
70
78
|
def example_pending(notification)
|
|
71
79
|
output_row(
|
|
72
80
|
type: :example_pending,
|
|
73
|
-
example: example_to_json(notification.example)
|
|
81
|
+
example: example_to_json(notification.example)
|
|
74
82
|
)
|
|
75
83
|
end
|
|
76
84
|
|
|
77
85
|
def example_failed(notification)
|
|
78
86
|
output_row(
|
|
79
87
|
type: :example_failed,
|
|
80
|
-
example: example_to_json(notification.example)
|
|
88
|
+
example: example_to_json(notification.example)
|
|
81
89
|
)
|
|
82
90
|
end
|
|
83
91
|
|
|
84
92
|
def seed(notification)
|
|
85
93
|
output_row(
|
|
86
94
|
type: :seed,
|
|
87
|
-
seed: notification.seed
|
|
95
|
+
seed: notification.seed
|
|
88
96
|
)
|
|
89
97
|
end
|
|
90
98
|
|
|
91
99
|
def close(_notification)
|
|
92
100
|
output_row(
|
|
93
|
-
type: :close
|
|
101
|
+
type: :close
|
|
94
102
|
)
|
|
95
103
|
end
|
|
96
104
|
|
|
97
105
|
def message(notification)
|
|
98
106
|
output_row(
|
|
99
107
|
type: :message,
|
|
100
|
-
message: notification.message
|
|
108
|
+
message: notification.message
|
|
109
|
+
)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def deprecation(notification)
|
|
113
|
+
output_row(
|
|
114
|
+
type: :deprecation,
|
|
115
|
+
deprecation: deprecation_to_json(notification)
|
|
116
|
+
)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def dump_profile(notification)
|
|
120
|
+
output_row(
|
|
121
|
+
type: :profile,
|
|
122
|
+
profile: profile_to_json(notification)
|
|
101
123
|
)
|
|
102
124
|
end
|
|
103
125
|
|
|
104
126
|
private
|
|
105
127
|
|
|
128
|
+
def profile_to_json(notification)
|
|
129
|
+
{
|
|
130
|
+
duration: notification.duration,
|
|
131
|
+
number_of_examples: notification.number_of_examples,
|
|
132
|
+
examples: notification.examples.map { |example| example_to_json(example) }
|
|
133
|
+
}
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def deprecation_to_json(notification)
|
|
137
|
+
{
|
|
138
|
+
deprecated: notification.deprecated,
|
|
139
|
+
message: notification.message,
|
|
140
|
+
replacement: notification.replacement,
|
|
141
|
+
call_site: notification.call_site
|
|
142
|
+
}
|
|
143
|
+
end
|
|
144
|
+
|
|
106
145
|
def exception_to_json(exception)
|
|
107
146
|
return unless exception
|
|
108
147
|
|
|
109
148
|
{
|
|
110
149
|
class_name: exception.class.name.to_s,
|
|
111
|
-
backtrace: exception.backtrace,
|
|
150
|
+
backtrace: filtered_backtrace(exception.backtrace),
|
|
112
151
|
message: exception.message,
|
|
113
|
-
cause: exception_to_json(exception.cause)
|
|
152
|
+
cause: exception_to_json(exception.cause)
|
|
114
153
|
}
|
|
115
154
|
end
|
|
116
155
|
|
|
156
|
+
def filtered_backtrace(backtrace)
|
|
157
|
+
Array(backtrace).reject do |line|
|
|
158
|
+
INTERNAL_BACKTRACE_PATTERNS.any? { |pattern| line.match?(pattern) }
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
117
162
|
def execution_result_to_json(result)
|
|
118
163
|
{
|
|
119
164
|
example_skipped?: result.example_skipped?,
|
|
@@ -121,13 +166,14 @@ module TurboTests
|
|
|
121
166
|
status: result.status,
|
|
122
167
|
pending_fixed?: result.pending_fixed?,
|
|
123
168
|
exception: exception_to_json(result.exception || result.pending_exception),
|
|
169
|
+
run_time: result.respond_to?(:run_time) ? result.run_time : nil
|
|
124
170
|
}
|
|
125
171
|
end
|
|
126
172
|
|
|
127
173
|
def stack_frame_to_json(frame)
|
|
128
174
|
{
|
|
129
175
|
shared_group_name: frame.shared_group_name,
|
|
130
|
-
inclusion_location: frame.inclusion_location
|
|
176
|
+
inclusion_location: frame.inclusion_location
|
|
131
177
|
}
|
|
132
178
|
end
|
|
133
179
|
|
|
@@ -142,29 +188,29 @@ module TurboTests
|
|
|
142
188
|
example
|
|
143
189
|
.metadata[:shared_group_inclusion_backtrace]
|
|
144
190
|
.map { |frame| stack_frame_to_json(frame) },
|
|
145
|
-
extra_failure_lines: example.metadata[:extra_failure_lines]
|
|
191
|
+
extra_failure_lines: example.metadata[:extra_failure_lines]
|
|
146
192
|
},
|
|
147
|
-
location_rerun_argument: example.location_rerun_argument
|
|
193
|
+
location_rerun_argument: example.location_rerun_argument
|
|
148
194
|
}
|
|
149
195
|
end
|
|
150
196
|
|
|
151
197
|
def load_summary_to_json(notification)
|
|
152
198
|
{
|
|
153
199
|
count: notification.count,
|
|
154
|
-
load_time: notification.load_time
|
|
200
|
+
load_time: notification.load_time
|
|
155
201
|
}
|
|
156
202
|
end
|
|
157
203
|
|
|
158
204
|
def group_to_json(notification)
|
|
159
205
|
{
|
|
160
206
|
group: {
|
|
161
|
-
description: notification.group.description
|
|
162
|
-
}
|
|
207
|
+
description: notification.group.description
|
|
208
|
+
}
|
|
163
209
|
}
|
|
164
210
|
end
|
|
165
211
|
|
|
166
212
|
def output_row(obj)
|
|
167
|
-
output.puts ENV
|
|
213
|
+
output.puts "#{ENV.fetch("RSPEC_FORMATTER_OUTPUT_ID", "")}#{obj.to_json}"
|
|
168
214
|
output.flush
|
|
169
215
|
end
|
|
170
216
|
end
|
data/lib/turbo_tests/reporter.rb
CHANGED
|
@@ -75,7 +75,7 @@ module TurboTests
|
|
|
75
75
|
delegate_to_formatters(:seed, RSpec::Core::Notifications::SeedNotification.new(@seed, @seed_used))
|
|
76
76
|
delegate_to_formatters(
|
|
77
77
|
:start,
|
|
78
|
-
RSpec::Core::Notifications::StartNotification.new(expected_example_count, @load_time)
|
|
78
|
+
RSpec::Core::Notifications::StartNotification.new(expected_example_count, @load_time)
|
|
79
79
|
)
|
|
80
80
|
end
|
|
81
81
|
|
|
@@ -127,6 +127,27 @@ module TurboTests
|
|
|
127
127
|
message(error_message)
|
|
128
128
|
end
|
|
129
129
|
|
|
130
|
+
def deprecation(deprecation)
|
|
131
|
+
notification = RSpec::Core::Notifications::DeprecationNotification.from_hash(
|
|
132
|
+
deprecated: deprecation[:deprecated],
|
|
133
|
+
message: deprecation[:message],
|
|
134
|
+
replacement: deprecation[:replacement],
|
|
135
|
+
call_site: deprecation[:call_site]
|
|
136
|
+
)
|
|
137
|
+
delegate_to_formatters(:deprecation, notification)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def profile(profile)
|
|
141
|
+
examples = profile[:examples].map { |example| FakeExample.from_obj(example) }
|
|
142
|
+
notification = RSpec::Core::Notifications::ProfileNotification.new(
|
|
143
|
+
profile[:duration],
|
|
144
|
+
examples,
|
|
145
|
+
profile[:number_of_examples],
|
|
146
|
+
{}
|
|
147
|
+
)
|
|
148
|
+
delegate_to_formatters(:dump_profile, notification)
|
|
149
|
+
end
|
|
150
|
+
|
|
130
151
|
def finish
|
|
131
152
|
end_time = RSpec::Core::Time.now
|
|
132
153
|
|
|
@@ -137,14 +158,14 @@ module TurboTests
|
|
|
137
158
|
delegate_to_formatters(
|
|
138
159
|
:dump_pending,
|
|
139
160
|
RSpec::Core::Notifications::ExamplesNotification.new(
|
|
140
|
-
self
|
|
141
|
-
)
|
|
161
|
+
self
|
|
162
|
+
)
|
|
142
163
|
)
|
|
143
164
|
delegate_to_formatters(
|
|
144
165
|
:dump_failures,
|
|
145
166
|
RSpec::Core::Notifications::ExamplesNotification.new(
|
|
146
|
-
self
|
|
147
|
-
)
|
|
167
|
+
self
|
|
168
|
+
)
|
|
148
169
|
)
|
|
149
170
|
delegate_to_formatters(
|
|
150
171
|
:dump_summary,
|
|
@@ -154,15 +175,15 @@ module TurboTests
|
|
|
154
175
|
@failed_examples,
|
|
155
176
|
@pending_examples,
|
|
156
177
|
@load_time,
|
|
157
|
-
@errors_outside_of_examples_count
|
|
158
|
-
)
|
|
178
|
+
@errors_outside_of_examples_count
|
|
179
|
+
)
|
|
159
180
|
)
|
|
160
181
|
delegate_to_formatters(
|
|
161
182
|
:seed,
|
|
162
183
|
RSpec::Core::Notifications::SeedNotification.new(
|
|
163
184
|
@seed,
|
|
164
|
-
@seed_used
|
|
165
|
-
)
|
|
185
|
+
@seed_used
|
|
186
|
+
)
|
|
166
187
|
)
|
|
167
188
|
ensure
|
|
168
189
|
delegate_to_formatters(:close, RSpec::Core::Notifications::NullNotification)
|
data/lib/turbo_tests/runner.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
4
|
require "parallel_tests/rspec/runner"
|
|
5
|
+
require "tempfile"
|
|
5
6
|
|
|
6
7
|
require_relative "../utils/hash_extension"
|
|
7
8
|
|
|
@@ -29,6 +30,7 @@ module TurboTests
|
|
|
29
30
|
|
|
30
31
|
start_time = opts.fetch(:start_time) { RSpec::Core::Time.now }
|
|
31
32
|
runtime_log = opts.fetch(:runtime_log, nil)
|
|
33
|
+
example_status_log = opts.fetch(:example_status_log, nil)
|
|
32
34
|
verbose = opts.fetch(:verbose, false)
|
|
33
35
|
fail_fast = opts.fetch(:fail_fast, nil)
|
|
34
36
|
count = opts.fetch(:count, nil)
|
|
@@ -39,7 +41,10 @@ module TurboTests
|
|
|
39
41
|
|
|
40
42
|
use_runtime_info = files == ["spec"]
|
|
41
43
|
|
|
42
|
-
if
|
|
44
|
+
if example_status_log
|
|
45
|
+
runtime_log = runtime_log_from_example_status(example_status_log)
|
|
46
|
+
parallel_options[:runtime_log] = runtime_log
|
|
47
|
+
elsif use_runtime_info
|
|
43
48
|
parallel_options[:runtime_log] = runtime_log
|
|
44
49
|
else
|
|
45
50
|
parallel_options[:group_by] = :filesize
|
|
@@ -56,6 +61,7 @@ module TurboTests
|
|
|
56
61
|
files: files,
|
|
57
62
|
tags: tags,
|
|
58
63
|
runtime_log: runtime_log,
|
|
64
|
+
example_status_log: example_status_log,
|
|
59
65
|
verbose: verbose,
|
|
60
66
|
fail_fast: fail_fast,
|
|
61
67
|
count: count,
|
|
@@ -64,9 +70,24 @@ module TurboTests
|
|
|
64
70
|
print_failed_group: print_failed_group,
|
|
65
71
|
use_runtime_info: use_runtime_info,
|
|
66
72
|
parallel_options: parallel_options,
|
|
67
|
-
nice: nice
|
|
73
|
+
nice: nice
|
|
68
74
|
).run
|
|
69
75
|
end
|
|
76
|
+
|
|
77
|
+
def runtime_log_from_example_status(example_status_log)
|
|
78
|
+
statuses = RSpec::Core::ExampleStatusPersister.load_from(example_status_log)
|
|
79
|
+
runtimes = statuses.each_with_object(Hash.new(0.0)) do |status, sums|
|
|
80
|
+
next unless status.fetch(:status).match?(/pass/i)
|
|
81
|
+
|
|
82
|
+
file_name = RSpec::Core::Example.parse_id(status.fetch(:example_id)).first
|
|
83
|
+
sums[file_name] += status.fetch(:run_time).to_s[/\d+(\.\d+)?/].to_f
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
path = File.join("tmp", "turbo_tests2_example_status_runtime.log")
|
|
87
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
88
|
+
File.write(path, runtimes.sort.map { |file, runtime| "#{file}:#{runtime}" }.join("\n"))
|
|
89
|
+
path
|
|
90
|
+
end
|
|
70
91
|
end
|
|
71
92
|
|
|
72
93
|
def initialize(**opts)
|
|
@@ -97,6 +118,7 @@ module TurboTests
|
|
|
97
118
|
@messages = Thread::Queue.new
|
|
98
119
|
@threads = []
|
|
99
120
|
@wait_threads = []
|
|
121
|
+
@exited_process_ids = []
|
|
100
122
|
@error = false
|
|
101
123
|
@print_failed_group = opts[:print_failed_group]
|
|
102
124
|
end
|
|
@@ -104,41 +126,44 @@ module TurboTests
|
|
|
104
126
|
def run
|
|
105
127
|
@num_processes = [
|
|
106
128
|
ParallelTests.determine_number_of_processes(@count),
|
|
107
|
-
ParallelTests::RSpec::Runner.tests_with_size(@files, {}).size
|
|
129
|
+
ParallelTests::RSpec::Runner.tests_with_size(@files, {}).size
|
|
108
130
|
].min
|
|
109
131
|
|
|
110
132
|
tests_in_groups =
|
|
111
133
|
ParallelTests::RSpec::Runner.tests_in_groups(
|
|
112
134
|
@files,
|
|
113
135
|
@num_processes,
|
|
114
|
-
**@parallel_options
|
|
136
|
+
**@parallel_options
|
|
115
137
|
)
|
|
138
|
+
@tests_in_groups = tests_in_groups
|
|
116
139
|
|
|
117
140
|
subprocess_opts = {
|
|
118
|
-
record_runtime: @record_runtime
|
|
141
|
+
record_runtime: @record_runtime
|
|
119
142
|
}
|
|
120
143
|
|
|
121
|
-
|
|
122
|
-
|
|
144
|
+
ParallelTests.with_pid_file do
|
|
145
|
+
@reporter.report(tests_in_groups) do |_reporter|
|
|
146
|
+
old_signal = Signal.trap(:INT) { handle_interrupt }
|
|
123
147
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
148
|
+
@wait_threads = tests_in_groups.map.with_index do |tests, process_id|
|
|
149
|
+
start_regular_subprocess(tests, process_id + 1, **subprocess_opts)
|
|
150
|
+
end.compact
|
|
151
|
+
@interrupt_handled = false
|
|
128
152
|
|
|
129
|
-
|
|
153
|
+
handle_messages
|
|
130
154
|
|
|
131
|
-
|
|
155
|
+
@threads.each(&:join)
|
|
132
156
|
|
|
133
|
-
|
|
157
|
+
report_failed_group(tests_in_groups) if @print_failed_group
|
|
134
158
|
|
|
135
|
-
|
|
159
|
+
Signal.trap(:INT, old_signal)
|
|
136
160
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
161
|
+
if @reporter.failed_examples.empty? && @wait_threads.map(&:value).all?(&:success?)
|
|
162
|
+
0
|
|
163
|
+
else
|
|
164
|
+
# From https://github.com/galtzo-floss/turbo_tests2/pull/20/
|
|
165
|
+
@wait_threads.map { |thread| thread.value.exitstatus }.max
|
|
166
|
+
end
|
|
142
167
|
end
|
|
143
168
|
end
|
|
144
169
|
end
|
|
@@ -150,6 +175,7 @@ module TurboTests
|
|
|
150
175
|
Kernel.exit
|
|
151
176
|
else
|
|
152
177
|
puts "\nShutting down subprocesses..."
|
|
178
|
+
report_unfinished_groups("Groups not finished")
|
|
153
179
|
@wait_threads.each do |wait_thr|
|
|
154
180
|
begin
|
|
155
181
|
child_pid = wait_thr.pid
|
|
@@ -165,11 +191,15 @@ module TurboTests
|
|
|
165
191
|
|
|
166
192
|
def start_regular_subprocess(tests, process_id, **opts)
|
|
167
193
|
start_subprocess(
|
|
168
|
-
{
|
|
194
|
+
{
|
|
195
|
+
"TEST_ENV_NUMBER" => process_id.to_s,
|
|
196
|
+
"PARALLEL_TEST_GROUPS" => @num_processes.to_s,
|
|
197
|
+
"PARALLEL_PID_FILE" => parallel_pid_file_path
|
|
198
|
+
},
|
|
169
199
|
@tags.map { |tag| "--tag=#{tag}" },
|
|
170
200
|
tests,
|
|
171
201
|
process_id,
|
|
172
|
-
**opts
|
|
202
|
+
**opts
|
|
173
203
|
)
|
|
174
204
|
end
|
|
175
205
|
|
|
@@ -177,7 +207,7 @@ module TurboTests
|
|
|
177
207
|
if tests.empty?
|
|
178
208
|
@messages << {
|
|
179
209
|
type: "exit",
|
|
180
|
-
process_id: process_id
|
|
210
|
+
process_id: process_id
|
|
181
211
|
}
|
|
182
212
|
|
|
183
213
|
nil
|
|
@@ -201,7 +231,7 @@ module TurboTests
|
|
|
201
231
|
"--format",
|
|
202
232
|
"ParallelTests::RSpec::RuntimeLogger",
|
|
203
233
|
"--out",
|
|
204
|
-
@runtime_log
|
|
234
|
+
@runtime_log
|
|
205
235
|
]
|
|
206
236
|
else
|
|
207
237
|
[]
|
|
@@ -209,7 +239,7 @@ module TurboTests
|
|
|
209
239
|
|
|
210
240
|
seed_option = if @seed_used
|
|
211
241
|
[
|
|
212
|
-
"--seed", @seed
|
|
242
|
+
"--seed", @seed
|
|
213
243
|
]
|
|
214
244
|
else
|
|
215
245
|
[]
|
|
@@ -225,20 +255,22 @@ module TurboTests
|
|
|
225
255
|
"TurboTests::JsonRowsFormatter",
|
|
226
256
|
*record_runtime_options,
|
|
227
257
|
*spec_opts,
|
|
228
|
-
*tests
|
|
258
|
+
*tests
|
|
229
259
|
]
|
|
230
260
|
command.unshift("nice") if @nice
|
|
231
261
|
|
|
232
262
|
if @verbose
|
|
233
263
|
command_str = [
|
|
234
264
|
env.map { |k, v| "#{k}=#{v}" }.join(" "),
|
|
235
|
-
command.join(" ")
|
|
265
|
+
command.join(" ")
|
|
236
266
|
].select { |x| x.size > 0 }.join(" ")
|
|
237
267
|
|
|
238
268
|
warn("Process #{process_id}: #{command_str}")
|
|
239
269
|
end
|
|
240
270
|
|
|
271
|
+
pid_file_path = env["PARALLEL_PID_FILE"] || parallel_pid_file_path
|
|
241
272
|
stdin, stdout, stderr, wait_thr = Open3.popen3(env, *command)
|
|
273
|
+
track_parallel_pid(wait_thr.pid, pid_file_path)
|
|
242
274
|
stdin.close
|
|
243
275
|
|
|
244
276
|
# rubocop:disable ThreadSafety/NewThread
|
|
@@ -267,7 +299,12 @@ module TurboTests
|
|
|
267
299
|
|
|
268
300
|
# rubocop:disable ThreadSafety/NewThread
|
|
269
301
|
@threads << Thread.new do
|
|
270
|
-
|
|
302
|
+
begin
|
|
303
|
+
status = wait_thr.value
|
|
304
|
+
@messages << {type: "error"} unless status.success?
|
|
305
|
+
ensure
|
|
306
|
+
untrack_parallel_pid(wait_thr.pid, pid_file_path)
|
|
307
|
+
end
|
|
271
308
|
end
|
|
272
309
|
# rubocop:enable ThreadSafety/NewThread
|
|
273
310
|
|
|
@@ -275,6 +312,18 @@ module TurboTests
|
|
|
275
312
|
end
|
|
276
313
|
end
|
|
277
314
|
|
|
315
|
+
def parallel_pid_file_path
|
|
316
|
+
ENV["PARALLEL_PID_FILE"]
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def track_parallel_pid(pid, pid_file_path = parallel_pid_file_path)
|
|
320
|
+
ParallelTests::Pids.new(pid_file_path).add(pid) if pid && pid_file_path
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def untrack_parallel_pid(pid, pid_file_path = parallel_pid_file_path)
|
|
324
|
+
ParallelTests::Pids.new(pid_file_path).delete(pid) if pid && pid_file_path
|
|
325
|
+
end
|
|
326
|
+
|
|
278
327
|
def start_copy_thread(src, dst)
|
|
279
328
|
# rubocop:disable ThreadSafety/NewThread
|
|
280
329
|
Thread.new do
|
|
@@ -318,6 +367,7 @@ module TurboTests
|
|
|
318
367
|
@reporter.example_failed(example)
|
|
319
368
|
@failure_count += 1
|
|
320
369
|
if fail_fast_met
|
|
370
|
+
report_unfinished_groups("Groups stopped by fail-fast")
|
|
321
371
|
@threads.each(&:kill)
|
|
322
372
|
break
|
|
323
373
|
end
|
|
@@ -328,13 +378,16 @@ module TurboTests
|
|
|
328
378
|
else
|
|
329
379
|
@reporter.message(message[:message])
|
|
330
380
|
end
|
|
331
|
-
when "
|
|
332
|
-
|
|
333
|
-
when "
|
|
381
|
+
when "deprecation"
|
|
382
|
+
@reporter.deprecation(message[:deprecation])
|
|
383
|
+
when "profile"
|
|
384
|
+
@reporter.profile(message[:profile])
|
|
385
|
+
when "seed", "close", "error"
|
|
334
386
|
# Do nothing
|
|
335
387
|
nil
|
|
336
388
|
when "exit"
|
|
337
389
|
exited += 1
|
|
390
|
+
@exited_process_ids << message[:process_id]
|
|
338
391
|
break if exited == @num_processes
|
|
339
392
|
else
|
|
340
393
|
warn("Unhandled message in main process: #{message}")
|
|
@@ -357,5 +410,20 @@ module TurboTests
|
|
|
357
410
|
puts "Group that failed: #{failing_group}"
|
|
358
411
|
end
|
|
359
412
|
end
|
|
413
|
+
|
|
414
|
+
def report_unfinished_groups(label)
|
|
415
|
+
groups = Array(@tests_in_groups)
|
|
416
|
+
unfinished_groups = groups.each_with_index.with_object([]) do |(tests, index), unfinished|
|
|
417
|
+
process_id = index + 1
|
|
418
|
+
unfinished << tests unless @exited_process_ids.include?(process_id)
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
return if unfinished_groups.empty?
|
|
422
|
+
|
|
423
|
+
puts "#{label}:"
|
|
424
|
+
unfinished_groups.each_with_index do |tests, index|
|
|
425
|
+
puts " #{index + 1}) #{tests.join(" ")}"
|
|
426
|
+
end
|
|
427
|
+
end
|
|
360
428
|
end
|
|
361
429
|
end
|
data/lib/turbo_tests/shim.rb
CHANGED
|
@@ -2,7 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
module TurboTests
|
|
4
4
|
class Shim
|
|
5
|
-
Result
|
|
5
|
+
class Result
|
|
6
|
+
attr_reader :status, :path, :message, :exit_code
|
|
7
|
+
|
|
8
|
+
def initialize(status:, path:, message:, exit_code:)
|
|
9
|
+
@status = status
|
|
10
|
+
@path = path
|
|
11
|
+
@message = message
|
|
12
|
+
@exit_code = exit_code
|
|
13
|
+
end
|
|
14
|
+
end
|
|
6
15
|
|
|
7
16
|
DEFAULT_RELATIVE_PATH = File.join("bin", "turbo_tests")
|
|
8
17
|
MANAGED_MARKER = "Generated by turbo_tests2 shim install"
|
data/lib/turbo_tests/version.rb
CHANGED
data/lib/turbo_tests.rb
CHANGED
|
@@ -39,7 +39,7 @@ module TurboTests
|
|
|
39
39
|
klass.new(
|
|
40
40
|
obj[:backtrace],
|
|
41
41
|
obj[:message],
|
|
42
|
-
FakeException.from_obj(obj[:cause])
|
|
42
|
+
FakeException.from_obj(obj[:cause])
|
|
43
43
|
)
|
|
44
44
|
end
|
|
45
45
|
end
|
|
@@ -52,6 +52,7 @@ module TurboTests
|
|
|
52
52
|
:pending_fixed?,
|
|
53
53
|
:exception,
|
|
54
54
|
:pending_exception,
|
|
55
|
+
:run_time
|
|
55
56
|
)
|
|
56
57
|
class FakeExecutionResult
|
|
57
58
|
class << self
|
|
@@ -63,6 +64,7 @@ module TurboTests
|
|
|
63
64
|
obj[:pending_fixed?],
|
|
64
65
|
FakeException.from_obj(obj[:exception]),
|
|
65
66
|
FakeException.from_obj(obj[:exception]),
|
|
67
|
+
obj[:run_time]
|
|
66
68
|
)
|
|
67
69
|
end
|
|
68
70
|
end
|
|
@@ -74,7 +76,7 @@ module TurboTests
|
|
|
74
76
|
:description,
|
|
75
77
|
:full_description,
|
|
76
78
|
:metadata,
|
|
77
|
-
:location_rerun_argument
|
|
79
|
+
:location_rerun_argument
|
|
78
80
|
)
|
|
79
81
|
class FakeExample
|
|
80
82
|
class << self
|
|
@@ -84,7 +86,7 @@ module TurboTests
|
|
|
84
86
|
metadata[:shared_group_inclusion_backtrace].map! do |frame|
|
|
85
87
|
RSpec::Core::SharedExampleGroupInclusionStackFrame.new(
|
|
86
88
|
frame[:shared_group_name],
|
|
87
|
-
frame[:inclusion_location]
|
|
89
|
+
frame[:inclusion_location]
|
|
88
90
|
)
|
|
89
91
|
end
|
|
90
92
|
|
|
@@ -96,14 +98,14 @@ module TurboTests
|
|
|
96
98
|
obj[:description],
|
|
97
99
|
obj[:full_description],
|
|
98
100
|
metadata,
|
|
99
|
-
obj[:location_rerun_argument]
|
|
101
|
+
obj[:location_rerun_argument]
|
|
100
102
|
)
|
|
101
103
|
end
|
|
102
104
|
end
|
|
103
105
|
|
|
104
106
|
def notification
|
|
105
107
|
RSpec::Core::Notifications::ExampleNotification.for(
|
|
106
|
-
self
|
|
108
|
+
self
|
|
107
109
|
)
|
|
108
110
|
end
|
|
109
111
|
end
|
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "bundler"
|
|
4
3
|
require "rspec/core"
|
|
5
4
|
|
|
6
5
|
RSpec.shared_context("with simplecov spawn coverage") do
|
|
7
6
|
let(:simplecov_spawn_path) do
|
|
8
|
-
File.expand_path("
|
|
7
|
+
[Dir.pwd, File.expand_path("..", Dir.pwd)]
|
|
8
|
+
.map { |dir| File.expand_path(".simplecov_spawn.rb", dir) }
|
|
9
|
+
.find { |path| File.file?(path) }
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
around do |example|
|
|
12
13
|
original_rubyopt = ENV.fetch("RUBYOPT", nil)
|
|
14
|
+
original_cov_min_hard = ENV.fetch("K_SOUP_COV_MIN_HARD", nil)
|
|
13
15
|
begin
|
|
14
16
|
if defined?(SimpleCov) && SimpleCov.running
|
|
15
17
|
spawn_path = simplecov_spawn_path
|
|
16
18
|
raise ArgumentError, "Expected SimpleCov spawn shim at #{spawn_path}" unless File.file?(spawn_path)
|
|
17
19
|
|
|
20
|
+
ENV["K_SOUP_COV_MIN_HARD"] = "false"
|
|
18
21
|
ENV["RUBYOPT"] = ["-r#{spawn_path}", original_rubyopt].compact.join(" ").strip
|
|
19
22
|
end
|
|
20
23
|
|
|
21
24
|
example.run
|
|
22
25
|
ensure
|
|
23
26
|
ENV["RUBYOPT"] = original_rubyopt
|
|
27
|
+
ENV["K_SOUP_COV_MIN_HARD"] = original_cov_min_hard
|
|
24
28
|
end
|
|
25
29
|
end
|
|
26
30
|
end
|
data/sig/turbo_tests/version.rbs
CHANGED
data.tar.gz.sig
CHANGED
|
Binary file
|