/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5200.3.1 by Robert Collins
Added ``bzrlib.tests.matchers`` as a place to put matchers, along with
1
# Copyright (C) 2010 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
17
"""Matchers for breezy.
5200.3.1 by Robert Collins
Added ``bzrlib.tests.matchers`` as a place to put matchers, along with
18
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
19
Primarily test support, Matchers are used by self.assertThat in the breezy
5200.3.1 by Robert Collins
Added ``bzrlib.tests.matchers`` as a place to put matchers, along with
20
test suite. A matcher is a stateful test helper which can be used to determine
21
if a passed object 'matches', much like a regex. If the object does not match
22
the mismatch can be described in a human readable fashion. assertThat then
23
raises if a mismatch occurs, showing the description as the assertion error.
24
25
Matchers are designed to be more reusable and composable than layered
26
assertions in Test Case objects, so they are recommended for new testing work.
27
"""
28
29
__all__ = [
6072.2.4 by Jelmer Vernooij
tests for matcher
30
    'HasLayout',
5972.3.13 by Jelmer Vernooij
Add matcher for ancestry.
31
    'MatchesAncestry',
6352.2.3 by Jelmer Vernooij
s/NoVfsCalls/ContainsNoVfsCalls/.
32
    'ContainsNoVfsCalls',
5200.3.2 by Robert Collins
Cleaner matcher matching revised unlocking protocol.
33
    'ReturnsUnlockable',
6228.3.1 by Jelmer Vernooij
Add RevisionHistoryMatches.
34
    'RevisionHistoryMatches',
5200.3.1 by Robert Collins
Added ``bzrlib.tests.matchers`` as a place to put matchers, along with
35
    ]
36
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
37
from .. import (
6110.6.2 by Jelmer Vernooij
In HasLayout, take into consideration Tree.has_versioned_directories.
38
    osutils,
5972.3.13 by Jelmer Vernooij
Add matcher for ancestry.
39
    revision as _mod_revision,
40
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
41
from .. import lazy_import
6352.2.1 by Jelmer Vernooij
Add matcher for NoVfsCalls.
42
lazy_import.lazy_import(globals(),
43
"""
6670.4.16 by Jelmer Vernooij
Move smart to breezy.bzr.
44
from breezy.bzr.smart.request import request_handlers as smart_request_handlers
45
from breezy.bzr.smart import vfs
6352.2.1 by Jelmer Vernooij
Add matcher for NoVfsCalls.
46
""")
6695.3.1 by Martin
Remove remaining uses of basestring from the codebase
47
from ..sixish import (
48
    text_type,
49
    )
5972.3.13 by Jelmer Vernooij
Add matcher for ancestry.
50
6072.2.1 by Jelmer Vernooij
Add HasLayout matcher.
51
from testtools.matchers import Equals, Mismatch, Matcher
5200.3.1 by Robert Collins
Added ``bzrlib.tests.matchers`` as a place to put matchers, along with
52
53
5200.3.2 by Robert Collins
Cleaner matcher matching revised unlocking protocol.
54
class ReturnsUnlockable(Matcher):
5200.3.1 by Robert Collins
Added ``bzrlib.tests.matchers`` as a place to put matchers, along with
55
    """A matcher that checks for the pattern we want lock* methods to have:
56
5200.3.2 by Robert Collins
Cleaner matcher matching revised unlocking protocol.
57
    They should return an object with an unlock() method.
58
    Calling that method should unlock the original object.
5200.3.1 by Robert Collins
Added ``bzrlib.tests.matchers`` as a place to put matchers, along with
59
60
    :ivar lockable_thing: The object which can be locked that will be
61
        inspected.
62
    """
63
64
    def __init__(self, lockable_thing):
65
        Matcher.__init__(self)
66
        self.lockable_thing = lockable_thing
67
68
    def __str__(self):
6110.6.2 by Jelmer Vernooij
In HasLayout, take into consideration Tree.has_versioned_directories.
69
        return ('ReturnsUnlockable(lockable_thing=%s)' %
5200.3.1 by Robert Collins
Added ``bzrlib.tests.matchers`` as a place to put matchers, along with
70
            self.lockable_thing)
71
72
    def match(self, lock_method):
5200.3.2 by Robert Collins
Cleaner matcher matching revised unlocking protocol.
73
        lock_method().unlock()
5200.3.1 by Robert Collins
Added ``bzrlib.tests.matchers`` as a place to put matchers, along with
74
        if self.lockable_thing.is_locked():
75
            return _IsLocked(self.lockable_thing)
76
        return None
77
78
79
class _IsLocked(Mismatch):
80
    """Something is locked."""
81
82
    def __init__(self, lockable_thing):
83
        self.lockable_thing = lockable_thing
84
85
    def describe(self):
86
        return "%s is locked" % self.lockable_thing
5972.3.13 by Jelmer Vernooij
Add matcher for ancestry.
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])]
5972.3.20 by Jelmer Vernooij
fix test.
123
            if _mod_revision.NULL_REVISION in got:
