aodh/ceilometer/tests/base.py
ZhiQiang Fan 59e647f82c Avoid reading real config files in unit test
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
2014-06-18 12:17:23 +08:00

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)