From 8a3b64e5b86cd78c52b9f24827258ed962416fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubom=C3=ADr=20Sedl=C3=A1=C5=99?= Date: Mon, 11 Mar 2024 11:05:02 +0100 Subject: [PATCH] kiwibuild: Add tests for the basic functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merges: https://pagure.io/pungi/pull-request/1739 Signed-off-by: Lubomír Sedlář --- tests/test_kiwibuildphase.py | 415 +++++++++++++++++++++++++++++++++++ 1 file changed, 415 insertions(+) create mode 100644 tests/test_kiwibuildphase.py diff --git a/tests/test_kiwibuildphase.py b/tests/test_kiwibuildphase.py new file mode 100644 index 00000000..c2ee7ea4 --- /dev/null +++ b/tests/test_kiwibuildphase.py @@ -0,0 +1,415 @@ +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"], + { + "release": None, + "target": "f40", + "descscm": MINIMAL_CONF["description_scm"], + "descpath": MINIMAL_CONF["description_path"], + }, + [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"], + { + "release": "1234", + "target": "f40", + "descscm": MINIMAL_CONF["description_scm"], + "descpath": MINIMAL_CONF["description_path"], + }, + [ + "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"], + { + "release": None, + "target": "f40", + "descscm": MINIMAL_CONF["description_scm"], + "descpath": MINIMAL_CONF["description_path"], + }, + [self.topdir + "/compose/Server/$arch/os"], + ["x86_64"], # failable arches + ) + ) + ] + + def test_with_phase_opts(self, ThreadPool): + cfg = {"kiwi_profile": "Generic"} + compose = DummyCompose( + self.topdir, + { + "kiwibuild": {"^Server$": [cfg]}, + "kiwibuild_target": "f40", + "kiwibuild_release": "1234", + "kiwibuild_description_scm": "foo", + "kiwibuild_description_path": "bar", + }, + ) + + 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"], + { + "release": "1234", + "target": "f40", + "descscm": "foo", + "descpath": "bar", + }, + [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"], + { + "release": "1234", + "target": "f40", + "descscm": MINIMAL_CONF["description_scm"], + "descpath": MINIMAL_CONF["description_path"], + }, + [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"], + { + "release": "1.6", + "target": "f40", + "descscm": MINIMAL_CONF["description_scm"], + "descpath": MINIMAL_CONF["description_path"], + }, + [self.repo], + [], + ), + 1, + ) + + assert KojiWrapper.return_value.koji_proxy.kiwiBuild.mock_calls == [ + mock.call( + "f40", + ["amd64", "x86_64"], + MINIMAL_CONF["description_scm"], + MINIMAL_CONF["description_path"], + profile=MINIMAL_CONF["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")) + Linker.return_value.link.assert_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", + ), + ], + any_order=True, + ) + + 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"], + { + "release": "1.6", + "target": "f40", + "descscm": MINIMAL_CONF["description_scm"], + "descpath": MINIMAL_CONF["description_path"], + }, + [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"], + { + "release": "1.6", + "target": "f40", + "descscm": MINIMAL_CONF["description_scm"], + "descpath": MINIMAL_CONF["description_path"], + }, + [self.repo], + ["amd64"], + ), + 1, + ) + + assert KojiWrapper.return_value.koji_proxy.kiwiBuild.mock_calls == [ + mock.call( + "f40", + ["amd64", "x86_64"], + MINIMAL_CONF["description_scm"], + MINIMAL_CONF["description_path"], + profile=MINIMAL_CONF["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