~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_lockdir.py

  • Committer: Danny van Heumen
  • Date: 2010-03-09 21:42:11 UTC
  • mto: (4634.139.5 2.0)
  • mto: This revision was merged to the branch mainline in revision 5160.
  • Revision ID: danny@dannyvanheumen.nl-20100309214211-iqh42x6qcikgd9p3
Reverted now-useless TODO list.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008, 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,
125
126
        lf1.attempt_lock()
126
127
        lf2 = LockDir(t, 'test_lock')
127
128
        try:
128
 
            # locking is between LockDir instances; aliases within 
 
129
            # locking is between LockDir instances; aliases within
129
130
            # a single process are not detected
130
131
            lf2.attempt_lock()
131
132
            self.fail('Failed to detect lock collision')
141
142
        lf1 = LockDir(t, 'test_lock')
142
143
        lf1.create()
143
144
        lf1.attempt_lock()
 
145
        self.addCleanup(lf1.unlock)
144
146
        # lock is held, should get some info on it
145
147
        info1 = lf1.peek()
146
148
        self.assertEqual(set(info1.keys()),
160
162
        lf2 = LockDir(self.get_readonly_transport(), 'test_lock')
161
163
        self.assertEqual(lf2.peek(), None)
162
164
        lf1.attempt_lock()
 
165
        self.addCleanup(lf1.unlock)
163
166
        info2 = lf2.peek()
164
167
        self.assertTrue(info2)
165
168
        self.assertEqual(info2['nonce'], lf1.nonce)
166
169
 
167
170
    def test_30_lock_wait_fail(self):
168
171
        """Wait on a lock, then fail
169
 
        
 
172
 
170
173
        We ask to wait up to 400ms; this should fail within at most one
171
174
        second.  (Longer times are more realistic but we don't want the test
172
175
        suite to take too long, and this should do for now.)
184
187
            after = time.time()
185
188
            # it should only take about 0.4 seconds, but we allow more time in
186
189
            # case the machine is heavily loaded
187
 
            self.assertTrue(after - before <= 8.0, 
 
190
            self.assertTrue(after - before <= 8.0,
188
191
                    "took %f seconds to detect lock contention" % (after - before))
189
192
        finally:
190
193
            lf1.unlock()
225
228
    def test_32_lock_wait_succeed(self):
226
229
        """Succeed when trying to acquire a lock that gets released
227
230
 
228
 
        One thread holds on a lock and then releases it; another 
 
231
        One thread holds on a lock and then releases it; another
229
232
        tries to lock it.
230
233
        """
231
234
        # This test sometimes fails like this:
276
279
        self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
277
280
 
278
281
    def test_34_lock_write_waits(self):
279
 
        """LockDir.lock_write() will wait for the lock.""" 
 
282
        """LockDir.lock_write() will wait for the lock."""
280
283
        # the test suite sets the default to 0 to make deadlocks fail fast.
281
284
        # change it for this test, as we want to try a manual deadlock.
282
285
        raise tests.TestSkipped('Timing-sensitive test')
319
322
 
320
323
    def test_35_wait_lock_changing(self):
321
324
        """LockDir.wait_lock() will report if the lock changes underneath.
322
 
        
 
325
 
323
326
        This is the stages we want to happen:
324
327
 
325
328
        0) Synchronization locks are created and locked.
326
329
        1) Lock1 obtains the lockdir, and releases the 'check' lock.
327
330
        2) Lock2 grabs the 'check' lock, and checks the lockdir.
328
 
           It sees the lockdir is already acquired, reports the fact, 
 
331
           It sees the lockdir is already acquired, reports the fact,
329
332
           and unsets the 'checked' lock.
330
333
        3) Thread1 blocks on acquiring the 'checked' lock, and then tells
331
334
           Lock1 to release and acquire the lockdir. This resets the 'check'
332
335
           lock.
333
336
        4) Lock2 acquires the 'check' lock, and checks again. It notices
334
 
           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
335
338
           lock holder.
336
339
        5) Thread1 blocks on the 'checked' lock, this time, it completely
