test_utils.py 28.8 KB
Newer Older
1 2
# -*- coding: utf-8 -*-

3 4 5 6
"""
util tests

"""
7
import itertools
8
import os
P
Pradyun S. Gedam 已提交
9
import shutil
10
import stat
11
import sys
12
import tempfile
P
Pradyun S. Gedam 已提交
13
import time
14
import warnings
15
from io import BytesIO
16

P
Pradyun S. Gedam 已提交
17
import pytest
18
from mock import Mock, patch
19

20
from pip._internal.exceptions import (
P
Pradyun Gedam 已提交
21
    HashMismatch, HashMissing, InstallationError, UnsupportedPythonVersion,
P
Pradyun S. Gedam 已提交
22
)
23 24 25 26
from pip._internal.utils.encoding import auto_decode
from pip._internal.utils.glibc import check_glibc_version
from pip._internal.utils.hashes import Hashes, MissingHashes
from pip._internal.utils.misc import (
P
Pradyun Gedam 已提交
27
    call_subprocess, egg_link_path, ensure_dir, get_installed_distributions,
28 29 30
    get_prog, make_vcs_requirement_url, normalize_path, redact_netloc,
    redact_password_from_url, remove_auth_from_url, rmtree,
    split_auth_from_netloc, untar_file, unzip_file,
P
Pradyun S. Gedam 已提交
31
)
32
from pip._internal.utils.packaging import check_dist_requires_python
33
from pip._internal.utils.temp_dir import TempDirectory, AdjacentTempDirectory
34 35 36 37 38 39 40 41 42 43 44 45


class Tests_EgglinkPath:
    "util.egg_link_path() tests"

    def setup(self):

        project = 'foo'

        self.mock_dist = Mock(project_name=project)
        self.site_packages = 'SITE_PACKAGES'
        self.user_site = 'USER_SITE'
46 47 48 49 50 51 52 53
        self.user_site_egglink = os.path.join(
            self.user_site,
            '%s.egg-link' % project
        )
        self.site_packages_egglink = os.path.join(
            self.site_packages,
            '%s.egg-link' % project,
        )
54

55
        # patches
56
        from pip._internal.utils import misc as utils
57 58 59 60
        self.old_site_packages = utils.site_packages
        self.mock_site_packages = utils.site_packages = 'SITE_PACKAGES'
        self.old_running_under_virtualenv = utils.running_under_virtualenv
        self.mock_running_under_virtualenv = utils.running_under_virtualenv = \
61
            Mock()
62 63 64 65
        self.old_virtualenv_no_global = utils.virtualenv_no_global
        self.mock_virtualenv_no_global = utils.virtualenv_no_global = Mock()
        self.old_user_site = utils.user_site
        self.mock_user_site = utils.user_site = self.user_site
66
        from os import path
67
        self.old_isfile = path.isfile
68 69
        self.mock_isfile = path.isfile = Mock()

70
    def teardown(self):
71
        from pip._internal.utils import misc as utils
72 73 74 75
        utils.site_packages = self.old_site_packages
        utils.running_under_virtualenv = self.old_running_under_virtualenv
        utils.virtualenv_no_global = self.old_virtualenv_no_global
        utils.user_site = self.old_user_site
76 77 78
        from os import path
        path.isfile = self.old_isfile

79 80
    def eggLinkInUserSite(self, egglink):
        return egglink == self.user_site_egglink
81

82 83
    def eggLinkInSitePackages(self, egglink):
        return egglink == self.site_packages_egglink
84

85 86 87
    # ####################### #
    # # egglink in usersite # #
    # ####################### #
88 89 90 91
    def test_egglink_in_usersite_notvenv(self):
        self.mock_virtualenv_no_global.return_value = False
        self.mock_running_under_virtualenv.return_value = False
        self.mock_isfile.side_effect = self.eggLinkInUserSite
92
        assert egg_link_path(self.mock_dist) == self.user_site_egglink
93 94 95 96 97

    def test_egglink_in_usersite_venv_noglobal(self):
        self.mock_virtualenv_no_global.return_value = True
        self.mock_running_under_virtualenv.return_value = True
        self.mock_isfile.side_effect = self.eggLinkInUserSite
