1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
10 from catapult_base
.dependency_manager
import base_config
11 from catapult_base
.dependency_manager
import dependency_info
12 from catapult_base
.dependency_manager
import exceptions
15 class BaseConfigTest(unittest
.TestCase
):
16 """ Subclassable unittests for BaseConfig.
17 For subclasses: override setUp, GetConfigDataFromDict,
18 and EndToEndExpectedConfigData as needed.
20 setUp must set the following properties:
21 self.config_type: String returnedd from GetConfigType in config subclass.
22 self.config_class: the class for the config subclass.
23 self.config_module: importable module for the config subclass.
24 self.empty_dict: expected dictionary for an empty config, as it would be
25 stored in a json file.
26 self.one_dep_dict: example dictionary for a config with one dependency,
27 as it would be stored in a json file.
30 self
.config_type
= 'BaseConfig'
31 self
.config_class
= base_config
.BaseConfig
32 self
.config_module
= 'catapult_base.dependency_manager.base_config'
34 self
.empty_dict
= {'config_type': self
.config_type
,
39 'cloud_storage_base_folder': 'cs_base_folder1',
40 'cloud_storage_bucket': 'bucket1',
43 'cloud_storage_hash': 'hash111',
44 'download_path': 'download_path111',
45 'cs_remote_path': 'cs_path111',
46 'version_in_cs': 'version_111',
47 'local_paths': ['local_path1110', 'local_path1111']
50 'cloud_storage_hash': 'hash112',
51 'download_path': 'download_path112',
52 'cs_remote_path': 'cs_path112',
53 'local_paths': ['local_path1120', 'local_path1121']
56 'cloud_storage_hash': 'hash1w1',
57 'download_path': 'download\\path\\1w1',
58 'cs_remote_path': 'cs_path1w1',
59 'local_paths': ['local\\path\\1w10', 'local\\path\\1w11']
64 self
.one_dep_dict
= {'config_type': self
.config_type
,
65 'dependencies': dependency_dict
}
67 def GetConfigDataFromDict(self
, config_dict
):
68 return config_dict
.get('dependencies', {})
71 # Init is not meant to be overridden, so we should be mocking the
72 # base_config's json module, even in subclasses.
73 @mock.patch('catapult_base.dependency_manager.base_config.json.dump')
74 @mock.patch('os.path.exists')
75 @mock.patch('__builtin__.open')
76 def testCreateEmptyConfig(self
, open_mock
, exists_mock
, dump_mock
):
77 exists_mock
.return_value
= False
78 expected_dump
= mock
.call(self
.empty_dict
, mock
.ANY
, sort_keys
=True,
80 expected_open
= mock
.call('file_path', 'w')
81 config_dict
= self
.config_class
.CreateEmptyConfig('file_path')
82 self
.assertEqual(dump_mock
.call_args
, expected_dump
)
83 self
.assertEqual(expected_open
, open_mock
.call_args
)
84 self
.assertEqual(self
.empty_dict
, config_dict
)
86 exists_mock
.return_value
= True
87 self
.assertRaises(ValueError,
88 self
.config_class
.CreateEmptyConfig
, 'file_path')
91 # Init is not meant to be overridden, so we should be mocking the
92 # base_config's json module, even in subclasses.
94 'catapult_base.dependency_manager.base_config.BaseConfig.CreateEmptyConfig') #pylint: disable=line-too-long
95 @mock.patch('catapult_base.dependency_manager.base_config.json')
96 @mock.patch('os.path')
97 @mock.patch('__builtin__.open')
98 def testInitNoFile(self
, open_mock
, path_mock
, json_mock
, create_config_mock
):
99 path_mock
.exists
.return_value
= False
101 config
= self
.config_class('file_path', writable
=True)
102 self
.assertEqual(self
.GetConfigDataFromDict(self
.empty_dict
),
104 # Not writable config.
105 self
.assertRaises(exceptions
.EmptyConfigError
,
106 self
.config_class
, 'file_path')
107 create_config_mock
.assert_called_once_with('file_path')
110 @mock.patch('os.path')
111 @mock.patch('__builtin__.open')
112 def testInitBaseProperties(self
, open_mock
, path_mock
):
113 # Init is not meant to be overridden, so we should be mocking the
114 # base_config's json module, even in subclasses.
115 json_module
= 'catapult_base.dependency_manager.base_config.json'
116 with mock
.patch(json_module
) as json_mock
:
117 json_mock
.load
.return_value
= self
.empty_dict
.copy()
118 config
= self
.config_class('file_path')
119 self
.assertEqual('file_path', config
._config
_path
)
120 self
.assertEqual(self
.config_type
, config
.GetConfigType())
121 self
.assertEqual(self
.GetConfigDataFromDict(self
.empty_dict
),
125 @mock.patch('catapult_base.dependency_manager.dependency_info.DependencyInfo')
126 @mock.patch('os.path')
127 @mock.patch('__builtin__.open')
128 def testInitWithDependencies(self
, open_mock
, path_mock
, dep_info_mock
):
129 # Init is not meant to be overridden, so we should be mocking the
130 # base_config's json module, even in subclasses.
131 json_module
= 'catapult_base.dependency_manager.base_config.json'
132 with mock
.patch(json_module
) as json_mock
:
133 json_mock
.load
.return_value
= self
.one_dep_dict
134 config
= self
.config_class('file_path')
135 self
.assertEqual('file_path', config
._config
_path
)
136 self
.assertEqual(self
.config_type
, config
.GetConfigType())
137 self
.assertEqual(self
.GetConfigDataFromDict(self
.one_dep_dict
),
140 def testFormatPath(self
):
141 self
.assertEqual(None, self
.config_class
._FormatPath
(None))
142 self
.assertEqual('', self
.config_class
._FormatPath
(''))
143 self
.assertEqual('some_string',
144 self
.config_class
._FormatPath
('some_string'))
146 expected_path
= os
.path
.join('some', 'file', 'path')
147 self
.assertEqual(expected_path
,
148 self
.config_class
._FormatPath
('some/file/path'))
149 self
.assertEqual(expected_path
,
150 self
.config_class
._FormatPath
('some\\file\\path'))
152 @mock.patch('catapult_base.dependency_manager.base_config.json')
153 @mock.patch('catapult_base.dependency_manager.dependency_info.DependencyInfo')
154 @mock.patch('os.path.exists')
155 @mock.patch('__builtin__.open')
156 def testIterDependenciesError(
157 self
, open_mock
, exists_mock
, dep_info_mock
, json_mock
):
158 # Init is not meant to be overridden, so we should be mocking the
159 # base_config's json module, even in subclasses.
160 json_mock
.load
.return_value
= self
.one_dep_dict
161 config
= self
.config_class('file_path', writable
=True)
162 self
.assertEqual(self
.GetConfigDataFromDict(self
.one_dep_dict
),
164 self
.assertTrue(config
._writable
)
165 with self
.assertRaises(exceptions
.ReadWriteError
):
166 for _
in config
.IterDependencyInfo():
169 @mock.patch('catapult_base.dependency_manager.base_config.json')
170 @mock.patch('catapult_base.dependency_manager.dependency_info.DependencyInfo')
171 @mock.patch('os.path.exists')
172 @mock.patch('__builtin__.open')
173 def testIterDependencies(
174 self
, open_mock
, exists_mock
, dep_info_mock
, json_mock
):
175 # Init is not meant to be overridden, so we should be mocking the
176 # base_config's json module, even in subclasses.
177 json_mock
.load
.return_value
= self
.one_dep_dict
178 config
= self
.config_class('file_path')
179 self
.assertEqual(self
.GetConfigDataFromDict(self
.one_dep_dict
),
181 expected_dep_info
= ['dep_info0', 'dep_info1', 'dep_info2']
182 dep_info_mock
.side_effect
= expected_dep_info
184 mock
.call('dep', 'plat1_arch1', 'file_path', cs_bucket
='bucket1',
185 cs_hash
='hash111', download_path
='download_path111',
186 cs_remote_path
='cs_path111',
187 local_paths
=['local_path1110', 'local_path1111']),
188 mock
.call('dep', 'plat1_arch1', 'file_path', cs_bucket
='bucket1',
189 cs_hash
='hash112', download_path
='download_path112',
190 cs_remote_path
='cs_path112',
191 local_paths
=['local_path1120', 'local_path1121']),
192 mock
.call('dep', 'win_arch1', 'file_path', cs_bucket
='bucket1',
194 download_path
=os
.path
.join('download', 'path', '1w1'),
195 cs_remote_path
='cs_path1w1',
196 local_paths
=[os
.path
.join('download', 'path', '1w10'),
197 os
.path
.join('download', 'path', '1w11')])]
199 for dep_info
in config
.IterDependencyInfo():
200 deps_seen
.append(dep_info
)
201 dep_info_mock
.assert_call_args(expected_calls
)
202 self
.assertItemsEqual(expected_dep_info
, deps_seen
)
204 @mock.patch('__builtin__.open')
205 def testConfigEndToEnd(self
, open_mock
):
206 file_path
= os
.path
.join(os
.path
.dirname(__file__
),
207 'test%s.json' % self
.config_type
)
208 dir_path
= os
.path
.dirname(file_path
)
209 expected_config_data
= self
.EndToEndExpectedConfigData()
211 mock
.call('dep4', 'default', file_path
, cs_bucket
='bucket4',
212 local_paths
=[os
.path
.join(dir_path
, 'local_path4d0'),
213 os
.path
.join(dir_path
, 'local_path4d1')],
214 cs_remote_path
='dep4_hash4d', cs_hash
='hash4d',
216 download_path
=os
.path
.join(dir_path
, 'download_path4d')),
217 mock
.call('dep4', 'plat1_arch2', file_path
, cs_bucket
='bucket4',
218 local_paths
=[], cs_remote_path
='dep4_hash412',
219 cs_hash
='hash412', version_in_cs
=None,
220 download_path
=os
.path
.join(dir_path
, 'download_path412')),
221 mock
.call('dep4', 'plat2_arch1', file_path
, cs_bucket
='bucket4',
222 local_paths
=[os
.path
.join(dir_path
, 'local_path4210')],
223 cs_remote_path
='dep4_hash421', cs_hash
='hash421',
225 download_path
=os
.path
.join(dir_path
, 'download_path421')),
226 mock
.call('dep1', 'plat1_arch1', file_path
, cs_bucket
='bucket1',
227 local_paths
=[os
.path
.join(dir_path
, 'local_path1110'),
228 os
.path
.join(dir_path
, 'local_path1111')],
229 cs_remote_path
='cs_base_folder1/dep1_hash111',
230 cs_hash
='hash111', version_in_cs
='111.111.111',
231 download_path
=os
.path
.join(dir_path
, 'download_path111')),
232 mock
.call('dep1', 'plat1_arch2', file_path
, cs_bucket
='bucket1',
233 local_paths
=[os
.path
.join(dir_path
, 'local_path1120'),
234 os
.path
.join(dir_path
, 'local_path1121')],
235 cs_remote_path
='cs_base_folder1/dep1_hash112',
236 cs_hash
='hash112', version_in_cs
='111.111.111',
237 download_path
=os
.path
.join(dir_path
, 'download_path112')),
238 mock
.call('dep1', 'plat2_arch1', file_path
, cs_bucket
='bucket1',
239 local_paths
=[os
.path
.join(dir_path
, 'local_path1210'),
240 os
.path
.join(dir_path
, 'local_path1211')],
241 cs_remote_path
='cs_base_folder1/dep1_hash121',
242 cs_hash
='hash121', version_in_cs
=None,
243 download_path
=os
.path
.join(dir_path
, 'download_path121')),
244 mock
.call('dep1', 'win_arch1', file_path
, cs_bucket
='bucket1',
245 local_paths
=[os
.path
.join(dir_path
, 'local', 'path', '1w10'),
246 os
.path
.join(dir_path
, 'local', 'path', '1w11')],
247 cs_remote_path
='cs_base_folder1/dep1_hash1w1',
248 cs_hash
='hash1w1', version_in_cs
=None,
249 download_path
=os
.path
.join(
250 dir_path
, 'download', 'path', '1w1')),
251 mock
.call('dep3', 'default', file_path
, cs_bucket
='bucket3',
252 local_paths
=[os
.path
.join(dir_path
, 'local_path3d0')],
253 cs_remote_path
='cs_base_folder3/dep3_hash3d',
254 cs_hash
='hash3d', version_in_cs
=None,
255 download_path
=os
.path
.join(dir_path
, 'download_path3d')),
256 mock
.call('dep2', 'win_arch2', file_path
, cs_bucket
='bucket2',
257 local_paths
=[], cs_remote_path
='cs/base/folder2/dep2_hash2w2',
258 cs_hash
='hash2w2', version_in_cs
=None,
259 download_path
=os
.path
.join(
260 dir_path
, 'download', 'path', '2w2')),
261 mock
.call('dep2', 'plat3_arch3', file_path
,
262 local_paths
=[os
.path
.join(dir_path
, 'local_path2330'),
263 os
.path
.join(dir_path
, 'local_path2331')]),
264 mock
.call('dep2', 'plat2_arch1', file_path
, cs_bucket
='bucket2',
265 local_paths
=[os
.path
.join(
266 dir_path
, 'local', 'path', '2210')],
267 cs_remote_path
='cs/base/folder2/dep2_hash221',
268 cs_hash
='hash221', version_in_cs
=None,
269 download_path
=os
.path
.join(
270 dir_path
, 'download', 'path', '221')),
272 json_dict
= {'config_type': self
.config_type
,
273 'dependencies': expected_config_data
}
274 # Init is not meant to be overridden, so we should be mocking the
275 # base_config's json module, even in subclasses.
276 json_module
= 'catapult_base.dependency_manager.base_config.json'
277 with mock
.patch(json_module
) as json_mock
:
278 with mock
.patch('os.path') as path_mock
:
279 path_mock
.exists
.return_value
= True
280 json_mock
.load
.return_value
= json_dict
281 config
= self
.config_class(file_path
)
282 # Make sure the basic info was set as expected in init.
283 self
.assertEqual(expected_config_data
, config
._config
_data
)
284 self
.assertEqual(self
.config_type
, config
.GetConfigType())
286 'catapult_base.dependency_manager.base_config.dependency_info.DependencyInfo', #pylint: disable=line-too-long
287 autospec
=dependency_info
.DependencyInfo
) as dep_info_mock
:
288 for _
in config
.IterDependencyInfo():
290 # Make sure all of the DependencyInfo's were created correctly.
291 self
.assertItemsEqual(expected_calls
, dep_info_mock
.mock_calls
)
292 # Make sure we didn't change the config_data while creating the iterator.
293 self
.assertEqual(expected_config_data
, config
._config
_data
)
295 def EndToEndExpectedConfigData(self
):
296 expected_config_data
= {
298 'cloud_storage_base_folder': 'cs_base_folder1',
299 'cloud_storage_bucket': 'bucket1',
302 'cloud_storage_hash': 'hash111',
303 'download_path': 'download_path111',
304 'version_in_cs': '111.111.111',
305 'local_paths': ['local_path1110', 'local_path1111']
308 'cloud_storage_hash': 'hash112',
309 'download_path': 'download_path112',
310 'version_in_cs': '111.111.111',
311 'local_paths': ['local_path1120', 'local_path1121']
314 'cloud_storage_hash': 'hash121',
315 'download_path': 'download_path121',
316 'local_paths': ['local_path1210', 'local_path1211']
319 'cloud_storage_hash': 'hash1w1',
320 'download_path': 'download\\path\\1w1',
321 'local_paths': ['local\\path\\1w10', 'local\\path\\1w11']
326 'cloud_storage_base_folder': 'cs/base/folder2',
327 'cloud_storage_bucket': 'bucket2',
330 'cloud_storage_hash': 'hash2w2',
331 'download_path': 'download\\path\\2w2'
334 'cloud_storage_hash': 'hash221',
335 'download_path': 'download/path/221',
336 'local_paths': ['local/path/2210']
339 'local_paths': ['local_path2330', 'local_path2331']
344 'cloud_storage_base_folder': 'cs_base_folder3',
345 'cloud_storage_bucket': 'bucket3',
348 'cloud_storage_hash': 'hash3d',
349 'download_path': 'download_path3d',
350 'local_paths': ['local_path3d0']
355 'cloud_storage_bucket': 'bucket4',
358 'cloud_storage_hash': 'hash4d',
359 'download_path': 'download_path4d',
360 'local_paths': ['local_path4d0', 'local_path4d1']
363 'cloud_storage_hash': 'hash412',
364 'download_path': 'download_path412'
367 'cloud_storage_hash': 'hash421',
368 'download_path': 'download_path421',
369 'local_paths': ['local_path4210']
374 return expected_config_data