/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
3388.2.1 by Martin Pool
Deprecate LockableFiles.get_utf8
1
# Copyright (C) 2005, 2006, 2008 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
3107.2.1 by John Arbash Meinel
Fix LockableFiles to not use modes that allow the user to write to things they create.
20
from bzrlib import (
21
    errors,
22
    lockdir,
23
    osutils,
24
    )
1185.65.29 by Robert Collins
Implement final review suggestions.
25
from bzrlib.errors import BzrBadParameterNotString, NoSuchFile, ReadOnlyError
1553.5.63 by Martin Pool
Lock type is now mandatory for LockableFiles constructor
26
from bzrlib.lockable_files import LockableFiles, TransportLock
3388.2.1 by Martin Pool
Deprecate LockableFiles.get_utf8
27
from bzrlib.symbol_versioning import (
28
    deprecated_in,
29
    )
1185.67.4 by Aaron Bentley
Throw if we try to write to a LockableFiles with no write lock
30
from bzrlib.tests import TestCaseInTempDir
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
31
from bzrlib.tests.test_smart import TestCaseWithSmartMedium
1594.2.22 by Robert Collins
Ensure that lockable files calls finish() on transactions.:
32
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
33
from bzrlib.transactions import (PassThroughTransaction,
34
                                 ReadOnlyTransaction,
35
                                 WriteTransaction,
36
                                 )
1185.67.4 by Aaron Bentley
Throw if we try to write to a LockableFiles with no write lock
37
from bzrlib.transport import get_transport
1185.65.23 by Robert Collins
update tests somewhat
38
1553.5.42 by Martin Pool
Start to parameterize LockableFiles test cases.
39
40
# these tests are applied in each parameterized suite for LockableFiles
3388.2.1 by Martin Pool
Deprecate LockableFiles.get_utf8
41
#
42
# they use an old style of parameterization, but we want to remove this class
43
# so won't modernize them now. - mbp 20080430
1553.5.42 by Martin Pool
Start to parameterize LockableFiles test cases.
44
class _TestLockableFiles_mixin(object):
1185.67.4 by Aaron Bentley
Throw if we try to write to a LockableFiles with no write lock
45
1185.67.6 by Aaron Bentley
Added tests and fixes for LockableFiles.put_utf8(); imported IterableFile
46
    def test_read_write(self):
3407.2.1 by Martin Pool
Deprecate LockableFiles.get
47
        self.assertRaises(NoSuchFile,
48
            self.applyDeprecated,
49
            deprecated_in((1, 5, 0)),
50
            self.lockable.get, 'foo')
3388.2.1 by Martin Pool
Deprecate LockableFiles.get_utf8
51
        self.assertRaises(NoSuchFile,
52
            self.applyDeprecated,
53
            deprecated_in((1, 5, 0)),
54
            self.lockable.get_utf8, 'foo')
1185.67.6 by Aaron Bentley
Added tests and fixes for LockableFiles.put_utf8(); imported IterableFile
55
        self.lockable.lock_write()
56
        try:
57
            unicode_string = u'bar\u1234'
58
            self.assertEqual(4, len(unicode_string))
59
            byte_string = unicode_string.encode('utf-8')
60
            self.assertEqual(6, len(byte_string))
2018.5.133 by Andrew Bennetts
All TestLockableFiles_RemoteLockDir tests passing.
61
            self.assertRaises(UnicodeEncodeError, self.lockable.put, 'foo',
1185.67.6 by Aaron Bentley
Added tests and fixes for LockableFiles.put_utf8(); imported IterableFile
62
                              StringIO(unicode_string))
63
            self.lockable.put('foo', StringIO(byte_string))
3407.2.1 by Martin Pool
Deprecate LockableFiles.get
64
            byte_stream = self.applyDeprecated(
65
                deprecated_in((1, 5, 0)),
66
                self.lockable.get,
67
                'foo')
68
            self.assertEqual(byte_string, byte_stream.read())
3388.2.1 by Martin Pool
Deprecate LockableFiles.get_utf8
69
            unicode_stream = self.applyDeprecated(
70
                deprecated_in((1, 5, 0)),
71
                self.lockable.get_utf8,
72
                'foo')
