2800b49
import pytest
2800b49
import yaml
2800b49
2800b49
from pathlib import Path
2800b49
from pprint import pprint
2800b49
2800b49
from pyproject_save_files import argparser, generate_file_list, main
2800b49
from pyproject_save_files import locate_record, parse_record, read_record
2800b49
from pyproject_save_files import BuildrootPath
2800b49
2800b49
2800b49
DIR = Path(__file__).parent
2800b49
BINDIR = BuildrootPath("/usr/bin")
2800b49
SITELIB = BuildrootPath("/usr/lib/python3.7/site-packages")
2800b49
SITEARCH = BuildrootPath("/usr/lib64/python3.7/site-packages")
2800b49
2800b49
yaml_file = DIR / "pyproject_save_files_test_data.yaml"
2800b49
yaml_data = yaml.safe_load(yaml_file.read_text())
2800b49
EXPECTED_DICT = yaml_data["classified"]
2800b49
EXPECTED_FILES = yaml_data["dumped"]
2800b49
TEST_RECORDS = yaml_data["records"]
2800b49
2800b49
2800b49
def create_root(tmp_path, *records):
2800b49
    r"""
2800b49
    Create mock buildroot in tmp_path
2800b49
2800b49
    parameters:
2800b49
    tmp_path: path where buildroot should be created
2800b49
    records: dicts with:
2800b49
      path: expected path found in buildroot
2800b49
      content: string content of the file
2800b49
2800b49
    Example:
2800b49
2800b49
        >>> record = {'path': '/usr/lib/python/tldr-0.5.dist-info/RECORD', 'content': '__pycache__/tldr.cpython-37.pyc,,\n...'}
2800b49
        >>> create_root(Path('tmp'), record)
2800b49
        PosixPath('tmp/buildroot')
2800b49
2800b49
    The example creates ./tmp/buildroot/usr/lib/python/tldr-0.5.dist-info/RECORD with the content.
2800b49
2800b49
        >>> import shutil
2800b49
        >>> shutil.rmtree(Path('./tmp'))
2800b49
    """
2800b49
    buildroot = tmp_path / "buildroot"
2800b49
    for record in records:
2800b49
        dest = buildroot / Path(record["path"]).relative_to("/")
2800b49
        dest.parent.mkdir(parents=True)
2800b49
        dest.write_text(record["content"])
2800b49
    return buildroot
2800b49
2800b49
2800b49
@pytest.fixture
2800b49
def tldr_root(tmp_path):
2800b49
    return create_root(tmp_path, TEST_RECORDS["tldr"])
2800b49
2800b49
2800b49
@pytest.fixture
2800b49
def output(tmp_path):
2800b49
    return tmp_path / "pyproject_files"
2800b49
2800b49
2800b49
def test_locate_record_good(tmp_path):
2800b49
    sitedir = tmp_path / "ha/ha/ha/site-packages"
2800b49
    distinfo = sitedir / "foo-0.6.dist-info"
2800b49
    distinfo.mkdir(parents=True)
2800b49
    record = distinfo / "RECORD"
2800b49
    record.write_text("\n")
2800b49
    sitedir = BuildrootPath.from_real(sitedir, root=tmp_path)
2800b49
    assert locate_record(tmp_path, {sitedir}) == record
2800b49
2800b49
2800b49
def test_locate_record_missing(tmp_path):
2800b49
    sitedir = tmp_path / "ha/ha/ha/site-packages"
2800b49
    distinfo = sitedir / "foo-0.6.dist-info"
2800b49
    distinfo.mkdir(parents=True)
2800b49
    sitedir = BuildrootPath.from_real(sitedir, root=tmp_path)
2800b49
    with pytest.raises(FileNotFoundError):
2800b49
        locate_record(tmp_path, {sitedir})
2800b49
2800b49
2800b49
def test_locate_record_misplaced(tmp_path):
2800b49
    sitedir = tmp_path / "ha/ha/ha/site-packages"
2800b49
    fakedir = tmp_path / "no/no/no/site-packages"
2800b49
    distinfo = fakedir / "foo-0.6.dist-info"
2800b49
    distinfo.mkdir(parents=True)
2800b49
    record = distinfo / "RECORD"
2800b49
    record.write_text("\n")
2800b49
    sitedir = BuildrootPath.from_real(sitedir, root=tmp_path)
2800b49
    with pytest.raises(FileNotFoundError):
