# -*- coding: utf-8 -*- import os import datetime ### FIXME: BEST FRONTEND PRACTICIES BELOW HTML_BASE_TEST_TEMPLATE = """ {title}

{header}

{test_part} """ HTML_TEST_PART = """ {headers} {rows}
""" BASE_HEADERS = ["Test name", "Test status"] class ReportColorTheme: class ReportColor: yellow = "#FFB400" red = "#F00" green = "#0A0" blue = "#00B4FF" default = (ReportColor.green, ReportColor.red, ReportColor.yellow) bugfixcheck = (ReportColor.yellow, ReportColor.blue, ReportColor.blue) def _format_header(header, branch_name, branch_url=None): result = " ".join([w.capitalize() for w in header.split(" ")]) result = result.replace("Clickhouse", "ClickHouse") result = result.replace("clickhouse", "ClickHouse") if "ClickHouse" not in result: result = "ClickHouse " + result result += " for " if branch_url: result += '{name}'.format(url=branch_url, name=branch_name) else: result += branch_name return result def _get_status_style(status, colortheme=None): ok_statuses = ("OK", "success", "PASSED") fail_statuses = ("FAIL", "failure", "error", "FAILED", "Timeout") if colortheme is None: colortheme = ReportColorTheme.default style = "font-weight: bold;" if status in ok_statuses: style += f"color: {colortheme[0]};" elif status in fail_statuses: style += f"color: {colortheme[1]};" else: style += f"color: {colortheme[2]};" return style def _get_html_url_name(url): if isinstance(url, str): return os.path.basename(url).replace("%2B", "+").replace("%20", " ") if isinstance(url, tuple): return url[1].replace("%2B", "+").replace("%20", " ") return None def _get_html_url(url): href = None name = None if isinstance(url, str): href, name = url, _get_html_url_name(url) if isinstance(url, tuple): href, name = url[0], _get_html_url_name(url) if href and name: return '{name}'.format( href=href, name=_get_html_url_name(url) ) return "" def create_test_html_report( header, test_result, raw_log_url, task_url, job_url, branch_url, branch_name, commit_url, additional_urls=None, with_raw_logs=False, statuscolors=None, ): if additional_urls is None: additional_urls = [] if test_result: rows_part = "" num_fails = 0 has_test_time = False has_test_logs = False if with_raw_logs: # Display entires with logs at the top (they correspond to failed tests) test_result.sort(key=lambda result: len(result) <= 3) for result in test_result: test_name = result[0] test_status = result[1] test_logs = None test_time = None if len(result) > 2: test_time = result[2] has_test_time = True if len(result) > 3: test_logs = result[3] has_test_logs = True row = "" is_fail = test_status in ("FAIL", "FLAKY") if is_fail and with_raw_logs and test_logs is not None: row = '' row += "" + test_name + "" style = _get_status_style(test_status, colortheme=statuscolors) # Allow to quickly scroll to the first failure. is_fail_id = "" if is_fail: num_fails = num_fails + 1 is_fail_id = 'id="fail' + str(num_fails) + '" ' row += ( "'.format(style) + test_status + "" ) if test_time is not None: row += "" + test_time + "" if test_logs is not None and not with_raw_logs: test_logs_html = "
".join([_get_html_url(url) for url in test_logs]) row += "" + test_logs_html + "" row += "" rows_part += row if test_logs is not None and with_raw_logs: row = '' # TODO: compute colspan too row += '
' + test_logs + "
" row += "" rows_part += row headers = BASE_HEADERS if has_test_time: headers.append("Test time, sec.") if has_test_logs and not with_raw_logs: headers.append("Logs") headers = "".join(["" + h + "" for h in headers]) test_part = HTML_TEST_PART.format(headers=headers, rows=rows_part) else: test_part = "" additional_html_urls = " ".join( [_get_html_url(url) for url in sorted(additional_urls, key=_get_html_url_name)] ) raw_log_name = os.path.basename(raw_log_url) if "?" in raw_log_name: raw_log_name = raw_log_name.split("?")[0] result = HTML_BASE_TEST_TEMPLATE.format( title=_format_header(header, branch_name), header=_format_header(header, branch_name, branch_url), raw_log_name=raw_log_name, raw_log_url=raw_log_url, task_url=task_url, job_url=job_url, test_part=test_part, branch_name=branch_name, commit_url=commit_url, additional_urls=additional_html_urls, ) return result HTML_BASE_BUILD_TEMPLATE = """ {title}

{header}

{rows}
Compiler Build type Sanitizer Bundled Libraries Status Build log Build time Artifacts
""" LINK_TEMPLATE = '{text}' def create_build_html_report( header, build_results, build_logs_urls, artifact_urls_list, task_url, branch_url, branch_name, commit_url, ): rows = "" for (build_result, build_log_url, artifact_urls) in zip( build_results, build_logs_urls, artifact_urls_list ): row = "" row += "{}".format(build_result.compiler) if build_result.build_type: row += "{}".format(build_result.build_type) else: row += "{}".format("relwithdebuginfo") if build_result.sanitizer: row += "{}".format(build_result.sanitizer) else: row += "{}".format("none") row += "{}".format(build_result.bundled) row += "{}".format(build_result.libraries) if build_result.status: style = _get_status_style(build_result.status) row += '{}'.format(style, build_result.status) else: style = _get_status_style("error") row += '{}'.format(style, "error") row += 'link'.format(build_log_url) if build_result.elapsed_seconds: delta = datetime.timedelta(seconds=build_result.elapsed_seconds) else: delta = "unknown" row += "{}".format(str(delta)) links = "" link_separator = "
" if artifact_urls: for artifact_url in artifact_urls: links += LINK_TEMPLATE.format( text=_get_html_url_name(artifact_url), url=artifact_url ) links += link_separator if links: links = links[: -len(link_separator)] row += "{}".format(links) row += "" rows += row return HTML_BASE_BUILD_TEMPLATE.format( title=_format_header(header, branch_name), header=_format_header(header, branch_name, branch_url), rows=rows, task_url=task_url, branch_name=branch_name, commit_url=commit_url, )