/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2005, 2006 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1185.67.4 by Aaron Bentley
Throw if we try to write to a LockableFiles with no write lock
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1185.67.4 by Aaron Bentley
Throw if we try to write to a LockableFiles with no write lock
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1185.67.4 by Aaron Bentley
Throw if we try to write to a LockableFiles with no write lock
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
1185.70.2 by Martin Pool
Fix funny mistake
17
from StringIO import StringIO
18
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
19
import bzrlib
1185.70.2 by Martin Pool
Fix funny mistake
20
from bzrlib.branch import Branch
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
21
import bzrlib.errors as errors
1185.65.29 by Robert Collins
Implement final review suggestions.
22
from bzrlib.errors import BzrBadParameterNotString, NoSuchFile, ReadOnlyError
1553.5.63 by Martin Pool
Lock type is now mandatory for LockableFiles constructor
23
from bzrlib.lockable_files import LockableFiles, TransportLock
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
24
from bzrlib import lockdir
1553.5.44 by Martin Pool
LockableFiles can now call LockDir directly
25
from bzrlib.lockdir import LockDir
1185.67.4 by Aaron Bentley
Throw if we try to write to a LockableFiles with no write lock
26
from bzrlib.tests import TestCaseInTempDir
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
27
from bzrlib.tests.test_smart import TestCaseWithSmartMedium
1594.2.22 by Robert Collins
Ensure that lockable files calls finish() on transactions.:
28
from bzrlib.tests.test_transactions import DummyWeave
1563.2.34 by Robert Collins
Remove the commit and rollback transaction methods as misleading, and implement a WriteTransaction
29
from bzrlib.transactions import (PassThroughTransaction,
30
                                 ReadOnlyTransaction,
31
                                 WriteTransaction,
32
                                 )
1185.67.4 by Aaron Bentley
Throw if we try to write to a LockableFiles with no write lock
33
from bzrlib.transport import get_transport
1185.65.23 by Robert Collins
update tests somewhat
34
1553.5.42 by Martin Pool
Start to parameterize LockableFiles test cases.
35
36
# these tests are applied in each parameterized suite for LockableFiles
37
class _TestLockableFiles_mixin(object):
1185.67.4 by Aaron Bentley
Throw if we try to write to a LockableFiles with no write lock
38
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
39
    def setUp(self):
40
        # Reduce the default timeout, so that if tests fail, they will do so
41
        # reasonably quickly.
42
        orig_timeout = lockdir._DEFAULT_TIMEOUT_SECONDS
43
        def resetTimeout():
44
            lockdir._DEFAULT_TIMEOUT_SECONDS = orig_timeout
45
        self.addCleanup(resetTimeout)
46
        lockdir._DEFAULT_TIMEOUT_SECONDS = 3
47
1185.67.6 by Aaron Bentley
Added tests and fixes for LockableFiles.put_utf8(); imported IterableFile
48
    def test_read_write(self):
1185.65.29 by Robert Collins
Implement final review suggestions.
49
        self.assertRaises(NoSuchFile, self.lockable.get, 'foo')
50
        self.assertRaises(NoSuchFile, self.lockable.get_utf8, 'foo')
1185.67.6 by Aaron Bentley
Added tests and fixes for LockableFiles.put_utf8(); imported IterableFile
51
        self.lockable.lock_write()
52
        try:
53
            unicode_string = u'bar\u1234'
54
            self.assertEqual(4, len(unicode_string))
55
            byte_string = unicode_string.encode('utf-8')
56
            self.assertEqual(6, len(byte_string))
57
            self.assertRaises(UnicodeEncodeError, self.lockable.put, 'foo', 
58
                              StringIO(unicode_string))
59
            self.lockable.put('foo', StringIO(byte_string))
60
            self.assertEqual(byte_string,
1185.65.29 by Robert Collins
Implement final review suggestions.
61
                             self.lockable.get('foo').read())
1185.67.6 by Aaron Bentley
Added tests and fixes for LockableFiles.put_utf8(); imported IterableFile
62
            self.assertEqual(unicode_string,
1185.65.29 by Robert Collins
Implement final review suggestions.
63
                             self.lockable.get_utf8('foo').read())
