[Checkins] SVN: Zope/trunk/ Remove all use of ``zope.app.pagetemplate`` by cloning / simplifying code.
Tres Seaver
tseaver at palladion.com
Mon May 25 21:32:42 EDT 2009
Log message for revision 100383:
Remove all use of ``zope.app.pagetemplate`` by cloning / simplifying code.
o Added tests for previously-untested clients.
Changed:
U Zope/trunk/ZOPE_APP_DEPENDENCIES.rst
U Zope/trunk/doc/CHANGES.rst
U Zope/trunk/src/Products/Five/browser/metaconfigure.py
U Zope/trunk/src/Products/Five/browser/pagetemplatefile.py
U Zope/trunk/src/Products/Five/browser/tests/aqlegacy_ftest.txt
A Zope/trunk/src/Products/Five/browser/tests/test_metaconfigure.py
A Zope/trunk/src/Products/Five/browser/tests/test_pagetemplatefile.py
-=-
Modified: Zope/trunk/ZOPE_APP_DEPENDENCIES.rst
===================================================================
--- Zope/trunk/ZOPE_APP_DEPENDENCIES.rst 2009-05-26 00:06:45 UTC (rev 100382)
+++ Zope/trunk/ZOPE_APP_DEPENDENCIES.rst 2009-05-26 01:32:41 UTC (rev 100383)
@@ -18,10 +18,10 @@
- [_] zope.app.form
o Products.Five.form.*
-- [_] zope.app.pagetemplate
- o Products.PageTemplates.Expressions
- o Products.Five.browser.pagetemplatefile
- o Products.Five.browser.metaconfigure
+- [X] zope.app.pagetemplate
+ * Products.PageTemplates.Expressions
+ * Products.Five.browser.pagetemplatefile
+ * Products.Five.browser.metaconfigure
- [_] zope.app.publication
o ZPublisher.BaseRequest
Modified: Zope/trunk/doc/CHANGES.rst
===================================================================
--- Zope/trunk/doc/CHANGES.rst 2009-05-26 00:06:45 UTC (rev 100382)
+++ Zope/trunk/doc/CHANGES.rst 2009-05-26 01:32:41 UTC (rev 100383)
@@ -11,6 +11,9 @@
Restructuring
+++++++++++++
+- Removed all use of ``zope.app.pagetemplate`` by cloning / simplifying
+ client code.
+
- Use ``zope.pagetemplate.engine`` instead of ``zope.app.pagetemplate.engine``.
(update to versions 3.5.0 and 3.7.0, respectively, along with version 3.8.1
of ``zope.app.publisher``).
Modified: Zope/trunk/src/Products/Five/browser/metaconfigure.py
===================================================================
--- Zope/trunk/src/Products/Five/browser/metaconfigure.py 2009-05-26 00:06:45 UTC (rev 100382)
+++ Zope/trunk/src/Products/Five/browser/metaconfigure.py 2009-05-26 01:32:41 UTC (rev 100383)
@@ -22,15 +22,17 @@
from inspect import ismethod
from zope import component
+from zope.interface import implements
from zope.interface import Interface
from zope.component.zcml import handler
from zope.component.interface import provideInterface
from zope.configuration.exceptions import ConfigurationError
+from zope.publisher.interfaces import NotFound
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+from zope.publisher.interfaces.browser import IBrowserPublisher
from zope.publisher.interfaces.browser import IBrowserRequest
-from zope.publisher.interfaces.browser import IDefaultBrowserLayer
import zope.app.publisher.browser.viewmeta
-import zope.app.pagetemplate.simpleviewclass
from zope.app.publisher.browser.viewmeta import providesCallable
from zope.app.publisher.browser.viewmeta import _handle_menu
from zope.app.publisher.browser.viewmeta import _handle_for
@@ -405,10 +407,25 @@
def __call__(self):
return getattr(self, self.__page_attribute__)
-class ViewMixinForTemplates(BrowserView,
- zope.app.pagetemplate.simpleviewclass.simple):
- pass
+class ViewMixinForTemplates(BrowserView):
+ # Cloned from zope.app.pagetemplate.simpleviewclass.simple
+ implements(IBrowserPublisher)
+ def browserDefault(self, request):
+ return self, ()
+
+ def publishTraverse(self, request, name):
+ if name == 'index.html':
+ return self.index
+
+ raise NotFound(self, name, request)
+
+ def __getitem__(self, name):
+ return self.index.macros[name]
+
+ def __call__(self, *args, **kw):
+ return self.index(*args, **kw)
+
def makeClassForTemplate(filename, globals=None, used_for=None,
bases=(), cdict=None, name=u''):
# XXX needs to deal with security from the bases?
Modified: Zope/trunk/src/Products/Five/browser/pagetemplatefile.py
===================================================================
--- Zope/trunk/src/Products/Five/browser/pagetemplatefile.py 2009-05-26 00:06:45 UTC (rev 100382)
+++ Zope/trunk/src/Products/Five/browser/pagetemplatefile.py 2009-05-26 01:32:41 UTC (rev 100383)
@@ -16,7 +16,9 @@
$Id$
"""
from os.path import basename
-from zope.app.pagetemplate import viewpagetemplatefile
+from zope.component import getMultiAdapter
+from zope.pagetemplate.pagetemplatefile import PageTemplateFile
+from zope.pagetemplate.engine import TrustedAppPT
from Acquisition import aq_get
from AccessControl import getSecurityManager
@@ -29,9 +31,14 @@
def getEngine():
return _engine
-class ViewPageTemplateFile(viewpagetemplatefile.ViewPageTemplateFile):
+class ViewPageTemplateFile(TrustedAppPT, PageTemplateFile):
"""Page Template used as class variable of views defined as Python classes.
"""
+ def __init__(self, filename, _prefix=None, content_type=None):
+ _prefix = self.get_path_from_prefix(_prefix)
+ super(ViewPageTemplateFile, self).__init__(filename, _prefix)
+ if content_type is not None:
+ self.content_type = content_type
def getId(self):
return basename(self.filename)
@@ -61,41 +68,69 @@
return getEngine()
def pt_getContext(self, instance, request, **kw):
- context = super(ViewPageTemplateFile, self).pt_getContext(
- instance, request, **kw)
+ namespace = super(ViewPageTemplateFile, self).pt_getContext(**kw)
+ namespace['request'] = request
+ namespace['view'] = instance
+ namespace['context'] = context = instance.context
+ namespace['views'] = ViewMapper(context, request)
# get the root
- obj = context['context']
+ obj = context
root = None
meth = aq_get(obj, 'getPhysicalRoot', None)
if meth is not None:
root = meth()
- context.update(here=obj,
- # philiKON thinks container should be the view,
- # but BBB is more important than aesthetics.
- container=obj,
- root=root,
- modules=SecureModuleImporter,
- traverse_subpath=[], # BBB, never really worked
- user = getSecurityManager().getUser()
- )
- return context
+ namespace.update(here=obj,
+ # philiKON thinks container should be the view,
+ # but BBB is more important than aesthetics.
+ container=obj,
+ root=root,
+ modules=SecureModuleImporter,
+ traverse_subpath=[], # BBB, never really worked
+ user = getSecurityManager().getUser()
+ )
+ return namespace
def __get__(self, instance, type):
return BoundPageTemplate(self, instance)
+class ViewMapper(object):
+ def __init__(self, ob, request):
+ self.ob = ob
+ self.request = request
+
+ def __getitem__(self, name):
+ return getMultiAdapter((self.ob, self.request), name=name)
+
# When a view's template is accessed e.g. as template.view, a
# BoundPageTemplate object is retured. For BBB reasons, it needs to
# support the aq_* methods and attributes known from Acquisition. For
# that it also needs to be locatable thru __parent__.
-class BoundPageTemplate(viewpagetemplatefile.BoundPageTemplate,
- AcquisitionBBB):
+class BoundPageTemplate(AcquisitionBBB):
+ def __init__(self, pt, ob):
+ object.__setattr__(self, 'im_func', pt)
+ object.__setattr__(self, 'im_self', ob)
+ macros = property(lambda self: self.im_func.macros)
+ filename = property(lambda self: self.im_func.filename)
__parent__ = property(lambda self: self.im_self)
+ def __call__(self, *args, **kw):
+ if self.im_self is None:
+ im_self, args = args[0], args[1:]
+ else:
+ im_self = self.im_self
+ return self.im_func(im_self, *args, **kw)
+ def __setattr__(self, name, v):
+ raise AttributeError("Can't set attribute", name)
+
+ def __repr__(self):
+ return "<BoundPageTemplateFile of %r>" % self.im_self
+
+
# BBB
ZopeTwoPageTemplateFile = ViewPageTemplateFile
Modified: Zope/trunk/src/Products/Five/browser/tests/aqlegacy_ftest.txt
===================================================================
--- Zope/trunk/src/Products/Five/browser/tests/aqlegacy_ftest.txt 2009-05-26 00:06:45 UTC (rev 100382)
+++ Zope/trunk/src/Products/Five/browser/tests/aqlegacy_ftest.txt 2009-05-26 01:32:41 UTC (rev 100383)
@@ -176,9 +176,6 @@
Passing in an argument called instance was supported by the old Five version
of ViewPageTemplateFile, so we still need to support it.
-In the zope.app.pagetemplate version, the first required argument is called
-instance, though.
-
>>> print template(instance='allowed')
<p>The falcon has taken flight</p>
Added: Zope/trunk/src/Products/Five/browser/tests/test_metaconfigure.py
===================================================================
--- Zope/trunk/src/Products/Five/browser/tests/test_metaconfigure.py (rev 0)
+++ Zope/trunk/src/Products/Five/browser/tests/test_metaconfigure.py 2009-05-26 01:32:41 UTC (rev 100383)
@@ -0,0 +1,88 @@
+import unittest
+
+class ViewMixinForTemplatesTests(unittest.TestCase):
+
+ def _getTargetClass(self):
+ from Products.Five.browser.metaconfigure import ViewMixinForTemplates
+ return ViewMixinForTemplates
+
+ def _makeOne(self, context=None, request=None):
+ if context is None:
+ context = DummyContext()
+ if request is None:
+ request = DummyRequest()
+ return self._getTargetClass()(context, request)
+
+ def test_class_conforms_to_IBrowserPublisher(self):
+ from zope.interface.verify import verifyClass
+ from zope.publisher.interfaces.browser import IBrowserPublisher
+ verifyClass(IBrowserPublisher, self._getTargetClass())
+
+ def test_browserDefault(self):
+ request = DummyRequest()
+ view = self._makeOne(request=request)
+ self.assertEqual(view.browserDefault(request), (view, ()))
+
+ def test_publishTraverse_not_index_raises_NotFound(self):
+ from zope.publisher.interfaces import NotFound
+ request = DummyRequest()
+ view = self._makeOne(request=request)
+ self.assertRaises(NotFound, view.publishTraverse, request, 'nonesuch')
+
+ def test_publishTraverse_w_index_returns_index(self):
+ request = DummyRequest()
+ view = self._makeOne(request=request)
+ index = view.index = DummyTemplate()
+ self.failUnless(view.publishTraverse(request, 'index.html') is index)
+
+ def test___getitem___uses_index_macros(self):
+ view = self._makeOne()
+ view.index = index = DummyTemplate()
+ index.macros = {}
+ index.macros['aaa'] = aaa = object()
+ self.failUnless(view['aaa'] is aaa)
+
+ def test___call___no_args_no_kw(self):
+ view = self._makeOne()
+ view.index = index = DummyTemplate()
+ result = view()
+ self.failUnless(result is index)
+ self.assertEqual(index._called_with, ((), {}))
+
+ def test___call___w_args_no_kw(self):
+ view = self._makeOne()
+ view.index = index = DummyTemplate()
+ result = view('abc')
+ self.failUnless(result is index)
+ self.assertEqual(index._called_with, (('abc',), {}))
+
+ def test___call___no_args_w_kw(self):
+ view = self._makeOne()
+ view.index = index = DummyTemplate()
+ result = view(foo='bar')
+ self.failUnless(result is index)
+ self.assertEqual(index._called_with, ((), {'foo': 'bar'}))
+
+ def test___call___no_args_no_kw(self):
+ view = self._makeOne()
+ view.index = index = DummyTemplate()
+ result = view('abc', foo='bar')
+ self.failUnless(result is index)
+ self.assertEqual(index._called_with, (('abc',), {'foo': 'bar'}))
+
+
+class DummyContext:
+ pass
+
+class DummyRequest:
+ pass
+
+class DummyTemplate:
+ def __call__(self, *args, **kw):
+ self._called_with = (args, kw)
+ return self
+
+def test_suite():
+ return unittest.TestSuite((
+ unittest.makeSuite(ViewMixinForTemplatesTests),
+ ))
Added: Zope/trunk/src/Products/Five/browser/tests/test_pagetemplatefile.py
===================================================================
--- Zope/trunk/src/Products/Five/browser/tests/test_pagetemplatefile.py (rev 0)
+++ Zope/trunk/src/Products/Five/browser/tests/test_pagetemplatefile.py 2009-05-26 01:32:41 UTC (rev 100383)
@@ -0,0 +1,315 @@
+import unittest
+
+class ViewPageTemplateFileTests(unittest.TestCase):
+
+ def setUp(self):
+ from AccessControl.SecurityManagement import noSecurityManager
+ noSecurityManager()
+
+ def tearDown(self):
+ from AccessControl.SecurityManagement import noSecurityManager
+ noSecurityManager()
+
+ def _getTargetClass(self):
+ from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
+ return ViewPageTemplateFile
+
+ def _makeOne(self, filename, _prefix=None, content_type=None):
+ return self._getTargetClass()(filename, _prefix, content_type)
+
+ def _makeView(self, context=None, request=None):
+ if context is None:
+ context = DummyContext()
+ if request is None:
+ request = DummyRequest()
+ return DummyView(context, request)
+
+ def test_getId_simple_name(self):
+ vptf = self._makeOne('seagull.pt')
+ self.assertEqual(vptf.getId(), 'seagull.pt')
+ self.assertEqual(vptf.id, 'seagull.pt')
+
+ def test_getId_with_path(self):
+ vptf = self._makeOne('pages/dirpage1.pt')
+ self.assertEqual(vptf.id, 'dirpage1.pt')
+
+ def test_pt_getEngine(self):
+ from zope.tales.expressions import DeferExpr
+ from zope.tales.expressions import NotExpr
+ from zope.tales.expressions import PathExpr
+ from zope.tales.expressions import StringExpr
+ from zope.tales.expressions import Undefs
+ from zope.tales.pythonexpr import PythonExpr
+ from zope.contentprovider.tales import TALESProviderExpression
+ from Products.PageTemplates.DeferExpr import LazyExpr
+ from Products.PageTemplates.Expressions import ZopePathExpr
+ from Products.PageTemplates.Expressions import SecureModuleImporter
+
+ vptf = self._makeOne('seagull.pt')
+ engine = vptf.pt_getEngine()
+ self.assertEqual(engine.types['standard'], ZopePathExpr)
+ self.assertEqual(engine.types['path'], ZopePathExpr)
+ self.assertEqual(engine.types['exists'], ZopePathExpr)
+ self.assertEqual(engine.types['nocall'], ZopePathExpr)
+ self.assertEqual(engine.types['string'], StringExpr)
+ self.assertEqual(engine.types['python'], PythonExpr)
+ self.assertEqual(engine.types['not'], NotExpr)
+ self.assertEqual(engine.types['defer'], DeferExpr)
+ self.assertEqual(engine.types['lazy'], LazyExpr)
+ self.assertEqual(engine.types['provider'], TALESProviderExpression)
+ self.assertEqual(engine.base_names['modules'], SecureModuleImporter)
+
+ def test_pt_getContext_no_kw_no_physicalRoot(self):
+ from Products.Five.browser.pagetemplatefile import ViewMapper
+ from Products.PageTemplates.Expressions import SecureModuleImporter
+ from AccessControl.SecurityManagement import newSecurityManager
+ newSecurityManager(None, DummyUser('a_user'))
+ context = DummyContext()
+ request = DummyRequest()
+ view = self._makeView(context, request)
+ vptf = self._makeOne('seagull.pt')
+ namespace = vptf.pt_getContext(view, request)
+ self.failUnless(namespace['context'] is context)
+ self.failUnless(namespace['request'] is request)
+ views = namespace['views']
+ self.failUnless(isinstance(views, ViewMapper))
+ self.assertEqual(views.ob, context)
+ self.assertEqual(views.request, request)
+ self.failUnless(namespace['here'] is context)
+ self.failUnless(namespace['container'] is context)
+ self.failUnless(namespace['root'] is None)
+ modules = namespace['modules']
+ self.failUnless(modules is SecureModuleImporter)
+ self.assertEqual(namespace['traverse_subpath'], [])
+ self.assertEqual(namespace['user'].getId(), 'a_user')
+
+ def test_pt_getContext_w_physicalRoot(self):
+ from Products.Five.browser.pagetemplatefile import ViewMapper
+ from Products.PageTemplates.Expressions import SecureModuleImporter
+ from AccessControl.SecurityManagement import newSecurityManager
+ newSecurityManager(None, DummyUser('a_user'))
+ context = DummyContext()
+ root = DummyContext()
+ context.getPhysicalRoot = lambda: root
+ request = DummyRequest()
+ view = self._makeView(context, request)
+ vptf = self._makeOne('seagull.pt')
+ namespace = vptf.pt_getContext(view, request)
+ self.failUnless(namespace['root'] is root)
+
+ def test_pt_getContext_w_ignored_kw(self):
+ from Products.Five.browser.pagetemplatefile import ViewMapper
+ from Products.PageTemplates.Expressions import SecureModuleImporter
+ from AccessControl.SecurityManagement import newSecurityManager
+ newSecurityManager(None, DummyUser('a_user'))
+ context = DummyContext()
+ request = DummyRequest()
+ view = self._makeView(context, request)
+ vptf = self._makeOne('seagull.pt')
+ namespace = vptf.pt_getContext(view, request, foo='bar')
+ self.failIf('foo' in namespace)
+ self.failIf('foo' in namespace['options'])
+
+ def test_pt_getContext_w_args_kw(self):
+ from Products.Five.browser.pagetemplatefile import ViewMapper
+ from Products.PageTemplates.Expressions import SecureModuleImporter
+ from AccessControl.SecurityManagement import newSecurityManager
+ newSecurityManager(None, DummyUser('a_user'))
+ context = DummyContext()
+ request = DummyRequest()
+ view = self._makeView(context, request)
+ vptf = self._makeOne('seagull.pt')
+ namespace = vptf.pt_getContext(view, request, args=('bar', 'baz'))
+ self.assertEqual(namespace['args'], ('bar', 'baz'))
+
+ def test_pt_getContext_w_options_kw(self):
+ from Products.Five.browser.pagetemplatefile import ViewMapper
+ from Products.PageTemplates.Expressions import SecureModuleImporter
+ from AccessControl.SecurityManagement import newSecurityManager
+ newSecurityManager(None, DummyUser('a_user'))
+ context = DummyContext()
+ request = DummyRequest()
+ view = self._makeView(context, request)
+ vptf = self._makeOne('seagull.pt')
+ namespace = vptf.pt_getContext(view, request, options={'bar': 'baz'})
+ self.assertEqual(namespace['options'], {'bar': 'baz'})
+
+ def test___call___no_previous_content_type(self):
+ context = DummyContext()
+ request = DummyRequest()
+ response = request.response = DummyResponse()
+ view = self._makeView(context, request)
+ vptf = self._makeOne('pages/dirpage1.pt')
+ body = vptf(view)
+ self.assertEqual(body, DIRPAGE1)
+ self.assertEqual(response._headers['Content-Type'], 'text/html')
+
+ def test___call___w_previous_content_type(self):
+ context = DummyContext()
+ request = DummyRequest()
+ response = request.response = DummyResponse(
+ {'Content-Type': 'text/xhtml'})
+ view = self._makeView(context, request)
+ vptf = self._makeOne('pages/dirpage1.pt')
+ body = vptf(view)
+ self.assertEqual(response._headers['Content-Type'], 'text/xhtml')
+
+ def test___get___(self):
+ from Products.Five.browser.pagetemplatefile import BoundPageTemplate
+ template = self._makeOne('pages/dirpage1.pt')
+ class Foo:
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+ bar = template
+ context = DummyContext()
+ request = DummyRequest()
+ foo = Foo(context, request)
+ bound = foo.bar
+ self.failUnless(isinstance(bound, BoundPageTemplate))
+ self.failUnless(bound.im_func is template)
+ self.failUnless(bound.im_self is foo)
+
+class ViewMapperTests(unittest.TestCase):
+
+ def setUp(self):
+ from zope.component.testing import setUp
+ setUp()
+
+ def tearDown(self):
+ from zope.component.testing import tearDown
+ tearDown()
+
+ def _getTargetClass(self):
+ from Products.Five.browser.pagetemplatefile import ViewMapper
+ return ViewMapper
+
+ def _makeOne(self, ob=None, request=None):
+ if ob is None:
+ ob = DummyContext()
+ if request is None:
+ request = DummyRequest()
+ return self._getTargetClass()(ob, request)
+
+ def test___getitem___miss(self):
+ from zope.component import ComponentLookupError
+ mapper = self._makeOne()
+ self.assertRaises(ComponentLookupError, mapper.__getitem__, 'nonesuch')
+
+ def test___getitem___hit(self):
+ from zope.interface import Interface
+ from zope.component import provideAdapter
+ def _adapt(context, request):
+ return self
+ provideAdapter(_adapt, (None, None), Interface, name='test')
+ mapper = self._makeOne()
+ self.failUnless(mapper['test'] is self)
+
+_marker = object()
+
+class BoundPageTemplateTests(unittest.TestCase):
+
+ def _getTargetClass(self):
+ from Products.Five.browser.pagetemplatefile import BoundPageTemplate
+ return BoundPageTemplate
+
+ def _makeOne(self, pt=_marker, ob=_marker):
+ if pt is _marker:
+ pt = DummyTemplate()
+ if ob is _marker:
+ ob = DummyContext()
+ return self._getTargetClass()(pt, ob)
+
+ def test___init__(self):
+ pt = DummyTemplate({'foo': 'bar'})
+ ob = DummyContext()
+ bpt = self._makeOne(pt, ob)
+ self.failUnless(bpt.im_func is pt)
+ self.failUnless(bpt.im_self is ob)
+ self.failUnless(bpt.__parent__ is ob)
+ self.assertEqual(bpt.macros['foo'], 'bar')
+ self.assertEqual(bpt.filename, 'dummy.pt')
+
+ def test___setattr___raises(self):
+ bpt = self._makeOne()
+ try:
+ bpt.foo = 'bar'
+ except AttributeError:
+ pass
+ else:
+ self.fail('Attribute assigned')
+
+ def test___call___w_real_im_self_no_args_no_kw(self):
+ pt = DummyTemplate()
+ ob = DummyContext()
+ bpt = self._makeOne(pt, ob)
+ rendered = bpt()
+ self.assertEqual(rendered, '<h1>Dummy</h1>')
+ self.assertEqual(pt._called_with, (ob, (), {}))
+
+ def test___call___w_real_im_self_w_args_w_kw(self):
+ pt = DummyTemplate()
+ ob = DummyContext()
+ bpt = self._makeOne(pt, ob)
+ rendered = bpt('abc', foo='bar')
+ self.assertEqual(rendered, '<h1>Dummy</h1>')
+ self.assertEqual(pt._called_with, (ob, ('abc',), {'foo': 'bar'}))
+
+ def test___call___wo_real_im_self_w_args_w_kw(self):
+ pt = DummyTemplate()
+ bpt = self._makeOne(pt, None)
+ rendered = bpt('abc', 'def', foo='bar')
+ self.assertEqual(rendered, '<h1>Dummy</h1>')
+ self.assertEqual(pt._called_with, ('abc', ('def',), {'foo': 'bar'}))
+
+DIRPAGE1 = """\
+<html>
+<p>This is page 1</p>
+</html>
+"""
+
+class DummyContext:
+ pass
+
+class DummyRequest:
+ debug = object()
+
+class DummyResponse:
+ def __init__(self, headers=None):
+ if headers is None:
+ headers = {}
+ self._headers = headers
+
+ def getHeader(self, name):
+ return self._headers.get(name)
+
+ def setHeader(self, name, value):
+ self._headers[name] = value
+
+class DummyTemplate:
+ filename = 'dummy.pt'
+ def __init__(self, macros=None):
+ if macros is None:
+ macros = {}
+ self.macros = macros
+ def __call__(self, im_self, *args, **kw):
+ self._called_with = (im_self, args, kw)
+ return '<h1>Dummy</h1>'
+
+class DummyView:
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+class DummyUser:
+ def __init__(self, name):
+ self._name = name
+ def getId(self):
+ return self._name
+
+def test_suite():
+ return unittest.TestSuite((
+ unittest.makeSuite(ViewPageTemplateFileTests),
+ unittest.makeSuite(ViewMapperTests),
+ unittest.makeSuite(BoundPageTemplateTests),
+ ))
More information about the Checkins
mailing list