1185.67.6 by Aaron Bentley
Added tests and fixes for LockableFiles.put_utf8(); imported IterableFile
73
            self.assertEqual(unicode_string,
3388.2.1 by Martin Pool
Deprecate LockableFiles.get_utf8
74
                unicode_stream.read())
1185.65.29 by Robert Collins
Implement final review suggestions.
75
            self.assertRaises(BzrBadParameterNotString,
76
                              self.lockable.put_utf8,
77
                              'bar',
78
                              StringIO(unicode_string)
79
                              )
80
            self.lockable.put_utf8('bar', unicode_string)
3388.2.1 by Martin Pool
Deprecate LockableFiles.get_utf8
81
            unicode_stream = self.applyDeprecated(
82
                deprecated_in((1, 5, 0)),
83
                self.lockable.get_utf8,
84
                'bar')
2249.5.11 by John Arbash Meinel
Audit Branch to ensure utf8 revision ids.
85
            self.assertEqual(unicode_string,
3388.2.1 by Martin Pool
Deprecate LockableFiles.get_utf8
86
                unicode_stream.read())
3407.2.1 by Martin Pool
Deprecate LockableFiles.get
87
            byte_stream = self.applyDeprecated(
88
                deprecated_in((1, 5, 0)),
89
                self.lockable.get,
90
                'bar')
91
            self.assertEqual(byte_string, byte_stream.read())
2249.5.11 by John Arbash Meinel
Audit Branch to ensure utf8 revision ids.
92
            self.lockable.put_bytes('raw', 'raw\xffbytes')
3407.2.1 by Martin Pool
Deprecate LockableFiles.get
93
            byte_stream = self.applyDeprecated(
94
                deprecated_in((1, 5, 0)),
95
                self.lockable.get,
96
                'raw')
97
            self.assertEqual('raw\xffbytes', byte_stream.read())
1185.67.6 by Aaron Bentley
Added tests and fixes for LockableFiles.put_utf8(); imported IterableFile
98
        finally:
99
            self.lockable.unlock()
100
1185.67.4 by Aaron Bentley
Throw if we try to write to a LockableFiles with no write lock
101
    def test_locks(self):
1185.67.8 by Aaron Bentley
Test and fix read locks
102
        self.lockable.lock_read()
1185.65.27 by Robert Collins
Tweak storage towards mergability.
103
        try:
104
            self.assertRaises(ReadOnlyError, self.lockable.put, 'foo', 
105
                              StringIO('bar\u1234'))
106
        finally:
107
            self.lockable.unlock()
1185.68.1 by Aaron Bentley
test transactions
108
109
    def test_transactions(self):
110
        self.assertIs(self.lockable.get_transaction().__class__,
111
                      PassThroughTransaction)
112
        self.lockable.lock_read()
113
        try:
114
            self.assertIs(self.lockable.get_transaction().__class__,
115
                          ReadOnlyTransaction)
116
        finally:
117
            self.lockable.unlock()
118
        self.assertIs(self.lockable.get_transaction().__class__,
119
                      PassThroughTransaction)
120
        self.lockable.lock_write()
121
        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
122
                      WriteTransaction)
1594.2.22 by Robert Collins
Ensure that lockable files calls finish() on transactions.:
123
        # check that finish is called:
124
        vf = DummyWeave('a')
125
        self.lockable.get_transaction().register_dirty(vf)
1185.68.1 by Aaron Bentley
test transactions
126
        self.lockable.unlock()
1594.2.22 by Robert Collins
Ensure that lockable files calls finish() on transactions.:
127
        self.assertTrue(vf.finished)
1185.65.23 by Robert Collins
update tests somewhat
128
129
    def test__escape(self):
130
        self.assertEqual('%25', self.lockable._escape('%'))
131
        
132
    def test__escape_empty(self):
133
        self.assertEqual('', self.lockable._escape(''))
134
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
135
    def test_break_lock(self):
136
        # some locks are not breakable
137
        self.lockable.lock_write()
138
        try:
139
            self.assertRaises(AssertionError, self.lockable.break_lock)
140
        except NotImplementedError:
141
            # this lock cannot be broken
142
            self.lockable.unlock()
143
            return
144
        l2 = self.get_lockable()
145
        orig_factory = bzrlib.ui.ui_factory
