") unless junits_dir
12 | abort("#{junits_dir} does not exist") unless Dir.exist?(junits_dir)
13 |
14 | job_pattern = ENV['BUILDKITE_PLUGIN_JUNIT_ANNOTATE_JOB_UUID_FILE_PATTERN']
15 | job_pattern = '-(.*).xml' if !job_pattern || job_pattern.empty?
16 |
17 | failure_format = ENV['BUILDKITE_PLUGIN_JUNIT_ANNOTATE_FAILURE_FORMAT']
18 | failure_format = 'classname' if !failure_format || failure_format.empty?
19 |
20 | report_slowest = ENV['BUILDKITE_PLUGIN_JUNIT_ANNOTATE_REPORT_SLOWEST'].to_i
21 | report_skipped = ENV['BUILDKITE_PLUGIN_JUNIT_ANNOTATE_REPORT_SKIPPED'] == 'true'
22 |
23 | class Failure < Struct.new(:name, :unit_name, :body, :job, :message)
24 | end
25 |
26 | class Timing < Struct.new(:name, :unit_name, :time)
27 | end
28 |
29 | junit_report_files = Dir.glob(File.join(junits_dir, "**", "*"), File::FNM_DOTMATCH)
30 | testcases = 0
31 | tests = {
32 | failure: [],
33 | error: [],
34 | skipped: []
35 | }
36 | timings = []
37 |
38 | def text_content(element)
39 | # Handle mulptiple CDATA/text children elements
40 | text = element.texts().map(&:value).join.strip
41 | if text.empty?
42 | nil
43 | else
44 | text
45 | end
46 | end
47 |
48 | def message_content(element)
49 | # Handle empty attributes
50 | message = element.attributes['message'];
51 | if message.nil? || message.empty?
52 | nil
53 | else
54 | message.to_s
55 | end
56 | end
57 |
58 | junit_report_files.sort.each do |file|
59 | next if File.directory?(file)
60 |
61 | STDERR.puts "Parsing #{file.sub(junits_dir, '')}"
62 | job = File.basename(file)[/#{job_pattern}/, 1]
63 | xml = File.read(file)
64 | doc = REXML::Document.new(xml)
65 |
66 | REXML::XPath.each(doc, '//testsuite/testcase') do |testcase|
67 | testcases += 1
68 | name = testcase.attributes['name'].to_s
69 | unit_name = testcase.attributes[failure_format].to_s
70 | time = testcase.attributes['time'].to_f
71 | timings << Timing.new(name, unit_name, time)
72 | testcase.elements.each("failure | error | skipped") do |elem|
73 | tests[elem.name.to_sym] << Failure.new(name, unit_name, text_content(elem), job, message_content(elem))
74 | end
75 | end
76 | end
77 |
78 | STDERR.puts "--- ✍️ Preparing annotation"
79 |
80 | puts "Failures: #{tests[:failure].length}"
81 | puts "Errors: #{tests[:error].length}"
82 | puts "Skipped: #{tests[:skipped].length}"
83 | puts "Total tests: #{testcases}"
84 |
85 | skipped = tests.delete(:skipped) # save value for later
86 |
87 | tests.values.flatten.each do |failure|
88 | puts ""
89 | puts ""
90 | puts "#{CGI.escapeHTML failure.name} in #{CGI.escapeHTML failure.unit_name}
\n\n"
91 | if failure.message
92 | puts "#{CGI.escapeHTML failure.message.chomp.strip}
\n\n"
93 | end
94 | if failure.body
95 | puts "#{CGI.escapeHTML(failure.body.chomp.strip)}
\n\n"
96 | end
97 | if failure.job
98 | puts "in Job ##{failure.job}"
99 | end
100 | puts " "
101 | end
102 |
103 | if report_slowest > 0
104 | STDERR.puts "Reporting slowest tests ⏱"
105 | puts ""
106 | puts ""
107 | puts "#{report_slowest} slowest tests
\n\n"
108 | puts ""
109 | puts "Unit | Test | Time |
"
110 | puts ""
111 | timings.sort_by(&:time).reverse.take(report_slowest).each do |timing|
112 | puts "#{timing.unit_name} | #{timing.name} | #{timing.time} |
"
113 | end
114 | puts ""
115 | puts "
"
116 | puts " "
117 | end
118 |
119 | if report_skipped
120 | STDERR.puts "Reporting skipped tests"
121 | puts ""
122 | puts ""
123 | puts "#{skipped.length} tests skipped
\n\n"
124 | puts ""
125 | skipped.each do |sk|
126 | puts "- #{CGI.escapeHTML sk.name} in #{CGI.escapeHTML sk.unit_name} (#{CGI.escapeHTML sk.message || "no reason"})
\n"
127 | end
128 | puts "
"
129 | puts " "
130 | end
131 |
132 | exit 64 if tests.values.flatten.any? # special exit code to signal test failures
133 |
--------------------------------------------------------------------------------
/ruby/tests/.tests-in-hidden-dir/junit-1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ruby/tests/annotate_test.rb:
--------------------------------------------------------------------------------
1 | require 'minitest/autorun'
2 | require 'open3'
3 |
4 | describe "Junit annotate plugin parser" do
5 | it "handles no failures" do
6 | stdout, stderr, status = Open3.capture3("#{__dir__}/../bin/annotate", "#{__dir__}/no-test-failures/")
7 |
8 | assert_equal stderr, <<~OUTPUT
9 | Parsing junit-1.xml
10 | Parsing junit-2.xml
11 | Parsing junit-3.xml
12 | --- ✍️ Preparing annotation
13 | OUTPUT
14 |
15 | assert_equal stdout, <<~OUTPUT
16 | Failures: 0
17 | Errors: 0
18 | Skipped: 0
19 | Total tests: 8
20 | OUTPUT
21 |
22 | assert_equal 0, status.exitstatus
23 | end
24 |
25 | it "handles failures across multiple files" do
26 | stdout, stderr, status = Open3.capture3("#{__dir__}/../bin/annotate", "#{__dir__}/two-test-failures/")
27 |
28 | assert_equal stderr, <<~OUTPUT
29 | Parsing junit-1.xml
30 | Parsing junit-2.xml
31 | Parsing junit-3.xml
32 | --- ✍️ Preparing annotation
33 | OUTPUT
34 |
35 | assert_equal stdout, <<~OUTPUT
36 | Failures: 4
37 | Errors: 0
38 | Skipped: 0
39 | Total tests: 6
40 |
41 |
42 | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default in spec.models.account_spec
43 |
44 | expected: 250 got: 500 (compared using eql?)
45 |
46 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
47 |
48 | expected: 250
49 | got: 500
50 |
51 | (compared using eql?)
52 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
53 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
54 | ./spec/support/log.rb:17:in `run'
55 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
56 |
57 | in Job #1
58 |
59 |
60 |
61 | Account#maximum_jobs_added_by_pipeline_changer returns 700 if the account is XYZ in spec.models.account_spec
62 |
63 | expected: 700 got: 500 (compared using eql?)
64 |
65 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
66 |
67 | expected: 700
68 | got: 500
69 |
70 | (compared using eql?)
71 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
72 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
73 | ./spec/support/log.rb:17:in `run'
74 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
75 |
76 | in Job #2
77 |
78 |
79 |
80 | Account#maximum_jobs_added_by_pipeline_changer returns 700 if the account is XYZ in spec.models.account_spec
81 |
82 | expected: 700 got: 500 (compared using eql?)
83 |
84 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
85 |
86 | expected: 700
87 | got: 500
88 |
89 | (compared using eql?)
90 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
91 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
92 | ./spec/support/log.rb:17:in `run'
93 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
94 |
95 | in Job #3
96 |
97 |
98 |
99 | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default in spec.models.account_spec
100 |
101 | expected: 250 got: 500 (compared using eql?)
102 |
103 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
104 |
105 | expected: 250
106 | got: 500
107 |
108 | (compared using eql?)
109 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
110 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
111 | ./spec/support/log.rb:17:in `run'
112 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
113 |
114 | in Job #3
115 |
116 | OUTPUT
117 |
118 | assert_equal 64, status.exitstatus
119 | end
120 |
121 | it "handles failures and errors across multiple files" do
122 | stdout, stderr, status = Open3.capture3("#{__dir__}/../bin/annotate", "#{__dir__}/test-failure-and-error/")
123 |
124 | assert_equal stderr, <<~OUTPUT
125 | Parsing junit-1.xml
126 | Parsing junit-2.xml
127 | Parsing junit-3.xml
128 | --- ✍️ Preparing annotation
129 | OUTPUT
130 |
131 | assert_equal stdout, <<~OUTPUT
132 | Failures: 2
133 | Errors: 2
134 | Skipped: 0
135 | Total tests: 6
136 |
137 |
138 | Account#maximum_jobs_added_by_pipeline_changer returns 700 if the account is XYZ in spec.models.account_spec
139 |
140 | expected: 700 got: 500 (compared using eql?)
141 |
142 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
143 |
144 | expected: 700
145 | got: 500
146 |
147 | (compared using eql?)
148 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
149 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
150 | ./spec/support/log.rb:17:in `run'
151 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
152 |
153 | in Job #2
154 |
155 |
156 |
157 | Account#maximum_jobs_added_by_pipeline_changer returns 700 if the account is XYZ in spec.models.account_spec
158 |
159 | expected: 700 got: 500 (compared using eql?)
160 |
161 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
162 |
163 | expected: 700
164 | got: 500
165 |
166 | (compared using eql?)
167 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
168 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
169 | ./spec/support/log.rb:17:in `run'
170 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
171 |
172 | in Job #3
173 |
174 |
175 |
176 | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default in spec.models.account_spec
177 |
178 | expected: 250 got: 500 (compared using eql?)
179 |
180 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
181 |
182 | expected: 250
183 | got: 500
184 |
185 | (compared using eql?)
186 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
187 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
188 | ./spec/support/log.rb:17:in `run'
189 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
190 |
191 | in Job #1
192 |
193 |
194 |
195 | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default in spec.models.account_spec
196 |
197 | expected: 250 got: 500 (compared using eql?)
198 |
199 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
200 |
201 | expected: 250
202 | got: 500
203 |
204 | (compared using eql?)
205 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
206 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
207 | ./spec/support/log.rb:17:in `run'
208 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
209 |
210 | in Job #3
211 |
212 | OUTPUT
213 |
214 | assert_equal 64, status.exitstatus
215 | end
216 |
217 | it "accepts custom regex filename patterns for job id" do
218 | stdout, stderr, status = Open3.capture3("env", "BUILDKITE_PLUGIN_JUNIT_ANNOTATE_JOB_UUID_FILE_PATTERN=junit-(.*)-custom-pattern.xml", "#{__dir__}/../bin/annotate", "#{__dir__}/custom-job-uuid-pattern/")
219 |
220 | assert_equal stderr, <<~OUTPUT
221 | Parsing junit-123-456-custom-pattern.xml
222 | --- ✍️ Preparing annotation
223 | OUTPUT
224 |
225 | assert_equal stdout, <<~OUTPUT
226 | Failures: 1
227 | Errors: 0
228 | Skipped: 0
229 | Total tests: 2
230 |
231 |
232 | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default in spec.models.account_spec
233 |
234 | expected: 250 got: 500 (compared using eql?)
235 |
236 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
237 |
238 | expected: 250
239 | got: 500
240 |
241 | (compared using eql?)
242 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
243 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
244 | ./spec/support/log.rb:17:in `run'
245 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
246 |
247 | in Job #123-456
248 |
249 | OUTPUT
250 |
251 | assert_equal 64, status.exitstatus
252 | end
253 |
254 | it "uses the file path instead of classname for annotation content when specified" do
255 | stdout, stderr, status = Open3.capture3("env", "BUILDKITE_PLUGIN_JUNIT_ANNOTATE_FAILURE_FORMAT=file", "#{__dir__}/../bin/annotate", "#{__dir__}/test-failure-and-error/")
256 |
257 | assert_equal stderr, <<~OUTPUT
258 | Parsing junit-1.xml
259 | Parsing junit-2.xml
260 | Parsing junit-3.xml
261 | --- ✍️ Preparing annotation
262 | OUTPUT
263 |
264 | assert_equal stdout, <<~OUTPUT
265 | Failures: 2
266 | Errors: 2
267 | Skipped: 0
268 | Total tests: 6
269 |
270 |
271 | Account#maximum_jobs_added_by_pipeline_changer returns 700 if the account is XYZ in ./spec/models/account_spec.rb
272 |
273 | expected: 700 got: 500 (compared using eql?)
274 |
275 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
276 |
277 | expected: 700
278 | got: 500
279 |
280 | (compared using eql?)
281 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
282 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
283 | ./spec/support/log.rb:17:in `run'
284 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
285 |
286 | in Job #2
287 |
288 |
289 |
290 | Account#maximum_jobs_added_by_pipeline_changer returns 700 if the account is XYZ in ./spec/models/account_spec.rb
291 |
292 | expected: 700 got: 500 (compared using eql?)
293 |
294 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
295 |
296 | expected: 700
297 | got: 500
298 |
299 | (compared using eql?)
300 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
301 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
302 | ./spec/support/log.rb:17:in `run'
303 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
304 |
305 | in Job #3
306 |
307 |
308 |
309 | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default in ./spec/models/account_spec.rb
310 |
311 | expected: 250 got: 500 (compared using eql?)
312 |
313 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
314 |
315 | expected: 250
316 | got: 500
317 |
318 | (compared using eql?)
319 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
320 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
321 | ./spec/support/log.rb:17:in `run'
322 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
323 |
324 | in Job #1
325 |
326 |
327 |
328 | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default in ./spec/models/account_spec.rb
329 |
330 | expected: 250 got: 500 (compared using eql?)
331 |
332 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
333 |
334 | expected: 250
335 | got: 500
336 |
337 | (compared using eql?)
338 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
339 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
340 | ./spec/support/log.rb:17:in `run'
341 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
342 |
343 | in Job #3
344 |
345 | OUTPUT
346 |
347 | assert_equal 64, status.exitstatus
348 | end
349 |
350 | it "handles failures across multiple files in sub dirs" do
351 | stdout, stderr, status = Open3.capture3("#{__dir__}/../bin/annotate", "#{__dir__}/tests-in-sub-dirs/")
352 |
353 | assert_equal stderr, <<~OUTPUT
354 | Parsing sub-dir/junit-1.xml
355 | Parsing sub-dir/junit-2.xml
356 | Parsing sub-dir/junit-3.xml
357 | --- ✍️ Preparing annotation
358 | OUTPUT
359 |
360 | assert_equal stdout, <<~OUTPUT
361 | Failures: 4
362 | Errors: 0
363 | Skipped: 0
364 | Total tests: 6
365 |
366 |
367 | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default in spec.models.account_spec
368 |
369 | expected: 250 got: 500 (compared using eql?)
370 |
371 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
372 |
373 | expected: 250
374 | got: 500
375 |
376 | (compared using eql?)
377 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
378 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
379 | ./spec/support/log.rb:17:in `run'
380 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
381 |
382 | in Job #1
383 |
384 |
385 |
386 | Account#maximum_jobs_added_by_pipeline_changer returns 700 if the account is XYZ in spec.models.account_spec
387 |
388 | expected: 700 got: 500 (compared using eql?)
389 |
390 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
391 |
392 | expected: 700
393 | got: 500
394 |
395 | (compared using eql?)
396 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
397 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
398 | ./spec/support/log.rb:17:in `run'
399 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
400 |
401 | in Job #2
402 |
403 |
404 |
405 | Account#maximum_jobs_added_by_pipeline_changer returns 700 if the account is XYZ in spec.models.account_spec
406 |
407 | expected: 700 got: 500 (compared using eql?)
408 |
409 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
410 |
411 | expected: 700
412 | got: 500
413 |
414 | (compared using eql?)
415 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
416 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
417 | ./spec/support/log.rb:17:in `run'
418 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
419 |
420 | in Job #3
421 |
422 |
423 |
424 | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default in spec.models.account_spec
425 |
426 | expected: 250 got: 500 (compared using eql?)
427 |
428 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
429 |
430 | expected: 250
431 | got: 500
432 |
433 | (compared using eql?)
434 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
435 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
436 | ./spec/support/log.rb:17:in `run'
437 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
438 |
439 | in Job #3
440 |
441 | OUTPUT
442 |
443 | assert_equal 64, status.exitstatus
444 | end
445 |
446 | it "handles empty failure bodies" do
447 | stdout, stderr, status = Open3.capture3("#{__dir__}/../bin/annotate", "#{__dir__}/empty-failure-body/")
448 |
449 | assert_equal stderr, <<~OUTPUT
450 | Parsing junit.xml
451 | --- ✍️ Preparing annotation
452 | OUTPUT
453 |
454 | assert_equal stdout, <<~OUTPUT
455 | Failures: 1
456 | Errors: 0
457 | Skipped: 0
458 | Total tests: 2
459 |
460 |
461 | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default in spec.models.account_spec
462 |
463 | expected: 250 got: 500 (compared using eql?)
464 |
465 |
466 | OUTPUT
467 |
468 | assert_equal 64, status.exitstatus
469 | end
470 |
471 | it "handles miss message attributes" do
472 | stdout, stderr, status = Open3.capture3("#{__dir__}/../bin/annotate", "#{__dir__}/missing-message-attribute/")
473 |
474 | assert_equal stderr, <<~OUTPUT
475 | Parsing junit.xml
476 | --- ✍️ Preparing annotation
477 | OUTPUT
478 |
479 | assert_equal stdout, <<~OUTPUT
480 | Failures: 1
481 | Errors: 2
482 | Skipped: 0
483 | Total tests: 4
484 |
485 |
486 | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default in spec.models.account_spec
487 |
488 |
489 |
490 |
491 | Account#maximum_jobs_added_by_pipeline_changer returns 100 by default in spec.models.account_spec
492 |
493 |
494 |
495 |
496 | Account#maximum_jobs_added_by_pipeline_changer returns 50 by default in spec.models.account_spec
497 |
498 |
499 | OUTPUT
500 |
501 | assert_equal 64, status.exitstatus
502 | end
503 |
504 | it "handles cdata formatted XML files" do
505 | stdout, stderr, status = Open3.capture3("#{__dir__}/../bin/annotate", "#{__dir__}/failure-with-cdata/")
506 |
507 | assert_equal stderr, <<~OUTPUT
508 | Parsing junit.xml
509 | --- ✍️ Preparing annotation
510 | OUTPUT
511 |
512 | assert_equal stdout, <<~OUTPUT
513 | Failures: 0
514 | Errors: 1
515 | Skipped: 0
516 | Total tests: 2
517 |
518 |
519 | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default in spec.models.account_spec
520 |
521 | expected: 250 got: 500 (compared using eql?)
522 |
523 | First line of failure output
524 | Second line of failure output
525 |
526 |
527 | OUTPUT
528 |
529 | assert_equal 64, status.exitstatus
530 | end
531 |
532 | it "reports specified amount of slowest tests" do
533 | stdout, stderr, status = Open3.capture3("env", "BUILDKITE_PLUGIN_JUNIT_ANNOTATE_REPORT_SLOWEST=5", "#{__dir__}/../bin/annotate", "#{__dir__}/no-test-failures/")
534 |
535 | assert_equal stderr, <<~OUTPUT
536 | Parsing junit-1.xml
537 | Parsing junit-2.xml
538 | Parsing junit-3.xml
539 | --- ✍️ Preparing annotation
540 | Reporting slowest tests ⏱
541 | OUTPUT
542 |
543 | assert_equal stdout, <<~OUTPUT
544 | Failures: 0
545 | Errors: 0
546 | Skipped: 0
547 | Total tests: 8
548 |
549 |
550 | 5 slowest tests
551 |
552 |
553 | Unit | Test | Time |
554 |
555 | spec.models.account_spec | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default | 0.977127 |
556 | spec.models.account_spec | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default | 0.967127 |
557 | spec.models.account_spec | Account#maximum_jobs_added_by_pipeline_changer returns 500 if the account is ABC | 0.620013 |
558 | spec.models.account_spec | Account#maximum_jobs_added_by_pipeline_changer returns 900 if the account is F00 | 0.520013 |
559 | spec.models.account_spec | Account#maximum_jobs_added_by_pipeline_changer returns 700 if the account is XYZ | 0.420013 |
560 |
561 |
562 |
563 | OUTPUT
564 |
565 | assert_equal 0, status.exitstatus
566 | end
567 |
568 | it "handles junit dir paths with hidden directories" do
569 | stdout, stderr, status = Open3.capture3("#{__dir__}/../bin/annotate", "#{__dir__}/.tests-in-hidden-dir/")
570 |
571 | assert_equal stderr, <<~OUTPUT
572 | Parsing junit-1.xml
573 | --- ✍️ Preparing annotation
574 | OUTPUT
575 |
576 | assert_equal stdout, <<~OUTPUT
577 | Failures: 0
578 | Errors: 0
579 | Skipped: 0
580 | Total tests: 2
581 | OUTPUT
582 |
583 | assert_equal 0, status.exitstatus
584 | end
585 |
586 | it "correctly parses skipped tests" do
587 | stdout, stderr, status = Open3.capture3("#{__dir__}/../bin/annotate", "#{__dir__}/skipped-test/")
588 |
589 | assert_equal stderr, <<~OUTPUT
590 | Parsing junit.xml
591 | --- ✍️ Preparing annotation
592 | OUTPUT
593 |
594 | assert_equal stdout, <<~OUTPUT
595 | Failures: 0
596 | Errors: 0
597 | Skipped: 1
598 | Total tests: 2
599 | OUTPUT
600 |
601 | assert_equal 0, status.exitstatus
602 | end
603 |
604 | it "can report skipped tests" do
605 | stdout, stderr, status = Open3.capture3("env", "BUILDKITE_PLUGIN_JUNIT_ANNOTATE_REPORT_SKIPPED=true", "#{__dir__}/../bin/annotate", "#{__dir__}/skipped-test/")
606 |
607 | assert_equal stderr, <<~OUTPUT
608 | Parsing junit.xml
609 | --- ✍️ Preparing annotation
610 | Reporting skipped tests
611 | OUTPUT
612 |
613 | assert_equal stdout, <<~OUTPUT
614 | Failures: 0
615 | Errors: 0
616 | Skipped: 1
617 | Total tests: 2
618 |
619 |
620 | 1 tests skipped
621 |
622 |
623 | - Account#maximum_jobs_added_by_pipeline_changer returns 250 by default in spec.models.account_spec (because of reasons)
624 |
625 |
626 | OUTPUT
627 |
628 | assert_equal 0, status.exitstatus
629 | end
630 | end
631 |
--------------------------------------------------------------------------------
/ruby/tests/custom-job-uuid-pattern/junit-123-456-custom-pattern.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
6 |
7 | expected: 250
8 | got: 500
9 |
10 | (compared using eql?)
11 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
12 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
13 | ./spec/support/log.rb:17:in `run'
14 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
15 |
16 |
--------------------------------------------------------------------------------
/ruby/tests/empty-failure-body/junit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ruby/tests/failure-with-cdata/junit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/ruby/tests/missing-message-attribute/junit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ruby/tests/no-test-failures/junit-1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/ruby/tests/no-test-failures/junit-2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/ruby/tests/no-test-failures/junit-3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ruby/tests/skipped-test/junit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ruby/tests/test-failure-and-error/junit-1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
6 |
7 | expected: 250
8 | got: 500
9 |
10 | (compared using eql?)
11 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
12 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
13 | ./spec/support/log.rb:17:in `run'
14 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
15 |
16 |
--------------------------------------------------------------------------------
/ruby/tests/test-failure-and-error/junit-2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
5 |
6 | expected: 700
7 | got: 500
8 |
9 | (compared using eql?)
10 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
11 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
12 | ./spec/support/log.rb:17:in `run'
13 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
14 |
15 |
--------------------------------------------------------------------------------
/ruby/tests/test-failure-and-error/junit-3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
6 |
7 | expected: 700
8 | got: 500
9 |
10 | (compared using eql?)
11 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
12 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
13 | ./spec/support/log.rb:17:in `run'
14 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
15 |
16 |
17 |
18 |
19 |
20 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
21 |
22 | expected: 250
23 | got: 500
24 |
25 | (compared using eql?)
26 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
27 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
28 | ./spec/support/log.rb:17:in `run'
29 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/ruby/tests/tests-in-sub-dirs/sub-dir/junit-1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
6 |
7 | expected: 250
8 | got: 500
9 |
10 | (compared using eql?)
11 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
12 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
13 | ./spec/support/log.rb:17:in `run'
14 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
15 |
16 |
--------------------------------------------------------------------------------
/ruby/tests/tests-in-sub-dirs/sub-dir/junit-2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
5 |
6 | expected: 700
7 | got: 500
8 |
9 | (compared using eql?)
10 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
11 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
12 | ./spec/support/log.rb:17:in `run'
13 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
14 |
15 |
--------------------------------------------------------------------------------
/ruby/tests/tests-in-sub-dirs/sub-dir/junit-3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
6 |
7 | expected: 700
8 | got: 500
9 |
10 | (compared using eql?)
11 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
12 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
13 | ./spec/support/log.rb:17:in `run'
14 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
15 |
16 |
17 |
18 |
19 |
20 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
21 |
22 | expected: 250
23 | got: 500
24 |
25 | (compared using eql?)
26 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
27 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
28 | ./spec/support/log.rb:17:in `run'
29 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/ruby/tests/two-test-failures/junit-1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
6 |
7 | expected: 250
8 | got: 500
9 |
10 | (compared using eql?)
11 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
12 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
13 | ./spec/support/log.rb:17:in `run'
14 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
15 |
16 |
--------------------------------------------------------------------------------
/ruby/tests/two-test-failures/junit-2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
5 |
6 | expected: 700
7 | got: 500
8 |
9 | (compared using eql?)
10 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
11 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
12 | ./spec/support/log.rb:17:in `run'
13 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
14 |
15 |
--------------------------------------------------------------------------------
/ruby/tests/two-test-failures/junit-3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
6 |
7 | expected: 700
8 | got: 500
9 |
10 | (compared using eql?)
11 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
12 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
13 | ./spec/support/log.rb:17:in `run'
14 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
15 |
16 |
17 |
18 |
19 |
20 | Failure/Error: expect(account.maximum_jobs_added_by_pipeline_changer).to eql(250)
21 |
22 | expected: 250
23 | got: 500
24 |
25 | (compared using eql?)
26 | ./spec/models/account_spec.rb:78:in `block (3 levels) in <top (required)>'
27 | ./spec/support/database.rb:16:in `block (2 levels) in <top (required)>'
28 | ./spec/support/log.rb:17:in `run'
29 | ./spec/support/log.rb:66:in `block (2 levels) in <top (required)>'
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/tests/2-slowest-tests.output:
--------------------------------------------------------------------------------
1 | Failures: 0
2 | Errors: 0
3 | Skipped: 0
4 | Total tests: 8
5 |
6 |
7 | 5 slowest tests
8 |
9 | Unit | Test | Time |
10 |
11 | spec.models.account_spec | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default | 0.977127 |
12 | spec.models.account_spec | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default | 0.967127 |
13 | spec.models.account_spec | Account#maximum_jobs_added_by_pipeline_changer returns 500 if the account is ABC | 0.620013 |
14 | spec.models.account_spec | Account#maximum_jobs_added_by_pipeline_changer returns 900 if the account is F00 | 0.520013 |
15 | spec.models.account_spec | Account#maximum_jobs_added_by_pipeline_changer returns 700 if the account is XYZ | 0.420013 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/tests/2-tests-1-failure.output:
--------------------------------------------------------------------------------
1 | Failures: 0
2 | Errors: 1
3 | Skipped: 0
4 | Total tests: 2
5 |
6 |
7 | Account#maximum_jobs_added_by_pipeline_changer returns 250 by default in spec.models.account_spec
8 |
9 | expected: 250 got: 500 (compared using eql?)
10 |
11 | First line of failure output
12 | Second line of failure output
13 |
14 |
15 |
--------------------------------------------------------------------------------
/tests/command.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 |
3 | load "${BATS_PLUGIN_PATH}/load.bash"
4 |
5 | # Uncomment to get debug output from each stub
6 | # export MKTEMP_STUB_DEBUG=/dev/tty
7 | # export BUILDKITE_AGENT_STUB_DEBUG=/dev/tty
8 | # export DOCKER_STUB_DEBUG=/dev/tty
9 | # export DU_STUB_DEBUG=/dev/tty
10 |
11 | export artifacts_tmp="tests/tmp/junit-artifacts"
12 | export annotation_tmp="tests/tmp/junit-annotation"
13 | export annotation_input="tests/tmp/annotation.input"
14 |
15 | DOCKER_STUB_DEFAULT_OPTIONS='--log-level error run --rm --volume \* --volume \* --env \* --env \* --env \* --env \* \*'
16 |
17 | @test "runs the annotator and creates the annotation" {
18 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
19 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_FAIL_BUILD_ON_ERROR=false
20 |
21 | stub mktemp \
22 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
23 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
24 |
25 | stub buildkite-agent \
26 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4" \
27 | "annotate --context \* --style \* : cat >'${annotation_input}'; echo Annotation added with context \$3 and style \$5, content saved"
28 |
29 | stub docker \
30 | "${DOCKER_STUB_DEFAULT_OPTIONS} ruby /src/bin/annotate /junits : echo 'Failure ' && exit 64"
31 |
32 | run "$PWD/hooks/command"
33 |
34 | assert_success
35 |
36 | assert_output --partial "Annotation added with context junit and style error"
37 | assert_equal "$(cat "${annotation_input}")" 'Failure '
38 |
39 | unstub mktemp
40 | unstub buildkite-agent
41 | unstub docker
42 | rm "${annotation_input}"
43 | }
44 |
45 | @test "can define a special context" {
46 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
47 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_CONTEXT="junit_custom_context"
48 |
49 | stub mktemp \
50 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
51 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
52 |
53 | stub buildkite-agent \
54 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4" \
55 | "annotate --context \* --style \* : cat >'${annotation_input}'; echo Annotation added with context \$3 and style \$5, content saved"
56 |
57 | stub docker \
58 | "${DOCKER_STUB_DEFAULT_OPTIONS} ruby /src/bin/annotate /junits : cat tests/2-tests-1-failure.output && exit 64"
59 |
60 | run "$PWD/hooks/command"
61 |
62 | assert_success
63 |
64 | assert_output --partial "Annotation added with context junit_custom_context"
65 |
66 | unstub mktemp
67 | unstub buildkite-agent
68 | unstub docker
69 | rm "${annotation_input}"
70 | }
71 |
72 | @test "can pass through optional job uuid file pattern" {
73 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
74 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_JOB_UUID_FILE_PATTERN="custom_(*)_pattern.xml"
75 |
76 | stub mktemp \
77 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
78 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
79 |
80 | stub buildkite-agent \
81 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4" \
82 | "annotate --context \* --style \* : cat >'${annotation_input}'; echo Annotation added with context \$3 and style \$5, content saved"
83 |
84 | stub docker \
85 | "--log-level error run --rm --volume \* --volume \* --env BUILDKITE_PLUGIN_JUNIT_ANNOTATE_JOB_UUID_FILE_PATTERN='custom_(*)_pattern.xml' --env \* --env \* --env \* \* ruby /src/bin/annotate /junits : cat tests/2-tests-1-failure.output && exit 64"
86 |
87 | run "$PWD/hooks/command"
88 |
89 | assert_success
90 |
91 | assert_output --partial "Annotation added"
92 |
93 | unstub mktemp
94 | unstub buildkite-agent
95 | unstub docker
96 | rm "${annotation_input}"
97 | }
98 |
99 | @test "can pass through optional failure format" {
100 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
101 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_FAILURE_FORMAT="file"
102 |
103 | stub mktemp \
104 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
105 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
106 |
107 | stub buildkite-agent \
108 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4" \
109 | "annotate --context \* --style \* : cat >'${annotation_input}'; echo Annotation added with context \$3 and style \$5, content saved"
110 |
111 | stub docker \
112 | "--log-level error run --rm --volume \* --volume \* --env \* --env BUILDKITE_PLUGIN_JUNIT_ANNOTATE_FAILURE_FORMAT='file' --env \* --env \* \* ruby /src/bin/annotate /junits : cat tests/2-tests-1-failure.output && exit 64"
113 |
114 | run "$PWD/hooks/command"
115 |
116 | assert_success
117 |
118 | assert_output --partial "Annotation added"
119 |
120 | unstub mktemp
121 | unstub buildkite-agent
122 | unstub docker
123 | rm "${annotation_input}"
124 | }
125 |
126 | @test "doesn't create annotation unless there's failures" {
127 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
128 |
129 | stub mktemp \
130 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
131 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
132 |
133 | stub buildkite-agent \
134 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4"
135 |
136 | stub docker \
137 | "${DOCKER_STUB_DEFAULT_OPTIONS} ruby /src/bin/annotate /junits : echo 'Total tests: 0'"
138 |
139 | run "$PWD/hooks/command"
140 |
141 | assert_success
142 |
143 | unstub mktemp
144 | unstub buildkite-agent
145 | unstub docker
146 | }
147 |
148 | @test "creates annotation with no failures but always annotate" {
149 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
150 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ALWAYS_ANNOTATE=1
151 |
152 | stub mktemp \
153 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
154 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
155 |
156 | stub buildkite-agent \
157 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4" \
158 | "annotate --context \* --style \* : cat >'${annotation_input}'; echo Annotation added with context \$3 and style \$5, content saved"
159 |
160 | stub docker \
161 | "${DOCKER_STUB_DEFAULT_OPTIONS} ruby /src/bin/annotate /junits : echo 'Total tests: 0'"
162 |
163 | run "$PWD/hooks/command"
164 |
165 | assert_success
166 | assert_output --partial "Total tests: 0"
167 | assert_output --partial "Will create annotation anyways"
168 | assert_equal "$(cat "${annotation_input}")" 'Total tests: 0'
169 |
170 | unstub mktemp
171 | unstub buildkite-agent
172 | unstub docker
173 | }
174 |
175 | @test "errors without the 'artifacts' property set" {
176 | run "$PWD/hooks/command"
177 |
178 | assert_failure
179 |
180 | assert_output --partial "Missing artifacts configuration for the plugin"
181 | refute_output --partial ":junit:"
182 | }
183 |
184 | @test "fails if the annotation is larger than 1MB even after summary" {
185 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
186 |
187 | stub mktemp \
188 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
189 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
190 |
191 | # 1KB over the 1MB size limit of annotations
192 | stub du \
193 | "-k \* : echo 1025$'\t'\$2" \
194 | "-k \* : echo 1025$'\t'\$2"
195 |
196 | stub buildkite-agent \
197 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4"
198 |
199 | stub docker \
200 | "${DOCKER_STUB_DEFAULT_OPTIONS} ruby /src/bin/annotate /junits : cat tests/2-tests-1-failure.output && exit 64"
201 |
202 | run "$PWD/hooks/command"
203 |
204 | assert_success
205 |
206 | assert_output --partial "Failures too large to annotate"
207 | assert_output --partial "failures are too large to create a build annotation"
208 |
209 | unstub docker
210 | unstub du
211 | unstub buildkite-agent
212 | unstub mktemp
213 | }
214 |
215 | @test "creates summary annotation if original is larger than 1MB" {
216 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
217 |
218 | stub mktemp \
219 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
220 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
221 |
222 | # 1KB over the 1MB size limit of annotations
223 | stub du \
224 | "-k \* : echo 1025$'\t'\$2" \
225 | "-k \* : echo 10$'\t'\$2"
226 |
227 | stub buildkite-agent \
228 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4" \
229 | "annotate --context \* --style \* : cat >'${annotation_input}'; echo Annotation added with context \$3 and style \$5, content saved"
230 |
231 | stub docker \
232 | "${DOCKER_STUB_DEFAULT_OPTIONS} ruby /src/bin/annotate /junits : cat tests/2-tests-1-failure.output && exit 64"
233 |
234 | run "$PWD/hooks/command"
235 |
236 | assert_success
237 |
238 | assert_output --partial "Failures too large to annotate"
239 | assert_output --partial "using a simplified annotation"
240 | assert_equal "6 ${annotation_input}" "$(wc -l "${annotation_input}" | cut -f 1)"
241 |
242 | unstub docker
243 | unstub du
244 | unstub buildkite-agent
245 | unstub mktemp
246 | rm "${annotation_input}"
247 | }
248 |
249 | @test "returns an error if fail-build-on-error is true" {
250 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
251 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_FAIL_BUILD_ON_ERROR=true
252 |
253 | stub mktemp \
254 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
255 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
256 |
257 | stub buildkite-agent \
258 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4" \
259 | "annotate --context \* --style \* : cat >'${annotation_input}'; echo Annotation added with context \$3 and style \$5, content saved"
260 |
261 | stub docker \
262 | "${DOCKER_STUB_DEFAULT_OPTIONS} ruby /src/bin/annotate /junits : cat tests/2-tests-1-failure.output && exit 64"
263 |
264 | run "$PWD/hooks/command"
265 |
266 | assert_failure
267 |
268 | unstub mktemp
269 | unstub buildkite-agent
270 | unstub docker
271 | rm "${annotation_input}"
272 | }
273 |
274 | @test "returns an error if fail-build-on-error is true and annotation is too large" {
275 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
276 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_FAIL_BUILD_ON_ERROR=true
277 |
278 | stub mktemp \
279 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
280 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
281 |
282 | # 1KB over the 1MB size limit of annotations
283 | stub du \
284 | "-k \* : echo 1025$'\t'\$2" \
285 | "-k \* : echo 1025$'\t'\$2"
286 |
287 | stub buildkite-agent \
288 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4"
289 |
290 | stub docker \
291 | "${DOCKER_STUB_DEFAULT_OPTIONS} ruby /src/bin/annotate /junits : cat tests/2-tests-1-failure.output && exit 64"
292 |
293 | run "$PWD/hooks/command"
294 |
295 | assert_failure
296 |
297 | assert_output --partial "Failures too large to annotate"
298 |
299 | unstub mktemp
300 | unstub du
301 | unstub buildkite-agent
302 | unstub docker
303 | }
304 |
305 | @test "error bubbles up when ruby code fails with anything but 64" {
306 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
307 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_FAIL_BUILD_ON_ERROR=false
308 |
309 | stub mktemp \
310 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
311 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
312 |
313 | stub buildkite-agent \
314 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4"
315 |
316 | stub docker \
317 | "${DOCKER_STUB_DEFAULT_OPTIONS} ruby /src/bin/annotate /junits : cat tests/2-tests-1-failure.output && exit 147"
318 |
319 | run "$PWD/hooks/command"
320 |
321 | assert_failure 147
322 |
323 | assert_output --partial "Error when processing JUnit tests"
324 |
325 | unstub mktemp
326 | unstub buildkite-agent
327 | unstub docker
328 | }
329 |
330 | @test "error bubbles up when agent download fails" {
331 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
332 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_FAIL_BUILD_ON_ERROR=false
333 |
334 | stub mktemp \
335 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
336 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
337 |
338 | stub buildkite-agent \
339 | "artifact download \* \* : exit 1"
340 |
341 | run "$PWD/hooks/command"
342 |
343 | assert_failure 2
344 |
345 | assert_output --partial "Could not download artifacts"
346 |
347 | unstub mktemp
348 | unstub buildkite-agent
349 | }
350 |
351 | @test "customize error when agent download fails" {
352 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
353 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_FAILED_DOWNLOAD_EXIT_CODE=5
354 |
355 | stub mktemp \
356 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
357 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
358 |
359 | stub buildkite-agent \
360 | "artifact download \* \* : exit 1"
361 |
362 | run "$PWD/hooks/command"
363 |
364 | assert_failure 5
365 |
366 | assert_output --partial "Could not download artifacts"
367 |
368 | unstub mktemp
369 | unstub buildkite-agent
370 | }
371 |
372 | @test "creates annotation with no failures but min tests triggers" {
373 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
374 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_MIN_TESTS=1
375 |
376 | stub mktemp \
377 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
378 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
379 |
380 | stub buildkite-agent \
381 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4" \
382 | "annotate --context \* --style \* : cat >'${annotation_input}'; echo Annotation added with context \$3 and style \$5, content saved"
383 |
384 | stub docker \
385 | "${DOCKER_STUB_DEFAULT_OPTIONS} ruby /src/bin/annotate /junits : echo 'Total tests: 0'"
386 |
387 | run "$PWD/hooks/command"
388 |
389 | assert_failure
390 | assert_output --partial "Total tests: 0"
391 | assert_output --partial "Less than 1 tests analyzed"
392 | assert_equal "$(cat "${annotation_input}")" 'Total tests: 0'
393 |
394 | unstub mktemp
395 | unstub buildkite-agent
396 | unstub docker
397 | }
398 |
399 | @test "no failures and min-tests ok does not create annotation" {
400 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
401 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_MIN_TESTS=12
402 |
403 | stub mktemp \
404 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
405 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
406 |
407 | stub buildkite-agent \
408 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4"
409 |
410 | stub docker \
411 | "${DOCKER_STUB_DEFAULT_OPTIONS} ruby /src/bin/annotate /junits : echo 'Total tests: 100'"
412 |
413 | run "$PWD/hooks/command"
414 |
415 | assert_success
416 | assert_output --partial "Total tests: 100"
417 | refute_output --partial "Less than 12 tests analyzed"
418 |
419 | unstub mktemp
420 | unstub buildkite-agent
421 | unstub docker
422 | }
423 |
424 | @test "min-tests doesn't interfere with actual failures" {
425 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
426 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_MIN_TESTS=10000
427 |
428 | stub mktemp \
429 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
430 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
431 |
432 | stub buildkite-agent \
433 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4" \
434 | "annotate --context \* --style \* : cat >'${annotation_input}'; echo Annotation added with context \$3 and style \$5, content saved"
435 |
436 | stub docker \
437 | "${DOCKER_STUB_DEFAULT_OPTIONS} ruby /src/bin/annotate /junits : cat tests/2-tests-1-failure.output && exit 64"
438 |
439 | run "$PWD/hooks/command"
440 |
441 | assert_success
442 | assert_output --partial "Total tests: 2"
443 |
444 | unstub mktemp
445 | unstub buildkite-agent
446 | unstub docker
447 | }
448 |
449 | @test "runs the annotator and creates the annotation with special image" {
450 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
451 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_RUBY_IMAGE="ruby:special"
452 |
453 | stub mktemp \
454 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
455 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
456 |
457 | stub buildkite-agent \
458 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4" \
459 | "annotate --context \* --style \* : cat >'${annotation_input}'; echo Annotation added with context \$3 and style \$5, content saved"
460 |
461 | stub docker \
462 | "--log-level error run --rm --volume \* --volume \* --env \* --env \* --env \* --env \* ruby:special ruby /src/bin/annotate /junits : echo 'Failure ' && exit 64"
463 |
464 | run "$PWD/hooks/command"
465 |
466 | assert_success
467 |
468 | assert_output --partial "Annotation added with context junit and style error"
469 | assert_equal "$(cat "${annotation_input}")" 'Failure '
470 |
471 | unstub mktemp
472 | unstub buildkite-agent
473 | unstub docker
474 | rm "${annotation_input}"
475 | }
476 |
477 | @test "can customize skipped tests variable" {
478 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
479 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_REPORT_SKIPPED="true"
480 |
481 | stub mktemp \
482 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
483 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
484 |
485 | stub buildkite-agent \
486 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4" \
487 | "annotate --context \* --style \* : cat >'${annotation_input}'; echo Annotation added with context \$3 and style \$5, content saved"
488 |
489 | stub docker \
490 | "--log-level error run --rm --volume \* --volume \* --env \* --env \* --env \* --env BUILDKITE_PLUGIN_JUNIT_ANNOTATE_REPORT_SKIPPED='true' \* ruby /src/bin/annotate /junits : cat tests/2-tests-1-failure.output && exit 64"
491 |
492 | run "$PWD/hooks/command"
493 |
494 | assert_success
495 |
496 | assert_output --partial "Annotation added"
497 |
498 | unstub mktemp
499 | unstub buildkite-agent
500 | unstub docker
501 | rm "${annotation_input}"
502 | }
503 |
504 | @test "creates annotation with no failures but with slowest tests trigger" {
505 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
506 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_REPORT_SLOWEST=5
507 |
508 | stub mktemp \
509 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
510 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
511 |
512 | stub buildkite-agent \
513 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4" \
514 | "annotate --context \* --style \* : cat >'${annotation_input}'; echo Annotation added with context \$3 and style \$5, content saved"
515 |
516 | stub docker \
517 | "--log-level error run --rm --volume \* --volume \* --env \* --env \* --env BUILDKITE_PLUGIN_JUNIT_ANNOTATE_REPORT_SLOWEST=5 --env \* \* ruby /src/bin/annotate /junits : cat tests/2-slowest-tests.output"
518 |
519 | run "$PWD/hooks/command"
520 |
521 | assert_success
522 |
523 | assert_output --partial "Create annotation with slowest tests"
524 | assert_output --partial "5 slowest tests"
525 |
526 | unstub mktemp
527 | unstub buildkite-agent
528 | unstub docker
529 | rm "${annotation_input}"
530 | }
531 |
532 | @test "does not run in docker when run-in-docker is false" {
533 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_ARTIFACTS="junits/*.xml"
534 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_FAIL_BUILD_ON_ERROR=false
535 | export BUILDKITE_PLUGIN_JUNIT_ANNOTATE_RUN_IN_DOCKER=false
536 |
537 | stub mktemp \
538 | "-d \* : mkdir -p '$artifacts_tmp'; echo '$artifacts_tmp'" \
539 | "-d \* : mkdir -p '$annotation_tmp'; echo '$annotation_tmp'"
540 |
541 | stub buildkite-agent \
542 | "artifact download \* \* : echo Downloaded artifact \$3 to \$4" \
543 | "annotate --context \* --style \* : cat >'${annotation_input}'; echo Annotation added with context \$3 and style \$5, content saved"
544 |
545 | stub ruby \
546 | "/plugin/hooks/../ruby/bin/annotate /plugin/${artifacts_tmp} : echo 'Failure ' && exit 64"
547 |
548 | run "$PWD/hooks/command"
549 |
550 | assert_success
551 |
552 | assert_output --partial "Annotation added with context junit and style error"
553 | assert_equal "$(cat "${annotation_input}")" 'Failure '
554 |
555 | unstub mktemp
556 | unstub buildkite-agent
557 | unstub ruby
558 | rm "${annotation_input}"
559 | }
--------------------------------------------------------------------------------