1""" log machine-parseable test session result information in a plain
2text file.
3"""
4from __future__ import absolute_import, division, print_function
5
6import py
7import os
8
9
10def pytest_addoption(parser):
11    group = parser.getgroup("terminal reporting", "resultlog plugin options")
12    group.addoption(
13        "--resultlog",
14        "--result-log",
15        action="store",
16        metavar="path",
17        default=None,
18        help="DEPRECATED path for machine-readable result log.",
19    )
20
21
22def pytest_configure(config):
23    resultlog = config.option.resultlog
24    # prevent opening resultlog on slave nodes (xdist)
25    if resultlog and not hasattr(config, "slaveinput"):
26        dirname = os.path.dirname(os.path.abspath(resultlog))
27        if not os.path.isdir(dirname):
28            os.makedirs(dirname)
29        logfile = open(resultlog, "w", 1)  # line buffered
30        config._resultlog = ResultLog(config, logfile)
31        config.pluginmanager.register(config._resultlog)
32
33        from _pytest.deprecated import RESULT_LOG
34
35        config.warn("C1", RESULT_LOG)
36
37
38def pytest_unconfigure(config):
39    resultlog = getattr(config, "_resultlog", None)
40    if resultlog:
41        resultlog.logfile.close()
42        del config._resultlog
43        config.pluginmanager.unregister(resultlog)
44
45
46def generic_path(item):
47    chain = item.listchain()
48    gpath = [chain[0].name]
49    fspath = chain[0].fspath
50    fspart = False
51    for node in chain[1:]:
52        newfspath = node.fspath
53        if newfspath == fspath:
54            if fspart:
55                gpath.append(":")
56                fspart = False
57            else:
58                gpath.append(".")
59        else:
60            gpath.append("/")
61            fspart = True
62        name = node.name
63        if name[0] in "([":
64            gpath.pop()
65        gpath.append(name)
66        fspath = newfspath
67    return "".join(gpath)
68
69
70class ResultLog(object):
71
72    def __init__(self, config, logfile):
73        self.config = config
74        self.logfile = logfile  # preferably line buffered
75
76    def write_log_entry(self, testpath, lettercode, longrepr):
77        print("%s %s" % (lettercode, testpath), file=self.logfile)
78        for line in longrepr.splitlines():
79            print(" %s" % line, file=self.logfile)
80
81    def log_outcome(self, report, lettercode, longrepr):
82        testpath = getattr(report, "nodeid", None)
83        if testpath is None:
84            testpath = report.fspath
85        self.write_log_entry(testpath, lettercode, longrepr)
86
87    def pytest_runtest_logreport(self, report):
88        if report.when != "call" and report.passed:
89            return
90        res = self.config.hook.pytest_report_teststatus(report=report)
91        code = res[1]
92        if code == "x":
93            longrepr = str(report.longrepr)
94        elif code == "X":
95            longrepr = ""
96        elif report.passed:
97            longrepr = ""
98        elif report.failed:
99            longrepr = str(report.longrepr)
100        elif report.skipped:
101            longrepr = str(report.longrepr[2])
102        self.log_outcome(report, code, longrepr)
103
104    def pytest_collectreport(self, report):
105        if not report.passed:
106            if report.failed:
107                code = "F"
108                longrepr = str(report.longrepr)
109            else:
110                assert report.skipped
111                code = "S"
112                longrepr = "%s:%d: %s" % report.longrepr
113            self.log_outcome(report, code, longrepr)
114
115    def pytest_internalerror(self, excrepr):
116        reprcrash = getattr(excrepr, "reprcrash", None)
117        path = getattr(reprcrash, "path", None)
118        if path is None:
119            path = "cwd:%s" % py.path.local()
120        self.write_log_entry(path, "!", str(excrepr))
121