64
            self.assertRaises(BzrBadParameterNotString,
65
                              self.lockable.put_utf8,
66
                              'bar',
67
                              StringIO(unicode_string)
68
                              )
69
            self.lockable.put_utf8('bar', unicode_string)
1185.67.6 by Aaron Bentley
Added tests and fixes for LockableFiles.put_utf8(); imported IterableFile
70
            self.assertEqual(unicode_string, 
1185.65.29 by Robert Collins
Implement final review suggestions.
71
                             self.lockable.get_utf8('bar').read())
1185.67.6 by Aaron Bentley
Added tests and fixes for LockableFiles.put_utf8(); imported IterableFile
72
            self.assertEqual(byte_string, 
1185.65.29 by Robert Collins
Implement final review suggestions.
73
                             self.lockable.get('bar').read())
1185.67.6 by Aaron Bentley
Added tests and fixes for LockableFiles.put_utf8(); imported IterableFile
74
        finally:
75
            self.lockable.unlock()
76
1185.67.4 by Aaron Bentley
Throw if we try to write to a LockableFiles with no write lock
77
    def test_locks(self):
1185.67.8 by Aaron Bentley
Test and fix read locks
78
        self.lockable.lock_read()
1185.65.27 by Robert Collins
Tweak storage towards mergability.
79
        try:
80
            self.assertRaises(ReadOnlyError, self.lockable.put, 'foo', 
81
                              StringIO('bar\u1234'))
82
        finally:
83
            self.lockable.unlock()
1185.68.1 by Aaron Bentley
test transactions
84
85
    def test_transactions(self):
86
        self.assertIs(self.lockable.get_transaction().__class__,
87
                      PassThroughTransaction)
88
        self.lockable.lock_read()
89
        try:
90
            self.assertIs(self.lockable.get_transaction().__class__,
91
                          ReadOnlyTransaction)
92
        finally:
93
            self.lockable.unlock()
94
        self.assertIs(self.lockable.get_transaction().__class__,
95
                      PassThroughTransaction)
96
        self.lockable.lock_write()
97
        self.assertIs(self.lockable.get_transaction().__class__,
1563.2.34 by Robert Collins
Remove the commit and rollback transaction methods as misleading, and implement a WriteTransaction
98
                      WriteTransaction)
1594.2.22 by Robert Collins
Ensure that lockable files calls finish() on transactions.:
99
        # check that finish is called:
100
        vf = DummyWeave('a')
101
        self.lockable.get_transaction().register_dirty(vf)
1185.68.1 by Aaron Bentley
test transactions
102
        self.lockable.unlock()
1594.2.22 by Robert Collins
Ensure that lockable files calls finish() on transactions.:
103
        self.assertTrue(vf.finished)
1185.65.23 by Robert Collins
update tests somewhat
104
105
    def test__escape(self):
106
        self.assertEqual('%25', self.lockable._escape('%'))
107
        
108
    def test__escape_empty(self):
109
        self.assertEqual('', self.lockable._escape(''))
110
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
111
    def test_break_lock(self):
112
        # some locks are not breakable
113
        self.lockable.lock_write()
114
        try:
115
            self.assertRaises(AssertionError, self.lockable.break_lock)
116
        except NotImplementedError:
117
            # this lock cannot be broken
118
            self.lockable.unlock()
119
            return
120
        l2 = self.get_lockable()
121
        orig_factory = bzrlib.ui.ui_factory
122
        # silent ui - no need for stdout
123
        bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
124
        bzrlib.ui.ui_factory.stdin = StringIO("y\n")
125
        try:
126
            l2.break_lock()
127
        finally:
128
            bzrlib.ui.ui_factory = orig_factory
129
        try:
130
            l2.lock_write()
131
            l2.unlock()
132
        finally:
133
            self.assertRaises(errors.LockBroken, self.lockable.unlock)
134
            self.assertFalse(self.lockable.is_locked())
135
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
136
    def test_lock_write_returns_None_refuses_token(self):
137
        token = self.lockable.lock_write()
138
        try:
