| |
@@ -0,0 +1,380 @@
|
| |
+ import os
|
| |
+
|
| |
+ try:
|
| |
+ from unittest import mock
|
| |
+ except ImportError:
|
| |
+ import mock
|
| |
+
|
| |
+ from pungi.phases.kiwibuild import KiwiBuildPhase, RunKiwiBuildThread
|
| |
+ from tests.helpers import DummyCompose, PungiTestCase
|
| |
+
|
| |
+
|
| |
+ MINIMAL_CONF = {
|
| |
+ "description_scm": "https://example.com/kiwi.git",
|
| |
+ "description_path": "Fedora.kiwi",
|
| |
+ "kiwi_profile": "Cloud-Base-Generic",
|
| |
+ }
|
| |
+
|
| |
+
|
| |
+ def _merge(a, b):
|
| |
+ """This would be a | b on 3.9 and later, or {**a, **b} or 3.5 and later."""
|
| |
+ c = a.copy()
|
| |
+ c.update(b)
|
| |
+ return c
|
| |
+
|
| |
+
|
| |
+ @mock.patch("pungi.phases.kiwibuild.ThreadPool")
|
| |
+ class TestKiwiBuildPhase(PungiTestCase):
|
| |
+ def test_minimal(self, ThreadPool):
|
| |
+ cfg = _merge({"target": "f40"}, MINIMAL_CONF)
|
| |
+ compose = DummyCompose(self.topdir, {"kiwibuild": {"^Server$": [cfg]}})
|
| |
+
|
| |
+ self.assertValidConfig(compose.conf)
|
| |
+
|
| |
+ phase = KiwiBuildPhase(compose)
|
| |
+
|
| |
+ phase.run()
|
| |
+ phase.pool.add.assert_called()
|
| |
+ assert phase.pool.queue_put.call_args_list == [
|
| |
+ mock.call(
|
| |
+ (
|
| |
+ compose,
|
| |
+ compose.variants["Server"],
|
| |
+ cfg,
|
| |
+ ["amd64", "x86_64"],
|
| |
+ None, # release
|
| |
+ "f40",
|
| |
+ [self.topdir + "/compose/Server/$arch/os"],
|
| |
+ [], # failable arches
|
| |
+ )
|
| |
+ )
|
| |
+ ]
|
| |
+
|
| |
+ def test_full(self, ThreadPool):
|
| |
+ cfg = _merge(
|
| |
+ {
|
| |
+ "target": "f40",
|
| |
+ "release": "1234",
|
| |
+ "arches": ["x86_64"],
|
| |
+ "repos": ["https://example.com/repo/", "Client"],
|
| |
+ "failable": ["*"],
|
| |
+ "subvariant": "Test",
|
| |
+ },
|
| |
+ MINIMAL_CONF,
|
| |
+ )
|
| |
+ compose = DummyCompose(self.topdir, {"kiwibuild": {"^Server$": [cfg]}})
|
| |
+
|
| |
+ self.assertValidConfig(compose.conf)
|
| |
+
|
| |
+ phase = KiwiBuildPhase(compose)
|
| |
+
|
| |
+ phase.run()
|
| |
+ phase.pool.add.assert_called()
|
| |
+ assert phase.pool.queue_put.call_args_list == [
|
| |
+ mock.call(
|
| |
+ (
|
| |
+ compose,
|
| |
+ compose.variants["Server"],
|
| |
+ cfg,
|
| |
+ ["x86_64"],
|
| |
+ "1234",
|
| |
+ "f40",
|
| |
+ [
|
| |
+ "https://example.com/repo/",
|
| |
+ self.topdir + "/compose/Client/$arch/os",
|
| |
+ self.topdir + "/compose/Server/$arch/os",
|
| |
+ ],
|
| |
+ ["x86_64"],
|
| |
+ )
|
| |
+ )
|
| |
+ ]
|
| |
+
|
| |
+ def test_failable(self, ThreadPool):
|
| |
+ cfg = _merge({"target": "f40", "failable": ["x86_64"]}, MINIMAL_CONF)
|
| |
+ compose = DummyCompose(self.topdir, {"kiwibuild": {"^Server$": [cfg]}})
|
| |
+
|
| |
+ self.assertValidConfig(compose.conf)
|
| |
+
|
| |
+ phase = KiwiBuildPhase(compose)
|
| |
+
|
| |
+ phase.run()
|
| |
+ phase.pool.add.assert_called()
|
| |
+ assert phase.pool.queue_put.call_args_list == [
|
| |
+ mock.call(
|
| |
+ (
|
| |
+ compose,
|
| |
+ compose.variants["Server"],
|
| |
+ cfg,
|
| |
+ ["amd64", "x86_64"],
|
| |
+ None, # release
|
| |
+ "f40",
|
| |
+ [self.topdir + "/compose/Server/$arch/os"],
|
| |
+ ["x86_64"], # failable arches
|
| |
+ )
|
| |
+ )
|
| |
+ ]
|
| |
+
|
| |
+ def test_with_phase_opts(self, ThreadPool):
|
| |
+ cfg = MINIMAL_CONF
|
| |
+ compose = DummyCompose(
|
| |
+ self.topdir,
|
| |
+ {
|
| |
+ "kiwibuild": {"^Server$": [cfg]},
|
| |
+ "kiwibuild_target": "f40",
|
| |
+ "kiwibuild_release": "1234",
|
| |
+ },
|
| |
+ )
|
| |
+
|
| |
+ self.assertValidConfig(compose.conf)
|
| |
+
|
| |
+ phase = KiwiBuildPhase(compose)
|
| |
+
|
| |
+ phase.run()
|
| |
+ phase.pool.add.assert_called()
|
| |
+ assert phase.pool.queue_put.call_args_list == [
|
| |
+ mock.call(
|
| |
+ (
|
| |
+ compose,
|
| |
+ compose.variants["Server"],
|
| |
+ cfg,
|
| |
+ ["amd64", "x86_64"],
|
| |
+ "1234", # release
|
| |
+ "f40",
|
| |
+ [self.topdir + "/compose/Server/$arch/os"],
|
| |
+ [], # failable arches
|
| |
+ )
|
| |
+ )
|
| |
+ ]
|
| |
+
|
| |
+ def test_with_global_opts(self, ThreadPool):
|
| |
+ cfg = MINIMAL_CONF
|
| |
+ compose = DummyCompose(
|
| |
+ self.topdir,
|
| |
+ {
|
| |
+ "kiwibuild": {"^Server$": [cfg]},
|
| |
+ "global_target": "f40",
|
| |
+ "global_release": "1234",
|
| |
+ },
|
| |
+ )
|
| |
+
|
| |
+ self.assertValidConfig(compose.conf)
|
| |
+
|
| |
+ phase = KiwiBuildPhase(compose)
|
| |
+
|
| |
+ phase.run()
|
| |
+ phase.pool.add.assert_called()
|
| |
+ assert phase.pool.queue_put.call_args_list == [
|
| |
+ mock.call(
|
| |
+ (
|
| |
+ compose,
|
| |
+ compose.variants["Server"],
|
| |
+ cfg,
|
| |
+ ["amd64", "x86_64"],
|
| |
+ "1234", # release
|
| |
+ "f40",
|
| |
+ [self.topdir + "/compose/Server/$arch/os"],
|
| |
+ [], # failable arches
|
| |
+ )
|
| |
+ )
|
| |
+ ]
|
| |
+
|
| |
+
|
| |
+ @mock.patch("pungi.phases.kiwibuild.Linker")
|
| |
+ @mock.patch("pungi.util.get_mtime")
|
| |
+ @mock.patch("pungi.util.get_file_size")
|
| |
+ @mock.patch("pungi.wrappers.kojiwrapper.KojiWrapper")
|
| |
+ class TestKiwiBuildThread(PungiTestCase):
|
| |
+ def _img_path(self, arch, filename=None):
|
| |
+ path = self.topdir + "/compose/Server/%s/images" % arch
|
| |
+ if filename:
|
| |
+ path += "/" + filename
|
| |
+ return path
|
| |
+
|
| |
+ def test_process(self, KojiWrapper, get_file_size, get_mtime, Linker):
|
| |
+ self.repo = self.topdir + "/compose/Server/$arch/os"
|
| |
+ compose = DummyCompose(self.topdir, {"koji_profile": "koji"})
|
| |
+ config = _merge({"subvariant": "Test"}, MINIMAL_CONF)
|
| |
+ pool = mock.Mock()
|
| |
+
|
| |
+ get_image_paths = KojiWrapper.return_value.get_image_paths
|
| |
+ get_image_paths.return_value = {
|
| |
+ "x86_64": [
|
| |
+ "/koji/task/1234/FCBG.x86_64-Rawhide-1.6.packages",
|
| |
+ "/koji/task/1234/FCBG.x86_64-Rawhide-1.6.qcow2",
|
| |
+ ],
|
| |
+ "amd64": [
|
| |
+ "/koji/task/1234/FCBG.amd64-Rawhide-1.6.packages",
|
| |
+ "/koji/task/1234/FCBG.amd64-Rawhide-1.6.qcow2",
|
| |
+ ],
|
| |
+ }
|
| |
+
|
| |
+ KojiWrapper.return_value.koji_proxy.kiwiBuild.return_value = 1234
|
| |
+ KojiWrapper.return_value.watch_task.return_value = 0
|
| |
+
|
| |
+ t = RunKiwiBuildThread(pool)
|
| |
+ get_file_size.return_value = 1024
|
| |
+ get_mtime.return_value = 13579
|
| |
+ t.process(
|
| |
+ (
|
| |
+ compose,
|
| |
+ compose.variants["Server"],
|
| |
+ config,
|
| |
+ ["amd64", "x86_64"],
|
| |
+ "1.6",
|
| |
+ "f40",
|
| |
+ [self.repo],
|
| |
+ [],
|
| |
+ ),
|
| |
+ 1,
|
| |
+ )
|
| |
+
|
| |
+ assert KojiWrapper.return_value.koji_proxy.kiwiBuild.mock_calls == [
|
| |
+ mock.call(
|
| |
+ "f40",
|
| |
+ ["amd64", "x86_64"],
|
| |
+ config["description_scm"],
|
| |
+ config["description_path"],
|
| |
+ profile=config["kiwi_profile"],
|
| |
+ release="1.6",
|
| |
+ repos=[self.repo],
|
| |
+ optional_arches=[],
|
| |
+ )
|
| |
+ ]
|
| |
+ assert get_image_paths.mock_calls == [mock.call(1234)]
|
| |
+ assert os.path.isdir(self._img_path("x86_64"))
|
| |
+ assert os.path.isdir(self._img_path("amd64"))
|
| |
+ assert Linker.return_value.link.has_calls(
|
| |
+ [
|
| |
+ mock.call(
|
| |
+ "/koji/task/1234/FCBG.amd64-Rawhide-1.6.qcow2",
|
| |
+ self._img_path("amd64", "FCBG.amd64-Rawhide-1.6.qcow2"),
|
| |
+ link_type="hardlink-or-copy",
|
| |
+ ),
|
| |
+ mock.call(
|
| |
+ "/koji/task/1234/FCBG.x86_64-Rawhide-1.6.qcow2",
|
| |
+ self._img_path("x86_64", "FCBG.x86_64-Rawhide-1.6.qcow2"),
|
| |
+ link_type="hardlink-or-copy",
|
| |
+ ),
|
| |
+ ]
|
| |
+ )
|
| |
+
|
| |
+ assert len(compose.im.add.call_args_list) == 2
|
| |
+ for call in compose.im.add.call_args_list:
|
| |
+ _, kwargs = call
|
| |
+ image = kwargs["image"]
|
| |
+ expected_path = (
|
| |
+ "Server/{0.arch}/images/FCBG.{0.arch}-Rawhide-1.6.qcow2".format(image)
|
| |
+ )
|
| |
+ assert kwargs["variant"] == "Server"
|
| |
+ assert kwargs["arch"] in ("amd64", "x86_64")
|
| |
+ assert kwargs["arch"] == image.arch
|
| |
+ assert image.path == expected_path
|
| |
+ assert "qcow2" == image.format
|
| |
+ assert "qcow2" == image.type
|
| |
+ assert "Test" == image.subvariant
|
| |
+
|
| |
+ def test_handle_koji_fail(self, KojiWrapper, get_file_size, get_mtime, Linker):
|
| |
+ self.repo = self.topdir + "/compose/Server/$arch/os"
|
| |
+ compose = DummyCompose(self.topdir, {"koji_profile": "koji"})
|
| |
+ config = MINIMAL_CONF
|
| |
+ pool = mock.Mock()
|
| |
+
|
| |
+ get_image_paths = KojiWrapper.return_value.get_image_paths
|
| |
+
|
| |
+ KojiWrapper.return_value.koji_proxy.kiwiBuild.return_value = 1234
|
| |
+ KojiWrapper.return_value.watch_task.return_value = 1
|
| |
+
|
| |
+ t = RunKiwiBuildThread(pool)
|
| |
+ try:
|
| |
+ t.process(
|
| |
+ (
|
| |
+ compose,
|
| |
+ compose.variants["Server"],
|
| |
+ config,
|
| |
+ ["amd64", "x86_64"],
|
| |
+ "1.6",
|
| |
+ "f40",
|
| |
+ [self.repo],
|
| |
+ [],
|
| |
+ ),
|
| |
+ 1,
|
| |
+ )
|
| |
+ assert False, "Exception should have been raised"
|
| |
+ except RuntimeError:
|
| |
+ pass
|
| |
+
|
| |
+ assert len(KojiWrapper.return_value.koji_proxy.kiwiBuild.mock_calls) == 1
|
| |
+ assert get_image_paths.mock_calls == []
|
| |
+ assert Linker.return_value.link.mock_calls == []
|
| |
+ assert len(compose.im.add.call_args_list) == 0
|
| |
+
|
| |
+ def test_handle_fail_on_optional_arch(
|
| |
+ self, KojiWrapper, get_file_size, get_mtime, Linker
|
| |
+ ):
|
| |
+ self.repo = self.topdir + "/compose/Server/$arch/os"
|
| |
+ compose = DummyCompose(self.topdir, {"koji_profile": "koji"})
|
| |
+ config = MINIMAL_CONF
|
| |
+ pool = mock.Mock()
|
| |
+
|
| |
+ get_image_paths = KojiWrapper.return_value.get_image_paths
|
| |
+ get_image_paths.return_value = {
|
| |
+ "x86_64": [
|
| |
+ "/koji/task/1234/FCBG.x86_64-Rawhide-1.6.packages",
|
| |
+ "/koji/task/1234/FCBG.x86_64-Rawhide-1.6.qcow2",
|
| |
+ ],
|
| |
+ }
|
| |
+
|
| |
+ KojiWrapper.return_value.koji_proxy.kiwiBuild.return_value = 1234
|
| |
+ KojiWrapper.return_value.watch_task.return_value = 0
|
| |
+
|
| |
+ t = RunKiwiBuildThread(pool)
|
| |
+ get_file_size.return_value = 1024
|
| |
+ get_mtime.return_value = 13579
|
| |
+ t.process(
|
| |
+ (
|
| |
+ compose,
|
| |
+ compose.variants["Server"],
|
| |
+ config,
|
| |
+ ["amd64", "x86_64"],
|
| |
+ "1.6",
|
| |
+ "f40",
|
| |
+ [self.repo],
|
| |
+ ["amd64"],
|
| |
+ ),
|
| |
+ 1,
|
| |
+ )
|
| |
+
|
| |
+ assert KojiWrapper.return_value.koji_proxy.kiwiBuild.mock_calls == [
|
| |
+ mock.call(
|
| |
+ "f40",
|
| |
+ ["amd64", "x86_64"],
|
| |
+ config["description_scm"],
|
| |
+ config["description_path"],
|
| |
+ profile=config["kiwi_profile"],
|
| |
+ release="1.6",
|
| |
+ repos=[self.repo],
|
| |
+ optional_arches=["amd64"],
|
| |
+ )
|
| |
+ ]
|
| |
+ assert get_image_paths.mock_calls == [mock.call(1234)]
|
| |
+ assert os.path.isdir(self._img_path("x86_64"))
|
| |
+ assert not os.path.isdir(self._img_path("amd64"))
|
| |
+ assert Linker.return_value.link.mock_calls == [
|
| |
+ mock.call(
|
| |
+ "/koji/task/1234/FCBG.x86_64-Rawhide-1.6.qcow2",
|
| |
+ self._img_path("x86_64", "FCBG.x86_64-Rawhide-1.6.qcow2"),
|
| |
+ link_type="hardlink-or-copy",
|
| |
+ ),
|
| |
+ ]
|
| |
+
|
| |
+ assert len(compose.im.add.call_args_list) == 1
|
| |
+ _, kwargs = compose.im.add.call_args_list[0]
|
| |
+ image = kwargs["image"]
|
| |
+ expected_path = "Server/x86_64/images/FCBG.x86_64-Rawhide-1.6.qcow2"
|
| |
+ assert kwargs["variant"] == "Server"
|
| |
+ assert kwargs["arch"] == "x86_64"
|
| |
+ assert kwargs["arch"] == image.arch
|
| |
+ assert image.path == expected_path
|
| |
+ assert "qcow2" == image.format
|
| |
+ assert "qcow2" == image.type
|
| |
+ assert "Server" == image.subvariant
|
| |
To get this to pass locally on Python 3.12, I had to change this to
Linker.return_value.link.assert_has_calls
and addany_order=True
. Without the former I got "AttributeError: 'has_calls' is not a valid assertion. Use a spec for the mock if 'has_calls' is meant to be an attribute.", and without the latter, the test failed because the calls were the other way around from what the assertion expected (not sure why).