98
        assert egg_link_path(self.mock_dist) is None
99 100 101 102 103

    def test_egglink_in_usersite_venv_global(self):
        self.mock_virtualenv_no_global.return_value = False
        self.mock_running_under_virtualenv.return_value = True
        self.mock_isfile.side_effect = self.eggLinkInUserSite
104
        assert egg_link_path(self.mock_dist) == self.user_site_egglink
105

106 107 108
    # ####################### #
    # # egglink in sitepkgs # #
    # ####################### #
109 110 111 112
    def test_egglink_in_sitepkgs_notvenv(self):
        self.mock_virtualenv_no_global.return_value = False
        self.mock_running_under_virtualenv.return_value = False
        self.mock_isfile.side_effect = self.eggLinkInSitePackages
113
        assert egg_link_path(self.mock_dist) == self.site_packages_egglink
114 115 116 117 118

    def test_egglink_in_sitepkgs_venv_noglobal(self):
        self.mock_virtualenv_no_global.return_value = True
        self.mock_running_under_virtualenv.return_value = True
        self.mock_isfile.side_effect = self.eggLinkInSitePackages
119
        assert egg_link_path(self.mock_dist) == self.site_packages_egglink
120 121 122 123 124

    def test_egglink_in_sitepkgs_venv_global(self):
        self.mock_virtualenv_no_global.return_value = False
        self.mock_running_under_virtualenv.return_value = True
        self.mock_isfile.side_effect = self.eggLinkInSitePackages
125
        assert egg_link_path(self.mock_dist) == self.site_packages_egglink
126

127 128 129
    # ################################## #
    # # egglink in usersite & sitepkgs # #
    # ################################## #
130 131 132 133
    def test_egglink_in_both_notvenv(self):
        self.mock_virtualenv_no_global.return_value = False
        self.mock_running_under_virtualenv.return_value = False
        self.mock_isfile.return_value = True
134
        assert egg_link_path(self.mock_dist) == self.user_site_egglink
135 136 137 138 139

    def test_egglink_in_both_venv_noglobal(self):
        self.mock_virtualenv_no_global.return_value = True
        self.mock_running_under_virtualenv.return_value = True
        self.mock_isfile.return_value = True
140
        assert egg_link_path(self.mock_dist) == self.site_packages_egglink
141 142 143 144 145

    def test_egglink_in_both_venv_global(self):
        self.mock_virtualenv_no_global.return_value = False
        self.mock_running_under_virtualenv.return_value = True
        self.mock_isfile.return_value = True
146
        assert egg_link_path(self.mock_dist) == self.site_packages_egglink
147

148 149 150
    # ############## #
    # # no egglink # #
    # ############## #
151 152 153 154
    def test_noegglink_in_sitepkgs_notvenv(self):
        self.mock_virtualenv_no_global.return_value = False
        self.mock_running_under_virtualenv.return_value = False
        self.mock_isfile.return_value = False
155
        assert egg_link_path(self.mock_dist) is None
156 157 158 159 160

    def test_noegglink_in_sitepkgs_venv_noglobal(self):
        self.mock_virtualenv_no_global.return_value = True
        self.mock_running_under_virtualenv.return_value = True
        self.mock_isfile.return_value = False
161
        assert egg_link_path(self.mock_dist) is None
162 163 164 165 166

    def test_noegglink_in_sitepkgs_venv_global(self):
        self.mock_virtualenv_no_global.return_value = False
        self.mock_running_under_virtualenv.return_value = True
        self.mock_isfile.return_value = False
167
        assert egg_link_path(self.mock_dist) is None
168

169

