Move the related_bug
decorator from test.py to tempest/lib
I think it's a good idea to move all utility decorators into tempest/lib/decorators.py. This patch does that for the `related_bug` decorator. Change-Id: I846d575e41f4dddfd5642b7750e988f75a717e7d
This commit is contained in:
parent
0d93900ba6
commit
c5665a6cc7
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
A new ``related_bug`` decorator has been added to
|
||||||
|
``tempest.lib.decorators``. Use it to decorate and tag a test that was
|
||||||
|
added in relation to a launchpad bug report.
|
@ -18,7 +18,6 @@ from tempest.common import fixed_network
|
|||||||
from tempest.common import waiters
|
from tempest.common import waiters
|
||||||
from tempest.lib.common.utils import data_utils
|
from tempest.lib.common.utils import data_utils
|
||||||
from tempest.lib import decorators
|
from tempest.lib import decorators
|
||||||
from tempest import test
|
|
||||||
|
|
||||||
|
|
||||||
class ServersAdminTestJSON(base.BaseV2ComputeAdminTest):
|
class ServersAdminTestJSON(base.BaseV2ComputeAdminTest):
|
||||||
@ -93,7 +92,7 @@ class ServersAdminTestJSON(base.BaseV2ComputeAdminTest):
|
|||||||
self.assertIn(self.s1_name, servers_name)
|
self.assertIn(self.s1_name, servers_name)
|
||||||
self.assertIn(self.s2_name, servers_name)
|
self.assertIn(self.s2_name, servers_name)
|
||||||
|
|
||||||
@test.related_bug('1659811')
|
@decorators.related_bug('1659811')
|
||||||
@decorators.idempotent_id('7e5d6b8f-454a-4ba1-8ae2-da857af8338b')
|
@decorators.idempotent_id('7e5d6b8f-454a-4ba1-8ae2-da857af8338b')
|
||||||
def test_list_servers_by_admin_with_specified_tenant(self):
|
def test_list_servers_by_admin_with_specified_tenant(self):
|
||||||
# In nova v2, tenant_id is ignored unless all_tenants is specified
|
# In nova v2, tenant_id is ignored unless all_tenants is specified
|
||||||
|
@ -46,7 +46,7 @@ class VolumesAdminNegativeTest(base.BaseV2ComputeAdminTest):
|
|||||||
self.server['id'], nonexistent_volume,
|
self.server['id'], nonexistent_volume,
|
||||||
volumeId=volume['id'])
|
volumeId=volume['id'])
|
||||||
|
|
||||||
@test.related_bug('1629110', status_code=400)
|
@decorators.related_bug('1629110', status_code=400)
|
||||||
@test.attr(type=['negative'])
|
@test.attr(type=['negative'])
|
||||||
@decorators.idempotent_id('7dcac15a-b107-46d3-a5f6-cb863f4e454a')
|
@decorators.idempotent_id('7dcac15a-b107-46d3-a5f6-cb863f4e454a')
|
||||||
def test_update_attached_volume_with_nonexistent_volume_in_body(self):
|
def test_update_attached_volume_with_nonexistent_volume_in_body(self):
|
||||||
|
@ -178,7 +178,7 @@ class ServersNegativeTestJSON(base.BaseV2ComputeTest):
|
|||||||
self.client.rebuild_server,
|
self.client.rebuild_server,
|
||||||
server['id'], self.image_ref)
|
server['id'], self.image_ref)
|
||||||
|
|
||||||
@test.related_bug('1660878', status_code=409)
|
@decorators.related_bug('1660878', status_code=409)
|
||||||
@test.attr(type=['negative'])
|
@test.attr(type=['negative'])
|
||||||
@decorators.idempotent_id('581a397d-5eab-486f-9cf9-1014bbd4c984')
|
@decorators.idempotent_id('581a397d-5eab-486f-9cf9-1014bbd4c984')
|
||||||
def test_reboot_deleted_server(self):
|
def test_reboot_deleted_server(self):
|
||||||
@ -219,7 +219,7 @@ class ServersNegativeTestJSON(base.BaseV2ComputeTest):
|
|||||||
name=server_name)
|
name=server_name)
|
||||||
|
|
||||||
@test.attr(type=['negative'])
|
@test.attr(type=['negative'])
|
||||||
@test.related_bug('1651064', status_code=500)
|
@decorators.related_bug('1651064', status_code=500)
|
||||||
@decorators.idempotent_id('12146ac1-d7df-4928-ad25-b1f99e5286cd')
|
@decorators.idempotent_id('12146ac1-d7df-4928-ad25-b1f99e5286cd')
|
||||||
def test_create_server_invalid_bdm_in_2nd_dict(self):
|
def test_create_server_invalid_bdm_in_2nd_dict(self):
|
||||||
volume = self.create_volume()
|
volume = self.create_volume()
|
||||||
|
@ -31,7 +31,7 @@ class AttachVolumeNegativeTest(base.BaseV2ComputeTest):
|
|||||||
raise cls.skipException(skip_msg)
|
raise cls.skipException(skip_msg)
|
||||||
|
|
||||||
@test.attr(type=['negative'])
|
@test.attr(type=['negative'])
|
||||||
@test.related_bug('1630783', status_code=500)
|
@decorators.related_bug('1630783', status_code=500)
|
||||||
@decorators.idempotent_id('a313b5cd-fbd0-49cc-94de-870e99f763c7')
|
@decorators.idempotent_id('a313b5cd-fbd0-49cc-94de-870e99f763c7')
|
||||||
def test_delete_attached_volume(self):
|
def test_delete_attached_volume(self):
|
||||||
server = self.create_test_server(wait_until='ACTIVE')
|
server = self.create_test_server(wait_until='ACTIVE')
|
||||||
|
@ -16,9 +16,12 @@ import functools
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import debtcollector.removals
|
import debtcollector.removals
|
||||||
|
from oslo_log import log as logging
|
||||||
import six
|
import six
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def skip_because(*args, **kwargs):
|
def skip_because(*args, **kwargs):
|
||||||
"""A decorator useful to skip tests hitting known bugs
|
"""A decorator useful to skip tests hitting known bugs
|
||||||
@ -45,6 +48,28 @@ def skip_because(*args, **kwargs):
|
|||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def related_bug(bug, status_code=None):
|
||||||
|
"""A decorator useful to know solutions from launchpad bug reports
|
||||||
|
|
||||||
|
@param bug: The launchpad bug number causing the test
|
||||||
|
@param status_code: The status code related to the bug report
|
||||||
|
"""
|
||||||
|
def decorator(f):
|
||||||
|
@functools.wraps(f)
|
||||||
|
def wrapper(self, *func_args, **func_kwargs):
|
||||||
|
try:
|
||||||
|
return f(self, *func_args, **func_kwargs)
|
||||||
|
except Exception as exc:
|
||||||
|
exc_status_code = getattr(exc, 'status_code', None)
|
||||||
|
if status_code is None or status_code == exc_status_code:
|
||||||
|
LOG.error('Hints: This test was made for the bug %s. '
|
||||||
|
'The failure could be related to '
|
||||||
|
'https://launchpad.net/bugs/%s', bug, bug)
|
||||||
|
raise exc
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def idempotent_id(id):
|
def idempotent_id(id):
|
||||||
"""Stub for metadata decorator"""
|
"""Stub for metadata decorator"""
|
||||||
if not isinstance(id, six.string_types):
|
if not isinstance(id, six.string_types):
|
||||||
|
@ -45,6 +45,11 @@ idempotent_id = debtcollector.moves.moved_function(
|
|||||||
version='Mitaka', removal_version='?')
|
version='Mitaka', removal_version='?')
|
||||||
|
|
||||||
|
|
||||||
|
related_bug = debtcollector.moves.moved_function(
|
||||||
|
decorators.related_bug, 'related_bug', __name__,
|
||||||
|
version='Pike', removal_version='?')
|
||||||
|
|
||||||
|
|
||||||
def attr(**kwargs):
|
def attr(**kwargs):
|
||||||
"""A decorator which applies the testtools attr decorator
|
"""A decorator which applies the testtools attr decorator
|
||||||
|
|
||||||
@ -143,28 +148,6 @@ def is_extension_enabled(extension_name, service):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def related_bug(bug, status_code=None):
|
|
||||||
"""A decorator useful to know solutions from launchpad bug reports
|
|
||||||
|
|
||||||
@param bug: The launchpad bug number causing the test
|
|
||||||
@param status_code: The status code related to the bug report
|
|
||||||
"""
|
|
||||||
def decorator(f):
|
|
||||||
@functools.wraps(f)
|
|
||||||
def wrapper(self, *func_args, **func_kwargs):
|
|
||||||
try:
|
|
||||||
return f(self, *func_args, **func_kwargs)
|
|
||||||
except Exception as exc:
|
|
||||||
exc_status_code = getattr(exc, 'status_code', None)
|
|
||||||
if status_code is None or status_code == exc_status_code:
|
|
||||||
LOG.error('Hints: This test was made for the bug %s. '
|
|
||||||
'The failure could be related to '
|
|
||||||
'https://launchpad.net/bugs/%s', bug, bug)
|
|
||||||
raise exc
|
|
||||||
return wrapper
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
def is_scheduler_filter_enabled(filter_name):
|
def is_scheduler_filter_enabled(filter_name):
|
||||||
"""Check the list of enabled compute scheduler filters from config.
|
"""Check the list of enabled compute scheduler filters from config.
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import mock
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
from tempest.lib import base as test
|
from tempest.lib import base as test
|
||||||
@ -123,3 +124,33 @@ class TestSkipUnlessAttrDecorator(base.TestCase):
|
|||||||
|
|
||||||
def test_no_skip_for_attr_exist_and_true(self):
|
def test_no_skip_for_attr_exist_and_true(self):
|
||||||
self._test_skip_unless_attr('expected_attr', expected_to_skip=False)
|
self._test_skip_unless_attr('expected_attr', expected_to_skip=False)
|
||||||
|
|
||||||
|
|
||||||
|
class TestRelatedBugDecorator(base.TestCase):
|
||||||
|
def test_relatedbug_when_no_exception(self):
|
||||||
|
f = mock.Mock()
|
||||||
|
sentinel = object()
|
||||||
|
|
||||||
|
@decorators.related_bug(bug="1234", status_code=500)
|
||||||
|
def test_foo(self):
|
||||||
|
f(self)
|
||||||
|
|
||||||
|
test_foo(sentinel)
|
||||||
|
f.assert_called_once_with(sentinel)
|
||||||
|
|
||||||
|
def test_relatedbug_when_exception(self):
|
||||||
|
class MyException(Exception):
|
||||||
|
def __init__(self, status_code):
|
||||||
|
self.status_code = status_code
|
||||||
|
|
||||||
|
def f(self):
|
||||||
|
raise MyException(status_code=500)
|
||||||
|
|
||||||
|
@decorators.related_bug(bug="1234", status_code=500)
|
||||||
|
def test_foo(self):
|
||||||
|
f(self)
|
||||||
|
|
||||||
|
with mock.patch.object(decorators.LOG, 'error') as m_error:
|
||||||
|
self.assertRaises(MyException, test_foo, object())
|
||||||
|
|
||||||
|
m_error.assert_called_once_with(mock.ANY, '1234', '1234')
|
||||||
|
Loading…
Reference in New Issue
Block a user