diff --git a/changelog/133.bugfix b/changelog/133.bugfix new file mode 100644 index 0000000..2e52656 --- /dev/null +++ b/changelog/133.bugfix @@ -0,0 +1 @@ +Fix serialization and deserialization dropping longrepr details. \ No newline at end of file diff --git a/testing/test_remote.py b/testing/test_remote.py index 727aeca..1f00e16 100644 --- a/testing/test_remote.py +++ b/testing/test_remote.py @@ -79,6 +79,20 @@ def test_remoteinitconfig(testdir): class TestReportSerialization: + def test_xdist_report_longrepr_reprcrash_130(self, testdir): + reprec = testdir.inline_runsource(""" + import py + def test_fail(): assert False, 'Expected Message' + """) + reports = reprec.getreports("pytest_runtest_logreport") + assert len(reports) == 3 + initial_failure_report = reports[1] + d = serialize_report(initial_failure_report) + check_marshallable(d) + processed_report = unserialize_report("testreport", d) + assert 'Expected Message' \ + in processed_report.longrepr.reprcrash.message + def test_itemreport_outcomes(self, testdir): reprec = testdir.inline_runsource(""" import py @@ -108,7 +122,7 @@ class TestReportSerialization: assert newrep.when == rep.when assert newrep.keywords == rep.keywords if rep.failed: - assert newrep.longrepr == str(rep.longrepr) + assert newrep.longreprtext == rep.longreprtext def test_collectreport_passed(self, testdir): reprec = testdir.inline_runsource("def test_func(): pass") diff --git a/xdist/remote.py b/xdist/remote.py index bdec4e8..135744d 100644 --- a/xdist/remote.py +++ b/xdist/remote.py @@ -98,10 +98,33 @@ class SlaveInteractor: def serialize_report(rep): + def disassembled_report(rep): + reprtraceback = rep.longrepr.reprtraceback.__dict__ + reprcrash = rep.longrepr.reprcrash.__dict__ + + new_entries = [] + for entry in reprtraceback['reprentries']: + new_entry = entry.__dict__ + for key, value in new_entry.items(): + if hasattr(value, '__dict__'): + new_entry[key] = value.__dict__ + new_entries.append(new_entry) + + reprtraceback['reprentries'] = new_entries + + return { + 'reprcrash': reprcrash, + 'reprtraceback': reprtraceback + } + import py d = rep.__dict__.copy() if hasattr(rep.longrepr, 'toterminal'): - d['longrepr'] = str(rep.longrepr) + if hasattr(rep.longrepr, 'reprtraceback') \ + and hasattr(rep.longrepr, 'reprcrash'): + d['longrepr'] = disassembled_report(rep) + else: + d['longrepr'] = str(rep.longrepr) else: d['longrepr'] = rep.longrepr for name in d: diff --git a/xdist/slavemanage.py b/xdist/slavemanage.py index bb44828..36fb4fd 100644 --- a/xdist/slavemanage.py +++ b/xdist/slavemanage.py @@ -327,7 +327,45 @@ class SlaveController(object): def unserialize_report(name, reportdict): + def assembled_report(reportdict): + from _pytest._code.code import ( + ReprExceptionInfo, + ReprFileLocation, + ReprEntry, + ReprFuncArgs, + ReprTraceback + ) + if reportdict['longrepr']: + if 'reprcrash' and 'reprtraceback' in reportdict['longrepr']: + + reprtraceback = reportdict['longrepr']['reprtraceback'] + reprcrash = reportdict['longrepr']['reprcrash'] + + unserialized_entries = [] + for entry in reprtraceback['reprentries']: + reprfuncargs, reprfileloc = None, None + if entry['reprfuncargs']: + reprfuncargs = ReprFuncArgs(**entry['reprfuncargs']) + if entry['reprfileloc']: + reprfileloc = ReprFileLocation(**entry['reprfileloc']) + + reprentry = ReprEntry( + lines=entry['lines'], + reprfuncargs=reprfuncargs, + reprlocals=entry['reprlocals'], + filelocrepr=reprfileloc, + style=entry['style'] + ) + unserialized_entries.append(reprentry) + reprtraceback['reprentries'] = unserialized_entries + + reportdict['longrepr'] = ReprExceptionInfo( + reprtraceback=ReprTraceback(**reprtraceback), + reprcrash=ReprFileLocation(**reprcrash), + ) + return reportdict + if name == "testreport": - return runner.TestReport(**reportdict) + return runner.TestReport(**assembled_report(reportdict)) elif name == "collectreport": - return runner.CollectReport(**reportdict) + return runner.CollectReport(**assembled_report(reportdict))