170 171 172
@patch('pip._internal.utils.misc.dist_in_usersite')
@patch('pip._internal.utils.misc.dist_is_local')
@patch('pip._internal.utils.misc.dist_is_editable')
173 174 175 176
class Tests_get_installed_distributions:
    """test util.get_installed_distributions"""

    workingset = [
177 178
        Mock(test_name="global"),
        Mock(test_name="editable"),
179
        Mock(test_name="normal"),
180
        Mock(test_name="user"),
181 182 183 184 185 186 187 188 189 190 191
    ]

    workingset_stdlib = [
        Mock(test_name='normal', key='argparse'),
        Mock(test_name='normal', key='wsgiref')
    ]

    workingset_freeze = [
        Mock(test_name='normal', key='pip'),
        Mock(test_name='normal', key='setuptools'),
        Mock(test_name='normal', key='distribute')
192
    ]
193 194 195 196 197

    def dist_is_editable(self, dist):
        return dist.test_name == "editable"

    def dist_is_local(self, dist):
198 199 200 201
        return dist.test_name != "global" and dist.test_name != 'user'

    def dist_in_usersite(self, dist):
        return dist.test_name == "user"
202

203
    @patch('pip._vendor.pkg_resources.working_set', workingset)
204 205 206
    def test_editables_only(self, mock_dist_is_editable,
                            mock_dist_is_local,
                            mock_dist_in_usersite):
207 208
        mock_dist_is_editable.side_effect = self.dist_is_editable
        mock_dist_is_local.side_effect = self.dist_is_local
209
        mock_dist_in_usersite.side_effect = self.dist_in_usersite
210 211 212 213
        dists = get_installed_distributions(editables_only=True)
        assert len(dists) == 1, dists
        assert dists[0].test_name == "editable"

214
    @patch('pip._vendor.pkg_resources.working_set', workingset)
215 216 217
    def test_exclude_editables(self, mock_dist_is_editable,
                               mock_dist_is_local,
                               mock_dist_in_usersite):
218 219
        mock_dist_is_editable.side_effect = self.dist_is_editable
        mock_dist_is_local.side_effect = self.dist_is_local
220
        mock_dist_in_usersite.side_effect = self.dist_in_usersite
221 222 223 224
        dists = get_installed_distributions(include_editables=False)
        assert len(dists) == 1
        assert dists[0].test_name == "normal"

225
    @patch('pip._vendor.pkg_resources.working_set', workingset)
226 227 228
    def test_include_globals(self, mock_dist_is_editable,
                             mock_dist_is_local,
                             mock_dist_in_usersite):
229 230
        mock_dist_is_editable.side_effect = self.dist_is_editable
        mock_dist_is_local.side_effect = self.dist_is_local
231
        mock_dist_in_usersite.side_effect = self.dist_in_usersite
232
        dists = get_installed_distributions(local_only=False)
233 234 235 236 237 238 239 240 241 242 243 244 245
        assert len(dists) == 4

    @patch('pip._vendor.pkg_resources.working_set', workingset)
    def test_user_only(self, mock_dist_is_editable,
                       mock_dist_is_local,
                       mock_dist_in_usersite):
        mock_dist_is_editable.side_effect = self.dist_is_editable
        mock_dist_is_local.side_effect = self.dist_is_local
        mock_dist_in_usersite.side_effect = self.dist_in_usersite
        dists = get_installed_distributions(local_only=False,
                                            user_only=True)
        assert len(dists) == 1
        assert dists[0].test_name == "user"
246

247 248
    @patch('pip._vendor.pkg_resources.working_set', workingset_stdlib)
    def test_gte_py27_excludes(self, mock_dist_is_editable,
249 250
                               mock_dist_is_local,
                               mock_dist_in_usersite):
251 252
        mock_dist_is_editable.side_effect = self.dist_is_editable
        mock_dist_is_local.side_effect = self.dist_is_local
253
        mock_dist_in_usersite.side_effect = self.dist_in_usersite
254 255 256 257
        dists = get_installed_distributions()
        assert len(dists) == 0

    @patch('pip._vendor.pkg_resources.working_set', workingset_freeze)
258 259 260
    def test_freeze_excludes(self, mock_dist_is_editable,
                             mock_dist_is_local,
                             mock_dist_in_usersite):
261 262
        mock_dist_is_editable.side_effect = self.dist_is_editable
        mock_dist_is_local.side_effect = self.dist_is_local
