class DNFManagerTestCase(unittest.TestCase):
    """Test the abstraction of the DNF base."""
    def setUp(self):
        self.maxDiff = None
        self.dnf_manager = DNFManager()

    def _check_configuration(self, *attributes):
        """Check the DNF configuration."""
        configuration = self.dnf_manager._base.conf.dump()
        configuration = configuration.splitlines(keepends=False)

        for attribute in attributes:
            assert attribute in configuration

    def _check_substitutions(self, substitutions):
        """Check the DNF substitutions."""
        assert dict(self.dnf_manager._base.conf.substitutions) == substitutions

    def test_create_base(self):
        """Test the creation of the DNF base."""
        assert self.dnf_manager._base is not None

    def test_reset_base(self):
        """Test the reset of the DNF base."""
        base_1 = self.dnf_manager._base
        assert self.dnf_manager._base == base_1
        self.dnf_manager.reset_base()

        base_2 = self.dnf_manager._base
        assert self.dnf_manager._base == base_2
        assert self.dnf_manager._base != base_1

    def test_clear_cache(self):
        """Test the clear_cache method."""
        self.dnf_manager.clear_cache()

    def test_set_default_configuration(self):
        """Test the default configuration of the DNF base."""
        self._check_configuration(
            "cachedir = /tmp/dnf.cache",
            "pluginconfpath = /tmp/dnf.pluginconf",
            "logdir = /tmp/",
        )
        self._check_configuration("installroot = /mnt/sysroot",
                                  "persistdir = /mnt/sysroot/var/lib/dnf")
        self._check_configuration("reposdir = "
                                  "/etc/yum.repos.d, "
                                  "/etc/anaconda.repos.d")
        self._check_substitutions({
            "arch": "x86_64",
            "basearch": "x86_64",
            "releasever": "rawhide"
        })

    @patch(
        "pyanaconda.modules.payloads.payload.dnf.dnf_manager.get_os_release_value"
    )
    def test_set_module_platform_id(self, get_platform_id):
        """Test the configuration of module_platform_id."""
        get_platform_id.return_value = "platform:f32"
        self.dnf_manager.reset_base()
        self._check_configuration("module_platform_id = platform:f32")

    def test_configure_proxy(self):
        """Test the proxy configuration."""
        self.dnf_manager.configure_proxy("http://*****:*****@example.com/proxy")
        self._check_configuration(
            "proxy = http://example.com:3128",
            "proxy_username = user",
            "proxy_password = pass",
        )

        self.dnf_manager.configure_proxy("@:/invalid")
        self._check_configuration(
            "proxy = ",
            "proxy_username = "******"proxy_password = "******"http://example.com/proxy")
        self._check_configuration(
            "proxy = http://example.com:3128",
            "proxy_username = "******"proxy_password = "******"proxy = ",
            "proxy_username = "******"proxy_password = "******"""Test the configuration of the DNF base."""
        data = PackagesConfigurationData()

        self.dnf_manager.configure_base(data)
        self._check_configuration(
            "multilib_policy = best",
            "timeout = 30",
            "retries = 10",
            "install_weak_deps = 1",
        )

        assert self.dnf_manager._ignore_broken_packages is False
        assert self.dnf_manager._ignore_missing_packages is False

        data.multilib_policy = MULTILIB_POLICY_ALL
        data.timeout = 100
        data.retries = 5
        data.broken_ignored = True
        data.missing_ignored = True
        data.weakdeps_excluded = True

        self.dnf_manager.configure_base(data)
        self._check_configuration(
            "multilib_policy = all",
            "timeout = 100",
            "retries = 5",
            "install_weak_deps = 0",
        )

        assert self.dnf_manager._ignore_broken_packages is True
        assert self.dnf_manager._ignore_missing_packages is True

    def test_dump_configuration(self):
        """Test the dump of the DNF configuration."""
        with self.assertLogs(level="DEBUG") as cm:
            self.dnf_manager.dump_configuration()

        msg = "DNF configuration:"
        assert any(map(lambda x: msg in x, cm.output))

        msg = "installroot = /mnt/sysroot"
        assert any(map(lambda x: msg in x, cm.output))

    def test_get_installation_size(self):
        """Test the get_installation_size method."""
        # No transaction.
        size = self.dnf_manager.get_installation_size()
        assert size == Size("3000 MiB")

        # Fake transaction.
        tsi_1 = Mock()
        tsi_1.pkg.installsize = 1024 * 100
        tsi_1.pkg.files = ["/file"] * 10

        tsi_2 = Mock()
        tsi_2.pkg.installsize = 1024 * 200
        tsi_2.pkg.files = ["/file"] * 20

        self.dnf_manager._base.transaction = [tsi_1, tsi_2]
        size = self.dnf_manager.get_installation_size()
        size = size.round_to_nearest("KiB", ROUND_UP)

        assert size == Size("528 KiB")

    def test_get_download_size(self):
        """Test the get_download_size method."""
        # No transaction.
        size = self.dnf_manager.get_download_size()
        assert size == Size(0)

        # Fake transaction.
        tsi_1 = Mock()
        tsi_1.pkg.downloadsize = 1024 * 1024 * 100

        tsi_2 = Mock()
        tsi_2.pkg.downloadsize = 1024 * 1024 * 200

        self.dnf_manager._base.transaction = [tsi_1, tsi_2]
        size = self.dnf_manager.get_download_size()

        assert size == Size("450 MiB")

    @patch("dnf.module.module_base.ModuleBase.enable")
    def test_enable_modules(self, module_base_enable):
        """Test the enable_modules method."""
        self.dnf_manager.enable_modules(module_specs=["m1", "m2:latest"])
        module_base_enable.assert_called_once_with(["m1", "m2:latest"])

    @patch("dnf.module.module_base.ModuleBase.enable")
    def test_enable_modules_error(self, module_base_enable):
        """Test the failed enable_modules method."""
        module_base_enable.side_effect = MarkingErrors(
            module_depsolv_errors=["e1", "e2"])

        with pytest.raises(BrokenSpecsError):
            self.dnf_manager.enable_modules(module_specs=["m1", "m2:latest"])

    @patch("dnf.module.module_base.ModuleBase.disable")
    def test_disable_modules(self, module_base_disable):
        """Test the enable_modules method."""
        self.dnf_manager.disable_modules(module_specs=["m1", "m2:latest"])
        module_base_disable.assert_called_once_with(["m1", "m2:latest"])

    @patch("dnf.module.module_base.ModuleBase.disable")
    def test_disable_modules_error(self, module_base_disable):
        """Test the failed enable_modules method."""
        module_base_disable.side_effect = MarkingErrors(
            module_depsolv_errors=["e1", "e2"])

        with pytest.raises(BrokenSpecsError):
            self.dnf_manager.disable_modules(module_specs=["m1", "m2:latest"])

    @patch("dnf.base.Base.install_specs")
    def test_apply_specs(self, install_specs):
        """Test the apply_specs method."""
        self.dnf_manager.apply_specs(include_list=["@g1", "p1"],
                                     exclude_list=["@g2", "p2"])

        install_specs.assert_called_once_with(install=["@g1", "p1"],
                                              exclude=["@g2", "p2"],
                                              strict=True)

    @patch("dnf.base.Base.install_specs")
    def test_apply_specs_error(self, install_specs):
        """Test the apply_specs method with an error."""
        install_specs.side_effect = MarkingErrors(error_group_specs=["@g1"])

        with pytest.raises(BrokenSpecsError):
            self.dnf_manager.apply_specs(include_list=["@g1", "p1"],
                                         exclude_list=["@g2", "p2"])

        install_specs.side_effect = MarkingErrors(no_match_group_specs=["@g1"])

        with pytest.raises(MissingSpecsError):
            self.dnf_manager.apply_specs(include_list=["@g1", "p1"],
                                         exclude_list=["@g2", "p2"])

    @patch("dnf.base.Base.install_specs")
    def test_apply_specs_ignore_broken(self, install_specs):
        """Test the apply_specs method with ignored broken packages."""
        self.dnf_manager._ignore_broken_packages = True
        self.dnf_manager.apply_specs(include_list=["@g1", "p1"],
                                     exclude_list=["@g2", "p2"])

        install_specs.assert_called_once_with(install=["@g1", "p1"],
                                              exclude=["@g2", "p2"],
                                              strict=False)

    @patch("dnf.base.Base.install_specs")
    def test_apply_specs_ignore_missing(self, install_specs):
        """Test the apply_specs method with ignored missing packages."""
        self.dnf_manager._ignore_missing_packages = True

        # Ignore a missing package.
        install_specs.side_effect = MarkingErrors(no_match_pkg_specs=["p1"])

        self.dnf_manager.apply_specs(include_list=["@g1", "p1"],
                                     exclude_list=["@g2", "p2"])

        install_specs.assert_called_once_with(install=["@g1", "p1"],
                                              exclude=["@g2", "p2"],
                                              strict=True)

        # Don't ignore a broken transaction.
        install_specs.side_effect = MarkingErrors(error_pkg_specs=["p1"])

        with pytest.raises(BrokenSpecsError):
            self.dnf_manager.apply_specs(include_list=["@g1", "p1"],
                                         exclude_list=["@g2", "p2"])

    @patch("dnf.base.Base.download_packages")
    @patch("dnf.base.Base.transaction")
    def test_download_packages(self, transaction, download_packages):
        """Test the download_packages method."""
        callback = Mock()
        transaction.install_set = ["p1", "p2", "p3"]
        download_packages.side_effect = self._download_packages

        self.dnf_manager.download_packages(callback)

        callback.assert_has_calls([
            call('Downloading 3 RPMs, 25 B / 300 B (8%) done.'),
            call('Downloading 3 RPMs, 75 B / 300 B (25%) done.'),
            call('Downloading 3 RPMs, 100 B / 300 B (33%) done.'),
            call('Downloading 3 RPMs, 125 B / 300 B (41%) done.'),
            call('Downloading 3 RPMs, 175 B / 300 B (58%) done.'),
            call('Downloading 3 RPMs, 200 B / 300 B (66%) done.'),
            call('Downloading 3 RPMs, 225 B / 300 B (75%) done.'),
            call('Downloading 3 RPMs, 275 B / 300 B (91%) done.'),
            call('Downloading 3 RPMs, 300 B / 300 B (100%) done.')
        ])

    def _download_packages(self, packages, progress):
        """Simulate the download of packages."""
        progress.start(total_files=3, total_size=300)

        for name in packages:
            payload = Mock()
            payload.__str__ = Mock(return_value=name)
            payload.download_size = 100

            progress.last_time = 0
            progress.progress(payload, 25)

            progress.last_time += 3600
            progress.progress(payload, 50)

            progress.last_time = 0
            progress.progress(payload, 75)

            progress.last_time = 0
            progress.end(payload, STATUS_OK, "Message!")

        assert progress.downloads == {"p1": 100, "p2": 100, "p3": 100}

    @patch("dnf.base.Base.download_packages")
    @patch("dnf.base.Base.transaction")
    def test_download_packages_failed(self, transaction, download_packages):
        """Test the download_packages method with failed packages."""
        callback = Mock()
        transaction.install_set = ["p1", "p2", "p3"]
        download_packages.side_effect = self._download_packages_failed

        self.dnf_manager.download_packages(callback)

        callback.assert_has_calls([
            call('Downloading 3 RPMs, 25 B / 300 B (8%) done.'),
            call('Downloading 3 RPMs, 50 B / 300 B (16%) done.'),
            call('Downloading 3 RPMs, 75 B / 300 B (25%) done.')
        ])

    def _download_packages_failed(self, packages, progress):
        """Simulate the failed download of packages."""
        progress.start(total_files=3, total_size=300)

        for name in packages:
            payload = Mock()
            payload.__str__ = Mock(return_value=name)
            payload.download_size = 100

            progress.last_time = 0
            progress.progress(payload, 25)

            progress.last_time = 0
            progress.end(payload, STATUS_FAILED, "Message!")

        assert progress.downloads == {"p1": 25, "p2": 25, "p3": 25}

    @patch("dnf.base.Base.do_transaction")
    def test_install_packages(self, do_transaction):
        """Test the install_packages method."""
        calls = []
        do_transaction.side_effect = self._install_packages

        self.dnf_manager.install_packages(calls.append)

        assert calls == [
            'Installing p1.x86_64 (0/3)',
            'Installing p2.x86_64 (1/3)',
            'Installing p3.x86_64 (2/3)',
            'Performing post-installation setup tasks',
            'Configuring p1.x86_64',
            'Configuring p2.x86_64',
            'Configuring p3.x86_64',
            'Verifying p1.x86_64 (1/3)',
            'Verifying p2.x86_64 (2/3)',
            'Verifying p3.x86_64 (3/3)',
        ]

    def _get_package(self, name):
        """Get a mocked package of the specified name."""
        package = Mock(spec=Package)
        package.name = name
        package.arch = "x86_64"
        package.evr = "1.2-3"
        package.buildtime = 100
        package.returnIdSum.return_value = ("", "1a2b3c")
        return package

    def _install_packages(self, progress):
        """Simulate the installation of packages."""
        packages = list(map(self._get_package, ["p1", "p2", "p3"]))
        ts_total = len(packages)

        for ts_done, package in enumerate(packages):
            progress.progress(package, PKG_INSTALL, 0, 100, ts_done, ts_total)
            progress.progress(package, PKG_INSTALL, 50, 100, ts_done, ts_total)
            progress.progress(package, PKG_SCRIPTLET, 75, 100, ts_done,
                              ts_total)
            progress.progress(package, PKG_INSTALL, 100, 100, ts_done + 1,
                              ts_total)

        progress.progress(None, TRANS_POST, None, None, None, None)

        for ts_done, package in enumerate(packages):
            progress.progress(package, PKG_SCRIPTLET, 100, 100, ts_done + 1,
                              ts_total)

        for ts_done, package in enumerate(packages):
            progress.progress(package, PKG_VERIFY, 100, 100, ts_done + 1,
                              ts_total)

    @patch("dnf.base.Base.do_transaction")
    def test_install_packages_failed(self, do_transaction):
        """Test the failed install_packages method."""
        calls = []
        do_transaction.side_effect = self._install_packages_failed

        with pytest.raises(PayloadInstallationError) as cm:
            self.dnf_manager.install_packages(calls.append)

        msg = "An error occurred during the transaction: " \
              "The p1 package couldn't be installed!"

        assert str(cm.value) == msg
        assert calls == []

    def _install_packages_failed(self, progress):
        """Simulate the failed installation of packages."""
        progress.error("The p1 package couldn't be installed!")

    @patch("dnf.base.Base.do_transaction")
    def test_install_packages_quit(self, do_transaction):
        """Test the terminated install_packages method."""
        calls = []
        do_transaction.side_effect = self._install_packages_quit

        with pytest.raises(RuntimeError) as cm:
            self.dnf_manager.install_packages(calls.append)

        msg = "The transaction process has ended abruptly: " \
              "Something went wrong with the p1 package!"

        assert msg in str(cm.value)
        assert calls == []

    def _install_packages_quit(self, progress):
        """Simulate the terminated installation of packages."""
        raise IOError("Something went wrong with the p1 package!")

    def _add_repo(self, name, enabled=True):
        """Add the DNF repo object."""
        repo = Repo(name, self.dnf_manager._base.conf)
        self.dnf_manager._base.repos.add(repo)

        if enabled:
            repo.enable()

        return repo

    def test_set_download_location(self):
        """Test the set_download_location method."""
        r1 = self._add_repo("r1")
        r2 = self._add_repo("r2")
        r3 = self._add_repo("r3")

        self.dnf_manager.set_download_location("/my/download/location")

        assert r1.pkgdir == "/my/download/location"
        assert r2.pkgdir == "/my/download/location"
        assert r3.pkgdir == "/my/download/location"

    def test_download_location(self):
        """Test the download_location property."""
        assert self.dnf_manager.download_location is None

        self.dnf_manager.set_download_location("/my/location")
        assert self.dnf_manager.download_location == "/my/location"

        self.dnf_manager.reset_base()
        assert self.dnf_manager.download_location is None

    def test_substitute(self):
        """Test the substitute method."""
        # No variables.
        assert self.dnf_manager.substitute(None) == ""
        assert self.dnf_manager.substitute("") == ""
        assert self.dnf_manager.substitute("/") == "/"
        assert self.dnf_manager.substitute("/text") == "/text"

        # Unknown variables.
        assert self.dnf_manager.substitute("/$unknown") == "/$unknown"

        # Supported variables.
        assert self.dnf_manager.substitute("/$basearch") != "/$basearch"
        assert self.dnf_manager.substitute("/$releasever") != "/$releasever"

    def test_configure_substitution(self):
        """Test the configure_substitution function."""
        self.dnf_manager.configure_substitution(release_version="123")
        self._check_substitutions({
            "arch": "x86_64",
            "basearch": "x86_64",
            "releasever": "123"
        })

    def test_reset_substitution(self):
        """Test the reset_substitution method."""
        self.dnf_manager.configure_substitution(release_version="123")
        self._check_substitutions({
            "arch": "x86_64",
            "basearch": "x86_64",
            "releasever": "123"
        })

        self.dnf_manager.reset_substitution()
        self._check_substitutions({
            "arch": "x86_64",
            "basearch": "x86_64",
            "releasever": "rawhide"
        })

    @patch("dnf.subject.Subject.get_best_query")
    def test_is_package_available(self, get_best_query):
        """Test the is_package_available method."""
        self.dnf_manager._base._sack = Mock()
        assert self.dnf_manager.is_package_available("kernel") is True

        # No package.
        get_best_query.return_value = None
        assert self.dnf_manager.is_package_available("kernel") is False

        # No metadata.
        self.dnf_manager._base._sack = None

        with self.assertLogs(level="WARNING") as cm:
            assert self.dnf_manager.is_package_available("kernel") is False

        msg = "There is no metadata about packages!"
        assert any(map(lambda x: msg in x, cm.output))

    def test_match_available_packages(self):
        """Test the match_available_packages method"""
        p1 = self._get_package("langpacks-cs")
        p2 = self._get_package("langpacks-core-cs")
        p3 = self._get_package("langpacks-core-font-cs")

        sack = Mock()
        sack.query.return_value.available.return_value.filter.return_value = [
            p1, p2, p3
        ]

        # With metadata.
        self.dnf_manager._base._sack = sack
        assert self.dnf_manager.match_available_packages("langpacks-*") == [
            "langpacks-cs", "langpacks-core-cs", "langpacks-core-font-cs"
        ]

        # No metadata.
        self.dnf_manager._base._sack = None

        with self.assertLogs(level="WARNING") as cm:
            assert self.dnf_manager.match_available_packages(
                "langpacks-*") == []

        msg = "There is no metadata about packages!"
        assert any(map(lambda x: msg in x, cm.output))

    @patch("dnf.base.Base.resolve")
    def test_resolve_selection(self, resolve):
        """Test the resolve_selection method."""
        self.dnf_manager._base.transaction = [Mock(), Mock()]

        with self.assertLogs(level="INFO") as cm:
            self.dnf_manager.resolve_selection()

        expected = "The software selection has been resolved (2 packages selected)."
        assert expected in "\n".join(cm.output)

        resolve.assert_called_once()

    @patch("dnf.base.Base.resolve")
    def test_resolve_selection_failed(self, resolve):
        """Test the failed resolve_selection method."""
        resolve.side_effect = DepsolveError("e1")

        with pytest.raises(InvalidSelectionError) as cm:
            self.dnf_manager.resolve_selection()

        expected = \
            "The following software marked for installation has errors.\n" \
            "This is likely caused by an error with your installation source.\n\n" \
            "e1"

        assert expected == str(cm.value)

    def test_clear_selection(self):
        """Test the clear_selection method."""
        self.dnf_manager.clear_selection()
Пример #2
0
class DNFMangerTestCase(unittest.TestCase):
    """Test the abstraction of the DNF base."""
    def setUp(self):
        self.maxDiff = None
        self.dnf_manager = DNFManager()

    def _check_configuration(self, *attributes):
        """Check the DNF configuration."""
        configuration = self.dnf_manager._base.conf.dump()
        configuration = configuration.splitlines(keepends=False)

        for attribute in attributes:
            self.assertIn(attribute, configuration)

    def _check_substitutions(self, substitutions):
        """Check the DNF substitutions."""
        self.assertEqual(dict(self.dnf_manager._base.conf.substitutions),
                         substitutions)

    def create_base_test(self):
        """Test the creation of the DNF base."""
        self.assertIsNotNone(self.dnf_manager._base)

    def reset_base_test(self):
        """Test the reset of the DNF base."""
        base_1 = self.dnf_manager._base
        self.assertEqual(self.dnf_manager._base, base_1)
        self.dnf_manager.reset_base()

        base_2 = self.dnf_manager._base
        self.assertEqual(self.dnf_manager._base, base_2)
        self.assertNotEqual(self.dnf_manager._base, base_1)

    def clear_cache_test(self):
        """Test the clear_cache method."""
        self.dnf_manager.clear_cache()

    def set_default_configuration_test(self):
        """Test the default configuration of the DNF base."""
        self._check_configuration(
            "cachedir = /tmp/dnf.cache",
            "pluginconfpath = /tmp/dnf.pluginconf",
            "logdir = /tmp/",
        )
        self._check_configuration("installroot = /mnt/sysroot",
                                  "persistdir = /mnt/sysroot/var/lib/dnf")
        self._check_configuration("reposdir = "
                                  "/etc/yum.repos.d, "
                                  "/etc/anaconda.repos.d")
        self._check_substitutions({
            "arch": "x86_64",
            "basearch": "x86_64",
            "releasever": "rawhide"
        })

    @patch(
        "pyanaconda.modules.payloads.payload.dnf.dnf_manager.get_os_release_value"
    )
    def set_module_platform_id_test(self, get_platform_id):
        """Test the configuration of module_platform_id."""
        get_platform_id.return_value = "platform:f32"
        self.dnf_manager.reset_base()
        self._check_configuration("module_platform_id = platform:f32")

    def configure_proxy_test(self):
        """Test the proxy configuration."""
        self.dnf_manager.configure_proxy("http://*****:*****@example.com/proxy")
        self._check_configuration(
            "proxy = http://example.com:3128",
            "proxy_username = user",
            "proxy_password = pass",
        )

        self.dnf_manager.configure_proxy("@:/invalid")
        self._check_configuration(
            "proxy = ",
            "proxy_username = "******"proxy_password = "******"http://example.com/proxy")
        self._check_configuration(
            "proxy = http://example.com:3128",
            "proxy_username = "******"proxy_password = "******"proxy = ",
            "proxy_username = "******"proxy_password = "******"""Test the configuration of the DNF base."""
        data = PackagesConfigurationData()

        self.dnf_manager.configure_base(data)
        self._check_configuration(
            "multilib_policy = best",
            "timeout = 30",
            "retries = 10",
            "install_weak_deps = 1",
        )

        self.assertEqual(self.dnf_manager._ignore_broken_packages, False)
        self.assertEqual(self.dnf_manager._ignore_missing_packages, False)

        data.multilib_policy = MULTILIB_POLICY_ALL
        data.timeout = 100
        data.retries = 5
        data.broken_ignored = True
        data.missing_ignored = True
        data.weakdeps_excluded = True

        self.dnf_manager.configure_base(data)
        self._check_configuration(
            "multilib_policy = all",
            "timeout = 100",
            "retries = 5",
            "install_weak_deps = 0",
        )

        self.assertEqual(self.dnf_manager._ignore_broken_packages, True)
        self.assertEqual(self.dnf_manager._ignore_missing_packages, True)

    def dump_configuration_test(self):
        """Test the dump of the DNF configuration."""
        with self.assertLogs(level="DEBUG") as cm:
            self.dnf_manager.dump_configuration()

        msg = "DNF configuration:"
        self.assertTrue(any(map(lambda x: msg in x, cm.output)))

        msg = "installroot = /mnt/sysroot"
        self.assertTrue(any(map(lambda x: msg in x, cm.output)))

    def get_installation_size_test(self):
        """Test the get_installation_size method."""
        # No transaction.
        size = self.dnf_manager.get_installation_size()
        self.assertEqual(size, Size("3000 MiB"))

        # Fake transaction.
        tsi_1 = Mock()
        tsi_1.pkg.installsize = 1024 * 100
        tsi_1.pkg.files = ["/file"] * 10

        tsi_2 = Mock()
        tsi_2.pkg.installsize = 1024 * 200
        tsi_2.pkg.files = ["/file"] * 20

        self.dnf_manager._base.transaction = [tsi_1, tsi_2]
        size = self.dnf_manager.get_installation_size()
        size = size.round_to_nearest("KiB", ROUND_UP)

        self.assertEqual(size, Size("528 KiB"))

    def get_download_size_test(self):
        """Test the get_download_size method."""
        # No transaction.
        size = self.dnf_manager.get_download_size()
        self.assertEqual(size, Size(0))

        # Fake transaction.
        tsi_1 = Mock()
        tsi_1.pkg.downloadsize = 1024 * 1024 * 100

        tsi_2 = Mock()
        tsi_2.pkg.downloadsize = 1024 * 1024 * 200

        self.dnf_manager._base.transaction = [tsi_1, tsi_2]
        size = self.dnf_manager.get_download_size()

        self.assertEqual(size, Size("450 MiB"))

    def environments_test(self):
        """Test the environments property."""
        self.assertEqual(self.dnf_manager.environments, [])

        # Fake environments.
        env_1 = Mock(id="environment-1")
        env_2 = Mock(id="environment-2")
        env_3 = Mock(id="environment-3")

        # Fake comps.
        comps = Mock(environments=[env_1, env_2, env_3])

        self.dnf_manager._base._comps = comps
        self.assertEqual(self.dnf_manager.environments, [
            "environment-1",
            "environment-2",
            "environment-3",
        ])

    @patch("dnf.module.module_base.ModuleBase.enable")
    def enable_modules_test(self, module_base_enable):
        """Test the enable_modules method."""
        self.dnf_manager.enable_modules(module_specs=["m1", "m2:latest"])
        module_base_enable.assert_called_once_with(["m1", "m2:latest"])

    @patch("dnf.module.module_base.ModuleBase.enable")
    def enable_modules_error_test(self, module_base_enable):
        """Test the failed enable_modules method."""
        module_base_enable.side_effect = MarkingErrors(
            module_depsolv_errors=["e1", "e2"])

        with self.assertRaises(MarkingErrors):
            self.dnf_manager.enable_modules(module_specs=["m1", "m2:latest"])

    @patch("dnf.module.module_base.ModuleBase.disable")
    def disable_modules_test(self, module_base_disable):
        """Test the enable_modules method."""
        self.dnf_manager.disable_modules(module_specs=["m1", "m2:latest"])
        module_base_disable.assert_called_once_with(["m1", "m2:latest"])

    @patch("dnf.module.module_base.ModuleBase.disable")
    def disable_modules_error_test(self, module_base_disable):
        """Test the failed enable_modules method."""
        module_base_disable.side_effect = MarkingErrors(
            module_depsolv_errors=["e1", "e2"])

        with self.assertRaises(MarkingErrors):
            self.dnf_manager.disable_modules(module_specs=["m1", "m2:latest"])

    @patch("dnf.base.Base.install_specs")
    def apply_specs_test(self, install_specs):
        """Test the apply_specs method."""
        self.dnf_manager.apply_specs(include_list=["@g1", "p1"],
                                     exclude_list=["@g2", "p2"])

        install_specs.assert_called_once_with(install=["@g1", "p1"],
                                              exclude=["@g2", "p2"],
                                              strict=True)

    @patch("dnf.base.Base.install_specs")
    def apply_specs_error_test(self, install_specs):
        """Test the apply_specs method with an error."""
        install_specs.side_effect = MarkingErrors(error_group_specs=["@g1"])

        with self.assertRaises(MarkingErrors):
            self.dnf_manager.apply_specs(include_list=["@g1", "p1"],
                                         exclude_list=["@g2", "p2"])

    @patch("dnf.base.Base.install_specs")
    def apply_specs_ignore_broken_test(self, install_specs):
        """Test the apply_specs method with ignored broken packages."""
        self.dnf_manager._ignore_broken_packages = True
        self.dnf_manager.apply_specs(include_list=["@g1", "p1"],
                                     exclude_list=["@g2", "p2"])

        install_specs.assert_called_once_with(install=["@g1", "p1"],
                                              exclude=["@g2", "p2"],
                                              strict=False)

    @patch("dnf.base.Base.install_specs")
    def apply_specs_ignore_missing_test(self, install_specs):
        """Test the apply_specs method with ignored missing packages."""
        self.dnf_manager._ignore_missing_packages = True

        # Ignore a missing package.
        install_specs.side_effect = MarkingErrors(no_match_pkg_specs=["p1"])

        self.dnf_manager.apply_specs(include_list=["@g1", "p1"],
                                     exclude_list=["@g2", "p2"])

        install_specs.assert_called_once_with(install=["@g1", "p1"],
                                              exclude=["@g2", "p2"],
                                              strict=True)

        # Don't ignore a broken transaction.
        install_specs.side_effect = MarkingErrors(error_pkg_specs=["p1"])

        with self.assertRaises(MarkingErrors):
            self.dnf_manager.apply_specs(include_list=["@g1", "p1"],
                                         exclude_list=["@g2", "p2"])

    @patch("dnf.base.Base.download_packages")
    @patch("dnf.base.Base.transaction")
    def download_packages_test(self, transaction, download_packages):
        """Test the download_packages method."""
        callback = Mock()
        transaction.install_set = ["p1", "p2", "p3"]
        download_packages.side_effect = self._download_packages

        self.dnf_manager.download_packages(callback)

        callback.assert_has_calls([
            call('Downloading 3 RPMs, 25 B / 300 B (8%) done.'),
            call('Downloading 3 RPMs, 75 B / 300 B (25%) done.'),
            call('Downloading 3 RPMs, 100 B / 300 B (33%) done.'),
            call('Downloading 3 RPMs, 125 B / 300 B (41%) done.'),
            call('Downloading 3 RPMs, 175 B / 300 B (58%) done.'),
            call('Downloading 3 RPMs, 200 B / 300 B (66%) done.'),
            call('Downloading 3 RPMs, 225 B / 300 B (75%) done.'),
            call('Downloading 3 RPMs, 275 B / 300 B (91%) done.'),
            call('Downloading 3 RPMs, 300 B / 300 B (100%) done.')
        ])

    def _download_packages(self, packages, progress):
        """Simulate the download of packages."""
        progress.start(total_files=3, total_size=300)

        for name in packages:
            payload = Mock()
            payload.__str__ = Mock(return_value=name)
            payload.download_size = 100

            progress.last_time = 0
            progress.progress(payload, 25)

            progress.last_time += 3600
            progress.progress(payload, 50)

            progress.last_time = 0
            progress.progress(payload, 75)

            progress.last_time = 0
            progress.end(payload, STATUS_OK, "Message!")

        self.assertEqual(progress.downloads, {"p1": 100, "p2": 100, "p3": 100})

    @patch("dnf.base.Base.download_packages")
    @patch("dnf.base.Base.transaction")
    def download_packages_failed_test(self, transaction, download_packages):
        """Test the download_packages method with failed packages."""
        callback = Mock()
        transaction.install_set = ["p1", "p2", "p3"]
        download_packages.side_effect = self._download_packages_failed

        self.dnf_manager.download_packages(callback)

        callback.assert_has_calls([
            call('Downloading 3 RPMs, 25 B / 300 B (8%) done.'),
            call('Downloading 3 RPMs, 50 B / 300 B (16%) done.'),
            call('Downloading 3 RPMs, 75 B / 300 B (25%) done.')
        ])

    def _download_packages_failed(self, packages, progress):
        """Simulate the failed download of packages."""
        progress.start(total_files=3, total_size=300)

        for name in packages:
            payload = Mock()
            payload.__str__ = Mock(return_value=name)
            payload.download_size = 100

            progress.last_time = 0
            progress.progress(payload, 25)

            progress.last_time = 0
            progress.end(payload, STATUS_FAILED, "Message!")

        self.assertEqual(progress.downloads, {"p1": 25, "p2": 25, "p3": 25})

    @patch("dnf.base.Base.do_transaction")
    def install_packages_test(self, do_transaction):
        """Test the install_packages method."""
        calls = []
        do_transaction.side_effect = self._install_packages

        self.dnf_manager.install_packages(calls.append)

        self.assertEqual(calls, [
            'Installing p1.x86_64 (0/3)',
            'Installing p2.x86_64 (1/3)',
            'Installing p3.x86_64 (2/3)',
            'Performing post-installation setup tasks',
            'Configuring p1.x86_64',
            'Configuring p2.x86_64',
            'Configuring p3.x86_64',
            'Verifying p1.x86_64 (1/3)',
            'Verifying p2.x86_64 (2/3)',
            'Verifying p3.x86_64 (3/3)',
        ])

    def _get_package(self, name):
        """Get a mocked package of the specified name."""
        package = Mock(spec=Package)
        package.name = name
        package.arch = "x86_64"
        package.evr = "1.2-3"
        package.buildtime = 100
        package.returnIdSum.return_value = ("", "1a2b3c")
        return package

    def _install_packages(self, progress):
        """Simulate the installation of packages."""
        packages = list(map(self._get_package, ["p1", "p2", "p3"]))
        ts_total = len(packages)

        for ts_done, package in enumerate(packages):
            progress.progress(package, PKG_INSTALL, 0, 100, ts_done, ts_total)
            progress.progress(package, PKG_INSTALL, 50, 100, ts_done, ts_total)
            progress.progress(package, PKG_SCRIPTLET, 75, 100, ts_done,
                              ts_total)
            progress.progress(package, PKG_INSTALL, 100, 100, ts_done + 1,
                              ts_total)

        progress.progress(None, TRANS_POST, None, None, None, None)

        for ts_done, package in enumerate(packages):
            progress.progress(package, PKG_SCRIPTLET, 100, 100, ts_done + 1,
                              ts_total)

        for ts_done, package in enumerate(packages):
            progress.progress(package, PKG_VERIFY, 100, 100, ts_done + 1,
                              ts_total)

    @patch("dnf.base.Base.do_transaction")
    def install_packages_failed_test(self, do_transaction):
        """Test the failed install_packages method."""
        calls = []
        do_transaction.side_effect = self._install_packages_failed

        with self.assertRaises(PayloadInstallationError) as cm:
            self.dnf_manager.install_packages(calls.append)

        msg = "An error occurred during the transaction: " \
              "The p1 package couldn't be installed!"

        self.assertEqual(str(cm.exception), msg)
        self.assertEqual(calls, [])

    def _install_packages_failed(self, progress):
        """Simulate the failed installation of packages."""
        progress.error("The p1 package couldn't be installed!")

    @patch("dnf.base.Base.do_transaction")
    def install_packages_quit_test(self, do_transaction):
        """Test the terminated install_packages method."""
        calls = []
        do_transaction.side_effect = self._install_packages_quit

        with self.assertRaises(RuntimeError) as cm:
            self.dnf_manager.install_packages(calls.append)

        msg = "The transaction process has ended abruptly: " \
              "Something went wrong with the p1 package!"

        self.assertIn(msg, str(cm.exception))
        self.assertEqual(calls, [])

    def _install_packages_quit(self, progress):
        """Simulate the terminated installation of packages."""
        raise IOError("Something went wrong with the p1 package!")

    def _add_repo(self, name, enabled=True):
        """Add the DNF repo object."""
        repo = Repo(name, self.dnf_manager._base.conf)
        self.dnf_manager._base.repos.add(repo)

        if enabled:
            repo.enable()

        return repo

    def set_download_location_test(self):
        """Test the set_download_location method."""
        r1 = self._add_repo("r1")
        r2 = self._add_repo("r2")
        r3 = self._add_repo("r3")

        self.dnf_manager.set_download_location("/my/download/location")

        self.assertEqual(r1.pkgdir, "/my/download/location")
        self.assertEqual(r2.pkgdir, "/my/download/location")
        self.assertEqual(r3.pkgdir, "/my/download/location")

    def download_location_test(self):
        """Test the download_location property."""
        self.assertEqual(self.dnf_manager.download_location, None)

        self.dnf_manager.set_download_location("/my/location")
        self.assertEqual(self.dnf_manager.download_location, "/my/location")

        self.dnf_manager.reset_base()
        self.assertEqual(self.dnf_manager.download_location, None)