[Checkins] SVN: zope.app.testing/trunk/src/zope/app/testing/functional.py Add minimal multi-database support to FunctionalTestSetup.

Jacob Holm jh at improva.dk
Tue May 15 16:20:48 EDT 2007


Log message for revision 75779:
  Add minimal multi-database support to FunctionalTestSetup.
  

Changed:
  U   zope.app.testing/trunk/src/zope/app/testing/functional.py

-=-
Modified: zope.app.testing/trunk/src/zope/app/testing/functional.py
===================================================================
--- zope.app.testing/trunk/src/zope/app/testing/functional.py	2007-05-15 19:49:01 UTC (rev 75778)
+++ zope.app.testing/trunk/src/zope/app/testing/functional.py	2007-05-15 20:20:47 UTC (rev 75779)
@@ -38,6 +38,7 @@
 from zope.testing import doctest
 
 import zope.app.testing.setup
+from zope.app.appsetup.appsetup import multi_database
 from zope.app.debug import Debugger
 from zope.app.publication.http import HTTPPublication
 from zope.app.publication.zopepublication import ZopePublication
@@ -101,18 +102,76 @@
         """Set up the manager, zope.mgr
         """
 
+class BaseDatabaseFactory(object):
+    """Factory object for passing to appsetup.multi_databases
+
+    This class is an internal implementation detail, subject to change
+    without notice!
+
+    It is currently used by FunctionalTestSetUp.__init__ to create the
+    basic storage(s) containing the data that is common to all tests
+    in a layer.
+
+    The constructor takes the name of the new database, and a
+    dictionary of storages.  The 'open' method creates a new
+    DemoStorage, and adds it to the storage dictionary under the given
+    name. Then creates and returns a named DB object using the
+    storage.
+    """
+
+    def __init__(self, name, base_storages):
+        self.name = name
+        self.base_storages = base_storages
+
+    def open(self):
+        name = self.name
+        if name in self.base_storages:
+            raise ValueError("Duplicate database name: %r" % name)
+        storage = DemoStorage("Memory storage %r" % name)
+        self.base_storages[name] = storage
+        return DB(storage, database_name=name)
+
+
+class DerivedDatabaseFactory(object):
+    """Factory object for passing to appsetup.multi_databases
+
+    This class is an internal implementation detail, subject to change
+    without notice!
+
+    It is currently used by FunctionalTestSetUp.setUp to create the
+    derived storage(s) used for each test in a layer.
+
+    The constructor takes the name of the new database, and a
+    dictionary of storages.  The 'open' method creates a new
+    DemoStorage as a wrapper around the storage with the given
+    name. Then creates and returns a named DB object using the
+    storage.
+    """
+
+    def __init__(self, name, base_storages):
+        self.name = name
+        self.storage = DemoStorage("Demo storage %r" % name,
+                                   base_storages[name])
+
+    def open(self):
+        return DB(self.storage, database_name=self.name)
+
+
 class FunctionalTestSetup(object):
     """Keeps shared state across several functional test cases."""
 
     __shared_state = { '_init': False }
 
-    def __init__(self, config_file=None):
+    def __init__(self, config_file=None, database_names=None):
         """Initializes Zope 3 framework.
 
         Creates a volatile memory storage.  Parses Zope3 configuration files.
         """
         self.__dict__ = self.__shared_state
 
+        if database_names is not None:
+            database_names = tuple(database_names)
+
         if not self._init:
 
             # Make sure unit tests are cleaned up
@@ -121,14 +180,22 @@
 
             if not config_file:
                 config_file = 'ftesting.zcml'
+            if database_names is None:
+                database_names = ('unnamed',)
             self.log = StringIO()
             # Make it silent but keep the log available for debugging
             logging.root.addHandler(logging.StreamHandler(self.log))
-            self.base_storage = DemoStorage("Memory Storage")
-            self.db = DB(self.base_storage)
+
+            self._base_storages = {}
+            self.db = multi_database(
+                BaseDatabaseFactory(name, self._base_storages)
+                for name in database_names
+                )[0][0]
             self.app = Debugger(self.db, config_file)
+
             self.connection = None
             self._config_file = config_file
+            self._database_names = database_names
             self._init = True
 
             # Make a local grant for the test user
@@ -144,13 +211,36 @@
             raise NotImplementedError('Already configured'
                                       ' with a different config file')
 
+        elif database_names and database_names != self._database_names:
+            # Running different tests with different configurations is not
+            # supported at the moment
+            raise NotImplementedError('Already configured'
+                                      ' with different database names')
+
+    # BBB: Simulate the old base_storage attribute, but only when not using
+    # multiple databases. There *is* code in the wild that uses the attribute.
+    def _get_base_storage(self):
+        if len(self._database_names)!=1:
+            raise AttributeError('base_storage')
+        return self._base_storages[self._database_names[0]]
+
+    def _set_base_storage(self, value):
+        if len(self._database_name)!=1:
+            raise AttributeError('base_storage')
+        self._base_storages[self._database_names[0]] = value
+
+    base_storage = property(_get_base_storage, _set_base_storage)
+
     def setUp(self):
         """Prepares for a functional test case."""
-        # Tear down the old demo storage (if any) and create a fresh one
+        # Tear down the old demo storages (if any) and create fresh ones
         abort()
-        self.db.close()
-        storage = DemoStorage("Demo Storage", self.base_storage)
-        self.db = self.app.db = DB(storage)
+        for db in self.db.databases.itervalues():
+            db.close()
+        self.db = self.app.db = multi_database(
+            DerivedDatabaseFactory(name, self._base_storages)
+            for name in self._database_names
+            )[0][0]
         self.connection = None
 
     def tearDown(self):
@@ -159,13 +249,15 @@
         if self.connection:
             self.connection.close()
             self.connection = None
-        self.db.close()
+        for db in self.db.databases.itervalues():
+            db.close()
         setSite(None)
 
     def tearDownCompletely(self):
         """Cleans up the setup done by the constructor."""
         zope.app.testing.setup.placefulTearDown()
         self._config_file = False
+        self._database_names = None
         self._init = False
 
     def getRootFolder(self):



More information about the Checkins mailing list