[virt-tools-list] [virt-bootstrap] [PATCH v4 12/26] tests: Add unit tests for Build_QCOW2_Image

Cedric Bosdonnat cbosdonnat at suse.com
Thu Aug 3 14:28:57 UTC 2017


Should be merged with the commit introducing that function.

Generally speaking, these tests shouldn't need to be rewritten when changing the
implementation. These are doing white box tests. This is fine, but doesn't help
insuring that a major refactoring like this one doesn't break anything.

For the qcow2 images creation, I would rather see more high level tests like:
  * Preparing a few tarball
  * Creating the qcow2 images from them
  * Check the results:
    * Check the backing chain with qemu-img info
    * Check the content (including permissions) of the qcow2 files

With such a test, you can run the test suite before and after the refactoring
to know if it breaks... and you're saving some work as you don't need to rework
the tests for each refactoring.

--
Cedric

On Thu, 2017-08-03 at 14:13 +0100, Radostin Stoyanov wrote:
> ---
>  tests/test_build_qcow2_image.py | 269 ++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 269 insertions(+)
>  create mode 100644 tests/test_build_qcow2_image.py
> 
> diff --git a/tests/test_build_qcow2_image.py b/tests/test_build_qcow2_image.py
> new file mode 100644
> index 0000000..09323c6
> --- /dev/null
> +++ b/tests/test_build_qcow2_image.py
> @@ -0,0 +1,269 @@
> +# Authors: Radostin Stoyanov <rstoyanov1 at gmail.com>
> +#
> +# Copyright (C) 2017 Radostin Stoyanov
> +#
> +# This program is free software: you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation, either version 3 of the License, or
> +# (at your option) any later version.
> +
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +
> +"""
> +Unit tests for functions defined in virtBootstrap.utils.Build_QCOW2_Image
> +"""
> +
> +from tests import unittest
> +from tests import mock
> +from tests import utils
> +
> +
> +# pylint: disable=invalid-name
> +class TestBuild_QCOW2_Image(unittest.TestCase):
> +    """
> +    Ensures that methods defined in the Build_QCOW2_Image class work
> +    as expected.
> +    """
> +
> +    ###################################
> +    # Tests for: __init__()
> +    ###################################
> +    def test_argument_assignment(self):
> +        """
> +        Ensures that __init__() assigns the arguments' values to instance
> +        variables.
> +        """
> +        kwargs = {
> +            'tar_files': ['foo', 'bar'],
> +            'progress': mock.Mock(),
> +            'dest': 'dest'
> +        }
> +
> +        m_guestfs = mock.Mock()
> +        m_guestfs.list_devices.return_value = ['/dev/sda']
> +
> +        with mock.patch('virtBootstrap.utils.guestfs') as m_libguestfs:
> +            m_libguestfs.GuestFS.return_value = m_guestfs
> +
> +            with mock.patch.multiple(
> +                'virtBootstrap.utils.Build_QCOW2_Image',
> +                create_base_qcow2_layer=mock.DEFAULT,
> +                create_backing_chains=mock.DEFAULT,
> +            ):
> +                src_instance = utils.Build_QCOW2_Image(**kwargs)
> +
> +        # The length of qcow2 files should be equal to the number of tar files
> +        self.assertEqual(
> +            len(src_instance.qcow2_files),
> +            len(kwargs['tar_files'])
> +        )
> +
> +        self.assertIs(src_instance.tar_files, kwargs['tar_files'])
> +        self.assertIs(src_instance.progress, kwargs['progress'])
> +        self.assertIs(src_instance.g, m_guestfs)
> +
> +    ###################################
> +    # Tests for: create_disk()
> +    ###################################
> +    def test_create_disk(self):
> +        """
> +        Ensures that create_disk() calls disk_create() and add_drive_opts()
> +        with correct parameters when backing file is not specified.
> +        """
> +        qcow2_file = 'layer-0.qcow2'
> +        m_self = mock.Mock(spec=utils.Build_QCOW2_Image)
> +
> +        m_self.g = mock.Mock()
> +        m_self.fmt = utils.DEFAULT_OUTPUT_FORMAT
> +
> +        utils.Build_QCOW2_Image.create_disk(m_self, qcow2_file)
> +
> +        m_self.g.disk_create.assert_called_once_with(
> +            qcow2_file,
> +            utils.DEFAULT_OUTPUT_FORMAT,
> +            utils.DEF_BASE_IMAGE_SIZE,
> +            None,  # backingfile
> +            None  # backingformat
> +        )
> +        m_self.g.add_drive_opts.assert_called_once_with(
> +            qcow2_file,
> +            False,  # readonly
> +            m_self.fmt
> +        )
> +
> +    def test_create_disk_with_backing_file(self):
> +        """
> +        Ensures that create_disk() calls disk_create() and add_drive_opts()
> +        with correct parameters when backing file is specified.
> +        """
> +        qcow2_file = 'layer-1.qcow2'
> +        backingfile = 'layer-0.qcow2'
> +
> +        m_self = mock.Mock(spec=utils.Build_QCOW2_Image)
> +        m_self.g = mock.Mock()
> +        m_self.fmt = utils.DEFAULT_OUTPUT_FORMAT
> +
> +        utils.Build_QCOW2_Image.create_disk(
> +            m_self,
> +            qcow2_file,
> +            backingfile
> +        )
> +
> +        m_self.g.disk_create.assert_called_once_with(
> +            qcow2_file,
> +            utils.DEFAULT_OUTPUT_FORMAT,
> +            -1,  # size
> +            backingfile,
> +            m_self.fmt  # backingformat
> +        )
> +        m_self.g.add_drive_opts.assert_called_once_with(
> +            qcow2_file,
> +            False,  # readonly
> +            m_self.fmt
> +        )
> +
> +    ###################################
> +    # Tests for: tar_in()
> +    ###################################
> +    def test_tar_in(self):
> +        """
> +        Ensures that tar_in() calls mount(), tar_in() and unmount()
> +        in this order and with correct parameters.
> +        """
> +        tar_file = 'foo.tar'
> +        dev = '/dev/sda'
> +
> +        m_self = mock.Mock(spec=utils.Build_QCOW2_Image)
> +        m_self.g = mock.Mock()
> +
> +        with mock.patch(
> +            'virtBootstrap.utils.get_compression_type'
> +        ) as m_get_compression_type:
> +            utils.Build_QCOW2_Image.tar_in(m_self, tar_file, dev)
> +
> +        self.assertEqual(
> +            m_self.g.method_calls,
> +            [
> +                mock.call.mount(dev, '/'),
> +                mock.call.tar_in(
> +                    tar_file,
> +                    '/',
> +                    m_get_compression_type(tar_file),
> +                    xattrs=True,
> +                    selinux=True,
> +                    acls=True
> +                ),
> +                mock.call.umount('/')
> +            ]
> +        )
> +
> +    ###################################
> +    # Tests for: create_base_qcow2_layer()
> +    ###################################
> +    def test_create_base_qcow2_layer(self):
> +        """
> +        Ensures that create_base_qcow2_layer() calls create_disk(),
> +        g.launch(), g.mkfs(), tar_in() and g.shutdown() in this order
> +        and with correct parameters.
> +        """
> +        tar_file = 'foo.tar'
> +        qcow2_file = 'layer-0.qcow2'
> +        dev = '/dev/sda'
> +
> +        m_self = mock.Mock(spec=utils.Build_QCOW2_Image)
> +        m_self.g = mock.Mock()
> +        m_self.progress = mock.Mock()
> +        m_self.g.list_devices.return_value = [dev]
> +
> +        utils.Build_QCOW2_Image.create_base_qcow2_layer(
> +            m_self,
> +            tar_file,
> +            qcow2_file
> +        )
> +
> +        self.assertEqual(
> +            m_self.mock_calls,
> +            [
> +                mock.call.progress(
> +                    'Creating base layer',
> +                    logger=utils.logger
> +                ),
> +                mock.call.create_disk(qcow2_file),
> +                mock.call.g.launch(),
> +                mock.call.g.list_devices(),
> +                mock.call.progress(
> +                    'Formating disk image',
> +                    logger=utils.logger
> +                ),
> +                mock.call.g.mkfs('ext3', dev),
> +                mock.call.tar_in(tar_file, dev),
> +                mock.call.progress(
> +                    'Extracting content of base layer',
> +                    logger=utils.logger
> +                ),
> +                mock.call.g.shutdown()
> +            ]
> +        )
> +
> +    ###################################
> +    # Tests for: create_backing_chains()
> +    ###################################
> +    def test_create_backing_chains(self):
> +        """
> +        Ensures that create_backing_chains() calls with correct parameters:
> +            - create_disk() n-1 times
> +            - g.launch()
> +            - tar_in() n-1 times
> +
> +        Where n is the number of layers
> +        """
> +        n = 5
> +        m_self = mock.Mock(spec=utils.Build_QCOW2_Image)
> +        m_self.progress = mock.Mock()
> +        m_self.g = mock.Mock()
> +
> +        devices = ['dev%d' % i for i in range(n)]
> +        m_self.g.list_devices.return_value = devices
> +        m_self.qcow2_files = ['bar%d.qcow2' % i for i in range(n)]
> +        m_self.tar_files = ['foo%d.tar' % i for i in range(n)]
> +
> +        utils.Build_QCOW2_Image.create_backing_chains(m_self)
> +
> +        expected_calls = []
> +
> +        # Append create_disk() calls
> +        for i in range(1, n):
> +            expected_calls.extend((
> +                mock.call.progress(
> +                    'Creating layer %d' % i,
> +                    logger=utils.logger
> +                ),
> +                mock.call.create_disk(
> +                    m_self.qcow2_files[i],
> +                    backingfile=m_self.qcow2_files[i - 1]
> +                )
> +            ))
> +
> +        expected_calls.extend((
> +            mock.call.g.launch(),
> +            mock.call.g.list_devices()
> +        ))
> +
> +        # Append tar_in() calls
> +        for i, tar_file in enumerate(m_self.tar_files[1:]):
> +            expected_calls.extend((
> +                mock.call.progress(
> +                    'Extracting content of layer %d' % (i + 1),
> +                    logger=utils.logger
> +                ),
> +                mock.call.tar_in(tar_file, devices[i])
> +            ))
> +
> +        self.assertEqual(m_self.method_calls, expected_calls)




More information about the virt-tools-list mailing list