| OLD | NEW |
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import posixpath |
| 6 |
| 5 from file_system import FileSystem, FileNotFoundError | 7 from file_system import FileSystem, FileNotFoundError |
| 6 from future import Gettable, Future | 8 from future import Gettable, Future |
| 7 from test_file_system import TestFileSystem | 9 from test_file_system import _List, _StatTracker, TestFileSystem |
| 10 from path_util import IsDirectory |
| 11 |
| 8 | 12 |
| 9 class MockFileSystem(FileSystem): | 13 class MockFileSystem(FileSystem): |
| 10 '''Wraps FileSystems to add a selection of mock behaviour: | 14 '''Wraps FileSystems to add a selection of mock behaviour: |
| 11 | |
| 12 - asserting how often Stat/Read calls are being made to it. | 15 - asserting how often Stat/Read calls are being made to it. |
| 13 - primitive changes/versioning via applying object "diffs", mapping paths to | 16 - primitive changes/versioning via applying object "diffs", mapping paths to |
| 14 new content (similar to how TestFileSystem works). | 17 new content (similar to how TestFileSystem works). |
| 15 ''' | 18 ''' |
| 16 def __init__(self, file_system): | 19 def __init__(self, file_system): |
| 17 self._file_system = file_system | 20 self._file_system = file_system |
| 18 # Updates are modelled are stored as TestFileSystems because they've | 21 # Updates are stored as TestFileSystems because it already implements a |
| 19 # implemented a bunch of logic to interpret paths into dictionaries. | 22 # bunch of logic to intepret paths into dictionaries. |
| 20 self._updates = [] | 23 self._updates = [] |
| 24 self._stat_tracker = _StatTracker() |
| 21 self._read_count = 0 | 25 self._read_count = 0 |
| 22 self._read_resolve_count = 0 | 26 self._read_resolve_count = 0 |
| 23 self._stat_count = 0 | 27 self._stat_count = 0 |
| 24 | 28 |
| 25 @staticmethod | 29 @staticmethod |
| 26 def Create(file_system, updates): | 30 def Create(file_system, updates): |
| 27 mock_file_system = MockFileSystem(file_system) | 31 mock_file_system = MockFileSystem(file_system) |
| 28 for update in updates: | 32 for update in updates: |
| 29 mock_file_system.Update(update) | 33 mock_file_system.Update(update) |
| 30 return mock_file_system | 34 return mock_file_system |
| 31 | 35 |
| 32 # | 36 # |
| 33 # FileSystem implementation. | 37 # FileSystem implementation. |
| 34 # | 38 # |
| 35 | 39 |
| 36 def Read(self, paths): | 40 def Read(self, paths): |
| 37 '''Reads |paths| from |_file_system|, then applies the most recent update | 41 '''Reads |paths| from |_file_system|, then applies the most recent update |
| 38 from |_updates|, if any. | 42 from |_updates|, if any. |
| 39 ''' | 43 ''' |
| 40 self._read_count += 1 | 44 self._read_count += 1 |
| 41 future_result = self._file_system.Read(paths) | 45 future_result = self._file_system.Read(paths) |
| 42 def resolve(): | 46 def resolve(): |
| 43 self._read_resolve_count += 1 | 47 self._read_resolve_count += 1 |
| 44 result = future_result.Get() | 48 result = future_result.Get() |
| 45 for path in result.iterkeys(): | 49 for path in result.iterkeys(): |
| 46 _, update = self._GetMostRecentUpdate(path) | 50 update = self._GetMostRecentUpdate(path) |
| 47 if update is not None: | 51 if update is not None: |
| 48 result[path] = update | 52 result[path] = update |
| 49 return result | 53 return result |
| 50 return Future(delegate=Gettable(resolve)) | 54 return Future(delegate=Gettable(resolve)) |
| 51 | 55 |
| 52 def Refresh(self): | 56 def Refresh(self): |
| 53 return self._file_system.Refresh() | 57 return self._file_system.Refresh() |
| 54 | 58 |
| 55 def _GetMostRecentUpdate(self, path): | 59 def _GetMostRecentUpdate(self, path): |
| 56 for revision, update in reversed(list(enumerate(self._updates))): | 60 '''Returns the latest update for the file at |path|, or None if |path| |
| 61 has never been updated. |
| 62 ''' |
| 63 for update in reversed(self._updates): |
| 57 try: | 64 try: |
| 58 return (revision + 1, update.ReadSingle(path).Get()) | 65 return update.ReadSingle(path).Get() |
| 59 except FileNotFoundError: | 66 except FileNotFoundError: |
| 60 pass | 67 pass |
| 61 return (0, None) | 68 return None |
| 62 | 69 |
| 63 def Stat(self, path): | 70 def Stat(self, path): |
| 64 self._stat_count += 1 | 71 self._stat_count += 1 |
| 65 return self._StatImpl(path) | |
| 66 | 72 |
| 67 def _StatImpl(self, path): | 73 # This only supports numeric stat values since we need to add to it. In |
| 68 result = self._file_system.Stat(path) | 74 # reality the logic here could just be to randomly mutate the stat values |
| 69 result.version = self._UpdateStat(result.version, path) | 75 # every time there's an Update but that's less meaningful for testing. |
| 70 child_versions = result.child_versions | 76 def stradd(a, b): |
| 71 if child_versions is not None: | 77 return str(int(a) + b) |
| 72 for child_path in child_versions.iterkeys(): | |
| 73 child_versions[child_path] = self._UpdateStat( | |
| 74 child_versions[child_path], | |
| 75 '%s%s' % (path, child_path)) | |
| 76 return result | |
| 77 | 78 |
| 78 def _UpdateStat(self, version, path): | 79 stat = self._file_system.Stat(path) |
| 79 if not path.endswith('/'): | 80 stat.version = stradd(stat.version, self._stat_tracker.GetVersion(path)) |
| 80 return str(int(version) + self._GetMostRecentUpdate(path)[0]) | 81 if stat.child_versions: |
| 81 # Bleh, it's a directory, need to recursively search all the children. | 82 for child_path, child_version in stat.child_versions.iteritems(): |
| 82 child_paths = self._file_system.ReadSingle(path).Get() | 83 stat.child_versions[child_path] = stradd( |
| 83 if not child_paths: | 84 stat.child_versions[child_path], |
| 84 return version | 85 self._stat_tracker.GetVersion(posixpath.join(path, child_path))) |
| 85 return str(max([int(version)] + | 86 |
| 86 [int(self._StatImpl('%s%s' % (path, child_path)).version) | 87 return stat |
| 87 for child_path in child_paths])) | |
| 88 | 88 |
| 89 def GetIdentity(self): | 89 def GetIdentity(self): |
| 90 return self._file_system.GetIdentity() | 90 return self._file_system.GetIdentity() |
| 91 | 91 |
| 92 def __str__(self): | 92 def __str__(self): |
| 93 return repr(self) | 93 return repr(self) |
| 94 | 94 |
| 95 def __repr__(self): | 95 def __repr__(self): |
| 96 return 'MockFileSystem(read_count=%s, stat_count=%s, updates=%s)' % ( | 96 return 'MockFileSystem(read_count=%s, stat_count=%s, updates=%s)' % ( |
| 97 self._read_count, self._stat_count, len(self._updates)) | 97 self._read_count, self._stat_count, len(self._updates)) |
| (...skipping 21 matching lines...) Expand all Loading... |
| 119 finally: | 119 finally: |
| 120 self.Reset() | 120 self.Reset() |
| 121 | 121 |
| 122 def Reset(self): | 122 def Reset(self): |
| 123 self._read_count = 0 | 123 self._read_count = 0 |
| 124 self._read_resolve_count = 0 | 124 self._read_resolve_count = 0 |
| 125 self._stat_count = 0 | 125 self._stat_count = 0 |
| 126 | 126 |
| 127 def Update(self, update): | 127 def Update(self, update): |
| 128 self._updates.append(TestFileSystem(update)) | 128 self._updates.append(TestFileSystem(update)) |
| 129 for path in _List(update).iterkeys(): |
| 130 # Any files (not directories) which changed are now at the version |
| 131 # derived from |_updates|. |
| 132 if not IsDirectory(path): |
| 133 self._stat_tracker.SetVersion(path, len(self._updates)) |
| OLD | NEW |