diff options
Diffstat (limited to 'tests/mlia/test_tools_metadata_corstone.py')
-rw-r--r-- | tests/mlia/test_tools_metadata_corstone.py | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/tests/mlia/test_tools_metadata_corstone.py b/tests/mlia/test_tools_metadata_corstone.py new file mode 100644 index 0000000..2ce3610 --- /dev/null +++ b/tests/mlia/test_tools_metadata_corstone.py @@ -0,0 +1,419 @@ +# SPDX-FileCopyrightText: Copyright 2022, Arm Limited and/or its affiliates. +# SPDX-License-Identifier: Apache-2.0 +"""Tests for Corstone related installation functions..""" +import tarfile +from pathlib import Path +from typing import List +from typing import Optional +from unittest.mock import MagicMock + +import pytest + +from mlia.tools.aiet_wrapper import AIETRunner +from mlia.tools.metadata.common import DownloadAndInstall +from mlia.tools.metadata.common import InstallFromPath +from mlia.tools.metadata.corstone import AIETBasedInstallation +from mlia.tools.metadata.corstone import AIETMetadata +from mlia.tools.metadata.corstone import BackendInfo +from mlia.tools.metadata.corstone import BackendInstaller +from mlia.tools.metadata.corstone import CompoundPathChecker +from mlia.tools.metadata.corstone import Corstone300Installer +from mlia.tools.metadata.corstone import get_corstone_installations +from mlia.tools.metadata.corstone import PackagePathChecker +from mlia.tools.metadata.corstone import PathChecker +from mlia.tools.metadata.corstone import StaticPathChecker + + +@pytest.fixture(name="test_mlia_resources") +def fixture_test_mlia_resources( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> Path: + """Redirect MLIA resources resolution to the temp directory.""" + mlia_resources = tmp_path / "resources" + mlia_resources.mkdir() + + monkeypatch.setattr( + "mlia.tools.metadata.corstone.get_mlia_resources", + MagicMock(return_value=mlia_resources), + ) + + return mlia_resources + + +def get_aiet_based_installation( # pylint: disable=too-many-arguments + aiet_runner_mock: MagicMock = MagicMock(), + name: str = "test_name", + description: str = "test_description", + download_artifact: Optional[MagicMock] = None, + path_checker: PathChecker = MagicMock(), + apps_resources: Optional[List[str]] = None, + system_config: Optional[str] = None, + backend_installer: BackendInstaller = MagicMock(), + supported_platforms: Optional[List[str]] = None, +) -> AIETBasedInstallation: + """Get AIET based installation.""" + return AIETBasedInstallation( + aiet_runner=aiet_runner_mock, + metadata=AIETMetadata( + name=name, + description=description, + system_config=system_config or "", + apps_resources=apps_resources or [], + fvp_dir_name="sample_dir", + download_artifact=download_artifact, + supported_platforms=supported_platforms, + ), + path_checker=path_checker, + backend_installer=backend_installer, + ) + + +@pytest.mark.parametrize( + "platform, supported_platforms, expected_result", + [ + ["Linux", ["Linux"], True], + ["Linux", [], True], + ["Linux", None, True], + ["Windows", ["Linux"], False], + ], +) +def test_could_be_installed_depends_on_platform( + platform: str, + supported_platforms: Optional[List[str]], + expected_result: bool, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test that installation could not be installed on unsupported platform.""" + monkeypatch.setattr( + "mlia.tools.metadata.corstone.platform.system", MagicMock(return_value=platform) + ) + monkeypatch.setattr( + "mlia.tools.metadata.corstone.all_paths_valid", MagicMock(return_value=True) + ) + aiet_runner_mock = MagicMock(spec=AIETRunner) + + installation = get_aiet_based_installation( + aiet_runner_mock, + supported_platforms=supported_platforms, + ) + assert installation.could_be_installed == expected_result + + +def test_get_corstone_installations() -> None: + """Test function get_corstone_installation.""" + installs = get_corstone_installations() + assert len(installs) == 2 + assert all(isinstance(install, AIETBasedInstallation) for install in installs) + + +def test_aiet_based_installation_metadata_resolving() -> None: + """Test AIET based installation metadata resolving.""" + aiet_runner_mock = MagicMock(spec=AIETRunner) + installation = get_aiet_based_installation(aiet_runner_mock) + + assert installation.name == "test_name" + assert installation.description == "test_description" + + aiet_runner_mock.all_installed.return_value = False + assert installation.already_installed is False + + assert installation.could_be_installed is True + + +def test_aiet_based_installation_supported_install_types(tmp_path: Path) -> None: + """Test supported installation types.""" + installation_no_download_artifact = get_aiet_based_installation() + assert installation_no_download_artifact.supports(DownloadAndInstall()) is False + + installation_with_download_artifact = get_aiet_based_installation( + download_artifact=MagicMock() + ) + assert installation_with_download_artifact.supports(DownloadAndInstall()) is True + + path_checker_mock = MagicMock(return_value=BackendInfo(tmp_path)) + installation_can_install_from_dir = get_aiet_based_installation( + path_checker=path_checker_mock + ) + assert installation_can_install_from_dir.supports(InstallFromPath(tmp_path)) is True + + any_installation = get_aiet_based_installation() + assert any_installation.supports("unknown_install_type") is False # type: ignore + + +def test_aiet_based_installation_install_wrong_type() -> None: + """Test that operation should fail if wrong install type provided.""" + with pytest.raises(Exception, match="Unable to install wrong_install_type"): + aiet_runner_mock = MagicMock(spec=AIETRunner) + installation = get_aiet_based_installation(aiet_runner_mock) + + installation.install("wrong_install_type") # type: ignore + + +def test_aiet_based_installation_install_from_path( + tmp_path: Path, test_mlia_resources: Path +) -> None: + """Test installation from the path.""" + system_config = test_mlia_resources / "example_config.json" + system_config.touch() + + sample_app = test_mlia_resources / "sample_app" + sample_app.mkdir() + + dist_dir = tmp_path / "dist" + dist_dir.mkdir() + + path_checker_mock = MagicMock(return_value=BackendInfo(dist_dir)) + + aiet_runner_mock = MagicMock(spec=AIETRunner) + installation = get_aiet_based_installation( + aiet_runner_mock=aiet_runner_mock, + path_checker=path_checker_mock, + apps_resources=[sample_app.name], + system_config="example_config.json", + ) + + assert installation.supports(InstallFromPath(dist_dir)) is True + installation.install(InstallFromPath(dist_dir)) + + aiet_runner_mock.install_system.assert_called_once() + aiet_runner_mock.install_application.assert_called_once_with(sample_app) + + +@pytest.mark.parametrize("copy_source", [True, False]) +def test_aiet_based_installation_install_from_static_path( + tmp_path: Path, test_mlia_resources: Path, copy_source: bool +) -> None: + """Test installation from the predefined path.""" + system_config = test_mlia_resources / "example_config.json" + system_config.touch() + + custom_system_config = test_mlia_resources / "custom_config.json" + custom_system_config.touch() + + sample_app = test_mlia_resources / "sample_app" + sample_app.mkdir() + + predefined_location = tmp_path / "backend" + predefined_location.mkdir() + + predefined_location_file = predefined_location / "file.txt" + predefined_location_file.touch() + + predefined_location_dir = predefined_location / "folder" + predefined_location_dir.mkdir() + nested_file = predefined_location_dir / "nested_file.txt" + nested_file.touch() + + aiet_runner_mock = MagicMock(spec=AIETRunner) + + def check_install_dir(install_dir: Path) -> None: + """Check content of the install dir.""" + assert install_dir.is_dir() + files = list(install_dir.iterdir()) + + if copy_source: + assert len(files) == 3 + assert all(install_dir / item in files for item in ["file.txt", "folder"]) + assert (install_dir / "folder/nested_file.txt").is_file() + else: + assert len(files) == 1 + + assert install_dir / "custom_config.json" in files + + aiet_runner_mock.install_system.side_effect = check_install_dir + + installation = get_aiet_based_installation( + aiet_runner_mock=aiet_runner_mock, + path_checker=StaticPathChecker( + predefined_location, + ["file.txt"], + copy_source=copy_source, + system_config=str(custom_system_config), + ), + apps_resources=[sample_app.name], + system_config="example_config.json", + ) + + assert installation.supports(InstallFromPath(predefined_location)) is True + installation.install(InstallFromPath(predefined_location)) + + aiet_runner_mock.install_system.assert_called_once() + aiet_runner_mock.install_application.assert_called_once_with(sample_app) + + +def create_sample_fvp_archive(tmp_path: Path) -> Path: + """Create sample FVP tar archive.""" + fvp_archive_dir = tmp_path / "archive" + fvp_archive_dir.mkdir() + + sample_file = fvp_archive_dir / "sample.txt" + sample_file.write_text("Sample file") + + sample_dir = fvp_archive_dir / "sample_dir" + sample_dir.mkdir() + + fvp_archive = tmp_path / "archive.tgz" + with tarfile.open(fvp_archive, "w:gz") as fvp_archive_tar: + fvp_archive_tar.add(fvp_archive_dir, arcname=fvp_archive_dir.name) + + return fvp_archive + + +def test_aiet_based_installation_download_and_install( + test_mlia_resources: Path, tmp_path: Path +) -> None: + """Test downloading and installation process.""" + fvp_archive = create_sample_fvp_archive(tmp_path) + + system_config = test_mlia_resources / "example_config.json" + system_config.touch() + + download_artifact_mock = MagicMock() + download_artifact_mock.download_to.return_value = fvp_archive + + path_checker = PackagePathChecker(["archive/sample.txt"], "archive/sample_dir") + + def installer(_eula_agreement: bool, dist_dir: Path) -> Path: + """Sample installer.""" + return dist_dir + + aiet_runner_mock = MagicMock(spec=AIETRunner) + installation = get_aiet_based_installation( + aiet_runner_mock, + download_artifact=download_artifact_mock, + backend_installer=installer, + path_checker=path_checker, + system_config="example_config.json", + ) + + installation.install(DownloadAndInstall()) + + aiet_runner_mock.install_system.assert_called_once() + + +@pytest.mark.parametrize( + "dir_content, expected_result", + [ + [ + ["models/", "file1.txt", "file2.txt"], + "models", + ], + [ + ["file1.txt", "file2.txt"], + None, + ], + [ + ["models/", "file2.txt"], + None, + ], + ], +) +def test_corstone_path_checker_valid_path( + tmp_path: Path, dir_content: List[str], expected_result: Optional[str] +) -> None: + """Test Corstone path checker valid scenario.""" + path_checker = PackagePathChecker(["file1.txt", "file2.txt"], "models") + + for item in dir_content: + if item.endswith("/"): + item_dir = tmp_path / item + item_dir.mkdir() + else: + item_file = tmp_path / item + item_file.touch() + + result = path_checker(tmp_path) + expected = ( + None if expected_result is None else BackendInfo(tmp_path / expected_result) + ) + + assert result == expected + + +@pytest.mark.parametrize("system_config", [None, "system_config"]) +@pytest.mark.parametrize("copy_source", [True, False]) +def test_static_path_checker( + tmp_path: Path, copy_source: bool, system_config: Optional[str] +) -> None: + """Test static path checker.""" + static_checker = StaticPathChecker( + tmp_path, [], copy_source=copy_source, system_config=system_config + ) + assert static_checker(tmp_path) == BackendInfo( + tmp_path, copy_source=copy_source, system_config=system_config + ) + + +def test_static_path_checker_not_valid_path(tmp_path: Path) -> None: + """Test static path checker should return None if path is not valid.""" + static_checker = StaticPathChecker(tmp_path, ["file.txt"]) + assert static_checker(tmp_path / "backend") is None + + +def test_static_path_checker_not_valid_structure(tmp_path: Path) -> None: + """Test static path checker should return None if files are missing.""" + static_checker = StaticPathChecker(tmp_path, ["file.txt"]) + assert static_checker(tmp_path) is None + + missing_file = tmp_path / "file.txt" + missing_file.touch() + + assert static_checker(tmp_path) == BackendInfo(tmp_path, copy_source=False) + + +def test_compound_path_checker(tmp_path: Path) -> None: + """Test compound path checker.""" + path_checker_path_valid_path = MagicMock(return_value=BackendInfo(tmp_path)) + path_checker_path_not_valid_path = MagicMock(return_value=None) + + checker = CompoundPathChecker( + path_checker_path_valid_path, path_checker_path_not_valid_path + ) + assert checker(tmp_path) == BackendInfo(tmp_path) + + checker = CompoundPathChecker(path_checker_path_not_valid_path) + assert checker(tmp_path) is None + + +@pytest.mark.parametrize( + "eula_agreement, expected_command", + [ + [ + True, + [ + "./FVP_Corstone_SSE-300.sh", + "-q", + "-d", + "corstone-300", + ], + ], + [ + False, + [ + "./FVP_Corstone_SSE-300.sh", + "-q", + "-d", + "corstone-300", + "--nointeractive", + "--i-agree-to-the-contained-eula", + ], + ], + ], +) +def test_corstone_300_installer( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + eula_agreement: bool, + expected_command: List[str], +) -> None: + """Test Corstone-300 installer.""" + command_mock = MagicMock() + + monkeypatch.setattr( + "mlia.tools.metadata.corstone.subprocess.check_call", command_mock + ) + installer = Corstone300Installer() + result = installer(eula_agreement, tmp_path) + + command_mock.assert_called_once_with(expected_command) + assert result == tmp_path / "corstone-300" |