146
        # silent ui - no need for stdout
147
        bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
148
        bzrlib.ui.ui_factory.stdin = StringIO("y\n")
149
        try:
150
            l2.break_lock()
151
        finally:
152
            bzrlib.ui.ui_factory = orig_factory
153
        try:
154
            l2.lock_write()
155
            l2.unlock()
156
        finally:
157
            self.assertRaises(errors.LockBroken, self.lockable.unlock)
158
            self.assertFalse(self.lockable.is_locked())
159
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
160
    def test_lock_write_returns_None_refuses_token(self):
161
        token = self.lockable.lock_write()
162
        try:
163
            if token is not None:
164
                # This test does not apply, because this lockable supports
165
                # tokens.
166
                return
167
            self.assertRaises(errors.TokenLockingNotSupported,
168
                              self.lockable.lock_write, token='token')
169
        finally:
170
            self.lockable.unlock()
171
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
172
    def test_lock_write_returns_token_when_given_token(self):
173
        token = self.lockable.lock_write()
174
        try:
175
            if token is None:
176
                # This test does not apply, because this lockable refuses
177
                # tokens.
178
                return
179
            new_lockable = self.get_lockable()
180
            token_from_new_lockable = new_lockable.lock_write(token=token)
181
            try:
182
                self.assertEqual(token, token_from_new_lockable)
183
            finally:
184
                new_lockable.unlock()
185
        finally:
186
            self.lockable.unlock()
187
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
188
    def test_lock_write_raises_on_token_mismatch(self):
189
        token = self.lockable.lock_write()
190
        try:
191
            if token is None:
192
                # This test does not apply, because this lockable refuses
193
                # tokens.
194
                return
195
            different_token = token + 'xxx'
196
            # Re-using the same lockable instance with a different token will
197
            # raise TokenMismatch.
198
            self.assertRaises(errors.TokenMismatch,
199
                              self.lockable.lock_write, token=different_token)
200
            # A seperate instance for the same lockable will also raise
201
            # TokenMismatch.
202
            # This detects the case where a caller claims to have a lock (via
203
            # the token) for an external resource, but doesn't (the token is
204
            # different).  Clients need a seperate lock object to make sure the
205
            # external resource is probed, whereas the existing lock object
206
            # might cache.
207
            new_lockable = self.get_lockable()
208
            self.assertRaises(errors.TokenMismatch,
209
                              new_lockable.lock_write, token=different_token)
210
        finally:
211
            self.lockable.unlock()
212
213
    def test_lock_write_with_matching_token(self):
214
        # If the token matches, so no exception is raised by lock_write.
215
        token = self.lockable.lock_write()
216
        try:
217
            if token is None:
218
                # This test does not apply, because this lockable refuses
219
                # tokens.
220
                return
221
            # The same instance will accept a second lock_write if the specified
222
            # token matches.
223
            self.lockable.lock_write(token=token)
224
            self.lockable.unlock()
225
            # Calling lock_write on a new instance for the same lockable will
226
            # also succeed.
227
            new_lockable = self.get_lockable()
228
            new_lockable.lock_write(token=token)
229
            new_lockable.unlock()
230
        finally:
231
            self.lockable.unlock()
232
233
    def test_unlock_after_lock_write_with_token(self):
234
        # If lock_write did not physically acquire the lock (because it was
235
        # passed a token), then unlock should not physically release it.
236
        token = self.lockable.lock_write()
237
        try:
238
            if token is None:
239
                # This test does not apply, because this lockable refuses
240
                # tokens.
241
                return
242
            new_lockable = self.get_lockable()
243
            new_lockable.lock_write(token=token)
244
            new_lockable.unlock()
245
            self.assertTrue(self.lockable.get_physical_lock_status())
246
        finally:
247
            self.lockable.unlock()
248
249
    def test_lock_write_with_token_fails_when_unlocked(self):
250
        # Lock and unlock to get a superficially valid token.  This mimics a
251
        # likely programming error, where a caller accidentally tries to lock
252
        # with a token that is no longer valid (because the original lock was
253
        # released).
254
        token = self.lockable.lock_write()
255
        self.lockable.unlock()
256
        if token is None:
257
            # This test does not apply, because this lockable refuses
