mirror of https://github.com/django/django.git
Fixed #32914 -- Prevented test --shuffle from skipping test methods.
"test --shuffle" skipped test methods when test classes were mixed.
This changes runner.py's reorder_tests() to group by TestCase class.
Regression in 90ba716bf0
.
This commit is contained in:
parent
6f60fa97b0
commit
5848b3a1d7
|
@ -11,6 +11,7 @@ import random
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
import unittest
|
import unittest
|
||||||
|
from collections import defaultdict
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
@ -986,21 +987,27 @@ def reorder_test_bin(tests, shuffler=None, reverse=False):
|
||||||
|
|
||||||
def reorder_tests(tests, classes, reverse=False, shuffler=None):
|
def reorder_tests(tests, classes, reverse=False, shuffler=None):
|
||||||
"""
|
"""
|
||||||
Reorder an iterable of tests by test type, removing any duplicates.
|
Reorder an iterable of tests, grouping by the given TestCase classes.
|
||||||
|
|
||||||
|
This function also removes any duplicates and reorders so that tests of the
|
||||||
|
same type are consecutive.
|
||||||
|
|
||||||
The result is returned as an iterator. `classes` is a sequence of types.
|
The result is returned as an iterator. `classes` is a sequence of types.
|
||||||
All tests of type classes[0] are placed first, then tests of type
|
Tests that are instances of `classes[0]` are grouped first, followed by
|
||||||
classes[1], etc. Tests with no match in classes are placed last.
|
instances of `classes[1]`, etc. Tests that are not instances of any of the
|
||||||
|
classes are grouped last.
|
||||||
|
|
||||||
If `reverse` is True, sort tests within classes in opposite order but
|
If `reverse` is True, the tests within each `classes` group are reversed,
|
||||||
don't reverse test classes.
|
but without reversing the order of `classes` itself.
|
||||||
|
|
||||||
The `shuffler` argument is an optional instance of this module's `Shuffler`
|
The `shuffler` argument is an optional instance of this module's `Shuffler`
|
||||||
class. If provided, tests will be shuffled within each `classes` group, but
|
class. If provided, tests will be shuffled within each `classes` group, but
|
||||||
keeping tests with other tests of their TestCase class. Reversing is
|
keeping tests with other tests of their TestCase class. Reversing is
|
||||||
applied after shuffling to allow reversing the same random order.
|
applied after shuffling to allow reversing the same random order.
|
||||||
"""
|
"""
|
||||||
bins = [OrderedSet() for i in range(len(classes) + 1)]
|
# Each bin maps TestCase class to OrderedSet of tests. This permits tests
|
||||||
|
# to be grouped by TestCase class even if provided non-consecutively.
|
||||||
|
bins = [defaultdict(OrderedSet) for i in range(len(classes) + 1)]
|
||||||
*class_bins, last_bin = bins
|
*class_bins, last_bin = bins
|
||||||
|
|
||||||
for test in tests:
|
for test in tests:
|
||||||
|
@ -1009,9 +1016,11 @@ def reorder_tests(tests, classes, reverse=False, shuffler=None):
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
test_bin = last_bin
|
test_bin = last_bin
|
||||||
test_bin.add(test)
|
test_bin[type(test)].add(test)
|
||||||
|
|
||||||
for tests in bins:
|
for test_bin in bins:
|
||||||
|
# Call list() since reorder_test_bin()'s input must support reversed().
|
||||||
|
tests = list(itertools.chain.from_iterable(test_bin.values()))
|
||||||
yield from reorder_test_bin(tests, shuffler=shuffler, reverse=reverse)
|
yield from reorder_test_bin(tests, shuffler=shuffler, reverse=reverse)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -181,6 +181,19 @@ class TestSuiteTests(SimpleTestCase):
|
||||||
'Tests1.test1', 'Tests1.test2', 'Tests2.test2', 'Tests2.test1',
|
'Tests1.test1', 'Tests1.test2', 'Tests2.test2', 'Tests2.test1',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def test_reorder_tests_same_type_consecutive(self):
|
||||||
|
"""Tests of the same type are made consecutive."""
|
||||||
|
tests = self.make_tests()
|
||||||
|
# Move the last item to the front.
|
||||||
|
tests.insert(0, tests.pop())
|
||||||
|
self.assertTestNames(tests, expected=[
|
||||||
|
'Tests2.test2', 'Tests1.test1', 'Tests1.test2', 'Tests2.test1',
|
||||||
|
])
|
||||||
|
reordered_tests = reorder_tests(tests, classes=[])
|
||||||
|
self.assertTestNames(reordered_tests, expected=[
|
||||||
|
'Tests2.test2', 'Tests2.test1', 'Tests1.test1', 'Tests1.test2',
|
||||||
|
])
|
||||||
|
|
||||||
def test_reorder_tests_random(self):
|
def test_reorder_tests_random(self):
|
||||||
tests = self.make_tests()
|
tests = self.make_tests()
|
||||||
# Choose a seed that shuffles both the classes and methods.
|
# Choose a seed that shuffles both the classes and methods.
|
||||||
|
@ -191,6 +204,19 @@ class TestSuiteTests(SimpleTestCase):
|
||||||
'Tests2.test1', 'Tests2.test2', 'Tests1.test2', 'Tests1.test1',
|
'Tests2.test1', 'Tests2.test2', 'Tests1.test2', 'Tests1.test1',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def test_reorder_tests_random_mixed_classes(self):
|
||||||
|
tests = self.make_tests()
|
||||||
|
# Move the last item to the front.
|
||||||
|
tests.insert(0, tests.pop())
|
||||||
|
shuffler = Shuffler(seed=9)
|
||||||
|
self.assertTestNames(tests, expected=[
|
||||||
|
'Tests2.test2', 'Tests1.test1', 'Tests1.test2', 'Tests2.test1',
|
||||||
|
])
|
||||||
|
reordered_tests = reorder_tests(tests, classes=[], shuffler=shuffler)
|
||||||
|
self.assertTestNames(reordered_tests, expected=[
|
||||||
|
'Tests2.test1', 'Tests2.test2', 'Tests1.test2', 'Tests1.test1',
|
||||||
|
])
|
||||||
|
|
||||||
def test_reorder_tests_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):
|
||||||
|
|
Loading…
Reference in New Issue