resultlog.py 3.6 KB

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