[Zope-Checkins] SVN: Zope/trunk/ Testing.ZopeTestCase: Introduced a "ZopeLite" test layer, making it

Stefan H. Holek stefan at epy.co.at
Sat Oct 13 12:15:39 EDT 2007


Log message for revision 80864:
  Testing.ZopeTestCase: Introduced a "ZopeLite" test layer, making it
  possible to mix ZTC and non-ZTC tests much more freely.
  

Changed:
  U   Zope/trunk/doc/CHANGES.txt
  U   Zope/trunk/lib/python/Testing/ZopeTestCase/ZopeLite.py
  U   Zope/trunk/lib/python/Testing/ZopeTestCase/__init__.py
  U   Zope/trunk/lib/python/Testing/ZopeTestCase/base.py
  A   Zope/trunk/lib/python/Testing/ZopeTestCase/layer.py
  U   Zope/trunk/lib/python/Testing/ZopeTestCase/testShoppingCart.py
  U   Zope/trunk/lib/python/Testing/ZopeTestCase/testWebserver.py
  U   Zope/trunk/lib/python/Testing/ZopeTestCase/testZODBCompat.py
  U   Zope/trunk/lib/python/Testing/ZopeTestCase/utils.py
  U   Zope/trunk/lib/python/Testing/ZopeTestCase/zopedoctest/testLayerExtraction.py

-=-
Modified: Zope/trunk/doc/CHANGES.txt
===================================================================
--- Zope/trunk/doc/CHANGES.txt	2007-10-13 16:02:33 UTC (rev 80863)
+++ Zope/trunk/doc/CHANGES.txt	2007-10-13 16:15:38 UTC (rev 80864)
@@ -71,6 +71,9 @@
 
     Features added
 
+      - Testing.ZopeTestCase: Introduced a "ZopeLite" test layer, making it
+        possible to mix ZTC and non-ZTC tests much more freely.
+
       - Testing/custom_zodb.py: added support use a different storage other
         than DemoStorage. A dedicated FileStorage can be mount by setting the
         $TEST_FILESTORAGE environment variable to a custom Data.fs file.  A 

