mirror of
https://github.com/django/django.git
synced 2024-11-21 10:59:04 +01:00
Fixed #35849 -- Made ParallelTestSuite report correct error location.
This commit is contained in:
parent
41da8a4f5a
commit
661dfdd598
1
AUTHORS
1
AUTHORS
@ -282,6 +282,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
David Sanders <dsanders11@ucsbalum.com>
|
David Sanders <dsanders11@ucsbalum.com>
|
||||||
David Schein
|
David Schein
|
||||||
David Tulig <david.tulig@gmail.com>
|
David Tulig <david.tulig@gmail.com>
|
||||||
|
David Winiecki <david.winiecki@gmail.com>
|
||||||
David Winterbottom <david.winterbottom@gmail.com>
|
David Winterbottom <david.winterbottom@gmail.com>
|
||||||
David Wobrock <david.wobrock@gmail.com>
|
David Wobrock <david.wobrock@gmail.com>
|
||||||
Davide Ceretti <dav.ceretti@gmail.com>
|
Davide Ceretti <dav.ceretti@gmail.com>
|
||||||
|
@ -12,6 +12,7 @@ import random
|
|||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
import unittest
|
import unittest
|
||||||
|
import unittest.suite
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
@ -292,7 +293,15 @@ failure and get a correct traceback.
|
|||||||
|
|
||||||
def addError(self, test, err):
|
def addError(self, test, err):
|
||||||
self.check_picklable(test, err)
|
self.check_picklable(test, err)
|
||||||
self.events.append(("addError", self.test_index, err))
|
|
||||||
|
event_occurred_before_first_test = self.test_index == -1
|
||||||
|
if event_occurred_before_first_test and isinstance(
|
||||||
|
test, unittest.suite._ErrorHolder
|
||||||
|
):
|
||||||
|
self.events.append(("addError", self.test_index, test.id(), err))
|
||||||
|
else:
|
||||||
|
self.events.append(("addError", self.test_index, err))
|
||||||
|
|
||||||
super().addError(test, err)
|
super().addError(test, err)
|
||||||
|
|
||||||
def addFailure(self, test, err):
|
def addFailure(self, test, err):
|
||||||
@ -558,8 +567,19 @@ class ParallelTestSuite(unittest.TestSuite):
|
|||||||
handler = getattr(result, event_name, None)
|
handler = getattr(result, event_name, None)
|
||||||
if handler is None:
|
if handler is None:
|
||||||
return
|
return
|
||||||
test = tests[event[1]]
|
test_index = event[1]
|
||||||
args = event[2:]
|
event_occurred_before_first_test = test_index == -1
|
||||||
|
if (
|
||||||
|
event_name == "addError"
|
||||||
|
and event_occurred_before_first_test
|
||||||
|
and len(event) >= 4
|
||||||
|
):
|
||||||
|
test_id = event[2]
|
||||||
|
test = unittest.suite._ErrorHolder(test_id)
|
||||||
|
args = event[3:]
|
||||||
|
else:
|
||||||
|
test = tests[test_index]
|
||||||
|
args = event[2:]
|
||||||
handler(test, *args)
|
handler(test, *args)
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import pickle
|
import pickle
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
from unittest.case import TestCase
|
||||||
|
from unittest.result import TestResult
|
||||||
|
from unittest.suite import TestSuite, _ErrorHolder
|
||||||
|
|
||||||
from django.test import SimpleTestCase
|
from django.test import SimpleTestCase
|
||||||
from django.test.runner import RemoteTestResult
|
from django.test.runner import ParallelTestSuite, RemoteTestResult
|
||||||
from django.utils.version import PY311, PY312
|
from django.utils.version import PY311, PY312
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -59,6 +62,18 @@ class SampleFailingSubtest(SimpleTestCase):
|
|||||||
self.fail("expected failure")
|
self.fail("expected failure")
|
||||||
|
|
||||||
|
|
||||||
|
class SampleErrorTest(SimpleTestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
raise ValueError("woops")
|
||||||
|
super().setUpClass()
|
||||||
|
|
||||||
|
# This method name doesn't begin with "test" to prevent test discovery
|
||||||
|
# from seeing it.
|
||||||
|
def dummy_test(self):
|
||||||
|
raise AssertionError("SampleErrorTest.dummy_test() was called")
|
||||||
|
|
||||||
|
|
||||||
class RemoteTestResultTest(SimpleTestCase):
|
class RemoteTestResultTest(SimpleTestCase):
|
||||||
def _test_error_exc_info(self):
|
def _test_error_exc_info(self):
|
||||||
try:
|
try:
|
||||||
@ -72,29 +87,70 @@ class RemoteTestResultTest(SimpleTestCase):
|
|||||||
|
|
||||||
def test_was_successful_one_success(self):
|
def test_was_successful_one_success(self):
|
||||||
result = RemoteTestResult()
|
result = RemoteTestResult()
|
||||||
result.addSuccess(None)
|
test = None
|
||||||
|
result.startTest(test)
|
||||||
|
try:
|
||||||
|
result.addSuccess(test)
|
||||||
|
finally:
|
||||||
|
result.stopTest(test)
|
||||||
self.assertIs(result.wasSuccessful(), True)
|
self.assertIs(result.wasSuccessful(), True)
|
||||||
|
|
||||||
def test_was_successful_one_expected_failure(self):
|
def test_was_successful_one_expected_failure(self):
|
||||||
result = RemoteTestResult()
|
result = RemoteTestResult()
|
||||||
result.addExpectedFailure(None, self._test_error_exc_info())
|
test = None
|
||||||
|
result.startTest(test)
|
||||||
|
try:
|
||||||
|
result.addExpectedFailure(test, self._test_error_exc_info())
|
||||||
|
finally:
|
||||||
|
result.stopTest(test)
|
||||||
self.assertIs(result.wasSuccessful(), True)
|
self.assertIs(result.wasSuccessful(), True)
|
||||||
|
|
||||||
def test_was_successful_one_skip(self):
|
def test_was_successful_one_skip(self):
|
||||||
result = RemoteTestResult()
|
result = RemoteTestResult()
|
||||||
result.addSkip(None, "Skipped")
|
test = None
|
||||||
|
result.startTest(test)
|
||||||
|
try:
|
||||||
|
result.addSkip(test, "Skipped")
|
||||||
|
finally:
|
||||||
|
result.stopTest(test)
|
||||||
self.assertIs(result.wasSuccessful(), True)
|
self.assertIs(result.wasSuccessful(), True)
|
||||||
|
|
||||||
@unittest.skipUnless(tblib is not None, "requires tblib to be installed")
|
@unittest.skipUnless(tblib is not None, "requires tblib to be installed")
|
||||||
def test_was_successful_one_error(self):
|
def test_was_successful_one_error(self):
|
||||||
result = RemoteTestResult()
|
result = RemoteTestResult()
|
||||||
result.addError(None, self._test_error_exc_info())
|
test = None
|
||||||
|
result.startTest(test)
|
||||||
|
try:
|
||||||
|
result.addError(test, self._test_error_exc_info())
|
||||||
|
finally:
|
||||||
|
result.stopTest(test)
|
||||||
self.assertIs(result.wasSuccessful(), False)
|
self.assertIs(result.wasSuccessful(), False)
|
||||||
|
|
||||||
@unittest.skipUnless(tblib is not None, "requires tblib to be installed")
|
@unittest.skipUnless(tblib is not None, "requires tblib to be installed")
|
||||||
def test_was_successful_one_failure(self):
|
def test_was_successful_one_failure(self):
|
||||||
result = RemoteTestResult()
|
result = RemoteTestResult()
|
||||||
result.addFailure(None, self._test_error_exc_info())
|
test = None
|
||||||
|
result.startTest(test)
|
||||||
|
try:
|
||||||
|
result.addFailure(test, self._test_error_exc_info())
|
||||||
|
finally:
|
||||||
|
result.stopTest(test)
|
||||||
|
self.assertIs(result.wasSuccessful(), False)
|
||||||
|
|
||||||
|
@unittest.skipUnless(tblib is not None, "requires tblib to be installed")
|
||||||
|
def test_add_error_before_first_test(self):
|
||||||
|
result = RemoteTestResult()
|
||||||
|
test_id = "test_foo (tests.test_foo.FooTest.test_foo)"
|
||||||
|
test = _ErrorHolder(test_id)
|
||||||
|
# Call addError() without a call to startTest().
|
||||||
|
result.addError(test, self._test_error_exc_info())
|
||||||
|
|
||||||
|
(event,) = result.events
|
||||||
|
self.assertEqual(event[0], "addError")
|
||||||
|
self.assertEqual(event[1], -1)
|
||||||
|
self.assertEqual(event[2], test_id)
|
||||||
|
(error_type, _, _) = event[3]
|
||||||
|
self.assertEqual(error_type, ValueError)
|
||||||
self.assertIs(result.wasSuccessful(), False)
|
self.assertIs(result.wasSuccessful(), False)
|
||||||
|
|
||||||
def test_picklable(self):
|
def test_picklable(self):
|
||||||
@ -161,3 +217,75 @@ class RemoteTestResultTest(SimpleTestCase):
|
|||||||
result = RemoteTestResult()
|
result = RemoteTestResult()
|
||||||
result.addDuration(None, 2.3)
|
result.addDuration(None, 2.3)
|
||||||
self.assertEqual(result.collectedDurations, [("None", 2.3)])
|
self.assertEqual(result.collectedDurations, [("None", 2.3)])
|
||||||
|
|
||||||
|
|
||||||
|
class ParallelTestSuiteTest(SimpleTestCase):
|
||||||
|
def test_handle_add_error_before_first_test(self):
|
||||||
|
dummy_subsuites = []
|
||||||
|
pts = ParallelTestSuite(dummy_subsuites, processes=2)
|
||||||
|
result = TestResult()
|
||||||
|
remote_result = RemoteTestResult()
|
||||||
|
test = SampleErrorTest(methodName="dummy_test")
|
||||||
|
suite = TestSuite([test])
|
||||||
|
suite.run(remote_result)
|
||||||
|
for event in remote_result.events:
|
||||||
|
pts.handle_event(result, tests=list(suite), event=event)
|
||||||
|
|
||||||
|
self.assertEqual(len(result.errors), 1)
|
||||||
|
actual_test, tb_and_details_str = result.errors[0]
|
||||||
|
self.assertIsInstance(actual_test, _ErrorHolder)
|
||||||
|
self.assertEqual(
|
||||||
|
actual_test.id(), "setUpClass (test_runner.test_parallel.SampleErrorTest)"
|
||||||
|
)
|
||||||
|
self.assertIn("Traceback (most recent call last):", tb_and_details_str)
|
||||||
|
self.assertIn("ValueError: woops", tb_and_details_str)
|
||||||
|
|
||||||
|
def test_handle_add_error_during_test(self):
|
||||||
|
dummy_subsuites = []
|
||||||
|
pts = ParallelTestSuite(dummy_subsuites, processes=2)
|
||||||
|
result = TestResult()
|
||||||
|
test = TestCase()
|
||||||
|
err = _test_error_exc_info()
|
||||||
|
event = ("addError", 0, err)
|
||||||
|
pts.handle_event(result, tests=[test], event=event)
|
||||||
|
|
||||||
|
self.assertEqual(len(result.errors), 1)
|
||||||
|
actual_test, tb_and_details_str = result.errors[0]
|
||||||
|
self.assertIsInstance(actual_test, TestCase)
|
||||||
|
self.assertEqual(actual_test.id(), "unittest.case.TestCase.runTest")
|
||||||
|
self.assertIn("Traceback (most recent call last):", tb_and_details_str)
|
||||||
|
self.assertIn("ValueError: woops", tb_and_details_str)
|
||||||
|
|
||||||
|
def test_handle_add_failure(self):
|
||||||
|
dummy_subsuites = []
|
||||||
|
pts = ParallelTestSuite(dummy_subsuites, processes=2)
|
||||||
|
result = TestResult()
|
||||||
|
test = TestCase()
|
||||||
|
err = _test_error_exc_info()
|
||||||
|
event = ("addFailure", 0, err)
|
||||||
|
pts.handle_event(result, tests=[test], event=event)
|
||||||
|
|
||||||
|
self.assertEqual(len(result.failures), 1)
|
||||||
|
actual_test, tb_and_details_str = result.failures[0]
|
||||||
|
self.assertIsInstance(actual_test, TestCase)
|
||||||
|
self.assertEqual(actual_test.id(), "unittest.case.TestCase.runTest")
|
||||||
|
self.assertIn("Traceback (most recent call last):", tb_and_details_str)
|
||||||
|
self.assertIn("ValueError: woops", tb_and_details_str)
|
||||||
|
|
||||||
|
def test_handle_add_success(self):
|
||||||
|
dummy_subsuites = []
|
||||||
|
pts = ParallelTestSuite(dummy_subsuites, processes=2)
|
||||||
|
result = TestResult()
|
||||||
|
test = TestCase()
|
||||||
|
event = ("addSuccess", 0)
|
||||||
|
pts.handle_event(result, tests=[test], event=event)
|
||||||
|
|
||||||
|
self.assertEqual(len(result.errors), 0)
|
||||||
|
self.assertEqual(len(result.failures), 0)
|
||||||
|
|
||||||
|
|
||||||
|
def _test_error_exc_info():
|
||||||
|
try:
|
||||||
|
raise ValueError("woops")
|
||||||
|
except ValueError:
|
||||||
|
return sys.exc_info()
|
||||||
|
Loading…
Reference in New Issue
Block a user