aboutsummaryrefslogtreecommitdiffstats
path: root/.github/scripts/tests/junit_utils.py
blob: eca82b39563eca50e743643fd97ba300dc8fffc2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import os
import glob
import sys
from xml.etree import ElementTree as ET


def get_or_create_properties(testcase):
    props = testcase.find("properties")
    if props is None:
        props = ET.Element("properties")
        testcase.append(props)
    return props


def add_junit_link_property(testcase, name, url):
    add_junit_property(testcase, f"url:{name}", url)


def add_junit_property(testcase, name, value):
    props = get_or_create_properties(testcase)

    # remove existing property if exists
    for item in props.findall("property"):
        if item.get('name') == name:
            props.remove(item)
            break

    props.append(ET.Element("property", dict(name=name, value=value)))


def add_junit_log_property(testcase, url):
    add_junit_link_property(testcase, "Log", url)


def get_property_value(testcase, name):
    props = testcase.find("properties")
    if props is None:
        return None

    for prop in props.findall("property"):
        if prop.attrib["name"] == name:
            return prop.attrib["value"]


def create_error_testsuite(testcases):
    n = str(len(testcases))
    suite = ET.Element("testsuite", dict(tests=n, errors=n))
    suite.extend(testcases)

    root = ET.Element("testsuites", dict(tests=n, errors=n))
    root.append(suite)
    return ET.ElementTree(root)


def create_error_testcase(shardname, classname, name, log_fn=None, log_url=None):
    testcase = ET.Element("testcase", dict(classname=classname, name=name))
    add_junit_property(testcase, "shard", shardname)
    if log_url:
        add_junit_log_property(testcase, log_url)

    err = ET.Element("error", dict(type="error"))

    if log_fn:
        with open(log_fn, "rt") as fp:
            err.text = fp.read(4096)
    testcase.append(err)

    return testcase


def suite_case_iterator(root):
    for suite in root.findall("testsuite"):
        for case in suite.findall("testcase"):
            cls, method = case.attrib["classname"], case.attrib["name"]
            yield suite, case, cls, method


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:
        try:
            tree = ET.parse(fn)
        except ET.ParseError as e:
            print(f"Unable to parse {fn}: {e}", file=sys.stderr)
            continue

        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 is_faulty_testcase(testcase):
    return testcase.find("failure") is not None or testcase.find("error") is not None