[Checkins] SVN: Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/ various changes to provide an IIndexableObject marker interface. Wrapping is applied to objects not providing this interface. Adjust tests to reflect this and provide a fixture for the PortalFolder tests instead of using a full-blown Catalog

Miles Waller miles at jamkit.com
Tue Mar 17 10:35:43 EDT 2009


Log message for revision 98183:
  various changes to provide an IIndexableObject marker interface.  Wrapping is applied to objects not providing this interface.  Adjust tests to reflect this and provide a fixture for the PortalFolder tests instead of using a full-blown Catalog

Changed:
  U   Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/CHANGES.txt
  U   Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/CatalogTool.py
  U   Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/configure.zcml
  U   Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/content.zcml
  D   Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/implements.zcml
  U   Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/interfaces/_tools.py
  U   Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/testing.py
  U   Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/tests/test_CatalogTool.py
  U   Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/tests/test_PortalFolder.py

-=-
Modified: Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/CHANGES.txt
===================================================================
--- Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/CHANGES.txt	2009-03-17 14:11:53 UTC (rev 98182)
+++ Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/CHANGES.txt	2009-03-17 14:35:42 UTC (rev 98183)
@@ -4,8 +4,9 @@
 2.2.0 (unreleased)
 ------------------
 
-- PortalCatalog: Changed to use a multi-adaptor to allow pluggable
-  IndexableObjectWrapper class.  The change will assist in integrating with
+- PortalCatalog: Changed to use a multi-adaptor to allow a pluggable
+  IndexableObjectWrapper class.  Objects that implement IIndexableObject
+  are not wrapped.  The change will assist in integrating with
   other indexing strategies from third-party packages.
 
 - Events: Changed 'handleContentishEvent' behavior for IObjectCopiedEvent.

Modified: Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/CatalogTool.py
===================================================================
--- Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/CatalogTool.py	2009-03-17 14:11:53 UTC (rev 98182)
+++ Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/CatalogTool.py	2009-03-17 14:35:42 UTC (rev 98183)
@@ -34,7 +34,8 @@
 from Products.CMFCore.ActionProviderBase import ActionProviderBase
 from Products.CMFCore.interfaces import ICatalogTool
 from Products.CMFCore.interfaces import IIndexableObjectWrapper
-from Products.CMFCore.interfaces import IContentish
+from Products.CMFCore.interfaces import IIndexableObject
+from Products.CMFCore.interfaces import ICatalogAware
 from Products.CMFCore.permissions import AccessInactivePortalContent
 from Products.CMFCore.permissions import ManagePortal
 from Products.CMFCore.permissions import View
@@ -45,8 +46,6 @@
 from Products.CMFCore.utils import getToolByName
 from Products.CMFCore.utils import UniqueObject
 
-from zope.component import getMultiAdapter
-
 class IndexableObjectSpecification(ObjectSpecificationDescriptor):
 
     # This class makes the wrapper transparent, adapter lookup is
@@ -63,8 +62,8 @@
 
 class IndexableObjectWrapper(object):
 
-    implements(IIndexableObjectWrapper)
-    adapts(IContentish, ICatalogTool)
+    implements(IIndexableObjectWrapper, IIndexableObject)
+    adapts(ICatalogAware, ICatalogTool)
     __providedBy__ = IndexableObjectSpecification()
 
     def __init__(self, ob, catalog):
@@ -258,7 +257,10 @@
         # information just before cataloging.
         # XXX: this method violates the rules for tools/utilities:
         # it depends on a non-utility tool
-        w = getMultiAdapter((obj, self), IIndexableObjectWrapper)
+        if not IIndexableObject.providedBy(obj):
+            w = IIndexableObject((obj, self))
+        else:
+            w = obj
         ZCatalog.catalog_object(self, w, uid, idxs, update_metadata,
                                 pghandler)
 

