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:
parent
a02b5848ae
commit
42dcceba61
@ -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,
|
||||||
|
@ -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
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
|
67
tests/test_runner/test_parallel.py
Normal file
67
tests/test_runner/test_parallel.py
Normal 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',)")
|
Loading…
Reference in New Issue
Block a user