Modified: Zope/trunk/lib/python/Testing/ZopeTestCase/ZopeLite.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/ZopeLite.py	2007-10-13 16:02:33 UTC (rev 80863)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/ZopeLite.py	2007-10-13 16:15:38 UTC (rev 80864)
@@ -26,6 +26,7 @@
 """
 
 import os, sys, time
+import layer
 
 # Allow code to tell it is run by the test framework
 os.environ['ZOPETESTCASE'] = '1'
@@ -105,7 +106,12 @@
 
 _patched = False
 
+ at layer.onsetup
 def _apply_patches():
+    # Do not patch a running Zope
+    if Zope2._began_startup:
+        return
+
     # Avoid expensive product import
     def null_import_products(): pass
     OFS.Application.import_products = null_import_products
@@ -126,10 +132,18 @@
     global _patched
     _patched = True
 
-# Do not patch a running Zope
-if not Zope2._began_startup:
-    _apply_patches()
+_apply_patches()
 
+_theApp = None
+
+ at layer.onsetup
+def _startup():
+    global _theApp
+    _theApp = Zope2.app()
+
+# Start ZopeLite
+_startup()
+
 # Allow test authors to install Zope products into the test environment. Note
 # that installProduct() must be called at module level -- never from tests.
 from OFS.Application import get_folder_permissions, get_products
@@ -137,7 +151,6 @@
 from OFS.Folder import Folder
 import Products
 
-_theApp = Zope2.app()
 _installedProducts = {}
 _installedPackages = {}
 
@@ -145,7 +158,13 @@
     '''Checks if a product can be found along Products.__path__'''
     return name in [n[1] for n in get_products()]
 
+ at layer.onsetup
 def installProduct(name, quiet=0):
+    '''Installs a Zope product at layer setup time.'''
+    quiet = 1 # Ignore argument
+    _installProduct(name, quiet)
+
+def _installProduct(name, quiet=0):
     '''Installs a Zope product.'''
     start = time.time()
     meta_types = []
@@ -170,8 +189,14 @@
     '''Checks if a package has been registered with five:registerPackage.'''
     return name in [m.__name__ for m in getattr(Products, '_registered_packages', [])]
 
+ at layer.onsetup
 def installPackage(name, quiet=0):
-    '''Installs a registered Python package like a Zope product.'''
+    '''Installs a registered Python package at layer setup time.'''
+    quiet = 1 # Ignore argument
+    _installPackage(name, quiet)
+
+def _installPackage(name, quiet=0):
+    '''Installs a registered Python package.'''
     start = time.time()
     if _patched and not _installedPackages.has_key(name):
         for module, init_func in getattr(Products, '_packages_to_initialize', []):
@@ -187,28 +212,9 @@
         else:
             if not quiet: _print('Installing %s ... NOT FOUND\n' % name)
 
-def _load_control_panel():
-    # Loading the Control_Panel of an existing ZODB may take
-    # a while; print another dot if it does.
-    start = time.time()
-    max = (start - _start) / 4
-    _exec('_theApp.Control_Panel')
-    _theApp.Control_Panel
-    if (time.time() - start) > max:
-        _write('.')
+installProduct('PluginIndexes', 1)  # Must install first
+installProduct('OFSP', 1)
 
-def _install_products():
-    installProduct('PluginIndexes', 1)  # Must install first
-    installProduct('OFSP', 1)
-    #installProduct('ExternalMethod', 1)
-    #installProduct('ZSQLMethods', 1)
-    #installProduct('ZGadflyDA', 1)
-    #installProduct('MIMETools', 1)
-    #installProduct('MailHost', 1)
-
-_load_control_panel()
-_install_products()
-
 # So people can use ZopeLite.app()
 app = Zope2.app
 debug = Zope2.debug

Modified: Zope/trunk/lib/python/Testing/ZopeTestCase/__init__.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/__init__.py	2007-10-13 16:02:33 UTC (rev 80863)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/__init__.py	2007-10-13 16:15:38 UTC (rev 80864)
@@ -17,6 +17,7 @@
 
 import ZopeLite as Zope2
 import utils
+import layer
 
 from ZopeLite import hasProduct
 from ZopeLite import installProduct

Modified: Zope/trunk/lib/python/Testing/ZopeTestCase/base.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/base.py	2007-10-13 16:02:33 UTC (rev 80863)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/base.py	2007-10-13 16:15:38 UTC (rev 80864)
@@ -21,12 +21,12 @@
 import utils
 import interfaces
 import connections
+import layer
 
 from zope.interface import implements
 from AccessControl.SecurityManagement import noSecurityManager
 
 
-
 def app():
     '''Opens a ZODB connection and returns the app object.'''
     app = Zope2.app()
@@ -34,18 +34,20 @@
     connections.register(app)
     return app
 
+
 def close(app):
     '''Closes the app's ZODB connection.'''
     connections.close(app)
 
 
-
 class TestCase(unittest.TestCase, object):
     '''Base test case for Zope testing
     '''
 
     implements(interfaces.IZopeTestCase)
 
+    layer = layer.ZopeLite
+
     def afterSetUp(self):
         '''Called after setUp() has completed. This is
            far and away the most useful hook.

Copied: Zope/trunk/lib/python/Testing/ZopeTestCase/layer.py (from rev 80450, Zope/branches/shh-2.11-zopelitelayer/lib/python/Testing/ZopeTestCase/layer.py)
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/layer.py	                        (rev 0)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/layer.py	2007-10-13 16:15:38 UTC (rev 80864)
@@ -0,0 +1,81 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""ZopeLite layer
+
+$Id$
+"""
+
+_deferred_setup = []
+
+
+class ZopeLite:
+    '''The most base layer'''
+
+    @classmethod
+    def setUp(cls):
+        '''Brings up the ZopeLite environment.'''
+        for func, args, kw in _deferred_setup:
+            func(*args, **kw)
+
+    @classmethod
+    def tearDown(cls):
+        '''ZopeLite doesn't support tear down.
+
+           We don't raise NotImplementedError to avoid
+           triggering the testrunner's "resume layer"
+           mechanism.
+
+           See zope.testing.testrunner-layers-ntd.txt
+        '''
+
+ZopeLiteLayer = ZopeLite
+
+
+def onsetup(func):
+    '''Defers a function call to layer setup.
+       Used as a decorator.
+    '''
+    def deferred_func(*args, **kw):
+        _deferred_setup.append((func, args, kw))
+    return deferred_func
+
+
+def appcall(func):
+    '''Defers a function call to layer setup.
+       Used as a decorator.
+
+       In addition, this decorator implements the appcall
+       protocol:
+
+       * The decorated function expects 'app' as first argument.
+
+       * If 'app' is provided by the caller, the function is
+         called immediately.
+
+       * If 'app' is omitted or None, the 'app' argument is
+         provided by the decorator, and the function call is
+         deferred to ZopeLite layer setup.
+
+       Also see utils.appcall.
+    '''
+    def appcalled_func(*args, **kw):
+        if args and args[0] is not None:
+            return func(*args, **kw)
+        if kw.get('app') is not None:
+            return func(*args, **kw)
+        def caller(*args, **kw):
+            import utils
+            utils.appcall(func, *args, **kw)
+        _deferred_setup.append((caller, args, kw))
+    return appcalled_func
+

