fuel-web/nailgun/nailgun/test/unit/test_mutable.py

357 lines
12 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2015 Mirantis, Inc.
#
# 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.
from copy import copy
from copy import deepcopy
import mock
import yaml
from nailgun.db.sqlalchemy.models.mutable import MutableDict
from nailgun.db.sqlalchemy.models.mutable import MutableList
from nailgun.test.base import BaseUnitTest
@mock.patch('sqlalchemy.ext.mutable.Mutable.coerce')
class TestMutableDictCoerce(BaseUnitTest):
def setUp(self):
super(TestMutableDictCoerce, self).setUp()
self.mutable_dict = MutableDict()
def test_coerce_mutable_dict(self, m_coerce):
dct = MutableDict()
self.assertIsInstance(
self.mutable_dict.coerce('key', dct), MutableDict)
self.assertFalse(m_coerce.called)
def test_coerce_dict(self, m_coerce):
dct = dict()
self.assertIsInstance(
self.mutable_dict.coerce('key', dct), MutableDict)
self.assertFalse(m_coerce.called)
def test_coerce_not_acceptable_object(self, m_coerce):
m_coerce.return_value = None
obj = list()
self.mutable_dict.coerce('key', obj)
m_coerce.assert_called_once_with('key', obj)
class TestMutableBase(BaseUnitTest):
def setUp(self):
self.standard = None
self.mutable_obj = None
def _assert_call_object_changed_once(self, m_changed):
m_changed.assert_called_once_with()
self.assertItemsEqual(self.standard, self.mutable_obj)
def _assert_object_not_changed(self, m_changed):
self.assertFalse(m_changed.called)
self.assertItemsEqual(self.standard, self.mutable_obj)
def _check(self, method, *args, **kwargs):
with mock.patch('sqlalchemy.ext.mutable.Mutable.changed') as m_changed:
self.assertEqual(
getattr(self.mutable_obj, method)(*args, **kwargs),
getattr(self.standard, method)(*args, **kwargs))
self._assert_call_object_changed_once(m_changed)
def _check_failure(self, error, method, *args, **kwargs):
with mock.patch('sqlalchemy.ext.mutable.Mutable.changed') as m_changed:
self.assertRaises(
error, getattr(self.mutable_obj, method), *args, **kwargs)
self._assert_object_not_changed(m_changed)
def check_yaml_serialization(self):
self.assertEqual(
yaml.safe_dump(self.standard),
yaml.safe_dump(self.mutable_obj)
)
self.assertEqual(
yaml.dump(self.standard),
yaml.dump(self.mutable_obj)
)
class TestMutableDictBase(TestMutableBase):
def setUp(self):
super(TestMutableDictBase, self).setUp()
self.standard = {'1': 1}
self.mutable_obj = MutableDict()
self.mutable_obj.update(self.standard)
class TestMutableDict(TestMutableDictBase):
def test_initialize(self):
with mock.patch('sqlalchemy.ext.mutable.Mutable.changed') as m_changed:
MutableDict(key1='value1', key2='value2')
self._assert_object_not_changed(m_changed)
def test_setitem(self):
self._check('__setitem__', '2', 2)
def test_setitem_failure(self):
self._check_failure(TypeError, '__setitem__', {}, 2)
def test_setdefault(self):
self._check('setdefault', '2')
def test_setdefault_with_default(self):
self._check('setdefault', '2', 4)
def test_setdefault_with_default_dict(self):
with mock.patch('sqlalchemy.ext.mutable.Mutable.changed') as m_changed:
self.mutable_obj.setdefault('num', 1)
self.mutable_obj.setdefault('num', 2)
self.assertEqual(m_changed.call_count, 1)
self.assertEqual(1, self.mutable_obj['num'])
def test_setdefault_failure(self):
self._check_failure(TypeError, 'setdefault', {})
def test_delitem(self):
self._check('__delitem__', '1')
def test_delitem_failure(self):
self._check_failure(KeyError, '__delitem__', '2')
def test_update(self):
self._check('update', {'1': 2, '2': 3})
def test_update_failure(self):
self._check_failure(TypeError, 'update', 0)
def test_clear(self):
self._check('clear')
def test_pop_existing(self):
self._check('pop', '1')
def test_pop_existing_with_default(self):
self._check('pop', '1', None)
def test_pop_not_existing(self):
self._check_failure(KeyError, 'pop', '2')
def test_pop_not_existing_with_default(self):
self._check('pop', '2', {})
def test_popitem(self):
self._check('popitem')
self._check_failure(KeyError, 'popitem')
@mock.patch('nailgun.db.sqlalchemy.models.mutable.MutableDict.changed')
class TestMutableDictIntegration(TestMutableDictBase):
def setUp(self):
super(TestMutableDictIntegration, self).setUp()
def test_getstate(self, m_changed):
self.assertIsInstance(self.mutable_obj.__getstate__(), dict)
self._assert_object_not_changed(m_changed)
def test_setstate(self, m_changed):
dct = {'1': 2, '2': 3}
self.standard.update(dct)
self.mutable_obj.__setstate__(dct)
self._assert_call_object_changed_once(m_changed)
@mock.patch(
'nailgun.db.sqlalchemy.models.mutable.MutableDict.__deepcopy__'
)
def test_copy(self, m_deepcopy, _):
m_deepcopy.return_value = mock.Mock()
clone = copy(self.mutable_obj)
self.assertEqual(clone, m_deepcopy.return_value)
self.assertTrue(m_deepcopy.called)
def test_deep_copy(self, m_changed):
dct = MutableDict({'1': 'element1', '2': 'element2'})
self.mutable_obj['2'] = dct
m_changed.reset_mock()
clone = deepcopy(self.mutable_obj)
self.assertEqual(0, m_changed.call_count)
dct['1'] = 'new_element'
self.assertEqual(clone['2']['1'], 'element1')
self.assertEqual(self.mutable_obj['2']['1'], 'new_element')
def test_yaml_serialization(self, _):
self.check_yaml_serialization()
@mock.patch('sqlalchemy.ext.mutable.Mutable.coerce')
class TestMutableListCoerce(BaseUnitTest):
def setUp(self):
self.mutable_list = MutableList()
def test_coerce_mutable_list(self, m_coerce):
lst = MutableList()
self.assertIsInstance(
self.mutable_list.coerce('key', lst), MutableList)
self.assertFalse(m_coerce.called)
def test_coerce_list(self, m_coerce):
lst = list()
self.assertIsInstance(
self.mutable_list.coerce('key', lst), MutableList)
self.assertFalse(m_coerce.called)
def test_coerce_not_acceptable_object(self, m_coerce):
m_coerce.return_value = None
obj = dict()
self.mutable_list.coerce('key', obj)
m_coerce.assert_called_once_with('key', obj)
class TestMutableListBase(TestMutableBase):
def setUp(self):
super(TestMutableListBase, self).setUp()
self.standard = ['element1', 'element2']
self.mutable_obj = MutableList()
self.mutable_obj.extend(self.standard)
class TestMutableList(TestMutableListBase):
def test_initialize(self):
with mock.patch('sqlalchemy.ext.mutable.Mutable.changed') as m_changed:
MutableList([1, 2, 3])
self._assert_object_not_changed(m_changed)
def test_append(self):
self._check('append', 'element')
def test_extend(self):
self._check('extend', ('element3', 'element4', 'element5'))
def test_extend_failure(self):
self._check_failure(TypeError, 'extend', None)
def test_insert(self):
self._check('insert', 1, 'new_element')
def test_insert_failure(self):
self._check_failure(TypeError, 'insert', None, 'element')
def test_pop_default(self):
self._check('pop')
def test_pop_of_specified_element(self):
self._check('pop', 0)
def test_pop_wrong_index_type(self):
self._check_failure(TypeError, 'pop', 'str')
def test_pop_out_of_range(self, ):
self._check_failure(IndexError, 'pop', 2)
def test_pop_default_from_empty_list(self):
self.standard = []
self.mutable_obj = MutableList()
self._check_failure(IndexError, 'pop')
def test_remove(self):
self._check('remove', 'element1')
def test_remove_failure(self):
self._check_failure(ValueError, 'remove', None)
def test_set_item_failure(self):
self._check_failure(IndexError, '__setitem__', 2, 'element')
def test_del_item(self):
self._check('__delitem__', 1)
def test_del_item_failure(self):
self._check_failure(IndexError, '__delitem__', 2)
def test_set_state_failure(self):
self._check_failure(TypeError, '__setstate__', None)
def test_set_slice(self):
self._check('__setslice__', 0, 2, ('element',))
def test_set_slice_failure(self):
self._check_failure(TypeError, '__setslice__', 0, 5, None)
def test_del_slice(self):
self._check('__delslice__', 0, 2)
def test_del_slice_failure(self):
self._check_failure(TypeError, '__delslice__', 0, None)
@mock.patch('nailgun.db.sqlalchemy.models.mutable.MutableList.changed')
class TestMutableListIntegration(TestMutableListBase):
def test_set_item_operator(self, m_changed):
self.mutable_obj[0] = 'new_element'
self.standard[0] = 'new_element'
self._assert_call_object_changed_once(m_changed)
def test_del_operator(self, m_changed):
del self.mutable_obj[0]
del self.standard[0]
self._assert_call_object_changed_once(m_changed)
def test_get_state(self, m_changed):
self.assertIsInstance(self.mutable_obj.__getstate__(), list)
self._assert_object_not_changed(m_changed)
def test_set_state(self, m_changed):
self.mutable_obj.__setstate__([])
self.standard = []
self._assert_call_object_changed_once(m_changed)
def test_set_slice_operator(self, m_changed):
self.mutable_obj[0:2] = ['new_element']
self.standard[0:2] = ['new_element']
self._assert_call_object_changed_once(m_changed)
m_changed.reset_mock()
self.mutable_obj[:] = ["again_new_element"]
self.standard[:] = ["again_new_element"]
self._assert_call_object_changed_once(m_changed)
def test_del_slice_operator(self, m_changed):
del self.mutable_obj[0:2]
del self.standard[0:2]
self._assert_call_object_changed_once(m_changed)
@mock.patch(
'nailgun.db.sqlalchemy.models.mutable.MutableList.__deepcopy__'
)
def test_copy(self, m_deepcopy, _):
m_deepcopy.return_value = mock.Mock()
clone = copy(self.mutable_obj)
self.assertEqual(clone, m_deepcopy.return_value)
self.assertTrue(m_deepcopy.called)
def test_deep_copy(self, m_changed):
lst = MutableList(('element1', 'element2'))
self.mutable_obj.insert(0, lst)
m_changed.reset_mock()
clone = deepcopy(self.mutable_obj)
self.assertEqual(0, m_changed.call_count)
lst[0] = 'new_element'
self.assertEqual(clone[0][0], 'element1')
self.assertEqual(self.mutable_obj[0][0], 'new_element')
def test_yaml_serialization(self, _):
self.check_yaml_serialization()