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