258
            # tokens.
259
            return
260
261
        self.assertRaises(errors.TokenMismatch,
262
                          self.lockable.lock_write, token=token)
263
264
    def test_lock_write_reenter_with_token(self):
265
        token = self.lockable.lock_write()
266
        try:
267
            if token is None:
268
                # This test does not apply, because this lockable refuses
269
                # tokens.
270
                return
271
            # Relock with a token.
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
272
            token_from_reentry = self.lockable.lock_write(token=token)
273
            try:
274
                self.assertEqual(token, token_from_reentry)
275
            finally:
276
                self.lockable.unlock()
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
277
        finally:
278
            self.lockable.unlock()
279
        # The lock should be unlocked on disk.  Verify that with a new lock
280
        # instance.
281
        new_lockable = self.get_lockable()
282
        # Calling lock_write now should work, rather than raise LockContention.
283
        new_lockable.lock_write()
284
        new_lockable.unlock()
285
2279.7.11 by Andrew Bennetts
Remove some XXXs.
286
    def test_second_lock_write_returns_same_token(self):
287
        first_token = self.lockable.lock_write()
288
        try:
289
            if first_token is None:
290
                # This test does not apply, because this lockable refuses
291
                # tokens.
292
                return
293
            # Relock the already locked lockable.  It should return the same
294
            # token.
295
            second_token = self.lockable.lock_write()
296
            try:
297
                self.assertEqual(first_token, second_token)
298
            finally:
299
                self.lockable.unlock()
300
        finally:
301
            self.lockable.unlock()
302
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
303
    def test_leave_in_place(self):
304
        token = self.lockable.lock_write()
305
        try:
306
            if token is None:
307
                # This test does not apply, because this lockable refuses
308
                # tokens.
309
                return
310
            self.lockable.leave_in_place()
311
        finally:
312
            self.lockable.unlock()
313
        # At this point, the lock is still in place on disk
314
        self.assertRaises(errors.LockContention, self.lockable.lock_write)
315
        # But should be relockable with a token.
316
        self.lockable.lock_write(token=token)
317
        self.lockable.unlock()
318
319
    def test_dont_leave_in_place(self):
320
        token = self.lockable.lock_write()
321
        try:
322
            if token is None:
323
                # This test does not apply, because this lockable refuses
324
                # tokens.
325
                return
326
            self.lockable.leave_in_place()
327
        finally:
328
            self.lockable.unlock()
329
        # At this point, the lock is still in place on disk.
330
        # Acquire the existing lock with the token, and ask that it is removed
331
        # when this object unlocks, and unlock to trigger that removal.
332
        new_lockable = self.get_lockable()
333
        new_lockable.lock_write(token=token)
334
        new_lockable.dont_leave_in_place()
335
        new_lockable.unlock()
336
        # At this point, the lock is no longer on disk, so we can lock it.
337
        third_lockable = self.get_lockable()
338
        third_lockable.lock_write()
339
        third_lockable.unlock()
2279.7.7 by Andrew Bennetts
LockDir, Repository and Branch lock token changes from the hpss branch.
340
341
1553.5.42 by Martin Pool
Start to parameterize LockableFiles test cases.
342
# This method of adapting tests to parameters is different to 
343
# the TestProviderAdapters used elsewhere, but seems simpler for this 
344
# case.  
1553.5.45 by Martin Pool
Clean up Transport-based locks for old branches
345
class TestLockableFiles_TransportLock(TestCaseInTempDir,
346
                                      _TestLockableFiles_mixin):
1553.5.42 by Martin Pool
Start to parameterize LockableFiles test cases.
347
348
    def setUp(self):
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
349
        TestCaseInTempDir.setUp(self)
1553.5.42 by Martin Pool
Start to parameterize LockableFiles test cases.
350
        transport = get_transport('.')
351
        transport.mkdir('.bzr')
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
352
        self.sub_transport = transport.clone('.bzr')
353
        self.lockable = self.get_lockable()
1553.5.61 by Martin Pool
Locks protecting LockableFiles must now be explicitly created before use.
354
        self.lockable.create_lock()
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
355
356
    def tearDown(self):
357
        super(TestLockableFiles_TransportLock, self).tearDown()