263
        mock_dist_in_usersite.side_effect = self.dist_in_usersite
264 265
        dists = get_installed_distributions(
            skip=('setuptools', 'pip', 'distribute'))
266 267
        assert len(dists) == 0

268

269 270
class TestUnpackArchives(object):
    """
271 272
    test_tar.tgz/test_tar.zip have content as follows engineered to confirm 3
    things:
273 274 275 276 277 278 279 280 281 282 283
     1) confirm that reg files, dirs, and symlinks get unpacked
     2) permissions are not preserved (and go by the 022 umask)
     3) reg files with *any* execute perms, get chmod +x

       file.txt         600 regular file
       symlink.txt      777 symlink to file.txt
       script_owner.sh  700 script where owner can execute
       script_group.sh  610 script where group can execute
       script_world.sh  601 script where world can execute
       dir              744 directory
       dir/dirfile      622 regular file
284 285
     4) the file contents are extracted correctly (though the content of
        each file isn't currently unique)
286

287
    """
288

289 290 291 292
    def setup(self):
        self.tempdir = tempfile.mkdtemp()
        self.old_mask = os.umask(0o022)
        self.symlink_expected_mode = None
293

294 295 296 297 298 299 300 301
    def teardown(self):
        os.umask(self.old_mask)
        shutil.rmtree(self.tempdir, ignore_errors=True)

    def mode(self, path):
        return stat.S_IMODE(os.stat(path).st_mode)

    def confirm_files(self):
J
Jakub Wilk 已提交
302
        # expectations based on 022 umask set above and the unpack logic that
303
        # sets execute permissions, not preservation
304 305 306 307 308 309 310 311 312 313
        for fname, expected_mode, test, expected_contents in [
                ('file.txt', 0o644, os.path.isfile, b'file\n'),
                # We don't test the "symlink.txt" contents for now.
                ('symlink.txt', 0o644, os.path.isfile, None),
                ('script_owner.sh', 0o755, os.path.isfile, b'file\n'),
                ('script_group.sh', 0o755, os.path.isfile, b'file\n'),
                ('script_world.sh', 0o755, os.path.isfile, b'file\n'),
                ('dir', 0o755, os.path.isdir, None),
                (os.path.join('dir', 'dirfile'), 0o644, os.path.isfile, b''),
        ]:
314 315 316 317 318
            path = os.path.join(self.tempdir, fname)
            if path.endswith('symlink.txt') and sys.platform == 'win32':
                # no symlinks created on windows
                continue
            assert test(path), path
319 320 321 322
            if expected_contents is not None:
                with open(path, mode='rb') as f:
                    contents = f.read()
                assert contents == expected_contents, 'fname: {}'.format(fname)
323 324 325 326 327
            if sys.platform == 'win32':
                # the permissions tests below don't apply in windows
                # due to os.chmod being a noop
                continue
            mode = self.mode(path)
328 329 330
            assert mode == expected_mode, (
                "mode: %s, expected mode: %s" % (mode, expected_mode)
            )
331

332
    def test_unpack_tgz(self, data):
333 334 335
        """
        Test unpacking a *.tgz, and setting execute permissions
        """
336
        test_file = data.packages.join("test_tar.tgz")
337 338
        untar_file(test_file, self.tempdir)
        self.confirm_files()
339 340 341 342
        # Check the timestamp of an extracted file
        file_txt_path = os.path.join(self.tempdir, 'file.txt')
        mtime = time.gmtime(os.stat(file_txt_path).st_mtime)
        assert mtime[0:6] == (2013, 8, 16, 5, 13, 37), mtime
343

344
    def test_unpack_zip(self, data):
345 346 347
        """
        Test unpacking a *.zip, and setting execute permissions
        """
348
        test_file = data.packages.join("test_zip.zip")
349 350
        unzip_file(test_file, self.tempdir)
        self.confirm_files()
351 352 353 354 355 356 357 358 359 360 361 362 363 364