2800b49
        locate_record(tmp_path, {sitedir})
2800b49
2800b49
2800b49
def test_locate_record_two_packages(tmp_path):
2800b49
    sitedir = tmp_path / "ha/ha/ha/site-packages"
2800b49
    for name in "foo-0.6.dist-info", "bar-1.8.dist-info":
2800b49
        distinfo = sitedir / name
2800b49
        distinfo.mkdir(parents=True)
2800b49
        record = distinfo / "RECORD"
2800b49
        record.write_text("\n")
2800b49
    sitedir = BuildrootPath.from_real(sitedir, root=tmp_path)
2800b49
    with pytest.raises(FileExistsError):
2800b49
        locate_record(tmp_path, {sitedir})
2800b49
2800b49
2800b49
def test_locate_record_two_sitedirs(tmp_path):
2800b49
    sitedirs = ["ha/ha/ha/site-packages", "ha/ha/ha64/site-packages"]
2800b49
    for idx, sitedir in enumerate(sitedirs):
2800b49
        sitedir = tmp_path / sitedir
2800b49
        distinfo = sitedir / "foo-0.6.dist-info"
2800b49
        distinfo.mkdir(parents=True)
2800b49
        record = distinfo / "RECORD"
2800b49
        record.write_text("\n")
2800b49
        sitedirs[idx] = BuildrootPath.from_real(sitedir, root=tmp_path)
2800b49
    with pytest.raises(FileExistsError):
2800b49
        locate_record(tmp_path, set(sitedirs))
2800b49
2800b49
2800b49
def test_parse_record_tldr():
2800b49
    record_path = BuildrootPath(TEST_RECORDS["tldr"]["path"])
2800b49
    record_content = read_record(DIR / "test_RECORD")
2800b49
    output = list(parse_record(record_path, record_content))
2800b49
    pprint(output)
2800b49
    expected = [
2800b49
        BINDIR / "__pycache__/tldr.cpython-37.pyc",
2800b49
        BINDIR / "tldr",
2800b49
        BINDIR / "tldr.py",
2800b49
        SITELIB / "__pycache__/tldr.cpython-37.pyc",
2800b49
        SITELIB / "tldr-0.5.dist-info/INSTALLER",
2800b49
        SITELIB / "tldr-0.5.dist-info/LICENSE",
2800b49
        SITELIB / "tldr-0.5.dist-info/METADATA",
2800b49
        SITELIB / "tldr-0.5.dist-info/RECORD",
2800b49
        SITELIB / "tldr-0.5.dist-info/WHEEL",
2800b49
        SITELIB / "tldr-0.5.dist-info/top_level.txt",
2800b49
        SITELIB / "tldr.py",
2800b49
    ]
2800b49
    assert output == expected
2800b49
2800b49
2800b49
def test_parse_record_tensorflow():
2800b49
    long = "tensorflow_core/include/tensorflow/core/common_runtime/base_collective_executor.h"
2800b49
    record_path = SITEARCH / "tensorflow-2.1.0.dist-info/RECORD"
2800b49
    record_content = [
2800b49
        ["../../../bin/toco_from_protos", "sha256=hello", "289"],
2800b49
        [f"../../../lib/python3.7/site-packages/{long}", "sha256=darkness", "1024"],
2800b49
        ["tensorflow-2.1.0.dist-info/METADATA", "sha256=friend", "2859"],
2800b49
    ]
2800b49
    output = list(parse_record(record_path, record_content))
2800b49
    pprint(output)
2800b49
    expected = [
2800b49
        BINDIR / "toco_from_protos",
2800b49
        SITELIB / long,
2800b49
        SITEARCH / "tensorflow-2.1.0.dist-info/METADATA",
2800b49
    ]
2800b49
    assert output == expected
2800b49
2800b49
2800b49
def remove_executables(expected):
2800b49
    return [p for p in expected if not p.startswith(str(BINDIR))]
2800b49
2800b49
2800b49
@pytest.mark.parametrize("include_executables", (True, False))
2800b49
@pytest.mark.parametrize("package, glob, expected", EXPECTED_FILES)
2800b49
def test_generate_file_list(package, glob, expected, include_executables):
2800b49
    paths_dict = EXPECTED_DICT[package]
2800b49
    modules_glob = {glob}
2800b49
    if not include_executables:
2800b49
        expected = remove_executables(expected)