124
                got.remove(_mod_revision.NULL_REVISION)
5972.3.13 by Jelmer Vernooij
Add matcher for ancestry.
125
        finally:
126
            self.repository.unlock()
127
        if sorted(got) != sorted(expected):
6072.2.1 by Jelmer Vernooij
Add HasLayout matcher.
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:
6110.6.2 by Jelmer Vernooij
In HasLayout, take into consideration Tree.has_versioned_directories.
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)
6072.2.1 by Jelmer Vernooij
Add HasLayout matcher.
151
        finally:
152
            tree.unlock()
153
6110.6.2 by Jelmer Vernooij
In HasLayout, take into consideration Tree.has_versioned_directories.
154
    @staticmethod
155
    def _strip_unreferenced_directories(entries):
6110.6.3 by Jelmer Vernooij
review feedback from mgz
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
        """
6110.6.2 by Jelmer Vernooij
In HasLayout, take into consideration Tree.has_versioned_directories.
160
        directories = []
161
        for entry in entries:
6695.3.1 by Martin
Remove remaining uses of basestring from the codebase
162
            if isinstance(entry, (str, text_type)):
6110.6.2 by Jelmer Vernooij
In HasLayout, take into consideration Tree.has_versioned_directories.
163
                path = entry
164
            else:
165
                path = entry[0]
6110.6.3 by Jelmer Vernooij
review feedback from mgz
166
            if not path or path[-1] == "/":
6110.6.2 by Jelmer Vernooij
In HasLayout, take into consideration Tree.has_versioned_directories.
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
6072.2.1 by Jelmer Vernooij
Add HasLayout matcher.
177
    def __str__(self):
6110.6.2 by Jelmer Vernooij
In HasLayout, take into consideration Tree.has_versioned_directories.
178
        return 'HasLayout(%r)' % self.entries
6072.2.1 by Jelmer Vernooij
Add HasLayout matcher.
179
180
    def match(self, tree):
6110.6.2 by Jelmer Vernooij
In HasLayout, take into consideration Tree.has_versioned_directories.
181
        actual = list(self.get_tree_layout(tree))
6695.3.1 by Martin
Remove remaining uses of basestring from the codebase
182
        if self.entries and isinstance(self.entries[0], (str, text_type)):
6072.2.3 by Jelmer Vernooij
Use the matcher in test_merge_from_branch.
183
            actual = [path for (path, fileid) in actual]
6110.6.2 by Jelmer Vernooij
In HasLayout, take into consideration Tree.has_versioned_directories.
184
        if not tree.has_versioned_directories():
185
            entries = list(self._strip_unreferenced_directories(self.entries))
186
        else:
187
            entries = self.entries
6110.6.3 by Jelmer Vernooij
review feedback from mgz
188
        return Equals(entries).match(actual)
6228.3.1 by Jelmer Vernooij
Add RevisionHistoryMatches.
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)
6228.3.4 by Jelmer Vernooij
Merge bzr.dev.
214
215
6352.2.1 by Jelmer Vernooij
Add matcher for NoVfsCalls.
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
6352.2.3 by Jelmer Vernooij
s/NoVfsCalls/ContainsNoVfsCalls/.
228
class ContainsNoVfsCalls(Matcher):
6352.2.1 by Jelmer Vernooij
Add matcher for NoVfsCalls.
229
    """Ensure that none of the specified calls are HPSS calls."""
230
231
    def __str__(self):
6352.2.3 by Jelmer Vernooij
s/NoVfsCalls/ContainsNoVfsCalls/.
232
        return 'ContainsNoVfsCalls()'
6352.2.1 by Jelmer Vernooij
Add matcher for NoVfsCalls.
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)