5
5
# python3 -m unittest discover -p generate_test_report.py
6
6
7
7
import argparse
8
+ import os
8
9
import subprocess
9
10
import unittest
10
11
from io import StringIO
@@ -267,6 +268,46 @@ def test_report_dont_list_failures(self):
267
268
),
268
269
)
269
270
271
+ def test_report_dont_list_failures_link_to_log (self ):
272
+ self .assertEqual (
273
+ _generate_report (
274
+ "Foo" ,
275
+ [
276
+ junit_from_xml (
277
+ dedent (
278
+ """\
279
+ <?xml version="1.0" encoding="UTF-8"?>
280
+ <testsuites time="0.02">
281
+ <testsuite name="Bar" tests="1" failures="1" skipped="0" time="0.02">
282
+ <testcase classname="Bar/test_1" name="test_1" time="0.02">
283
+ <failure><![CDATA[Output goes here]]></failure>
284
+ </testcase>
285
+ </testsuite>
286
+ </testsuites>"""
287
+ )
288
+ )
289
+ ],
290
+ list_failures = False ,
291
+ buildkite_info = {
292
+ "BUILDKITE_ORGANIZATION_SLUG" : "organization_slug" ,
293
+ "BUILDKITE_PIPELINE_SLUG" : "pipeline_slug" ,
294
+ "BUILDKITE_BUILD_NUMBER" : "build_number" ,
295
+ "BUILDKITE_JOB_ID" : "job_id" ,
296
+ },
297
+ ),
298
+ (
299
+ dedent (
300
+ """\
301
+ # Foo
302
+
303
+ * 1 test failed
304
+
305
+ Failed tests and their output was too large to report. [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."""
306
+ ),
307
+ "error" ,
308
+ ),
309
+ )
310
+
270
311
def test_report_size_limit (self ):
271
312
self .assertEqual (
272
313
_generate_report (
@@ -308,7 +349,13 @@ def test_report_size_limit(self):
308
349
# listed. This minimal report will always fit into an annotation.
309
350
# If include failures is False, total number of test will be reported but their names
310
351
# and output will not be.
311
- def _generate_report (title , junit_objects , size_limit = 1024 * 1024 , list_failures = True ):
352
+ def _generate_report (
353
+ title ,
354
+ junit_objects ,
355
+ size_limit = 1024 * 1024 ,
356
+ list_failures = True ,
357
+ buildkite_info = None ,
358
+ ):
312
359
if not junit_objects :
313
360
return ("" , "success" )
314
361
@@ -354,11 +401,21 @@ def plural(num_tests):
354
401
report .append (f"* { tests_failed } { plural (tests_failed )} failed" )
355
402
356
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"
413
+
357
414
report .extend (
358
415
[
359
416
"" ,
360
417
"Failed tests and their output was too large to report. "
361
- "Download the build's log file to see the details." ,
418
+ f" { download_text } the build's log file to see the details." ,
362
419
]
363
420
)
364
421
elif failures :
@@ -381,13 +438,23 @@ def plural(num_tests):
381
438
382
439
report = "\n " .join (report )
383
440
if len (report .encode ("utf-8" )) > size_limit :
384
- return _generate_report (title , junit_objects , size_limit , list_failures = False )
441
+ return _generate_report (
442
+ title ,
443
+ junit_objects ,
444
+ size_limit ,
445
+ list_failures = False ,
446
+ buildkite_info = buildkite_info ,
447
+ )
385
448
386
449
return report , style
387
450
388
451
389
- def generate_report (title , junit_files ):
390
- return _generate_report (title , [JUnitXml .fromfile (p ) for p in junit_files ])
452
+ def generate_report (title , junit_files , buildkite_info ):
453
+ return _generate_report (
454
+ title ,
455
+ [JUnitXml .fromfile (p ) for p in junit_files ],
456
+ buildkite_info = buildkite_info ,
457
+ )
391
458
392
459
393
460
if __name__ == "__main__" :
@@ -399,7 +466,18 @@ def generate_report(title, junit_files):
399
466
parser .add_argument ("junit_files" , help = "Paths to JUnit report files." , nargs = "*" )
400
467
args = parser .parse_args ()
401
468
402
- report , style = generate_report (args .title , args .junit_files )
469
+ # All of these are required to build a link to download the log file.
470
+ env_var_names = [
471
+ "BUILDKITE_ORGANIZATION_SLUG" ,
472
+ "BUILDKITE_PIPELINE_SLUG" ,
473
+ "BUILDKITE_BUILD_NUMBER" ,
474
+ "BUILDKITE_JOB_ID" ,
475
+ ]
476
+ buildkite_info = {k : v for k , v in os .environ .items () if k in env_var_names }
477
+ if len (buildkite_info ) != len (env_var_names ):
478
+ buildkite_info = None
479
+
480
+ report , style = generate_report (args .title , args .junit_files , buildkite_info )
403
481
404
482
if report :
405
483
p = subprocess .Popen (
0 commit comments