~bzr-pqm/bzr/bzr.dev

5557.1.15 by John Arbash Meinel
Merge bzr.dev 5597 to resolve NEWS, aka bzr-2.3.txt
1
# Copyright (C) 2005-2011 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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1185.67.4 by Aaron Bentley
Throw if we try to write to a LockableFiles with no write lock
16
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
17
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.
18
from bzrlib import (
19
    errors,
20
    lockdir,
21
    osutils,
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
22
    transport,
3107.2.1 by John Arbash Meinel
Fix LockableFiles to not use modes that allow the user to write to things they create.
23
    )
1553.5.63 by Martin Pool
Lock type is now mandatory for LockableFiles constructor
24
from bzrlib.lockable_files import LockableFiles, TransportLock
3474.1.1 by Martin Pool
Better reporting of inapplicable lockable_files tests
25
from bzrlib.tests import (
26
    TestCaseInTempDir,
27
    TestNotApplicable,
28
    )
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
29
from bzrlib.tests.test_smart import TestCaseWithSmartMedium
1594.2.22 by Robert Collins
Ensure that lockable files calls finish() on transactions.:
30
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
31
from bzrlib.transactions import (PassThroughTransaction,
32
                                 ReadOnlyTransaction,
33
                                 WriteTransaction,
34
                                 )
1185.65.23 by Robert Collins
update tests somewhat
35
1553.5.42 by Martin Pool
Start to parameterize LockableFiles test cases.
36
37
# these tests are applied in each parameterized suite for LockableFiles
3388.2.1 by Martin Pool
Deprecate LockableFiles.get_utf8
38
#
39
# they use an old style of parameterization, but we want to remove this class
40
# so won't modernize them now. - mbp 20080430
1553.5.42 by Martin Pool
Start to parameterize LockableFiles test cases.
41
class _TestLockableFiles_mixin(object):
1185.67.4 by Aaron Bentley
Throw if we try to write to a LockableFiles with no write lock
42
1185.68.1 by Aaron Bentley
test transactions
43
    def test_transactions(self):
44
        self.assertIs(self.lockable.get_transaction().__class__,
45
                      PassThroughTransaction)
46
        self.lockable.lock_read()
47
        try:
48
            self.assertIs(self.lockable.get_transaction().__class__,
49
                          ReadOnlyTransaction)
50
        finally:
51
            self.lockable.unlock()
52
        self.assertIs(self.lockable.get_transaction().__class__,
53
                      PassThroughTransaction)
54
        self.lockable.lock_write()
55
        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
56
                      WriteTransaction)
1594.2.22 by Robert Collins
Ensure that lockable files calls finish() on transactions.:
57
        # check that finish is called:
58
        vf = DummyWeave('a')
59
        self.lockable.get_transaction().register_dirty(vf)
1185.68.1 by Aaron Bentley
test transactions
60
        self.lockable.unlock()
1594.2.22 by Robert Collins
Ensure that lockable files calls finish() on transactions.:
61
        self.assertTrue(vf.finished)
1185.65.23 by Robert Collins
update tests somewhat
62
63
    def test__escape(self):
64
        self.assertEqual('%25', self.lockable._escape('%'))
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
65
1185.65.23 by Robert Collins
update tests somewhat
66
    def test__escape_empty(self):
67
        self.assertEqual('', self.lockable._escape(''))
68
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
69
    def test_break_lock(self):
70
        # some locks are not breakable
71
        self.lockable.lock_write()
72
        try:
73
            self.assertRaises(AssertionError, self.lockable.break_lock)
74
        except NotImplementedError:
75
            # this lock cannot be broken
76
            self.lockable.unlock()
3474.1.1 by Martin Pool
Better reporting of inapplicable lockable_files tests
77
            raise TestNotApplicable("%r is not breakable" % (self.lockable,))
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
78
        l2 = self.get_lockable()
79
        orig_factory = bzrlib.ui.ui_factory
80
        # silent ui - no need for stdout
4449.3.27 by Martin Pool
More test updates to use CannedInputUIFactory
81
        bzrlib.ui.ui_factory = bzrlib.ui.CannedInputUIFactory([True])
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
82
        try:
83
            l2.break_lock()
84
        finally:
