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