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.
10 from caching_file_system
import CachingFileSystem
11 from extensions_paths
import SERVER2
12 from file_system
import FileNotFoundError
, 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
21 return LocalFileSystem
.Create(SERVER2
, 'test_data', 'file_system/')
24 class CachingFileSystemTest(unittest
.TestCase
):
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
= {}
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|.
35 if namespace
not in self
._object
_store
_dbs
:
36 self
._object
_store
_dbs
[namespace
] = {}
37 db
= self
._object
_store
_dbs
[namespace
]
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
)
45 def testReadFiles(self
):
46 file_system
= self
._CreateCachingFileSystem
(
47 _CreateLocalFs(), start_empty
=False)
49 './test1.txt': 'test1\n',
50 './test2.txt': 'test2\n',
51 './test3.txt': 'test3\n',
55 file_system
.Read(['./test1.txt', './test2.txt', './test3.txt']).Get())
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
_cache
.Set(
63 (expected
, file_system
.Stat('list/').version
))
64 self
.assertEqual(expected
, sorted(file_system
.ReadSingle('list/').Get()))
66 expected
.remove('file0.html')
67 file_system
._read
_cache
.Set(
69 (expected
, file_system
.Stat('list/').version
))
70 self
.assertEqual(expected
, sorted(file_system
.ReadSingle('list/').Get()))
72 def testCaching(self
):
73 test_fs
= TestFileSystem({
75 'bob0': 'bob/bob0 contents',
76 'bob1': 'bob/bob1 contents',
77 'bob2': 'bob/bob2 contents',
78 'bob3': 'bob/bob3 contents',
81 mock_fs
= MockFileSystem(test_fs
)
82 def create_empty_caching_fs():
83 return self
._CreateCachingFileSystem
(mock_fs
, start_empty
=True)
85 file_system
= create_empty_caching_fs()
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))
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())
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))
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))
113 # Test directory and subdirectory stats are cached.
114 file_system
= create_empty_caching_fs()
115 file_system
._stat
_cache
.Del('bob/bob0')
116 file_system
._read
_cache
.Del('bob/bob0')
117 file_system
._stat
_cache
.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())
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
_cache
.Del('bob/bob0')
132 file_system
._read
_cache
.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))
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))
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))
152 file_system
= create_empty_caching_fs()
153 file_system
._stat
_cache
.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())
162 # Test skip_not_found caching behavior.
163 file_system
= create_empty_caching_fs()
164 future
= file_system
.ReadSingle('bob/no_file', skip_not_found
=True)
165 self
.assertTrue(*mock_fs
.CheckAndReset(read_count
=1))
166 self
.assertEqual(None, future
.Get())
167 self
.assertTrue(*mock_fs
.CheckAndReset(read_resolve_count
=1, stat_count
=1))
168 future
= file_system
.ReadSingle('bob/no_file', skip_not_found
=True)
169 # There shouldn't be another read/stat from the file system;
170 # we know the file is not there.
171 self
.assertTrue(*mock_fs
.CheckAndReset())
172 future
= file_system
.ReadSingle('bob/no_file')
173 self
.assertTrue(*mock_fs
.CheckAndReset(read_count
=1))
174 # Even though we cached information about non-existent files,
175 # trying to read one without specifiying skip_not_found should
176 # still raise an error.
177 self
.assertRaises(FileNotFoundError
, future
.Get
)
179 def testCachedStat(self
):
180 test_fs
= TestFileSystem({
182 'bob0': 'bob/bob0 contents',
183 'bob1': 'bob/bob1 contents'
186 mock_fs
= MockFileSystem(test_fs
)
188 file_system
= self
._CreateCachingFileSystem
(mock_fs
, start_empty
=False)
190 self
.assertEqual(StatInfo('0'), file_system
.Stat('bob/bob0'))
191 self
.assertTrue(*mock_fs
.CheckAndReset(stat_count
=1))
192 self
.assertEqual(StatInfo('0'), file_system
.Stat('bob/bob0'))
193 self
.assertTrue(*mock_fs
.CheckAndReset())
195 # Caching happens on a directory basis, so reading other files from that
196 # directory won't result in a stat.
197 self
.assertEqual(StatInfo('0'), file_system
.Stat('bob/bob1'))
199 StatInfo('0', child_versions
={'bob0': '0', 'bob1': '0'}),
200 file_system
.Stat('bob/'))
201 self
.assertTrue(*mock_fs
.CheckAndReset())
203 # Even though the stat is bumped, the object store still has it cached so
205 test_fs
.IncrementStat()
206 self
.assertEqual(StatInfo('0'), file_system
.Stat('bob/bob0'))
207 self
.assertEqual(StatInfo('0'), file_system
.Stat('bob/bob1'))
209 StatInfo('0', child_versions
={'bob0': '0', 'bob1': '0'}),
210 file_system
.Stat('bob/'))
211 self
.assertTrue(*mock_fs
.CheckAndReset())
213 def testFreshStat(self
):
214 test_fs
= TestFileSystem({
216 'bob0': 'bob/bob0 contents',
217 'bob1': 'bob/bob1 contents'
220 mock_fs
= MockFileSystem(test_fs
)
222 def run_expecting_stat(stat
):
224 file_system
= self
._CreateCachingFileSystem
(mock_fs
, start_empty
=True)
226 StatInfo(stat
, child_versions
={'bob0': stat
, 'bob1': stat
}),
227 file_system
.Stat('bob/'))
228 self
.assertTrue(*mock_fs
.CheckAndReset(stat_count
=1))
229 self
.assertEqual(StatInfo(stat
), file_system
.Stat('bob/bob0'))
230 self
.assertEqual(StatInfo(stat
), file_system
.Stat('bob/bob0'))
231 self
.assertTrue(*mock_fs
.CheckAndReset())
235 run_expecting_stat('0')
236 test_fs
.IncrementStat()
237 run_expecting_stat('1')
239 def testSkipNotFound(self
):
240 caching_fs
= self
._CreateCachingFileSystem
(TestFileSystem({
242 'bob0': 'bob/bob0 contents',
243 'bob1': 'bob/bob1 contents'
246 def read_skip_not_found(paths
):
247 return caching_fs
.Read(paths
, skip_not_found
=True).Get()
248 self
.assertEqual({}, read_skip_not_found(('grub',)))
249 self
.assertEqual({}, read_skip_not_found(('bob/bob2',)))
251 'bob/bob0': 'bob/bob0 contents',
252 }, read_skip_not_found(('bob/bob0', 'bob/bob2')))
254 def testWalkCaching(self
):
255 test_fs
= TestFileSystem({
260 'dir1_file1': 'dir1_file1',
263 'dir3_file1': 'dir3_file1',
264 'dir3_file2': 'dir3_file2'
269 mock_fs
= MockFileSystem(test_fs
)
270 file_system
= self
._CreateCachingFileSystem
(mock_fs
, start_empty
=True)
271 for walkinfo
in file_system
.Walk(''):
273 self
.assertTrue(*mock_fs
.CheckAndReset(
274 read_resolve_count
=5, read_count
=5, stat_count
=5))
276 all_dirs
, all_files
= [], []
277 for root
, dirs
, files
in file_system
.Walk(''):
278 all_dirs
.extend(dirs
)
279 all_files
.extend(files
)
280 self
.assertEqual(sorted(['root/', 'dir1/', 'dir2/', 'dir3/']),
283 sorted(['file1', 'file2', 'dir1_file1', 'dir3_file1', 'dir3_file2']),
285 # All data should be cached.
286 self
.assertTrue(*mock_fs
.CheckAndReset())
288 # Starting from a different root should still pull cached data.
289 for walkinfo
in file_system
.Walk('root/dir1/'):
291 self
.assertTrue(*mock_fs
.CheckAndReset())
292 # TODO(ahernandez): Test with a new instance CachingFileSystem so a
293 # different object store is utilized.
295 def testVersionedStat(self
):
296 test_fs
= TestFileSystem({
298 'bob0': 'bob/bob0 contents',
299 'bob1': 'bob/bob1 contents'
303 # Create a versioned FileSystem and verify that multiple CachingFileSystem
304 # instances wrapping it will share the same stat cache.
305 mock_fs
= MockFileSystem(test_fs
)
306 mock_fs
.SetVersion('abcdefg')
308 def run_and_expect_stat_count(paths
, stat_count
=0):
309 file_system
= self
._CreateCachingFileSystem
(mock_fs
, start_empty
=True)
310 [file_system
.Stat(path
) for path
in paths
]
311 self
.assertTrue(*mock_fs
.CheckAndReset(stat_count
=stat_count
))
313 run_and_expect_stat_count(['bob/', 'bob/bob0', 'bob/bob1'], stat_count
=1)
314 run_and_expect_stat_count(['bob/', 'bob/bob0', 'bob/bob1'], stat_count
=0)
316 if __name__
== '__main__':