Modified: Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/configure.zcml
===================================================================
--- Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/configure.zcml	2009-03-17 14:11:53 UTC (rev 98182)
+++ Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/configure.zcml	2009-03-17 14:35:42 UTC (rev 98183)
@@ -13,6 +13,4 @@
 
   <include file="tool.zcml"/>
 
-  <include file="implements.zcml"/>
-
 </configure>

Modified: Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/content.zcml
===================================================================
--- Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/content.zcml	2009-03-17 14:11:53 UTC (rev 98182)
+++ Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/content.zcml	2009-03-17 14:35:42 UTC (rev 98183)
@@ -29,4 +29,11 @@
       name="cmf.folder.btree"
       />
 
+  <!-- Default wrapper for indexing IContentish objects -->
+  <adapter
+      for=".interfaces.ICatalogAware
+           .interfaces.ICatalogTool"
+      provides=".interfaces.IIndexableObject"
+      factory=".CatalogTool.IndexableObjectWrapper" />
+
 </configure>

Deleted: Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/implements.zcml
===================================================================
--- Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/implements.zcml	2009-03-17 14:11:53 UTC (rev 98182)
+++ Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/implements.zcml	2009-03-17 14:35:42 UTC (rev 98183)
@@ -1,11 +0,0 @@
-<configure
-    xmlns="http://namespaces.zope.org/zope">
-
-  <!-- Default wrapper for indexing IContentish objects -->
-  <adapter
-      for="Products.CMFCore.interfaces.IContentish
-           Products.CMFCore.interfaces.ICatalogTool"
-      provides="Products.CMFCore.interfaces.IIndexableObjectWrapper"
-      factory="Products.CMFCore.CatalogTool.IndexableObjectWrapper" />
-
-</configure>

