summaryrefslogtreecommitdiff
path: root/buildscripts/simple_report.py
blob: 668f0bd6096669e2a78df9a4abafddc21d86a577 (plain)
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
"""Given a test name, path to log file and exit code, generate/append an Evergreen report.json."""
import json
import pathlib
import os
from typing import List, Dict, Optional
from typing_extensions import TypedDict
import click


class Result(TypedDict, total=False):
    """Evergreen test result."""

    status: str
    exit_code: int
    test_file: str
    start: float
    end: float
    elapsed: float
    log_raw: str


class Report(TypedDict):
    """Evergreen report."""

    failures: int
    results: List[Result]


def _open_and_truncate_log_lines(log_file: pathlib.Path) -> List[str]:
    with open(log_file) as fh:
        lines = fh.read().splitlines()
        for i, line in enumerate(lines):
            if line == "scons: done reading SConscript files.":
                offset = i
                # if possible, also shave off the current and next line
                # as they contain:
                # scons: done reading SConscript files.
                # scons: Building targets ...
                # which is superfluous.
                if len(lines) > i + 2:
                    offset = i + 2
                return lines[offset:]

        return lines


def _clean_log_file(log_file: pathlib.Path, dedup_lines: bool) -> str:
    lines = _open_and_truncate_log_lines(log_file)
    if dedup_lines:
        lines = _dedup_lines(lines)
    return os.linesep.join(lines)


def make_report(test_name: str, log_file_contents: str, exit_code: int) -> Report:
    status = "pass" if exit_code == 0 else "fail"
    return Report({
        'failures':
            0 if exit_code == 0 else 1, "results": [
                Result({
                    "status": status, "exit_code": exit_code, "test_file": test_name,
                    "log_raw": log_file_contents
                })
            ]
    })


def try_combine_reports(out: Report):
    try:
        with open("report.json") as fh:
            report = json.load(fh)
            out["results"] += report["results"]
            out["failures"] += report["failures"]
    except NameError:
        pass
    except IOError:
        pass


def _dedup_lines(lines: List[str]) -> List[str]:
    return list(set(lines))


def put_report(out: Report):
    with open("report.json", "w") as fh:
        json.dump(out, fh)


@click.command()
@click.option("--test-name", required=True, type=str)
@click.option("--log-file", required=True, type=pathlib.Path)
@click.option("--exit-code", required=True, type=int)
@click.option("--dedup-lines", is_flag=True)
def main(test_name: str, log_file: pathlib.Path, exit_code: int, dedup_lines: bool):
    """Given a test name, path to log file and exit code, generate/append an Evergreen report.json."""
    log_file_contents = _clean_log_file(log_file, dedup_lines)
    report = make_report(test_name, log_file_contents, exit_code)
    try_combine_reports(report)
    put_report(report)


if __name__ == "__main__":
    main()  # pylint: disable=no-value-for-parameter