~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/test/test_lockfile.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2005 Divmod, Inc.
 
2
# Copyright (c) 2008 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
4
 
 
5
"""
 
6
Tests for L{twisted.python.lockfile}.
 
7
"""
 
8
 
 
9
import os, errno
 
10
 
 
11
from twisted.trial import unittest
 
12
from twisted.python import lockfile
 
13
from twisted.python.runtime import platform
 
14
 
 
15
skipKill = None
 
16
if platform.isWindows():
 
17
    try:
 
18
        from win32api import OpenProcess
 
19
        import pywintypes
 
20
    except ImportError:
 
21
        skipKill = ("On windows, lockfile.kill is not implemented in the "
 
22
                    "absence of win32api and/or pywintypes.")
 
23
 
 
24
class UtilTests(unittest.TestCase):
 
25
    """
 
26
    Tests for the helper functions used to implement L{FilesystemLock}.
 
27
    """
 
28
    def test_symlinkEEXIST(self):
 
29
        """
 
30
        L{lockfile.symlink} raises L{OSError} with C{errno} set to L{EEXIST}
 
31
        when an attempt is made to create a symlink which already exists.
 
32
        """
 
33
        name = self.mktemp()
 
34
        lockfile.symlink('foo', name)
 
35
        exc = self.assertRaises(OSError, lockfile.symlink, 'foo', name)
 
36
        self.assertEqual(exc.errno, errno.EEXIST)
 
37
 
 
38
 
 
39
    def test_symlinkEIOWindows(self):
 
40
        """
 
41
        L{lockfile.symlink} raises L{OSError} with C{errno} set to L{EIO} when
 
42
        the underlying L{rename} call fails with L{EIO}.
 
43
 
 
44
        Renaming a file on Windows may fail if the target of the rename is in
 
45
        the process of being deleted (directory deletion appears not to be
 
46
        atomic).
 
47
        """
 
48
        name = self.mktemp()
 
49
        def fakeRename(src, dst):
 
50
            raise IOError(errno.EIO, None)
 
51
        self.patch(lockfile, 'rename', fakeRename)
 
52
        exc = self.assertRaises(IOError, lockfile.symlink, name, "foo")
 
53
        self.assertEqual(exc.errno, errno.EIO)
 
54
    if not platform.isWindows():
 
55
        test_symlinkEIOWindows.skip = (
 
56
            "special rename EIO handling only necessary and correct on "
 
57
            "Windows.")
 
58
 
 
59
 
 
60
    def test_readlinkENOENT(self):
 
61
        """
 
62
        L{lockfile.readlink} raises L{OSError} with C{errno} set to L{ENOENT}
 
63
        when an attempt is made to read a symlink which does not exist.
 
64
        """
 
65
        name = self.mktemp()
 
66
        exc = self.assertRaises(OSError, lockfile.readlink, name)
 
67
        self.assertEqual(exc.errno, errno.ENOENT)
 
68
 
 
69
 
 
70
    def test_readlinkEACCESWindows(self):
 
71
        """
 
72
        L{lockfile.readlink} raises L{OSError} with C{errno} set to L{EACCES}
 
73
        on Windows when the underlying file open attempt fails with C{EACCES}.
 
74
 
 
75
        Opening a file on Windows may fail if the path is inside a directory
 
76
        which is in the process of being deleted (directory deletion appears
 
77
        not to be atomic).
 
78
        """
 
79
        name = self.mktemp()
 
80
        def fakeOpen(path, mode):
 
81
            raise IOError(errno.EACCES, None)
 
82
        self.patch(lockfile, '_open', fakeOpen)
 
83
        exc = self.assertRaises(IOError, lockfile.readlink, name)
 
84
        self.assertEqual(exc.errno, errno.EACCES)
 
85
    if not platform.isWindows():
 
86
        test_readlinkEACCESWindows.skip = (
 
87
            "special readlink EACCES handling only necessary and correct on "
 
88
            "Windows.")
 
89
 
 
90
 
 
91
    def test_kill(self):
 
