summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorJakub Paczkowski <jakub@paczkowski.eu>2015-11-07 14:57:56 +0100
committerTim Graham <timograham@gmail.com>2016-02-17 09:44:18 -0500
commitd4dc775620fc57e962165eab98a77264e3dd16b2 (patch)
treeef18bd2705ca738d087c4e725c489cffada1945d /django
parent0db7e61076116c2d93d61f98ef31690542359e48 (diff)
Fixed #25735 -- Added support for test tags to DiscoverRunner.
Thanks Carl Meyer, Claude Paroz, and Simon Charette for review.
Diffstat (limited to 'django')
-rw-r--r--django/test/runner.py31
-rw-r--r--django/test/utils.py10
2 files changed, 39 insertions, 2 deletions
diff --git a/django/test/runner.py b/django/test/runner.py
index 7d24aae9dc..b025e2a1f7 100644
--- a/django/test/runner.py
+++ b/django/test/runner.py
@@ -360,11 +360,10 @@ class DiscoverRunner(object):
def __init__(self, pattern=None, top_level=None, verbosity=1,
interactive=True, failfast=False, keepdb=False,
reverse=False, debug_sql=False, parallel=0,
- **kwargs):
+ tags=None, exclude_tags=None, **kwargs):
self.pattern = pattern
self.top_level = top_level
-
self.verbosity = verbosity
self.interactive = interactive
self.failfast = failfast
@@ -372,6 +371,8 @@ class DiscoverRunner(object):
self.reverse = reverse
self.debug_sql = debug_sql
self.parallel = parallel
+ self.tags = set(tags or [])
+ self.exclude_tags = set(exclude_tags or [])
@classmethod
def add_arguments(cls, parser):
@@ -394,6 +395,10 @@ class DiscoverRunner(object):
'--parallel', dest='parallel', nargs='?', default=1, type=int,
const=default_test_processes(), metavar='N',
help='Run tests using up to N parallel processes.')
+ parser.add_argument('--tag', action='append', dest='tags',
+ help='Run only tests with the specified tag. Can be used multiple times.')
+ parser.add_argument('--exclude-tag', action='append', dest='exclude_tags',
+ help='Do not run tests with the specified tag. Can be used multiple times.')
def setup_test_environment(self, **kwargs):
setup_test_environment()
@@ -459,6 +464,8 @@ class DiscoverRunner(object):
for test in extra_tests:
suite.addTest(test)
+ if self.tags or self.exclude_tags:
+ suite = filter_tests_by_tags(suite, self.tags, self.exclude_tags)
suite = reorder_suite(suite, self.reorder_by, self.reverse)
if self.parallel > 1:
@@ -747,3 +754,23 @@ def setup_databases(verbosity, interactive, keepdb=False, debug_sql=False, paral
connections[alias].force_debug_cursor = True
return old_names
+
+
+def filter_tests_by_tags(suite, tags, exclude_tags):
+ suite_class = type(suite)
+ filtered_suite = suite_class()
+
+ for test in suite:
+ if isinstance(test, suite_class):
+ filtered_suite.addTests(filter_tests_by_tags(test, tags, exclude_tags))
+ else:
+ test_tags = set(getattr(test, 'tags', set()))
+ test_fn_name = getattr(test, '_testMethodName', str(test))
+ test_fn = getattr(test, test_fn_name, test)
+ test_fn_tags = set(getattr(test_fn, 'tags', set()))
+ all_tags = test_tags.union(test_fn_tags)
+ matched_tags = all_tags.intersection(tags)
+ if (matched_tags or not tags) and not all_tags.intersection(exclude_tags):
+ filtered_suite.addTest(test)
+
+ return filtered_suite
diff --git a/django/test/utils.py b/django/test/utils.py
index ee2837f497..28b02e2199 100644
--- a/django/test/utils.py
+++ b/django/test/utils.py
@@ -707,3 +707,13 @@ class isolate_apps(TestContextDecorator):
def disable(self):
setattr(Options, 'default_apps', self.old_apps)
+
+
+def tag(*tags):
+ """
+ Decorator to add tags to a test class or method.
+ """
+ def decorator(obj):
+ setattr(obj, 'tags', set(tags))
+ return obj
+ return decorator