/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: Martin
  • Date: 2017-06-18 10:15:11 UTC
  • mto: This revision was merged to the branch mainline in revision 6715.
  • Revision ID: gzlist@googlemail.com-20170618101511-fri1mouxt1hc09r8
Make _simple_set tests pass on py3 and with random hash

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
    'MatchesAncestry',
 
32
    'ContainsNoVfsCalls',
30
33
    'ReturnsUnlockable',
 
34
    'RevisionHistoryMatches',
31
35
    ]
32
36
 
33
 
from testtools.matchers import Mismatch, Matcher
 
37
from .. import (
 
38
    osutils,
 
39
    revision as _mod_revision,
 
40
    )
 
41
from .. import lazy_import
 
42
lazy_import.lazy_import(globals(),
 
43
"""
 
44
from breezy.bzr.smart.request import request_handlers as smart_request_handlers
 
45
from breezy.bzr.smart import vfs
 
46
""")
 
47
from ..sixish import (
 
48
    text_type,
 
49
    )
 
50
 
 
51
from testtools.matchers import Equals, Mismatch, Matcher
34
52
 
35
53
 
36
54
class ReturnsUnlockable(Matcher):
48
66
        self.lockable_thing = lockable_thing
49
67
 
50
68
    def __str__(self):
51
 
        return ('ReturnsUnlockable(lockable_thing=%s)' % 
 
69
        return ('ReturnsUnlockable(lockable_thing=%s)' %
52
70
            self.lockable_thing)
53
71
 
54
72
    def match(self, lock_method):
66
84
 
67
85
    def describe(self):
68
86
        return "%s is locked" % self.lockable_thing
 
87
 
 
88
 
 
89
class _AncestryMismatch(Mismatch):
 
90
    """Ancestry matching mismatch."""
 
91
 
 
92
    def __init__(self, tip_revision, got, expected):
 
93
        self.tip_revision = tip_revision
 
94
        self.got = got
 
95
        self.expected = expected
 
96
 
 
97
    def describe(self):
 
98
        return "mismatched ancestry for revision %r was %r, expected %r" % (
 
99
            self.tip_revision, self.got, self.expected)
 
100
 
 
101
 
 
102
class MatchesAncestry(Matcher):
 
103
    """A matcher that checks the ancestry of a particular revision.
 
104
 
 
105
    :ivar graph: Graph in which to check the ancestry
 
106
    :ivar revision_id: Revision id of the revision
 
107
    """
 
108
 
 
109
    def __init__(self, repository, revision_id):
 
110
        Matcher.__init__(self)
 
111
        self.repository = repository
 
112
        self.revision_id = revision_id
 
113
 
 
114
    def __str__(self):
 
115
        return ('MatchesAncestry(repository=%r, revision_id=%r)' % (
 
116
            self.repository, self.revision_id))
 
117
 
 
118
    def match(self, expected):
 
119
        self.repository.lock_read()
 
120
        try:
 
121
            graph = self.repository.get_graph()
 
122
            got = [r for r, p in graph.iter_ancestry([self.revision_id])]
 
123
            if _mod_revision.NULL_REVISION in got:
 
124
                got.remove(_mod_revision.NULL_REVISION)
 
125
        finally:
 
126
            self.repository.unlock()
 
127
        if sorted(got) != sorted(expected):
 
128
            return _AncestryMismatch(self.revision_id, sorted(got),
 
129
                sorted(expected))
 
130
 
 
131
 
 
132
class HasLayout(Matcher):
 
133
    """A matcher that checks if a tree has a specific layout.
 
134
 
 
135
    :ivar entries: List of expected entries, as (path, file_id) pairs.
 
136
    """
 
137
 
 
138
    def __init__(self, entries):
 
139
        Matcher.__init__(self)
 
140
        self.entries = entries
 
141
 
 
142
    def get_tree_layout(self, tree):
 
143
        """Get the (path, file_id) pairs for the current tree."""
 
144
        tree.lock_read()
 
145
        try:
 
146
            for path, ie in tree.iter_entries_by_dir():
 
147
                if ie.parent_id is None:
 
148
                    yield (u"", ie.file_id)
 
149
                else:
 
150
                    yield (path+ie.kind_character(), ie.file_id)
 
151
        finally:
 
152
            tree.unlock()
 
153
 
 
154
    @staticmethod
 
155
    def _strip_unreferenced_directories(entries):
 
156
        """Strip all directories that don't (in)directly contain any files.
 
157
 
 
158
        :param entries: List of path strings or (path, ie) tuples to process
 
159
        """
 
160
        directories = []
 
161
        for entry in entries:
 
162
            if isinstance(entry, (str, text_type)):
 
163
                path = entry
 
164
            else:
 
165
                path = entry[0]
 
166
            if not path or path[-1] == "/":
 
167
                # directory
 
168
                directories.append((path, entry))
 
169
            else:
 
170
                # Yield the referenced parent directories
 
171
                for dirpath, direntry in directories:
 
172
                    if osutils.is_inside(dirpath, path):
 
173
                        yield direntry
 
174
                directories = []
 
175
                yield entry
 
176
 
 
177
    def __str__(self):
 
178
        return 'HasLayout(%r)' % self.entries
 
179
 
 
180
    def match(self, tree):
 
181
        actual = list(self.get_tree_layout(tree))
 
182
        if self.entries and isinstance(self.entries[0], (str, text_type)):
 
183
            actual = [path for (path, fileid) in actual]
 
184
        if not tree.has_versioned_directories():
 
185
            entries = list(self._strip_unreferenced_directories(self.entries))
 
186
        else:
 
187
            entries = self.entries
 
188
        return Equals(entries).match(actual)
 
189
 
 
190
 
 
191
class RevisionHistoryMatches(Matcher):
 
192
    """A matcher that checks if a branch has a specific revision history.
 
193
 
 
194
    :ivar history: Revision history, as list of revisions. Oldest first.
 
195
    """
 
196
 
 
197
    def __init__(self, history):
 
198
        Matcher.__init__(self)
 
199
        self.expected = history
 
200
 
 
201
    def __str__(self):
 
202
        return 'RevisionHistoryMatches(%r)' % self.expected
 
203
 
 
204
    def match(self, branch):
 
205
        branch.lock_read()
 
206
        try:
 
207
            graph = branch.repository.get_graph()
 
208
            history = list(graph.iter_lefthand_ancestry(
 
209
                branch.last_revision(), [_mod_revision.NULL_REVISION]))
 
210
            history.reverse()
 
211
        finally:
 
212
            branch.unlock()
 
213
        return Equals(self.expected).match(history)
 
214
 
 
215
 
 
216
class _NoVfsCallsMismatch(Mismatch):
 
217
    """Mismatch describing a list of HPSS calls which includes VFS requests."""
 
218
 
 
219
    def __init__(self, vfs_calls):
 
220
        self.vfs_calls = vfs_calls
 
221
 
 
222
    def describe(self):
 
223
        return "no VFS calls expected, got: %s" % ",".join([
 
224
            "%s(%s)" % (c.method,
 
225
                ", ".join([repr(a) for a in c.args])) for c in self.vfs_calls])
 
226
 
 
227
 
 
228
class ContainsNoVfsCalls(Matcher):
 
229
    """Ensure that none of the specified calls are HPSS calls."""
 
230
 
 
231
    def __str__(self):
 
232
        return 'ContainsNoVfsCalls()'
 
233
 
 
234
    @classmethod
 
235
    def match(cls, hpss_calls):
 
236
        vfs_calls = []
 
237
        for call in hpss_calls:
 
238
            try:
 
239
                request_method = smart_request_handlers.get(call.call.method)
 
240
            except KeyError:
 
241
                # A method we don't know about doesn't count as a VFS method.
 
242
                continue
 
243
            if issubclass(request_method, vfs.VfsRequest):
 
244
                vfs_calls.append(call.call)
 
245
        if len(vfs_calls) == 0:
 
246
            return None
 
247
        return _NoVfsCallsMismatch(vfs_calls)