1 #!/usr/bin/env python 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 3 # Use of this source code is governed by a BSD-style license that can be 4 # found in the LICENSE file. 5 6 import os 7 import sys 8 import unittest 9 10 from caching_file_system import CachingFileSystem 11 from extensions_paths import SERVER2 12 from file_system import StatInfo 13 from local_file_system import LocalFileSystem 14 from mock_file_system import MockFileSystem 15 from object_store_creator import ObjectStoreCreator 16 from test_file_system import TestFileSystem 17 from test_object_store import TestObjectStore 18 19 20 def _CreateLocalFs(): 21 return LocalFileSystem.Create(SERVER2, 'test_data', 'file_system/') 22 23 24 class CachingFileSystemTest(unittest.TestCase): 25 def setUp(self): 26 # Use this to make sure that every time _CreateCachingFileSystem is called 27 # the underlying object store data is the same, within each test. 28 self._object_store_dbs = {} 29 30 def _CreateCachingFileSystem(self, fs, start_empty=False): 31 def store_type_constructor(namespace, start_empty=False): 32 '''Returns an ObjectStore backed onto test-lifetime-persistent objects 33 in |_object_store_dbs|. 34 ''' 35 if namespace not in self._object_store_dbs: 36 self._object_store_dbs[namespace] = {} 37 db = self._object_store_dbs[namespace] 38 if start_empty: 39 db.clear() 40 return TestObjectStore(namespace, init=db) 41 object_store_creator = ObjectStoreCreator(start_empty=start_empty, 42 store_type=store_type_constructor) 43 return CachingFileSystem(fs, object_store_creator) 44 45 def testReadFiles(self): 46 file_system = self._CreateCachingFileSystem( 47 _CreateLocalFs(), start_empty=False) 48 expected = { 49 './test1.txt': 'test1\n', 50 './test2.txt': 'test2\n', 51 './test3.txt': 'test3\n', 52 } 53 self.assertEqual( 54 expected, 55 file_system.Read(['./test1.txt', './test2.txt', './test3.txt']).Get()) 56 57 def testListDir(self): 58 file_system = self._CreateCachingFileSystem( 59 _CreateLocalFs(), start_empty=False) 60 expected = ['dir/'] + ['file%d.html' % i for i in range(7)] 61 file_system._read_object_store.Set( 62 'list/', 63 (expected, file_system.Stat('list/').version)) 64 self.assertEqual(expected, sorted(file_system.ReadSingle('list/').Get())) 65 66 expected.remove('file0.html') 67 file_system._read_object_store.Set( 68 'list/', 69 (expected, file_system.Stat('list/').version)) 70 self.assertEqual(expected, sorted(file_system.ReadSingle('list/').Get())) 71 72 def testCaching(self): 73 test_fs = TestFileSystem({ 74 'bob': { 75 'bob0': 'bob/bob0 contents', 76 'bob1': 'bob/bob1 contents', 77 'bob2': 'bob/bob2 contents', 78 'bob3': 'bob/bob3 contents', 79 } 80 }) 81 mock_fs = MockFileSystem(test_fs) 82 def create_empty_caching_fs(): 83 return self._CreateCachingFileSystem(mock_fs, start_empty=True) 84 85 file_system = create_empty_caching_fs() 86 87 # The stat/read should happen before resolving the Future, and resolving 88 # the future shouldn't do any additional work. 89 get_future = file_system.ReadSingle('bob/bob0') 90 self.assertTrue(*mock_fs.CheckAndReset(read_count=1)) 91 self.assertEqual('bob/bob0 contents', get_future.Get()) 92 self.assertTrue(*mock_fs.CheckAndReset(read_resolve_count=1, stat_count=1)) 93 94 # Resource has been cached, so test resource is not re-fetched. 95 self.assertEqual('bob/bob0 contents', 96 file_system.ReadSingle('bob/bob0').Get()) 97 self.assertTrue(*mock_fs.CheckAndReset()) 98 99 # Test if the Stat version is the same the resource is not re-fetched. 100 file_system = create_empty_caching_fs() 101 self.assertEqual('bob/bob0 contents', 102 file_system.ReadSingle('bob/bob0').Get()) 103 self.assertTrue(*mock_fs.CheckAndReset(stat_count=1)) 104 105 # Test if there is a newer version, the resource is re-fetched. 106 file_system = create_empty_caching_fs() 107 test_fs.IncrementStat(); 108 future = file_system.ReadSingle('bob/bob0') 109 self.assertTrue(*mock_fs.CheckAndReset(read_count=1, stat_count=1)) 110 self.assertEqual('bob/bob0 contents', future.Get()) 111 self.assertTrue(*mock_fs.CheckAndReset(read_resolve_count=1)) 112 113 # Test directory and subdirectory stats are cached. 114 file_system = create_empty_caching_fs() 115 file_system._stat_object_store.Del('bob/bob0') 116 file_system._read_object_store.Del('bob/bob0') 117 file_system._stat_object_store.Del('bob/bob1') 118 test_fs.IncrementStat(); 119 futures = (file_system.ReadSingle('bob/bob1'), 120 file_system.ReadSingle('bob/bob0')) 121 self.assertTrue(*mock_fs.CheckAndReset(read_count=2)) 122 self.assertEqual(('bob/bob1 contents', 'bob/bob0 contents'), 123 tuple(future.Get() for future in futures)) 124 self.assertTrue(*mock_fs.CheckAndReset(read_resolve_count=2, stat_count=1)) 125 self.assertEqual('bob/bob1 contents', 126 file_system.ReadSingle('bob/bob1').Get()) 127 self.assertTrue(*mock_fs.CheckAndReset()) 128 129 # Test a more recent parent directory doesn't force a refetch of children. 130 file_system = create_empty_caching_fs() 131 file_system._read_object_store.Del('bob/bob0') 132 file_system._read_object_store.Del('bob/bob1') 133 futures = (file_system.ReadSingle('bob/bob1'), 134 file_system.ReadSingle('bob/bob2'), 135 file_system.ReadSingle('bob/bob3')) 136 self.assertTrue(*mock_fs.CheckAndReset(read_count=3)) 137 self.assertEqual( 138 ('bob/bob1 contents', 'bob/bob2 contents', 'bob/bob3 contents'), 139 tuple(future.Get() for future in futures)) 140 self.assertTrue(*mock_fs.CheckAndReset(read_resolve_count=3, stat_count=1)) 141 142 test_fs.IncrementStat(path='bob/bob0') 143 file_system = create_empty_caching_fs() 144 self.assertEqual('bob/bob1 contents', 145 file_system.ReadSingle('bob/bob1').Get()) 146 self.assertEqual('bob/bob2 contents', 147 file_system.ReadSingle('bob/bob2').Get()) 148 self.assertEqual('bob/bob3 contents', 149 file_system.ReadSingle('bob/bob3').Get()) 150 self.assertTrue(*mock_fs.CheckAndReset(stat_count=1)) 151 152 file_system = create_empty_caching_fs() 153 file_system._stat_object_store.Del('bob/bob0') 154 future = file_system.ReadSingle('bob/bob0') 155 self.assertTrue(*mock_fs.CheckAndReset(read_count=1)) 156 self.assertEqual('bob/bob0 contents', future.Get()) 157 self.assertTrue(*mock_fs.CheckAndReset(read_resolve_count=1, stat_count=1)) 158 self.assertEqual('bob/bob0 contents', 159 file_system.ReadSingle('bob/bob0').Get()) 160 self.assertTrue(*mock_fs.CheckAndReset()) 161 162 def testCachedStat(self): 163 test_fs = TestFileSystem({ 164 'bob': { 165 'bob0': 'bob/bob0 contents', 166 'bob1': 'bob/bob1 contents' 167 } 168 }) 169 mock_fs = MockFileSystem(test_fs) 170 171 file_system = self._CreateCachingFileSystem(mock_fs, start_empty=False) 172 173 self.assertEqual(StatInfo('0'), file_system.Stat('bob/bob0')) 174 self.assertTrue(*mock_fs.CheckAndReset(stat_count=1)) 175 self.assertEqual(StatInfo('0'), file_system.Stat('bob/bob0')) 176 self.assertTrue(*mock_fs.CheckAndReset()) 177 178 # Caching happens on a directory basis, so reading other files from that 179 # directory won't result in a stat. 180 self.assertEqual(StatInfo('0'), file_system.Stat('bob/bob1')) 181 self.assertEqual( 182 StatInfo('0', child_versions={'bob0': '0', 'bob1': '0'}), 183 file_system.Stat('bob/')) 184 self.assertTrue(*mock_fs.CheckAndReset()) 185 186 # Even though the stat is bumped, the object store still has it cached so 187 # this won't update. 188 test_fs.IncrementStat() 189 self.assertEqual(StatInfo('0'), file_system.Stat('bob/bob0')) 190 self.assertEqual(StatInfo('0'), file_system.Stat('bob/bob1')) 191 self.assertEqual( 192 StatInfo('0', child_versions={'bob0': '0', 'bob1': '0'}), 193 file_system.Stat('bob/')) 194 self.assertTrue(*mock_fs.CheckAndReset()) 195 196 def testFreshStat(self): 197 test_fs = TestFileSystem({ 198 'bob': { 199 'bob0': 'bob/bob0 contents', 200 'bob1': 'bob/bob1 contents' 201 } 202 }) 203 mock_fs = MockFileSystem(test_fs) 204 205 def run_expecting_stat(stat): 206 def run(): 207 file_system = self._CreateCachingFileSystem(mock_fs, start_empty=True) 208 self.assertEqual( 209 StatInfo(stat, child_versions={'bob0': stat, 'bob1': stat}), 210 file_system.Stat('bob/')) 211 self.assertTrue(*mock_fs.CheckAndReset(stat_count=1)) 212 self.assertEqual(StatInfo(stat), file_system.Stat('bob/bob0')) 213 self.assertEqual(StatInfo(stat), file_system.Stat('bob/bob0')) 214 self.assertTrue(*mock_fs.CheckAndReset()) 215 run() 216 run() 217 218 run_expecting_stat('0') 219 test_fs.IncrementStat() 220 run_expecting_stat('1') 221 222 def testSkipNotFound(self): 223 caching_fs = self._CreateCachingFileSystem(TestFileSystem({ 224 'bob': { 225 'bob0': 'bob/bob0 contents', 226 'bob1': 'bob/bob1 contents' 227 } 228 })) 229 def read_skip_not_found(paths): 230 return caching_fs.Read(paths, skip_not_found=True).Get() 231 self.assertEqual({}, read_skip_not_found(('grub',))) 232 self.assertEqual({}, read_skip_not_found(('bob/bob2',))) 233 self.assertEqual({ 234 'bob/bob0': 'bob/bob0 contents', 235 }, read_skip_not_found(('bob/bob0', 'bob/bob2'))) 236 237 238 if __name__ == '__main__': 239 unittest.main() 240