Преглед на файлове

Fixed #31509 -- Made DiscoverRunner enable faulthandler by default.

ovkulkarni преди 4 години
родител
ревизия
b7a438c7e2
променени са 5 файла, в които са добавени 61 реда и са изтрити 3 реда
  1. 13 1
      django/test/runner.py
  2. 8 0
      docs/ref/django-admin.txt
  3. 4 0
      docs/releases/3.2.txt
  4. 8 1
      docs/topics/testing/advanced.txt
  5. 28 1
      tests/test_runner/test_discover_runner.py

+ 13 - 1
django/test/runner.py

@@ -1,9 +1,12 @@
 import ctypes
+import faulthandler
+import io
 import itertools
 import logging
 import multiprocessing
 import os
 import pickle
+import sys
 import textwrap
 import unittest
 from importlib import import_module
@@ -434,7 +437,7 @@ class DiscoverRunner:
                  interactive=True, failfast=False, keepdb=False,
                  reverse=False, debug_mode=False, debug_sql=False, parallel=0,
                  tags=None, exclude_tags=None, test_name_patterns=None,
-                 pdb=False, buffer=False, **kwargs):
+                 pdb=False, buffer=False, enable_faulthandler=True, **kwargs):
 
         self.pattern = pattern
         self.top_level = top_level
@@ -448,6 +451,11 @@ class DiscoverRunner:
         self.parallel = parallel
         self.tags = set(tags or [])
         self.exclude_tags = set(exclude_tags or [])
+        if not faulthandler.is_enabled() and enable_faulthandler:
+            try:
+                faulthandler.enable(file=sys.stderr.fileno())
+            except (AttributeError, io.UnsupportedOperation):
+                faulthandler.enable(file=sys.__stderr__.fileno())
         self.pdb = pdb
         if self.pdb and self.parallel > 1:
             raise ValueError('You cannot use --pdb with parallel tests; pass --parallel=1 to use it.')
@@ -513,6 +521,10 @@ class DiscoverRunner:
             '-b', '--buffer', action='store_true',
             help='Discard output from passing tests.',
         )
+        parser.add_argument(
+            '--no-faulthandler', action='store_false', dest='enable_faulthandler',
+            help='Disables the Python faulthandler module during tests.',
+        )
         if PY37:
             parser.add_argument(
                 '-k', action='append', dest='test_name_patterns',

+ 8 - 0
docs/ref/django-admin.txt

@@ -1515,6 +1515,14 @@ installed, ``ipdb`` is used instead.
 Discards output (``stdout`` and ``stderr``) for passing tests, in the same way
 as :option:`unittest's --buffer option<unittest.-b>`.
 
+.. django-admin-option:: --no-faulthandler
+
+.. versionadded:: 3.2
+
+Django automatically calls :func:`faulthandler.enable()` when starting the
+tests, which allows it to print a traceback if the interpreter crashes. Pass
+``--no-faulthandler`` to disable this behavior.
+
 ``testserver``
 --------------
 

+ 4 - 0
docs/releases/3.2.txt

@@ -266,6 +266,10 @@ Tests
   creating deep copies with :py:func:`copy.deepcopy`. Assigning objects which
   don't support ``deepcopy()`` is deprecated and will be removed in Django 4.1.
 
+* :class:`~django.test.runner.DiscoverRunner` now enables
+  :py:mod:`faulthandler` by default. This can be disabled by using the
+  :option:`test --no-faulthandler` option.
+
 * :class:`~django.test.Client` now preserves the request query string when
   following 307 and 308 redirects.
 

+ 8 - 1
docs/topics/testing/advanced.txt

@@ -510,7 +510,7 @@ behavior. This class defines the ``run_tests()`` entry point, plus a
 selection of other methods that are used to by ``run_tests()`` to set up,
 execute and tear down the test suite.
 
-.. class:: DiscoverRunner(pattern='test*.py', top_level=None, verbosity=1, interactive=True, failfast=False, keepdb=False, reverse=False, debug_mode=False, debug_sql=False, test_name_patterns=None, pdb=False, buffer=False, **kwargs)
+.. class:: DiscoverRunner(pattern='test*.py', top_level=None, verbosity=1, interactive=True, failfast=False, keepdb=False, reverse=False, debug_mode=False, debug_sql=False, test_name_patterns=None, pdb=False, buffer=False, enable_faulthandler=True, **kwargs)
 
     ``DiscoverRunner`` will search for tests in any file matching ``pattern``.
 
@@ -557,6 +557,9 @@ execute and tear down the test suite.
 
     If ``buffer`` is ``True``, outputs from passing tests will be discarded.
 
+    If ``enable_faulthandler`` is ``True``, :py:mod:`faulthandler` will be
+    enabled.
+
     Django may, from time to time, extend the capabilities of the test runner
     by adding new arguments. The ``**kwargs`` declaration allows for this
     expansion. If you subclass ``DiscoverRunner`` or write your own test
@@ -571,6 +574,10 @@ execute and tear down the test suite.
 
         The ``buffer`` argument was added.
 
+    .. versionadded:: 3.2
+
+        The ``enable_faulthandler`` argument was added.
+
 Attributes
 ~~~~~~~~~~
 

+ 28 - 1
tests/test_runner/test_discover_runner.py

@@ -1,7 +1,9 @@
 import os
 from argparse import ArgumentParser
 from contextlib import contextmanager
-from unittest import TestSuite, TextTestRunner, defaultTestLoader, skipUnless
+from unittest import (
+    TestSuite, TextTestRunner, defaultTestLoader, mock, skipUnless,
+)
 
 from django.db import connections
 from django.test import SimpleTestCase
@@ -297,6 +299,31 @@ class DiscoverRunnerTests(SimpleTestCase):
         self.assertIn('Write to stderr.', stderr.getvalue())
         self.assertIn('Write to stdout.', stdout.getvalue())
 
+    @mock.patch('faulthandler.enable')
+    def test_faulthandler_enabled(self, mocked_enable):
+        with mock.patch('faulthandler.is_enabled', return_value=False):
+            DiscoverRunner(enable_faulthandler=True)
+            mocked_enable.assert_called()
+
+    @mock.patch('faulthandler.enable')
+    def test_faulthandler_already_enabled(self, mocked_enable):
+        with mock.patch('faulthandler.is_enabled', return_value=True):
+            DiscoverRunner(enable_faulthandler=True)
+            mocked_enable.assert_not_called()
+
+    @mock.patch('faulthandler.enable')
+    def test_faulthandler_enabled_fileno(self, mocked_enable):
+        # sys.stderr that is not an actual file.
+        with mock.patch('faulthandler.is_enabled', return_value=False), captured_stderr():
+            DiscoverRunner(enable_faulthandler=True)
+            mocked_enable.assert_called()
+
+    @mock.patch('faulthandler.enable')
+    def test_faulthandler_disabled(self, mocked_enable):
+        with mock.patch('faulthandler.is_enabled', return_value=False):
+            DiscoverRunner(enable_faulthandler=False)
+            mocked_enable.assert_not_called()
+
 
 class DiscoverRunnerGetDatabasesTests(SimpleTestCase):
     runner = DiscoverRunner(verbosity=2)