forked from openkylin/python-installer
255 lines
7.3 KiB
Python
255 lines
7.3 KiB
Python
"""Tests for installer.utils
|
|
"""
|
|
|
|
import base64
|
|
import hashlib
|
|
import textwrap
|
|
from email.message import Message
|
|
from io import BytesIO
|
|
|
|
import pytest
|
|
from test_records import SAMPLE_RECORDS
|
|
|
|
from installer.records import RecordEntry
|
|
from installer.utils import (
|
|
WheelFilename,
|
|
construct_record_file,
|
|
copyfileobj_with_hashing,
|
|
fix_shebang,
|
|
canonicalize_name,
|
|
parse_entrypoints,
|
|
parse_metadata_file,
|
|
parse_wheel_filename,
|
|
)
|
|
|
|
|
|
class TestParseMetadata:
|
|
def test_basics(self):
|
|
result = parse_metadata_file(
|
|
textwrap.dedent(
|
|
"""\
|
|
Name: package
|
|
Version: 1.0.0
|
|
Multi-Use-Field: 1
|
|
Multi-Use-Field: 2
|
|
Multi-Use-Field: 3
|
|
"""
|
|
)
|
|
)
|
|
assert isinstance(result, Message)
|
|
assert result.get("Name") == "package"
|
|
assert result.get("version") == "1.0.0"
|
|
assert result.get_all("MULTI-USE-FIELD") == ["1", "2", "3"]
|
|
|
|
|
|
class TestCanonicalizeDistributionName:
|
|
@pytest.mark.parametrize(
|
|
"string, expected",
|
|
[
|
|
# Noop
|
|
(
|
|
"package-1",
|
|
"package-1",
|
|
),
|
|
# PEP 508 canonicalization
|
|
(
|
|
"ABC..12",
|
|
"abc-12",
|
|
),
|
|
],
|
|
)
|
|
def test_valid_cases(self, string, expected):
|
|
got = canonicalize_name(string)
|
|
assert expected == got, (expected, got)
|
|
|
|
|
|
class TestParseWheelFilename:
|
|
@pytest.mark.parametrize(
|
|
"string, expected",
|
|
[
|
|
# Crafted package name w/ a "complex" version and build tag
|
|
(
|
|
"package-1!1.0+abc.7-753-py3-none-any.whl",
|
|
WheelFilename("package", "1!1.0+abc.7", "753", "py3-none-any"),
|
|
),
|
|
# Crafted package name w/ a "complex" version and no build tag
|
|
(
|
|
"package-1!1.0+abc.7-py3-none-any.whl",
|
|
WheelFilename("package", "1!1.0+abc.7", None, "py3-none-any"),
|
|
),
|
|
# Use real tensorflow wheel names
|
|
(
|
|
"tensorflow-2.3.0-cp38-cp38-macosx_10_11_x86_64.whl",
|
|
WheelFilename(
|
|
"tensorflow", "2.3.0", None, "cp38-cp38-macosx_10_11_x86_64"
|
|
),
|
|
),
|
|
(
|
|
"tensorflow-2.3.0-cp38-cp38-manylinux2010_x86_64.whl",
|
|
WheelFilename(
|
|
"tensorflow", "2.3.0", None, "cp38-cp38-manylinux2010_x86_64"
|
|
),
|
|
),
|
|
(
|
|
"tensorflow-2.3.0-cp38-cp38-win_amd64.whl",
|
|
WheelFilename("tensorflow", "2.3.0", None, "cp38-cp38-win_amd64"),
|
|
),
|
|
],
|
|
)
|
|
def test_valid_cases(self, string, expected):
|
|
got = parse_wheel_filename(string)
|
|
assert expected == got, (expected, got)
|
|
|
|
@pytest.mark.parametrize(
|
|
"string",
|
|
[
|
|
# Not ".whl"
|
|
"pip-20.0.0-py2.py3-none-any.zip",
|
|
# No tag
|
|
"pip-20.0.0.whl",
|
|
# Empty tag
|
|
"pip-20.0.0---.whl",
|
|
],
|
|
)
|
|
def test_invalid_cases(self, string):
|
|
with pytest.raises(ValueError):
|
|
parse_wheel_filename(string)
|
|
|
|
|
|
class TestCopyFileObjWithHashing:
|
|
def test_basic_functionality(self):
|
|
data = b"input data is this"
|
|
hash_ = (
|
|
base64.urlsafe_b64encode(hashlib.sha256(data).digest())
|
|
.decode("ascii")
|
|
.rstrip("=")
|
|
)
|
|
size = len(data)
|
|
|
|
with BytesIO(data) as source:
|
|
with BytesIO() as dest:
|
|
result = copyfileobj_with_hashing(source, dest, hash_algorithm="sha256")
|
|
written_data = dest.getvalue()
|
|
|
|
assert result == (hash_, size)
|
|
assert written_data == data
|
|
|
|
|
|
class TestScript:
|
|
@pytest.mark.parametrize(
|
|
("data", "expected"),
|
|
[
|
|
pytest.param(
|
|
b"#!python\ntest",
|
|
b"#!/my/python\ntest",
|
|
id="python",
|
|
),
|
|
pytest.param(
|
|
b"#!pythonw\ntest",
|
|
b"#!/my/python\ntest",
|
|
id="pythonw",
|
|
),
|
|
pytest.param(
|
|
b"#!python something\ntest",
|
|
b"#!/my/python\ntest",
|
|
id="python-with-args",
|
|
),
|
|
pytest.param(
|
|
b"#!python",
|
|
b"#!/my/python\n",
|
|
id="python-no-content",
|
|
),
|
|
],
|
|
)
|
|
def test_replace_shebang(self, data, expected):
|
|
with BytesIO(data) as source:
|
|
with fix_shebang(source, "/my/python") as stream:
|
|
result = stream.read()
|
|
assert result == expected
|
|
|
|
@pytest.mark.parametrize(
|
|
"data",
|
|
[
|
|
b"#!py\ntest",
|
|
b"#!something\ntest",
|
|
b"#something\ntest",
|
|
b"#something",
|
|
b"something",
|
|
],
|
|
)
|
|
def test_keep_data(self, data):
|
|
with BytesIO(data) as source:
|
|
with fix_shebang(source, "/my/python") as stream:
|
|
result = stream.read()
|
|
assert result == data
|
|
|
|
|
|
class TestConstructRecord:
|
|
def test_construct(self):
|
|
records = [
|
|
(scheme, RecordEntry.from_elements(*elements))
|
|
for scheme, elements, _, _ in SAMPLE_RECORDS
|
|
]
|
|
assert construct_record_file(records).read() == (
|
|
b"test1.py,sha256=Y0sCextp4SQtQNU-MSs7SsdxD1W-gfKJtUlEbvZ3i-4,6\n"
|
|
b"test2.py,sha256=fW_Xd08Nh2JNptzxbQ09EEwxkedx--LznIau1LK_Gg8,6\n"
|
|
b"test3.py,sha256=qwPDTx7OCCEf4qgDn9ZCQZmz9de1X_E7ETSzZHdsRcU,6\n"
|
|
b"test4.py,sha256=Y0sCextp4SQtQNU-MSs7SsdxD1W-gfKJtUlEbvZ3i-4,7\n"
|
|
b"test5.py,sha256=Y0sCextp4SQtQNU-MSs7SsdxD1W-gfKJtUlEbvZ3i-4,\n"
|
|
b"test6.py,,\n"
|
|
)
|
|
|
|
|
|
class TestParseEntryPoints:
|
|
@pytest.mark.parametrize(
|
|
("script", "expected"),
|
|
[
|
|
pytest.param("", [], id="empty"),
|
|
pytest.param(
|
|
"""
|
|
[foo]
|
|
foo = foo.bar
|
|
""",
|
|
[],
|
|
id="unrelated",
|
|
),
|
|
pytest.param(
|
|
"""
|
|
[console_scripts]
|
|
package = package.__main__:package
|
|
""",
|
|
[
|
|
("package", "package.__main__", "package", "console"),
|
|
],
|
|
id="cli",
|
|
),
|
|
pytest.param(
|
|
"""
|
|
[gui_scripts]
|
|
package = package.__main__:package
|
|
""",
|
|
[
|
|
("package", "package.__main__", "package", "gui"),
|
|
],
|
|
id="gui",
|
|
),
|
|
pytest.param(
|
|
"""
|
|
[console_scripts]
|
|
magic-cli = magic.cli:main
|
|
|
|
[gui_scripts]
|
|
magic-gui = magic.gui:main
|
|
""",
|
|
[
|
|
("magic-cli", "magic.cli", "main", "console"),
|
|
("magic-gui", "magic.gui", "main", "gui"),
|
|
],
|
|
id="cli-and-gui",
|
|
),
|
|
],
|
|
)
|
|
def test_valid(self, script, expected):
|
|
iterable = parse_entrypoints(textwrap.dedent(script))
|
|
assert list(iterable) == expected, expected
|