debian-koji/tests/test_lib/test_tasks.py
2025-01-06 10:55:41 +01:00

854 lines
37 KiB
Python

from __future__ import absolute_import
import os
from os import path, makedirs
import tempfile
import unittest
try:
from unittest import mock
from unittest.mock import patch, MagicMock, Mock, call
except ImportError:
import mock
from mock import patch, MagicMock, Mock, call
import requests_mock
import shutil
import six
from six.moves import range
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'
)))
class TaskTest(BaseTaskHandler):
Methods = ['some_method']
_taskWeight = 5.2
def handler(self, *args):
return 42
class TaskNoWeightTest(BaseTaskHandler):
Methods = ['some_method']
def handler(self, *args):
return 42
class BadTask(BaseTaskHandler):
Methods = ['some_method']
class TasksTestCase(unittest.TestCase):
def setUp(self):
self.tempdir = tempfile.mkdtemp()
def tearDown(self):
shutil.rmtree(self.tempdir)
def get_tmp_dir_path(self, name):
return os.path.join(self.tempdir, name)
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.assertEqual(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.assertEqual(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.assertEqual(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.assertEqual(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,
(self.get_tmp_dir_path('BadTask')))
try:
obj.handler()
raise Exception('The NotImplementedError exception was not raised')
except NotImplementedError as e:
self.assertEqual(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 = TaskNoWeightTest(123, 'some_method', ['random_arg'], None, None,
(self.get_tmp_dir_path('TaskNoWeightTest')))
self.assertEqual(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 = TaskTest(123, 'some_method', ['random_arg'], None, None,
(self.get_tmp_dir_path('TaskTest')))
self.assertEqual(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 = self.get_tmp_dir_path('TaskTest')
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
obj.workdir = None
obj.createWorkdir()
self.assertEqual(path.isdir(temp_path), False)
# This patch removes the dependence on removeWorkdir functioning
@patch('{0}.TaskTest.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 = self.get_tmp_dir_path('TaskTest')
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
obj.createWorkdir()
self.assertEqual(path.isdir(temp_path), True)
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 = self.get_tmp_dir_path('TaskTest')
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
makedirs(temp_path)
self.assertEqual(path.isdir(temp_path), True)
obj.removeWorkdir()
self.assertEqual(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 = self.get_tmp_dir_path('TaskTest')
obj = TaskTest(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.assertEqual(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 = self.get_tmp_dir_path('TaskTest')
obj = TaskTest(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.assertEqual(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 = self.get_tmp_dir_path('TaskTest')
obj = TaskTest(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.assertEqual(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 = self.get_tmp_dir_path('TaskTest')
obj = TaskTest(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.assertEqual(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 = self.get_tmp_dir_path('TaskTest')
obj = TaskTest(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))
if six.PY3:
sigtimedwait = patch('signal.sigtimedwait').start()
try:
obj.wait([99, 100, 101], timeout=3600)
raise Exception('A GenericError was not raised.')
except koji.GenericError as e:
self.assertEqual(e.args[0][:24], 'Subtasks timed out after')
if six.PY3:
sigtimedwait.stop()
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 = self.get_tmp_dir_path('TaskTest')
obj = TaskTest(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
if six.PY3:
sigtimedwait = patch('signal.sigtimedwait').start()
obj.wait([99, 100, 101], timeout=3600)
if six.PY3:
sigtimedwait.stop()
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 = self.get_tmp_dir_path('TaskTest')
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
self.assertEqual(obj.getUploadDir(), 'tasks/123/123')
# This patch removes the dependence on getUploadDir functioning
@patch('{0}.TaskTest.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 = self.get_tmp_dir_path('TaskTest')
makedirs(temp_path)
temp_file = path.join(temp_path, 'test.txt')
with open(temp_file, 'wt') as temp_file_handler:
temp_file_handler.write('Test')
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
obj.session = Mock()
self.assertEqual(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}.TaskTest.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 = self.get_tmp_dir_path('TaskTest')
makedirs(temp_path)
temp_file = path.join(temp_path, 'test.txt')
temp_file_handler = open(temp_file, 'w')
temp_file_handler.close()
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
obj.session = Mock()
self.assertEqual(obj.uploadFile(temp_file), None)
obj.session.uploadWrapper.assert_called_once()
def test_BaseTaskHandler_uploadTree(self):
""" Tests that the uploadTree function calls the uploadFile function
with the correct parameters.
"""
temp_path = self.get_tmp_dir_path('TaskTest')
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, 'wt') as temp_file_handler:
temp_file_handler.write('Test')
dummy_file2 = path.join(dummy_dir, 'test2.txt')
with open(dummy_file2, 'wt') as temp_file_handler2:
temp_file_handler2.write('Test2')
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
obj.uploadFile = Mock()
obj.uploadFile.return_value = None
self.assertEqual(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 = self.get_tmp_dir_path('TaskTest')
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 = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
self.assertEqual(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 = self.get_tmp_dir_path('TaskTest')
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 = TaskTest(123, 'some_method', ['random_arg'], None, options, temp_path)
self.assertEqual(obj.localPath('test.txt'), dummy_file)
@requests_mock.Mocker()
def test_BaseTaskHandler_localPath_no_file(self, m_requests):
"""
"""
temp_path = self.get_tmp_dir_path('TaskTest')
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'
url = options.topurl + '/test.txt'
m_requests.register_uri('GET', url, text='Important things\nSome more important things\n')
obj = TaskTest(123, 'some_method', ['random_arg'], None, options, temp_path)
self.assertEqual(obj.localPath('test.txt'), target_file_path)
self.assertEqual(m_requests.call_count, 1)
self.assertEqual(m_requests.request_history[0].url, url)
def test_BaseTaskHandler_localPath_no_topurl(self):
""" Tests that the localPath function returns a path when options.topurl is not defined.
"""
temp_path = self.get_tmp_dir_path('TaskTest')
makedirs(temp_path)
options = Mock()
options.topurl = None
options.topdir = self.tempdir
obj = TaskTest(123, 'some_method', ['random_arg'], None, options, temp_path)
self.assertEqual(obj.localPath('test.txt'), path.join(self.tempdir, '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 = self.get_tmp_dir_path('TaskTest')
makedirs(temp_path)
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
self.assertEqual(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 = self.get_tmp_dir_path('TaskTest')
makedirs(temp_path)
host = {'arches': None, 'name': 'test.domain.local'}
obj = TaskTest(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.assertEqual(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 = self.get_tmp_dir_path('TaskTest')
makedirs(temp_path)
host = {'arches': 'x86_64', 'name': 'test.domain.local'}
tag = {'arches': None, 'name': 'some_package-1.2-build'}
obj = TaskTest(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.assertEqual(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 = self.get_tmp_dir_path('TaskTest')
makedirs(temp_path)
host = {'arches': 'x86_64', 'name': 'test.domain.local'}
tag = {'arches': 'x86_64 aarch64', 'name': 'some_package-1.2-build'}
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
self.assertEqual(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 = self.get_tmp_dir_path('TaskTest')
makedirs(temp_path)
host = {'arches': 'i386', 'name': 'test.domain.local'}
tag = {'arches': 'x86_64 aarch64', 'name': 'some_package-1.2-build'}
obj = TaskTest(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.assertEqual(e.args[0],
('host test.domain.local (i386) does not support any arches '
'of tag some_package-1.2-build (aarch64, x86_64)'))
@patch('koji.util.RepoWatcher')
def test_getRepo_no_wait_task(self, RepoWatcher):
""" Tests that the getRepo method does not wait if repo is available"""
temp_path = self.get_tmp_dir_path('TaskTest')
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
}
handler = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
handler.session = mock.MagicMock()
handler.wait = mock.MagicMock()
watcher = mock.MagicMock()
watcher.getRepo.return_value = repo_dict
RepoWatcher.return_value = watcher
result = handler.getRepo(8472)
handler.session.host.subtask.assert_not_called()
handler.wait.assert_not_called()
self.assertEqual(result, repo_dict)
@patch('koji.util.RepoWatcher')
def test_getRepo_last_event(self, RepoWatcher):
""" Tests that the getRepo method uses min_event='last' when requested"""
temp_path = self.get_tmp_dir_path('TaskTest')
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
}
handler = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
handler.session = mock.MagicMock()
handler.wait = mock.MagicMock()
watcher = mock.MagicMock()
watcher.getRepo.return_value = repo_dict
RepoWatcher.return_value = watcher
result = handler.getRepo(8472, wait=True)
RepoWatcher.assert_called_once_with(handler.session, 8472, nvrs=None, min_event='last', logger=handler.logger)
handler.session.host.subtask.assert_not_called()
handler.wait.assert_not_called()
self.assertEqual(result, repo_dict)
@patch('koji.util.RepoWatcher')
def test_getRepo_wait_task(self, RepoWatcher):
""" Tests that the getRepo function waits for subtask if repo not immediately available"""
temp_path = self.get_tmp_dir_path('TaskTest')
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
}
handler = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
handler.session = mock.MagicMock()
handler.session.host.subtask.return_value = 'TASKID'
handler.wait = mock.MagicMock()
handler.wait.return_value = {'TASKID': repo_dict}
watcher = mock.MagicMock()
watcher.getRepo.return_value = None
RepoWatcher.return_value = watcher
result = handler.getRepo(8472)
handler.session.host.subtask.assert_called_once()
handler.wait.assert_called_once_with('TASKID')
self.assertEqual(result, repo_dict)
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,
(self.get_tmp_dir_path('FakeTask')))
self.assertEqual(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, (self.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, (self.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, (self.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, (self.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.assertEqual(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.assertEqual(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.assertEqual(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.assertEqual(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.assertEqual(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.assertEqual(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)