Roll src/third_party/WebKit a3b4a2e:7441784 (svn 202551:202552)
[chromium-blink-merge.git] / tools / telemetry / catapult_base / dependency_manager / base_config_unittest.py
blob5beee8c44ebe7e0b10a1661ecb40ef8911dbb0bf
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.
5 import os
6 import unittest
8 import mock
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.
28 """
29 def setUp(self):
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,
35 'dependencies': {}}
37 dependency_dict = {
38 'dep': {
39 'cloud_storage_base_folder': 'cs_base_folder1',
40 'cloud_storage_bucket': 'bucket1',
41 'file_info': {
42 'plat1_arch1': {
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']
49 'plat1_arch2': {
50 'cloud_storage_hash': 'hash112',
51 'download_path': 'download_path112',
52 'cs_remote_path': 'cs_path112',
53 'local_paths': ['local_path1120', 'local_path1121']
55 'win_arch1': {
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,
79 indent=2)
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.
93 @mock.patch(
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
100 # Writable config.
101 config = self.config_class('file_path', writable=True)
102 self.assertEqual(self.GetConfigDataFromDict(self.empty_dict),
103 config._config_data)
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),
122 config._config_data)
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),
138 config._config_data)
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),
163 config._config_data)
164 self.assertTrue(config._writable)
165 with self.assertRaises(exceptions.ReadWriteError):
166 for _ in config.IterDependencyInfo():
167 pass
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),
180 config._config_data)
181 expected_dep_info = ['dep_info0', 'dep_info1', 'dep_info2']
182 dep_info_mock.side_effect = expected_dep_info
183 expected_calls = [
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',
193 cs_hash='hash1w1',
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')])]
198 deps_seen = []
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()
210 expected_calls = [
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',
215 version_in_cs=None,
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',
224 version_in_cs=None,
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())
285 with mock.patch(
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():
289 pass
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 = {
297 'dep1': {
298 'cloud_storage_base_folder': 'cs_base_folder1',
299 'cloud_storage_bucket': 'bucket1',
300 'file_info': {
301 'plat1_arch1': {
302 'cloud_storage_hash': 'hash111',
303 'download_path': 'download_path111',
304 'version_in_cs': '111.111.111',
305 'local_paths': ['local_path1110', 'local_path1111']
307 'plat1_arch2': {
308 'cloud_storage_hash': 'hash112',
309 'download_path': 'download_path112',
310 'version_in_cs': '111.111.111',
311 'local_paths': ['local_path1120', 'local_path1121']
313 'plat2_arch1': {
314 'cloud_storage_hash': 'hash121',
315 'download_path': 'download_path121',
316 'local_paths': ['local_path1210', 'local_path1211']
318 'win_arch1': {
319 'cloud_storage_hash': 'hash1w1',
320 'download_path': 'download\\path\\1w1',
321 'local_paths': ['local\\path\\1w10', 'local\\path\\1w11']
325 'dep2': {
326 'cloud_storage_base_folder': 'cs/base/folder2',
327 'cloud_storage_bucket': 'bucket2',
328 'file_info': {
329 'win_arch2': {
330 'cloud_storage_hash': 'hash2w2',
331 'download_path': 'download\\path\\2w2'
333 'plat2_arch1': {
334 'cloud_storage_hash': 'hash221',
335 'download_path': 'download/path/221',
336 'local_paths': ['local/path/2210']
338 'plat3_arch3': {
339 'local_paths': ['local_path2330', 'local_path2331']
343 'dep3': {
344 'cloud_storage_base_folder': 'cs_base_folder3',
345 'cloud_storage_bucket': 'bucket3',
346 'file_info': {
347 'default': {
348 'cloud_storage_hash': 'hash3d',
349 'download_path': 'download_path3d',
350 'local_paths': ['local_path3d0']
354 'dep4': {
355 'cloud_storage_bucket': 'bucket4',
356 'file_info': {
357 'default': {
358 'cloud_storage_hash': 'hash4d',
359 'download_path': 'download_path4d',
360 'local_paths': ['local_path4d0', 'local_path4d1']
362 'plat1_arch2': {
363 'cloud_storage_hash': 'hash412',
364 'download_path': 'download_path412'
366 'plat2_arch1': {
367 'cloud_storage_hash': 'hash421',
368 'download_path': 'download_path421',
369 'local_paths': ['local_path4210']
374 return expected_config_data