~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_lockdir.py

  • Committer: John Arbash Meinel
  • Date: 2010-02-17 17:11:16 UTC
  • mfrom: (4797.2.17 2.1)
  • mto: (4797.2.18 2.1)
  • mto: This revision was merged to the branch mainline in revision 5055.
  • Revision ID: john@arbash-meinel.com-20100217171116-h7t9223ystbnx5h8
merge bzr.2.1 in preparation for NEWS entry.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2006-2010 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,
30
31
    transport,
31
32
    )
32
33
from bzrlib.errors import (
33
 
        LockBreakMismatch,
34
 
        LockContention, LockError, UnlockableTransport,
35
 
        LockNotHeld, LockBroken
36
 
        )
 
34
    LockBreakMismatch,
 
35
    LockBroken,
 
36
    LockContention,
 
37
    LockError,
 
38
    LockFailed,
 
39
    LockNotHeld,
 
40
    )
37
41
from bzrlib.lockdir import LockDir
38
42
from bzrlib.tests import TestCaseWithTransport
39
43
from bzrlib.trace import note
105
109
        """Fail to create lock on readonly transport"""
106
110
        t = self.get_readonly_transport()
107
111
        lf = LockDir(t, 'test_lock')
108
 
        self.assertRaises(UnlockableTransport, lf.create)
 
112
        self.assertRaises(LockFailed, lf.create)
109
113
 
110
114
    def test_12_lock_readonly_transport(self):
111
115
        """Fail to lock on readonly transport"""
112
116
        lf = LockDir(self.get_transport(), 'test_lock')
113
117
        lf.create()
114
118
        lf = LockDir(self.get_readonly_transport(), 'test_lock')
115
 
        self.assertRaises(UnlockableTransport, lf.attempt_lock)
 
119
        self.assertRaises(LockFailed, lf.attempt_lock)
116
120
 
117
121
    def test_20_lock_contested(self):
118
122
        """Contention to get a lock"""
122
126
        lf1.attempt_lock()
123
127
        lf2 = LockDir(t, 'test_lock')
124
128
        try:
125
 
            # locking is between LockDir instances; aliases within 
 
129
            # locking is between LockDir instances; aliases within
126
130
            # a single process are not detected
127
131
            lf2.attempt_lock()
128
132
            self.fail('Failed to detect lock collision')
138
142
        lf1 = LockDir(t, 'test_lock')
139
143
        lf1.create()
140
144
        lf1.attempt_lock()
 
145
        self.addCleanup(lf1.unlock)
141
146
        # lock is held, should get some info on it
142
147
        info1 = lf1.peek()
