2016-05-03 14:31:20 +00:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
2016-05-25 11:39:02 +00:00
|
|
|
try:
|
|
|
|
import unittest2 as unittest
|
|
|
|
except ImportError:
|
|
|
|
import unittest
|
2016-05-03 14:31:20 +00:00
|
|
|
|
2016-06-06 08:29:40 +00:00
|
|
|
import mock
|
2016-05-03 14:31:20 +00:00
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
|
|
|
|
|
|
import pungi.phases.test as test_phase
|
2017-07-27 09:59:03 +00:00
|
|
|
from tests.helpers import DummyCompose, PungiTestCase, touch, mk_boom
|
2016-05-03 14:31:20 +00:00
|
|
|
|
|
|
|
|
2017-09-05 08:01:21 +00:00
|
|
|
PAD = b'\0' * 100
|
|
|
|
UNBOOTABLE_ISO = (b'\0' * 0x8001) + b'CD001' + PAD
|
|
|
|
ISO_WITH_MBR = (b'\0' * 0x1fe) + b'\x55\xAA' + (b'\0' * 0x7e01) + b'CD001' + PAD
|
|
|
|
ISO_WITH_GPT = (b'\0' * 0x200) + b'EFI PART' + (b'\0' * 0x7df9) + b'CD001' + PAD
|
|
|
|
ISO_WITH_MBR_AND_GPT = (b'\0' * 0x1fe) + b'\x55\xAAEFI PART' + (b'\0' * 0x7df9) + b'CD001' + PAD
|
|
|
|
ISO_WITH_TORITO = (b'\0' * 0x8001) + b'CD001' + (b'\0' * 0x7fa) + b'\0CD001\1EL TORITO SPECIFICATION' + PAD
|
2016-05-03 14:31:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
class TestCheckImageSanity(PungiTestCase):
|
|
|
|
|
|
|
|
def test_missing_file_reports_error(self):
|
|
|
|
compose = DummyCompose(self.topdir, {})
|
|
|
|
|
|
|
|
with self.assertRaises(IOError):
|
|
|
|
test_phase.check_image_sanity(compose)
|
|
|
|
|
|
|
|
def test_missing_file_doesnt_report_if_failable(self):
|
2016-06-24 07:44:40 +00:00
|
|
|
compose = DummyCompose(self.topdir, {})
|
2016-05-03 14:31:20 +00:00
|
|
|
compose.image.deliverable = 'iso'
|
2016-06-24 07:44:40 +00:00
|
|
|
compose.image.can_fail = True
|
2016-05-03 14:31:20 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
test_phase.check_image_sanity(compose)
|
2017-01-26 08:44:45 +00:00
|
|
|
except Exception:
|
2016-05-03 14:31:20 +00:00
|
|
|
self.fail('Failable deliverable must not raise')
|
|
|
|
|
|
|
|
def test_correct_iso_does_not_raise(self):
|
|
|
|
compose = DummyCompose(self.topdir, {})
|
|
|
|
compose.image.format = 'iso'
|
|
|
|
compose.image.bootable = False
|
|
|
|
touch(os.path.join(self.topdir, 'compose', compose.image.path), UNBOOTABLE_ISO)
|
|
|
|
|
|
|
|
try:
|
|
|
|
test_phase.check_image_sanity(compose)
|
2017-01-26 08:44:45 +00:00
|
|
|
except Exception:
|
2016-05-03 14:31:20 +00:00
|
|
|
self.fail('Correct unbootable image must not raise')
|
|
|
|
|
|
|
|
def test_incorrect_iso_raises(self):
|
|
|
|
compose = DummyCompose(self.topdir, {})
|
|
|
|
compose.image.format = 'iso'
|
|
|
|
compose.image.bootable = False
|
|
|
|
touch(os.path.join(self.topdir, 'compose', compose.image.path), 'Hey there')
|
|
|
|
|
|
|
|
with self.assertRaises(RuntimeError) as ctx:
|
|
|
|
test_phase.check_image_sanity(compose)
|
|
|
|
|
|
|
|
self.assertIn('does not look like an ISO file', str(ctx.exception))
|
|
|
|
|
2016-08-10 11:02:56 +00:00
|
|
|
def test_bootable_iso_without_mbr_or_gpt_raises_on_x86_64(self):
|
2016-05-03 14:31:20 +00:00
|
|
|
compose = DummyCompose(self.topdir, {})
|
2016-08-10 11:02:56 +00:00
|
|
|
compose.image.arch = 'x86_64'
|
2016-05-03 14:31:20 +00:00
|
|
|
compose.image.format = 'iso'
|
|
|
|
compose.image.bootable = True
|
|
|
|
touch(os.path.join(self.topdir, 'compose', compose.image.path), UNBOOTABLE_ISO)
|
|
|
|
|
|
|
|
with self.assertRaises(RuntimeError) as ctx:
|
|
|
|
test_phase.check_image_sanity(compose)
|
|
|
|
|
|
|
|
self.assertIn('is supposed to be bootable, but does not have MBR nor GPT',
|
|
|
|
str(ctx.exception))
|
|
|
|
|
2016-08-10 11:02:56 +00:00
|
|
|
def test_bootable_iso_without_mbr_or_gpt_doesnt_raise_on_arm(self):
|
|
|
|
compose = DummyCompose(self.topdir, {})
|
|
|
|
compose.image.arch = 'armhfp'
|
|
|
|
compose.image.format = 'iso'
|
|
|
|
compose.image.bootable = True
|
|
|
|
touch(os.path.join(self.topdir, 'compose', compose.image.path), UNBOOTABLE_ISO)
|
|
|
|
|
|
|
|
try:
|
|
|
|
test_phase.check_image_sanity(compose)
|
2017-01-26 08:44:45 +00:00
|
|
|
except Exception:
|
2016-08-10 11:02:56 +00:00
|
|
|
self.fail('Failable deliverable must not raise')
|
|
|
|
|
2016-05-03 14:31:20 +00:00
|
|
|
def test_failable_bootable_iso_without_mbr_gpt_doesnt_raise(self):
|
2016-06-24 07:44:40 +00:00
|
|
|
compose = DummyCompose(self.topdir, {})
|
2016-05-03 14:31:20 +00:00
|
|
|
compose.image.format = 'iso'
|
|
|
|
compose.image.bootable = True
|
|
|
|
compose.image.deliverable = 'iso'
|
2016-06-24 07:44:40 +00:00
|
|
|
compose.image.can_fail = True
|
2016-05-03 14:31:20 +00:00
|
|
|
touch(os.path.join(self.topdir, 'compose', compose.image.path), UNBOOTABLE_ISO)
|
|
|
|
|
|
|
|
try:
|
|
|
|
test_phase.check_image_sanity(compose)
|
2017-01-26 08:44:45 +00:00
|
|
|
except Exception:
|
2016-05-03 14:31:20 +00:00
|
|
|
self.fail('Failable deliverable must not raise')
|
|
|
|
|
|
|
|
def test_bootable_iso_with_mbr_does_not_raise(self):
|
|
|
|
compose = DummyCompose(self.topdir, {})
|
|
|
|
compose.image.format = 'iso'
|
|
|
|
compose.image.bootable = True
|
|
|
|
touch(os.path.join(self.topdir, 'compose', compose.image.path), ISO_WITH_MBR)
|
|
|
|
|
|
|
|
try:
|
|
|
|
test_phase.check_image_sanity(compose)
|
2017-01-26 08:44:45 +00:00
|
|
|
except Exception:
|
2016-05-03 14:31:20 +00:00
|
|
|
self.fail('Bootable image with MBR must not raise')
|
|
|
|
|
|
|
|
def test_bootable_iso_with_gpt_does_not_raise(self):
|
|
|
|
compose = DummyCompose(self.topdir, {})
|
|
|
|
compose.image.format = 'iso'
|
|
|
|
compose.image.bootable = True
|
|
|
|
touch(os.path.join(self.topdir, 'compose', compose.image.path), ISO_WITH_GPT)
|
|
|
|
|
|
|
|
try:
|
|
|
|
test_phase.check_image_sanity(compose)
|
2017-01-26 08:44:45 +00:00
|
|
|
except Exception:
|
2016-05-03 14:31:20 +00:00
|
|
|
self.fail('Bootable image with GPT must not raise')
|
|
|
|
|
|
|
|
def test_bootable_iso_with_mbr_and_gpt_does_not_raise(self):
|
|
|
|
compose = DummyCompose(self.topdir, {})
|
|
|
|
compose.image.format = 'iso'
|
|
|
|
compose.image.bootable = True
|
|
|
|
touch(os.path.join(self.topdir, 'compose', compose.image.path), ISO_WITH_MBR_AND_GPT)
|
|
|
|
|
|
|
|
try:
|
|
|
|
test_phase.check_image_sanity(compose)
|
2017-01-26 08:44:45 +00:00
|
|
|
except Exception:
|
2016-05-03 14:31:20 +00:00
|
|
|
self.fail('Bootable image with MBR and GPT must not raise')
|
|
|
|
|
2016-07-22 11:58:29 +00:00
|
|
|
def test_bootable_iso_with_el_torito_does_not_raise(self):
|
|
|
|
compose = DummyCompose(self.topdir, {})
|
|
|
|
compose.image.format = 'iso'
|
|
|
|
compose.image.bootable = True
|
|
|
|
touch(os.path.join(self.topdir, 'compose', compose.image.path), ISO_WITH_TORITO)
|
|
|
|
|
|
|
|
try:
|
|
|
|
test_phase.check_image_sanity(compose)
|
2017-01-26 08:44:45 +00:00
|
|
|
except Exception:
|
2016-07-22 11:58:29 +00:00
|
|
|
self.fail('Bootable image with El Torito must not raise')
|
|
|
|
|
2016-06-06 08:29:40 +00:00
|
|
|
def test_checks_with_optional_variant(self):
|
|
|
|
compose = DummyCompose(self.topdir, {})
|
|
|
|
compose.variants['Server'].variants = {
|
|
|
|
'optional': mock.Mock(uid='Server-optional', arches=['x86_64'],
|
|
|
|
type='optional', is_empty=False)
|
|
|
|
}
|
|
|
|
compose.image.format = 'iso'
|
|
|
|
compose.image.bootable = True
|
|
|
|
touch(os.path.join(self.topdir, 'compose', compose.image.path), ISO_WITH_MBR_AND_GPT)
|
|
|
|
|
|
|
|
image = mock.Mock(path="Server/i386/optional/iso/image.iso",
|
|
|
|
format='iso', bootable=False)
|
|
|
|
compose.im.images['Server-optional'] = {'i386': [image]}
|
|
|
|
|
|
|
|
try:
|
|
|
|
test_phase.check_image_sanity(compose)
|
2017-01-26 08:44:45 +00:00
|
|
|
except Exception:
|
2016-06-06 08:29:40 +00:00
|
|
|
self.fail('Checking optional variant must not raise')
|
|
|
|
|
2016-05-03 14:31:20 +00:00
|
|
|
|
2017-01-26 08:44:45 +00:00
|
|
|
class TestRepoclosure(PungiTestCase):
|
|
|
|
|
2017-07-27 09:59:03 +00:00
|
|
|
def setUp(self):
|
|
|
|
super(TestRepoclosure, self).setUp()
|
|
|
|
self.maxDiff = None
|
|
|
|
|
2017-01-26 08:44:45 +00:00
|
|
|
def _get_repo(self, variant, arch, path=None):
|
|
|
|
path = path or arch + '/os'
|
|
|
|
return {
|
|
|
|
'repoclosure-%s.%s' % (variant, arch): self.topdir + '/compose/%s/%s' % (variant, path)
|
|
|
|
}
|
|
|
|
|
2017-07-27 09:59:03 +00:00
|
|
|
@mock.patch('pungi.wrappers.repoclosure.get_repoclosure_cmd')
|
|
|
|
@mock.patch('pungi.phases.test.run')
|
|
|
|
def test_repoclosure_skip_if_disabled(self, mock_run, mock_grc):
|
|
|
|
compose = DummyCompose(self.topdir, {
|
|
|
|
'repoclosure_strictness': [('^.*$', {'*': 'off'})]
|
|
|
|
})
|
|
|
|
test_phase.run_repoclosure(compose)
|
|
|
|
|
2017-09-05 08:01:21 +00:00
|
|
|
self.assertEqual(mock_grc.call_args_list, [])
|
2017-07-27 09:59:03 +00:00
|
|
|
|
2017-01-26 08:44:45 +00:00
|
|
|
@mock.patch('pungi.wrappers.repoclosure.get_repoclosure_cmd')
|
|
|
|
@mock.patch('pungi.phases.test.run')
|
2017-01-26 08:19:12 +00:00
|
|
|
def test_repoclosure_default_backend(self, mock_run, mock_grc):
|
2017-01-26 08:44:45 +00:00
|
|
|
compose = DummyCompose(self.topdir, {})
|
|
|
|
test_phase.run_repoclosure(compose)
|
|
|
|
|
|
|
|
self.assertItemsEqual(
|
|
|
|
mock_grc.call_args_list,
|
2017-01-26 08:19:12 +00:00
|
|
|
[mock.call(backend='yum', arch=['amd64', 'x86_64', 'noarch'], lookaside={},
|
2017-01-26 08:44:45 +00:00
|
|
|
repos=self._get_repo('Everything', 'amd64')),
|
2017-01-26 08:19:12 +00:00
|
|
|
mock.call(backend='yum', arch=['amd64', 'x86_64', 'noarch'], lookaside={},
|
2017-01-26 08:44:45 +00:00
|
|
|
repos=self._get_repo('Client', 'amd64')),
|
2017-01-26 08:19:12 +00:00
|
|
|
mock.call(backend='yum', arch=['amd64', 'x86_64', 'noarch'], lookaside={},
|
2017-01-26 08:44:45 +00:00
|
|
|
repos=self._get_repo('Server', 'amd64')),
|
2017-01-26 08:19:12 +00:00
|
|
|
mock.call(backend='yum', arch=['x86_64', 'noarch'], lookaside={},
|
2017-01-26 08:44:45 +00:00
|
|
|
repos=self._get_repo('Server', 'x86_64')),
|
2017-01-26 08:19:12 +00:00
|
|
|
mock.call(backend='yum', arch=['x86_64', 'noarch'], lookaside={},
|
2017-01-30 09:16:03 +00:00
|
|
|
repos=self._get_repo('Everything', 'x86_64'))])
|
2017-01-26 08:19:12 +00:00
|
|
|
|
|
|
|
@mock.patch('pungi.wrappers.repoclosure.get_repoclosure_cmd')
|
|
|
|
@mock.patch('pungi.phases.test.run')
|
|
|
|
def test_repoclosure_dnf_backend(self, mock_run, mock_grc):
|
|
|
|
compose = DummyCompose(self.topdir, {'repoclosure_backend': 'dnf'})
|
|
|
|
test_phase.run_repoclosure(compose)
|
|
|
|
|
|
|
|
self.assertItemsEqual(
|
|
|
|
mock_grc.call_args_list,
|
|
|
|
[mock.call(backend='dnf', arch=['amd64', 'x86_64', 'noarch'], lookaside={},
|
|
|
|
repos=self._get_repo('Everything', 'amd64')),
|
|
|
|
mock.call(backend='dnf', arch=['amd64', 'x86_64', 'noarch'], lookaside={},
|
|
|
|
repos=self._get_repo('Client', 'amd64')),
|
|
|
|
mock.call(backend='dnf', arch=['amd64', 'x86_64', 'noarch'], lookaside={},
|
|
|
|
repos=self._get_repo('Server', 'amd64')),
|
|
|
|
mock.call(backend='dnf', arch=['x86_64', 'noarch'], lookaside={},
|
|
|
|
repos=self._get_repo('Server', 'x86_64')),
|
|
|
|
mock.call(backend='dnf', arch=['x86_64', 'noarch'], lookaside={},
|
2017-01-30 09:16:03 +00:00
|
|
|
repos=self._get_repo('Everything', 'x86_64'))])
|
2017-01-26 08:44:45 +00:00
|
|
|
|
2017-07-27 09:59:03 +00:00
|
|
|
@mock.patch('pungi.wrappers.repoclosure.get_repoclosure_cmd')
|
|
|
|
@mock.patch('pungi.phases.test.run')
|
|
|
|
def test_repoclosure_report_error(self, mock_run, mock_grc):
|
|
|
|
compose = DummyCompose(self.topdir, {
|
|
|
|
'repoclosure_strictness': [('^.*$', {'*': 'fatal'})]
|
|
|
|
})
|
|
|
|
mock_run.side_effect = mk_boom(cls=RuntimeError)
|
|
|
|
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
|
|
test_phase.run_repoclosure(compose)
|
|
|
|
|
|
|
|
@mock.patch('pungi.wrappers.repoclosure.get_repoclosure_cmd')
|
|
|
|
@mock.patch('pungi.phases.test.run')
|
|
|
|
def test_repoclosure_overwrite_options_creates_correct_commands(self, mock_run, mock_grc):
|
|
|
|
compose = DummyCompose(self.topdir, {
|
|
|
|
'repoclosure_backend': 'dnf',
|
|
|
|
'repoclosure_strictness': [
|
|
|
|
('^.*$', {'*': 'off'}),
|
|
|
|
('^Server$', {'*': 'fatal'}),
|
|
|
|
]
|
|
|
|
})
|
|
|
|
test_phase.run_repoclosure(compose)
|
|
|
|
|
|
|
|
self.assertItemsEqual(
|
|
|
|
mock_grc.call_args_list,
|
|
|
|
[mock.call(backend='dnf', arch=['amd64', 'x86_64', 'noarch'], lookaside={},
|
|
|
|
repos=self._get_repo('Server', 'amd64')),
|
|
|
|
mock.call(backend='dnf', arch=['x86_64', 'noarch'], lookaside={},
|
|
|
|
repos=self._get_repo('Server', 'x86_64')),
|
|
|
|
])
|
|
|
|
|
|
|
|
@mock.patch('pungi.wrappers.repoclosure.get_repoclosure_cmd')
|
|
|
|
@mock.patch('pungi.phases.test.run')
|
|
|
|
def test_repoclosure_uses_correct_behaviour(self, mock_run, mock_grc):
|
|
|
|
compose = DummyCompose(self.topdir, {
|
|
|
|
'repoclosure_backend': 'dnf',
|
|
|
|
'repoclosure_strictness': [
|
|
|
|
('^.*$', {'*': 'off'}),
|
|
|
|
('^Server$', {'*': 'fatal'}),
|
|
|
|
]
|
|
|
|
})
|
|
|
|
mock_run.side_effect = mk_boom(cls=RuntimeError)
|
|
|
|
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
|
|
test_phase.run_repoclosure(compose)
|
|
|
|
|
2017-01-26 08:44:45 +00:00
|
|
|
|
2016-05-03 14:31:20 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
unittest.main()
|