From 2bb65408ee15ff3e23450a011521caf07e836098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubom=C3=ADr=20Sedl=C3=A1=C5=99?= Date: Fri, 22 Apr 2016 11:15:06 +0200 Subject: [PATCH] [pkgset] Add tests for KojiPackageSet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a lot of mock objects needed: we bypass calls to Koji, use a mock FileCache that does not need valid RPMs on disk and avoid any multithreading. The test data in tests/fixtures/tagged-rpms.json comes from Koji. It is filtered down to only a few packages to make it manageable. Signed-off-by: Lubomír Sedlář --- pungi/phases/pkgset/pkgsets.py | 1 + tests/fixtures/tagged-rpms.json | 157 +++++++++++++++++++++++ tests/helpers.py | 5 +- tests/test_pkgset_pkgsets.py | 219 ++++++++++++++++++++++++++++++++ 4 files changed, 381 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/tagged-rpms.json create mode 100755 tests/test_pkgset_pkgsets.py diff --git a/pungi/phases/pkgset/pkgsets.py b/pungi/phases/pkgset/pkgsets.py index adc2d071..58f6e6a3 100644 --- a/pungi/phases/pkgset/pkgsets.py +++ b/pungi/phases/pkgset/pkgsets.py @@ -64,6 +64,7 @@ class ReaderThread(WorkerThread): class PackageSetBase(kobo.log.LoggingBase): + def __init__(self, sigkey_ordering, arches=None, logger=None): kobo.log.LoggingBase.__init__(self, logger=logger) self.file_cache = kobo.pkgset.FileCache(kobo.pkgset.SimpleRpmWrapper) diff --git a/tests/fixtures/tagged-rpms.json b/tests/fixtures/tagged-rpms.json new file mode 100644 index 00000000..7a3cbb4f --- /dev/null +++ b/tests/fixtures/tagged-rpms.json @@ -0,0 +1,157 @@ +[ + [ + { + "build_id": 753756, + "name": "pungi", + "extra": null, + "arch": "noarch", + "buildtime": 1460478249, + "id": 7582950, + "epoch": null, + "version": "4.1.3", + "metadata_only": false, + "release": "3.fc25", + "buildroot_id": 5351458, + "payloadhash": "161a677309e0f6e1f1f5c82b4ace9cd4", + "size": 515910 + }, + { + "build_id": 753756, + "name": "pungi", + "extra": null, + "arch": "src", + "buildtime": 1460478105, + "id": 7582949, + "epoch": null, + "version": "4.1.3", + "metadata_only": false, + "release": "3.fc25", + "buildroot_id": 5351458, + "payloadhash": "85d1a317590e239682737beab6dacb25", + "size": 238767 + }, + { + "build_id": 716627, + "name": "bash-debuginfo", + "extra": null, + "arch": "i686", + "buildtime": 1454522240, + "id": 7269614, + "epoch": null, + "version": "4.3.42", + "metadata_only": false, + "release": "4.fc24", + "buildroot_id": 4909606, + "payloadhash": "e3e8d576d79dfbc2b7bd426cc56587fc", + "size": 1743570 + }, + { + "build_id": 716627, + "name": "bash", + "extra": null, + "arch": "i686", + "buildtime": 1454522240, + "id": 7269612, + "epoch": null, + "version": "4.3.42", + "metadata_only": false, + "release": "4.fc24", + "buildroot_id": 4909606, + "payloadhash": "f61a36e228aa9ee7a9109a69a5ad86fc", + "size": 1496786 + }, + { + "build_id": 716627, + "name": "bash-debuginfo", + "extra": null, + "arch": "x86_64", + "buildtime": 1454522175, + "id": 7269608, + "epoch": null, + "version": "4.3.42", + "metadata_only": false, + "release": "4.fc24", + "buildroot_id": 4909605, + "payloadhash": "1b0f2835eab14b58e3d5cd91f9a14b4e", + "size": 1776734 + }, + { + "build_id": 716627, + "name": "bash", + "extra": null, + "arch": "x86_64", + "buildtime": 1454522175, + "id": 7269607, + "epoch": null, + "version": "4.3.42", + "metadata_only": false, + "release": "4.fc24", + "buildroot_id": 4909605, + "payloadhash": "7a7a4671c2f3df055fadfb3a80d64bd2", + "size": 1488054 + }, + { + "build_id": 716627, + "name": "bash", + "extra": null, + "arch": "src", + "buildtime": 1454522120, + "id": 7269602, + "epoch": null, + "version": "4.3.42", + "metadata_only": false, + "release": "4.fc24", + "buildroot_id": 4909580, + "payloadhash": "a3828d9e797352cc091896c0a02c3011", + "size": 8061934 + } + ], + [ + { + "build_id": 753756, + "tag_name": "f25", + "owner_name": "ausil", + "package_name": "pungi", + "task_id": 13636602, + "volume_name": "DEFAULT", + "start_time": "2016-04-12 16:20:18.865725", + "creation_event_id": 15609005, + "creation_time": "2016-04-12 16:20:18.865725", + "epoch": null, + "tag_id": 335, + "name": "pungi", + "completion_time": "2016-04-12 16:26:23.83646", + "state": 1, + "version": "4.1.3", + "volume_id": 0, + "release": "3.fc25", + "package_id": 3528, + "owner_id": 131, + "id": 753756, + "nvr": "pungi-4.1.3-3.fc25" + }, + { + "build_id": 716627, + "tag_name": "f25", + "owner_name": "releng", + "package_name": "bash", + "task_id": 12807455, + "volume_name": "DEFAULT", + "start_time": "2016-02-03 17:34:58.904133", + "creation_event_id": 14505842, + "creation_time": "2016-02-03 17:34:58.904133", + "epoch": null, + "tag_id": 335, + "name": "bash", + "completion_time": "2016-02-03 18:05:26.867167", + "state": 1, + "version": "4.3.42", + "volume_id": 0, + "release": "4.fc24", + "package_id": 1088, + "owner_id": 3445, + "id": 716627, + "nvr": "bash-4.3.42-4.fc24" + } + ] +] diff --git a/tests/helpers.py b/tests/helpers.py index 42556965..d0049af4 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -94,8 +94,11 @@ def touch(path, content=None): f.write(content) +FIXTURE_DIR = os.path.join(os.path.dirname(__file__), 'fixtures') + + def copy_fixture(fixture_name, dest): - src = os.path.join(os.path.dirname(__file__), 'fixtures', fixture_name) + src = os.path.join(FIXTURE_DIR, fixture_name) touch(dest) shutil.copy2(src, dest) diff --git a/tests/test_pkgset_pkgsets.py b/tests/test_pkgset_pkgsets.py new file mode 100755 index 00000000..0374c169 --- /dev/null +++ b/tests/test_pkgset_pkgsets.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +import mock +import os +import sys +import unittest +import json +import functools + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) + +from pungi.phases.pkgset import pkgsets +from tests import helpers + + +class MockPathInfo(object): + def __init__(self, topdir): + self.topdir = topdir + + def build(self, build_info): + return self.topdir + + def get_filename(self, rpm_info): + return '{name}@{version}@{release}@{arch}'.format(**rpm_info) + + def signed(self, rpm_info, sigkey): + return os.path.join('signed', sigkey, self.get_filename(rpm_info)) + + def rpm(self, rpm_info): + return os.path.join('rpms', self.get_filename(rpm_info)) + + +@functools.total_ordering +class MockFile(object): + def __init__(self, path): + if path.startswith('/tmp'): + # Drop /tmp/something/ from path + path = path.split('/', 3)[-1] + self.path = path + self.file_name = os.path.basename(path) + self.name, self.version, self.release, self.arch = self.file_name.split('@') + self.sourcerpm = self.name + + def __hash__(self): + return hash(self.path) + + def __repr__(self): + return self.path + + def __eq__(self, other): + try: + return self.path == other.path + except AttributeError: + return self.path == other + + def __le__(self, other): + try: + return self.path < other.path + except AttributeError: + return self.path < other + + +class MockFileCache(dict): + """Mock for kobo.pkgset.FileCache. + It gets data from filename and does not touch filesystem. + """ + def __init__(self, _wrapper): + super(MockFileCache, self).__init__() + + def add(self, file_path): + obj = MockFile(file_path) + self[file_path] = obj + return obj + + +class FakePool(object): + """This class will be substituted for ReaderPool. + It implements the same interface, but uses only the last added worker to + process all tasks sequentially. + """ + def __init__(self, package_set, logger=None): + self.queue = [] + self.worker = None + self.package_set = package_set + + def log_warning(self, *args, **kwargs): + pass + + @property + def queue_total(self): + return len(self.queue) + + def queue_put(self, item): + self.queue.append(item) + + def add(self, worker): + self.worker = worker + + def start(self): + for i, item in enumerate(self.queue): + self.worker.process(item, i) + + def stop(self): + pass + + +@mock.patch('pungi.phases.pkgset.pkgsets.ReaderPool', new=FakePool) +@mock.patch('kobo.pkgset.FileCache', new=MockFileCache) +class TestKojiPkgset(helpers.PungiTestCase): + + def setUp(self): + super(TestKojiPkgset, self).setUp() + with open(os.path.join(helpers.FIXTURE_DIR, 'tagged-rpms.json')) as f: + self.tagged_rpms = json.load(f) + + self.path_info = MockPathInfo(self.topdir) + + self.koji_wrapper = mock.Mock() + self.koji_wrapper.koji_proxy.listTaggedRPMS.return_value = self.tagged_rpms + self.koji_wrapper.koji_module.pathinfo = self.path_info + + def _touch_files(self, filenames): + for filename in filenames: + helpers.touch(os.path.join(self.topdir, filename)) + + def assertPkgsetEqual(self, actual, expected): + for k, v1 in expected.iteritems(): + self.assertIn(k, actual) + v2 = actual.pop(k) + self.assertItemsEqual(v1, v2) + self.assertEqual({}, actual, msg='Some architectures were missing') + + def test_all_arches(self): + self._touch_files([ + 'rpms/pungi@4.1.3@3.fc25@noarch', + 'rpms/pungi@4.1.3@3.fc25@src', + 'rpms/bash@4.3.42@4.fc24@i686', + 'rpms/bash@4.3.42@4.fc24@x86_64', + 'rpms/bash@4.3.42@4.fc24@src', + 'rpms/bash-debuginfo@4.3.42@4.fc24@i686', + 'rpms/bash-debuginfo@4.3.42@4.fc24@x86_64', + ]) + + pkgset = pkgsets.KojiPackageSet(self.koji_wrapper, [None]) + + result = pkgset.populate('f25') + + self.assertEqual( + self.koji_wrapper.koji_proxy.mock_calls, + [mock.call.listTaggedRPMS('f25', event=None, inherit=True, latest=True)]) + + self.assertPkgsetEqual(result, + {'src': ['rpms/pungi@4.1.3@3.fc25@src', + 'rpms/bash@4.3.42@4.fc24@src'], + 'noarch': ['rpms/pungi@4.1.3@3.fc25@noarch'], + 'i686': ['rpms/bash@4.3.42@4.fc24@i686', + 'rpms/bash-debuginfo@4.3.42@4.fc24@i686'], + 'x86_64': ['rpms/bash@4.3.42@4.fc24@x86_64', + 'rpms/bash-debuginfo@4.3.42@4.fc24@x86_64']}) + + def test_only_one_arch(self): + self._touch_files([ + 'rpms/bash@4.3.42@4.fc24@x86_64', + 'rpms/bash-debuginfo@4.3.42@4.fc24@x86_64', + ]) + + pkgset = pkgsets.KojiPackageSet(self.koji_wrapper, [None], arches=['x86_64']) + + result = pkgset.populate('f25') + + self.assertEqual( + self.koji_wrapper.koji_proxy.mock_calls, + [mock.call.listTaggedRPMS('f25', event=None, inherit=True, latest=True)]) + + self.assertPkgsetEqual(result, + {'x86_64': ['rpms/bash-debuginfo@4.3.42@4.fc24@x86_64', + 'rpms/bash@4.3.42@4.fc24@x86_64']}) + + def test_find_signed_with_preference(self): + self._touch_files([ + 'signed/cafebabe/bash@4.3.42@4.fc24@x86_64', + 'signed/deadbeef/bash@4.3.42@4.fc24@x86_64', + 'signed/deadbeef/bash-debuginfo@4.3.42@4.fc24@x86_64', + ]) + + pkgset = pkgsets.KojiPackageSet(self.koji_wrapper, ['cafebabe', 'deadbeef'], arches=['x86_64']) + + result = pkgset.populate('f25') + + self.assertEqual( + self.koji_wrapper.koji_proxy.mock_calls, + [mock.call.listTaggedRPMS('f25', event=None, inherit=True, latest=True)]) + + self.assertPkgsetEqual(result, + {'x86_64': ['signed/cafebabe/bash@4.3.42@4.fc24@x86_64', + 'signed/deadbeef/bash-debuginfo@4.3.42@4.fc24@x86_64']}) + + def test_find_signed_fallback_unsigned(self): + self._touch_files([ + 'signed/cafebabe/bash@4.3.42@4.fc24@x86_64', + 'rpms/bash-debuginfo@4.3.42@4.fc24@x86_64', + ]) + + pkgset = pkgsets.KojiPackageSet(self.koji_wrapper, ['cafebabe', None], arches=['x86_64']) + + result = pkgset.populate('f25') + + self.assertEqual( + self.koji_wrapper.koji_proxy.mock_calls, + [mock.call.listTaggedRPMS('f25', event=None, inherit=True, latest=True)]) + + self.assertPkgsetEqual(result, + {'x86_64': ['rpms/bash-debuginfo@4.3.42@4.fc24@x86_64', + 'signed/cafebabe/bash@4.3.42@4.fc24@x86_64']}) + + +if __name__ == "__main__": + unittest.main()