143
148
        self.assertEqual(set(info1.keys()),
157
162
        lf2 = LockDir(self.get_readonly_transport(), 'test_lock')
158
163
        self.assertEqual(lf2.peek(), None)
159
164
        lf1.attempt_lock()
 
165
        self.addCleanup(lf1.unlock)
160
166
        info2 = lf2.peek()
161
167
        self.assertTrue(info2)
162
168
        self.assertEqual(info2['nonce'], lf1.nonce)
163
169
 
164
170
    def test_30_lock_wait_fail(self):
165
171
        """Wait on a lock, then fail
166
 
        
 
172
 
167
173
        We ask to wait up to 400ms; this should fail within at most one
168
174
        second.  (Longer times are more realistic but we don't want the test
169
175
        suite to take too long, and this should do for now.)
181
187
            after = time.time()
182
188
            # it should only take about 0.4 seconds, but we allow more time in
183
189
            # case the machine is heavily loaded
184
 
            self.assertTrue(after - before <= 8.0, 
 
190
            self.assertTrue(after - before <= 8.0,
185
191
                    "took %f seconds to detect lock contention" % (after - before))
186
192
        finally:
187
193
            lf1.unlock()
188
194
        lock_base = lf2.transport.abspath(lf2.path)
189
195
        self.assertEqual(1, len(self._logged_reports))
 
196
        lock_url = lf2.transport.abspath(lf2.path)
190
197
        self.assertEqual('%s %s\n'
191
198
                         '%s\n%s\n'
192
 
                         'Will continue to try until %s\n',
 
199
                         'Will continue to try until %s, unless '
 
200
                         'you press Ctrl-C.\n'
 
201
                         'See "bzr help break-lock" for more.',
193
202
                         self._logged_reports[0][0])
194
203
        args = self._logged_reports[0][1]
195
204
        self.assertEqual('Unable to obtain', args[0])
218
227
    def test_32_lock_wait_succeed(self):
219
228
        """Succeed when trying to acquire a lock that gets released
220
229
 
221
 
        One thread holds on a lock and then releases it; another 
 
230
        One thread holds on a lock and then releases it; another
222
231
        tries to lock it.
223
232
        """
224
233
        # This test sometimes fails like this:
269
278
        self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
270
279
 
271
280
    def test_34_lock_write_waits(self):
272
 
        """LockDir.lock_write() will wait for the lock.""" 
 
281
        """LockDir.lock_write() will wait for the lock."""
273
282
        # the test suite sets the default to 0 to make deadlocks fail fast.
274
283
        # change it for this test, as we want to try a manual deadlock.
275
284
        raise tests.TestSkipped('Timing-sensitive test')
312
321
 
313
322
    def test_35_wait_lock_changing(self):
314
323
        """LockDir.wait_lock() will report if the lock changes underneath.
315
 
        
 
324
 
316
325
        This is the stages we want to happen:
317
326
 
318
327
        0) Synchronization locks are created and locked.
319
328
        1) Lock1 obtains the lockdir, and releases the 'check' lock.
320
329
        2) Lock2 grabs the 'check' lock, and checks the lockdir.
321
 
           It sees the lockdir is already acquired, reports the fact, 
 
330
           It sees the lockdir is already acquired, reports the fact,
322
331
           and unsets the 'checked' lock.
323
332
        3) Thread1 blocks on acquiring the 'checked' lock, and then tells
324
333
           Lock1 to release and acquire the lockdir. This resets the 'check'
325
334
           lock.
326
335
        4) Lock2 acquires the 'check' lock, and checks again. It notices
327
 
           that the holder of the lock has changed, and so reports a new 
 
336
           that the holder of the lock has changed, and so reports a new
328
337
           lock holder.
329
338
        5) Thread1 blocks on the 'checked' lock, this time, it completely
330
339
           unlocks the lockdir, allowing Lock2 to acquire the lock.
