resultlog.py 3.5 KB

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