[Zope-Checkins] SVN: Zope/trunk/lib/python/Testing/ZopeTestCase/
added ZopeTestCase 0.9
Andreas Jung
andreas at andreas-jung.com
Mon Sep 6 15:04:42 EDT 2004
Log message for revision 27453:
added ZopeTestCase 0.9
Changed:
A Zope/trunk/lib/python/Testing/ZopeTestCase/
A Zope/trunk/lib/python/Testing/ZopeTestCase/PortalTestCase.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/ZopeLite.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/ZopeTestCase.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/__init__.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/doc/
A Zope/trunk/lib/python/Testing/ZopeTestCase/doc/API.stx
A Zope/trunk/lib/python/Testing/ZopeTestCase/doc/CHANGES.txt
A Zope/trunk/lib/python/Testing/ZopeTestCase/doc/ENVIRONMENT.txt
A Zope/trunk/lib/python/Testing/ZopeTestCase/doc/FunctionalTesting.stx
A Zope/trunk/lib/python/Testing/ZopeTestCase/doc/HOWTO.stx
A Zope/trunk/lib/python/Testing/ZopeTestCase/doc/INSTALL.stx
A Zope/trunk/lib/python/Testing/ZopeTestCase/doc/IZopeTestCase.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/doc/PROFILER.stx
A Zope/trunk/lib/python/Testing/ZopeTestCase/doc/PortalTestCase.stx
A Zope/trunk/lib/python/Testing/ZopeTestCase/doc/README.stx
A Zope/trunk/lib/python/Testing/ZopeTestCase/doc/SECURITY.stx
A Zope/trunk/lib/python/Testing/ZopeTestCase/doc/TIMELINES.txt
A Zope/trunk/lib/python/Testing/ZopeTestCase/doc/VERSION.txt
A Zope/trunk/lib/python/Testing/ZopeTestCase/framework.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/functional.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/profiler.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/runalltests.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/sandbox.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/testFunctional.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/testPortalTestCase.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/testPythonScript.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/testShoppingCart.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/testSkeleton.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/testWebserver.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/testZODBCompat.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/testZopeTestCase.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/threadutils.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/utils.py
A Zope/trunk/lib/python/Testing/ZopeTestCase/ztc_common.py
-=-
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/PortalTestCase.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/PortalTestCase.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/PortalTestCase.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,135 @@
+#
+# Abstract base test case for working with CMF-style portals
+#
+# This base class maintains a fixture consisting of:
+#
+# - a portal object (self.portal)
+# - a user folder inside the portal
+# - a default user with role 'Member' inside the user folder
+# - the default user's memberarea (self.folder)
+# - the default user is logged in
+#
+# The twist is that the portal object itself is *not* created
+# by the PortalTestCase class! Subclasses must make sure
+# getPortal() returns a usable portal object to the setup code.
+#
+
+# $Id: PortalTestCase.py,v 1.24 2004/03/29 01:14:14 shh42 Exp $
+
+import ZopeTestCase
+
+from AccessControl import getSecurityManager
+from AccessControl.SecurityManagement import newSecurityManager
+from Acquisition import aq_base
+
+portal_name = 'portal'
+user_name = ZopeTestCase.user_name
+
+
+class PortalTestCase(ZopeTestCase.ZopeTestCase):
+ '''Base test case for testing CMF-style portals
+
+ __implements__ = (IPortalTestCase, ISimpleSecurity, IExtensibleSecurity)
+
+ See doc/IZopeTestCase.py for more.
+ '''
+
+ _configure_portal = 1
+
+ def getPortal(self):
+ '''Returns the portal object for use by the setup
+ code. Will typically be overridden by subclasses
+ to return the object serving as the portal.
+ '''
+ return self.app[portal_name]
+
+ def createMemberarea(self, member_id):
+ '''Creates a memberarea for the specified member.
+ Subclasses may override to provide a customized
+ or more lightweight version of the memberarea.
+ '''
+ pm = self.portal.portal_membership
+ pm.createMemberarea(member_id)
+
+ def setUp(self):
+ '''Sets up the fixture. Do not override,
+ use the hooks instead.
+ '''
+ try:
+ self.beforeSetUp()
+ self.app = self._app()
+ self.portal = self.getPortal()
+ self._setup()
+ self._refreshSkinData()
+ self.afterSetUp()
+ except:
+ self._clear()
+ raise
+
+ def _setup(self):
+ '''Configures the portal. Framework authors may override.'''
+ if self._configure_portal:
+ self._setupUserFolder()
+ self._setupUser()
+ self.login()
+ self._setupHomeFolder()
+
+ def _setupUserFolder(self):
+ '''Creates the user folder if missing.'''
+ if not hasattr(aq_base(self.portal), 'acl_users'):
+ self.portal.manage_addUserFolder()
+
+ def _setupUser(self):
+ '''Creates the default user.'''
+ uf = self.portal.acl_users
+ uf._doAddUser(user_name, 'secret', ['Member'], [])
+
+ def _setupHomeFolder(self):
+ '''Creates the default user's home folder.'''
+ self.createMemberarea(user_name)
+ pm = self.portal.portal_membership
+ self.folder = pm.getHomeFolder(user_name)
+
+ def _refreshSkinData(self):
+ '''Refreshes the magic _v_skindata attribute.'''
+ if hasattr(self.portal, '_v_skindata'):
+ self.portal._v_skindata = None
+ if hasattr(self.portal, 'setupCurrentSkin'):
+ self.portal.setupCurrentSkin()
+
+ def _clear(self, call_close_hook=0):
+ '''Clears the fixture.'''
+ # No automagic cleanups here. We rely on
+ # transaction abort. Those who commit are
+ # required to clean up their own mess.
+ if call_close_hook:
+ self.beforeClose()
+ self._close()
+ self.logout()
+ self.afterClear()
+
+ # Security interfaces
+
+ def setRoles(self, roles, name=user_name):
+ '''Changes the user's roles.'''
+ uf = self.portal.acl_users
+ uf._doChangeUser(name, None, roles, [])
+ if name == getSecurityManager().getUser().getId():
+ self.login(name)
+
+ def setPermissions(self, permissions, role='Member'):
+ '''Changes the user's permissions.'''
+ self.portal.manage_role(role, permissions)
+
+ def login(self, name=user_name):
+ '''Logs in.'''
+ uf = self.portal.acl_users
+ user = uf.getUserById(name)
+ if not hasattr(user, 'aq_base'):
+ user = user.__of__(uf)
+ newSecurityManager(None, user)
+
+
+# b/w compatibility names
+_portal_name = portal_name
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/ZopeLite.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/ZopeLite.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/ZopeLite.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,172 @@
+#
+# Lightweight Zope startup
+#
+# Fast Zope startup is achieved by not installing (m)any
+# products. If your tests require a product you must
+# install it yourself using installProduct().
+#
+# Typically used as in
+#
+# import ZopeLite as Zope
+# Zope.installProduct('SomeProduct')
+# app = Zope.app()
+#
+
+# $Id: ZopeLite.py,v 1.19 2004/03/19 13:51:32 shh42 Exp $
+
+import os, sys, time
+
+# Increase performance on MP hardware
+sys.setcheckinterval(2500)
+
+# Shut up if we are not in control of the import process
+_quiet = 'Zope' in sys.modules.keys()
+
+def _print(msg):
+ '''Writes 'msg' to stderr and flushes the stream.'''
+ sys.stderr.write(msg)
+ sys.stderr.flush()
+
+def _write(msg):
+ '''Writes 'msg' to stderr if not _quiet.'''
+ if not _quiet:
+ _print(msg)
+
+def _exec(cmd):
+ '''Prints the time it takes to execute 'cmd'.'''
+ if os.environ.get('X', None):
+ start = time.time()
+ exec cmd
+ _print('(%.3fs)' % (time.time() - start))
+
+_write('Loading Zope, please stand by ')
+_start = time.time()
+
+# Zope 2.7 specifics
+try:
+ import App.config
+except ImportError:
+ pass # Zope < 2.7
+else:
+ # Need to import Zope early on as the
+ # ZTUtils package relies on it
+ config = App.config.getConfiguration()
+ config.debug_mode = 0
+ App.config.setConfiguration(config)
+ _exec('import Zope')
+ import Zope
+
+_exec('import ZODB')
+import ZODB
+_write('.')
+
+_exec('import Globals')
+import Globals
+# Work around a bug in Zope 2.7.0
+Globals.DevelopmentMode = 0
+_exec('import OFS.SimpleItem')
+import OFS.SimpleItem
+_exec('import OFS.ObjectManager')
+import OFS.ObjectManager
+_write('.')
+
+_exec('import OFS.Application')
+import OFS.Application
+import App.ProductContext
+
+# Avoid expensive product import
+def _null_import_products(): pass
+OFS.Application.import_products = _null_import_products
+
+# Avoid expensive product installation
+def _null_initialize(app): pass
+OFS.Application.initialize = _null_initialize
+
+# Avoid expensive help registration
+def _null_register_topic(self,id,topic): pass
+App.ProductContext.ProductContext.registerHelpTopic = _null_register_topic
+def _null_register_title(self,title): pass
+App.ProductContext.ProductContext.registerHelpTitle = _null_register_title
+def _null_register_help(self,directory='',clear=1,title_re=None): pass
+App.ProductContext.ProductContext.registerHelp = _null_register_help
+
+# Make sure to use a temporary client cache
+if os.environ.get('ZEO_CLIENT'): del os.environ['ZEO_CLIENT']
+
+# Load Zope (< 2.7)
+_exec('import Zope')
+import Zope
+_write('.')
+
+from OFS.Application import get_folder_permissions, get_products, install_product
+from OFS.Folder import Folder
+import Products
+
+_theApp = Zope.app()
+_installedProducts = {}
+
+def hasProduct(name):
+ '''Tests if a product can be found along Products.__path__'''
+ return name in [n[1] for n in get_products()]
+
+def installProduct(name, quiet=0):
+ '''Installs a Zope product.'''
+ start = time.time()
+ app = _theApp
+ meta_types = []
+ if not _installedProducts.has_key(name):
+ for priority, product_name, index, product_dir in get_products():
+ if product_name == name:
+ if not quiet: _print('Installing %s ... ' % product_name)
+ # We want to fail immediately if a product throws an exception
+ # during install, so we set the raise_exc flag.
+ install_product(app, product_dir, product_name, meta_types,
+ get_folder_permissions(), raise_exc=1)
+ _installedProducts[product_name] = 1
+ Products.meta_types = Products.meta_types + tuple(meta_types)
+ Globals.default__class_init__(Folder)
+ if not quiet: _print('done (%.3fs)\n' % (time.time() - start))
+ break
+ else:
+ if name != 'SomeProduct': # Ignore the skeleton tests :-P
+ if not quiet: _print('Installing %s ... NOT FOUND\n' % name)
+
+# Loading the Control_Panel of an existing ZODB may take
+# a while; print another dot if it does.
+_s = time.time(); _max = (_s - _start) / 4
+_exec('_theApp.Control_Panel')
+_cp = _theApp.Control_Panel
+if hasattr(_cp, 'initialize_cache'):
+ _cp.initialize_cache()
+if (time.time() - _s) > _max:
+ _write('.')
+
+installProduct('PluginIndexes', 1) # Must install first
+installProduct('OFSP', 1)
+#installProduct('ExternalMethod', 1)
+#installProduct('ZSQLMethods', 1)
+#installProduct('ZGadflyDA', 1)
+#installProduct('MIMETools', 1)
+#installProduct('MailHost', 1)
+
+# So people can use ZopeLite.app()
+app = Zope.app
+debug = Zope.debug
+DB = Zope.DB
+# startup appeared in Zope 2.6.1
+def startup(): pass
+# configure appeared in Zope 2.7
+try: configure = Zope.configure
+except AttributeError: pass
+
+from ZODB.DemoStorage import DemoStorage
+def sandbox(base=None):
+ '''Returns what amounts to a sandbox copy of the base ZODB.'''
+ if base is None: base = Zope.DB
+ base_storage = base._storage
+ quota = getattr(base_storage, '_quota', None)
+ storage = DemoStorage(base=base_storage, quota=quota)
+ return ZODB.DB(storage)
+
+_write(' done (%.3fs)\n' % (time.time() - _start))
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/ZopeTestCase.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/ZopeTestCase.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/ZopeTestCase.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,207 @@
+#
+# Default test case & fixture for Zope testing
+#
+# The fixture consists of:
+#
+# - a folder (self.folder)
+# - a user folder inside that folder
+# - a default user inside the user folder
+#
+# The default user is logged in and has the 'Access contents information'
+# and 'View' permissions given to his role.
+#
+
+# $Id: ZopeTestCase.py,v 1.15 2004/03/29 01:14:13 shh42 Exp $
+
+import ZopeLite as Zope
+
+import unittest
+import utils
+import profiler
+
+from AccessControl import getSecurityManager
+from AccessControl.SecurityManagement import newSecurityManager
+from AccessControl.SecurityManagement import noSecurityManager
+from AccessControl.Permissions import access_contents_information
+from AccessControl.Permissions import view
+
+folder_name = 'test_folder_1_'
+user_name = 'test_user_1_'
+user_role = 'test_role_1_'
+standard_permissions = [access_contents_information, view]
+
+_connections = utils.ConnectionRegistry()
+
+
+
+def app():
+ '''Opens a ZODB connection and returns the app object.'''
+ app = Zope.app()
+ _connections.register(app._p_jar)
+ return utils.makerequest(app)
+
+def close(app):
+ '''Closes the app's ZODB connection.'''
+ _connections.close(app._p_jar)
+
+def closeConnections():
+ '''Closes all registered ZODB connections.'''
+ _connections.closeAll()
+
+
+
+class ZopeTestCase(profiler.Profiled, unittest.TestCase):
+ '''Base test case for Zope testing
+
+ __implements__ = (IZopeTestCase, ISimpleSecurity, IExtensibleSecurity)
+
+ See doc/IZopeTestCase.py for more
+ '''
+
+ _setup_fixture = 1
+
+ def afterSetUp(self):
+ '''Called after setUp() has completed. This is
+ far and away the most useful hook.
+ '''
+ pass
+
+ def beforeTearDown(self):
+ '''Called before tearDown() is executed.
+ Note that tearDown() is not called if
+ setUp() fails.
+ '''
+ pass
+
+ def afterClear(self):
+ '''Called after the fixture has been cleared.
+ Note that this may occur during setUp() *and*
+ tearDown().
+ '''
+ pass
+
+ def beforeSetUp(self):
+ '''Called before the ZODB connection is opened,
+ at the start of setUp(). By default begins
+ a new transaction.
+ '''
+ get_transaction().begin()
+
+ def beforeClose(self):
+ '''Called before the ZODB connection is closed,
+ at the end of tearDown(). By default aborts
+ the transaction.
+ '''
+ get_transaction().abort()
+
+ def setUp(self):
+ '''Sets up the fixture. Do not override,
+ use the hooks instead.
+ '''
+ try:
+ self.beforeSetUp()
+ self.app = self._app()
+ self._setup()
+ self.afterSetUp()
+ except:
+ self._clear()
+ raise
+
+ def tearDown(self):
+ '''Tears down the fixture. Do not override,
+ use the hooks instead.
+ '''
+ try:
+ self.beforeTearDown()
+ self._clear(1)
+ except:
+ self._clear()
+ raise
+
+ def _app(self):
+ '''Returns the app object for a test.'''
+ return app()
+
+ def _setup(self):
+ '''Sets up the fixture. Framework authors may override.'''
+ if self._setup_fixture:
+ self._setupFolder()
+ self._setupUserFolder()
+ self._setupUser()
+ self.login()
+
+ def _setupFolder(self):
+ '''Creates and configures the folder.'''
+ self.app.manage_addFolder(folder_name)
+ self.folder = self.app._getOb(folder_name)
+ self.folder._addRole(user_role)
+ self.folder.manage_role(user_role, standard_permissions)
+
+ def _setupUserFolder(self):
+ '''Creates the user folder.'''
+ self.folder.manage_addUserFolder()
+
+ def _setupUser(self):
+ '''Creates the default user.'''
+ uf = self.folder.acl_users
+ uf._doAddUser(user_name, 'secret', [user_role], [])
+
+ def _clear(self, call_close_hook=0):
+ '''Clears the fixture.'''
+ if self._setup_fixture:
+ try: self.app._delObject(folder_name)
+ except (AttributeError, RuntimeError): pass
+ if call_close_hook:
+ self.beforeClose()
+ self._close()
+ self.logout()
+ self.afterClear()
+
+ def _close(self):
+ '''Closes the ZODB connection.'''
+ get_transaction().abort()
+ closeConnections()
+
+ # Security interfaces
+
+ def setRoles(self, roles, name=user_name):
+ '''Changes the user's roles.'''
+ uf = self.folder.acl_users
+ uf._doChangeUser(name, None, roles, [])
+ if name == getSecurityManager().getUser().getId():
+ self.login(name)
+
+ def setPermissions(self, permissions, role=user_role):
+ '''Changes the user's permissions.'''
+ self.folder.manage_role(role, permissions)
+
+ def login(self, name=user_name):
+ '''Logs in.'''
+ uf = self.folder.acl_users
+ user = uf.getUserById(name)
+ if not hasattr(user, 'aq_base'):
+ user = user.__of__(uf)
+ newSecurityManager(None, user)
+
+ def logout(self):
+ '''Logs out.'''
+ noSecurityManager()
+
+ # b/w compatibility methods
+
+ def _setRoles(self, roles, name=user_name):
+ self.setRoles(roles, name)
+ def _setPermissions(self, permissions, role=user_role):
+ self.setPermissions(permissions, role)
+ def _login(self, name=user_name):
+ self.login(name)
+ def _logout(self):
+ self.logout()
+
+
+# b/w compatibility names
+_folder_name = folder_name
+_user_name = user_name
+_user_role = user_role
+_standard_permissions = standard_permissions
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/__init__.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/__init__.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/__init__.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,43 @@
+#
+# Names exported by the ZopeTestCase module
+#
+
+# $Id: __init__.py,v 1.11 2004/02/06 18:00:02 shh42 Exp $
+
+import ZopeLite as Zope
+import utils
+
+from ZopeLite import installProduct
+from ZopeLite import hasProduct
+from ZopeLite import _print
+
+from ZopeTestCase import folder_name
+from ZopeTestCase import user_name
+from ZopeTestCase import user_role
+from ZopeTestCase import standard_permissions
+from ZopeTestCase import ZopeTestCase
+
+from PortalTestCase import portal_name
+from PortalTestCase import PortalTestCase
+
+from profiler import Profiled
+from sandbox import Sandboxed
+from functional import Functional
+
+from ZopeTestCase import app
+from ZopeTestCase import close
+from ZopeTestCase import closeConnections
+
+from unittest import main
+
+# Convenience class for functional unit testing
+class FunctionalTestCase(Functional, ZopeTestCase):
+ pass
+
+# b/w compatibility names
+_folder_name = folder_name
+_user_name = user_name
+_user_role = user_role
+_standard_permissions = standard_permissions
+_portal_name = portal_name
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/doc/API.stx
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/doc/API.stx 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/doc/API.stx 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,273 @@
+<style type="text/css"> <!-- li { margin: 1em } --> </style>
+
+ZopeTestCase API Reference
+
+ A nicely rendered version of this document can be found at
+ http://zope.org/Members/shh/ZopeTestCaseWiki/ApiReference
+
+
+
+Module Testing.ZopeTestCase
+
+ Top-level package exposing names from contained modules
+
+ Constants
+
+ folder_name
+
+ user_name
+
+ user_role
+
+ standard_permissions
+
+ portal_name
+
+ Functions
+
+ hasProduct(name)
+
+ installProduct(name, quiet=0)
+
+ app()
+
+ close(app)
+
+ main()
+
+ _print(msg)
+
+ Classes
+
+ ZopeTestCase
+
+ PortalTestCase
+
+ Profiled
+
+ Sandboxed
+
+ Functional
+
+ Modules
+
+ ZopeLite as Zope
+
+ utils
+
+
+
+Module ZopeLite
+
+ Lightweight replacement for the Zope module
+
+ Constants
+
+ DB
+
+ Functions
+
+ hasProduct(name)
+
+ installProduct(name, quiet=0)
+
+ app(connection=None)
+
+ sandbox(base=None)
+
+ startup()
+
+ _print(msg)
+
+
+
+Module ZopeTestCase
+
+ Default test case and fixture for Zope testing
+
+ Constants
+
+ folder_name
+
+ user_name
+
+ user_role
+
+ standard_permissions
+
+ Functions
+
+ app()
+
+ close(app)
+
+ Classes
+
+ ZopeTestCase
+
+
+
+Class ZopeTestCase
+
+ Base test case for Zope testing
+
+ Attributes
+
+ _setup_fixture = 1
+
+ Methods
+
+ afterSetUp()
+
+ beforeTearDown()
+
+ afterClear()
+
+ beforeSetUp()
+
+ beforeClose()
+
+ setRoles(roles, name=user_name)
+
+ setPermissions(permissions, role=user_role)
+
+ login(name=user_name)
+
+ logout()
+
+
+
+Module PortalTestCase
+
+ Test case and fixture for testing CMF-based applications
+
+ Constants
+
+ portal_name
+
+ user_name
+
+ Classes
+
+ PortalTestCase
+
+
+
+Class PortalTestCase
+
+ Base test case for CMF testing
+
+ Attributes
+
+ _configure_portal = 1
+
+ Methods
+
+ getPortal()
+
+ createMemberarea(name)
+
+ afterSetUp()
+
+ beforeTearDown()
+
+ afterClear()
+
+ beforeSetUp()
+
+ beforeClose()
+
+ setRoles(roles, name=user_name)
+
+ setPermissions(permissions, role=user_role)
+
+ login(name=user_name)
+
+ logout()
+
+
+
+Module profiler
+
+ Profiling support
+
+ Functions
+
+ runcall(func, *args, **kw)
+
+ print_stats()
+
+ dump_stats(filename)
+
+ Classes
+
+ Profiled
+
+
+
+Class Profiled
+
+ Profiling support mix-in for xTestCases
+
+ Methods
+
+ runcall(func, *args, **kw)
+
+
+
+Module sandbox
+
+ ZODB sandbox support
+
+ Classes
+
+ Sandboxed
+
+
+
+Class Sandboxed
+
+ Sandbox support mix-in for xTestCases
+
+ Methods
+
+ *No public methods*
+
+
+
+Module functional
+
+ Functional testing support
+
+ Classes
+
+ Functional
+
+
+
+Class Functional
+
+ Functional testing mix-in for xTestCases
+
+ Methods
+
+ publish(path, basic=None, env=None, extra=None, request_method='GET')
+
+
+
+Module utils
+
+ Utility functions to extend the test environment
+
+ Functions
+
+ setupCoreSessions(app=None)
+
+ setupZGlobals(app=None)
+
+ setupSiteErrorLog(app=None)
+
+ startZServer(number_of_threads=1, log=None)
+
+ importObjectFromFile(container, filename, quiet=0)
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/doc/CHANGES.txt
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/doc/CHANGES.txt 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/doc/CHANGES.txt 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,104 @@
+0.9.0
+- No longer support Zope 2.4 as its DemoStorage is broken.
+- Made PortalTestCase derive from ZopeTestCase (again).
+- Made all xTestCases profiler aware by default.
+- Renamed the Profiler module to profiler.py (lowercase).
+- Added support for ZODB sandboxes, sandbox.py.
+- Added support for functional unit testing, functional.py.
+- The profiler module now provides a dump_stats() method to write
+ profiler statistics to a file for manual inspection.
+- The REQUEST now fakes a published object to make the URL1
+ request variable available to tests. Thanks to Alan Runyan.
+- startZServer() now accepts a log argument, allowing to pass
+ a stream which the ZServer access log (Z2.log) will be written to.
+- The 'app' argument of utility functions is now optional.
+- Fixed custom_zodb.py support for Zope 2.7.
+- Most mercilessly refactored ztc_common.py.
+- ZopeLite now loads silently if it does not control the import process.
+
+0.8.6
+- Revised and amended much of the existing documentation.
+- Added an API reference (skeleton), API.stx.
+- Documented what's going on when tests are run in TIMELINES.txt.
+- Fixed issues with testZODBCompat.py and Zope < 2.6.
+- setupZGlobals() now uses a new-style BTrees.OOBTree.
+- Profiling can now be activated from the command line.
+
+0.8.4
+- framework.py now flushes stdout to not mess up the output in batch mode.
+- framework.py no longer adds os.pardir to the sys.path. Thanks to
+ Yoshinori Okuji.
+- Made sure user objects are not inadvertently wrapped twice by login().
+- Made sure "renegade" transactions are aborted if something goes wrong
+ during the setup phase.
+- initialize_cache() is no longer called for Zope 2.7.
+
+0.8.2
+- Removed the leading underscores from all constant names. They proved
+ non-private in "real life" anyway. The old names are still available
+ for backward compatibility, but are deprecated.
+- Removed NO_PRODUCT_LOAD for reasons of obscureness and YAGNI.
+- Added a test for ZODB behavior in ZTC, testZODBCompat.py.
+
+0.8.0
+- Added a PortalTestCase base class to aid testing of CMF-style portals.
+- Added simple profiling support using the Python profile library.
+- Got rid of the ill-conceived FX interface (don't even ask).
+- ZopeLite now supports Zope 2.7.
+
+0.7.2 (not released)
+- ZopeLite gained a do-nothing startup() method for API compliance.
+- The ZopeTestCase module now has a main() method like unittest has.
+- Made sure the test user's 'roles' attribute is a list because CMF
+ role-mapping assumes it can append to it. :-/
+
+0.7.0
+- Fixed a bug that caused setRoles() to only work with the
+ default user folder. Refactored the fixture code in the process.
+- Reworked the connection registry and wrote tests for it.
+- Made afterClear() largely redundant because it turned out to be just that.
+- Added close() method to be able to close ZODB connections individually.
+- Added ISimpleSecurity and IExtensibleSecurity interfaces.
+
+0.6.4
+- installProduct() now immediately fails if a product throws an
+ exception during installation. Thanks to Tom Jenkins.
+- The REQUEST no longer contains the entire shell environment.
+- Moved all documentation files to the 'doc' subdirectory.
+- Added IZopeTestCase and IZopeTestCaseFX interfaces.
+
+0.6.2
+- The effects of setting INSTANCE_HOME have been changed to something
+ less surprising. Please see ENVIRONMENT.txt for details.
+- Now uses the environment variable ZEO_INSTANCE_HOME to enable ZEO
+ support.
+
+0.6.0
+- Use a module-level database connection registry to avoid freezing
+ after too many errors.
+- All tests are now transactional by default.
+- Added beforeSetUp() and beforeClose() hooks to the ZopeTestCase class.
+- Added utility method importObjectFromFile()
+- Added utility method setupSiteErrorLog().
+- Added utility method startZServer().
+- Added accompanying test, testWebserver.py.
+- Added first incarnation of a How-To.
+- Revised the example tests.
+
+0.5.3
+- Zope 2.6 compatibility adjustments.
+- Hardening in the face of incomplete Zope installations.
+
+0.5.2
+- Delete ZEO_CLIENT environment variable to enforce a temporary client
+ cache. Repair Zope 2.4 Testing package issue in the process.
+- Provide NO_PRODUCT_LOAD environment variable for completeness.
+- Added hasProduct() method to allow testing for product availability.
+- Added new utility method setupZGlobals().
+- Added a skeleton test suite, testSkeleton.py.
+- Added runalltests.py script.
+- Added CHANGES, INSTALL, and VERSION documents.
+
+0.5.0
+- Unit and regression testing framework for Zope. Initial release.
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/doc/ENVIRONMENT.txt
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/doc/ENVIRONMENT.txt 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/doc/ENVIRONMENT.txt 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,48 @@
+
+ZTC makes the following assumptions about its environment:
+
+a) The 'ZopeTestCase' package is installed in the Zope "trunk" inside the
+ 'Testing' module, which means: SOFTWARE_HOME/Testing/ZopeTestCase.
+
+b) A 'Products' directory exists inside SOFTWARE_HOME and INSTANCE_HOME.
+
+c) The tests (the 'tests' subdirectories) are located either below a
+ SOFTWARE_HOME or INSTANCE_HOME, typically in Products/MyCoolProduct/tests.
+
+d) The somewhat weak assumption is that ZTC can walk up the directory tree from
+ 'tests', and find a 'Products' directory. This is how INSTANCE_HOME
+ detection works. It regrettably fails on some filesystems when symbolic
+ links are involved (a solution is detailed below, so hang on).
+
+
+The non-trivial part is that INSTANCE_HOME has two distinct purposes:
+
+ 1) INSTANCE_HOME/lib/python must be added to sys.path and
+ INSTANCE_HOME/Products to Products.__path__.
+
+ 2) INSTANCE_HOME/custom_zodb.py must be used to set up a ZODB.
+
+
+ZTC attempts to resolve this by detecting an INSTANCE_HOME for 1) but leaving
+the actual environment variable untouched so 2) works by still pointing into
+SOFTWARE_HOME/Testing.
+
+As soon as I allow you to set INSTANCE_HOME yourself, I lose the ability to
+distinguish whether you mean 1) or 2) or both.
+
+Before ZTC 0.6.2 the code assumed "both" and did the magic ZEO dance. This was
+clearly too surprising.
+
+The behaviour has now been changed to 1).
+
+That way, if your setup does not fit c) or you run into the symbolic link
+problem d), you can solve it by setting INSTANCE_HOME prior to running the
+tests.
+
+ZEO support ("both") is handled separately, through the new environment
+variable ZEO_INSTANCE_HOME.
+
+
+You may want to consider using a testrunner to run your tests. You can find one
+here: http://zope.org/Members/shh/TestRunner
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/doc/FunctionalTesting.stx
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/doc/FunctionalTesting.stx 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/doc/FunctionalTesting.stx 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,47 @@
+<style type="text/css"> <!-- li { margin: 1em } --> </style>
+
+Functional Testing Readme
+
+ The functional testing support of ZopeTestCase was inspired by
+ Marius Gedminas' work for Zope 3.
+
+ Deriving from the 'Functional' mix-in (and an xTestCase) adds a
+ 'publish' method to your test case class. Tests can call
+ 'self.publish(path, basic=None, env=None, extra=None, request_method='GET')',
+ passing a path and, optionally, basic-auth info and form data.
+ The path may contain a query string.
+
+ 'publish' returns an enhanced Response object, that can be queried
+ for status, response body, headers, etc.
+
+ 'publish' invokes the ZPublisher machinery just as if the request
+ had come in through ZServer. This allows for high-level testing
+ of things like argument marshalling, form validation, and traversal.
+
+ Note that the tests have *full access to the ZODB*. This means you
+ can easily prepare a fixture for 'publish' and/or check the impact
+ of a publication on the database. This represents a major advantage
+ over purely URL-based test environments!
+
+ Please see the 'testFunctional.py' example test for more.
+
+ While the modules are called 'functional.py' in both Zope 3 and
+ ZopeTestCase, it is current wisdom that such tests are not truly
+ "functional tests", but rather "integration tests".
+
+ True functional tests, in their most-helpful guise as "acceptance
+ tests", must be able to test the end-user experience. For web
+ applications this means: browser simulation.
+
+ Plone 2 comes with an 'ftests' package combining the functional
+ testing support of ZopeTestCase with the "mechanize" browser
+ simulator library: http://wwwsearch.sourceforge.net/mechanize/
+ (For some version of 2, currently only available from the Plone
+ CVS HEAD.)
+
+Read the Source
+
+ Amen. Read 'functional.py' and 'sandbox.py' if you want to know
+ what's going on behind the scenes.
+
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/doc/HOWTO.stx
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/doc/HOWTO.stx 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/doc/HOWTO.stx 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,238 @@
+<style type="text/css"> <!-- li { margin: 1em } --> </style>
+
+ZopeTestCase Mini How-To
+
+ (Still-terse-but-the-Wiki-has-more Version 0.3)
+
+Introduction
+
+ *ZopeTestCase* is an attempt to make writing unit and regression tests
+ in Zope a more rewarding experience. The package deals with all the plumbing
+ required, and leaves the programmer with writing the tests - and the tests only.
+ It is built on PyUnit and the Testing package coming with Zope.
+
+ ZopeTestCase provides a Zope sandbox to every test. This sandbox comes fully
+ equipped with ZODB connection, root application object, and REQUEST.
+ When writing tests you have complete control over Zope, the way you have from a Python
+ product. The framework is easily customized and can be adapted to virtually every
+ conceivable test situation.
+
+ What you can test with ZTC
+
+ - Basically everything. Including security, transactions, sessions, web access, ...
+
+ What you cannot test with ZTC
+
+ - Web forms and user interaction. This should be left to FunctionalTests.
+
+Prereqs
+
+ For an excellent introduction to unit testing read chapter 11 of Mark Pilgrim's
+ "Dive Into Python.":http://diveintopython.org/unit_testing/index.html
+
+ Read about what unit tests are and what they are not in Chris McDonough's
+ "Unit Testing Zope.":http://zope.org/Members/mcdonc/PDG/UnitTesting
+
+ Download and install "ZopeTestCase.":http://zope.org/Members/shh/ZopeTestCase
+ Be prepared to read the source files, especially the example tests. And the README of course.
+
+What You Get
+
+ The ZopeTestCase package consists of the following components:
+
+ framework.py
+
+ This file must be copied into the individual 'tests' directories (See the README for more).
+ It locates the Testing and ZopeTestCase packages, detects INSTANCE_HOME installations,
+ and determines the 'custom_zodb.py' file used to set up the test ZODB. 'framework.py' is
+ included first thing in every test module.
+
+ ZopeLite.py
+
+ This module is designed to work like the 'Zope' module, but to load a lot faster.
+ The speed-up is achieved by not installing Zope products and help files. If your
+ tests require a product to be installed (most don't actually), you must install
+ it yourself using the 'installProduct' method. 'ZopeLite' also provides a
+ 'hasProduct' method, that allows to test whether a product can be found along the
+ products path, i.e. whether it is available to the test instance.
+
+ ZopeTestCase.py
+
+ This module provides the 'ZopeTestCase' class, which is used as a base class for
+ all test cases.
+
+ A ZopeTestCase sets up a default fixture in the newly started Zope instance.
+ This fixture consist of a folder, a userfolder inside that folder, and a
+ user created in that userfolder. The user's role is configured at folder level
+ and populated with default permissions. Finally, the user is logged in.
+
+ This way, once a test runs, a complete and sane Zope "toy" environment is
+ already in place.
+
+ The fixture is destroyed during the tearDown phase and can be switched off
+ entirely by setting '_setup_fixture=0' in your test case.
+
+ The ZopeTestCase class provides a number of hooks that allow you to
+ adapt the fixture to your needs:
+
+ - **'afterSetUp'** is called after the default fixture has been set up. Override this
+ method to add the objects you intend to test.
+
+ *This is clearly the most useful hook. You may ignore the remaining hooks until you
+ really need them, if ever.*
+
+ - **'beforeTearDown'** is called at the start of the tearDown phase. The fixture
+ is still in place at this point.
+
+ - **'afterClear'** is called after the fixture has been destroyed. Note that this
+ may occur during setUp *and* tearDown. This method must not throw an exception
+ even when called repeatedly.
+
+ Hooks for controlling transactions:
+
+ - **'beforeSetUp'** is called before the ZODB connection is opened, at the start of setUp.
+ The default behaviour of this hook is to call 'get_transaction().begin()'.
+ You will rarely want to override this.
+
+ - **'beforeClose'** is called before the ZODB connection is closed, at the end of
+ tearDown. By default this method calls 'get_transaction().abort()' to discard
+ any changes made by the test. In some situations you may need to override
+ this hook and commit the transaction instead. Make sure you really know what
+ you are doing though.
+
+ utils.py
+
+ Provides utility methods to extend the test environment:
+
+ - **'setupCoreSessions'** creates the Zope sessioning objects in the test ZODB.
+
+ - **'setupZGlobals'** creates the ZGlobals BTree required by ZClasses.
+
+ - **'setupSiteErrorLog'** creates the error_log object required by ZPublisher.
+
+ - **'startZServer'** starts a ZServer thread on the local host. Use this if the
+ objects you test require URL access to Zope.
+
+ - **'importObjectFromFile'** imports a (.zexp) file into a specified container.
+ Handy for adding "prerolled" components to the sandbox.
+
+ These methods must be run at module level. Do not call them from 'afterSetUp' or from tests!
+
+Writing Tests
+
+ Generally, writing tests with ZTC is no different from writing "ordinary" Zope code. A complete
+ Zope environment is made available to every test. The only real difference is that it is not
+ driven by ZServer + ZPublisher but by the PyUnit TestRunner.
+
+ Inside a ZopeTestCase the root application object can be accessed as 'self.app'.
+ The folder serving as the fixture is available as 'self.folder'. The REQUEST is
+ reachable as 'self.app.REQUEST'.
+
+ 1. In your test module create a test case derived from 'ZopeTestCase'.
+
+ 2. Override 'afterSetUp' to add your own objects to 'self.folder'.
+
+ 3. Write one or more tests exercising these objects.
+
+ How to setup and run your tests is covered in detail by the
+ "README":http://zope.org/Members/shh/ZopeTestCase/README
+
+ The easiest way to start with your own tests is to copy the skeleton test
+ 'testSkeleton.py', and take it from there.
+
+ Note that tests are written in unrestricted Python and are not affected by Zope's
+ security policy. To test the security of individual methods or objects, you must invoke
+ 'restrictedTraverse' or 'validateValue' explicitly!
+
+ A simple test may look like::
+
+ from Testing import ZopeTestCase
+ from AccessControl import Unauthorized
+
+ class ExampleTest(ZopeTestCase.ZopeTestCase):
+
+ def afterSetUp(self):
+ # Add objects to the workarea
+ self.folder.addDTMLMethod('doc', file='foo')
+
+ def testDocument(self):
+ self.assertEqual(self.folder.doc(), 'foo')
+
+ def testEditDocument(self):
+ self.folder.doc.manage_edit('bar', '')
+ self.assertEqual(self.folder.doc(), 'bar')
+
+ def testAccessDocument(self):
+ self.folder.doc.manage_permission('View', ['Manager'])
+ self.assertRaises(Unauthorized,
+ self.folder.restrictedTraverse, 'doc')
+
+ Read the Examples
+
+ Please study the example tests for more:
+
+ - **'testSkeleton.py'** represents the simplest possible ZopeTestCase. The module contains all
+ the plumbing required. It is recommended that you copy this file to start your own tests.
+
+ - **'testPythonScript.py'** tests a PythonScript object in the default fixture.
+ It demonstrates how to manipulate the test user's roles and permissions and how
+ security is validated.
+
+ - **'testShoppingCart.py'** tests the ShoppingCart example. This test
+ uses Sessions and shows how to test a TTW Zope application.
+
+ - **'testFunctional.py'** demonstrates the new functional testing features.
+ Tests may call 'self.publish()' to simulate URL calls to the ZPublisher.
+
+ - **'testWebserver.py'** starts up an HTTP ZServer thread and tests URL access to it. This
+ test is an example for explicit transaction handling and shows how to use ZODB
+ sandboxes to further increase isolation between individual tests.
+
+ - **'testZopeTestCase.py'** tests the ZopeTestCase class itself. May be of interest to the
+ investigative types.
+
+ - **'testPortalTestCase.py'** contains an equivalent test suite for the PortalTestCase
+ base class.
+
+ - **'testZODBCompat.py'** tests various aspects of ZODB behavior in a ZopeTestCase environment.
+ Shows things like cut/paste, import/export, and that _v_ and _p_ variables survive
+ transaction boundaries.
+
+ Read the Source
+
+ The source files are well documented and an overall worthy read <wink>:
+
+ - The ZopeTestCase class is defined in file 'ZopeTestCase.py'.
+
+ - The interfaces implemented by this class are documented in 'doc/IZopeTestCase.py'.
+
+ - All names exported by the ZopeTestCase package are listed in '__init__.py'.
+
+Resources
+
+ A Wiki with all the documentation:
+ "ZopeTestCaseWiki":http://zope.org/Members/shh/ZopeTestCaseWiki
+
+ Slides of my talk at EuroPython 2003:
+ "UnitTestingZope":http://zope.org/Members/shh/UnitTestingZope.pdf
+
+ A testrunner with INSTANCE_HOME support:
+ "TestRunner":http://zope.org/Members/shh/TestRunner
+
+Related
+
+ "PyUnit":http://pyunit.sf.net (Steve Purcell)
+
+ "WebUnit":http://mechanicalcat.net/tech/webunit (Richard Jones) and
+ "WebUnit":http://webunit.sf.net (Steve Purcell)
+
+ "ZUnit":http://zope.org/Members/lalo/ZUnit (Lalo Martins)
+
+ "FunctionalTests":http://zope.org/Members/tseaver/FunctionalTests (Tres Seaver)
+
+ "WebsiteLoadTest":http://zope.org/Members/ajung/WebsiteLoadTest (Andreas Jung)
+
+Contact
+
+ Feel free to send bug reports, feature requests, etc to stefan at epy.co.at.
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/doc/INSTALL.stx
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/doc/INSTALL.stx 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/doc/INSTALL.stx 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,27 @@
+<style type="text/css"> <!-- li { margin: 1em } --> </style>
+
+Installation Instructions for ZopeTestCase
+
+ Requires Python 2.1 and Zope 2.5 or higher
+
+ Upgrade Notice: Please remove an existing installation
+ of ZopeTestCase before installing version 0.9.0!
+
+ 1. Extract the tarball into the 'lib/python/Testing'
+ directory of your Zope installation.
+
+ 2. Cd into the ZopeTestCase directory and execute
+ 'python runalltests.py' to test the package and to
+ make sure all modules get compiled.
+
+ You must use the same Python that is running your
+ Zope here. On Windows this may for example be::
+
+ "C:\Program Files\Zope\bin\python.exe" runalltests.py
+
+ 3. See the HOWTO for the big picture, the README for
+ getting started with your own tests.
+
+ Visit the "ZopeTestCaseWiki":http://zope.org/Members/shh/ZopeTestCaseWiki
+ for additional documentation.
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/doc/IZopeTestCase.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/doc/IZopeTestCase.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/doc/IZopeTestCase.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,108 @@
+from Interface import Interface
+
+# $Id: IZopeTestCase.py,v 1.13 2004/02/21 18:54:38 shh42 Exp $
+
+
+#
+# ZopeTestCase.__implements__ = (
+# IZopeTestCase, ISimpleSecurity, IExtensibleSecurity)
+#
+# PortalTestCase.__implements__ = (
+# IPortalTestCase, ISimpleSecurity, IExtensibleSecurity)
+#
+
+
+class ISimpleSecurity(Interface):
+
+ def setRoles(roles):
+ '''Changes the user's roles.'''
+
+ def setPermissions(permissions):
+ '''Changes the user's permissions.'''
+
+ def login():
+ '''Logs in.'''
+
+ def logout():
+ '''Logs out.'''
+
+
+class IExtensibleSecurity(Interface):
+
+ def setRoles(roles, name):
+ '''Changes the roles assigned to a user.'''
+
+ def setPermissions(permissions, role):
+ '''Changes the permissions assigned to a role.'''
+
+ def login(name):
+ '''Logs in as the specified user.'''
+
+ def logout():
+ '''Logs out.'''
+
+
+class IZopeTestCase(Interface):
+
+ def afterSetUp():
+ '''Called after setUp() has completed. This is
+ far and away the most useful hook.
+ '''
+
+ def beforeTearDown():
+ '''Called before tearDown() is executed.
+ Note that tearDown() is not called if
+ setUp() fails.
+ '''
+
+ def afterClear():
+ '''Called after the fixture has been cleared.
+ Note that this is done during setUp() *and*
+ tearDown().
+ '''
+
+ def beforeSetUp():
+ '''Called before the ZODB connection is opened,
+ at the start of setUp(). By default begins a
+ new transaction.
+ '''
+
+ def beforeClose():
+ '''Called before the ZODB connection is closed,
+ at the end of tearDown(). By default aborts
+ the transaction.
+ '''
+
+
+class IPortalTestCase(IZopeTestCase):
+
+ def getPortal():
+ '''Returns the portal object for use by the setup
+ code. Will typically be overridden by subclasses
+ to return the object serving as the portal.
+ '''
+
+ def createMemberarea(member_id):
+ '''Creates a memberarea for the specified member.
+ Subclasses may override to provide a customized
+ or more lightweight version of the memberarea.
+ '''
+
+
+class IProfiled(Interface):
+
+ def runcall(func, *args, **kw):
+ '''Allows to run a function under profiler control
+ adding to the accumulated profiler statistics.
+ '''
+
+
+class IFunctional(Interface):
+
+ def publish(path, basic=None, env=None, extra=None, request_method='GET'):
+ '''Publishes the object at 'path' returning an
+ extended response object. The path may contain
+ a query string.
+ '''
+
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/doc/PROFILER.stx
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/doc/PROFILER.stx 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/doc/PROFILER.stx 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,19 @@
+<style type="text/css"> <!-- li { margin: 1em } --> </style>
+
+Profiler Readme
+
+ Since version 0.9.0 all xTestCases are profiler aware by default.
+
+ You can run your tests under profiler control like this::
+
+ python testSomething.py profile
+
+ If you want to profile fixture creation or destruction type one of::
+
+ python testSomething.py profile-setup
+ python testSomething.py profile-teardown
+
+ Profiler statistics will be printed after the test results.
+
+ See the API reference for more on the 'profiler' module.
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/doc/PortalTestCase.stx
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/doc/PortalTestCase.stx 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/doc/PortalTestCase.stx 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,93 @@
+<style type="text/css"> <!-- li { margin: 1em } --> </style>
+
+PortalTestCase Readme
+
+ The PortalTestCase class is a close relative of the ZopeTestCase
+ class. It was devised at the Plone Castle Sprint to form the base of an
+ integration testing framework for Plone 2.0. Thanks to Gidon Friedman
+ and Godefroid Chapelle for their collaboration.
+
+
+Much of what is true for ZopeTestCase is true for PortalTestCase as well:
+
+ * PortalTestCase handles the ZODB connection, transaction, and
+ application object; and it provides a REQUEST.
+
+ * PortalTestCase sets up a user folder and a user.
+
+ * PortalTestCase provides the same hooks as ZopeTestCase.
+
+ * PortalTestCase implements the same security interfaces as
+ ZopeTestCase.
+
+
+What's different?
+
+ * PortalTestCase is designed for testing CMF-based applications.
+
+ * The fixture is slightly more complex, consisting of a portal object,
+ a userfolder + user, and the user's memberarea.
+
+ * For flexibility, the portal is *not* created by the base class but
+ must be provided by the user (typically a derived xTestCase) of the
+ PortalTestCase base class.
+
+ * Subclasses will have to override 'getPortal' to return the object
+ serving as the portal.
+
+ * The portal will however be configured by the machinery which means
+ creating a user and a fresh memberarea for every test.
+
+ * Subclasses may override 'createMemberarea' to provide customized
+ and/or more lightweight memberareas to the tests. This can improve
+ performance quite significantly.
+
+
+Feature Comparison:
+
+ ZopeTestCase
+
+ * 1 user + 1 role
+
+ * Folder contains user folder and role definition
+
+ * Folder also serves as workarea
+
+ * User is logged in
+
+ * Provides attributes::
+
+ self.app
+ self.app.REQUEST
+ self.folder
+ self.folder.acl_users
+
+ PortalTestCase
+
+ * 1 user + 'Member' role
+
+ * Portal contains user folder and role definition
+
+ * User's home folder serves as workarea
+
+ * User is logged in
+
+ * Provides attributes::
+
+ self.app
+ self.app.REQUEST
+ self.portal
+ self.portal.acl_users
+ self.folder
+
+
+Read the Source
+
+ As always, I recommend to look at the source code of both
+ 'ZopeTestCase.py' and 'PortalTestCase.py' for all the details you may need.
+ Interface documentation can be found in 'doc/IZopeTestCase.py'.
+
+ The test framework shipping with Plone 2.0 is a good example of how the
+ PortalTestCase class can be put to use.
+
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/doc/README.stx
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/doc/README.stx 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/doc/README.stx 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,89 @@
+<style type="text/css"> <!-- li { margin: 1em } --> </style>
+
+ZopeTestCase Readme
+
+ The ZopeTestCase package has been developed in the hope that it will make
+ testing Zope packages more convenient. It has features to support various
+ scenarios from unit-testing individual components inside a "toy" environment
+ to running regression tests against live ZEO servers.
+
+ To add a test suite to a Zope package:
+
+ 1. Make a 'tests' subdirectory.
+
+ 2. Create an (empty) '__init__.py' in 'tests' to make it a package.
+
+ 3. Copy 'framework.py' from the 'ZopeTestCase' package into 'tests'.
+
+ Once a test suite has been set up, you can add test modules:
+
+ 1. Create a file with a name matching 'test*.py'.
+
+ 2. Import the 'ZopeTestCase' package as in 'from Testing import ZopeTestCase'
+ and define one or more subclasses of 'ZopeTestCase.ZopeTestCase'.
+
+ 3. Define methods for the test classes. Each method's name must start
+ with 'test'. It should test one small case, preferably using a PyUnit
+ assertion method. Here's a minimal example::
+
+ class ExampleTest(ZopeTestCase.ZopeTestCase):
+ def testAddition(self):
+ self.assertEqual(1+1, 2)
+
+ 4. You can add 'afterSetUp' and 'beforeTearDown' methods that are automatically
+ called after the fixture has been set up and before the fixture is destroyed
+ respectively.
+
+ 5. Follow the instructions in 'framework.py' about adding lines to the
+ top and bottom of the file.
+
+ Now you can run the test as 'python path/to/tests/testName.py', or
+ simply go to the 'tests' directory and type 'python testName.py'.
+
+ Note that there is a skeleton test suite named 'testSkeleton.py' that you
+ may copy into your 'tests' directory and take it from there.
+
+ Note also that when the tests are run in an INSTANCE_HOME installation of
+ Zope, you must set the SOFTWARE_HOME environment variable for the 'Testing'
+ and 'ZopeTestCase' packages to be found.
+
+ See the sample tests in the 'ZopeTestCase' directory for details on writing
+ your own tests.
+
+framework.py
+
+ 1. Uses SOFTWARE_HOME (if set) to locate the Testing package.
+
+ 2. Detects and handles INSTANCE_HOME installations of Zope. Please
+ see ENVIRONMENT.txt for the assumptions ZTC makes about its
+ environment.
+
+ 3. Supports setting up a ZODB from a 'custom_zodb.py' file in
+ the 'tests' directory.
+
+ 4. Allows to connect to a running ZEO server by setting the
+ ZEO_INSTANCE_HOME environment variable.
+
+testrunner.py
+
+ Alternatively, you may use Zope's testrunner utility to run your tests
+ ('testrunner.py' can be found in the 'utilities' directory of your Zope
+ installation). If you do so, you will have to define a 'test_suite' method
+ in your modules (see examples).
+
+ There is no need to set SOFTWARE_HOME when using the testrunner but you may
+ have to provide the -i flag when testing in an INSTANCE_HOME setup.
+
+ Example: 'python /path/to/Zope/utilities/testrunner.py -q -i -a'
+
+ If your testrunner does not appear to support the -i flag get the one from
+ 'http://zope.org/Members/shh/TestRunner'
+
+ Note that the 'custom_zodb.py' magic (3. + 4.) is not available when using
+ the testrunner.
+
+ If you have tests that should not be picked up by the testrunner, make a
+ 'test_suite' method that returns an empty TestSuite.
+
+ Note that in Zope 2.7 the testrunner lives in '/path/to/Zope/bin'.
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/doc/SECURITY.stx
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/doc/SECURITY.stx 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/doc/SECURITY.stx 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,57 @@
+<style type="text/css"> <!-- li { margin: 1em } --> </style>
+
+Default Fixture
+
+ - **'self.app'** is the root application object of the test ZODB (contains Control_Panel, ...)
+
+ Note that a ZODB connections has already been opened and a transaction begun at this point.
+
+ - **'self.app.REQUEST'** is the request object. Note that the REQUEST is rather minimal because
+ ZPublisher is not involved when running tests, and as such many REQUEST variables are never
+ set. Feel free to add to the REQUEST whatever your tests require.
+
+ - **'self.folder'** is the work area. This folder will be created anew for each test and thrown
+ away once the test has finished. The name of the folder is 'test_folder_1_'. You should
+ use the 'ZopeTestCase.folder_name' constant when you need the folder's name. 'self.folder' is a
+ reference to the object at 'self.app[folder_name]'.
+
+ A default role definition ('ZopeTestCase.user_role') is added to the folder, and a list of
+ permissions ('ZopeTestCase.standard_permissions') is assigned to the role.
+
+ - **'self.folder.acl_users'** is the user folder providing a security context to the work area.
+
+ A default user account is added to the user folder with name 'test_user_1_' and password 'secret'.
+ You should use the 'ZopeTestCase.user_name' constant when you need the user's name.
+
+ The default user has a single role, 'ZopeTestCase.user_role'.
+
+ At the end of the setup process the default user is logged in, and the 'afterSetUp' hook is called.
+
+Security API
+
+ - **'self.setRoles(roles, name=user_name)'** allows to change the roles assigned to a user.
+ If the 'name' argument is omitted, changes the roles of the default user.
+
+ - **'self.setPermissions(permissions, role=user_role)'** allows to change the permissions
+ assigned to a role. If the 'role' argument is omitted, changes the permissions of the
+ default role.
+
+ - **'self.login(name=user_name)'** allows to log in as a specified user.
+ If the 'name' argument is omitted, logs in as the default user.
+
+ - **'self.logout()'** allows to log out and become 'Anonymous User'.
+
+Testing Security
+
+ - **'ob.restrictedTraverse(attr)'** is a simple way to check whether the currently logged in user is
+ allowed to access attribute 'attr' of object 'ob'.
+
+ - **'getSecurityManager().validate(None, ob, attr, ob.attr)'** uses the security manager to do the same.
+ The convenience method 'getSecurityManager().validateValue(ob.attr)' will no longer work
+ in Zope 2.8 (from what I hear).
+
+ Also see the 'testPythonScript.py' example test.
+
+ Note that you have the entire Zope security API at your disposal to further refine your fixture.
+ E.g. to add another user call 'self.folder.acl_users.userFolderAddUser("user2", "secret", ["role2"], [])'.
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/doc/TIMELINES.txt
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/doc/TIMELINES.txt 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/doc/TIMELINES.txt 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,85 @@
+Timelines
+
+When the skeleton test is run by typing 'python testSkeleton.py', it
+
+ 1. includes file framework.py
+
+ 1.1 locates and imports the Testing package by means of
+ - SOFTWARE_HOME environment variable
+ - auto-detection
+
+ 1.2 locates and includes file ztc_common.py
+
+ 1.2.1 sets up the instance environment by means of
+ - ZEO_INSTANCE_HOME environment variable
+ - INSTANCE_HOME environment variable
+ - auto-detection
+ - optional custom_zodb.py
+
+ 2. imports module Testing.ZopeTestCase
+
+ 2.1 imports module Testing.ZopeTestCase.ZopeLite
+
+ 2.1.1 imports module ZODB
+ 2.1.2 imports module Globals
+ 2.1.3 patches OFS.Application to not auto-install all products
+ 2.1.4 patches App.ProductContext to not auto-install all help files
+ 2.1.5 imports module Zope
+ 2.1.6 starts Zope
+ 2.1.7 installs product PluginIndexes
+ 2.1.8 installs product OFSP
+
+ 2.2 imports module Testing.ZopeTestCase.ZopeTestCase
+
+ 2.2.1 creates the connection registry
+ 2.2.2 defines class ZopeTestCase(unittest.TestCase)
+
+ 3. installs product SomeProduct
+
+ 4. defines class TestSomeProduct(ZopeTestCase.ZopeTestCase)
+
+ 5. executes method framework()
+
+ 5.1 collects all TestCase-derived classes in a test suite
+ 5.2 runs the test suite using the TextTestRunner
+
+
+When a ZopeTestCase test method is run, it
+
+ 1. executes setUp()
+
+ 1.1 calls the beforeSetUp() hook
+
+ 1.1.1 by default begins a new transaction
+
+ 1.2 opens a ZODB connection and retrieves the root application object
+
+ 1.3 sets up the default fixture
+
+ 1.3.1 creates a Folder object in the root
+ 1.3.2 creates a UserFolder object in the folder
+ 1.3.3 creates a default user in the user folder
+ 1.3.4 logs in as the default user
+
+ 1.4 calls the afterSetUp() hook
+
+ 2. executes the test method
+
+ 3. executes tearDown()
+
+ 3.1 calls the beforeTearDown() hook
+
+ 3.2 calls the beforeClose() hook
+
+ 3.2.1 by default aborts the transaction
+
+ 3.3 clears the fixture *)
+
+ 3.1.1 aborts all transactions
+ 3.1.2 closes all ZODB connections
+ 3.1.3 logs out
+ 3.1.4 calls the afterClear() hook
+
+
+*) Note: The fixture is also cleared if an error occurs during setUp()
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/doc/VERSION.txt
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/doc/VERSION.txt 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/doc/VERSION.txt 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,6 @@
+ZopeTestCase 0.9.0
+(c) 2002-2004, Stefan H. Holek, stefan at epy.co.at
+http://zope.org/Members/shh/ZopeTestCase
+License: ZPL
+Zope: 2.5-2.7
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/framework.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/framework.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/framework.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,107 @@
+##############################################################################
+#
+# ZopeTestCase
+#
+# COPY THIS FILE TO YOUR 'tests' DIRECTORY.
+#
+# This version of framework.py will use the SOFTWARE_HOME
+# environment variable to locate Zope and the Testing package.
+#
+# If the tests are run in an INSTANCE_HOME installation of Zope,
+# Products.__path__ and sys.path with be adjusted to include the
+# instance's Products and lib/python directories respectively.
+#
+# If you explicitly set INSTANCE_HOME prior to running the tests,
+# auto-detection is disabled and the specified path will be used
+# instead.
+#
+# If the 'tests' directory contains a custom_zodb.py file, INSTANCE_HOME
+# will be adjusted to use it.
+#
+# If you set the ZEO_INSTANCE_HOME environment variable a ZEO setup
+# is assumed, and you can attach to a running ZEO server (via the
+# instance's custom_zodb.py).
+#
+##############################################################################
+#
+# The following code should be at the top of every test module:
+#
+# import os, sys
+# if __name__ == '__main__':
+# execfile(os.path.join(sys.path[0], 'framework.py'))
+#
+# ...and the following at the bottom:
+#
+# if __name__ == '__main__':
+# framework()
+#
+##############################################################################
+
+__version__ = '0.2.3'
+
+# Save start state
+#
+__SOFTWARE_HOME = os.environ.get('SOFTWARE_HOME', '')
+__INSTANCE_HOME = os.environ.get('INSTANCE_HOME', '')
+
+if __SOFTWARE_HOME.endswith(os.sep):
+ __SOFTWARE_HOME = os.path.dirname(__SOFTWARE_HOME)
+
+if __INSTANCE_HOME.endswith(os.sep):
+ __INSTANCE_HOME = os.path.dirname(__INSTANCE_HOME)
+
+# Find and import the Testing package
+#
+if not sys.modules.has_key('Testing'):
+ p0 = sys.path[0]
+ if p0 and __name__ == '__main__':
+ os.chdir(p0)
+ p0 = ''
+ s = __SOFTWARE_HOME
+ p = d = s and s or os.getcwd()
+ while d:
+ if os.path.isdir(os.path.join(p, 'Testing')):
+ zope_home = os.path.dirname(os.path.dirname(p))
+ sys.path[:1] = [p0, p, zope_home]
+ break
+ p, d = s and ('','') or os.path.split(p)
+ else:
+ print 'Unable to locate Testing package.',
+ print 'You might need to set SOFTWARE_HOME.'
+ sys.exit(1)
+
+import Testing, unittest
+execfile(os.path.join(os.path.dirname(Testing.__file__), 'common.py'))
+
+# Include ZopeTestCase support
+#
+if 1: # Create a new scope
+
+ p = os.path.join(os.path.dirname(Testing.__file__), 'ZopeTestCase')
+
+ if not os.path.isdir(p):
+ print 'Unable to locate ZopeTestCase package.',
+ print 'You might need to install ZopeTestCase.'
+ sys.exit(1)
+
+ ztc_common = 'ztc_common.py'
+ ztc_common_global = os.path.join(p, ztc_common)
+
+ f = 0
+ if os.path.exists(ztc_common_global):
+ execfile(ztc_common_global)
+ f = 1
+ if os.path.exists(ztc_common):
+ execfile(ztc_common)
+ f = 1
+
+ if not f:
+ print 'Unable to locate %s.' % ztc_common
+ sys.exit(1)
+
+# Debug
+#
+print 'SOFTWARE_HOME: %s' % os.environ.get('SOFTWARE_HOME', 'Not set')
+print 'INSTANCE_HOME: %s' % os.environ.get('INSTANCE_HOME', 'Not set')
+sys.stdout.flush()
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/functional.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/functional.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/functional.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,87 @@
+#
+# Support for functional unit testing in ZTC
+# After Marius Gedmina's functional.py module for Zope3.
+#
+
+# $Id: functional.py,v 1.2 2004/01/14 12:41:32 shh42 Exp $
+
+import sys, re, base64
+import sandbox
+
+
+class Functional(sandbox.Sandboxed):
+ '''Derive from this class and an xTestCase to get functional
+ testing support::
+
+ class MyTest(Functional, ZopeTestCase):
+ ...
+ '''
+
+ def publish(self, path, basic=None, env=None, extra=None, request_method='GET'):
+ '''Publishes the object at 'path' returning an enhanced response object.'''
+
+ from StringIO import StringIO
+ from ZPublisher.Response import Response
+ from ZPublisher.Test import publish_module
+
+ # Commit the sandbox for good measure
+ get_transaction().commit()
+
+ if env is None:
+ env = {}
+ if extra is None:
+ extra = {}
+
+ request = self.app.REQUEST
+
+ env['SERVER_NAME'] = request['SERVER_NAME']
+ env['SERVER_PORT'] = request['SERVER_PORT']
+ env['REQUEST_METHOD'] = request_method
+
+ p = path.split('?')
+ if len(p) == 1:
+ env['PATH_INFO'] = p[0]
+ elif len(p) == 2:
+ [env['PATH_INFO'], env['QUERY_STRING']] = p
+ else:
+ raise TypeError, ''
+
+ if basic:
+ env['HTTP_AUTHORIZATION'] = "Basic %s" % base64.encodestring(basic)
+
+ outstream = StringIO()
+ response = Response(stdout=outstream, stderr=sys.stderr)
+
+ publish_module('Zope', response=response, environ=env, extra=extra)
+
+ return ResponseWrapper(response, outstream, path)
+
+
+class ResponseWrapper:
+ '''Acts like a response object with some additional introspective methods.'''
+
+ _bodyre = re.compile('^$^\n(.*)', re.MULTILINE | re.DOTALL)
+
+ def __init__(self, response, outstream, path):
+ self._response = response
+ self._outstream = outstream
+ self._path = path
+
+ def getOutput(self):
+ '''Returns the complete output, headers and all.'''
+ return self._outstream.getvalue()
+
+ def getBody(self):
+ '''Returns the page body, i.e. the output par headers.'''
+ body = self._bodyre.search(self.getOutput())
+ if body is not None:
+ body = body.group(1)
+ return body
+
+ def getPath(self):
+ '''Returns the path used by the request.'''
+ return self._path
+
+ def __getattr__(self, name):
+ return getattr(self._response, name)
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/profiler.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/profiler.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/profiler.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,99 @@
+#
+# Profiling support for ZTC
+#
+
+# $Id: profiler.py,v 1.2 2004/01/12 18:45:42 shh42 Exp $
+
+import os, sys
+
+from profile import Profile
+from pstats import Stats
+
+_profile = Profile()
+_have_stats = 0
+
+limit = ('.py:', 200)
+sort = ('cumulative', 'time', 'pcalls')
+strip_dirs = 1
+
+
+def runcall(*args, **kw):
+ global _have_stats
+ _have_stats = 1
+ return apply(_profile.runcall, args, kw)
+
+
+def print_stats(limit=limit, sort=sort, strip_dirs=strip_dirs):
+ if _have_stats:
+ stats = Stats(_profile)
+ if strip_dirs:
+ stats.strip_dirs()
+ apply(stats.sort_stats, sort)
+ apply(stats.print_stats, limit)
+
+
+def dump_stats(filename):
+ if _have_stats:
+ _profile.dump_stats(filename)
+
+
+class Profiled:
+ '''Derive from this class and an xTestCase to get profiling support::
+
+ class MyTest(Profiled, ZopeTestCase):
+ ...
+
+ Then run the test module by typing::
+
+ $ python testSomething.py profile
+
+ Profiler statistics will be printed after the test results.
+ '''
+
+ def runcall(self, *args, **kw):
+ return apply(runcall, args, kw)
+
+ def __call__(self, result=None):
+ if result is None: result = self.defaultTestResult()
+ result.startTest(self)
+ testMethod = getattr(self, self._TestCase__testMethodName)
+ try:
+ try:
+ if int(os.environ.get('PROFILE_SETUP', 0)):
+ self.runcall(self.setUp)
+ else:
+ self.setUp()
+ except KeyboardInterrupt:
+ raise
+ except:
+ result.addError(self, self._TestCase__exc_info())
+ return
+
+ ok = 0
+ try:
+ if int(os.environ.get('PROFILE_TESTS', 0)):
+ self.runcall(testMethod)
+ else:
+ testMethod()
+ ok = 1
+ except self.failureException:
+ result.addFailure(self, self._TestCase__exc_info())
+ except KeyboardInterrupt:
+ raise
+ except:
+ result.addError(self, self._TestCase__exc_info())
+
+ try:
+ if int(os.environ.get('PROFILE_TEARDOWN', 0)):
+ self.runcall(self.tearDown)
+ else:
+ self.tearDown()
+ except KeyboardInterrupt:
+ raise
+ except:
+ result.addError(self, self._TestCase__exc_info())
+ ok = 0
+ if ok: result.addSuccess(self)
+ finally:
+ result.stopTest(self)
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/runalltests.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/runalltests.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/runalltests.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,29 @@
+#
+# Runs all tests in the current directory
+#
+# Execute like:
+# python runalltests.py
+#
+# Alternatively use the testrunner:
+# python /path/to/Zope/utilities/testrunner.py -qa
+#
+
+import os, sys
+if __name__ == '__main__':
+ execfile(os.path.join(sys.path[0], 'framework.py'))
+
+import unittest
+TestRunner = unittest.TextTestRunner
+suite = unittest.TestSuite()
+
+tests = os.listdir(os.curdir)
+tests = [n[:-3] for n in tests if n.startswith('test') and n.endswith('.py')]
+
+for test in tests:
+ m = __import__(test)
+ if hasattr(m, 'test_suite'):
+ suite.addTest(m.test_suite())
+
+if __name__ == '__main__':
+ TestRunner().run(suite)
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/sandbox.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/sandbox.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/sandbox.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,60 @@
+#
+# Support for ZODB sandboxes in ZTC
+#
+
+# $Id: sandbox.py,v 1.1 2004/01/09 15:03:04 shh42 Exp $
+
+import ZopeLite as Zope
+import utils
+
+
+class Sandboxed:
+ '''Derive from this class and an xTestCase to make each test
+ run in its own ZODB sandbox::
+
+ class MyTest(Sandboxed, ZopeTestCase):
+ ...
+ '''
+
+ def _app(self):
+ '''Returns the app object for a test.'''
+ app = Zope.app(Zope.sandbox().open())
+ AppZapper().set(app)
+ return utils.makerequest(app)
+
+ def _close(self):
+ '''Clears the transaction and the AppZapper.'''
+ get_transaction().abort()
+ AppZapper().clear()
+
+
+class AppZapper:
+ '''Application object share point'''
+
+ __shared_state = {'_app': None}
+
+ def __init__(self):
+ self.__dict__ = self.__shared_state
+
+ def set(self, app):
+ self._app = app
+
+ def clear(self):
+ self._app = None
+
+ def app(self):
+ return self._app
+
+
+def __bobo_traverse__(self, REQUEST=None, name=None):
+ '''Makes ZPublisher.publish() use the current app object.'''
+ app = AppZapper().app()
+ if app is not None:
+ return app
+ return self.__old_bobo_traverse__(REQUEST, name)
+
+
+from ZODB.ZApplication import ZApplicationWrapper
+ZApplicationWrapper.__old_bobo_traverse__ = ZApplicationWrapper.__bobo_traverse__
+ZApplicationWrapper.__bobo_traverse__ = __bobo_traverse__
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/testFunctional.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/testFunctional.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/testFunctional.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,80 @@
+#
+# Example functional ZopeTestCase
+#
+
+# $Id: testFunctional.py,v 1.5 2004/04/09 12:38:37 shh42 Exp $
+
+import os, sys
+if __name__ == '__main__':
+ execfile(os.path.join(sys.path[0], 'framework.py'))
+
+from Testing import ZopeTestCase
+
+ZopeTestCase.installProduct('PythonScripts')
+
+
+class TestZPublication(ZopeTestCase.Functional, ZopeTestCase.ZopeTestCase):
+
+ def afterSetUp(self):
+ self.folder_path = self.folder.absolute_url(1)
+ self.basic_auth = '%s:secret' % ZopeTestCase.user_name
+
+ self.folder.addDTMLMethod('index_html', file='foo')
+
+ dispatcher = self.folder.manage_addProduct['PythonScripts']
+ dispatcher.manage_addPythonScript('script')
+ self.folder.script.ZPythonScript_edit('a=0', 'return a+1')
+
+ self.folder.manage_addFolder('object', '')
+ self.folder.addDTMLMethod('change_title',
+ file='''<dtml-call "manage_changeProperties(title=REQUEST.get('title'))">''')
+
+ def testPublishDocument(self):
+ response = self.publish('/%s/index_html' % self.folder_path)
+ self.assertEqual(response.getStatus(), 200)
+ self.assertEqual(response.getBody(), 'foo')
+
+ def testPublishScript(self):
+ response = self.publish('/%s/script' % self.folder_path)
+ self.assertEqual(response.getStatus(), 200)
+ self.assertEqual(response.getBody(), '1')
+
+ def testPublishScriptWithArgument(self):
+ response = self.publish('/%s/script?a:int=2' % self.folder_path)
+ self.assertEqual(response.getStatus(), 200)
+ self.assertEqual(response.getBody(), '3')
+
+ def testServerError(self):
+ response = self.publish('/%s/script?a=2' % self.folder_path)
+ self.assertEqual(response.getStatus(), 500)
+
+ def testUnauthorized(self):
+ self.folder.index_html.manage_permission('View', ['Owner'])
+ response = self.publish('/%s/index_html' % self.folder_path)
+ self.assertEqual(response.getStatus(), 401)
+
+ def testBasicAuthentication(self):
+ self.folder.index_html.manage_permission('View', ['Owner'])
+ response = self.publish('/%s/index_html'
+ % self.folder_path, self.basic_auth)
+ self.assertEqual(response.getStatus(), 200)
+ self.assertEqual(response.getBody(), 'foo')
+
+ def testModifyObject(self):
+ from AccessControl.Permissions import manage_properties
+ self.setPermissions([manage_properties])
+ response = self.publish('/%s/object/change_title?title=Foo'
+ % self.folder_path, self.basic_auth)
+ self.assertEqual(response.getStatus(), 200)
+ self.assertEqual(self.folder.object.title_or_id(), 'Foo')
+
+
+def test_suite():
+ from unittest import TestSuite, makeSuite
+ suite = TestSuite()
+ suite.addTest(makeSuite(TestZPublication))
+ return suite
+
+if __name__ == '__main__':
+ framework()
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/testPortalTestCase.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/testPortalTestCase.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/testPortalTestCase.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,411 @@
+#
+# Tests the PortalTestCase
+#
+# NOTE: This is *not* an example PortalTestCase. Do not
+# use this file as a blueprint for your own tests!
+#
+# See testPythonScript.py and testShoppingCart.py for
+# example test cases. See testSkeleton.py for a quick
+# way of getting started.
+#
+
+# $Id: testPortalTestCase.py,v 1.21 2004/04/09 12:38:37 shh42 Exp $
+
+import os, sys
+if __name__ == '__main__':
+ execfile(os.path.join(sys.path[0], 'framework.py'))
+
+from Testing import ZopeTestCase
+
+from Acquisition import aq_base
+from AccessControl import getSecurityManager
+from types import ListType
+
+portal_name = 'dummy_1_'
+user_name = ZopeTestCase.user_name
+
+
+def hasattr_(ob, attr):
+ return hasattr(aq_base(ob), attr)
+
+
+# Dummy portal
+from OFS.SimpleItem import SimpleItem
+from OFS.Folder import Folder
+
+class DummyMembershipTool(SimpleItem):
+ id = 'portal_membership'
+ def createMemberarea(self, member_id):
+ portal = self.aq_inner.aq_parent
+ portal.Members.manage_addFolder(member_id)
+ def getHomeFolder(self, member_id):
+ portal = self.aq_inner.aq_parent
+ return portal.Members[member_id]
+
+class DummyPortal(Folder):
+ _v_skindata = None
+ def __init__(self, id):
+ self.id = id
+ self._addRole('Member')
+ self._setObject('portal_membership', DummyMembershipTool())
+ self.manage_addFolder('Members')
+ def setupCurrentSkin(self):
+ if self._v_skindata is None:
+ self._v_skindata = 'refreshed'
+
+
+class TestPortalTestCase(ZopeTestCase.PortalTestCase):
+ '''Tests the PortalTestCase.'''
+
+ _setUp = ZopeTestCase.PortalTestCase.setUp
+ _tearDown = ZopeTestCase.PortalTestCase.tearDown
+
+ def getPortal(self):
+ self.app._setObject(portal_name, DummyPortal(portal_name))
+ return self.app[portal_name]
+
+ def setUp(self):
+ # For this test case we *want* to start
+ # with an empty fixture.
+ self._called = []
+ # Implicitly aborts previous transaction
+ get_transaction().begin()
+
+ def beforeSetUp(self):
+ self._called.append('beforeSetUp')
+
+ def afterSetUp(self):
+ self._called.append('afterSetUp')
+
+ def beforeTearDown(self):
+ self._called.append('beforeTearDown')
+
+ def beforeClose(self):
+ self._called.append('beforeClose')
+
+ def afterClear(self):
+ self._called.append('afterClear')
+
+ def test_01_getPortal(self):
+ # Portal should be set up
+ self.app = self._app()
+ self.portal = self.getPortal()
+ self.failUnless(hasattr_(self.app, portal_name))
+ self.failUnless(hasattr_(self.portal, 'Members'))
+ self.failUnless(hasattr_(self.portal, 'portal_membership'))
+ self.failUnless('Member' in self.portal.userdefined_roles())
+
+ def test_02_setupUserFolder(self):
+ # User folder should be set up.
+ self.app = self._app()
+ self.portal = self.getPortal()
+ self.failIf(hasattr_(self.portal, 'acl_users'))
+ self._setupUserFolder()
+ self.failUnless(hasattr_(self.portal, 'acl_users'))
+ # Must not complain if UF already exists
+ self._setupUserFolder()
+
+ def test_03_setupUser(self):
+ # User should be set up
+ self.app = self._app()
+ self.portal = self.getPortal()
+ self._setupUserFolder()
+ self._setupUser()
+ acl_user = self.portal.acl_users.getUserById(user_name)
+ self.failUnless(acl_user)
+ self.assertEqual(acl_user.getRoles(), ('Member', 'Authenticated'))
+ self.assertEqual(type(acl_user.roles), ListType)
+
+ def test_04_setupHomeFolder(self):
+ # User's home folder should be set up
+ self.app = self._app()
+ self.portal = self.getPortal()
+ self._setupUserFolder()
+ self._setupUser()
+ self.login()
+ self._setupHomeFolder()
+ self.failUnless(hasattr_(self.portal.Members, user_name))
+ self.failIf(self.folder is None)
+ # Shut up deprecation warnings
+ try: owner_info = self.folder.getOwnerTuple()
+ except AttributeError:
+ owner_info = self.folder.getOwner(info=1)
+ self.assertEqual(owner_info, ([portal_name, 'acl_users'], user_name))
+
+ def test_05_refreshSkinData(self):
+ # The _v_skindata attribute should be refreshed
+ self.app = self._app()
+ self.portal = self.getPortal()
+ self.assertEqual(self.portal._v_skindata, None)
+ self._refreshSkinData()
+ self.assertEqual(self.portal._v_skindata, 'refreshed')
+
+ def test_06_setRoles(self):
+ # Roles should be set for user
+ self.app = self._app()
+ self.portal = self.getPortal()
+ self._setupUserFolder()
+ self._setupUser()
+ test_roles = ['Manager', 'Member']
+ test_roles.sort()
+ self.setRoles(test_roles)
+ acl_user = self.portal.acl_users.getUserById(user_name)
+ user_roles = list(acl_user.getRoles())
+ user_roles.remove('Authenticated')
+ user_roles.sort()
+ self.assertEqual(user_roles, test_roles)
+
+ def test_07_setRoles_2(self):
+ # Roles should be set for logged in user
+ self.app = self._app()
+ self.portal = self.getPortal()
+ self._setupUserFolder()
+ self._setupUser()
+ self.login()
+ test_roles = ['Manager', 'Member']
+ test_roles.sort()
+ self.setRoles(test_roles)
+ auth_user = getSecurityManager().getUser()
+ user_roles = list(auth_user.getRoles())
+ user_roles.remove('Authenticated')
+ user_roles.sort()
+ self.assertEqual(user_roles, test_roles)
+
+ def test_08_setRoles_3(self):
+ # Roles should be set for a specified user
+ self.app = self._app()
+ self.portal = self.getPortal()
+ self._setupUserFolder()
+ self.portal.acl_users._doAddUser('test_user_2_', 'secret', [], [])
+ test_roles = ['Manager', 'Member']
+ test_roles.sort()
+ self.setRoles(test_roles, 'test_user_2_')
+ acl_user = self.portal.acl_users.getUserById('test_user_2_')
+ user_roles = list(acl_user.getRoles())
+ user_roles.remove('Authenticated')
+ user_roles.sort()
+ self.assertEqual(user_roles, test_roles)
+
+ def test_09_setPermissions(self):
+ # Permissions should be set for user
+ self.app = self._app()
+ self.portal = self.getPortal()
+ test_perms = ['Add Folders']
+ self.setPermissions(test_perms)
+ self.assertPermissionsOfRole(test_perms, 'Member')
+
+ def test_10_setPermissions_2(self):
+ # Permissions should be set for a specified role
+ self.app = self._app()
+ self.portal = self.getPortal()
+ self.portal._addRole('test_role_2_')
+ test_perms = ['Add Folders']
+ self.assertPermissionsOfRole([], 'test_role_2_')
+ self.setPermissions(test_perms, 'test_role_2_')
+ self.assertPermissionsOfRole(test_perms, 'test_role_2_')
+
+ def test_11_login(self):
+ # User should be able to log in
+ self.app = self._app()
+ self.portal = self.getPortal()
+ self._setupUserFolder()
+ self._setupUser()
+ auth_name = getSecurityManager().getUser().getUserName()
+ self.assertEqual(auth_name, 'Anonymous User')
+ self.login()
+ auth_name = getSecurityManager().getUser().getId()
+ self.assertEqual(auth_name, user_name)
+
+ def test_12_login_2(self):
+ # A specified user should be logged in
+ self.app = self._app()
+ self.portal = self.getPortal()
+ self._setupUserFolder()
+ self.portal.acl_users._doAddUser('test_user_2_', 'secret', [], [])
+ auth_name = getSecurityManager().getUser().getUserName()
+ self.assertEqual(auth_name, 'Anonymous User')
+ self.login('test_user_2_')
+ auth_name = getSecurityManager().getUser().getId()
+ self.assertEqual(auth_name, 'test_user_2_')
+
+ def test_13_login_3(self):
+ # Unknown user should raise AttributeError
+ self.app = self._app()
+ self.portal = self.getPortal()
+ self._setupUserFolder()
+ self.assertRaises(AttributeError, self.login, 'test_user_3_')
+
+ def test_14_logout(self):
+ # User should be able to log out
+ self.app = self._app()
+ self.portal = self.getPortal()
+ self._setupUserFolder()
+ self._setupUser()
+ self.login()
+ self.logout()
+ auth_name = getSecurityManager().getUser().getUserName()
+ self.assertEqual(auth_name, 'Anonymous User')
+
+ def test_15_clear(self):
+ # Everything should be removed
+ self.app = self._app()
+ self.portal = self.getPortal()
+ self._setupUserFolder()
+ self._setupUser()
+ self._setupHomeFolder()
+ self._clear(1)
+ # XXX: No more cleanups in _clear()
+ #self.failIf(self.portal.acl_users.getUserById(user_name))
+ #self.failIf(hasattr_(self.portal.Members, user_name))
+ auth_name = getSecurityManager().getUser().getUserName()
+ self.assertEqual(auth_name, 'Anonymous User')
+ self.assertEqual(self._called, ['beforeClose', 'afterClear'])
+ # clear must not fail when called repeatedly
+ self._clear()
+
+ def test_16_setUp(self):
+ # Everything should be set up
+ self._setUp()
+ self.failUnless(hasattr_(self.app, portal_name))
+ self.failUnless(hasattr_(self.portal, 'acl_users'))
+ self.failUnless(hasattr_(self.portal, 'Members'))
+ self.failUnless(hasattr_(self.portal, 'portal_membership'))
+ self.failUnless('Member' in self.portal.userdefined_roles())
+ self.failUnless(hasattr_(self.portal.Members, user_name))
+ acl_user = self.portal.acl_users.getUserById(user_name)
+ self.failUnless(acl_user)
+ self.assertEqual(acl_user.getRoles(), ('Member', 'Authenticated'))
+ self.assertEqual(type(acl_user.roles), ListType)
+ auth_name = getSecurityManager().getUser().getId()
+ self.assertEqual(auth_name, user_name)
+ # XXX: Changed in 0.9.0
+ #self.assertEqual(self._called, ['afterClear', 'beforeSetUp', 'afterSetUp'])
+ self.assertEqual(self._called, ['beforeSetUp', 'afterSetUp'])
+ self.assertEqual(self.portal._v_skindata, 'refreshed')
+
+ def test_17_tearDown(self):
+ # Everything should be removed
+ self._setUp()
+ self._called = []
+ self._tearDown()
+ # XXX: No more cleanups in _clear()
+ #self.failIf(hasattr_(self.portal.Members, user_name))
+ #self.assertEqual(self.portal.acl_users.getUserById(user_name), None)
+ auth_name = getSecurityManager().getUser().getUserName()
+ self.assertEqual(auth_name, 'Anonymous User')
+ self.assertEqual(self._called, ['beforeTearDown', 'beforeClose', 'afterClear'])
+
+ def test_18_configureFlag(self):
+ # Nothing should be configured
+ self._configure_portal = 0
+ self._setUp()
+ self.assertEqual(self.portal.acl_users.getUserById(user_name), None)
+ self.failIf(hasattr_(self.portal.Members, user_name))
+ auth_name = getSecurityManager().getUser().getUserName()
+ self.assertEqual(auth_name, 'Anonymous User')
+ # XXX: Changed in 0.9.0
+ #self.assertEqual(self._called, ['afterClear', 'beforeSetUp', 'afterSetUp'])
+ self.assertEqual(self._called, ['beforeSetUp', 'afterSetUp'])
+ self.assertEqual(self.portal._v_skindata, 'refreshed')
+
+ def test_19_configureFlag_2(self):
+ # Nothing should be cleared
+ self._setUp()
+ self._configure_portal = 0
+ self._called = []
+ self._clear()
+ # XXX: Since 0.8.4 we abort before closing the connection
+ #self.failUnless(hasattr_(self.portal.Members, user_name))
+ #self.failUnless(self.portal.acl_users.getUserById(user_name))
+ auth_name = getSecurityManager().getUser().getUserName()
+ self.assertEqual(auth_name, 'Anonymous User')
+ self.assertEqual(self._called, ['afterClear'])
+
+ # Helpers
+
+ def getPermissionsOfRole(self, role, context=None):
+ '''Returns sorted list of permission names of the
+ given role in the given context.
+ '''
+ if context is None: context = self.portal
+ perms = context.permissionsOfRole(role)
+ perms = [p['name'] for p in perms if p['selected']]
+ perms.sort()
+ return perms
+
+ def assertPermissionsOfRole(self, permissions, role, context=None):
+ '''Compares list of permission names to permissions of the
+ given role in the given context. Fails if the lists are not
+ found equal.
+ '''
+ perms = list(permissions)[:]
+ perms.sort()
+ self.assertEqual(self.getPermissionsOfRole(role, context), perms)
+
+
+from AccessControl.User import UserFolder
+
+class WrappingUserFolder(UserFolder):
+ '''User folder returning wrapped user objects'''
+
+ def getUser(self, name):
+ return UserFolder.getUser(self, name).__of__(self)
+
+
+class TestPlainUserFolder(ZopeTestCase.PortalTestCase):
+ '''Tests whether user objects are properly wrapped'''
+
+ def getPortal(self):
+ self.app._setObject(portal_name, DummyPortal(portal_name))
+ return self.app[portal_name]
+
+ def testGetUserDoesNotWrapUser(self):
+ user = self.portal.acl_users.getUserById(user_name)
+ self.failIf(hasattr(user, 'aq_base'))
+ self.failUnless(user is aq_base(user))
+
+ def testLoggedInUserIsWrapped(self):
+ user = getSecurityManager().getUser()
+ self.assertEqual(user.getId(), user_name)
+ self.failUnless(hasattr(user, 'aq_base'))
+ self.failUnless(user.__class__.__name__, 'User')
+ self.failUnless(user.aq_parent.__class__.__name__, 'UserFolder')
+ self.failUnless(user.aq_parent.aq_parent.__class__.__name__, 'Folder')
+
+
+class TestWrappingUserFolder(ZopeTestCase.PortalTestCase):
+ '''Tests whether user objects are properly wrapped'''
+
+ def getPortal(self):
+ self.app._setObject(portal_name, DummyPortal(portal_name))
+ return self.app[portal_name]
+
+ def _setupUserFolder(self):
+ self.portal._setObject('acl_users', WrappingUserFolder())
+
+ def testGetUserWrapsUser(self):
+ user = self.folder.acl_users.getUserById(user_name)
+ self.failUnless(hasattr(user, 'aq_base'))
+ self.failIf(user is aq_base(user))
+ self.failUnless(user.aq_parent.__class__.__name__, 'WrappingUserFolder')
+
+ def testLoggedInUserIsWrapped(self):
+ user = getSecurityManager().getUser()
+ self.assertEqual(user.getId(), user_name)
+ self.failUnless(hasattr(user, 'aq_base'))
+ self.failUnless(user.__class__.__name__, 'User')
+ self.failUnless(user.aq_parent.__class__.__name__, 'WrappingUserFolder')
+ self.failUnless(user.aq_parent.aq_parent.__class__.__name__, 'Folder')
+
+
+def test_suite():
+ from unittest import TestSuite, makeSuite
+ suite = TestSuite()
+ suite.addTest(makeSuite(TestPortalTestCase))
+ suite.addTest(makeSuite(TestPlainUserFolder))
+ suite.addTest(makeSuite(TestWrappingUserFolder))
+ return suite
+
+if __name__ == '__main__':
+ framework()
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/testPythonScript.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/testPythonScript.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/testPythonScript.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,186 @@
+#
+# Example ZopeTestCase testing a PythonScript object in the default fixture.
+#
+# Note that you are encouraged to call any of the following methods
+# from your own tests to modify the test user's security credentials:
+#
+# - setRoles()
+# - setPermissions()
+# - login()
+# - logout()
+#
+
+# $Id: testPythonScript.py,v 1.9 2004/04/09 12:38:37 shh42 Exp $
+
+import os, sys
+if __name__ == '__main__':
+ execfile(os.path.join(sys.path[0], 'framework.py'))
+
+from Testing import ZopeTestCase
+from AccessControl import Unauthorized
+from AccessControl import getSecurityManager
+
+ZopeTestCase.installProduct('PythonScripts')
+
+access_permissions = ['View management screens']
+change_permissions = ['Change Python Scripts']
+
+ps_params1 = 'a=1'
+ps_body1 = 'return a'
+ps_params2 = 'a'
+ps_body2 = 'return a+1'
+
+
+class TestPythonScript(ZopeTestCase.ZopeTestCase):
+ '''Tries various things allowed by the ZopeTestCase API.'''
+
+ def afterSetUp(self):
+ '''Adds a PythonScript object to the default fixture'''
+ dispatcher = self.folder.manage_addProduct['PythonScripts']
+ dispatcher.manage_addPythonScript('ps')
+ self.ps = self.folder['ps']
+ self.ps.ZPythonScript_edit(ps_params1, ps_body1)
+
+ # Test the fixture ##############################################
+
+ def testFixture(self):
+ # The PythonScript should exist and be properly set up
+ self.failUnless(hasattr(self.folder, 'ps'))
+ self.assertEqual(self.ps.body(), ps_body1+'\n')
+ self.assertEqual(self.ps.params(), ps_params1)
+ owner = self.ps.getOwner()
+ self.assertEqual(owner.getUserName(), ZopeTestCase.user_name)
+
+ # Test the scripts ##############################################
+
+ def testCanCallScript1WithArgument(self):
+ # PythonScript should return 2
+ self.assertEqual(self.ps(2), 2)
+
+ def testCanCallScript1WithoutArgument(self):
+ # PythonScript should return 1
+ self.assertEqual(self.ps(), 1)
+
+ def testCanCallScript2WithArgument(self):
+ # PythonScript should return 2
+ self.ps.ZPythonScript_edit(ps_params2, ps_body2)
+ self.assertEqual(self.ps(1), 2)
+
+ def testCannotCallScript2WithoutArgument(self):
+ # PythonScript should raise a TypeError
+ self.ps.ZPythonScript_edit(ps_params2, ps_body2)
+ self.assertRaises(TypeError, self.ps, ())
+
+ # Test access protection ########################################
+
+ def testCannotAccessWithoutAccessPermission(self):
+ # manage_main should be protected
+ self.assertRaises(Unauthorized, self.ps.restrictedTraverse, 'manage_main')
+
+ def testCanAccessWithAccessPermission(self):
+ # manage_main should be accessible
+ self.setPermissions(access_permissions)
+ try:
+ self.ps.restrictedTraverse('manage_main')
+ except Unauthorized:
+ self.fail('Access to manage_main was denied')
+
+ def testCannotAccessIfAnonymous(self):
+ # manage_main should be protected
+ self.logout()
+ self.assertRaises(Unauthorized, self.ps.restrictedTraverse, 'manage_main')
+
+ def testCanAccessIfManager(self):
+ # manage_main should be accessible to Managers
+ self.setRoles(['Manager'])
+ try:
+ self.ps.restrictedTraverse('manage_main')
+ except Unauthorized:
+ self.fail('Access to manage_main was denied to Manager')
+
+ # Test access protection with SecurityManager ###################
+
+ def testCannotAccessWithoutAccessPermissionSecurityManager(self):
+ # manage_main should be protected
+ self.assertRaises(Unauthorized, getSecurityManager().validateValue, self.ps.manage_main)
+
+ def testCanAccessWithAccessPermissionSecurityManager(self):
+ # manage_main should be accessible
+ self.setPermissions(access_permissions)
+ try:
+ getSecurityManager().validateValue(self.ps.manage_main)
+ except Unauthorized:
+ self.fail('Access to manage_main was denied')
+
+ def testCannotAccessIfAnonymousSecurityManager(self):
+ # manage_main should be protected
+ self.logout()
+ self.assertRaises(Unauthorized, getSecurityManager().validateValue, self.ps.manage_main)
+
+ def testCanAccessIfManagerSecurityManager(self):
+ # manage_main should be accessible to Managers
+ self.setRoles(['Manager'])
+ try:
+ getSecurityManager().validateValue(self.ps.manage_main)
+ except Unauthorized:
+ self.fail('Access to manage_main was denied to Manager')
+
+ # Test edit protection ##########################################
+
+ def testCannotEditWithoutChangePermission(self):
+ # PythonScript should not be editable
+ try:
+ self.ps.restrictedTraverse('ZPythonScript_edit')(ps_params2, ps_body2)
+ except Unauthorized:
+ pass # Test passed
+ else:
+ self.assertEqual(self.ps.body(), ps_body2+'\n',
+ 'ZPythonScript_edit was not protected')
+ self.assertEqual(self.ps.body(), ps_body1+'\n',
+ 'ZPythonScript_edit was protected but no exception was raised')
+
+ def testCanEditWithChangePermission(self):
+ # PythonScript should be editable
+ self.setPermissions(change_permissions)
+ try:
+ self.ps.restrictedTraverse('ZPythonScript_edit')(ps_params2, ps_body2)
+ except Unauthorized:
+ self.fail('Access to ZPythonScript_edit was denied')
+ else:
+ self.assertEqual(self.ps.body(), ps_body2+'\n')
+ self.assertEqual(self.ps.params(), ps_params2)
+
+ def testCannotEditIfAnonymous(self):
+ # PythonScript should not be editable
+ self.logout()
+ try:
+ self.ps.restrictedTraverse('ZPythonScript_edit')(ps_params2, ps_body2)
+ except Unauthorized:
+ pass # Test passed
+ else:
+ self.assertEqual(self.ps.body(), ps_body2+'\n',
+ 'ZPythonScript_edit was not protected')
+ self.assertEqual(self.ps.body(), ps_body1+'\n',
+ 'ZPythonScript_edit was protected but no exception was raised')
+
+ def testCanEditIfManager(self):
+ # PythonScript should be editable for Managers
+ self.setRoles(['Manager'])
+ try:
+ self.ps.restrictedTraverse('ZPythonScript_edit')(ps_params2, ps_body2)
+ except Unauthorized:
+ self.fail('Access to ZPythonScript_edit was denied to Manager')
+ else:
+ self.assertEqual(self.ps.body(), ps_body2+'\n')
+ self.assertEqual(self.ps.params(), ps_params2)
+
+
+def test_suite():
+ from unittest import TestSuite, makeSuite
+ suite = TestSuite()
+ suite.addTest(makeSuite(TestPythonScript))
+ return suite
+
+if __name__ == '__main__':
+ framework()
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/testShoppingCart.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/testShoppingCart.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/testShoppingCart.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,116 @@
+#
+# Example ZopeTestCase testing the ShoppingCart example application.
+#
+# Note the use of sessions and how the SESSION object is added to
+# the REQUEST in afterSetUp().
+#
+# You can use zLOG.LOG() if you set up the event log variables first.
+# Handy for debugging and tracing your tests.
+#
+
+# $Id: testShoppingCart.py,v 1.10 2004/04/09 12:38:37 shh42 Exp $
+
+import os, sys
+if __name__ == '__main__':
+ execfile(os.path.join(sys.path[0], 'framework.py'))
+
+#os.environ['STUPID_LOG_FILE'] = os.path.join(os.getcwd(), 'zLOG.log')
+#os.environ['STUPID_LOG_SEVERITY'] = '0'
+
+from Testing import ZopeTestCase
+
+from Globals import SOFTWARE_HOME
+examples_path = os.path.join(SOFTWARE_HOME, '..', '..', 'import', 'Examples.zexp')
+examples_path = os.path.abspath(examples_path)
+
+
+if not ZopeTestCase.hasProduct('TemporaryFolder'):
+
+ print 'testShoppingCart.py: The tests in this module require Zope >= 2.5'
+
+elif not os.path.isfile(examples_path):
+
+ print "testShoppingCart.py: Cannot find file '%s'" % examples_path
+
+else:
+
+ # Open ZODB connection
+ app = ZopeTestCase.app()
+
+ # Set up sessioning objects
+ ZopeTestCase.utils.setupCoreSessions(app)
+
+ # Set up example applications
+ if not hasattr(app, 'Examples'):
+ ZopeTestCase.utils.importObjectFromFile(app, examples_path)
+
+ # Close ZODB connection
+ ZopeTestCase.close(app)
+
+
+ class DummyOrder:
+ '''Construct an order we can add to the cart'''
+ __allow_access_to_unprotected_subobjects__ = 1
+
+ def __init__(self, id, quantity):
+ self.id = id
+ self.quantity = quantity
+
+
+ class TestShoppingCart(ZopeTestCase.ZopeTestCase):
+ '''Test the ShoppingCart example application'''
+
+ _setup_fixture = 0 # No default fixture
+
+ def afterSetUp(self):
+ self.cart = self.app.Examples.ShoppingCart
+ # Put SESSION object into REQUEST
+ request = self.app.REQUEST
+ sdm = self.app.session_data_manager
+ request.set('SESSION', sdm.getSessionData())
+ self.session = request.SESSION
+
+ def testSession(self):
+ # Session should work
+ self.session.set('boring', 'boring')
+ self.assertEqual(self.session.get('boring'), 'boring')
+
+ def testCartIsEmpty(self):
+ # Cart should be empty
+ self.assertEqual(len(self.cart.currentItems()), 0)
+
+ def testAddItems(self):
+ # Adding to the cart should work
+ self.cart.addItems([DummyOrder('510-115', 1),])
+ self.assertEqual(len(self.cart.currentItems()), 1)
+
+ def testDeleteItems(self):
+ # Deleting from the cart should work
+ self.cart.addItems([DummyOrder('510-115', 1),])
+ self.cart.deleteItems(['510-115'])
+ self.assertEqual(len(self.cart.currentItems()), 0)
+
+ def testAddQuantity(self):
+ # Adding to quantity should work
+ self.cart.addItems([DummyOrder('510-115', 1),])
+ self.cart.addItems([DummyOrder('510-115', 2),])
+ self.cart.addItems([DummyOrder('510-115', 3),])
+ self.assertEqual(self.cart.currentItems()[0]['quantity'], 6)
+
+ def testGetTotal(self):
+ # Totals should be computed correctly
+ self.cart.addItems([DummyOrder('510-115', 1),])
+ self.cart.addItems([DummyOrder('510-122', 2),])
+ self.cart.addItems([DummyOrder('510-007', 2),])
+ self.assertEqual(self.cart.getTotal(), 149.95)
+
+
+ def test_suite():
+ from unittest import TestSuite, makeSuite
+ suite = TestSuite()
+ suite.addTest(makeSuite(TestShoppingCart))
+ return suite
+
+ if __name__ == '__main__':
+ framework()
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/testSkeleton.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/testSkeleton.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/testSkeleton.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,32 @@
+#
+# Skeleton ZopeTestCase
+#
+
+import os, sys
+if __name__ == '__main__':
+ execfile(os.path.join(sys.path[0], 'framework.py'))
+
+from Testing import ZopeTestCase
+
+ZopeTestCase.installProduct('SomeProduct')
+
+
+class TestSomeProduct(ZopeTestCase.ZopeTestCase):
+
+ def afterSetUp(self):
+ pass
+
+ def testSomething(self):
+ # Test something
+ self.assertEqual(1+1, 2)
+
+
+def test_suite():
+ from unittest import TestSuite, makeSuite
+ suite = TestSuite()
+ suite.addTest(makeSuite(TestSomeProduct))
+ return suite
+
+if __name__ == '__main__':
+ framework()
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/testWebserver.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/testWebserver.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/testWebserver.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,195 @@
+#
+# Example ZopeTestCase testing web access to a freshly started ZServer.
+#
+# Note that we need to set up the error_log before starting the ZServer.
+#
+# Note further that the test thread needs to explicitly commit its
+# transactions, so the ZServer threads can see modifications made to
+# the ZODB.
+#
+# IF YOU THINK YOU NEED THE WEBSERVER STARTED YOU ARE PROBABLY WRONG!
+# This is only required in very special cases, like when testing
+# ZopeXMLMethods where XSLT processing is done by external tools that
+# need to URL-call back into the Zope server.
+#
+# If you want to write functional unit tests, see the testFunctional.py
+# example instead.
+#
+
+# $Id: testWebserver.py,v 1.14 2004/04/09 12:38:37 shh42 Exp $
+
+import os, sys
+if __name__ == '__main__':
+ execfile(os.path.join(sys.path[0], 'framework.py'))
+
+#os.environ['STUPID_LOG_FILE'] = os.path.join(os.getcwd(), 'zLOG.log')
+#os.environ['STUPID_LOG_SEVERITY'] = '0'
+
+from Testing import ZopeTestCase
+
+from AccessControl import Unauthorized
+import urllib
+
+# Create the error_log object
+ZopeTestCase.utils.setupSiteErrorLog()
+
+# Start the web server
+host, port = ZopeTestCase.utils.startZServer(4)
+folder_url = 'http://%s:%d/%s' %(host, port, ZopeTestCase.folder_name)
+
+
+class ManagementOpener(urllib.FancyURLopener):
+ '''Logs on as manager when prompted'''
+ 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):
+ raise Unauthorized, 'The URLopener was asked for authentication'
+
+
+class TestWebserver(ZopeTestCase.ZopeTestCase):
+
+ def afterSetUp(self):
+ uf = self.folder.acl_users
+ uf._doAddUser('manager', 'secret', ['Manager'], [])
+ manager = uf.getUserById('manager').__of__(uf)
+
+ self.folder.addDTMLMethod('index_html', file='index_html called')
+ self.folder.addDTMLMethod('secret_html', file='secret_html called')
+ self.folder.manage_addFolder('object', '')
+
+ for p in ZopeTestCase.standard_permissions:
+ self.folder.secret_html.manage_permission(p, ['Manager'], acquire=0)
+
+ self.folder.addDTMLMethod('object_ids', file='<dtml-var objectIds>')
+ self.folder.addDTMLMethod('user_ids', file='<dtml-var "acl_users.getUserNames()">')
+ self.folder.addDTMLMethod('change_title',
+ file='''<dtml-call "manage_changeProperties(title=REQUEST.get('title'))">'''
+ '''<dtml-var title_or_id>''')
+
+ self.folder.object_ids.changeOwnership(manager)
+ self.folder.user_ids.changeOwnership(manager)
+ self.folder.change_title.changeOwnership(manager)
+
+ # Commit so the ZServer threads can see the changes
+ get_transaction().commit()
+
+ def beforeClose(self):
+ # Commit after cleanup
+ get_transaction().commit()
+
+ def testAccessPublicObject(self):
+ # Test access to a public resource
+ page = self.folder.index_html(self.folder)
+ self.assertEqual(page, 'index_html called')
+
+ def testURLAccessPublicObject(self):
+ # Test web access to a public resource
+ urllib._urlopener = ManagementOpener()
+ page = urllib.urlopen(folder_url+'/index_html').read()
+ self.assertEqual(page, 'index_html called')
+
+ def testAccessProtectedObject(self):
+ # Test access to a protected resource
+ page = self.folder.secret_html(self.folder)
+ self.assertEqual(page, 'secret_html called')
+
+ def testURLAccessProtectedObject(self):
+ # Test web access to a protected resource
+ urllib._urlopener = ManagementOpener()
+ page = urllib.urlopen(folder_url+'/secret_html').read()
+ self.assertEqual(page, 'secret_html called')
+
+ def testSecurityOfPublicObject(self):
+ # Test security of a public resource
+ try:
+ self.folder.restrictedTraverse('index_html')
+ except Unauthorized:
+ # Convert error to failure
+ self.fail('Unauthorized')
+
+ def testURLSecurityOfPublicObject(self):
+ # Test web security of a public resource
+ urllib._urlopener = UnauthorizedOpener()
+ try:
+ urllib.urlopen(folder_url+'/index_html')
+ except Unauthorized:
+ # Convert error to failure
+ self.fail('Unauthorized')
+
+ def testSecurityOfProtectedObject(self):
+ # Test security of a protected resource
+ try:
+ self.folder.restrictedTraverse('secret_html')
+ except Unauthorized:
+ pass # Test passed
+ else:
+ self.fail('Resource not protected')
+
+ def testURLSecurityOfProtectedObject(self):
+ # Test web security of a protected resource
+ urllib._urlopener = UnauthorizedOpener()
+ try:
+ urllib.urlopen(folder_url+'/secret_html')
+ except Unauthorized:
+ pass # Test passed
+ else:
+ self.fail('Resource not protected')
+
+ def testModifyObject(self):
+ # Test a script that modifies the ZODB
+ self.setRoles(['Manager'])
+ self.app.REQUEST.set('title', 'Foo')
+ page = self.folder.object.change_title(self.folder.object,
+ self.app.REQUEST)
+ self.assertEqual(page, 'Foo')
+ self.assertEqual(self.folder.object.title, 'Foo')
+
+ def testURLModifyObject(self):
+ # Test a transaction that actually commits something
+ urllib._urlopener = ManagementOpener()
+ page = urllib.urlopen(folder_url+'/object/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.
+ '''
+
+ # Note: By inheriting from TestWebserver we run the same
+ # test methods as above!
+
+ def testConnectionIsShared(self):
+ # Due to sandboxing the ZServer thread operates on the
+ # same connection as the main thread, allowing us to
+ # see changes made to 'object' right away.
+ urllib._urlopener = ManagementOpener()
+ urllib.urlopen(folder_url+'/object/change_title?title=Foo')
+ self.assertEqual(self.folder.object.title, 'Foo')
+
+ def testCanCommit(self):
+ # Additionally, it allows us to commit transactions without
+ # harming the test ZODB.
+ self.folder.foo = 1
+ get_transaction().commit()
+ self.folder.foo = 2
+ get_transaction().commit()
+
+
+def test_suite():
+ from unittest import TestSuite, makeSuite
+ suite = TestSuite()
+ suite.addTest(makeSuite(TestWebserver))
+ suite.addTest(makeSuite(TestSandboxedWebserver))
+ return suite
+
+if __name__ == '__main__':
+ framework()
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/testZODBCompat.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/testZODBCompat.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/testZODBCompat.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,353 @@
+#
+# Tests ZODB behavior in ZopeTestCase
+#
+# Demonstrates that cut/copy/paste/clone/rename and import/export
+# work in ZopeTestCase if a subtransaction is commited before performing
+# the respective operations.
+#
+
+# $Id: testZODBCompat.py,v 1.17 2004/04/09 12:38:37 shh42 Exp $
+
+import os, sys
+if __name__ == '__main__':
+ execfile(os.path.join(sys.path[0], 'framework.py'))
+
+from Testing import ZopeTestCase
+
+from AccessControl.Permissions import add_documents_images_and_files
+from AccessControl.Permissions import delete_objects
+import tempfile
+
+folder_name = ZopeTestCase.folder_name
+cutpaste_permissions = [add_documents_images_and_files, delete_objects]
+
+
+class TestCopyPaste(ZopeTestCase.ZopeTestCase):
+
+ def afterSetUp(self):
+ self.setPermissions(cutpaste_permissions)
+ self.folder.addDTMLMethod('doc', file='foo')
+ # _p_oids are None until we commit a subtransaction
+ self.assertEqual(self.folder._p_oid, None)
+ get_transaction().commit(1)
+ self.failIfEqual(self.folder._p_oid, None)
+
+ def testCutPaste(self):
+ cb = self.folder.manage_cutObjects(['doc'])
+ self.folder.manage_pasteObjects(cb)
+ self.failUnless(hasattr(self.folder, 'doc'))
+ self.failIf(hasattr(self.folder, 'copy_of_doc'))
+
+ def testCopyPaste(self):
+ cb = self.folder.manage_copyObjects(['doc'])
+ self.folder.manage_pasteObjects(cb)
+ self.failUnless(hasattr(self.folder, 'doc'))
+ self.failUnless(hasattr(self.folder, 'copy_of_doc'))
+
+ def testClone(self):
+ self.folder.manage_clone(self.folder.doc, 'new_doc')
+ self.failUnless(hasattr(self.folder, 'doc'))
+ self.failUnless(hasattr(self.folder, 'new_doc'))
+
+ def testRename(self):
+ self.folder.manage_renameObjects(['doc'], ['new_doc'])
+ self.failIf(hasattr(self.folder, 'doc'))
+ self.failUnless(hasattr(self.folder, 'new_doc'))
+
+ def testCOPY(self):
+ # WebDAV COPY
+ request = self.app.REQUEST
+ request.environ['HTTP_DEPTH'] = 'infinity'
+ request.environ['HTTP_DESTINATION'] = 'http://foo.com/%s/new_doc' % folder_name
+ self.folder.doc.COPY(request, request.RESPONSE)
+ self.failUnless(hasattr(self.folder, 'doc'))
+ self.failUnless(hasattr(self.folder, 'new_doc'))
+
+ def testMOVE(self):
+ # WebDAV MOVE
+ request = self.app.REQUEST
+ request.environ['HTTP_DEPTH'] = 'infinity'
+ request.environ['HTTP_DESTINATION'] = 'http://foo.com/%s/new_doc' % folder_name
+ self.folder.doc.MOVE(request, request.RESPONSE)
+ self.failIf(hasattr(self.folder, 'doc'))
+ self.failUnless(hasattr(self.folder, 'new_doc'))
+
+
+class TestImportExport(ZopeTestCase.ZopeTestCase):
+
+ def afterSetUp(self):
+ self.setupLocalEnvironment()
+ self.folder.addDTMLMethod('doc', file='foo')
+ # _p_oids are None until we commit a subtransaction
+ self.assertEqual(self.folder._p_oid, None)
+ get_transaction().commit(1)
+ self.failIfEqual(self.folder._p_oid, None)
+
+ def testExport(self):
+ self.folder.manage_exportObject('doc')
+ self.failUnless(os.path.exists(self.zexp_file))
+
+ def testImport(self):
+ self.folder.manage_exportObject('doc')
+ self.folder._delObject('doc')
+ self.folder.manage_importObject('doc.zexp')
+ self.failUnless(hasattr(self.folder, 'doc'))
+
+ # To make export and import happy, we have to provide a file-
+ # system 'import' directory and adapt the configuration a bit:
+
+ local_home = tempfile.gettempdir()
+ import_dir = os.path.join(local_home, 'import')
+ zexp_file = os.path.join(import_dir, 'doc.zexp')
+
+ def setupLocalEnvironment(self):
+ # Create the 'import' directory
+ os.mkdir(self.import_dir)
+ try:
+ import App.config
+ except ImportError:
+ # Modify builtins
+ builtins = getattr(__builtins__, '__dict__', __builtins__)
+ self._ih = INSTANCE_HOME
+ builtins['INSTANCE_HOME'] = self.local_home
+ self._ch = CLIENT_HOME
+ builtins['CLIENT_HOME'] = self.import_dir
+ else:
+ # Zope >= 2.7
+ config = App.config.getConfiguration()
+ self._ih = config.instancehome
+ config.instancehome = self.local_home
+ self._ch = config.clienthome
+ config.clienthome = self.import_dir
+ App.config.setConfiguration(config)
+
+ def afterClear(self):
+ # Remove external resources
+ try: os.remove(self.zexp_file)
+ except OSError: pass
+ try: os.rmdir(self.import_dir)
+ except OSError: pass
+ try:
+ import App.config
+ except ImportError:
+ # Restore builtins
+ builtins = getattr(__builtins__, '__dict__', __builtins__)
+ if hasattr(self, '_ih'):
+ builtins['INSTANCE_HOME'] = self._ih
+ if hasattr(self, '_ch'):
+ builtins['CLIENT_HOME'] = self._ch
+ else:
+ # Zope >= 2.7
+ config = App.config.getConfiguration()
+ if hasattr(self, '_ih'):
+ config.instancehome = self._ih
+ if hasattr(self, '_ch'):
+ config.clienthome = self._ch
+ 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())
+get_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
+ left untouched (clean). This means that such variables will keep
+ their values across tests.
+
+ The only use case yet encountered in the wild is portal_memberdata's
+ _v_temps attribute. Test authors are cautioned to watch out for
+ occurrences of _v_ and _p_ attributes of objects that are not recreated
+ for every test method execution, but preexist in the test ZODB.
+
+ It is therefore deemed essential to initialize any _v_ and _p_
+ attributes of such objects in afterSetup(), as otherwise test results
+ will be distorted!
+
+ Note that _v_ attributes used to be transactional in Zope < 2.6.
+
+ This testcase exploits the fact that test methods are sorted by name.
+ '''
+
+ def afterSetUp(self):
+ self.dummy = self.app.dummy1 # See above
+
+ def testNormal_01(self):
+ # foo is always None
+ self.assertEqual(self.dummy.foo, None)
+ self.dummy.foo = 'foo'
+
+ def testNormal_02(self):
+ # foo is always None
+ self.assertEqual(self.dummy.foo, None)
+ self.dummy.foo = 'bar'
+
+ def testNormal_03(self):
+ # foo is always None
+ self.assertEqual(self.dummy.foo, None)
+
+ def testPersistent_01(self):
+ # _p_foo is initially None
+ self.assertEqual(self.dummy._p_foo, None)
+ self.dummy._p_foo = 'foo'
+
+ def testPersistent_02(self):
+ # _p_foo retains value assigned by previous test
+ self.assertEqual(self.dummy._p_foo, 'foo')
+ self.dummy._p_foo = 'bar'
+
+ def testPersistent_03(self):
+ # _p_foo retains value assigned by previous test
+ self.assertEqual(self.dummy._p_foo, 'bar')
+
+ def testVolatile_01(self):
+ # _v_foo is initially None
+ self.assertEqual(self.dummy._v_foo, None)
+ self.dummy._v_foo = 'foo'
+
+ def testVolatile_02(self):
+ if hasattr(self.app._p_jar, 'register'):
+ # _v_foo retains value assigned by previous test
+ self.assertEqual(self.dummy._v_foo, 'foo')
+ else:
+ # XXX: _v_foo is transactional in Zope < 2.6
+ self.assertEqual(self.dummy._v_foo, None)
+ self.dummy._v_foo = 'bar'
+
+ def testVolatile_03(self):
+ if hasattr(self.app._p_jar, 'register'):
+ # _v_foo retains value assigned by previous test
+ self.assertEqual(self.dummy._v_foo, 'bar')
+ else:
+ # XXX: _v_foo is transactional in Zope < 2.6
+ self.assertEqual(self.dummy._v_foo, None)
+
+
+class TestAttributesOfDirtyObjects(ZopeTestCase.ZopeTestCase):
+ '''This testcase shows that _v_ and _p_ attributes of dirty objects
+ ARE removed on abort.
+
+ This testcase exploits the fact that test methods are sorted by name.
+ '''
+
+ def afterSetUp(self):
+ self.dummy = self.app.dummy2 # See above
+ self.dummy.touchme = 1 # Tag, you're dirty
+
+ def testDirtyNormal_01(self):
+ # foo is always None
+ self.assertEqual(self.dummy.foo, None)
+ self.dummy.foo = 'foo'
+
+ def testDirtyNormal_02(self):
+ # foo is always None
+ self.assertEqual(self.dummy.foo, None)
+ self.dummy.foo = 'bar'
+
+ def testDirtyNormal_03(self):
+ # foo is always None
+ self.assertEqual(self.dummy.foo, None)
+
+ def testDirtyPersistent_01(self):
+ # _p_foo is alwas None
+ self.assertEqual(self.dummy._p_foo, None)
+ self.dummy._p_foo = 'foo'
+
+ def testDirtyPersistent_02(self):
+ # _p_foo is alwas None
+ self.assertEqual(self.dummy._p_foo, None)
+ self.dummy._p_foo = 'bar'
+
+ def testDirtyPersistent_03(self):
+ # _p_foo is alwas None
+ self.assertEqual(self.dummy._p_foo, None)
+
+ def testDirtyVolatile_01(self):
+ # _v_foo is always None
+ self.assertEqual(self.dummy._v_foo, None)
+ self.dummy._v_foo = 'foo'
+
+ def testDirtyVolatile_02(self):
+ # _v_foo is always None
+ self.assertEqual(self.dummy._v_foo, None)
+ self.dummy._v_foo = 'bar'
+
+ def testDirtyVolatile_03(self):
+ # _v_foo is always None
+ self.assertEqual(self.dummy._v_foo, None)
+
+
+class TestTransactionAbort(ZopeTestCase.ZopeTestCase):
+
+ def testTransactionAbort(self):
+ self.folder.foo = 1
+ self.failUnless(hasattr(self.folder, 'foo'))
+ get_transaction().abort()
+ # The foo attribute is still present
+ self.failUnless(hasattr(self.folder, 'foo'))
+
+ def testSubTransactionAbort(self):
+ self.folder.foo = 1
+ self.failUnless(hasattr(self.folder, 'foo'))
+ get_transaction().commit(1)
+ get_transaction().abort()
+ # This time the abort nukes the foo attribute...
+ self.failIf(hasattr(self.folder, 'foo'))
+
+ def testTransactionAbortPersistent(self):
+ self.folder._p_foo = 1
+ self.failUnless(hasattr(self.folder, '_p_foo'))
+ get_transaction().abort()
+ # The _p_foo attribute is still present
+ self.failUnless(hasattr(self.folder, '_p_foo'))
+
+ def testSubTransactionAbortPersistent(self):
+ self.folder._p_foo = 1
+ self.failUnless(hasattr(self.folder, '_p_foo'))
+ get_transaction().commit(1)
+ get_transaction().abort()
+ # This time the abort nukes the _p_foo attribute...
+ self.failIf(hasattr(self.folder, '_p_foo'))
+
+ def testTransactionAbortVolatile(self):
+ self.folder._v_foo = 1
+ self.failUnless(hasattr(self.folder, '_v_foo'))
+ get_transaction().abort()
+ # The _v_foo attribute is still present
+ self.failUnless(hasattr(self.folder, '_v_foo'))
+
+ def testSubTransactionAbortVolatile(self):
+ self.folder._v_foo = 1
+ self.failUnless(hasattr(self.folder, '_v_foo'))
+ get_transaction().commit(1)
+ get_transaction().abort()
+ # This time the abort nukes the _v_foo attribute...
+ self.failIf(hasattr(self.folder, '_v_foo'))
+
+
+def test_suite():
+ from unittest import TestSuite, makeSuite
+ suite = TestSuite()
+ suite.addTest(makeSuite(TestCopyPaste))
+ suite.addTest(makeSuite(TestImportExport))
+ suite.addTest(makeSuite(TestAttributesOfCleanObjects))
+ suite.addTest(makeSuite(TestAttributesOfDirtyObjects))
+ suite.addTest(makeSuite(TestTransactionAbort))
+ return suite
+
+if __name__ == '__main__':
+ framework()
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/testZopeTestCase.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/testZopeTestCase.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/testZopeTestCase.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,470 @@
+#
+# Tests the ZopeTestCase, eating its own dogfood
+#
+# NOTE: This is *not* an example ZopeTestCase. Do not
+# use this file as a blueprint for your own tests!
+#
+# See testPythonScript.py and testShoppingCart.py for
+# example test cases. See testSkeleton.py for a quick
+# way of getting started.
+#
+
+# $Id: testZopeTestCase.py,v 1.17 2004/04/09 12:38:37 shh42 Exp $
+
+import os, sys
+if __name__ == '__main__':
+ execfile(os.path.join(sys.path[0], 'framework.py'))
+
+from Testing import ZopeTestCase
+
+from Acquisition import aq_base
+from AccessControl import getSecurityManager
+from types import ListType
+
+folder_name = ZopeTestCase.folder_name
+user_name = ZopeTestCase.user_name
+user_role = ZopeTestCase.user_role
+standard_permissions = ZopeTestCase.standard_permissions
+
+
+def hasattr_(ob, attr):
+ return hasattr(aq_base(ob), attr)
+
+
+class TestZopeTestCase(ZopeTestCase.ZopeTestCase):
+ '''Incrementally exercise the ZopeTestCase API.
+ Exploit the fact that tests are sorted by name.
+ '''
+
+ _setUp = ZopeTestCase.ZopeTestCase.setUp
+ _tearDown = ZopeTestCase.ZopeTestCase.tearDown
+
+ def setUp(self):
+ # For this test case we *want* to start
+ # with an empty fixture.
+ self._called = []
+ # Implicitly aborts previous transaction
+ get_transaction().begin()
+
+ def beforeSetUp(self):
+ self._called.append('beforeSetUp')
+
+ def afterSetUp(self):
+ self._called.append('afterSetUp')
+
+ def beforeTearDown(self):
+ self._called.append('beforeTearDown')
+
+ def beforeClose(self):
+ self._called.append('beforeClose')
+
+ def afterClear(self):
+ self._called.append('afterClear')
+
+ def test_01_setupFolder(self):
+ # Folder should be set up
+ self.app = self._app()
+ self._setupFolder()
+ self.failUnless(hasattr_(self.app, folder_name))
+ self.failUnless(hasattr(self, 'folder'))
+ self.failUnless(user_role in self.folder.userdefined_roles())
+ self.assertPermissionsOfRole(standard_permissions, user_role)
+
+ def test_02_setupUserFolder(self):
+ # User folder should be set up
+ self.app = self._app()
+ self._setupFolder()
+ self._setupUserFolder()
+ self.failUnless(hasattr_(self.folder, 'acl_users'))
+
+ def test_03_setupUser(self):
+ # User should be set up
+ self.app = self._app()
+ self._setupFolder()
+ self._setupUserFolder()
+ self._setupUser()
+ acl_user = self.folder.acl_users.getUserById(user_name)
+ self.failUnless(acl_user)
+ self.assertEqual(acl_user.getRoles(), (user_role, 'Authenticated'))
+ self.assertEqual(type(acl_user.roles), ListType)
+
+ def test_04_setRoles(self):
+ # Roles should be set for user
+ self.app = self._app()
+ self._setupFolder()
+ self._setupUserFolder()
+ self._setupUser()
+ test_roles = ['Manager', user_role]
+ test_roles.sort()
+ self.setRoles(test_roles)
+ acl_user = self.folder.acl_users.getUserById(user_name)
+ user_roles = list(acl_user.getRoles())
+ user_roles.remove('Authenticated')
+ user_roles.sort()
+ self.assertEqual(user_roles, test_roles)
+
+ def test_05_setRoles_2(self):
+ # Roles of logged in user should be updated
+ self.app = self._app()
+ self._setupFolder()
+ self._setupUserFolder()
+ self._setupUser()
+ self.login()
+ test_roles = ['Manager', user_role]
+ test_roles.sort()
+ self.setRoles(test_roles)
+ auth_user = getSecurityManager().getUser()
+ user_roles = list(auth_user.getRoles())
+ user_roles.remove('Authenticated')
+ user_roles.sort()
+ self.assertEqual(user_roles, test_roles)
+
+ def test_06_setRoles_3(self):
+ # Roles should be set for a specified user
+ self.app = self._app()
+ self._setupFolder()
+ self._setupUserFolder()
+ self.folder.acl_users._doAddUser('test_user_2_', 'secret', [], [])
+ test_roles = ['Manager', user_role]
+ test_roles.sort()
+ self.setRoles(test_roles, 'test_user_2_')
+ acl_user = self.folder.acl_users.getUserById('test_user_2_')
+ user_roles = list(acl_user.getRoles())
+ user_roles.remove('Authenticated')
+ user_roles.sort()
+ self.assertEqual(user_roles, test_roles)
+
+ def test_07_setPermissions(self):
+ # Permissions should be set for user
+ self.app = self._app()
+ self._setupFolder()
+ test_perms = standard_permissions + ['Add Folders']
+ self.assertPermissionsOfRole(standard_permissions, user_role)
+ self.setPermissions(test_perms)
+ self.assertPermissionsOfRole(test_perms, user_role)
+
+ def test_08_setPermissions_2(self):
+ # Permissions should be set for a specified role
+ self.app = self._app()
+ self._setupFolder()
+ self.folder._addRole('test_role_2_')
+ self.assertPermissionsOfRole([], 'test_role_2_')
+ self.setPermissions(standard_permissions, 'test_role_2_')
+ self.assertPermissionsOfRole(standard_permissions, 'test_role_2_')
+
+ def test_09_login(self):
+ # User should be able to log in
+ self.app = self._app()
+ self._setupFolder()
+ self._setupUserFolder()
+ self._setupUser()
+ auth_name = getSecurityManager().getUser().getUserName()
+ self.assertEqual(auth_name, 'Anonymous User')
+ self.login()
+ auth_name = getSecurityManager().getUser().getId()
+ self.assertEqual(auth_name, user_name)
+
+ def test_10_login_2(self):
+ # A specified user should be logged in
+ self.app = self._app()
+ self._setupFolder()
+ self._setupUserFolder()
+ self.folder.acl_users._doAddUser('test_user_2_', 'secret', [], [])
+ auth_name = getSecurityManager().getUser().getUserName()
+ self.assertEqual(auth_name, 'Anonymous User')
+ self.login('test_user_2_')
+ auth_name = getSecurityManager().getUser().getId()
+ self.assertEqual(auth_name, 'test_user_2_')
+
+ def test_11_login_3(self):
+ # Unknown user should raise AttributeError
+ self.app = self._app()
+ self._setupFolder()
+ self._setupUserFolder()
+ self.assertRaises(AttributeError, self.login, 'test_user_3_')
+
+ def test_12_logout(self):
+ # User should be able to log out
+ self.app = self._app()
+ self._setupFolder()
+ self._setupUserFolder()
+ self._setupUser()
+ self.login()
+ self.logout()
+ auth_name = getSecurityManager().getUser().getUserName()
+ self.assertEqual(auth_name, 'Anonymous User')
+
+ def test_13_clear(self):
+ # Everything should be removed
+ self.app = self._app()
+ self._setupFolder()
+ self._setupUserFolder()
+ self._setupUser()
+ self._clear(1)
+ self.failIf(hasattr_(self.app, folder_name))
+ auth_name = getSecurityManager().getUser().getUserName()
+ self.assertEqual(auth_name, 'Anonymous User')
+ self.assertEqual(self._called, ['beforeClose', 'afterClear'])
+ # _clear must not fail when called repeatedly
+ self._clear()
+
+ def test_14_setUp(self):
+ # Everything should be set up
+ self._setUp()
+ self.failUnless(hasattr_(self.app, folder_name))
+ self.failUnless(hasattr(self, 'folder'))
+ self.failUnless(user_role in self.folder.userdefined_roles())
+ self.assertPermissionsOfRole(standard_permissions, user_role)
+ self.failUnless(hasattr_(self.folder, 'acl_users'))
+ acl_user = self.folder.acl_users.getUserById(user_name)
+ self.failUnless(acl_user)
+ self.assertEqual(acl_user.getRoles(), (user_role, 'Authenticated'))
+ self.assertEqual(type(acl_user.roles), ListType)
+ auth_name = getSecurityManager().getUser().getId()
+ self.assertEqual(auth_name, user_name)
+ # XXX: Changed in 0.9.0
+ #self.assertEqual(self._called, ['afterClear', 'beforeSetUp', 'afterSetUp'])
+ self.assertEqual(self._called, ['beforeSetUp', 'afterSetUp'])
+
+ def test_15_tearDown(self):
+ # Everything should be removed
+ self._setUp()
+ self._called = []
+ self._tearDown()
+ self.failIf(hasattr_(self.app, folder_name))
+ auth_name = getSecurityManager().getUser().getUserName()
+ self.assertEqual(auth_name, 'Anonymous User')
+ self.assertEqual(self._called, ['beforeTearDown', 'beforeClose', 'afterClear'])
+
+ def test_16_setupFlag(self):
+ # Nothing should be set up
+ self._setup_fixture = 0
+ self._setUp()
+ self.failIf(hasattr_(self.app, folder_name))
+ auth_name = getSecurityManager().getUser().getUserName()
+ self.assertEqual(auth_name, 'Anonymous User')
+ # XXX: Changed in 0.9.0
+ #self.assertEqual(self._called, ['afterClear', 'beforeSetUp', 'afterSetUp'])
+ self.assertEqual(self._called, ['beforeSetUp', 'afterSetUp'])
+
+ def test_17_setupFlag_2(self):
+ # Nothing should be cleared
+ self._setUp()
+ self._setup_fixture = 0
+ self._called = []
+ self._clear()
+ # XXX: Since 0.8.4 we abort before closing the connection
+ #self.failUnless(hasattr_(self.app, folder_name))
+ auth_name = getSecurityManager().getUser().getUserName()
+ self.assertEqual(auth_name, 'Anonymous User')
+ self.assertEqual(self._called, ['afterClear'])
+
+ # Bug tests
+
+ def test_18_setOwnerPermissions(self):
+ # Permissions should be modified for the Owner role
+ self.app = self._app()
+ self._setupFolder()
+ self.assertPermissionsOfRole([], 'Owner')
+ self.setPermissions(standard_permissions, 'Owner')
+ self.assertPermissionsOfRole(standard_permissions, 'Owner')
+
+ def test_19_setManagerPermissions(self):
+ # Permissions should *not* be modified for the Manager role
+ self.app = self._app()
+ self._setupFolder()
+ # Setting permissions for Manager role does not work like this
+ manager_perms = self.getPermissionsOfRole('Manager')
+ self.setPermissions(standard_permissions, 'Manager')
+ # Manager does still have all permissions
+ self.assertPermissionsOfRole(manager_perms, 'Manager')
+
+ def test_20_setManagerPermissions_2(self):
+ # Permissions should be modified for the Manager role
+ self.app = self._app()
+ self._setupFolder()
+ # However, it works like that (because we turn off acquisition?)
+ manager_perms = self.getPermissionsOfRole('Manager')
+ self.folder.manage_permission('Take ownership', ['Owner'], acquire=0)
+ self.assertPermissionsOfRole(['Take ownership'], 'Owner')
+ # Manager does not have 'Take ownership' anymore
+ manager_perms.remove('Take ownership')
+ self.assertPermissionsOfRole(manager_perms, 'Manager')
+
+ # Helpers
+
+ def getPermissionsOfRole(self, role, context=None):
+ '''Returns sorted list of permission names of the
+ given role in the given context.
+ '''
+ if context is None: context = self.folder
+ perms = context.permissionsOfRole(role)
+ perms = [p['name'] for p in perms if p['selected']]
+ perms.sort()
+ return perms
+
+ def assertPermissionsOfRole(self, permissions, role, context=None):
+ '''Compares list of permission names to permissions of the
+ given role in the given context. Fails if the lists are not
+ found equal.
+ '''
+ perms = list(permissions)[:]
+ perms.sort()
+ self.assertEqual(self.getPermissionsOfRole(role, context), perms)
+
+
+import unittest
+
+class TestConnectionRegistry(unittest.TestCase):
+ '''Tests the ZODB connection registry'''
+
+ class Conn:
+ closed = 0
+ def close(self):
+ self.closed = 1
+
+ def setUp(self):
+ self.reg = ZopeTestCase.utils.ConnectionRegistry()
+ self.conns = [self.Conn(), self.Conn(), self.Conn()]
+
+ def testRegister(self):
+ # Should be able to register connections
+ for conn in self.conns:
+ self.reg.register(conn)
+ assert len(self.reg) == 3
+
+ def testCloseConnection(self):
+ # Should be able to close a single registered connection
+ for conn in self.conns:
+ self.reg.register(conn)
+ assert len(self.reg) == 3
+ self.reg.close(self.conns[0])
+ assert len(self.reg) == 2
+ assert self.conns[0].closed == 1
+ assert self.conns[1].closed == 0
+ assert self.conns[2].closed == 0
+
+ def testCloseSeveralConnections(self):
+ # Should be able to close all registered connections one-by-one
+ for conn in self.conns:
+ self.reg.register(conn)
+ assert len(self.reg) == 3
+ self.reg.close(self.conns[0])
+ assert len(self.reg) == 2
+ assert self.conns[0].closed == 1
+ assert self.conns[1].closed == 0
+ assert self.conns[2].closed == 0
+ self.reg.close(self.conns[2])
+ assert len(self.reg) == 1
+ assert self.conns[0].closed == 1
+ assert self.conns[1].closed == 0
+ assert self.conns[2].closed == 1
+ self.reg.close(self.conns[1])
+ assert len(self.reg) == 0
+ assert self.conns[0].closed == 1
+ assert self.conns[1].closed == 1
+ assert self.conns[2].closed == 1
+
+ def testCloseForeignConnection(self):
+ # Should be able to close a connection that has not been registered
+ for conn in self.conns:
+ self.reg.register(conn)
+ assert len(self.reg) == 3
+ conn = self.Conn()
+ self.reg.close(conn)
+ assert len(self.reg) == 3
+ assert self.conns[0].closed == 0
+ assert self.conns[1].closed == 0
+ assert self.conns[2].closed == 0
+ assert conn.closed == 1
+
+ def testCloseAllConnections(self):
+ # Should be able to close all registered connections at once
+ for conn in self.conns:
+ self.reg.register(conn)
+ assert len(self.reg) == 3
+ self.reg.closeAll()
+ assert len(self.reg) == 0
+ assert self.conns[0].closed == 1
+ assert self.conns[1].closed == 1
+ assert self.conns[2].closed == 1
+
+
+from AccessControl.User import UserFolder
+from Acquisition import aq_inner, aq_parent, aq_chain
+
+class WrappingUserFolder(UserFolder):
+ '''User folder returning wrapped user objects'''
+
+ def getUser(self, name):
+ return UserFolder.getUser(self, name).__of__(self)
+
+
+class TestPlainUserFolder(ZopeTestCase.ZopeTestCase):
+ '''Tests whether user objects are properly wrapped'''
+
+ def testGetUserDoesNotWrapUser(self):
+ user = self.folder.acl_users.getUserById(user_name)
+ self.failIf(hasattr(user, 'aq_base'))
+ self.failUnless(user is aq_base(user))
+
+ def testLoggedInUserIsWrapped(self):
+ user = getSecurityManager().getUser()
+ self.assertEqual(user.getId(), user_name)
+ self.failUnless(hasattr(user, 'aq_base'))
+ self.failUnless(user.__class__.__name__, 'User')
+ self.failUnless(user.aq_parent.__class__.__name__, 'UserFolder')
+ self.failUnless(user.aq_parent.aq_parent.__class__.__name__, 'Folder')
+
+
+class TestWrappingUserFolder(ZopeTestCase.ZopeTestCase):
+ '''Tests whether user objects are properly wrapped'''
+
+ def _setupUserFolder(self):
+ self.folder._setObject('acl_users', WrappingUserFolder())
+
+ def testGetUserWrapsUser(self):
+ user = self.folder.acl_users.getUserById(user_name)
+ self.failUnless(hasattr(user, 'aq_base'))
+ self.failIf(user is aq_base(user))
+ self.failUnless(user.aq_parent.__class__.__name__, 'WrappingUserFolder')
+
+ def testLoggedInUserIsWrapped(self):
+ user = getSecurityManager().getUser()
+ self.assertEqual(user.getId(), user_name)
+ self.failUnless(hasattr(user, 'aq_base'))
+ self.failUnless(user.__class__.__name__, 'User')
+ self.failUnless(user.aq_parent.__class__.__name__, 'WrappingUserFolder')
+ self.failUnless(user.aq_parent.aq_parent.__class__.__name__, 'Folder')
+
+
+class TestRequestVariables(ZopeTestCase.ZopeTestCase):
+ '''Makes sure the REQUEST contains required variables'''
+
+ def testRequestVariables(self):
+ request = self.app.REQUEST
+ self.failIfEqual(request.get('SERVER_NAME', ''), '')
+ self.failIfEqual(request.get('SERVER_PORT', ''), '')
+ self.failIfEqual(request.get('REQUEST_METHOD', ''), '')
+ self.failIfEqual(request.get('URL', ''), '')
+ self.failIfEqual(request.get('SERVER_URL', ''), '')
+ self.failIfEqual(request.get('URL0', ''), '')
+ self.failIfEqual(request.get('URL1', ''), '')
+ self.failIfEqual(request.get('BASE0', ''), '')
+ self.failIfEqual(request.get('BASE1', ''), '')
+ self.failIfEqual(request.get('BASE2', ''), '')
+
+
+def test_suite():
+ from unittest import TestSuite, makeSuite
+ suite = TestSuite()
+ suite.addTest(makeSuite(TestZopeTestCase))
+ suite.addTest(makeSuite(TestConnectionRegistry))
+ suite.addTest(makeSuite(TestPlainUserFolder))
+ suite.addTest(makeSuite(TestWrappingUserFolder))
+ suite.addTest(makeSuite(TestRequestVariables))
+ return suite
+
+if __name__ == '__main__':
+ framework()
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/threadutils.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/threadutils.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/threadutils.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,46 @@
+#
+# Parts of ZServer support are in this module so they can
+# be imported more selectively.
+#
+
+# $Id: threadutils.py,v 1.5 2004/01/09 14:35:08 shh42 Exp $
+
+from threading import Thread
+from StringIO import StringIO
+
+dummyLOG = StringIO()
+
+
+def zserverRunner(host, port, log=None):
+ '''Runs an HTTP ZServer on host:port.'''
+ from ZServer import logger, asyncore
+ from ZServer import zhttp_server, zhttp_handler
+ if log is None: log = dummyLOG
+ lg = logger.file_logger(log)
+ hs = zhttp_server(ip=host, port=port, resolver=None, logger_object=lg)
+ zh = zhttp_handler(module='Zope', uri_base='')
+ hs.install_handler(zh)
+ asyncore.loop()
+
+
+class QuietThread(Thread):
+ '''This thread eats all exceptions'''
+ def __init__(self, target=None, args=(), kwargs={}):
+ Thread.__init__(self, target=target, args=args, kwargs=kwargs)
+ self.__old_bootstrap = Thread._Thread__bootstrap
+ def __bootstrap(self):
+ try: self.__old_bootstrap(self)
+ except: pass
+ _Thread__bootstrap = __bootstrap
+
+
+def QuietPublisher(self, accept):
+ '''This server eats all exceptions'''
+ try: self.__old_init__(accept)
+ except: pass
+
+
+from ZServer.PubCore.ZServerPublisher import ZServerPublisher
+ZServerPublisher.__old_init__ = ZServerPublisher.__init__
+ZServerPublisher.__init__ = QuietPublisher
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/utils.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/utils.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/utils.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,162 @@
+#
+# Utility functions
+#
+# These functions are designed to be imported and run at
+# module level to add functionality to the test environment.
+#
+
+# $Id: utils.py,v 1.13 2004/02/17 19:34:36 shh42 Exp $
+
+
+def setupCoreSessions(app=None):
+ '''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')
+ app._setObject('temp_folder', tf)
+ commit = 1
+
+ if not hasattr(aq_base(app.temp_folder), 'session_data'):
+ from Products.Transience.Transience import TransientObjectContainer
+ toc = TransientObjectContainer('session_data',
+ 'Session Data Container',
+ timeout_mins=3,
+ limit=100)
+ app.temp_folder._setObject('session_data', toc)
+ commit = 1
+
+ if not hasattr(app, 'browser_id_manager'):
+ from Products.Sessions.BrowserIdManager import BrowserIdManager
+ bid = BrowserIdManager('browser_id_manager',
+ 'Browser Id Manager')
+ app._setObject('browser_id_manager', bid)
+ commit = 1
+
+ if not hasattr(app, 'session_data_manager'):
+ from Products.Sessions.SessionDataManager import SessionDataManager
+ sdm = SessionDataManager('session_data_manager',
+ title='Session Data Manager',
+ path='/temp_folder/session_data',
+ requestName='SESSION')
+ app._setObject('session_data_manager', sdm)
+ commit = 1
+
+ if commit: get_transaction().commit()
+
+
+def setupZGlobals(app=None):
+ '''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
+ root['ZGlobals'] = OOBTree()
+ get_transaction().commit()
+
+
+def setupSiteErrorLog(app=None):
+ '''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
+ except ImportError:
+ pass
+ else:
+ app._setObject('error_log', SiteErrorLog())
+ get_transaction().commit()
+
+
+import os, time
+
+def importObjectFromFile(container, filename, quiet=0):
+ '''Imports an object from a (.zexp) file into the given container.'''
+ from ZopeLite import _print
+ start = time.time()
+ if not quiet: _print("Importing %s ... " % os.path.basename(filename))
+ container._importObjectFromFile(filename, verify=0)
+ get_transaction().commit()
+ if not quiet: _print('done (%.3fs)\n' % (time.time() - start))
+
+
+_Z2HOST = None
+_Z2PORT = None
+
+def startZServer(number_of_threads=1, log=None):
+ '''Starts an HTTP ZServer thread.'''
+ global _Z2HOST, _Z2PORT
+ if _Z2HOST is None:
+ import random
+ _Z2HOST = '127.0.0.1'
+ _Z2PORT = random.choice(range(55000, 55500))
+ from ZServer import setNumberOfThreads
+ setNumberOfThreads(number_of_threads)
+ from threadutils import QuietThread, zserverRunner
+ t = QuietThread(target=zserverRunner, args=(_Z2HOST, _Z2PORT, log))
+ t.setDaemon(1)
+ t.start()
+ return _Z2HOST, _Z2PORT
+
+
+import sys
+
+def makerequest(app, stdout=sys.stdout):
+ '''Wraps the app into a fresh REQUEST.'''
+ from ZPublisher.BaseRequest import RequestContainer
+ from ZPublisher.Request import Request
+ from ZPublisher.Response import Response
+ response = Response(stdout=stdout)
+ environ = {}
+ environ['SERVER_NAME'] = _Z2HOST or 'nohost'
+ environ['SERVER_PORT'] = '%d' % (_Z2PORT or 80)
+ environ['REQUEST_METHOD'] = 'GET'
+ request = Request(sys.stdin, environ, response)
+ request._steps = ['noobject'] # Fake a published object
+ return app.__of__(RequestContainer(REQUEST=request))
+
+
+def appcall(function, *args, **kw):
+ '''Calls a function passing 'app' as first argument.'''
+ import ZopeTestCase
+ app = ZopeTestCase.app()
+ args = (app,) + args
+ try:
+ return function(*args, **kw)
+ finally:
+ ZopeTestCase.close(app)
+
+
+class ConnectionRegistry:
+ '''ZODB connection registry'''
+
+ def __init__(self):
+ self._conns = []
+
+ def register(self, conn):
+ self._conns.append(conn)
+
+ def close(self, conn):
+ try: self._conns.remove(conn)
+ except: pass
+ try: conn.close()
+ except: pass
+
+ def closeAll(self):
+ for conn in self._conns:
+ try: conn.close()
+ except: pass
+ self._conns = []
+
+ def __len__(self):
+ return len(self._conns)
+
Added: Zope/trunk/lib/python/Testing/ZopeTestCase/ztc_common.py
===================================================================
--- Zope/trunk/lib/python/Testing/ZopeTestCase/ztc_common.py 2004-09-06 15:42:10 UTC (rev 27452)
+++ Zope/trunk/lib/python/Testing/ZopeTestCase/ztc_common.py 2004-09-06 19:04:41 UTC (rev 27453)
@@ -0,0 +1,169 @@
+#
+# ztc_common.py
+#
+# This file must be called from framework.py like so
+#
+# execfile(os.path.join(os.path.dirname(Testing.__file__),
+# 'ZopeTestCase', 'ztc_common.py'))
+#
+
+# $Id: ztc_common.py,v 1.13 2004/03/30 16:40:04 shh42 Exp $
+
+
+# Overwrites the default framework() method to expose the
+# TestRunner parameters and add profiler support.
+#
+def framework(stream=sys.stderr, descriptions=1, verbosity=1):
+ if __name__ != '__main__':
+ return
+
+ if len(sys.argv) > 1:
+ arg = sys.argv[1]
+ if arg in ('profile', 'profile-tests'):
+ os.environ['PROFILE_TESTS'] = '1'
+ elif arg == 'profile-setup':
+ os.environ['PROFILE_SETUP'] = '1'
+ elif arg == 'profile-teardown':
+ os.environ['PROFILE_TEARDOWN'] = '1'
+ else:
+ sys.exit(globals()[arg]() and 1 or 0)
+
+ errors = TestRunner(stream, descriptions, verbosity).run(test_suite())
+ from Testing.ZopeTestCase import profiler; profiler.print_stats()
+ sys.exit(errors and 1 or 0)
+
+
+# Configures the Zope environment
+#
+class Configurator:
+
+ def __init__(self):
+ '''Sets up the configurator.'''
+ self.cwd = self.realpath(os.getcwd())
+ self.software_home = self.realpath(os.environ.get('SOFTWARE_HOME', ''))
+ self.instance_home = self.realpath(globals()['__INSTANCE_HOME'])
+ self.zeo_instance_home = self.realpath(os.environ.get('ZEO_INSTANCE_HOME', ''))
+ self.zope_config = self.realpath(os.environ.get('ZOPE_CONFIG', ''))
+
+ def run(self):
+ '''Runs the configurator.'''
+ if self.zope_config:
+ # Don't configure anything if people use the ZOPE_CONFIG patch
+ return
+ if self.zeo_instance_home:
+ self.setup_zeo_instance_home()
+ else:
+ if self.instance_home:
+ self.setup_instance_home()
+ else:
+ self.detect_and_setup_instance_home()
+ self.setup_custom_zodb()
+
+ def setup_zeo_instance_home(self):
+ '''If ZEO_INSTANCE_HOME has been given, assume a ZEO setup and use the
+ instance's custom_zodb.py to connect to a running ZEO server.'''
+ if os.path.isdir(os.path.join(self.zeo_instance_home, 'Products')):
+ if os.path.exists(os.path.join(self.zeo_instance_home, 'custom_zodb.py')):
+ self.add_instance(self.zeo_instance_home)
+ if self.getconfig('testinghome'):
+ self.setconfig(testinghome=self.zeo_instance_home)
+ self.setconfig(instancehome=self.zeo_instance_home)
+ else:
+ os.environ['INSTANCE_HOME'] = INSTANCE_HOME = self.zeo_instance_home
+ self.setconfig(instancehome=self.zeo_instance_home)
+ else:
+ print 'Unable to locate custom_zodb.py in %s.' % self.zeo_instance_home
+ sys.exit(1)
+ else:
+ print 'Unable to locate Products directory in %s.' % self.zeo_instance_home
+ sys.exit(1)
+
+ def setup_instance_home(self):
+ '''If INSTANCE_HOME has been given, add the instance's Products
+ and lib/python directories to the appropriate paths.'''
+ if os.path.isdir(os.path.join(self.instance_home, 'Products')):
+ self.add_instance(self.instance_home)
+ if self.getconfig('testinghome'):
+ self.setconfig(instancehome=self.instance_home)
+ else:
+ print 'Unable to locate Products directory in %s.' % self.instance_home
+ sys.exit(1)
+
+ def detect_and_setup_instance_home(self):
+ '''If INSTANCE_HOME has not been given, try to detect whether we run
+ in an instance home installation by walking up from cwd until we
+ find a 'Products' dir.'''
+ if not self.cwd.startswith(self.software_home):
+ p = d = self.cwd
+ while d:
+ if os.path.isdir(os.path.join(p, 'Products')):
+ self.add_instance(p)
+ if self.getconfig('testinghome'):
+ self.setconfig(instancehome=p)
+ break
+ p, d = os.path.split(p)
+ else:
+ print 'Unable to locate Products directory.',
+ print 'You might need to set INSTANCE_HOME.'
+ sys.exit(1)
+
+ def setup_custom_zodb(self):
+ '''If there is a custom_zodb.py file in the tests dir, use it.
+ Note that the instance has already been set at this point
+ so redirecting INSTANCE_HOME should be safe.'''
+ if os.path.exists(os.path.join(self.cwd, 'custom_zodb.py')):
+ if self.getconfig('testinghome'):
+ self.setconfig(testinghome=self.cwd)
+ else:
+ os.environ['INSTANCE_HOME'] = INSTANCE_HOME = self.cwd
+ self.setconfig(instancehome=self.cwd)
+
+ def add_instance(self, p):
+ '''Adds an INSTANCE_HOME directory to Products.__path__ and sys.path.'''
+ import Products
+ products = os.path.join(p, 'Products')
+ if os.path.isdir(products) and products not in Products.__path__:
+ Products.__path__.insert(0, products)
+ libpython = os.path.join(p, 'lib', 'python')
+ if os.path.isdir(libpython) and libpython not in sys.path:
+ sys.path.insert(0, libpython)
+
+ def getconfig(self, key):
+ '''Reads a value from Zope configuration.'''
+ try:
+ import App.config
+ except ImportError:
+ pass
+ else:
+ config = App.config.getConfiguration()
+ return getattr(config, key, None)
+
+ def setconfig(self, **kw):
+ '''Updates Zope configuration'''
+ try:
+ import App.config
+ except ImportError:
+ pass
+ else:
+ config = App.config.getConfiguration()
+ for key, value in kw.items():
+ setattr(config, key, value)
+ App.config.setConfiguration(config)
+
+ def realpath(self, path):
+ try:
+ from os.path import realpath
+ except ImportError:
+ try:
+ from App.Common import realpath
+ except ImportError:
+ realpath = os.path.abspath
+ if not path:
+ return path
+ return realpath(path)
+
+
+if __name__ == '__main__':
+ Configurator().run()
+ del Configurator
+
More information about the Zope-Checkins
mailing list