# Copyright 2017 Mirantis 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.

from unittest import mock

import ddt
from oslo_config import cfg

from manila import exception
from manila import quota
from manila import test

CONF = cfg.CONF


@ddt.ddt
class DbQuotaDriverTestCase(test.TestCase):

    def setUp(self):
        super(DbQuotaDriverTestCase, self).setUp()
        self.project_id = 'fake_project_id'
        self.user_id = 'fake_user_id'
        self.share_type_id = 'fake_share_type_id'
        self.ctxt = type(
            'FakeContext', (object, ),
            {'project_id': self.project_id, 'user_id': self.user_id,
             'quota_class': 'fake_quota_class', 'elevated': mock.Mock()})
        self.driver = quota.DbQuotaDriver()
        self.reservations = ['foo', 'bar']
        self.resources = {k: quota.BaseResource(k) for k in ('foo', 'bar')}

    def test_get_by_class(self):
        self.mock_object(quota.db, 'quota_class_get')

        result = self.driver.get_by_class(
            self.ctxt, 'fake_quota_class', 'fake_res')

        self.assertEqual(quota.db.quota_class_get.return_value, result)
        quota.db.quota_class_get.assert_called_once_with(
            self.ctxt, 'fake_quota_class', 'fake_res')

    def test_get_defaults(self):
        self.mock_object(
            quota.db, 'quota_class_get_default',
            mock.Mock(return_value={'foo': 13}))

        result = self.driver.get_defaults(self.ctxt, self.resources)

        self.assertEqual(
            {'foo': 13, 'bar': self.resources['bar'].default}, result)
        quota.db.quota_class_get_default.assert_called_once_with(self.ctxt)

    @ddt.data(True, False)
    def test_get_class_quotas(self, defaults):
        self.mock_object(
            quota.db, 'quota_class_get_all_by_name',
            mock.Mock(return_value={'foo': 13}))

        result = self.driver.get_class_quotas(
            self.ctxt, self.resources, 'fake_quota_class', defaults)

        expected = {'foo': 13, 'bar': -1} if defaults else {'foo': 13}
        self.assertEqual(expected, result)
        quota.db.quota_class_get_all_by_name.assert_called_once_with(
            self.ctxt, 'fake_quota_class')

    @ddt.data(
        ('fake_project_id', {'foo': 20}, None, True, None, True),
        ('fake_different_project_id', {'bar': 40}, 'fake_quota_class', True,
         {'foo': {'in_use': 3, 'reserved': 2}}, False),
        ('fake_project_id', {'bar': 30}, 'fake_quota_class', True, None, False)
    )
    @ddt.unpack
    def test__process_quotas(self, project_id, quotas, quota_class, defaults,
                             usages, remains):
        self.mock_object(quota.db, 'quota_get_all', mock.Mock(return_value=[]))
        self.mock_object(quota.db, 'quota_class_get_all_by_name')
        self.mock_object(
            self.driver, 'get_defaults',
            mock.Mock(return_value={'foo': 11, 'bar': 12}))
        self.mock_object(
            quota.db, 'quota_get_all',
            mock.Mock(return_value=[]))

        result = self.driver._process_quotas(
            self.ctxt, self.resources, project_id, quotas, quota_class,
            defaults, usages, remains)

        expected = {key: {'limit': mock.ANY} for key in ('foo', 'bar')}
        if usages:
            for res in self.resources.values():
                usage = usages.get(res.name, {})
                expected[res.name].update(
                    in_use=usage.get('in_use', 0),
                    reserved=usage.get('reserved', 0))
        if remains:
            quota.db.quota_get_all.assert_called_once_with(
                self.ctxt, project_id)
            for res in self.resources.values():
                expected[res.name]['remains'] = mock.ANY
        else:
            self.assertEqual(0, quota.db.quota_get_all.call_count)

        self.assertEqual(expected, result)
        if quota_class or project_id == self.ctxt.project_id:
            quota.db.quota_class_get_all_by_name.assert_called_once_with(
                self.ctxt, quota_class or self.ctxt.quota_class)
        else:
            self.assertEqual(
                0, quota.db.quota_class_get_all_by_name.call_count)

    @ddt.data(
        ('fake_quota_class', True, None, 'fake_remains'),
        (None, False, 'fake_usages', False),
    )
    @ddt.unpack
    def test_get_project_quotas(self, quota_class, defaults, usages, remains):
        self.mock_object(quota.db, 'quota_get_all_by_project')
        self.mock_object(quota.db, 'quota_usage_get_all_by_project')
        self.mock_object(self.driver, '_process_quotas')

        result = self.driver.get_project_quotas(
            self.ctxt, self.resources, self.project_id,
            quota_class, defaults, usages, remains)

        self.assertEqual(
            result, self.driver._process_quotas.return_value)
        project_usages = None
        if usages:
            project_usages = (
                quota.db.quota_usage_get_all_by_project.return_value)
        self.driver._process_quotas.assert_called_once_with(
            self.ctxt, self.resources, self.project_id,
            quota.db.quota_get_all_by_project.return_value,
            quota_class, defaults=defaults, usages=project_usages,
            remains=remains)
        quota.db.quota_get_all_by_project.assert_called_once_with(
            self.ctxt, self.project_id)
        if usages:
            quota.db.quota_usage_get_all_by_project.assert_called_once_with(
                self.ctxt, self.project_id)
        else:
            self.assertEqual(
                0, quota.db.quota_usage_get_all_by_project.call_count)

    @ddt.data(
        (None, True, True),
        ('fake_quota_class', False, True),
        ('fake_quota_class', True, False),
    )
    @ddt.unpack
    def test_get_user_quotas(self, quota_class, defaults, usages):
        project_quotas = {'fake_resource': 5}
        self.mock_object(
            quota.db, 'quota_get_all_by_project',
            mock.Mock(return_value=project_quotas))
        self.mock_object(
            quota.db, 'quota_get_all_by_project_and_user',
            mock.Mock(return_value={'fake_user_defined_resource': 14}))
        mock_user_usages = self.mock_object(
            quota.db, 'quota_usage_get_all_by_project_and_user')
        self.mock_object(self.driver, '_process_quotas')

        result = self.driver.get_user_quotas(
            self.ctxt, self.resources, self.project_id, self.user_id,
            quota_class, defaults, usages)

        self.assertEqual(
            self.driver._process_quotas.return_value, result)
        quota.db.quota_get_all_by_project.assert_called_once_with(
            self.ctxt, self.project_id)
        quota.db.quota_get_all_by_project_and_user.assert_called_once_with(
            self.ctxt, self.project_id, self.user_id)
        if usages:
            user_usages = mock_user_usages.return_value
            mock_user_usages.assert_called_once_with(
                self.ctxt, self.project_id, self.user_id)
        else:
            user_usages = None
            self.assertEqual(0, mock_user_usages.call_count)
        expected_user_quotas = {'fake_user_defined_resource': 14}
        expected_user_quotas.update(project_quotas)
        self.driver._process_quotas.assert_called_once_with(
            self.ctxt, self.resources, self.project_id, expected_user_quotas,
            quota_class, defaults=defaults, usages=user_usages)

    @ddt.data(
        (None, True, True),
        ('fake_quota_class', False, True),
        ('fake_quota_class', True, False),
    )
    @ddt.unpack
    def test_get_share_type_quotas(self, quota_class, defaults, usages):
        project_quotas = {'fake_resource': 5}
        self.mock_object(
            quota.db, 'quota_get_all_by_project',
            mock.Mock(return_value=project_quotas))
        mock_st_quotas = self.mock_object(
            quota.db, 'quota_get_all_by_project_and_share_type',
            mock.Mock(return_value={'fake_st_defined_resource': 14}))
        mock_st_usages = self.mock_object(
            quota.db, 'quota_usage_get_all_by_project_and_share_type')
        self.mock_object(self.driver, '_process_quotas')

        result = self.driver.get_share_type_quotas(
            self.ctxt, self.resources, self.project_id, self.share_type_id,
            quota_class, defaults, usages)

        self.assertEqual(
            self.driver._process_quotas.return_value, result)
        quota.db.quota_get_all_by_project.assert_called_once_with(
            self.ctxt, self.project_id)
        mock_st_quotas.assert_called_once_with(
            self.ctxt, self.project_id, self.share_type_id)
        if usages:
            st_usages = mock_st_usages.return_value
            mock_st_usages.assert_called_once_with(
                self.ctxt, self.project_id, self.share_type_id)
        else:
            st_usages = None
            self.assertEqual(0, mock_st_usages.call_count)
        expected_st_quotas = {'fake_st_defined_resource': 14}
        expected_st_quotas.update(project_quotas)
        self.driver._process_quotas.assert_called_once_with(
            self.ctxt, self.resources, self.project_id, expected_st_quotas,
            quota_class, defaults=defaults, usages=st_usages)

    @ddt.data((None, None), (None, 'foo_st_id'), ('foo_user_id', None))
    @ddt.unpack
    def test_get_settable_quotas(self, user_id, st_id):
        project_quotas = {'fake': {
            'limit': 13, 'in_use': 7, 'reserved': 5, 'remains': 1,
        }}
        user_or_st_quotas = {'fake': {
            'limit': 11, 'in_use': 5, 'reserved': 2,
        }}
        self.mock_object(
            self.driver, 'get_project_quotas',
            mock.Mock(return_value=project_quotas))
        self.mock_object(
            self.driver, 'get_user_quotas',
            mock.Mock(return_value=user_or_st_quotas))
        self.mock_object(
            self.driver, 'get_share_type_quotas',
            mock.Mock(return_value=user_or_st_quotas))

        result = self.driver.get_settable_quotas(
            self.ctxt, self.resources, self.project_id, user_id, st_id)

        if user_id:
            self.driver.get_user_quotas.assert_called_once_with(
                self.ctxt, self.resources, self.project_id, user_id)
        else:
            self.assertEqual(0, self.driver.get_user_quotas.call_count)
        if st_id:
            self.driver.get_share_type_quotas.assert_called_once_with(
                self.ctxt, self.resources, self.project_id, st_id)
        else:
            self.assertEqual(0, self.driver.get_share_type_quotas.call_count)
        if user_id or st_id:
            expected_settable_quotas = {'fake': {'maximum': 13, 'minimum': 7}}
        else:
            expected_settable_quotas = {'fake': {'maximum': -1, 'minimum': 12}}
        self.driver.get_project_quotas.assert_called_once_with(
            self.ctxt, self.resources, self.project_id, remains=True)
        self.assertEqual(expected_settable_quotas, result)

    @ddt.data((None, None), (None, 'fake_st_id'), ('fake_user_id', None))
    @ddt.unpack
    def test__get_quotas(self, user_id, st_id):
        quotas = {'foo': {'limit': 5}, 'bar': {'limit': 13}}
        self.mock_object(
            self.driver, 'get_project_quotas', mock.Mock(return_value=quotas))
        self.mock_object(
            self.driver, 'get_user_quotas', mock.Mock(return_value=quotas))
        self.mock_object(
            self.driver, 'get_share_type_quotas',
            mock.Mock(return_value=quotas))

        result = self.driver._get_quotas(
            self.ctxt, self.resources, ('foo', 'bar'), False,
            self.project_id, user_id, st_id)

        expected = {k: v['limit'] for k, v in quotas.items()}
        self.assertEqual(expected, result)
        sub_resources = {k: v for k, v in self.resources.items()}
        if user_id:
            self.driver.get_user_quotas.assert_called_once_with(
                self.ctxt, sub_resources, self.project_id, user_id,
                self.ctxt.quota_class, usages=False)
            self.assertEqual(0, self.driver.get_project_quotas.call_count)
            self.assertEqual(0, self.driver.get_share_type_quotas.call_count)
        elif st_id:
            self.driver.get_share_type_quotas.assert_called_once_with(
                self.ctxt, sub_resources, self.project_id, st_id,
                self.ctxt.quota_class, usages=False)
            self.assertEqual(0, self.driver.get_project_quotas.call_count)
            self.assertEqual(0, self.driver.get_user_quotas.call_count)
        else:
            self.driver.get_project_quotas.assert_called_once_with(
                self.ctxt, sub_resources, self.project_id,
                self.ctxt.quota_class, usages=False)
            self.assertEqual(0, self.driver.get_user_quotas.call_count)
            self.assertEqual(0, self.driver.get_share_type_quotas.call_count)

    def test__get_quotas_unknown(self):
        quotas = {'foo': {'limit': 5}, 'bar': {'limit': 13}}
        self.mock_object(
            self.driver, 'get_project_quotas', mock.Mock(return_value=quotas))
        self.mock_object(
            self.driver, 'get_user_quotas', mock.Mock(return_value=quotas))
        self.mock_object(
            self.driver, 'get_share_type_quotas',
            mock.Mock(return_value=quotas))

        self.assertRaises(
            exception.QuotaResourceUnknown,
            self.driver._get_quotas,
            self.ctxt, self.resources, ['foo', 'bar'], True,
            self.project_id, self.user_id, self.share_type_id)

        self.assertEqual(0, self.driver.get_project_quotas.call_count)
        self.assertEqual(0, self.driver.get_user_quotas.call_count)
        self.assertEqual(0, self.driver.get_share_type_quotas.call_count)

    @ddt.data(
        {}, {'project_id': 'fake_project'}, {'user_id': 'fake_user'},
        {'share_type_id': 'fake_share_type_id'},
    )
    def test_reserve(self, kwargs):
        self.mock_object(quota.db, 'quota_reserve')
        deltas = {'delta1': 1, 'delta2': 2}
        quotas, user_quotas, st_quotas = 'fake1', 'fake2', 'fake3'
        self.mock_object(
            self.driver, '_get_quotas', mock.Mock(
                side_effect=[quotas, user_quotas, st_quotas]))

        result = self.driver.reserve(
            self.ctxt, self.resources, deltas, None, **kwargs)

        expected_kwargs = {
            'project_id': self.ctxt.project_id,
            'user_id': self.ctxt.user_id,
            'share_type_id': None,
            'overquota_allowed': False
        }
        expected_kwargs.update(kwargs)
        st_quotas = st_quotas if kwargs.get('share_type_id') else {}
        self.assertEqual(quota.db.quota_reserve.return_value, result)
        quota.db.quota_reserve.assert_called_once_with(
            self.ctxt, self.resources, quotas, user_quotas, st_quotas,
            deltas, mock.ANY, CONF.quota.until_refresh, CONF.quota.max_age,
            **expected_kwargs)
        self.assertEqual(
            3 if kwargs.get('share_type_id') else 2,
            self.driver._get_quotas.call_count)

    def test_reserve_wrong_expire(self):
        self.assertRaises(
            exception.InvalidReservationExpiration,
            self.driver.reserve,
            self.ctxt, self.resources, 'fake_deltas', 'fake_expire')

    def test_commit(self):
        self.mock_object(quota.db, 'reservation_commit')

        result = self.driver.commit(
            self.ctxt, self.reservations, self.project_id, self.user_id,
            self.share_type_id)

        self.assertIsNone(result)
        quota.db.reservation_commit.assert_called_once_with(
            self.ctxt, self.reservations, project_id=self.project_id,
            user_id=self.user_id, share_type_id=self.share_type_id)

    @ddt.data(
        (None, None),
        ('fake_project_id', 'fake_user_id'),
    )
    @ddt.unpack
    def test_rollback(self, project_id, user_id):
        self.mock_object(quota.db, 'reservation_rollback')

        result = self.driver.rollback(
            self.ctxt, self.reservations, project_id, user_id,
            self.share_type_id)

        expected_project_id = project_id or self.ctxt.project_id
        expected_user_id = user_id or self.ctxt.user_id
        self.assertIsNone(result)
        quota.db.reservation_rollback.assert_called_once_with(
            self.ctxt, self.reservations, project_id=expected_project_id,
            user_id=expected_user_id, share_type_id=self.share_type_id)

    def test_usage_reset(self):
        self.mock_object(
            quota.db, 'quota_usage_update',
            mock.Mock(side_effect=[
                'foo',
                exception.QuotaUsageNotFound(project_id=self.project_id)]))

        result = self.driver.usage_reset(self.ctxt, ['foo', 'bar'])

        self.assertIsNone(result)
        quota.db.quota_usage_update.assert_has_calls([
            mock.call(
                self.ctxt.elevated.return_value, self.ctxt.project_id,
                self.ctxt.user_id, res, in_use=-1)
            for res in ('foo', 'bar')
        ])

    def test_destroy_all_by_project(self):
        self.mock_object(quota.db, 'quota_destroy_all_by_project')

        result = self.driver.destroy_all_by_project(self.ctxt, self.project_id)

        self.assertIsNone(result)
        quota.db.quota_destroy_all_by_project.assert_called_once_with(
            self.ctxt, self.project_id)

    def test_destroy_all_by_project_and_user(self):
        self.mock_object(quota.db, 'quota_destroy_all_by_project_and_user')

        result = self.driver.destroy_all_by_project_and_user(
            self.ctxt, self.project_id, self.user_id)

        self.assertIsNone(result)
        quota.db.quota_destroy_all_by_project_and_user.assert_called_once_with(
            self.ctxt, self.project_id, self.user_id)

    def test_destroy_all_by_project_and_share_type(self):
        mock_destroy_all = self.mock_object(
            quota.db, 'quota_destroy_all_by_share_type')

        result = self.driver.destroy_all_by_project_and_share_type(
            self.ctxt, self.project_id, self.share_type_id)

        self.assertIsNone(result)
        mock_destroy_all.assert_called_once_with(
            self.ctxt, self.share_type_id, project_id=self.project_id)

    def test_expire(self):
        self.mock_object(quota.db, 'reservation_expire')

        result = self.driver.expire(self.ctxt)

        self.assertIsNone(result)
        quota.db.reservation_expire.assert_called_once_with(self.ctxt)


