~bzr-pqm/bzr/bzr.dev

1711.8.5 by John Arbash Meinel
Move the new locking tests into their own files, and move the helper functions into a test helper.
1
# Copyright (C) 2006 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
17
"""Tests for the (un)lock interfaces on all working tree implemenations."""
1711.8.5 by John Arbash Meinel
Move the new locking tests into their own files, and move the helper functions into a test helper.
18
1957.1.17 by John Arbash Meinel
Change tests that expect locking to fail to timeout sooner.
19
from bzrlib import (
20
    branch,
21
    errors,
22
    lockdir,
23
    )
1711.8.5 by John Arbash Meinel
Move the new locking tests into their own files, and move the helper functions into a test helper.
24
from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
25
26
27
class TestWorkingTreeLocking(TestCaseWithWorkingTree):
28
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
29
    def test_trivial_lock_read_unlock(self):
30
        """Locking and unlocking should work trivially."""
1711.8.5 by John Arbash Meinel
Move the new locking tests into their own files, and move the helper functions into a test helper.
31
        wt = self.make_branch_and_tree('.')
32
33
        self.assertFalse(wt.is_locked())
34
        self.assertFalse(wt.branch.is_locked())
35
        wt.lock_read()
36
        try:
37
            self.assertTrue(wt.is_locked())
38
            self.assertTrue(wt.branch.is_locked())
39
        finally:
40
            wt.unlock()
41
        self.assertFalse(wt.is_locked())
42
        self.assertFalse(wt.branch.is_locked())
43
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
44
    def test_trivial_lock_write_unlock(self):
45
        """Locking for write and unlocking should work trivially."""
46
        wt = self.make_branch_and_tree('.')
1711.8.5 by John Arbash Meinel
Move the new locking tests into their own files, and move the helper functions into a test helper.
47
48
        self.assertFalse(wt.is_locked())
49
        self.assertFalse(wt.branch.is_locked())
50
        wt.lock_write()
51
        try:
52
            self.assertTrue(wt.is_locked())
53
            self.assertTrue(wt.branch.is_locked())
54
        finally:
55
            wt.unlock()
56
        self.assertFalse(wt.is_locked())
57
        self.assertFalse(wt.branch.is_locked())
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
58
1997.1.1 by Robert Collins
Add WorkingTree.lock_tree_write.
59
    def test_trivial_lock_tree_write_unlock(self):
60
        """Locking for tree write is ok when the branch is not locked."""
61
        wt = self.make_branch_and_tree('.')
62
63
        self.assertFalse(wt.is_locked())
64
        self.assertFalse(wt.branch.is_locked())
65
        wt.lock_tree_write()
66
        try:
67
            self.assertTrue(wt.is_locked())
68
            self.assertTrue(wt.branch.is_locked())
69
        finally:
70
            wt.unlock()
71
        self.assertFalse(wt.is_locked())
72
        self.assertFalse(wt.branch.is_locked())
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
73
1997.1.1 by Robert Collins
Add WorkingTree.lock_tree_write.
74
    def test_trivial_lock_tree_write_branch_read_locked(self):
75
        """It is ok to lock_tree_write when the branch is read locked."""
76
        wt = self.make_branch_and_tree('.')
77
78
        self.assertFalse(wt.is_locked())
79
        self.assertFalse(wt.branch.is_locked())
80
        wt.branch.lock_read()
81
        try:
82
            wt.lock_tree_write()
83
        except errors.ReadOnlyError:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
84
            # When ReadOnlyError is raised, it indicates that the
1997.1.1 by Robert Collins
Add WorkingTree.lock_tree_write.
85
            # workingtree shares its lock with the branch, which is what
86
            # the git/hg/bzr0.6 formats do.
87
            # in this case, no lock should have been taken - but the tree
88
            # will have been locked because they share a lock. Unlocking
89
            # just the branch should make everything match again correctly.
90
            wt.branch.unlock()
91
            self.assertFalse(wt.is_locked())
92
            self.assertFalse(wt.branch.is_locked())
93
            return
94
        try:
95
            self.assertTrue(wt.is_locked())
96
            self.assertTrue(wt.branch.is_locked())
97
        finally:
98
            wt.unlock()
99
        self.assertFalse(wt.is_locked())
100
        self.assertTrue(wt.branch.is_locked())
101
        wt.branch.unlock()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
