123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- import pickle
- import sys
- 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.runner import ParallelTestSuite, RemoteTestResult
- from django.utils.version import PY311, PY312
- try:
- import tblib.pickling_support
- except ImportError:
- tblib = None
- class ExceptionThatFailsUnpickling(Exception):
- """
- After pickling, this class fails unpickling with an error about incorrect
- arguments passed to __init__().
- """
- def __init__(self, arg):
- super().__init__()
- 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.
- """
- def test_subtest(self):
- """
- 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)
- # 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 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):
- def _test_error_exc_info(self):
- try:
- raise ValueError("woops")
- except ValueError:
- return sys.exc_info()
- def test_was_successful_no_events(self):
- result = RemoteTestResult()
- self.assertIs(result.wasSuccessful(), True)
- def test_was_successful_one_success(self):
- result = RemoteTestResult()
- test = None
- result.startTest(test)
- try:
- result.addSuccess(test)
- finally:
- result.stopTest(test)
- self.assertIs(result.wasSuccessful(), True)
- def test_was_successful_one_expected_failure(self):
- result = RemoteTestResult()
- test = None
- result.startTest(test)
- try:
- result.addExpectedFailure(test, self._test_error_exc_info())
- finally:
- result.stopTest(test)
- self.assertIs(result.wasSuccessful(), True)
- def test_was_successful_one_skip(self):
- result = RemoteTestResult()
- test = None
- result.startTest(test)
- try:
- result.addSkip(test, "Skipped")
- finally:
- result.stopTest(test)
- self.assertIs(result.wasSuccessful(), True)
- @unittest.skipUnless(tblib is not None, "requires tblib to be installed")
- def test_was_successful_one_error(self):
- result = RemoteTestResult()
- test = None
- result.startTest(test)
- try:
- result.addError(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_was_successful_one_failure(self):
- result = RemoteTestResult()
- 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)
- def test_picklable(self):
- result = RemoteTestResult()
- loaded_result = pickle.loads(pickle.dumps(result))
- self.assertEqual(result.events, loaded_result.events)
- def test_pickle_errors_detection(self):
- picklable_error = RuntimeError("This is fine")
- not_unpicklable_error = ExceptionThatFailsUnpickling("arg")
- result = RemoteTestResult()
- result._confirm_picklable(picklable_error)
- msg = "__init__() missing 1 required positional argument"
- with self.assertRaisesMessage(TypeError, msg):
- result._confirm_picklable(not_unpicklable_error)
- @unittest.skipUnless(tblib is not None, "requires tblib to be installed")
- 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):
- """
- 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
- # addDurations added in Python 3.12.
- if PY312:
- self.assertEqual(len(events), 5)
- else:
- self.assertEqual(len(events), 4)
- self.assertIs(result.wasSuccessful(), False)
- event = events[1]
- self.assertEqual(event[0], "addSubTest")
- self.assertEqual(
- str(event[2]),
- "dummy_test (test_runner.test_parallel.SampleFailingSubtest%s) (index=0)"
- # Python 3.11 uses fully qualified test name in the output.
- % (".dummy_test" if PY311 else ""),
- )
- self.assertEqual(repr(event[3][1]), "AssertionError('0 != 1')")
- event = events[2]
- self.assertEqual(repr(event[3][1]), "AssertionError('2 != 1')")
- @unittest.skipUnless(PY312, "unittest --durations option requires Python 3.12")
- def test_add_duration(self):
- result = RemoteTestResult()
- result.addDuration(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()
|