--- tests/__init__.py | 42 +++++++ tests/test_virt_bootstrap.py | 287 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 329 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/test_virt_bootstrap.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..2d3edce --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,42 @@ +""" + +Test suite for virt-bootstrap + + Authors: + Cedric Bosdonnat <cbosdonnat@xxxxxxxx> + Radostin Stoyanov <rstoyanov1@xxxxxxxxx> + + 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/>. +""" + +import sys +import unittest + +try: + import mock +except ImportError: + import unittest.mock as mock + +sys.path += '../src' # noqa: E402 + +# pylint: disable=import-error +from virtBootstrap import virt_bootstrap +from virtBootstrap import sources +from virtBootstrap import progress +from virtBootstrap import utils + +__all__ = ['unittest', 'mock', + 'virt_bootstrap', 'sources', 'progress', 'utils'] diff --git a/tests/test_virt_bootstrap.py b/tests/test_virt_bootstrap.py new file mode 100644 index 0000000..4d66211 --- /dev/null +++ b/tests/test_virt_bootstrap.py @@ -0,0 +1,287 @@ +# Authors: +# Cedric Bosdonnat <cbosdonnat@xxxxxxxx> +# Radostin Stoyanov <rstoyanov1@xxxxxxxxx> +# +# Copyright (C) 2017 SUSE, Inc. +# 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.virt-bootstrap +""" + +from tests import unittest +from tests import mock +from tests import virt_bootstrap +from tests import sources + + +# pylint: disable=invalid-name +class TestVirtBootstrap(unittest.TestCase): + """ + Test cases for virt_bootstrap module + """ + + ################################### + # Tests for: get_source(source_type) + ################################### + def test_get_invaid_source_type_should_fail(self): + """ + Ensures that get_source() throws an Exception when invalid source + name was specified. + """ + with self.assertRaises(Exception) as source: + virt_bootstrap.get_source('fake') + self.assertIn('fake', str(source.exception)) + + def test_get_docker_source(self): + """ + Ensures that get_source() returns DockerSource when source name + "docker" is requested. + """ + self.assertIs(virt_bootstrap.get_source('docker'), + sources.DockerSource) + + def test_get_file_source(self): + """ + Ensures that get_source() returns FileSource when source name + "file" is requested. + """ + self.assertIs(virt_bootstrap.get_source('file'), + sources.FileSource) + + ################################### + # Tests for: map_id() + ################################### + @mock.patch('os.path.realpath') + def test_map_id(self, m_realpath): + """ + Ensures that the UID/GID mapping applies to all files + and directories in root file system. + """ + root_path = '/root' + files = ['foo1', 'foo2'] + uid = gid = {} + uid['mapped'] = 1000 + gid['mapped'] = 1001 + uid['user'] = 10 + gid['user'] = 0 + uid['file'] = 10 + gid['file'] = 100 + for i in [uid, gid]: + i['expected'] = i['mapped'] + i['file'] - i['user'] + + files_path = ["%s/%s" % (root_path, filename) for filename in files] + expected_calls = [mock.call(path, uid['expected'], gid['expected']) + for path in [root_path] + files_path] + + m_realpath.return_value = root_path + with mock.patch.multiple('os', + geteuid=mock.DEFAULT, + getegid=mock.DEFAULT, + lchown=mock.DEFAULT, + lstat=mock.DEFAULT, + walk=mock.DEFAULT) as mocked: + mocked['geteuid'].return_value = uid['user'] + mocked['getegid'].return_value = gid['user'] + mocked['walk'].return_value = [(root_path, [], files)] + mocked['lstat']().st_uid = uid['file'] + mocked['lstat']().st_gid = gid['file'] + + virt_bootstrap.map_id(root_path, uid['mapped'], gid['mapped']) + + mocked['lchown'].assert_has_calls(expected_calls) + + ################################### + # Tests for: bootstrap() + ################################### + def test_bootsrap_creates_directory_if_does_not_exist(self): + """ + Ensures that bootstrap() creates destination directory if + it does not exists. + """ + src, dest = 'foo', 'bar' + with mock.patch.multiple(virt_bootstrap, + get_source=mock.DEFAULT, + os=mock.DEFAULT) as mocked: + mocked['os'].path.exists.return_value = False + virt_bootstrap.bootstrap(src, dest) + mocked['os'].path.exists.assert_called_once_with(dest) + mocked['os'].makedirs.assert_called_once_with(dest) + + def test_bootstrap_exit_if_dest_is_invalid(self): + """ + Ensures that bootstrap() exits with code 1 when the destination + path exists but it is not directory. + """ + src, dest = 'foo', 'bar' + with mock.patch.multiple(virt_bootstrap, + get_source=mock.DEFAULT, + os=mock.DEFAULT, + logger=mock.DEFAULT, + sys=mock.DEFAULT) as mocked: + mocked['os'].path.exists.return_value = True + mocked['os'].path.isdir.return_value = False + virt_bootstrap.bootstrap(src, dest) + mocked['os'].path.isdir.assert_called_once_with(dest) + mocked['sys'].exit.assert_called_once_with(1) + + def test_bootsrap_exit_if_no_write_access_on_dest(self): + """ + Ensures that bootstrap() exits with code 1 when the current user + has not write permissions on the destination folder. + """ + src, dest = 'foo', 'bar' + with mock.patch.multiple(virt_bootstrap, + get_source=mock.DEFAULT, + os=mock.DEFAULT, + logger=mock.DEFAULT, + sys=mock.DEFAULT) as mocked: + mocked['os'].path.exists.return_value = True + mocked['os'].path.isdir.return_value = True + mocked['os'].access.return_value = False + virt_bootstrap.bootstrap(src, dest) + mocked['os'].access.assert_called_once_with(dest, + mocked['os'].W_OK) + mocked['sys'].exit.assert_called_once_with(1) + + def test_bootstrap_use_file_source_if_none_was_specified(self): + """ + Ensures that bootstrap() calls get_source() with argument + 'file' when source format is not specified. + """ + src, dest = 'foo', 'bar' + with mock.patch.multiple(virt_bootstrap, + get_source=mock.DEFAULT, + os=mock.DEFAULT, + sys=mock.DEFAULT) as mocked: + virt_bootstrap.bootstrap(src, dest) + mocked['get_source'].assert_called_once_with('file') + + def test_bootstrap_successful_call(self): + """ + Ensures that bootstrap() creates source instance and calls the + unpack method with destination path as argument. + """ + src, dest = 'foo', 'bar' + with mock.patch.multiple(virt_bootstrap, + get_source=mock.DEFAULT, + os=mock.DEFAULT, + sys=mock.DEFAULT) as mocked: + mocked['os'].path.exists.return_value = True + mocked['os'].path.isdir.return_value = True + mocked['os'].access.return_value = True + mocked_source = mock.Mock() + mocked_unpack = mock.Mock() + mocked_source.return_value.unpack = mocked_unpack + mocked['get_source'].return_value = mocked_source + virt_bootstrap.bootstrap(src, dest) + # sys.exit should not be called + mocked['sys'].exit.assert_not_called() + mocked_source.assert_called_once() + mocked_unpack.assert_called_once_with(dest) + + def test_bootstrap_all_params_are_passed_to_source_instance(self): + """ + Ensures that bootstrap() is passing all arguments to newly created + source instance. + """ + params_list = ['dest', 'fmt', 'username', 'password', 'root_password', + 'not_secure', 'no_cache', 'progress_cb'] + params = {param: param for param in params_list} + + for kw_param in params_list: + params[kw_param] = kw_param + + with mock.patch.multiple(virt_bootstrap, + get_source=mock.DEFAULT, + os=mock.DEFAULT, + urlparse=mock.DEFAULT, + progress=mock.DEFAULT, + utils=mock.DEFAULT, + sys=mock.DEFAULT) as mocked: + mocked['os'].path.exists.return_value = True + mocked['os'].path.isdir.return_value = True + mocked['os'].access.return_value = True + + mocked['progress'].Progress.return_value = params['progress_cb'] + + mocked_source = mock.Mock() + mocked_unpack = mock.Mock() + mocked_source.return_value.unpack = mocked_unpack + mocked['get_source'].return_value = mocked_source + + mocked_uri = mock.Mock() + mocked['urlparse'].return_value = mocked_uri + params['uri'] = mocked_uri + + virt_bootstrap.bootstrap(**params) + # sys.exit should not be called + mocked['sys'].exit.assert_not_called() + + mocked_source.assert_called_once() + mocked_unpack.assert_called_once_with(params['dest']) + + called_with_args, called_with_kwargs = mocked_source.call_args + self.assertEqual(called_with_args, ()) + + del params['dest'] + del params['root_password'] + params['progress'] = params.pop('progress_cb') + for kwarg in params: + self.assertEqual(called_with_kwargs[kwarg], params[kwarg]) + + def test_if_bootstrap_calls_set_root_password(self): + """ + Ensures that bootstrap() calls set_root_password() when the argument + root_password is specified. + """ + src, dest, root_password = 'foo', 'bar', 'root_password' + with mock.patch.multiple(virt_bootstrap, + get_source=mock.DEFAULT, + os=mock.DEFAULT, + utils=mock.DEFAULT, + sys=mock.DEFAULT) as mocked: + mocked['os'].path.exists.return_value = True + mocked['os'].path.isdir.return_value = True + mocked['os'].access.return_value = True + + virt_bootstrap.bootstrap(src, dest, root_password=root_password) + mocked['utils'].set_root_password.assert_called_once_with( + dest, root_password) + + ################################### + # Tests for: set_logging_conf() + ################################### + def test_if_logging_level_format_handler_are_set(self): + """ + Ensures that set_logging_conf() sets log level and adds new stream + handler with formatting. + """ + with mock.patch('virtBootstrap.virt_bootstrap.logging') as m_logging: + mocked_stream_hdlr = mock.Mock() + m_logger = mock.Mock() + m_logging.getLogger.return_value = m_logger + m_logging.StreamHandler.return_value = mocked_stream_hdlr + virt_bootstrap.set_logging_conf() + m_logging.getLogger.assert_called_once_with('virtBootstrap') + mocked_stream_hdlr.setFormatter.assert_called_once() + m_logger.addHandler.assert_called_once_with(mocked_stream_hdlr) + m_logger.setLevel.assert_called_once() + + +if __name__ == '__main__': + unittest.main(exit=False) -- 2.9.4 _______________________________________________ virt-tools-list mailing list virt-tools-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/virt-tools-list