mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
Switch to resolver variants in the test suite
This commit is contained in:
parent
6028e6a0fb
commit
5cba61e118
5 changed files with 47 additions and 44 deletions
|
@ -103,17 +103,20 @@ def pytest_collection_modifyitems(config, items):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session", autouse=True)
|
@pytest.fixture(scope="session", autouse=True)
|
||||||
def use_new_resolver(request):
|
def resolver_variant(request):
|
||||||
"""Set environment variable to make pip default to the new resolver.
|
"""Set environment variable to make pip default to the correct resolver.
|
||||||
"""
|
"""
|
||||||
new_resolver = request.config.getoption("--new-resolver")
|
new_resolver = request.config.getoption("--new-resolver")
|
||||||
features = set(os.environ.get("PIP_USE_FEATURE", "").split())
|
features = set(os.environ.get("PIP_USE_FEATURE", "").split())
|
||||||
if new_resolver:
|
if new_resolver:
|
||||||
|
retval = "2020-resolver"
|
||||||
features.add("2020-resolver")
|
features.add("2020-resolver")
|
||||||
else:
|
else:
|
||||||
|
retval = "legacy"
|
||||||
features.discard("2020-resolver")
|
features.discard("2020-resolver")
|
||||||
|
|
||||||
with patch.dict(os.environ, {"PIP_USE_FEATURE": " ".join(features)}):
|
with patch.dict(os.environ, {"PIP_USE_FEATURE": " ".join(features)}):
|
||||||
yield new_resolver
|
yield retval
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='session')
|
@pytest.fixture(scope='session')
|
||||||
|
|
|
@ -941,7 +941,7 @@ def test_install_nonlocal_compatible_wheel(script, data):
|
||||||
def test_install_nonlocal_compatible_wheel_path(
|
def test_install_nonlocal_compatible_wheel_path(
|
||||||
script,
|
script,
|
||||||
data,
|
data,
|
||||||
use_new_resolver
|
resolver_variant,
|
||||||
):
|
):
|
||||||
target_dir = script.scratch_path / 'target'
|
target_dir = script.scratch_path / 'target'
|
||||||
|
|
||||||
|
@ -952,9 +952,9 @@ def test_install_nonlocal_compatible_wheel_path(
|
||||||
'--no-index',
|
'--no-index',
|
||||||
'--only-binary=:all:',
|
'--only-binary=:all:',
|
||||||
Path(data.packages) / 'simplewheel-2.0-py3-fakeabi-fakeplat.whl',
|
Path(data.packages) / 'simplewheel-2.0-py3-fakeabi-fakeplat.whl',
|
||||||
expect_error=use_new_resolver
|
expect_error=(resolver_variant == "2020-resolver"),
|
||||||
)
|
)
|
||||||
if use_new_resolver:
|
if resolver_variant == "2020-resolver":
|
||||||
assert result.returncode == ERROR
|
assert result.returncode == ERROR
|
||||||
else:
|
else:
|
||||||
assert result.returncode == SUCCESS
|
assert result.returncode == SUCCESS
|
||||||
|
@ -1456,7 +1456,7 @@ def test_install_no_binary_disables_cached_wheels(script, data, with_wheel):
|
||||||
assert "Running setup.py install for upper" in str(res), str(res)
|
assert "Running setup.py install for upper" in str(res), str(res)
|
||||||
|
|
||||||
|
|
||||||
def test_install_editable_with_wrong_egg_name(script, use_new_resolver):
|
def test_install_editable_with_wrong_egg_name(script, resolver_variant):
|
||||||
script.scratch_path.joinpath("pkga").mkdir()
|
script.scratch_path.joinpath("pkga").mkdir()
|
||||||
pkga_path = script.scratch_path / 'pkga'
|
pkga_path = script.scratch_path / 'pkga'
|
||||||
pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
|
pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
|
||||||
|
@ -1467,12 +1467,12 @@ def test_install_editable_with_wrong_egg_name(script, use_new_resolver):
|
||||||
result = script.pip(
|
result = script.pip(
|
||||||
'install', '--editable',
|
'install', '--editable',
|
||||||
'file://{pkga_path}#egg=pkgb'.format(**locals()),
|
'file://{pkga_path}#egg=pkgb'.format(**locals()),
|
||||||
expect_error=use_new_resolver,
|
expect_error=(resolver_variant == "2020-resolver"),
|
||||||
)
|
)
|
||||||
assert ("Generating metadata for package pkgb produced metadata "
|
assert ("Generating metadata for package pkgb produced metadata "
|
||||||
"for project name pkga. Fix your #egg=pkgb "
|
"for project name pkga. Fix your #egg=pkgb "
|
||||||
"fragments.") in result.stderr
|
"fragments.") in result.stderr
|
||||||
if use_new_resolver:
|
if resolver_variant == "2020-resolver":
|
||||||
assert "has different name in metadata" in result.stderr, str(result)
|
assert "has different name in metadata" in result.stderr, str(result)
|
||||||
else:
|
else:
|
||||||
assert "Successfully installed pkga" in str(result), str(result)
|
assert "Successfully installed pkga" in str(result), str(result)
|
||||||
|
@ -1505,7 +1505,7 @@ def test_double_install(script):
|
||||||
assert msg not in result.stderr
|
assert msg not in result.stderr
|
||||||
|
|
||||||
|
|
||||||
def test_double_install_fail(script, use_new_resolver):
|
def test_double_install_fail(script, resolver_variant):
|
||||||
"""
|
"""
|
||||||
Test double install failing with two different version requirements
|
Test double install failing with two different version requirements
|
||||||
"""
|
"""
|
||||||
|
@ -1514,9 +1514,9 @@ def test_double_install_fail(script, use_new_resolver):
|
||||||
'pip==7.*',
|
'pip==7.*',
|
||||||
'pip==7.1.2',
|
'pip==7.1.2',
|
||||||
# The new resolver is perfectly capable of handling this
|
# The new resolver is perfectly capable of handling this
|
||||||
expect_error=(not use_new_resolver)
|
expect_error=(resolver_variant == "legacy"),
|
||||||
)
|
)
|
||||||
if not use_new_resolver:
|
if resolver_variant == "legacy":
|
||||||
msg = ("Double requirement given: pip==7.1.2 (already in pip==7.*, "
|
msg = ("Double requirement given: pip==7.1.2 (already in pip==7.*, "
|
||||||
"name='pip')")
|
"name='pip')")
|
||||||
assert msg in result.stderr
|
assert msg in result.stderr
|
||||||
|
@ -1770,11 +1770,11 @@ def test_user_config_accepted(script):
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize("use_module", [True, False])
|
@pytest.mark.parametrize("use_module", [True, False])
|
||||||
def test_install_pip_does_not_modify_pip_when_satisfied(
|
def test_install_pip_does_not_modify_pip_when_satisfied(
|
||||||
script, install_args, expected_message, use_module, use_new_resolver):
|
script, install_args, expected_message, use_module, resolver_variant):
|
||||||
"""
|
"""
|
||||||
Test it doesn't upgrade the pip if it already satisfies the requirement.
|
Test it doesn't upgrade the pip if it already satisfies the requirement.
|
||||||
"""
|
"""
|
||||||
variation = "satisfied" if use_new_resolver else "up-to-date"
|
variation = "satisfied" if resolver_variant else "up-to-date"
|
||||||
expected_message = expected_message.format(variation)
|
expected_message = expected_message.format(variation)
|
||||||
result = script.pip_install_local(
|
result = script.pip_install_local(
|
||||||
'pip', *install_args, use_module=use_module
|
'pip', *install_args, use_module=use_module
|
||||||
|
|
|
@ -376,7 +376,7 @@ def test_constraints_local_install_causes_error(script, data):
|
||||||
def test_constraints_constrain_to_local_editable(
|
def test_constraints_constrain_to_local_editable(
|
||||||
script,
|
script,
|
||||||
data,
|
data,
|
||||||
use_new_resolver
|
resolver_variant,
|
||||||
):
|
):
|
||||||
to_install = data.src.joinpath("singlemodule")
|
to_install = data.src.joinpath("singlemodule")
|
||||||
script.scratch_path.joinpath("constraints.txt").write_text(
|
script.scratch_path.joinpath("constraints.txt").write_text(
|
||||||
|
@ -386,15 +386,15 @@ def test_constraints_constrain_to_local_editable(
|
||||||
'install', '--no-index', '-f', data.find_links, '-c',
|
'install', '--no-index', '-f', data.find_links, '-c',
|
||||||
script.scratch_path / 'constraints.txt', 'singlemodule',
|
script.scratch_path / 'constraints.txt', 'singlemodule',
|
||||||
allow_stderr_warning=True,
|
allow_stderr_warning=True,
|
||||||
expect_error=use_new_resolver
|
expect_error=(resolver_variant == "2020-resolver"),
|
||||||
)
|
)
|
||||||
if use_new_resolver:
|
if resolver_variant == "2020-resolver":
|
||||||
assert 'Links are not allowed as constraints' in result.stderr
|
assert 'Links are not allowed as constraints' in result.stderr
|
||||||
else:
|
else:
|
||||||
assert 'Running setup.py develop for singlemodule' in result.stdout
|
assert 'Running setup.py develop for singlemodule' in result.stdout
|
||||||
|
|
||||||
|
|
||||||
def test_constraints_constrain_to_local(script, data, use_new_resolver):
|
def test_constraints_constrain_to_local(script, data, resolver_variant):
|
||||||
to_install = data.src.joinpath("singlemodule")
|
to_install = data.src.joinpath("singlemodule")
|
||||||
script.scratch_path.joinpath("constraints.txt").write_text(
|
script.scratch_path.joinpath("constraints.txt").write_text(
|
||||||
"{url}#egg=singlemodule".format(url=path_to_url(to_install))
|
"{url}#egg=singlemodule".format(url=path_to_url(to_install))
|
||||||
|
@ -403,15 +403,15 @@ def test_constraints_constrain_to_local(script, data, use_new_resolver):
|
||||||
'install', '--no-index', '-f', data.find_links, '-c',
|
'install', '--no-index', '-f', data.find_links, '-c',
|
||||||
script.scratch_path / 'constraints.txt', 'singlemodule',
|
script.scratch_path / 'constraints.txt', 'singlemodule',
|
||||||
allow_stderr_warning=True,
|
allow_stderr_warning=True,
|
||||||
expect_error=use_new_resolver
|
expect_error=(resolver_variant == "2020-resolver"),
|
||||||
)
|
)
|
||||||
if use_new_resolver:
|
if resolver_variant == "2020-resolver":
|
||||||
assert 'Links are not allowed as constraints' in result.stderr
|
assert 'Links are not allowed as constraints' in result.stderr
|
||||||
else:
|
else:
|
||||||
assert 'Running setup.py install for singlemodule' in result.stdout
|
assert 'Running setup.py install for singlemodule' in result.stdout
|
||||||
|
|
||||||
|
|
||||||
def test_constrained_to_url_install_same_url(script, data, use_new_resolver):
|
def test_constrained_to_url_install_same_url(script, data, resolver_variant):
|
||||||
to_install = data.src.joinpath("singlemodule")
|
to_install = data.src.joinpath("singlemodule")
|
||||||
constraints = path_to_url(to_install) + "#egg=singlemodule"
|
constraints = path_to_url(to_install) + "#egg=singlemodule"
|
||||||
script.scratch_path.joinpath("constraints.txt").write_text(constraints)
|
script.scratch_path.joinpath("constraints.txt").write_text(constraints)
|
||||||
|
@ -419,9 +419,9 @@ def test_constrained_to_url_install_same_url(script, data, use_new_resolver):
|
||||||
'install', '--no-index', '-f', data.find_links, '-c',
|
'install', '--no-index', '-f', data.find_links, '-c',
|
||||||
script.scratch_path / 'constraints.txt', to_install,
|
script.scratch_path / 'constraints.txt', to_install,
|
||||||
allow_stderr_warning=True,
|
allow_stderr_warning=True,
|
||||||
expect_error=use_new_resolver
|
expect_error=(resolver_variant == "2020-resolver"),
|
||||||
)
|
)
|
||||||
if use_new_resolver:
|
if resolver_variant == "2020-resolver":
|
||||||
assert 'Links are not allowed as constraints' in result.stderr
|
assert 'Links are not allowed as constraints' in result.stderr
|
||||||
else:
|
else:
|
||||||
assert ('Running setup.py install for singlemodule'
|
assert ('Running setup.py install for singlemodule'
|
||||||
|
@ -462,7 +462,7 @@ def test_double_install_spurious_hash_mismatch(
|
||||||
assert 'Successfully installed simple-1.0' in str(result)
|
assert 'Successfully installed simple-1.0' in str(result)
|
||||||
|
|
||||||
|
|
||||||
def test_install_with_extras_from_constraints(script, data, use_new_resolver):
|
def test_install_with_extras_from_constraints(script, data, resolver_variant):
|
||||||
to_install = data.packages.joinpath("LocalExtras")
|
to_install = data.packages.joinpath("LocalExtras")
|
||||||
script.scratch_path.joinpath("constraints.txt").write_text(
|
script.scratch_path.joinpath("constraints.txt").write_text(
|
||||||
"{url}#egg=LocalExtras[bar]".format(url=path_to_url(to_install))
|
"{url}#egg=LocalExtras[bar]".format(url=path_to_url(to_install))
|
||||||
|
@ -470,9 +470,9 @@ def test_install_with_extras_from_constraints(script, data, use_new_resolver):
|
||||||
result = script.pip_install_local(
|
result = script.pip_install_local(
|
||||||
'-c', script.scratch_path / 'constraints.txt', 'LocalExtras',
|
'-c', script.scratch_path / 'constraints.txt', 'LocalExtras',
|
||||||
allow_stderr_warning=True,
|
allow_stderr_warning=True,
|
||||||
expect_error=use_new_resolver
|
expect_error=(resolver_variant == "2020-resolver"),
|
||||||
)
|
)
|
||||||
if use_new_resolver:
|
if resolver_variant == "2020-resolver":
|
||||||
assert 'Links are not allowed as constraints' in result.stderr
|
assert 'Links are not allowed as constraints' in result.stderr
|
||||||
else:
|
else:
|
||||||
result.did_create(script.site_packages / 'simple')
|
result.did_create(script.site_packages / 'simple')
|
||||||
|
@ -494,7 +494,7 @@ def test_install_with_extras_from_install(script):
|
||||||
result.did_create(script.site_packages / 'singlemodule.py')
|
result.did_create(script.site_packages / 'singlemodule.py')
|
||||||
|
|
||||||
|
|
||||||
def test_install_with_extras_joined(script, data, use_new_resolver):
|
def test_install_with_extras_joined(script, data, resolver_variant):
|
||||||
to_install = data.packages.joinpath("LocalExtras")
|
to_install = data.packages.joinpath("LocalExtras")
|
||||||
script.scratch_path.joinpath("constraints.txt").write_text(
|
script.scratch_path.joinpath("constraints.txt").write_text(
|
||||||
"{url}#egg=LocalExtras[bar]".format(url=path_to_url(to_install))
|
"{url}#egg=LocalExtras[bar]".format(url=path_to_url(to_install))
|
||||||
|
@ -502,16 +502,16 @@ def test_install_with_extras_joined(script, data, use_new_resolver):
|
||||||
result = script.pip_install_local(
|
result = script.pip_install_local(
|
||||||
'-c', script.scratch_path / 'constraints.txt', 'LocalExtras[baz]',
|
'-c', script.scratch_path / 'constraints.txt', 'LocalExtras[baz]',
|
||||||
allow_stderr_warning=True,
|
allow_stderr_warning=True,
|
||||||
expect_error=use_new_resolver
|
expect_error=(resolver_variant == "2020-resolver"),
|
||||||
)
|
)
|
||||||
if use_new_resolver:
|
if resolver_variant == "2020-resolver":
|
||||||
assert 'Links are not allowed as constraints' in result.stderr
|
assert 'Links are not allowed as constraints' in result.stderr
|
||||||
else:
|
else:
|
||||||
result.did_create(script.site_packages / 'simple')
|
result.did_create(script.site_packages / 'simple')
|
||||||
result.did_create(script.site_packages / 'singlemodule.py')
|
result.did_create(script.site_packages / 'singlemodule.py')
|
||||||
|
|
||||||
|
|
||||||
def test_install_with_extras_editable_joined(script, data, use_new_resolver):
|
def test_install_with_extras_editable_joined(script, data, resolver_variant):
|
||||||
to_install = data.packages.joinpath("LocalExtras")
|
to_install = data.packages.joinpath("LocalExtras")
|
||||||
script.scratch_path.joinpath("constraints.txt").write_text(
|
script.scratch_path.joinpath("constraints.txt").write_text(
|
||||||
"-e {url}#egg=LocalExtras[bar]".format(url=path_to_url(to_install))
|
"-e {url}#egg=LocalExtras[bar]".format(url=path_to_url(to_install))
|
||||||
|
@ -519,9 +519,9 @@ def test_install_with_extras_editable_joined(script, data, use_new_resolver):
|
||||||
result = script.pip_install_local(
|
result = script.pip_install_local(
|
||||||
'-c', script.scratch_path / 'constraints.txt', 'LocalExtras[baz]',
|
'-c', script.scratch_path / 'constraints.txt', 'LocalExtras[baz]',
|
||||||
allow_stderr_warning=True,
|
allow_stderr_warning=True,
|
||||||
expect_error=use_new_resolver
|
expect_error=(resolver_variant == "2020-resolver"),
|
||||||
)
|
)
|
||||||
if use_new_resolver:
|
if resolver_variant == "2020-resolver":
|
||||||
assert 'Links are not allowed as constraints' in result.stderr
|
assert 'Links are not allowed as constraints' in result.stderr
|
||||||
else:
|
else:
|
||||||
result.did_create(script.site_packages / 'simple')
|
result.did_create(script.site_packages / 'simple')
|
||||||
|
@ -550,7 +550,7 @@ def test_install_distribution_duplicate_extras(script, data):
|
||||||
def test_install_distribution_union_with_constraints(
|
def test_install_distribution_union_with_constraints(
|
||||||
script,
|
script,
|
||||||
data,
|
data,
|
||||||
use_new_resolver
|
resolver_variant,
|
||||||
):
|
):
|
||||||
to_install = data.packages.joinpath("LocalExtras")
|
to_install = data.packages.joinpath("LocalExtras")
|
||||||
script.scratch_path.joinpath("constraints.txt").write_text(
|
script.scratch_path.joinpath("constraints.txt").write_text(
|
||||||
|
@ -558,9 +558,9 @@ def test_install_distribution_union_with_constraints(
|
||||||
result = script.pip_install_local(
|
result = script.pip_install_local(
|
||||||
'-c', script.scratch_path / 'constraints.txt', to_install + '[baz]',
|
'-c', script.scratch_path / 'constraints.txt', to_install + '[baz]',
|
||||||
allow_stderr_warning=True,
|
allow_stderr_warning=True,
|
||||||
expect_error=use_new_resolver
|
expect_error=(resolver_variant == "2020-resolver"),
|
||||||
)
|
)
|
||||||
if use_new_resolver:
|
if resolver_variant == "2020-resolver":
|
||||||
msg = 'Unnamed requirements are not allowed as constraints'
|
msg = 'Unnamed requirements are not allowed as constraints'
|
||||||
assert msg in result.stderr
|
assert msg in result.stderr
|
||||||
else:
|
else:
|
||||||
|
@ -571,16 +571,16 @@ def test_install_distribution_union_with_constraints(
|
||||||
def test_install_distribution_union_with_versions(
|
def test_install_distribution_union_with_versions(
|
||||||
script,
|
script,
|
||||||
data,
|
data,
|
||||||
use_new_resolver,
|
resolver_variant,
|
||||||
):
|
):
|
||||||
to_install_001 = data.packages.joinpath("LocalExtras")
|
to_install_001 = data.packages.joinpath("LocalExtras")
|
||||||
to_install_002 = data.packages.joinpath("LocalExtras-0.0.2")
|
to_install_002 = data.packages.joinpath("LocalExtras-0.0.2")
|
||||||
result = script.pip_install_local(
|
result = script.pip_install_local(
|
||||||
to_install_001 + "[bar]",
|
to_install_001 + "[bar]",
|
||||||
to_install_002 + "[baz]",
|
to_install_002 + "[baz]",
|
||||||
expect_error=use_new_resolver,
|
expect_error=(resolver_variant == "2020-resolver"),
|
||||||
)
|
)
|
||||||
if use_new_resolver:
|
if resolver_variant == "2020-resolver":
|
||||||
assert (
|
assert (
|
||||||
"Cannot install localextras[bar] 0.0.1 and localextras[baz] 0.0.2 "
|
"Cannot install localextras[bar] 0.0.1 and localextras[baz] 0.0.2 "
|
||||||
"because these package versions have conflicting dependencies."
|
"because these package versions have conflicting dependencies."
|
||||||
|
|
|
@ -40,7 +40,7 @@ def test_invalid_upgrade_strategy_causes_error(script):
|
||||||
|
|
||||||
def test_only_if_needed_does_not_upgrade_deps_when_satisfied(
|
def test_only_if_needed_does_not_upgrade_deps_when_satisfied(
|
||||||
script,
|
script,
|
||||||
use_new_resolver,
|
resolver_variant,
|
||||||
with_wheel
|
with_wheel
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
|
@ -62,7 +62,7 @@ def test_only_if_needed_does_not_upgrade_deps_when_satisfied(
|
||||||
), "should not have uninstalled simple==2.0"
|
), "should not have uninstalled simple==2.0"
|
||||||
|
|
||||||
msg = "Requirement already satisfied"
|
msg = "Requirement already satisfied"
|
||||||
if not use_new_resolver:
|
if resolver_variant == "legacy":
|
||||||
msg = msg + ", skipping upgrade: simple"
|
msg = msg + ", skipping upgrade: simple"
|
||||||
assert (
|
assert (
|
||||||
msg in result.stdout
|
msg in result.stdout
|
||||||
|
@ -184,7 +184,7 @@ def test_upgrade_if_requested(script, with_wheel):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_upgrade_with_newest_already_installed(script, data, use_new_resolver):
|
def test_upgrade_with_newest_already_installed(script, data, resolver_variant):
|
||||||
"""
|
"""
|
||||||
If the newest version of a package is already installed, the package should
|
If the newest version of a package is already installed, the package should
|
||||||
not be reinstalled and the user should be informed.
|
not be reinstalled and the user should be informed.
|
||||||
|
@ -194,7 +194,7 @@ def test_upgrade_with_newest_already_installed(script, data, use_new_resolver):
|
||||||
'install', '--upgrade', '-f', data.find_links, '--no-index', 'simple'
|
'install', '--upgrade', '-f', data.find_links, '--no-index', 'simple'
|
||||||
)
|
)
|
||||||
assert not result.files_created, 'simple upgraded when it should not have'
|
assert not result.files_created, 'simple upgraded when it should not have'
|
||||||
if use_new_resolver:
|
if resolver_variant == "2020-resolver":
|
||||||
msg = "Requirement already satisfied"
|
msg = "Requirement already satisfied"
|
||||||
else:
|
else:
|
||||||
msg = "already up-to-date"
|
msg = "already up-to-date"
|
||||||
|
|
|
@ -193,7 +193,7 @@ def test_pip_wheel_fail(script, data):
|
||||||
def test_no_clean_option_blocks_cleaning_after_wheel(
|
def test_no_clean_option_blocks_cleaning_after_wheel(
|
||||||
script,
|
script,
|
||||||
data,
|
data,
|
||||||
use_new_resolver,
|
resolver_variant,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Test --no-clean option blocks cleaning after wheel build
|
Test --no-clean option blocks cleaning after wheel build
|
||||||
|
@ -209,7 +209,7 @@ def test_no_clean_option_blocks_cleaning_after_wheel(
|
||||||
allow_stderr_warning=True,
|
allow_stderr_warning=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not use_new_resolver:
|
if resolver_variant == "legacy":
|
||||||
build = build / 'simple'
|
build = build / 'simple'
|
||||||
message = "build/simple should still exist {}".format(result)
|
message = "build/simple should still exist {}".format(result)
|
||||||
assert exists(build), message
|
assert exists(build), message
|
||||||
|
|
Loading…
Reference in a new issue