... and use the built-in ones instead. Also replace assertDictEqual because assertEqual should select an appropriate method automatically[1]. [1] https://docs.python.org/3.13/library/unittest.html The list of type-specific methods automatically used by assertEqual() are summarized in the following table. Note that it’s usually not necessary to invoke these methods directly. Change-Id: I4b7d3563e5a025cd3efa602a28c12865dcf3474d Signed-off-by: Takashi Kajinami <kajinamit@oss.nttdata.com>
207 lines
6.7 KiB
Python
207 lines
6.7 KiB
Python
# Copyright (C) 2012 Yahoo! Inc. All Rights Reserved.
|
|
#
|
|
# 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.
|
|
|
|
import collections
|
|
import logging
|
|
from unittest import mock
|
|
|
|
import fixtures
|
|
from oslotest import base
|
|
|
|
from testtools import matchers
|
|
|
|
from taskflow import exceptions
|
|
from taskflow.tests import fixtures as taskflow_fixtures
|
|
from taskflow.tests import utils
|
|
|
|
|
|
class FailureRegexpMatcher:
|
|
"""Matches if the failure was caused by the given exception and message.
|
|
|
|
This will match if a given failure contains and exception of the given
|
|
class type and if its string message matches to the given regular
|
|
expression pattern.
|
|
"""
|
|
|
|
def __init__(self, exc_class, pattern):
|
|
self.exc_class = exc_class
|
|
self.pattern = pattern
|
|
|
|
def match(self, failure):
|
|
for cause in failure:
|
|
if cause.check(self.exc_class) is not None:
|
|
return matchers.MatchesRegex(
|
|
self.pattern).match(cause.exception_str)
|
|
return matchers.Mismatch("The `%s` wasn't caused by the `%s`" %
|
|
(failure, self.exc_class))
|
|
|
|
|
|
class TestCase(base.BaseTestCase):
|
|
"""Test case base class for all taskflow unit tests."""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.useFixture(taskflow_fixtures.WarningsFixture())
|
|
|
|
def makeTmpDir(self):
|
|
t_dir = self.useFixture(fixtures.TempDir())
|
|
return t_dir.path
|
|
|
|
def assertRaisesAttrAccess(self, exc_class, obj, attr_name):
|
|
|
|
def access_func():
|
|
getattr(obj, attr_name)
|
|
|
|
self.assertRaises(exc_class, access_func)
|
|
|
|
def assertIsSuperAndSubsequence(self, super_seq, sub_seq, msg=None):
|
|
super_seq = list(super_seq)
|
|
sub_seq = list(sub_seq)
|
|
current_tail = super_seq
|
|
for sub_elem in sub_seq:
|
|
try:
|
|
super_index = current_tail.index(sub_elem)
|
|
except ValueError:
|
|
# element not found
|
|
if msg is None:
|
|
msg = ("%r is not subsequence of %r: "
|
|
"element %r not found in tail %r"
|
|
% (sub_seq, super_seq, sub_elem, current_tail))
|
|
self.fail(msg)
|
|
else:
|
|
current_tail = current_tail[super_index + 1:]
|
|
|
|
def assertFailuresRegexp(self, exc_class, pattern, callable_obj, *args,
|
|
**kwargs):
|
|
"""Asserts the callable failed with the given exception and message."""
|
|
try:
|
|
with utils.wrap_all_failures():
|
|
callable_obj(*args, **kwargs)
|
|
except exceptions.WrappedFailure as e:
|
|
self.assertThat(e, FailureRegexpMatcher(exc_class, pattern))
|
|
|
|
|
|
class MockTestCase(TestCase):
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.master_mock = mock.Mock(name='master_mock')
|
|
|
|
def patch(self, target, autospec=True, **kwargs):
|
|
"""Patch target and attach it to the master mock."""
|
|
f = self.useFixture(fixtures.MockPatch(target,
|
|
autospec=autospec, **kwargs))
|
|
mocked = f.mock
|
|
attach_as = kwargs.pop('attach_as', None)
|
|
if attach_as is not None:
|
|
self.master_mock.attach_mock(mocked, attach_as)
|
|
return mocked
|
|
|
|
def patchClass(self, module, name, autospec=True, attach_as=None):
|
|
"""Patches a modules class.
|
|
|
|
This will create a class instance mock (using the provided name to
|
|
find the class in the module) and attach a mock class the master mock
|
|
to be cleaned up on test exit.
|
|
"""
|
|
if autospec:
|
|
instance_mock = mock.Mock(spec_set=getattr(module, name))
|
|
else:
|
|
instance_mock = mock.Mock()
|
|
|
|
f = self.useFixture(fixtures.MockPatchObject(module, name,
|
|
autospec=autospec))
|
|
class_mock = f.mock
|
|
class_mock.return_value = instance_mock
|
|
|
|
if attach_as is None:
|
|
attach_class_as = name
|
|
attach_instance_as = name.lower()
|
|
else:
|
|
attach_class_as = attach_as + '_class'
|
|
attach_instance_as = attach_as
|
|
|
|
self.master_mock.attach_mock(class_mock, attach_class_as)
|
|
self.master_mock.attach_mock(instance_mock, attach_instance_as)
|
|
return class_mock, instance_mock
|
|
|
|
def resetMasterMock(self):
|
|
self.master_mock.reset_mock()
|
|
|
|
|
|
class CapturingLoggingHandler(logging.Handler):
|
|
"""A handler that saves record contents for post-test analysis."""
|
|
|
|
def __init__(self, level=logging.DEBUG):
|
|
# It seems needed to use the old style of base class calling, we
|
|
# can remove this old style when we only support py3.x
|
|
logging.Handler.__init__(self, level=level)
|
|
self._records = []
|
|
|
|
@property
|
|
def counts(self):
|
|
"""Returns a dictionary with the number of records at each level."""
|
|
self.acquire()
|
|
try:
|
|
captured = collections.defaultdict(int)
|
|
for r in self._records:
|
|
captured[r.levelno] += 1
|
|
return captured
|
|
finally:
|
|
self.release()
|
|
|
|
@property
|
|
def messages(self):
|
|
"""Returns a dictionary with list of record messages at each level."""
|
|
self.acquire()
|
|
try:
|
|
captured = collections.defaultdict(list)
|
|
for r in self._records:
|
|
captured[r.levelno].append(r.getMessage())
|
|
return captured
|
|
finally:
|
|
self.release()
|
|
|
|
@property
|
|
def exc_infos(self):
|
|
"""Returns a list of all the record exc_info tuples captured."""
|
|
self.acquire()
|
|
try:
|
|
captured = []
|
|
for r in self._records:
|
|
if r.exc_info:
|
|
captured.append(r.exc_info)
|
|
return captured
|
|
finally:
|
|
self.release()
|
|
|
|
def emit(self, record):
|
|
self.acquire()
|
|
try:
|
|
self._records.append(record)
|
|
finally:
|
|
self.release()
|
|
|
|
def reset(self):
|
|
"""Resets *all* internally captured state."""
|
|
self.acquire()
|
|
try:
|
|
self._records = []
|
|
finally:
|
|
self.release()
|
|
|
|
def close(self):
|
|
logging.Handler.close(self)
|
|
self.reset()
|