test_parallel.py 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. import pickle
  2. import sys
  3. import unittest
  4. from unittest.case import TestCase
  5. from unittest.result import TestResult
  6. from unittest.suite import TestSuite, _ErrorHolder
  7. from django.test import SimpleTestCase
  8. from django.test.runner import ParallelTestSuite, RemoteTestResult
  9. from django.utils.version import PY311, PY312
  10. try:
  11. import tblib.pickling_support
  12. except ImportError:
  13. tblib = None
  14. class ExceptionThatFailsUnpickling(Exception):
  15. """
  16. After pickling, this class fails unpickling with an error about incorrect
  17. arguments passed to __init__().
  18. """
  19. def __init__(self, arg):
  20. super().__init__()
  21. class ParallelTestRunnerTest(SimpleTestCase):
  22. """
  23. End-to-end tests of the parallel test runner.
  24. These tests are only meaningful when running tests in parallel using
  25. the --parallel option, though it doesn't hurt to run them not in
  26. parallel.
  27. """
  28. def test_subtest(self):
  29. """
  30. Passing subtests work.
  31. """
  32. for i in range(2):
  33. with self.subTest(index=i):
  34. self.assertEqual(i, i)
  35. class SampleFailingSubtest(SimpleTestCase):
  36. # This method name doesn't begin with "test" to prevent test discovery
  37. # from seeing it.
  38. def dummy_test(self):
  39. """
  40. A dummy test for testing subTest failures.
  41. """
  42. for i in range(3):
  43. with self.subTest(index=i):
  44. self.assertEqual(i, 1)
  45. # This method name doesn't begin with "test" to prevent test discovery
  46. # from seeing it.
  47. def pickle_error_test(self):
  48. with self.subTest("TypeError: cannot pickle memoryview object"):
  49. self.x = memoryview(b"")
  50. self.fail("expected failure")
  51. class SampleErrorTest(SimpleTestCase):
  52. @classmethod
  53. def setUpClass(cls):
  54. raise ValueError("woops")
  55. super().setUpClass()
  56. # This method name doesn't begin with "test" to prevent test discovery
  57. # from seeing it.
  58. def dummy_test(self):
  59. raise AssertionError("SampleErrorTest.dummy_test() was called")
  60. class RemoteTestResultTest(SimpleTestCase):
  61. def _test_error_exc_info(self):
  62. try:
  63. raise ValueError("woops")
  64. except ValueError:
  65. return sys.exc_info()
  66. def test_was_successful_no_events(self):
  67. result = RemoteTestResult()
  68. self.assertIs(result.wasSuccessful(), True)
  69. def test_was_successful_one_success(self):
  70. result = RemoteTestResult()
  71. test = None
  72. result.startTest(test)
  73. try:
  74. result.addSuccess(test)
  75. finally:
  76. result.stopTest(test)
  77. self.assertIs(result.wasSuccessful(), True)
  78. def test_was_successful_one_expected_failure(self):
  79. result = RemoteTestResult()
  80. test = None
  81. result.startTest(test)
  82. try:
  83. result.addExpectedFailure(test, self._test_error_exc_info())
  84. finally:
  85. result.stopTest(test)
  86. self.assertIs(result.wasSuccessful(), True)
  87. def test_was_successful_one_skip(self):
  88. result = RemoteTestResult()
  89. test = None
  90. result.startTest(test)
  91. try:
  92. result.addSkip(test, "Skipped")
  93. finally:
  94. result.stopTest(test)
  95. self.assertIs(result.wasSuccessful(), True)
  96. @unittest.skipUnless(tblib is not None, "requires tblib to be installed")
  97. def test_was_successful_one_error(self):
  98. result = RemoteTestResult()
  99. test = None
  100. result.startTest(test)
  101. try:
  102. result.addError(test, self._test_error_exc_info())
  103. finally:
  104. result.stopTest(test)
  105. self.assertIs(result.wasSuccessful(), False)
  106. @unittest.skipUnless(tblib is not None, "requires tblib to be installed")
  107. def test_was_successful_one_failure(self):
  108. result = RemoteTestResult()
  109. test = None
  110. result.startTest(test)
  111. try:
  112. result.addFailure(test, self._test_error_exc_info())
  113. finally:
  114. result.stopTest(test)
  115. self.assertIs(result.wasSuccessful(), False)
  116. @unittest.skipUnless(tblib is not None, "requires tblib to be installed")
  117. def test_add_error_before_first_test(self):
  118. result = RemoteTestResult()
  119. test_id = "test_foo (tests.test_foo.FooTest.test_foo)"
  120. test = _ErrorHolder(test_id)
  121. # Call addError() without a call to startTest().
  122. result.addError(test, self._test_error_exc_info())
  123. (event,) = result.events
  124. self.assertEqual(event[0], "addError")
  125. self.assertEqual(event[1], -1)
  126. self.assertEqual(event[2], test_id)
  127. (error_type, _, _) = event[3]
  128. self.assertEqual(error_type, ValueError)
  129. self.assertIs(result.wasSuccessful(), False)
  130. def test_picklable(self):
  131. result = RemoteTestResult()
  132. loaded_result = pickle.loads(pickle.dumps(result))
  133. self.assertEqual(result.events, loaded_result.events)
  134. def test_pickle_errors_detection(self):
  135. picklable_error = RuntimeError("This is fine")
  136. not_unpicklable_error = ExceptionThatFailsUnpickling("arg")
  137. result = RemoteTestResult()
  138. result._confirm_picklable(picklable_error)
  139. msg = "__init__() missing 1 required positional argument"
  140. with self.assertRaisesMessage(TypeError, msg):
  141. result._confirm_picklable(not_unpicklable_error)
  142. @unittest.skipUnless(tblib is not None, "requires tblib to be installed")
  143. def test_unpicklable_subtest(self):
  144. result = RemoteTestResult()
  145. subtest_test = SampleFailingSubtest(methodName="pickle_error_test")
  146. subtest_test.run(result=result)
  147. events = result.events
  148. subtest_event = events[1]
  149. assertion_error = subtest_event[3]
  150. self.assertEqual(str(assertion_error[1]), "expected failure")
  151. @unittest.skipUnless(tblib is not None, "requires tblib to be installed")
  152. def test_add_failing_subtests(self):
  153. """
  154. Failing subtests are added correctly using addSubTest().
  155. """
  156. # Manually run a test with failing subtests to prevent the failures
  157. # from affecting the actual test run.
  158. result = RemoteTestResult()
  159. subtest_test = SampleFailingSubtest(methodName="dummy_test")
  160. subtest_test.run(result=result)
  161. events = result.events
  162. # addDurations added in Python 3.12.
  163. if PY312:
  164. self.assertEqual(len(events), 5)
  165. else:
  166. self.assertEqual(len(events), 4)
  167. self.assertIs(result.wasSuccessful(), False)
  168. event = events[1]
  169. self.assertEqual(event[0], "addSubTest")
  170. self.assertEqual(
  171. str(event[2]),
  172. "dummy_test (test_runner.test_parallel.SampleFailingSubtest%s) (index=0)"
  173. # Python 3.11 uses fully qualified test name in the output.
  174. % (".dummy_test" if PY311 else ""),
  175. )
  176. self.assertEqual(repr(event[3][1]), "AssertionError('0 != 1')")
  177. event = events[2]
  178. self.assertEqual(repr(event[3][1]), "AssertionError('2 != 1')")
  179. @unittest.skipUnless(PY312, "unittest --durations option requires Python 3.12")
  180. def test_add_duration(self):
  181. result = RemoteTestResult()
  182. result.addDuration(None, 2.3)
  183. self.assertEqual(result.collectedDurations, [("None", 2.3)])
  184. class ParallelTestSuiteTest(SimpleTestCase):
  185. def test_handle_add_error_before_first_test(self):
  186. dummy_subsuites = []
  187. pts = ParallelTestSuite(dummy_subsuites, processes=2)
  188. result = TestResult()
  189. remote_result = RemoteTestResult()
  190. test = SampleErrorTest(methodName="dummy_test")
  191. suite = TestSuite([test])
  192. suite.run(remote_result)
  193. for event in remote_result.events:
  194. pts.handle_event(result, tests=list(suite), event=event)
  195. self.assertEqual(len(result.errors), 1)
  196. actual_test, tb_and_details_str = result.errors[0]
  197. self.assertIsInstance(actual_test, _ErrorHolder)
  198. self.assertEqual(
  199. actual_test.id(), "setUpClass (test_runner.test_parallel.SampleErrorTest)"
  200. )
  201. self.assertIn("Traceback (most recent call last):", tb_and_details_str)
  202. self.assertIn("ValueError: woops", tb_and_details_str)
  203. def test_handle_add_error_during_test(self):
  204. dummy_subsuites = []
  205. pts = ParallelTestSuite(dummy_subsuites, processes=2)
  206. result = TestResult()
  207. test = TestCase()
  208. err = _test_error_exc_info()
  209. event = ("addError", 0, err)
  210. pts.handle_event(result, tests=[test], event=event)
  211. self.assertEqual(len(result.errors), 1)
  212. actual_test, tb_and_details_str = result.errors[0]
  213. self.assertIsInstance(actual_test, TestCase)
  214. self.assertEqual(actual_test.id(), "unittest.case.TestCase.runTest")
  215. self.assertIn("Traceback (most recent call last):", tb_and_details_str)
  216. self.assertIn("ValueError: woops", tb_and_details_str)
  217. def test_handle_add_failure(self):
  218. dummy_subsuites = []
  219. pts = ParallelTestSuite(dummy_subsuites, processes=2)
  220. result = TestResult()
  221. test = TestCase()
  222. err = _test_error_exc_info()
  223. event = ("addFailure", 0, err)
  224. pts.handle_event(result, tests=[test], event=event)
  225. self.assertEqual(len(result.failures), 1)
  226. actual_test, tb_and_details_str = result.failures[0]
  227. self.assertIsInstance(actual_test, TestCase)
  228. self.assertEqual(actual_test.id(), "unittest.case.TestCase.runTest")
  229. self.assertIn("Traceback (most recent call last):", tb_and_details_str)
  230. self.assertIn("ValueError: woops", tb_and_details_str)
  231. def test_handle_add_success(self):
  232. dummy_subsuites = []
  233. pts = ParallelTestSuite(dummy_subsuites, processes=2)
  234. result = TestResult()
  235. test = TestCase()
  236. event = ("addSuccess", 0)
  237. pts.handle_event(result, tests=[test], event=event)
  238. self.assertEqual(len(result.errors), 0)
  239. self.assertEqual(len(result.failures), 0)
  240. def _test_error_exc_info():
  241. try:
  242. raise ValueError("woops")
  243. except ValueError:
  244. return sys.exc_info()