~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_lockdir.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-11-27 23:21:36 UTC
  • mfrom: (4585.1.19 foreign-tests-4)
  • Revision ID: pqm@pqm.ubuntu.com-20091127232136-bkm3vrye2wt4odbw
(Jelmer) Add basic tests for foreign repositories.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Tests for LockDir"""
18
18
 
25
25
from bzrlib import (
26
26
    config,
27
27
    errors,
 
28
    lock,
28
29
    osutils,
29
30
    tests,
 
31
    transport,
30
32
    )
31
33
from bzrlib.errors import (
32
 
        LockBreakMismatch,
33
 
        LockContention, LockError, UnlockableTransport,
34
 
        LockNotHeld, LockBroken
35
 
        )
 
34
    LockBreakMismatch,
 
35
    LockBroken,
 
36
    LockContention,
 
37
    LockError,
 
38
    LockFailed,
 
39
    LockNotHeld,
 
40
    )
36
41
from bzrlib.lockdir import LockDir
37
42
from bzrlib.tests import TestCaseWithTransport
38
43
from bzrlib.trace import note
104
109
        """Fail to create lock on readonly transport"""
105
110
        t = self.get_readonly_transport()
106
111
        lf = LockDir(t, 'test_lock')
107
 
        self.assertRaises(UnlockableTransport, lf.create)
 
112
        self.assertRaises(LockFailed, lf.create)
108
113
 
109
114
    def test_12_lock_readonly_transport(self):
110
115
        """Fail to lock on readonly transport"""
111
116
        lf = LockDir(self.get_transport(), 'test_lock')
112
117
        lf.create()
113
118
        lf = LockDir(self.get_readonly_transport(), 'test_lock')
114
 
        self.assertRaises(UnlockableTransport, lf.attempt_lock)
 
119
        self.assertRaises(LockFailed, lf.attempt_lock)
115
120
 
116
121
    def test_20_lock_contested(self):
117
122
        """Contention to get a lock"""
121
126
        lf1.attempt_lock()
122
127
        lf2 = LockDir(t, 'test_lock')
123
128
        try:
124
 
            # locking is between LockDir instances; aliases within 
 
129
            # locking is between LockDir instances; aliases within
125
130
            # a single process are not detected
126
131
            lf2.attempt_lock()
127
132
            self.fail('Failed to detect lock collision')
137
142
        lf1 = LockDir(t, 'test_lock')
138
143
        lf1.create()
139
144
        lf1.attempt_lock()
 
145
        self.addCleanup(lf1.unlock)
140
146
        # lock is held, should get some info on it
141
147
        info1 = lf1.peek()
142
148
        self.assertEqual(set(info1.keys()),
156
162
        lf2 = LockDir(self.get_readonly_transport(), 'test_lock')
157
163
        self.assertEqual(lf2.peek(), None)
158
164
        lf1.attempt_lock()
 
165
        self.addCleanup(lf1.unlock)
159
166
        info2 = lf2.peek()
160
167
        self.assertTrue(info2)
161
168
        self.assertEqual(info2['nonce'], lf1.nonce)
162
169
 
163
170
    def test_30_lock_wait_fail(self):
164
171
        """Wait on a lock, then fail
165
 
        
 
172
 
166
173
        We ask to wait up to 400ms; this should fail within at most one
167
174
        second.  (Longer times are more realistic but we don't want the test
168
175
        suite to take too long, and this should do for now.)
180
187
            after = time.time()
181
188
            # it should only take about 0.4 seconds, but we allow more time in
182
189
            # case the machine is heavily loaded
