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
3 # found in the LICENSE file.
7 from file_system
import FileSystem
, FileNotFoundError
8 from future
import Future
9 from test_file_system
import _List
, _StatTracker
, TestFileSystem
10 from path_util
import IsDirectory
13 class MockFileSystem(FileSystem
):
14 '''Wraps FileSystems to add a selection of mock behaviour:
15 - asserting how often Stat/Read calls are being made to it.
16 - primitive changes/versioning via applying object "diffs", mapping paths to
17 new content (similar to how TestFileSystem works).
19 def __init__(self
, file_system
):
20 self
._file
_system
= file_system
21 # Updates are stored as TestFileSystems because it already implements a
22 # bunch of logic to intepret paths into dictionaries.
24 self
._stat
_tracker
= _StatTracker()
26 self
._read
_resolve
_count
= 0
31 def Create(file_system
, updates
):
32 mock_file_system
= MockFileSystem(file_system
)
33 for update
in updates
:
34 mock_file_system
.Update(update
)
35 return mock_file_system
38 # FileSystem implementation.
41 def Read(self
, paths
, skip_not_found
=False):
42 '''Reads |paths| from |_file_system|, then applies the most recent update
43 from |_updates|, if any.
47 self
._read
_resolve
_count
+= 1
48 for path
in result
.iterkeys():
49 update
= self
._GetMostRecentUpdate
(path
)
50 if update
is not None:
53 return self
._file
_system
.Read(paths
,
54 skip_not_found
=skip_not_found
).Then(next
)
57 return self
._file
_system
.Refresh()
59 def _GetMostRecentUpdate(self
, path
):
60 '''Returns the latest update for the file at |path|, or None if |path|
61 has never been updated.
63 for update
in reversed(self
._updates
):
65 return update
.ReadSingle(path
).Get()
66 except FileNotFoundError
:
73 # This only supports numeric stat values since we need to add to it. In
74 # reality the logic here could just be to randomly mutate the stat values
75 # every time there's an Update but that's less meaningful for testing.
77 return str(int(a
) + b
)
79 stat
= self
._file
_system
.Stat(path
)
80 stat
.version
= stradd(stat
.version
, self
._stat
_tracker
.GetVersion(path
))
81 if stat
.child_versions
:
82 for child_path
, child_version
in stat
.child_versions
.iteritems():
83 stat
.child_versions
[child_path
] = stradd(
84 stat
.child_versions
[child_path
],
85 self
._stat
_tracker
.GetVersion(posixpath
.join(path
, child_path
)))
89 def GetCommitID(self
):
90 return Future(value
=str(self
._stat
_tracker
.GetVersion('')))
92 def GetPreviousCommitID(self
):
93 return Future(value
=str(self
._stat
_tracker
.GetVersion('') - 1))
95 def GetIdentity(self
):
96 return self
._file
_system
.GetIdentity()
105 return 'MockFileSystem(read_count=%s, stat_count=%s, updates=%s)' % (
106 self
._read
_count
, self
._stat
_count
, len(self
._updates
))
112 def GetStatCount(self
):
113 return self
._stat
_count
115 def CheckAndReset(self
, stat_count
=0, read_count
=0, read_resolve_count
=0):
116 '''Returns a tuple (success, error). Use in tests like:
117 self.assertTrue(*object_store.CheckAndReset(...))
120 for desc
, expected
, actual
in (
121 ('read_count', read_count
, self
._read
_count
),
122 ('read_resolve_count', read_resolve_count
, self
._read
_resolve
_count
),
123 ('stat_count', stat_count
, self
._stat
_count
)):
124 if actual
!= expected
:
125 errors
.append('%s: expected %s got %s' % (desc
, expected
, actual
))
127 return (len(errors
) == 0, ', '.join(errors
))
133 self
._read
_resolve
_count
= 0
136 def Update(self
, update
):
137 self
._updates
.append(TestFileSystem(update
))
138 for path
in _List(update
).iterkeys():
139 # Any files (not directories) which changed are now at the version
140 # derived from |_updates|.
141 if not IsDirectory(path
):
142 self
._stat
_tracker
.SetVersion(path
, len(self
._updates
))
144 def SetVersion(self
, version
):
145 '''Override the reported FileSystem version (default None) for testing.'''
146 self
._version
= version