f013afa78631b558623710dd39a095038e18e2b3
[openstack-build/neutron-build.git] / neutron / tests / unit / callbacks / test_manager.py
1 # Copyright 2015 OpenStack Foundation
2 #
3 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
4 #    not use this file except in compliance with the License. You may obtain
5 #    a copy of the License at
6 #
7 #         http://www.apache.org/licenses/LICENSE-2.0
8 #
9 #    Unless required by applicable law or agreed to in writing, software
10 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 #    License for the specific language governing permissions and limitations
13 #    under the License.
14
15 import mock
16
17 from neutron.callbacks import events
18 from neutron.callbacks import exceptions
19 from neutron.callbacks import manager
20 from neutron.callbacks import resources
21 from neutron.tests import base
22
23
24 def callback_1(*args, **kwargs):
25     callback_1.counter += 1
26 callback_id_1 = manager._get_id(callback_1)
27
28
29 def callback_2(*args, **kwargs):
30     callback_2.counter += 1
31 callback_id_2 = manager._get_id(callback_2)
32
33
34 def callback_raise(*args, **kwargs):
35     raise Exception()
36
37
38 class CallBacksManagerTestCase(base.BaseTestCase):
39
40     def setUp(self):
41         super(CallBacksManagerTestCase, self).setUp()
42         self.manager = manager.CallbacksManager()
43         callback_1.counter = 0
44         callback_2.counter = 0
45
46     def test_subscribe(self):
47         self.manager.subscribe(
48             callback_1, resources.PORT, events.BEFORE_CREATE)
49         self.assertIsNotNone(
50             self.manager._callbacks[resources.PORT][events.BEFORE_CREATE])
51         self.assertIn(callback_id_1, self.manager._index)
52
53     def test_subscribe_unknown(self):
54         self.manager.subscribe(
55             callback_1, 'my_resource', 'my-event')
56         self.assertIsNotNone(
57             self.manager._callbacks['my_resource']['my-event'])
58         self.assertIn(callback_id_1, self.manager._index)
59
60     def test_subscribe_is_idempotent(self):
61         self.manager.subscribe(
62             callback_1, resources.PORT, events.BEFORE_CREATE)
63         self.manager.subscribe(
64             callback_1, resources.PORT, events.BEFORE_CREATE)
65         self.assertEqual(
66             1,
67             len(self.manager._callbacks[resources.PORT][events.BEFORE_CREATE]))
68         callbacks = self.manager._index[callback_id_1][resources.PORT]
69         self.assertEqual(1, len(callbacks))
70
71     def test_subscribe_multiple_callbacks(self):
72         self.manager.subscribe(
73             callback_1, resources.PORT, events.BEFORE_CREATE)
74         self.manager.subscribe(
75             callback_2, resources.PORT, events.BEFORE_CREATE)
76         self.assertEqual(2, len(self.manager._index))
77         self.assertEqual(
78             2,
79             len(self.manager._callbacks[resources.PORT][events.BEFORE_CREATE]))
80
81     def test_unsubscribe(self):
82         self.manager.subscribe(
83             callback_1, resources.PORT, events.BEFORE_CREATE)
84         self.manager.unsubscribe(
85             callback_1, resources.PORT, events.BEFORE_CREATE)
86         self.assertNotIn(
87             callback_id_1,
88             self.manager._callbacks[resources.PORT][events.BEFORE_CREATE])
89         self.assertNotIn(callback_id_1, self.manager._index)
90
91     def test_unsubscribe_unknown_callback(self):
92         self.manager.subscribe(
93             callback_2, resources.PORT, events.BEFORE_CREATE)
94         self.manager.unsubscribe(callback_1, mock.ANY, mock.ANY)
95         self.assertEqual(1, len(self.manager._index))
96
97     def test_unsubscribe_is_idempotent(self):
98         self.manager.subscribe(
99             callback_1, resources.PORT, events.BEFORE_CREATE)
100         self.manager.unsubscribe(
101             callback_1, resources.PORT, events.BEFORE_CREATE)
102         self.manager.unsubscribe(
103             callback_1, resources.PORT, events.BEFORE_CREATE)
104         self.assertNotIn(callback_id_1, self.manager._index)
105         self.assertNotIn(callback_id_1,
106             self.manager._callbacks[resources.PORT][events.BEFORE_CREATE])
107
108     def test_unsubscribe_by_resource(self):
109         self.manager.subscribe(
110             callback_1, resources.PORT, events.BEFORE_CREATE)
111         self.manager.subscribe(
112             callback_1, resources.PORT, events.BEFORE_DELETE)
113         self.manager.subscribe(
114             callback_2, resources.PORT, events.BEFORE_DELETE)
115         self.manager.unsubscribe_by_resource(callback_1, resources.PORT)
116         self.assertNotIn(
117             callback_id_1,
118             self.manager._callbacks[resources.PORT][events.BEFORE_CREATE])
119         self.assertIn(
120             callback_id_2,
121             self.manager._callbacks[resources.PORT][events.BEFORE_DELETE])
122         self.assertNotIn(callback_id_1, self.manager._index)
123
124     def test_unsubscribe_all(self):
125         self.manager.subscribe(
126             callback_1, resources.PORT, events.BEFORE_CREATE)
127         self.manager.subscribe(
128             callback_1, resources.PORT, events.BEFORE_DELETE)
129         self.manager.subscribe(
130             callback_1, resources.ROUTER, events.BEFORE_CREATE)
131         self.manager.unsubscribe_all(callback_1)
132         self.assertNotIn(
133             callback_id_1,
134             self.manager._callbacks[resources.PORT][events.BEFORE_CREATE])
135         self.assertNotIn(callback_id_1, self.manager._index)
136
137     def test_notify_none(self):
138         self.manager.notify(resources.PORT, events.BEFORE_CREATE, mock.ANY)
139         self.assertEqual(0, callback_1.counter)
140         self.assertEqual(0, callback_2.counter)
141
142     def test_feebly_referenced_callback(self):
143         self.manager.subscribe(lambda *x, **y: None, resources.PORT,
144                                events.BEFORE_CREATE)
145         self.manager.notify(resources.PORT, events.BEFORE_CREATE, mock.ANY)
146
147     def test_notify_with_exception(self):
148         with mock.patch.object(self.manager, '_notify_loop') as n:
149             n.return_value = ['error']
150             self.assertRaises(exceptions.CallbackFailure,
151                               self.manager.notify,
152                               mock.ANY, events.BEFORE_CREATE,
153                               'trigger', params={'a': 1})
154             expected_calls = [
155                 mock.call(mock.ANY, 'before_create',
156                           'trigger', params={'a': 1}),
157                 mock.call(mock.ANY, 'abort_create',
158                           'trigger', params={'a': 1})
159             ]
160             n.assert_has_calls(expected_calls)
161
162     def test_notify_handle_exception(self):
163         self.manager.subscribe(
164             callback_raise, resources.PORT, events.BEFORE_CREATE)
165         e = self.assertRaises(exceptions.CallbackFailure, self.manager.notify,
166                               resources.PORT, events.BEFORE_CREATE, self)
167         self.assertIsInstance(e.errors[0], exceptions.NotificationError)
168
169     def test_notify_called_once_with_no_failures(self):
170         with mock.patch.object(self.manager, '_notify_loop') as n:
171             n.return_value = False
172             self.manager.notify(resources.PORT, events.BEFORE_CREATE, mock.ANY)
173             n.assert_called_once_with(
174                 resources.PORT, events.BEFORE_CREATE, mock.ANY)
175
176     def test__notify_loop_single_event(self):
177         self.manager.subscribe(
178             callback_1, resources.PORT, events.BEFORE_CREATE)
179         self.manager.subscribe(
180             callback_2, resources.PORT, events.BEFORE_CREATE)
181         self.manager._notify_loop(
182             resources.PORT, events.BEFORE_CREATE, mock.ANY)
183         self.assertEqual(1, callback_1.counter)
184         self.assertEqual(1, callback_2.counter)
185
186     def test__notify_loop_multiple_events(self):
187         self.manager.subscribe(
188             callback_1, resources.PORT, events.BEFORE_CREATE)
189         self.manager.subscribe(
190             callback_1, resources.ROUTER, events.BEFORE_DELETE)
191         self.manager.subscribe(
192             callback_2, resources.PORT, events.BEFORE_CREATE)
193         self.manager._notify_loop(
194             resources.PORT, events.BEFORE_CREATE, mock.ANY)
195         self.manager._notify_loop(
196             resources.ROUTER, events.BEFORE_DELETE, mock.ANY)
197         self.assertEqual(2, callback_1.counter)
198         self.assertEqual(1, callback_2.counter)