67
80
def describe(self):
68
81
return "%s is locked" % self.lockable_thing
84
class _AncestryMismatch(Mismatch):
85
"""Ancestry matching mismatch."""
87
def __init__(self, tip_revision, got, expected):
88
self.tip_revision = tip_revision
90
self.expected = expected
93
return "mismatched ancestry for revision %r was %r, expected %r" % (
94
self.tip_revision, self.got, self.expected)
97
class MatchesAncestry(Matcher):
98
"""A matcher that checks the ancestry of a particular revision.
100
:ivar graph: Graph in which to check the ancestry
101
:ivar revision_id: Revision id of the revision
104
def __init__(self, repository, revision_id):
105
Matcher.__init__(self)
106
self.repository = repository
107
self.revision_id = revision_id
110
return ('MatchesAncestry(repository=%r, revision_id=%r)' % (
111
self.repository, self.revision_id))
113
def match(self, expected):
114
with self.repository.lock_read():
115
graph = self.repository.get_graph()
116
got = [r for r, p in graph.iter_ancestry([self.revision_id])]
117
if _mod_revision.NULL_REVISION in got:
118
got.remove(_mod_revision.NULL_REVISION)
119
if sorted(got) != sorted(expected):
120
return _AncestryMismatch(self.revision_id, sorted(got),
124
class HasLayout(Matcher):
125
"""A matcher that checks if a tree has a specific layout.
127
:ivar entries: List of expected entries, as (path, file_id) pairs.
130
def __init__(self, entries):
131
Matcher.__init__(self)
132
self.entries = entries
134
def get_tree_layout(self, tree, include_file_ids):
135
"""Get the (path, file_id) pairs for the current tree."""
136
with tree.lock_read():
137
for path, ie in tree.iter_entries_by_dir():
139
path += ie.kind_character()
141
yield (path, ie.file_id)
146
def _strip_unreferenced_directories(entries):
147
"""Strip all directories that don't (in)directly contain any files.
149
:param entries: List of path strings or (path, ie) tuples to process
152
for entry in entries:
153
if isinstance(entry, (str, text_type)):
157
if not path or path[-1] == "/":
159
directories.append((path, entry))
161
# Yield the referenced parent directories
162
for dirpath, direntry in directories:
163
if osutils.is_inside(dirpath, path):
169
return 'HasLayout(%r)' % self.entries
171
def match(self, tree):
172
include_file_ids = self.entries and not isinstance(
173
self.entries[0], (str, text_type))
174
actual = list(self.get_tree_layout(
175
tree, include_file_ids=include_file_ids))
176
if not tree.has_versioned_directories():
177
entries = list(self._strip_unreferenced_directories(self.entries))
179
entries = self.entries
180
return Equals(entries).match(actual)
183
class HasPathRelations(Matcher):
184
"""Matcher verifies that paths have a relation to those in another tree.
186
:ivar previous_tree: tree to compare to
187
:ivar previous_entries: List of expected entries, as (path, previous_path) pairs.
190
def __init__(self, previous_tree, previous_entries):
191
Matcher.__init__(self)
192
self.previous_tree = previous_tree
193
self.previous_entries = previous_entries
195
def get_path_map(self, tree):
196
"""Get the (path, previous_path) pairs for the current tree."""
197
previous_intertree = InterTree.get(self.previous_tree, tree)
198
with tree.lock_read(), self.previous_tree.lock_read():
199
for path, ie in tree.iter_entries_by_dir():
200
if tree.supports_rename_tracking():
201
previous_path = previous_intertree.find_source_path(path)
203
if self.previous_tree.is_versioned(path):
208
kind = self.previous_tree.kind(previous_path)
209
if kind == 'directory':
212
yield (u"", previous_path)
214
yield (path + ie.kind_character(), previous_path)
217
def _strip_unreferenced_directories(entries):
218
"""Strip all directories that don't (in)directly contain any files.
220
:param entries: List of path strings or (path, previous_path) tuples to process
222
directory_used = set()
224
for (path, previous_path) in entries:
225
if not path or path[-1] == "/":
227
directories.append((path, previous_path))
229
# Yield the referenced parent directories
230
for direntry in directories:
231
if osutils.is_inside(direntry[0], path):
232
directory_used.add(direntry[0])
233
for (path, previous_path) in entries:
234
if (not path.endswith("/")) or path in directory_used:
235
yield (path, previous_path)
238
return 'HasPathRelations(%r, %r)' % (self.previous_tree, self.previous_entries)
240
def match(self, tree):
241
actual = list(self.get_path_map(tree))
242
if not tree.has_versioned_directories():
243
entries = list(self._strip_unreferenced_directories(
244
self.previous_entries))
246
entries = self.previous_entries
247
if not tree.supports_rename_tracking():
249
(path, path if self.previous_tree.is_versioned(path) else None)
250
for (path, previous_path) in entries]
251
return Equals(entries).match(actual)
254
class RevisionHistoryMatches(Matcher):
255
"""A matcher that checks if a branch has a specific revision history.
257
:ivar history: Revision history, as list of revisions. Oldest first.
260
def __init__(self, history):
261
Matcher.__init__(self)
262
self.expected = history
265
return 'RevisionHistoryMatches(%r)' % self.expected
267
def match(self, branch):
268
with branch.lock_read():
269
graph = branch.repository.get_graph()
270
history = list(graph.iter_lefthand_ancestry(
271
branch.last_revision(), [_mod_revision.NULL_REVISION]))
273
return Equals(self.expected).match(history)