# Copyright (C) 2017 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions of
# the GNU General Public License v.2, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY expressed or implied, including the implied warranties of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details. You should have received a copy of the
# GNU General Public License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
# source code or documentation are not subject to the GNU General Public
# License and may only be used or replicated with the express permission of
# Red Hat, Inc.
#
from __future__ import absolute_import
from __future__ import unicode_literals
import os
import shutil
import tempfile
import unittest
import libdnf
import dnf.conf
import dnf.base
TOP_DIR = os.path.abspath(os.path.dirname(__file__))
REPOS_DIR = os.path.join(TOP_DIR, "modules/modules")
# with profile
MODULE_NSVAP = "module-name:stream:1::x86_64/profile"
MODULE_NSVP = "module-name:stream:1/profile"
MODULE_NSAP = "module-name:stream::x86_64/profile"
MODULE_NSP = "module-name:stream/profile"
MODULE_NP = "module-name/profile"
MODULE_NAP = "module-name::x86_64/profile"
# without profile
MODULE_NSVA = "module-name:stream:1::x86_64"
MODULE_NSV = "module-name:stream:1"
MODULE_NSA = "module-name:stream::x86_64"
MODULE_NS = "module-name:stream"
MODULE_N = "module-name"
MODULE_NA = "module-name::x86_64"
class ModuleTest(unittest.TestCase):
def assertInstalls(self, nevras):
expected = sorted(set(nevras))
actual = sorted(set([str(i) for i in self.base._goal.list_installs()]))
self.assertEqual(expected, actual)
def setUp(self):
self.tmpdir = tempfile.mkdtemp(prefix="dnf_test_")
self.conf = dnf.conf.MainConf()
self.conf.cachedir = os.path.join(self.tmpdir, "cache")
self.conf.installroot = os.path.join(TOP_DIR, "modules")
self.conf.persistdir = os.path.join(self.conf.installroot, self.conf.persistdir.lstrip("/"))
self.conf.substitutions["arch"] = "x86_64"
self.conf.substitutions["basearch"] = dnf.rpm.basearch(self.conf.substitutions["arch"])
self.conf.assumeyes = True
self.base = dnf.Base(conf=self.conf)
self.module_base = dnf.module.module_base.ModuleBase(self.base)
self._add_module_repo("_all")
self.base.fill_sack(load_system_repo=False)
def tearDown(self):
shutil.rmtree(self.tmpdir)
def _add_module_repo(self, repo_id, modules=True):
url = "file://" + os.path.join(REPOS_DIR, repo_id, self.conf.substitutions["arch"])
repo = self.base.repos.add_new_repo(repo_id, self.base.conf, baseurl=[url], modules=modules)
return repo
# dnf module enable
def test_enable_name(self):
# use default stream
self.module_base.enable(["httpd"])
self.assertEqual(self.base._moduleContainer.getModuleState("httpd"),
libdnf.module.ModulePackageContainer.ModuleState_ENABLED)
self.assertEqual(self.base._moduleContainer.getEnabledStream("httpd"), "2.4")
def test_enable_name_stream(self):
self.module_base.enable(["httpd:2.4"])
self.assertEqual(self.base._moduleContainer.getModuleState("httpd"),
libdnf.module.ModulePackageContainer.ModuleState_ENABLED)
self.assertEqual(self.base._moduleContainer.getEnabledStream("httpd"), "2.4")
# also enable base-runtime; it's a dependency that's used in other tests
self.module_base.enable(["base-runtime:f26"])
def test_enable_pkgspec(self):
self.module_base.enable(["httpd:2.4:1/foo"])
self.assertEqual(self.base._moduleContainer.getModuleState("httpd"),
libdnf.module.ModulePackageContainer.ModuleState_ENABLED)
self.assertEqual(self.base._moduleContainer.getEnabledStream("httpd"), "2.4")
def test_enable_invalid(self):
with self.assertRaises(dnf.exceptions.Error):
self.module_base.enable(["httpd:invalid"])
def test_enable_different_stream(self):
self.module_base.enable(["httpd:2.4"])
self.assertEqual(self.base._moduleContainer.getModuleState("httpd"),
libdnf.module.ModulePackageContainer.ModuleState_ENABLED)
self.assertEqual(self.base._moduleContainer.getEnabledStream("httpd"), "2.4")
self.module_base.enable(["httpd:2.2"])
self.assertEqual(self.base._moduleContainer.getModuleState("httpd"),
libdnf.module.ModulePackageContainer.ModuleState_ENABLED)
self.assertEqual(self.base._moduleContainer.getEnabledStream("httpd"), "2.2")
def test_enable_different_stream_missing_profile(self):
pass
# dnf module disable
def test_disable_name(self):
self.module_base.enable(["httpd:2.4"])
self.assertEqual(self.base._moduleContainer.getModuleState("httpd"),
libdnf.module.ModulePackageContainer.ModuleState_ENABLED)
self.assertEqual(self.base._moduleContainer.getEnabledStream("httpd"), "2.4")
self.module_base.disable(["httpd"])
self.assertEqual(self.base._moduleContainer.getModuleState("httpd"),
libdnf.module.ModulePackageContainer.ModuleState_DISABLED)
self.assertEqual(self.base._moduleContainer.getEnabledStream("httpd"), "")
def test_disable_name_stream(self):
# It should disable whole module not only stream (strem = "")
self.module_base.enable(["httpd:2.4"])
self.assertEqual(self.base._moduleContainer.getModuleState("httpd"),
libdnf.module.ModulePackageContainer.ModuleState_ENABLED)
self.assertEqual(self.base._moduleContainer.getEnabledStream("httpd"), "2.4")
self.module_base.disable(["httpd:2.4"])
self.assertEqual(self.base._moduleContainer.getModuleState("httpd"),
libdnf.module.ModulePackageContainer.ModuleState_DISABLED)
self.assertEqual(self.base._moduleContainer.getEnabledStream("httpd"), "")
def test_disable_pkgspec(self):
# It should disable whole module not only profile (strem = "")
self.module_base.enable(["httpd:2.4"])
self.assertEqual(self.base._moduleContainer.getModuleState("httpd"),
libdnf.module.ModulePackageContainer.ModuleState_ENABLED)
self.assertEqual(self.base._moduleContainer.getEnabledStream("httpd"), "2.4")
self.module_base.disable(["httpd:2.4:1/foo"])
self.assertEqual(self.base._moduleContainer.getModuleState("httpd"),
libdnf.module.ModulePackageContainer.ModuleState_DISABLED)
self.assertEqual(self.base._moduleContainer.getEnabledStream("httpd"), "")
def test_disable_invalid(self):
self.module_base.enable(["httpd:2.4"])
self.assertEqual(self.base._moduleContainer.getModuleState("httpd"),
libdnf.module.ModulePackageContainer.ModuleState_ENABLED)
self.assertEqual(self.base._moduleContainer.getEnabledStream("httpd"), "2.4")
with self.assertRaises(dnf.exceptions.Error):
self.module_base.disable(["httpd:invalid"])
def test_info_name(self):
pass
def test_info_name_stream(self):
pass
def test_info_pkgspec(self):
pass
# dnf module list
def test_list_installed(self):
# install
self.module_base.install(["base-runtime"])
# check module conf
self.assertEqual(self.base._moduleContainer.getModuleState("base-runtime"),
libdnf.module.ModulePackageContainer.ModuleState_ENABLED)
self.assertEqual(self.base._moduleContainer.getEnabledStream("base-runtime"), "f26")
self.assertEqual(list(self.base._moduleContainer.getInstalledProfiles("base-runtime")),
["minimal"])
# dnf module install / dnf install @
def test_install_profile_latest(self):
self.test_enable_name_stream()
self.module_base.install(["httpd/default"])
self.base.resolve()
expected = [
"basesystem-11-3.noarch",
"filesystem-3.2-40.x86_64",
"glibc-2.25.90-2.x86_64",
"glibc-common-2.25.90-2.x86_64",
"httpd-2.4.25-8.x86_64",
"libnghttp2-1.21.1-1.x86_64", # expected behaviour, non-modular rpm pulled in
]
self.assertInstalls(expected)
def test_install_profile(self):
self.test_enable_name_stream()
self.module_base.install(["httpd:2.4:1/default"])
self.base.resolve()
expected = [
"basesystem-11-3.noarch",
"filesystem-3.2-40.x86_64",
"glibc-2.25.90-2.x86_64",
"glibc-common-2.25.90-2.x86_64",
"httpd-2.4.25-7.x86_64",
"libnghttp2-1.21.1-1.x86_64", # expected behaviour, non-modular rpm pulled in
]
self.assertInstalls(expected)
def test_install_two_profiles(self):
self.test_enable_name_stream()
self.module_base.install(["httpd:2.4:1/default", "httpd:2.4:1/doc"])
self.base.resolve()
expected = [
"basesystem-11-3.noarch",
"filesystem-3.2-40.x86_64",
"glibc-2.25.90-2.x86_64",
"glibc-common-2.25.90-2.x86_64",
"httpd-2.4.25-7.x86_64",
"httpd-doc-2.4.25-7.x86_64",
"libnghttp2-1.21.1-1.x86_64", # expected behaviour, non-modular rpm pulled in
]
self.assertInstalls(expected)
def test_install_two_profiles_different_versions(self):
self.test_enable_name_stream()
self.module_base.install(["httpd:2.4:2/default", "httpd:2.4:1/doc"])
self.base.resolve()
expected = [
"basesystem-11-3.noarch",
"filesystem-3.2-40.x86_64",
"glibc-2.25.90-2.x86_64",
"glibc-common-2.25.90-2.x86_64",
"httpd-2.4.25-8.x86_64",
"httpd-doc-2.4.25-8.x86_64",
"libnghttp2-1.21.1-1.x86_64", # expected behaviour, non-modular rpm pulled in
]
self.assertInstalls(expected)
def test_install_profile_updated(self):
return
"""
# install profile1 from an old module version
# then install profile2 from latest module version
# -> dnf forces upgrade profile1 to the latest module version
"""
self.test_install_profile()
self.module_base.install(["httpd:2.4:2/doc"])
self.base.resolve()
expected = [
"basesystem-11-3.noarch",
"filesystem-3.2-40.x86_64",
"glibc-2.25.90-2.x86_64",
"glibc-common-2.25.90-2.x86_64",
"httpd-2.4.25-8.x86_64",
"httpd-doc-2.4.25-8.x86_64",
"libnghttp2-1.21.1-1.x86_64",
]
self.assertInstalls(expected)
def test_install_deps_same_module_version(self):
pass
def test_install_implicit_empty_default_profile(self):
# install module without a 'default' profile
# -> It should raise an error
self.assertRaises(dnf.exceptions.MarkingErrors, self.module_base.install, ["m4:1.4.18"])
# dnf module upgrade / dnf upgrade @
def test_upgrade(self):
pass
def test_upgrade_lower_rpm_nevra(self):
pass
def test_upgrade_lower_module_nsvap(self):
pass
def test_upgrade_missing_profile(self):
pass
# dnf module downgrade / dnf downgrade @
def test_downgrade(self):
pass
# dnf module remove / dnf remove @
def test_remove(self):
pass
def test_remove_shared_rpms(self):
# don't remove RPMs that are part of another installed module / module profile
# also don't remove RPMs that are required by user-installed RPMs
pass
def test_remove_invalid(self):
pass
def test_bare_rpms_filtering(self):
"""
Test hybrid repos where RPMs of the same name (or Provides)
can be both modular and bare (non-modular).
"""
# no match with modular RPM $name -> keep
q = self.base.sack.query().filter(nevra="grub2-2.02-0.40.x86_64")
self.assertEqual(len(q), 1)
# $name matches with modular RPM $name -> exclude
q = self.base.sack.query().filter(nevra="httpd-2.2.10-1.x86_64")
self.assertEqual(len(q), 0)
# Provides: $name matches with modular RPM $name -> exclude
q = self.base.sack.query().filter(nevra="httpd-provides-name-3.0-1.x86_64")
self.assertEqual(len(q), 0)
# Provides: $name = ... matches with modular RPM $name -> exclude
q = self.base.sack.query().filter(nevra="httpd-provides-name-version-release-3.0-1.x86_64")
self.assertEqual(len(q), 0)