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