139
            if token is not None:
140
                # This test does not apply, because this lockable supports
141
                # tokens.
142
                return
143
            self.assertRaises(errors.TokenLockingNotSupported,
144
                              self.lockable.lock_write, token='token')
145
        finally:
146
            self.lockable.unlock()
147
148
    def test_lock_write_raises_on_token_mismatch(self):
149
        token = self.lockable.lock_write()
150
        try:
151
            if token is None:
152
                # This test does not apply, because this lockable refuses
153
                # tokens.
154
                return
155
            different_token = token + 'xxx'
156
            # Re-using the same lockable instance with a different token will
157
            # raise TokenMismatch.
158
            self.assertRaises(errors.TokenMismatch,
159
                              self.lockable.lock_write, token=different_token)
160
            # A seperate instance for the same lockable will also raise
161
            # TokenMismatch.
162
            # This detects the case where a caller claims to have a lock (via
163
            # the token) for an external resource, but doesn't (the token is
164
            # different).  Clients need a seperate lock object to make sure the
165
            # external resource is probed, whereas the existing lock object
166
            # might cache.
167
            new_lockable = self.get_lockable()
168
            self.assertRaises(errors.TokenMismatch,
169
                              new_lockable.lock_write, token=different_token)
170
        finally:
171
            self.lockable.unlock()
172
173
    def test_lock_write_with_matching_token(self):
174
        # If the token matches, so no exception is raised by lock_write.
175
        token = self.lockable.lock_write()
176
        try:
177
            if token is None:
178
                # This test does not apply, because this lockable refuses
179
                # tokens.
180
                return
181
            # The same instance will accept a second lock_write if the specified
182
            # token matches.
183
            self.lockable.lock_write(token=token)
184
            self.lockable.unlock()
185
            # Calling lock_write on a new instance for the same lockable will
186
            # also succeed.
187
            new_lockable = self.get_lockable()
188
            new_lockable.lock_write(token=token)
189
            new_lockable.unlock()
190
        finally:
191
            self.lockable.unlock()
192
193
    def test_unlock_after_lock_write_with_token(self):
194
        # If lock_write did not physically acquire the lock (because it was
195
        # passed a token), then unlock should not physically release it.
196
        token = self.lockable.lock_write()
197
        try:
198
            if token is None:
199
                # This test does not apply, because this lockable refuses
200
                # tokens.
201
                return
202
            new_lockable = self.get_lockable()
203
            new_lockable.lock_write(token=token)
204
            new_lockable.unlock()
205
            self.assertTrue(self.lockable.get_physical_lock_status())
206
        finally:
207
            self.lockable.unlock()
208
209
    def test_lock_write_with_token_fails_when_unlocked(self):
210
        # Lock and unlock to get a superficially valid token.  This mimics a
211
        # likely programming error, where a caller accidentally tries to lock
212
        # with a token that is no longer valid (because the original lock was
213
        # released).
214
        token = self.lockable.lock_write()
215
        self.lockable.unlock()
216
        if token is None:
217
            # This test does not apply, because this lockable refuses
218
            # tokens.
219
            return
220
221
        self.assertRaises(errors.TokenMismatch,
222
                          self.lockable.lock_write, token=token)
223
224
    def test_lock_write_reenter_with_token(self):
225
        token = self.lockable.lock_write()
226
        try:
227
            if token is None:
228
                # This test does not apply, because this lockable refuses
229
                # tokens.
230
                return
231
            # Relock with a token.
232
            self.lockable.lock_write(token=token)
233
            self.lockable.unlock()
234
        finally:
235
            self.lockable.unlock()
236
        # The lock should be unlocked on disk.  Verify that with a new lock
237
        # instance.
238
        new_lockable = self.get_lockable()
239
        # Calling lock_write now should work, rather than raise LockContention.
240
        new_lockable.lock_write()
241
        new_lockable.unlock()
242
243
244
245
1553.5.42 by Martin Pool
Start to parameterize LockableFiles test cases.
246
247
# This method of adapting tests to parameters is different to 
248
# the TestProviderAdapters used elsewhere, but seems simpler for this 
249
# case.  
1553.5.45 by Martin Pool
Clean up Transport-based locks for old branches
250
class TestLockableFiles_TransportLock(TestCaseInTempDir,
251
                                      _TestLockableFiles_mixin):
