#!/usr/bin/env python # Copyright (c) 2012 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import os import sys import unittest from caching_file_system import CachingFileSystem from extensions_paths import SERVER2 from file_system import StatInfo from local_file_system import LocalFileSystem from mock_file_system import MockFileSystem from object_store_creator import ObjectStoreCreator from test_file_system import TestFileSystem from test_object_store import TestObjectStore def _CreateLocalFs(): return LocalFileSystem.Create(SERVER2, 'test_data', 'file_system/') class CachingFileSystemTest(unittest.TestCase): def setUp(self): # Use this to make sure that every time _CreateCachingFileSystem is called # the underlying object store data is the same, within each test. self._object_store_dbs = {} def _CreateCachingFileSystem(self, fs, start_empty=False): def store_type_constructor(namespace, start_empty=False): '''Returns an ObjectStore backed onto test-lifetime-persistent objects in |_object_store_dbs|. ''' if namespace not in self._object_store_dbs: self._object_store_dbs[namespace] = {} db = self._object_store_dbs[namespace] if start_empty: db.clear() return TestObjectStore(namespace, init=db) object_store_creator = ObjectStoreCreator(start_empty=start_empty, store_type=store_type_constructor) return CachingFileSystem(fs, object_store_creator) def testReadFiles(self): file_system = self._CreateCachingFileSystem( _CreateLocalFs(), start_empty=False) expected = { './test1.txt': 'test1\n', './test2.txt': 'test2\n', './test3.txt': 'test3\n', } self.assertEqual( expected, file_system.Read(['./test1.txt', './test2.txt', './test3.txt']).Get()) def testListDir(self): file_system = self._CreateCachingFileSystem( _CreateLocalFs(), start_empty=False) expected = ['dir/'] + ['file%d.html' % i for i in range(7)] file_system._read_object_store.Set( 'list/', (expected, file_system.Stat('list/').version)) self.assertEqual(expected, sorted(file_system.ReadSingle('list/').Get())) expected.remove('file0.html') file_system._read_object_store.Set( 'list/', (expected, file_system.Stat('list/').version)) self.assertEqual(expected, sorted(file_system.ReadSingle('list/').Get())) def testCaching(self): test_fs = TestFileSystem({ 'bob': { 'bob0': 'bob/bob0 contents', 'bob1': 'bob/bob1 contents', 'bob2': 'bob/bob2 contents', 'bob3': 'bob/bob3 contents', } }) mock_fs = MockFileSystem(test_fs) def create_empty_caching_fs(): return self._CreateCachingFileSystem(mock_fs, start_empty=True) file_system = create_empty_caching_fs() # The stat/read should happen before resolving the Future, and resolving # the future shouldn't do any additional work. get_future = file_system.ReadSingle('bob/bob0') self.assertTrue(*mock_fs.CheckAndReset(read_count=1)) self.assertEqual('bob/bob0 contents', get_future.Get()) self.assertTrue(*mock_fs.CheckAndReset(read_resolve_count=1, stat_count=1)) # Resource has been cached, so test resource is not re-fetched. self.assertEqual('bob/bob0 contents', file_system.ReadSingle('bob/bob0').Get()) self.assertTrue(*mock_fs.CheckAndReset()) # Test if the Stat version is the same the resource is not re-fetched. file_system = create_empty_caching_fs() self.assertEqual('bob/bob0 contents', file_system.ReadSingle('bob/bob0').Get()) self.assertTrue(*mock_fs.CheckAndReset(stat_count=1)) # Test if there is a newer version, the resource is re-fetched. file_system = create_empty_caching_fs() test_fs.IncrementStat(); future = file_system.ReadSingle('bob/bob0') self.assertTrue(*mock_fs.CheckAndReset(read_count=1, stat_count=1)) self.assertEqual('bob/bob0 contents', future.Get()) self.assertTrue(*mock_fs.CheckAndReset(read_resolve_count=1)) # Test directory and subdirectory stats are cached. file_system = create_empty_caching_fs() file_system._stat_object_store.Del('bob/bob0') file_system._read_object_store.Del('bob/bob0') file_system._stat_object_store.Del('bob/bob1') test_fs.IncrementStat(); futures = (file_system.ReadSingle('bob/bob1'), file_system.ReadSingle('bob/bob0')) self.assertTrue(*mock_fs.CheckAndReset(read_count=2)) self.assertEqual(('bob/bob1 contents', 'bob/bob0 contents'), tuple(future.Get() for future in futures)) self.assertTrue(*mock_fs.CheckAndReset(read_resolve_count=2, stat_count=1)) self.assertEqual('bob/bob1 contents', file_system.ReadSingle('bob/bob1').Get()) self.assertTrue(*mock_fs.CheckAndReset()) # Test a more recent parent directory doesn't force a refetch of children. file_system = create_empty_caching_fs() file_system._read_object_store.Del('bob/bob0') file_system._read_object_store.Del('bob/bob1') futures = (file_system.ReadSingle('bob/bob1'), file_system.ReadSingle('bob/bob2'), file_system.ReadSingle('bob/bob3')) self.assertTrue(*mock_fs.CheckAndReset(read_count=3)) self.assertEqual( ('bob/bob1 contents', 'bob/bob2 contents', 'bob/bob3 contents'), tuple(future.Get() for future in futures)) self.assertTrue(*mock_fs.CheckAndReset(read_resolve_count=3, stat_count=1)) test_fs.IncrementStat(path='bob/bob0') file_system = create_empty_caching_fs() self.assertEqual('bob/bob1 contents', file_system.ReadSingle('bob/bob1').Get()) self.assertEqual('bob/bob2 contents', file_system.ReadSingle('bob/bob2').Get()) self.assertEqual('bob/bob3 contents', file_system.ReadSingle('bob/bob3').Get()) self.assertTrue(*mock_fs.CheckAndReset(stat_count=1)) file_system = create_empty_caching_fs() file_system._stat_object_store.Del('bob/bob0') future = file_system.ReadSingle('bob/bob0') self.assertTrue(*mock_fs.CheckAndReset(read_count=1)) self.assertEqual('bob/bob0 contents', future.Get()) self.assertTrue(*mock_fs.CheckAndReset(read_resolve_count=1, stat_count=1)) self.assertEqual('bob/bob0 contents', file_system.ReadSingle('bob/bob0').Get()) self.assertTrue(*mock_fs.CheckAndReset()) def testCachedStat(self): test_fs = TestFileSystem({ 'bob': { 'bob0': 'bob/bob0 contents', 'bob1': 'bob/bob1 contents' } }) mock_fs = MockFileSystem(test_fs) file_system = self._CreateCachingFileSystem(mock_fs, start_empty=False) self.assertEqual(StatInfo('0'), file_system.Stat('bob/bob0')) self.assertTrue(*mock_fs.CheckAndReset(stat_count=1)) self.assertEqual(StatInfo('0'), file_system.Stat('bob/bob0')) self.assertTrue(*mock_fs.CheckAndReset()) # Caching happens on a directory basis, so reading other files from that # directory won't result in a stat. self.assertEqual(StatInfo('0'), file_system.Stat('bob/bob1')) self.assertEqual( StatInfo('0', child_versions={'bob0': '0', 'bob1': '0'}), file_system.Stat('bob/')) self.assertTrue(*mock_fs.CheckAndReset()) # Even though the stat is bumped, the object store still has it cached so # this won't update. test_fs.IncrementStat() self.assertEqual(StatInfo('0'), file_system.Stat('bob/bob0')) self.assertEqual(StatInfo('0'), file_system.Stat('bob/bob1')) self.assertEqual( StatInfo('0', child_versions={'bob0': '0', 'bob1': '0'}), file_system.Stat('bob/')) self.assertTrue(*mock_fs.CheckAndReset()) def testFreshStat(self): test_fs = TestFileSystem({ 'bob': { 'bob0': 'bob/bob0 contents', 'bob1': 'bob/bob1 contents' } }) mock_fs = MockFileSystem(test_fs) def run_expecting_stat(stat): def run(): file_system = self._CreateCachingFileSystem(mock_fs, start_empty=True) self.assertEqual( StatInfo(stat, child_versions={'bob0': stat, 'bob1': stat}), file_system.Stat('bob/')) self.assertTrue(*mock_fs.CheckAndReset(stat_count=1)) self.assertEqual(StatInfo(stat), file_system.Stat('bob/bob0')) self.assertEqual(StatInfo(stat), file_system.Stat('bob/bob0')) self.assertTrue(*mock_fs.CheckAndReset()) run() run() run_expecting_stat('0') test_fs.IncrementStat() run_expecting_stat('1') def testSkipNotFound(self): caching_fs = self._CreateCachingFileSystem(TestFileSystem({ 'bob': { 'bob0': 'bob/bob0 contents', 'bob1': 'bob/bob1 contents' } })) def read_skip_not_found(paths): return caching_fs.Read(paths, skip_not_found=True).Get() self.assertEqual({}, read_skip_not_found(('grub',))) self.assertEqual({}, read_skip_not_found(('bob/bob2',))) self.assertEqual({ 'bob/bob0': 'bob/bob0 contents', }, read_skip_not_found(('bob/bob0', 'bob/bob2'))) if __name__ == '__main__': unittest.main()