Modified: Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/interfaces/_tools.py
===================================================================
--- Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/interfaces/_tools.py	2009-03-17 14:11:53 UTC (rev 98182)
+++ Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/interfaces/_tools.py	2009-03-17 14:35:42 UTC (rev 98183)
@@ -406,7 +406,12 @@
           a user is not allowed to see.
         """
 
+class IIndexableObject(Interface):
 
+    """ Marker interface for objects that can be indexed in
+        the portal catalog
+    """
+
 #
 #   PUT factory handler interfaces
 #

Modified: Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/testing.py
===================================================================
--- Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/testing.py	2009-03-17 14:11:53 UTC (rev 98182)
+++ Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/testing.py	2009-03-17 14:35:42 UTC (rev 98183)
@@ -131,7 +131,6 @@
         zcml.load_config('traversing.zcml', Products.Five)
         zcml.load_config('event.zcml', Products.Five)
         zcml.load_config('event.zcml', Products.CMFCore)
-        zcml.load_config('implements.zcml', Products.CMFCore)
         setHooks()
 
     @classmethod

Modified: Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/tests/test_CatalogTool.py
===================================================================
--- Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/tests/test_CatalogTool.py	2009-03-17 14:11:53 UTC (rev 98182)
+++ Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/tests/test_CatalogTool.py	2009-03-17 14:35:42 UTC (rev 98183)
@@ -21,6 +21,10 @@
 from zope.interface import implements
 from zope.component import getMultiAdapter
 from Products.CMFCore.interfaces import ICatalogTool
+from Products.CMFCore.tests.base.dummy import DummyContent
+from Products.CMFCore.interfaces import IIndexableObject
+from Products.CMFCore.interfaces import ICatalogAware
+
 from Products.CMFCore.tests.base.testcase import SecurityTest
 
 class FakeFolder(Implicit):
@@ -39,6 +43,22 @@
     def getCatalogVariablesFor(self, ob):
         return self._vars
 
+class CatalogDummyContent(DummyContent):
+
+    """ Dummy content that already provides IIndexableObject
+        and therefore does not need a wrapper to be registered
+    """
+
+    implements(IIndexableObject, ICatalogAware)
+    allowedRolesAndUsers = ['Manager'] # default value
+
+class CatalogDummyContent2(DummyContent):
+
+    """ Dummy content that needs a wrapper to be registered
+    """
+
+    implements(ICatalogAware)
+
 class IndexableObjectWrapperTests(unittest.TestCase):
 
     def _getTargetClass(self):
@@ -101,12 +121,12 @@
 
     def test_adapts(self):
         from zope.component import adaptedBy
-        from Products.CMFCore.interfaces import IContentish
+        from Products.CMFCore.interfaces import ICatalogAware
         from Products.CMFCore.interfaces import ICatalogTool
 
         w = self._getTargetClass()
         adapts =  adaptedBy(w) 
-        self.assertEqual(adapts, (IContentish, ICatalogTool))
+        self.assertEqual(adapts, (ICatalogAware, ICatalogTool))
 
 class CatalogToolTests(SecurityTest):
 
@@ -119,18 +139,8 @@
         return self._getTargetClass()(*args, **kw)
 
     def _makeContent(self, *args, **kw):
-        from Products.CMFCore.tests.base.dummy import DummyContent
-        from Products.CMFCore.interfaces import ICatalogTool
-        from Products.CMFCore.interfaces import IContentish
-        from Products.CMFCore.interfaces import IIndexableObjectWrapper
-        from Products.CMFCore.CatalogTool import IndexableObjectWrapper
+        return CatalogDummyContent(*args, **kw)
 
-        from zope.component import getSiteManager
-        self.sm = getSiteManager()
-        self.sm.registerAdapter(IndexableObjectWrapper, (IContentish, ICatalogTool), IIndexableObjectWrapper)
-
-        return DummyContent(*args, **kw)
-
     def test_interfaces(self):
         from zope.interface.verify import verifyClass
         from Products.CMFCore.interfaces import IActionProvider
@@ -185,7 +195,7 @@
         catalog = self._makeOne()
         catalog.addIndex('allowedRolesAndUsers', 'KeywordIndex')
         dummy = self._makeContent(catalog=1)
-        dummy._View_Permission = ('Blob',)
+        dummy.allowedRolesAndUsers = ('Blob',)
         catalog.catalog_object(dummy, '/dummy')
 
         self.loginWithRoles('Blob')
@@ -197,7 +207,7 @@
         catalog = self._makeOne()
         catalog.addIndex('allowedRolesAndUsers', 'KeywordIndex')
         dummy = self._makeContent(catalog=1)
-        dummy._View_Permission = ('Blob',)
+        dummy.allowedRolesAndUsers = ('Blob',)
         catalog.catalog_object(dummy, '/dummy')
 
         self.loginWithRoles('Blob')
@@ -210,7 +220,7 @@
         catalog = self._makeOne()
         catalog.addIndex('allowedRolesAndUsers', 'KeywordIndex')
         dummy = self._makeContent(catalog=1)
-        dummy._View_Permission = ('Blob',)
+        dummy.allowedRoleAndUsers = ('Blob',)
         catalog.catalog_object(dummy, '/dummy')
 
         self.loginWithRoles('Waggle')
@@ -222,7 +232,7 @@
         catalog = self._makeOne()
         catalog.addIndex('allowedRolesAndUsers', 'KeywordIndex')
         dummy = self._makeContent(catalog=1)
-        dummy._View_Permission = ('Blob',)
+        dummy.allowedRolesAndUsers = ('Blob',)
         catalog.catalog_object(dummy, '/dummy')
 
         self.loginWithRoles('Waggle')
@@ -239,7 +249,7 @@
         catalog.addIndex('expires', 'DateIndex')
         now = DateTime()
         dummy = self._makeContent(catalog=1)
-        dummy._View_Permission = ('Blob',)
+        dummy.allowedRolesAndUsers = ('Blob',)
 
         self.loginWithRoles('Blob')
 
@@ -300,7 +310,7 @@
         catalog.addIndex('expires', 'DateIndex')
         now = DateTime()
         dummy = self._makeContent(catalog=1)
-        dummy._View_Permission = ('Blob',)
+        dummy.allowedRolesAndUsers = ('Blob',)
 
         self.loginWithRoles('Blob')
 
@@ -334,7 +344,7 @@
         catalog.addIndex('expires', 'DateIndex')
         now = DateTime()
         dummy = self._makeContent(catalog=1)
-        dummy._View_Permission = ('Blob',)
+        dummy.allowedRolesAndUsers = ('Blob',)
 
         self.loginWithRoles('Blob')
 
@@ -480,7 +490,40 @@
         self.failUnless('Blob' in arus)
         self.failUnless('user:%s' % user.getId() in arus)       
 
+    def test_wrapping1(self):
+        # DummyContent implements IIndexableObject
+        # so should be indexed
+        dummy = self._makeContent(catalog=1)
+        ctool = self._makeOne()
+        ctool.catalog_object(dummy, '/dummy')
+        self.assertEqual(1, len(ctool._catalog.searchResults()))
 
+    def test_wrapping2(self):
+        # DummyContent does not implement IIndexableObject
+        # no wrapper registered - should create an error
+        dummy = DummyContent(catalog=1)
+        ctool = self._makeOne()
+        self.assertRaises(TypeError, ctool.catalog_object, 
+                              dummy, '/dummy')
+
+    def test_wrapping3(self):
+        # DummyContent does not implement IIndexableObject
+        # wrapper registered - should look this up
+
+        def FakeWrapper(object, catalog):
+            return object
+
+        from zope.component import getSiteManager
+        self.sm = getSiteManager()
+        self.sm.registerAdapter( FakeWrapper
+                               , (ICatalogAware, ICatalogTool)
+                               , IIndexableObject )
+
+        dummy = CatalogDummyContent2(catalog=1)
+        ctool = self._makeOne()
+        ctool.catalog_object(dummy, '/dummy')
+        self.assertEqual(1, len(ctool._catalog.searchResults()))
+
 def test_suite():
     return unittest.TestSuite((
         unittest.makeSuite(IndexableObjectWrapperTests),

Modified: Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/tests/test_PortalFolder.py
===================================================================
--- Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/tests/test_PortalFolder.py	2009-03-17 14:11:53 UTC (rev 98182)
+++ Products.CMFCore/branches/miwa-catalog-adapter/Products/CMFCore/tests/test_PortalFolder.py	2009-03-17 14:35:42 UTC (rev 98183)
@@ -31,7 +31,7 @@
 from zope.interface import implements
 from zope.interface.verify import verifyClass
 
-from Products.CMFCore.CatalogTool import CatalogTool
+from Products.CMFCore.interfaces import ICatalogTool
 from Products.CMFCore.exceptions import BadRequest
 from Products.CMFCore.interfaces import ITypesTool
 from Products.CMFCore.testing import ConformsToFolder
@@ -45,17 +45,44 @@
 from Products.CMFCore.tests.base.testcase import SecurityTest
 from Products.CMFCore.tests.base.tidata import FTIDATA_CMF15
 from Products.CMFCore.tests.base.tidata import FTIDATA_DUMMY
-from Products.CMFCore.tests.base.utils import has_path
 from Products.CMFCore.TypesTool import FactoryTypeInformation as FTI
 from Products.CMFCore.TypesTool import TypesTool
 from Products.CMFCore.WorkflowTool import WorkflowTool
+from types import TupleType
 
-
 def extra_meta_types():
     return [{'name': 'Dummy', 'action': 'manage_addFolder',
              'permission': 'View'}]
 
+class DummyCatalogTool:
+    implements(ICatalogTool)
 
+    def __init__(self):
+       self.paths = []
+       self.ids = []
+
+    def indexObject(self, object):
+       self.paths.append( '/'.join(object.getPhysicalPath()) )
+       self.ids.append( object.getId() )
+
+    def unindexObject(self, object):
+       self.paths.remove( '/'.join(object.getPhysicalPath()) )
+       self.ids.append( object.getId() )
+
+    def reindexObject(self, object):
+       pass
+
+    def __len__(self):
+       return len(self.paths)
+
+def has_path(catalog, path):
+    if type(path) is TupleType:
+       path = '/'.join(path)
+    return path in catalog.paths
+
+def has_id(catalog, id):
+    return id in catalog.ids
+
 class PortalFolderFactoryTests(SecurityTest):
 
     layer = TraversingEventZCMLLayer
@@ -211,7 +238,7 @@
         #
         test = self._makeOne('test')
         ttool = self.site._setObject( 'portal_types', TypesTool() )
-        ctool = self.site._setObject( 'portal_catalog', CatalogTool() )
+        ctool = self.site._setObject( 'portal_catalog', DummyCatalogTool() )
         self.assertEqual( len(ctool), 0 )
 
         test._setObject( 'foo', DummyContent( 'foo' , catalog=1 ) )
@@ -232,7 +259,7 @@
         # instantiation (Tracker issue 309)
         #
         ttool = self.site._setObject( 'portal_types', TypesTool() )
-        ctool = self.site._setObject( 'portal_catalog', CatalogTool() )
+        ctool = self.site._setObject( 'portal_catalog', DummyCatalogTool() )
         wftool = self.site._setObject( 'portal_workflow', WorkflowTool() )
         test = self._makeOne('test')
         wftool.notifyCreated(test)
@@ -248,7 +275,7 @@
 
         test = self._makeOne('test')
         ttool = self.site._setObject( 'portal_types', TypesTool() )
-        ctool = self.site._setObject( 'portal_catalog', CatalogTool() )
+        ctool = self.site._setObject( 'portal_catalog', DummyCatalogTool() )
         self.assertEqual( len(ctool), 0 )
 
         test._setObject( 'sub', PortalFolder( 'sub', '' ) )
@@ -436,29 +463,28 @@
         from Products.CMFCore.PortalFolder import PortalFolder
 
         ttool = self.site._setObject( 'portal_types', TypesTool() )
-        ctool = self.site._setObject( 'portal_catalog', CatalogTool() )
-        ctool.addIndex('getId', 'FieldIndex')
+        ctool = self.site._setObject( 'portal_catalog', DummyCatalogTool() )
         self.assertEqual( len(ctool), 0 )
 
         folder = self._makeOne('folder')
         folder._setObject( 'sub', PortalFolder( 'sub', '' ) )
         folder.sub._setObject( 'foo', DummyContent( 'foo', catalog=1 ) )
         self.assertEqual( len(ctool), 1 )
-        self.failUnless( 'foo' in ctool.uniqueValuesFor('getId') )
-        self.failUnless( has_path(ctool._catalog,
+        self.failUnless( has_id(ctool, 'foo') )
+        self.failUnless( has_path(ctool,
                                   '/bar/site/folder/sub/foo') )
 
         transaction.savepoint(optimistic=True)
         folder.manage_renameObject(id='sub', new_id='new_sub')
         self.assertEqual( len(ctool), 1 )
-        self.failUnless( 'foo' in ctool.uniqueValuesFor('getId') )
-        self.failUnless( has_path(ctool._catalog,
+        self.failUnless( has_id(ctool, 'foo') )
+        self.failUnless( has_path(ctool,
                                   '/bar/site/folder/new_sub/foo') )
 
         folder._setObject( 'bar', DummyContent( 'bar', catalog=1 ) )
         self.assertEqual( len(ctool), 2 )
-        self.failUnless( 'bar' in ctool.uniqueValuesFor('getId') )
-        self.failUnless( has_path(ctool._catalog, '/bar/site/folder/bar') )
+        self.failUnless( has_id(ctool, 'bar') )
+        self.failUnless( has_path(ctool, '/bar/site/folder/bar') )
 
         folder._setObject( 'sub2', PortalFolder( 'sub2', '' ) )
         sub2 = folder.sub2
@@ -471,17 +497,17 @@
         cookie = folder.manage_cutObjects(ids=['bar'])
         sub2.manage_pasteObjects(cookie)
 
-        self.failUnless( 'foo' in ctool.uniqueValuesFor('getId') )
-        self.failUnless( 'bar' in ctool.uniqueValuesFor('getId') )
+        self.failUnless( has_id( ctool, 'foo' ) )
+        self.failUnless( has_id( ctool, 'bar' ) )
         self.assertEqual( len(ctool), 2 )
-        self.failUnless( has_path(ctool._catalog,
+        self.failUnless( has_path(ctool,
                                   '/bar/site/folder/sub2/bar') )
 
     def test_contentPaste(self):
         #
         #   Does copy / paste work?
         #
-        ctool = self.site._setObject( 'portal_catalog', CatalogTool() )
+        ctool = self.site._setObject( 'portal_catalog', DummyCatalogTool() )
         ttool = self.site._setObject( 'portal_types', TypesTool() )
         fti = FTIDATA_DUMMY[0].copy()
         ttool._setObject( 'Dummy Content', FTI(**fti) )
@@ -497,9 +523,9 @@
         self.failIf( 'dummy' in sub2.contentIds() )
         self.failIf( 'dummy' in sub3.objectIds() )
         self.failIf( 'dummy' in sub3.contentIds() )
-        self.failUnless( has_path(ctool._catalog, '/bar/site/sub1/dummy') )
-        self.failIf( has_path(ctool._catalog, '/bar/site/sub2/dummy') )
-        self.failIf( has_path(ctool._catalog, '/bar/site/sub3/dummy') )
+        self.failUnless( has_path(ctool, '/bar/site/sub1/dummy') )
+        self.failIf( has_path(ctool, '/bar/site/sub2/dummy') )
+        self.failIf( has_path(ctool, '/bar/site/sub3/dummy') )
 
         cookie = sub1.manage_copyObjects( ids = ( 'dummy', ) )
         # Waaa! force sub2 to allow paste of Dummy object.
@@ -513,9 +539,9 @@
         self.failUnless( 'dummy' in sub2.contentIds() )
         self.failIf( 'dummy' in sub3.objectIds() )
         self.failIf( 'dummy' in sub3.contentIds() )
-        self.failUnless( has_path(ctool._catalog, '/bar/site/sub1/dummy') )
-        self.failUnless( has_path(ctool._catalog, '/bar/site/sub2/dummy') )
-        self.failIf( has_path(ctool._catalog, '/bar/site/sub3/dummy') )
+        self.failUnless( has_path(ctool, '/bar/site/sub1/dummy') )
+        self.failUnless( has_path(ctool, '/bar/site/sub2/dummy') )
+        self.failIf( has_path(ctool, '/bar/site/sub3/dummy') )
 
         transaction.savepoint(optimistic=True)
         cookie = sub1.manage_cutObjects( ids = ('dummy',) )
@@ -530,9 +556,9 @@
         self.failUnless( 'dummy' in sub2.contentIds() )
         self.failUnless( 'dummy' in sub3.objectIds() )
         self.failUnless( 'dummy' in sub3.contentIds() )
-        self.failIf( has_path(ctool._catalog, '/bar/site/sub1/dummy') )
-        self.failUnless( has_path(ctool._catalog, '/bar/site/sub2/dummy') )
-        self.failUnless( has_path(ctool._catalog, '/bar/site/sub3/dummy') )
+        self.failIf( has_path(ctool, '/bar/site/sub1/dummy') )
+        self.failUnless( has_path(ctool, '/bar/site/sub2/dummy') )
+        self.failUnless( has_path(ctool, '/bar/site/sub3/dummy') )
 
 
 class ContentFilterTests(unittest.TestCase):



More information about the Checkins mailing list