class Failer:
    def __init__(self, duration=1):
        self.succeed_after = time.time() + duration

    def call(self, *args, **kw):
        """Fail with OSError self.max_fails times"""
        if time.time() < self.succeed_after:
            raise OSError("Failed")


def test_rmtree_retries(tmpdir, monkeypatch):
    """
365
    Test pip._internal.utils.rmtree will retry failures
366 367 368 369 370 371 372
    """
    monkeypatch.setattr(shutil, 'rmtree', Failer(duration=1).call)
    rmtree('foo')


def test_rmtree_retries_for_3sec(tmpdir, monkeypatch):
    """
373
    Test pip._internal.utils.rmtree will retry failures for no more than 3 sec
374 375 376 377
    """
    monkeypatch.setattr(shutil, 'rmtree', Failer(duration=5).call)
    with pytest.raises(OSError):
        rmtree('foo')
T
Thomas Kluyver 已提交
378

T
Thomas Kluyver 已提交
379

T
Thomas Kluyver 已提交
380 381 382 383 384
class Test_normalize_path(object):
    # Technically, symlinks are possible on Windows, but you need a special
    # permission bit to create them, and Python 2 doesn't support it anyway, so
    # it's easiest just to skip this test on Windows altogether.
    @pytest.mark.skipif("sys.platform == 'win32'")
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
    def test_resolve_symlinks(self, tmpdir):
        print(type(tmpdir))
        print(dir(tmpdir))
        orig_working_dir = os.getcwd()
        os.chdir(tmpdir)
        try:
            d = os.path.join('foo', 'bar')
            f = os.path.join(d, 'file1')
            os.makedirs(d)
            with open(f, 'w'):  # Create the file
                pass

            os.symlink(d, 'dir_link')
            os.symlink(f, 'file_link')

            assert normalize_path(
                'dir_link/file1', resolve_symlinks=True
            ) == os.path.join(tmpdir, f)
            assert normalize_path(
                'dir_link/file1', resolve_symlinks=False
            ) == os.path.join(tmpdir, 'dir_link', 'file1')

            assert normalize_path(
                'file_link', resolve_symlinks=True
            ) == os.path.join(tmpdir, f)
            assert normalize_path(
                'file_link', resolve_symlinks=False
            ) == os.path.join(tmpdir, 'file_link')
        finally:
            os.chdir(orig_working_dir)
415 416 417


class TestHashes(object):
418
    """Tests for pip._internal.utils.hashes"""
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438

    def test_success(self, tmpdir):
        """Make sure no error is raised when at least one hash matches.

        Test check_against_path because it calls everything else.

        """
        file = tmpdir / 'to_hash'
        file.write('hello')
        hashes = Hashes({
            'sha256': ['2cf24dba5fb0a30e26e83b2ac5b9e29e'
                       '1b161e5c1fa7425e73043362938b9824'],
            'sha224': ['wrongwrong'],
            'md5': ['5d41402abc4b2a76b9719d911017c592']})
        hashes.check_against_path(file)

    def test_failure(self):
        """Hashes should raise HashMismatch when no hashes match."""
        hashes = Hashes({'sha256': ['wrongwrong']})
        with pytest.raises(HashMismatch):
439
            hashes.check_against_file(BytesIO(b'hello'))
440 441 442 443

    def test_missing_hashes(self):
        """MissingHashes should raise HashMissing when any check is done."""
        with pytest.raises(HashMissing):
444
            MissingHashes().check_against_file(BytesIO(b'hello'))
445 446 447 448 449 450

    def test_unknown_hash(self):
        """Hashes should raise InstallationError when it encounters an unknown
        hash."""
        hashes = Hashes({'badbad': ['dummy']})
        with pytest.raises(InstallationError):
451
            hashes.check_against_file(BytesIO(b'hello'))
452 453 454 455 456 457 458

    def test_non_zero(self):
        """Test that truthiness tests tell whether any known-good hashes
        exist."""
        assert Hashes({'sha256': 'dummy'})
        assert not Hashes()
        assert not Hashes({})
459 460 461


class TestEncoding(object):
462
    """Tests for pip._internal.utils.encoding"""