Modified: Zope/trunk/lib/python/Testing/ZopeTestCase/testShoppingCart.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/testShoppingCart.py	2007-10-13 16:02:33 UTC (rev 80863)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/testShoppingCart.py	2007-10-13 16:15:38 UTC (rev 80864)
@@ -30,23 +30,31 @@
 
 from Testing import ZopeTestCase
 
+from Testing.ZopeTestCase import layer
+from Testing.ZopeTestCase import utils
+from Testing.ZopeTestCase import transaction
+
 from Globals import SOFTWARE_HOME
 examples_path = os.path.join(SOFTWARE_HOME, '..', '..', 'skel', 'import', 'Examples.zexp')
 examples_path = os.path.abspath(examples_path)
 
 
-# Open ZODB connection
-app = ZopeTestCase.app()
+class ShoppingCartLayer(layer.ZopeLite):
 
-# Set up sessioning objects
-ZopeTestCase.utils.setupCoreSessions(app)
+    @classmethod
+    def setUp(cls):
+        # Set up sessioning objects
+        utils.appcall(utils.setupCoreSessions)
 
-# Set up example applications
-if not hasattr(app, 'Examples'):
-    ZopeTestCase.utils.importObjectFromFile(app, examples_path)
+        # Set up example applications
+        utils.appcall(utils.importObjectFromFile, examples_path, quiet=1)
 
-# Close ZODB connection
-ZopeTestCase.close(app)
+    @classmethod
+    def tearDown(cls):
+        def cleanup(app):
+            app._delObject('Examples')
+            transaction.commit()
+        utils.appcall(cleanup)
 
 
 class DummyOrder:
@@ -63,6 +71,8 @@
 
     _setup_fixture = 0  # No default fixture
 
+    layer = ShoppingCartLayer
+
     def afterSetUp(self):
         self.cart = self.app.Examples.ShoppingCart
         # Put SESSION object into REQUEST

Modified: Zope/trunk/lib/python/Testing/ZopeTestCase/testWebserver.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/testWebserver.py	2007-10-13 16:02:33 UTC (rev 80863)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/testWebserver.py	2007-10-13 16:15:38 UTC (rev 80864)
@@ -46,8 +46,7 @@
 ZopeTestCase.utils.setupSiteErrorLog()
 
 # Start the web server
-host, port = ZopeTestCase.utils.startZServer(4)
-folder_url = 'http://%s:%d/%s' %(host, port, ZopeTestCase.folder_name)
+ZopeTestCase.utils.startZServer()
 
 
 class ManagementOpener(urllib.FancyURLopener):
@@ -55,6 +54,7 @@
     def prompt_user_passwd(self, host, realm):
         return ('manager', 'secret')
 
+
 class UnauthorizedOpener(urllib.FancyURLopener):
     '''Raises Unauthorized when prompted'''
     def prompt_user_passwd(self, host, realm):
@@ -67,6 +67,8 @@
         uf = self.folder.acl_users
         uf.userFolderAddUser('manager', 'secret', ['Manager'], [])
 
+        self.folder_url = self.folder.absolute_url()
+
         # A simple document
         self.folder.addDTMLDocument('index_html', file='index_html called')
 
@@ -99,7 +101,7 @@
     def testURLAccessPublicObject(self):
         # Test web access to a public resource
         urllib._urlopener = ManagementOpener()
