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