331
340
        """
332
341
 
 
342
        raise tests.KnownFailure(
 
343
            "timing dependency in lock tests (#213182)")
 
344
 
333
345
        wait_to_check_lock = Lock()
334
346
        wait_until_checked_lock = Lock()
335
347
 
403
415
        # There should be 2 reports, because the lock changed
404
416
        lock_base = lf2.transport.abspath(lf2.path)
405
417
        self.assertEqual(2, len(self._logged_reports))
406
 
 
 
418
        lock_url = lf2.transport.abspath(lf2.path)
407
419
        self.assertEqual('%s %s\n'
408
420
                         '%s\n%s\n'
409
 
                         'Will continue to try until %s\n',
 
421
                         'Will continue to try until %s, unless '
 
422
                         'you press Ctrl-C.\n'
 
423
                         'See "bzr help break-lock" for more.',
410
424
                         self._logged_reports[0][0])
411
425
        args = self._logged_reports[0][1]
412
426
        self.assertEqual('Unable to obtain', args[0])
418
432
 
419
433
        self.assertEqual('%s %s\n'
420
434
                         '%s\n%s\n'
421
 
                         'Will continue to try until %s\n',
 
435
                         'Will continue to try until %s, unless '
 
436
                         'you press Ctrl-C.\n'
 
437
                         'See "bzr help break-lock" for more.',
422
438
                         self._logged_reports[1][0])
423
439
        args = self._logged_reports[1][1]
424
440
        self.assertEqual('Lock owner changed for', args[0])
434
450
        lf1 = LockDir(t, 'test_lock')
435
451
        lf1.create()
436
452
        lf1.attempt_lock()
 
453
        self.addCleanup(lf1.unlock)
437
454
        lf1.confirm()
438
455
 
439
456
    def test_41_confirm_not_held(self):
451
468
        lf1.attempt_lock()
452
469
        t.move('test_lock', 'lock_gone_now')
453
470
        self.assertRaises(LockBroken, lf1.confirm)
 
471
        # Clean up
 
472
        t.move('lock_gone_now', 'test_lock')
 
473
        lf1.unlock()
454
474
 
455
475
    def test_43_break(self):
456
476
        """Break a lock whose caller has forgotten it"""
467
487
        lf2.force_break(holder_info)
468
488
        # now we should be able to take it
469
489
        lf2.attempt_lock()
 
490
        self.addCleanup(lf2.unlock)
470
491
        lf2.confirm()
471
492
 
472
493
    def test_44_break_already_released(self):
484
505
        lf2.force_break(holder_info)
485
506
        # now we should be able to take it
486
507
        lf2.attempt_lock()
 
508
        self.addCleanup(lf2.unlock)
487
509
        lf2.confirm()
488
510
 
489
511
    def test_45_break_mismatch(self):
515
537
        """Check the on-disk representation of LockDirs is as expected.
516
538
 
517
539
        There should always be a top-level directory named by the lock.
518
 
        When the lock is held, there should be a lockname/held directory 
 
540
        When the lock is held, there should be a lockname/held directory
519
541
        containing an info file.
