mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-17 21:26:53 +00:00
[ci] Include a log download link when test report is truncated (#117985)
Now "Download" will be a link to the file so people don't have to know to open the build tab and find the download button. This is a URL from a real build: https://buildkite.com/organizations/llvm-project/pipelines/github-pull-requests/builds/123979/jobs/01937132-0fc3-4c95-a884-2fc0048cb9a7/download.txt And this is how we can build it: https://buildkite.com/organizations/{BUILDKITE_ORGANIZATION_SLUG}/pipelines/{BUILDKITE_PIPELINE_SLUG}/builds/{BUILDKITE_BUILD_NUMBER}/jobs/{BUILDKITE_JOB_ID}/download.txt Given these env vars that were set in that job: BUILDKITE_ORGANIZATION_SLUG="llvm-project" BUILDKITE_PIPELINE_SLUG="github-pull-requests" BUILDKITE_BUILD_NUMBER="123979" BUILDKITE_JOB_ID="01937132-0fc3-4c95-a884-2fc0048cb9a7" In theory these will always be available but: 1. Rather safe than sorry with this script, I don't want to make a passing build a failure because this script failed. 2. It would get very annoying if you had to set all these to test the script locally.
This commit is contained in:
parent
a1ee1a9126
commit
71fd5288d2
@ -5,6 +5,7 @@
|
|||||||
# python3 -m unittest discover -p generate_test_report.py
|
# python3 -m unittest discover -p generate_test_report.py
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import unittest
|
import unittest
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
@ -267,6 +268,46 @@ class TestReports(unittest.TestCase):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_report_dont_list_failures_link_to_log(self):
|
||||||
|
self.assertEqual(
|
||||||
|
_generate_report(
|
||||||
|
"Foo",
|
||||||
|
[
|
||||||
|
junit_from_xml(
|
||||||
|
dedent(
|
||||||
|
"""\
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<testsuites time="0.02">
|
||||||
|
<testsuite name="Bar" tests="1" failures="1" skipped="0" time="0.02">
|
||||||
|
<testcase classname="Bar/test_1" name="test_1" time="0.02">
|
||||||
|
<failure><![CDATA[Output goes here]]></failure>
|
||||||
|
</testcase>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>"""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
list_failures=False,
|
||||||
|
buildkite_info={
|
||||||
|
"BUILDKITE_ORGANIZATION_SLUG": "organization_slug",
|
||||||
|
"BUILDKITE_PIPELINE_SLUG": "pipeline_slug",
|
||||||
|
"BUILDKITE_BUILD_NUMBER": "build_number",
|
||||||
|
"BUILDKITE_JOB_ID": "job_id",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
dedent(
|
||||||
|
"""\
|
||||||
|
# Foo
|
||||||
|
|
||||||
|
* 1 test failed
|
||||||
|
|
||||||
|
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."""
|
||||||
|
),
|
||||||
|
"error",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
def test_report_size_limit(self):
|
def test_report_size_limit(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
_generate_report(
|
_generate_report(
|
||||||
@ -308,7 +349,13 @@ class TestReports(unittest.TestCase):
|
|||||||
# listed. This minimal report will always fit into an annotation.
|
# listed. This minimal report will always fit into an annotation.
|
||||||
# If include failures is False, total number of test will be reported but their names
|
# If include failures is False, total number of test will be reported but their names
|
||||||
# and output will not be.
|
# and output will not be.
|
||||||
def _generate_report(title, junit_objects, size_limit=1024 * 1024, list_failures=True):
|
def _generate_report(
|
||||||
|
title,
|
||||||
|
junit_objects,
|
||||||
|
size_limit=1024 * 1024,
|
||||||
|
list_failures=True,
|
||||||
|
buildkite_info=None,
|
||||||
|
):
|
||||||
if not junit_objects:
|
if not junit_objects:
|
||||||
return ("", "success")
|
return ("", "success")
|
||||||
|
|
||||||
@ -354,11 +401,21 @@ def _generate_report(title, junit_objects, size_limit=1024 * 1024, list_failures
|
|||||||
report.append(f"* {tests_failed} {plural(tests_failed)} failed")
|
report.append(f"* {tests_failed} {plural(tests_failed)} failed")
|
||||||
|
|
||||||
if not list_failures:
|
if not list_failures:
|
||||||
|
if buildkite_info is not None:
|
||||||
|
log_url = (
|
||||||
|
"https://buildkite.com/organizations/{BUILDKITE_ORGANIZATION_SLUG}/"
|
||||||
|
"pipelines/{BUILDKITE_PIPELINE_SLUG}/builds/{BUILDKITE_BUILD_NUMBER}/"
|
||||||
|
"jobs/{BUILDKITE_JOB_ID}/download.txt".format(**buildkite_info)
|
||||||
|
)
|
||||||
|
download_text = f"[Download]({log_url})"
|
||||||
|
else:
|
||||||
|
download_text = "Download"
|
||||||
|
|
||||||
report.extend(
|
report.extend(
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"Failed tests and their output was too large to report. "
|
"Failed tests and their output was too large to report. "
|
||||||
"Download the build's log file to see the details.",
|
f"{download_text} the build's log file to see the details.",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
elif failures:
|
elif failures:
|
||||||
@ -381,13 +438,23 @@ def _generate_report(title, junit_objects, size_limit=1024 * 1024, list_failures
|
|||||||
|
|
||||||
report = "\n".join(report)
|
report = "\n".join(report)
|
||||||
if len(report.encode("utf-8")) > size_limit:
|
if len(report.encode("utf-8")) > size_limit:
|
||||||
return _generate_report(title, junit_objects, size_limit, list_failures=False)
|
return _generate_report(
|
||||||
|
title,
|
||||||
|
junit_objects,
|
||||||
|
size_limit,
|
||||||
|
list_failures=False,
|
||||||
|
buildkite_info=buildkite_info,
|
||||||
|
)
|
||||||
|
|
||||||
return report, style
|
return report, style
|
||||||
|
|
||||||
|
|
||||||
def generate_report(title, junit_files):
|
def generate_report(title, junit_files, buildkite_info):
|
||||||
return _generate_report(title, [JUnitXml.fromfile(p) for p in junit_files])
|
return _generate_report(
|
||||||
|
title,
|
||||||
|
[JUnitXml.fromfile(p) for p in junit_files],
|
||||||
|
buildkite_info=buildkite_info,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@ -399,7 +466,18 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument("junit_files", help="Paths to JUnit report files.", nargs="*")
|
parser.add_argument("junit_files", help="Paths to JUnit report files.", nargs="*")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
report, style = generate_report(args.title, args.junit_files)
|
# All of these are required to build a link to download the log file.
|
||||||
|
env_var_names = [
|
||||||
|
"BUILDKITE_ORGANIZATION_SLUG",
|
||||||
|
"BUILDKITE_PIPELINE_SLUG",
|
||||||
|
"BUILDKITE_BUILD_NUMBER",
|
||||||
|
"BUILDKITE_JOB_ID",
|
||||||
|
]
|
||||||
|
buildkite_info = {k: v for k, v in os.environ.items() if k in env_var_names}
|
||||||
|
if len(buildkite_info) != len(env_var_names):
|
||||||
|
buildkite_info = None
|
||||||
|
|
||||||
|
report, style = generate_report(args.title, args.junit_files, buildkite_info)
|
||||||
|
|
||||||
if report:
|
if report:
|
||||||
p = subprocess.Popen(
|
p = subprocess.Popen(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user