Fixed #32529 -- Delayed creating a test suite in build_suite().

This commit is contained in:
Chris Jerdonek 2021-03-08 08:29:44 -08:00 committed by Mariusz Felisiak
parent d8a4bcffdb
commit d828beb68f
3 changed files with 27 additions and 25 deletions

View File

@ -553,7 +553,6 @@ class DiscoverRunner:
unittest.installHandler() unittest.installHandler()
def build_suite(self, test_labels=None, extra_tests=None, **kwargs): def build_suite(self, test_labels=None, extra_tests=None, **kwargs):
suite = self.test_suite()
test_labels = test_labels or ['.'] test_labels = test_labels or ['.']
extra_tests = extra_tests or [] extra_tests = extra_tests or []
self.test_loader.testNamePatterns = self.test_name_patterns self.test_loader.testNamePatterns = self.test_name_patterns
@ -564,6 +563,7 @@ class DiscoverRunner:
if self.top_level is not None: if self.top_level is not None:
discover_kwargs['top_level_dir'] = self.top_level discover_kwargs['top_level_dir'] = self.top_level
all_tests = []
for label in test_labels: for label in test_labels:
kwargs = discover_kwargs.copy() kwargs = discover_kwargs.copy()
tests = None tests = None
@ -607,10 +607,9 @@ class DiscoverRunner:
# run, to support running tests from two different top-levels. # run, to support running tests from two different top-levels.
self.test_loader._top_level_dir = None self.test_loader._top_level_dir = None
suite.addTests(tests) all_tests.extend(iter_test_cases(tests))
for test in extra_tests: all_tests.extend(iter_test_cases(extra_tests))
suite.addTest(test)
if self.tags or self.exclude_tags: if self.tags or self.exclude_tags:
if self.verbosity >= 2: if self.verbosity >= 2:
@ -618,8 +617,10 @@ class DiscoverRunner:
print('Including test tag(s): %s.' % ', '.join(sorted(self.tags))) print('Including test tag(s): %s.' % ', '.join(sorted(self.tags)))
if self.exclude_tags: if self.exclude_tags:
print('Excluding test tag(s): %s.' % ', '.join(sorted(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) all_tests = filter_tests_by_tags(all_tests, self.tags, self.exclude_tags)
suite = reorder_suite(suite, self.reorder_by, self.reverse)
all_tests = reorder_tests(all_tests, self.reorder_by, self.reverse)
suite = self.test_suite(all_tests)
if self.parallel > 1: if self.parallel > 1:
parallel_suite = self.parallel_test_suite(suite, self.parallel, self.failfast) 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)) 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 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. 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)] bins = [OrderedSet() for i in range(len(classes) + 1)]
*class_bins, last_bin = bins *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): for test_bin, test_class in zip(class_bins, classes):
if isinstance(test, test_class): if isinstance(test, test_class):
break break
@ -789,8 +790,7 @@ def reorder_suite(suite, classes, reverse=False):
if reverse: if reverse:
bins = (reversed(tests) for tests in bins) bins = (reversed(tests) for tests in bins)
suite_class = type(suite) return itertools.chain(*bins)
return suite_class(itertools.chain(*bins))
def partition_suite_by_case(suite): 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) return (matched_tags or not tags) and not test_tags.intersection(exclude_tags)
def filter_tests_by_tags(suite, tags, exclude_tags): def filter_tests_by_tags(tests, tags, exclude_tags):
suite_class = type(suite) """Return the matching tests as an iterator."""
return suite_class( return (test for test in tests if test_match_tags(test, tags, exclude_tags))
test for test in iter_test_cases(suite)
if test_match_tags(test, tags, exclude_tags)
)

View File

@ -321,6 +321,10 @@ Miscellaneous
* Unsupported operations on a sliced queryset now raise ``TypeError`` instead * Unsupported operations on a sliced queryset now raise ``TypeError`` instead
of ``AssertionError``. 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: .. _deprecated-features-4.0:
Features deprecated in 4.0 Features deprecated in 4.0

View File

@ -14,7 +14,7 @@ from django.core.management.base import SystemCheckError
from django.test import ( from django.test import (
SimpleTestCase, TransactionTestCase, skipUnlessDBFeature, 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.testcases import connections_support_transactions
from django.test.utils import ( from django.test.utils import (
captured_stderr, dependency_ordered, get_unique_databases_and_mirrors, captured_stderr, dependency_ordered, get_unique_databases_and_mirrors,
@ -118,7 +118,7 @@ class TestSuiteTests(unittest.TestCase):
self.assertEqual(len(tests), 4) self.assertEqual(len(tests), 4)
self.assertNotIsInstance(tests[0], unittest.TestSuite) 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): class Tests1(unittest.TestCase):
def test1(self): def test1(self):
pass pass
@ -133,15 +133,16 @@ class TestSuiteTests(unittest.TestCase):
suite = self.build_test_suite((Tests1, Tests2)) suite = self.build_test_suite((Tests1, Tests2))
subsuite = list(suite)[0] subsuite = list(suite)[0]
suite.addTest(subsuite) 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', 'Tests1.test1', 'Tests2.test2', 'Tests2.test3', 'Tests1.test1',
]) ])
reordered_suite = reorder_suite(suite, classes=[]) reordered_tests = reorder_tests(tests, classes=[])
self.assertTestNames(reordered_suite, expected=[ self.assertTestNames(reordered_tests, expected=[
'Tests1.test1', 'Tests2.test2', 'Tests2.test3', 'Tests1.test1', 'Tests2.test2', 'Tests2.test3',
]) ])
reordered_suite = reorder_suite(suite, classes=[], reverse=True) reordered_tests = reorder_tests(tests, classes=[], reverse=True)
self.assertTestNames(reordered_suite, expected=[ self.assertTestNames(reordered_tests, expected=[
'Tests2.test3', 'Tests2.test2', 'Tests1.test1', 'Tests2.test3', 'Tests2.test2', 'Tests1.test1',
]) ])