~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: 2010-07-07 21:30:06 UTC
  • mfrom: (5333.1.2 better_pyqt_include)
  • Revision ID: pqm@pqm.ubuntu.com-20100707213006-lriphkkbzwwrl7ne
(jameinel) Use a better list of PyQt includes and excludes. (Gary van der
 Merwe)

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
 
        lock_base = lf2.transport.abspath(lf2.path)
189
194
        self.assertEqual(1, len(self._logged_reports))
190
 
        self.assertEqual('%s %s\n'
191
 
                         '%s\n%s\n'
192
 
                         'Will continue to try until %s\n',
193
 
                         self._logged_reports[0][0])
194
 
        args = self._logged_reports[0][1]
195
 
        self.assertEqual('Unable to obtain', args[0])
196
 
        self.assertEqual('lock %s' % (lock_base,), args[1])
197
 
        self.assertStartsWith(args[2], 'held by ')
198
 
        self.assertStartsWith(args[3], 'locked ')
199
 
        self.assertEndsWith(args[3], ' ago')
200
 
        self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
 
195
        self.assertEqual(self._logged_reports[0][0],
 
196
            '%s lock %s held by %s\n'
 
197
            'at %s [process #%s], acquired %s.\n'
 
198
            'Will continue to try until %s, unless '
 
199
            'you press Ctrl-C.\n'
 
200
            'See "bzr help break-lock" for more.')
 
201
        start, lock_url, user, hostname, pid, time_ago, deadline_str = \
 
202
            self._logged_reports[0][1]
 
203
        self.assertEqual(start, u'Unable to obtain')
 
204
        self.assertEqual(user, u'jrandom@example.com')
 
205
        # skip hostname
 
206
        self.assertContainsRe(pid, r'\d+')
 
207
        self.assertContainsRe(time_ago, r'.* ago')
 
208
        self.assertContainsRe(deadline_str, r'\d{2}:\d{2}:\d{2}')
201
209
 
202
210
    def test_31_lock_wait_easy(self):
203
211
        """Succeed when waiting on a lock with no contention.
218
226
    def test_32_lock_wait_succeed(self):
219
227
        """Succeed when trying to acquire a lock that gets released
220
228
 
221
 
        One thread holds on a lock and then releases it; another 
 
229
        One thread holds on a lock and then releases it; another
222
230
        tries to lock it.
223
231
        """
224
232
        # This test sometimes fails like this:
269
277
        self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
270
278
 
271
279
    def test_34_lock_write_waits(self):
272
 
        """LockDir.lock_write() will wait for the lock.""" 
 
280
        """LockDir.lock_write() will wait for the lock."""
273
281
        # the test suite sets the default to 0 to make deadlocks fail fast.
274
282
        # change it for this test, as we want to try a manual deadlock.
 
283
        raise tests.TestSkipped('Timing-sensitive test')
275
284
        bzrlib.lockdir._DEFAULT_TIMEOUT_SECONDS = 300
276
285
        t = self.get_transport()
277
286
        lf1 = LockDir(t, 'test_lock')
311
320
 
312
321
    def test_35_wait_lock_changing(self):
313
322
        """LockDir.wait_lock() will report if the lock changes underneath.
314
 
        
 
323
 
315
324
        This is the stages we want to happen:
316
325
 
317
326
        0) Synchronization locks are created and locked.
318
327
        1) Lock1 obtains the lockdir, and releases the 'check' lock.
319
328
        2) Lock2 grabs the 'check' lock, and checks the lockdir.
320
 
           It sees the lockdir is already acquired, reports the fact, 
 
329
           It sees the lockdir is already acquired, reports the fact,
321
330
           and unsets the 'checked' lock.
322
331
        3) Thread1 blocks on acquiring the 'checked' lock, and then tells
323
332
           Lock1 to release and acquire the lockdir. This resets the 'check'
324
333
           lock.
