Skip to content

Commit f721829

Browse files
authored
Merge pull request #3183 from cheezman34/master
Fix ordering of tests to minimize fixture creating
2 parents 7152707 + 3425edd commit f721829

File tree

3 files changed

+57
-7
lines changed

3 files changed

+57
-7
lines changed

_pytest/fixtures.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -166,20 +166,27 @@ def reorder_items(items):
166166
items_by_argkey = {}
167167
for scopenum in range(0, scopenum_function):
168168
argkeys_cache[scopenum] = d = {}
169-
items_by_argkey[scopenum] = item_d = defaultdict(list)
169+
items_by_argkey[scopenum] = item_d = defaultdict(deque)
170170
for item in items:
171171
keys = OrderedDict.fromkeys(get_parametrized_fixture_keys(item, scopenum))
172172
if keys:
173173
d[item] = keys
174174
for key in keys:
175175
item_d[key].append(item)
176176
items = OrderedDict.fromkeys(items)
177-
return list(reorder_items_atscope(items, set(), argkeys_cache, items_by_argkey, 0))
177+
return list(reorder_items_atscope(items, argkeys_cache, items_by_argkey, 0))
178178

179179

180-
def reorder_items_atscope(items, ignore, argkeys_cache, items_by_argkey, scopenum):
180+
def fix_cache_order(item, argkeys_cache, items_by_argkey):
181+
for scopenum in range(0, scopenum_function):
182+
for key in argkeys_cache[scopenum].get(item, []):
183+
items_by_argkey[scopenum][key].appendleft(item)
184+
185+
186+
def reorder_items_atscope(items, argkeys_cache, items_by_argkey, scopenum):
181187
if scopenum >= scopenum_function or len(items) < 3:
182188
return items
189+
ignore = set()
183190
items_deque = deque(items)
184191
items_done = OrderedDict()
185192
scoped_items_by_argkey = items_by_argkey[scopenum]
@@ -197,13 +204,14 @@ def reorder_items_atscope(items, ignore, argkeys_cache, items_by_argkey, scopenu
197204
else:
198205
slicing_argkey, _ = argkeys.popitem()
199206
# we don't have to remove relevant items from later in the deque because they'll just be ignored
200-
for i in reversed(scoped_items_by_argkey[slicing_argkey]):
201-
if i in items:
202-
items_deque.appendleft(i)
207+
matching_items = [i for i in scoped_items_by_argkey[slicing_argkey] if i in items]
208+
for i in reversed(matching_items):
209+
fix_cache_order(i, argkeys_cache, items_by_argkey)
210+
items_deque.appendleft(i)
203211
break
204212
if no_argkey_group:
205213
no_argkey_group = reorder_items_atscope(
206-
no_argkey_group, set(), argkeys_cache, items_by_argkey, scopenum + 1)
214+
no_argkey_group, argkeys_cache, items_by_argkey, scopenum + 1)
207215
for item in no_argkey_group:
208216
items_done[item] = None
209217
ignore.add(slicing_argkey)

changelog/3161.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix ordering of tests using parametrized fixtures which can lead to fixtures being created more than necessary.

testing/python/fixture.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2168,6 +2168,47 @@ def test_func4(marg):
21682168
test_mod1.py::test_func1[m2] PASSED
21692169
""")
21702170

2171+
def test_dynamic_parametrized_ordering(self, testdir):
2172+
testdir.makeini("""
2173+
[pytest]
2174+
console_output_style=classic
2175+
""")
2176+
testdir.makeconftest("""
2177+
import pytest
2178+
2179+
def pytest_configure(config):
2180+
class DynamicFixturePlugin(object):
2181+
@pytest.fixture(scope='session', params=['flavor1', 'flavor2'])
2182+
def flavor(self, request):
2183+
return request.param
2184+
config.pluginmanager.register(DynamicFixturePlugin(), 'flavor-fixture')
2185+
2186+
@pytest.fixture(scope='session', params=['vxlan', 'vlan'])
2187+
def encap(request):
2188+
return request.param
2189+
2190+
@pytest.fixture(scope='session', autouse='True')
2191+
def reprovision(request, flavor, encap):
2192+
pass
2193+
""")
2194+
testdir.makepyfile("""
2195+
def test(reprovision):
2196+
pass
2197+
def test2(reprovision):
2198+
pass
2199+
""")
2200+
result = testdir.runpytest("-v")
2201+
result.stdout.fnmatch_lines("""
2202+
test_dynamic_parametrized_ordering.py::test[flavor1-vxlan] PASSED
2203+
test_dynamic_parametrized_ordering.py::test2[flavor1-vxlan] PASSED
2204+
test_dynamic_parametrized_ordering.py::test[flavor2-vxlan] PASSED
2205+
test_dynamic_parametrized_ordering.py::test2[flavor2-vxlan] PASSED
2206+
test_dynamic_parametrized_ordering.py::test[flavor2-vlan] PASSED
2207+
test_dynamic_parametrized_ordering.py::test2[flavor2-vlan] PASSED
2208+
test_dynamic_parametrized_ordering.py::test[flavor1-vlan] PASSED
2209+
test_dynamic_parametrized_ordering.py::test2[flavor1-vlan] PASSED
2210+
""")
2211+
21712212
def test_class_ordering(self, testdir):
21722213
testdir.makeini("""
21732214
[pytest]

0 commit comments

Comments
 (0)