~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: 2009-08-25 18:45:40 UTC
  • mto: (4634.6.15 2.0)
  • mto: This revision was merged to the branch mainline in revision 4667.
  • Revision ID: john@arbash-meinel.com-20090825184540-6dn3xjq62xhgj2gq
Add support for skipping ghost nodes.

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,
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
                         'If you\'re sure that it\'s not being '
 
202
                         'modified, use bzr break-lock %s',
193
203
                         self._logged_reports[0][0])
194
204
        args = self._logged_reports[0][1]
195
205
        self.assertEqual('Unable to obtain', args[0])
218
228
    def test_32_lock_wait_succeed(self):
219
229
        """Succeed when trying to acquire a lock that gets released
220
230
 
221
 
        One thread holds on a lock and then releases it; another 
 
231
        One thread holds on a lock and then releases it; another
222
232
        tries to lock it.
223
233
        """
224
234
        # This test sometimes fails like this:
269
279
        self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
270
280
 
271
281
    def test_34_lock_write_waits(self):
272
 
        """LockDir.lock_write() will wait for the lock.""" 
 
282
        """LockDir.lock_write() will wait for the lock."""
273
283
        # the test suite sets the default to 0 to make deadlocks fail fast.
274
284
        # change it for this test, as we want to try a manual deadlock.
275
285
        raise tests.TestSkipped('Timing-sensitive test')
312
322
 
313
323
    def test_35_wait_lock_changing(self):
314
324
        """LockDir.wait_lock() will report if the lock changes underneath.
315
 
        
 
325
 
316
326
        This is the stages we want to happen:
317
327
 
318
328
        0) Synchronization locks are created and locked.
319
329
        1) Lock1 obtains the lockdir, and releases the 'check' lock.
320
330
        2) Lock2 grabs the 'check' lock, and checks the lockdir.
321
 
           It sees the lockdir is already acquired, reports the fact, 
 
331
           It sees the lockdir is already acquired, reports the fact,
322
332
           and unsets the 'checked' lock.
323
333
        3) Thread1 blocks on acquiring the 'checked' lock, and then tells
324
334
           Lock1 to release and acquire the lockdir. This resets the 'check'
325
335
           lock.
326
336
        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 
 
337
           that the holder of the lock has changed, and so reports a new
328
338
           lock holder.
329
339
        5) Thread1 blocks on the 'checked' lock, this time, it completely
330
340
           unlocks the lockdir, allowing Lock2 to acquire the lock.
331
341
        """
332
342
 
 
343
        raise tests.KnownFailure(
 
344
            "timing dependency in lock tests (#213182)")
 
345
 
333
346
        wait_to_check_lock = Lock()
334
347
        wait_until_checked_lock = Lock()
335
348
 
403
416
        # There should be 2 reports, because the lock changed
404
417
        lock_base = lf2.transport.abspath(lf2.path)
405
418
        self.assertEqual(2, len(self._logged_reports))
406
 
 
 
419
        lock_url = lf2.transport.abspath(lf2.path)
407
420
        self.assertEqual('%s %s\n'
408
421
                         '%s\n%s\n'
409
 
                         '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',
410
426
                         self._logged_reports[0][0])
411
427
        args = self._logged_reports[0][1]
412
428
        self.assertEqual('Unable to obtain', args[0])
418
434
 
419
435
        self.assertEqual('%s %s\n'
420
436
                         '%s\n%s\n'
421
 
                         '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',
422
441
                         self._logged_reports[1][0])
423
442
        args = self._logged_reports[1][1]
424
443
        self.assertEqual('Lock owner changed for', args[0])
434
453
        lf1 = LockDir(t, 'test_lock')
435
454
        lf1.create()
436
455
        lf1.attempt_lock()
 
456
        self.addCleanup(lf1.unlock)
437
457
        lf1.confirm()
438
458
 
439
459
    def test_41_confirm_not_held(self):
451
471
        lf1.attempt_lock()
452
472
        t.move('test_lock', 'lock_gone_now')
453
473
        self.assertRaises(LockBroken, lf1.confirm)
 
474
        # Clean up
 
475
        t.move('lock_gone_now', 'test_lock')
 
476
        lf1.unlock()
454
477
 
455
478
    def test_43_break(self):
456
479
        """Break a lock whose caller has forgotten it"""
467
490
        lf2.force_break(holder_info)
468
491
        # now we should be able to take it
469
492
        lf2.attempt_lock()
 
493
        self.addCleanup(lf2.unlock)
470
494
        lf2.confirm()
471
495
 
472
496
    def test_44_break_already_released(self):
484
508
        lf2.force_break(holder_info)
485
509
        # now we should be able to take it
486
510
        lf2.attempt_lock()
 
511
        self.addCleanup(lf2.unlock)
487
512
        lf2.confirm()
488
513
 
489
514
    def test_45_break_mismatch(self):
515
540
        """Check the on-disk representation of LockDirs is as expected.
516
541
 
517
542
        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 
 
543
        When the lock is held, there should be a lockname/held directory
519
544
        containing an info file.
520
545
        """
521
546
        t = self.get_transport()
536
561
        # do this without IO redirection to ensure it doesn't prompt.
537
562
        self.assertRaises(AssertionError, ld1.break_lock)
538
563
        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")
 
564
        bzrlib.ui.ui_factory = bzrlib.ui.CannedInputUIFactory([True])
542
565
        try:
543
566
            ld2.break_lock()
544
567
            self.assertRaises(LockBroken, ld1.unlock)
599
622
        lock_path = ld1.transport.local_abspath('test_lock')
600
623
        os.mkdir(lock_path)
601
624
        osutils.make_readonly(lock_path)
602
 
        self.assertRaises(errors.PermissionDenied, ld1.attempt_lock)
 
625
        self.assertRaises(errors.LockFailed, ld1.attempt_lock)
603
626
 
604
627
    def test_lock_by_token(self):
605
628
        ld1 = self.get_lock()
606
629
        token = ld1.lock_write()
 
630
        self.addCleanup(ld1.unlock)
607
631
        self.assertNotEqual(None, token)
608
632
        ld2 = self.get_lock()
609
633
        t2 = ld2.lock_write(token)
 
634
        self.addCleanup(ld2.unlock)
610
635
        self.assertEqual(token, t2)
611
636
 
612
637
    def test_lock_with_buggy_rename(self):
637
662
        check_dir([])
638
663
        # when held, that's all we see
639
664
        ld1.attempt_lock()
 
665
        self.addCleanup(ld1.unlock)
640
666
        check_dir(['held'])
641
667
        # second guy should fail
642
668
        self.assertRaises(errors.LockContention, ld2.attempt_lock)
643
669
        # no kibble
644
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)