/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/matchers.py

  • Committer: Breezy landing bot
  • Author(s): Jelmer Vernooij
  • Date: 2018-11-16 18:26:22 UTC
  • mfrom: (7167.1.4 run-flake8)
  • Revision ID: breezy.the.bot@gmail.com-20181116182622-qw3gan3hz78a2imw
Add a flake8 test.

Merged from https://code.launchpad.net/~jelmer/brz/run-flake8/+merge/358902

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
"""Matchers for bzrlib.
 
17
"""Matchers for breezy.
18
18
 
19
 
Primarily test support, Matchers are used by self.assertThat in the bzrlib
 
19
Primarily test support, Matchers are used by self.assertThat in the breezy
20
20
test suite. A matcher is a stateful test helper which can be used to determine
21
21
if a passed object 'matches', much like a regex. If the object does not match
22
22
the mismatch can be described in a human readable fashion. assertThat then
27
27
"""
28
28
 
29
29
__all__ = [
 
30
    'HasLayout',
 
31
    'HasPathRelations',
 
32
    'MatchesAncestry',
 
33
    'ContainsNoVfsCalls',
30
34
    'ReturnsUnlockable',
 
35
    'RevisionHistoryMatches',
31
36
    ]
32
37
 
33
 
from testtools.matchers import Mismatch, Matcher
 
38
from .. import (
 
39
    osutils,
 
40
    revision as _mod_revision,
 
41
    )
 
42
from .. import lazy_import
 
43
lazy_import.lazy_import(globals(),
 
44
"""
 
45
from breezy.bzr.smart.request import request_handlers as smart_request_handlers
 
46
from breezy.bzr.smart import vfs
 
47
""")
 
48
from ..sixish import (
 
49
    text_type,
 
50
    )
 
51
from ..tree import find_previous_path
 
52
 
 
53
from testtools.matchers import Equals, Mismatch, Matcher
34
54
 
35
55
 
36
56
class ReturnsUnlockable(Matcher):
48
68
        self.lockable_thing = lockable_thing
49
69
 
50
70
    def __str__(self):
51
 
        return ('ReturnsUnlockable(lockable_thing=%s)' % 
 
71
        return ('ReturnsUnlockable(lockable_thing=%s)' %
52
72
            self.lockable_thing)
53
73
 
54
74
    def match(self, lock_method):
66
86
 
67
87
    def describe(self):
68
88
        return "%s is locked" % self.lockable_thing
 
89
 
 
90
 
 
91
class _AncestryMismatch(Mismatch):
 
92
    """Ancestry matching mismatch."""
 
93
 
 
94
    def __init__(self, tip_revision, got, expected):
 
95
        self.tip_revision = tip_revision
 
96
        self.got = got
 
97
        self.expected = expected
 
98
 
 
99
    def describe(self):
 
100
        return "mismatched ancestry for revision %r was %r, expected %r" % (
 
101
            self.tip_revision, self.got, self.expected)
 
102
 
 
103
 
 
104
class MatchesAncestry(Matcher):
 
105
    """A matcher that checks the ancestry of a particular revision.
 
106
 
 
107
    :ivar graph: Graph in which to check the ancestry
 
108
    :ivar revision_id: Revision id of the revision
 
109
    """
 
110
 
 
111
    def __init__(self, repository, revision_id):
 
112
        Matcher.__init__(self)
 
113
        self.repository = repository
 
114
        self.revision_id = revision_id
 
115
 
 
116
    def __str__(self):
 
117
        return ('MatchesAncestry(repository=%r, revision_id=%r)' % (
 
118
            self.repository, self.revision_id))
 
119
 
 
120
    def match(self, expected):
 
121
        with self.repository.lock_read():
 
122
            graph = self.repository.get_graph()
 
123
            got = [r for r, p in graph.iter_ancestry([self.revision_id])]
 
124
            if _mod_revision.NULL_REVISION in got:
 
125
                got.remove(_mod_revision.NULL_REVISION)
 
126
        if sorted(got) != sorted(expected):
 
127
            return _AncestryMismatch(self.revision_id, sorted(got),
 
128
                sorted(expected))
 
129
 
 
130
 
 
131
class HasLayout(Matcher):
 
132
    """A matcher that checks if a tree has a specific layout.
 
