diff --git a/django/test/runner.py b/django/test/runner.py index 72863aa8fe5..dbbd03441af 100644 --- a/django/test/runner.py +++ b/django/test/runner.py @@ -553,7 +553,6 @@ class DiscoverRunner: unittest.installHandler() def build_suite(self, test_labels=None, extra_tests=None, **kwargs): - suite = self.test_suite() test_labels = test_labels or ['.'] extra_tests = extra_tests or [] self.test_loader.testNamePatterns = self.test_name_patterns @@ -564,6 +563,7 @@ class DiscoverRunner: if self.top_level is not None: discover_kwargs['top_level_dir'] = self.top_level + all_tests = [] for label in test_labels: kwargs = discover_kwargs.copy() tests = None @@ -607,10 +607,9 @@ class DiscoverRunner: # run, to support running tests from two different top-levels. self.test_loader._top_level_dir = None - suite.addTests(tests) + all_tests.extend(iter_test_cases(tests)) - for test in extra_tests: - suite.addTest(test) + all_tests.extend(iter_test_cases(extra_tests)) if self.tags or self.exclude_tags: if self.verbosity >= 2: @@ -618,8 +617,10 @@ class DiscoverRunner: print('Including test tag(s): %s.' % ', '.join(sorted(self.tags))) if self.exclude_tags: print('Excluding test tag(s): %s.' % ', '.join(sorted(self.exclude_tags))) - suite = filter_tests_by_tags(suite, self.tags, self.exclude_tags) - suite = reorder_suite(suite, self.reorder_by, self.reverse) + all_tests = filter_tests_by_tags(all_tests, self.tags, self.exclude_tags) + + all_tests = reorder_tests(all_tests, self.reorder_by, self.reverse) + suite = self.test_suite(all_tests) if self.parallel > 1: parallel_suite = self.parallel_test_suite(suite, self.parallel, self.failfast) @@ -764,11 +765,11 @@ def is_discoverable(label): return os.path.isdir(os.path.abspath(label)) -def reorder_suite(suite, classes, reverse=False): +def reorder_tests(tests, classes, reverse=False): """ - Reorder a test suite by test type, removing any duplicates. + Reorder an iterable of tests by test type, removing any duplicates. - `classes` is a sequence of types + `classes` is a sequence of types. The result is returned as an iterator. All tests of type classes[0] are placed first, then tests of type classes[1], etc. Tests with no match in classes are placed last. @@ -779,7 +780,7 @@ def reorder_suite(suite, classes, reverse=False): bins = [OrderedSet() for i in range(len(classes) + 1)] *class_bins, last_bin = bins - for test in iter_test_cases(suite): + for test in tests: for test_bin, test_class in zip(class_bins, classes): if isinstance(test, test_class): break @@ -789,8 +790,7 @@ def reorder_suite(suite, classes, reverse=False): if reverse: bins = (reversed(tests) for tests in bins) - suite_class = type(suite) - return suite_class(itertools.chain(*bins)) + return itertools.chain(*bins) def partition_suite_by_case(suite): @@ -813,9 +813,6 @@ def test_match_tags(test, tags, exclude_tags): return (matched_tags or not tags) and not test_tags.intersection(exclude_tags) -def filter_tests_by_tags(suite, tags, exclude_tags): - suite_class = type(suite) - return suite_class( - test for test in iter_test_cases(suite) - if test_match_tags(test, tags, exclude_tags) - ) +def filter_tests_by_tags(tests, tags, exclude_tags): + """Return the matching tests as an iterator.""" + return (test for test in tests if test_match_tags(test, tags, exclude_tags)) diff --git a/docs/releases/4.0.txt b/docs/releases/4.0.txt index 3f04005a3ae..770045e1794 100644 --- a/docs/releases/4.0.txt +++ b/docs/releases/4.0.txt @@ -321,6 +321,10 @@ Miscellaneous * Unsupported operations on a sliced queryset now raise ``TypeError`` instead of ``AssertionError``. +* The undocumented ``django.test.runner.reorder_suite()`` function is renamed + to ``reorder_tests()``. It now accepts an iterable of tests rather than a + test suite, and returns an iterator of tests. + .. _deprecated-features-4.0: Features deprecated in 4.0 diff --git a/tests/test_runner/tests.py b/tests/test_runner/tests.py index ec84ba31b74..7479b80ef1b 100644 --- a/tests/test_runner/tests.py +++ b/tests/test_runner/tests.py @@ -14,7 +14,7 @@ from django.core.management.base import SystemCheckError from django.test import ( SimpleTestCase, TransactionTestCase, skipUnlessDBFeature, ) -from django.test.runner import DiscoverRunner, reorder_suite +from django.test.runner import DiscoverRunner, reorder_tests from django.test.testcases import connections_support_transactions from django.test.utils import ( captured_stderr, dependency_ordered, get_unique_databases_and_mirrors, @@ -118,7 +118,7 @@ class TestSuiteTests(unittest.TestCase): self.assertEqual(len(tests), 4) self.assertNotIsInstance(tests[0], unittest.TestSuite) - def test_reorder_suite_reverse_with_duplicates(self): + def test_reorder_tests_reverse_with_duplicates(self): class Tests1(unittest.TestCase): def test1(self): pass @@ -133,15 +133,16 @@ class TestSuiteTests(unittest.TestCase): suite = self.build_test_suite((Tests1, Tests2)) subsuite = list(suite)[0] suite.addTest(subsuite) - self.assertTestNames(iter_test_cases(suite), expected=[ + tests = list(iter_test_cases(suite)) + self.assertTestNames(tests, expected=[ 'Tests1.test1', 'Tests2.test2', 'Tests2.test3', 'Tests1.test1', ]) - reordered_suite = reorder_suite(suite, classes=[]) - self.assertTestNames(reordered_suite, expected=[ + reordered_tests = reorder_tests(tests, classes=[]) + self.assertTestNames(reordered_tests, expected=[ 'Tests1.test1', 'Tests2.test2', 'Tests2.test3', ]) - reordered_suite = reorder_suite(suite, classes=[], reverse=True) - self.assertTestNames(reordered_suite, expected=[ + reordered_tests = reorder_tests(tests, classes=[], reverse=True) + self.assertTestNames(reordered_tests, expected=[ 'Tests2.test3', 'Tests2.test2', 'Tests1.test1', ])