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 from file_system import FileSystem, FileNotFoundError, StatInfo | 5 from file_system import FileSystem, FileNotFoundError, StatInfo |
6 from future import Future | 6 from future import Future |
7 | 7 |
8 class TestFileSystem(FileSystem): | 8 class TestFileSystem(FileSystem): |
9 '''A FileSystem backed by an object. Create with an object representing file | 9 '''A FileSystem backed by an object. Create with an object representing file |
10 paths such that {'a': {'b': 'hello'}} will resolve Read('a/b') as 'hello', | 10 paths such that {'a': {'b': 'hello'}} will resolve Read('a/b') as 'hello', |
11 Read('a/') as ['b'], and Stat determined by a value incremented via | 11 Read('a/') as ['b'], and Stat determined by a value incremented via |
12 IncrementStat. | 12 IncrementStat. |
13 ''' | 13 ''' |
| 14 |
| 15 # TODO(kalman): this method would be unnecessary if we injected paths properly |
| 16 # in ServerInstance. |
| 17 @staticmethod |
| 18 def MoveTo(base, obj): |
| 19 '''Returns an object as |obj| moved to |prefix|. That is, |
| 20 MoveTo('foo/bar', {'a': 'b'}) -> {'foo': {'bar': {'a': 'b'}}} |
| 21 ''' |
| 22 result = {} |
| 23 leaf = result |
| 24 for k in base.split('/'): |
| 25 leaf[k] = {} |
| 26 leaf = leaf[k] |
| 27 leaf.update(obj) |
| 28 return result |
| 29 |
14 def __init__(self, obj): | 30 def __init__(self, obj): |
15 self._obj = obj | 31 self._obj = obj |
16 self._stat = 0 | 32 self._global_stat = 0 |
| 33 self._path_stats = {} |
| 34 self._read_count = 0 |
| 35 self._stat_count = 0 |
| 36 |
| 37 # |
| 38 # FileSystem implementation. |
| 39 # |
17 | 40 |
18 def Read(self, paths, binary=False): | 41 def Read(self, paths, binary=False): |
| 42 self._read_count += 1 |
| 43 return self._ReadImpl(paths, binary=binary) |
| 44 |
| 45 def _ReadImpl(self, paths, binary=False): |
19 try: | 46 try: |
20 result = {} | 47 result = {} |
21 for path in paths: | 48 for path in paths: |
22 result[path] = self._ResolvePath(path) | 49 result[path] = self._ResolvePath(path) |
23 return Future(value=result) | 50 return Future(value=result) |
24 except FileNotFoundError as error: | 51 except FileNotFoundError as error: |
25 return Future(error=error) | 52 return Future(error=error) |
26 | 53 |
27 def _ResolvePath(self, path): | 54 def _ResolvePath(self, path): |
28 def Resolve(parts): | 55 def Resolve(parts): |
(...skipping 10 matching lines...) Expand all Loading... |
39 | 66 |
40 def GetPaths(obj): | 67 def GetPaths(obj): |
41 '''Lists the paths within |obj|; this is basially keys() but with | 68 '''Lists the paths within |obj|; this is basially keys() but with |
42 directory paths (i.e. dicts) with a trailing /. | 69 directory paths (i.e. dicts) with a trailing /. |
43 ''' | 70 ''' |
44 def ToPath(k, v): | 71 def ToPath(k, v): |
45 if isinstance(v, basestring): | 72 if isinstance(v, basestring): |
46 return k | 73 return k |
47 if isinstance(v, dict): | 74 if isinstance(v, dict): |
48 return '%s/' % k | 75 return '%s/' % k |
49 raise ValueError('Cannot convert type % to path', type(v)) | 76 raise ValueError('Cannot convert type %s to path', type(v)) |
50 return [ToPath(k, v) for k, v in obj.items()] | 77 return [ToPath(k, v) for k, v in obj.items()] |
51 | 78 |
52 path = path.lstrip('/') | 79 path = path.lstrip('/') |
53 | 80 |
54 if path == '': | 81 if path == '': |
55 return GetPaths(self._obj) | 82 return GetPaths(self._obj) |
56 | 83 |
57 parts = path.split('/') | 84 parts = path.split('/') |
58 if parts[-1] != '': | 85 if parts[-1] != '': |
59 file_contents = Resolve(parts) | 86 file_contents = Resolve(parts) |
60 if not isinstance(file_contents, basestring): | 87 if not isinstance(file_contents, basestring): |
61 raise FileNotFoundError( | 88 raise FileNotFoundError( |
62 '%s (%s) did not resolve to a string, instead %s' % | 89 '%s (%s) did not resolve to a string, instead %s' % |
63 (path, parts, file_contents)) | 90 (path, parts, file_contents)) |
64 return file_contents | 91 return file_contents |
65 | 92 |
66 dir_contents = Resolve(parts[:-1]) | 93 dir_contents = Resolve(parts[:-1]) |
67 if not isinstance(dir_contents, dict): | 94 if not isinstance(dir_contents, dict): |
68 raise FileNotFoundError( | 95 raise FileNotFoundError( |
69 '%s (%s) did not resolve to a dict, instead %s' % | 96 '%s (%s) did not resolve to a dict, instead %s' % |
70 (path, parts, dir_contents)) | 97 (path, parts, dir_contents)) |
71 | 98 |
72 return GetPaths(dir_contents) | 99 return GetPaths(dir_contents) |
73 | 100 |
74 def Stat(self, path): | 101 def Stat(self, path): |
75 read_result = self.ReadSingle(path) | 102 self._stat_count += 1 |
76 stat_result = StatInfo(str(self._stat)) | 103 return self._StatImpl(path) |
| 104 |
| 105 def _StatImpl(self, path): |
| 106 read_result = self._ReadImpl([path]).Get()[path] |
| 107 stat_result = StatInfo(self._SinglePathStat(path)) |
77 if isinstance(read_result, list): | 108 if isinstance(read_result, list): |
78 stat_result.child_versions = dict((file_result, str(self._stat)) | 109 stat_result.child_versions = dict( |
79 for file_result in read_result) | 110 (file_result, self._SinglePathStat('%s%s' % (path, file_result))) |
| 111 for file_result in read_result) |
80 return stat_result | 112 return stat_result |
81 | 113 |
82 def IncrementStat(self): | 114 def _SinglePathStat(self, path): |
83 self._stat += 1 | 115 return str(self._global_stat + self._path_stats.get(path, 0)) |
| 116 |
| 117 # |
| 118 # Testing methods. |
| 119 # |
| 120 |
| 121 def IncrementStat(self, path=None): |
| 122 if path is not None: |
| 123 self._path_stats[path] = self._path_stats.get(path, 0) + 1 |
| 124 else: |
| 125 self._global_stat += 1 |
| 126 |
| 127 def CheckAndReset(self, stat_count=0, read_count=0): |
| 128 try: |
| 129 return (self._read_count == read_count and |
| 130 self._stat_count == stat_count) |
| 131 finally: |
| 132 self._read_count = 0 |
| 133 self._stat_count = 0 |
OLD | NEW |