325
334
        4) Lock2 acquires the 'check' lock, and checks again. It notices
326
 
           that the holder of the lock has changed, and so reports a new 
 
335
           that the holder of the lock has changed, and so reports a new
327
336
           lock holder.
328
337
        5) Thread1 blocks on the 'checked' lock, this time, it completely
329
338
           unlocks the lockdir, allowing Lock2 to acquire the lock.
330
339
        """
331
340
 
 
341
        raise tests.KnownFailure(
 
342
            "timing dependency in lock tests (#213182)")
 
343
 
332
344
        wait_to_check_lock = Lock()
333
345
        wait_until_checked_lock = Lock()
334
346
 
394
406
        unlocker.start()
395
407
        try:
396
408
            # Wait and play against the other thread
397
 
            lf2.wait_lock(timeout=1.0, poll=0.01)
 
409
            lf2.wait_lock(timeout=20.0, poll=0.01)
398
410
        finally:
399
411
            unlocker.join()
400
412
        lf2.unlock()
402
414
        # There should be 2 reports, because the lock changed
403
415
        lock_base = lf2.transport.abspath(lf2.path)
404
416
        self.assertEqual(2, len(self._logged_reports))
405
 
 
 
417
        lock_url = lf2.transport.abspath(lf2.path)
406
418
        self.assertEqual('%s %s\n'
407
419
                         '%s\n%s\n'
408
 
                         'Will continue to try until %s\n',
 
420
                         'Will continue to try until %s, unless '
 
421
                         'you press Ctrl-C.\n'
 
422
                         'See "bzr help break-lock" for more.',
409
423
                         self._logged_reports[0][0])
410
424
        args = self._logged_reports[0][1]
411
425
        self.assertEqual('Unable to obtain', args[0])
417
431
 
418
432
        self.assertEqual('%s %s\n'
419
433
                         '%s\n%s\n'
420
 
                         'Will continue to try until %s\n',
 
434
                         'Will continue to try until %s, unless '
 
435
                         'you press Ctrl-C.\n'
 
436
                         'See "bzr help break-lock" for more.',
421
437
                         self._logged_reports[1][0])
422
438
        args = self._logged_reports[1][1]
423
439
        self.assertEqual('Lock owner changed for', args[0])
433
449
        lf1 = LockDir(t, 'test_lock')
434
450
        lf1.create()
435
451
        lf1.attempt_lock()
 
452
        self.addCleanup(lf1.unlock)
436
453
        lf1.confirm()
437
454
 
438
455
    def test_41_confirm_not_held(self):
450
467
        lf1.attempt_lock()
451
468
        t.move('test_lock', 'lock_gone_now')
452
469
        self.assertRaises(LockBroken, lf1.confirm)
 
470
        # Clean up
 
471
        t.move('lock_gone_now', 'test_lock')
 
472
        lf1.unlock()
453
473
 
454
474
    def test_43_break(self):
455
475
        """Break a lock whose caller has forgotten it"""
466
486
        lf2.force_break(holder_info)
467
487
        # now we should be able to take it
468
488
        lf2.attempt_lock()
 
489
        self.addCleanup(lf2.unlock)
469
490
        lf2.confirm()
470
491
 
471
492
    def test_44_break_already_released(self):
483
504
        lf2.force_break(holder_info)
484
505
        # now we should be able to take it
485
506
        lf2.attempt_lock()
 
507
        self.addCleanup(lf2.unlock)
486
508
        lf2.confirm()
487
509
 
488
510
    def test_45_break_mismatch(self):
514
536
        """Check the on-disk representation of LockDirs is as expected.
515
537
 
516
538
        There should always be a top-level directory named by the lock.
517
 
        When the lock is held, there should be a lockname/held directory 
 
539
        When the lock is held, there should be a lockname/held directory
518
540
        containing an info file.
