diff options
author | Nikita Kozlovskiy <nikitka@gmail.com> | 2023-06-19 11:28:03 +0000 |
---|---|---|
committer | nkozlovskiy <nmk@ydb.tech> | 2023-06-19 14:28:03 +0300 |
commit | ae3b073bf18da0b90a7c933e688f56d09cd1fd12 (patch) | |
tree | cdb655221d55198ce3ce228e1a6eb990a4486556 /.github/scripts/tests/generate-summary.py | |
parent | cb828d899b578d6d5da199b00750a5276f0004d7 (diff) | |
download | ydb-ae3b073bf18da0b90a7c933e688f56d09cd1fd12.tar.gz |
ci: summary extraction as independent step, add functional tests summary
ci: summary extraction as independent step, add functional tests summary
Pull Request resolved: #264
Diffstat (limited to '.github/scripts/tests/generate-summary.py')
-rwxr-xr-x | .github/scripts/tests/generate-summary.py | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/.github/scripts/tests/generate-summary.py b/.github/scripts/tests/generate-summary.py new file mode 100755 index 00000000000..e7dbca46eb2 --- /dev/null +++ b/.github/scripts/tests/generate-summary.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +import argparse +import os +import glob +import dataclasses +import sys +from typing import Optional, List +from xml.etree import ElementTree as ET +from junit_utils import get_property_value + + +@dataclasses.dataclass +class SummaryEntry: + target: str + log_url: Optional[str] + reason = "" + is_failure: bool = False + is_error: bool = False + is_muted: bool = False + is_skipped: bool = False + + @property + def display_status(self): + if self.is_error: + return "Error" + elif self.is_failure: + return "Failure" + elif self.is_muted: + return "Muted" + elif self.is_skipped: + return "Skipped" + + return "?" + + +def iter_xml_files(folder_or_file): + if os.path.isfile(folder_or_file): + files = [folder_or_file] + else: + files = glob.glob(os.path.join(folder_or_file, "*.xml")) + + for fn in files: + tree = ET.parse(fn) + root = tree.getroot() + + if root.tag == "testsuite": + suites = [root] + elif root.tag == "testsuites": + suites = root.findall("testsuite") + else: + raise ValueError(f"Invalid root tag {root.tag}") + for suite in suites: + for case in suite.findall("testcase"): + yield fn, suite, case + + +def parse_junit(folder_or_file): + result = [] + for fn, suite, case in iter_xml_files(folder_or_file): + is_failure = case.find("failure") is not None + is_error = case.find("error") is not None + is_muted = get_property_value(case, "mute") is not None + is_skipped = is_muted is False and case.find("skipped") is not None + + if any([is_failure, is_muted, is_skipped, is_error]): + cls, method = case.attrib["classname"], case.attrib["name"] + log_url = get_property_value(case, "url:Log") + target = f"{ cls }::{ method }" if cls != method else cls + + result.append( + SummaryEntry( + target=target, + log_url=log_url, + is_skipped=is_skipped, + is_muted=is_muted, + is_failure=is_failure, + is_error=is_error, + ) + ) + return result + + +def generate_summary(summary: List[SummaryEntry]): + log_icon = ":floppy_disk:" + mute_icon = ":white_check_mark:" + + text = [ + "| Test | Muted | Log |", + "| ----: | :---: | --: |", + ] + + for entry in summary: + if entry.log_url: + log_url = f"[{log_icon}]({entry.log_url})" + else: + log_url = "" + + mute_target = mute_icon if entry.is_muted else "" + + text.append(f"| {entry.target} | {mute_target} | {log_url} |") + + return text + + +def write_summary(title, lines: List[str]): + summary_fn = os.environ.get("GITHUB_STEP_SUMMARY") + if summary_fn: + fp = open(summary_fn, "at") + else: + fp = sys.stdout + + if title: + fp.write(f"{title}\n") + for line in lines: + fp.write(f"{line}\n") + fp.write("\n") + + if summary_fn: + fp.close() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-t", "--title") + parser.add_argument("folder_or_file") + + args = parser.parse_args() + + summary = parse_junit(args.folder_or_file) + + if summary: + text = generate_summary(summary) + write_summary(args.title, text) + + +if __name__ == "__main__": + main() |