ci-queue 0.64.0 → 0.65.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/Gemfile.lock +1 -1
- data/lib/ci/queue/configuration.rb +8 -1
- data/lib/ci/queue/redis/base.rb +6 -2
- data/lib/ci/queue/redis/supervisor.rb +8 -9
- data/lib/ci/queue/version.rb +1 -1
- data/lib/minitest/queue/build_status_reporter.rb +124 -12
- data/lib/minitest/queue/error_report.rb +4 -0
- data/lib/minitest/queue/runner.rb +6 -11
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0bd235c5c9a18d43b30d5fc614e960e297b60fbaa3391a47db9eaa07cfa1bb4f
|
4
|
+
data.tar.gz: 9f61f75c48456aaac0abb2b25e3cb200a22f00baf5c5612828bd61b54a59ed46
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 640842ac7e07b54184cab3a9881ec5f0401f3c128b6d8bf0011d576650e94c4f85864664fcc51b3fac6d6028b4fd95a7943364aa397e060acc3ddc56647662f6
|
7
|
+
data.tar.gz: 4d8aa80210424460498cb29bee6fb7d18db2f1a9c453550c98e4133f34862d7a9882df5c60bf0b0f88e245210af7b6be943cf811c20d3e0f5a53d5ab2bf8e8dd
|
data/Gemfile.lock
CHANGED
@@ -25,7 +25,14 @@ module CI
|
|
25
25
|
|
26
26
|
def load_flaky_tests(path)
|
27
27
|
return [] unless path
|
28
|
-
::File.
|
28
|
+
if ::File.extname(path) == ".xml"
|
29
|
+
require 'rexml/document'
|
30
|
+
REXML::Document.new(::File.read(path)).elements.to_a("//testcase").map do |element|
|
31
|
+
"#{element.attributes['classname']}##{element.attributes['name']}"
|
32
|
+
end.to_set
|
33
|
+
else
|
34
|
+
::File.readlines(path).map(&:chomp).to_set
|
35
|
+
end
|
29
36
|
rescue SystemCallError
|
30
37
|
[]
|
31
38
|
end
|
data/lib/ci/queue/redis/base.rb
CHANGED
@@ -133,11 +133,15 @@ module CI
|
|
133
133
|
redis.zcard(key('running'))
|
134
134
|
end
|
135
135
|
|
136
|
-
def
|
136
|
+
def test_ids
|
137
137
|
redis.multi do |transaction|
|
138
138
|
transaction.lrange(key('queue'), 0, -1)
|
139
139
|
transaction.zrange(key('running'), 0, -1)
|
140
|
-
end.flatten
|
140
|
+
end.flatten
|
141
|
+
end
|
142
|
+
|
143
|
+
def to_a
|
144
|
+
test_ids.reverse.map { |k| index.fetch(k) }
|
141
145
|
end
|
142
146
|
|
143
147
|
def progress
|
@@ -24,29 +24,28 @@ module CI
|
|
24
24
|
|
25
25
|
yield if block_given?
|
26
26
|
|
27
|
-
time_left = config.report_timeout - duration.to_i
|
28
|
-
time_left_with_no_workers = config.inactive_workers_timeout
|
29
|
-
until exhausted? || time_left <= 0 || max_test_failed? || time_left_with_no_workers <= 0
|
30
|
-
time_left -= 1
|
27
|
+
@time_left = config.report_timeout - duration.to_i
|
28
|
+
@time_left_with_no_workers = config.inactive_workers_timeout
|
29
|
+
until exhausted? || @time_left <= 0 || max_test_failed? || @time_left_with_no_workers <= 0
|
30
|
+
@time_left -= 1
|
31
31
|
sleep 1
|
32
32
|
|
33
33
|
if active_workers?
|
34
|
-
time_left_with_no_workers = config.inactive_workers_timeout
|
34
|
+
@time_left_with_no_workers = config.inactive_workers_timeout
|
35
35
|
else
|
36
|
-
time_left_with_no_workers -= 1
|
36
|
+
@time_left_with_no_workers -= 1
|
37
37
|
end
|
38
38
|
|
39
39
|
yield if block_given?
|
40
40
|
end
|
41
41
|
|
42
|
-
puts "Aborting, timed out." if time_left <= 0 && !exhausted?
|
43
|
-
puts "Aborting, it seems all workers died." if time_left_with_no_workers <= 0 && !exhausted?
|
44
|
-
|
45
42
|
exhausted?
|
46
43
|
rescue CI::Queue::Redis::LostMaster
|
47
44
|
false
|
48
45
|
end
|
49
46
|
|
47
|
+
attr_reader :time_left, :time_left_with_no_workers
|
48
|
+
|
50
49
|
private
|
51
50
|
|
52
51
|
def active_workers?
|
data/lib/ci/queue/version.rb
CHANGED
@@ -4,8 +4,91 @@ module Minitest
|
|
4
4
|
class BuildStatusReporter < Minitest::Reporters::BaseReporter
|
5
5
|
include ::CI::Queue::OutputHelpers
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
class JUnitReporter
|
8
|
+
def initialize(file, error_reports)
|
9
|
+
@file = file
|
10
|
+
@error_reports = error_reports
|
11
|
+
end
|
12
|
+
|
13
|
+
def write
|
14
|
+
File.open(@file, 'w+') do |file|
|
15
|
+
format_document(generate_document(@error_reports), file)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def generate_document(error_reports)
|
22
|
+
suites = error_reports.group_by { |error_report| error_report.test_suite }
|
23
|
+
|
24
|
+
doc = REXML::Document.new(nil, {
|
25
|
+
:prologue_quote => :quote,
|
26
|
+
:attribute_quote => :quote,
|
27
|
+
})
|
28
|
+
doc << REXML::XMLDecl.new('1.1', 'utf-8')
|
29
|
+
|
30
|
+
testsuites = doc.add_element('testsuites')
|
31
|
+
suites.each do |suite, error_reports|
|
32
|
+
add_tests_to(testsuites, suite, error_reports)
|
33
|
+
end
|
34
|
+
|
35
|
+
doc
|
36
|
+
end
|
37
|
+
|
38
|
+
def format_document(doc, io)
|
39
|
+
formatter = REXML::Formatters::Pretty.new
|
40
|
+
formatter.write(doc, io)
|
41
|
+
io << "\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_tests_to(testsuites, suite, error_reports)
|
45
|
+
testsuite = testsuites.add_element(
|
46
|
+
'testsuite',
|
47
|
+
'name' => suite,
|
48
|
+
'filepath' => Minitest::Queue.relative_path(error_reports.first.test_file),
|
49
|
+
'tests' => error_reports.count,
|
50
|
+
)
|
51
|
+
|
52
|
+
error_reports.each do |error_report|
|
53
|
+
attributes = {
|
54
|
+
'name' => error_report.test_name,
|
55
|
+
'classname' => error_report.test_suite,
|
56
|
+
}
|
57
|
+
attributes['lineno'] = error_report.test_line
|
58
|
+
|
59
|
+
testcase = testsuite.add_element('testcase', attributes)
|
60
|
+
add_xml_message_for(testcase, error_report)
|
61
|
+
rescue REXML::ParseException, RuntimeError => error
|
62
|
+
puts error
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def add_xml_message_for(testcase, error_report)
|
67
|
+
failure = testcase.add_element('failure', 'type' => error_report.error_class, 'message' => truncate_message(error_report.to_s))
|
68
|
+
failure.add_text(REXML::CData.new(message_for(error_report)))
|
69
|
+
end
|
70
|
+
|
71
|
+
def truncate_message(message)
|
72
|
+
message.lines.first.chomp.gsub(/\e\[[^m]+m/, '')
|
73
|
+
end
|
74
|
+
|
75
|
+
def project_root_path_matcher
|
76
|
+
@project_root_path_matcher ||= %r{(?<=\s)#{Regexp.escape(Minitest::Queue.project_root)}/}
|
77
|
+
end
|
78
|
+
|
79
|
+
def message_for(error_report)
|
80
|
+
suite = error_report.test_suite
|
81
|
+
name = error_report.test_name
|
82
|
+
error = error_report.to_s
|
83
|
+
|
84
|
+
message_with_relative_paths = error.gsub(project_root_path_matcher, '')
|
85
|
+
"\nFailure:\n#{name}(#{suite}) [#{Minitest::Queue.relative_path(error_report.test_file)}]:\n#{message_with_relative_paths}\n"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def initialize(supervisor:, **options)
|
90
|
+
@supervisor = supervisor
|
91
|
+
@build = supervisor.build
|
9
92
|
super(options)
|
10
93
|
end
|
11
94
|
|
@@ -26,23 +109,46 @@ module Minitest
|
|
26
109
|
end
|
27
110
|
|
28
111
|
def report
|
112
|
+
if requeued_tests.to_a.any?
|
113
|
+
step("Requeued #{requeued_tests.size} tests")
|
114
|
+
requeued_tests.to_a.sort.each do |test_id, count|
|
115
|
+
puts yellow("REQUEUE")
|
116
|
+
puts "#{test_id} (requeued #{count} times)"
|
117
|
+
puts ""
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
29
121
|
puts aggregates
|
30
|
-
errors = error_reports
|
31
|
-
puts errors
|
32
122
|
|
33
|
-
|
34
|
-
puts
|
35
|
-
|
36
|
-
|
123
|
+
if supervisor.time_left.to_i <= 0
|
124
|
+
puts red("Timed out waiting for tests to be executed.")
|
125
|
+
|
126
|
+
remaining_tests = supervisor.test_ids
|
127
|
+
remaining_tests.first(10).each do |id|
|
128
|
+
puts " #{id}"
|
129
|
+
end
|
130
|
+
|
131
|
+
if remaining_tests.size > 10
|
132
|
+
puts " ..."
|
133
|
+
end
|
134
|
+
elsif supervisor.time_left_with_no_workers.to_i <= 0
|
135
|
+
puts red("All workers died.")
|
136
|
+
elsif supervisor.max_test_failed?
|
137
|
+
puts red("Encountered too many failed tests. Test run was ended early.")
|
37
138
|
end
|
38
139
|
|
140
|
+
puts
|
141
|
+
|
142
|
+
errors = error_reports
|
143
|
+
puts errors
|
144
|
+
|
39
145
|
build.worker_errors.to_a.sort.each do |worker_id, error|
|
40
146
|
puts red("Worker #{worker_id } crashed")
|
41
147
|
puts error
|
42
148
|
puts ""
|
43
149
|
end
|
44
150
|
|
45
|
-
|
151
|
+
success?
|
46
152
|
end
|
47
153
|
|
48
154
|
def success?
|
@@ -83,16 +189,22 @@ module Minitest
|
|
83
189
|
end
|
84
190
|
|
85
191
|
def write_failure_file(file)
|
86
|
-
File.
|
192
|
+
File.open(file, 'w') do |f|
|
193
|
+
JSON.dump(error_reports.map(&:to_h), f)
|
194
|
+
end
|
195
|
+
xml_file = File.join(File.dirname(file), "#{File.basename(file, File.extname(file))}.xml")
|
196
|
+
JUnitReporter.new(xml_file, error_reports).write
|
87
197
|
end
|
88
198
|
|
89
199
|
def write_flaky_tests_file(file)
|
90
|
-
File.
|
200
|
+
File.open(file, 'w') do |f|
|
201
|
+
JSON.dump(flaky_reports, f)
|
202
|
+
end
|
91
203
|
end
|
92
204
|
|
93
205
|
private
|
94
206
|
|
95
|
-
attr_reader :build
|
207
|
+
attr_reader :build, :supervisor
|
96
208
|
|
97
209
|
def aggregates
|
98
210
|
success = failures.zero? && errors.zero?
|
@@ -257,23 +257,16 @@ module Minitest
|
|
257
257
|
end
|
258
258
|
|
259
259
|
unless supervisor.exhausted?
|
260
|
-
reporter = BuildStatusReporter.new(
|
260
|
+
reporter = BuildStatusReporter.new(supervisor: supervisor)
|
261
261
|
reporter.report
|
262
262
|
reporter.write_failure_file(queue_config.failure_file) if queue_config.failure_file
|
263
263
|
reporter.write_flaky_tests_file(queue_config.export_flaky_tests_file) if queue_config.export_flaky_tests_file
|
264
264
|
|
265
|
-
|
266
|
-
|
267
|
-
if supervisor.max_test_failed?
|
268
|
-
puts('Encountered too many failed tests. Test run was ended early.')
|
269
|
-
abort!(msg)
|
270
|
-
else
|
271
|
-
abort!(msg)
|
272
|
-
end
|
265
|
+
abort!("#{supervisor.size} tests weren't run.")
|
273
266
|
end
|
274
267
|
end
|
275
268
|
|
276
|
-
reporter = BuildStatusReporter.new(
|
269
|
+
reporter = BuildStatusReporter.new(supervisor: supervisor)
|
277
270
|
reporter.write_failure_file(queue_config.failure_file) if queue_config.failure_file
|
278
271
|
reporter.write_flaky_tests_file(queue_config.export_flaky_tests_file) if queue_config.export_flaky_tests_file
|
279
272
|
reporter.report
|
@@ -330,7 +323,9 @@ module Minitest
|
|
330
323
|
warnings = build.pop_warnings.map do |type, attributes|
|
331
324
|
attributes.merge(type: type)
|
332
325
|
end.compact
|
333
|
-
File.
|
326
|
+
File.open(queue_config.warnings_file, 'w') do |f|
|
327
|
+
JSON.dump(warnings, f)
|
328
|
+
end
|
334
329
|
end
|
335
330
|
|
336
331
|
def run_tests_in_fork(queue)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ci-queue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.65.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean Boussier
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: logger
|
@@ -254,7 +254,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
254
254
|
- !ruby/object:Gem::Version
|
255
255
|
version: '0'
|
256
256
|
requirements: []
|
257
|
-
rubygems_version: 3.6.
|
257
|
+
rubygems_version: 3.6.8
|
258
258
|
specification_version: 4
|
259
259
|
summary: Distribute tests over many workers using a queue
|
260
260
|
test_files: []
|