Acknoweldeged a limitation of the parallel test runner.
Notably it will fail to report a Model.DoesNotExist exceptions because the class itself isn't pickleable. (Django has specific code to make its instances pickleable.)
This commit is contained in:
parent
b799a50c8e
commit
073ea9e852
|
@ -4,6 +4,8 @@ import itertools
|
|||
import logging
|
||||
import multiprocessing
|
||||
import os
|
||||
import pickle
|
||||
import textwrap
|
||||
import unittest
|
||||
from importlib import import_module
|
||||
|
||||
|
@ -82,6 +84,55 @@ class RemoteTestResult(object):
|
|||
def test_index(self):
|
||||
return self.testsRun - 1
|
||||
|
||||
def check_pickleable(self, test, err):
|
||||
# Ensure that sys.exc_info() tuples are picklable. This displays a
|
||||
# clear multiprocessing.pool.RemoteTraceback generated in the child
|
||||
# process instead of a multiprocessing.pool.MaybeEncodingError, making
|
||||
# the root cause easier to figure out for users who aren't familiar
|
||||
# with the multiprocessing module. Since we're in a forked process,
|
||||
# our best chance to communicate with them is to print to stdout.
|
||||
try:
|
||||
pickle.dumps(err)
|
||||
except Exception as exc:
|
||||
original_exc_txt = repr(err[1])
|
||||
original_exc_txt = textwrap.fill(original_exc_txt, 75)
|
||||
original_exc_txt = textwrap.indent(original_exc_txt, ' ')
|
||||
pickle_exc_txt = repr(exc)
|
||||
pickle_exc_txt = textwrap.fill(pickle_exc_txt, 75)
|
||||
pickle_exc_txt = textwrap.indent(pickle_exc_txt, ' ')
|
||||
if tblib is None:
|
||||
print("""
|
||||
|
||||
{} failed:
|
||||
|
||||
{}
|
||||
|
||||
Unfortunately, tracebacks cannot be pickled, making it impossible for the
|
||||
parallel test runner to handle this exception cleanly.
|
||||
|
||||
In order to see the traceback, you should install tblib:
|
||||
|
||||
pip install tblib
|
||||
""".format(test, original_exc_txt))
|
||||
else:
|
||||
print("""
|
||||
|
||||
{} failed:
|
||||
|
||||
{}
|
||||
|
||||
Unfortunately, the exception it raised cannot be pickled, making it impossible
|
||||
for the parallel test runner to handle it cleanly.
|
||||
|
||||
Here's the error encountered while trying to pickle the exception:
|
||||
|
||||
{}
|
||||
|
||||
You should re-run this test without the --parallel option to reproduce the
|
||||
failure and get a correct traceback.
|
||||
""".format(test, original_exc_txt, pickle_exc_txt))
|
||||
raise
|
||||
|
||||
def stop_if_failfast(self):
|
||||
if self.failfast:
|
||||
self.stop()
|
||||
|
@ -103,10 +154,12 @@ class RemoteTestResult(object):
|
|||
self.events.append(('stopTest', self.test_index))
|
||||
|
||||
def addError(self, test, err):
|
||||
self.check_pickleable(test, err)
|
||||
self.events.append(('addError', self.test_index, err))
|
||||
self.stop_if_failfast()
|
||||
|
||||
def addFailure(self, test, err):
|
||||
self.check_pickleable(test, err)
|
||||
self.events.append(('addFailure', self.test_index, err))
|
||||
self.stop_if_failfast()
|
||||
|
||||
|
@ -120,6 +173,7 @@ class RemoteTestResult(object):
|
|||
self.events.append(('addSkip', self.test_index, reason))
|
||||
|
||||
def addExpectedFailure(self, test, err):
|
||||
self.check_pickleable(test, err)
|
||||
self.events.append(('addExpectedFailure', self.test_index, err))
|
||||
|
||||
def addUnexpectedSuccess(self, test):
|
||||
|
|
|
@ -1285,6 +1285,17 @@ correctly:
|
|||
|
||||
$ pip install tblib
|
||||
|
||||
.. warning::
|
||||
|
||||
When test parallelization is enabled and a test fails, Django may be
|
||||
unable to display the exception traceback. This can make debugging
|
||||
difficult. If you encounter this problem, run the affected test without
|
||||
parallelization to see the traceback of the failure.
|
||||
|
||||
This is a known limitation. It arises from the need to serialize objects
|
||||
in order to exchange them between processes. See
|
||||
:ref:`python:pickle-picklable` for details.
|
||||
|
||||
testserver <fixture fixture ...>
|
||||
--------------------------------
|
||||
|
||||
|
|
Loading…
Reference in New Issue