-        page = urllib.urlopen(folder_url+'/index_html').read()
+        page = urllib.urlopen(self.folder_url+'/index_html').read()
         self.assertEqual(page, 'index_html called')
 
     def testAccessProtectedObject(self):
@@ -110,7 +112,7 @@
     def testURLAccessProtectedObject(self):
         # Test web access to a protected resource
         urllib._urlopener = ManagementOpener()
-        page = urllib.urlopen(folder_url+'/secret_html').read()
+        page = urllib.urlopen(self.folder_url+'/secret_html').read()
         self.assertEqual(page, 'secret_html called')
 
     def testSecurityOfPublicObject(self):
@@ -125,7 +127,7 @@
         # Test web security of a public resource
         urllib._urlopener = UnauthorizedOpener()
         try: 
-            urllib.urlopen(folder_url+'/index_html')
+            urllib.urlopen(self.folder_url+'/index_html')
         except Unauthorized:
             # Convert error to failure
             self.fail('Unauthorized')
@@ -143,7 +145,7 @@
         # Test web security of a protected resource
         urllib._urlopener = UnauthorizedOpener()
         try: 
-            urllib.urlopen(folder_url+'/secret_html')
+            urllib.urlopen(self.folder_url+'/secret_html')
         except Unauthorized:
             pass    # Test passed
         else:
@@ -161,14 +163,10 @@
     def testURLModifyObject(self):
         # Test a transaction that actually commits something
         urllib._urlopener = ManagementOpener()
-        page = urllib.urlopen(folder_url+'/index_html/change_title?title=Foo').read()
+        page = urllib.urlopen(self.folder_url+'/index_html/change_title?title=Foo').read()
         self.assertEqual(page, 'Foo')
 
-    def testAbsoluteURL(self):
-        # Test absolute_url
-        self.assertEqual(self.folder.absolute_url(), folder_url)
 
-
 class TestSandboxedWebserver(ZopeTestCase.Sandboxed, TestWebserver):
     '''Demonstrates that tests involving ZServer threads can also be 
        run from sandboxes. In fact, it may be preferable to do so.
@@ -182,7 +180,7 @@
         # same connection as the main thread, allowing us to
         # see changes made to 'index_html' right away.
         urllib._urlopener = ManagementOpener()
-        urllib.urlopen(folder_url+'/index_html/change_title?title=Foo')
+        urllib.urlopen(self.folder_url+'/index_html/change_title?title=Foo')
         self.assertEqual(self.folder.index_html.title, 'Foo')
 
     def testCanCommit(self):

Modified: Zope/trunk/lib/python/Testing/ZopeTestCase/testZODBCompat.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/testZODBCompat.py	2007-10-13 16:02:33 UTC (rev 80863)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/testZODBCompat.py	2007-10-13 16:15:38 UTC (rev 80864)
@@ -25,7 +25,9 @@
 
 from Testing import ZopeTestCase
 
-import transaction
+from Testing.ZopeTestCase import layer
+from Testing.ZopeTestCase import utils
+from Testing.ZopeTestCase import transaction
 
 from AccessControl.Permissions import add_documents_images_and_files
 from AccessControl.Permissions import delete_objects
@@ -34,7 +36,36 @@
 folder_name = ZopeTestCase.folder_name
 cutpaste_permissions = [add_documents_images_and_files, delete_objects]
 
+# Dummy object
+from OFS.SimpleItem import SimpleItem
 
+class DummyObject(SimpleItem):
+    id = 'dummy'
+    foo = None
+    _v_foo = None
+    _p_foo = None
+
+
+
+class ZODBCompatLayer(layer.ZopeLite):
+
+    @classmethod
+    def setUp(cls):
+        def setup(app):
+            app._setObject('dummy1', DummyObject())
+            app._setObject('dummy2', DummyObject())
+            transaction.commit()
+        utils.appcall(setup)
+
+    @classmethod
+    def tearDown(cls):
+        def cleanup(app):
+            app._delObject('dummy1')
+            app._delObject('dummy2')
+            transaction.commit()
+        utils.appcall(cleanup)
+
+
 class TestCopyPaste(ZopeTestCase.ZopeTestCase):
 
     def afterSetUp(self):
@@ -159,22 +190,6 @@
             App.config.setConfiguration(config)
 
 
-# Dummy object
-from OFS.SimpleItem import SimpleItem
-
-class DummyObject(SimpleItem):
-    id = 'dummy'
-    foo = None
-    _v_foo = None
-    _p_foo = None
-
-app = ZopeTestCase.app()
-app._setObject('dummy1', DummyObject())
-app._setObject('dummy2', DummyObject())
-transaction.commit()
-ZopeTestCase.close(app)
-
-
 class TestAttributesOfCleanObjects(ZopeTestCase.ZopeTestCase):
     '''This testcase shows that _v_ and _p_ attributes are NOT bothered
        by transaction boundaries, if the respective object is otherwise
