@@ -19,12 +19,13 @@ def junit_from_xml(xml):
19
19
20
20
class TestReports (unittest .TestCase ):
21
21
def test_title_only (self ):
22
- self .assertEqual (_generate_report ("Foo" , []), ("" , "success" ))
22
+ self .assertEqual (_generate_report ("Foo" , 0 , []), ("" , "success" ))
23
23
24
24
def test_no_tests_in_testsuite (self ):
25
25
self .assertEqual (
26
26
_generate_report (
27
27
"Foo" ,
28
+ 1 ,
28
29
[
29
30
junit_from_xml (
30
31
dedent (
@@ -45,6 +46,7 @@ def test_no_failures(self):
45
46
self .assertEqual (
46
47
_generate_report (
47
48
"Foo" ,
49
+ 0 ,
48
50
[
49
51
junit_from_xml (
50
52
dedent (
@@ -70,10 +72,51 @@ def test_no_failures(self):
70
72
),
71
73
)
72
74
75
+ def test_no_failures_build_failed (self ):
76
+ self .assertEqual (
77
+ _generate_report (
78
+ "Foo" ,
79
+ 1 ,
80
+ [
81
+ junit_from_xml (
82
+ dedent (
83
+ """\
84
+ <?xml version="1.0" encoding="UTF-8"?>
85
+ <testsuites time="0.00">
86
+ <testsuite name="Passed" tests="1" failures="0" skipped="0" time="0.00">
87
+ <testcase classname="Bar/test_1" name="test_1" time="0.00"/>
88
+ </testsuite>
89
+ </testsuites>"""
90
+ )
91
+ )
92
+ ],
93
+ buildkite_info = {
94
+ "BUILDKITE_ORGANIZATION_SLUG" : "organization_slug" ,
95
+ "BUILDKITE_PIPELINE_SLUG" : "pipeline_slug" ,
96
+ "BUILDKITE_BUILD_NUMBER" : "build_number" ,
97
+ "BUILDKITE_JOB_ID" : "job_id" ,
98
+ },
99
+ ),
100
+ (
101
+ dedent (
102
+ """\
103
+ # Foo
104
+
105
+ * 1 test passed
106
+
107
+ All tests passed but another part of the build **failed**.
108
+
109
+ [Download](https://buildkite.com/organizations/organization_slug/pipelines/pipeline_slug/builds/build_number/jobs/job_id/download.txt) the build's log file to see the details."""
110
+ ),
111
+ "error" ,
112
+ ),
113
+ )
114
+
73
115
def test_report_single_file_single_testsuite (self ):
74
116
self .assertEqual (
75
117
_generate_report (
76
118
"Foo" ,
119
+ 1 ,
77
120
[
78
121
junit_from_xml (
79
122
dedent (
@@ -166,6 +209,7 @@ def test_report_single_file_multiple_testsuites(self):
166
209
self .assertEqual (
167
210
_generate_report (
168
211
"ABC and DEF" ,
212
+ 1 ,
169
213
[
170
214
junit_from_xml (
171
215
dedent (
@@ -198,6 +242,7 @@ def test_report_multiple_files_multiple_testsuites(self):
198
242
self .assertEqual (
199
243
_generate_report (
200
244
"ABC and DEF" ,
245
+ 1 ,
201
246
[
202
247
junit_from_xml (
203
248
dedent (
@@ -238,6 +283,7 @@ def test_report_dont_list_failures(self):
238
283
self .assertEqual (
239
284
_generate_report (
240
285
"Foo" ,
286
+ 1 ,
241
287
[
242
288
junit_from_xml (
243
289
dedent (
@@ -272,6 +318,7 @@ def test_report_dont_list_failures_link_to_log(self):
272
318
self .assertEqual (
273
319
_generate_report (
274
320
"Foo" ,
321
+ 1 ,
275
322
[
276
323
junit_from_xml (
277
324
dedent (
@@ -312,6 +359,7 @@ def test_report_size_limit(self):
312
359
self .assertEqual (
313
360
_generate_report (
314
361
"Foo" ,
362
+ 1 ,
315
363
[
316
364
junit_from_xml (
317
365
dedent (
@@ -351,12 +399,18 @@ def test_report_size_limit(self):
351
399
# and output will not be.
352
400
def _generate_report (
353
401
title ,
402
+ return_code ,
354
403
junit_objects ,
355
404
size_limit = 1024 * 1024 ,
356
405
list_failures = True ,
357
406
buildkite_info = None ,
358
407
):
359
408
if not junit_objects :
409
+ # Note that we do not post an empty report, therefore we can ignore a
410
+ # non-zero return code in situations like this.
411
+ #
412
+ # If we were going to post a report, then yes, it would be misleading
413
+ # to say we succeeded when the final return code was non-zero.
360
414
return ("" , "success" )
361
415
362
416
failures = {}
@@ -385,7 +439,11 @@ def _generate_report(
385
439
if not tests_run :
386
440
return ("" , None )
387
441
388
- style = "error" if tests_failed else "success"
442
+ style = "success"
443
+ # Either tests failed, or all tests passed but something failed to build.
444
+ if tests_failed or return_code != 0 :
445
+ style = "error"
446
+
389
447
report = [f"# { title } " , "" ]
390
448
391
449
tests_passed = tests_run - tests_skipped - tests_failed
@@ -400,17 +458,17 @@ def plural(num_tests):
400
458
if tests_failed :
401
459
report .append (f"* { tests_failed } { plural (tests_failed )} failed" )
402
460
403
- if not list_failures :
404
- if buildkite_info is not None :
405
- log_url = (
406
- "https://buildkite.com/organizations/{BUILDKITE_ORGANIZATION_SLUG}/"
407
- "pipelines/{BUILDKITE_PIPELINE_SLUG}/builds/{BUILDKITE_BUILD_NUMBER}/"
408
- "jobs/{BUILDKITE_JOB_ID}/download.txt" .format (** buildkite_info )
409
- )
410
- download_text = f"[Download]({ log_url } )"
411
- else :
412
- download_text = "Download"
461
+ if buildkite_info is not None :
462
+ log_url = (
463
+ "https://buildkite.com/organizations/{BUILDKITE_ORGANIZATION_SLUG}/"
464
+ "pipelines/{BUILDKITE_PIPELINE_SLUG}/builds/{BUILDKITE_BUILD_NUMBER}/"
465
+ "jobs/{BUILDKITE_JOB_ID}/download.txt" .format (** buildkite_info )
466
+ )
467
+ download_text = f"[Download]({ log_url } )"
468
+ else :
469
+ download_text = "Download"
413
470
471
+ if not list_failures :
414
472
report .extend (
415
473
[
416
474
"" ,
@@ -435,11 +493,23 @@ def plural(num_tests):
435
493
"</details>" ,
436
494
]
437
495
)
496
+ elif return_code != 0 :
497
+ # No tests failed but the build was in a failed state. Bring this to the user's
498
+ # attention.
499
+ report .extend (
500
+ [
501
+ "" ,
502
+ "All tests passed but another part of the build **failed**." ,
503
+ "" ,
504
+ f"{ download_text } the build's log file to see the details." ,
505
+ ]
506
+ )
438
507
439
508
report = "\n " .join (report )
440
509
if len (report .encode ("utf-8" )) > size_limit :
441
510
return _generate_report (
442
511
title ,
512
+ return_code ,
443
513
junit_objects ,
444
514
size_limit ,
445
515
list_failures = False ,
@@ -449,9 +519,10 @@ def plural(num_tests):
449
519
return report , style
450
520
451
521
452
- def generate_report (title , junit_files , buildkite_info ):
522
+ def generate_report (title , return_code , junit_files , buildkite_info ):
453
523
return _generate_report (
454
524
title ,
525
+ return_code ,
455
526
[JUnitXml .fromfile (p ) for p in junit_files ],
456
527
buildkite_info = buildkite_info ,
457
528
)
@@ -463,6 +534,7 @@ def generate_report(title, junit_files, buildkite_info):
463
534
"title" , help = "Title of the test report, without Markdown formatting."
464
535
)
465
536
parser .add_argument ("context" , help = "Annotation context to write to." )
537
+ parser .add_argument ("return_code" , help = "The build's return code." , type = int )
466
538
parser .add_argument ("junit_files" , help = "Paths to JUnit report files." , nargs = "*" )
467
539
args = parser .parse_args ()
468
540
@@ -477,7 +549,9 @@ def generate_report(title, junit_files, buildkite_info):
477
549
if len (buildkite_info ) != len (env_var_names ):
478
550
buildkite_info = None
479
551
480
- report , style = generate_report (args .title , args .junit_files , buildkite_info )
552
+ report , style = generate_report (
553
+ args .title , args .return_code , args .junit_files , buildkite_info
554
+ )
481
555
482
556
if report :
483
557
p = subprocess .Popen (
0 commit comments