133
 
 
134
    :ivar entries: List of expected entries, as (path, file_id) pairs.
 
135
    """
 
136
 
 
137
    def __init__(self, entries):
 
138
        Matcher.__init__(self)
 
139
        self.entries = entries
 
140
 
 
141
    def get_tree_layout(self, tree, include_file_ids):
 
142
        """Get the (path, file_id) pairs for the current tree."""
 
143
        with tree.lock_read():
 
144
            for path, ie in tree.iter_entries_by_dir():
 
145
                if path != u'':
 
146
                    path += ie.kind_character()
 
147
                if include_file_ids:
 
148
                    yield (path, ie.file_id)
 
149
                else:
 
150
                    yield path
 
151
 
 
152
    @staticmethod
 
153
    def _strip_unreferenced_directories(entries):
 
154
        """Strip all directories that don't (in)directly contain any files.
 
155
 
 
156
        :param entries: List of path strings or (path, ie) tuples to process
 
157
        """
 
158
        directories = []
 
159
        for entry in entries:
 
160
            if isinstance(entry, (str, text_type)):
 
161
                path = entry
 
162
            else:
 
163
                path = entry[0]
 
164
            if not path or path[-1] == "/":
 
165
                # directory
 
166
                directories.append((path, entry))
 
167
            else:
 
168
                # Yield the referenced parent directories
 
169
                for dirpath, direntry in directories:
 
170
                    if osutils.is_inside(dirpath, path):
 
171
                        yield direntry
 
172
                directories = []
 
173
                yield entry
 
174
 
 
175
    def __str__(self):
 
176
        return 'HasLayout(%r)' % self.entries
 
177
 
 
178
    def match(self, tree):
 
179
        include_file_ids = self.entries and not isinstance(self.entries[0], (str, text_type))
 
180
        actual = list(self.get_tree_layout(tree, include_file_ids=include_file_ids))
 
181
        if not tree.has_versioned_directories():
 
182
            entries = list(self._strip_unreferenced_directories(self.entries))
 
183
        else:
 
184
            entries = self.entries
 
185
        return Equals(entries).match(actual)
 
186
 
 
187
 
 
188
class HasPathRelations(Matcher):
 
189
    """Matcher verifies that paths have a relation to those in another tree.
 
190
 
 
191
    :ivar previous_tree: tree to compare to
 
192
    :ivar previous_entries: List of expected entries, as (path, previous_path) pairs.
 
193
    """
 
194
 
 
195
    def __init__(self, previous_tree, previous_entries):
 
196
        Matcher.__init__(self)
 
197
        self.previous_tree = previous_tree
 
198
        self.previous_entries = previous_entries
 
199
 
 
200
    def get_path_map(self, tree):
 
201
        """Get the (path, previous_path) pairs for the current tree."""
 
202
        with tree.lock_read(), self.previous_tree.lock_read():
 
203
            for path, ie in tree.iter_entries_by_dir():
 
204
                if tree.supports_rename_tracking():
 
205
                    previous_path = find_previous_path(tree, self.previous_tree, path)
 
206
                else:
 
207
                    if self.previous_tree.is_versioned(path):
 
208
                        previous_path = path
 
209
                    else:
 
210
                        previous_path = None
 
211
                if previous_path:
 
212
                    kind = self.previous_tree.kind(previous_path)
 
213
                    if kind == 'directory':
 
214
                        previous_path += '/'
 
215
                if path == u'':
 
216
                    yield (u"", previous_path)
 
217
                else:
 
218
                    yield (path+ie.kind_character(), previous_path)
 
219
 
 
220
    @staticmethod
 
221
    def _strip_unreferenced_directories(entries):
 
222
        """Strip all directories that don't (in)directly contain any files.
 
