mirror of https://github.com/django/django.git
Fixed #30676 -- Added --pdb option to test runner.
This commit is contained in:
parent
c5075360c5
commit
052388aba4
|
@ -19,6 +19,11 @@ from django.test.utils import (
|
||||||
from django.utils.datastructures import OrderedSet
|
from django.utils.datastructures import OrderedSet
|
||||||
from django.utils.version import PY37
|
from django.utils.version import PY37
|
||||||
|
|
||||||
|
try:
|
||||||
|
import ipdb as pdb
|
||||||
|
except ImportError:
|
||||||
|
import pdb
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import tblib.pickling_support
|
import tblib.pickling_support
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -72,6 +77,26 @@ class DebugSQLTextTestResult(unittest.TextTestResult):
|
||||||
self.stream.writeln(sql_debug)
|
self.stream.writeln(sql_debug)
|
||||||
|
|
||||||
|
|
||||||
|
class PDBDebugResult(unittest.TextTestResult):
|
||||||
|
"""
|
||||||
|
Custom result class that triggers a PDB session when an error or failure
|
||||||
|
occurs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def addError(self, test, err):
|
||||||
|
super().addError(test, err)
|
||||||
|
self.debug(err)
|
||||||
|
|
||||||
|
def addFailure(self, test, err):
|
||||||
|
super().addFailure(test, err)
|
||||||
|
self.debug(err)
|
||||||
|
|
||||||
|
def debug(self, error):
|
||||||
|
exc_type, exc_value, traceback = error
|
||||||
|
print("\nOpening PDB: %r" % exc_value)
|
||||||
|
pdb.post_mortem(traceback)
|
||||||
|
|
||||||
|
|
||||||
class RemoteTestResult:
|
class RemoteTestResult:
|
||||||
"""
|
"""
|
||||||
Record information about which tests have succeeded and which have failed.
|
Record information about which tests have succeeded and which have failed.
|
||||||
|
@ -408,7 +433,8 @@ class DiscoverRunner:
|
||||||
def __init__(self, pattern=None, top_level=None, verbosity=1,
|
def __init__(self, pattern=None, top_level=None, verbosity=1,
|
||||||
interactive=True, failfast=False, keepdb=False,
|
interactive=True, failfast=False, keepdb=False,
|
||||||
reverse=False, debug_mode=False, debug_sql=False, parallel=0,
|
reverse=False, debug_mode=False, debug_sql=False, parallel=0,
|
||||||
tags=None, exclude_tags=None, test_name_patterns=None, **kwargs):
|
tags=None, exclude_tags=None, test_name_patterns=None,
|
||||||
|
pdb=False, **kwargs):
|
||||||
|
|
||||||
self.pattern = pattern
|
self.pattern = pattern
|
||||||
self.top_level = top_level
|
self.top_level = top_level
|
||||||
|
@ -422,6 +448,9 @@ class DiscoverRunner:
|
||||||
self.parallel = parallel
|
self.parallel = parallel
|
||||||
self.tags = set(tags or [])
|
self.tags = set(tags or [])
|
||||||
self.exclude_tags = set(exclude_tags or [])
|
self.exclude_tags = set(exclude_tags or [])
|
||||||
|
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.')
|
||||||
self.test_name_patterns = None
|
self.test_name_patterns = None
|
||||||
if test_name_patterns:
|
if test_name_patterns:
|
||||||
# unittest does not export the _convert_select_pattern function
|
# unittest does not export the _convert_select_pattern function
|
||||||
|
@ -470,6 +499,10 @@ class DiscoverRunner:
|
||||||
'--exclude-tag', action='append', dest='exclude_tags',
|
'--exclude-tag', action='append', dest='exclude_tags',
|
||||||
help='Do not run tests with the specified tag. Can be used multiple times.',
|
help='Do not run tests with the specified tag. Can be used multiple times.',
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--pdb', action='store_true',
|
||||||
|
help='Runs a debugger (pdb, or ipdb if installed) on error or failure.'
|
||||||
|
)
|
||||||
if PY37:
|
if PY37:
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-k', action='append', dest='test_name_patterns',
|
'-k', action='append', dest='test_name_patterns',
|
||||||
|
@ -574,7 +607,10 @@ class DiscoverRunner:
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_resultclass(self):
|
def get_resultclass(self):
|
||||||
return DebugSQLTextTestResult if self.debug_sql else None
|
if self.debug_sql:
|
||||||
|
return DebugSQLTextTestResult
|
||||||
|
elif self.pdb:
|
||||||
|
return PDBDebugResult
|
||||||
|
|
||||||
def get_test_runner_kwargs(self):
|
def get_test_runner_kwargs(self):
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1456,6 +1456,12 @@ Runs test methods and classes matching test name patterns, in the same way as
|
||||||
|
|
||||||
This feature is only available for Python 3.7 and later.
|
This feature is only available for Python 3.7 and later.
|
||||||
|
|
||||||
|
.. django-admin-option:: --pdb
|
||||||
|
|
||||||
|
.. versionadded:: 3.0
|
||||||
|
|
||||||
|
Spawns a ``pdb`` debugger at each test error or failure. If you have it
|
||||||
|
installed, ``ipdb`` is used instead.
|
||||||
|
|
||||||
``testserver``
|
``testserver``
|
||||||
--------------
|
--------------
|
||||||
|
|
|
@ -348,6 +348,9 @@ Tests
|
||||||
* Django test runner now supports ``--start-at`` and ``--start-after`` options
|
* Django test runner now supports ``--start-at`` and ``--start-after`` options
|
||||||
to run tests starting from a specific top-level module.
|
to run tests starting from a specific top-level module.
|
||||||
|
|
||||||
|
* Django test runner now supports a ``--pdb`` option to spawn a debugger at
|
||||||
|
each error or failure.
|
||||||
|
|
||||||
URLs
|
URLs
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
|
|
|
@ -284,7 +284,7 @@ class ActionSelenium(argparse.Action):
|
||||||
|
|
||||||
def django_tests(verbosity, interactive, failfast, keepdb, reverse,
|
def django_tests(verbosity, interactive, failfast, keepdb, reverse,
|
||||||
test_labels, debug_sql, parallel, tags, exclude_tags,
|
test_labels, debug_sql, parallel, tags, exclude_tags,
|
||||||
test_name_patterns, start_at, start_after):
|
test_name_patterns, start_at, start_after, pdb):
|
||||||
state = setup(verbosity, test_labels, parallel, start_at, start_after)
|
state = setup(verbosity, test_labels, parallel, start_at, start_after)
|
||||||
extra_tests = []
|
extra_tests = []
|
||||||
|
|
||||||
|
@ -304,6 +304,7 @@ def django_tests(verbosity, interactive, failfast, keepdb, reverse,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
exclude_tags=exclude_tags,
|
exclude_tags=exclude_tags,
|
||||||
test_name_patterns=test_name_patterns,
|
test_name_patterns=test_name_patterns,
|
||||||
|
pdb=pdb,
|
||||||
)
|
)
|
||||||
failures = test_runner.run_tests(
|
failures = test_runner.run_tests(
|
||||||
test_labels or get_installed(),
|
test_labels or get_installed(),
|
||||||
|
@ -495,6 +496,10 @@ if __name__ == "__main__":
|
||||||
'--start-at', dest='start_at',
|
'--start-at', dest='start_at',
|
||||||
help='Run tests starting at the specified top-level module.',
|
help='Run tests starting at the specified top-level module.',
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--pdb', action='store_true',
|
||||||
|
help='Runs the PDB debugger on error or failure.'
|
||||||
|
)
|
||||||
if PY37:
|
if PY37:
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-k', dest='test_name_patterns', action='append',
|
'-k', dest='test_name_patterns', action='append',
|
||||||
|
@ -561,7 +566,7 @@ if __name__ == "__main__":
|
||||||
options.debug_sql, options.parallel, options.tags,
|
options.debug_sql, options.parallel, options.tags,
|
||||||
options.exclude_tags,
|
options.exclude_tags,
|
||||||
getattr(options, 'test_name_patterns', None),
|
getattr(options, 'test_name_patterns', None),
|
||||||
options.start_at, options.start_after,
|
options.start_at, options.start_after, options.pdb,
|
||||||
)
|
)
|
||||||
if failures:
|
if failures:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
Loading…
Reference in New Issue