summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Godwin <andrew@aeracode.org>2019-08-05 13:41:14 +1000
committerCarlton Gibson <carlton@noumenal.es>2019-08-07 08:16:31 +0200
commit052388aba47ba7166886fc741cac5ce3b22fea58 (patch)
treed5509efba1acea95aab6975e09d9aaea2e9731bc
parentc5075360c50b6e681fb3e7d58e6e93ae96662f49 (diff)
Fixed #30676 -- Added --pdb option to test runner.
-rw-r--r--django/test/runner.py40
-rw-r--r--docs/ref/django-admin.txt6
-rw-r--r--docs/releases/3.0.txt3
-rwxr-xr-xtests/runtests.py9
4 files changed, 54 insertions, 4 deletions
diff --git a/django/test/runner.py b/django/test/runner.py
index 85a7cd1265..194077d81a 100644
--- a/django/test/runner.py
+++ b/django/test/runner.py
@@ -20,6 +20,11 @@ from django.utils.datastructures import OrderedSet
from django.utils.version import PY37
try:
+ import ipdb as pdb
+except ImportError:
+ import pdb
+
+try:
import tblib.pickling_support
except ImportError:
tblib = None
@@ -72,6 +77,26 @@ class DebugSQLTextTestResult(unittest.TextTestResult):
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:
"""
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,
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, **kwargs):
+ tags=None, exclude_tags=None, test_name_patterns=None,
+ pdb=False, **kwargs):
self.pattern = pattern
self.top_level = top_level
@@ -422,6 +448,9 @@ class DiscoverRunner:
self.parallel = parallel
self.tags = set(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
if test_name_patterns:
# unittest does not export the _convert_select_pattern function
@@ -470,6 +499,10 @@ class DiscoverRunner:
'--exclude-tag', action='append', dest='exclude_tags',
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:
parser.add_argument(
'-k', action='append', dest='test_name_patterns',
@@ -574,7 +607,10 @@ class DiscoverRunner:
)
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):
return {
diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt
index b5572cd5c7..b38740669f 100644
--- a/docs/ref/django-admin.txt
+++ b/docs/ref/django-admin.txt
@@ -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.
+.. 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``
--------------
diff --git a/docs/releases/3.0.txt b/docs/releases/3.0.txt
index 9de85ab770..ad303ede3e 100644
--- a/docs/releases/3.0.txt
+++ b/docs/releases/3.0.txt
@@ -348,6 +348,9 @@ Tests
* Django test runner now supports ``--start-at`` and ``--start-after`` options
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
~~~~
diff --git a/tests/runtests.py b/tests/runtests.py
index 153cb0df26..840c06321d 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -284,7 +284,7 @@ class ActionSelenium(argparse.Action):
def django_tests(verbosity, interactive, failfast, keepdb, reverse,
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)
extra_tests = []
@@ -304,6 +304,7 @@ def django_tests(verbosity, interactive, failfast, keepdb, reverse,
tags=tags,
exclude_tags=exclude_tags,
test_name_patterns=test_name_patterns,
+ pdb=pdb,
)
failures = test_runner.run_tests(
test_labels or get_installed(),
@@ -495,6 +496,10 @@ if __name__ == "__main__":
'--start-at', dest='start_at',
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:
parser.add_argument(
'-k', dest='test_name_patterns', action='append',
@@ -561,7 +566,7 @@ if __name__ == "__main__":
options.debug_sql, options.parallel, options.tags,
options.exclude_tags,
getattr(options, 'test_name_patterns', None),
- options.start_at, options.start_after,
+ options.start_at, options.start_after, options.pdb,
)
if failures:
sys.exit(1)