78
67
def describe(self):
79
68
return "%s is locked" % self.lockable_thing
82
class _AncestryMismatch(Mismatch):
83
"""Ancestry matching mismatch."""
85
def __init__(self, tip_revision, got, expected):
86
self.tip_revision = tip_revision
88
self.expected = expected
91
return "mismatched ancestry for revision %r was %r, expected %r" % (
92
self.tip_revision, self.got, self.expected)
95
class MatchesAncestry(Matcher):
96
"""A matcher that checks the ancestry of a particular revision.
98
:ivar graph: Graph in which to check the ancestry
99
:ivar revision_id: Revision id of the revision
102
def __init__(self, repository, revision_id):
103
Matcher.__init__(self)
104
self.repository = repository
105
self.revision_id = revision_id
108
return ('MatchesAncestry(repository=%r, revision_id=%r)' % (
109
self.repository, self.revision_id))
111
def match(self, expected):
112
with self.repository.lock_read():
113
graph = self.repository.get_graph()
114
got = [r for r, p in graph.iter_ancestry([self.revision_id])]
115
if _mod_revision.NULL_REVISION in got:
116
got.remove(_mod_revision.NULL_REVISION)
117
if sorted(got) != sorted(expected):
118
return _AncestryMismatch(self.revision_id, sorted(got),
122
class HasLayout(Matcher):
123
"""A matcher that checks if a tree has a specific layout.
125
:ivar entries: List of expected entries, as (path, file_id) pairs.
128
def __init__(self, entries):
129
Matcher.__init__(self)
130
self.entries = entries
132
def get_tree_layout(self, tree, include_file_ids):
133
"""Get the (path, file_id) pairs for the current tree."""
134
with tree.lock_read():
135
for path, ie in tree.iter_entries_by_dir():
137
path += ie.kind_character()
139
yield (path, ie.file_id)
144
def _strip_unreferenced_directories(entries):
145
"""Strip all directories that don't (in)directly contain any files.
147
:param entries: List of path strings or (path, ie) tuples to process
150
for entry in entries:
151
if isinstance(entry, str):
155
if not path or path[-1] == "/":
157
directories.append((path, entry))
159
# Yield the referenced parent directories
160
for dirpath, direntry in directories:
161
if osutils.is_inside(dirpath, path):
167
return 'HasLayout(%r)' % self.entries
169
def match(self, tree):
170
include_file_ids = self.entries and not isinstance(
171
self.entries[0], str)
172
actual = list(self.get_tree_layout(
173
tree, include_file_ids=include_file_ids))
174
if not tree.has_versioned_directories():
175
entries = list(self._strip_unreferenced_directories(self.entries))
177
entries = self.entries
178
return Equals(entries).match(actual)
181
class HasPathRelations(Matcher):
182
"""Matcher verifies that paths have a relation to those in another tree.
184
:ivar previous_tree: tree to compare to
185
:ivar previous_entries: List of expected entries, as (path, previous_path) pairs.
188
def __init__(self, previous_tree, previous_entries):
189
Matcher.__init__(self)
190
self.previous_tree = previous_tree
191
self.previous_entries = previous_entries
193
def get_path_map(self, tree):
194
"""Get the (path, previous_path) pairs for the current tree."""
195
previous_intertree = InterTree.get(self.previous_tree, tree)
196
with tree.lock_read(), self.previous_tree.lock_read():
197
for path, ie in tree.iter_entries_by_dir():
198
if tree.supports_rename_tracking():
199
previous_path = previous_intertree.find_source_path(path)
201
if self.previous_tree.is_versioned(path):
206
kind = self.previous_tree.kind(previous_path)
207
if kind == 'directory':
210
yield (u"", previous_path)
212
yield (path + ie.kind_character(), previous_path)
215
def _strip_unreferenced_directories(entries):
216
"""Strip all directories that don't (in)directly contain any files.
218
:param entries: List of path strings or (path, previous_path) tuples to process
220
directory_used = set()
222
for (path, previous_path) in entries:
223
if not path or path[-1] == "/":
225
directories.append((path, previous_path))
227
# Yield the referenced parent directories
228
for direntry in directories:
229
if osutils.is_inside(direntry[0], path):
230
directory_used.add(direntry[0])
231
for (path, previous_path) in entries:
232
if (not path.endswith("/")) or path in directory_used:
233
yield (path, previous_path)
236
return 'HasPathRelations(%r, %r)' % (self.previous_tree, self.previous_entries)
238
def match(self, tree):
239
actual = list(self.get_path_map(tree))
240
if not tree.has_versioned_directories():
241
entries = list(self._strip_unreferenced_directories(
242
self.previous_entries))
244
entries = self.previous_entries
245
if not tree.supports_rename_tracking():
247
(path, path if self.previous_tree.is_versioned(path) else None)
248
for (path, previous_path) in entries]
249
return Equals(entries).match(actual)
252
class RevisionHistoryMatches(Matcher):
253
"""A matcher that checks if a branch has a specific revision history.
255
:ivar history: Revision history, as list of revisions. Oldest first.
258
def __init__(self, history):
259
Matcher.__init__(self)
260
self.expected = history
263
return 'RevisionHistoryMatches(%r)' % self.expected
265
def match(self, branch):
266
with branch.lock_read():
267
graph = branch.repository.get_graph()
268
history = list(graph.iter_lefthand_ancestry(
269
branch.last_revision(), [_mod_revision.NULL_REVISION]))
271
return Equals(self.expected).match(history)