520
542
        """
521
543
        t = self.get_transport()
536
558
        # do this without IO redirection to ensure it doesn't prompt.
537
559
        self.assertRaises(AssertionError, ld1.break_lock)
538
560
        orig_factory = bzrlib.ui.ui_factory
539
 
        # silent ui - no need for stdout
540
 
        bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
541
 
        bzrlib.ui.ui_factory.stdin = StringIO("y\n")
 
561
        bzrlib.ui.ui_factory = bzrlib.ui.CannedInputUIFactory([True])
542
562
        try:
543
563
            ld2.break_lock()
544
564
            self.assertRaises(LockBroken, ld1.unlock)
599
619
        lock_path = ld1.transport.local_abspath('test_lock')
600
620
        os.mkdir(lock_path)
601
621
        osutils.make_readonly(lock_path)
602
 
        self.assertRaises(errors.PermissionDenied, ld1.attempt_lock)
 
622
        self.assertRaises(errors.LockFailed, ld1.attempt_lock)
603
623
 
604
624
    def test_lock_by_token(self):
605
625
        ld1 = self.get_lock()
606
626
        token = ld1.lock_write()
 
627
        self.addCleanup(ld1.unlock)
607
628
        self.assertNotEqual(None, token)
608
629
        ld2 = self.get_lock()
609
630
        t2 = ld2.lock_write(token)
 
631
        self.addCleanup(ld2.unlock)
610
632
        self.assertEqual(token, t2)
611
633
 
612
634
    def test_lock_with_buggy_rename(self):
637
659
        check_dir([])
638
660
        # when held, that's all we see
639
661
        ld1.attempt_lock()
 
662
        self.addCleanup(ld1.unlock)
640
663
        check_dir(['held'])
641
664
        # second guy should fail
642
665
        self.assertRaises(errors.LockContention, ld2.attempt_lock)
643
666
        # no kibble
644
667
        check_dir(['held'])
 
668
 
 
669
 
 
670
class TestLockDirHooks(TestCaseWithTransport):
 
671
 
 
672
    def setUp(self):
 
673
        super(TestLockDirHooks, self).setUp()
 
674
        self._calls = []
 
675
 
 
676
    def get_lock(self):
 
677
        return LockDir(self.get_transport(), 'test_lock')
 
678
 
 
679
    def record_hook(self, result):
 
680
        self._calls.append(result)
 
681
 
 
682
    def test_LockDir_acquired_success(self):
 
683
        # the LockDir.lock_acquired hook fires when a lock is acquired.
 
684
        LockDir.hooks.install_named_hook('lock_acquired',
 
685
                                         self.record_hook, 'record_hook')
 
686
        ld = self.get_lock()
 
687
        ld.create()
 
688
        self.assertEqual([], self._calls)
 
689
        result = ld.attempt_lock()
 
690
        lock_path = ld.transport.abspath(ld.path)
 
691
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
 
692
        ld.unlock()
 
693
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
 
694
 
 
695
    def test_LockDir_acquired_fail(self):
 
696
        # the LockDir.lock_acquired hook does not fire on failure.
 
697
        ld = self.get_lock()
 
698
        ld.create()
 
699
        ld2 = self.get_lock()
 
700
        ld2.attempt_lock()
 
701
        # install a lock hook now, when the disk lock is locked
 
702
        LockDir.hooks.install_named_hook('lock_acquired',
 
703
                                         self.record_hook, 'record_hook')
 
704
        self.assertRaises(errors.LockContention, ld.attempt_lock)
 
705
        self.assertEqual([], self._calls)
 
706
        ld2.unlock()
 
707
        self.assertEqual([], self._calls)
 
708
 
 
709
    def test_LockDir_released_success(self):
 
710
        # the LockDir.lock_released hook fires when a lock is acquired.
 
711
        LockDir.hooks.install_named_hook('lock_released',
 
712
                                         self.record_hook, 'record_hook')
 
713
        ld = self.get_lock()
 
714
        ld.create()
 
715
        self.assertEqual([], self._calls)
 
716
        result = ld.attempt_lock()
 
717
        self.assertEqual([], self._calls)
 
718
        ld.unlock()
 
719
        lock_path = ld.transport.abspath(ld.path)
 
720
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
 
721
 
 
722
    def test_LockDir_released_fail(self):
 
723
        # the LockDir.lock_released hook does not fire on failure.
 
724
        ld = self.get_lock()
 
725
        ld.create()
 
726
        ld2 = self.get_lock()
 
727
        ld.attempt_lock()
 
728
        ld2.force_break(ld2.peek())
 
729
        LockDir.hooks.install_named_hook('lock_released',
 
730
                                         self.record_hook, 'record_hook')
 
731
        self.assertRaises(LockBroken, ld.unlock)
 
732
        self.assertEqual([], self._calls)
 
733
 
 
734
    def test_LockDir_broken_success(self):
 
735
        # the LockDir.lock_broken hook fires when a lock is broken.
 
736
        ld = self.get_lock()
 
737
        ld.create()
 
738
        ld2 = self.get_lock()
 
739
        result = ld.attempt_lock()
 
740
        LockDir.hooks.install_named_hook('lock_broken',
 
741
                                         self.record_hook, 'record_hook')
 
742
        ld2.force_break(ld2.peek())
 
743
        lock_path = ld.transport.abspath(ld.path)
 
744
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
 
745
 
 
746
    def test_LockDir_broken_failure(self):
 
747
        # the LockDir.lock_broken hook does not fires when a lock is already
 
748
        # released.
 
749
        ld = self.get_lock()
 
750
        ld.create()
 
751
        ld2 = self.get_lock()
 
752
        result = ld.attempt_lock()
 
753
        holder_info = ld2.peek()
 
754
        ld.unlock()
 
755
        LockDir.hooks.install_named_hook('lock_broken',
 
756
                                         self.record_hook, 'record_hook')
 
757
        ld2.force_break(holder_info)
 
758
        lock_path = ld.transport.abspath(ld.path)
 
759
        self.assertEqual([], self._calls)