PR#184: Port Koji comps/group functionality from yum.comps to libcomps

Merges #170
https://pagure.io/koji/pull-request/170

Fixes #184
This commit is contained in:
Mike McLean 2016-10-13 00:16:40 -04:00
commit 0e664ad779
14 changed files with 44921 additions and 12 deletions

View file

@ -62,12 +62,15 @@ import urlgrabber.grabber as grabber
import urlgrabber.progress as progress
import xmlrpclib
try:
import yum.comps as yumcomps
import libcomps
except ImportError:
yumcomps = None
libcomps = None
try:
import yum.comps as yumcomps
except ImportError:
yumcomps = None
import optparse
#for import-comps handler (currently disabled)
#from rhpl.comps import Comps
# fix OptionParser for python 2.3 (optparse verion 1.4.1+)
# code taken from optparse version 1.5a2
@ -1760,15 +1763,60 @@ def handle_import_comps(options, session, args):
if len(args) != 2:
parser.error(_("Incorrect number of arguments"))
assert False
if yumcomps is None:
print "yum comps module not available"
return 1
# TODO: port to libcomps
comps = yumcomps.Comps()
comps.add(args[0])
tag = args[1]
force = local_options.force
activate_session(session)
# check if the tag exists
dsttag = session.getTag(args[1])
if dsttag is None:
print "No such tag: %s" % args[1]
return 1
if libcomps is not None:
_import_comps(session, args[0], args[1], local_options)
elif yumcomps is not None:
_import_comps_alt(session, args[0], args[1], local_options)
else:
print "comps module not available"
return 1
def _import_comps(session, filename, tag, options):
"""Import comps data using libcomps module"""
comps = libcomps.Comps()
comps.fromxml_f(filename)
force = options['force']
ptypes = {
libcomps.PACKAGE_TYPE_DEFAULT : 'default',
libcomps.PACKAGE_TYPE_OPTIONAL : 'optional',
libcomps.PACKAGE_TYPE_CONDITIONAL : 'conditional',
libcomps.PACKAGE_TYPE_MANDATORY : 'mandatory',
libcomps.PACKAGE_TYPE_UNKNOWN : 'unknown',
}
for group in comps.groups:
print "Group: %s (%s)" % (group.id, group.name)
session.groupListAdd(
tag, group.id, force=force, display_name=group.name,
is_default=bool(group.default),
uservisible=bool(group.uservisible),
description=group.desc,
langonly=group.lang_only,
biarchonly=bool(group.biarchonly))
for pkg in group.packages:
pkgopts = {'type' : ptypes[pkg.type],
'basearchonly' : bool(pkg.basearchonly),
}
if pkg.type == libcomps.PACKAGE_TYPE_CONDITIONAL:
pkgopts['requires'] = pkg.requires
print " Package: %s: %r" % (pkg.name, pkgopts)
session.groupPackageListAdd(tag, group.id, pkg.name, force=force, **pkgopts)
# libcomps does not support group dependencies
# libcomps does not support metapkgs
def _import_comps_alt(session, filename, tag, options):
"""Import comps data using yum.comps module"""
print 'WARN: yum.comps does not support the biarchonly of group and basearchonly of package'
comps = yumcomps.Comps()
comps.add(filename)
force = options['force']
for group in comps.groups:
print "Group: %(groupid)s (%(name)s)" % vars(group)
session.groupListAdd(tag, group.groupid, force=force, display_name=group.name,
@ -1791,6 +1839,7 @@ def handle_import_comps(options, session, args):
#yum.comps does not support group dependencies
#yum.comps does not support metapkgs
def handle_import_sig(options, session, args):
"[admin] Import signatures into the database"
usage = _("usage: %prog import-sig [options] package [package...]")

View file

@ -35,6 +35,9 @@ BuildRequires: python
BuildRequires: systemd
BuildRequires: pkgconfig
%endif
%if 0%{?fedora} || 0%{?rhel} >= 7
Requires: python-libcomps
%endif
%description
Koji is a system for building and tracking RPMS. The base package

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,39 @@
[call.groupListAdd('tag', u'additional-devel', biarchonly=True, description=u'Additional development headers and libraries for developing applications', display_name=u'Additional Development', force=None, is_default=False, langonly=u'fr', uservisible=False),
call.groupPackageListAdd('tag', u'additional-devel', u'alsa-lib-devel', basearchonly=False, force=None, type='default'),
call.groupPackageListAdd('tag', u'additional-devel', u'audit-libs-devel', basearchonly=False, force=None, type='default'),
call.groupPackageListAdd('tag', u'additional-devel', u'binutils-devel', basearchonly=False, force=None, type='default'),
call.groupPackageListAdd('tag', u'additional-devel', u'boost-devel', basearchonly=False, force=None, type='default'),
call.groupPackageListAdd('tag', u'additional-devel', u'bzip2-devel', basearchonly=False, force=None, type='default'),
call.groupPackageListAdd('tag', u'additional-devel', u'cyrus-sasl-devel', basearchonly=False, force=None, type='default'),
call.groupListAdd('tag', u'backup-client', biarchonly=False, description=u'Client tools for connecting to a backup server and doing backups.', display_name=u'Backup Client', force=None, is_default=True, langonly=None, uservisible=True),
call.groupPackageListAdd('tag', u'backup-client', u'amanda-client', basearchonly=False, force=None, type='mandatory'),
call.groupPackageListAdd('tag', u'backup-client', u'bacula-client', basearchonly=False, force=None, type='optional'),
call.groupListAdd('tag', u'backup-server', biarchonly=False, description=u"Software to centralize your infrastructure's backups.", display_name=u'Backup Server', force=None, is_default=False, langonly=None, uservisible=True),
call.groupPackageListAdd('tag', u'backup-server', u'amanda-server', basearchonly=False, force=None, type='mandatory'),
call.groupPackageListAdd('tag', u'backup-server', u'mt-st', basearchonly=False, force=None, type='optional'),
call.groupPackageListAdd('tag', u'backup-server', u'mtx', basearchonly=False, force=None, type='optional'),
call.groupListAdd('tag', u'ansible-node', biarchonly=False, description=None, display_name=u'Ansible node', force=None, is_default=False, langonly=None, uservisible=True),
call.groupPackageListAdd('tag', u'ansible-node', u'python2-dnf', basearchonly=False, force=None, type='mandatory'),
call.groupPackageListAdd('tag', u'ansible-node', u'libselinux-python', basearchonly=False, force=None, requires=u'selinux-policy', type='conditional'),
call.groupListAdd('tag', u'd-development', biarchonly=False, description=u'These include development tools and libraries such as ldc, and geany-tag.', display_name=u'D Development Tools and Libraries', force=None, is_default=False, langonly=None, uservisible=True),
call.groupPackageListAdd('tag', u'd-development', u'ldc', basearchonly=True, force=None, type='mandatory'),
call.groupPackageListAdd('tag', u'd-development', u'ldc-druntime', basearchonly=True, force=None, type='mandatory'),
call.groupPackageListAdd('tag', u'd-development', u'ldc-druntime-devel', basearchonly=True, force=None, type='mandatory'),
call.groupPackageListAdd('tag', u'd-development', u'ldc-phobos-devel', basearchonly=True, force=None, type='mandatory'),
call.groupPackageListAdd('tag', u'd-development', u'make', basearchonly=False, force=None, type='mandatory'),
call.groupPackageListAdd('tag', u'd-development', u'pkgconfig', basearchonly=False, force=None, type='mandatory'),
call.groupPackageListAdd('tag', u'd-development', u'ctags', basearchonly=False, force=None, type='default'),
call.groupPackageListAdd('tag', u'd-development', u'indent', basearchonly=False, force=None, type='default'),
call.groupPackageListAdd('tag', u'd-development', u'astyle', basearchonly=False, force=None, type='optional'),
call.groupPackageListAdd('tag', u'd-development', u'cmake', basearchonly=False, force=None, type='optional'),
call.groupPackageListAdd('tag', u'd-development', u'derelict-devel', basearchonly=False, force=None, type='optional'),
call.groupPackageListAdd('tag', u'd-development', u'geany', basearchonly=False, force=None, type='optional'),
call.groupPackageListAdd('tag', u'd-development', u'gl3n-devel', basearchonly=False, force=None, type='optional'),
call.groupPackageListAdd('tag', u'd-development', u'insight', basearchonly=False, force=None, type='optional'),
call.groupPackageListAdd('tag', u'd-development', u'nemiver', basearchonly=False, force=None, type='optional'),
call.groupPackageListAdd('tag', u'd-development', u'uncrustify', basearchonly=False, force=None, type='optional'),
call.groupListAdd('tag', u'empty-group-1', biarchonly=False, description=u'empty group 1 desc', display_name=u'empty group 1', force=None, is_default=False, langonly=None, uservisible=True),
call.groupListAdd('tag', u'empty-group-2', biarchonly=False, description=u'empty group 2 desc', display_name=u'empty group 2', force=None, is_default=False, langonly=None, uservisible=True),
call.groupListAdd('tag', u'unknown-group', biarchonly=False, description=u'unknown group desc', display_name=u'unknown group', force=None, is_default=False, langonly=None, uservisible=True),
call.groupPackageListAdd('tag', u'unknown-group', u'unknown', basearchonly=False, force=None, type='unknown'),
call.groupPackageListAdd('tag', u'unknown-group', u'unknown2', basearchonly=False, force=None, type='unknown')]

View file

@ -0,0 +1,39 @@
Group: additional-devel (Additional Development)
Package: alsa-lib-devel: {'type': 'default', 'basearchonly': False}
Package: audit-libs-devel: {'type': 'default', 'basearchonly': False}
Package: binutils-devel: {'type': 'default', 'basearchonly': False}
Package: boost-devel: {'type': 'default', 'basearchonly': False}
Package: bzip2-devel: {'type': 'default', 'basearchonly': False}
Package: cyrus-sasl-devel: {'type': 'default', 'basearchonly': False}
Group: backup-client (Backup Client)
Package: amanda-client: {'type': 'mandatory', 'basearchonly': False}
Package: bacula-client: {'type': 'optional', 'basearchonly': False}
Group: backup-server (Backup Server)
Package: amanda-server: {'type': 'mandatory', 'basearchonly': False}
Package: mt-st: {'type': 'optional', 'basearchonly': False}
Package: mtx: {'type': 'optional', 'basearchonly': False}
Group: ansible-node (Ansible node)
Package: python2-dnf: {'type': 'mandatory', 'basearchonly': False}
Package: libselinux-python: {'requires': u'selinux-policy', 'type': 'conditional', 'basearchonly': False}
Group: d-development (D Development Tools and Libraries)
Package: ldc: {'type': 'mandatory', 'basearchonly': True}
Package: ldc-druntime: {'type': 'mandatory', 'basearchonly': True}
Package: ldc-druntime-devel: {'type': 'mandatory', 'basearchonly': True}
Package: ldc-phobos-devel: {'type': 'mandatory', 'basearchonly': True}
Package: make: {'type': 'mandatory', 'basearchonly': False}
Package: pkgconfig: {'type': 'mandatory', 'basearchonly': False}
Package: ctags: {'type': 'default', 'basearchonly': False}
Package: indent: {'type': 'default', 'basearchonly': False}
Package: astyle: {'type': 'optional', 'basearchonly': False}
Package: cmake: {'type': 'optional', 'basearchonly': False}
Package: derelict-devel: {'type': 'optional', 'basearchonly': False}
Package: geany: {'type': 'optional', 'basearchonly': False}
Package: gl3n-devel: {'type': 'optional', 'basearchonly': False}
Package: insight: {'type': 'optional', 'basearchonly': False}
Package: nemiver: {'type': 'optional', 'basearchonly': False}
Package: uncrustify: {'type': 'optional', 'basearchonly': False}
Group: empty-group-1 (empty group 1)
Group: empty-group-2 (empty group 2)
Group: unknown-group (unknown group)
Package: unknown: {'type': 'unknown', 'basearchonly': False}
Package: unknown2: {'type': 'unknown', 'basearchonly': False}

View file

@ -0,0 +1,105 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE comps PUBLIC "-//Red Hat, Inc.//DTD Comps info//EN" "comps.dtd">
<comps>
<group>
<id>additional-devel</id>
<name>Additional Development</name>
<description>Additional development headers and libraries for developing applications</description>
<default>false</default>
<uservisible>false</uservisible>
<biarchonly>true</biarchonly>
<langonly>fr</langonly>
<packagelist>
<packagereq type="default">alsa-lib-devel</packagereq>
<packagereq type="default">audit-libs-devel</packagereq>
<packagereq type="default">binutils-devel</packagereq>
<packagereq type="default">boost-devel</packagereq>
<packagereq type="default">bzip2-devel</packagereq>
<packagereq type="default">cyrus-sasl-devel</packagereq>
</packagelist>
</group>
<group>
<id>backup-client</id>
<name>Backup Client</name>
<description>Client tools for connecting to a backup server and doing backups.</description>
<default>true</default>
<uservisible>true</uservisible>
<packagelist>
<packagereq type="mandatory">amanda-client</packagereq>
<packagereq type="optional">bacula-client</packagereq>
</packagelist>
</group>
<group>
<id>backup-server</id>
<name>Backup Server</name>
<description>Software to centralize your infrastructure's backups.</description>
<default>false</default>
<uservisible>true</uservisible>
<packagelist>
<packagereq type="mandatory">amanda-server</packagereq>
<packagereq type="optional">mt-st</packagereq>
<packagereq type="optional">mtx</packagereq>
</packagelist>
</group>
<group>
<id>ansible-node</id>
<name>Ansible node</name>
<default>false</default>
<uservisible>true</uservisible>
<packagelist>
<packagereq type="mandatory">python2-dnf</packagereq>
<packagereq type="conditional" requires="selinux-policy">libselinux-python</packagereq>
</packagelist>
</group>
<group>
<id>d-development</id>
<name>D Development Tools and Libraries</name>
<description>These include development tools and libraries such as ldc, and geany-tag.</description>
<default>false</default>
<uservisible>true</uservisible>
<packagelist>
<packagereq type="mandatory" basearchonly="true">ldc</packagereq>
<packagereq type="mandatory" basearchonly="true">ldc-druntime</packagereq>
<packagereq type="mandatory" basearchonly="true">ldc-druntime-devel</packagereq>
<packagereq type="mandatory" basearchonly="true">ldc-phobos-devel</packagereq>
<packagereq type="mandatory">make</packagereq>
<packagereq type="mandatory">pkgconfig</packagereq>
<packagereq type="default">ctags</packagereq>
<packagereq type="default">indent</packagereq>
<packagereq type="optional">astyle</packagereq>
<packagereq type="optional">cmake</packagereq>
<packagereq type="optional">derelict-devel</packagereq>
<packagereq type="optional">geany</packagereq>
<packagereq type="optional">gl3n-devel</packagereq>
<packagereq type="optional">insight</packagereq>
<packagereq type="optional">nemiver</packagereq>
<packagereq type="optional">uncrustify</packagereq>
</packagelist>
</group>
<group>
<id>empty-group-1</id>
<name>empty group 1</name>
<description>empty group 1 desc</description>
<default>false</default>
<uservisible>true</uservisible>
<packagelist/>
</group>
<group>
<id>empty-group-2</id>
<name>empty group 2</name>
<description>empty group 2 desc</description>
<default>false</default>
<uservisible>true</uservisible>
</group>
<group>
<id>unknown-group</id>
<name>unknown group</name>
<description>unknown group desc</description>
<default>false</default>
<uservisible>true</uservisible>
<packagelist>
<packagereq type="unknown">unknown</packagereq>
<packagereq type="what">unknown2</packagereq>
</packagelist>
</group>
</comps>

View file

@ -0,0 +1,37 @@
[call.groupListAdd('tag', 'additional-devel', description='Additional development headers and libraries for developing applications', display_name='Additional Development', force=None, is_default=False, langonly='fr', uservisible=False),
call.groupPackageListAdd('tag', 'additional-devel', 'binutils-devel', force=None, type='default'),
call.groupPackageListAdd('tag', 'additional-devel', 'boost-devel', force=None, type='default'),
call.groupPackageListAdd('tag', 'additional-devel', 'bzip2-devel', force=None, type='default'),
call.groupPackageListAdd('tag', 'additional-devel', 'cyrus-sasl-devel', force=None, type='default'),
call.groupPackageListAdd('tag', 'additional-devel', 'audit-libs-devel', force=None, type='default'),
call.groupPackageListAdd('tag', 'additional-devel', 'alsa-lib-devel', force=None, type='default'),
call.groupListAdd('tag', 'ansible-node', description='', display_name='Ansible node', force=None, is_default=False, langonly=None, uservisible=True),
call.groupPackageListAdd('tag', 'ansible-node', 'python2-dnf', force=None, type='mandatory'),
call.groupPackageListAdd('tag', 'ansible-node', 'libselinux-python', force=None, requires='selinux-policy', type='conditional'),
call.groupListAdd('tag', 'backup-client', description='Client tools for connecting to a backup server and doing backups.', display_name='Backup Client', force=None, is_default=True, langonly=None, uservisible=True),
call.groupPackageListAdd('tag', 'backup-client', 'amanda-client', force=None, type='mandatory'),
call.groupPackageListAdd('tag', 'backup-client', 'bacula-client', force=None, type='optional'),
call.groupListAdd('tag', 'backup-server', description="Software to centralize your infrastructure's backups.", display_name='Backup Server', force=None, is_default=False, langonly=None, uservisible=True),
call.groupPackageListAdd('tag', 'backup-server', 'amanda-server', force=None, type='mandatory'),
call.groupPackageListAdd('tag', 'backup-server', 'mtx', force=None, type='optional'),
call.groupPackageListAdd('tag', 'backup-server', 'mt-st', force=None, type='optional'),
call.groupListAdd('tag', 'd-development', description='These include development tools and libraries such as ldc, and geany-tag.', display_name='D Development Tools and Libraries', force=None, is_default=False, langonly=None, uservisible=True),
call.groupPackageListAdd('tag', 'd-development', 'ldc', force=None, type='mandatory'),
call.groupPackageListAdd('tag', 'd-development', 'ldc-druntime', force=None, type='mandatory'),
call.groupPackageListAdd('tag', 'd-development', 'make', force=None, type='mandatory'),
call.groupPackageListAdd('tag', 'd-development', 'pkgconfig', force=None, type='mandatory'),
call.groupPackageListAdd('tag', 'd-development', 'ldc-phobos-devel', force=None, type='mandatory'),
call.groupPackageListAdd('tag', 'd-development', 'ldc-druntime-devel', force=None, type='mandatory'),
call.groupPackageListAdd('tag', 'd-development', 'ctags', force=None, type='default'),
call.groupPackageListAdd('tag', 'd-development', 'indent', force=None, type='default'),
call.groupPackageListAdd('tag', 'd-development', 'geany', force=None, type='optional'),
call.groupPackageListAdd('tag', 'd-development', 'cmake', force=None, type='optional'),
call.groupPackageListAdd('tag', 'd-development', 'derelict-devel', force=None, type='optional'),
call.groupPackageListAdd('tag', 'd-development', 'insight', force=None, type='optional'),
call.groupPackageListAdd('tag', 'd-development', 'nemiver', force=None, type='optional'),
call.groupPackageListAdd('tag', 'd-development', 'gl3n-devel', force=None, type='optional'),
call.groupPackageListAdd('tag', 'd-development', 'astyle', force=None, type='optional'),
call.groupPackageListAdd('tag', 'd-development', 'uncrustify', force=None, type='optional'),
call.groupListAdd('tag', 'empty-group-1', description='empty group 1 desc', display_name='empty group 1', force=None, is_default=False, langonly=None, uservisible=True),
call.groupListAdd('tag', 'empty-group-2', description='empty group 2 desc', display_name='empty group 2', force=None, is_default=False, langonly=None, uservisible=True),
call.groupListAdd('tag', 'unknown-group', description='unknown group desc', display_name='unknown group', force=None, is_default=False, langonly=None, uservisible=True)]

View file

@ -0,0 +1,38 @@
WARN: yum.comps does not support the biarchonly of group and basearchonly of package
Group: additional-devel (Additional Development)
Package: binutils-devel: {'type': 'default'}
Package: boost-devel: {'type': 'default'}
Package: bzip2-devel: {'type': 'default'}
Package: cyrus-sasl-devel: {'type': 'default'}
Package: audit-libs-devel: {'type': 'default'}
Package: alsa-lib-devel: {'type': 'default'}
Group: ansible-node (Ansible node)
Package: python2-dnf: {'type': 'mandatory'}
Package: libselinux-python: {'requires': 'selinux-policy', 'type': 'conditional'}
Group: backup-client (Backup Client)
Package: amanda-client: {'type': 'mandatory'}
Package: bacula-client: {'type': 'optional'}
Group: backup-server (Backup Server)
Package: amanda-server: {'type': 'mandatory'}
Package: mtx: {'type': 'optional'}
Package: mt-st: {'type': 'optional'}
Group: d-development (D Development Tools and Libraries)
Package: ldc: {'type': 'mandatory'}
Package: ldc-druntime: {'type': 'mandatory'}
Package: make: {'type': 'mandatory'}
Package: pkgconfig: {'type': 'mandatory'}
Package: ldc-phobos-devel: {'type': 'mandatory'}
Package: ldc-druntime-devel: {'type': 'mandatory'}
Package: ctags: {'type': 'default'}
Package: indent: {'type': 'default'}
Package: geany: {'type': 'optional'}
Package: cmake: {'type': 'optional'}
Package: derelict-devel: {'type': 'optional'}
Package: insight: {'type': 'optional'}
Package: nemiver: {'type': 'optional'}
Package: gl3n-devel: {'type': 'optional'}
Package: astyle: {'type': 'optional'}
Package: uncrustify: {'type': 'optional'}
Group: empty-group-1 (empty group 1)
Group: empty-group-2 (empty group 2)
Group: unknown-group (unknown group)

15
tests/test_cli/loadcli.py Normal file
View file

@ -0,0 +1,15 @@
import os
import sys
# 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)

View file

@ -0,0 +1,363 @@
import unittest
import StringIO as stringio
import os
import sys
import mock
import loadcli
try:
import libcomps
except ImportError:
libcomps = None
try:
import yum.comps as yumcomps
except ImportError:
yumcomps = None
cli = loadcli.cli
class TestImportComps(unittest.TestCase):
# Show long diffs in error output...
maxDiff = None
@mock.patch('sys.stdout', new_callable=stringio.StringIO)
@mock.patch('koji_cli.libcomps')
@mock.patch('koji_cli.activate_session')
@mock.patch('koji_cli._import_comps')
@mock.patch('koji_cli._import_comps_alt')
def test_handle_import_comps_libcomps(
self,
mock_import_comps_alt,
mock_import_comps,
mock_activate_session,
libcomps,
stdout):
filename = './data/comps-example.xml'
tag = 'tag'
tag_info = {'name': tag, 'id': 1}
force = None
args = [filename, tag]
kwargs = {'force': force}
options = mock.MagicMock()
# Mock out the xmlrpc server
session = mock.MagicMock()
session.getTag.return_value = tag_info
# Run it and check immediate output
# args: ./data/comps-example.xml, tag
# expected: success
rv = cli.handle_import_comps(options, session, args)
actual = stdout.getvalue()
expected = ''
self.assertMultiLineEqual(actual, expected)
# Finally, assert that things were called as we expected.
mock_activate_session.assert_called_once_with(session)
session.getTag.assert_called_once_with(tag)
mock_import_comps.assert_called_once_with(
session, filename, tag, kwargs)
mock_import_comps_alt.assert_not_called()
self.assertNotEqual(rv, 1)
@mock.patch('sys.stdout', new_callable=stringio.StringIO)
@mock.patch('koji_cli.libcomps', new=None)
@mock.patch('koji_cli.yumcomps', create=True)
@mock.patch('koji_cli.activate_session')
@mock.patch('koji_cli._import_comps')
@mock.patch('koji_cli._import_comps_alt')
def test_handle_import_comps_yumcomps(
self,
mock_import_comps_alt,
mock_import_comps,
mock_activate_session,
yumcomps,
stdout):
filename = './data/comps-example.xml'
tag = 'tag'
tag_info = {'name': tag, 'id': 1}
force = True
args = ['--force', filename, tag]
local_options = {'force': force}
options = mock.MagicMock()
# Mock out the xmlrpc server
session = mock.MagicMock()
session.getTag.return_value = tag_info
# Run it and check immediate output
# args: --force, ./data/comps-example.xml, tag
# expected: success
rv = cli.handle_import_comps(options, session, args)
actual = stdout.getvalue()
expected = ''
self.assertMultiLineEqual(actual, expected)
# Finally, assert that things were called as we expected.
mock_activate_session.assert_called_once_with(session)
session.getTag.assert_called_once_with(tag)
mock_import_comps.assert_not_called()
mock_import_comps_alt.assert_called_once_with(
session, filename, tag, local_options)
self.assertNotEqual(rv, 1)
@mock.patch('sys.stdout', new_callable=stringio.StringIO)
@mock.patch('koji_cli.libcomps', new=None)
@mock.patch('koji_cli.yumcomps', new=None, create=True)
@mock.patch('koji_cli.activate_session')
@mock.patch('koji_cli._import_comps')
@mock.patch('koji_cli._import_comps_alt')
def test_handle_import_comps_comps_na(
self,
mock_import_comps_alt,
mock_import_comps,
mock_activate_session,
stdout):
filename = './data/comps-example.xml'
tag = 'tag'
tag_info = {'name': tag, 'id': 1}
args = ['--force', filename, tag]
options = mock.MagicMock()
# Mock out the xmlrpc server
session = mock.MagicMock()
session.getTag.return_value = tag_info
# Run it and check immediate output
# args: --force, ./data/comps-example.xml, tag
# expected: failed, no comps available
rv = cli.handle_import_comps(options, session, args)
actual = stdout.getvalue()
expected = 'comps module not available\n'
self.assertMultiLineEqual(actual, expected)
# Finally, assert that things were called as we expected.
mock_activate_session.assert_called_once_with(session)
session.getTag.assert_called_once_with(tag)
mock_import_comps.assert_not_called()
mock_import_comps_alt.assert_not_called()
self.assertEqual(rv, 1)
@mock.patch('sys.stdout', new_callable=stringio.StringIO)
@mock.patch('koji_cli.activate_session')
@mock.patch('koji_cli._import_comps')
@mock.patch('koji_cli._import_comps_alt')
def test_handle_import_comps_tag_not_exists(
self,
mock_import_comps_alt,
mock_import_comps,
mock_activate_session,
stdout):
filename = './data/comps-example.xml'
tag = 'tag'
tag_info = None
args = [filename, tag]
options = mock.MagicMock()
# Mock out the xmlrpc server
session = mock.MagicMock()
session.getTag.return_value = tag_info
# Run it and check immediate output
# args: ./data/comps-example.xml, tag
# expected: failed: tag does not exist
rv = cli.handle_import_comps(options, session, args)
actual = stdout.getvalue()
expected = 'No such tag: tag\n'
self.assertMultiLineEqual(actual, expected)
# Finally, assert that things were called as we expected.
mock_activate_session.assert_called_once_with(session)
session.getTag.assert_called_once_with(tag)
mock_import_comps.assert_not_called()
mock_import_comps_alt.assert_not_called()
self.assertEqual(rv, 1)
@mock.patch('sys.stdout', new_callable=stringio.StringIO)
@mock.patch('sys.stderr', new_callable=stringio.StringIO)
@mock.patch('koji_cli.activate_session')
@mock.patch('koji_cli._import_comps')
@mock.patch('koji_cli._import_comps_alt')
def test_handle_import_comps_help(
self,
mock_import_comps_alt, mock_import_comps,
mock_activate_session,
stderr,
stdout):
args = []
progname = os.path.basename(sys.argv[0]) or 'koji'
options = mock.MagicMock()
# Mock out the xmlrpc server
session = mock.MagicMock()
# Run it and check immediate output
with self.assertRaises(SystemExit) as cm:
rv = cli.handle_import_comps(options, session, args)
actual_stdout = stdout.getvalue()
actual_stderr = stderr.getvalue()
expected_stdout = ''
expected_stderr = """Usage: %s import-comps [options] <file> <tag>
(Specify the --help global option for a list of other help options)
%s: error: Incorrect number of arguments
""" % (progname, progname)
self.assertMultiLineEqual(actual_stdout, expected_stdout)
self.assertMultiLineEqual(actual_stderr, expected_stderr)
# Finally, assert that things were called as we expected.
mock_activate_session.assert_not_called()
session.getTag.assert_not_called()
session.getTagGroups.assert_not_called()
session.groupListAdd.assert_not_called()
self.assertEqual(cm.exception.code, 2)
@unittest.skipIf(libcomps is None, "No libcomps")
@mock.patch('sys.stdout', new_callable=stringio.StringIO)
def test_import_comps_libcomps(self, stdout):
comps_file = os.path.dirname(__file__) + '/data/comps-example.xml'
stdout_file = os.path.dirname(
__file__) + '/data/comps-example.libcomps.out'
calls_file = os.path.dirname(
__file__) + '/data/comps-example.libcomps.calls'
self._test_import_comps(
cli._import_comps,
comps_file,
stdout_file,
calls_file,
stdout)
@unittest.skipIf(libcomps is None, "No libcomps")
@mock.patch('sys.stdout', new_callable=stringio.StringIO)
def test_import_comps_sample_libcomps(self, stdout):
comps_file = os.path.dirname(__file__) + '/data/comps-sample.xml'
stdout_file = os.path.dirname(
__file__) + '/data/comps-sample.libcomps.out'
calls_file = os.path.dirname(
__file__) + '/data/comps-sample.libcomps.calls'
self._test_import_comps(
cli._import_comps,
comps_file,
stdout_file,
calls_file,
stdout)
@unittest.skipIf(yumcomps is None, "No yum.comps")
@mock.patch('sys.stdout', new_callable=stringio.StringIO)
@mock.patch('koji_cli.libcomps', new=None)
@mock.patch('koji_cli.yumcomps', create=True, new=yumcomps)
def test_import_comps_yumcomps(self, stdout):
comps_file = os.path.dirname(__file__) + '/data/comps-example.xml'
stdout_file = os.path.dirname(
__file__) + '/data/comps-example.yumcomps.out'
calls_file = os.path.dirname(
__file__) + '/data/comps-example.yumcomps.calls'
self._test_import_comps(
cli._import_comps_alt,
comps_file,
stdout_file,
calls_file,
stdout)
@unittest.skipIf(yumcomps is None, "No yum.comps")
@mock.patch('sys.stdout', new_callable=stringio.StringIO)
@mock.patch('koji_cli.libcomps', new=None)
@mock.patch('koji_cli.yumcomps', create=True, new=yumcomps)
def test_import_comps_sample_yumcomps(self, stdout):
comps_file = os.path.dirname(__file__) + '/data/comps-sample.xml'
stdout_file = os.path.dirname(
__file__) + '/data/comps-sample.yumcomps.out'
calls_file = os.path.dirname(
__file__) + '/data/comps-sample.yumcomps.calls'
self._test_import_comps(
cli._import_comps_alt,
comps_file,
stdout_file,
calls_file,
stdout)
def _test_import_comps(
self,
method,
comps_file,
stdout_file,
calls_file,
stdout):
tag = 'tag'
force = None
options = {'force': force}
# Mock out the xmlrpc server
session = mock.MagicMock()
# Run it and check immediate output
# args: comps.xml, tag
# expected: success
rv = method.__call__(session, comps_file, tag, options)
expected = ''
with open(stdout_file, 'rb') as f:
expected = f.read().decode('ascii')
self.assertMultiLineEqual(stdout.getvalue(), expected)
# compare mock_calls by literal string
with open(calls_file, 'rb') as f:
expected = f.read().decode('ascii')
self.assertMultiLineEqual(str(session.mock_calls) + '\n', expected)
self.assertNotEqual(rv, 1)
def _generate_out_calls(method, comps_file, stdout_file, calls_file):
tag = 'tag'
force = None
options = {'force': force}
# Mock out the xmlrpc server
session = mock.MagicMock()
with open(stdout_file, 'wb') as f:
# redirect stdout to stdout_file
orig_stdout = sys.stdout
sys.stdout = f
# args: comps.xml, tag
# expected: success
method.__call__(session, comps_file, tag, options)
sys.stdout = orig_stdout
with open(calls_file, 'wb') as f:
f.write(str(session.mock_calls).encode('ascii') + '\n')
def generate_out_calls():
"""Generate .out and .calls files for tests.
These files should be carefully check to make sure they're excepted"""
path = os.path.dirname(__file__)
comps_file = path + '/data/comps-example.xml'
stdout_file = path + '/data/comps-example.libcomps.out'
calls_file = path + '/data/comps-example.libcomps.calls'
_generate_out_calls(cli._import_comps, comps_file, stdout_file, calls_file)
comps_file = path + '/data/comps-sample.xml'
stdout_file = path + '/data/comps-sample.libcomps.out'
calls_file = path + '/data/comps-sample.libcomps.calls'
_generate_out_calls(cli._import_comps, comps_file, stdout_file, calls_file)
cli.yumcomps = yumcomps
comps_file = path + '/data/comps-example.xml'
stdout_file = path + '/data/comps-example.yumcomps.out'
calls_file = path + '/data/comps-example.yumcomps.calls'
_generate_out_calls(cli._import_comps_alt, comps_file, stdout_file, calls_file)
comps_file = path + '/data/comps-sample.xml'
stdout_file = path + '/data/comps-sample.yumcomps.out'
calls_file = path + '/data/comps-sample.yumcomps.calls'
_generate_out_calls(cli._import_comps_alt, comps_file, stdout_file, calls_file)
if __name__ == '__main__':
unittest.main()