~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
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
19
import bzrlib.branch as branch
20
import bzrlib.errors as errors
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.
21
from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
22
23
24
class TestWorkingTreeLocking(TestCaseWithWorkingTree):
25
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
26
    def test_trivial_lock_read_unlock(self):
27
        """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.
28
        wt = self.make_branch_and_tree('.')
29
30
        self.assertFalse(wt.is_locked())
31
        self.assertFalse(wt.branch.is_locked())
32
        wt.lock_read()
33
        try:
34
            self.assertTrue(wt.is_locked())
35
            self.assertTrue(wt.branch.is_locked())
36
        finally:
37
            wt.unlock()
38
        self.assertFalse(wt.is_locked())
39
        self.assertFalse(wt.branch.is_locked())
40
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
41
    def test_trivial_lock_write_unlock(self):
42
        """Locking for write and unlocking should work trivially."""
43
        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.
44
45
        self.assertFalse(wt.is_locked())
46
        self.assertFalse(wt.branch.is_locked())
47
        wt.lock_write()
48
        try:
49
            self.assertTrue(wt.is_locked())
50
            self.assertTrue(wt.branch.is_locked())
51
        finally:
52
            wt.unlock()
53
        self.assertFalse(wt.is_locked())
54
        self.assertFalse(wt.branch.is_locked())
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
55
        
1997.1.1 by Robert Collins
Add WorkingTree.lock_tree_write.
56
    def test_trivial_lock_tree_write_unlock(self):
57
        """Locking for tree write is ok when the branch is not locked."""
58
        wt = self.make_branch_and_tree('.')
59
60
        self.assertFalse(wt.is_locked())
61
        self.assertFalse(wt.branch.is_locked())
62
        wt.lock_tree_write()
63
        try:
64
            self.assertTrue(wt.is_locked())
65
            self.assertTrue(wt.branch.is_locked())
66
        finally:
67
            wt.unlock()
68
        self.assertFalse(wt.is_locked())
69
        self.assertFalse(wt.branch.is_locked())
70
        
71
    def test_trivial_lock_tree_write_branch_read_locked(self):
72
        """It is ok to lock_tree_write when the branch is read locked."""
73
        wt = self.make_branch_and_tree('.')
74
75
        self.assertFalse(wt.is_locked())
76
        self.assertFalse(wt.branch.is_locked())
77
        wt.branch.lock_read()
78
        try:
79
            wt.lock_tree_write()
80
        except errors.ReadOnlyError:
81
            # When ReadOnlyError is raised, it indicates that the 
82
            # workingtree shares its lock with the branch, which is what
83
            # the git/hg/bzr0.6 formats do.
84
            # in this case, no lock should have been taken - but the tree
85
            # will have been locked because they share a lock. Unlocking
86
            # just the branch should make everything match again correctly.
87
            wt.branch.unlock()
88
            self.assertFalse(wt.is_locked())
89
            self.assertFalse(wt.branch.is_locked())
90
            return
91
        try:
92
            self.assertTrue(wt.is_locked())
93
            self.assertTrue(wt.branch.is_locked())
94
        finally:
95
            wt.unlock()
96
        self.assertFalse(wt.is_locked())
97
        self.assertTrue(wt.branch.is_locked())
98
        wt.branch.unlock()
99
        
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
100
    def test_unlock_branch_failures(self):
101
        """If the branch unlock fails the tree must still unlock."""
102
        # The public interface for WorkingTree requires a branch, but
103
        # does not require that the working tree use the branch - its
104
        # implementation specific how the WorkingTree, Branch, and Repository
105
        # hang together.
106
        # in order to test that implementations which *do* unlock via the branch
107
        # do so correctly, we unlock the branch after locking the working tree.
108
        # The next unlock on working tree should trigger a LockNotHeld exception
109
        # from the branch object, which must be exposed to the caller. To meet 
110
        # our object model - where locking a tree locks its branch, and
111
        # unlocking a branch does not unlock a working tree, *even* for 
112
        # all-in-one implementations like bzr 0.6, git, and hg, implementations
113
        # must have some separate counter for each object, so our explicit 
114
        # unlock should trigger some error on all implementations, and 
115
        # requiring that to be LockNotHeld seems reasonable.
116
        #
117
        # we use this approach rather than decorating the Branch, because the
118
        # public interface of WorkingTree does not permit altering the branch
119
        # object - and we cannot tell which attribute might allow us to 
120
        # backdoor-in and change it reliably. For implementation specific tests
121
        # we can do such skullduggery, but not for interface specific tests.
122
        # And, its simpler :)
123
        wt = self.make_branch_and_tree('.')
124
125
        self.assertFalse(wt.is_locked())
126
        self.assertFalse(wt.branch.is_locked())
127
        wt.lock_write()
128
        self.assertTrue(wt.is_locked())
129
        self.assertTrue(wt.branch.is_locked())
130
131
        # manually unlock the branch, preparing a LockNotHeld error.
132
        wt.branch.unlock()
133
        # the branch *may* still be locked here, if its an all-in-one
1852.4.6 by Robert Collins
Review feedback.
134
        # implementation because there is a single lock object with three
135
        # 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.
136
        self.assertRaises(errors.LockNotHeld, wt.unlock)
137
        # but now, the tree must be unlocked
138
        self.assertFalse(wt.is_locked())
139
        # and the branch too.
140
        self.assertFalse(wt.branch.is_locked())
141
142
    def test_failing_to_lock_branch_does_not_lock(self):
143
        """If the branch cannot be locked, dont lock the tree."""
144
        # Many implementations treat read-locks as non-blocking, but some
145
        # treat them as blocking with writes.. Accordingly we test this by
146
        # opening the branch twice, and locking the branch for write in the
147
        # second instance.  Our lock contract requires separate instances to
148
        # mutually exclude if a lock is exclusive at all: If we get no error
149
        # locking, the test still passes.
150
        wt = self.make_branch_and_tree('.')
151
        branch_copy = branch.Branch.open('.')
152
        branch_copy.lock_write()
153
        try:
154
            try:
155
                wt.lock_read()
1852.4.5 by Robert Collins
Change bare except in working tree locking tests to catch LockError only.
156
            except errors.LockError:
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
157
                # any error here means the locks are exclusive in some 
158
                # manner
159
                self.assertFalse(wt.is_locked())
160
                self.assertFalse(wt.branch.is_locked())
161
                return
162
            else:
163
                # no error - the branch allows read locks while writes
164
                # are taken, just pass.
165
                wt.unlock()
166
        finally:
167
            branch_copy.unlock()
168
169
    def test_failing_to_lock_write_branch_does_not_lock(self):
170
        """If the branch cannot be write locked, dont lock the tree."""
171
        # all implementations of branch are required to treat write 
172
        # locks as blocking (compare to repositories which are not required
173
        # to do so).
174
        # Accordingly we test this by opening the branch twice, and locking the
175
        # branch for write in the second instance.  Our lock contract requires
176
        # separate instances to mutually exclude.
177
        wt = self.make_branch_and_tree('.')
178
        branch_copy = branch.Branch.open('.')
179
        branch_copy.lock_write()
180
        try:
181
            try:
182
                self.assertRaises(errors.LockError, wt.lock_write)
183
                self.assertFalse(wt.is_locked())
184
                self.assertFalse(wt.branch.is_locked())
185
            finally:
186
                if wt.is_locked():
187
                    wt.unlock()
188
        finally:
189
            branch_copy.unlock()
1997.1.1 by Robert Collins
Add WorkingTree.lock_tree_write.
190
191
    def test_failing_to_lock_tree_write_branch_does_not_lock(self):
192
        """If the branch cannot be read 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_tree_write()
205
            except errors.LockError:
206
                # any error here means the locks are exclusive in some 
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()