@ddt.ddt
class QuotaEngineTestCase(test.TestCase):

    def setUp(self):
        super(QuotaEngineTestCase, self).setUp()
        self.ctxt = 'fake_context'
        self.mock_class('manila.quota.DbQuotaDriver')
        self.engine = quota.QuotaEngine()
        self.driver = self.engine._driver
        self.resources = [quota.BaseResource('foo'), quota.BaseResource('bar')]
        self.project_id = 'fake_project_id'
        self.user_id = 'fake_user_id'
        self.share_type_id = 'fake_share_type_id'
        self.quota_class = 'fake_quota_class'

    def test_register_resource(self):
        self.assertNotIn(self.resources[0].name, self.engine)
        self.engine.register_resource(self.resources[0])
        self.assertIn(self.resources[0].name, self.engine)

    def test_register_resources(self):
        for res in self.resources:
            self.assertNotIn(res.name, self.engine)
        self.engine.register_resources(self.resources)
        for res in self.resources:
            self.assertIn(res.name, self.engine)

    def test_get_by_class(self):
        result = self.engine.get_by_class(
            self.ctxt, self.quota_class, 'fake_res')

        self.assertEqual(result, self.driver.get_by_class.return_value)
        self.driver.get_by_class.assert_called_once_with(
            self.ctxt, self.quota_class, 'fake_res')

    def test_get_defaults(self):
        result = self.engine.get_defaults(self.ctxt)

        self.assertEqual(result, self.driver.get_defaults.return_value)
        self.driver.get_defaults.assert_called_once_with(
            self.ctxt, self.engine._resources)

    @ddt.data(None, True, False)
    def test_get_class_quotas(self, defaults):
        kwargs = {}
        if defaults is not None:
            kwargs['defaults'] = defaults

        result = self.engine.get_class_quotas(
            self.ctxt, self.quota_class, **kwargs)

        self.assertEqual(result, self.driver.get_class_quotas.return_value)
        kwargs['defaults'] = defaults if defaults is not None else True
        self.driver.get_class_quotas.assert_called_once_with(
            self.ctxt, self.engine._resources, self.quota_class, **kwargs)

    @ddt.data(
        {},
        {'quota_class': 'foo'},
        {'defaults': False},
        {'usages': False},
    )
    def test_get_user_quotas(self, kwargs):
        expected_kwargs = {
            'quota_class': None,
            'defaults': True,
            'usages': True,
        }
        expected_kwargs.update(kwargs)

        result = self.engine.get_user_quotas(
            self.ctxt, self.project_id, self.user_id, **kwargs)

        self.assertEqual(result, self.driver.get_user_quotas.return_value)
        self.driver.get_user_quotas.assert_called_once_with(
            self.ctxt, self.engine._resources,
            self.project_id, self.user_id, **expected_kwargs)

    @ddt.data(
        {},
        {'quota_class': 'foo'},
        {'defaults': False},
        {'usages': False},
    )
    def test_get_share_type_quotas(self, kwargs):
        expected_kwargs = {
            'quota_class': None,
            'defaults': True,
            'usages': True,
        }
        expected_kwargs.update(kwargs)

        result = self.engine.get_share_type_quotas(
            self.ctxt, self.project_id, self.share_type_id, **kwargs)

        self.assertEqual(
            result, self.driver.get_share_type_quotas.return_value)
        self.driver.get_share_type_quotas.assert_called_once_with(
            self.ctxt, self.engine._resources,
            self.project_id, self.share_type_id, **expected_kwargs)

    @ddt.data(
        {},
        {'quota_class': 'foo'},
        {'defaults': False},
        {'usages': False},
        {'remains': True},
    )
    def test_get_project_quotas(self, kwargs):
        expected_kwargs = {
            'quota_class': None,
            'defaults': True,
            'usages': True,
            'remains': False,
        }
        expected_kwargs.update(kwargs)

        result = self.engine.get_project_quotas(
            self.ctxt, self.project_id, **kwargs)

        self.assertEqual(result, self.driver.get_project_quotas.return_value)
        self.driver.get_project_quotas.assert_called_once_with(
            self.ctxt, self.engine._resources,
            self.project_id, **expected_kwargs)

    @ddt.data(
        {},
        {'user_id': 'fake_user_id'},
        {'share_type_id': 'fake_share_type_id'},
    )
    def test_get_settable_quotas(self, kwargs):
        expected_kwargs = {'user_id': None, 'share_type_id': None}
        expected_kwargs.update(kwargs)

        result = self.engine.get_settable_quotas(
            self.ctxt, self.project_id, **kwargs)

        self.assertEqual(result, self.driver.get_settable_quotas.return_value)
        self.driver.get_settable_quotas.assert_called_once_with(
            self.ctxt, self.engine._resources,
            self.project_id, **expected_kwargs)

    def test_count(self):
        mock_count = mock.Mock()
        resource = quota.CountableResource('FakeCountableResource', mock_count)
        self.engine.register_resource(resource)

        result = self.engine.count(self.ctxt, resource.name)

        self.assertEqual(mock_count.return_value, result)

    def test_count_unknown_resource(self):
        self.assertRaises(
            exception.QuotaResourceUnknown,
            self.engine.count,
            self.ctxt, 'nonexistent_resource', 'foo_arg', foo='kwarg')

    def test_reserve(self):
        result = self.engine.reserve(
            self.ctxt, 'fake_expire', self.project_id, self.user_id,
            self.share_type_id, delta1=1, delta2=2)

        self.assertEqual(self.driver.reserve.return_value, result)
        self.driver.reserve.assert_called_once_with(
            self.ctxt, self.engine._resources, {'delta1': 1, 'delta2': 2},
            expire='fake_expire', project_id=self.project_id,
            user_id=self.user_id, share_type_id=self.share_type_id,
            overquota_allowed=False)

    @ddt.data(Exception('FakeException'), [None])
    def test_commit(self, side_effect):
        fake_reservations = ['foo', 'bar']
        self.driver.commit.side_effect = side_effect
        self.mock_object(quota.LOG, 'exception')

        result = self.engine.commit(
            self.ctxt, fake_reservations, 'fake_project_id',
            'fake_user_id', 'fake_share_type_id')

        self.assertIsNone(result)
        self.driver.commit.assert_called_once_with(
            self.ctxt, fake_reservations, project_id='fake_project_id',
            user_id='fake_user_id', share_type_id='fake_share_type_id')
        if side_effect == [None]:
            self.assertEqual(0, quota.LOG.exception.call_count)
        else:
            quota.LOG.exception.assert_called_once_with(
                mock.ANY, fake_reservations)

    @ddt.data(Exception('FakeException'), [None])
    def test_rollback(self, side_effect):
        fake_reservations = ['foo', 'bar']
        self.driver.rollback.side_effect = side_effect
        self.mock_object(quota.LOG, 'exception')

        result = self.engine.rollback(
            self.ctxt, fake_reservations, 'fake_project_id',
            'fake_user_id', 'fake_share_type_id')

        self.assertIsNone(result)
        self.driver.rollback.assert_called_once_with(
            self.ctxt, fake_reservations, project_id='fake_project_id',
            user_id='fake_user_id', share_type_id='fake_share_type_id')
        if side_effect == [None]:
            self.assertEqual(0, quota.LOG.exception.call_count)
        else:
            quota.LOG.exception.assert_called_once_with(
                mock.ANY, fake_reservations)

    def test_usage_reset(self):
        result = self.engine.usage_reset(self.ctxt, 'fake_resources')

        self.assertIsNone(result)
        self.driver.usage_reset.assert_called_once_with(
            self.ctxt, 'fake_resources')

    def test_destroy_all_by_project_and_user(self):
        result = self.engine.destroy_all_by_project_and_user(
            self.ctxt, 'fake_project_id', 'fake_user_id')

        self.assertIsNone(result)
        self.driver.destroy_all_by_project_and_user.assert_called_once_with(
            self.ctxt, 'fake_project_id', 'fake_user_id')

    def test_destroy_all_by_project_and_share_type(self):
        result = self.engine.destroy_all_by_project_and_share_type(
            self.ctxt, 'fake_project_id', 'fake_st_id')

        self.assertIsNone(result)
        mock_destroy_all_by_project_and_share_type = (
            self.driver.destroy_all_by_project_and_share_type)
        mock_destroy_all_by_project_and_share_type.assert_called_once_with(
            self.ctxt, 'fake_project_id', 'fake_st_id')

    def test_destroy_all_by_project(self):
        result = self.engine.destroy_all_by_project(
            self.ctxt, 'fake_project_id')

        self.assertIsNone(result)
        self.driver.destroy_all_by_project.assert_called_once_with(
            self.ctxt, 'fake_project_id')

    def test_expire(self):
        result = self.engine.expire(self.ctxt)

        self.assertIsNone(result)
        self.driver.expire.assert_called_once_with(self.ctxt)

    def test_resources(self):
        self.engine.register_resources(self.resources)
        self.assertEqual(['bar', 'foo'], self.engine.resources)

    def test_current_common_resources(self):
        self.assertEqual(
            ['gigabytes', 'per_share_gigabytes', 'replica_gigabytes',
             'share_group_snapshots', 'share_groups', 'share_networks',
             'share_replicas', 'shares', 'snapshot_gigabytes', 'snapshots'],
            quota.QUOTAS.resources)