92
        """
 
93
        L{lockfile.kill} returns without error if passed the PID of a
 
94
        process which exists and signal C{0}.
 
95
        """
 
96
        lockfile.kill(os.getpid(), 0)
 
97
    test_kill.skip = skipKill
 
98
 
 
99
 
 
100
    def test_killESRCH(self):
 
101
        """
 
102
        L{lockfile.kill} raises L{OSError} with errno of L{ESRCH} if
 
103
        passed a PID which does not correspond to any process.
 
104
        """
 
105
        # Hopefully there is no process with PID 2 ** 31 - 1
 
106
        exc = self.assertRaises(OSError, lockfile.kill, 2 ** 31 - 1, 0)
 
107
        self.assertEqual(exc.errno, errno.ESRCH)
 
108
    test_killESRCH.skip = skipKill
 
109
 
 
110
 
 
111
    def test_noKillCall(self):
 
112
        """
 
113
        Verify that when L{lockfile.kill} does end up as None (e.g. on Windows
 
114
        without pywin32), it doesn't end up being called and raising a
 
115
        L{TypeError}.
 
116
        """
 
117
        self.patch(lockfile, "kill", None)
 
118
        fl = lockfile.FilesystemLock(self.mktemp())
 
119
        fl.lock()
 
120
        self.assertFalse(fl.lock())
 
121
 
 
122
 
 
123
 
 
124
class LockingTestCase(unittest.TestCase):
 
125
    def _symlinkErrorTest(self, errno):
 
126
        def fakeSymlink(source, dest):
 
127
            raise OSError(errno, None)
 
128
        self.patch(lockfile, 'symlink', fakeSymlink)
 
129
 
 
130
        lockf = self.mktemp()
 
131
        lock = lockfile.FilesystemLock(lockf)
 
132
        exc = self.assertRaises(OSError, lock.lock)
 
133
        self.assertEqual(exc.errno, errno)
 
134
 
 
135
 
 
136
    def test_symlinkError(self):
 
137
        """
 
138
        An exception raised by C{symlink} other than C{EEXIST} is passed up to
 
139
        the caller of L{FilesystemLock.lock}.
 
140
        """
 
141
        self._symlinkErrorTest(errno.ENOSYS)
 
142
 
 
143
 
 
144
    def test_symlinkErrorPOSIX(self):
 
145
        """
 
146
        An L{OSError} raised by C{symlink} on a POSIX platform with an errno of
 
147
        C{EACCES} or C{EIO} is passed to the caller of L{FilesystemLock.lock}.
 
148
 
 
149
        On POSIX, unlike on Windows, these are unexpected errors which cannot
 
150
        be handled by L{FilesystemLock}.
 
151
        """
 
152
        self._symlinkErrorTest(errno.EACCES)
 
153
        self._symlinkErrorTest(errno.EIO)
 
154
    if platform.isWindows():
 
155
        test_symlinkErrorPOSIX.skip = (
 
156
            "POSIX-specific error propagation not expected on Windows.")
 
157
 
 
158
 
 
159
    def test_cleanlyAcquire(self):
 
160
        """
 
161
        If the lock has never been held, it can be acquired and the C{clean}
 
162
        and C{locked} attributes are set to C{True}.
 
163
        """
 
164
        lockf = self.mktemp()
 
165
        lock = lockfile.FilesystemLock(lockf)
 
166
        self.assertTrue(lock.lock())
 
167
        self.assertTrue(lock.clean)
 
168
        self.assertTrue(lock.locked)
 
169
 
 
170
 
 
171
    def test_cleanlyRelease(self):
 
172
        """
 
173
        If a lock is released cleanly, it can be re-acquired and the C{clean}
 
174
        and C{locked} attributes are set to C{True}.
 
175
        """
 
176
        lockf = self.mktemp()
 
177
        lock = lockfile.FilesystemLock(lockf)
 
178
        self.assertTrue(lock.lock())
 
179
        lock.unlock()
 
180
        self.assertFalse(lock.locked)
 
181
 
 
182
        lock = lockfile.FilesystemLock(lockf)
 