85
            bzrlib.ui.ui_factory = orig_factory
86
        try:
87
            l2.lock_write()
88
            l2.unlock()
89
        finally:
90
            self.assertRaises(errors.LockBroken, self.lockable.unlock)
91
            self.assertFalse(self.lockable.is_locked())
92
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
93
    def test_lock_write_returns_None_refuses_token(self):
94
        token = self.lockable.lock_write()
4327.1.7 by Vincent Ladeuil
Fix 2 more lock-related test failures.
95
        self.addCleanup(self.lockable.unlock)
96
        if token is not None:
97
            # This test does not apply, because this lockable supports
98
            # tokens.
99
            raise TestNotApplicable("%r uses tokens" % (self.lockable,))
100
        self.assertRaises(errors.TokenLockingNotSupported,
101
                          self.lockable.lock_write, token='token')
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
102
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
103
    def test_lock_write_returns_token_when_given_token(self):
104
        token = self.lockable.lock_write()
4327.1.7 by Vincent Ladeuil
Fix 2 more lock-related test failures.
105
        self.addCleanup(self.lockable.unlock)
106
        if token is None:
107
            # This test does not apply, because this lockable refuses
108
            # tokens.
109
            return
110
        new_lockable = self.get_lockable()
111
        token_from_new_lockable = new_lockable.lock_write(token=token)
112
        self.addCleanup(new_lockable.unlock)
113
        self.assertEqual(token, token_from_new_lockable)
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
114
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
115
    def test_lock_write_raises_on_token_mismatch(self):
116
        token = self.lockable.lock_write()
4327.1.7 by Vincent Ladeuil
Fix 2 more lock-related test failures.
117
        self.addCleanup(self.lockable.unlock)
118
        if token is None:
119
            # This test does not apply, because this lockable refuses
120
            # tokens.
121
            return
122
        different_token = token + 'xxx'
123
        # Re-using the same lockable instance with a different token will
124
        # raise TokenMismatch.
125
        self.assertRaises(errors.TokenMismatch,
126
                          self.lockable.lock_write, token=different_token)
127
        # A separate instance for the same lockable will also raise
128
        # TokenMismatch.
129
        # This detects the case where a caller claims to have a lock (via
130
        # the token) for an external resource, but doesn't (the token is
131
        # different).  Clients need a separate lock object to make sure the
132
        # external resource is probed, whereas the existing lock object
133
        # might cache.
134
        new_lockable = self.get_lockable()
135
        self.assertRaises(errors.TokenMismatch,
136
                          new_lockable.lock_write, token=different_token)
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
137
138
    def test_lock_write_with_matching_token(self):
139
        # If the token matches, so no exception is raised by lock_write.
140
        token = self.lockable.lock_write()
4327.1.7 by Vincent Ladeuil
Fix 2 more lock-related test failures.
141
        self.addCleanup(self.lockable.unlock)
142
        if token is None:
143
            # This test does not apply, because this lockable refuses
144
            # tokens.
145
            return
146
        # The same instance will accept a second lock_write if the specified
147
        # token matches.
148
        self.lockable.lock_write(token=token)
149
        self.lockable.unlock()
150
        # Calling lock_write on a new instance for the same lockable will
151
        # also succeed.
152
        new_lockable = self.get_lockable()
153
        new_lockable.lock_write(token=token)
154
        new_lockable.unlock()
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
155
156
    def test_unlock_after_lock_write_with_token(self):
157
        # If lock_write did not physically acquire the lock (because it was
158
        # passed a token), then unlock should not physically release it.
159
        token = self.lockable.lock_write()
4327.1.7 by Vincent Ladeuil
Fix 2 more lock-related test failures.
160
        self.addCleanup(self.lockable.unlock)
161
        if token is None:
162
            # This test does not apply, because this lockable refuses
163
            # tokens.
164
            return
165
        new_lockable = self.get_lockable()
166
        new_lockable.lock_write(token=token)
167
        new_lockable.unlock()
168
        self.assertTrue(self.lockable.get_physical_lock_status())
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
169
170
    def test_lock_write_with_token_fails_when_unlocked(self):
171
        # Lock and unlock to get a superficially valid token.  This mimics a
172
        # likely programming error, where a caller accidentally tries to lock
