diff --git a/news/12183.trivial.rst b/news/12183.trivial.rst new file mode 100644 index 00000000000..c22e854c9a5 --- /dev/null +++ b/news/12183.trivial.rst @@ -0,0 +1 @@ +Add test cases for some behaviors of ``install --dry-run`` and ``--use-feature=fast-deps``. diff --git a/tests/functional/test_fast_deps.py b/tests/functional/test_fast_deps.py index 0109db825b7..9e529c0891e 100644 --- a/tests/functional/test_fast_deps.py +++ b/tests/functional/test_fast_deps.py @@ -2,12 +2,14 @@ import json import os import pathlib +import re from os.path import basename from typing import Iterable from pip._vendor.packaging.utils import canonicalize_name from pytest import mark +from pip._internal.utils.misc import hash_file from tests.lib import PipTestEnvironment, TestData, TestPipResult @@ -101,3 +103,36 @@ def test_hash_mismatch(script: PipTestEnvironment, tmp_path: pathlib.Path) -> No expect_error=True, ) assert "DO NOT MATCH THE HASHES" in result.stderr + + +@mark.network +def test_hash_mismatch_existing_download_for_metadata_only_wheel( + script: PipTestEnvironment, tmp_path: pathlib.Path +) -> None: + """Metadata-only wheels from PEP 658 or fast-deps check for hash matching in + a separate code path than when the wheel is downloaded all at once. Make sure we + still check for hash mismatches.""" + reqs = tmp_path / "requirements.txt" + reqs.write_text("idna==2.10") + dl_dir = tmp_path / "downloads" + dl_dir.mkdir() + idna_wheel = dl_dir / "idna-2.10-py2.py3-none-any.whl" + idna_wheel.write_text("asdf") + result = script.pip( + "download", + # Ensure that we have a metadata-only dist for idna. + "--use-feature=fast-deps", + "-r", + str(reqs), + "-d", + str(dl_dir), + allow_stderr_warning=True, + ) + assert re.search( + r"WARNING: Previously-downloaded file.*has bad hash", result.stderr + ) + # This is the correct hash for idna==2.10. + assert ( + hash_file(str(idna_wheel))[0].hexdigest() + == "b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" + ) diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index eabddfe58fa..5e8a82fb345 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -7,7 +7,7 @@ import textwrap from os.path import curdir, join, pardir from pathlib import Path -from typing import Dict, List, Tuple +from typing import Dict, Iterable, List, Optional, Tuple import pytest @@ -20,6 +20,7 @@ PipTestEnvironment, ResolverVariant, TestData, + TestPipResult, _create_svn_repo, _create_test_package, create_basic_wheel_for_package, @@ -2371,14 +2372,68 @@ def test_install_logs_pip_version_in_debug( assert_re_match(pattern, result.stdout) -def test_install_dry_run(script: PipTestEnvironment, data: TestData) -> None: - """Test that pip install --dry-run logs what it would install.""" - result = script.pip( - "install", "--dry-run", "--find-links", data.find_links, "simple" +def install_find_links( + script: PipTestEnvironment, + data: TestData, + args: Iterable[str], + *, + dry_run: bool, + target_dir: Optional[Path], +) -> TestPipResult: + return script.pip( + "install", + *( + ( + "--target", + str(target_dir), + ) + if target_dir is not None + else () + ), + *(("--dry-run",) if dry_run else ()), + "--no-index", + "--find-links", + data.find_links, + *args, + ) + + +@pytest.mark.parametrize( + "with_target_dir", + (True, False), +) +def test_install_dry_run_nothing_installed( + script: PipTestEnvironment, + data: TestData, + tmpdir: Path, + with_target_dir: bool, +) -> None: + """Test that pip install --dry-run logs what it would install, but doesn't actually + install anything.""" + if with_target_dir: + install_dir = tmpdir / "fake-install" + install_dir.mkdir() + else: + install_dir = None + + result = install_find_links( + script, data, ["simple"], dry_run=True, target_dir=install_dir ) assert "Would install simple-3.0" in result.stdout assert "Successfully installed" not in result.stdout + script.assert_not_installed("simple") + if with_target_dir: + assert not os.listdir(install_dir) + + # Ensure that the same install command would normally have worked if not for + # --dry-run. + install_find_links(script, data, ["simple"], dry_run=False, target_dir=install_dir) + if with_target_dir: + assert os.listdir(install_dir) + else: + script.assert_installed(simple="3.0") + @pytest.mark.skipif( sys.version_info < (3, 11), diff --git a/tests/unit/metadata/test_metadata.py b/tests/unit/metadata/test_metadata.py index f77178fb9c1..47093fb54d1 100644 --- a/tests/unit/metadata/test_metadata.py +++ b/tests/unit/metadata/test_metadata.py @@ -129,3 +129,17 @@ def test_dist_found_in_zip(tmp_path: Path) -> None: dist = get_environment([location]).get_distribution("pkg") assert dist is not None and dist.location is not None assert Path(dist.location) == Path(location) + + +@pytest.mark.parametrize( + "path", + ( + "/path/to/foo.egg-info".replace("/", os.path.sep), + # Tests issue fixed by https://github.com/pypa/pip/pull/2530 + "/path/to/foo.egg-info/".replace("/", os.path.sep), + ), +) +def test_trailing_slash_directory_metadata(path: str) -> None: + dist = get_directory_distribution(path) + assert dist.raw_name == dist.canonical_name == "foo" + assert dist.location == "/path/to".replace("/", os.path.sep) diff --git a/tests/unit/test_req.py b/tests/unit/test_req.py index 545828f8eea..2d1fa269490 100644 --- a/tests/unit/test_req.py +++ b/tests/unit/test_req.py @@ -23,7 +23,6 @@ PreviousBuildDirError, ) from pip._internal.index.package_finder import PackageFinder -from pip._internal.metadata import select_backend from pip._internal.models.direct_url import ArchiveInfo, DirectUrl, DirInfo, VcsInfo from pip._internal.models.link import Link from pip._internal.network.session import PipSession @@ -600,22 +599,6 @@ def test_url_preserved_editable_req(self) -> None: assert req.link is not None assert req.link.url == url - @pytest.mark.parametrize( - "path", - ( - "/path/to/foo.egg-info".replace("/", os.path.sep), - # Tests issue fixed by https://github.com/pypa/pip/pull/2530 - "/path/to/foo.egg-info/".replace("/", os.path.sep), - ), - ) - def test_get_dist(self, path: str) -> None: - req = install_req_from_line("foo") - req.metadata_directory = path - dist = req.get_dist() - assert isinstance(dist, select_backend().Distribution) - assert dist.raw_name == dist.canonical_name == "foo" - assert dist.location == "/path/to".replace("/", os.path.sep) - def test_markers(self) -> None: for line in ( # recommended syntax