463 464 465 466 467 468 469 470

    def test_auto_decode_utf16_le(self):
        data = (
            b'\xff\xfeD\x00j\x00a\x00n\x00g\x00o\x00=\x00'
            b'=\x001\x00.\x004\x00.\x002\x00'
        )
        assert auto_decode(data) == "Django==1.4.2"

X
Xavier Fernandez 已提交
471 472
    def test_auto_decode_no_bom(self):
        assert auto_decode(b'foobar') == u'foobar'
473 474 475 476

    def test_auto_decode_pep263_headers(self):
        latin1_req = u'# coding=latin1\n# Pas trop de café'
        assert auto_decode(latin1_req.encode('latin1')) == latin1_req
477

478 479 480 481 482 483 484 485 486 487
    def test_auto_decode_no_preferred_encoding(self):
        om, em = Mock(), Mock()
        om.return_value = 'ascii'
        em.return_value = None
        data = u'data'
        with patch('sys.getdefaultencoding', om):
            with patch('locale.getpreferredencoding', em):
                ret = auto_decode(data.encode(sys.getdefaultencoding()))
        assert ret == data

488

489 490
class TestTempDirectory(object):

491 492
    # No need to test symlinked directories on Windows
    @pytest.mark.skipif("sys.platform == 'win32'")
493 494 495 496 497
    def test_symlinked_path(self):
        with TempDirectory() as tmp_dir:
            assert os.path.exists(tmp_dir.path)

            alt_tmp_dir = tempfile.mkdtemp(prefix="pip-test-")
498
            assert (
499 500
                os.path.dirname(tmp_dir.path) ==
                os.path.dirname(os.path.realpath(alt_tmp_dir))
501 502
            )
            # are we on a system where /tmp is a symlink
503 504 505 506 507
            if os.path.realpath(alt_tmp_dir) != os.path.abspath(alt_tmp_dir):
                assert (
                    os.path.dirname(tmp_dir.path) !=
                    os.path.dirname(alt_tmp_dir)
                )
508
            else:
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
                assert (
                    os.path.dirname(tmp_dir.path) ==
                    os.path.dirname(alt_tmp_dir)
                )
            os.rmdir(tmp_dir.path)
            assert not os.path.exists(tmp_dir.path)

    def test_deletes_readonly_files(self):
        def create_file(*args):
            fpath = os.path.join(*args)
            ensure_dir(os.path.dirname(fpath))
            with open(fpath, "w") as f:
                f.write("Holla!")

        def readonly_file(*args):
            fpath = os.path.join(*args)
            os.chmod(fpath, stat.S_IREAD)

        with TempDirectory() as tmp_dir:
            create_file(tmp_dir.path, "normal-file")
            create_file(tmp_dir.path, "readonly-file")
            readonly_file(tmp_dir.path, "readonly-file")

            create_file(tmp_dir.path, "subfolder", "normal-file")
            create_file(tmp_dir.path, "subfolder", "readonly-file")
            readonly_file(tmp_dir.path, "subfolder", "readonly-file")

        assert tmp_dir.path is None

    def test_create_and_cleanup_work(self):
        tmp_dir = TempDirectory()
        assert tmp_dir.path is None

        tmp_dir.create()
        created_path = tmp_dir.path
        assert tmp_dir.path is not None
        assert os.path.exists(created_path)

        tmp_dir.cleanup()
        assert tmp_dir.path is None
        assert not os.path.exists(created_path)
550

S
Steve Dower 已提交
551 552 553 554 555 556
    @pytest.mark.parametrize("name", [
        "ABC",
        "ABC.dist-info",
        "_+-",
        "_package",
    ])
557
    def test_adjacent_directory_names(self, name):
S
Steve Dower 已提交
558 559 560
        def names():
            return AdjacentTempDirectory._generate_names(name)

561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
        chars = AdjacentTempDirectory.LEADING_CHARS

        # Ensure many names are unique
        # (For long *name*, this sequence can be extremely long.
        # However, since we're only ever going to take the first
        # result that works, provided there are many of those
        # and that shorter names result in totally unique sets,
        # it's okay to skip part of the test.)
        some_names = list(itertools.islice(names(), 10000))
        assert len(some_names) == len(set(some_names))

        # Ensure original name does not appear
        assert not any(n == name for n in names())

        # Check the first group are correct
