mirror of https://github.com/django/django.git
Fixed #32114 -- Fixed parallel test crash on non-picklable objects in subtests.
This commit is contained in:
parent
a269d8d1d8
commit
c09e8f5fd8
|
@ -1,6 +1,7 @@
|
|||
import difflib
|
||||
import json
|
||||
import logging
|
||||
import pickle
|
||||
import posixpath
|
||||
import sys
|
||||
import threading
|
||||
|
@ -92,6 +93,18 @@ def to_list(value):
|
|||
return value
|
||||
|
||||
|
||||
def is_pickable(obj):
|
||||
"""
|
||||
Returns true if the object can be dumped and loaded through the pickle
|
||||
module.
|
||||
"""
|
||||
try:
|
||||
pickle.loads(pickle.dumps(obj))
|
||||
except (AttributeError, TypeError):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def assert_and_parse_html(self, html, user_msg, msg):
|
||||
try:
|
||||
dom = parse_html(html)
|
||||
|
@ -303,6 +316,23 @@ class SimpleTestCase(unittest.TestCase):
|
|||
"""
|
||||
self._setup_and_call(result)
|
||||
|
||||
def __getstate__(self):
|
||||
"""
|
||||
Make SimpleTestCase picklable for parallel tests using subtests.
|
||||
"""
|
||||
state = super().__dict__
|
||||
# _outcome and _subtest cannot be tested on picklability, since they
|
||||
# contain the TestCase itself, leading to an infinite recursion.
|
||||
if state["_outcome"]:
|
||||
pickable_state = {"_outcome": None, "_subtest": None}
|
||||
for key, value in state.items():
|
||||
if key in pickable_state or not is_pickable(value):
|
||||
continue
|
||||
pickable_state[key] = value
|
||||
return pickable_state
|
||||
|
||||
return state
|
||||
|
||||
def debug(self):
|
||||
"""Perform the same as __call__(), without catching the exception."""
|
||||
debug_result = _DebugResult()
|
||||
|
|
|
@ -51,6 +51,13 @@ class SampleFailingSubtest(SimpleTestCase):
|
|||
with self.subTest(index=i):
|
||||
self.assertEqual(i, 1)
|
||||
|
||||
# This method name doesn't begin with "test" to prevent test discovery
|
||||
# from seeing it.
|
||||
def pickle_error_test(self):
|
||||
with self.subTest("TypeError: cannot pickle memoryview object"):
|
||||
self.x = memoryview(b"")
|
||||
self.fail("expected failure")
|
||||
|
||||
|
||||
class RemoteTestResultTest(SimpleTestCase):
|
||||
def _test_error_exc_info(self):
|
||||
|
@ -106,6 +113,16 @@ class RemoteTestResultTest(SimpleTestCase):
|
|||
with self.assertRaisesMessage(TypeError, msg):
|
||||
result._confirm_picklable(not_unpicklable_error)
|
||||
|
||||
def test_unpicklable_subtest(self):
|
||||
result = RemoteTestResult()
|
||||
subtest_test = SampleFailingSubtest(methodName="pickle_error_test")
|
||||
subtest_test.run(result=result)
|
||||
|
||||
events = result.events
|
||||
subtest_event = events[1]
|
||||
assertion_error = subtest_event[3]
|
||||
self.assertEqual(str(assertion_error[1]), "expected failure")
|
||||
|
||||
@unittest.skipUnless(tblib is not None, "requires tblib to be installed")
|
||||
def test_add_failing_subtests(self):
|
||||
"""
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
import pickle
|
||||
from functools import wraps
|
||||
|
||||
from django.db import IntegrityError, connections, transaction
|
||||
from django.test import TestCase, skipUnlessDBFeature
|
||||
from django.test.testcases import DatabaseOperationForbidden, TestData
|
||||
from django.test.testcases import DatabaseOperationForbidden, SimpleTestCase, TestData
|
||||
|
||||
from .models import Car, Person, PossessedCar
|
||||
|
||||
|
||||
class TestSimpleTestCase(SimpleTestCase):
|
||||
def test_is_picklable_with_non_picklable_properties(self):
|
||||
"""ParallelTestSuite requires that all TestCases are picklable."""
|
||||
self.non_picklable = lambda: 0
|
||||
self.assertEqual(self, pickle.loads(pickle.dumps(self)))
|
||||
|
||||
|
||||
class TestTestCase(TestCase):
|
||||
@skipUnlessDBFeature("can_defer_constraint_checks")
|
||||
@skipUnlessDBFeature("supports_foreign_keys")
|
||||
|
|
Loading…
Reference in New Issue