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. 4 5# pylint: disable=unused-argument 6 7from catapult_base import cloud_storage 8import mock 9from pyfakefs import fake_filesystem_unittest 10 11import dependency_manager 12from dependency_manager import exceptions 13 14 15class DependencyManagerTest(fake_filesystem_unittest.TestCase): 16 17 def setUp(self): 18 self.lp_info012 = dependency_manager.LocalPathInfo( 19 ['path0', 'path1', 'path2']) 20 self.cloud_storage_info = dependency_manager.CloudStorageInfo( 21 'cs_bucket', 'cs_hash', 'download_path', 'cs_remote_path') 22 23 self.dep_info = dependency_manager.DependencyInfo( 24 'dep', 'platform', 'config_file', local_path_info=self.lp_info012, 25 cloud_storage_info=self.cloud_storage_info) 26 self.setUpPyfakefs() 27 28 def tearDown(self): 29 self.tearDownPyfakefs() 30 31 # TODO(nednguyen): add a test that construct 32 # dependency_manager.DependencyManager from a list of DependencyInfo. 33 def testErrorInit(self): 34 with self.assertRaises(ValueError): 35 dependency_manager.DependencyManager(None) 36 with self.assertRaises(ValueError): 37 dependency_manager.DependencyManager('config_file?') 38 39 def testInitialUpdateDependencies(self): 40 dep_manager = dependency_manager.DependencyManager([]) 41 42 # Empty BaseConfig. 43 dep_manager._lookup_dict = {} 44 base_config_mock = mock.MagicMock(spec=dependency_manager.BaseConfig) 45 base_config_mock.IterDependencyInfo.return_value = iter([]) 46 dep_manager._UpdateDependencies(base_config_mock) 47 self.assertFalse(dep_manager._lookup_dict) 48 49 # One dependency/platform in a BaseConfig. 50 dep_manager._lookup_dict = {} 51 base_config_mock = mock.MagicMock(spec=dependency_manager.BaseConfig) 52 dep_info = mock.MagicMock(spec=dependency_manager.DependencyInfo) 53 dep = 'dependency' 54 plat = 'platform' 55 dep_info.dependency = dep 56 dep_info.platform = plat 57 base_config_mock.IterDependencyInfo.return_value = iter([dep_info]) 58 expected_lookup_dict = {dep: {plat: dep_info}} 59 dep_manager._UpdateDependencies(base_config_mock) 60 self.assertEqual(expected_lookup_dict, dep_manager._lookup_dict) 61 self.assertFalse(dep_info.Update.called) 62 63 # One dependency multiple platforms in a BaseConfig. 64 dep_manager._lookup_dict = {} 65 base_config_mock = mock.MagicMock(spec=dependency_manager.BaseConfig) 66 dep = 'dependency' 67 plat1 = 'platform1' 68 plat2 = 'platform2' 69 dep_info1 = mock.MagicMock(spec=dependency_manager.DependencyInfo) 70 dep_info1.dependency = dep 71 dep_info1.platform = plat1 72 dep_info2 = mock.MagicMock(spec=dependency_manager.DependencyInfo) 73 dep_info2.dependency = dep 74 dep_info2.platform = plat2 75 base_config_mock.IterDependencyInfo.return_value = iter([dep_info1, 76 dep_info2]) 77 expected_lookup_dict = {dep: {plat1: dep_info1, 78 plat2: dep_info2}} 79 dep_manager._UpdateDependencies(base_config_mock) 80 self.assertEqual(expected_lookup_dict, dep_manager._lookup_dict) 81 self.assertFalse(dep_info1.Update.called) 82 self.assertFalse(dep_info2.Update.called) 83 84 # Multiple dependencies, multiple platforms in a BaseConfig. 85 dep_manager._lookup_dict = {} 86 base_config_mock = mock.MagicMock(spec=dependency_manager.BaseConfig) 87 dep1 = 'dependency1' 88 dep2 = 'dependency2' 89 plat1 = 'platform1' 90 plat2 = 'platform2' 91 dep_info1 = mock.MagicMock(spec=dependency_manager.DependencyInfo) 92 dep_info1.dependency = dep1 93 dep_info1.platform = plat1 94 dep_info2 = mock.MagicMock(spec=dependency_manager.DependencyInfo) 95 dep_info2.dependency = dep1 96 dep_info2.platform = plat2 97 dep_info3 = mock.MagicMock(spec=dependency_manager.DependencyInfo) 98 dep_info3.dependency = dep2 99 dep_info3.platform = plat2 100 base_config_mock.IterDependencyInfo.return_value = iter( 101 [dep_info1, dep_info2, dep_info3]) 102 expected_lookup_dict = {dep1: {plat1: dep_info1, 103 plat2: dep_info2}, 104 dep2: {plat2: dep_info3}} 105 dep_manager._UpdateDependencies(base_config_mock) 106 self.assertEqual(expected_lookup_dict, dep_manager._lookup_dict) 107 self.assertFalse(dep_info1.Update.called) 108 self.assertFalse(dep_info2.Update.called) 109 self.assertFalse(dep_info3.Update.called) 110 111 def testFollowupUpdateDependenciesNoOverlap(self): 112 dep_manager = dependency_manager.DependencyManager([]) 113 dep = 'dependency' 114 dep1 = 'dependency1' 115 dep2 = 'dependency2' 116 dep3 = 'dependency3' 117 plat1 = 'platform1' 118 plat2 = 'platform2' 119 plat3 = 'platform3' 120 dep_info_a = mock.MagicMock(spec=dependency_manager.DependencyInfo) 121 dep_info_a.dependency = dep1 122 dep_info_a.platform = plat1 123 dep_info_b = mock.MagicMock(spec=dependency_manager.DependencyInfo) 124 dep_info_b.dependency = dep1 125 dep_info_b.platform = plat2 126 dep_info_c = mock.MagicMock(spec=dependency_manager.DependencyInfo) 127 dep_info_c.dependency = dep 128 dep_info_c.platform = plat1 129 130 start_lookup_dict = {dep: {plat1: dep_info_a, 131 plat2: dep_info_b}, 132 dep1: {plat1: dep_info_c}} 133 base_config_mock = mock.MagicMock(spec=dependency_manager.BaseConfig) 134 135 # Empty BaseConfig. 136 dep_manager._lookup_dict = start_lookup_dict.copy() 137 base_config_mock.IterDependencyInfo.return_value = iter([]) 138 dep_manager._UpdateDependencies(base_config_mock) 139 self.assertEqual(start_lookup_dict, dep_manager._lookup_dict) 140 141 # One dependency/platform in a BaseConfig. 142 dep_manager._lookup_dict = start_lookup_dict.copy() 143 dep_info = mock.MagicMock(spec=dependency_manager.DependencyInfo) 144 dep_info.dependency = dep3 145 dep_info.platform = plat1 146 base_config_mock.IterDependencyInfo.return_value = iter([dep_info]) 147 expected_lookup_dict = {dep: {plat1: dep_info_a, 148 plat2: dep_info_b}, 149 dep1: {plat1: dep_info_c}, 150 dep3: {plat3: dep_info}} 151 152 dep_manager._UpdateDependencies(base_config_mock) 153 self.assertItemsEqual(expected_lookup_dict, dep_manager._lookup_dict) 154 self.assertFalse(dep_info.Update.called) 155 self.assertFalse(dep_info_a.Update.called) 156 self.assertFalse(dep_info_b.Update.called) 157 self.assertFalse(dep_info_c.Update.called) 158 159 # One dependency multiple platforms in a BaseConfig. 160 dep_manager._lookup_dict = start_lookup_dict.copy() 161 dep_info1 = mock.MagicMock(spec=dependency_manager.DependencyInfo) 162 dep_info1.dependency = dep2 163 dep_info1.platform = plat1 164 dep_info2 = mock.MagicMock(spec=dependency_manager.DependencyInfo) 165 dep_info2.dependency = dep2 166 dep_info2.platform = plat2 167 base_config_mock.IterDependencyInfo.return_value = iter([dep_info1, 168 dep_info2]) 169 expected_lookup_dict = {dep: {plat1: dep_info_a, 170 plat2: dep_info_b}, 171 dep1: {plat1: dep_info_c}, 172 dep2: {plat1: dep_info1, 173 plat2: dep_info2}} 174 dep_manager._UpdateDependencies(base_config_mock) 175 self.assertEqual(expected_lookup_dict, dep_manager._lookup_dict) 176 self.assertFalse(dep_info1.Update.called) 177 self.assertFalse(dep_info2.Update.called) 178 self.assertFalse(dep_info_a.Update.called) 179 self.assertFalse(dep_info_b.Update.called) 180 self.assertFalse(dep_info_c.Update.called) 181 182 # Multiple dependencies, multiple platforms in a BaseConfig. 183 dep_manager._lookup_dict = start_lookup_dict.copy() 184 dep1 = 'dependency1' 185 plat1 = 'platform1' 186 plat2 = 'platform2' 187 dep_info1 = mock.MagicMock(spec=dependency_manager.DependencyInfo) 188 dep_info1.dependency = dep2 189 dep_info1.platform = plat1 190 dep_info2 = mock.MagicMock(spec=dependency_manager.DependencyInfo) 191 dep_info2.dependency = dep2 192 dep_info2.platform = plat2 193 dep_info3 = mock.MagicMock(spec=dependency_manager.DependencyInfo) 194 dep_info3.dependency = dep3 195 dep_info3.platform = plat2 196 base_config_mock.IterDependencyInfo.return_value = iter( 197 [dep_info1, dep_info2, dep_info3]) 198 expected_lookup_dict = {dep: {plat1: dep_info_a, 199 plat2: dep_info_b}, 200 dep1: {plat1: dep_info_c}, 201 dep2: {plat1: dep_info1, 202 plat2: dep_info2}, 203 dep3: {plat2: dep_info3}} 204 dep_manager._UpdateDependencies(base_config_mock) 205 self.assertEqual(expected_lookup_dict, dep_manager._lookup_dict) 206 self.assertFalse(dep_info1.Update.called) 207 self.assertFalse(dep_info2.Update.called) 208 self.assertFalse(dep_info3.Update.called) 209 self.assertFalse(dep_info_a.Update.called) 210 self.assertFalse(dep_info_b.Update.called) 211 self.assertFalse(dep_info_c.Update.called) 212 213 # Ensure the testing data wasn't corrupted. 214 self.assertEqual(start_lookup_dict, 215 {dep: {plat1: dep_info_a, 216 plat2: dep_info_b}, 217 dep1: {plat1: dep_info_c}}) 218 219 def testFollowupUpdateDependenciesWithCollisions(self): 220 dep_manager = dependency_manager.DependencyManager([]) 221 dep = 'dependency' 222 dep1 = 'dependency1' 223 dep2 = 'dependency2' 224 plat1 = 'platform1' 225 plat2 = 'platform2' 226 dep_info_a = mock.MagicMock(spec=dependency_manager.DependencyInfo) 227 dep_info_a.dependency = dep1 228 dep_info_a.platform = plat1 229 dep_info_b = mock.MagicMock(spec=dependency_manager.DependencyInfo) 230 dep_info_b.dependency = dep1 231 dep_info_b.platform = plat2 232 dep_info_c = mock.MagicMock(spec=dependency_manager.DependencyInfo) 233 dep_info_c.dependency = dep 234 dep_info_c.platform = plat1 235 236 start_lookup_dict = {dep: {plat1: dep_info_a, 237 plat2: dep_info_b}, 238 dep1: {plat1: dep_info_c}} 239 base_config_mock = mock.MagicMock(spec=dependency_manager.BaseConfig) 240 241 # One dependency/platform. 242 dep_manager._lookup_dict = start_lookup_dict.copy() 243 dep_info = mock.MagicMock(spec=dependency_manager.DependencyInfo) 244 dep_info.dependency = dep 245 dep_info.platform = plat1 246 base_config_mock.IterDependencyInfo.return_value = iter([dep_info]) 247 expected_lookup_dict = {dep: {plat1: dep_info_a, 248 plat2: dep_info_b}, 249 dep1: {plat1: dep_info_c}} 250 251 dep_manager._UpdateDependencies(base_config_mock) 252 self.assertItemsEqual(expected_lookup_dict, dep_manager._lookup_dict) 253 dep_info_a.Update.assert_called_once_with(dep_info) 254 self.assertFalse(dep_info.Update.called) 255 self.assertFalse(dep_info_b.Update.called) 256 self.assertFalse(dep_info_c.Update.called) 257 dep_info_a.reset_mock() 258 dep_info_b.reset_mock() 259 dep_info_c.reset_mock() 260 261 # One dependency multiple platforms in a BaseConfig. 262 dep_manager._lookup_dict = start_lookup_dict.copy() 263 dep_info1 = mock.MagicMock(spec=dependency_manager.DependencyInfo) 264 dep_info1.dependency = dep1 265 dep_info1.platform = plat1 266 dep_info2 = mock.MagicMock(spec=dependency_manager.DependencyInfo) 267 dep_info2.dependency = dep2 268 dep_info2.platform = plat2 269 base_config_mock.IterDependencyInfo.return_value = iter([dep_info1, 270 dep_info2]) 271 expected_lookup_dict = {dep: {plat1: dep_info_a, 272 plat2: dep_info_b}, 273 dep1: {plat1: dep_info_c}, 274 dep2: {plat2: dep_info2}} 275 dep_manager._UpdateDependencies(base_config_mock) 276 self.assertEqual(expected_lookup_dict, dep_manager._lookup_dict) 277 self.assertFalse(dep_info1.Update.called) 278 self.assertFalse(dep_info2.Update.called) 279 self.assertFalse(dep_info_a.Update.called) 280 self.assertFalse(dep_info_b.Update.called) 281 dep_info_c.Update.assert_called_once_with(dep_info1) 282 dep_info_a.reset_mock() 283 dep_info_b.reset_mock() 284 dep_info_c.reset_mock() 285 286 # Multiple dependencies, multiple platforms in a BaseConfig. 287 dep_manager._lookup_dict = start_lookup_dict.copy() 288 dep1 = 'dependency1' 289 plat1 = 'platform1' 290 plat2 = 'platform2' 291 dep_info1 = mock.MagicMock(spec=dependency_manager.DependencyInfo) 292 dep_info1.dependency = dep 293 dep_info1.platform = plat1 294 dep_info2 = mock.MagicMock(spec=dependency_manager.DependencyInfo) 295 dep_info2.dependency = dep1 296 dep_info2.platform = plat1 297 dep_info3 = mock.MagicMock(spec=dependency_manager.DependencyInfo) 298 dep_info3.dependency = dep2 299 dep_info3.platform = plat2 300 base_config_mock.IterDependencyInfo.return_value = iter( 301 [dep_info1, dep_info2, dep_info3]) 302 expected_lookup_dict = {dep: {plat1: dep_info_a, 303 plat2: dep_info_b}, 304 dep1: {plat1: dep_info_c}, 305 dep2: {plat2: dep_info3}} 306 dep_manager._UpdateDependencies(base_config_mock) 307 self.assertEqual(expected_lookup_dict, dep_manager._lookup_dict) 308 self.assertFalse(dep_info1.Update.called) 309 self.assertFalse(dep_info2.Update.called) 310 self.assertFalse(dep_info3.Update.called) 311 self.assertFalse(dep_info_b.Update.called) 312 dep_info_a.Update.assert_called_once_with(dep_info1) 313 dep_info_c.Update.assert_called_once_with(dep_info2) 314 315 # Collision error. 316 dep_manager._lookup_dict = start_lookup_dict.copy() 317 dep_info = mock.MagicMock(spec=dependency_manager.DependencyInfo) 318 dep_info.dependency = dep 319 dep_info.platform = plat1 320 base_config_mock.IterDependencyInfo.return_value = iter([dep_info]) 321 dep_info_a.Update.side_effect = ValueError 322 self.assertRaises(ValueError, 323 dep_manager._UpdateDependencies, base_config_mock) 324 325 # Ensure the testing data wasn't corrupted. 326 self.assertEqual(start_lookup_dict, 327 {dep: {plat1: dep_info_a, 328 plat2: dep_info_b}, 329 dep1: {plat1: dep_info_c}}) 330 331 def testGetDependencyInfo(self): 332 dep_manager = dependency_manager.DependencyManager([]) 333 self.assertFalse(dep_manager._lookup_dict) 334 335 # No dependencies in the dependency manager. 336 self.assertEqual(None, dep_manager._GetDependencyInfo('missing_dep', 337 'missing_plat')) 338 339 dep_manager._lookup_dict = {'dep1': {'plat1': 'dep_info11', 340 'plat2': 'dep_info12', 341 'plat3': 'dep_info13'}, 342 'dep2': {'plat1': 'dep_info11', 343 'plat2': 'dep_info21', 344 'plat3': 'dep_info23', 345 'default': 'dep_info2d'}, 346 'dep3': {'plat1': 'dep_info31', 347 'plat2': 'dep_info32', 348 'default': 'dep_info3d'}} 349 # Dependency not in the dependency manager. 350 self.assertEqual(None, dep_manager._GetDependencyInfo( 351 'missing_dep', 'missing_plat')) 352 # Dependency in the dependency manager, but not the platform. No default. 353 self.assertEqual(None, dep_manager._GetDependencyInfo( 354 'dep1', 'missing_plat')) 355 # Dependency in the dependency manager, but not the platform, but a default 356 # exists. 357 self.assertEqual('dep_info2d', dep_manager._GetDependencyInfo( 358 'dep2', 'missing_plat')) 359 # Dependency and platform in the dependency manager. A default exists. 360 self.assertEqual('dep_info23', dep_manager._GetDependencyInfo( 361 'dep2', 'plat3')) 362 # Dependency and platform in the dependency manager. No default exists. 363 self.assertEqual('dep_info12', dep_manager._GetDependencyInfo( 364 'dep1', 'plat2')) 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 @mock.patch( 384 'dependency_manager.dependency_info.DependencyInfo.GetRemotePath') # pylint: disable=line-too-long 385 def testFetchPathUnititializedDependency( 386 self, cs_path_mock): 387 dep_manager = dependency_manager.DependencyManager([]) 388 self.assertFalse(cs_path_mock.call_args) 389 cs_path = 'cs_path' 390 cs_path_mock.return_value = cs_path 391 392 # Empty lookup_dict 393 with self.assertRaises(exceptions.NoPathFoundError): 394 dep_manager.FetchPath('dep', 'plat_arch_x86') 395 396 # Non-empty lookup dict that doesn't contain the dependency we're looking 397 # for. 398 dep_manager._lookup_dict = {'dep1': mock.MagicMock(), 399 'dep2': mock.MagicMock()} 400 with self.assertRaises(exceptions.NoPathFoundError): 401 dep_manager.FetchPath('dep', 'plat_arch_x86') 402 403 @mock.patch('os.path') 404 @mock.patch( 405 'dependency_manager.DependencyManager._GetDependencyInfo') 406 @mock.patch( 407 'dependency_manager.dependency_info.DependencyInfo.GetRemotePath') # pylint: disable=line-too-long 408 def testFetchPathLocalFile(self, cs_path_mock, dep_info_mock, path_mock): 409 dep_manager = dependency_manager.DependencyManager([]) 410 self.assertFalse(cs_path_mock.call_args) 411 cs_path = 'cs_path' 412 dep_info = self.dep_info 413 cs_path_mock.return_value = cs_path 414 # The DependencyInfo returned should be passed through to LocalPath. 415 dep_info_mock.return_value = dep_info 416 417 # Non-empty lookup dict that contains the dependency we're looking for. 418 # Local path exists. 419 dep_manager._lookup_dict = {'dep': {'platform' : self.dep_info}, 420 'dep2': mock.MagicMock()} 421 self.fs.CreateFile('path1') 422 found_path = dep_manager.FetchPath('dep', 'platform') 423 424 self.assertEqual('path1', found_path) 425 self.assertFalse(cs_path_mock.call_args) 426 427 428 @mock.patch( 429 'dependency_manager.dependency_info.DependencyInfo.GetRemotePath') # pylint: disable=line-too-long 430 def testFetchPathRemoteFile( 431 self, cs_path_mock): 432 dep_manager = dependency_manager.DependencyManager([]) 433 self.assertFalse(cs_path_mock.call_args) 434 cs_path = 'cs_path' 435 def FakeCSPath(): 436 self.fs.CreateFile(cs_path) 437 return cs_path 438 cs_path_mock.side_effect = FakeCSPath 439 440 # Non-empty lookup dict that contains the dependency we're looking for. 441 # Local path doesn't exist, but cloud_storage_path is downloaded. 442 dep_manager._lookup_dict = {'dep': {'platform' : self.dep_info, 443 'plat1': mock.MagicMock()}, 444 'dep2': {'plat2': mock.MagicMock()}} 445 found_path = dep_manager.FetchPath('dep', 'platform') 446 self.assertEqual(cs_path, found_path) 447 448 449 @mock.patch( 450 'dependency_manager.dependency_info.DependencyInfo.GetRemotePath') # pylint: disable=line-too-long 451 def testFetchPathError( 452 self, cs_path_mock): 453 dep_manager = dependency_manager.DependencyManager([]) 454 self.assertFalse(cs_path_mock.call_args) 455 cs_path_mock.return_value = None 456 dep_manager._lookup_dict = {'dep': {'platform' : self.dep_info, 457 'plat1': mock.MagicMock()}, 458 'dep2': {'plat2': mock.MagicMock()}} 459 # Non-empty lookup dict that contains the dependency we're looking for. 460 # Local path doesn't exist, and cloud_storage path wasn't successfully 461 # found. 462 self.assertRaises(exceptions.NoPathFoundError, 463 dep_manager.FetchPath, 'dep', 'platform') 464 465 cs_path_mock.side_effect = cloud_storage.CredentialsError 466 self.assertRaises(cloud_storage.CredentialsError, 467 dep_manager.FetchPath, 'dep', 'platform') 468 469 cs_path_mock.side_effect = cloud_storage.CloudStorageError 470 self.assertRaises(cloud_storage.CloudStorageError, 471 dep_manager.FetchPath, 'dep', 'platform') 472 473 cs_path_mock.side_effect = cloud_storage.PermissionError 474 self.assertRaises(cloud_storage.PermissionError, 475 dep_manager.FetchPath, 'dep', 'platform') 476 477 def testLocalPath(self): 478 dep_manager = dependency_manager.DependencyManager([]) 479 # Empty lookup_dict 480 with self.assertRaises(exceptions.NoPathFoundError): 481 dep_manager.LocalPath('dep', 'plat') 482 483 def testLocalPathNoDependency(self): 484 # Non-empty lookup dict that doesn't contain the dependency we're looking 485 # for. 486 dep_manager = dependency_manager.DependencyManager([]) 487 dep_manager._lookup_dict = {'dep1': mock.MagicMock(), 488 'dep2': mock.MagicMock()} 489 with self.assertRaises(exceptions.NoPathFoundError): 490 dep_manager.LocalPath('dep', 'plat') 491 492 def testLocalPathExists(self): 493 # Non-empty lookup dict that contains the dependency we're looking for. 494 # Local path exists. 495 dep_manager = dependency_manager.DependencyManager([]) 496 dep_manager._lookup_dict = {'dependency' : {'platform': self.dep_info}, 497 'dep1': mock.MagicMock(), 498 'dep2': mock.MagicMock()} 499 self.fs.CreateFile('path1') 500 found_path = dep_manager.LocalPath('dependency', 'platform') 501 502 self.assertEqual('path1', found_path) 503 504 def testLocalPathMissingPaths(self): 505 # Non-empty lookup dict that contains the dependency we're looking for. 506 # Local path is found but doesn't exist. 507 dep_manager = dependency_manager.DependencyManager([]) 508 dep_manager._lookup_dict = {'dependency' : {'platform': self.dep_info}, 509 'dep1': mock.MagicMock(), 510 'dep2': mock.MagicMock()} 511 self.assertRaises(exceptions.NoPathFoundError, 512 dep_manager.LocalPath, 'dependency', 'platform') 513 514 def testLocalPathNoPaths(self): 515 # Non-empty lookup dict that contains the dependency we're looking for. 516 # Local path isn't found. 517 dep_manager = dependency_manager.DependencyManager([]) 518 dep_info = dependency_manager.DependencyInfo( 519 'dep', 'platform', 'config_file', 520 cloud_storage_info=self.cloud_storage_info) 521 dep_manager._lookup_dict = {'dependency' : {'platform': dep_info}, 522 'dep1': mock.MagicMock(), 523 'dep2': mock.MagicMock()} 524 self.assertRaises(exceptions.NoPathFoundError, 525 dep_manager.LocalPath, 'dependency', 'platform') 526 527