S
Steve Dower 已提交
576 577
        assert all(x == y for x, y in
                   zip(some_names, [c + name[1:] for c in chars]))
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611


class TestGlibc(object):
    def test_manylinux1_check_glibc_version(self):
        """
        Test that the check_glibc_version function is robust against weird
        glibc version strings.
        """
        for two_twenty in ["2.20",
                           # used by "linaro glibc", see gh-3588
                           "2.20-2014.11",
                           # weird possibilities that I just made up
                           "2.20+dev",
                           "2.20-custom",
                           "2.20.1",
                           ]:
            assert check_glibc_version(two_twenty, 2, 15)
            assert check_glibc_version(two_twenty, 2, 20)
            assert not check_glibc_version(two_twenty, 2, 21)
            assert not check_glibc_version(two_twenty, 3, 15)
            assert not check_glibc_version(two_twenty, 1, 15)

        # For strings that we just can't parse at all, we should warn and
        # return false
        for bad_string in ["asdf", "", "foo.bar"]:
            with warnings.catch_warnings(record=True) as ws:
                warnings.filterwarnings("always")
                assert not check_glibc_version(bad_string, 2, 5)
                for w in ws:
                    if "Expected glibc version with" in str(w.message):
                        break
                else:
                    # Didn't find the warning we were expecting
                    assert False
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633


class TestCheckRequiresPython(object):

    @pytest.mark.parametrize(
        ("metadata", "should_raise"),
        [
            ("Name: test\n", False),
            ("Name: test\nRequires-Python:", False),
            ("Name: test\nRequires-Python: invalid_spec", False),
            ("Name: test\nRequires-Python: <=1", True),
        ],
    )
    def test_check_requires(self, metadata, should_raise):
        fake_dist = Mock(
            has_metadata=lambda _: True,
            get_metadata=lambda _: metadata)
        if should_raise:
            with pytest.raises(UnsupportedPythonVersion):
                check_dist_requires_python(fake_dist)
        else:
            check_dist_requires_python(fake_dist)
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653


class TestGetProg(object):

    @pytest.mark.parametrize(
        ("argv", "executable", "expected"),
        [
            ('/usr/bin/pip', '', 'pip'),
            ('-c', '/usr/bin/python', '/usr/bin/python -m pip'),
            ('__main__.py', '/usr/bin/python', '/usr/bin/python -m pip'),
            ('/usr/bin/pip3', '', 'pip3'),
        ]
    )
    def test_get_prog(self, monkeypatch, argv, executable, expected):
        monkeypatch.setattr('pip._internal.utils.misc.sys.argv', [argv])
        monkeypatch.setattr(
            'pip._internal.utils.misc.sys.executable',
            executable
        )
        assert get_prog() == expected
P
Pradyun Gedam 已提交
654 655 656 657 658 659 660 661 662 663 664 665


def test_call_subprocess_works_okay_when_just_given_nothing():
    try:
        call_subprocess([sys.executable, '-c', 'print("Hello")'])
    except Exception:
        assert False, "Expected subprocess call to succeed"


def test_call_subprocess_closes_stdin():
    with pytest.raises(InstallationError):
        call_subprocess([sys.executable, '-c', 'input()'])
666 667


668 669 670 671 672 673 674 675 676 677
@pytest.mark.parametrize('args, expected', [
    # Test without subdir.
    (('git+https://example.com/pkg', 'dev', 'myproj'),
     'git+https://example.com/pkg@dev#egg=myproj'),
    # Test with subdir.
    (('git+https://example.com/pkg', 'dev', 'myproj', 'sub/dir'),
     'git+https://example.com/pkg@dev#egg=myproj&subdirectory=sub/dir'),
    # Test with None subdir.
    (('git+https://example.com/pkg', 'dev', 'myproj', None),
     'git+https://example.com/pkg@dev#egg=myproj'),
678 679 680
    # Test an unescaped project name.
    (('git+https://example.com/pkg', 'dev', 'zope-interface'),
     'git+https://example.com/pkg@dev#egg=zope_interface'),
681 682
])
def test_make_vcs_requirement_url(args, expected):
C
Chris Jerdonek 已提交
683
    actual = make_vcs_requirement_url(*args)
