67
85
def describe(self):
68
86
return "%s is locked" % self.lockable_thing
89
class _AncestryMismatch(Mismatch):
90
"""Ancestry matching mismatch."""
92
def __init__(self, tip_revision, got, expected):
93
self.tip_revision = tip_revision
95
self.expected = expected
98
return "mismatched ancestry for revision %r was %r, expected %r" % (
99
self.tip_revision, self.got, self.expected)
102
class MatchesAncestry(Matcher):
103
"""A matcher that checks the ancestry of a particular revision.
105
:ivar graph: Graph in which to check the ancestry
106
:ivar revision_id: Revision id of the revision
109
def __init__(self, repository, revision_id):
110
Matcher.__init__(self)
111
self.repository = repository
112
self.revision_id = revision_id
115
return ('MatchesAncestry(repository=%r, revision_id=%r)' % (
116
self.repository, self.revision_id))
118
def match(self, expected):
119
with self.repository.lock_read():
120
graph = self.repository.get_graph()
121
got = [r for r, p in graph.iter_ancestry([self.revision_id])]
122
if _mod_revision.NULL_REVISION in got:
123
got.remove(_mod_revision.NULL_REVISION)
124
if sorted(got) != sorted(expected):
125
return _AncestryMismatch(self.revision_id, sorted(got),
129
class HasLayout(Matcher):
130
"""A matcher that checks if a tree has a specific layout.
132
:ivar entries: List of expected entries, as (path, file_id) pairs.
135
def __init__(self, entries):
136
Matcher.__init__(self)
137
self.entries = entries
139
def get_tree_layout(self, tree):
140
"""Get the (path, file_id) pairs for the current tree."""
141
with tree.lock_read():
142
for path, ie in tree.iter_entries_by_dir():
143
if ie.parent_id is None:
144
yield (u"", ie.file_id)
146
yield (path+ie.kind_character(), ie.file_id)
149
def _strip_unreferenced_directories(entries):
150
"""Strip all directories that don't (in)directly contain any files.
152
:param entries: List of path strings or (path, ie) tuples to process
155
for entry in entries:
156
if isinstance(entry, (str, text_type)):
160
if not path or path[-1] == "/":
162
directories.append((path, entry))
164
# Yield the referenced parent directories
165
for dirpath, direntry in directories:
166
if osutils.is_inside(dirpath, path):
172
return 'HasLayout(%r)' % self.entries
174
def match(self, tree):
175
actual = list(self.get_tree_layout(tree))
176
if self.entries and isinstance(self.entries[0], (str, text_type)):
177
actual = [path for (path, fileid) in actual]
178
if not tree.has_versioned_directories():
179
entries = list(self._strip_unreferenced_directories(self.entries))
181
entries = self.entries
182
return Equals(entries).match(actual)
185
class RevisionHistoryMatches(Matcher):
186
"""A matcher that checks if a branch has a specific revision history.
188
:ivar history: Revision history, as list of revisions. Oldest first.
191
def __init__(self, history):
192
Matcher.__init__(self)
193
self.expected = history
196
return 'RevisionHistoryMatches(%r)' % self.expected
198
def match(self, branch):
199
with branch.lock_read():
200
graph = branch.repository.get_graph()
201
history = list(graph.iter_lefthand_ancestry(
202
branch.last_revision(), [_mod_revision.NULL_REVISION]))
204
return Equals(self.expected).match(history)
207
class _NoVfsCallsMismatch(Mismatch):
208
"""Mismatch describing a list of HPSS calls which includes VFS requests."""
210
def __init__(self, vfs_calls):
211
self.vfs_calls = vfs_calls
214
return "no VFS calls expected, got: %s" % ",".join([
215
"%s(%s)" % (c.method,
216
", ".join([repr(a) for a in c.args])) for c in self.vfs_calls])
219
class ContainsNoVfsCalls(Matcher):
220
"""Ensure that none of the specified calls are HPSS calls."""
223
return 'ContainsNoVfsCalls()'
226
def match(cls, hpss_calls):
228
for call in hpss_calls:
230
request_method = smart_request_handlers.get(call.call.method)
232
# A method we don't know about doesn't count as a VFS method.
234
if issubclass(request_method, vfs.VfsRequest):
235
vfs_calls.append(call.call)
236
if len(vfs_calls) == 0:
238
return _NoVfsCallsMismatch(vfs_calls)