519
541
        """
520
542
        t = self.get_transport()
535
557
        # do this without IO redirection to ensure it doesn't prompt.
536
558
        self.assertRaises(AssertionError, ld1.break_lock)
537
559
        orig_factory = bzrlib.ui.ui_factory
538
 
        # silent ui - no need for stdout
539
 
        bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
540
 
        bzrlib.ui.ui_factory.stdin = StringIO("y\n")
 
560
        bzrlib.ui.ui_factory = bzrlib.ui.CannedInputUIFactory([True])
541
561
        try:
542
562
            ld2.break_lock()
543
563
            self.assertRaises(LockBroken, ld1.unlock)
576
596
            info_list = ld1._format_lock_info(ld1.peek())
577
597
        finally:
578
598
            ld1.unlock()
579
 
        self.assertEqual('lock %s' % (ld1.transport.abspath(ld1.path),),
580
 
                         info_list[0])
581
 
        self.assertContainsRe(info_list[1],
582
 
                              r'^held by .* on host .* \[process #\d*\]$')
583
 
        self.assertContainsRe(info_list[2], r'locked \d+ seconds? ago$')
 
599
        self.assertEqual(info_list[0], u'jrandom@example.com')
 
600
        # info_list[1] is hostname. we skip this.
 
601
        self.assertContainsRe(info_list[2], '^\d+$') # pid
 
602
        self.assertContainsRe(info_list[3], r'^\d+ seconds? ago$') # time_ago
584
603
 
585
604
    def test_lock_without_email(self):
586
605
        global_config = config.GlobalConfig()
598
617
        lock_path = ld1.transport.local_abspath('test_lock')
599
618
        os.mkdir(lock_path)
600
619
        osutils.make_readonly(lock_path)
601
 
        self.assertRaises(errors.PermissionDenied, ld1.attempt_lock)
 
620
        self.assertRaises(errors.LockFailed, ld1.attempt_lock)
602
621
 
603
622
    def test_lock_by_token(self):
604
623
        ld1 = self.get_lock()
605
624
        token = ld1.lock_write()
 
625
        self.addCleanup(ld1.unlock)
606
626
        self.assertNotEqual(None, token)
607
627
        ld2 = self.get_lock()
608
628
        t2 = ld2.lock_write(token)
 
629
        self.addCleanup(ld2.unlock)
609
630
        self.assertEqual(token, t2)
610
631
 
611
632
    def test_lock_with_buggy_rename(self):
636
657
        check_dir([])
637
658
        # when held, that's all we see
638
659
        ld1.attempt_lock()
 
660
        self.addCleanup(ld1.unlock)
639
661
        check_dir(['held'])
640
662
        # second guy should fail
641
663
        self.assertRaises(errors.LockContention, ld2.attempt_lock)
642
664
        # no kibble
643
665
        check_dir(['held'])
 
666
 
 
667
    def test_no_lockdir_info(self):
 
668
        """We can cope with empty info files."""
 
669
        # This seems like a fairly common failure case - see
 
670
        # <https://bugs.launchpad.net/bzr/+bug/185103> and all its dupes.
 
671
        # Processes are often interrupted after opening the file
 
672
        # before the actual contents are committed.
 
673
        t = self.get_transport()
 
674
        t.mkdir('test_lock')
 
675
        t.mkdir('test_lock/held')
 
676
        t.put_bytes('test_lock/held/info', '')
 
677
        lf = LockDir(t, 'test_lock')
 
678
        info = lf.peek()
 
679
        formatted_info = lf._format_lock_info(info)
 
680
        self.assertEquals(
 
681
            ['<unknown>', '<unknown>', '<unknown>', '(unknown)'],
 
682
            formatted_info)
 
683
 
 
684
 
 
685
class TestLockDirHooks(TestCaseWithTransport):
 
686
 
 
687
    def setUp(self):
 
688
        super(TestLockDirHooks, self).setUp()
 
689
        self._calls = []
 
690
 
 
691
    def get_lock(self):
 
692
        return LockDir(self.get_transport(), 'test_lock')
 
693
 
 
694
    def record_hook(self, result):
 
695
        self._calls.append(result)
 
696
 
 
697
    def test_LockDir_acquired_success(self):
 
698
        # the LockDir.lock_acquired hook fires when a lock is acquired.
 
699
        LockDir.hooks.install_named_hook('lock_acquired',
 
700
                                         self.record_hook, 'record_hook')
 
701
        ld = self.get_lock()
 
702
        ld.create()
 
703
        self.assertEqual([], self._calls)
 
704
        result = ld.attempt_lock()
 
705
        lock_path = ld.transport.abspath(ld.path)
 
706
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
 
707
        ld.unlock()
 
708
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
 
709
 
 
710
    def test_LockDir_acquired_fail(self):
 
711
        # the LockDir.lock_acquired hook does not fire on failure.
 
712
        ld = self.get_lock()
 
713
        ld.create()
 
714
        ld2 = self.get_lock()
 
715
        ld2.attempt_lock()
 
716
        # install a lock hook now, when the disk lock is locked
 
717
        LockDir.hooks.install_named_hook('lock_acquired',
 
718
                                         self.record_hook, 'record_hook')
 
719
        self.assertRaises(errors.LockContention, ld.attempt_lock)
 
720
        self.assertEqual([], self._calls)
 
721
        ld2.unlock()
 
722
        self.assertEqual([], self._calls)
 
723
 
 
724
    def test_LockDir_released_success(self):
 
725
        # the LockDir.lock_released hook fires when a lock is acquired.
 
726
        LockDir.hooks.install_named_hook('lock_released',
 
727
                                         self.record_hook, 'record_hook')
 
728
        ld = self.get_lock()
 
729
        ld.create()
 
730
        self.assertEqual([], self._calls)
 
731
        result = ld.attempt_lock()
 
732
        self.assertEqual([], self._calls)
 
733
        ld.unlock()
 
734
        lock_path = ld.transport.abspath(ld.path)
 
735
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
 
736
 
 
737
    def test_LockDir_released_fail(self):
 
738
        # the LockDir.lock_released hook does not fire on failure.
 
739
        ld = self.get_lock()
 
740
        ld.create()
 
741
        ld2 = self.get_lock()
 
742
        ld.attempt_lock()
 
743
        ld2.force_break(ld2.peek())
 
744
        LockDir.hooks.install_named_hook('lock_released',
 
745
                                         self.record_hook, 'record_hook')
 
746
        self.assertRaises(LockBroken, ld.unlock)
 
747
        self.assertEqual([], self._calls)
 
748
 
 
749
    def test_LockDir_broken_success(self):
 
750
        # the LockDir.lock_broken hook fires when a lock is broken.
 
751
        ld = self.get_lock()
 
752
        ld.create()
 
753
        ld2 = self.get_lock()
 
754
        result = ld.attempt_lock()
 
755
        LockDir.hooks.install_named_hook('lock_broken',
 
756
                                         self.record_hook, 'record_hook')
 
757
        ld2.force_break(ld2.peek())
 
758
        lock_path = ld.transport.abspath(ld.path)
 
759
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
 
760
 
 
761
    def test_LockDir_broken_failure(self):
 
762
        # the LockDir.lock_broken hook does not fires when a lock is already
 
763
        # released.
 
764
        ld = self.get_lock()
 
765
        ld.create()
 
766
        ld2 = self.get_lock()
 
767
        result = ld.attempt_lock()
 
768
        holder_info = ld2.peek()
 
769
        ld.unlock()
 
770
        LockDir.hooks.install_named_hook('lock_broken',
 
771
                                         self.record_hook, 'record_hook')
 
772
        ld2.force_break(holder_info)
 
773
        lock_path = ld.transport.abspath(ld.path)
 
774
        self.assertEqual([], self._calls)