__init__.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import py
  2. # we know this bit is bad, but we cant help it with the current pytest setup
  3. from _pytest import runner
  4. import pytest
  5. # copied from xdist remote
  6. def serialize_report(rep):
  7. import py
  8. d = rep.__dict__.copy()
  9. if hasattr(rep.longrepr, 'toterminal'):
  10. d['longrepr'] = str(rep.longrepr)
  11. else:
  12. d['longrepr'] = rep.longrepr
  13. for name in d:
  14. if isinstance(d[name], py.path.local):
  15. d[name] = str(d[name])
  16. elif name == "result":
  17. d[name] = None # for now
  18. return d
  19. def pytest_addoption(parser):
  20. group = parser.getgroup("forked", "forked subprocess test execution")
  21. group.addoption(
  22. '--forked',
  23. action="store_true", dest="forked", default=False,
  24. help="box each test run in a separate process (unix)")
  25. @pytest.mark.tryfirst
  26. def pytest_runtest_protocol(item):
  27. if item.config.getvalue("forked"):
  28. reports = forked_run_report(item)
  29. for rep in reports:
  30. item.ihook.pytest_runtest_logreport(report=rep)
  31. return True
  32. def forked_run_report(item):
  33. # for now, we run setup/teardown in the subprocess
  34. # XXX optionally allow sharing of setup/teardown
  35. from _pytest.runner import runtestprotocol
  36. EXITSTATUS_TESTEXIT = 4
  37. import marshal
  38. def runforked():
  39. try:
  40. reports = runtestprotocol(item, log=False)
  41. except KeyboardInterrupt:
  42. py.std.os._exit(EXITSTATUS_TESTEXIT)
  43. return marshal.dumps([serialize_report(x) for x in reports])
  44. ff = py.process.ForkedFunc(runforked)
  45. result = ff.waitfinish()
  46. if result.retval is not None:
  47. report_dumps = marshal.loads(result.retval)
  48. return [runner.TestReport(**x) for x in report_dumps]
  49. else:
  50. if result.exitstatus == EXITSTATUS_TESTEXIT:
  51. py.test.exit("forked test item %s raised Exit" % (item,))
  52. return [report_process_crash(item, result)]
  53. def report_process_crash(item, result):
  54. path, lineno = item._getfslineno()
  55. info = ("%s:%s: running the test CRASHED with signal %d" %
  56. (path, lineno, result.signal))
  57. from _pytest import runner
  58. call = runner.CallInfo(lambda: 0/0, "???")
  59. call.excinfo = info
  60. rep = runner.pytest_runtest_makereport(item, call)
  61. if result.out:
  62. rep.sections.append(("captured stdout", result.out))
  63. if result.err:
  64. rep.sections.append(("captured stderr", result.err))
  65. return rep