223
 
 
224
        :param entries: List of path strings or (path, previous_path) tuples to process
 
225
        """
 
226
        directory_used = set()
 
227
        directories = []
 
228
        for (path, previous_path) in entries:
 
229
            if not path or path[-1] == "/":
 
230
                # directory
 
231
                directories.append((path, previous_path))
 
232
            else:
 
233
                # Yield the referenced parent directories
 
234
                for direntry in directories:
 
235
                    if osutils.is_inside(direntry[0], path):
 
236
                        directory_used.add(direntry[0])
 
237
        for (path, previous_path) in entries:
 
238
            if (not path.endswith("/")) or path in directory_used:
 
239
                yield (path, previous_path)
 
240
 
 
241
    def __str__(self):
 
242
        return 'HasPathRelations(%r, %r)' % (self.previous_tree, self.previous_entries)
 
243
 
 
244
    def match(self, tree):
 
245
        actual = list(self.get_path_map(tree))
 
246
        if not tree.has_versioned_directories():
 
247
            entries = list(self._strip_unreferenced_directories(self.previous_entries))
 
248
        else:
 
249
            entries = self.previous_entries
 
250
        if not tree.supports_rename_tracking():
 
251
            entries = [
 
252
                (path, path if self.previous_tree.is_versioned(path) else None)
 
253
                for (path, previous_path) in entries]
 
254
        return Equals(entries).match(actual)
 
255
 
 
256
 
 
257
class RevisionHistoryMatches(Matcher):
 
258
    """A matcher that checks if a branch has a specific revision history.
 
259
 
 
260
    :ivar history: Revision history, as list of revisions. Oldest first.
 
261
    """
 
262
 
 
263
    def __init__(self, history):
 
264
        Matcher.__init__(self)
 
265
        self.expected = history
 
266
 
 
267
    def __str__(self):
 
268
        return 'RevisionHistoryMatches(%r)' % self.expected
 
269
 
 
270
    def match(self, branch):
 
271
        with branch.lock_read():
 
272
            graph = branch.repository.get_graph()
 
273
            history = list(graph.iter_lefthand_ancestry(
 
274
                branch.last_revision(), [_mod_revision.NULL_REVISION]))
 
275
            history.reverse()
 
276
        return Equals(self.expected).match(history)
 
277
 
 
278
 
 
279
class _NoVfsCallsMismatch(Mismatch):
 
280
    """Mismatch describing a list of HPSS calls which includes VFS requests."""
 
281
 
 
282
    def __init__(self, vfs_calls):
 
283
        self.vfs_calls = vfs_calls
 
284
 
 
285
    def describe(self):
 
286
        return "no VFS calls expected, got: %s" % ",".join([
 
287
            "%s(%s)" % (c.method,
 
288
                ", ".join([repr(a) for a in c.args])) for c in self.vfs_calls])
 
289
 
 
290
 
 
291
class ContainsNoVfsCalls(Matcher):
 
292
    """Ensure that none of the specified calls are HPSS calls."""
 
293
 
 
294
    def __str__(self):
 
295
        return 'ContainsNoVfsCalls()'
 
296
 
 
297
    @classmethod
 
298
    def match(cls, hpss_calls):
 
299
        vfs_calls = []
 
300
        for call in hpss_calls:
 
301
            try:
 
302
                request_method = smart_request_handlers.get(call.call.method)
 
303
            except KeyError:
 
304
                # A method we don't know about doesn't count as a VFS method.
 
305
                continue
 
306
            if issubclass(request_method, vfs.VfsRequest):
 
307
                vfs_calls.append(call.call)
 
308
        if len(vfs_calls) == 0:
 
309
            return None
 
310
        return _NoVfsCallsMismatch(vfs_calls)