From e2a0885081152e2638dce064d0c5d6322b21faca Mon Sep 17 00:00:00 2001 From: Mike McLean Date: Jun 08 2016 19:30:23 +0000 Subject: Merge branch 'test-prs' Various unit test pull requests Merges #105 Merges #106 Merges #107 Merges #108 --- diff --git a/.gitignore b/.gitignore index e28848b..b075196 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ tests/test.py .coverage docs/build/ +cli/kojic diff --git a/tests/test_cli/__init__.py b/tests/test_cli/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/test_cli/__init__.py diff --git a/tests/test_cli/data/list-commands-admin.txt b/tests/test_cli/data/list-commands-admin.txt new file mode 100644 index 0000000..f6f7db2 --- /dev/null +++ b/tests/test_cli/data/list-commands-admin.txt @@ -0,0 +1,71 @@ +Available commands: + +admin commands: + add-external-repo Create an external repo and/or add one to a tag + add-group Add a group to a tag + add-group-pkg Add a package to a group's package listing + add-group-req Add a group to a group's required list + add-host Add a host + add-host-to-channel Add a host to a channel + add-pkg Add a package to the listing for tag + add-tag Add a new tag to the database + add-tag-inheritance Add to a tag's inheritance + add-target Create a new build target + add-user Add a user + add-volume Add a new storage volume + block-group-pkg Block a package from a group's package listing + block-group-req Block a group's requirement listing + block-pkg Block a package in the listing for tag + clone-tag Duplicate the contents of one tag onto another tag + disable-host Mark one or more hosts as disabled + disable-user Disable logins by a user + edit-external-repo Edit data for an external repo + edit-host Edit a host + edit-tag Alter tag information + edit-tag-inheritance Edit tag inheritance + edit-target Set the name, build_tag, and/or dest_tag of an existing build target to new values + enable-host Mark one or more hosts as enabled + enable-user Enable logins by a user + free-task Free a task + grant-cg-access Add a user to a content generator + grant-permission Grant a permission to a user + import Import externally built RPMs into the database + import-archive Import an archive file and associate it with a build + import-cg Import external builds with rich metadata + import-in-place Import RPMs that are already in place + import-sig Import signatures into the database + list-permissions List user permissions + list-signed List signed copies of rpms + lock-tag Lock a tag + make-task Create an arbitrary task + prune-signed-copies Prune signed copies + regen-repo Force a repo to be regenerated + remove-channel Remove a channel entirely + remove-external-repo Remove an external repo from a tag or tags, or remove entirely + remove-host-from-channel Remove a host from a channel + remove-pkg Remove a package from the listing for tag + remove-tag Remove a tag + remove-tag-inheritance Remove a tag inheritance link + remove-target Remove a build target + rename-channel Rename a channel + restart-hosts Restart enabled hosts + revoke-cg-access Remove a user from a content generator + revoke-permission Revoke a permission from a user + runroot Run a command in a buildroot + set-build-volume Move a build to a different volume + set-pkg-arches Set the list of extra arches for a package + set-pkg-owner Set the owner for a package + set-pkg-owner-global Set the owner for a package globally + set-task-priority Set task priority + spin-livemedia Create a livemedia image given a kickstart file + unblock-group-pkg Unblock a package from a group's package listing + unblock-group-req Unblock a group's requirement listing + unblock-pkg Unblock a package in the listing for tag + unlock-tag Unlock a tag + write-signed-rpm Write signed RPMs to disk + +Try "koji --help" for help about global options +Try "koji help" to get all available commands +Try "koji --help" for help about the options of a particular command +Try "koji help " to get commands under a particular category +Available categories are: admin, all, bind, build, download, info, misc, monitor, search diff --git a/tests/test_cli/data/list-commands.txt b/tests/test_cli/data/list-commands.txt new file mode 100644 index 0000000..d2b991f --- /dev/null +++ b/tests/test_cli/data/list-commands.txt @@ -0,0 +1,133 @@ +Available commands: + +admin commands: + add-external-repo Create an external repo and/or add one to a tag + add-group Add a group to a tag + add-group-pkg Add a package to a group's package listing + add-group-req Add a group to a group's required list + add-host Add a host + add-host-to-channel Add a host to a channel + add-pkg Add a package to the listing for tag + add-tag Add a new tag to the database + add-tag-inheritance Add to a tag's inheritance + add-target Create a new build target + add-user Add a user + add-volume Add a new storage volume + block-group-pkg Block a package from a group's package listing + block-group-req Block a group's requirement listing + block-pkg Block a package in the listing for tag + clone-tag Duplicate the contents of one tag onto another tag + disable-host Mark one or more hosts as disabled + disable-user Disable logins by a user + edit-external-repo Edit data for an external repo + edit-host Edit a host + edit-tag Alter tag information + edit-tag-inheritance Edit tag inheritance + edit-target Set the name, build_tag, and/or dest_tag of an existing build target to new values + enable-host Mark one or more hosts as enabled + enable-user Enable logins by a user + free-task Free a task + grant-cg-access Add a user to a content generator + grant-permission Grant a permission to a user + import Import externally built RPMs into the database + import-archive Import an archive file and associate it with a build + import-cg Import external builds with rich metadata + import-in-place Import RPMs that are already in place + import-sig Import signatures into the database + list-permissions List user permissions + list-signed List signed copies of rpms + lock-tag Lock a tag + make-task Create an arbitrary task + prune-signed-copies Prune signed copies + regen-repo Force a repo to be regenerated + remove-channel Remove a channel entirely + remove-external-repo Remove an external repo from a tag or tags, or remove entirely + remove-host-from-channel Remove a host from a channel + remove-pkg Remove a package from the listing for tag + remove-tag Remove a tag + remove-tag-inheritance Remove a tag inheritance link + remove-target Remove a build target + rename-channel Rename a channel + restart-hosts Restart enabled hosts + revoke-cg-access Remove a user from a content generator + revoke-permission Revoke a permission from a user + runroot Run a command in a buildroot + set-build-volume Move a build to a different volume + set-pkg-arches Set the list of extra arches for a package + set-pkg-owner Set the owner for a package + set-pkg-owner-global Set the owner for a package globally + set-task-priority Set task priority + spin-livemedia Create a livemedia image given a kickstart file + unblock-group-pkg Unblock a package from a group's package listing + unblock-group-req Unblock a group's requirement listing + unblock-pkg Unblock a package in the listing for tag + unlock-tag Unlock a tag + write-signed-rpm Write signed RPMs to disk + +bind commands: + move-build 'Move' one or more builds between tags + tag-build Apply a tag to one or more builds + untag-build Remove a tag from one or more builds + +build commands: + build Build a package from source + cancel Cancel tasks and/or builds + chain-build Build one or more packages from source + image-build Create a disk image given an install tree + image-build-indirection Create a disk image using other disk images via the Indirection plugin + maven-build Build a Maven package from source + maven-chain Run a set of Maven builds in dependency order + resubmit Retry a canceled or failed task, using the same parameter as the original task. + spin-appliance Create an appliance given a kickstart file + spin-livecd Create a live CD image given a kickstart file + win-build Build a Windows package from source + wrapper-rpm Build wrapper rpms for any archives associated with a build. + +download commands: + download-build Download a built package + download-logs Download a logs for package + download-task Download the output of a build task + +info commands: + buildinfo Print basic information about a build + help List available commands + latest-build Print the latest builds for a tag + list-api Print the list of XML-RPC APIs + list-buildroot List the rpms used in or built in a buildroot + list-external-repos List external repos + list-groups Print the group listings + list-history Display historical data + list-hosts Print the host listing + list-pkgs Print the package listing for tag or for owner + list-tag-history Print a history of tag operations + list-tag-inheritance Print the inheritance information for a tag + list-tagged List the builds or rpms in a tag + list-tags Print the list of tags + list-targets List the build targets + list-tasks Print the list of tasks + list-untagged List untagged builds + list-volumes List storage volumes + mock-config Create a mock config + rpminfo Print basic information about an RPM + show-groups Show groups data for a tag + taginfo Print basic information about a tag + taskinfo Show information about a task + +miscellaneous commands: + call Execute an arbitrary XML-RPC call + import-comps Import group/package information from a comps file + moshimoshi Introduce yourself + +monitor commands: + wait-repo Wait for a repo to be regenerated + watch-logs Watch logs in realtime + watch-task Track progress of particular tasks + +search commands: + search Search the system + +Try "koji --help" for help about global options +Try "koji help" to get all available commands +Try "koji --help" for help about the options of a particular command +Try "koji help " to get commands under a particular category +Available categories are: admin, all, bind, build, download, info, misc, monitor, search diff --git a/tests/test_cli/test_list_commands.py b/tests/test_cli/test_list_commands.py new file mode 100644 index 0000000..a4de0d9 --- /dev/null +++ b/tests/test_cli/test_list_commands.py @@ -0,0 +1,60 @@ +import os +import sys +import unittest + +import StringIO as stringio + +import mock + + +# We have to do this craziness because 'import koji' is ambiguous. Is it the +# koji module, or the koji cli module. Jump through hoops accordingly. +# http://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path +CLI_FILENAME = os.path.dirname(__file__) + "/../../cli/koji" +if sys.version_info[0] >= 3: + import importlib.util + spec = importlib.util.spec_from_file_location("koji_cli", CLI_FILENAME) + cli = importlib.util.module_from_spec(spec) + spec.loader.exec_module(cli) +else: + import imp + cli = imp.load_source('koji_cli', CLI_FILENAME) + + +class TestListCommands(unittest.TestCase): + def setUp(self): + self.options = mock.MagicMock() + self.session = mock.MagicMock() + self.args = mock.MagicMock() + self.original_parser = cli.OptionParser + cli.OptionParser = mock.MagicMock() + self.parser = cli.OptionParser.return_value + + def tearDown(self): + cli.OptionParser = self.original_parser + + # Show long diffs in error output... + maxDiff = None + + @mock.patch('sys.stdout', new_callable=stringio.StringIO) + def test_list_commands(self, stdout): + cli.list_commands() + actual = stdout.getvalue() + actual = actual.replace('nosetests', 'koji') + filename = os.path.dirname(__file__) + '/data/list-commands.txt' + with open(filename, 'rb') as f: + expected = f.read().decode('ascii') + self.assertMultiLineEqual(actual, expected) + + @mock.patch('sys.stdout', new_callable=stringio.StringIO) + def test_handle_admin_help(self, stdout): + options, arguments = mock.MagicMock(), mock.MagicMock() + options.admin = True + self.parser.parse_args.return_value = [options, arguments] + cli.handle_help(self.options, self.session, self.args) + actual = stdout.getvalue() + actual = actual.replace('nosetests', 'koji') + filename = os.path.dirname(__file__) + '/data/list-commands-admin.txt' + with open(filename, 'rb') as f: + expected = f.read().decode('ascii') + self.assertMultiLineEqual(actual, expected) diff --git a/tests/test_cli/test_runroot.py b/tests/test_cli/test_runroot.py new file mode 100644 index 0000000..2dd473e --- /dev/null +++ b/tests/test_cli/test_runroot.py @@ -0,0 +1,71 @@ +import os +import sys +import unittest + +import StringIO as stringio + +import mock + + +# We have to do this craziness because 'import koji' is ambiguous. Is it the +# koji module, or the koji cli module. Jump through hoops accordingly. +# http://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path +CLI_FILENAME = os.path.dirname(__file__) + "/../../cli/koji" +if sys.version_info[0] >= 3: + import importlib.util + spec = importlib.util.spec_from_file_location("koji_cli", CLI_FILENAME) + cli = importlib.util.module_from_spec(spec) + spec.loader.exec_module(cli) +else: + import imp + cli = imp.load_source('koji_cli', CLI_FILENAME) + + +class TestListCommands(unittest.TestCase): + def setUp(self): + self.options = mock.MagicMock() + self.session = mock.MagicMock() + self.args = mock.MagicMock() + self.original_parser = cli.OptionParser + cli.OptionParser = mock.MagicMock() + self.parser = cli.OptionParser.return_value + cli.options = self.options # globals!!! + + def tearDown(self): + cli.OptionParser = self.original_parser + + # Show long diffs in error output... + maxDiff = None + + @mock.patch('sys.stdout', new_callable=stringio.StringIO) + def test_handle_runroot(self, stdout): + tag = 'tag' + arch = 'arch' + command = 'command' + arguments = [tag, arch, command] + options = mock.MagicMock() + options.admin = True + self.parser.parse_args.return_value = [options, arguments] + + # Mock out the xmlrpc server + self.session.getTaskInfo.return_value = {'state': 1} + self.session.downloadTaskOutput.return_value = 'task output' + self.session.listTaskOutput.return_value = ['runroot.log'] + self.session.runroot.return_value = 1 + + # Run it and check immediate output + cli.handle_runroot(self.options, self.session, self.args) + actual = stdout.getvalue() + actual = actual.replace('nosetests', 'koji') + expected = 'successfully connected to hub\n1\ntask output' + self.assertMultiLineEqual(actual, expected) + + # Finally, assert that things were called as we expected. + self.session.getTaskInfo.assert_called_once_with(1) + self.session.listTaskOutput.assert_called_once_with(1) + self.session.downloadTaskOutput.assert_called_once_with(1, 'runroot.log') + self.session.runroot.assert_called_once_with( + tag, arch, command, repo_id=mock.ANY, weight=mock.ANY, + mounts=mock.ANY, packages=mock.ANY, skip_setarch=mock.ANY, + channel=mock.ANY, + ) diff --git a/tests/test_hub/test_import_build.py b/tests/test_hub/test_import_build.py new file mode 100644 index 0000000..b338757 --- /dev/null +++ b/tests/test_hub/test_import_build.py @@ -0,0 +1,265 @@ +import copy +import mock +import shutil +import tempfile +import unittest + +import koji +import kojihub + + +class TestImportRPM(unittest.TestCase): + def setUp(self): + self.tempdir = tempfile.mkdtemp() + self.filename = self.tempdir + "/name-version-release.arch.rpm" + # Touch a file + with open(self.filename, 'w'): + pass + self.src_filename = self.tempdir + "/name-version-release.src.rpm" + # Touch a file + with open(self.src_filename, 'w'): + pass + + self.rpm_header_retval = { + 'filename': 'name-version-release.arch.rpm', + 1000: 'name', + 1001: 'version', + 1002: 'release', + 1003: 'epoch', + 1006: 'buildtime', + 1022: 'arch', + 1044: 'name-version-release.arch', + 1106: 'sourcepackage', + 261: 'payload hash', + } + + def tearDown(self): + shutil.rmtree(self.tempdir) + + def test_nonexistant_rpm(self): + with self.assertRaises(koji.GenericError): + kojihub.import_rpm("this does not exist") + + @mock.patch('kojihub.get_build') + @mock.patch('koji.get_rpm_header') + def test_import_rpm_failed_build(self, get_rpm_header, get_build): + get_rpm_header.return_value = self.rpm_header_retval + get_build.return_value = { + 'state': koji.BUILD_STATES['FAILED'], + 'name': 'name', + 'version': 'version', + 'release': 'release', + } + with self.assertRaises(koji.GenericError): + kojihub.import_rpm(self.filename) + + @mock.patch('kojihub._dml') + @mock.patch('kojihub._singleValue') + @mock.patch('kojihub.get_build') + @mock.patch('koji.get_rpm_header') + def test_import_rpm_completed_build(self, get_rpm_header, get_build, + _singleValue, _dml): + get_rpm_header.return_value = self.rpm_header_retval + get_build.return_value = { + 'state': koji.BUILD_STATES['COMPLETE'], + 'name': 'name', + 'version': 'version', + 'release': 'release', + 'id': 12345, + } + _singleValue.return_value = 9876 + kojihub.import_rpm(self.filename) + fields = [ + 'build_id', + 'name', + 'arch', + 'buildtime', + 'payloadhash', + 'epoch', + 'version', + 'buildroot_id', + 'release', + 'external_repo_id', + 'id', + 'size', + ] + statement = 'INSERT INTO rpminfo (%s) VALUES (%s)' % ( + ", ".join(fields), + ", ".join(['%%(%s)s' % field for field in fields]) + ) + values = { + 'build_id': 12345, + 'name': 'name', + 'arch': 'arch', + 'buildtime': 'buildtime', + 'payloadhash': '7061796c6f61642068617368', + 'epoch': 'epoch', + 'version': 'version', + 'buildroot_id': None, + 'release': 'release', + 'external_repo_id': 0, + 'id': 9876, + 'size': 0, + } + _dml.assert_called_once_with(statement, values) + + @mock.patch('kojihub._dml') + @mock.patch('kojihub._singleValue') + @mock.patch('kojihub.get_build') + @mock.patch('koji.get_rpm_header') + def test_import_rpm_completed_source_build(self, get_rpm_header, get_build, + _singleValue, _dml): + retval = copy.copy(self.rpm_header_retval) + retval.update({ + 'filename': 'name-version-release.arch.rpm', + 1044: 'name-version-release.src', + 1022: 'src', + 1106: 1, + }) + get_rpm_header.return_value = retval + get_build.return_value = { + 'state': koji.BUILD_STATES['COMPLETE'], + 'name': 'name', + 'version': 'version', + 'release': 'release', + 'id': 12345, + } + _singleValue.return_value = 9876 + kojihub.import_rpm(self.src_filename) + fields = [ + 'build_id', + 'name', + 'arch', + 'buildtime', + 'payloadhash', + 'epoch', + 'version', + 'buildroot_id', + 'release', + 'external_repo_id', + 'id', + 'size', + ] + statement = 'INSERT INTO rpminfo (%s) VALUES (%s)' % ( + ", ".join(fields), + ", ".join(['%%(%s)s' % field for field in fields]) + ) + values = { + 'build_id': 12345, + 'name': 'name', + 'arch': 'src', + 'buildtime': 'buildtime', + 'payloadhash': '7061796c6f61642068617368', + 'epoch': 'epoch', + 'version': 'version', + 'buildroot_id': None, + 'release': 'release', + 'external_repo_id': 0, + 'id': 9876, + 'size': 0, + } + _dml.assert_called_once_with(statement, values) + + +class TestImportBuild(unittest.TestCase): + def setUp(self): + self.tempdir = tempfile.mkdtemp() + self.filename = self.tempdir + "/name-version-release.arch.rpm" + # Touch a file + with open(self.filename, 'w'): + pass + self.src_filename = self.tempdir + "/name-version-release.src.rpm" + # Touch a file + with open(self.src_filename, 'w'): + pass + + self.rpm_header_retval = { + 'filename': 'name-version-release.arch.rpm', + 1000: 'name', + 1001: 'version', + 1002: 'release', + 1003: 'epoch', + 1006: 'buildtime', + 1022: 'arch', + 1044: 'name-version-release.arch', + 1106: 'sourcepackage', + 261: 'payload hash', + } + + def tearDown(self): + shutil.rmtree(self.tempdir) + + @mock.patch('kojihub._dml') + @mock.patch('kojihub._singleValue') + @mock.patch('kojihub.get_build') + @mock.patch('kojihub.add_rpm_sig') + @mock.patch('koji.rip_rpm_sighdr') + @mock.patch('kojihub.import_rpm_file') + @mock.patch('kojihub.import_rpm') + @mock.patch('kojihub.QueryProcessor') + @mock.patch('kojihub.context') + @mock.patch('kojihub.new_package') + @mock.patch('koji.get_rpm_header') + @mock.patch('koji.pathinfo.work') + def test_import_build_completed_build(self, work, get_rpm_header, + new_package, context, query, + import_rpm, import_rpm_file, + rip_rpm_sighdr, add_rpm_sig, + get_build, _singleValue, _dml): + + rip_rpm_sighdr.return_value = (0, 0) + + processor = mock.MagicMock() + processor.executeOne.return_value = None + query.return_value = processor + + context.session.user_id = 99 + + work.return_value = '/' + + retval = copy.copy(self.rpm_header_retval) + retval.update({ + 'filename': 'name-version-release.arch.rpm', + 1044: 'name-version-release.src', + 1022: 'src', + 1106: 1, + }) + get_rpm_header.return_value = retval + + kojihub.import_build(self.src_filename, [self.filename]) + + fields = [ + 'task_id', + 'extra', + 'start_time', + 'epoch', + 'completion_time', + 'state', + 'version', + 'source', + 'volume_id', + 'owner', + 'release', + 'pkg_id', + 'id', + ] + statement = 'INSERT INTO build (%s) VALUES (%s)' % ( + ", ".join(fields), + ", ".join(['%%(%s)s' % field for field in fields]) + ) + values = { + 'task_id': None, + 'extra': None, + 'start_time': 'NOW', + 'epoch': 'epoch', + 'completion_time': 'NOW', + 'state': 1, + 'version': 'version', + 'source': None, + 'volume_id': 0, + 'owner': 99, + 'release': 'release', + 'pkg_id': mock.ANY, + 'id': mock.ANY, + } + _dml.assert_called_once_with(statement, values) diff --git a/tests/test_hub/test_import_image_internal.py b/tests/test_hub/test_import_image_internal.py new file mode 100644 index 0000000..287c172 --- /dev/null +++ b/tests/test_hub/test_import_image_internal.py @@ -0,0 +1,105 @@ +import unittest +import mock +import os +import shutil +import tempfile + +import kojihub + + + +class TestImportImageInternal(unittest.TestCase): + def setUp(self): + self.tempdir = tempfile.mkdtemp() + + def tearDown(self): + shutil.rmtree(self.tempdir) + + @mock.patch('koji.pathinfo.work') + @mock.patch('kojihub.import_archive') + @mock.patch('kojihub.get_archive_type') + @mock.patch('kojihub.get_build') + @mock.patch('kojihub.Task') + @mock.patch('kojihub.context') + def test_basic(self, context, Task, get_build, get_archive_type, import_archive, work): + imgdata = { + 'arch': 'x86_64', + 'task_id': 1, + 'files': [ + 'some_file', + ], + 'rpmlist': [ + ], + } + cursor = mock.MagicMock() + context.cnx.cursor.return_value = cursor + context.session.host_id = 42 + get_build.return_value = { + 'id': 2, + } + get_archive_type.return_value = 4 + work.return_value = self.tempdir + os.makedirs(self.tempdir + "/tasks/1/1") + kojihub.importImageInternal(task_id=1, build_id=2, imgdata=imgdata) + + @mock.patch('kojihub.get_rpm') + @mock.patch('koji.pathinfo.build') + @mock.patch('koji.pathinfo.work') + @mock.patch('kojihub.import_archive') + @mock.patch('kojihub.get_archive_type') + @mock.patch('kojihub.get_build') + @mock.patch('kojihub.Task') + @mock.patch('kojihub.context') + def test_with_rpm(self, context, Task, get_build, get_archive_type, import_archive, build, work, get_rpm): + rpm = { + #'location': 'foo', + 'id': 6, + 'name': 'foo', + 'version': '3.1', + 'release': '2', + 'epoch': 0, + 'arch': 'noarch', + 'payloadhash': 'laksjdflkasjdf', + 'size': 42, + 'buildtime': 12345, + } + imgdata = { + 'arch': 'x86_64', + 'task_id': 1, + 'files': [ + 'some_file', + ], + 'rpmlist': [rpm], + } + cursor = mock.MagicMock() + context.cnx.cursor.return_value = cursor + context.session.host_id = 42 + get_build.return_value = {'id': 2 } + get_rpm.return_value = rpm + get_archive_type.return_value = 4 + work.return_value = self.tempdir + build.return_value = self.tempdir + import_archive.return_value = { + 'id': 9, + 'filename': self.tempdir + '/foo.archive', + } + workdir = self.tempdir + "/tasks/1/1" + os.makedirs(workdir) + # Create a log file to exercise that code path + with open(workdir + '/foo.log', 'w'): + pass + + kojihub.importImageInternal(task_id=1, build_id=2, imgdata=imgdata) + + # Check that the log symlink made it to where it was supposed to. + dest = os.readlink(workdir + '/foo.log') + self.assertEquals(dest, self.tempdir + '/data/logs/image/foo.log') + + # And.. check all the sql statements + self.assertEquals(len(cursor.execute.mock_calls), 1) + expression, kwargs = cursor.execute.mock_calls[0][1] + expression = " ".join(expression.split()) + expected = 'INSERT INTO image_listing (image_id,rpm_id) ' + \ + 'VALUES (%(image_id)i,%(rpm_id)i)' + self.assertEquals(expression, expected) + self.assertEquals(kwargs, {'image_id': 9, 'rpm_id': 6}) diff --git a/tests/test_hub/test_rpmdiff.py b/tests/test_hub/test_rpmdiff.py new file mode 100644 index 0000000..277902f --- /dev/null +++ b/tests/test_hub/test_rpmdiff.py @@ -0,0 +1,75 @@ +import copy +import unittest +import mock + +import koji +import kojihub + + +class TestRPMDiff(unittest.TestCase): + + @mock.patch('kojihub.subprocess') + def test_rpmdiff_empty_invocation(self, subprocess): + process = mock.MagicMock() + subprocess.Popen.return_value = process + kojihub.rpmdiff('basepath', []) + self.assertEquals(len(subprocess.Popen.mock_calls), 0) + kojihub.rpmdiff('basepath', ['foo']) + self.assertEquals(len(subprocess.Popen.mock_calls), 0) + + @mock.patch('kojihub.subprocess') + def test_rpmdiff_simple_success(self, subprocess): + process = mock.MagicMock() + subprocess.Popen.return_value = process + process.wait.return_value = 0 + kojihub.rpmdiff('basepath', ['foo', 'bar']) + self.assertEquals(len(subprocess.Popen.call_args_list), 1) + + @mock.patch('kojihub.subprocess') + def test_rpmdiff_simple_failure(self, subprocess): + process = mock.MagicMock() + subprocess.Popen.return_value = process + process.wait.return_value = 1 + with self.assertRaises(koji.BuildError): + kojihub.rpmdiff('basepath', ['foo', 'bar']) + +class TestCheckNoarchRpms(unittest.TestCase): + @mock.patch('kojihub.rpmdiff') + def test_check_noarch_rpms_empty_invocation(self, rpmdiff): + originals = ['foo', 'bar'] + result = kojihub.check_noarch_rpms('basepath', copy.copy(originals)) + self.assertEquals(result, originals) + + @mock.patch('kojihub.rpmdiff') + def test_check_noarch_rpms_simple_invocation(self, rpmdiff): + originals = ['foo.noarch.rpm', 'bar.noarch.rpm'] + result = kojihub.check_noarch_rpms('basepath', copy.copy(originals)) + self.assertEquals(result, originals) + self.assertEquals(len(rpmdiff.mock_calls), 2) + + @mock.patch('kojihub.rpmdiff') + def test_check_noarch_rpms_with_duplicates(self, rpmdiff): + originals = [ + 'bar.noarch.rpm', + 'bar.noarch.rpm', + 'bar.noarch.rpm', + ] + result = kojihub.check_noarch_rpms('basepath', copy.copy(originals)) + self.assertEquals(result, ['bar.noarch.rpm']) + rpmdiff.assert_called_once_with('basepath', originals) + + @mock.patch('kojihub.rpmdiff') + def test_check_noarch_rpms_with_mixed(self, rpmdiff): + originals = [ + 'foo.x86_64.rpm', + 'bar.x86_64.rpm', + 'bar.noarch.rpm', + 'bar.noarch.rpm', + ] + result = kojihub.check_noarch_rpms('basepath', copy.copy(originals)) + self.assertEquals(result, [ + 'foo.x86_64.rpm', 'bar.x86_64.rpm', 'bar.noarch.rpm' + ]) + rpmdiff.assert_called_once_with('basepath', [ + 'bar.noarch.rpm', 'bar.noarch.rpm' + ])