1553.5.42 by Martin Pool
Start to parameterize LockableFiles test cases.
252
253
    def setUp(self):
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
254
        TestCaseInTempDir.setUp(self)
255
        _TestLockableFiles_mixin.setUp(self)
1553.5.42 by Martin Pool
Start to parameterize LockableFiles test cases.
256
        transport = get_transport('.')
257
        transport.mkdir('.bzr')
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
258
        self.sub_transport = transport.clone('.bzr')
259
        self.lockable = self.get_lockable()
1553.5.61 by Martin Pool
Locks protecting LockableFiles must now be explicitly created before use.
260
        self.lockable.create_lock()
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
261
262
    def tearDown(self):
263
        super(TestLockableFiles_TransportLock, self).tearDown()
1687.1.15 by Robert Collins
Review comments.
264
        # free the subtransport so that we do not get a 5 second
265
        # timeout due to the SFTP connection cache.
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
266
        del self.sub_transport
267
268
    def get_lockable(self):
269
        return LockableFiles(self.sub_transport, 'my-lock', TransportLock)
1553.5.43 by Martin Pool
Get LockableFiles tests running against LockDir
270
        
271
272
class TestLockableFiles_LockDir(TestCaseInTempDir,
273
                              _TestLockableFiles_mixin):
274
    """LockableFile tests run with LockDir underneath"""
275
276
    def setUp(self):
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
277
        TestCaseInTempDir.setUp(self)
278
        _TestLockableFiles_mixin.setUp(self)
1553.5.61 by Martin Pool
Locks protecting LockableFiles must now be explicitly created before use.
279
        self.transport = get_transport('.')
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
280
        self.lockable = self.get_lockable()
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
281
        # the lock creation here sets mode - test_permissions on branch 
282
        # tests that implicitly, but it might be a good idea to factor 
283
        # out the mode checking logic and have it applied to loackable files
284
        # directly. RBC 20060418
1553.5.61 by Martin Pool
Locks protecting LockableFiles must now be explicitly created before use.
285
        self.lockable.create_lock()
1553.5.43 by Martin Pool
Get LockableFiles tests running against LockDir
286
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
287
    def get_lockable(self):
288
        return LockableFiles(self.transport, 'my-lock', LockDir)
1553.5.60 by Martin Pool
New LockableFiles.create_lock() method
289
290
    def test_lock_created(self):
1553.5.61 by Martin Pool
Locks protecting LockableFiles must now be explicitly created before use.
291
        self.assertTrue(self.transport.has('my-lock'))
292
        self.lockable.lock_write()
293
        self.assertTrue(self.transport.has('my-lock/held/info'))
294
        self.lockable.unlock()
295
        self.assertFalse(self.transport.has('my-lock/held/info'))
296
        self.assertTrue(self.transport.has('my-lock'))
297
298
299
    # TODO: Test the lockdir inherits the right file and directory permissions
300
    # from the LockableFiles.
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
301
        
302
303
class TestLockableFiles_RemoteLockDir(TestCaseWithSmartMedium,
304
                              _TestLockableFiles_mixin):
305
    """LockableFile tests run with RemoteLockDir on a branch."""
306
307
    def setUp(self):
308
        super(TestLockableFiles_RemoteLockDir, self).setUp()
309
        # can only get a RemoteLockDir with some RemoteObject...
310
        # use a branch as thats what we want. These mixin tests test the end
311
        # to end behaviour, so stubbing out the backend and simulating would
312
        # defeat the purpose. We test the protocol implementation separately
313
        # in test_remote and test_smart as usual.
314
        self.make_branch('foo')
315
        self.transport = get_transport('.')
316
        self.lockable = self.get_lockable()
317
318
    def get_lockable(self):
319
        # getting a new lockable involves opening a new instance of the branch
320
        branch = bzrlib.branch.Branch.open(self.get_url('foo'))
321
        return branch.control_files