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