Initial commit
[ta/config-manager.git] / cmframework / test / cmupdateimpl_test.py
1 # Copyright 2019 Nokia
2
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain 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,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import unittest
16 import mock
17 from mock import call
18 import json
19
20 from cmframework.apis.cmupdate import CMUpdate
21 from cmframework.lib.cmupdateimpl import CMUpdateImpl
22 from cmframework.apis.cmerror import CMError
23
24
25 class CMUpdateImplTest(unittest.TestCase):
26     @mock.patch('cmframework.lib.cmupdateimpl.CMPluginLoader')
27     @mock.patch('cmframework.lib.cmupdateimpl.CMManage')
28     @mock.patch('cmframework.lib.cmupdateimpl.logging')
29     def test_init(self, mock_logging, mock_client, mock_pluginloader):
30         mock_pluginloader.return_value.load.return_value = ({}, None)
31
32         updater = CMUpdate('test_plugin_path', 'test_server_ip', 'test_server_port',
33                            'test_client_lib_impl_module', 'test_verbose_logger')
34
35         mock_pluginloader.assert_called_once_with('test_plugin_path')
36         mock_client.assert_called_once_with('test_server_ip',
37                                             'test_server_port',
38                                             'test_client_lib_impl_module',
39                                             'test_verbose_logger')
40
41     @staticmethod
42     def _test__read_dependency_file_incorrect(file_name):
43         if file_name == 'test_plugin_path/test_handler_a.deps':
44             return ([], ['x'])
45         if file_name == 'test_plugin_path/test_handler_b.deps':
46             return (['y'], [])
47
48     @mock.patch.object(CMUpdateImpl, '_read_dependency_file')
49     @mock.patch('cmframework.lib.cmupdateimpl.CMPluginLoader')
50     @mock.patch('cmframework.lib.cmupdateimpl.CMManage')
51     @mock.patch('cmframework.lib.cmupdateimpl.logging')
52     def test_init_missing_handler_in_dependencies(self,
53                                                   mock_logging,
54                                                   mock_client,
55                                                   mock_pluginloader,
56                                                   mock__read_dependency_file):
57         mock_pluginloader.return_value.load.return_value = ({}, None)
58         mock__read_dependency_file.side_effect = \
59             CMUpdateImplTest._test__read_dependency_file_incorrect
60
61         test_handler_a_module = mock.MagicMock()
62         test_handler_a_class = mock.MagicMock()
63         test_handler_a_class.return_value.__str__.return_value = 'test_handler_a'
64         setattr(test_handler_a_module, 'test_handler_a', test_handler_a_class)
65
66         mock_pluginloader.return_value.load.return_value = \
67             ({'test_handler_a': test_handler_a_module}, None)
68
69         with self.assertRaises(CMError) as context:
70             updater = CMUpdate('test_plugin_path', 'test_server_ip', 'test_server_port',
71                                'test_client_lib_impl_module', 'test_verbose_logger')
72
73         test_handler_b_module = mock.MagicMock()
74         test_handler_b_class = mock.MagicMock()
75         test_handler_b_class.return_value.__str__.return_value = 'test_handler_b'
76         setattr(test_handler_b_module, 'test_handler_b', test_handler_b_class)
77
78         mock_pluginloader.return_value.load.return_value = \
79             ({'test_handler_b': test_handler_b_module}, None)
80
81         with self.assertRaises(CMError) as context:
82             updater = CMUpdate('test_plugin_path', 'test_server_ip', 'test_server_port',
83                                'test_client_lib_impl_module', 'test_verbose_logger')
84
85         mock_client.assert_not_called()
86
87     @staticmethod
88     def _test__read_dependency_file(file_name):
89         if file_name == 'test_plugin_path/test_handler_a.deps':
90             return ([], ['test_handler_b'])
91         if file_name == 'test_plugin_path/test_handler_b.deps':
92             return (['test_handler_a'], [])
93         if file_name == 'test_plugin_path/test_handler_c.deps':
94             return (['test_handler_b'], ['test_handler_a'])
95
96     @staticmethod
97     def _test_update_func_a(confman):
98         CMUpdateImplTest._test_update_func_calls.append('test_handler_a')
99         if str(confman) == 'raise exception':
100             raise Exception('test_update_exception')
101
102     @staticmethod
103     def _test_update_func_b(confman):
104         CMUpdateImplTest._test_update_func_calls.append('test_handler_b')
105
106     @staticmethod
107     def _test_update_func_c(confman):
108         confman.get_config.return_value = {'test_properties': '_test_update_func_c properties'}
109         CMUpdateImplTest._test_update_func_calls.append('test_handler_c')
110
111     def _setup_test_handlers(self, mock_pluginloader, mock__read_dependency_file, mock_sorter):
112         CMUpdateImplTest._test_update_func_calls = []
113
114         test_handler_a_module = mock.MagicMock()
115         test_handler_a_class = mock.MagicMock()
116         test_handler_a_class.return_value.__str__.return_value = 'test_handler_a'
117         test_handler_a_class.return_value.update.side_effect = CMUpdateImplTest._test_update_func_a
118         setattr(test_handler_a_module, 'test_handler_a', test_handler_a_class)
119
120         test_handler_b_module = mock.MagicMock()
121         test_handler_b_class = mock.MagicMock()
122         test_handler_b_class.return_value.__str__.return_value = 'test_handler_b'
123         test_handler_b_class.return_value.update.side_effect = CMUpdateImplTest._test_update_func_b
124         setattr(test_handler_b_module, 'test_handler_b', test_handler_b_class)
125
126         test_handler_c_module = mock.MagicMock()
127         test_handler_c_class = mock.MagicMock()
128         test_handler_c_class.return_value.__str__.return_value = 'test_handler_c'
129         test_handler_c_class.return_value.update.side_effect = CMUpdateImplTest._test_update_func_c
130         setattr(test_handler_c_module, 'test_handler_c', test_handler_c_class)
131
132         mock_pluginloader.return_value.load.return_value = \
133             ({'test_handler_a': test_handler_a_module,
134               'test_handler_b': test_handler_b_module,
135               'test_handler_c': test_handler_c_module}, None)
136
137         mock__read_dependency_file.side_effect = CMUpdateImplTest._test__read_dependency_file
138
139         mock_sorter.return_value.sort.return_value = ['test_handler_c',
140                                                       'test_handler_a',
141                                                       'test_handler_b']
142
143         return (test_handler_a_class, test_handler_b_class, test_handler_c_class)
144
145     @mock.patch.object(CMUpdateImpl, '_read_dependency_file')
146     @mock.patch('cmframework.lib.cmupdateimpl.CMDependencySort')
147     @mock.patch('cmframework.lib.cmupdateimpl.CMPluginLoader')
148     @mock.patch('cmframework.lib.cmupdateimpl.CMManage')
149     @mock.patch('cmframework.lib.cmupdateimpl.logging')
150     def test_update(self,
151                     mock_logging,
152                     mock_client,
153                     mock_pluginloader,
154                     mock_sorter,
155                     mock__read_dependency_file):
156         test_handler_a_class, test_handler_b_class, test_handler_c_class = \
157             self._setup_test_handlers(mock_pluginloader, mock__read_dependency_file, mock_sorter)
158
159         updater = CMUpdate('test_plugin_path', 'test_server_ip', 'test_server_port',
160                            'test_client_lib_impl_module', 'test_verbose_logger')
161
162         mock_confman = mock.MagicMock()
163         mock_confman.__str__.return_value = 'confman'
164         mock_confman.get_config.return_value = {'test_properties': 'some properties'}
165
166         updater.update(mock_confman)
167
168         sorter_after_graph = mock_sorter.call_args[0][0]
169         assert len(sorter_after_graph) == 3
170         assert 'test_handler_a' in sorter_after_graph
171         assert 'test_handler_b' in sorter_after_graph
172         assert 'test_handler_c' in sorter_after_graph
173         assert sorter_after_graph['test_handler_a'] == ['test_handler_b']
174         assert sorter_after_graph['test_handler_b'] == []
175         assert sorter_after_graph['test_handler_c'] == ['test_handler_a']
176
177         sorter_before_graph = mock_sorter.call_args[0][1]
178         assert len(sorter_before_graph) == 3
179         assert 'test_handler_a' in sorter_before_graph
180         assert 'test_handler_b' in sorter_before_graph
181         assert 'test_handler_c' in sorter_before_graph
182         assert sorter_before_graph['test_handler_a'] == []
183         assert sorter_before_graph['test_handler_b'] == ['test_handler_a']
184         assert sorter_before_graph['test_handler_c'] == ['test_handler_b']
185
186         mock_sorter.return_value.sort.assert_called_once()
187
188         test_handler_a_class.return_value.update.assert_called_once_with(mock_confman)
189         test_handler_b_class.return_value.update.assert_called_once_with(mock_confman)
190         test_handler_c_class.return_value.update.assert_called_once_with(mock_confman)
191
192         assert CMUpdateImplTest._test_update_func_calls == \
193             mock_sorter.return_value.sort.return_value
194
195         mock_client.return_value.set_properties.assert_called_once_with(
196             {'test_properties': '_test_update_func_c properties'}, True)
197
198     @mock.patch.object(CMUpdateImpl, '_read_dependency_file')
199     @mock.patch('cmframework.lib.cmupdateimpl.ConfigManager')
200     @mock.patch('cmframework.lib.cmupdateimpl.CMDependencySort')
201     @mock.patch('cmframework.lib.cmupdateimpl.CMPluginLoader')
202     @mock.patch('cmframework.lib.cmupdateimpl.CMManage')
203     @mock.patch('cmframework.lib.cmupdateimpl.logging')
204     def test_update_no_confman(self,
205                                mock_logging,
206                                mock_client,
207                                mock_pluginloader,
208                                mock_sorter,
209                                mock_configmanager,
210                                mock__read_dependency_file):
211         test_handler_a_class, test_handler_b_class, test_handler_c_class = \
212             self._setup_test_handlers(mock_pluginloader, mock__read_dependency_file, mock_sorter)
213
214         updater = CMUpdate('test_plugin_path', 'test_server_ip', 'test_server_port',
215                            'test_client_lib_impl_module', 'test_verbose_logger')
216
217         updater.update()
218
219         test_handler_a_class.return_value.update.assert_called_once_with(
220             mock_configmanager.return_value)
221         test_handler_b_class.return_value.update.assert_called_once_with(
222             mock_configmanager.return_value)
223         test_handler_c_class.return_value.update.assert_called_once_with(
224             mock_configmanager.return_value)
225
226         '''
227         mock_configmanager.assert_called_once_with(
228             mock_client.return_value.get_properties.return_value)
229         '''
230
231         assert CMUpdateImplTest._test_update_func_calls == \
232             mock_sorter.return_value.sort.return_value
233
234     @mock.patch.object(CMUpdateImpl, '_read_dependency_file')
235     @mock.patch('cmframework.lib.cmupdateimpl.ConfigManager')
236     @mock.patch('cmframework.lib.cmupdateimpl.CMDependencySort')
237     @mock.patch('cmframework.lib.cmupdateimpl.CMPluginLoader')
238     @mock.patch('cmframework.lib.cmupdateimpl.CMManage')
239     @mock.patch('cmframework.lib.cmupdateimpl.logging')
240     def test_update_exception(self,
241                               mock_logging,
242                               mock_client,
243                               mock_pluginloader,
244                               mock_sorter,
245                               mock_configmanager,
246                               mock__read_dependency_file):
247         test_handler_a_class, test_handler_b_class, test_handler_c_class = \
248             self._setup_test_handlers(mock_pluginloader, mock__read_dependency_file, mock_sorter)
249
250         updater = CMUpdate('test_plugin_path', 'test_server_ip', 'test_server_port',
251                            'test_client_lib_impl_module', 'test_verbose_logger')
252
253         mock_confman = mock.MagicMock()
254         mock_confman.__str__.return_value = 'raise exception'
255
256         with self.assertRaises(Exception) as context:
257             updater.update(mock_confman)
258
259         # TODO:verify plugin(s) before exception are called.
260         mock_logging.warning.assert_called_with('Update handler %s failed: %s',
261                                                 'test_handler_a',
262                                                 'test_update_exception')
263
264     @mock.patch('cmframework.lib.cmupdateimpl.logging')
265     def test_dependency_files(self, mock_logging):
266         with mock.patch('cmframework.lib.cmupdateimpl.open', create=True) as mock_open:
267             mock_open.return_value = mock.MagicMock(spec=file)
268             file_handle = mock_open.return_value.__enter__.return_value
269
270             file_handle.readline.side_effect = IOError('File not found')
271             before, after = CMUpdateImpl._read_dependency_file('testexception: not existing')
272             assert before == []
273             assert after == []
274             mock_logging.debug.assert_called_with('Dependency file %s not found.',
275                                                   'testexception: not existing')
276
277             file_handle.readline.side_effect = [None]
278             before, after = CMUpdateImpl._read_dependency_file('./test_deps/1.deps')
279             assert before == []
280             assert after == []
281
282             file_handle.readline.side_effect = ['foo', 'bar', None]
283             before, after = CMUpdateImpl._read_dependency_file('./test_deps/1.deps')
284             assert before == []
285             assert after == []
286
287             file_handle.readline.side_effect = ['Before: a, b', 'After: c, d', None]
288             before, after = CMUpdateImpl._read_dependency_file('./test_deps/1.deps')
289             assert before == ['a', 'b']
290             assert after == ['c', 'd']
291
292             file_handle.readline.side_effect = ['foo',
293                                                 'Before: a, b',
294                                                 'bar',
295                                                 'After: c, d',
296                                                 'something',
297                                                 None]
298             before, after = CMUpdateImpl._read_dependency_file('./test_deps/1.deps')
299             assert before == ['a', 'b']
300             assert after == ['c', 'd']
301
302             file_handle.readline.side_effect = ['After: c, d', 'Before: a, b', None]
303             before, after = CMUpdateImpl._read_dependency_file('./test_deps/1.deps')
304             assert before == ['a', 'b']
305             assert after == ['c', 'd']
306
307             file_handle.readline.side_effect = ['Before:a,b', 'After:c,d', None]
308             before, after = CMUpdateImpl._read_dependency_file('./test_deps/1.deps')
309             assert before == ['a', 'b']
310             assert after == ['c', 'd']
311
312             file_handle.readline.side_effect = ['Before:  a,  b  ', 'After:   c,    d   ', None]
313             before, after = CMUpdateImpl._read_dependency_file('./test_deps/1.deps')
314             assert before == ['a', 'b']
315             assert after == ['c', 'd']
316
317 if __name__ == '__main__':
318     unittest.main()