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