Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" log machine-parseable test session result information in a plain 

2text file. 

3""" 

4import os 

5 

6import py 

7 

8from _pytest._code.code import ExceptionRepr 

9from _pytest.config import Config 

10from _pytest.config.argparsing import Parser 

11from _pytest.reports import CollectReport 

12from _pytest.reports import TestReport 

13from _pytest.store import StoreKey 

14 

15 

16resultlog_key = StoreKey["ResultLog"]() 

17 

18 

19def pytest_addoption(parser: Parser) -> None: 

20 group = parser.getgroup("terminal reporting", "resultlog plugin options") 

21 group.addoption( 

22 "--resultlog", 

23 "--result-log", 

24 action="store", 

25 metavar="path", 

26 default=None, 

27 help="DEPRECATED path for machine-readable result log.", 

28 ) 

29 

30 

31def pytest_configure(config: Config) -> None: 

32 resultlog = config.option.resultlog 

33 # prevent opening resultlog on worker nodes (xdist) 

34 if resultlog and not hasattr(config, "workerinput"): 

35 dirname = os.path.dirname(os.path.abspath(resultlog)) 

36 if not os.path.isdir(dirname): 

37 os.makedirs(dirname) 

38 logfile = open(resultlog, "w", 1) # line buffered 

39 config._store[resultlog_key] = ResultLog(config, logfile) 

40 config.pluginmanager.register(config._store[resultlog_key]) 

41 

42 from _pytest.deprecated import RESULT_LOG 

43 from _pytest.warnings import _issue_warning_captured 

44 

45 _issue_warning_captured(RESULT_LOG, config.hook, stacklevel=2) 

46 

47 

48def pytest_unconfigure(config: Config) -> None: 

49 resultlog = config._store.get(resultlog_key, None) 

50 if resultlog: 

51 resultlog.logfile.close() 

52 del config._store[resultlog_key] 

53 config.pluginmanager.unregister(resultlog) 

54 

55 

56class ResultLog: 

57 def __init__(self, config, logfile): 

58 self.config = config 

59 self.logfile = logfile # preferably line buffered 

60 

61 def write_log_entry(self, testpath, lettercode, longrepr): 

62 print("{} {}".format(lettercode, testpath), file=self.logfile) 

63 for line in longrepr.splitlines(): 

64 print(" %s" % line, file=self.logfile) 

65 

66 def log_outcome(self, report, lettercode, longrepr): 

67 testpath = getattr(report, "nodeid", None) 

68 if testpath is None: 

69 testpath = report.fspath 

70 self.write_log_entry(testpath, lettercode, longrepr) 

71 

72 def pytest_runtest_logreport(self, report: TestReport) -> None: 

73 if report.when != "call" and report.passed: 

74 return 

75 res = self.config.hook.pytest_report_teststatus( 

76 report=report, config=self.config 

77 ) 

78 code = res[1] 

79 if code == "x": 

80 longrepr = str(report.longrepr) 

81 elif code == "X": 

82 longrepr = "" 

83 elif report.passed: 

84 longrepr = "" 

85 elif report.skipped: 

86 assert report.longrepr is not None 

87 longrepr = str(report.longrepr[2]) 

88 else: 

89 longrepr = str(report.longrepr) 

90 self.log_outcome(report, code, longrepr) 

91 

92 def pytest_collectreport(self, report: CollectReport) -> None: 

93 if not report.passed: 

94 if report.failed: 

95 code = "F" 

96 longrepr = str(report.longrepr) 

97 else: 

98 assert report.skipped 

99 code = "S" 

100 longrepr = "%s:%d: %s" % report.longrepr # type: ignore 

101 self.log_outcome(report, code, longrepr) 

102 

103 def pytest_internalerror(self, excrepr: ExceptionRepr) -> None: 

104 if excrepr.reprcrash is not None: 

105 path = excrepr.reprcrash.path 

106 else: 

107 path = "cwd:%s" % py.path.local() 

108 self.write_log_entry(path, "!", str(excrepr))