resultlog.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. # -*- coding: utf-8 -*-
  2. """ log machine-parseable test session result information in a plain
  3. text file.
  4. """
  5. from __future__ import absolute_import
  6. from __future__ import division
  7. from __future__ import print_function
  8. import os
  9. import py
  10. def 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. def pytest_configure(config):
  21. resultlog = config.option.resultlog
  22. # prevent opening resultlog on slave nodes (xdist)
  23. if resultlog and not hasattr(config, "slaveinput"):
  24. dirname = os.path.dirname(os.path.abspath(resultlog))
  25. if not os.path.isdir(dirname):
  26. os.makedirs(dirname)
  27. logfile = open(resultlog, "w", 1) # line buffered
  28. config._resultlog = ResultLog(config, logfile)
  29. config.pluginmanager.register(config._resultlog)
  30. from _pytest.deprecated import RESULT_LOG
  31. from _pytest.warnings import _issue_warning_captured
  32. _issue_warning_captured(RESULT_LOG, config.hook, stacklevel=2)
  33. def pytest_unconfigure(config):
  34. resultlog = getattr(config, "_resultlog", None)
  35. if resultlog:
  36. resultlog.logfile.close()
  37. del config._resultlog
  38. config.pluginmanager.unregister(resultlog)
  39. class ResultLog(object):
  40. def __init__(self, config, logfile):
  41. self.config = config
  42. self.logfile = logfile # preferably line buffered
  43. def write_log_entry(self, testpath, lettercode, longrepr):
  44. print("%s %s" % (lettercode, testpath), file=self.logfile)
  45. for line in longrepr.splitlines():
  46. print(" %s" % line, file=self.logfile)
  47. def log_outcome(self, report, lettercode, longrepr):
  48. testpath = getattr(report, "nodeid", None)
  49. if testpath is None:
  50. testpath = report.fspath
  51. self.write_log_entry(testpath, lettercode, longrepr)
  52. def pytest_runtest_logreport(self, report):
  53. if report.when != "call" and report.passed:
  54. return
  55. res = self.config.hook.pytest_report_teststatus(
  56. report=report, config=self.config
  57. )
  58. code = res[1]
  59. if code == "x":
  60. longrepr = str(report.longrepr)
  61. elif code == "X":
  62. longrepr = ""
  63. elif report.passed:
  64. longrepr = ""
  65. elif report.failed:
  66. longrepr = str(report.longrepr)
  67. elif report.skipped:
  68. longrepr = str(report.longrepr[2])
  69. self.log_outcome(report, code, longrepr)
  70. def pytest_collectreport(self, report):
  71. if not report.passed:
  72. if report.failed:
  73. code = "F"
  74. longrepr = str(report.longrepr)
  75. else:
  76. assert report.skipped
  77. code = "S"
  78. longrepr = "%s:%d: %s" % report.longrepr
  79. self.log_outcome(report, code, longrepr)
  80. def pytest_internalerror(self, excrepr):
  81. reprcrash = getattr(excrepr, "reprcrash", None)
  82. path = getattr(reprcrash, "path", None)
  83. if path is None:
  84. path = "cwd:%s" % py.path.local()
  85. self.write_log_entry(path, "!", str(excrepr))