Commit c55cf411 authored by Andreas Jung's avatar Andreas Jung

added ZopeTestCase 0.9

parent 0f7da2f9
#
# 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
#
# 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))
#
# 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
#
# 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
<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)
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.
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
<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.
This diff is collapsed.
<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.
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.
'''
<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.
<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.
<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'.
<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"], [])'.
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()
ZopeTestCase 0.9.0
(c) 2002-2004, Stefan H. Holek, stefan@epy.co.at
http://zope.org/Members/shh/ZopeTestCase
License: ZPL
Zope: 2.5-2.7
##############################################################################
#
# 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()
#
# 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)
#
# 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)
#
# 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)
#
# 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__
#
# 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()
This diff is collapsed.
#
# 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()
#
# 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()
#
# 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()
#
# 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()
This diff is collapsed.
This diff is collapsed.
#
# 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
#
# 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)
#
# 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment