0
0
mirror of https://github.com/django/django.git synced 2024-11-29 14:46:18 +01:00

Fixed #26942 -- Added support for subtests during parallel testing.

This commit is contained in:
Chris Jerdonek 2016-08-11 03:47:12 -07:00 committed by Tim Graham
parent a02b5848ae
commit 42dcceba61
3 changed files with 105 additions and 4 deletions

View File

@ -77,6 +77,9 @@ class RemoteTestResult(object):
""" """
def __init__(self): def __init__(self):
if tblib is not None:
tblib.pickling_support.install()
self.events = [] self.events = []
self.failfast = False self.failfast = False
self.shouldStop = False self.shouldStop = False
@ -86,6 +89,22 @@ class RemoteTestResult(object):
def test_index(self): def test_index(self):
return self.testsRun - 1 return self.testsRun - 1
def _print_unpicklable_subtest(self, test, subtest, pickle_exc):
print("""
Subtest failed:
test: {}
subtest: {}
Unfortunately, the subtest that failed cannot be pickled, so the parallel
test runner cannot handle it cleanly. Here is the pickling error:
> {}
You should re-run this test with --parallel=1 to reproduce the failure
with a cleaner failure message.
""".format(test, subtest, pickle_exc))
def check_picklable(self, test, err): def check_picklable(self, test, err):
# Ensure that sys.exc_info() tuples are picklable. This displays a # Ensure that sys.exc_info() tuples are picklable. This displays a
# clear multiprocessing.pool.RemoteTraceback generated in the child # clear multiprocessing.pool.RemoteTraceback generated in the child
@ -133,6 +152,13 @@ failure and get a correct traceback.
""".format(test, original_exc_txt, pickle_exc_txt)) """.format(test, original_exc_txt, pickle_exc_txt))
raise raise
def check_subtest_picklable(self, test, subtest):
try:
pickle.dumps(subtest)
except Exception as exc:
self._print_unpicklable_subtest(test, subtest, exc)
raise
def stop_if_failfast(self): def stop_if_failfast(self):
if self.failfast: if self.failfast:
self.stop() self.stop()
@ -164,7 +190,15 @@ failure and get a correct traceback.
self.stop_if_failfast() self.stop_if_failfast()
def addSubTest(self, test, subtest, err): def addSubTest(self, test, subtest, err):
raise NotImplementedError("subtests aren't supported at this time") # Follow Python 3.5's implementation of unittest.TestResult.addSubTest()
# by not doing anything when a subtest is successful.
if err is not None:
# Call check_picklable() before check_subtest_picklable() since
# check_picklable() performs the tblib check.
self.check_picklable(test, err)
self.check_subtest_picklable(test, subtest)
self.events.append(('addSubTest', self.test_index, subtest, err))
self.stop_if_failfast()
def addSuccess(self, test): def addSuccess(self, test):
self.events.append(('addSuccess', self.test_index)) self.events.append(('addSuccess', self.test_index))
@ -307,9 +341,6 @@ class ParallelTestSuite(unittest.TestSuite):
Even with tblib, errors may still occur for dynamically created Even with tblib, errors may still occur for dynamically created
exception classes such Model.DoesNotExist which cannot be unpickled. exception classes such Model.DoesNotExist which cannot be unpickled.
""" """
if tblib is not None:
tblib.pickling_support.install()
counter = multiprocessing.Value(ctypes.c_int, 0) counter = multiprocessing.Value(ctypes.c_int, 0)
pool = multiprocessing.Pool( pool = multiprocessing.Pool(
processes=self.processes, processes=self.processes,

View File

@ -313,6 +313,9 @@ Tests
``django.test.runner``) and :func:`~django.test.utils.teardown_databases` ``django.test.runner``) and :func:`~django.test.utils.teardown_databases`
functions make it easier to build custom test runners. functions make it easier to build custom test runners.
* Added support for :meth:`python:unittest.TestCase.subTest`s when using the
:option:`test --parallel` option.
URLs URLs
~~~~ ~~~~

View File

@ -0,0 +1,67 @@
import unittest
from django.test import SimpleTestCase
from django.test.runner import RemoteTestResult
from django.utils import six
try:
import tblib
except ImportError:
tblib = None
class ParallelTestRunnerTest(SimpleTestCase):
"""
End-to-end tests of the parallel test runner.
These tests are only meaningful when running tests in parallel using
the --parallel option, though it doesn't hurt to run them not in
parallel.
"""
@unittest.skipUnless(six.PY3, 'subtests were added in Python 3.4')
def test_subtest(self):
"""
Check that passing subtests work.
"""
for i in range(2):
with self.subTest(index=i):
self.assertEqual(i, i)
class SampleFailingSubtest(SimpleTestCase):
# This method name doesn't begin with "test" to prevent test discovery
# from seeing it.
def dummy_test(self):
"""
A dummy test for testing subTest failures.
"""
for i in range(3):
with self.subTest(index=i):
self.assertEqual(i, 1)
class RemoteTestResultTest(SimpleTestCase):
@unittest.skipUnless(six.PY3 and tblib is not None, 'requires tblib to be installed')
def test_add_failing_subtests(self):
"""
Failing subtests are added correctly using addSubTest().
"""
# Manually run a test with failing subtests to prevent the failures
# from affecting the actual test run.
result = RemoteTestResult()
subtest_test = SampleFailingSubtest(methodName='dummy_test')
subtest_test.run(result=result)
events = result.events
self.assertEqual(len(events), 4)
event = events[1]
self.assertEqual(event[0], 'addSubTest')
self.assertEqual(str(event[2]), 'dummy_test (test_runner.test_parallel.SampleFailingSubtest) (index=0)')
self.assertEqual(repr(event[3][1]), "AssertionError('0 != 1',)")
event = events[2]
self.assertEqual(repr(event[3][1]), "AssertionError('2 != 1',)")