2800b49
    tested = generate_file_list(paths_dict, modules_glob, include_executables)
2800b49
2800b49
    assert tested == expected
2800b49
2800b49
2800b49
def test_generate_file_list_unused_glob():
2800b49
    paths_dict = EXPECTED_DICT["kerberos"]
2800b49
    modules_glob = {"kerberos", "unused_glob1", "unused_glob2", "kerb*"}
2800b49
    with pytest.raises(ValueError) as excinfo:
2800b49
        generate_file_list(paths_dict, modules_glob, True)
2800b49
2800b49
    assert "unused_glob1, unused_glob2" in str(excinfo.value)
2800b49
    assert "kerb" not in str(excinfo.value)
2800b49
2800b49
2800b49
def default_options(output, mock_root):
2800b49
    return [
2800b49
        "--output",
2800b49
        str(output),
2800b49
        "--buildroot",
2800b49
        str(mock_root),
2800b49
        "--sitelib",
2800b49
        str(SITELIB),
2800b49
        "--sitearch",
2800b49
        str(SITEARCH),
2800b49
        "--bindir",
2800b49
        str(BINDIR),
2800b49
        "--python-version",
2800b49
        "3.7",  # test data are for 3.7
2800b49
    ]
2800b49
2800b49
2800b49
@pytest.mark.parametrize("include_executables", (True, False))
2800b49
@pytest.mark.parametrize("package, glob, expected", EXPECTED_FILES)
2800b49
def test_cli(tmp_path, package, glob, expected, include_executables):
2800b49
    mock_root = create_root(tmp_path, TEST_RECORDS[package])
2800b49
    output = tmp_path / "files"
2800b49
    globs = [glob, "+bindir"] if include_executables else [glob]
2800b49
    cli_args = argparser().parse_args([*default_options(output, mock_root), *globs])
2800b49
    main(cli_args)
2800b49
2800b49
    if not include_executables:
2800b49
        expected = remove_executables(expected)
2800b49
    tested = output.read_text()
2800b49
    assert tested == "\n".join(expected) + "\n"
2800b49
2800b49
2800b49
def test_cli_no_RECORD(tmp_path):
2800b49
    mock_root = create_root(tmp_path)
2800b49
    output = tmp_path / "files"
2800b49
    cli_args = argparser().parse_args([*default_options(output, mock_root), "tldr*"])
2800b49
2800b49
    with pytest.raises(FileNotFoundError):
2800b49
        main(cli_args)
2800b49
2800b49
2800b49
def test_cli_misplaced_RECORD(tmp_path, output):
2800b49
    record = {"path": "/usr/lib/", "content": TEST_RECORDS["tldr"]["content"]}
2800b49
    mock_root = create_root(tmp_path, record)
2800b49
    cli_args = argparser().parse_args([*default_options(output, mock_root), "tldr*"])
2800b49
2800b49
    with pytest.raises(FileNotFoundError):
2800b49
        main(cli_args)
2800b49
2800b49
2800b49
def test_cli_find_too_many_RECORDS(tldr_root, output):
2800b49
    mock_root = create_root(tldr_root.parent, TEST_RECORDS["tensorflow"])
2800b49
    cli_args = argparser().parse_args([*default_options(output, mock_root), "tldr*"])
2800b49
2800b49
    with pytest.raises(FileExistsError):
2800b49
        main(cli_args)
2800b49
2800b49
2800b49
def test_cli_bad_argument(tldr_root, output):
2800b49
    cli_args = argparser().parse_args(
2800b49
        [*default_options(output, tldr_root), "tldr*", "+foodir"]
2800b49
    )
2800b49
2800b49
    with pytest.raises(ValueError):
2800b49
        main(cli_args)
2800b49
2800b49
2800b49
def test_cli_bad_option(tldr_root, output):
2800b49
    cli_args = argparser().parse_args(
2800b49
        [*default_options(output, tldr_root), "tldr*", "you_cannot_have_this"]
2800b49
    )
2800b49
2800b49
    with pytest.raises(ValueError):
2800b49
        main(cli_args)
2800b49
2800b49
2800b49
def test_cli_bad_namespace(tldr_root, output):
2800b49
    cli_args = argparser().parse_args(
2800b49
        [*default_options(output, tldr_root), "tldr.didntread"]
2800b49
    )
2800b49
2800b49
    with pytest.raises(ValueError):
2800b49
        main(cli_args)