337
340
           unlocks the lockdir, allowing Lock2 to acquire the lock.
450
453
        lf1 = LockDir(t, 'test_lock')
451
454
        lf1.create()
452
455
        lf1.attempt_lock()
 
456
        self.addCleanup(lf1.unlock)
453
457
        lf1.confirm()
454
458
 
455
459
    def test_41_confirm_not_held(self):
467
471
        lf1.attempt_lock()
468
472
        t.move('test_lock', 'lock_gone_now')
469
473
        self.assertRaises(LockBroken, lf1.confirm)
 
474
        # Clean up
 
475
        t.move('lock_gone_now', 'test_lock')
 
476
        lf1.unlock()
470
477
 
471
478
    def test_43_break(self):
472
479
        """Break a lock whose caller has forgotten it"""
483
490
        lf2.force_break(holder_info)
484
491
        # now we should be able to take it
485
492
        lf2.attempt_lock()
 
493
        self.addCleanup(lf2.unlock)
486
494
        lf2.confirm()
487
495
 
488
496
    def test_44_break_already_released(self):
500
508
        lf2.force_break(holder_info)
501
509
        # now we should be able to take it
502
510
        lf2.attempt_lock()
 
511
        self.addCleanup(lf2.unlock)
503
512
        lf2.confirm()
504
513
 
505
514
    def test_45_break_mismatch(self):
531
540
        """Check the on-disk representation of LockDirs is as expected.
532
541
 
533
542
        There should always be a top-level directory named by the lock.
534
 
        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
535
544
        containing an info file.
536
545
        """
537
546
        t = self.get_transport()
552
561
        # do this without IO redirection to ensure it doesn't prompt.
553
562
        self.assertRaises(AssertionError, ld1.break_lock)
554
563
        orig_factory = bzrlib.ui.ui_factory
555
 
        # silent ui - no need for stdout
556
 
        bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
557
 
        bzrlib.ui.ui_factory.stdin = StringIO("y\n")
 
564
        bzrlib.ui.ui_factory = bzrlib.ui.CannedInputUIFactory([True])
558
565
        try:
559
566
            ld2.break_lock()
560
567
            self.assertRaises(LockBroken, ld1.unlock)
620
627
    def test_lock_by_token(self):
621
628
        ld1 = self.get_lock()
622
629
        token = ld1.lock_write()
 
630
        self.addCleanup(ld1.unlock)
623
631
        self.assertNotEqual(None, token)
624
632
        ld2 = self.get_lock()
625
633
        t2 = ld2.lock_write(token)
 
634
        self.addCleanup(ld2.unlock)
626
635
        self.assertEqual(token, t2)
627
636
 
628
637
    def test_lock_with_buggy_rename(self):
653
662
        check_dir([])
654
663
        # when held, that's all we see
655
664
        ld1.attempt_lock()
 
665
        self.addCleanup(ld1.unlock)
656
666
        check_dir(['held'])
657
667
        # second guy should fail
658
668
        self.assertRaises(errors.LockContention, ld2.attempt_lock)
659
669
        # no kibble
660
670
        check_dir(['held'])
 
671
 
 
672
    def test_no_lockdir_info(self):
 
673
        """We can cope with empty info files."""
 
674
        # This seems like a fairly common failure case - see
 
675
        # <https://bugs.edge.launchpad.net/bzr/+bug/185103> and all its dupes.
 
676
        # Processes are often interrupted after opening the file
 
677
        # before the actual contents are committed.
 
678
        t = self.get_transport()
 
679
        t.mkdir('test_lock')
 
680
        t.mkdir('test_lock/held')
 
681
        t.put_bytes('test_lock/held/info', '')
 
682
        lf = LockDir(t, 'test_lock')
 
683
        info = lf.peek()
 
684
        formatted_info = lf._format_lock_info(info)
 
685
        self.assertEquals(
 
686
            ['lock %s' % t.abspath('test_lock'),
 
687
             'held by <unknown> on host <unknown> [process #<unknown>]',
 
688
             'locked (unknown)'],
 
689
            formatted_info)
 
690
 
 
691
 
 
692
class TestLockDirHooks(TestCaseWithTransport):
 