1687.1.15 by Robert Collins
Review comments.
358
        # free the subtransport so that we do not get a 5 second
359
        # timeout due to the SFTP connection cache.
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
360
        del self.sub_transport
361
362
    def get_lockable(self):
363
        return LockableFiles(self.sub_transport, 'my-lock', TransportLock)
1553.5.43 by Martin Pool
Get LockableFiles tests running against LockDir
364
        
365
366
class TestLockableFiles_LockDir(TestCaseInTempDir,
367
                              _TestLockableFiles_mixin):
368
    """LockableFile tests run with LockDir underneath"""
369
370
    def setUp(self):
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
371
        TestCaseInTempDir.setUp(self)
1553.5.61 by Martin Pool
Locks protecting LockableFiles must now be explicitly created before use.
372
        self.transport = get_transport('.')
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
373
        self.lockable = self.get_lockable()
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
374
        # the lock creation here sets mode - test_permissions on branch 
375
        # tests that implicitly, but it might be a good idea to factor 
376
        # out the mode checking logic and have it applied to loackable files
377
        # directly. RBC 20060418
1553.5.61 by Martin Pool
Locks protecting LockableFiles must now be explicitly created before use.
378
        self.lockable.create_lock()
1553.5.43 by Martin Pool
Get LockableFiles tests running against LockDir
379
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
380
    def get_lockable(self):
3107.2.1 by John Arbash Meinel
Fix LockableFiles to not use modes that allow the user to write to things they create.
381
        return LockableFiles(self.transport, 'my-lock', lockdir.LockDir)
1553.5.60 by Martin Pool
New LockableFiles.create_lock() method
382
383
    def test_lock_created(self):
1553.5.61 by Martin Pool
Locks protecting LockableFiles must now be explicitly created before use.
384
        self.assertTrue(self.transport.has('my-lock'))
385
        self.lockable.lock_write()
386
        self.assertTrue(self.transport.has('my-lock/held/info'))
387
        self.lockable.unlock()
388
        self.assertFalse(self.transport.has('my-lock/held/info'))
389
        self.assertTrue(self.transport.has('my-lock'))
390
3107.2.1 by John Arbash Meinel
Fix LockableFiles to not use modes that allow the user to write to things they create.
391
    def test__file_modes(self):
392
        self.transport.mkdir('readonly')
393
        osutils.make_readonly('readonly')
394
        lockable = LockableFiles(self.transport.clone('readonly'), 'test-lock',
395
                                 lockdir.LockDir)
396
        # The directory mode should be read-write-execute for the current user
397
        self.assertEqual(00700, lockable._dir_mode & 00700)
398
        # Files should be read-write for the current user
399
        self.assertEqual(00600, lockable._file_mode & 00700)
1553.5.61 by Martin Pool
Locks protecting LockableFiles must now be explicitly created before use.
400
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
401
402
class TestLockableFiles_RemoteLockDir(TestCaseWithSmartMedium,
403
                              _TestLockableFiles_mixin):
404
    """LockableFile tests run with RemoteLockDir on a branch."""
405
406
    def setUp(self):
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
407
        TestCaseWithSmartMedium.setUp(self)
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
408
        # can only get a RemoteLockDir with some RemoteObject...
409
        # use a branch as thats what we want. These mixin tests test the end
410
        # to end behaviour, so stubbing out the backend and simulating would
411
        # defeat the purpose. We test the protocol implementation separately
412
        # in test_remote and test_smart as usual.
2018.5.171 by Andrew Bennetts
Disconnect RemoteTransports in some tests to avoid tripping up test_strace with leftover threads from previous tests.
413
        b = self.make_branch('foo')
414
        self.addCleanup(b.bzrdir.transport.disconnect)
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
415
        self.transport = get_transport('.')
416
        self.lockable = self.get_lockable()
417
418
    def get_lockable(self):
419
        # getting a new lockable involves opening a new instance of the branch
420
        branch = bzrlib.branch.Branch.open(self.get_url('foo'))
2018.5.171 by Andrew Bennetts
Disconnect RemoteTransports in some tests to avoid tripping up test_strace with leftover threads from previous tests.
421
        self.addCleanup(branch.bzrdir.transport.disconnect)
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
422
        return branch.control_files