python: fix scope assignment for indirect parameter sets (#11277)

Previously, when assigning a scope for a fully-indirect parameter set,
when there are multiple fixturedefs for a param (i.e. same-name fixture
chain), the highest scope was used, but it should be the lowest scope,
since that's the effective scope of the fixture.
This commit is contained in:
Sadra Barikbin 2023-08-06 17:29:54 +03:30 committed by GitHub
parent 1c04a92503
commit e8a8a5f320
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 67 additions and 3 deletions

View File

@ -0,0 +1,2 @@
Fixed a bug that when there are multiple fixtures for an indirect parameter,
the scope of the highest-scope fixture is picked for the parameter set, instead of that of the one with the narrowest scope.

View File

@ -492,7 +492,7 @@ class FixtureRequest:
node: Optional[Union[nodes.Item, nodes.Collector]] = self._pyfuncitem
elif scope is Scope.Package:
# FIXME: _fixturedef is not defined on FixtureRequest (this class),
# but on FixtureRequest (a subclass).
# but on SubRequest (a subclass).
node = get_scope_package(self._pyfuncitem, self._fixturedef) # type: ignore[attr-defined]
else:
node = get_scope_node(self._pyfuncitem, scope)

View File

@ -1516,7 +1516,7 @@ def _find_parametrized_scope(
if all_arguments_are_fixtures:
fixturedefs = arg2fixturedefs or {}
used_scopes = [
fixturedef[0]._scope
fixturedef[-1]._scope
for name, fixturedef in fixturedefs.items()
if name in argnames
]
@ -1682,7 +1682,7 @@ class Function(PyobjMixin, nodes.Item):
:param config:
The pytest Config object.
:param callspec:
If given, this is function has been parametrized and the callspec contains
If given, this function has been parametrized and the callspec contains
meta information about the parametrization.
:param callobj:
If given, the object which will be called when the Function is invoked,

View File

@ -151,6 +151,7 @@ class TestMetafunc:
module_fix=[DummyFixtureDef(Scope.Module)],
class_fix=[DummyFixtureDef(Scope.Class)],
func_fix=[DummyFixtureDef(Scope.Function)],
mixed_fix=[DummyFixtureDef(Scope.Module), DummyFixtureDef(Scope.Class)],
),
)
@ -187,6 +188,7 @@ class TestMetafunc:
)
== Scope.Module
)
assert find_scope(["mixed_fix"], indirect=True) == Scope.Class
def test_parametrize_and_id(self) -> None:
def func(x, y):
@ -1503,6 +1505,66 @@ class TestMetafuncFunctional:
result = pytester.runpytest()
assert result.ret == 0
def test_reordering_with_scopeless_and_just_indirect_parametrization(
self, pytester: Pytester
) -> None:
pytester.makeconftest(
"""
import pytest
@pytest.fixture(scope="package")
def fixture1():
pass
"""
)
pytester.makepyfile(
"""
import pytest
@pytest.fixture(scope="module")
def fixture0():
pass
@pytest.fixture(scope="module")
def fixture1(fixture0):
pass
@pytest.mark.parametrize("fixture1", [0], indirect=True)
def test_0(fixture1):
pass
@pytest.fixture(scope="module")
def fixture():
pass
@pytest.mark.parametrize("fixture", [0], indirect=True)
def test_1(fixture):
pass
def test_2():
pass
class Test:
@pytest.fixture(scope="class")
def fixture(self, fixture):
pass
@pytest.mark.parametrize("fixture", [0], indirect=True)
def test_3(self, fixture):
pass
"""
)
result = pytester.runpytest("-v")
assert result.ret == 0
result.stdout.fnmatch_lines(
[
"*test_0*",
"*test_1*",
"*test_2*",
"*test_3*",
]
)
class TestMetafuncFunctionalAuto:
"""Tests related to automatically find out the correct scope for