183
 
            self.assertTrue(after - before <= 8.0, 
 
190
            self.assertTrue(after - before <= 8.0,
184
191
                    "took %f seconds to detect lock contention" % (after - before))
185
192
        finally:
186
193
            lf1.unlock()
187
194
        lock_base = lf2.transport.abspath(lf2.path)
188
195
        self.assertEqual(1, len(self._logged_reports))
 
196
        lock_url = lf2.transport.abspath(lf2.path)
189
197
        self.assertEqual('%s %s\n'
190
198
                         '%s\n%s\n'
191
 
                         'Will continue to try until %s\n',
 
199
                         'Will continue to try until %s, unless '
 
200
                         'you press Ctrl-C\n'
 
201
                         'If you\'re sure that it\'s not being '
 
202
                         'modified, use bzr break-lock %s',
192
203
                         self._logged_reports[0][0])
193
204
        args = self._logged_reports[0][1]
194
205
        self.assertEqual('Unable to obtain', args[0])
217
228
    def test_32_lock_wait_succeed(self):
218
229
        """Succeed when trying to acquire a lock that gets released
219
230
 
220
 
        One thread holds on a lock and then releases it; another 
 
231
        One thread holds on a lock and then releases it; another
221
232
        tries to lock it.
222
233
        """
 
234
        # This test sometimes fails like this:
 
235
        # Traceback (most recent call last):
 
236
 
 
237
        #   File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/
 
238
        # test_lockdir.py", line 247, in test_32_lock_wait_succeed
 
239
        #     self.assertEqual(1, len(self._logged_reports))
 
240
        # AssertionError: not equal:
 
241
        # a = 1
 
242
        # b = 0
 
243
        raise tests.TestSkipped("Test fails intermittently")
223
244
        t = self.get_transport()
224
245
        lf1 = LockDir(t, 'test_lock')
225
246
        lf1.create()
257
278
        self.assertEndsWith(args[3], ' ago')
258
279
        self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
259
280
 
260
 
    def test_33_wait(self):
261
 
        """Succeed when waiting on a lock that gets released
262
 
 
263
 
        The difference from test_32_lock_wait_succeed is that the second 
264
 
        caller does not actually acquire the lock, but just waits for it
265
 
        to be released.  This is done over a readonly transport.
266
 
        """
267
 
        t = self.get_transport()
268
 
        lf1 = LockDir(t, 'test_lock')
269
 
        lf1.create()
270
 
        lf1.attempt_lock()
271
 
 
272
 
        def wait_and_unlock():
273
 
            time.sleep(0.1)
274
 
            lf1.unlock()
275
 
        unlocker = Thread(target=wait_and_unlock)
276
 
        unlocker.start()
277
 
        try:
278
 
            lf2 = LockDir(self.get_readonly_transport(), 'test_lock')
279
 
            before = time.time()
280
 
            # wait but don't lock
281
 
            lf2.wait(timeout=0.4, poll=0.1)
282
 
            after = time.time()
283
 
            self.assertTrue(after - before <= 1.0)
284
 
        finally:
285
 
            unlocker.join()
286
 
 
287
281
    def test_34_lock_write_waits(self):
288
 
        """LockDir.lock_write() will wait for the lock.""" 
 
282
        """LockDir.lock_write() will wait for the lock."""
289
283
        # the test suite sets the default to 0 to make deadlocks fail fast.
290
284
        # change it for this test, as we want to try a manual deadlock.
 
285
        raise tests.TestSkipped('Timing-sensitive test')
291
286
        bzrlib.lockdir._DEFAULT_TIMEOUT_SECONDS = 300
292
287
        t = self.get_transport()
293
288
        lf1 = LockDir(t, 'test_lock')
327
322
 
328
323
    def test_35_wait_lock_changing(self):
329
324
        """LockDir.wait_lock() will report if the lock changes underneath.
330
 
        
 
325
 
331
326
        This is the stages we want to happen:
332
327
 
333
328
        0) Synchronization locks are created and locked.
334
329
        1) Lock1 obtains the lockdir, and releases the 'check' lock.
335
330
        2) Lock2 grabs the 'check' lock, and checks the lockdir.
336
 
           It sees the lockdir is already acquired, reports the fact, 
 
331
           It sees the lockdir is already acquired, reports the fact,
337
332
           and unsets the 'checked' lock.
338
333
        3) Thread1 blocks on acquiring the 'checked' lock, and then tells
339
334
           Lock1 to release and acquire the lockdir. This resets the 'check'
340
335
           lock.
341
336
        4) Lock2 acquires the 'check' lock, and checks again. It notices
342
 
           that the holder of the lock has changed, and so reports a new 
 
337
           that the holder of the lock has changed, and so reports a new
343
338
           lock holder.
344
339
        5) Thread1 blocks on the 'checked' lock, this time, it completely
345
340
           unlocks the lockdir, allowing Lock2 to acquire the lock.
346
341
        """
347
342
 
 
343
        raise tests.KnownFailure(
 
344
            "timing dependency in lock tests (#213182)")
 
345
 
348
346
        wait_to_check_lock = Lock()
349
347
        wait_until_checked_lock = Lock()
350
348
 
410
408
        unlocker.start()
411
409
        try:
412
410
            # Wait and play against the other thread
413
 
            lf2.wait_lock(timeout=1.0, poll=0.01)
 
411
            lf2.wait_lock(timeout=20.0, poll=0.01)
414
412
        finally:
415
413
            unlocker.join()
416
414
        lf2.unlock()
418
416
        # There should be 2 reports, because the lock changed
419
417
        lock_base = lf2.transport.abspath(lf2.path)
420
418
        self.assertEqual(2, len(self._logged_reports))
421
 
 
 
419
        lock_url = lf2.transport.abspath(lf2.path)
422
420
        self.assertEqual('%s %s\n'
423
421
                         '%s\n%s\n'
424
 
                         'Will continue to try until %s\n',
 
422
                         'Will continue to try until %s, unless '
 
423
                         'you press Ctrl-C\n'
 
424
                         'If you\'re sure that it\'s not being '
 
425
                         'modified, use bzr break-lock %s',
425
426
                         self._logged_reports[0][0])
426
427
        args = self._logged_reports[0][1]
427
428
        self.assertEqual('Unable to obtain', args[0])
433
434
 
434
435
        self.assertEqual('%s %s\n'
435
436
                         '%s\n%s\n'
436
 
                         'Will continue to try until %s\n',
 
437
                         'Will continue to try until %s, unless '
 
438
                         'you press Ctrl-C\n'
 
439
                         'If you\'re sure that it\'s not being '
 
440
                         'modified, use bzr break-lock %s',
437
441
                         self._logged_reports[1][0])
438
442
        args = self._logged_reports[1][1]
439
443
        self.assertEqual('Lock owner changed for', args[0])
449
453
        lf1 = LockDir(t, 'test_lock')
450
454
        lf1.create()
451
455
        lf1.attempt_lock()
 
456
        self.addCleanup(lf1.unlock)
452
457
        lf1.confirm()
453
458
 
454
459
    def test_41_confirm_not_held(self):
466
471
        lf1.attempt_lock()
467
472
        t.move('test_lock', 'lock_gone_now')
468
473
        self.assertRaises(LockBroken, lf1.confirm)
 
474
        # Clean up
 
475
        t.move('lock_gone_now', 'test_lock')
 
476
        lf1.unlock()
469
477
 
470
478
    def test_43_break(self):
471
479
        """Break a lock whose caller has forgotten it"""
482
490
        lf2.force_break(holder_info)
483
491
        # now we should be able to take it
484
492
        lf2.attempt_lock()
 
493
        self.addCleanup(lf2.unlock)
485
494
        lf2.confirm()
486
495
 
487
496
    def test_44_break_already_released(self):
499
508
        lf2.force_break(holder_info)
500
509
        # now we should be able to take it
501
510
        lf2.attempt_lock()
 
511
        self.addCleanup(lf2.unlock)
502
512
        lf2.confirm()
503
513
 
504
514
    def test_45_break_mismatch(self):
530
540
        """Check the on-disk representation of LockDirs is as expected.
531
541
 
532
542
        There should always be a top-level directory named by the lock.
533
 
        When the lock is held, there should be a lockname/held directory 
 
543
        When the lock is held, there should be a lockname/held directory
534
544
        containing an info file.
535
545
        """
536
546
        t = self.get_transport()
551
561
        # do this without IO redirection to ensure it doesn't prompt.
552
562
        self.assertRaises(AssertionError, ld1.break_lock)
553
563
        orig_factory = bzrlib.ui.ui_factory
554
 
        # silent ui - no need for stdout
555
 
        bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
556
 
        bzrlib.ui.ui_factory.stdin = StringIO("y\n")
 
564
        bzrlib.ui.ui_factory = bzrlib.ui.CannedInputUIFactory([True])
557
565
        try:
558
566
            ld2.break_lock()
559
567
            self.assertRaises(LockBroken, ld1.unlock)
614
622
        lock_path = ld1.transport.local_abspath('test_lock')
615
623
        os.mkdir(lock_path)
616
624
        osutils.make_readonly(lock_path)
617
 
        self.assertRaises(errors.PermissionDenied, ld1.attempt_lock)
 
625
        self.assertRaises(errors.LockFailed, ld1.attempt_lock)
 
626
 
 
627
    def test_lock_by_token(self):
 
628
        ld1 = self.get_lock()
 
629
        token = ld1.lock_write()
 
630
        self.addCleanup(ld1.unlock)
 
631
        self.assertNotEqual(None, token)
 
632
        ld2 = self.get_lock()
 
633
        t2 = ld2.lock_write(token)
 
634
        self.addCleanup(ld2.unlock)
 
635
        self.assertEqual(token, t2)
 
636
 
 
637
    def test_lock_with_buggy_rename(self):
 
638
        # test that lock acquisition handles servers which pretend they
 
639
        # renamed correctly but that actually fail
 
640
        t = transport.get_transport('brokenrename+' + self.get_url())
 
641
        ld1 = LockDir(t, 'test_lock')
 
642
        ld1.create()
 
643
        ld1.attempt_lock()
 
644
        ld2 = LockDir(t, 'test_lock')
 
645
        # we should fail to lock
 
646
        e = self.assertRaises(errors.LockContention, ld2.attempt_lock)
 
647
        # now the original caller should succeed in unlocking
 
648
        ld1.unlock()
 
649
        # and there should be nothing left over
 
650
        self.assertEquals([], t.list_dir('test_lock'))
 
651
 
 
652
    def test_failed_lock_leaves_no_trash(self):
 
653
        # if we fail to acquire the lock, we don't leave pending directories
 
654
        # behind -- https://bugs.launchpad.net/bzr/+bug/109169
 
655
        ld1 = self.get_lock()
 
656
        ld2 = self.get_lock()
 
657
        # should be nothing before we start
 
658
        ld1.create()
 
659
        t = self.get_transport().clone('test_lock')
 
660
        def check_dir(a):
 
661
            self.assertEquals(a, t.list_dir('.'))
 
662
        check_dir([])
 
663
        # when held, that's all we see
 
664
        ld1.attempt_lock()
 
665
        self.addCleanup(ld1.unlock)
 
666
        check_dir(['held'])
 
667
        # second guy should fail
 
668
        self.assertRaises(errors.LockContention, ld2.attempt_lock)
 
669
        # no kibble
 
670
        check_dir(['held'])
 
671
 
 
672
 
 
673
class TestLockDirHooks(TestCaseWithTransport):
 
674
 
 
675
    def setUp(self):
 
676
        super(TestLockDirHooks, self).setUp()
 
677
        self._calls = []
 
678
 
 
679
    def get_lock(self):
 
680
        return LockDir(self.get_transport(), 'test_lock')
 
681
 
 
682
    def record_hook(self, result):
 
683
        self._calls.append(result)
 
684
 
 
685
    def test_LockDir_acquired_success(self):
 
686
        # the LockDir.lock_acquired hook fires when a lock is acquired.
 
687
        LockDir.hooks.install_named_hook('lock_acquired',
 
688
                                         self.record_hook, 'record_hook')
 
689
        ld = self.get_lock()
 
690
        ld.create()
 
691
        self.assertEqual([], self._calls)
 
692
        result = ld.attempt_lock()
 
693
        lock_path = ld.transport.abspath(ld.path)
 
694
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
 
695
        ld.unlock()
 
696
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
 
697
 
 
698
    def test_LockDir_acquired_fail(self):
 
699
        # the LockDir.lock_acquired hook does not fire on failure.
 
700
        ld = self.get_lock()
 
701
        ld.create()
 
702
        ld2 = self.get_lock()
 
703
        ld2.attempt_lock()
 
704
        # install a lock hook now, when the disk lock is locked
 
705
        LockDir.hooks.install_named_hook('lock_acquired',
 
706
                                         self.record_hook, 'record_hook')
 
707
        self.assertRaises(errors.LockContention, ld.attempt_lock)
 
708
        self.assertEqual([], self._calls)
 
709
        ld2.unlock()
 
710
        self.assertEqual([], self._calls)
 
711
 
 
712
    def test_LockDir_released_success(self):
 
713
        # the LockDir.lock_released hook fires when a lock is acquired.
 
714
        LockDir.hooks.install_named_hook('lock_released',
 
715
                                         self.record_hook, 'record_hook')
 
716
        ld = self.get_lock()
 
717
        ld.create()
 
718
        self.assertEqual([], self._calls)
 
719
        result = ld.attempt_lock()
 
720
        self.assertEqual([], self._calls)
 
721
        ld.unlock()
 
722
        lock_path = ld.transport.abspath(ld.path)
 
723
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
 
724
 
 
725
    def test_LockDir_released_fail(self):
 
726
        # the LockDir.lock_released hook does not fire on failure.
 
727
        ld = self.get_lock()
 
728
        ld.create()
 
729
        ld2 = self.get_lock()
 
730
        ld.attempt_lock()
 
731
        ld2.force_break(ld2.peek())
 
732
        LockDir.hooks.install_named_hook('lock_released',
 
733
                                         self.record_hook, 'record_hook')
 
734
        self.assertRaises(LockBroken, ld.unlock)
 
735
        self.assertEqual([], self._calls)
 
736
 
 
737
    def test_LockDir_broken_success(self):
 
738
        # the LockDir.lock_broken hook fires when a lock is broken.
 
739
        ld = self.get_lock()
 
740
        ld.create()
 
741
        ld2 = self.get_lock()
 
742
        result = ld.attempt_lock()
 
743
        LockDir.hooks.install_named_hook('lock_broken',
 
744
                                         self.record_hook, 'record_hook')
 
745
        ld2.force_break(ld2.peek())
 
746
        lock_path = ld.transport.abspath(ld.path)
 
747
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
 
748
 
 
749
    def test_LockDir_broken_failure(self):
 
750
        # the LockDir.lock_broken hook does not fires when a lock is already
 
751
        # released.
 
752
        ld = self.get_lock()
 
753
        ld.create()
 
754
        ld2 = self.get_lock()
 
755
        result = ld.attempt_lock()
 
756
        holder_info = ld2.peek()
 
757
        ld.unlock()
 
758
        LockDir.hooks.install_named_hook('lock_broken',
 
759
                                         self.record_hook, 'record_hook')
 
760
        ld2.force_break(holder_info)
 
761
        lock_path = ld.transport.abspath(ld.path)
 
762
        self.assertEqual([], self._calls)