pungi/tests/test_iso_wrapper.py
Lubomír Sedlář ff5a7e6377 Make python3-mock dependency optional
https://fedoraproject.org/wiki/Changes/RemovePythonMockUsage

Prefer using unittest.mock to a standalone package. The separate
packages should only really be needed on Python 2.7 these days.

The test requirements file is updated to only require mock on old
Python, and the dependency is removed from setup.py to avoid issues
there.

Relates: https://src.fedoraproject.org/rpms/pungi/pull-request/9

Signed-off-by: Lubomír Sedlář <lsedlar@redhat.com>
2024-01-26 09:45:19 +01:00

220 lines
8.8 KiB
Python

# -*- coding: utf-8 -*-
import itertools
try:
from unittest import mock
except ImportError:
import mock
import os
import six
try:
import unittest2 as unittest
except ImportError:
import unittest
from pungi.wrappers import iso
CORRECT_OUTPUT = """dummy.iso: 31ff3e405e26ad01c63b62f6b11d30f6
Fragment sums: 6eb92e7bda221d7fe5f19b4d21468c9bf261d84c96d145d96c76444b9cbc
Fragment count: 20
Supported ISO: no
"""
INCORRECT_OUTPUT = """This should never happen: File not found"""
# Cached to use in tests that mock os.listdir
orig_listdir = os.listdir
def fake_listdir(pattern, result=None, exc=None):
"""Create a function that mocks os.listdir. If the path contains pattern,
result will be returned or exc raised. Otherwise it's normal os.listdir
"""
# The point of this is to avoid issues on Python 2, where apparently
# isdir() is using listdir(), so the mocking is breaking it.
def worker(path):
if isinstance(path, six.string_types) and pattern in path:
if exc:
raise exc
return result
return orig_listdir(path)
return worker
class TestIsoUtils(unittest.TestCase):
@mock.patch("pungi.wrappers.iso.run")
def test_get_implanted_md5_correct(self, mock_run):
mock_run.return_value = (0, CORRECT_OUTPUT)
logger = mock.Mock()
self.assertEqual(
iso.get_implanted_md5("dummy.iso", logger=logger),
"31ff3e405e26ad01c63b62f6b11d30f6",
)
self.assertEqual(
mock_run.call_args_list,
[
mock.call(
["/usr/bin/checkisomd5", "--md5sumonly", "dummy.iso"],
universal_newlines=True,
)
],
)
self.assertEqual(logger.mock_calls, [])
@mock.patch("pungi.wrappers.iso.run")
def test_get_implanted_md5_incorrect(self, mock_run):
mock_run.return_value = (0, INCORRECT_OUTPUT)
logger = mock.Mock()
self.assertEqual(iso.get_implanted_md5("dummy.iso", logger=logger), None)
self.assertEqual(
mock_run.call_args_list,
[
mock.call(
["/usr/bin/checkisomd5", "--md5sumonly", "dummy.iso"],
universal_newlines=True,
)
],
)
self.assertTrue(len(logger.mock_calls) > 0)
@mock.patch("pungi.util.run_unmount_cmd")
@mock.patch("pungi.wrappers.iso.run")
def test_mount_iso(self, mock_run, mock_unmount):
# first tuple is return value for command 'which guestmount'
# value determines type of the mount/unmount
# command ('1' - guestmount is not available)
# for approach as a root, pair commands mount-umount are used
mock_run.side_effect = [(1, ""), (0, "")]
with iso.mount("dummy") as temp_dir:
self.assertTrue(os.path.isdir(temp_dir))
self.assertEqual(len(mock_run.call_args_list), 2)
mount_call_str = str(mock_run.call_args_list[1])
self.assertTrue(mount_call_str.startswith("call(['mount'"))
self.assertEqual(len(mock_unmount.call_args_list), 1)
unmount_call_str = str(mock_unmount.call_args_list[0])
self.assertTrue(unmount_call_str.startswith("call(['umount'"))
self.assertFalse(os.path.isdir(temp_dir))
@mock.patch("pungi.util.rmtree")
@mock.patch("os.listdir", new=fake_listdir("guestfs", ["root"]))
@mock.patch("pungi.util.run_unmount_cmd")
@mock.patch("pungi.wrappers.iso.run")
def test_guestmount(self, mock_run, mock_unmount, mock_rmtree):
# first tuple is return value for command 'which guestmount'
# value determines type of the mount/unmount
# command ('0' - guestmount is available)
# for approach as a non-root, pair commands guestmount-fusermount are used
mock_run.side_effect = [(0, ""), (0, "")]
with iso.mount("dummy") as temp_dir:
self.assertTrue(os.path.isdir(temp_dir))
self.assertEqual(len(mock_run.call_args_list), 2)
mount_call_str = str(mock_run.call_args_list[1])
self.assertTrue(mount_call_str.startswith("call(['guestmount'"))
self.assertEqual(len(mock_unmount.call_args_list), 1)
unmount_call_str = str(mock_unmount.call_args_list[0])
self.assertTrue(unmount_call_str.startswith("call(['fusermount'"))
self.assertFalse(os.path.isdir(temp_dir))
@mock.patch("pungi.util.rmtree")
@mock.patch("os.listdir", new=fake_listdir("guestfs", []))
@mock.patch("pungi.util.run_unmount_cmd")
@mock.patch("pungi.wrappers.iso.run")
def test_guestmount_cleans_up_cache(self, mock_run, mock_unmount, mock_rmtree):
# first tuple is return value for command 'which guestmount'
# value determines type of the mount/unmount
# command ('0' - guestmount is available)
# for approach as a non-root, pair commands guestmount-fusermount are used
mock_run.side_effect = [(0, ""), (0, "")]
with iso.mount("dummy") as temp_dir:
self.assertTrue(os.path.isdir(temp_dir))
self.assertEqual(len(mock_run.call_args_list), 2)
mount_call_str = str(mock_run.call_args_list[1])
self.assertTrue(mount_call_str.startswith("call(['guestmount'"))
self.assertEqual(len(mock_unmount.call_args_list), 1)
unmount_call_str = str(mock_unmount.call_args_list[0])
self.assertTrue(unmount_call_str.startswith("call(['fusermount'"))
self.assertFalse(os.path.isdir(temp_dir))
@mock.patch("pungi.util.rmtree")
@mock.patch("os.listdir", new=fake_listdir("guestfs", OSError("No such file")))
@mock.patch("pungi.util.run_unmount_cmd")
@mock.patch("pungi.wrappers.iso.run")
def test_guestmount_handles_missing_cache(
self, mock_run, mock_unmount, mock_rmtree
):
# first tuple is return value for command 'which guestmount'
# value determines type of the mount/unmount
# command ('0' - guestmount is available)
# for approach as a non-root, pair commands guestmount-fusermount are used
mock_run.side_effect = [(0, ""), (0, "")]
with iso.mount("dummy") as temp_dir:
self.assertTrue(os.path.isdir(temp_dir))
self.assertEqual(len(mock_run.call_args_list), 2)
mount_call_str = str(mock_run.call_args_list[1])
self.assertTrue(mount_call_str.startswith("call(['guestmount'"))
self.assertEqual(len(mock_unmount.call_args_list), 1)
unmount_call_str = str(mock_unmount.call_args_list[0])
self.assertTrue(unmount_call_str.startswith("call(['fusermount'"))
self.assertFalse(os.path.isdir(temp_dir))
@mock.patch("pungi.util.run_unmount_cmd")
@mock.patch("pungi.wrappers.iso.run")
def test_mount_iso_always_unmounts(self, mock_run, mock_unmount):
mock_run.side_effect = [(1, ""), (0, "")]
try:
with iso.mount("dummy") as temp_dir:
self.assertTrue(os.path.isdir(temp_dir))
raise RuntimeError()
except RuntimeError:
pass
self.assertEqual(len(mock_run.call_args_list), 2)
self.assertEqual(len(mock_unmount.call_args_list), 1)
self.assertFalse(os.path.isdir(temp_dir))
@mock.patch("pungi.util.run_unmount_cmd")
@mock.patch("pungi.wrappers.iso.run")
def test_mount_iso_raises_on_error(self, mock_run, mock_unmount):
log = mock.Mock()
mock_run.side_effect = [(1, ""), (1, "Boom")]
with self.assertRaises(RuntimeError):
with iso.mount("dummy", logger=log) as temp_dir:
self.assertTrue(os.path.isdir(temp_dir))
self.assertEqual(len(mock_run.call_args_list), 2)
self.assertEqual(len(mock_unmount.call_args_list), 0)
self.assertEqual(len(log.mock_calls), 1)
class TestCmpGraftPoints(unittest.TestCase):
def assertSorted(self, *args):
"""Tests that all permutations of arguments yield the same sorted results."""
for perm in itertools.permutations(args):
self.assertEqual(sorted(perm, key=iso.graft_point_sort_key), list(args))
def test_eq(self):
self.assertSorted("pkgs/foo.rpm", "pkgs/foo.rpm")
def test_rpms_sorted_alphabetically(self):
self.assertSorted("pkgs/bar.rpm", "pkgs/foo.rpm")
def test_images_sorted_alphabetically(self):
self.assertSorted("aaa.img", "images/foo", "isolinux/foo")
def test_other_files_sorted_alphabetically(self):
self.assertSorted("bar.txt", "foo.txt")
def test_rpms_after_images(self):
self.assertSorted("foo.ins", "bar.rpm")
def test_other_after_images(self):
self.assertSorted("EFI/anything", "zzz.txt")
def test_rpm_after_other(self):
self.assertSorted("bbb.txt", "aaa.rpm")
def test_all_kinds(self):
self.assertSorted("etc/file", "ppc/file", "c.txt", "d.txt", "a.rpm", "b.rpm")