[Checkins] SVN: gocept.zeoraid/trunk/src/gocept/zeoraid/ - Started
testing infrastructure to simulate storages that fail.
Christian Theune
ct at gocept.com
Thu Jan 10 06:00:33 EST 2008
Log message for revision 82777:
- Started testing infrastructure to simulate storages that fail.
- Started testing the IStorage methods
- Fixed _apply_all_storages to also check whether a storage was closed after
a method was used (e.g. when the server closes the connection due to an
error) but no ClientDisconnected exception is thrown.
Changed:
U gocept.zeoraid/trunk/src/gocept/zeoraid/storage.py
A gocept.zeoraid/trunk/src/gocept/zeoraid/tests/component.xml
A gocept.zeoraid/trunk/src/gocept/zeoraid/tests/failingstorage.py
U gocept.zeoraid/trunk/src/gocept/zeoraid/tests/test_basics.py
-=-
Modified: gocept.zeoraid/trunk/src/gocept/zeoraid/storage.py
===================================================================
--- gocept.zeoraid/trunk/src/gocept/zeoraid/storage.py 2008-01-10 09:22:54 UTC (rev 82776)
+++ gocept.zeoraid/trunk/src/gocept/zeoraid/storage.py 2008-01-10 11:00:33 UTC (rev 82777)
@@ -104,53 +104,52 @@
# IStorage
- # XXX
def close(self):
+ """Close the storage."""
if self.closed:
# Storage may be closed more than once, e.g. by tear-down methods
# of tests.
return
- self._apply_all_storages('close')
- self.storages_optimal = []
- self.closed = True
+ try:
+ self._apply_all_storages('close', _raid_expect_connected=False)
+ finally:
+ self.closed = True
+ del self.storages_optimal[:]
- # XXX
def getName(self):
+ """The name of the storage."""
return self.__name__
- # XXX
def getSize(self):
+ """An approximate size of the database, in bytes."""
return self._apply_single_storage('getSize')
- # XXX
def history(self, oid, version=None, size=1):
+ """Return a sequence of history information dictionaries."""
return self._apply_single_storage('history', oid, version, size)
- # XXX
def isReadOnly(self):
- """
- XXX Revisit this approach?
- """
+ """Test whether a storage allows committing new transactions."""
return self.read_only
- # XXX
def lastTransaction(self):
- return self._apply_single_storage('lastTransaction')
+ """Return the id of the last committed transaction."""
+ return self._last_tid
- # XXX
def __len__(self):
+ """The approximate number of objects in the storage."""
return self._apply_single_storage('__len__')
- # XXX
def load(self, oid, version):
+ """Load data for an object id and version."""
return self._apply_single_storage('load', oid, version)
- # XXX
def loadBefore(self, oid, tid):
+ """Load the object data written before a transaction id."""
return self._apply_single_storage('loadBefore', oid, tid)
- # XXX
def loadSerial(self, oid, serial):
+ """Load the object record for the give transaction id."""
return self._apply_single_storage('loadSerial', oid, serial)
# XXX
@@ -450,10 +449,16 @@
except ZEO.ClientStorage.ClientDisconnected:
# XXX find other possible exceptions
self._degrade_storage(name)
+ else:
+ if not storage.is_connected():
+ self._degrade_storage(name)
def _apply_all_storages(self, method_name, *args, **kw):
if self.closed:
raise gocept.zeoraid.interfaces.RAIDClosedError("Storage has been closed.")
+ # kw might contain special parameters. We need to do this
+ # to avoid interfering with the actual arguments that we proxy.
+ expect_connected = kw.pop('_raid_expect_connected', True)
results = []
storages = self.storages_optimal[:]
if not storages:
@@ -466,6 +471,9 @@
results.append(method(*args, **kw))
except ZEO.ClientStorage.ClientDisconnected:
self._degrade_storage(name)
+ else:
+ if expect_connected and not storage.is_connected():
+ self._degrade_storage(name)
res = results[:]
for test1 in res:
Added: gocept.zeoraid/trunk/src/gocept/zeoraid/tests/component.xml
===================================================================
--- gocept.zeoraid/trunk/src/gocept/zeoraid/tests/component.xml (rev 0)
+++ gocept.zeoraid/trunk/src/gocept/zeoraid/tests/component.xml 2008-01-10 11:00:33 UTC (rev 82777)
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+
+<!-- Support for unit testing with storages that simulate errors. -->
+
+<component prefix="gocept.zeoraid.tests.failingstorage">
+
+ <sectiontype
+ name="failingstorage"
+ implements="ZODB.storage"
+ datatype=".Opener">
+ </sectiontype>
+
+</component>
Added: gocept.zeoraid/trunk/src/gocept/zeoraid/tests/failingstorage.py
===================================================================
--- gocept.zeoraid/trunk/src/gocept/zeoraid/tests/failingstorage.py (rev 0)
+++ gocept.zeoraid/trunk/src/gocept/zeoraid/tests/failingstorage.py 2008-01-10 11:00:33 UTC (rev 82777)
@@ -0,0 +1,28 @@
+# vim:fileencoding=utf-8
+# Copyright (c) 2007 gocept gmbh & co. kg
+# See also LICENSE.txt
+# $Id$
+"""Unit test support."""
+
+import ZODB.utils
+import ZODB.config
+import ZODB.MappingStorage
+
+
+class Opener(ZODB.config.BaseConfig):
+
+ def open(self):
+ return FailingStorage(self.name)
+
+
+class FailingStorage(ZODB.MappingStorage.MappingStorage):
+
+ def getExtensionMethods(self):
+ return dict(fail=None)
+
+ def fail(self, method):
+ old_method = getattr(self, method)
+ def failing_method(*args, **kw):
+ setattr(self, method, old_method)
+ raise Exception()
+ setattr(self, method, failing_method)
Property changes on: gocept.zeoraid/trunk/src/gocept/zeoraid/tests/failingstorage.py
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: gocept.zeoraid/trunk/src/gocept/zeoraid/tests/test_basics.py
===================================================================
--- gocept.zeoraid/trunk/src/gocept/zeoraid/tests/test_basics.py 2008-01-10 09:22:54 UTC (rev 82776)
+++ gocept.zeoraid/trunk/src/gocept/zeoraid/tests/test_basics.py 2008-01-10 11:00:33 UTC (rev 82777)
@@ -19,7 +19,11 @@
import ZODB.interfaces
import ZEO.interfaces
+# Uncomment this to get helpful logging from the ZEO servers on the console
+#import logging
+#logging.getLogger().addHandler(logging.StreamHandler())
+
class ZEOOpener(object):
def __init__(self, name, **kwargs):
@@ -95,7 +99,86 @@
self.assert_(zope.interface.verify.verifyObject(iface,
self._storage))
+class FailingStorageTestsBase(unittest.TestCase):
+ backend_count = None
+
+ def setUp(self):
+ # Ensure compatibility
+ gocept.zeoraid.compatibility.setup()
+
+ self._servers = []
+ self._storages = []
+ for i in xrange(self.backend_count):
+ port = get_port()
+ zconf = forker.ZEOConfig(('', port))
+ zport, adminaddr, pid, path = forker.start_zeo_server(
+ """%import gocept.zeoraid.tests
+ <failingstorage 1>
+ </failingstorage>""",
+ zconf, port)
+ self._servers.append(adminaddr)
+ self._storages.append(ZEOOpener(zport, storage='1',
+ cache_size=2000000,
+ min_disconnect_poll=0.5, wait=1,
+ wait_timeout=60))
+ self._storage = gocept.zeoraid.storage.RAIDStorage('teststorage',
+ self._storages)
+
+ def tearDown(self):
+ try:
+ self._storage.close()
+ except:
+ pass
+ for server in self._servers:
+ forker.shutdown_zeo_server(server)
+ # XXX wait for servers to come down
+
+
+class FailingStorageTests1Backend(FailingStorageTestsBase):
+
+ backend_count = 1
+
+ def test_close(self):
+ self._storage.close()
+ self.assertEquals(self._storage.closed, True)
+
+ def test_double_close(self):
+ self._storage.close()
+ self.assertEquals(self._storage.closed, True)
+ self._storage.close()
+ self.assertEquals(self._storage.closed, True)
+
+ def test_close_failing(self):
+ # Even though we make the server-side storage fail, we do not get
+ # receive an error or a degradation because the result of the failure
+ # is that the connection is closed. This is actually what we wanted.
+ # Unfortunately that means that an error can be hidden while closing.
+ self._storage.storages[self._storage.storages_optimal[0]].fail('close')
+ self._storage.close()
+ self.assertEquals(True, self._storage.closed)
+
+
+class FailingStorageTests2Backends(FailingStorageTestsBase):
+
+ backend_count = 2
+
+ def test_close_degrading(self):
+ # See the comment on `test_close_failing`.
+ self._storage.storages[self._storage.storages_optimal[0]].fail('close')
+ self._storage.close()
+ self.assertEquals([], self._storage.storages_degraded)
+ self.assertEquals(True, self._storage.closed)
+
+ def test_close_server_missing(self):
+ # See the comment on `test_close_failing`.
+ forker.shutdown_zeo_server(self._servers[0])
+ del self._servers[0]
+ self._storage.close()
+ self.assertEquals([], self._storage.storages_degraded)
+ self.assertEquals(True, self._storage.closed)
+
+
class ZEOReplicationStorageTests(ZEOStorageBackendTests,
ReplicationStorageTests,
ThreadTests.ThreadTests):
@@ -105,6 +188,8 @@
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(ZEOReplicationStorageTests, "check"))
+ suite.addTest(unittest.makeSuite(FailingStorageTests1Backend))
+ suite.addTest(unittest.makeSuite(FailingStorageTests2Backends))
return suite
if __name__=='__main__':
More information about the Checkins
mailing list