| """ log machine-parseable test session result information in a plain |
| text file. |
| """ |
| |
| import py |
| import os |
| |
| def pytest_addoption(parser): |
| group = parser.getgroup("terminal reporting", "resultlog plugin options") |
| group.addoption('--resultlog', '--result-log', action="store", |
| metavar="path", default=None, |
| help="path for machine-readable result log.") |
| |
| def pytest_configure(config): |
| resultlog = config.option.resultlog |
| # prevent opening resultlog on slave nodes (xdist) |
| if resultlog and not hasattr(config, 'slaveinput'): |
| dirname = os.path.dirname(os.path.abspath(resultlog)) |
| if not os.path.isdir(dirname): |
| os.makedirs(dirname) |
| logfile = open(resultlog, 'w', 1) # line buffered |
| config._resultlog = ResultLog(config, logfile) |
| config.pluginmanager.register(config._resultlog) |
| |
| def pytest_unconfigure(config): |
| resultlog = getattr(config, '_resultlog', None) |
| if resultlog: |
| resultlog.logfile.close() |
| del config._resultlog |
| config.pluginmanager.unregister(resultlog) |
| |
| def generic_path(item): |
| chain = item.listchain() |
| gpath = [chain[0].name] |
| fspath = chain[0].fspath |
| fspart = False |
| for node in chain[1:]: |
| newfspath = node.fspath |
| if newfspath == fspath: |
| if fspart: |
| gpath.append(':') |
| fspart = False |
| else: |
| gpath.append('.') |
| else: |
| gpath.append('/') |
| fspart = True |
| name = node.name |
| if name[0] in '([': |
| gpath.pop() |
| gpath.append(name) |
| fspath = newfspath |
| return ''.join(gpath) |
| |
| class ResultLog(object): |
| def __init__(self, config, logfile): |
| self.config = config |
| self.logfile = logfile # preferably line buffered |
| |
| def write_log_entry(self, testpath, lettercode, longrepr): |
| py.builtin.print_("%s %s" % (lettercode, testpath), file=self.logfile) |
| for line in longrepr.splitlines(): |
| py.builtin.print_(" %s" % line, file=self.logfile) |
| |
| def log_outcome(self, report, lettercode, longrepr): |
| testpath = getattr(report, 'nodeid', None) |
| if testpath is None: |
| testpath = report.fspath |
| self.write_log_entry(testpath, lettercode, longrepr) |
| |
| def pytest_runtest_logreport(self, report): |
| if report.when != "call" and report.passed: |
| return |
| res = self.config.hook.pytest_report_teststatus(report=report) |
| code = res[1] |
| if code == 'x': |
| longrepr = str(report.longrepr) |
| elif code == 'X': |
| longrepr = '' |
| elif report.passed: |
| longrepr = "" |
| elif report.failed: |
| longrepr = str(report.longrepr) |
| elif report.skipped: |
| longrepr = str(report.longrepr[2]) |
| self.log_outcome(report, code, longrepr) |
| |
| def pytest_collectreport(self, report): |
| if not report.passed: |
| if report.failed: |
| code = "F" |
| longrepr = str(report.longrepr) |
| else: |
| assert report.skipped |
| code = "S" |
| longrepr = "%s:%d: %s" % report.longrepr |
| self.log_outcome(report, code, longrepr) |
| |
| def pytest_internalerror(self, excrepr): |
| reprcrash = getattr(excrepr, 'reprcrash', None) |
| path = getattr(reprcrash, "path", None) |
| if path is None: |
| path = "cwd:%s" % py.path.local() |
| self.write_log_entry(path, '!', str(excrepr)) |