684 685 686
    assert actual == expected


687 688 689 690 691 692 693 694 695 696 697 698 699
@pytest.mark.parametrize('netloc, expected', [
    # Test a basic case.
    ('example.com', ('example.com', (None, None))),
    # Test with username and no password.
    ('user@example.com', ('example.com', ('user', None))),
    # Test with username and password.
    ('user:pass@example.com', ('example.com', ('user', 'pass'))),
    # Test with username and empty password.
    ('user:@example.com', ('example.com', ('user', ''))),
    # Test the password containing an @ symbol.
    ('user:pass@word@example.com', ('example.com', ('user', 'pass@word'))),
    # Test the password containing a : symbol.
    ('user:pass:word@example.com', ('example.com', ('user', 'pass:word'))),
C
Chris Jerdonek 已提交
700 701 702
    # Test URL-encoded reserved characters.
    ('user%3Aname:%23%40%5E@example.com',
     ('example.com', ('user:name', '#@^'))),
703 704 705 706 707 708
])
def test_split_auth_from_netloc(netloc, expected):
    actual = split_auth_from_netloc(netloc)
    assert actual == expected


709 710 711 712 713 714 715 716 717 718 719 720 721
@pytest.mark.parametrize('netloc, expected', [
    # Test a basic case.
    ('example.com', 'example.com'),
    # Test with username and no password.
    ('user@example.com', 'user@example.com'),
    # Test with username and password.
    ('user:pass@example.com', 'user:****@example.com'),
    # Test with username and empty password.
    ('user:@example.com', 'user:****@example.com'),
    # Test the password containing an @ symbol.
    ('user:pass@word@example.com', 'user:****@example.com'),
    # Test the password containing a : symbol.
    ('user:pass:word@example.com', 'user:****@example.com'),
722 723
    # Test URL-encoded reserved characters.
    ('user%3Aname:%23%40%5E@example.com', 'user%3Aname:****@example.com'),
724 725 726 727 728 729
])
def test_redact_netloc(netloc, expected):
    actual = redact_netloc(netloc)
    assert actual == expected


730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
@pytest.mark.parametrize('auth_url, expected_url', [
    ('https://user:pass@domain.tld/project/tags/v0.2',
     'https://domain.tld/project/tags/v0.2'),
    ('https://domain.tld/project/tags/v0.2',
     'https://domain.tld/project/tags/v0.2',),
    ('https://user:pass@domain.tld/svn/project/trunk@8181',
     'https://domain.tld/svn/project/trunk@8181'),
    ('https://domain.tld/project/trunk@8181',
     'https://domain.tld/project/trunk@8181',),
    ('git+https://pypi.org/something',
     'git+https://pypi.org/something'),
    ('git+https://user:pass@pypi.org/something',
     'git+https://pypi.org/something'),
    ('git+ssh://git@pypi.org/something',
     'git+ssh://pypi.org/something'),
])
def test_remove_auth_from_url(auth_url, expected_url):
    url = remove_auth_from_url(auth_url)
748
    assert url == expected_url
749 750 751 752 753 754


@pytest.mark.parametrize('auth_url, expected_url', [
    ('https://user@example.com/abc', 'https://user@example.com/abc'),
    ('https://user:password@example.com', 'https://user:****@example.com'),
    ('https://user:@example.com', 'https://user:****@example.com'),
755 756 757 758
    ('https://example.com', 'https://example.com'),
    # Test URL-encoded reserved characters.
    ('https://user%3Aname:%23%40%5E@example.com',
     'https://user%3Aname:****@example.com'),
759 760 761 762
])
def test_redact_password_from_url(auth_url, expected_url):
    url = redact_password_from_url(auth_url)
    assert url == expected_url