183
        self.assertTrue(lock.lock())
 
184
        self.assertTrue(lock.clean)
 
185
        self.assertTrue(lock.locked)
 
186
 
 
187
 
 
188
    def test_cannotLockLocked(self):
 
189
        """
 
190
        If a lock is currently locked, it cannot be locked again.
 
191
        """
 
192
        lockf = self.mktemp()
 
193
        firstLock = lockfile.FilesystemLock(lockf)
 
194
        self.assertTrue(firstLock.lock())
 
195
 
 
196
        secondLock = lockfile.FilesystemLock(lockf)
 
197
        self.assertFalse(secondLock.lock())
 
198
        self.assertFalse(secondLock.locked)
 
199
 
 
200
 
 
201
    def test_uncleanlyAcquire(self):
 
202
        """
 
203
        If a lock was held by a process which no longer exists, it can be
 
204
        acquired, the C{clean} attribute is set to C{False}, and the
 
205
        C{locked} attribute is set to C{True}.
 
206
        """
 
207
        owner = 12345
 
208
 
 
209
        def fakeKill(pid, signal):
 
210
            if signal != 0:
 
211
                raise OSError(errno.EPERM, None)
 
212
            if pid == owner:
 
213
                raise OSError(errno.ESRCH, None)
 
214
 
 
215
        lockf = self.mktemp()
 
216
        self.patch(lockfile, 'kill', fakeKill)
 
217
        lockfile.symlink(str(owner), lockf)
 
218
 
 
219
        lock = lockfile.FilesystemLock(lockf)
 
220
        self.assertTrue(lock.lock())
 
221
        self.assertFalse(lock.clean)
 
222
        self.assertTrue(lock.locked)
 
223
 
 
224
        self.assertEqual(lockfile.readlink(lockf), str(os.getpid()))
 
225
 
 
226
 
 
227
    def test_lockReleasedBeforeCheck(self):
 
228
        """
 
229
        If the lock is initially held but then released before it can be
 
230
        examined to determine if the process which held it still exists, it is
 
231
        acquired and the C{clean} and C{locked} attributes are set to C{True}.
 
232
        """
 
233
        def fakeReadlink(name):
 
234
            # Pretend to be another process releasing the lock.
 
235
            lockfile.rmlink(lockf)
 
236
            # Fall back to the real implementation of readlink.
 
237
            readlinkPatch.restore()
 
238
            return lockfile.readlink(name)
 
239
        readlinkPatch = self.patch(lockfile, 'readlink', fakeReadlink)
 
240
 
 
241
        def fakeKill(pid, signal):
 
242
            if signal != 0:
 
243
                raise OSError(errno.EPERM, None)
 
244
            if pid == 43125:
 
245
                raise OSError(errno.ESRCH, None)
 
246
        self.patch(lockfile, 'kill', fakeKill)
 
247
 
 
248
        lockf = self.mktemp()
 
249
        lock = lockfile.FilesystemLock(lockf)
 
250
        lockfile.symlink(str(43125), lockf)
 
251
        self.assertTrue(lock.lock())
 
252
        self.assertTrue(lock.clean)
 
253
        self.assertTrue(lock.locked)
 
254
 
 
255
 
 
256
    def test_lockReleasedDuringAcquireSymlink(self):
 
257
        """
 
258
        If the lock is released while an attempt is made to acquire
 
259
        it, the lock attempt fails and C{FilesystemLock.lock} returns
 
260
        C{False}.  This can happen on Windows when L{lockfile.symlink}
 
261
        fails with L{IOError} of C{EIO} because another process is in
 
262
        the middle of a call to L{os.rmdir} (implemented in terms of
 
263
        RemoveDirectory) which is not atomic.
 
264
        """
 
265
        def fakeSymlink(src, dst):
 
266
            # While another process id doing os.rmdir which the Windows
 
267
            # implementation of rmlink does, a rename call will fail with EIO.
 
268
            raise OSError(errno.EIO, None)
 
269
 
 
270
        self.patch(lockfile, 'symlink', fakeSymlink)
 
