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