Home | History | Annotate | Download | only in dependency_manager
      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 os
      8 import unittest
      9 
     10 from py_utils import cloud_storage
     11 import mock
     12 from pyfakefs import fake_filesystem_unittest
     13 from pyfakefs import fake_filesystem
     14 
     15 import dependency_manager
     16 from dependency_manager import uploader
     17 
     18 
     19 class BaseConfigCreationAndUpdateUnittests(fake_filesystem_unittest.TestCase):
     20   def setUp(self):
     21     self.addTypeEqualityFunc(uploader.CloudStorageUploader,
     22                              uploader.CloudStorageUploader.__eq__)
     23     self.setUpPyfakefs()
     24     self.dependencies = {
     25         'dep1': {'cloud_storage_bucket': 'bucket1',
     26                  'cloud_storage_base_folder': 'dependencies_folder',
     27                  'file_info': {
     28                      'plat1': {
     29                          'cloud_storage_hash': 'hash11',
     30                          'download_path': '../../relative/dep1/path1'},
     31                      'plat2': {
     32                          'cloud_storage_hash': 'hash12',
     33                          'download_path': '../../relative/dep1/path2'}}},
     34         'dep2': {'cloud_storage_bucket': 'bucket2',
     35                  'file_info': {
     36                      'plat1': {
     37                          'cloud_storage_hash': 'hash21',
     38                          'download_path': '../../relative/dep2/path1'},
     39                      'plat2': {
     40                          'cloud_storage_hash': 'hash22',
     41                          'download_path': '../../relative/dep2/path2'}}}}
     42 
     43     self.expected_file_lines = [
     44       # pylint: disable=bad-continuation
     45       '{', '"config_type": "BaseConfig",', '"dependencies": {',
     46         '"dep1": {', '"cloud_storage_base_folder": "dependencies_folder",',
     47           '"cloud_storage_bucket": "bucket1",', '"file_info": {',
     48             '"plat1": {', '"cloud_storage_hash": "hash11",',
     49               '"download_path": "../../relative/dep1/path1"', '},',
     50             '"plat2": {', '"cloud_storage_hash": "hash12",',
     51               '"download_path": "../../relative/dep1/path2"', '}', '}', '},',
     52         '"dep2": {', '"cloud_storage_bucket": "bucket2",', '"file_info": {',
     53             '"plat1": {', '"cloud_storage_hash": "hash21",',
     54               '"download_path": "../../relative/dep2/path1"', '},',
     55             '"plat2": {', '"cloud_storage_hash": "hash22",',
     56               '"download_path": "../../relative/dep2/path2"', '}', '}', '}',
     57       '}', '}']
     58 
     59     self.file_path = os.path.abspath(os.path.join(
     60         'path', 'to', 'config', 'file'))
     61 
     62     self.new_dep_path = 'path/to/new/dep'
     63     self.fs.CreateFile(self.new_dep_path)
     64     self.new_dep_hash = 'A23B56B7F23E798601F'
     65     self.new_dependencies = {
     66         'dep1': {'cloud_storage_bucket': 'bucket1',
     67                  'cloud_storage_base_folder': 'dependencies_folder',
     68                  'file_info': {
     69                      'plat1': {
     70                          'cloud_storage_hash': 'hash11',
     71                          'download_path': '../../relative/dep1/path1'},
     72                      'plat2': {
     73                          'cloud_storage_hash': self.new_dep_hash,
     74                          'download_path': '../../relative/dep1/path2'}}},
     75         'dep2': {'cloud_storage_bucket': 'bucket2',
     76                  'file_info': {
     77                      'plat1': {
     78                          'cloud_storage_hash': 'hash21',
     79                          'download_path': '../../relative/dep2/path1'},
     80                      'plat2': {
     81                          'cloud_storage_hash': 'hash22',
     82                          'download_path': '../../relative/dep2/path2'}}}}
     83     self.new_bucket = 'bucket1'
     84     self.new_remote_path = 'dependencies_folder/dep1_%s' % self.new_dep_hash
     85     self.new_pending_upload = uploader.CloudStorageUploader(
     86         self.new_bucket, self.new_remote_path, self.new_dep_path)
     87     self.expected_new_backup_path = '.'.join([self.new_remote_path, 'old'])
     88     self.new_expected_file_lines = [
     89       # pylint: disable=bad-continuation
     90       '{', '"config_type": "BaseConfig",', '"dependencies": {',
     91         '"dep1": {', '"cloud_storage_base_folder": "dependencies_folder",',
     92           '"cloud_storage_bucket": "bucket1",', '"file_info": {',
     93             '"plat1": {', '"cloud_storage_hash": "hash11",',
     94               '"download_path": "../../relative/dep1/path1"', '},',
     95             '"plat2": {', '"cloud_storage_hash": "%s",' % self.new_dep_hash,
     96               '"download_path": "../../relative/dep1/path2"', '}', '}', '},',
     97         '"dep2": {', '"cloud_storage_bucket": "bucket2",', '"file_info": {',
     98             '"plat1": {', '"cloud_storage_hash": "hash21",',
     99               '"download_path": "../../relative/dep2/path1"', '},',
    100             '"plat2": {', '"cloud_storage_hash": "hash22",',
    101               '"download_path": "../../relative/dep2/path2"', '}', '}', '}',
    102       '}', '}']
    103 
    104     self.final_dep_path = 'path/to/final/dep'
    105     self.fs.CreateFile(self.final_dep_path)
    106     self.final_dep_hash = 'B34662F23B56B7F98601F'
    107     self.final_bucket = 'bucket2'
    108     self.final_remote_path = 'dep1_%s' % self.final_dep_hash
    109     self.final_pending_upload = uploader.CloudStorageUploader(
    110         self.final_bucket, self.final_remote_path, self.final_dep_path)
    111     self.expected_final_backup_path = '.'.join([self.final_remote_path,
    112                                                 'old'])
    113     self.final_dependencies = {
    114         'dep1': {'cloud_storage_bucket': 'bucket1',
    115                  'cloud_storage_base_folder': 'dependencies_folder',
    116                  'file_info': {
    117                      'plat1': {
    118                          'cloud_storage_hash': 'hash11',
    119                          'download_path': '../../relative/dep1/path1'},
    120                      'plat2': {
    121                          'cloud_storage_hash': self.new_dep_hash,
    122                          'download_path': '../../relative/dep1/path2'}}},
    123         'dep2': {'cloud_storage_bucket': 'bucket2',
    124                  'file_info': {
    125                      'plat1': {
    126                          'cloud_storage_hash': self.final_dep_hash,
    127                          'download_path': '../../relative/dep2/path1'},
    128                      'plat2': {
    129                          'cloud_storage_hash': 'hash22',
    130                          'download_path': '../../relative/dep2/path2'}}}}
    131     self.final_expected_file_lines = [
    132       # pylint: disable=bad-continuation
    133       '{', '"config_type": "BaseConfig",', '"dependencies": {',
    134         '"dep1": {', '"cloud_storage_base_folder": "dependencies_folder",',
    135           '"cloud_storage_bucket": "bucket1",', '"file_info": {',
    136             '"plat1": {', '"cloud_storage_hash": "hash11",',
    137               '"download_path": "../../relative/dep1/path1"', '},',
    138             '"plat2": {', '"cloud_storage_hash": "%s",' % self.new_dep_hash,
    139               '"download_path": "../../relative/dep1/path2"', '}', '}', '},',
    140         '"dep2": {', '"cloud_storage_bucket": "bucket2",', '"file_info": {',
    141             '"plat1": {', '"cloud_storage_hash": "%s",' % self.final_dep_hash,
    142               '"download_path": "../../relative/dep2/path1"', '},',
    143             '"plat2": {', '"cloud_storage_hash": "hash22",',
    144               '"download_path": "../../relative/dep2/path2"', '}', '}', '}',
    145       '}', '}']
    146 
    147 
    148   def tearDown(self):
    149     self.tearDownPyfakefs()
    150 
    151   # Init is not meant to be overridden, so we should be mocking the
    152   # base_config's json module, even in subclasses.
    153   def testCreateEmptyConfig(self):
    154     expected_file_lines = ['{',
    155                            '"config_type": "BaseConfig",',
    156                            '"dependencies": {}',
    157                            '}']
    158     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    159 
    160     file_module = fake_filesystem.FakeFileOpen(self.fs)
    161     for line in file_module(self.file_path):
    162       self.assertEqual(expected_file_lines.pop(0), line.strip())
    163     self.fs.CloseOpenFile(file_module(self.file_path))
    164     self.assertEqual({}, config._config_data)
    165     self.assertEqual(self.file_path, config._config_path)
    166 
    167   def testCreateEmptyConfigError(self):
    168     self.assertRaises(dependency_manager.EmptyConfigError,
    169                       dependency_manager.BaseConfig, self.file_path)
    170 
    171   def testCloudStorageRemotePath(self):
    172     dependency = 'dep_name'
    173     cs_hash = self.new_dep_hash
    174     cs_base_folder = 'dependency_remote_folder'
    175     expected_remote_path = '%s/%s_%s' % (cs_base_folder, dependency, cs_hash)
    176     remote_path = dependency_manager.BaseConfig._CloudStorageRemotePath(
    177         dependency, cs_hash, cs_base_folder)
    178     self.assertEqual(expected_remote_path, remote_path)
    179 
    180     cs_base_folder = 'dependency_remote_folder'
    181     expected_remote_path = '%s_%s' % (dependency, cs_hash)
    182     remote_path = dependency_manager.BaseConfig._CloudStorageRemotePath(
    183         dependency, cs_hash, cs_base_folder)
    184 
    185   def testGetEmptyJsonDict(self):
    186     expected_json_dict = {'config_type': 'BaseConfig',
    187                           'dependencies': {}}
    188     json_dict = dependency_manager.BaseConfig._GetJsonDict()
    189     self.assertEqual(expected_json_dict, json_dict)
    190 
    191   def testGetNonEmptyJsonDict(self):
    192     expected_json_dict = {"config_type": "BaseConfig",
    193                           "dependencies": self.dependencies}
    194     json_dict = dependency_manager.BaseConfig._GetJsonDict(self.dependencies)
    195     self.assertEqual(expected_json_dict, json_dict)
    196 
    197   def testWriteEmptyConfigToFile(self):
    198     expected_file_lines = ['{', '"config_type": "BaseConfig",',
    199                            '"dependencies": {}', '}']
    200     self.assertFalse(os.path.exists(self.file_path))
    201     dependency_manager.BaseConfig._WriteConfigToFile(self.file_path)
    202     self.assertTrue(os.path.exists(self.file_path))
    203     file_module = fake_filesystem.FakeFileOpen(self.fs)
    204     for line in file_module(self.file_path):
    205       self.assertEqual(expected_file_lines.pop(0), line.strip())
    206     self.fs.CloseOpenFile(file_module(self.file_path))
    207 
    208   def testWriteNonEmptyConfigToFile(self):
    209     self.assertFalse(os.path.exists(self.file_path))
    210     dependency_manager.BaseConfig._WriteConfigToFile(self.file_path,
    211                                                      self.dependencies)
    212     self.assertTrue(os.path.exists(self.file_path))
    213     expected_file_lines = list(self.expected_file_lines)
    214     file_module = fake_filesystem.FakeFileOpen(self.fs)
    215     for line in file_module(self.file_path):
    216       self.assertEqual(expected_file_lines.pop(0), line.strip())
    217     self.fs.CloseOpenFile(file_module(self.file_path))
    218 
    219   @mock.patch('dependency_manager.uploader.cloud_storage')
    220   def testExecuteUpdateJobsNoOp(self, uploader_cs_mock):
    221     self.fs.CreateFile(self.file_path,
    222                        contents='\n'.join(self.expected_file_lines))
    223     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    224 
    225     self.assertFalse(config.ExecuteUpdateJobs())
    226     self.assertFalse(config._IsDirty())
    227     self.assertFalse(config._pending_uploads)
    228     self.assertEqual(self.dependencies, config._config_data)
    229     file_module = fake_filesystem.FakeFileOpen(self.fs)
    230     expected_file_lines = list(self.expected_file_lines)
    231     for line in file_module(self.file_path):
    232       self.assertEqual(expected_file_lines.pop(0), line.strip())
    233     self.fs.CloseOpenFile(file_module(self.file_path))
    234 
    235   @mock.patch('dependency_manager.uploader.cloud_storage')
    236   def testExecuteUpdateJobsFailureOnInsertNoCSCollision(
    237       self, uploader_cs_mock):
    238     uploader_cs_mock.Exists.return_value = False
    239     uploader_cs_mock.Insert.side_effect = cloud_storage.CloudStorageError
    240     self.fs.CreateFile(self.file_path,
    241                        contents='\n'.join(self.expected_file_lines))
    242     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    243     config._config_data = self.new_dependencies.copy()
    244     config._is_dirty = True
    245     config._pending_uploads = [self.new_pending_upload]
    246     self.assertEqual(self.new_dependencies, config._config_data)
    247     self.assertTrue(config._is_dirty)
    248     self.assertEqual(1, len(config._pending_uploads))
    249     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    250     expected_exists_calls = [mock.call(self.new_bucket, self.new_remote_path)]
    251     expected_insert_calls = [mock.call(self.new_bucket, self.new_remote_path,
    252                                        self.new_dep_path)]
    253     expected_copy_calls = []
    254     expected_delete_calls = []
    255 
    256     self.assertRaises(cloud_storage.CloudStorageError,
    257                       config.ExecuteUpdateJobs)
    258     self.assertTrue(config._is_dirty)
    259     self.assertEqual(1, len(config._pending_uploads))
    260     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    261     self.assertEqual(self.new_dependencies, config._config_data)
    262     file_module = fake_filesystem.FakeFileOpen(self.fs)
    263     expected_file_lines = list(self.expected_file_lines)
    264     for line in file_module(self.file_path):
    265       self.assertEqual(expected_file_lines.pop(0), line.strip())
    266     self.fs.CloseOpenFile(file_module(self.file_path))
    267     self.assertEqual(1, len(config._pending_uploads))
    268     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    269     self.assertEqual(expected_insert_calls,
    270                      uploader_cs_mock.Insert.call_args_list)
    271     self.assertEqual(expected_exists_calls,
    272                      uploader_cs_mock.Exists.call_args_list)
    273     self.assertEqual(expected_copy_calls,
    274                      uploader_cs_mock.Copy.call_args_list)
    275     self.assertEqual(expected_delete_calls,
    276                      uploader_cs_mock.Delete.call_args_list)
    277 
    278   @mock.patch('dependency_manager.uploader.cloud_storage')
    279   def testExecuteUpdateJobsFailureOnInsertCSCollisionForce(
    280       self, uploader_cs_mock):
    281     uploader_cs_mock.Exists.return_value = True
    282     uploader_cs_mock.Insert.side_effect = cloud_storage.CloudStorageError
    283     self.fs.CreateFile(self.file_path,
    284                        contents='\n'.join(self.expected_file_lines))
    285     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    286     config._config_data = self.new_dependencies.copy()
    287     config._is_dirty = True
    288     config._pending_uploads = [self.new_pending_upload]
    289     self.assertEqual(self.new_dependencies, config._config_data)
    290     self.assertTrue(config._is_dirty)
    291     self.assertEqual(1, len(config._pending_uploads))
    292     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    293     expected_exists_calls = [mock.call(self.new_bucket, self.new_remote_path)]
    294     expected_insert_calls = [mock.call(self.new_bucket, self.new_remote_path,
    295                                        self.new_dep_path)]
    296     expected_copy_calls = [mock.call(self.new_bucket, self.new_bucket,
    297                                      self.new_remote_path,
    298                                      self.expected_new_backup_path),
    299                            mock.call(self.new_bucket, self.new_bucket,
    300                                      self.expected_new_backup_path,
    301                                      self.new_remote_path)]
    302     expected_delete_calls = []
    303 
    304     self.assertRaises(cloud_storage.CloudStorageError,
    305                       config.ExecuteUpdateJobs, force=True)
    306     self.assertTrue(config._is_dirty)
    307     self.assertEqual(1, len(config._pending_uploads))
    308     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    309     self.assertEqual(self.new_dependencies, config._config_data)
    310     file_module = fake_filesystem.FakeFileOpen(self.fs)
    311     expected_file_lines = list(self.expected_file_lines)
    312     for line in file_module(self.file_path):
    313       self.assertEqual(expected_file_lines.pop(0), line.strip())
    314     self.fs.CloseOpenFile(file_module(self.file_path))
    315     self.assertEqual(1, len(config._pending_uploads))
    316     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    317     self.assertEqual(expected_insert_calls,
    318                      uploader_cs_mock.Insert.call_args_list)
    319     self.assertEqual(expected_exists_calls,
    320                      uploader_cs_mock.Exists.call_args_list)
    321     self.assertEqual(expected_copy_calls,
    322                      uploader_cs_mock.Copy.call_args_list)
    323     self.assertEqual(expected_delete_calls,
    324                      uploader_cs_mock.Delete.call_args_list)
    325 
    326   @mock.patch('dependency_manager.uploader.cloud_storage')
    327   def testExecuteUpdateJobsFailureOnInsertCSCollisionNoForce(
    328       self, uploader_cs_mock):
    329     uploader_cs_mock.Exists.return_value = True
    330     uploader_cs_mock.Insert.side_effect = cloud_storage.CloudStorageError
    331     self.fs.CreateFile(self.file_path,
    332                        contents='\n'.join(self.expected_file_lines))
    333     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    334     config._config_data = self.new_dependencies.copy()
    335     config._is_dirty = True
    336     config._pending_uploads = [self.new_pending_upload]
    337     self.assertEqual(self.new_dependencies, config._config_data)
    338     self.assertTrue(config._is_dirty)
    339     self.assertEqual(1, len(config._pending_uploads))
    340     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    341     expected_exists_calls = [mock.call(self.new_bucket, self.new_remote_path)]
    342     expected_insert_calls = []
    343     expected_copy_calls = []
    344     expected_delete_calls = []
    345 
    346     self.assertRaises(cloud_storage.CloudStorageError,
    347                       config.ExecuteUpdateJobs)
    348     self.assertTrue(config._is_dirty)
    349     self.assertEqual(1, len(config._pending_uploads))
    350     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    351     self.assertEqual(self.new_dependencies, config._config_data)
    352     file_module = fake_filesystem.FakeFileOpen(self.fs)
    353     expected_file_lines = list(self.expected_file_lines)
    354     for line in file_module(self.file_path):
    355       self.assertEqual(expected_file_lines.pop(0), line.strip())
    356     self.fs.CloseOpenFile(file_module(self.file_path))
    357     self.assertEqual(1, len(config._pending_uploads))
    358     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    359     self.assertEqual(expected_insert_calls,
    360                      uploader_cs_mock.Insert.call_args_list)
    361     self.assertEqual(expected_exists_calls,
    362                      uploader_cs_mock.Exists.call_args_list)
    363     self.assertEqual(expected_copy_calls,
    364                      uploader_cs_mock.Copy.call_args_list)
    365     self.assertEqual(expected_delete_calls,
    366                      uploader_cs_mock.Delete.call_args_list)
    367 
    368   @mock.patch('dependency_manager.uploader.cloud_storage')
    369   def testExecuteUpdateJobsFailureOnCopy(
    370       self, uploader_cs_mock):
    371     uploader_cs_mock.Exists.return_value = True
    372     uploader_cs_mock.Copy.side_effect = cloud_storage.CloudStorageError
    373     self.fs.CreateFile(self.file_path,
    374                        contents='\n'.join(self.expected_file_lines))
    375     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    376     config._config_data = self.new_dependencies.copy()
    377     config._is_dirty = True
    378     config._pending_uploads = [self.new_pending_upload]
    379     self.assertEqual(self.new_dependencies, config._config_data)
    380     self.assertTrue(config._is_dirty)
    381     self.assertEqual(1, len(config._pending_uploads))
    382     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    383     expected_exists_calls = [mock.call(self.new_bucket, self.new_remote_path)]
    384     expected_insert_calls = []
    385     expected_copy_calls = [mock.call(self.new_bucket, self.new_bucket,
    386                                      self.new_remote_path,
    387                                      self.expected_new_backup_path)]
    388     expected_delete_calls = []
    389 
    390     self.assertRaises(cloud_storage.CloudStorageError,
    391                       config.ExecuteUpdateJobs, force=True)
    392     self.assertTrue(config._is_dirty)
    393     self.assertEqual(1, len(config._pending_uploads))
    394     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    395     self.assertEqual(self.new_dependencies, config._config_data)
    396     file_module = fake_filesystem.FakeFileOpen(self.fs)
    397     expected_file_lines = list(self.expected_file_lines)
    398     for line in file_module(self.file_path):
    399       self.assertEqual(expected_file_lines.pop(0), line.strip())
    400     self.fs.CloseOpenFile(file_module(self.file_path))
    401     self.assertEqual(1, len(config._pending_uploads))
    402     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    403     self.assertEqual(expected_insert_calls,
    404                      uploader_cs_mock.Insert.call_args_list)
    405     self.assertEqual(expected_exists_calls,
    406                      uploader_cs_mock.Exists.call_args_list)
    407     self.assertEqual(expected_copy_calls,
    408                      uploader_cs_mock.Copy.call_args_list)
    409     self.assertEqual(expected_delete_calls,
    410                      uploader_cs_mock.Delete.call_args_list)
    411 
    412   @mock.patch('dependency_manager.uploader.cloud_storage')
    413   def testExecuteUpdateJobsFailureOnSecondInsertNoCSCollision(
    414       self, uploader_cs_mock):
    415     uploader_cs_mock.Exists.return_value = False
    416     uploader_cs_mock.Insert.side_effect = [
    417         True, cloud_storage.CloudStorageError]
    418     self.fs.CreateFile(self.file_path,
    419                        contents='\n'.join(self.expected_file_lines))
    420     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    421     config._config_data = self.new_dependencies.copy()
    422     config._is_dirty = True
    423     config._pending_uploads = [self.new_pending_upload,
    424                                self.final_pending_upload]
    425     self.assertEqual(self.new_dependencies, config._config_data)
    426     self.assertTrue(config._is_dirty)
    427     self.assertEqual(2, len(config._pending_uploads))
    428     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    429     self.assertEqual(self.final_pending_upload, config._pending_uploads[1])
    430     expected_exists_calls = [mock.call(self.new_bucket, self.new_remote_path),
    431                              mock.call(self.final_bucket,
    432                                        self.final_remote_path)]
    433     expected_insert_calls = [mock.call(self.new_bucket, self.new_remote_path,
    434                                        self.new_dep_path),
    435                              mock.call(self.final_bucket,
    436                                        self.final_remote_path,
    437                                        self.final_dep_path)]
    438     expected_copy_calls = []
    439     expected_delete_calls = [mock.call(self.new_bucket, self.new_remote_path)]
    440 
    441     self.assertRaises(cloud_storage.CloudStorageError,
    442                       config.ExecuteUpdateJobs)
    443     self.assertTrue(config._is_dirty)
    444     self.assertEqual(2, len(config._pending_uploads))
    445     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    446     self.assertEqual(self.final_pending_upload, config._pending_uploads[1])
    447     self.assertEqual(self.new_dependencies, config._config_data)
    448     file_module = fake_filesystem.FakeFileOpen(self.fs)
    449     expected_file_lines = list(self.expected_file_lines)
    450     for line in file_module(self.file_path):
    451       self.assertEqual(expected_file_lines.pop(0), line.strip())
    452     self.fs.CloseOpenFile(file_module(self.file_path))
    453     self.assertEqual(expected_insert_calls,
    454                      uploader_cs_mock.Insert.call_args_list)
    455     self.assertEqual(expected_exists_calls,
    456                      uploader_cs_mock.Exists.call_args_list)
    457     self.assertEqual(expected_copy_calls,
    458                      uploader_cs_mock.Copy.call_args_list)
    459     self.assertEqual(expected_delete_calls,
    460                      uploader_cs_mock.Delete.call_args_list)
    461 
    462   @mock.patch('dependency_manager.uploader.cloud_storage')
    463   def testExecuteUpdateJobsFailureOnSecondInsertCSCollisionForce(
    464       self, uploader_cs_mock):
    465     uploader_cs_mock.Exists.return_value = True
    466     uploader_cs_mock.Insert.side_effect = [
    467         True, cloud_storage.CloudStorageError]
    468     self.fs.CreateFile(self.file_path,
    469                        contents='\n'.join(self.expected_file_lines))
    470     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    471     config._config_data = self.new_dependencies.copy()
    472     config._is_dirty = True
    473     config._pending_uploads = [self.new_pending_upload,
    474                                self.final_pending_upload]
    475     self.assertEqual(self.new_dependencies, config._config_data)
    476     self.assertTrue(config._is_dirty)
    477     self.assertEqual(2, len(config._pending_uploads))
    478     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    479     self.assertEqual(self.final_pending_upload, config._pending_uploads[1])
    480     expected_exists_calls = [mock.call(self.new_bucket, self.new_remote_path),
    481                              mock.call(self.final_bucket,
    482                                        self.final_remote_path)]
    483     expected_insert_calls = [mock.call(self.new_bucket, self.new_remote_path,
    484                                        self.new_dep_path),
    485                              mock.call(self.final_bucket,
    486                                        self.final_remote_path,
    487                                        self.final_dep_path)]
    488     expected_copy_calls = [mock.call(self.new_bucket, self.new_bucket,
    489                                      self.new_remote_path,
    490                                      self.expected_new_backup_path),
    491                            mock.call(self.final_bucket, self.final_bucket,
    492                                      self.final_remote_path,
    493                                      self.expected_final_backup_path),
    494                            mock.call(self.final_bucket, self.final_bucket,
    495                                      self.expected_final_backup_path,
    496                                      self.final_remote_path),
    497                            mock.call(self.new_bucket, self.new_bucket,
    498                                      self.expected_new_backup_path,
    499                                      self.new_remote_path)]
    500     expected_delete_calls = []
    501 
    502     self.assertRaises(cloud_storage.CloudStorageError,
    503                       config.ExecuteUpdateJobs, force=True)
    504     self.assertTrue(config._is_dirty)
    505     self.assertEqual(2, len(config._pending_uploads))
    506     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    507     self.assertEqual(self.final_pending_upload, config._pending_uploads[1])
    508     self.assertEqual(self.new_dependencies, config._config_data)
    509     file_module = fake_filesystem.FakeFileOpen(self.fs)
    510     expected_file_lines = list(self.expected_file_lines)
    511     for line in file_module(self.file_path):
    512       self.assertEqual(expected_file_lines.pop(0), line.strip())
    513     self.fs.CloseOpenFile(file_module(self.file_path))
    514     self.assertEqual(expected_insert_calls,
    515                      uploader_cs_mock.Insert.call_args_list)
    516     self.assertEqual(expected_exists_calls,
    517                      uploader_cs_mock.Exists.call_args_list)
    518     self.assertEqual(expected_copy_calls,
    519                      uploader_cs_mock.Copy.call_args_list)
    520     self.assertEqual(expected_delete_calls,
    521                      uploader_cs_mock.Delete.call_args_list)
    522 
    523   @mock.patch('dependency_manager.uploader.cloud_storage')
    524   def testExecuteUpdateJobsFailureOnSecondInsertFirstCSCollisionForce(
    525       self, uploader_cs_mock):
    526     uploader_cs_mock.Exists.side_effect = [True, False, True]
    527     uploader_cs_mock.Insert.side_effect = [
    528         True, cloud_storage.CloudStorageError]
    529     self.fs.CreateFile(self.file_path,
    530                        contents='\n'.join(self.expected_file_lines))
    531     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    532     config._config_data = self.new_dependencies.copy()
    533     config._is_dirty = True
    534     config._pending_uploads = [self.new_pending_upload,
    535                                self.final_pending_upload]
    536     self.assertEqual(self.new_dependencies, config._config_data)
    537     self.assertTrue(config._is_dirty)
    538     self.assertEqual(2, len(config._pending_uploads))
    539     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    540     self.assertEqual(self.final_pending_upload, config._pending_uploads[1])
    541     expected_exists_calls = [mock.call(self.new_bucket, self.new_remote_path),
    542                              mock.call(self.final_bucket,
    543                                        self.final_remote_path)]
    544     expected_insert_calls = [mock.call(self.new_bucket, self.new_remote_path,
    545                                        self.new_dep_path),
    546                              mock.call(self.final_bucket,
    547                                        self.final_remote_path,
    548                                        self.final_dep_path)]
    549     expected_copy_calls = [mock.call(self.new_bucket, self.new_bucket,
    550                                      self.new_remote_path,
    551                                      self.expected_new_backup_path),
    552                            mock.call(self.new_bucket, self.new_bucket,
    553                                      self.expected_new_backup_path,
    554                                      self.new_remote_path)]
    555     expected_delete_calls = []
    556 
    557     self.assertRaises(cloud_storage.CloudStorageError,
    558                       config.ExecuteUpdateJobs, force=True)
    559     self.assertTrue(config._is_dirty)
    560     self.assertEqual(2, len(config._pending_uploads))
    561     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    562     self.assertEqual(self.final_pending_upload, config._pending_uploads[1])
    563     self.assertEqual(self.new_dependencies, config._config_data)
    564     file_module = fake_filesystem.FakeFileOpen(self.fs)
    565     expected_file_lines = list(self.expected_file_lines)
    566     for line in file_module(self.file_path):
    567       self.assertEqual(expected_file_lines.pop(0), line.strip())
    568     self.fs.CloseOpenFile(file_module(self.file_path))
    569     self.assertEqual(expected_insert_calls,
    570                      uploader_cs_mock.Insert.call_args_list)
    571     self.assertEqual(expected_exists_calls,
    572                      uploader_cs_mock.Exists.call_args_list)
    573     self.assertEqual(expected_copy_calls,
    574                      uploader_cs_mock.Copy.call_args_list)
    575     self.assertEqual(expected_delete_calls,
    576                      uploader_cs_mock.Delete.call_args_list)
    577 
    578   @mock.patch('dependency_manager.uploader.cloud_storage')
    579   def testExecuteUpdateJobsFailureOnFirstCSCollisionNoForce(
    580       self, uploader_cs_mock):
    581     uploader_cs_mock.Exists.side_effect = [True, False, True]
    582     uploader_cs_mock.Insert.side_effect = [
    583         True, cloud_storage.CloudStorageError]
    584     self.fs.CreateFile(self.file_path,
    585                        contents='\n'.join(self.expected_file_lines))
    586     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    587     config._config_data = self.new_dependencies.copy()
    588     config._is_dirty = True
    589     config._pending_uploads = [self.new_pending_upload,
    590                                self.final_pending_upload]
    591     self.assertEqual(self.new_dependencies, config._config_data)
    592     self.assertTrue(config._is_dirty)
    593     self.assertEqual(2, len(config._pending_uploads))
    594     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    595     self.assertEqual(self.final_pending_upload, config._pending_uploads[1])
    596     expected_exists_calls = [mock.call(self.new_bucket, self.new_remote_path)]
    597     expected_insert_calls = []
    598     expected_copy_calls = []
    599     expected_delete_calls = []
    600 
    601     self.assertRaises(cloud_storage.CloudStorageError,
    602                       config.ExecuteUpdateJobs)
    603     self.assertTrue(config._is_dirty)
    604     self.assertEqual(2, len(config._pending_uploads))
    605     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    606     self.assertEqual(self.final_pending_upload, config._pending_uploads[1])
    607     self.assertEqual(self.new_dependencies, config._config_data)
    608     file_module = fake_filesystem.FakeFileOpen(self.fs)
    609     expected_file_lines = list(self.expected_file_lines)
    610     for line in file_module(self.file_path):
    611       self.assertEqual(expected_file_lines.pop(0), line.strip())
    612     self.fs.CloseOpenFile(file_module(self.file_path))
    613     self.assertEqual(expected_insert_calls,
    614                      uploader_cs_mock.Insert.call_args_list)
    615     self.assertEqual(expected_exists_calls,
    616                      uploader_cs_mock.Exists.call_args_list)
    617     self.assertEqual(expected_copy_calls,
    618                      uploader_cs_mock.Copy.call_args_list)
    619     self.assertEqual(expected_delete_calls,
    620                      uploader_cs_mock.Delete.call_args_list)
    621 
    622   @mock.patch('dependency_manager.uploader.cloud_storage')
    623   def testExecuteUpdateJobsFailureOnSecondCopyCSCollision(
    624       self, uploader_cs_mock):
    625     uploader_cs_mock.Exists.return_value = True
    626     uploader_cs_mock.Insert.return_value = True
    627     uploader_cs_mock.Copy.side_effect = [
    628         True, cloud_storage.CloudStorageError, True]
    629     self.fs.CreateFile(self.file_path,
    630                        contents='\n'.join(self.expected_file_lines))
    631     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    632     config._config_data = self.new_dependencies.copy()
    633     config._is_dirty = True
    634     config._pending_uploads = [self.new_pending_upload,
    635                                self.final_pending_upload]
    636     self.assertEqual(self.new_dependencies, config._config_data)
    637     self.assertTrue(config._is_dirty)
    638     self.assertEqual(2, len(config._pending_uploads))
    639     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    640     self.assertEqual(self.final_pending_upload, config._pending_uploads[1])
    641     expected_exists_calls = [mock.call(self.new_bucket, self.new_remote_path),
    642                              mock.call(self.final_bucket,
    643                                        self.final_remote_path)]
    644     expected_insert_calls = [mock.call(self.new_bucket, self.new_remote_path,
    645                                        self.new_dep_path)]
    646     expected_copy_calls = [mock.call(self.new_bucket, self.new_bucket,
    647                                      self.new_remote_path,
    648                                      self.expected_new_backup_path),
    649                            mock.call(self.final_bucket, self.final_bucket,
    650                                      self.final_remote_path,
    651                                      self.expected_final_backup_path),
    652                            mock.call(self.new_bucket, self.new_bucket,
    653                                      self.expected_new_backup_path,
    654                                      self.new_remote_path)]
    655     expected_delete_calls = []
    656 
    657     self.assertRaises(cloud_storage.CloudStorageError,
    658                       config.ExecuteUpdateJobs, force=True)
    659     self.assertTrue(config._is_dirty)
    660     self.assertEqual(2, len(config._pending_uploads))
    661     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    662     self.assertEqual(self.final_pending_upload, config._pending_uploads[1])
    663     self.assertEqual(self.new_dependencies, config._config_data)
    664     file_module = fake_filesystem.FakeFileOpen(self.fs)
    665     expected_file_lines = list(self.expected_file_lines)
    666     for line in file_module(self.file_path):
    667       self.assertEqual(expected_file_lines.pop(0), line.strip())
    668     self.fs.CloseOpenFile(file_module(self.file_path))
    669     self.assertEqual(expected_insert_calls,
    670                      uploader_cs_mock.Insert.call_args_list)
    671     self.assertEqual(expected_exists_calls,
    672                      uploader_cs_mock.Exists.call_args_list)
    673     self.assertEqual(expected_copy_calls,
    674                      uploader_cs_mock.Copy.call_args_list)
    675     self.assertEqual(expected_delete_calls,
    676                      uploader_cs_mock.Delete.call_args_list)
    677 
    678   @mock.patch('dependency_manager.uploader.cloud_storage')
    679   def testExecuteUpdateJobsFailureOnSecondCopyNoCSCollisionForce(
    680       self, uploader_cs_mock):
    681     uploader_cs_mock.Exists.side_effect = [False, True, False]
    682     uploader_cs_mock.Copy.side_effect = cloud_storage.CloudStorageError
    683     self.fs.CreateFile(self.file_path,
    684                        contents='\n'.join(self.expected_file_lines))
    685     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    686     config._config_data = self.new_dependencies.copy()
    687     config._is_dirty = True
    688     config._pending_uploads = [self.new_pending_upload,
    689                                self.final_pending_upload]
    690     self.assertEqual(self.new_dependencies, config._config_data)
    691     self.assertTrue(config._is_dirty)
    692     self.assertEqual(2, len(config._pending_uploads))
    693     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    694     self.assertEqual(self.final_pending_upload, config._pending_uploads[1])
    695     expected_exists_calls = [mock.call(self.new_bucket, self.new_remote_path),
    696                              mock.call(self.final_bucket,
    697                                        self.final_remote_path)]
    698     expected_insert_calls = [mock.call(self.new_bucket, self.new_remote_path,
    699                                        self.new_dep_path)]
    700     expected_copy_calls = [mock.call(self.final_bucket, self.final_bucket,
    701                                      self.final_remote_path,
    702                                      self.expected_final_backup_path)]
    703     expected_delete_calls = [mock.call(self.new_bucket, self.new_remote_path)]
    704 
    705     self.assertRaises(cloud_storage.CloudStorageError,
    706                       config.ExecuteUpdateJobs, force=True)
    707     self.assertTrue(config._is_dirty)
    708     self.assertEqual(2, len(config._pending_uploads))
    709     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    710     self.assertEqual(self.final_pending_upload, config._pending_uploads[1])
    711     self.assertEqual(self.new_dependencies, config._config_data)
    712     file_module = fake_filesystem.FakeFileOpen(self.fs)
    713     expected_file_lines = list(self.expected_file_lines)
    714     for line in file_module(self.file_path):
    715       self.assertEqual(expected_file_lines.pop(0), line.strip())
    716     self.fs.CloseOpenFile(file_module(self.file_path))
    717     self.assertEqual(expected_insert_calls,
    718                      uploader_cs_mock.Insert.call_args_list)
    719     self.assertEqual(expected_exists_calls,
    720                      uploader_cs_mock.Exists.call_args_list)
    721     self.assertEqual(expected_copy_calls,
    722                      uploader_cs_mock.Copy.call_args_list)
    723     self.assertEqual(expected_delete_calls,
    724                      uploader_cs_mock.Delete.call_args_list)
    725 
    726   @mock.patch('dependency_manager.uploader.cloud_storage')
    727   def testExecuteUpdateJobsFailureOnSecondCopyNoCSCollisionNoForce(
    728       self, uploader_cs_mock):
    729     uploader_cs_mock.Exists.side_effect = [False, True, False]
    730     uploader_cs_mock.Copy.side_effect = cloud_storage.CloudStorageError
    731     self.fs.CreateFile(self.file_path,
    732                        contents='\n'.join(self.expected_file_lines))
    733     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    734     config._config_data = self.new_dependencies.copy()
    735     config._is_dirty = True
    736     config._pending_uploads = [self.new_pending_upload,
    737                                self.final_pending_upload]
    738     self.assertEqual(self.new_dependencies, config._config_data)
    739     self.assertTrue(config._is_dirty)
    740     self.assertEqual(2, len(config._pending_uploads))
    741     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    742     self.assertEqual(self.final_pending_upload, config._pending_uploads[1])
    743     expected_exists_calls = [mock.call(self.new_bucket, self.new_remote_path),
    744                              mock.call(self.final_bucket,
    745                                        self.final_remote_path)]
    746     expected_insert_calls = [mock.call(self.new_bucket, self.new_remote_path,
    747                                        self.new_dep_path)]
    748     expected_copy_calls = []
    749     expected_delete_calls = [mock.call(self.new_bucket, self.new_remote_path)]
    750 
    751     self.assertRaises(cloud_storage.CloudStorageError,
    752                       config.ExecuteUpdateJobs)
    753     self.assertTrue(config._is_dirty)
    754     self.assertEqual(2, len(config._pending_uploads))
    755     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    756     self.assertEqual(self.final_pending_upload, config._pending_uploads[1])
    757     self.assertEqual(self.new_dependencies, config._config_data)
    758     file_module = fake_filesystem.FakeFileOpen(self.fs)
    759     expected_file_lines = list(self.expected_file_lines)
    760     for line in file_module(self.file_path):
    761       self.assertEqual(expected_file_lines.pop(0), line.strip())
    762     self.fs.CloseOpenFile(file_module(self.file_path))
    763     self.assertEqual(expected_insert_calls,
    764                      uploader_cs_mock.Insert.call_args_list)
    765     self.assertEqual(expected_exists_calls,
    766                      uploader_cs_mock.Exists.call_args_list)
    767     self.assertEqual(expected_copy_calls,
    768                      uploader_cs_mock.Copy.call_args_list)
    769     self.assertEqual(expected_delete_calls,
    770                      uploader_cs_mock.Delete.call_args_list)
    771 
    772   @mock.patch('dependency_manager.uploader.cloud_storage')
    773   def testExecuteUpdateJobsSuccessOnePendingDepNoCloudStorageCollision(
    774       self, uploader_cs_mock):
    775     uploader_cs_mock.Exists.return_value = False
    776     self.fs.CreateFile(self.file_path,
    777                        contents='\n'.join(self.expected_file_lines))
    778     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    779     config._config_data = self.new_dependencies.copy()
    780     config._pending_uploads = [self.new_pending_upload]
    781     self.assertEqual(self.new_dependencies, config._config_data)
    782     self.assertTrue(config._IsDirty())
    783     self.assertEqual(1, len(config._pending_uploads))
    784     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    785     expected_exists_calls = [mock.call(self.new_bucket, self.new_remote_path)]
    786     expected_insert_calls = [mock.call(self.new_bucket, self.new_remote_path,
    787                                        self.new_dep_path)]
    788     expected_copy_calls = []
    789     expected_delete_calls = []
    790 
    791     self.assertTrue(config.ExecuteUpdateJobs())
    792     self.assertFalse(config._IsDirty())
    793     self.assertFalse(config._pending_uploads)
    794     self.assertEqual(self.new_dependencies, config._config_data)
    795     file_module = fake_filesystem.FakeFileOpen(self.fs)
    796     expected_file_lines = list(self.new_expected_file_lines)
    797     for line in file_module(self.file_path):
    798       self.assertEqual(expected_file_lines.pop(0), line.strip())
    799     self.fs.CloseOpenFile(file_module(self.file_path))
    800     self.assertFalse(config._pending_uploads)
    801     self.assertEqual(expected_insert_calls,
    802                      uploader_cs_mock.Insert.call_args_list)
    803     self.assertEqual(expected_exists_calls,
    804                      uploader_cs_mock.Exists.call_args_list)
    805     self.assertEqual(expected_copy_calls,
    806                      uploader_cs_mock.Copy.call_args_list)
    807     self.assertEqual(expected_delete_calls,
    808                      uploader_cs_mock.Delete.call_args_list)
    809 
    810   @mock.patch('dependency_manager.uploader.cloud_storage')
    811   def testExecuteUpdateJobsSuccessOnePendingDepCloudStorageCollision(
    812       self, uploader_cs_mock):
    813     uploader_cs_mock.Exists.return_value = True
    814     self.fs.CreateFile(self.file_path,
    815                        contents='\n'.join(self.expected_file_lines))
    816     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    817     config._config_data = self.new_dependencies.copy()
    818     config._pending_uploads = [self.new_pending_upload]
    819     self.assertEqual(self.new_dependencies, config._config_data)
    820     self.assertTrue(config._IsDirty())
    821     self.assertEqual(1, len(config._pending_uploads))
    822     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    823     expected_exists_calls = [mock.call(self.new_bucket, self.new_remote_path)]
    824     expected_insert_calls = [mock.call(self.new_bucket, self.new_remote_path,
    825                                        self.new_dep_path)]
    826     expected_copy_calls = [mock.call(self.new_bucket, self.new_bucket,
    827                                      self.new_remote_path,
    828                                      self.expected_new_backup_path)]
    829 
    830     self.assertTrue(config.ExecuteUpdateJobs(force=True))
    831     self.assertFalse(config._IsDirty())
    832     self.assertFalse(config._pending_uploads)
    833     self.assertEqual(self.new_dependencies, config._config_data)
    834     file_module = fake_filesystem.FakeFileOpen(self.fs)
    835     expected_file_lines = list(self.new_expected_file_lines)
    836     for line in file_module(self.file_path):
    837       self.assertEqual(expected_file_lines.pop(0), line.strip())
    838     self.fs.CloseOpenFile(file_module(self.file_path))
    839     self.assertFalse(config._pending_uploads)
    840     self.assertEqual(expected_insert_calls,
    841                      uploader_cs_mock.Insert.call_args_list)
    842     self.assertEqual(expected_exists_calls,
    843                      uploader_cs_mock.Exists.call_args_list)
    844     self.assertEqual(expected_copy_calls,
    845                      uploader_cs_mock.Copy.call_args_list)
    846 
    847   @mock.patch('dependency_manager.uploader.cloud_storage')
    848   def testExecuteUpdateJobsErrorOnePendingDepCloudStorageCollisionNoForce(
    849       self, uploader_cs_mock):
    850     uploader_cs_mock.Exists.return_value = True
    851     self.fs.CreateFile(self.file_path,
    852                        contents='\n'.join(self.expected_file_lines))
    853     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    854     config._config_data = self.new_dependencies.copy()
    855     config._is_dirty = True
    856     config._pending_uploads = [self.new_pending_upload]
    857     self.assertEqual(self.new_dependencies, config._config_data)
    858     self.assertTrue(config._is_dirty)
    859     self.assertEqual(1, len(config._pending_uploads))
    860     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    861     expected_exists_calls = [mock.call(self.new_bucket, self.new_remote_path)]
    862     expected_insert_calls = []
    863     expected_copy_calls = []
    864 
    865     self.assertRaises(dependency_manager.CloudStorageUploadConflictError,
    866                       config.ExecuteUpdateJobs)
    867     self.assertTrue(config._is_dirty)
    868     self.assertTrue(config._pending_uploads)
    869     self.assertEqual(self.new_dependencies, config._config_data)
    870     self.assertEqual(1, len(config._pending_uploads))
    871     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    872     file_module = fake_filesystem.FakeFileOpen(self.fs)
    873     expected_file_lines = list(self.expected_file_lines)
    874     for line in file_module(self.file_path):
    875       self.assertEqual(expected_file_lines.pop(0), line.strip())
    876     self.fs.CloseOpenFile(file_module(self.file_path))
    877     self.assertEqual(expected_insert_calls,
    878                      uploader_cs_mock.Insert.call_args_list)
    879     self.assertEqual(expected_exists_calls,
    880                      uploader_cs_mock.Exists.call_args_list)
    881     self.assertEqual(expected_copy_calls,
    882                      uploader_cs_mock.Copy.call_args_list)
    883 
    884   @mock.patch('dependency_manager.uploader.cloud_storage')
    885   def testExecuteUpdateJobsSuccessMultiplePendingDepsOneCloudStorageCollision(
    886       self, uploader_cs_mock):
    887     uploader_cs_mock.Exists.side_effect = [False, True]
    888     self.fs.CreateFile(self.file_path,
    889                        contents='\n'.join(self.expected_file_lines))
    890     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    891     config._config_data = self.final_dependencies.copy()
    892     config._pending_uploads = [self.new_pending_upload,
    893                                self.final_pending_upload]
    894     self.assertEqual(self.final_dependencies, config._config_data)
    895     self.assertTrue(config._IsDirty())
    896     self.assertEqual(2, len(config._pending_uploads))
    897     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
    898     self.assertEqual(self.final_pending_upload, config._pending_uploads[1])
    899 
    900     expected_exists_calls = [mock.call(self.new_bucket, self.new_remote_path),
    901                              mock.call(self.final_bucket,
    902                                        self.final_remote_path)]
    903     expected_insert_calls = [mock.call(self.new_bucket, self.new_remote_path,
    904                                        self.new_dep_path),
    905                              mock.call(self.final_bucket,
    906                                        self.final_remote_path,
    907                                        self.final_dep_path)]
    908     expected_copy_calls = [mock.call(self.final_bucket, self.final_bucket,
    909                                      self.final_remote_path,
    910                                      self.expected_final_backup_path)]
    911 
    912     self.assertTrue(config.ExecuteUpdateJobs(force=True))
    913     self.assertFalse(config._IsDirty())
    914     self.assertFalse(config._pending_uploads)
    915     self.assertEqual(self.final_dependencies, config._config_data)
    916     file_module = fake_filesystem.FakeFileOpen(self.fs)
    917     expected_file_lines = list(self.final_expected_file_lines)
    918     for line in file_module(self.file_path):
    919       self.assertEqual(expected_file_lines.pop(0), line.strip())
    920     self.fs.CloseOpenFile(file_module(self.file_path))
    921     self.assertFalse(config._pending_uploads)
    922     self.assertEqual(expected_insert_calls,
    923                      uploader_cs_mock.Insert.call_args_list)
    924     self.assertEqual(expected_exists_calls,
    925                      uploader_cs_mock.Exists.call_args_list)
    926     self.assertEqual(expected_copy_calls,
    927                      uploader_cs_mock.Copy.call_args_list)
    928 
    929   @mock.patch('dependency_manager.uploader.cloud_storage')
    930   def testUpdateCloudStorageDependenciesReadOnlyConfig(
    931       self, uploader_cs_mock):
    932     self.fs.CreateFile(self.file_path,
    933                        contents='\n'.join(self.expected_file_lines))
    934     config = dependency_manager.BaseConfig(self.file_path)
    935     with self.assertRaises(dependency_manager.ReadWriteError):
    936       config.AddCloudStorageDependencyUpdateJob(
    937           'dep', 'plat', 'path')
    938     with self.assertRaises(dependency_manager.ReadWriteError):
    939       config.AddCloudStorageDependencyUpdateJob(
    940           'dep', 'plat', 'path', version='1.2.3')
    941     with self.assertRaises(dependency_manager.ReadWriteError):
    942       config.AddCloudStorageDependencyUpdateJob(
    943           'dep', 'plat', 'path', execute_job=False)
    944     with self.assertRaises(dependency_manager.ReadWriteError):
    945       config.AddCloudStorageDependencyUpdateJob(
    946           'dep', 'plat', 'path', version='1.2.3', execute_job=False)
    947 
    948   @mock.patch('dependency_manager.uploader.cloud_storage')
    949   def testUpdateCloudStorageDependenciesMissingDependency(
    950       self, uploader_cs_mock):
    951     self.fs.CreateFile(self.file_path,
    952                        contents='\n'.join(self.expected_file_lines))
    953     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    954     self.assertRaises(ValueError, config.AddCloudStorageDependencyUpdateJob,
    955                       'dep', 'plat', 'path')
    956     self.assertRaises(ValueError, config.AddCloudStorageDependencyUpdateJob,
    957                       'dep', 'plat', 'path', version='1.2.3')
    958     self.assertRaises(ValueError, config.AddCloudStorageDependencyUpdateJob,
    959                       'dep', 'plat', 'path', execute_job=False)
    960     self.assertRaises(ValueError, config.AddCloudStorageDependencyUpdateJob,
    961                       'dep', 'plat', 'path', version='1.2.3', execute_job=False)
    962 
    963   @mock.patch('dependency_manager.uploader.cloud_storage')
    964   @mock.patch('dependency_manager.base_config.cloud_storage')
    965   def testUpdateCloudStorageDependenciesWrite(
    966       self, base_config_cs_mock, uploader_cs_mock):
    967     expected_dependencies = self.dependencies
    968     self.fs.CreateFile(self.file_path,
    969                        contents='\n'.join(self.expected_file_lines))
    970     config = dependency_manager.BaseConfig(self.file_path, writable=True)
    971     self.assertFalse(config._IsDirty())
    972     self.assertEqual(expected_dependencies, config._config_data)
    973 
    974     base_config_cs_mock.CalculateHash.return_value = self.new_dep_hash
    975     uploader_cs_mock.Exists.return_value = False
    976     expected_dependencies = self.new_dependencies
    977     config.AddCloudStorageDependencyUpdateJob(
    978         'dep1', 'plat2', self.new_dep_path, execute_job=True)
    979     self.assertFalse(config._IsDirty())
    980     self.assertFalse(config._pending_uploads)
    981     self.assertEqual(expected_dependencies, config._config_data)
    982     # check that file contents has been updated
    983     file_module = fake_filesystem.FakeFileOpen(self.fs)
    984     expected_file_lines = list(self.new_expected_file_lines)
    985     for line in file_module(self.file_path):
    986       self.assertEqual(expected_file_lines.pop(0), line.strip())
    987     self.fs.CloseOpenFile(file_module(self.file_path))
    988 
    989     expected_dependencies = self.final_dependencies
    990     base_config_cs_mock.CalculateHash.return_value = self.final_dep_hash
    991     config.AddCloudStorageDependencyUpdateJob(
    992         'dep2', 'plat1', self.final_dep_path, execute_job=True)
    993     self.assertFalse(config._IsDirty())
    994     self.assertFalse(config._pending_uploads)
    995     self.assertEqual(expected_dependencies, config._config_data)
    996     # check that file contents has been updated
    997     expected_file_lines = list(self.final_expected_file_lines)
    998     file_module = fake_filesystem.FakeFileOpen(self.fs)
    999     for line in file_module(self.file_path):
   1000       self.assertEqual(expected_file_lines.pop(0), line.strip())
   1001     self.fs.CloseOpenFile(file_module(self.file_path))
   1002 
   1003   @mock.patch('dependency_manager.uploader.cloud_storage')
   1004   @mock.patch('dependency_manager.base_config.cloud_storage')
   1005   def testUpdateCloudStorageDependenciesNoWrite(
   1006       self, base_config_cs_mock, uploader_cs_mock):
   1007     self.fs.CreateFile(self.file_path,
   1008                        contents='\n'.join(self.expected_file_lines))
   1009     config = dependency_manager.BaseConfig(self.file_path, writable=True)
   1010 
   1011     self.assertRaises(ValueError, config.AddCloudStorageDependencyUpdateJob,
   1012                       'dep', 'plat', 'path')
   1013     self.assertRaises(ValueError, config.AddCloudStorageDependencyUpdateJob,
   1014                       'dep', 'plat', 'path', version='1.2.3')
   1015 
   1016     expected_dependencies = self.dependencies
   1017     config = dependency_manager.BaseConfig(self.file_path, writable=True)
   1018     self.assertFalse(config._IsDirty())
   1019     self.assertFalse(config._pending_uploads)
   1020     self.assertEqual(expected_dependencies, config._config_data)
   1021 
   1022     base_config_cs_mock.CalculateHash.return_value = self.new_dep_hash
   1023     uploader_cs_mock.Exists.return_value = False
   1024     expected_dependencies = self.new_dependencies
   1025     config.AddCloudStorageDependencyUpdateJob(
   1026         'dep1', 'plat2', self.new_dep_path, execute_job=False)
   1027     self.assertTrue(config._IsDirty())
   1028     self.assertEqual(1, len(config._pending_uploads))
   1029     self.assertEqual(self.new_pending_upload, config._pending_uploads[0])
   1030     self.assertEqual(expected_dependencies, config._config_data)
   1031     # check that file contents have not been updated.
   1032     expected_file_lines = list(self.expected_file_lines)
   1033     file_module = fake_filesystem.FakeFileOpen(self.fs)
   1034     for line in file_module(self.file_path):
   1035       self.assertEqual(expected_file_lines.pop(0), line.strip())
   1036     self.fs.CloseOpenFile(file_module(self.file_path))
   1037 
   1038     expected_dependencies = self.final_dependencies
   1039     base_config_cs_mock.CalculateHash.return_value = self.final_dep_hash
   1040     config.AddCloudStorageDependencyUpdateJob(
   1041         'dep2', 'plat1', self.final_dep_path, execute_job=False)
   1042     self.assertTrue(config._IsDirty())
   1043     self.assertEqual(expected_dependencies, config._config_data)
   1044     # check that file contents have not been updated.
   1045     expected_file_lines = list(self.expected_file_lines)
   1046     file_module = fake_filesystem.FakeFileOpen(self.fs)
   1047     for line in file_module(self.file_path):
   1048       self.assertEqual(expected_file_lines.pop(0), line.strip())
   1049     self.fs.CloseOpenFile(file_module(self.file_path))
   1050 
   1051 
   1052 class BaseConfigDataManipulationUnittests(fake_filesystem_unittest.TestCase):
   1053   def setUp(self):
   1054     self.addTypeEqualityFunc(uploader.CloudStorageUploader,
   1055                              uploader.CloudStorageUploader.__eq__)
   1056     self.setUpPyfakefs()
   1057 
   1058     self.cs_bucket = 'bucket1'
   1059     self.cs_base_folder = 'dependencies_folder'
   1060     self.cs_hash = 'hash12'
   1061     self.download_path = '../../relative/dep1/path2'
   1062     self.local_paths = ['../../../relative/local/path21',
   1063                         '../../../relative/local/path22']
   1064     self.platform_dict = {'cloud_storage_hash': self.cs_hash,
   1065                           'download_path': self.download_path,
   1066                           'local_paths': self.local_paths}
   1067     self.dependencies = {
   1068         'dep1': {
   1069             'cloud_storage_bucket': self.cs_bucket,
   1070             'cloud_storage_base_folder': self.cs_base_folder,
   1071             'file_info': {
   1072                 'plat1': {
   1073                     'cloud_storage_hash': 'hash11',
   1074                     'download_path': '../../relative/dep1/path1',
   1075                     'local_paths': ['../../../relative/local/path11',
   1076                                     '../../../relative/local/path12']},
   1077                 'plat2': self.platform_dict
   1078             }
   1079         },
   1080         'dep2': {
   1081             'cloud_storage_bucket': 'bucket2',
   1082             'file_info': {
   1083                 'plat1': {
   1084                     'cloud_storage_hash': 'hash21',
   1085                     'download_path': '../../relative/dep2/path1',
   1086                     'local_paths': ['../../../relative/local/path31',
   1087                                     '../../../relative/local/path32']},
   1088                 'plat2': {
   1089                     'cloud_storage_hash': 'hash22',
   1090                     'download_path': '../../relative/dep2/path2'}}}}
   1091 
   1092     self.file_path = os.path.abspath(os.path.join(
   1093         'path', 'to', 'config', 'file'))
   1094 
   1095 
   1096     self.expected_file_lines = [
   1097       # pylint: disable=bad-continuation
   1098       '{', '"config_type": "BaseConfig",', '"dependencies": {',
   1099         '"dep1": {', '"cloud_storage_base_folder": "dependencies_folder",',
   1100           '"cloud_storage_bucket": "bucket1",', '"file_info": {',
   1101             '"plat1": {', '"cloud_storage_hash": "hash11",',
   1102               '"download_path": "../../relative/dep1/path1",',
   1103               '"local_paths": [', '"../../../relative/local/path11",',
   1104                               '"../../../relative/local/path12"', ']', '},',
   1105             '"plat2": {', '"cloud_storage_hash": "hash12",',
   1106               '"download_path": "../../relative/dep1/path2",',
   1107               '"local_paths": [', '"../../../relative/local/path21",',
   1108                               '"../../../relative/local/path22"', ']',
   1109               '}', '}', '},',
   1110         '"dep2": {', '"cloud_storage_bucket": "bucket2",', '"file_info": {',
   1111             '"plat1": {', '"cloud_storage_hash": "hash21",',
   1112               '"download_path": "../../relative/dep2/path1",',
   1113               '"local_paths": [', '"../../../relative/local/path31",',
   1114                               '"../../../relative/local/path32"', ']', '},',
   1115             '"plat2": {', '"cloud_storage_hash": "hash22",',
   1116               '"download_path": "../../relative/dep2/path2"', '}', '}', '}',
   1117       '}', '}']
   1118     self.fs.CreateFile(self.file_path,
   1119                        contents='\n'.join(self.expected_file_lines))
   1120 
   1121 
   1122   def testSetPlatformDataFailureNotWritable(self):
   1123     config = dependency_manager.BaseConfig(self.file_path)
   1124     self.assertRaises(
   1125         dependency_manager.ReadWriteError, config._SetPlatformData,
   1126         'dep1', 'plat1', 'cloud_storage_bucket', 'new_bucket')
   1127     self.assertEqual(self.dependencies, config._config_data)
   1128 
   1129   def testSetPlatformDataFailure(self):
   1130     config = dependency_manager.BaseConfig(self.file_path, writable=True)
   1131     self.assertRaises(ValueError, config._SetPlatformData, 'missing_dep',
   1132                       'plat2', 'cloud_storage_bucket', 'new_bucket')
   1133     self.assertEqual(self.dependencies, config._config_data)
   1134     self.assertRaises(ValueError, config._SetPlatformData, 'dep1',
   1135                       'missing_plat', 'cloud_storage_bucket', 'new_bucket')
   1136     self.assertEqual(self.dependencies, config._config_data)
   1137 
   1138 
   1139   def testSetPlatformDataCloudStorageBucketSuccess(self):
   1140     config = dependency_manager.BaseConfig(self.file_path, writable=True)
   1141     updated_cs_dependencies = {
   1142         'dep1': {'cloud_storage_bucket': 'new_bucket',
   1143                  'cloud_storage_base_folder': 'dependencies_folder',
   1144                  'file_info': {
   1145                      'plat1': {
   1146                          'cloud_storage_hash': 'hash11',
   1147                          'download_path': '../../relative/dep1/path1',
   1148                          'local_paths': ['../../../relative/local/path11',
   1149                                          '../../../relative/local/path12']},
   1150                      'plat2': {
   1151                          'cloud_storage_hash': 'hash12',
   1152                          'download_path': '../../relative/dep1/path2',
   1153                          'local_paths': ['../../../relative/local/path21',
   1154                                          '../../../relative/local/path22']}}},
   1155         'dep2': {'cloud_storage_bucket': 'bucket2',
   1156                  'file_info': {
   1157                      'plat1': {
   1158                          'cloud_storage_hash': 'hash21',
   1159                          'download_path': '../../relative/dep2/path1',
   1160                          'local_paths': ['../../../relative/local/path31',
   1161                                          '../../../relative/local/path32']},
   1162                      'plat2': {
   1163                          'cloud_storage_hash': 'hash22',
   1164                          'download_path': '../../relative/dep2/path2'}}}}
   1165     config._SetPlatformData('dep1', 'plat2', 'cloud_storage_bucket',
   1166                             'new_bucket')
   1167     self.assertEqual(updated_cs_dependencies, config._config_data)
   1168 
   1169   def testSetPlatformDataCloudStorageBaseFolderSuccess(self):
   1170     config = dependency_manager.BaseConfig(self.file_path, writable=True)
   1171     updated_cs_dependencies = {
   1172         'dep1': {'cloud_storage_bucket': 'bucket1',
   1173                  'cloud_storage_base_folder': 'new_dependencies_folder',
   1174                  'file_info': {
   1175                      'plat1': {
   1176                          'cloud_storage_hash': 'hash11',
   1177                          'download_path': '../../relative/dep1/path1',
   1178                          'local_paths': ['../../../relative/local/path11',
   1179                                          '../../../relative/local/path12']},
   1180                      'plat2': {
   1181                          'cloud_storage_hash': 'hash12',
   1182                          'download_path': '../../relative/dep1/path2',
   1183                          'local_paths': ['../../../relative/local/path21',
   1184                                          '../../../relative/local/path22']}}},
   1185         'dep2': {'cloud_storage_bucket': 'bucket2',
   1186                  'file_info': {
   1187                      'plat1': {
   1188                          'cloud_storage_hash': 'hash21',
   1189                          'download_path': '../../relative/dep2/path1',
   1190                          'local_paths': ['../../../relative/local/path31',
   1191                                          '../../../relative/local/path32']},
   1192                      'plat2': {
   1193                          'cloud_storage_hash': 'hash22',
   1194                          'download_path': '../../relative/dep2/path2'}}}}
   1195     config._SetPlatformData('dep1', 'plat2', 'cloud_storage_base_folder',
   1196                             'new_dependencies_folder')
   1197     self.assertEqual(updated_cs_dependencies, config._config_data)
   1198 
   1199   def testSetPlatformDataHashSuccess(self):
   1200     config = dependency_manager.BaseConfig(self.file_path, writable=True)
   1201     updated_cs_dependencies = {
   1202         'dep1': {'cloud_storage_bucket': 'bucket1',
   1203                  'cloud_storage_base_folder': 'dependencies_folder',
   1204                  'file_info': {
   1205                      'plat1': {
   1206                          'cloud_storage_hash': 'hash11',
   1207                          'download_path': '../../relative/dep1/path1',
   1208                          'local_paths': ['../../../relative/local/path11',
   1209                                          '../../../relative/local/path12']},
   1210                      'plat2': {
   1211                          'cloud_storage_hash': 'new_hash',
   1212                          'download_path': '../../relative/dep1/path2',
   1213                          'local_paths': ['../../../relative/local/path21',
   1214                                          '../../../relative/local/path22']}}},
   1215         'dep2': {'cloud_storage_bucket': 'bucket2',
   1216                  'file_info': {
   1217                      'plat1': {
   1218                          'cloud_storage_hash': 'hash21',
   1219                          'download_path': '../../relative/dep2/path1',
   1220                          'local_paths': ['../../../relative/local/path31',
   1221                                          '../../../relative/local/path32']},
   1222                      'plat2': {
   1223                          'cloud_storage_hash': 'hash22',
   1224                          'download_path': '../../relative/dep2/path2'}}}}
   1225     config._SetPlatformData('dep1', 'plat2', 'cloud_storage_hash',
   1226                             'new_hash')
   1227     self.assertEqual(updated_cs_dependencies, config._config_data)
   1228 
   1229   def testSetPlatformDataDownloadPathSuccess(self):
   1230     config = dependency_manager.BaseConfig(self.file_path, writable=True)
   1231     updated_cs_dependencies = {
   1232         'dep1': {'cloud_storage_bucket': 'bucket1',
   1233                  'cloud_storage_base_folder': 'dependencies_folder',
   1234                  'file_info': {
   1235                      'plat1': {
   1236                          'cloud_storage_hash': 'hash11',
   1237                          'download_path': '../../relative/dep1/path1',
   1238                          'local_paths': ['../../../relative/local/path11',
   1239                                          '../../../relative/local/path12']},
   1240                      'plat2': {
   1241                          'cloud_storage_hash': 'hash12',
   1242                          'download_path': '../../new/dep1/path2',
   1243                          'local_paths': ['../../../relative/local/path21',
   1244                                          '../../../relative/local/path22']}}},
   1245         'dep2': {'cloud_storage_bucket': 'bucket2',
   1246                  'file_info': {
   1247                      'plat1': {
   1248                          'cloud_storage_hash': 'hash21',
   1249                          'download_path': '../../relative/dep2/path1',
   1250                          'local_paths': ['../../../relative/local/path31',
   1251                                          '../../../relative/local/path32']},
   1252                      'plat2': {
   1253                          'cloud_storage_hash': 'hash22',
   1254                          'download_path': '../../relative/dep2/path2'}}}}
   1255     config._SetPlatformData('dep1', 'plat2', 'download_path',
   1256                             '../../new/dep1/path2')
   1257     self.assertEqual(updated_cs_dependencies, config._config_data)
   1258 
   1259   def testSetPlatformDataLocalPathSuccess(self):
   1260     config = dependency_manager.BaseConfig(self.file_path, writable=True)
   1261     updated_cs_dependencies = {
   1262         'dep1': {'cloud_storage_bucket': 'bucket1',
   1263                  'cloud_storage_base_folder': 'dependencies_folder',
   1264                  'file_info': {
   1265                      'plat1': {
   1266                          'cloud_storage_hash': 'hash11',
   1267                          'download_path': '../../relative/dep1/path1',
   1268                          'local_paths': ['../../../relative/local/path11',
   1269                                          '../../../relative/local/path12']},
   1270                      'plat2': {
   1271                          'cloud_storage_hash': 'hash12',
   1272                          'download_path': '../../relative/dep1/path2',
   1273                          'local_paths': ['../../new/relative/local/path21',
   1274                                          '../../new/relative/local/path22']}}},
   1275         'dep2': {'cloud_storage_bucket': 'bucket2',
   1276                  'file_info': {
   1277                      'plat1': {
   1278                          'cloud_storage_hash': 'hash21',
   1279                          'download_path': '../../relative/dep2/path1',
   1280                          'local_paths': ['../../../relative/local/path31',
   1281                                          '../../../relative/local/path32']},
   1282                      'plat2': {
   1283                          'cloud_storage_hash': 'hash22',
   1284                          'download_path': '../../relative/dep2/path2'}}}}
   1285     config._SetPlatformData('dep1', 'plat2', 'local_paths',
   1286                             ['../../new/relative/local/path21',
   1287                              '../../new/relative/local/path22'])
   1288     self.assertEqual(updated_cs_dependencies, config._config_data)
   1289 
   1290   def testGetPlatformDataFailure(self):
   1291     config = dependency_manager.BaseConfig(self.file_path, writable=True)
   1292     self.assertRaises(ValueError, config._GetPlatformData, 'missing_dep',
   1293                       'plat2', 'cloud_storage_bucket')
   1294     self.assertEqual(self.dependencies, config._config_data)
   1295     self.assertRaises(ValueError, config._GetPlatformData, 'dep1',
   1296                       'missing_plat', 'cloud_storage_bucket')
   1297     self.assertEqual(self.dependencies, config._config_data)
   1298 
   1299   def testGetPlatformDataDictSuccess(self):
   1300     config = dependency_manager.BaseConfig(self.file_path, writable=True)
   1301     self.assertEqual(self.platform_dict,
   1302                      config._GetPlatformData('dep1', 'plat2'))
   1303     self.assertEqual(self.dependencies, config._config_data)
   1304 
   1305   def testGetPlatformDataCloudStorageBucketSuccess(self):
   1306     config = dependency_manager.BaseConfig(self.file_path, writable=True)
   1307     self.assertEqual(self.cs_bucket, config._GetPlatformData(
   1308         'dep1', 'plat2', 'cloud_storage_bucket'))
   1309     self.assertEqual(self.dependencies, config._config_data)
   1310 
   1311   def testGetPlatformDataCloudStorageBaseFolderSuccess(self):
   1312     config = dependency_manager.BaseConfig(self.file_path, writable=True)
   1313     self.assertEqual(self.cs_base_folder, config._GetPlatformData(
   1314         'dep1', 'plat2', 'cloud_storage_base_folder'))
   1315     self.assertEqual(self.dependencies, config._config_data)
   1316 
   1317   def testGetPlatformDataHashSuccess(self):
   1318     config = dependency_manager.BaseConfig(self.file_path, writable=True)
   1319     self.assertEqual(self.cs_hash, config._GetPlatformData(
   1320         'dep1', 'plat2', 'cloud_storage_hash'))
   1321     self.assertEqual(self.dependencies, config._config_data)
   1322 
   1323   def testGetPlatformDataDownloadPathSuccess(self):
   1324     config = dependency_manager.BaseConfig(self.file_path, writable=True)
   1325     self.assertEqual(self.download_path, config._GetPlatformData(
   1326         'dep1', 'plat2', 'download_path'))
   1327     self.assertEqual(self.dependencies, config._config_data)
   1328 
   1329   def testGetPlatformDataLocalPathSuccess(self):
   1330     config = dependency_manager.BaseConfig(self.file_path, writable=True)
   1331     self.assertEqual(self.local_paths, config._GetPlatformData(
   1332         'dep1', 'plat2', 'local_paths'))
   1333     self.assertEqual(self.dependencies, config._config_data)
   1334 
   1335 class BaseConfigTest(unittest.TestCase):
   1336   """ Subclassable unittests for BaseConfig.
   1337   For subclasses: override setUp, GetConfigDataFromDict,
   1338     and EndToEndExpectedConfigData as needed.
   1339 
   1340     setUp must set the following properties:
   1341       self.config_type: String returnedd from GetConfigType in config subclass.
   1342       self.config_class: the class for the config subclass.
   1343       self.config_module: importable module for the config subclass.
   1344       self.empty_dict: expected dictionary for an empty config, as it would be
   1345         stored in a json file.
   1346       self.one_dep_dict: example dictionary for a config with one dependency,
   1347         as it would be stored in a json file.
   1348   """
   1349   def setUp(self):
   1350     self.config_type = 'BaseConfig'
   1351     self.config_class = dependency_manager.BaseConfig
   1352     self.config_module = 'dependency_manager.base_config'
   1353 
   1354     self.empty_dict = {'config_type': self.config_type,
   1355                        'dependencies': {}}
   1356 
   1357     dependency_dict = {
   1358         'dep': {
   1359             'cloud_storage_base_folder': 'cs_base_folder1',
   1360             'cloud_storage_bucket': 'bucket1',
   1361             'file_info': {
   1362                 'plat1_arch1': {
   1363                     'cloud_storage_hash': 'hash111',
   1364                     'download_path': 'download_path111',
   1365                     'cs_remote_path': 'cs_path111',
   1366                     'version_in_cs': 'version_111',
   1367                     'local_paths': ['local_path1110', 'local_path1111']
   1368                 },
   1369                 'plat1_arch2': {
   1370                     'cloud_storage_hash': 'hash112',
   1371                     'download_path': 'download_path112',
   1372                     'cs_remote_path': 'cs_path112',
   1373                     'local_paths': ['local_path1120', 'local_path1121']
   1374                 },
   1375                 'win_arch1': {
   1376                     'cloud_storage_hash': 'hash1w1',
   1377                     'download_path': 'download\\path\\1w1',
   1378                     'cs_remote_path': 'cs_path1w1',
   1379                     'local_paths': ['local\\path\\1w10', 'local\\path\\1w11']
   1380                 },
   1381                 'all_the_variables': {
   1382                     'cloud_storage_hash': 'hash111',
   1383                     'download_path': 'download_path111',
   1384                     'cs_remote_path': 'cs_path111',
   1385                     'version_in_cs': 'version_111',
   1386                     'path_in_archive': 'path/in/archive',
   1387                     'local_paths': ['local_path1110', 'local_path1111']
   1388                 }
   1389             }
   1390         }
   1391     }
   1392     self.one_dep_dict = {'config_type': self.config_type,
   1393                          'dependencies': dependency_dict}
   1394 
   1395   def GetConfigDataFromDict(self, config_dict):
   1396     return config_dict.get('dependencies', {})
   1397 
   1398   @mock.patch('os.path')
   1399   @mock.patch('__builtin__.open')
   1400   def testInitBaseProperties(self, open_mock, path_mock):
   1401     # Init is not meant to be overridden, so we should be mocking the
   1402     # base_config's json module, even in subclasses.
   1403     json_module = 'dependency_manager.base_config.json'
   1404     with mock.patch(json_module) as json_mock:
   1405       json_mock.load.return_value = self.empty_dict.copy()
   1406       config = self.config_class('file_path')
   1407       self.assertEqual('file_path', config._config_path)
   1408       self.assertEqual(self.config_type, config.GetConfigType())
   1409       self.assertEqual(self.GetConfigDataFromDict(self.empty_dict),
   1410                        config._config_data)
   1411 
   1412 
   1413   @mock.patch('dependency_manager.dependency_info.DependencyInfo')
   1414   @mock.patch('os.path')
   1415   @mock.patch('__builtin__.open')
   1416   def testInitWithDependencies(self, open_mock, path_mock, dep_info_mock):
   1417     # Init is not meant to be overridden, so we should be mocking the
   1418     # base_config's json module, even in subclasses.
   1419     json_module = 'dependency_manager.base_config.json'
   1420     with mock.patch(json_module) as json_mock:
   1421       json_mock.load.return_value = self.one_dep_dict
   1422       config = self.config_class('file_path')
   1423       self.assertEqual('file_path', config._config_path)
   1424       self.assertEqual(self.config_type, config.GetConfigType())
   1425       self.assertEqual(self.GetConfigDataFromDict(self.one_dep_dict),
   1426                        config._config_data)
   1427 
   1428   def testFormatPath(self):
   1429     self.assertEqual(None, self.config_class._FormatPath(None))
   1430     self.assertEqual('', self.config_class._FormatPath(''))
   1431     self.assertEqual('some_string',
   1432                      self.config_class._FormatPath('some_string'))
   1433 
   1434     expected_path = os.path.join('some', 'file', 'path')
   1435     self.assertEqual(expected_path,
   1436                      self.config_class._FormatPath('some/file/path'))
   1437     self.assertEqual(expected_path,
   1438                      self.config_class._FormatPath('some\\file\\path'))
   1439 
   1440   @mock.patch('dependency_manager.base_config.json')
   1441   @mock.patch('dependency_manager.dependency_info.DependencyInfo')
   1442   @mock.patch('os.path.exists')
   1443   @mock.patch('__builtin__.open')
   1444   def testIterDependenciesError(
   1445       self, open_mock, exists_mock, dep_info_mock, json_mock):
   1446     # Init is not meant to be overridden, so we should be mocking the
   1447     # base_config's json module, even in subclasses.
   1448     json_mock.load.return_value = self.one_dep_dict
   1449     config = self.config_class('file_path', writable=True)
   1450     self.assertEqual(self.GetConfigDataFromDict(self.one_dep_dict),
   1451                      config._config_data)
   1452     self.assertTrue(config._writable)
   1453     with self.assertRaises(dependency_manager.ReadWriteError):
   1454       for _ in config.IterDependencyInfo():
   1455         pass
   1456 
   1457   @mock.patch('dependency_manager.base_config.json')
   1458   @mock.patch('dependency_manager.dependency_info.DependencyInfo')
   1459   @mock.patch('os.path.exists')
   1460   @mock.patch('__builtin__.open')
   1461   def testIterDependencies(
   1462       self, open_mock, exists_mock, dep_info_mock, json_mock):
   1463     json_mock.load.return_value = self.one_dep_dict
   1464     config = self.config_class('file_path')
   1465     self.assertEqual(self.GetConfigDataFromDict(self.one_dep_dict),
   1466                      config._config_data)
   1467     expected_dep_info = ['dep_info0', 'dep_info1', 'dep_info2']
   1468     dep_info_mock.side_effect = expected_dep_info
   1469     expected_calls = [
   1470         mock.call('dep', 'plat1_arch1', 'file_path', cs_bucket='bucket1',
   1471                   cs_hash='hash111', download_path='download_path111',
   1472                   cs_remote_path='cs_path111',
   1473                   local_paths=['local_path1110', 'local_path1111']),
   1474         mock.call('dep', 'plat1_arch1', 'file_path', cs_bucket='bucket1',
   1475                   cs_hash='hash112', download_path='download_path112',
   1476                   cs_remote_path='cs_path112',
   1477                   local_paths=['local_path1120', 'local_path1121']),
   1478         mock.call('dep', 'win_arch1', 'file_path', cs_bucket='bucket1',
   1479                   cs_hash='hash1w1',
   1480                   download_path=os.path.join('download', 'path', '1w1'),
   1481                   cs_remote_path='cs_path1w1',
   1482                   local_paths=[os.path.join('download', 'path', '1w10'),
   1483                                os.path.join('download', 'path', '1w11')])]
   1484     deps_seen = []
   1485     for dep_info in config.IterDependencyInfo():
   1486       deps_seen.append(dep_info)
   1487     dep_info_mock.assert_call_args(expected_calls)
   1488     self.assertItemsEqual(expected_dep_info, deps_seen)
   1489