@@ -194,7 +209,9 @@
 
        This testcase exploits the fact that test methods are sorted by name.
     '''
-    
+
+    layer = ZODBCompatLayer
+
     def afterSetUp(self):
         self.dummy = self.app.dummy1 # See above
 
@@ -256,6 +273,8 @@
        This testcase exploits the fact that test methods are sorted by name.
     '''
 
+    layer = ZODBCompatLayer
+
     def afterSetUp(self):
         self.dummy = self.app.dummy2 # See above
         self.dummy.touchme = 1 # Tag, you're dirty

Modified: Zope/trunk/lib/python/Testing/ZopeTestCase/utils.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/utils.py	2007-10-13 16:02:33 UTC (rev 80863)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/utils.py	2007-10-13 16:15:38 UTC (rev 80864)
@@ -23,16 +23,15 @@
 import time
 import random
 import transaction
+import layer
 
 
-def setupCoreSessions(app=None):
+ at layer.appcall
+def setupCoreSessions(app):
     '''Sets up the session_data_manager e.a.'''
     from Acquisition import aq_base
     commit = 0
 
-    if app is None: 
-        return appcall(setupCoreSessions)
-
     if not hasattr(app, 'temp_folder'):
         from Products.TemporaryFolder.TemporaryFolder import MountedTemporaryFolder
         tf = MountedTemporaryFolder('temp_folder', 'Temporary Folder')
@@ -68,11 +67,9 @@
         transaction.commit()
 
 
-def setupZGlobals(app=None):
+ at layer.appcall
+def setupZGlobals(app):
     '''Sets up the ZGlobals BTree required by ZClasses.'''
-    if app is None: 
-        return appcall(setupZGlobals)
-
     root = app._p_jar.root()
     if not root.has_key('ZGlobals'):
         from BTrees.OOBTree import OOBTree
@@ -80,11 +77,9 @@
         transaction.commit()
 
 
-def setupSiteErrorLog(app=None):
+ at layer.appcall
+def setupSiteErrorLog(app):
     '''Sets up the error_log object required by ZPublisher.'''
-    if app is None: 
-        return appcall(setupSiteErrorLog)
-
     if not hasattr(app, 'error_log'):
         try:
             from Products.SiteErrorLog.SiteErrorLog import SiteErrorLog
@@ -135,13 +130,13 @@
     return _makerequest(app, stdout=stdout, environ=environ)
 
 
-def appcall(function, *args, **kw):
+def appcall(func, *args, **kw):
     '''Calls a function passing 'app' as first argument.'''
     from base import app, close
     app = app()
     args = (app,) + args
     try:
-        return function(*args, **kw)
+        return func(*args, **kw)
     finally:
         transaction.abort()
         close(app)

Modified: Zope/trunk/lib/python/Testing/ZopeTestCase/zopedoctest/testLayerExtraction.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/zopedoctest/testLayerExtraction.py	2007-10-13 16:02:33 UTC (rev 80863)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/zopedoctest/testLayerExtraction.py	2007-10-13 16:15:38 UTC (rev 80864)
@@ -20,9 +20,10 @@
 from Testing.ZopeTestCase import ZopeDocFileSuite
 from Testing.ZopeTestCase import ZopeDocTestSuite
 from Testing.ZopeTestCase import transaction
+from Testing.ZopeTestCase import layer
 
 
-class TestLayer:
+class TestLayer(layer.ZopeLite):
     """
     If the layer is extracted properly, we should see the following
     variable



More information about the Zope-Checkins mailing list