271
 
 
272
        lockf = self.mktemp()
 
273
        lock = lockfile.FilesystemLock(lockf)
 
274
        self.assertFalse(lock.lock())
 
275
        self.assertFalse(lock.locked)
 
276
    if not platform.isWindows():
 
277
        test_lockReleasedDuringAcquireSymlink.skip = (
 
278
            "special rename EIO handling only necessary and correct on "
 
279
            "Windows.")
 
280
 
 
281
 
 
282
    def test_lockReleasedDuringAcquireReadlink(self):
 
283
        """
 
284
        If the lock is initially held but is released while an attempt
 
285
        is made to acquire it, the lock attempt fails and
 
286
        L{FilesystemLock.lock} returns C{False}.
 
287
        """
 
288
        def fakeReadlink(name):
 
289
            # While another process is doing os.rmdir which the
 
290
            # Windows implementation of rmlink does, a readlink call
 
291
            # will fail with EACCES.
 
292
            raise IOError(errno.EACCES, None)
 
293
        readlinkPatch = self.patch(lockfile, 'readlink', fakeReadlink)
 
294
 
 
295
        lockf = self.mktemp()
 
296
        lock = lockfile.FilesystemLock(lockf)
 
297
        lockfile.symlink(str(43125), lockf)
 
298
        self.assertFalse(lock.lock())
 
299
        self.assertFalse(lock.locked)
 
300
    if not platform.isWindows():
 
301
        test_lockReleasedDuringAcquireReadlink.skip = (
 
302
            "special readlink EACCES handling only necessary and correct on "
 
303
            "Windows.")
 
304
 
 
305
 
 
306
    def _readlinkErrorTest(self, exceptionType, errno):
 
307
        def fakeReadlink(name):
 
308
            raise exceptionType(errno, None)
 
309
        self.patch(lockfile, 'readlink', fakeReadlink)
 
310
 
 
311
        lockf = self.mktemp()
 
312
 
 
313
        # Make it appear locked so it has to use readlink
 
314
        lockfile.symlink(str(43125), lockf)
 
315
 
 
316
        lock = lockfile.FilesystemLock(lockf)
 
317
        exc = self.assertRaises(exceptionType, lock.lock)
 
318
        self.assertEqual(exc.errno, errno)
 
319
        self.assertFalse(lock.locked)
 
320
 
 
321
 
 
322
    def test_readlinkError(self):
 
323
        """
 
324
        An exception raised by C{readlink} other than C{ENOENT} is passed up to
 
325
        the caller of L{FilesystemLock.lock}.
 
326
        """
 
327
        self._readlinkErrorTest(OSError, errno.ENOSYS)
 
328
        self._readlinkErrorTest(IOError, errno.ENOSYS)
 
329
 
 
330
 
 
331
    def test_readlinkErrorPOSIX(self):
 
332
        """
 
333
        Any L{IOError} raised by C{readlink} on a POSIX platform passed to the
 
334
        caller of L{FilesystemLock.lock}.
 
335
 
 
336
        On POSIX, unlike on Windows, these are unexpected errors which cannot
 
337
        be handled by L{FilesystemLock}.
 
338
        """
 
339
        self._readlinkErrorTest(IOError, errno.ENOSYS)
 
340
        self._readlinkErrorTest(IOError, errno.EACCES)
 
341
    if platform.isWindows():
 
342
        test_readlinkErrorPOSIX.skip = (
 
343
            "POSIX-specific error propagation not expected on Windows.")
 
344
 
 
345
 
 
346
    def test_lockCleanedUpConcurrently(self):
 
347
        """
 
348
        If a second process cleans up the lock after a first one checks the
 
349
        lock and finds that no process is holding it, the first process does
 
350
        not fail when it tries to clean up the lock.
 
351
        """
 
352
        def fakeRmlink(name):
 
353
            rmlinkPatch.restore()
 
354
            # Pretend to be another process cleaning up the lock.
 
355
            lockfile.rmlink(lockf)
 
356
            # Fall back to the real implementation of rmlink.
 
