305 lines
12 KiB
Python
305 lines
12 KiB
Python
import unittest
|
|
from mock import call, patch
|
|
import yaml
|
|
|
|
import tools.charm_helpers_sync.charm_helpers_sync as sync
|
|
|
|
import six
|
|
if not six.PY3:
|
|
builtin_open = '__builtin__.open'
|
|
else:
|
|
builtin_open = 'builtins.open'
|
|
|
|
|
|
INCLUDE = """
|
|
include:
|
|
- core
|
|
- contrib.openstack
|
|
- contrib.storage
|
|
- contrib.hahelpers:
|
|
- utils
|
|
- ceph_utils
|
|
- cluster_utils
|
|
- haproxy_utils
|
|
"""
|
|
|
|
|
|
class HelperSyncTests(unittest.TestCase):
|
|
def test_clone_helpers(self):
|
|
'''It properly branches the correct helpers branch'''
|
|
with patch('subprocess.check_call') as check_call:
|
|
sync.clone_helpers(work_dir='/tmp/foo', repo='git:charm-helpers')
|
|
check_call.assert_called_with(['git',
|
|
'clone', '--depth=1',
|
|
'git:charm-helpers',
|
|
'/tmp/foo/charm-helpers'])
|
|
|
|
def test_module_path(self):
|
|
'''It converts a python module path to a filesystem path'''
|
|
self.assertEquals(sync._module_path('some.test.module'),
|
|
'some/test/module')
|
|
|
|
def test_src_path(self):
|
|
'''It renders the correct path to module within charm-helpers tree'''
|
|
path = sync._src_path(src='/tmp/charm-helpers',
|
|
module='contrib.openstack')
|
|
self.assertEquals('/tmp/charm-helpers/charmhelpers/contrib/openstack',
|
|
path)
|
|
|
|
def test_dest_path(self):
|
|
'''It correctly finds the correct install path within a charm'''
|
|
path = sync._dest_path(dest='/tmp/mycharm/hooks/charmhelpers',
|
|
module='contrib.openstack')
|
|
self.assertEquals('/tmp/mycharm/hooks/charmhelpers/contrib/openstack',
|
|
path)
|
|
|
|
@patch(builtin_open)
|
|
@patch('os.path.exists')
|
|
@patch('os.walk')
|
|
def test_ensure_init(self, walk, exists, _open):
|
|
'''It ensures all subdirectories of a parent are python importable'''
|
|
# os walk
|
|
# os.path.join
|
|
# os.path.exists
|
|
# open
|
|
def _walk(path):
|
|
yield ('/tmp/hooks/', ['helpers'], [])
|
|
yield ('/tmp/hooks/helpers', ['foo'], [])
|
|
yield ('/tmp/hooks/helpers/foo', [], [])
|
|
walk.side_effect = _walk
|
|
exists.return_value = False
|
|
sync.ensure_init('hooks/helpers/foo/')
|
|
ex = [call('/tmp/hooks/__init__.py', 'wb'),
|
|
call('/tmp/hooks/helpers/__init__.py', 'wb'),
|
|
call('/tmp/hooks/helpers/foo/__init__.py', 'wb')]
|
|
for c in ex:
|
|
self.assertIn(c, _open.call_args_list)
|
|
|
|
@patch('tools.charm_helpers_sync.charm_helpers_sync.ensure_init')
|
|
@patch('os.path.isfile')
|
|
@patch('shutil.copy')
|
|
@patch('os.makedirs')
|
|
@patch('os.path.exists')
|
|
def test_sync_pyfile(self, exists, mkdirs, copy, isfile, ensure_init):
|
|
'''It correctly syncs a py src file from src to dest'''
|
|
exists.return_value = False
|
|
isfile.return_value = True
|
|
sync.sync_pyfile('/tmp/charm-helpers/core/host',
|
|
'hooks/charmhelpers/core')
|
|
mkdirs.assert_called_with('hooks/charmhelpers/core')
|
|
copy_f = call('/tmp/charm-helpers/core/host.py',
|
|
'hooks/charmhelpers/core')
|
|
copy_i = call('/tmp/charm-helpers/core/__init__.py',
|
|
'hooks/charmhelpers/core')
|
|
self.assertIn(copy_f, copy.call_args_list)
|
|
self.assertIn(copy_i, copy.call_args_list)
|
|
ensure_init.assert_called_with('hooks/charmhelpers/core')
|
|
|
|
def _test_filter_dir(self, opts, isfile, isdir):
|
|
'''It filters non-python files and non-module dirs from source'''
|
|
files = {
|
|
'bad_file.bin': 'f',
|
|
'some_dir': 'd',
|
|
'good_helper.py': 'f',
|
|
'good_helper2.py': 'f',
|
|
'good_helper3.py': 'f',
|
|
'bad_file.img': 'f',
|
|
}
|
|
|
|
def _isfile(f):
|
|
try:
|
|
return files[f.split('/').pop()] == 'f'
|
|
except KeyError:
|
|
return False
|
|
|
|
def _isdir(f):
|
|
try:
|
|
return files[f.split('/').pop()] == 'd'
|
|
except KeyError:
|
|
return False
|
|
|
|
isfile.side_effect = _isfile
|
|
isdir.side_effect = _isdir
|
|
result = sync.get_filter(opts)(dir='/tmp/charm-helpers/core',
|
|
ls=six.iterkeys(files))
|
|
return result
|
|
|
|
@patch('os.path.isdir')
|
|
@patch('os.path.isfile')
|
|
def test_filter_dir_no_opts(self, isfile, isdir):
|
|
'''It filters out all non-py files by default'''
|
|
result = self._test_filter_dir(opts=None, isfile=isfile, isdir=isdir)
|
|
ex = ['bad_file.bin', 'bad_file.img', 'some_dir']
|
|
self.assertEquals(sorted(ex), sorted(result))
|
|
|
|
@patch('os.path.isdir')
|
|
@patch('os.path.isfile')
|
|
def test_filter_dir_with_include(self, isfile, isdir):
|
|
'''It includes non-py files if specified as an include opt'''
|
|
result = sorted(self._test_filter_dir(opts=['inc=*.img'],
|
|
isfile=isfile, isdir=isdir))
|
|
ex = sorted(['bad_file.bin', 'some_dir'])
|
|
self.assertEquals(ex, result)
|
|
|
|
@patch('os.path.isdir')
|
|
@patch('os.path.isfile')
|
|
def test_filter_dir_include_all(self, isfile, isdir):
|
|
'''It does not filter anything if option specified to include all'''
|
|
self.assertEquals(sync.get_filter(opts=['inc=*']), None)
|
|
|
|
@patch('tools.charm_helpers_sync.charm_helpers_sync.get_filter')
|
|
@patch('tools.charm_helpers_sync.charm_helpers_sync.ensure_init')
|
|
@patch('shutil.copytree')
|
|
@patch('shutil.rmtree')
|
|
@patch('os.path.exists')
|
|
def test_sync_directory(self, exists, rmtree, copytree, ensure_init,
|
|
_filter):
|
|
'''It correctly syncs src directory to dest directory'''
|
|
_filter.return_value = None
|
|
sync.sync_directory('/tmp/charm-helpers/charmhelpers/core',
|
|
'hooks/charmhelpers/core')
|
|
exists.return_value = True
|
|
rmtree.assert_called_with('hooks/charmhelpers/core')
|
|
copytree.assert_called_with('/tmp/charm-helpers/charmhelpers/core',
|
|
'hooks/charmhelpers/core', ignore=None)
|
|
ensure_init.assert_called_with('hooks/charmhelpers/core')
|
|
|
|
@patch('os.path.isfile')
|
|
def test_is_pyfile(self, isfile):
|
|
'''It correctly identifies incomplete path to a py src file as such'''
|
|
sync._is_pyfile('/tmp/charm-helpers/charmhelpers/core/host')
|
|
isfile.assert_called_with(
|
|
'/tmp/charm-helpers/charmhelpers/core/host.py'
|
|
)
|
|
|
|
@patch('tools.charm_helpers_sync.charm_helpers_sync.sync_pyfile')
|
|
@patch('tools.charm_helpers_sync.charm_helpers_sync.sync_directory')
|
|
@patch('os.path.isdir')
|
|
def test_syncs_directory(self, is_dir, sync_dir, sync_pyfile):
|
|
'''It correctly syncs a module directory'''
|
|
is_dir.return_value = True
|
|
sync.sync(src='/tmp/charm-helpers',
|
|
dest='hooks/charmhelpers',
|
|
module='contrib.openstack')
|
|
|
|
sync_dir.assert_called_with(
|
|
'/tmp/charm-helpers/charmhelpers/contrib/openstack',
|
|
'hooks/charmhelpers/contrib/openstack', None)
|
|
|
|
# __init__.py files leading to the directory were also synced.
|
|
sync_pyfile.assert_has_calls([
|
|
call('/tmp/charm-helpers/charmhelpers/__init__',
|
|
'hooks/charmhelpers'),
|
|
call('/tmp/charm-helpers/charmhelpers/contrib/__init__',
|
|
'hooks/charmhelpers/contrib')])
|
|
|
|
@patch('tools.charm_helpers_sync.charm_helpers_sync.sync_pyfile')
|
|
@patch('tools.charm_helpers_sync.charm_helpers_sync._is_pyfile')
|
|
@patch('os.path.isdir')
|
|
def test_syncs_file(self, is_dir, is_pyfile, sync_pyfile):
|
|
'''It correctly syncs a module file'''
|
|
is_dir.return_value = False
|
|
is_pyfile.return_value = True
|
|
sync.sync(src='/tmp/charm-helpers',
|
|
dest='hooks/charmhelpers',
|
|
module='contrib.openstack.utils')
|
|
sync_pyfile.assert_has_calls([
|
|
call('/tmp/charm-helpers/charmhelpers/__init__',
|
|
'hooks/charmhelpers'),
|
|
call('/tmp/charm-helpers/charmhelpers/contrib/__init__',
|
|
'hooks/charmhelpers/contrib'),
|
|
call('/tmp/charm-helpers/charmhelpers/contrib/openstack/__init__',
|
|
'hooks/charmhelpers/contrib/openstack'),
|
|
call('/tmp/charm-helpers/charmhelpers/contrib/openstack/utils',
|
|
'hooks/charmhelpers/contrib/openstack')])
|
|
|
|
@patch('tools.charm_helpers_sync.charm_helpers_sync.sync')
|
|
@patch('os.path.isdir')
|
|
@patch('os.path.exists')
|
|
def test_sync_helpers_from_config(self, exists, isdir, _sync):
|
|
'''It correctly syncs a list of included helpers'''
|
|
include = yaml.safe_load(INCLUDE)['include']
|
|
isdir.return_value = True
|
|
exists.return_value = False
|
|
sync.sync_helpers(include=include,
|
|
src='/tmp/charm-helpers',
|
|
|
|
dest='hooks/charmhelpers')
|
|
mods = [
|
|
'core',
|
|
'contrib.openstack',
|
|
'contrib.storage',
|
|
'contrib.hahelpers.utils',
|
|
'contrib.hahelpers.ceph_utils',
|
|
'contrib.hahelpers.cluster_utils',
|
|
'contrib.hahelpers.haproxy_utils'
|
|
]
|
|
|
|
ex_calls = []
|
|
[ex_calls.append(
|
|
call('/tmp/charm-helpers', 'hooks/charmhelpers', c, [])
|
|
) for c in mods]
|
|
self.assertEquals(ex_calls, _sync.call_args_list)
|
|
|
|
@patch('tools.charm_helpers_sync.charm_helpers_sync.sync')
|
|
@patch('os.path.isdir')
|
|
@patch('os.path.exists')
|
|
@patch('shutil.rmtree')
|
|
def test_sync_helpers_from_config_cleanup(self, _rmtree, _exists,
|
|
isdir, _sync):
|
|
'''It correctly syncs a list of included helpers'''
|
|
include = yaml.safe_load(INCLUDE)['include']
|
|
isdir.return_value = True
|
|
_exists.return_value = True
|
|
|
|
sync.sync_helpers(include=include,
|
|
src='/tmp/charm-helpers',
|
|
|
|
dest='hooks/charmhelpers')
|
|
_rmtree.assert_called_with('hooks/charmhelpers')
|
|
mods = [
|
|
'core',
|
|
'contrib.openstack',
|
|
'contrib.storage',
|
|
'contrib.hahelpers.utils',
|
|
'contrib.hahelpers.ceph_utils',
|
|
'contrib.hahelpers.cluster_utils',
|
|
'contrib.hahelpers.haproxy_utils'
|
|
]
|
|
|
|
ex_calls = []
|
|
[ex_calls.append(
|
|
call('/tmp/charm-helpers', 'hooks/charmhelpers', c, [])
|
|
) for c in mods]
|
|
self.assertEquals(ex_calls, _sync.call_args_list)
|
|
|
|
def test_extract_option_no_globals(self):
|
|
'''It extracts option from an included item with no global options'''
|
|
inc = 'contrib.openstack.templates|inc=*.template'
|
|
result = sync.extract_options(inc)
|
|
ex = ('contrib.openstack.templates', ['inc=*.template'])
|
|
self.assertEquals(ex, result)
|
|
|
|
def test_extract_option_with_global_as_string(self):
|
|
'''It extracts option for include with global options as str'''
|
|
inc = 'contrib.openstack.templates|inc=*.template'
|
|
result = sync.extract_options(inc, global_options='inc=foo.*')
|
|
ex = ('contrib.openstack.templates',
|
|
['inc=*.template', 'inc=foo.*'])
|
|
self.assertEquals(ex, result)
|
|
|
|
def test_extract_option_with_globals(self):
|
|
'''It extracts option from an included item with global options'''
|
|
inc = 'contrib.openstack.templates|inc=*.template'
|
|
result = sync.extract_options(inc, global_options=['inc=*.cfg'])
|
|
ex = ('contrib.openstack.templates', ['inc=*.template', 'inc=*.cfg'])
|
|
self.assertEquals(ex, result)
|
|
|
|
def test_extract_multiple_options_with_globals(self):
|
|
'''It extracts multiple options from an included item'''
|
|
inc = 'contrib.openstack.templates|inc=*.template,inc=foo.*'
|
|
result = sync.extract_options(inc, global_options=['inc=*.cfg'])
|
|
ex = ('contrib.openstack.templates',
|
|
['inc=*.template', 'inc=foo.*', 'inc=*.cfg'])
|
|
self.assertEquals(ex, result)
|