Rev 2362: Update the lock code and test code so that if more than one in http://bzr.arbash-meinel.com/branches/bzr/0.15-dev/lock_cleanup
John Arbash Meinel
john at arbash-meinel.com
Wed Mar 14 20:16:10 GMT 2007
At http://bzr.arbash-meinel.com/branches/bzr/0.15-dev/lock_cleanup
------------------------------------------------------------
revno: 2362
revision-id: john at arbash-meinel.com-20070314201552-bjtfua57456dviep
parent: john at arbash-meinel.com-20070313231812-h74g1zz32v12lv6s
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: lock_cleanup
timestamp: Wed 2007-03-14 15:15:52 -0500
message:
Update the lock code and test code so that if more than one
lock implementation is available, they will both be tested.
It is quite a bit of overhead, for a case where we are likely to only have 1
real lock implementation per platform, but hey, for now we have 2.
added:
bzrlib/tests/per_lock/ bzrlibtestsper_lock-20070314195914-llb0phfp2laomqb3-1
bzrlib/tests/per_lock/__init__.py __init__.py-20070314201444-u92yjsqrkh2m3qcb-1
renamed:
bzrlib/tests/test_lock.py => bzrlib/tests/per_lock/test_lock.py test_lock.py-20070313190612-mfpoa7t8kvrgrhj2-1
modified:
bzrlib/lock.py lock.py-20050527050856-ec090bb51bc03349
bzrlib/tests/__init__.py selftest.py-20050531073622-8d0e3c8845c97a64
bzrlib/tests/per_lock/test_lock.py test_lock.py-20070313190612-mfpoa7t8kvrgrhj2-1
-------------- next part --------------
=== added directory 'bzrlib/tests/per_lock'
=== added file 'bzrlib/tests/per_lock/__init__.py'
--- a/bzrlib/tests/per_lock/__init__.py 1970-01-01 00:00:00 +0000
+++ b/bzrlib/tests/per_lock/__init__.py 2007-03-14 20:15:52 +0000
@@ -0,0 +1,74 @@
+# Copyright (C) 2007 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+"""OS Lock implementation tests for bzr.
+
+These test the conformance of all the lock variations to the expected API.
+"""
+
+from copy import deepcopy
+
+from bzrlib import (
+ lock,
+ tests,
+ )
+
+
+class TestCaseWithLock(tests.TestCaseWithTransport):
+
+ write_lock = None
+ read_lock = None
+
+
+class LockTestProviderAdapter(object):
+ """A tool to generate a suite testing multiple lock formats at once.
+
+ This is done by copying the test once for each lock and injecting the
+ read_lock and write_lock classes.
+ They are also given a new test id.
+ """
+
+ def __init__(self, lock_classes):
+ self._lock_classes = lock_classes
+
+ def _clone_test(self, test, write_lock, read_lock, variation):
+ """Clone test for adaption."""
+ new_test = deepcopy(test)
+ new_test.write_lock = write_lock
+ new_test.read_lock = read_lock
+ def make_new_test_id():
+ new_id = "%s(%s)" % (test.id(), variation)
+ return lambda: new_id
+ new_test.id = make_new_test_id()
+ return new_test
+
+ def adapt(self, test):
+ result = tests.TestSuite()
+ for name, write_lock, read_lock in self._lock_classes:
+ new_test = self._clone_test(test, write_lock, read_lock, name)
+ result.addTest(new_test)
+ return result
+
+
+def test_suite():
+ result = tests.TestSuite()
+ test_lock_implementations = [
+ 'bzrlib.tests.per_lock.test_lock',
+ ]
+ adapter = LockTestProviderAdapter(lock._lock_classes)
+ loader = tests.TestLoader()
+ tests.adapt_modules(test_lock_implementations, adapter, loader, result)
+ return result
=== renamed file 'bzrlib/tests/test_lock.py' => 'bzrlib/tests/per_lock/test_lock.py'
--- a/bzrlib/tests/test_lock.py 2007-03-13 23:11:51 +0000
+++ b/bzrlib/tests/per_lock/test_lock.py 2007-03-14 20:15:52 +0000
@@ -18,13 +18,13 @@
from bzrlib import (
errors,
- lock,
osutils,
- tests,
)
-
-class TestLock(tests.TestCaseInTempDir):
+from bzrlib.tests.per_lock import TestCaseWithLock
+
+
+class TestLock(TestCaseWithLock):
def setUp(self):
super(TestLock, self).setUp()
@@ -32,7 +32,7 @@
def test_read_lock(self):
"""Smoke test for read locks."""
- a_lock = lock.ReadLock('a-file')
+ a_lock = self.read_lock('a-file')
self.addCleanup(a_lock.unlock)
# The lock file should be opened for reading
txt = a_lock.f.read()
@@ -40,14 +40,14 @@
def test_create_if_needed_read(self):
"""We will create the file if it doesn't exist yet."""
- a_lock = lock.ReadLock('other-file')
+ a_lock = self.read_lock('other-file')
self.addCleanup(a_lock.unlock)
txt = a_lock.f.read()
self.assertEqual('', txt)
def test_create_if_needed_write(self):
"""We will create the file if it doesn't exist yet."""
- a_lock = lock.WriteLock('other-file')
+ a_lock = self.write_lock('other-file')
self.addCleanup(a_lock.unlock)
txt = a_lock.f.read()
self.assertEqual('', txt)
@@ -64,15 +64,15 @@
osutils.make_readonly('a-file')
# Make sure the file is read-only (on all platforms)
self.assertRaises(IOError, open, 'a-file', 'rb+')
- a_lock = lock.ReadLock('a-file')
+ a_lock = self.read_lock('a-file')
a_lock.unlock()
# TODO: jam 20070313 This should be a specific subclass
- self.assertRaises(errors.ReadOnlyLockError, lock.WriteLock, 'a-file')
+ self.assertRaises(errors.ReadOnlyLockError, self.write_lock, 'a-file')
def test_write_lock(self):
"""Smoke test for write locks."""
- a_lock = lock.WriteLock('a-file')
+ a_lock = self.write_lock('a-file')
self.addCleanup(a_lock.unlock)
# You should be able to read and write to the lock file.
txt = a_lock.f.read()
@@ -87,24 +87,24 @@
def test_multiple_read_locks(self):
"""You can take out more than one read lock on the same file."""
- a_lock = lock.ReadLock('a-file')
+ a_lock = self.read_lock('a-file')
self.addCleanup(a_lock.unlock)
- b_lock = lock.ReadLock('a-file')
+ b_lock = self.read_lock('a-file')
self.addCleanup(b_lock.unlock)
def test_multiple_write_locks_exclude(self):
"""Taking out more than one write lock should fail."""
- a_lock = lock.WriteLock('a-file')
+ a_lock = self.write_lock('a-file')
self.addCleanup(a_lock.unlock)
# Taking out a lock on a locked file should raise LockContention
- self.assertRaises(errors.LockContention, lock.WriteLock, 'a-file')
+ self.assertRaises(errors.LockContention, self.write_lock, 'a-file')
def _disabled_test_read_then_write_excludes(self):
"""If a file is read-locked, taking out a write lock should fail."""
- a_lock = lock.ReadLock('a-file')
+ a_lock = self.read_lock('a-file')
self.addCleanup(a_lock.unlock)
# Taking out a lock on a locked file should raise LockContention
- self.assertRaises(errors.LockContention, lock.WriteLock, 'a-file')
+ self.assertRaises(errors.LockContention, self.write_lock, 'a-file')
def _disabled_test_write_then_read_excludes(self):
"""If a file is write-locked, taking out a read lock should fail.
@@ -112,7 +112,7 @@
The file is exclusively owned by the write lock, so we shouldn't be
able to take out a shared read lock.
"""
- a_lock = lock.WriteLock('a-file')
+ a_lock = self.write_lock('a-file')
self.addCleanup(a_lock.unlock)
# Taking out a lock on a locked file should raise LockContention
- self.assertRaises(errors.LockContention, lock.ReadLock, 'a-file')
+ self.assertRaises(errors.LockContention, self.read_lock, 'a-file')
=== modified file 'bzrlib/lock.py'
--- a/bzrlib/lock.py 2007-03-13 23:18:12 +0000
+++ b/bzrlib/lock.py 2007-03-14 20:15:52 +0000
@@ -26,7 +26,7 @@
It is not specified whether these locks are reentrant (i.e. can be
taken repeatedly by a single process) or whether they exclude
-different threads in a single process. That reentrancy is provided by
+different threads in a single process. That reentrancy is provided by
LockableFiles.
This defines two classes: ReadLock and WriteLock, which can be
@@ -61,7 +61,7 @@
# maybe this is an old branch (before may 2005)
mutter("trying to create missing branch lock %r", filename)
-
+
self.f = open(filename, 'wb+')
return self.f
@@ -76,7 +76,7 @@
from warnings import warn
warn("lock on %r not released" % self.f)
self.unlock()
-
+
def unlock(self):
raise NotImplementedError()
@@ -86,16 +86,20 @@
import fcntl
have_fcntl = True
except ImportError:
- try:
- import win32con, win32file, pywintypes, winerror, msvcrt
- have_pywin32 = True
- except ImportError:
- try:
- import ctypes, msvcrt
- have_ctypes = True
- except ImportError:
- raise NotImplementedError("please write a locking method "
- "for platform %r" % sys.platform)
+ have_fcntl = False
+try:
+ import win32con, win32file, pywintypes, winerror, msvcrt
+ have_pywin32 = True
+except ImportError:
+ have_pywin32 = False
+try:
+ import ctypes, msvcrt
+ have_ctypes = True
+except ImportError:
+ have_ctypes = False
+
+
+_lock_classes = []
if have_fcntl:
@@ -164,10 +168,9 @@
self._unlock()
- WriteLock = _fcntl_WriteLock
- ReadLock = _fcntl_ReadLock
+ _lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
-elif have_pywin32:
+if have_pywin32:
LOCK_SH = 0 # the default
LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
@@ -212,10 +215,9 @@
super(_w32c_WriteLock, self).__init__()
self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
- WriteLock = _w32c_WriteLock
- ReadLock = _w32c_ReadLock
-else:
- assert have_ctypes, "We should have ctypes installed"
+ _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
+
+if have_ctypes:
# These constants were copied from the win32con.py module.
LOCKFILE_FAIL_IMMEDIATELY = 1
LOCKFILE_EXCLUSIVE_LOCK = 2
@@ -242,7 +244,7 @@
# PVOID Pointer;
# };
# HANDLE hEvent;
- # } OVERLAPPED,
+ # } OVERLAPPED,
class _inner_struct(ctypes.Structure):
_fields_ = [('Offset', ctypes.c_uint), # DWORD
@@ -311,6 +313,46 @@
super(_ctypes_WriteLock, self).__init__()
self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
- WriteLock = _ctypes_WriteLock
- ReadLock = _ctypes_ReadLock
-
+ _lock_classes.append(('ctypes', _ctypes_WriteLock, _ctypes_ReadLock))
+
+
+if len(_lock_classes) == 0:
+ raise NotImplementedError("We only have support for"
+ " fcntl, pywin32 or ctypes locking."
+ " If your platform (windows) does not"
+ " support fcntl locks, you must have"
+ " either pywin32 or ctypes installed.")
+
+# We default to using the first available lock class.
+_lock_type, WriteLock, ReadLock = _lock_classes[0]
+
+
+class LockTreeTestProviderAdapter(object):
+ """A tool to generate a suite testing multiple lock formats at once.
+
+ This is done by copying the test once for each lock and injecting the
+ read_lock and write_lock classes.
+ They are also given a new test id.
+ """
+
+ def __init__(self, lock_classes):
+ self._lock_classes = lock_classes
+
+ def _clone_test(self, test, write_lock, read_lock, variation):
+ """Clone test for adaption."""
+ new_test = deepcopy(test)
+ new_test.write_lock = write_lock
+ new_test.read_lock = read_lock
+ def make_new_test_id():
+ new_id = "%s(%s)" % (test.id(), variation)
+ return lambda: new_id
+ new_test.id = make_new_test_id()
+ return new_test
+
+ def adapt(self, test):
+ from bzrlib.tests import TestSuite
+ result = TestSuite()
+ for name, write_lock, read_lock in self._lock_classes:
+ new_test = self._clone_test(test, write_lock, read_lock, name)
+ result.addTest(new_test)
+ return result
=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py 2007-03-13 19:11:05 +0000
+++ b/bzrlib/tests/__init__.py 2007-03-14 20:15:52 +0000
@@ -120,6 +120,7 @@
import bzrlib.tests.interrepository_implementations
import bzrlib.tests.interversionedfile_implementations
import bzrlib.tests.intertree_implementations
+ import bzrlib.tests.per_lock
import bzrlib.tests.repository_implementations
import bzrlib.tests.revisionstore_implementations
import bzrlib.tests.tree_implementations
@@ -132,6 +133,7 @@
bzrlib.tests.interrepository_implementations,
bzrlib.tests.interversionedfile_implementations,
bzrlib.tests.intertree_implementations,
+ bzrlib.tests.per_lock,
bzrlib.tests.repository_implementations,
bzrlib.tests.revisionstore_implementations,
bzrlib.tests.tree_implementations,
@@ -1950,7 +1952,6 @@
'bzrlib.tests.test_knit',
'bzrlib.tests.test_lazy_import',
'bzrlib.tests.test_lazy_regex',
- 'bzrlib.tests.test_lock',
'bzrlib.tests.test_lockdir',
'bzrlib.tests.test_lockable_files',
'bzrlib.tests.test_log',
More information about the bazaar-commits
mailing list