173
        # with a token that is no longer valid (because the original lock was
174
        # released).
175
        token = self.lockable.lock_write()
176
        self.lockable.unlock()
177
        if token is None:
178
            # This test does not apply, because this lockable refuses
179
            # tokens.
180
            return
181
182
        self.assertRaises(errors.TokenMismatch,
183
                          self.lockable.lock_write, token=token)
184
185
    def test_lock_write_reenter_with_token(self):
186
        token = self.lockable.lock_write()
187
        try:
188
            if token is None:
189
                # This test does not apply, because this lockable refuses
190
                # tokens.
191
                return
192
            # Relock with a token.
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
193
            token_from_reentry = self.lockable.lock_write(token=token)
194
            try:
195
                self.assertEqual(token, token_from_reentry)
196
            finally:
197
                self.lockable.unlock()
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
198
        finally:
199
            self.lockable.unlock()
200
        # The lock should be unlocked on disk.  Verify that with a new lock
201
        # instance.
202
        new_lockable = self.get_lockable()
203
        # Calling lock_write now should work, rather than raise LockContention.
204
        new_lockable.lock_write()
205
        new_lockable.unlock()
206
2279.7.11 by Andrew Bennetts
Remove some XXXs.
207
    def test_second_lock_write_returns_same_token(self):
208
        first_token = self.lockable.lock_write()
209
        try:
210
            if first_token is None:
211
                # This test does not apply, because this lockable refuses
212
                # tokens.
213
                return
214
            # Relock the already locked lockable.  It should return the same
215
            # token.
216
            second_token = self.lockable.lock_write()
217
            try:
218
                self.assertEqual(first_token, second_token)
219
            finally:
220
                self.lockable.unlock()
221
        finally:
222
            self.lockable.unlock()
