From 08e1bfe95e55fce0d64c8fa0d5d00fe58e54729f Mon Sep 17 00:00:00 2001 From: Yu Ming Zhu Date: Dec 11 2018 09:00:44 +0000 Subject: [PATCH 1/6] urllib fix for python3 --- diff --git a/koji/auth.py b/koji/auth.py index de03e03..209aaa1 100644 --- a/koji/auth.py +++ b/koji/auth.py @@ -29,9 +29,9 @@ try: except ImportError: krbV = None import koji -import urlparse #for parse_qs from .context import context from six.moves import range +from six.moves import urllib from six.moves import zip import six from .util import to_list @@ -83,7 +83,7 @@ class Session(object): if not args: self.message = 'no session args' return - args = urlparse.parse_qs(args, strict_parsing=True) + args = urllib.parse.parse_qs(args, strict_parsing=True) hostip = self.get_remote_ip(override=hostip) try: id = int(args['session-id'][0]) diff --git a/koji/daemon.py b/koji/daemon.py index 803f8f1..7d6b4dd 100644 --- a/koji/daemon.py +++ b/koji/daemon.py @@ -31,7 +31,7 @@ from koji.util import md5_constructor, adler32_constructor, parseStatus, \ import os import signal import logging -import urlparse +from six.moves import urllib from fnmatch import fnmatch import base64 import time @@ -258,7 +258,7 @@ class SCM(object): # replace the scheme with http:// so that the urlparse works in all cases dummyurl = self.url.replace(scheme, 'http://', 1) - dummyscheme, netloc, path, params, query, fragment = urlparse.urlparse(dummyurl) + dummyscheme, netloc, path, params, query, fragment = urllib.parse.urlparse(dummyurl) user = None userhost = netloc.split('@') @@ -532,7 +532,7 @@ class SCM(object): netloc = self.host path = self.repository query = self.module - r['source'] = urlparse.urlunsplit([scheme, netloc, path, query, fragment]) + r['source'] = urllib.parse.urlunsplit([scheme, netloc, path, query, fragment]) else: # just use the same url r['source'] = self.url From 65415841b2e4f4fcf3bc33067a14b85537b34227 Mon Sep 17 00:00:00 2001 From: Yu Ming Zhu Date: Dec 11 2018 09:01:46 +0000 Subject: [PATCH 2/6] fix zip for python3 --- diff --git a/koji/rpmdiff.py b/koji/rpmdiff.py index a1c3c8b..aefe6e0 100644 --- a/koji/rpmdiff.py +++ b/koji/rpmdiff.py @@ -25,6 +25,7 @@ import rpm import os import itertools import six +from six.moves import zip class Rpmdiff: @@ -179,8 +180,8 @@ class Rpmdiff: if not isinstance(oldflags, list): oldflags = [ oldflags ] if not isinstance(newflags, list): newflags = [ newflags ] - o = zip(old[name], oldflags, old[name[:-1]+'VERSION']) - n = zip(new[name], newflags, new[name[:-1]+'VERSION']) + o = list(zip(old[name], oldflags, old[name[:-1]+'VERSION'])) + n = list(zip(new[name], newflags, new[name[:-1]+'VERSION'])) if name == 'PROVIDES': # filter our self provide oldNV = (old['name'], rpm.RPMSENSE_EQUAL, diff --git a/koji/util.py b/koji/util.py index bf2e2b2..f2cb45a 100644 --- a/koji/util.py +++ b/koji/util.py @@ -39,6 +39,7 @@ from zlib import adler32 from six.moves import range import six import warnings +from six.moves import zip # imported from kojiweb and kojihub try: @@ -77,6 +78,7 @@ def formatChangelog(entries): DATE_RE = re.compile(r'(\d+)-(\d+)-(\d+)') TIME_RE = re.compile(r'(\d+):(\d+):(\d+)') + def parseTime(val): """ Parse a string time in either "YYYY-MM-DD HH24:MI:SS" or "YYYY-MM-DD" @@ -98,6 +100,7 @@ def parseTime(val): return calendar.timegm( datetime.datetime(*(date + time)).timetuple()) + def checkForBuilds(session, tag, builds, event, latest=False): """Check that the builds existed in tag at the time of the event. If latest=True, check that the builds are the latest in tag.""" @@ -114,6 +117,7 @@ def checkForBuilds(session, tag, builds, event, latest=False): return True + def duration(start): """Return the duration between start and now in MM:SS format""" elapsed = time.time() - start @@ -121,6 +125,7 @@ def duration(start): secs = int(elapsed % 60) return '%s:%02i' % (mins, secs) + def printList(l): """Print the contents of the list comma-separated""" if len(l) == 0: @@ -135,6 +140,7 @@ def printList(l): ret += l[-1] return ret + def multi_fnmatch(s, patterns): """Returns true if s matches any pattern in the list @@ -147,6 +153,7 @@ def multi_fnmatch(s, patterns): return True return False + def dslice(dict, keys, strict=True): """Returns a new dictionary containing only the specified keys""" ret = {} @@ -156,6 +163,7 @@ def dslice(dict, keys, strict=True): ret[key] = dict[key] return ret + def dslice_ex(dict, keys, strict=True): """Returns a new dictionary with only the specified keys removed""" ret = dict.copy() @@ -488,6 +496,7 @@ def eventFromOpts(session, opts): 'ts' : rinfo['create_ts']} return None + def filedigestAlgo(hdr): """ Get the file digest algorithm used in hdr. @@ -505,6 +514,7 @@ def filedigestAlgo(hdr): digest_algo = koji.RPM_FILEDIGESTALGO_IDS.get(digest_algo_id, 'unknown') return digest_algo.lower() + def parseStatus(rv, prefix): if isinstance(prefix, (list, tuple)): prefix = ' '.join(prefix) @@ -515,6 +525,7 @@ def parseStatus(rv, prefix): else: return '%s terminated for unknown reasons' % prefix + def isSuccess(rv): """Return True if rv indicates successful completion (exited with status 0), False otherwise.""" @@ -523,6 +534,7 @@ def isSuccess(rv): else: return False + def setup_rlimits(opts, logger=None): logger = logger or logging.getLogger("koji") for key in opts: @@ -548,6 +560,7 @@ def setup_rlimits(opts, logger=None): except ValueError as e: logger.error("Unable to set %s: %s", key, e) + class adler32_constructor(object): #mimicing the hashlib constructors @@ -577,6 +590,7 @@ class adler32_constructor(object): digest_size = 4 block_size = 1 #I think + def tsort(parts): """Given a partial ordering, return a totally ordered list. @@ -598,6 +612,7 @@ def tsort(parts): raise ValueError('total ordering not possible') return result + class MavenConfigOptAdapter(object): """ Wrap a ConfigParser so it looks like a optparse.Values instance @@ -621,6 +636,7 @@ class MavenConfigOptAdapter(object): return value raise AttributeError(name) + def maven_opts(values, chain=False, scratch=False): """ Convert the argument (an optparse.Values object) to a dict of build options @@ -656,10 +672,12 @@ def maven_opts(values, chain=False, scratch=False): opts['scratch'] = True return opts + def maven_params(config, package, chain=False, scratch=False): values = MavenConfigOptAdapter(config, package) return maven_opts(values, chain=chain, scratch=scratch) + def wrapper_params(config, package, chain=False, scratch=False): params = {} values = MavenConfigOptAdapter(config, package) @@ -670,6 +688,7 @@ def wrapper_params(config, package, chain=False, scratch=False): params['create_build'] = True return params + def parse_maven_params(confs, chain=False, scratch=False): """ Parse .ini files that contain parameters to launch a Maven build. @@ -703,6 +722,7 @@ def parse_maven_params(confs, chain=False, scratch=False): raise ValueError("No sections found in: %s" % ', '.join(confs)) return builds + def parse_maven_param(confs, chain=False, scratch=False, section=None): """ Parse .ini files that contain parameters to launch a Maven build. @@ -723,6 +743,7 @@ def parse_maven_param(confs, chain=False, scratch=False, section=None): raise ValueError("Multiple sections in: %s, you must specify the section" % ', '.join(confs)) return builds + def parse_maven_chain(confs, scratch=False): """ Parse maven-chain config. @@ -741,6 +762,7 @@ def parse_maven_chain(confs, scratch=False): raise ValueError('No possible build order, missing/circular dependencies') return builds + def to_list(l): """ Helper function for py2/py3 compatibility used e.g. in From 755fa17b49018691bf7399d3e6efcf1241d22cba Mon Sep 17 00:00:00 2001 From: Yu Ming Zhu Date: Dec 11 2018 09:04:44 +0000 Subject: [PATCH 3/6] fix some unittests --- diff --git a/tests/test_lib/test_auth.py b/tests/test_lib/test_auth.py new file mode 100644 index 0000000..0791017 --- /dev/null +++ b/tests/test_lib/test_auth.py @@ -0,0 +1,237 @@ +from __future__ import absolute_import + +import mock + +try: + import unittest2 as unittest +except ImportError: + import unittest +import six + +import koji +import koji.auth + + +class TestAuthSession(unittest.TestCase): + def test_instance(self): + """Simple auth.Session instance""" + s = koji.auth.Session() + # no args in request/environment + self.assertEqual(s.message, 'no session args') + + @mock.patch('koji.auth.context') + def get_session(self, context): + """auth.Session instance""" + # base session from test_basic_instance + context.environ = { + 'QUERY_STRING': 'session-id=123&session-key=xyz&callnum=345', + 'REMOTE_ADDR': 'remote-addr', + } + cursor = mock.MagicMock(name='cursor') + context.cnx.cursor.return_value = cursor + cursor.fetchone.side_effect = [ + # get session + [koji.AUTHTYPE_NORMAL, 344, False, False, 'master', 'start_time', + 'start_ts', 'update_time', 'update_ts', 'user_id'], + # get user + ['name', koji.USER_STATUS['NORMAL'], koji.USERTYPES['NORMAL']], + # get excl.session + None, + # upd. timestamp + None, + # upd callnum + None, + ] + + s = koji.auth.Session() + return s, context, cursor + + @mock.patch('koji.auth.context') + def test_basic_instance(self, context): + """auth.Session instance""" + s, cntext, cursor = self.get_session() + context.cnx = cntext.cnx + + self.assertEqual(s.id, 123) + self.assertEqual(s.key, 'xyz') + self.assertEqual(s.hostip, 'remote-addr') + self.assertEqual(s.callnum, 345) + self.assertEqual(s.user_id, 'user_id') + self.assertEqual(s.authtype, koji.AUTHTYPE_NORMAL) + self.assertEqual(s.master, 'master') + self.assertTrue(s.logged_in) + + # 5 SQL calls: get session, get user, get excl. session, + # update timestamp, update callnum + self.assertEqual(cursor.execute.call_count, 5) + + @mock.patch('koji.auth.context') + def test_getattr(self, context): + """auth.Session instance""" + s, cntext, cursor = self.get_session() + context.cnx = cntext.cnx + + # test + self.assertEqual(s.perms, {}) + self.assertEqual(s.groups, {}) + self.assertEqual(s.host_id, None) + # all other names should raise error + with self.assertRaises(AttributeError): + s.non_existing_attribute + + @mock.patch('koji.auth.context') + def test_str(self, context): + """auth.Session string representation""" + s, cntext, cursor = self.get_session() + context.cnx = cntext.cnx + + s.logged_in = False + s.message = 'msg' + self.assertEqual(str(s), 'session: not logged in (msg)') + s.logged_in = True + self.assertNotEqual(str(s), 'session: not logged in') + + @mock.patch('koji.auth.context') + def test_validate(self, context): + """Session.validate""" + s, cntext, cursor = self.get_session() + context.cnx = cntext.cnx + + s.lockerror = True + with self.assertRaises(koji.AuthLockError): + s.validate() + + s.lockerror = False + self.assertTrue(s.validate()) + + @mock.patch('koji.auth.context') + def test_makeShared(self, context): + """Session.makeShared""" + s, cntext, cursor = self.get_session() + context.cnx = cntext.cnx + + s.makeShared() + c = cursor.execute.call_args[0] + self.assertEqual(c[0], + 'UPDATE sessions SET "exclusive"=NULL WHERE id=%(session_id)s') + self.assertEqual(c[1]['session_id'], 123) + + @mock.patch('socket.gethostbyname') + @mock.patch('koji.auth.context') + def test_get_remote_ip(self, context, gethostbyname): + """Session.get_remote_ip""" + s, cntext, cursor = self.get_session() + + context.opts = {'CheckClientIP': False} + self.assertEqual(s.get_remote_ip(), '-') + + context.opts = {'CheckClientIP': True} + self.assertEqual(s.get_remote_ip(override='xoverride'), 'xoverride') + + context.environ = {'REMOTE_ADDR': '123.123.123.123'} + self.assertEqual(s.get_remote_ip(), '123.123.123.123') + + gethostbyname.return_value = 'ip' + context.environ = {'REMOTE_ADDR': '127.0.0.1'} + self.assertEqual(s.get_remote_ip(), 'ip') + + @mock.patch('koji.auth.context') + def test_login(self, context): + s, cntext, cursor = self.get_session() + context.cnx = cntext.cnx + + # already logged in + with self.assertRaises(koji.GenericError): + s.login('user', 'password') + + s.logged_in = False + with self.assertRaises(koji.AuthError): + s.login('user', 123) + with self.assertRaises(koji.AuthError): + s.login('user', '') + + # correct + s.get_remote_ip = mock.MagicMock() + s.get_remote_ip.return_value = 'hostip' + s.checkLoginAllowed = mock.MagicMock() + s.checkLoginAllowed.return_value = True + s.createSession = mock.MagicMock() + s.createSession.return_value = {'session-id': 'session-id'} + cursor.fetchone = mock.MagicMock() + cursor.fetchone.return_value = ['user_id'] + result = s.login('user', 'password') + + self.assertEqual(s.get_remote_ip.call_count, 1) + self.assertEqual(s.checkLoginAllowed.call_args, mock.call('user_id')) + self.assertEqual(result, s.createSession.return_value) + + # one more try for non-existing user + cursor.fetchone.return_value = None + with self.assertRaises(koji.AuthError): + s.login('user', 'password') + + @mock.patch('koji.auth.context') + @mock.patch('koji.auth.socket') + @mock.patch('koji.auth.base64') + def test_krbLogin(self, base64, socket, context): + # TODO + s, cntext, cursor = self.get_session() + context.cnx = cntext.cnx + + with self.assertRaises(koji.AuthError) as cm: + s.krbLogin('krb_req', 'proxyuser') + self.assertEqual(cm.exception.args[0], 'Already logged in') + + s.logged_in = False + if six.PY3: + with self.assertRaises(koji.AuthError) as cm: + s.krbLogin('krb_req', 'proxyuser') + self.assertEqual(cm.exception.args[0], 'krbV module not installed') + else: + with mock.patch('koji.auth.krbV', create=True) as krbV: + princ = mock.MagicMock() + princ.name = 'princ_name' + krbV.default_context.return_value \ + .rd_req.return_value = (mock.MagicMock(), 2, 3, + [1, 2, princ]) + with self.assertRaises(koji.AuthError) as cm: + s.krbLogin('krb_req', 'proxyuser') + self.assertEqual(cm.exception.args[0], + 'Kerberos principal princ_name is' + ' not authorized to log in other users') + context.opts = {'ProxyPrincipals': 'anyothers,' + princ.name, + 'AuthPrincipal': 'authprinc', + 'AuthKeytab': 'authkeytab', + 'LoginCreatesUser': False, + 'CheckClientIP': False} + with self.assertRaises(koji.AuthError) as cm: + s.krbLogin('krb_req', 'proxyuser@realm.com') + self.assertEqual(cm.exception.args[0], + 'Unknown Kerberos principal:' + ' proxyuser@realm.com') + context.opts['LoginCreatesUser'] = True + context.cnx.cursor.return_value. \ + fetchone.side_effect = [None, + None, + None, + (1,), + ('name', 'type', + koji.USER_STATUS['NORMAL']), + ('session-id',)] + s.krbLogin('krb_req', 'proxyuser@realm.com') + + # functions outside Session object + + @mock.patch('koji.auth.context') + def test_get_user_data(self, context): + """koji.auth.get_user_data""" + cursor = mock.MagicMock(name='cursor') + context.cnx.cursor.return_value = cursor + cursor.fetchone.return_value = ['name', 'status', 'usertype'] + + self.assertEqual(sorted(koji.auth.get_user_data(1).items()), + sorted({'name': 'name', 'status': 'status', + 'usertype': 'usertype'}.items())) + + cursor.fetchone.return_value = None + self.assertEqual(koji.auth.get_user_data(1), None) diff --git a/tests/test_lib/test_context.py b/tests/test_lib/test_context.py index 92d19c0..660e6cd 100644 --- a/tests/test_lib/test_context.py +++ b/tests/test_lib/test_context.py @@ -2,6 +2,7 @@ from __future__ import absolute_import import six import time import random +from six.moves import range try: import unittest2 as unittest except ImportError: diff --git a/tests/test_lib/test_krbv.py b/tests/test_lib/test_krbv.py new file mode 100644 index 0000000..2201f35 --- /dev/null +++ b/tests/test_lib/test_krbv.py @@ -0,0 +1,41 @@ +from __future__ import absolute_import + +import base64 +# This is python-mock, not the rpm mock tool we know and love +import mock +import six +try: + import unittest2 as unittest +except ImportError: + import unittest + +import koji + + +class KrbVTestCase(unittest.TestCase): + @mock.patch('koji.krbV', new=None) + @mock.patch('koji.requests_kerberos', new=None) + def test_krbv_disabled(self): + """Test that when krbV and gssapi are absent, we behave rationally""" + self.assertEquals(koji.krbV, None) + session = koji.ClientSession('whatever') + with self.assertRaises(ImportError): + session.krb_login() + + # this case should work on python3, but skipped still + @unittest.skipIf(six.PY3, "skipped on python3 since missing of python-krbV") + @mock.patch('koji.krbV', create=True) + @mock.patch('requests_kerberos.__version__', new='0.7.0') + @mock.patch('koji.ClientSession._serverPrincipal') + def test_krbv_old_requests_kerberos(self, _serverPrincipal_mock, krbV_mock): + self.assertIsNotNone(koji.krbV) + ctx = koji.krbV.default_context.return_value + ctx.mk_req = mock.MagicMock() + ac = mock.MagicMock() + ctx.mk_req.return_value = (ac, six.b('req')) + ac.rd_priv = mock.MagicMock(return_value='session-id session-key') + session = koji.ClientSession('whatever') + session._callMethod = mock.MagicMock( + return_value=(base64.encodestring(six.b('a')), base64.encodestring(six.b('b')), [0, 1, 2, 3])) + rv = session.krb_login(principal='any@SOMEWHERE.COM', keytab='/path/to/keytab') + self.assertTrue(rv) diff --git a/tests/test_lib/test_restart_tasks.py b/tests/test_lib/test_restart_tasks.py new file mode 100644 index 0000000..f43df03 --- /dev/null +++ b/tests/test_lib/test_restart_tasks.py @@ -0,0 +1,245 @@ +from __future__ import absolute_import +import mock +import shutil +import tempfile +try: + import unittest2 as unittest +except ImportError: + import unittest + +import koji.tasks + + +class TestRestartTask(unittest.TestCase): + + def setUp(self): + self.session = mock.MagicMock() + self.options = mock.MagicMock() + self.manager = mock.MagicMock() + self.workdir = tempfile.mkdtemp() + self.options.workdir = self.workdir + self.safe_rmtree = mock.patch('koji.tasks.safe_rmtree').start() + + def tearDown(self): + shutil.rmtree(self.workdir) + mock.patch.stopall() + + def get_handler(self, *args, **kwargs): + params = koji.encode_args(*args, **kwargs) + handler = koji.tasks.RestartTask(137, 'restart', params, self.session, + self.options) + # this is a foreground task + handler.setManager(self.manager) + return handler + + def test_restart_task(self): + host = {'id': 'HOST ID'} + self.session.host.getID.return_value = "HOST ID" + handler = self.get_handler(host) + self.assertEqual(handler.Foreground, True) + result = handler.run() + + self.assertEqual(self.manager.restart_pending, True) + + def test_restart_wrong_host(self): + host = {'id': 'HOST ID'} + self.session.host.getID.return_value = "ANOTHER HOST" + handler = self.get_handler(host) + self.assertEqual(handler.Foreground, True) + with self.assertRaises(koji.GenericError): + result = handler.run() + + +class TestRestartVerifyTask(unittest.TestCase): + + def setUp(self): + self.session = mock.MagicMock() + self.options = mock.MagicMock() + self.manager = mock.MagicMock() + self.workdir = tempfile.mkdtemp() + self.options.workdir = self.workdir + self.safe_rmtree = mock.patch('koji.tasks.safe_rmtree').start() + + def tearDown(self): + shutil.rmtree(self.workdir) + mock.patch.stopall() + + def get_handler(self, *args, **kwargs): + params = koji.encode_args(*args, **kwargs) + handler = koji.tasks.RestartVerifyTask(137, 'restartVerify', params, self.session, + self.options) + # this is a foreground task + handler.setManager(self.manager) + return handler + + def test_restart_verify_task(self): + task1 = { + 'id': 'TASK ID', + 'state': koji.TASK_STATES['CLOSED'], + 'completion_ts': 10, + } + host = {'id': 'HOST ID'} + self.session.host.getID.return_value = "HOST ID" + self.session.getTaskInfo.return_value = task1 + handler = self.get_handler(task1['id'], host) + self.manager.start_time = 100 # greater than task1['start_time'] + self.assertEqual(handler.Foreground, True) + result = handler.run() + + def test_restart_verify_not_closed(self): + task1 = { + 'id': 'TASK ID', + 'state': koji.TASK_STATES['OPEN'], + 'completion_ts': 10, + } + host = {'id': 'HOST ID'} + self.session.host.getID.return_value = "HOST ID" + self.session.getTaskInfo.return_value = task1 + handler = self.get_handler(task1['id'], host) + try: + result = handler.run() + except koji.GenericError as e: + self.assertEqual(e.args[0], 'Stage one restart task is OPEN') + else: + raise Exception('Error not raised') + + def test_restart_verify_wrong_host(self): + task1 = { + 'id': 'TASK ID', + 'state': koji.TASK_STATES['CLOSED'], + 'completion_ts': 10, + } + host = {'id': 'HOST ID'} + self.session.host.getID.return_value = "OTHER HOST" + self.session.getTaskInfo.return_value = task1 + handler = self.get_handler(task1['id'], host) + try: + result = handler.run() + except koji.GenericError as e: + self.assertEqual(e.args[0], 'Host mismatch') + else: + raise Exception('Error not raised') + + def test_restart_verify_wrong_time(self): + task1 = { + 'id': 'TASK ID', + 'state': koji.TASK_STATES['CLOSED'], + 'completion_ts': 10, + } + host = {'id': 'HOST ID'} + self.session.host.getID.return_value = "HOST ID" + self.session.getTaskInfo.return_value = task1 + handler = self.get_handler(task1['id'], host) + self.manager.start_time = 0 # LESS THAN task1['start_time'] + try: + result = handler.run() + except koji.GenericError as e: + self.assertEqual(e.args[0][:30], 'Restart failed - start time is') + else: + raise Exception('Error not raised') + + +class TestRestartHostsTask(unittest.TestCase): + + def setUp(self): + self.session = mock.MagicMock() + self.options = mock.MagicMock() + self.manager = mock.MagicMock() + self.workdir = tempfile.mkdtemp() + self.options.workdir = self.workdir + self.safe_rmtree = mock.patch('koji.tasks.safe_rmtree').start() + + def tearDown(self): + shutil.rmtree(self.workdir) + mock.patch.stopall() + + def get_handler(self, *args, **kwargs): + params = koji.encode_args(*args, **kwargs) + handler = koji.tasks.RestartHostsTask(137, 'restartHosts', params, self.session, + self.options) + handler.wait = mock.MagicMock() + handler.subtask = mock.MagicMock() + return handler + + def test_restart_hosts_task(self): + self.session.host.getID.return_value = "THIS HOST" + host = {'id': 99} + self.session.listHosts.return_value = [host] + handler = self.get_handler({}) + handler.subtask.side_effect = [101, 102] + result = handler.run() + + self.session.listHosts.assert_called_once_with(enabled=True) + self.session.taskFinished.assert_not_called() + handler.wait.assert_called_once_with([101, 102], all=True, timeout=3600*24) + # subtask calls + call1 = mock.call('restart', [host], assign=host['id'], label="restart %i" % host['id']) + call2 = mock.call('restartVerify', [101, host], assign=host['id'], label="sleep %i" % host['id']) + handler.subtask.assert_has_calls([call1, call2]) + + def test_restart_hosts_no_host(self): + self.session.listHosts.return_value = [] + handler = self.get_handler({}) + try: + result = handler.run() + except koji.GenericError as e: + self.assertEqual(e.args[0], 'No matching hosts') + else: + raise Exception('Error not raised') + + self.session.listHosts.assert_called_once_with(enabled=True) + self.session.taskFinished.assert_not_called() + handler.wait.assert_not_called() + handler.subtask.assert_not_called() + + def test_restart_hosts_with_opts(self): + self.session.host.getID.return_value = "THIS HOST" + host = {'id': 99} + self.session.listHosts.return_value = [host] + self.session.getChannel.return_value = {'id': 1, 'name': 'default'} + handler = self.get_handler({'channel': 'default', 'arches': ['x86_64']}) + handler.subtask.side_effect = [101, 102] + result = handler.run() + + self.session.listHosts.assert_called_once_with(enabled=True, channelID=1, arches=['x86_64']) + self.session.taskFinished.assert_not_called() + handler.wait.assert_called_once_with([101, 102], all=True, timeout=3600*24) + # subtask calls + call1 = mock.call('restart', [host], assign=host['id'], label="restart %i" % host['id']) + call2 = mock.call('restartVerify', [101, host], assign=host['id'], label="sleep %i" % host['id']) + handler.subtask.assert_has_calls([call1, call2]) + + def test_restart_hosts_self_finished(self): + self.session.host.getID.return_value = 99 + host = {'id': 99} + self.session.listHosts.return_value = [host] + handler = self.get_handler({}) + self.session.taskFinished.return_value = True + handler.subtask.side_effect = [101, 102] + result = handler.run() + + self.session.listHosts.assert_called_once_with(enabled=True) + self.session.taskFinished.assert_called_once() + call1 = mock.call('restart', [host], assign=host['id'], label="restart %i" % host['id']) + call2 = mock.call('restartVerify', [101, host], assign=host['id'], label="sleep %i" % host['id']) + handler.subtask.assert_has_calls([call1, call2]) + call1 = mock.call(101, timeout=3600*24) + call2 = mock.call([101, 102], all=True, timeout=3600*24) + handler.wait.assert_has_calls([call1, call2]) + + def test_restart_hosts_self_unfinished(self): + self.session.host.getID.return_value = 99 + host = {'id': 99} + self.session.listHosts.return_value = [host] + handler = self.get_handler({}) + self.session.taskFinished.return_value = False + handler.subtask.side_effect = [101, 102] + with self.assertRaises(koji.tasks.ServerRestart): + result = handler.run() + + self.session.listHosts.assert_called_once_with(enabled=True) + self.session.taskFinished.assert_called_once() + call1 = mock.call('restart', [host], assign=host['id'], label="restart %i" % host['id']) + call2 = mock.call('restartVerify', [101, host], assign=host['id'], label="sleep %i" % host['id']) + handler.subtask.assert_has_calls([call1, call2]) + handler.wait.assert_called_once_with(101, timeout=3600*24) diff --git a/tests/test_lib_py2only/test_auth.py b/tests/test_lib_py2only/test_auth.py deleted file mode 100644 index 0ee45a9..0000000 --- a/tests/test_lib_py2only/test_auth.py +++ /dev/null @@ -1,193 +0,0 @@ -from __future__ import absolute_import -import mock -try: - import unittest2 as unittest -except ImportError: - import unittest - -import koji -import koji.auth - -class TestAuthSession(unittest.TestCase): - def test_instance(self): - """Simple auth.Session instance""" - s = koji.auth.Session() - # no args in request/environment - self.assertEqual(s.message, 'no session args') - - @mock.patch('koji.auth.context') - def get_session(self, context): - """auth.Session instance""" - # base session from test_basic_instance - context.environ = { - 'QUERY_STRING': 'session-id=123&session-key=xyz&callnum=345', - 'REMOTE_ADDR': 'remote-addr', - } - cursor = mock.MagicMock(name='cursor') - context.cnx.cursor.return_value = cursor - cursor.fetchone.side_effect = [ - # get session - [koji.AUTHTYPE_NORMAL, 344, False, False, 'master', 'start_time', 'start_ts', 'update_time', 'update_ts', 'user_id'], - # get user - ['name', koji.USER_STATUS['NORMAL'], koji.USERTYPES['NORMAL']], - # get excl.session - None, - # upd. timestamp - None, - # upd callnum - None, - ] - - s = koji.auth.Session() - return s, context, cursor - - @mock.patch('koji.auth.context') - def test_basic_instance(self, context): - """auth.Session instance""" - s, cntext, cursor = self.get_session() - context.cnx = cntext.cnx - - self.assertEqual(s.id, 123) - self.assertEqual(s.key, 'xyz') - self.assertEqual(s.hostip, 'remote-addr') - self.assertEqual(s.callnum, 345) - self.assertEqual(s.user_id, 'user_id') - self.assertEqual(s.authtype, koji.AUTHTYPE_NORMAL) - self.assertEqual(s.master, 'master') - self.assertTrue(s.logged_in) - - # 5 SQL calls: get session, get user, get excl. session, - # update timestamp, update callnum - self.assertEqual(cursor.execute.call_count, 5) - - @mock.patch('koji.auth.context') - def test_getattr(self, context): - """auth.Session instance""" - s, cntext, cursor = self.get_session() - context.cnx = cntext.cnx - - # test - self.assertEqual(s.perms, {}) - self.assertEqual(s.groups, {}) - self.assertEqual(s.host_id, None) - # all other names should raise error - with self.assertRaises(AttributeError): - s.non_existing_attribute - - @mock.patch('koji.auth.context') - def test_str(self, context): - """auth.Session string representation""" - s, cntext, cursor = self.get_session() - context.cnx = cntext.cnx - - s.logged_in = False - s.message = 'msg' - self.assertEqual(str(s), 'session: not logged in (msg)') - s.logged_in = True - self.assertNotEqual(str(s), 'session: not logged in') - - @mock.patch('koji.auth.context') - def test_validate(self, context): - """Session.validate""" - s, cntext, cursor = self.get_session() - context.cnx = cntext.cnx - - s.lockerror = True - with self.assertRaises(koji.AuthLockError): - s.validate() - - s.lockerror = False - self.assertTrue(s.validate()) - - @mock.patch('koji.auth.context') - def test_makeShared(self, context): - """Session.makeShared""" - s, cntext, cursor = self.get_session() - context.cnx = cntext.cnx - - s.makeShared() - c = cursor.execute.call_args[0] - self.assertEqual(c[0], 'UPDATE sessions SET "exclusive"=NULL WHERE id=%(session_id)s') - self.assertEqual(c[1]['session_id'], 123) - - @mock.patch('socket.gethostbyname') - @mock.patch('koji.auth.context') - def test_get_remote_ip(self, context, gethostbyname): - """Session.get_remote_ip""" - s, cntext, cursor = self.get_session() - - context.opts = {'CheckClientIP': False} - self.assertEqual(s.get_remote_ip(), '-') - - context.opts = {'CheckClientIP': True} - self.assertEqual(s.get_remote_ip(override='xoverride'), 'xoverride') - - context.environ = {'REMOTE_ADDR': '123.123.123.123'} - self.assertEqual(s.get_remote_ip(), '123.123.123.123') - - gethostbyname.return_value = 'ip' - context.environ = {'REMOTE_ADDR': '127.0.0.1'} - self.assertEqual(s.get_remote_ip(), 'ip') - - @mock.patch('koji.auth.context') - def test_login(self, context): - s, cntext, cursor = self.get_session() - context.cnx = cntext.cnx - - # already logged in - with self.assertRaises(koji.GenericError): - s.login('user', 'password') - - s.logged_in = False - with self.assertRaises(koji.AuthError): - s.login('user', 123) - with self.assertRaises(koji.AuthError): - s.login('user', '') - - # correct - s.get_remote_ip = mock.MagicMock() - s.get_remote_ip.return_value = 'hostip' - s.checkLoginAllowed = mock.MagicMock() - s.checkLoginAllowed.return_value = True - s.createSession = mock.MagicMock() - s.createSession.return_value = {'session-id': 'session-id'} - cursor.fetchone = mock.MagicMock() - cursor.fetchone.return_value = ['user_id'] - result = s.login('user', 'password') - - self.assertEqual(s.get_remote_ip.call_count, 1) - self.assertEqual(s.checkLoginAllowed.call_args, mock.call('user_id')) - self.assertEqual(result, s.createSession.return_value) - - # one more try for non-existing user - cursor.fetchone.return_value = None - with self.assertRaises(koji.AuthError): - s.login('user', 'password') - - @mock.patch('koji.auth.context') - def test_krbLogin(self, context): - # TODO - s, cntext, cursor = self.get_session() - context.cnx = cntext.cnx - - with self.assertRaises(koji.AuthError): - s.krbLogin('krb_req', 'proxyuser') - - s.logged_in = False - with self.assertRaises(TypeError): - s.krbLogin('krb_req', 'proxyuser') - - # functions outside Session object - - @mock.patch('koji.auth.context') - def test_get_user_data(self, context): - """koji.auth.get_user_data""" - cursor = mock.MagicMock(name='cursor') - context.cnx.cursor.return_value = cursor - cursor.fetchone.return_value = ['name', 'status', 'usertype'] - - self.assertEqual(sorted(koji.auth.get_user_data(1).items()), - sorted({'name': 'name', 'status': 'status', 'usertype': 'usertype'}.items())) - - cursor.fetchone.return_value = None - self.assertEqual(koji.auth.get_user_data(1), None) diff --git a/tests/test_lib_py2only/test_krbv.py b/tests/test_lib_py2only/test_krbv.py deleted file mode 100644 index 47e6943..0000000 --- a/tests/test_lib_py2only/test_krbv.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import absolute_import - -import base64 -# This is python-mock, not the rpm mock tool we know and love -import mock -import six -try: - import unittest2 as unittest -except ImportError: - import unittest - -import koji - - -class KrbVTestCase(unittest.TestCase): - @mock.patch('koji.krbV', new=None) - @mock.patch('koji.requests_kerberos', new=None) - def test_krbv_disabled(self): - """Test that when krbV and gssapi are absent, we behave rationally""" - self.assertEquals(koji.krbV, None) - session = koji.ClientSession('whatever') - with self.assertRaises(ImportError): - session.krb_login() - - @mock.patch('koji.krbV', create=True) - @mock.patch('requests_kerberos.__version__', new='0.7.0') - @mock.patch('koji.ClientSession._serverPrincipal') - def test_krbv_old_requests_kerberos(self, _serverPrincipal_mock, krbV_mock): - self.assertIsNotNone(koji.krbV) - ctx = koji.krbV.default_context.return_value - ctx.mk_req = mock.MagicMock() - ac = mock.MagicMock() - ctx.mk_req.return_value = (ac, six.b('req')) - ac.rd_priv = mock.MagicMock(return_value='session-id session-key') - session = koji.ClientSession('whatever') - session._callMethod = mock.MagicMock( - return_value=(base64.encodestring(six.b('a')), base64.encodestring(six.b('b')), [0, 1, 2, 3])) - rv = session.krb_login(principal='any@SOMEWHERE.COM', keytab='/path/to/keytab') - self.assertTrue(rv) diff --git a/tests/test_lib_py2only/test_restart_tasks.py b/tests/test_lib_py2only/test_restart_tasks.py deleted file mode 100644 index f43df03..0000000 --- a/tests/test_lib_py2only/test_restart_tasks.py +++ /dev/null @@ -1,245 +0,0 @@ -from __future__ import absolute_import -import mock -import shutil -import tempfile -try: - import unittest2 as unittest -except ImportError: - import unittest - -import koji.tasks - - -class TestRestartTask(unittest.TestCase): - - def setUp(self): - self.session = mock.MagicMock() - self.options = mock.MagicMock() - self.manager = mock.MagicMock() - self.workdir = tempfile.mkdtemp() - self.options.workdir = self.workdir - self.safe_rmtree = mock.patch('koji.tasks.safe_rmtree').start() - - def tearDown(self): - shutil.rmtree(self.workdir) - mock.patch.stopall() - - def get_handler(self, *args, **kwargs): - params = koji.encode_args(*args, **kwargs) - handler = koji.tasks.RestartTask(137, 'restart', params, self.session, - self.options) - # this is a foreground task - handler.setManager(self.manager) - return handler - - def test_restart_task(self): - host = {'id': 'HOST ID'} - self.session.host.getID.return_value = "HOST ID" - handler = self.get_handler(host) - self.assertEqual(handler.Foreground, True) - result = handler.run() - - self.assertEqual(self.manager.restart_pending, True) - - def test_restart_wrong_host(self): - host = {'id': 'HOST ID'} - self.session.host.getID.return_value = "ANOTHER HOST" - handler = self.get_handler(host) - self.assertEqual(handler.Foreground, True) - with self.assertRaises(koji.GenericError): - result = handler.run() - - -class TestRestartVerifyTask(unittest.TestCase): - - def setUp(self): - self.session = mock.MagicMock() - self.options = mock.MagicMock() - self.manager = mock.MagicMock() - self.workdir = tempfile.mkdtemp() - self.options.workdir = self.workdir - self.safe_rmtree = mock.patch('koji.tasks.safe_rmtree').start() - - def tearDown(self): - shutil.rmtree(self.workdir) - mock.patch.stopall() - - def get_handler(self, *args, **kwargs): - params = koji.encode_args(*args, **kwargs) - handler = koji.tasks.RestartVerifyTask(137, 'restartVerify', params, self.session, - self.options) - # this is a foreground task - handler.setManager(self.manager) - return handler - - def test_restart_verify_task(self): - task1 = { - 'id': 'TASK ID', - 'state': koji.TASK_STATES['CLOSED'], - 'completion_ts': 10, - } - host = {'id': 'HOST ID'} - self.session.host.getID.return_value = "HOST ID" - self.session.getTaskInfo.return_value = task1 - handler = self.get_handler(task1['id'], host) - self.manager.start_time = 100 # greater than task1['start_time'] - self.assertEqual(handler.Foreground, True) - result = handler.run() - - def test_restart_verify_not_closed(self): - task1 = { - 'id': 'TASK ID', - 'state': koji.TASK_STATES['OPEN'], - 'completion_ts': 10, - } - host = {'id': 'HOST ID'} - self.session.host.getID.return_value = "HOST ID" - self.session.getTaskInfo.return_value = task1 - handler = self.get_handler(task1['id'], host) - try: - result = handler.run() - except koji.GenericError as e: - self.assertEqual(e.args[0], 'Stage one restart task is OPEN') - else: - raise Exception('Error not raised') - - def test_restart_verify_wrong_host(self): - task1 = { - 'id': 'TASK ID', - 'state': koji.TASK_STATES['CLOSED'], - 'completion_ts': 10, - } - host = {'id': 'HOST ID'} - self.session.host.getID.return_value = "OTHER HOST" - self.session.getTaskInfo.return_value = task1 - handler = self.get_handler(task1['id'], host) - try: - result = handler.run() - except koji.GenericError as e: - self.assertEqual(e.args[0], 'Host mismatch') - else: - raise Exception('Error not raised') - - def test_restart_verify_wrong_time(self): - task1 = { - 'id': 'TASK ID', - 'state': koji.TASK_STATES['CLOSED'], - 'completion_ts': 10, - } - host = {'id': 'HOST ID'} - self.session.host.getID.return_value = "HOST ID" - self.session.getTaskInfo.return_value = task1 - handler = self.get_handler(task1['id'], host) - self.manager.start_time = 0 # LESS THAN task1['start_time'] - try: - result = handler.run() - except koji.GenericError as e: - self.assertEqual(e.args[0][:30], 'Restart failed - start time is') - else: - raise Exception('Error not raised') - - -class TestRestartHostsTask(unittest.TestCase): - - def setUp(self): - self.session = mock.MagicMock() - self.options = mock.MagicMock() - self.manager = mock.MagicMock() - self.workdir = tempfile.mkdtemp() - self.options.workdir = self.workdir - self.safe_rmtree = mock.patch('koji.tasks.safe_rmtree').start() - - def tearDown(self): - shutil.rmtree(self.workdir) - mock.patch.stopall() - - def get_handler(self, *args, **kwargs): - params = koji.encode_args(*args, **kwargs) - handler = koji.tasks.RestartHostsTask(137, 'restartHosts', params, self.session, - self.options) - handler.wait = mock.MagicMock() - handler.subtask = mock.MagicMock() - return handler - - def test_restart_hosts_task(self): - self.session.host.getID.return_value = "THIS HOST" - host = {'id': 99} - self.session.listHosts.return_value = [host] - handler = self.get_handler({}) - handler.subtask.side_effect = [101, 102] - result = handler.run() - - self.session.listHosts.assert_called_once_with(enabled=True) - self.session.taskFinished.assert_not_called() - handler.wait.assert_called_once_with([101, 102], all=True, timeout=3600*24) - # subtask calls - call1 = mock.call('restart', [host], assign=host['id'], label="restart %i" % host['id']) - call2 = mock.call('restartVerify', [101, host], assign=host['id'], label="sleep %i" % host['id']) - handler.subtask.assert_has_calls([call1, call2]) - - def test_restart_hosts_no_host(self): - self.session.listHosts.return_value = [] - handler = self.get_handler({}) - try: - result = handler.run() - except koji.GenericError as e: - self.assertEqual(e.args[0], 'No matching hosts') - else: - raise Exception('Error not raised') - - self.session.listHosts.assert_called_once_with(enabled=True) - self.session.taskFinished.assert_not_called() - handler.wait.assert_not_called() - handler.subtask.assert_not_called() - - def test_restart_hosts_with_opts(self): - self.session.host.getID.return_value = "THIS HOST" - host = {'id': 99} - self.session.listHosts.return_value = [host] - self.session.getChannel.return_value = {'id': 1, 'name': 'default'} - handler = self.get_handler({'channel': 'default', 'arches': ['x86_64']}) - handler.subtask.side_effect = [101, 102] - result = handler.run() - - self.session.listHosts.assert_called_once_with(enabled=True, channelID=1, arches=['x86_64']) - self.session.taskFinished.assert_not_called() - handler.wait.assert_called_once_with([101, 102], all=True, timeout=3600*24) - # subtask calls - call1 = mock.call('restart', [host], assign=host['id'], label="restart %i" % host['id']) - call2 = mock.call('restartVerify', [101, host], assign=host['id'], label="sleep %i" % host['id']) - handler.subtask.assert_has_calls([call1, call2]) - - def test_restart_hosts_self_finished(self): - self.session.host.getID.return_value = 99 - host = {'id': 99} - self.session.listHosts.return_value = [host] - handler = self.get_handler({}) - self.session.taskFinished.return_value = True - handler.subtask.side_effect = [101, 102] - result = handler.run() - - self.session.listHosts.assert_called_once_with(enabled=True) - self.session.taskFinished.assert_called_once() - call1 = mock.call('restart', [host], assign=host['id'], label="restart %i" % host['id']) - call2 = mock.call('restartVerify', [101, host], assign=host['id'], label="sleep %i" % host['id']) - handler.subtask.assert_has_calls([call1, call2]) - call1 = mock.call(101, timeout=3600*24) - call2 = mock.call([101, 102], all=True, timeout=3600*24) - handler.wait.assert_has_calls([call1, call2]) - - def test_restart_hosts_self_unfinished(self): - self.session.host.getID.return_value = 99 - host = {'id': 99} - self.session.listHosts.return_value = [host] - handler = self.get_handler({}) - self.session.taskFinished.return_value = False - handler.subtask.side_effect = [101, 102] - with self.assertRaises(koji.tasks.ServerRestart): - result = handler.run() - - self.session.listHosts.assert_called_once_with(enabled=True) - self.session.taskFinished.assert_called_once() - call1 = mock.call('restart', [host], assign=host['id'], label="restart %i" % host['id']) - call2 = mock.call('restartVerify', [101, host], assign=host['id'], label="sleep %i" % host['id']) - handler.subtask.assert_has_calls([call1, call2]) - handler.wait.assert_called_once_with(101, timeout=3600*24) From c3924be9b9e4e2cb42f56b1e76f0cd9d39328590 Mon Sep 17 00:00:00 2001 From: Yu Ming Zhu Date: Dec 11 2018 09:36:33 +0000 Subject: [PATCH 4/6] sorted set in Error msgs of tasks.find_arch --- diff --git a/koji/tasks.py b/koji/tasks.py index 4e1fa12..32397d5 100644 --- a/koji/tasks.py +++ b/koji/tasks.py @@ -504,7 +504,8 @@ class BaseTaskHandler(object): # c) is canonical host_arches = host['arches'] if not host_arches: - raise koji.BuildError("No arch list for this host: %s" % host['name']) + raise koji.BuildError("No arch list for this host: %s" % + host['name']) tag_arches = tag['arches'] if not tag_arches: raise koji.BuildError("No arch list for tag: %s" % tag['name']) @@ -520,12 +521,17 @@ class BaseTaskHandler(object): # because we just forked from a common parent random.seed() arch = random.choice(common_arches) - self.logger.info('Valid arches: %s, using: %s' % (' '.join(common_arches), arch)) + self.logger.info('Valid arches: %s, using: %s' % + (' '.join(sorted(common_arches)), arch)) return arch else: # no overlap - raise koji.BuildError("host %s (%s) does not support any arches of tag %s (%s)" % \ - (host['name'], ', '.join(host_arches), tag['name'], ', '.join(tag_arches))) + raise koji.BuildError("host %s (%s) does not support any arches" + " of tag %s (%s)" % + (host['name'], + ', '.join(sorted(host_arches)), + tag['name'], + ', '.join(sorted(tag_arches)))) def getRepo(self, tag): """ diff --git a/tests/test_lib/test_tasks.py b/tests/test_lib/test_tasks.py new file mode 100644 index 0000000..e196453 --- /dev/null +++ b/tests/test_lib/test_tasks.py @@ -0,0 +1,817 @@ +from __future__ import absolute_import +import random +import shutil +import six +from six.moves import range +try: + import unittest2 as unittest +except ImportError: + import unittest + +from os import path, makedirs +from tempfile import gettempdir +from mock import patch, MagicMock, Mock, call + +import koji +from koji.tasks import BaseTaskHandler, FakeTask, ForkTask, SleepTask, \ + WaitTestTask, scan_mounts, umount_all, \ + safe_rmtree + + +def get_fake_mounts_file(): + """ Returns contents of /prc/mounts in a file-like object + """ + return six.StringIO(six.text_type(( + 'sysfs /sys sysfs rw,seclabel,nosuid,nodev,noexec,relatime 0 0\n' + 'proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0\n' + 'devtmpfs /dev devtmpfs rw,seclabel,nosuid,size=238836k,nr_inodes=59709,mode=755 0 0\n' + 'securityfs /sys/kernel/security securityfs rw,nosuid,nodev,noexec,relatime 0 0\n' + 'tmpfs /dev/shm\\040(deleted) tmpfs rw,seclabel,nosuid,nodev 0 0\n' + 'devpts /dev/pts devpts rw,seclabel,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0\n' + 'tmpfs /run tmpfs rw,seclabel,nosuid,nodev,mode=755 0 0\n' + 'tmpfs /sys/fs/cgroup tmpfs ro,seclabel,nosuid,nodev,noexec,mode=755 0 0\n' + 'pstore /sys/fs/pstore pstore rw,seclabel,nosuid,nodev,noexec,relatime 0 0\n' + 'cgroup /sys/fs/cgroup/devices cgroup rw,nosuid,nodev,noexec,relatime,devices 0 0\n' + 'cgroup /sys/fs/cgroup/perf_event cgroup rw,nosuid,nodev,noexec,relatime,perf_event 0 0\n' + 'cgroup /sys/fs/cgroup/net_cls,net_prio cgroup rw,nosuid,nodev,noexec,relatime,net_cls,net_prio 0 0\n' + 'cgroup /sys/fs/cgroup/cpu,cpuacct cgroup rw,nosuid,nodev,noexec,relatime,cpu,cpuacct 0 0\n' + 'cgroup /sys/fs/cgroup/blkio cgroup rw,nosuid,nodev,noexec,relatime,blkio 0 0\n' + 'cgroup /sys/fs/cgroup/cpuset cgroup rw,nosuid,nodev,noexec,relatime,cpuset 0 0\n' + 'cgroup /sys/fs/cgroup/freezer cgroup rw,nosuid,nodev,noexec,relatime,freezer 0 0\n' + 'cgroup /sys/fs/cgroup/memory cgroup rw,nosuid,nodev,noexec,relatime,memory 0 0\n' + 'cgroup /sys/fs/cgroup/hugetlb cgroup rw,nosuid,nodev,noexec,relatime,hugetlb 0 0\n' + 'configfs /sys/kernel/config configfs rw,relatime 0 0\n' + 'hugetlbfs /dev/hugepages hugetlbfs rw,seclabel,relatime 0 0\n' + 'mqueue /dev/mqueue mqueue rw,seclabel,relatime 0 0\n' + ))) + + +def get_temp_dir_root(): + return path.join(gettempdir(), 'koji_tests') + + +def get_tmp_dir_path(folder_starts_with): + return path.join(get_temp_dir_root(), ('{0}{1}'.format(folder_starts_with, random.randint(1, 999999999999)))) + + +class TestTask(BaseTaskHandler): + Methods = ['some_method'] + _taskWeight = 5.2 + + def handler(self, *args): + return 42 + + +class TestTaskNoWeight(BaseTaskHandler): + Methods = ['some_method'] + + def handler(self, *args): + return 42 + + +class BadTask(BaseTaskHandler): + Methods = ['some_method'] + + +class TasksTestCase(unittest.TestCase): + + def tearDown(self): + temp_dir_root = get_temp_dir_root() + + if path.isdir(temp_dir_root): + shutil.rmtree(get_temp_dir_root()) + + def test_scan_mounts_results(self): + """ Tests the scan_mounts function with a mocked /proc/mounts file. A list containing mount points + starting with /dev are expected to be returned from the function based on the function input of /dev. + """ + fake_mounts_file_contents = get_fake_mounts_file() + + with patch('koji.tasks.open', return_value=fake_mounts_file_contents, create=True): + self.assertIn(scan_mounts('/dev'), [['/dev/shm', '/dev/pts', '/dev/mqueue', '/dev/hugepages', '/dev']]) + + def test_scan_mounts_no_results(self): + """ Tests the scan_mounts function with a mocked /proc/mounts file. An argument of /nonexistent/path + to the function should return an empty list. + """ + fake_mounts_file_contents = get_fake_mounts_file() + + with patch('koji.tasks.open', return_value=fake_mounts_file_contents, create=True): + self.assertEquals(scan_mounts('/nonexistent/path'), []) + + # Patching the scan_mounts function instead of the built-in open function because this is only testing umount_all + @patch('koji.tasks.scan_mounts', side_effect=[['/dev/shm', '/dev/pts', '/dev/mqueue'], []]) + @patch('os.spawnvp', return_value=0) + def test_umount_all(self, mocked_spawnvp, mocked_scan_mounts): + """ Tests that umount_all returns nothing when successful. + """ + self.assertEquals(umount_all('/test'), None) + + # Patching the scan_mounts function instead of the built-in open function because this is only testing umount_all + @patch('koji.tasks.scan_mounts', return_value=['/dev/shm', '/dev/pts', '/dev/mqueue']) + @patch('os.spawnvp', return_value=1) + def test_umount_all_failure(self, mocked_spawnvp, mocked_scan_mounts): + """ Tests that umount_all raises an exception when a mount point can't be unmounted. + """ + try: + umount_all('/dev') + raise Exception('A GenericError was not raised during the test') + except koji.GenericError as e: + self.assertEquals(e.args[0], + 'umount failed (exit code 1) for /dev/shm') + + # Patching the scan_mounts function instead of the built-in open function because this is only testing umount_all + @patch('koji.tasks.scan_mounts', side_effect=[['/dev/shm', '/dev/pts', '/dev/mqueue'], ['/dev/shm', '/dev/mqueue']]) + @patch('os.spawnvp', return_value=0) + def test_umount_all_unexpected_failure(self, mocked_spawnvp, mocked_scan_mounts): + """ Tests that umount_all will fail if the command to unmount the mount points was successful + but a second run of scan_mounts still shows some of the unmount mount points still mounted. + """ + try: + umount_all('/dev') + raise Exception('A GenericError was not raised during the test') + except koji.GenericError as e: + self.assertEquals(e.args[0], 'Unmounting incomplete: [\'/dev/shm\', \'/dev/mqueue\']') + + def test_BaseTaskHandler_handler_not_set(self): + """ Tests that an exception is thrown when the handler function is not overwritten by the child class. + """ + obj = BadTask(123, 'some_method', ['random_arg'], None, None, (get_tmp_dir_path('BadTask'))) + try: + obj.handler() + raise Exception('The NotImplementedError exception was not raised') + except NotImplementedError as e: + self.assertEquals(e.__class__.__name__, 'NotImplementedError') + + def test_BaseTaskHandler_weight_default(self): + """ Tests that the weight function returns 1.0 when _taskWeight is not set in the child class' definition. + """ + obj = TestTaskNoWeight(123, 'some_method', ['random_arg'], None, None, (get_tmp_dir_path('TestTaskNoWeight'))) + self.assertEquals(obj.weight(), 1.0) + + def test_BaseTaskHandler_weight_set(self): + """ Tests that the weight function returns the value of _taskWeight when it is set in the + child class' definition. + """ + obj = TestTask(123, 'some_method', ['random_arg'], None, None, (get_tmp_dir_path('TestTask'))) + self.assertEquals(obj.weight(), 5.2) + + def test_BaseTaskHandler_createWorkdir_workdir_not_defined(self): + """ Tests that the createWorkdir function does nothing when the workdir member variable is set to None. + """ + temp_path = get_tmp_dir_path('TestTask') + obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) + obj.workdir = None + obj.createWorkdir() + self.assertEquals(path.isdir(temp_path), False) + + # This patch removes the dependence on removeWorkdir functioning + @patch('{0}.TestTask.removeWorkdir'.format(__name__)) + def test_BaseTaskHandler_createWorkdir(self, mock_removeWorkDir): + """ Tests that the createWorkdir function creates a folder based on the path given to the + workdir member variable. + """ + temp_path = get_tmp_dir_path('TestTask') + obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) + obj.createWorkdir() + self.assertEquals(path.isdir(temp_path), True) + shutil.rmtree(get_temp_dir_root()) + + def test_BaseTaskHandler_removeWorkdir(self): + """ Tests that the removeWOrkdir function deletes a folder based on the path given to the + workdir member variable. + """ + temp_path = get_tmp_dir_path('TestTask') + obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) + makedirs(temp_path) + self.assertEquals(path.isdir(temp_path), True) + obj.removeWorkdir() + self.assertEquals(path.isdir(temp_path), False) + + def test_BaseTaskHandler_wait_all_done(self): + """ Tests that the wait function returns the subtask results of when the taskWait function returns only + two finished tasks + """ + temp_path = get_tmp_dir_path('TestTask') + obj = TestTask(12345678, 'some_method', ['random_arg'], None, None, temp_path) + makedirs(temp_path) + obj.session = Mock() + obj.session.host.taskSetWait.return_value = None + obj.session.host.taskWait.return_value = [[1551234, 1591234], []] + taskWaitResults = [ + ['1551234', { + 'brootid': 2342345, + 'logs': ['tasks/5678/12345678/root.log', + 'tasks/5678/12345678/state.log', + 'tasks/5678/12345678/build.log'], + 'srpm': 'tasks/5678/12345678/some_package-1.2.3p5-25.src.rpm' + }], + + ['1591234', { + 'brootid': 1231234, + 'logs': ['tasks/6789/2345678/root.log', + 'tasks/6789/2345678/state.log', + 'tasks/6789/2345678/build.log'], + 'rpms': ['tasks/6789/2345678/some_other_package-doc-1.2.3p5-25.el7.noarch.rpm'], + 'srpms': ['tasks/6789/2345678/some_other_package-1.2.3p5-25.el7.src.rpm'] + }] + ] + + obj.session.host.taskWaitResults.return_value = taskWaitResults + self.assertEquals(obj.wait([1551234, 1591234]), dict(taskWaitResults)) + obj.session.host.taskSetWait.assert_called_once_with(12345678, [1551234, 1591234]) + obj.session.host.taskWaitResults.assert_called_once_with(12345678, [1551234, 1591234], canfail=None) + + def test_BaseTaskHandler_wait_some_not_done(self): + """ Tests that the wait function returns the one finished subtask results of + when the taskWait function returns one finished task and one unfinished + """ + temp_path = get_tmp_dir_path('TestTask') + obj = TestTask(12345678, 'some_method', ['random_arg'], None, None, temp_path) + makedirs(temp_path) + obj.session = Mock() + obj.session.host.taskSetWait.return_value = None + obj.session.host.taskWait.return_value = [[1551234], [1591234]] + taskWaitResults = [ + ['1551234', { + 'brootid': 2342345, + 'logs': ['tasks/5678/12345678/root.log', + 'tasks/5678/12345678/state.log', + 'tasks/5678/12345678/build.log'], + 'srpm': 'tasks/5678/12345678/some_package-1.2.3p5-25.src.rpm' + }] + ] + + obj.session.host.taskWaitResults.return_value = taskWaitResults + self.assertEquals(obj.wait([1551234, 1591234]), dict(taskWaitResults)) + obj.session.host.taskSetWait.assert_called_once_with(12345678, [1551234, 1591234]) + obj.session.host.taskWaitResults.assert_called_once_with(12345678, [1551234], canfail=None) + + @patch('signal.pause', return_value=None) + def test_BaseTaskHandler_wait_some_not_done_all_set(self, mock_signal_pause): + """ Tests that the wait function returns the two subtask results since the all kwarg is set to True. + The taskWait function should first return one finished and one unfinished task, then the second time it should + return two finished tasks. + """ + temp_path = get_tmp_dir_path('TestTask') + obj = TestTask(12345678, 'some_method', ['random_arg'], None, None, temp_path) + makedirs(temp_path) + obj.session = Mock() + obj.session.host.taskSetWait.return_value = None + obj.session.host.taskWait.side_effect = [[[1551234], [1591234]], [[1551234, 1591234], []]] + taskWaitResults = [ + ['1551234', { + 'brootid': 2342345, + 'logs': ['tasks/5678/12345678/root.log', + 'tasks/5678/12345678/state.log', + 'tasks/5678/12345678/build.log'], + 'srpm': 'tasks/5678/12345678/some_package-1.2.3p5-25.src.rpm' + }], + + ['1591234', { + 'brootid': 1231234, + 'logs': ['tasks/6789/2345678/root.log', + 'tasks/6789/2345678/state.log', + 'tasks/6789/2345678/build.log'], + 'rpms': ['tasks/6789/2345678/some_other_package-doc-1.2.3p5-25.el7.noarch.rpm'], + 'srpms': ['tasks/6789/2345678/some_other_package-1.2.3p5-25.el7.src.rpm'] + }] + ] + + obj.session.getTaskResult.side_effect + + obj.session.host.taskWaitResults.return_value = taskWaitResults + self.assertEquals(obj.wait([1551234, 1591234], all=True), dict(taskWaitResults)) + obj.session.host.taskSetWait.assert_called_once_with(12345678, [1551234, 1591234]) + obj.session.host.taskWait.assert_has_calls([call(12345678), call(12345678)]) + mock_signal_pause.assert_called_once_with() + obj.session.host.taskWaitResults.assert_called_once_with(12345678, [1551234, 1591234], canfail=None) + + def test_BaseTaskHandler_wait_some_not_done_all_set_failany_set_failed_task(self): + """ Tests that the wait function raises an exception when one of the subtask fails when the failany flag is set + to True. + """ + temp_path = get_tmp_dir_path('TestTask') + obj = TestTask(12345678, 'some_method', ['random_arg'], None, None, temp_path) + makedirs(temp_path) + obj.session = Mock() + obj.session.host.taskSetWait.return_value = None + obj.session.host.taskWait.side_effect = [[[1551234], [1591234]], [[1551234, 1591234], []]] + obj.session.getTaskResult.side_effect = koji.GenericError('Uh oh, we\'ve got a problem here!') + try: + obj.wait([1551234, 1591234], all=True, failany=True) + raise Exception('A GeneralError was not raised.') + except koji.GenericError as e: + self.assertEquals(e.args[0], 'Uh oh, we\'ve got a problem here!') + obj.session.host.taskSetWait.assert_called_once_with(12345678, [1551234, 1591234]) + + @patch('time.time') + @patch('time.sleep') + @patch('signal.pause') + def test_BaseTaskHandler_wait_timeout(self, pause, sleep, time): + """Tests timeout behavior in the wait function""" + temp_path = get_tmp_dir_path('TestTask') + obj = TestTask(95, 'some_method', ['random_arg'], None, None, temp_path) + makedirs(temp_path) + obj.session = MagicMock() + obj.session.host.taskWait.return_value = [[], [99, 100, 101]] + time.side_effect = list(range(0, 4000, 60)) + try: + obj.wait([99, 100, 101], timeout=3600) + raise Exception('A GenericError was not raised.') + except koji.GenericError as e: + self.assertEquals(e.args[0][:24], 'Subtasks timed out after') + obj.session.host.taskSetWait.assert_called_once_with(95, [99, 100, 101]) + obj.session.cancelTaskChildren.assert_called_once_with(95) + obj.session.getTaskResult.assert_not_called() + pause.assert_not_called() + + @patch('time.time') + @patch('time.sleep') + @patch('signal.pause') + def test_BaseTaskHandler_wait_avoid_timeout(self, pause, sleep, time): + """Tests that timeout does not happen if tasks finish in time""" + temp_path = get_tmp_dir_path('TestTask') + obj = TestTask(95, 'some_method', ['random_arg'], None, None, temp_path) + makedirs(temp_path) + obj.session = MagicMock() + time.side_effect = list(range(0, 4000, 20)) + # time ticks every 20s for a little over an "hour" + # code checks time 3x each cycle (twice directly, once via logging) + # so each cycle is a "minute" + # report all unfinished for most of an hour + taskWait_returns = [[[], [99, 100, 101]]] * 50 + # and then report all done + taskWait_returns.append([[99, 100, 101], []]) + obj.session.host.taskWait.side_effect = taskWait_returns + obj.wait([99, 100, 101], timeout=3600) + + obj.session.host.taskSetWait.assert_called_once_with(95, [99, 100, 101]) + obj.session.cancelTaskChildren.assert_not_called() + pause.assert_not_called() + + def test_BaseTaskHandler_getUploadDir(self): + """ Tests that the getUploadDir function returns the appropriate path based on the id of the handler. + """ + temp_path = get_tmp_dir_path('TestTask') + obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) + self.assertEquals(obj.getUploadDir(), 'tasks/123/123') + + # This patch removes the dependence on getUploadDir functioning + @patch('{0}.TestTask.getUploadDir'.format(__name__), return_value='tasks/123/123') + def test_BaseTaskHandler_uploadFile(self, mock_getUploadDir): + """ Tests that the uploadFile function calls the uploadWrapper function on the session member variable + with the correct input + """ + temp_path = get_tmp_dir_path('TestTask') + makedirs(temp_path) + temp_file = path.join(temp_path, 'test.txt') + with open(temp_file, 'w') as temp_file_handler: + temp_file_handler.write('Test') + + obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) + obj.session = Mock() + self.assertEquals(obj.uploadFile(temp_file), None) + obj.session.uploadWrapper.assert_called_once_with(temp_file, 'tasks/123/123', None, volume=None) + + # This patch removes the dependence on getUploadDir functioning + @patch('{0}.TestTask.getUploadDir'.format(__name__), return_value='tasks/123/123') + def test_BaseTaskHandler_uploadFile_no_content(self, mock_getUploadDir): + """ Tests that the uploadFile function calls the uploadWrapper function on the session member variable + without including empty files. + """ + temp_path = get_tmp_dir_path('TestTask') + makedirs(temp_path) + + temp_file = path.join(temp_path, 'test.txt') + temp_file_handler = open(temp_file, 'w') + temp_file_handler.close() + + obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) + obj.session = Mock() + self.assertEquals(obj.uploadFile(temp_file), None) + self.assertEquals(obj.session.uploadWrapper.called, False) + + def test_BaseTaskHandler_uploadTree(self): + """ Tests that the uploadTree function calls the uploadFile function with the correct parameters. + """ + temp_path = get_tmp_dir_path('TestTask') + makedirs(temp_path) + + dummy_dir = path.join(temp_path, 'some_directory') + makedirs(dummy_dir) + + dummy_file = path.join(temp_path, 'test.txt') + with open(dummy_file, 'w') as temp_file_handler: + temp_file_handler.write('Test') + + dummy_file2 = path.join(dummy_dir, 'test2.txt') + with open(dummy_file2, 'w') as temp_file_handler2: + temp_file_handler2.write('Test2') + + obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) + obj.uploadFile = Mock() + obj.uploadFile.return_value = None + self.assertEquals(obj.uploadTree(temp_path), None) + obj.uploadFile.assert_has_calls([call(dummy_file, '', volume=None), call(dummy_file2, 'some_directory', volume=None)]) + + @patch('os.lchown', return_value=None) + def test_BaseTaskHandler_chownTree(self, mock_lchown): + """ Tests that the chownTree functions as expected on dummy files created in a temp directory + """ + temp_path = get_tmp_dir_path('TestTask') + makedirs(temp_path) + + dummy_file = path.join(temp_path, 'test.txt') + dummy_file_handler = open(dummy_file, 'w') + dummy_file_handler.close() + + dummy_file2 = path.join(temp_path, 'test2.txt') + dummy_file_handler2 = open(dummy_file2, 'w') + dummy_file_handler2.close() + + obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) + self.assertEquals(obj.chownTree(temp_path, 2, 0), None) + mock_lchown.assert_has_calls([call(temp_path, 2, 0), call(dummy_file2, 2, 0), call(dummy_file, 2, 0)], any_order=True) + + def test_BaseTaskHandler_localPath_file_exists(self): + """ Tests the localPath function to ensure that when a file exists, it returns that path without + trying to download it. + """ + temp_path = get_tmp_dir_path('TestTask') + makedirs(temp_path) + + local_folder = path.join(temp_path, 'local') + makedirs(local_folder) + + dummy_file = path.join(local_folder, 'test.txt') + dummy_file_handler = open(dummy_file, 'w') + dummy_file_handler.close() + options = Mock() + options.topurl = 'https://www.domain.local' + obj = TestTask(123, 'some_method', ['random_arg'], None, options, temp_path) + self.assertEquals(obj.localPath('test.txt'), dummy_file) + + @patch('six.moves.urllib.request.urlopen', return_value=six.StringIO(six.text_type('Important things\nSome more important things\n'))) + def test_BaseTaskHandler_localPath_no_file(self, mock_urlopen): + """ + """ + temp_path = get_tmp_dir_path('TestTask') + makedirs(temp_path) + + local_folder = path.join(temp_path, 'local') + makedirs(local_folder) + + target_file_path = path.join(local_folder, 'test.txt') + + options = Mock() + options.topurl = 'https://www.domain.local' + obj = TestTask(123, 'some_method', ['random_arg'], None, options, temp_path) + + self.assertEquals(obj.localPath('test.txt'), target_file_path) + mock_urlopen.assert_called_once_with('https://www.domain.local/test.txt') + + def test_BaseTaskHandler_localPath_no_topurl(self): + """ Tests that the localPath function returns a path when options.topurl is not defined. + """ + temp_path = get_tmp_dir_path('TestTask') + makedirs(temp_path) + + options = Mock() + options.topurl = None + options.topdir = get_temp_dir_root() + obj = TestTask(123, 'some_method', ['random_arg'], None, options, temp_path) + + self.assertEquals(obj.localPath('test.txt'), path.join(get_temp_dir_root(), 'test.txt')) + + def test_BaseTaskHandler_find_arch(self): + """ Tests that the find_arch function returns the input for arch when the input is not "noarch". + """ + temp_path = get_tmp_dir_path('TestTask') + makedirs(temp_path) + obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) + self.assertEquals(obj.find_arch('x86_64', None, None), 'x86_64') + + def test_BaseTaskHandler_find_arch_noarch_bad_host(self): + """ Tests that the find_arch function raises an exception when the host parameter doesn't contain a + value for the arches key. + """ + temp_path = get_tmp_dir_path('TestTask') + makedirs(temp_path) + host = {'arches': None, 'name': 'test.domain.local'} + obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) + try: + obj.find_arch('noarch', host, None) + raise Exception('The BuildError Exception was not raised') + except koji.BuildError as e: + self.assertEquals(e.args[0], 'No arch list for this host: test.domain.local') + + def test_BaseTaskHandler_find_arch_noarch_bad_tag(self): + """ Tests that the find_arch function raises an exception when the tag parameter doesn't contain a + value for the arches key. + """ + temp_path = get_tmp_dir_path('TestTask') + makedirs(temp_path) + host = {'arches': 'x86_64', 'name': 'test.domain.local'} + tag = {'arches': None, 'name': 'some_package-1.2-build'} + obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) + try: + obj.find_arch('noarch', host, tag) + raise Exception('The BuildError Exception was not raised') + except koji.BuildError as e: + self.assertEquals(e.args[0], 'No arch list for tag: some_package-1.2-build') + + def test_BaseTaskHandler_find_arch_noarch(self): + """ Tests that the find_arch function finds a match of x86_64 when the host only supports x86_64 + and the tag supports x86_64 and aarch64. + """ + temp_path = get_tmp_dir_path('TestTask') + makedirs(temp_path) + host = {'arches': 'x86_64', 'name': 'test.domain.local'} + tag = {'arches': 'x86_64 aarch64', 'name': 'some_package-1.2-build'} + obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) + self.assertEquals(obj.find_arch('noarch', host, tag), 'x86_64') + + def test_BaseTaskHandler_find_arch__noarch_no_match(self): + """ Tests that the find_arch function raises an exception when there isn't a common arch supported between + the host and the tag. + """ + temp_path = get_tmp_dir_path('TestTask') + makedirs(temp_path) + host = {'arches': 'i386', 'name': 'test.domain.local'} + tag = {'arches': 'x86_64 aarch64', 'name': 'some_package-1.2-build'} + obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) + try: + obj.find_arch('noarch', host, tag) + raise Exception('The BuildError Exception was not raised') + except koji.BuildError as e: + self.assertEquals(e.args[0], ('host test.domain.local (i386) does not support ' + 'any arches of tag some_package-1.2-build (aarch64, x86_64)')) + + def test_getRepo_tied_to_session(self): + """ Tests that the getRepo function calls session.getRepo(), and returns the result when successful + """ + temp_path = get_tmp_dir_path('TestTask') + makedirs(temp_path) + + repo_dict = { + 'create_event': 13635166, + 'create_ts': 1469039671.5743899, + 'creation_time': '2016-07-20 18:34:31.574386', + 'id': 1630631, + 'state': 1 + } + + obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) + obj.session = Mock() + obj.session.getRepo.return_value = repo_dict + + self.assertEquals(obj.getRepo(8472), repo_dict) + + @patch('{0}.TestTask.wait'.format(__name__)) + def test_getRepo_not_tied_to_session(self, mock_wait): + """ Tests that the getRepo function waits until the results are available for session.getRepo, when it is + not available at the start of the function call. + """ + temp_path = get_tmp_dir_path('TestTask') + makedirs(temp_path) + + repo_dict = { + 'create_event': 13413120, + 'create_ts': 1466140834.9119599, + 'creation_time': '2016-06-17 05:20:34.911962', + 'id': 1592850, + 'state': 1 + } + + obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) + obj.session = Mock() + obj.session.getRepo.return_value = None + obj.session.getTag.return_value = { + 'arches': 'i386 ia64 x86_64 ppc s390 s390x ppc64', + 'extra': {}, + 'id': 851, + 'locked': True, + 'maven_include_all': False, + 'maven_support': False, + 'name': 'dist-3.0E-build', + 'perm': None, + 'perm_id': None + } + obj.session.getBuildTargets.return_value = [{ + 'build_tag': 3093, + 'build_tag_name': 'dist-6E-dsrv-9-build', + 'dest_tag': 3092, + 'dest_tag_name': 'dist-6E-dsrv-9-qu-candidate', + 'id': 851, + 'name': 'dist-6E-dsrv-9-qu-candidate' + }] + + obj.session.host.subtask.return_value = 123 + mock_wait.return_value = {123: repo_dict} + + self.assertEquals(obj.getRepo(851), repo_dict) + obj.session.getRepo.assert_called_once_with(851) + obj.session.getTag.assert_called_once_with(851, strict=True) + + @patch('{0}.TestTask.wait'.format(__name__)) + def test_getRepo_not_tied_to_session_no_build_targets(self, mock_wait): + """ Tests that the getRepo function raises an exception when session.getBuildTargets returns an empty list + """ + temp_path = get_tmp_dir_path('TestTask') + makedirs(temp_path) + + obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) + obj.session = Mock() + obj.session.getRepo.return_value = None + obj.session.getTag.return_value = { + 'arches': 'i686 x86_64 ppc ppc64 ppc64le s390 s390x aarch64', + 'extra': {}, + 'id': 8472, + 'locked': False, + 'maven_include_all': False, + 'maven_support': False, + 'name': 'rhel-7.3-build', + 'perm': 'admin', + 'perm_id': 1 + } + obj.session.getBuildTargets.return_value = [] + + try: + obj.getRepo(8472) + raise Exception('The BuildError Exception was not raised') + except koji.BuildError as e: + obj.session.getRepo.assert_called_once_with(8472) + self.assertEquals(e.args[0], 'no repo (and no target) for tag rhel-7.3-build') + + def test_FakeTask_handler(self): + """ Tests that the FakeTest handler can be instantiated and returns 42 when run + """ + obj = FakeTask(123, 'someMethod', ['random_arg'], None, None, (get_tmp_dir_path('FakeTask'))) + self.assertEquals(obj.run(), 42) + + @patch('time.sleep') + def test_SleepTask_handler(self, mock_sleep): + """ Tests that the SleepTask handler can be instantiated and runs appropriately based on the input + """ + obj = SleepTask(123, 'sleep', [5], None, None, (get_tmp_dir_path('SleepTask'))) + obj.run() + mock_sleep.assert_called_once_with(5) + + @patch('os.spawnvp') + def test_ForkTask_handler(self, mock_spawnvp): + """ Tests that the ForkTask handler can be instantiated and runs appropriately based on the input + """ + obj = ForkTask(123, 'fork', [1, 20], None, None, (get_tmp_dir_path('ForkTask'))) + obj.run() + mock_spawnvp.assert_called_once_with(1, 'sleep', ['sleep', '20']) + + @patch('signal.pause', return_value=None) + @patch('time.sleep') + def test_WaitTestTask_handler(self, mock_sleep, mock_signal_pause): + """ Tests that the WaitTestTask handler can be instantiated and runs appropriately based on the input + Specifically, that forking works and canfail behaves correctly. + """ + self.mock_subtask_id = 1 + def mock_subtask(method, arglist, id, **opts): + self.assertEqual(method, 'sleep') + task_id = self.mock_subtask_id + self.mock_subtask_id += 1 + obj = SleepTask(task_id, 'sleep', arglist, None, None, (get_tmp_dir_path('SleepTask'))) + obj.run() + return task_id + + mock_taskWait = [ + [[], [1, 2, 3, 4]], + [[3, 4], [1, 2]], + [[1, 2, 3, 4], []], + ] + def mock_getTaskResult(task_id): + if task_id == 4: + raise koji.GenericError() + + + obj = WaitTestTask(123, 'waittest', [3], None, None, (get_tmp_dir_path('WaitTestTask'))) + obj.session = Mock() + obj.session.host.subtask.side_effect = mock_subtask + obj.session.getTaskResult.side_effect = mock_getTaskResult + obj.session.host.taskWait.side_effect = mock_taskWait + obj.session.host.taskWaitResults.return_value = [ ['1', {}], ['2', {}], ['3', {}], ['4', {}], ] + obj.run() + #self.assertEqual(mock_sleep.call_count, 4) + obj.session.host.taskSetWait.assert_called_once() + obj.session.host.taskWait.assert_has_calls([call(123), call(123), call(123)]) + # getTaskResult should be called in 2nd round only for task 3, as 4 + # will be skipped as 'canfail' + obj.session.getTaskResult.assert_has_calls([call(3)]) + +class TestSafeRmtree(unittest.TestCase): + @patch('os.path.exists', return_value=True) + @patch('os.path.isfile', return_value=True) + @patch('os.path.islink', return_value=False) + @patch('os.remove') + @patch('koji.util.rmtree') + def test_safe_rmtree_file(self, rmtree, remove, islink, isfile, exists): + """ Tests that the koji.util.rmtree function returns nothing when the path parameter is a file. + """ + path = '/mnt/folder/some_file' + self.assertEquals(safe_rmtree(path, False, True), 0) + isfile.assert_called_once_with(path) + islink.assert_not_called() + exists.assert_not_called() + remove.assert_called_once_with(path) + rmtree.assert_not_called() + + @patch('os.path.exists', return_value=True) + @patch('os.path.isfile', return_value=False) + @patch('os.path.islink', return_value=True) + @patch('os.remove') + @patch('koji.util.rmtree') + def test_rmtree_link(self, rmtree, remove, islink, isfile, exists): + """ Tests that the koji.util.rmtree function returns nothing when the path parameter is a link. + """ + path = '/mnt/folder/some_link' + self.assertEquals(safe_rmtree(path, False, True), 0) + isfile.assert_called_once_with(path) + islink.assert_called_once_with(path) + exists.assert_not_called() + remove.assert_called_once_with(path) + rmtree.assert_not_called() + + + @patch('os.path.exists', return_value=False) + @patch('os.path.isfile', return_value=False) + @patch('os.path.islink', return_value=False) + @patch('os.remove') + @patch('koji.util.rmtree') + def test_rmtree_does_not_exist(self, rmtree, remove, islink, isfile, exists): + """ Tests that the koji.util.rmtree function returns nothing if the path does not exist. + """ + path = '/mnt/folder/some_file' + self.assertEquals(safe_rmtree(path, False, True), 0) + isfile.assert_called_once_with(path) + islink.assert_called_once_with(path) + exists.assert_called_once_with(path) + remove.assert_not_called() + rmtree.assert_not_called() + + @patch('os.path.exists', return_value=True) + @patch('os.path.isfile', return_value=False) + @patch('os.path.islink', return_value=False) + @patch('os.remove') + @patch('koji.util.rmtree') + def test_rmtree_directory(self, rmtree, remove, islink, isfile, exists): + """ Tests that the koji.util.rmtree function returns nothing when the path is a directory. + """ + path = '/mnt/folder' + self.assertEquals(safe_rmtree(path, False, True), 0) + isfile.assert_called_once_with(path) + islink.assert_called_once_with(path) + exists.assert_called_once_with(path) + remove.assert_not_called() + rmtree.assert_called_once_with(path) + + @patch('os.path.exists', return_value=True) + @patch('os.path.isfile', return_value=False) + @patch('os.path.islink', return_value=False) + @patch('os.remove') + @patch('koji.util.rmtree') + def test_rmtree_directory_scrub_file_failure(self, rmtree, remove, islink, isfile, exists): + """ Tests that the koji.util.rmtree function returns a GeneralException when the path parameter is a directory + and the scrub of the files in the directory fails. + """ + rmtree.side_effect = koji.GenericError('xyz') + path = '/mnt/folder' + try: + safe_rmtree(path, False, 1) + raise Exception('A GenericError was not raised during the test') + except koji.GenericError as e: + self.assertEquals(e.args[0], 'xyz') + isfile.assert_called_once_with(path) + islink.assert_called_once_with(path) + exists.assert_called_once_with(path) + remove.assert_not_called() + rmtree.assert_called_once_with(path) + + @patch('os.path.exists', return_value=True) + @patch('os.path.isfile', return_value=False) + @patch('os.path.islink', return_value=False) + @patch('os.remove') + @patch('koji.util.rmtree') + def test_safe_rmtree_directory_scrub_directory_failure(self, rmtree, remove, islink, isfile, exists): + """ Tests that the koji.util.rmtree function returns a GeneralException when the path parameter is a directory + and the scrub of the directories in the directory fails. + """ + rmtree.side_effect = OSError('xyz') + path = '/mnt/folder' + try: + safe_rmtree(path, False, True) + raise Exception('An OSError was not raised during the test') + except OSError as e: + self.assertEquals(e.args[0], 'xyz') + + isfile.assert_called_once_with(path) + islink.assert_called_once_with(path) + exists.assert_called_once_with(path) + remove.assert_not_called() + rmtree.assert_called_once_with(path) diff --git a/tests/test_lib_py2only/test_tasks.py b/tests/test_lib_py2only/test_tasks.py deleted file mode 100644 index e196453..0000000 --- a/tests/test_lib_py2only/test_tasks.py +++ /dev/null @@ -1,817 +0,0 @@ -from __future__ import absolute_import -import random -import shutil -import six -from six.moves import range -try: - import unittest2 as unittest -except ImportError: - import unittest - -from os import path, makedirs -from tempfile import gettempdir -from mock import patch, MagicMock, Mock, call - -import koji -from koji.tasks import BaseTaskHandler, FakeTask, ForkTask, SleepTask, \ - WaitTestTask, scan_mounts, umount_all, \ - safe_rmtree - - -def get_fake_mounts_file(): - """ Returns contents of /prc/mounts in a file-like object - """ - return six.StringIO(six.text_type(( - 'sysfs /sys sysfs rw,seclabel,nosuid,nodev,noexec,relatime 0 0\n' - 'proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0\n' - 'devtmpfs /dev devtmpfs rw,seclabel,nosuid,size=238836k,nr_inodes=59709,mode=755 0 0\n' - 'securityfs /sys/kernel/security securityfs rw,nosuid,nodev,noexec,relatime 0 0\n' - 'tmpfs /dev/shm\\040(deleted) tmpfs rw,seclabel,nosuid,nodev 0 0\n' - 'devpts /dev/pts devpts rw,seclabel,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0\n' - 'tmpfs /run tmpfs rw,seclabel,nosuid,nodev,mode=755 0 0\n' - 'tmpfs /sys/fs/cgroup tmpfs ro,seclabel,nosuid,nodev,noexec,mode=755 0 0\n' - 'pstore /sys/fs/pstore pstore rw,seclabel,nosuid,nodev,noexec,relatime 0 0\n' - 'cgroup /sys/fs/cgroup/devices cgroup rw,nosuid,nodev,noexec,relatime,devices 0 0\n' - 'cgroup /sys/fs/cgroup/perf_event cgroup rw,nosuid,nodev,noexec,relatime,perf_event 0 0\n' - 'cgroup /sys/fs/cgroup/net_cls,net_prio cgroup rw,nosuid,nodev,noexec,relatime,net_cls,net_prio 0 0\n' - 'cgroup /sys/fs/cgroup/cpu,cpuacct cgroup rw,nosuid,nodev,noexec,relatime,cpu,cpuacct 0 0\n' - 'cgroup /sys/fs/cgroup/blkio cgroup rw,nosuid,nodev,noexec,relatime,blkio 0 0\n' - 'cgroup /sys/fs/cgroup/cpuset cgroup rw,nosuid,nodev,noexec,relatime,cpuset 0 0\n' - 'cgroup /sys/fs/cgroup/freezer cgroup rw,nosuid,nodev,noexec,relatime,freezer 0 0\n' - 'cgroup /sys/fs/cgroup/memory cgroup rw,nosuid,nodev,noexec,relatime,memory 0 0\n' - 'cgroup /sys/fs/cgroup/hugetlb cgroup rw,nosuid,nodev,noexec,relatime,hugetlb 0 0\n' - 'configfs /sys/kernel/config configfs rw,relatime 0 0\n' - 'hugetlbfs /dev/hugepages hugetlbfs rw,seclabel,relatime 0 0\n' - 'mqueue /dev/mqueue mqueue rw,seclabel,relatime 0 0\n' - ))) - - -def get_temp_dir_root(): - return path.join(gettempdir(), 'koji_tests') - - -def get_tmp_dir_path(folder_starts_with): - return path.join(get_temp_dir_root(), ('{0}{1}'.format(folder_starts_with, random.randint(1, 999999999999)))) - - -class TestTask(BaseTaskHandler): - Methods = ['some_method'] - _taskWeight = 5.2 - - def handler(self, *args): - return 42 - - -class TestTaskNoWeight(BaseTaskHandler): - Methods = ['some_method'] - - def handler(self, *args): - return 42 - - -class BadTask(BaseTaskHandler): - Methods = ['some_method'] - - -class TasksTestCase(unittest.TestCase): - - def tearDown(self): - temp_dir_root = get_temp_dir_root() - - if path.isdir(temp_dir_root): - shutil.rmtree(get_temp_dir_root()) - - def test_scan_mounts_results(self): - """ Tests the scan_mounts function with a mocked /proc/mounts file. A list containing mount points - starting with /dev are expected to be returned from the function based on the function input of /dev. - """ - fake_mounts_file_contents = get_fake_mounts_file() - - with patch('koji.tasks.open', return_value=fake_mounts_file_contents, create=True): - self.assertIn(scan_mounts('/dev'), [['/dev/shm', '/dev/pts', '/dev/mqueue', '/dev/hugepages', '/dev']]) - - def test_scan_mounts_no_results(self): - """ Tests the scan_mounts function with a mocked /proc/mounts file. An argument of /nonexistent/path - to the function should return an empty list. - """ - fake_mounts_file_contents = get_fake_mounts_file() - - with patch('koji.tasks.open', return_value=fake_mounts_file_contents, create=True): - self.assertEquals(scan_mounts('/nonexistent/path'), []) - - # Patching the scan_mounts function instead of the built-in open function because this is only testing umount_all - @patch('koji.tasks.scan_mounts', side_effect=[['/dev/shm', '/dev/pts', '/dev/mqueue'], []]) - @patch('os.spawnvp', return_value=0) - def test_umount_all(self, mocked_spawnvp, mocked_scan_mounts): - """ Tests that umount_all returns nothing when successful. - """ - self.assertEquals(umount_all('/test'), None) - - # Patching the scan_mounts function instead of the built-in open function because this is only testing umount_all - @patch('koji.tasks.scan_mounts', return_value=['/dev/shm', '/dev/pts', '/dev/mqueue']) - @patch('os.spawnvp', return_value=1) - def test_umount_all_failure(self, mocked_spawnvp, mocked_scan_mounts): - """ Tests that umount_all raises an exception when a mount point can't be unmounted. - """ - try: - umount_all('/dev') - raise Exception('A GenericError was not raised during the test') - except koji.GenericError as e: - self.assertEquals(e.args[0], - 'umount failed (exit code 1) for /dev/shm') - - # Patching the scan_mounts function instead of the built-in open function because this is only testing umount_all - @patch('koji.tasks.scan_mounts', side_effect=[['/dev/shm', '/dev/pts', '/dev/mqueue'], ['/dev/shm', '/dev/mqueue']]) - @patch('os.spawnvp', return_value=0) - def test_umount_all_unexpected_failure(self, mocked_spawnvp, mocked_scan_mounts): - """ Tests that umount_all will fail if the command to unmount the mount points was successful - but a second run of scan_mounts still shows some of the unmount mount points still mounted. - """ - try: - umount_all('/dev') - raise Exception('A GenericError was not raised during the test') - except koji.GenericError as e: - self.assertEquals(e.args[0], 'Unmounting incomplete: [\'/dev/shm\', \'/dev/mqueue\']') - - def test_BaseTaskHandler_handler_not_set(self): - """ Tests that an exception is thrown when the handler function is not overwritten by the child class. - """ - obj = BadTask(123, 'some_method', ['random_arg'], None, None, (get_tmp_dir_path('BadTask'))) - try: - obj.handler() - raise Exception('The NotImplementedError exception was not raised') - except NotImplementedError as e: - self.assertEquals(e.__class__.__name__, 'NotImplementedError') - - def test_BaseTaskHandler_weight_default(self): - """ Tests that the weight function returns 1.0 when _taskWeight is not set in the child class' definition. - """ - obj = TestTaskNoWeight(123, 'some_method', ['random_arg'], None, None, (get_tmp_dir_path('TestTaskNoWeight'))) - self.assertEquals(obj.weight(), 1.0) - - def test_BaseTaskHandler_weight_set(self): - """ Tests that the weight function returns the value of _taskWeight when it is set in the - child class' definition. - """ - obj = TestTask(123, 'some_method', ['random_arg'], None, None, (get_tmp_dir_path('TestTask'))) - self.assertEquals(obj.weight(), 5.2) - - def test_BaseTaskHandler_createWorkdir_workdir_not_defined(self): - """ Tests that the createWorkdir function does nothing when the workdir member variable is set to None. - """ - temp_path = get_tmp_dir_path('TestTask') - obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) - obj.workdir = None - obj.createWorkdir() - self.assertEquals(path.isdir(temp_path), False) - - # This patch removes the dependence on removeWorkdir functioning - @patch('{0}.TestTask.removeWorkdir'.format(__name__)) - def test_BaseTaskHandler_createWorkdir(self, mock_removeWorkDir): - """ Tests that the createWorkdir function creates a folder based on the path given to the - workdir member variable. - """ - temp_path = get_tmp_dir_path('TestTask') - obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) - obj.createWorkdir() - self.assertEquals(path.isdir(temp_path), True) - shutil.rmtree(get_temp_dir_root()) - - def test_BaseTaskHandler_removeWorkdir(self): - """ Tests that the removeWOrkdir function deletes a folder based on the path given to the - workdir member variable. - """ - temp_path = get_tmp_dir_path('TestTask') - obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) - makedirs(temp_path) - self.assertEquals(path.isdir(temp_path), True) - obj.removeWorkdir() - self.assertEquals(path.isdir(temp_path), False) - - def test_BaseTaskHandler_wait_all_done(self): - """ Tests that the wait function returns the subtask results of when the taskWait function returns only - two finished tasks - """ - temp_path = get_tmp_dir_path('TestTask') - obj = TestTask(12345678, 'some_method', ['random_arg'], None, None, temp_path) - makedirs(temp_path) - obj.session = Mock() - obj.session.host.taskSetWait.return_value = None - obj.session.host.taskWait.return_value = [[1551234, 1591234], []] - taskWaitResults = [ - ['1551234', { - 'brootid': 2342345, - 'logs': ['tasks/5678/12345678/root.log', - 'tasks/5678/12345678/state.log', - 'tasks/5678/12345678/build.log'], - 'srpm': 'tasks/5678/12345678/some_package-1.2.3p5-25.src.rpm' - }], - - ['1591234', { - 'brootid': 1231234, - 'logs': ['tasks/6789/2345678/root.log', - 'tasks/6789/2345678/state.log', - 'tasks/6789/2345678/build.log'], - 'rpms': ['tasks/6789/2345678/some_other_package-doc-1.2.3p5-25.el7.noarch.rpm'], - 'srpms': ['tasks/6789/2345678/some_other_package-1.2.3p5-25.el7.src.rpm'] - }] - ] - - obj.session.host.taskWaitResults.return_value = taskWaitResults - self.assertEquals(obj.wait([1551234, 1591234]), dict(taskWaitResults)) - obj.session.host.taskSetWait.assert_called_once_with(12345678, [1551234, 1591234]) - obj.session.host.taskWaitResults.assert_called_once_with(12345678, [1551234, 1591234], canfail=None) - - def test_BaseTaskHandler_wait_some_not_done(self): - """ Tests that the wait function returns the one finished subtask results of - when the taskWait function returns one finished task and one unfinished - """ - temp_path = get_tmp_dir_path('TestTask') - obj = TestTask(12345678, 'some_method', ['random_arg'], None, None, temp_path) - makedirs(temp_path) - obj.session = Mock() - obj.session.host.taskSetWait.return_value = None - obj.session.host.taskWait.return_value = [[1551234], [1591234]] - taskWaitResults = [ - ['1551234', { - 'brootid': 2342345, - 'logs': ['tasks/5678/12345678/root.log', - 'tasks/5678/12345678/state.log', - 'tasks/5678/12345678/build.log'], - 'srpm': 'tasks/5678/12345678/some_package-1.2.3p5-25.src.rpm' - }] - ] - - obj.session.host.taskWaitResults.return_value = taskWaitResults - self.assertEquals(obj.wait([1551234, 1591234]), dict(taskWaitResults)) - obj.session.host.taskSetWait.assert_called_once_with(12345678, [1551234, 1591234]) - obj.session.host.taskWaitResults.assert_called_once_with(12345678, [1551234], canfail=None) - - @patch('signal.pause', return_value=None) - def test_BaseTaskHandler_wait_some_not_done_all_set(self, mock_signal_pause): - """ Tests that the wait function returns the two subtask results since the all kwarg is set to True. - The taskWait function should first return one finished and one unfinished task, then the second time it should - return two finished tasks. - """ - temp_path = get_tmp_dir_path('TestTask') - obj = TestTask(12345678, 'some_method', ['random_arg'], None, None, temp_path) - makedirs(temp_path) - obj.session = Mock() - obj.session.host.taskSetWait.return_value = None - obj.session.host.taskWait.side_effect = [[[1551234], [1591234]], [[1551234, 1591234], []]] - taskWaitResults = [ - ['1551234', { - 'brootid': 2342345, - 'logs': ['tasks/5678/12345678/root.log', - 'tasks/5678/12345678/state.log', - 'tasks/5678/12345678/build.log'], - 'srpm': 'tasks/5678/12345678/some_package-1.2.3p5-25.src.rpm' - }], - - ['1591234', { - 'brootid': 1231234, - 'logs': ['tasks/6789/2345678/root.log', - 'tasks/6789/2345678/state.log', - 'tasks/6789/2345678/build.log'], - 'rpms': ['tasks/6789/2345678/some_other_package-doc-1.2.3p5-25.el7.noarch.rpm'], - 'srpms': ['tasks/6789/2345678/some_other_package-1.2.3p5-25.el7.src.rpm'] - }] - ] - - obj.session.getTaskResult.side_effect - - obj.session.host.taskWaitResults.return_value = taskWaitResults - self.assertEquals(obj.wait([1551234, 1591234], all=True), dict(taskWaitResults)) - obj.session.host.taskSetWait.assert_called_once_with(12345678, [1551234, 1591234]) - obj.session.host.taskWait.assert_has_calls([call(12345678), call(12345678)]) - mock_signal_pause.assert_called_once_with() - obj.session.host.taskWaitResults.assert_called_once_with(12345678, [1551234, 1591234], canfail=None) - - def test_BaseTaskHandler_wait_some_not_done_all_set_failany_set_failed_task(self): - """ Tests that the wait function raises an exception when one of the subtask fails when the failany flag is set - to True. - """ - temp_path = get_tmp_dir_path('TestTask') - obj = TestTask(12345678, 'some_method', ['random_arg'], None, None, temp_path) - makedirs(temp_path) - obj.session = Mock() - obj.session.host.taskSetWait.return_value = None - obj.session.host.taskWait.side_effect = [[[1551234], [1591234]], [[1551234, 1591234], []]] - obj.session.getTaskResult.side_effect = koji.GenericError('Uh oh, we\'ve got a problem here!') - try: - obj.wait([1551234, 1591234], all=True, failany=True) - raise Exception('A GeneralError was not raised.') - except koji.GenericError as e: - self.assertEquals(e.args[0], 'Uh oh, we\'ve got a problem here!') - obj.session.host.taskSetWait.assert_called_once_with(12345678, [1551234, 1591234]) - - @patch('time.time') - @patch('time.sleep') - @patch('signal.pause') - def test_BaseTaskHandler_wait_timeout(self, pause, sleep, time): - """Tests timeout behavior in the wait function""" - temp_path = get_tmp_dir_path('TestTask') - obj = TestTask(95, 'some_method', ['random_arg'], None, None, temp_path) - makedirs(temp_path) - obj.session = MagicMock() - obj.session.host.taskWait.return_value = [[], [99, 100, 101]] - time.side_effect = list(range(0, 4000, 60)) - try: - obj.wait([99, 100, 101], timeout=3600) - raise Exception('A GenericError was not raised.') - except koji.GenericError as e: - self.assertEquals(e.args[0][:24], 'Subtasks timed out after') - obj.session.host.taskSetWait.assert_called_once_with(95, [99, 100, 101]) - obj.session.cancelTaskChildren.assert_called_once_with(95) - obj.session.getTaskResult.assert_not_called() - pause.assert_not_called() - - @patch('time.time') - @patch('time.sleep') - @patch('signal.pause') - def test_BaseTaskHandler_wait_avoid_timeout(self, pause, sleep, time): - """Tests that timeout does not happen if tasks finish in time""" - temp_path = get_tmp_dir_path('TestTask') - obj = TestTask(95, 'some_method', ['random_arg'], None, None, temp_path) - makedirs(temp_path) - obj.session = MagicMock() - time.side_effect = list(range(0, 4000, 20)) - # time ticks every 20s for a little over an "hour" - # code checks time 3x each cycle (twice directly, once via logging) - # so each cycle is a "minute" - # report all unfinished for most of an hour - taskWait_returns = [[[], [99, 100, 101]]] * 50 - # and then report all done - taskWait_returns.append([[99, 100, 101], []]) - obj.session.host.taskWait.side_effect = taskWait_returns - obj.wait([99, 100, 101], timeout=3600) - - obj.session.host.taskSetWait.assert_called_once_with(95, [99, 100, 101]) - obj.session.cancelTaskChildren.assert_not_called() - pause.assert_not_called() - - def test_BaseTaskHandler_getUploadDir(self): - """ Tests that the getUploadDir function returns the appropriate path based on the id of the handler. - """ - temp_path = get_tmp_dir_path('TestTask') - obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) - self.assertEquals(obj.getUploadDir(), 'tasks/123/123') - - # This patch removes the dependence on getUploadDir functioning - @patch('{0}.TestTask.getUploadDir'.format(__name__), return_value='tasks/123/123') - def test_BaseTaskHandler_uploadFile(self, mock_getUploadDir): - """ Tests that the uploadFile function calls the uploadWrapper function on the session member variable - with the correct input - """ - temp_path = get_tmp_dir_path('TestTask') - makedirs(temp_path) - temp_file = path.join(temp_path, 'test.txt') - with open(temp_file, 'w') as temp_file_handler: - temp_file_handler.write('Test') - - obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) - obj.session = Mock() - self.assertEquals(obj.uploadFile(temp_file), None) - obj.session.uploadWrapper.assert_called_once_with(temp_file, 'tasks/123/123', None, volume=None) - - # This patch removes the dependence on getUploadDir functioning - @patch('{0}.TestTask.getUploadDir'.format(__name__), return_value='tasks/123/123') - def test_BaseTaskHandler_uploadFile_no_content(self, mock_getUploadDir): - """ Tests that the uploadFile function calls the uploadWrapper function on the session member variable - without including empty files. - """ - temp_path = get_tmp_dir_path('TestTask') - makedirs(temp_path) - - temp_file = path.join(temp_path, 'test.txt') - temp_file_handler = open(temp_file, 'w') - temp_file_handler.close() - - obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) - obj.session = Mock() - self.assertEquals(obj.uploadFile(temp_file), None) - self.assertEquals(obj.session.uploadWrapper.called, False) - - def test_BaseTaskHandler_uploadTree(self): - """ Tests that the uploadTree function calls the uploadFile function with the correct parameters. - """ - temp_path = get_tmp_dir_path('TestTask') - makedirs(temp_path) - - dummy_dir = path.join(temp_path, 'some_directory') - makedirs(dummy_dir) - - dummy_file = path.join(temp_path, 'test.txt') - with open(dummy_file, 'w') as temp_file_handler: - temp_file_handler.write('Test') - - dummy_file2 = path.join(dummy_dir, 'test2.txt') - with open(dummy_file2, 'w') as temp_file_handler2: - temp_file_handler2.write('Test2') - - obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) - obj.uploadFile = Mock() - obj.uploadFile.return_value = None - self.assertEquals(obj.uploadTree(temp_path), None) - obj.uploadFile.assert_has_calls([call(dummy_file, '', volume=None), call(dummy_file2, 'some_directory', volume=None)]) - - @patch('os.lchown', return_value=None) - def test_BaseTaskHandler_chownTree(self, mock_lchown): - """ Tests that the chownTree functions as expected on dummy files created in a temp directory - """ - temp_path = get_tmp_dir_path('TestTask') - makedirs(temp_path) - - dummy_file = path.join(temp_path, 'test.txt') - dummy_file_handler = open(dummy_file, 'w') - dummy_file_handler.close() - - dummy_file2 = path.join(temp_path, 'test2.txt') - dummy_file_handler2 = open(dummy_file2, 'w') - dummy_file_handler2.close() - - obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) - self.assertEquals(obj.chownTree(temp_path, 2, 0), None) - mock_lchown.assert_has_calls([call(temp_path, 2, 0), call(dummy_file2, 2, 0), call(dummy_file, 2, 0)], any_order=True) - - def test_BaseTaskHandler_localPath_file_exists(self): - """ Tests the localPath function to ensure that when a file exists, it returns that path without - trying to download it. - """ - temp_path = get_tmp_dir_path('TestTask') - makedirs(temp_path) - - local_folder = path.join(temp_path, 'local') - makedirs(local_folder) - - dummy_file = path.join(local_folder, 'test.txt') - dummy_file_handler = open(dummy_file, 'w') - dummy_file_handler.close() - options = Mock() - options.topurl = 'https://www.domain.local' - obj = TestTask(123, 'some_method', ['random_arg'], None, options, temp_path) - self.assertEquals(obj.localPath('test.txt'), dummy_file) - - @patch('six.moves.urllib.request.urlopen', return_value=six.StringIO(six.text_type('Important things\nSome more important things\n'))) - def test_BaseTaskHandler_localPath_no_file(self, mock_urlopen): - """ - """ - temp_path = get_tmp_dir_path('TestTask') - makedirs(temp_path) - - local_folder = path.join(temp_path, 'local') - makedirs(local_folder) - - target_file_path = path.join(local_folder, 'test.txt') - - options = Mock() - options.topurl = 'https://www.domain.local' - obj = TestTask(123, 'some_method', ['random_arg'], None, options, temp_path) - - self.assertEquals(obj.localPath('test.txt'), target_file_path) - mock_urlopen.assert_called_once_with('https://www.domain.local/test.txt') - - def test_BaseTaskHandler_localPath_no_topurl(self): - """ Tests that the localPath function returns a path when options.topurl is not defined. - """ - temp_path = get_tmp_dir_path('TestTask') - makedirs(temp_path) - - options = Mock() - options.topurl = None - options.topdir = get_temp_dir_root() - obj = TestTask(123, 'some_method', ['random_arg'], None, options, temp_path) - - self.assertEquals(obj.localPath('test.txt'), path.join(get_temp_dir_root(), 'test.txt')) - - def test_BaseTaskHandler_find_arch(self): - """ Tests that the find_arch function returns the input for arch when the input is not "noarch". - """ - temp_path = get_tmp_dir_path('TestTask') - makedirs(temp_path) - obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) - self.assertEquals(obj.find_arch('x86_64', None, None), 'x86_64') - - def test_BaseTaskHandler_find_arch_noarch_bad_host(self): - """ Tests that the find_arch function raises an exception when the host parameter doesn't contain a - value for the arches key. - """ - temp_path = get_tmp_dir_path('TestTask') - makedirs(temp_path) - host = {'arches': None, 'name': 'test.domain.local'} - obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) - try: - obj.find_arch('noarch', host, None) - raise Exception('The BuildError Exception was not raised') - except koji.BuildError as e: - self.assertEquals(e.args[0], 'No arch list for this host: test.domain.local') - - def test_BaseTaskHandler_find_arch_noarch_bad_tag(self): - """ Tests that the find_arch function raises an exception when the tag parameter doesn't contain a - value for the arches key. - """ - temp_path = get_tmp_dir_path('TestTask') - makedirs(temp_path) - host = {'arches': 'x86_64', 'name': 'test.domain.local'} - tag = {'arches': None, 'name': 'some_package-1.2-build'} - obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) - try: - obj.find_arch('noarch', host, tag) - raise Exception('The BuildError Exception was not raised') - except koji.BuildError as e: - self.assertEquals(e.args[0], 'No arch list for tag: some_package-1.2-build') - - def test_BaseTaskHandler_find_arch_noarch(self): - """ Tests that the find_arch function finds a match of x86_64 when the host only supports x86_64 - and the tag supports x86_64 and aarch64. - """ - temp_path = get_tmp_dir_path('TestTask') - makedirs(temp_path) - host = {'arches': 'x86_64', 'name': 'test.domain.local'} - tag = {'arches': 'x86_64 aarch64', 'name': 'some_package-1.2-build'} - obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) - self.assertEquals(obj.find_arch('noarch', host, tag), 'x86_64') - - def test_BaseTaskHandler_find_arch__noarch_no_match(self): - """ Tests that the find_arch function raises an exception when there isn't a common arch supported between - the host and the tag. - """ - temp_path = get_tmp_dir_path('TestTask') - makedirs(temp_path) - host = {'arches': 'i386', 'name': 'test.domain.local'} - tag = {'arches': 'x86_64 aarch64', 'name': 'some_package-1.2-build'} - obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) - try: - obj.find_arch('noarch', host, tag) - raise Exception('The BuildError Exception was not raised') - except koji.BuildError as e: - self.assertEquals(e.args[0], ('host test.domain.local (i386) does not support ' - 'any arches of tag some_package-1.2-build (aarch64, x86_64)')) - - def test_getRepo_tied_to_session(self): - """ Tests that the getRepo function calls session.getRepo(), and returns the result when successful - """ - temp_path = get_tmp_dir_path('TestTask') - makedirs(temp_path) - - repo_dict = { - 'create_event': 13635166, - 'create_ts': 1469039671.5743899, - 'creation_time': '2016-07-20 18:34:31.574386', - 'id': 1630631, - 'state': 1 - } - - obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) - obj.session = Mock() - obj.session.getRepo.return_value = repo_dict - - self.assertEquals(obj.getRepo(8472), repo_dict) - - @patch('{0}.TestTask.wait'.format(__name__)) - def test_getRepo_not_tied_to_session(self, mock_wait): - """ Tests that the getRepo function waits until the results are available for session.getRepo, when it is - not available at the start of the function call. - """ - temp_path = get_tmp_dir_path('TestTask') - makedirs(temp_path) - - repo_dict = { - 'create_event': 13413120, - 'create_ts': 1466140834.9119599, - 'creation_time': '2016-06-17 05:20:34.911962', - 'id': 1592850, - 'state': 1 - } - - obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) - obj.session = Mock() - obj.session.getRepo.return_value = None - obj.session.getTag.return_value = { - 'arches': 'i386 ia64 x86_64 ppc s390 s390x ppc64', - 'extra': {}, - 'id': 851, - 'locked': True, - 'maven_include_all': False, - 'maven_support': False, - 'name': 'dist-3.0E-build', - 'perm': None, - 'perm_id': None - } - obj.session.getBuildTargets.return_value = [{ - 'build_tag': 3093, - 'build_tag_name': 'dist-6E-dsrv-9-build', - 'dest_tag': 3092, - 'dest_tag_name': 'dist-6E-dsrv-9-qu-candidate', - 'id': 851, - 'name': 'dist-6E-dsrv-9-qu-candidate' - }] - - obj.session.host.subtask.return_value = 123 - mock_wait.return_value = {123: repo_dict} - - self.assertEquals(obj.getRepo(851), repo_dict) - obj.session.getRepo.assert_called_once_with(851) - obj.session.getTag.assert_called_once_with(851, strict=True) - - @patch('{0}.TestTask.wait'.format(__name__)) - def test_getRepo_not_tied_to_session_no_build_targets(self, mock_wait): - """ Tests that the getRepo function raises an exception when session.getBuildTargets returns an empty list - """ - temp_path = get_tmp_dir_path('TestTask') - makedirs(temp_path) - - obj = TestTask(123, 'some_method', ['random_arg'], None, None, temp_path) - obj.session = Mock() - obj.session.getRepo.return_value = None - obj.session.getTag.return_value = { - 'arches': 'i686 x86_64 ppc ppc64 ppc64le s390 s390x aarch64', - 'extra': {}, - 'id': 8472, - 'locked': False, - 'maven_include_all': False, - 'maven_support': False, - 'name': 'rhel-7.3-build', - 'perm': 'admin', - 'perm_id': 1 - } - obj.session.getBuildTargets.return_value = [] - - try: - obj.getRepo(8472) - raise Exception('The BuildError Exception was not raised') - except koji.BuildError as e: - obj.session.getRepo.assert_called_once_with(8472) - self.assertEquals(e.args[0], 'no repo (and no target) for tag rhel-7.3-build') - - def test_FakeTask_handler(self): - """ Tests that the FakeTest handler can be instantiated and returns 42 when run - """ - obj = FakeTask(123, 'someMethod', ['random_arg'], None, None, (get_tmp_dir_path('FakeTask'))) - self.assertEquals(obj.run(), 42) - - @patch('time.sleep') - def test_SleepTask_handler(self, mock_sleep): - """ Tests that the SleepTask handler can be instantiated and runs appropriately based on the input - """ - obj = SleepTask(123, 'sleep', [5], None, None, (get_tmp_dir_path('SleepTask'))) - obj.run() - mock_sleep.assert_called_once_with(5) - - @patch('os.spawnvp') - def test_ForkTask_handler(self, mock_spawnvp): - """ Tests that the ForkTask handler can be instantiated and runs appropriately based on the input - """ - obj = ForkTask(123, 'fork', [1, 20], None, None, (get_tmp_dir_path('ForkTask'))) - obj.run() - mock_spawnvp.assert_called_once_with(1, 'sleep', ['sleep', '20']) - - @patch('signal.pause', return_value=None) - @patch('time.sleep') - def test_WaitTestTask_handler(self, mock_sleep, mock_signal_pause): - """ Tests that the WaitTestTask handler can be instantiated and runs appropriately based on the input - Specifically, that forking works and canfail behaves correctly. - """ - self.mock_subtask_id = 1 - def mock_subtask(method, arglist, id, **opts): - self.assertEqual(method, 'sleep') - task_id = self.mock_subtask_id - self.mock_subtask_id += 1 - obj = SleepTask(task_id, 'sleep', arglist, None, None, (get_tmp_dir_path('SleepTask'))) - obj.run() - return task_id - - mock_taskWait = [ - [[], [1, 2, 3, 4]], - [[3, 4], [1, 2]], - [[1, 2, 3, 4], []], - ] - def mock_getTaskResult(task_id): - if task_id == 4: - raise koji.GenericError() - - - obj = WaitTestTask(123, 'waittest', [3], None, None, (get_tmp_dir_path('WaitTestTask'))) - obj.session = Mock() - obj.session.host.subtask.side_effect = mock_subtask - obj.session.getTaskResult.side_effect = mock_getTaskResult - obj.session.host.taskWait.side_effect = mock_taskWait - obj.session.host.taskWaitResults.return_value = [ ['1', {}], ['2', {}], ['3', {}], ['4', {}], ] - obj.run() - #self.assertEqual(mock_sleep.call_count, 4) - obj.session.host.taskSetWait.assert_called_once() - obj.session.host.taskWait.assert_has_calls([call(123), call(123), call(123)]) - # getTaskResult should be called in 2nd round only for task 3, as 4 - # will be skipped as 'canfail' - obj.session.getTaskResult.assert_has_calls([call(3)]) - -class TestSafeRmtree(unittest.TestCase): - @patch('os.path.exists', return_value=True) - @patch('os.path.isfile', return_value=True) - @patch('os.path.islink', return_value=False) - @patch('os.remove') - @patch('koji.util.rmtree') - def test_safe_rmtree_file(self, rmtree, remove, islink, isfile, exists): - """ Tests that the koji.util.rmtree function returns nothing when the path parameter is a file. - """ - path = '/mnt/folder/some_file' - self.assertEquals(safe_rmtree(path, False, True), 0) - isfile.assert_called_once_with(path) - islink.assert_not_called() - exists.assert_not_called() - remove.assert_called_once_with(path) - rmtree.assert_not_called() - - @patch('os.path.exists', return_value=True) - @patch('os.path.isfile', return_value=False) - @patch('os.path.islink', return_value=True) - @patch('os.remove') - @patch('koji.util.rmtree') - def test_rmtree_link(self, rmtree, remove, islink, isfile, exists): - """ Tests that the koji.util.rmtree function returns nothing when the path parameter is a link. - """ - path = '/mnt/folder/some_link' - self.assertEquals(safe_rmtree(path, False, True), 0) - isfile.assert_called_once_with(path) - islink.assert_called_once_with(path) - exists.assert_not_called() - remove.assert_called_once_with(path) - rmtree.assert_not_called() - - - @patch('os.path.exists', return_value=False) - @patch('os.path.isfile', return_value=False) - @patch('os.path.islink', return_value=False) - @patch('os.remove') - @patch('koji.util.rmtree') - def test_rmtree_does_not_exist(self, rmtree, remove, islink, isfile, exists): - """ Tests that the koji.util.rmtree function returns nothing if the path does not exist. - """ - path = '/mnt/folder/some_file' - self.assertEquals(safe_rmtree(path, False, True), 0) - isfile.assert_called_once_with(path) - islink.assert_called_once_with(path) - exists.assert_called_once_with(path) - remove.assert_not_called() - rmtree.assert_not_called() - - @patch('os.path.exists', return_value=True) - @patch('os.path.isfile', return_value=False) - @patch('os.path.islink', return_value=False) - @patch('os.remove') - @patch('koji.util.rmtree') - def test_rmtree_directory(self, rmtree, remove, islink, isfile, exists): - """ Tests that the koji.util.rmtree function returns nothing when the path is a directory. - """ - path = '/mnt/folder' - self.assertEquals(safe_rmtree(path, False, True), 0) - isfile.assert_called_once_with(path) - islink.assert_called_once_with(path) - exists.assert_called_once_with(path) - remove.assert_not_called() - rmtree.assert_called_once_with(path) - - @patch('os.path.exists', return_value=True) - @patch('os.path.isfile', return_value=False) - @patch('os.path.islink', return_value=False) - @patch('os.remove') - @patch('koji.util.rmtree') - def test_rmtree_directory_scrub_file_failure(self, rmtree, remove, islink, isfile, exists): - """ Tests that the koji.util.rmtree function returns a GeneralException when the path parameter is a directory - and the scrub of the files in the directory fails. - """ - rmtree.side_effect = koji.GenericError('xyz') - path = '/mnt/folder' - try: - safe_rmtree(path, False, 1) - raise Exception('A GenericError was not raised during the test') - except koji.GenericError as e: - self.assertEquals(e.args[0], 'xyz') - isfile.assert_called_once_with(path) - islink.assert_called_once_with(path) - exists.assert_called_once_with(path) - remove.assert_not_called() - rmtree.assert_called_once_with(path) - - @patch('os.path.exists', return_value=True) - @patch('os.path.isfile', return_value=False) - @patch('os.path.islink', return_value=False) - @patch('os.remove') - @patch('koji.util.rmtree') - def test_safe_rmtree_directory_scrub_directory_failure(self, rmtree, remove, islink, isfile, exists): - """ Tests that the koji.util.rmtree function returns a GeneralException when the path parameter is a directory - and the scrub of the directories in the directory fails. - """ - rmtree.side_effect = OSError('xyz') - path = '/mnt/folder' - try: - safe_rmtree(path, False, True) - raise Exception('An OSError was not raised during the test') - except OSError as e: - self.assertEquals(e.args[0], 'xyz') - - isfile.assert_called_once_with(path) - islink.assert_called_once_with(path) - exists.assert_called_once_with(path) - remove.assert_not_called() - rmtree.assert_called_once_with(path) From d22b27352d65e3f376aec4bc417302972807758c Mon Sep 17 00:00:00 2001 From: Yuming Zhu Date: Dec 11 2018 09:46:38 +0000 Subject: [PATCH 5/6] using explicit coverage2/3 in Makefile --- diff --git a/Makefile b/Makefile index 2db779b..6b91849 100644 --- a/Makefile +++ b/Makefile @@ -65,21 +65,21 @@ git-clean: @git clean -d -q -x test: - coverage erase - PYTHONPATH=hub/.:plugins/hub/.:plugins/builder/.:plugins/cli/.:cli/.:www/lib coverage run \ + coverage2 erase + PYTHONPATH=hub/.:plugins/hub/.:plugins/builder/.:plugins/cli/.:cli/.:www/lib coverage2 run \ --source . /usr/bin/nosetests - coverage report - coverage html + coverage2 report + coverage2 html @echo Full coverage report in htmlcov/index.html test3: - coverage erase + coverage3 erase PYTHONPATH=hub/.:plugins/hub/.:plugins/builder/.:plugins/cli/.:cli/. coverage3 run \ --rcfile .coveragerc3 --source . \ - /usr/bin/nosetests-3 \ + /usr/bin/nosetests \ tests/test_lib tests/test_cli - coverage report --rcfile .coveragerc3 - coverage html --rcfile .coveragerc3 + coverage3 report --rcfile .coveragerc3 + coverage3 html --rcfile .coveragerc3 @echo Full coverage report at file://${PWD}/htmlcov/index.html test-tarball: diff --git a/tests/test_cli/data/list-commands-admin.txt b/tests/test_cli/data/list-commands-admin.txt index 18290ba..1d5e1c1 100644 --- a/tests/test_cli/data/list-commands-admin.txt +++ b/tests/test_cli/data/list-commands-admin.txt @@ -62,8 +62,8 @@ admin commands: 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 +Try "{progname} --help" for help about global options +Try "{progname} help" to get all available commands +Try "{progname} --help" for help about the options of a particular command +Try "{progname} 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 index 98a901b..a33fab2 100644 --- a/tests/test_cli/data/list-commands.txt +++ b/tests/test_cli/data/list-commands.txt @@ -134,8 +134,8 @@ monitor commands: 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 +Try "{progname} --help" for help about global options +Try "{progname} help" to get all available commands +Try "{progname} --help" for help about the options of a particular command +Try "{progname} 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 index bc982ce..ef9b1f2 100644 --- a/tests/test_cli/test_list_commands.py +++ b/tests/test_cli/test_list_commands.py @@ -7,7 +7,7 @@ try: except ImportError: import unittest -from . import loadcli +from . import loadcli, utils cli = loadcli.cli @@ -31,13 +31,9 @@ class TestListCommands(unittest.TestCase): def test_list_commands(self, stdout): cli.list_commands() actual = stdout.getvalue() - if six.PY2: - actual = actual.replace('nosetests', 'koji') - else: - actual = actual.replace('nosetests-3', 'koji') filename = os.path.dirname(__file__) + '/data/list-commands.txt' with open(filename, 'rb') as f: - expected = f.read().decode('ascii') + expected = f.read().decode('ascii').format(progname=utils.PROGNAME) self.assertMultiLineEqual(actual, expected) @mock.patch('sys.stdout', new_callable=six.StringIO) @@ -47,11 +43,7 @@ class TestListCommands(unittest.TestCase): self.parser.parse_args.return_value = [options, arguments] cli.handle_help(self.options, self.session, self.args) actual = stdout.getvalue() - if six.PY2: - actual = actual.replace('nosetests', 'koji') - else: - actual = actual.replace('nosetests-3', 'koji') filename = os.path.dirname(__file__) + '/data/list-commands-admin.txt' with open(filename, 'rb') as f: - expected = f.read().decode('ascii') + expected = f.read().decode('ascii').format(progname=utils.PROGNAME) self.assertMultiLineEqual(actual, expected) diff --git a/tests/test_cli/utils.py b/tests/test_cli/utils.py index e01639e..cc53c12 100644 --- a/tests/test_cli/utils.py +++ b/tests/test_cli/utils.py @@ -11,6 +11,7 @@ except ImportError: import unittest +PROGNAME = os.path.basename(sys.argv[0]) or 'koji' """ Classes @@ -40,7 +41,7 @@ class _dummy_(object): class CliTestCase(unittest.TestCase): # public attribute - progname = os.path.basename(sys.argv[0]) or 'koji' + progname = PROGNAME error_format = None STDOUT = sys.stdout STDERR = sys.stderr From 13dbaabb1edc58eefbc1519a7d698f4500424ab8 Mon Sep 17 00:00:00 2001 From: Yu Ming Zhu Date: Dec 11 2018 09:47:47 +0000 Subject: [PATCH 6/6] misc changes for build and test --- diff --git a/.coveragerc3 b/.coveragerc3 index 3e60769..b2b47e0 100644 --- a/.coveragerc3 +++ b/.coveragerc3 @@ -7,8 +7,6 @@ omit = tests/* hub/* util/* - koji/daemon.py - koji/tasks.py [report] exclude_lines = diff --git a/koji/Makefile b/koji/Makefile index c138231..1320f05 100644 --- a/koji/Makefile +++ b/koji/Makefile @@ -1,15 +1,8 @@ PYTHON=python PACKAGE = $(shell basename `pwd`) -ifeq ($(PYTHON), python3) - # for python3 we fully support only basic library + CLI - PYFILES = __init__.py util.py plugin.py xmlrpcplus.py - PYSCRIPTS = - SUBDIRS = -else - PYFILES = $(wildcard *.py) - PYSCRIPTS = context.py - SUBDIRS = -endif +PYFILES = $(wildcard *.py) +PYSCRIPTS = +SUBDIRS = PYVER := $(shell $(PYTHON) -c 'import sys; print("%.3s" % (sys.version))') PYSYSDIR := $(shell $(PYTHON) -c 'import sys; print(sys.prefix)') PYLIBDIR = $(PYSYSDIR)/lib/python$(PYVER)