693
 
 
694
    def setUp(self):
 
695
        super(TestLockDirHooks, self).setUp()
 
696
        self._calls = []
 
697
 
 
698
    def get_lock(self):
 
699
        return LockDir(self.get_transport(), 'test_lock')
 
700
 
 
701
    def record_hook(self, result):
 
702
        self._calls.append(result)
 
703
 
 
704
    def test_LockDir_acquired_success(self):
 
705
        # the LockDir.lock_acquired hook fires when a lock is acquired.
 
706
        LockDir.hooks.install_named_hook('lock_acquired',
 
707
                                         self.record_hook, 'record_hook')
 
708
        ld = self.get_lock()
 
709
        ld.create()
 
710
        self.assertEqual([], self._calls)
 
711
        result = ld.attempt_lock()
 
712
        lock_path = ld.transport.abspath(ld.path)
 
713
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
 
714
        ld.unlock()
 
715
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
 
716
 
 
717
    def test_LockDir_acquired_fail(self):
 
718
        # the LockDir.lock_acquired hook does not fire on failure.
 
719
        ld = self.get_lock()
 
720
        ld.create()
 
721
        ld2 = self.get_lock()
 
722
        ld2.attempt_lock()
 
723
        # install a lock hook now, when the disk lock is locked
 
724
        LockDir.hooks.install_named_hook('lock_acquired',
 
725
                                         self.record_hook, 'record_hook')
 
726
        self.assertRaises(errors.LockContention, ld.attempt_lock)
 
727
        self.assertEqual([], self._calls)
 
728
        ld2.unlock()
 
729
        self.assertEqual([], self._calls)
 
730
 
 
731
    def test_LockDir_released_success(self):
 
732
        # the LockDir.lock_released hook fires when a lock is acquired.
 
733
        LockDir.hooks.install_named_hook('lock_released',
 
734
                                         self.record_hook, 'record_hook')
 
735
        ld = self.get_lock()
 
736
        ld.create()
 
737
        self.assertEqual([], self._calls)
 
738
        result = ld.attempt_lock()
 
739
        self.assertEqual([], self._calls)
 
740
        ld.unlock()
 
741
        lock_path = ld.transport.abspath(ld.path)
 
742
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
 
743
 
 
744
    def test_LockDir_released_fail(self):
 
745
        # the LockDir.lock_released hook does not fire on failure.
 
746
        ld = self.get_lock()
 
747
        ld.create()
 
748
        ld2 = self.get_lock()
 
749
        ld.attempt_lock()
 
750
        ld2.force_break(ld2.peek())
 
751
        LockDir.hooks.install_named_hook('lock_released',
 
752
                                         self.record_hook, 'record_hook')
 
753
        self.assertRaises(LockBroken, ld.unlock)
 
754
        self.assertEqual([], self._calls)
 
755
 
 
756
    def test_LockDir_broken_success(self):
 
757
        # the LockDir.lock_broken hook fires when a lock is broken.
 
758
        ld = self.get_lock()
 
759
        ld.create()
 
760
        ld2 = self.get_lock()
 
761
        result = ld.attempt_lock()
 
762
        LockDir.hooks.install_named_hook('lock_broken',
 
763
                                         self.record_hook, 'record_hook')
 
764
        ld2.force_break(ld2.peek())
 
765
        lock_path = ld.transport.abspath(ld.path)
 
766
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
 
767
 
 
768
    def test_LockDir_broken_failure(self):
 
769
        # the LockDir.lock_broken hook does not fires when a lock is already
 
770
        # released.
 
771
        ld = self.get_lock()
 
772
        ld.create()
 
773
        ld2 = self.get_lock()
 
774
        result = ld.attempt_lock()
 
775
        holder_info = ld2.peek()
 
776
        ld.unlock()
 
777
        LockDir.hooks.install_named_hook('lock_broken',
 
778
                                         self.record_hook, 'record_hook')
 
779
        ld2.force_break(holder_info)
 
780
        lock_path = ld.transport.abspath(ld.path)
 
781
        self.assertEqual([], self._calls)