223
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
224
    def test_leave_in_place(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
            self.lockable.leave_in_place()
232
        finally:
233
            self.lockable.unlock()
234
        # At this point, the lock is still in place on disk
235
        self.assertRaises(errors.LockContention, self.lockable.lock_write)
236
        # But should be relockable with a token.
237
        self.lockable.lock_write(token=token)
238
        self.lockable.unlock()
4327.1.7 by Vincent Ladeuil
Fix 2 more lock-related test failures.
239
        # Cleanup: we should still be able to get the lock, but we restore the
240
        # behavior to clearing the lock when unlocking.
241
        self.lockable.lock_write(token=token)
242
        self.lockable.dont_leave_in_place()
243
        self.lockable.unlock()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
244
245
    def test_dont_leave_in_place(self):
246
        token = self.lockable.lock_write()
247
        try:
248
            if token is None:
249
                # This test does not apply, because this lockable refuses
250
                # tokens.
251
                return
252
            self.lockable.leave_in_place()
253
        finally:
254
            self.lockable.unlock()
255
        # At this point, the lock is still in place on disk.
256
        # Acquire the existing lock with the token, and ask that it is removed
257
        # when this object unlocks, and unlock to trigger that removal.
258
        new_lockable = self.get_lockable()
259
        new_lockable.lock_write(token=token)
260
        new_lockable.dont_leave_in_place()
261
        new_lockable.unlock()
262
        # At this point, the lock is no longer on disk, so we can lock it.
263
        third_lockable = self.get_lockable()
264
        third_lockable.lock_write()
265
        third_lockable.unlock()
2279.7.7 by Andrew Bennetts
LockDir, Repository and Branch lock token changes from the hpss branch.
266
267
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
268
# This method of adapting tests to parameters is different to
269
# the TestProviderAdapters used elsewhere, but seems simpler for this
270
# case.
1553.5.45 by Martin Pool
Clean up Transport-based locks for old branches
271
class TestLockableFiles_TransportLock(TestCaseInTempDir,
272
                                      _TestLockableFiles_mixin):
1553.5.42 by Martin Pool
Start to parameterize LockableFiles test cases.
273
274
    def setUp(self):
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
275
        TestCaseInTempDir.setUp(self)
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
276
        t = transport.get_transport('.')
277
        t.mkdir('.bzr')
278
        self.sub_transport = t.clone('.bzr')
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
279
        self.lockable = self.get_lockable()
1553.5.61 by Martin Pool
Locks protecting LockableFiles must now be explicitly created before use.
280
        self.lockable.create_lock()
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
281
4934.3.1 by Martin Pool
Rename Server.tearDown to .stop_server
282
    def stop_server(self):
283
        super(TestLockableFiles_TransportLock, self).stop_server()
1687.1.15 by Robert Collins
Review comments.
284
        # free the subtransport so that we do not get a 5 second
285
        # timeout due to the SFTP connection cache.
3474.1.1 by Martin Pool
Better reporting of inapplicable lockable_files tests
286
        try:
287
            del self.sub_transport
288
        except AttributeError:
289
            pass
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
290
291
    def get_lockable(self):
292
        return LockableFiles(self.sub_transport, 'my-lock', TransportLock)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
293
1553.5.43 by Martin Pool
Get LockableFiles tests running against LockDir
294
295
class TestLockableFiles_LockDir(TestCaseInTempDir,
296
                              _TestLockableFiles_mixin):
297
    """LockableFile tests run with LockDir underneath"""
298
299
    def setUp(self):
2279.7.1 by Andrew Bennetts
``LockableFiles.lock_write()`` now accepts a ``token`` keyword argument, so that
300
        TestCaseInTempDir.setUp(self)
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
301
        self.transport = transport.get_transport('.')
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
302
        self.lockable = self.get_lockable()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
303
        # the lock creation here sets mode - test_permissions on branch
304
        # tests that implicitly, but it might be a good idea to factor
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
305
        # out the mode checking logic and have it applied to loackable files
306
        # directly. RBC 20060418
1553.5.61 by Martin Pool
Locks protecting LockableFiles must now be explicitly created before use.
307
        self.lockable.create_lock()
1553.5.43 by Martin Pool
Get LockableFiles tests running against LockDir
308
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
309
    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.
310
        return LockableFiles(self.transport, 'my-lock', lockdir.LockDir)
1553.5.60 by Martin Pool
New LockableFiles.create_lock() method
311
312
    def test_lock_created(self):
1553.5.61 by Martin Pool
Locks protecting LockableFiles must now be explicitly created before use.
313
        self.assertTrue(self.transport.has('my-lock'))
314
        self.lockable.lock_write()
315
        self.assertTrue(self.transport.has('my-lock/held/info'))
316
        self.lockable.unlock()
317
        self.assertFalse(self.transport.has('my-lock/held/info'))
318
        self.assertTrue(self.transport.has('my-lock'))
319
3107.2.1 by John Arbash Meinel
Fix LockableFiles to not use modes that allow the user to write to things they create.
320
    def test__file_modes(self):
321
        self.transport.mkdir('readonly')
322
        osutils.make_readonly('readonly')
323
        lockable = LockableFiles(self.transport.clone('readonly'), 'test-lock',
324
                                 lockdir.LockDir)
325
        # The directory mode should be read-write-execute for the current user
326
        self.assertEqual(00700, lockable._dir_mode & 00700)
327
        # Files should be read-write for the current user
328
        self.assertEqual(00600, lockable._file_mode & 00700)
1553.5.61 by Martin Pool
Locks protecting LockableFiles must now be explicitly created before use.
329
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
330
331
class TestLockableFiles_RemoteLockDir(TestCaseWithSmartMedium,
332
                              _TestLockableFiles_mixin):
333
    """LockableFile tests run with RemoteLockDir on a branch."""
334
335
    def setUp(self):
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
336
        TestCaseWithSmartMedium.setUp(self)
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
337
        # can only get a RemoteLockDir with some RemoteObject...
338
        # use a branch as thats what we want. These mixin tests test the end
339
        # to end behaviour, so stubbing out the backend and simulating would
340
        # defeat the purpose. We test the protocol implementation separately
341
        # 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.
342
        b = self.make_branch('foo')
343
        self.addCleanup(b.bzrdir.transport.disconnect)
5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
344
        self.transport = transport.get_transport('.')
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
345
        self.lockable = self.get_lockable()
346
347
    def get_lockable(self):
348
        # getting a new lockable involves opening a new instance of the branch
349
        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.
350
        self.addCleanup(branch.bzrdir.transport.disconnect)
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
351
        return branch.control_files