When we call oslo.config.cfg.CONF(), it will do basic setup and
search configuration files under certain directories, by default,
they are [/etc, /etc/ceilometer, ~/, ~/.ceilometer]. However, we
should avoid such behaviour when we run unit test code, because that
if the system has run devstack or installed ceilometer services,
then /etc/ceilometer/ceilometer.conf will exist, then unit test result
can be different.
So we should mock the default searching directories of cfg in the
begining of all unit tests, which will call cfg.CONF() at somewhere
during the test. The best place to mock the default directories
can be ceilometer.test.base.BaseTestCase, which should be used as
parent class of all other test classes.
Note: since this patch sets default searching directory to
{repo}/etc/ceilometer, you can run the unit test with different settings
which are set in etc/ceilometer/ceilometer.conf (not tracked by git).
Note: mock.patch.stopall has compatibility problem with oslo.fixture,
so this patch slightly refactors the
tests.compute.pollsters.base.TestPollsterBase.setUp method.
Change-Id: I533ffb2ba2c9be0223cecbcf04176312e4a96369
Closes-Bug: #1328550
123 lines
4.1 KiB
Python
123 lines
4.1 KiB
Python
#!/usr/bin/env python
|
|
#
|
|
# Copyright 2012 New Dream Network (DreamHost)
|
|
#
|
|
# Author: Doug Hellmann <doug.hellmann@dreamhost.com>
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
"""Test base classes.
|
|
"""
|
|
import functools
|
|
import os.path
|
|
import six
|
|
|
|
import eventlet
|
|
import oslo.messaging
|
|
from testtools import testcase
|
|
|
|
from ceilometer import messaging
|
|
from ceilometer.openstack.common.fixture import mockpatch
|
|
from ceilometer.openstack.common import test
|
|
from ceilometer.openstack.common import timeutils
|
|
|
|
|
|
class BaseTestCase(test.BaseTestCase):
|
|
def setup_messaging(self, conf, exchange=None):
|
|
self.useFixture(oslo.messaging.conffixture.ConfFixture(conf))
|
|
conf.set_override("notification_driver", "messaging")
|
|
if not exchange:
|
|
exchange = 'ceilometer'
|
|
conf.set_override("control_exchange", exchange)
|
|
|
|
# NOTE(sileht): oslo.messaging fake driver uses time.sleep
|
|
# for task switch, so we need to monkey_patch it
|
|
# and also ensure the correct exchange have been set
|
|
eventlet.monkey_patch(time=True)
|
|
|
|
# NOTE(sileht): Ensure a new oslo.messaging driver is loaded
|
|
# between each tests
|
|
self.transport = messaging.get_transport("fake://", cache=False)
|
|
self.useFixture(mockpatch.Patch(
|
|
'ceilometer.messaging.get_transport',
|
|
return_value=self.transport))
|
|
|
|
def setUp(self):
|
|
super(BaseTestCase, self).setUp()
|
|
self.useFixture(mockpatch.Patch(
|
|
'oslo.config.cfg._get_config_dirs',
|
|
return_value=[self.path_get('etc/ceilometer')]))
|
|
|
|
def assertTimestampEqual(self, first, second, msg=None):
|
|
"""Checks that two timestamps are equals.
|
|
|
|
This relies on assertAlmostEqual to avoid rounding problem, and only
|
|
checks up the first microsecond values.
|
|
|
|
"""
|
|
return self.assertAlmostEqual(
|
|
timeutils.delta_seconds(first, second),
|
|
0.0,
|
|
places=5)
|
|
|
|
def assertIsEmpty(self, obj):
|
|
try:
|
|
if len(obj) != 0:
|
|
self.fail("%s is not empty" % type(obj))
|
|
except (TypeError, AttributeError):
|
|
self.fail("%s doesn't have length" % type(obj))
|
|
|
|
def assertIsNotEmpty(self, obj):
|
|
try:
|
|
if len(obj) == 0:
|
|
self.fail("%s is empty" % type(obj))
|
|
except (TypeError, AttributeError):
|
|
self.fail("%s doesn't have length" % type(obj))
|
|
|
|
@staticmethod
|
|
def path_get(project_file=None):
|
|
root = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
|
'..',
|
|
'..',
|
|
)
|
|
)
|
|
if project_file:
|
|
return os.path.join(root, project_file)
|
|
else:
|
|
return root
|
|
|
|
|
|
def _skip_decorator(func):
|
|
@functools.wraps(func)
|
|
def skip_if_not_implemented(*args, **kwargs):
|
|
try:
|
|
return func(*args, **kwargs)
|
|
except AssertionError:
|
|
raise
|
|
except NotImplementedError as e:
|
|
raise testcase.TestSkipped(six.text_type(e))
|
|
except Exception as e:
|
|
if 'not implemented' in six.text_type(e):
|
|
raise testcase.TestSkipped(six.text_type(e))
|
|
raise
|
|
return skip_if_not_implemented
|
|
|
|
|
|
class SkipNotImplementedMeta(type):
|
|
def __new__(cls, name, bases, local):
|
|
for attr in local:
|
|
value = local[attr]
|
|
if callable(value) and (
|
|
attr.startswith('test_') or attr == 'setUp'):
|
|
local[attr] = _skip_decorator(value)
|
|
return type.__new__(cls, name, bases, local)
|