102
1986.5.3 by Robert Collins
New method ``WorkingTree.flush()`` which will write the current memory
103
    def _test_unlock_with_lock_method(self, methodname):
104
        """Create a tree and then test its unlocking behaviour.
105
106
        :param methodname: The lock method to use to establish locks.
107
        """
108
        # when unlocking the last lock count from tree_write_lock,
109
        # the tree should do a flush().
110
        # we test that by changing the inventory using set_root_id
111
        tree = self.make_branch_and_tree('tree')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
112
        # prepare for a series of changes that will modify the
1986.5.3 by Robert Collins
New method ``WorkingTree.flush()`` which will write the current memory
113
        # inventory
114
        getattr(tree, methodname)()
115
        # note that we dont have a try:finally here because of two reasons:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
116
        # firstly there will only be errors reported if the test fails, and
1986.5.3 by Robert Collins
New method ``WorkingTree.flush()`` which will write the current memory
117
        # when it fails thats ok as long as the test suite cleanup still works,
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
118
        # which it will as the lock objects are released (thats where the
119
        # warning comes from.  Secondly, it is hard in this test to be
1986.5.3 by Robert Collins
New method ``WorkingTree.flush()`` which will write the current memory
120
        # sure that we've got the right interactions between try:finally
121
        # and the lock/unlocks we are doing.
122
        getattr(tree, methodname)()
123
        # this should really do something within the public api
124
        # e.g. mkdir('foo') but all the mutating methods at the
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
125
        # moment trigger inventory writes and thus will not
1986.5.3 by Robert Collins
New method ``WorkingTree.flush()`` which will write the current memory
126
        # let us trigger a read-when-dirty situation.
127
        old_root = tree.get_root_id()
128
        tree.set_root_id('new-root')
129
        # to detect that the inventory is written by unlock, we
130
        # first check that it was not written yet.
131
        reference_tree = tree.bzrdir.open_workingtree()
132
        self.assertEqual(old_root, reference_tree.get_root_id())
133
        # now unlock the second held lock, which should do nothing.
134
        tree.unlock()
135
        reference_tree = tree.bzrdir.open_workingtree()
136
        self.assertEqual(old_root, reference_tree.get_root_id())
137
        # unlocking the first lock we took will now flush.
138
        tree.unlock()
139
        # and check it was written using another reference tree
140
        reference_tree = tree.bzrdir.open_workingtree()
141
        self.assertEqual('new-root', reference_tree.get_root_id())
142
143
    def test_unlock_from_tree_write_lock_flushes(self):
144
        self._test_unlock_with_lock_method("lock_tree_write")
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
145
1986.5.3 by Robert Collins
New method ``WorkingTree.flush()`` which will write the current memory
146
    def test_unlock_from_write_lock_flushes(self):
147
        self._test_unlock_with_lock_method("lock_write")
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
148
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
149
    def test_unlock_branch_failures(self):
150
        """If the branch unlock fails the tree must still unlock."""
151
        # The public interface for WorkingTree requires a branch, but
152
        # does not require that the working tree use the branch - its
153
        # implementation specific how the WorkingTree, Branch, and Repository
154
        # hang together.
155
        # in order to test that implementations which *do* unlock via the branch
156
        # do so correctly, we unlock the branch after locking the working tree.
157
        # The next unlock on working tree should trigger a LockNotHeld exception
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
158
        # from the branch object, which must be exposed to the caller. To meet
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
159
        # our object model - where locking a tree locks its branch, and
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
160
        # unlocking a branch does not unlock a working tree, *even* for
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
161
        # all-in-one implementations like bzr 0.6, git, and hg, implementations
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
162
        # must have some separate counter for each object, so our explicit
163
        # unlock should trigger some error on all implementations, and
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
164
        # requiring that to be LockNotHeld seems reasonable.
165
        #
166
        # we use this approach rather than decorating the Branch, because the
167
        # public interface of WorkingTree does not permit altering the branch
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
168
        # object - and we cannot tell which attribute might allow us to
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
169
        # backdoor-in and change it reliably. For implementation specific tests
170
        # we can do such skullduggery, but not for interface specific tests.
171
        # And, its simpler :)
172
        wt = self.make_branch_and_tree('.')
173
174
        self.assertFalse(wt.is_locked())
175
        self.assertFalse(wt.branch.is_locked())
176
        wt.lock_write()
