Add unit tests

This commit is contained in:
James Page 2013-10-20 15:28:18 -07:00
parent 502cd9fd15
commit 983929d169
15 changed files with 277 additions and 59 deletions

6
.coveragerc Normal file
View File

@ -0,0 +1,6 @@
[report]
# Regexes for lines to exclude from consideration
exclude_lines =
if __name__ == .__main__.:
include=
hooks/ceilometer_*

17
.project Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>ceilometer-agent</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>

9
.pydevproject Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/ceilometer-agent/hooks</path>
<path>/ceilometer-agent/unit_tests</path>
</pydev_pathproperty>
</pydev_project>

View File

@ -1,10 +1,7 @@
import os
import uuid
from charmhelpers.core.hookenv import ( from charmhelpers.core.hookenv import (
relation_ids, relation_ids,
relation_get, relation_get,
related_units, related_units,
config
) )
from charmhelpers.contrib.openstack.context import ( from charmhelpers.contrib.openstack.context import (
@ -12,55 +9,6 @@ from charmhelpers.contrib.openstack.context import (
context_complete context_complete
) )
CEILOMETER_DB = 'ceilometer'
class LoggingConfigContext(OSContextGenerator):
def __call__(self):
return {'debug': config('debug'), 'verbose': config('verbose')}
class MongoDBContext(OSContextGenerator):
interfaces = ['mongodb']
def __call__(self):
for relid in relation_ids('shared-db'):
for unit in related_units(relid):
conf = {
"db_host": relation_get('hostname', unit, relid),
"db_port": relation_get('port', unit, relid),
"db_name": CEILOMETER_DB
}
if context_complete(conf):
return conf
return {}
SHARED_SECRET = "/etc/ceilometer/secret.txt"
def get_shared_secret():
secret = None
if not os.path.exists(SHARED_SECRET):
secret = str(uuid.uuid4())
with open(SHARED_SECRET, 'w') as secret_file:
secret_file.write(secret)
else:
with open(SHARED_SECRET, 'r') as secret_file:
secret = secret_file.read().strip()
return secret
CEILOMETER_PORT = 8777
class CeilometerContext(OSContextGenerator):
def __call__(self):
ctxt = {
'port': CEILOMETER_PORT,
'metering_secret': get_shared_secret()
}
return ctxt
class CeilometerServiceContext(OSContextGenerator): class CeilometerServiceContext(OSContextGenerator):
interfaces = ['ceilometer-service'] interfaces = ['ceilometer-service']

View File

@ -7,9 +7,6 @@ from charmhelpers.fetch import (
) )
from charmhelpers.core.hookenv import ( from charmhelpers.core.hookenv import (
config, config,
relation_ids,
related_units,
relation_get,
Hooks, UnregisteredHookError, Hooks, UnregisteredHookError,
log log
) )
@ -37,9 +34,9 @@ def install():
filter_installed_packages(CEILOMETER_AGENT_PACKAGES), filter_installed_packages(CEILOMETER_AGENT_PACKAGES),
fatal=True) fatal=True)
# TODO(jamespage): Locally scoped relation for nova and others # TODO(jamespage): Locally scoped relation for nova and others
#ceilometer_utils.modify_config_file(ceilometer_utils.NOVA_CONF, #ceilometer_utils.modify_config_file(ceilometer_utils.NOVA_CONF
# ceilometer_utils.NOVA_SETTINGS) # ceilometer_utils.NOVA_SETTINGS)
@hooks.hook("ceilometer-service-relation-changed", @hooks.hook("ceilometer-service-relation-changed",

View File

@ -44,7 +44,7 @@ def register_configs():
# just default to earliest supported release. configs dont get touched # just default to earliest supported release. configs dont get touched
# till post-install, anyway. # till post-install, anyway.
release = get_os_codename_package('ceilometer-common', fatal=False) \ release = get_os_codename_package('ceilometer-common', fatal=False) \
or 'grizzly' or 'grizzly'
configs = templating.OSConfigRenderer(templates_dir=TEMPLATES, configs = templating.OSConfigRenderer(templates_dir=TEMPLATES,
openstack_release=release) openstack_release=release)

1
hooks/start Symbolic link
View File

@ -0,0 +1 @@
ceilometer_hooks.py

1
hooks/stop Symbolic link
View File

@ -0,0 +1 @@
ceilometer_hooks.py

View File

@ -11,6 +11,9 @@ description: |
. .
This charm should be used in conjunction with the ceilometer and nova charm to collect This charm should be used in conjunction with the ceilometer and nova charm to collect
Openstack measures. Openstack measures.
categories:
- miscellanous
- openstack
requires: requires:
container: container:
interface: juju-info interface: juju-info

6
setup.cfg Normal file
View File

@ -0,0 +1,6 @@
[nosetests]
verbosity=1
with-coverage=1
cover-erase=1
cover-package=hooks

2
unit_tests/__init__.py Normal file
View File

@ -0,0 +1,2 @@
import sys
sys.path.append('hooks')

View File

@ -0,0 +1,35 @@
from mock import patch
import ceilometer_contexts as contexts
from test_utils import CharmTestCase
TO_PATCH = [
'relation_get',
'relation_ids',
'related_units',
]
class CeilometerContextsTest(CharmTestCase):
def setUp(self):
super(CeilometerContextsTest, self).setUp(contexts, TO_PATCH)
self.relation_get.side_effect = self.test_relation.get
def tearDown(self):
super(CeilometerContextsTest, self).tearDown()
def test_ceilometer_service_context(self):
self.relation_ids.return_value = ['ceilometer-service:0']
self.related_units.return_value = ['ceilometer/0']
data = {
'metering_secret': 'mysecret',
'keystone_host': 'test'
}
self.test_relation.set(data)
self.assertEquals(contexts.CeilometerServiceContext()(), data)
def test_ceilometer_service_context_not_related(self):
self.relation_ids.return_value = []
self.assertEquals(contexts.CeilometerServiceContext()(), {})

View File

@ -0,0 +1,48 @@
from mock import patch, MagicMock
import ceilometer_utils
# Patch out register_configs for import of hooks
_register_configs = ceilometer_utils.register_configs
ceilometer_utils.register_configs = MagicMock()
import ceilometer_hooks as hooks
# Renable old function
ceilometer_utils.register_configs = _register_configs
from test_utils import CharmTestCase
TO_PATCH = [
'configure_installation_source',
'apt_install',
'apt_update',
'config',
'filter_installed_packages',
'CONFIGS',
]
class CeilometerHooksTest(CharmTestCase):
def setUp(self):
super(CeilometerHooksTest, self).setUp(hooks, TO_PATCH)
self.config.side_effect = self.test_config.get
def test_configure_source(self):
self.test_config.set('openstack-origin', 'cloud:precise-havana')
hooks.hooks.execute(['hooks/install'])
self.configure_installation_source.\
assert_called_with('cloud:precise-havana')
def test_install_hook(self):
self.filter_installed_packages.return_value = \
hooks.CEILOMETER_AGENT_PACKAGES
hooks.hooks.execute(['hooks/install'])
self.assertTrue(self.configure_installation_source.called)
self.apt_update.assert_called_with(fatal=True)
self.apt_install.assert_called_with(hooks.CEILOMETER_AGENT_PACKAGES,
fatal=True)
def test_ceilometer_changed(self):
hooks.hooks.execute(['hooks/ceilometer-service-relation-changed'])
self.assertTrue(self.CONFIGS.write_all.called)

View File

@ -0,0 +1,34 @@
from mock import patch, call
import ceilometer_utils as utils
from test_utils import CharmTestCase
TO_PATCH = [
'get_os_codename_package',
'templating',
'CeilometerServiceContext',
]
class CeilometerUtilsTest(CharmTestCase):
def setUp(self):
super(CeilometerUtilsTest, self).setUp(utils, TO_PATCH)
def tearDown(self):
super(CeilometerUtilsTest, self).tearDown()
def test_register_configs(self):
configs = utils.register_configs()
calls = []
for conf in utils.CONFIG_FILES:
calls.append(call(conf,
utils.CONFIG_FILES[conf]['hook_contexts']))
configs.register.assert_has_calls(calls, any_order=True)
def test_restart_map(self):
restart_map = utils.restart_map()
self.assertEquals(restart_map,
{'/etc/ceilometer/ceilometer.conf': [
'ceilometer-agent-compute']})

111
unit_tests/test_utils.py Normal file
View File

@ -0,0 +1,111 @@
import logging
import unittest
import os
import yaml
import io
from contextlib import contextmanager
from mock import patch
@contextmanager
def mock_open(filename, contents=None):
''' Slightly simpler mock of open to return contents for filename '''
def mock_file(*args):
if args[0] == filename:
return io.StringIO(contents)
else:
return open(*args)
with patch('__builtin__.open', mock_file):
yield
def load_config():
'''
Walk backwords from __file__ looking for config.yaml, load and return the
'options' section'
'''
config = None
f = __file__
while config is None:
d = os.path.dirname(f)
if os.path.isfile(os.path.join(d, 'config.yaml')):
config = os.path.join(d, 'config.yaml')
break
f = d
if not config:
logging.error('Could not find config.yaml in any parent directory '
'of %s. ' % file)
raise Exception
return yaml.safe_load(open(config).read())['options']
def get_default_config():
'''
Load default charm config from config.yaml return as a dict.
If no default is set in config.yaml, its value is None.
'''
default_config = {}
config = load_config()
for k, v in config.iteritems():
if 'default' in v:
default_config[k] = v['default']
else:
default_config[k] = None
return default_config
class CharmTestCase(unittest.TestCase):
def setUp(self, obj, patches):
super(CharmTestCase, self).setUp()
self.patches = patches
self.obj = obj
self.test_config = TestConfig()
self.test_relation = TestRelation()
self.patch_all()
def patch(self, method):
_m = patch.object(self.obj, method)
mock = _m.start()
self.addCleanup(_m.stop)
return mock
def patch_all(self):
for method in self.patches:
setattr(self, method, self.patch(method))
class TestConfig(object):
def __init__(self):
self.config = get_default_config()
def get(self, attr):
try:
return self.config[attr]
except KeyError:
return None
def get_all(self):
return self.config
def set(self, attr, value):
if attr not in self.config:
raise KeyError
self.config[attr] = value
class TestRelation(object):
def __init__(self, relation_data={}):
self.relation_data = relation_data
def set(self, relation_data):
self.relation_data = relation_data
def get(self, attr=None, unit=None, rid=None):
if attr is None:
return self.relation_data
elif attr in self.relation_data:
return self.relation_data[attr]
return None