357
            return lockfile.rmlink(name)
 
358
        rmlinkPatch = self.patch(lockfile, 'rmlink', fakeRmlink)
 
359
 
 
360
        def fakeKill(pid, signal):
 
361
            if signal != 0:
 
362
                raise OSError(errno.EPERM, None)
 
363
            if pid == 43125:
 
364
                raise OSError(errno.ESRCH, None)
 
365
        self.patch(lockfile, 'kill', fakeKill)
 
366
 
 
367
        lockf = self.mktemp()
 
368
        lock = lockfile.FilesystemLock(lockf)
 
369
        lockfile.symlink(str(43125), lockf)
 
370
        self.assertTrue(lock.lock())
 
371
        self.assertTrue(lock.clean)
 
372
        self.assertTrue(lock.locked)
 
373
 
 
374
 
 
375
    def test_rmlinkError(self):
 
376
        """
 
377
        An exception raised by L{rmlink} other than C{ENOENT} is passed up
 
378
        to the caller of L{FilesystemLock.lock}.
 
379
        """
 
380
        def fakeRmlink(name):
 
381
            raise OSError(errno.ENOSYS, None)
 
382
        self.patch(lockfile, 'rmlink', fakeRmlink)
 
383
 
 
384
        def fakeKill(pid, signal):
 
385
            if signal != 0:
 
386
                raise OSError(errno.EPERM, None)
 
387
            if pid == 43125:
 
388
                raise OSError(errno.ESRCH, None)
 
389
        self.patch(lockfile, 'kill', fakeKill)
 
390
 
 
391
        lockf = self.mktemp()
 
392
 
 
393
        # Make it appear locked so it has to use readlink
 
394
        lockfile.symlink(str(43125), lockf)
 
395
 
 
396
        lock = lockfile.FilesystemLock(lockf)
 
397
        exc = self.assertRaises(OSError, lock.lock)
 
398
        self.assertEqual(exc.errno, errno.ENOSYS)
 
399
        self.assertFalse(lock.locked)
 
400
 
 
401
 
 
402
    def test_killError(self):
 
403
        """
 
404
        If L{kill} raises an exception other than L{OSError} with errno set to
 
405
        C{ESRCH}, the exception is passed up to the caller of
 
406
        L{FilesystemLock.lock}.
 
407
        """
 
408
        def fakeKill(pid, signal):
 
409
            raise OSError(errno.EPERM, None)
 
410
        self.patch(lockfile, 'kill', fakeKill)
 
411
 
 
412
        lockf = self.mktemp()
 
413
 
 
414
        # Make it appear locked so it has to use readlink
 
415
        lockfile.symlink(str(43125), lockf)
 
416
 
 
417
        lock = lockfile.FilesystemLock(lockf)
 
418
        exc = self.assertRaises(OSError, lock.lock)
 
419
        self.assertEqual(exc.errno, errno.EPERM)
 
420
        self.assertFalse(lock.locked)
 
421
 
 
422
 
 
423
    def test_unlockOther(self):
 
424
        """
 
425
        L{FilesystemLock.unlock} raises L{ValueError} if called for a lock
 
426
        which is held by a different process.
 
427
        """
 
428
        lockf = self.mktemp()
 
429
        lockfile.symlink(str(os.getpid() + 1), lockf)
 
430
        lock = lockfile.FilesystemLock(lockf)
 
431
        self.assertRaises(ValueError, lock.unlock)
 
432
 
 
433
 
 
434
    def test_isLocked(self):
 
435
        """
 
436
        L{isLocked} returns C{True} if the named lock is currently locked,
 
437
        C{False} otherwise.
 
438
        """
 
439
        lockf = self.mktemp()
 
440
        self.assertFalse(lockfile.isLocked(lockf))
 
441
        lock = lockfile.FilesystemLock(lockf)
 
442
        self.assertTrue(lock.lock())
 
443
        self.assertTrue(lockfile.isLocked(lockf))
 
444
        lock.unlock()
 
445
        self.assertFalse(lockfile.isLocked(lockf))