177
        self.assertTrue(wt.is_locked())
178
        self.assertTrue(wt.branch.is_locked())
179
180
        # manually unlock the branch, preparing a LockNotHeld error.
181
        wt.branch.unlock()
182
        # the branch *may* still be locked here, if its an all-in-one
1852.4.6 by Robert Collins
Review feedback.
183
        # implementation because there is a single lock object with three
184
        # references on it, and unlocking the branch only drops this by two
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
185
        self.assertRaises(errors.LockNotHeld, wt.unlock)
186
        # but now, the tree must be unlocked
187
        self.assertFalse(wt.is_locked())
188
        # and the branch too.
189
        self.assertFalse(wt.branch.is_locked())
190
191
    def test_failing_to_lock_branch_does_not_lock(self):
192
        """If the branch cannot be locked, dont lock the tree."""
193
        # Many implementations treat read-locks as non-blocking, but some
194
        # treat them as blocking with writes.. Accordingly we test this by
195
        # opening the branch twice, and locking the branch for write in the
196
        # second instance.  Our lock contract requires separate instances to
197
        # mutually exclude if a lock is exclusive at all: If we get no error
198
        # locking, the test still passes.
199
        wt = self.make_branch_and_tree('.')
200
        branch_copy = branch.Branch.open('.')
201
        branch_copy.lock_write()
202
        try:
203
            try:
204
                wt.lock_read()
1852.4.5 by Robert Collins
Change bare except in working tree locking tests to catch LockError only.
205
            except errors.LockError:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
206
                # any error here means the locks are exclusive in some
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
207
                # manner
208
                self.assertFalse(wt.is_locked())
209
                self.assertFalse(wt.branch.is_locked())
210
                return
211
            else:
212
                # no error - the branch allows read locks while writes
213
                # are taken, just pass.
214
                wt.unlock()
215
        finally:
216
            branch_copy.unlock()
217
218
    def test_failing_to_lock_write_branch_does_not_lock(self):
219
        """If the branch cannot be write locked, dont lock the tree."""
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
220
        # all implementations of branch are required to treat write
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
221
        # locks as blocking (compare to repositories which are not required
222
        # to do so).
223
        # Accordingly we test this by opening the branch twice, and locking the
224
        # branch for write in the second instance.  Our lock contract requires
225
        # separate instances to mutually exclude.
226
        wt = self.make_branch_and_tree('.')
227
        branch_copy = branch.Branch.open('.')
228
        branch_copy.lock_write()
229
        try:
230
            try:
3287.4.2 by Martin Pool
Change more tests to use reduceLockdirTimeout
231
                self.assertRaises(errors.LockError, wt.lock_write)
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
232
                self.assertFalse(wt.is_locked())
233
                self.assertFalse(wt.branch.is_locked())
234
            finally:
235
                if wt.is_locked():
236
                    wt.unlock()
237
        finally:
238
            branch_copy.unlock()
1997.1.1 by Robert Collins
Add WorkingTree.lock_tree_write.
239
240
    def test_failing_to_lock_tree_write_branch_does_not_lock(self):
241
        """If the branch cannot be read locked, dont lock the tree."""
242
        # Many implementations treat read-locks as non-blocking, but some
243
        # treat them as blocking with writes.. Accordingly we test this by
244
        # opening the branch twice, and locking the branch for write in the
245
        # second instance.  Our lock contract requires separate instances to
246
        # mutually exclude if a lock is exclusive at all: If we get no error
247
        # locking, the test still passes.
248
        wt = self.make_branch_and_tree('.')
249
        branch_copy = branch.Branch.open('.')
1957.1.17 by John Arbash Meinel
Change tests that expect locking to fail to timeout sooner.
250
1997.1.1 by Robert Collins
Add WorkingTree.lock_tree_write.
251
        branch_copy.lock_write()
252
        try:
253
            try:
254
                wt.lock_tree_write()
255
            except errors.LockError:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
256
                # any error here means the locks are exclusive in some
1997.1.1 by Robert Collins
Add WorkingTree.lock_tree_write.
257
                # manner
258
                self.assertFalse(wt.is_locked())
259
                self.assertFalse(wt.branch.is_locked())
260
                return
261
            else:
262
                # no error - the branch allows read locks while writes
263
                # are taken, just pass.
264
                wt.unlock()
265
        finally:
266
            branch_copy.unlock()